Skip to content

Commit 957158a

Browse files
authored
Merge pull request #128 from Michaelliv/function_to_item
Function yamls to item yamls
2 parents bafa2ed + 62e60c0 commit 957158a

File tree

58 files changed

+995
-68
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+995
-68
lines changed

aggregate/item.yaml

+3-4
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,20 @@ categories:
44
description: Rolling aggregation over Metrics and Lables according to specifications
55
doc: ''
66
example: aggregate.ipynb
7-
generationDate: '2021-02-13 11:28:47.287600'
7+
generationDate: 2021-05-19:22-31
88
icon: ''
99
labels:
1010
author: avia
1111
maintainers: []
1212
marketplaceType: ''
1313
mlrunVersion: 0.6.2
1414
name: aggregate
15-
org: Iguazio
1615
platformVersion: 3.0.0
1716
spec:
1817
filename: aggregate.py
1918
handler: aggregate
2019
image: mlrun/ml-models
2120
kind: job
22-
requirements: ['mlrun','pandas']
21+
requirements: []
2322
url: ''
24-
version: 0.0.1
23+
version: 0.0.1

aggregate/requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mlrun
2+
pandas

arc_to_parquet/item.yaml

+3-4
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,20 @@ categories:
55
description: retrieve remote archive, open and save as parquet
66
doc: ''
77
example: arc_to_parquet.ipynb
8-
generationDate: '2021-02-13 11:28:47.396240'
8+
generationDate: 2021-05-19:22-04
99
icon: ''
1010
labels:
1111
author: yjb
1212
maintainers: []
1313
marketplaceType: ''
1414
mlrunVersion: 0.5.4
1515
name: arc-to-parquet
16-
org: Iguazio
1716
platformVersion: 2.10.0
1817
spec:
1918
filename: arc_to_parquet.py
2019
handler: arc_to_parquet
2120
image: mlrun/ml-base
2221
kind: job
23-
requirements: ['mlrun','pandas','pyarrow']
22+
requirements: []
2423
url: ''
25-
version: 0.0.1
24+
version: 0.0.1

arc_to_parquet/requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mlrun
2+
pyarrow
3+
pandas

bert_embeddings/bert_embeddings.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
from transformers import BertModel, BertTokenizer
2-
import torch
3-
from typing import Union, List
41
import json
52
import pickle
63

4+
import torch
5+
from transformers import BertModel, BertTokenizer
6+
77

88
def init_context(context):
99
tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
@@ -24,4 +24,4 @@ def handler(context, event):
2424
with torch.no_grad():
2525
embeddings = context.user_data.model(**docs)
2626
embeddings = [embeddings[0].numpy(), embeddings[1].numpy()]
27-
return pickle.dumps(embeddings)
27+
return pickle.dumps(embeddings)

bert_embeddings/item.yaml

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,22 @@ categories:
66
description: Get BERT based embeddings for given text
77
doc: ''
88
example: bert_embeddings.ipynb
9-
generationDate: '2021-02-13 11:28:47.759006'
9+
generationDate: 2021-05-19:22-04
1010
icon: ''
1111
labels:
1212
framework: pytorch
1313
maintainers: []
1414
marketplaceType: ''
1515
mlrunVersion: 0.5.4
1616
name: bert-embeddings
17-
org: Iguazio
1817
platformVersion: 2.10.0
1918
spec:
2019
filename: bert_embeddings.py
21-
handler: ''
20+
handler: handler
2221
image: ''
2322
kind: nuclio
24-
requirements: ['torch==1.6.0','transformers==3.0.1','nuclio']
23+
requirements:
24+
- torch==1.6.0
25+
- transformers==3.0.1
2526
url: ''
26-
version: 0.0.1
27+
version: 0.0.1

catalog.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,14 @@ model-monitoring-batch:
153153
docfile: /home/michaell/projects/functions/model_monitoring_batch/model_monitoring_batch.ipynb
154154
kind: job
155155
versions:
156-
latest: /home/michaell/projects/functions/model_monitoring_batch/model_monitoring_batch.yaml
156+
latest: /home/michaell/projects/functions/model_monitoring_batch/function.yaml
157157
model-monitoring-stream:
158158
categories: []
159159
description: ''
160160
docfile: /home/michaell/projects/functions/model_monitoring_stream/model_monitoring_stream.ipynb
161161
kind: remote
162162
versions:
163-
latest: /home/michaell/projects/functions/model_monitoring_stream/model_monitoring_stream.yaml
163+
latest: /home/michaell/projects/functions/model_monitoring_stream/function.yaml
164164
model-server:
165165
categories:
166166
- serving

churn_server/item.yaml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
apiVersion: v1
2+
categories:
3+
- serving
4+
- ml
5+
description: churn classification and predictor
6+
doc: ''
7+
example: churn_server.ipynb
8+
generationDate: 2021-05-19:22-04
9+
icon: ''
10+
labels:
11+
framework: churn
12+
maintainers: []
13+
marketplaceType: ''
14+
mlrunVersion: ''
15+
name: churn-server
16+
platformVersion: ''
17+
spec:
18+
filename: churn_server.py
19+
handler: handler
20+
image: mlrun/ml-models
21+
kind: remote
22+
requirements: []
23+
url: ''
24+
version: 0.0.1

cli/function_to_item.py

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
from datetime import datetime
2+
from pathlib import Path
3+
from typing import Union
4+
5+
import click
6+
import yaml
7+
8+
from cli.helpers import is_function_dir
9+
from cli.path_iterator import PathIterator
10+
11+
12+
@click.command()
13+
@click.option(
14+
"-p",
15+
"-path",
16+
help="Path to one of: specific function.yaml, directory containing function.yaml or a root directory to search function.yamls in",
17+
)
18+
def function_to_item_cli(path: str):
19+
function_to_item(path)
20+
21+
22+
def function_to_item(path: str):
23+
path = Path(path)
24+
25+
if not path.exists():
26+
click.echo(f"{path} not found")
27+
exit(1)
28+
29+
# If this is a directory, its either project root or a specific directory in the project
30+
if path.is_dir():
31+
function_path = path / "function.yaml"
32+
# If its a function directory
33+
if function_path.exists():
34+
item_path = path / "item.yaml"
35+
item = function_yaml_to_item(function_path)
36+
with open(item_path, "w") as f:
37+
yaml.dump(item, f)
38+
# Otherwise its a root directory to be iterated
39+
else:
40+
function_iterator = PathIterator(
41+
root=path, rule=is_function_dir, as_path=True
42+
)
43+
for function in function_iterator:
44+
function_path = function / "function.yaml"
45+
item_path = function / "item.yaml"
46+
item = function_yaml_to_item(function_path)
47+
with open(item_path, "w") as f:
48+
yaml.dump(item, f)
49+
# Otherwise its a path to function.yaml
50+
else:
51+
path_dir = path.parent
52+
item_path = path_dir / "item.yaml"
53+
item = function_yaml_to_item(path)
54+
with open(item_path, "w") as f:
55+
yaml.dump(item, f)
56+
exit(0)
57+
58+
59+
def function_yaml_to_item(function_path: Union[str, Path]) -> dict:
60+
61+
function_path = Path(function_path)
62+
function_yaml = yaml.full_load(open(function_path))
63+
64+
metadata = function_yaml.get("metadata", {})
65+
spec = function_yaml.get("spec", {})
66+
67+
item = {
68+
"apiVersion": "v1",
69+
"categories": metadata.get("categories") or [],
70+
"description": spec.get("description") or "",
71+
"doc": "",
72+
"example": get_ipynb_file(function_path.parent),
73+
"generationDate": datetime.utcnow().strftime("%Y-%m-%d:%H-%M"),
74+
"icon": "",
75+
"labels": metadata.get("labels") or {},
76+
"maintainers": [],
77+
"mlrunVersion": "",
78+
"name": metadata.get("name") or "",
79+
"platformVersion": "",
80+
"spec": {
81+
"filename": get_py_file(function_path.parent),
82+
"handler": get_handler(function_yaml),
83+
"image": get_image(function_yaml),
84+
"kind": function_yaml.get("kind") or "",
85+
"requirements": get_requirements(function_yaml),
86+
},
87+
"url": "",
88+
"version": metadata.get("tag") or "0.0.1",
89+
"marketplaceType": "",
90+
}
91+
92+
return item
93+
94+
95+
def get_ipynb_file(path: Path) -> str:
96+
default = path / f"{path.name}.ipynb"
97+
98+
if default.exists():
99+
return f"{path.name}.ipynb"
100+
101+
ipynbs = list(filter(lambda d: d.suffix == ".ipynb", path.iterdir()))
102+
ipynbs = list(filter(lambda d: not d.name.startswith("test"), ipynbs))
103+
104+
if len(ipynbs) > 1:
105+
click.echo(f"{path.name}: Notebook not found")
106+
return ""
107+
elif len(ipynbs) == 1:
108+
return ipynbs[0].name
109+
110+
click.echo(f"{path.name}: Notebook not found")
111+
return ""
112+
113+
114+
def get_py_file(path: Path) -> str:
115+
default_py_file = path / "function.py"
116+
117+
if default_py_file.exists():
118+
return "function.py"
119+
120+
py_file = filter(lambda d: d.suffix == ".py", path.iterdir())
121+
py_file = list(filter(lambda d: not d.name.startswith("test"), py_file))
122+
123+
if len(py_file) > 1:
124+
click.echo(f"{path.name}: Python file not found")
125+
return ""
126+
elif len(py_file) == 1:
127+
return py_file[0].name
128+
129+
click.echo(f"{path.name}: Python file not found")
130+
return ""
131+
132+
133+
def get_handler(function_yaml: dict) -> str:
134+
spec = function_yaml["spec"]
135+
handler = spec.get("default_handler", "handler")
136+
return handler
137+
138+
139+
def get_image(function_yaml: dict):
140+
spec = function_yaml.get("spec", {})
141+
spec_image = spec.get("image")
142+
143+
build = spec.get("build", {})
144+
build_image = build.get("base_image")
145+
146+
base_spec = spec.get("base_spec", {}).get("spec", {})
147+
base_spec_image = base_spec.get("build", {}).get("baseImage")
148+
149+
return spec_image or build_image or base_spec_image or ""
150+
151+
152+
def get_requirements(function_yaml: dict):
153+
spec = function_yaml.get("spec", {})
154+
base_spec = spec.get("base_spec", {}).get("spec", {})
155+
156+
spec_commands = spec.get("build", {}).get("commands", [])
157+
base_spec_commands = base_spec.get("build", {}).get("commands", [])
158+
159+
commands = set()
160+
161+
for command in spec_commands:
162+
commands.add(command)
163+
164+
for command in base_spec_commands:
165+
commands.add(command)
166+
167+
requirements = []
168+
for command in commands:
169+
if "uninstall" in command:
170+
click.echo(f"{function_yaml['metadata']['name']}: Unsupported requirements")
171+
return []
172+
if "python -m pip install " in command:
173+
command = command.split("python -m pip install ")[-1]
174+
elif "pip install " in command:
175+
command = command.split("pip install ")[-1]
176+
else:
177+
click.echo(f"{function_yaml['metadata']['name']}: Unsupported requirements")
178+
return []
179+
180+
allowed_chars = {".", "_", "-", "="}
181+
182+
if " " in command:
183+
sub_commands = command.split(" ")
184+
for sub_command in sub_commands:
185+
for char in sub_command:
186+
if not char.isalnum() and char not in allowed_chars:
187+
click.echo(
188+
f"{function_yaml['metadata']['name']}: Unsupported requirements"
189+
)
190+
return []
191+
requirements.append(sub_command)
192+
else:
193+
requirements.append(command)
194+
return requirements
195+
196+
197+
if __name__ == "__main__":
198+
function_to_item("/home/michaell/projects/functions")

cli/helpers.py

+9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ def is_item_dir(path: Path) -> bool:
1212
return path.is_dir() and (path / "item.yaml").exists()
1313

1414

15+
def is_function_dir(path: Path) -> bool:
16+
if path.is_file():
17+
return False
18+
# dir_name = path.name
19+
# ipynb_found = any((f.name.endswith(".ipynb") for f in path.iterdir()))
20+
# py_found = any((f.name.endswith(".py") for f in path.iterdir()))
21+
return any((f.name == "function.yaml" for f in path.iterdir()))
22+
23+
1524
def render_jinja_file(
1625
template_path: Union[str, Path], output_path: Union[str, Path], data: dict
1726
):

cli/item_to_function.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from optparse import OptionParser
21
from pathlib import Path
32
from typing import Optional
43

@@ -42,7 +41,7 @@ def item_to_function(item_path: str, output_path: Optional[str] = None):
4241
description=item_yaml.get("description", ""),
4342
requirements=item_yaml.get("spec", {}).get("requirements"),
4443
categories=item_yaml.get("categories", []),
45-
labels=item_yaml.get("labels", {})
44+
labels=item_yaml.get("labels", {}),
4645
)
4746

4847
if output_path is None:

0 commit comments

Comments
 (0)