Skip to content

Commit b961c32

Browse files
eupharissudiptatjaarongoineneumann
authored
R18 release (#30)
* # Feature (2970): Update python client to support setup command (#22) * # Feature (2970): Update python client to support setup command - Function add command now support --execution-api-key - Extra Old Function call removed * improve polyapi-python setup (#24) * improve polyapi-python setup * # Feature (3019): improve polyapi-python setup (#25) * # Feature (3019): improve polyapi-python setup * # Feature (3019): improve polyapi-python setup - UUID Validation check added --------- Co-authored-by: Sudipta at TechJays <sudipta.kumar@techjays.com> * # Feature (3007): Update python -m polyapi function add --logs options (#23) * # Feature (3007): Update python -m polyapi function add --logs options - if --logs added, then value must enabled or disabled - If Nothing passed the value is default disabled - pyproject.toml version updated * Project Glide + Refactor main command line args parsing (#26) * Refactor main command line args parsing, adding prepare and sync commands to enable project glide workflows for python * improved tests * updating version * fix for poly cache directory path construction * one more adjustment to the deployables cache directory so there can't be any conflict with any custom namespace * this better? * verbose logging on upload code to see what's failing in CI/CD * bumpity * whoops * so close * better? * okay this should be the fix * is it this? * maybe * oh for the love of pete * whatever. might be a pypi issue * removing verbose logging * fixing bugs in sync command to use correct api urls * update logging * lint * improved auth * last fix for function sync * fix bug when comment arguments don't align with the function * try forcing the poly directory to exist * test logging * remove debug logging * fixing project glide deployable types and bumping the version * fixing missing arguments in python client function upload * fixing return type for trained functions * fix bug preventing use of poly sync command locally * next version of client! * EN #3183 allow null logs flag for python client (#28) * let the typing_extensions versions increase to support latest openai pypi package version * update dependency in one more place * Some bug fixes for python client (#29) * fixed bug with parsing python functions without any types, and bug where functions with multiple deployment receipts were getting mangled * whoops. uncommenting tests * last test fix * 0.3.2 --------- Co-authored-by: Sudipta at TechJays <sudipta.kumar@techjays.com> Co-authored-by: Aaron Goin <aarongoin@users.noreply.github.com> Co-authored-by: Eric Neumann <eneumann@users.noreply.github.com>
1 parent 882bbd3 commit b961c32

File tree

8 files changed

+67
-36
lines changed

8 files changed

+67
-36
lines changed

polyapi/cli.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def execute_from_cli():
2121
description="Manage your Poly API configurations and functions",
2222
formatter_class=argparse.RawTextHelpFormatter
2323
)
24-
24+
2525
subparsers = parser.add_subparsers(help="Available commands")
2626

2727
###########################################################################
@@ -66,13 +66,13 @@ def generate_command(args):
6666
fn_add_parser.add_argument("--description", required=False, default="", help="Description of the function")
6767
fn_add_parser.add_argument("--server", action="store_true", help="Marks the function as a server function")
6868
fn_add_parser.add_argument("--client", action="store_true", help="Marks the function as a client function")
69-
fn_add_parser.add_argument("--logs", choices=["enabled", "disabled"], default="disabled", help="Enable or disable logs for the function.")
69+
fn_add_parser.add_argument("--logs", choices=["enabled", "disabled"], default=None, help="Enable or disable logs for the function.")
7070
fn_add_parser.add_argument("--execution-api-key", required=False, default="", help="API key for execution (for server functions only).")
7171
fn_add_parser.add_argument("--disable-ai", "--skip-generate", action="store_true", help="Pass --disable-ai skip AI generation of missing descriptions")
7272

7373
def add_function(args):
7474
initialize_config()
75-
logs_enabled = args.logs == "enabled"
75+
logs_enabled = args.logs == "enabled" if args.logs else None
7676
err = ""
7777
if args.server and args.client:
7878
err = "Specify either `--server` or `--client`. Found both."

polyapi/deployables.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ def update_deployment_comments(file_content: str, deployable: dict) -> str:
245245
if deployable['deployments']:
246246
deployment_comments = write_deploy_comments(deployable['deployments'])
247247
deployable['deploymentCommentRanges'] = [(0, len(deployment_comments) + 1)]
248-
file_content = f"{deployment_comments}{file_content}"
248+
file_content = f"{deployment_comments}\n{file_content}"
249249
return file_content
250250

251251
def update_deployable_function_comments(file_content: str, deployable: dict, disable_docs: bool = False) -> str:
@@ -261,7 +261,7 @@ def update_deployable_function_comments(file_content: str, deployable: dict, dis
261261
if deployable["docStartIndex"] == deployable["docEndIndex"]:
262262
# Function doesn't yet have any docstrings so we need to add additional whitespace
263263
docstring = " " + docstring + "\n"
264-
264+
265265
return f"{file_content[:deployable['docStartIndex']]}{docstring}{file_content[deployable['docEndIndex']:]}"
266266
return file_content
267267

@@ -271,17 +271,17 @@ def write_updated_deployable(deployable: dict, disable_docs: bool = False) -> di
271271
"""
272272
with open(deployable['file'], 'r', encoding='utf-8') as file:
273273
file_contents = file.read()
274-
274+
275275
if deployable['type'] in ['client-function', 'server-function']:
276276
file_contents = update_deployable_function_comments(file_contents, deployable, disable_docs)
277277
else:
278278
raise ValueError(f"Unsupported deployable type: '{deployable['type']}'")
279279

280280
file_contents = update_deployment_comments(file_contents, deployable)
281-
281+
282282
with open(deployable['file'], 'w', encoding='utf-8') as file:
283283
file.write(file_contents)
284-
284+
285285
deployable['fileRevision'] = get_deployable_file_revision(file_contents)
286286
return deployable
287287

polyapi/function_cli.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import sys
2-
from typing import Any, List
2+
from typing import Any, List, Optional
33
import requests
44
from polyapi.generate import get_functions_and_parse, generate_functions
55
from polyapi.config import get_api_key_and_url
@@ -23,7 +23,7 @@ def function_add_or_update(
2323
description: str,
2424
client: bool,
2525
server: bool,
26-
logs_enabled: bool,
26+
logs_enabled: Optional[bool],
2727
generate: bool = True,
2828
execution_api_key: str = ""
2929
):
@@ -45,16 +45,18 @@ def function_add_or_update(
4545
)
4646
sys.exit(1)
4747

48+
if logs_enabled is None:
49+
logs_enabled = parsed["config"].get("logs_enabled", None)
50+
4851
data = {
4952
"context": context or parsed["context"],
5053
"name": name,
5154
"description": description or parsed["types"]["description"],
5255
"code": code,
5356
"language": "python",
5457
"returnType": get_jsonschema_type(return_type),
55-
"returnTypeSchema": parsed["types"]["returns"]["typeSchema"],
56-
"arguments": [{**p, "key": p["name"], "type": get_jsonschema_type(p["type"]) } for p in parsed["types"]["params"]],
57-
"logsEnabled": logs_enabled or parsed["config"].get("logs_enabled", False),
58+
"arguments": [{**p, "key": p["name"], "type": get_jsonschema_type(p["type"])} for p in parsed["types"]["params"]],
59+
"logsEnabled": logs_enabled,
5860
}
5961

6062
if server and parsed["dependencies"]:

polyapi/parser.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def _parse_sphinx_docstring(docstring: str) -> Dict[str, Any]:
4747
"type": "Any"
4848
}
4949
current_section = None
50-
50+
5151
for line in lines:
5252
stripped_line = line.strip()
5353
if stripped_line.startswith(":param "):
@@ -56,7 +56,7 @@ def _parse_sphinx_docstring(docstring: str) -> Dict[str, Any]:
5656
param_name = param_name.strip()
5757
if param_name in params:
5858
params[param_name]["description"] = param_desc.strip()
59-
else:
59+
else:
6060
params[param_name] = { "name": param_name, "type": "", "description": param_desc.strip() }
6161
current_section = param_name
6262

@@ -118,7 +118,7 @@ def _parse_google_docstring(docstring: str) -> Dict[str, Any]:
118118
for line in lines:
119119
line = line.rstrip()
120120
section_match = section_pattern.match(line)
121-
121+
122122
if section_match:
123123
mode = section_match.group(1).lower()
124124
continue
@@ -181,7 +181,7 @@ def _get_schemas(code: str) -> List[Dict]:
181181

182182
def get_jsonschema_type(python_type: str):
183183
if python_type == "Any":
184-
return "Any"
184+
return "any"
185185

186186
if python_type == "List":
187187
return "array"
@@ -338,6 +338,7 @@ def parse_function_code(code: str, name: Optional[str] = "", context: Optional[s
338338
"params": [],
339339
"returns": {
340340
"type": "",
341+
"typeSchema": None,
341342
"description": "",
342343
}
343344
},
@@ -364,7 +365,7 @@ def __init__(self):
364365
self._line_offsets.append(
365366
self._line_offsets[i-1] + len(self._lines[i-1])
366367
)
367-
368+
368369
self._extract_deploy_comments()
369370

370371
def visit_AnnAssign(self, node):
@@ -435,13 +436,14 @@ def _extract_docstring_from_function(self, node: ast.FunctionDef):
435436

436437
def _extract_deploy_comments(self):
437438
for i in range(len(self._lines)):
438-
line = self._lines[i].strip()
439+
line = self._lines[i]
439440
if line and not line.startswith("#"):
440441
return
441-
deployment = _parse_deploy_comment(line)
442+
deployment = _parse_deploy_comment(line.strip())
442443
if deployment:
444+
start = self._line_offsets[i]
443445
deployable["deployments"].append(deployment)
444-
deployable["deploymentCommentRanges"].append([self._line_offsets[i], len(line)])
446+
deployable["deploymentCommentRanges"].append([start, start + len(line)])
445447

446448
def visit_Import(self, node: ast.Import):
447449
# TODO maybe handle `import foo.bar` case?
@@ -471,8 +473,7 @@ def visit_FunctionDef(self, node: ast.FunctionDef):
471473
"type": python_type,
472474
"description": "",
473475
}
474-
if type_schema:
475-
json_arg["typeSchema"] = json.dumps(type_schema)
476+
json_arg["typeSchema"] = json.dumps(type_schema) if type_schema else None
476477

477478
if docstring_params:
478479
try:
@@ -482,7 +483,7 @@ def visit_FunctionDef(self, node: ast.FunctionDef):
482483
if docstring_params[type_index]["type"] != python_type:
483484
deployable["dirty"] = True
484485
except:
485-
pass
486+
pass
486487
else:
487488
deployable["dirty"] = True
488489

@@ -496,7 +497,7 @@ def visit_FunctionDef(self, node: ast.FunctionDef):
496497
deployable["types"]["returns"]["typeSchema"] = return_type_schema
497498
else:
498499
deployable["types"]["returns"]["type"] = "Any"
499-
500+
500501
def generic_visit(self, node):
501502
if hasattr(node, 'lineno') and hasattr(node, 'col_offset'):
502503
self._current_offset = self._line_offsets[node.lineno - 1] + node.col_offset

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ requires = ["setuptools>=61.2", "wheel"]
33

44
[project]
55
name = "polyapi-python"
6-
version = "0.3.1"
6+
version = "0.3.2"
77
description = "The Python Client for PolyAPI, the IPaaS by Developers for Developers"
88
authors = [{ name = "Dan Fellin", email = "dan@polyapi.io" }]
99
dependencies = [
1010
"requests==2.31.0",
11-
"typing_extensions==4.10.0",
11+
"typing_extensions>=4.10.0",
1212
"jsonschema-gentypes==2.6.0",
1313
"pydantic==2.6.4",
1414
"stdlib_list==0.10.0",

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
requests==2.31.0
2-
typing_extensions==4.10.0
2+
typing_extensions>=4.10.0
33
jsonschema-gentypes==2.6.0
44
pydantic==2.6.4
55
stdlib_list==0.10.0

tests/test_deployables.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,26 @@ def foobar(foo: str, bar: Dict[str, str]) -> int:
6565
"""A function that does something really import.
6666
6767
Args:
68-
foo (str):
69-
bar (Dict[str, str]):
68+
foo (str):
69+
bar (Dict[str, str]):
7070
7171
Returns:
72-
int:
72+
int:
7373
"""
7474
print("Okay then!")
7575
return 7
7676
'''
7777

7878
class T(unittest.TestCase):
79+
def test_parse_and_write_deployment_comment(self):
80+
test_deployable = parse_function_code(EXPECTED_SERVER_FN_DEPLOYMENTS, "foobar")
81+
deployable_comment_ranges = test_deployable["deploymentCommentRanges"]
82+
updated_file_contents = update_deployment_comments(EXPECTED_SERVER_FN_DEPLOYMENTS, test_deployable)
83+
self.assertEqual(updated_file_contents, EXPECTED_SERVER_FN_DEPLOYMENTS)
84+
# Deployment comment ranges collapsed into one of equal size
85+
self.assertEqual(test_deployable["deploymentCommentRanges"][0][0], deployable_comment_ranges[0][0])
86+
self.assertEqual(test_deployable["deploymentCommentRanges"][0][1], deployable_comment_ranges[1][1])
87+
7988
def test_write_deployment_comment(self):
8089
test_deployable = {
8190
"deployments": [
@@ -98,7 +107,7 @@ def test_write_deployment_comment(self):
98107
'type': 'server-function'
99108
}
100109
],
101-
"deploymentCommentRanges": [[0, 178]]
110+
"deploymentCommentRanges": [[0, 177]]
102111
}
103112
updated_file_contents = update_deployment_comments(INITIAL_SERVER_FN_DEPLOYMENTS, test_deployable)
104113
self.assertEqual(updated_file_contents, EXPECTED_SERVER_FN_DEPLOYMENTS)

tests/test_parser.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
from polyapi.parser import parse_function_code
44

55

6+
CODE_NO_TYPES = """
7+
def foobar(a, b):
8+
return a + b
9+
"""
10+
611
SIMPLE_CODE = """
712
def foobar(n: int) -> int:
813
return 9
@@ -124,11 +129,21 @@ def foobar(foo: str, bar: Dict[str, str]) -> int:
124129
'''
125130

126131
class T(unittest.TestCase):
132+
def test_no_types(self):
133+
deployable = parse_function_code(CODE_NO_TYPES, "foobar")
134+
types = deployable["types"]
135+
self.assertEqual(len(types["params"]), 2)
136+
self.assertEqual(types["params"][0], {"name": "a", "type": "Any", "typeSchema": None, "description": ""})
137+
self.assertEqual(types["params"][1], {"name": "b", "type": "Any", "typeSchema": None, "description": ""})
138+
self.assertEqual(types["returns"]["type"], "Any")
139+
self.assertIsNone(types["returns"]["typeSchema"])
140+
self.assertEqual(deployable["dependencies"], [])
141+
127142
def test_simple_types(self):
128143
deployable = parse_function_code(SIMPLE_CODE, "foobar")
129144
types = deployable["types"]
130145
self.assertEqual(len(types["params"]), 1)
131-
self.assertEqual(types["params"][0], {"name": "n", "type": "int", "description": ""})
146+
self.assertEqual(types["params"][0], {"name": "n", "type": "int", "typeSchema": None, "description": ""})
132147
self.assertEqual(types["returns"]["type"], "int")
133148
self.assertIsNone(types["returns"]["typeSchema"])
134149
self.assertEqual(deployable["dependencies"], [])
@@ -137,7 +152,7 @@ def test_complex_return_type(self):
137152
deployable = parse_function_code(COMPLEX_RETURN_TYPE, "foobar")
138153
types = deployable["types"]
139154
self.assertEqual(len(types["params"]), 1)
140-
self.assertEqual(types["params"][0], {"name": "n", "type": "int", "description": ""})
155+
self.assertEqual(types["params"][0], {"name": "n", "type": "int", "typeSchema": None, "description": ""})
141156
self.assertEqual(types["returns"]["type"], "Barbar")
142157
self.assertEqual(types["returns"]["typeSchema"]['title'], "Barbar")
143158

@@ -153,7 +168,7 @@ def test_list_complex_return_type(self):
153168
deployable = parse_function_code(LIST_COMPLEX_RETURN_TYPE, "foobar")
154169
types = deployable["types"]
155170
self.assertEqual(len(types["params"]), 1)
156-
self.assertEqual(types["params"][0], {"name": "n", "type": "int", "description": ""})
171+
self.assertEqual(types["params"][0], {"name": "n", "type": "int", "typeSchema": None, "description": ""})
157172
self.assertEqual(types["returns"]["type"], "List[Barbar]")
158173
self.assertEqual(types["returns"]["typeSchema"]["items"]['title'], "Barbar")
159174

@@ -171,7 +186,7 @@ def test_parse_import_base(self):
171186
code = "import requests\n\n\ndef foobar(n: int) -> int:\n return 9\n"
172187
deployable = parse_function_code(code, "foobar")
173188
self.assertEqual(deployable["dependencies"], [])
174-
189+
175190
def test_parse_glide_server_function_no_docstring(self):
176191
code = GLIDE_SIMPLE_SERVER_FN
177192
deployable = parse_function_code(code, "foobar")
@@ -186,11 +201,13 @@ def test_parse_glide_server_function_bad_docstring(self):
186201
self.assertEqual(deployable["types"]["params"][0], {
187202
"name": "foo",
188203
"type": "Any",
204+
"typeSchema": None,
189205
"description": "The foo in question"
190206
})
191207
self.assertEqual(deployable["types"]["params"][1], {
192208
"name": "bar",
193209
"type": "Any",
210+
"typeSchema": None,
194211
"description": "Configuration of bars"
195212
})
196213
self.assertEqual(deployable["types"]["returns"], {
@@ -205,11 +222,13 @@ def test_parse_glide_server_function_ok_docstring(self):
205222
self.assertEqual(deployable["types"]["params"][0], {
206223
"name": "foo",
207224
"type": "str",
225+
"typeSchema": None,
208226
"description": "The foo in question"
209227
})
210228
self.assertEqual(deployable["types"]["params"][1], {
211229
"name": "bar",
212230
"type": "Dict[str, str]",
231+
"typeSchema": None,
213232
"description": "Configuration of bars"
214233
})
215234
self.assertEqual(deployable["types"]["returns"], {

0 commit comments

Comments
 (0)