Skip to content

Commit 339d779

Browse files
authored
AML-6 Model Manifest Compilation Integration (#113)
* first commit (CLI function) * wip * initial functional commit with Compilation * added CLI endpoint to CLI tool * moved subparser before parser arg processing * renamed CLI function compile to lock, fixed grammar issue * renamed lock to freeze, addressed bug detected by lemonez * removed unnecessary import
1 parent dda808e commit 339d779

File tree

3 files changed

+55
-5
lines changed

3 files changed

+55
-5
lines changed

Algorithmia/CLI.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
import os
33
from Algorithmia.errors import DataApiError
44
from Algorithmia.algo_response import AlgoResponse
5+
from Algorithmia.util import md5_for_file, md5_for_str
56
import json, re, requests, six
67
import toml
78
import shutil
8-
9+
from time import time
910

1011
class CLI:
1112
def __init__(self):
@@ -101,7 +102,7 @@ def runalgo(self, options, client):
101102
elif (options.binary_file != None):
102103
# binary file
103104
with open(options.binary_file, "rb") as f:
104-
algo_inputs = bytes(f.read())
105+
algo_input = bytes(f.read())
105106
key = self.getAPIkey(options.profile)
106107
content = 'application/octet-stream'
107108

@@ -245,6 +246,31 @@ def cat(self, path, client):
245246

246247
return result
247248

249+
# algo freeze
250+
def freezeAlgo(self, client, manifest_path="model_manifest.json"):
251+
if os.path.exists(manifest_path):
252+
with open(manifest_path, 'r') as f:
253+
manifest_file = json.load(f)
254+
manifest_file['timestamp'] = str(time())
255+
required_files = manifest_file['required_files']
256+
optional_files = manifest_file['optional_files']
257+
for i in range(len(required_files)):
258+
uri = required_files[i]['source_uri']
259+
local_file = client.file(uri).getFile(as_path=True)
260+
md5_checksum = md5_for_file(local_file)
261+
required_files[i]['md5_checksum'] = md5_checksum
262+
for i in range(len(optional_files)):
263+
uri = required_files[i]['source_uri']
264+
local_file = client.file(uri).getFile(as_path=True)
265+
md5_checksum = md5_for_file(local_file)
266+
required_files[i]['md5_checksum'] = md5_checksum
267+
lock_md5_checksum = md5_for_str(str(manifest_file))
268+
manifest_file['lock_checksum'] = lock_md5_checksum
269+
with open('model_manifest.json.freeze', 'w') as f:
270+
json.dump(manifest_file, f)
271+
else:
272+
print("Expected to find a model_manifest.json file, none was discovered in working directory")
273+
248274
# algo cp <src> <dest>
249275
def cp(self, src, dest, client):
250276

Algorithmia/__main__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def main():
108108
parser_cat.add_argument('--profile', action = 'store', type = str, default = 'default')
109109

110110
#sub parser for getting environment template
111-
parser_template = subparsers.add_parser('template',help='template <envid> <dest> downloads an environment template to the destination')
111+
parser_template = subparsers.add_parser('template', help='template <envid> <dest> downloads an environment template to the destination')
112112
parser_template.add_argument('envid',help='environment specification id')
113113
parser_template.add_argument('dest',help='destination for template download')
114114

@@ -130,8 +130,12 @@ def main():
130130
subparsers.add_parser('help')
131131
parser.add_argument('--profile', action = 'store', type = str, default = 'default')
132132

133+
#sub parser for freeze
134+
subparsers.add_parser('freeze', help="freezes a model_manifest.json file into a model_manifest.json.freeze")
135+
133136
args = parser.parse_args()
134137

138+
135139
#run auth before trying to create a client
136140
if args.cmd == 'auth':
137141

@@ -215,6 +219,9 @@ def main():
215219
elif args.cmd == 'builds':
216220
print(CLI().getBuildLogs(args.user, args.algo, client))
217221

222+
elif args.cmd == "freeze":
223+
print(CLI().freezeAlgo(client))
224+
218225
else:
219226
parser.parse_args(['-h'])
220227

Algorithmia/util.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import re
2-
from Algorithmia.errors import DataApiError
2+
import hashlib
33

44
FNAME_MATCH = re.compile(r'/([^/]+)$') # From the last slash to the end of the string
5-
PREFIX = re.compile(r'([^:]+://)(/)?(.+)') # Check for a prefix like data://
5+
PREFIX = re.compile(r'([^:]+://)(/)?(.+)') # Check for a prefix like data://
6+
7+
68
def getParentAndBase(path):
79
match = PREFIX.match(path)
810
if match is None:
@@ -26,7 +28,22 @@ def getParentAndBase(path):
2628
parent_path = '{prefix}{uri}'.format(prefix=prefix, uri='/'.join(parts[:-1]))
2729
return parent_path, parts[-1]
2830

31+
2932
def pathJoin(parent, base):
3033
if parent.endswith('/'):
3134
return parent + base
3235
return parent + '/' + base
36+
37+
38+
def md5_for_file(fname):
39+
hash_md5 = hashlib.md5()
40+
with open(fname, "rb") as f:
41+
for chunk in iter(lambda: f.read(4096), b""):
42+
hash_md5.update(chunk)
43+
return str(hash_md5.hexdigest())
44+
45+
46+
def md5_for_str(content):
47+
hash_md5 = hashlib.md5()
48+
hash_md5.update(content.encode())
49+
return str(hash_md5.hexdigest())

0 commit comments

Comments
 (0)