polyapi-python 0.3.2.dev2__py3-none-any.whl → 0.3.3.dev9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
polyapi/api.py CHANGED
@@ -8,6 +8,7 @@ API_DEFS_TEMPLATE = """
8
8
  from typing import List, Dict, Any, TypedDict
9
9
  {args_def}
10
10
  {return_type_def}
11
+
11
12
  class {api_response_type}(TypedDict):
12
13
  status: int
13
14
  headers: Dict
@@ -41,6 +42,7 @@ def render_api_function(
41
42
  arg_names = [a["name"] for a in arguments]
42
43
  args, args_def = parse_arguments(function_name, arguments)
43
44
  return_type_name, return_type_def = get_type_and_def(return_type) # type: ignore
45
+
44
46
  data = "{" + ", ".join([f"'{arg}': {rewrite_arg_name(arg)}" for arg in arg_names]) + "}"
45
47
 
46
48
  api_response_type = f"{function_name}Response"
polyapi/cli.py CHANGED
@@ -13,6 +13,7 @@ from .sync import sync_deployables
13
13
 
14
14
  CLI_COMMANDS = ["setup", "generate", "function", "clear", "help", "update_rendered_spec"]
15
15
 
16
+
16
17
  def execute_from_cli():
17
18
  # First we setup all our argument parsing logic
18
19
  # Then we parse the arguments (waaay at the bottom)
@@ -21,7 +22,7 @@ def execute_from_cli():
21
22
  description="Manage your Poly API configurations and functions",
22
23
  formatter_class=argparse.RawTextHelpFormatter
23
24
  )
24
-
25
+
25
26
  subparsers = parser.add_subparsers(help="Available commands")
26
27
 
27
28
  ###########################################################################
@@ -46,9 +47,7 @@ def execute_from_cli():
46
47
 
47
48
  def generate_command(args):
48
49
  initialize_config()
49
- print("Generating Poly functions...", end="")
50
50
  generate()
51
- print_green("DONE")
52
51
 
53
52
  generate_parser.set_defaults(command=generate_command)
54
53
 
polyapi/config.py CHANGED
@@ -55,6 +55,10 @@ def set_api_key_and_url(key: str, url: str):
55
55
  config.set("polyapi", "poly_api_base_url", url)
56
56
  with open(get_config_file_path(), "w") as f:
57
57
  config.write(f)
58
+ global API_KEY
59
+ global API_URL
60
+ API_KEY = key
61
+ API_URL = url
58
62
 
59
63
 
60
64
  def initialize_config(force=False):
@@ -81,7 +85,7 @@ def initialize_config(force=False):
81
85
  sys.exit(1)
82
86
 
83
87
  set_api_key_and_url(key, url)
84
- print_green(f"Poly setup complete.")
88
+ print_green("Poly setup complete.")
85
89
 
86
90
  if not key or not url:
87
91
  print_yellow("Poly API Key and Poly API Base URL are required.")
polyapi/deployables.py CHANGED
@@ -261,7 +261,7 @@ def update_deployable_function_comments(file_content: str, deployable: dict, dis
261
261
  if deployable["docStartIndex"] == deployable["docEndIndex"]:
262
262
  # Function doesn't yet have any docstrings so we need to add additional whitespace
263
263
  docstring = " " + docstring + "\n"
264
-
264
+
265
265
  return f"{file_content[:deployable['docStartIndex']]}{docstring}{file_content[deployable['docEndIndex']:]}"
266
266
  return file_content
267
267
 
@@ -271,17 +271,17 @@ def write_updated_deployable(deployable: dict, disable_docs: bool = False) -> di
271
271
  """
272
272
  with open(deployable['file'], 'r', encoding='utf-8') as file:
273
273
  file_contents = file.read()
274
-
274
+
275
275
  if deployable['type'] in ['client-function', 'server-function']:
276
276
  file_contents = update_deployable_function_comments(file_contents, deployable, disable_docs)
277
277
  else:
278
278
  raise ValueError(f"Unsupported deployable type: '{deployable['type']}'")
279
279
 
280
280
  file_contents = update_deployment_comments(file_contents, deployable)
281
-
281
+
282
282
  with open(deployable['file'], 'w', encoding='utf-8') as file:
283
283
  file.write(file_contents)
284
-
284
+
285
285
  deployable['fileRevision'] = get_deployable_file_revision(file_contents)
286
286
  return deployable
287
287
 
polyapi/execute.py CHANGED
@@ -1,3 +1,4 @@
1
+ from typing import Dict
1
2
  import requests
2
3
  from requests import Response
3
4
  from polyapi.config import get_api_key_and_url
@@ -7,10 +8,14 @@ from polyapi.exceptions import PolyApiException
7
8
  def execute(function_type, function_id, data) -> Response:
8
9
  """ execute a specific function id/type
9
10
  """
11
+ data_without_None = data
12
+ if isinstance(data, Dict):
13
+ data_without_None = {k: v for k, v in data.items() if v is not None}
14
+
10
15
  api_key, api_url = get_api_key_and_url()
11
16
  headers = {"Authorization": f"Bearer {api_key}"}
12
17
  url = f"{api_url}/functions/{function_type}/{function_id}/execute"
13
- resp = requests.post(url, json=data, headers=headers)
18
+ resp = requests.post(url, json=data_without_None, headers=headers)
14
19
  # print(resp.status_code)
15
20
  # print(resp.headers["content-type"])
16
21
  if resp.status_code < 200 or resp.status_code >= 300:
polyapi/function_cli.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import sys
2
2
  from typing import Any, List, Optional
3
3
  import requests
4
- from polyapi.generate import get_functions_and_parse, generate_functions
4
+ from polyapi.generate import generate as generate_library
5
5
  from polyapi.config import get_api_key_and_url
6
6
  from polyapi.utils import get_auth_headers, print_green, print_red, print_yellow
7
7
  from polyapi.parser import parse_function_code, get_jsonschema_type
@@ -55,7 +55,7 @@ def function_add_or_update(
55
55
  "code": code,
56
56
  "language": "python",
57
57
  "returnType": get_jsonschema_type(return_type),
58
- "arguments": [{**p, "key": p["name"], "type": get_jsonschema_type(p["type"]) } for p in parsed["types"]["params"]],
58
+ "arguments": [{**p, "key": p["name"], "type": get_jsonschema_type(p["type"])} for p in parsed["types"]["params"]],
59
59
  "logsEnabled": logs_enabled,
60
60
  }
61
61
 
@@ -87,10 +87,7 @@ def function_add_or_update(
87
87
  function_id = resp.json()["id"]
88
88
  print(f"Function ID: {function_id}")
89
89
  if generate:
90
- print("Generating new custom function...", end="")
91
- functions = get_functions_and_parse(limit_ids=[function_id])
92
- generate_functions(functions)
93
- print_green("DONE")
90
+ generate_library()
94
91
  else:
95
92
  print("Error adding function.")
96
93
  print(resp.status_code)
polyapi/generate.py CHANGED
@@ -2,28 +2,38 @@ import json
2
2
  import requests
3
3
  import os
4
4
  import shutil
5
- from typing import List
5
+ from typing import List, Tuple, cast
6
6
 
7
- from polyapi.auth import render_auth_function
8
- from polyapi.client import render_client_function
9
- from polyapi.webhook import render_webhook_handle
7
+ from .auth import render_auth_function
8
+ from .client import render_client_function
9
+ from .poly_schemas import generate_schemas
10
+ from .webhook import render_webhook_handle
10
11
 
11
- from .typedefs import PropertySpecification, SpecificationDto, VariableSpecDto
12
+ from .typedefs import PropertySpecification, SchemaSpecDto, SpecificationDto, VariableSpecDto
12
13
  from .api import render_api_function
13
14
  from .server import render_server_function
14
- from .utils import add_import_to_init, get_auth_headers, init_the_init, to_func_namespace
15
+ from .utils import add_import_to_init, get_auth_headers, init_the_init, print_green, to_func_namespace
15
16
  from .variables import generate_variables
16
17
  from .config import get_api_key_and_url
17
18
 
18
19
  SUPPORTED_FUNCTION_TYPES = {
19
20
  "apiFunction",
20
21
  "authFunction",
21
- "customFunction",
22
+ "customFunction", # client function - this is badly named in /specs atm
22
23
  "serverFunction",
23
24
  "webhookHandle",
24
25
  }
25
26
 
26
- SUPPORTED_TYPES = SUPPORTED_FUNCTION_TYPES | {"serverVariable"}
27
+ SUPPORTED_TYPES = SUPPORTED_FUNCTION_TYPES | {"serverVariable", "schema", "snippet"}
28
+
29
+
30
+ X_POLY_REF_WARNING = '''"""
31
+ x-poly-ref:
32
+ path:'''
33
+
34
+ X_POLY_REF_BETTER_WARNING = '''"""
35
+ Unresolved schema, please add the following schema to complete it:
36
+ path:'''
27
37
 
28
38
 
29
39
  def get_specs() -> List:
@@ -38,9 +48,56 @@ def get_specs() -> List:
38
48
  raise NotImplementedError(resp.content)
39
49
 
40
50
 
51
+ def build_schema_index(items):
52
+ index = {}
53
+ for item in items:
54
+ if item.get("type") == "schema" and "contextName" in item:
55
+ index[item["contextName"]] = {**item.get("definition", {}), "name": item.get("name")}
56
+ return index
57
+
58
+
59
+ def resolve_poly_refs(obj, schema_index):
60
+ if isinstance(obj, dict):
61
+ if "x-poly-ref" in obj:
62
+ ref = obj["x-poly-ref"]
63
+ if isinstance(ref, dict) and "path" in ref:
64
+ path = ref["path"]
65
+ if path in schema_index:
66
+ return resolve_poly_refs(schema_index[path], schema_index)
67
+ else:
68
+ return obj
69
+ return {k: resolve_poly_refs(v, schema_index) for k, v in obj.items()}
70
+ elif isinstance(obj, list):
71
+ return [resolve_poly_refs(item, schema_index) for item in obj]
72
+ else:
73
+ return obj
74
+
75
+
76
+ def replace_poly_refs_in_functions(specs: List[SpecificationDto], schema_index):
77
+ spec_idxs_to_remove = []
78
+ for idx, spec in enumerate(specs):
79
+ if spec.get("type") in ("apiFunction", "customFunction", "serverFunction"):
80
+ func = spec.get("function")
81
+ if func:
82
+ try:
83
+ spec["function"] = resolve_poly_refs(func, schema_index)
84
+ except Exception:
85
+ # print()
86
+ # print(f"{spec['context']}.{spec['name']} (id: {spec['id']}) failed to resolve poly refs, skipping!")
87
+ spec_idxs_to_remove.append(idx)
88
+
89
+ # reverse the list so we pop off later indexes first
90
+ spec_idxs_to_remove.reverse()
91
+
92
+ for idx in spec_idxs_to_remove:
93
+ specs.pop(idx)
94
+
95
+ return specs
96
+
97
+
41
98
  def parse_function_specs(
42
99
  specs: List[SpecificationDto],
43
- limit_ids: List[str] | None, # optional list of ids to limit to
100
+ limit_ids: List[str] | None = None, # optional list of ids to limit to
44
101
  ) -> List[SpecificationDto]:
45
102
  functions = []
46
103
  for spec in specs:
@@ -91,23 +148,14 @@ def read_cached_specs() -> List[SpecificationDto]:
91
148
  return json.loads(f.read())
92
149
 
93
150
 
94
- def get_functions_and_parse(limit_ids: List[str] | None = None) -> List[SpecificationDto]:
95
- specs = get_specs()
96
- cache_specs(specs)
97
- return parse_function_specs(specs, limit_ids=limit_ids)
151
+ def get_variables() -> List[VariableSpecDto]:
152
+ specs = read_cached_specs()
153
+ return [cast(VariableSpecDto, spec) for spec in specs if spec["type"] == "serverVariable"]
98
154
 
99
155
 
100
- def get_variables() -> List[VariableSpecDto]:
101
- api_key, api_url = get_api_key_and_url()
102
- headers = {"Authorization": f"Bearer {api_key}"}
103
- # TODO do some caching so this and get_functions just do 1 function call
104
- url = f"{api_url}/specs"
105
- resp = requests.get(url, headers=headers)
106
- if resp.status_code == 200:
107
- specs = resp.json()
108
- return [spec for spec in specs if spec["type"] == "serverVariable"]
109
- else:
110
- raise NotImplementedError(resp.content)
156
+ def get_schemas() -> List[SchemaSpecDto]:
157
+ specs = read_cached_specs()
158
+ return [cast(SchemaSpecDto, spec) for spec in specs if spec["type"] == "schema"]
111
159
 
112
160
 
113
161
  def remove_old_library():
@@ -120,12 +168,28 @@ def remove_old_library():
120
168
  if os.path.exists(path):
121
169
  shutil.rmtree(path)
122
170
 
171
+ path = os.path.join(currdir, "schemas")
172
+ if os.path.exists(path):
173
+ shutil.rmtree(path)
123
174
 
124
- def generate() -> None:
125
175
 
176
+ def generate() -> None:
177
+ print("Generating Poly Python SDK...", end="", flush=True)
126
178
  remove_old_library()
127
179
 
128
- functions = get_functions_and_parse()
180
+ specs = get_specs()
181
+ cache_specs(specs)
182
+
183
+ limit_ids: List[str] = [] # useful for narrowing down generation to a single function to debug
184
+ functions = parse_function_specs(specs, limit_ids=limit_ids)
185
+
186
+ schemas = get_schemas()
187
+ if schemas:
188
+ generate_schemas(schemas)
189
+
190
+ schema_index = build_schema_index(schemas)
191
+ functions = replace_poly_refs_in_functions(functions, schema_index)
192
+
129
193
  if functions:
130
194
  generate_functions(functions)
131
195
  else:
@@ -138,10 +202,13 @@ def generate() -> None:
138
202
  if variables:
139
203
  generate_variables(variables)
140
204
 
205
+
141
206
  # indicator to vscode extension that this is a polyapi-python project
142
207
  file_path = os.path.join(os.getcwd(), ".polyapi-python")
143
208
  open(file_path, "w").close()
144
209
 
210
+ print_green("DONE")
211
+
145
212
 
146
213
  def clear() -> None:
147
214
  base = os.path.dirname(os.path.abspath(__file__))
@@ -155,7 +222,7 @@ def clear() -> None:
155
222
  print("Cleared!")
156
223
 
157
224
 
158
- def render_spec(spec: SpecificationDto):
225
+ def render_spec(spec: SpecificationDto) -> Tuple[str, str]:
159
226
  function_type = spec["type"]
160
227
  function_description = spec["description"]
161
228
  function_name = spec["name"]
@@ -214,6 +281,12 @@ def render_spec(spec: SpecificationDto):
214
281
  arguments,
215
282
  return_type,
216
283
  )
284
+
285
+ if X_POLY_REF_WARNING in func_type_defs:
286
+ # this indicates that jsonschema_gentypes has detected an x-poly-ref
287
+ # let's add a more user friendly error explaining what is going on
288
+ func_type_defs = func_type_defs.replace(X_POLY_REF_WARNING, X_POLY_REF_BETTER_WARNING)
289
+
217
290
  return func_str, func_type_defs
218
291
 
219
292
 
polyapi/parser.py CHANGED
@@ -5,7 +5,7 @@ import sys
5
5
  import re
6
6
  from typing import Dict, List, Mapping, Optional, Tuple, Any
7
7
  from typing import _TypedDictMeta as BaseTypedDict # type: ignore
8
- from typing_extensions import _TypedDictMeta # type: ignore
8
+ from typing_extensions import _TypedDictMeta, cast # type: ignore
9
9
  from stdlib_list import stdlib_list
10
10
  from pydantic import TypeAdapter
11
11
  from importlib.metadata import packages_distributions
@@ -47,7 +47,7 @@ def _parse_sphinx_docstring(docstring: str) -> Dict[str, Any]:
47
47
  "type": "Any"
48
48
  }
49
49
  current_section = None
50
-
50
+
51
51
  for line in lines:
52
52
  stripped_line = line.strip()
53
53
  if stripped_line.startswith(":param "):
@@ -56,7 +56,7 @@ def _parse_sphinx_docstring(docstring: str) -> Dict[str, Any]:
56
56
  param_name = param_name.strip()
57
57
  if param_name in params:
58
58
  params[param_name]["description"] = param_desc.strip()
59
- else:
59
+ else:
60
60
  params[param_name] = { "name": param_name, "type": "", "description": param_desc.strip() }
61
61
  current_section = param_name
62
62
 
@@ -118,7 +118,7 @@ def _parse_google_docstring(docstring: str) -> Dict[str, Any]:
118
118
  for line in lines:
119
119
  line = line.rstrip()
120
120
  section_match = section_pattern.match(line)
121
-
121
+
122
122
  if section_match:
123
123
  mode = section_match.group(1).lower()
124
124
  continue
@@ -158,6 +158,7 @@ def _parse_google_docstring(docstring: str) -> Dict[str, Any]:
158
158
 
159
159
  return parsed
160
160
 
161
+
161
162
  def _get_schemas(code: str) -> List[Dict]:
162
163
  schemas = []
163
164
  user_code = types.SimpleNamespace()
@@ -245,7 +246,7 @@ def _get_type_schema(json_type: str, python_type: str, schemas: List[Dict]):
245
246
  return schema
246
247
 
247
248
 
248
- def _get_type(expr: ast.expr | None, schemas: List[Dict]) -> Tuple[str, Dict | None]:
249
+ def _get_type(expr: ast.expr | None, schemas: List[Dict]) -> Tuple[Any, Any, Any]:
249
250
  if not expr:
250
251
  return "any", "Any", None
251
252
  python_type = get_python_type_from_ast(expr)
@@ -317,7 +318,7 @@ def _parse_value(value):
317
318
  return None
318
319
 
319
320
 
320
- def parse_function_code(code: str, name: Optional[str] = "", context: Optional[str] = ""):
321
+ def parse_function_code(code: str, name: Optional[str] = "", context: Optional[str] = ""): # noqa: C901
321
322
  schemas = _get_schemas(code)
322
323
 
323
324
  # the pip name and the import name might be different
@@ -325,9 +326,9 @@ def parse_function_code(code: str, name: Optional[str] = "", context: Optional[s
325
326
  # see https://stackoverflow.com/a/75144378
326
327
  pip_name_lookup = packages_distributions()
327
328
 
328
- deployable: DeployableRecord = {
329
- "context": context,
330
- "name": name,
329
+ deployable: DeployableRecord = { # type: ignore
330
+ "context": context, # type: ignore
331
+ "name": name, # type: ignore
331
332
  "description": "",
332
333
  "config": {},
333
334
  "gitRevision": "",
@@ -365,7 +366,7 @@ def parse_function_code(code: str, name: Optional[str] = "", context: Optional[s
365
366
  self._line_offsets.append(
366
367
  self._line_offsets[i-1] + len(self._lines[i-1])
367
368
  )
368
-
369
+
369
370
  self._extract_deploy_comments()
370
371
 
371
372
  def visit_AnnAssign(self, node):
@@ -381,7 +382,7 @@ def parse_function_code(code: str, name: Optional[str] = "", context: Optional[s
381
382
  if node.annotation.id == "PolyServerFunction":
382
383
  deployable["type"] = "server-function"
383
384
  elif node.annotation.id == "PolyClientFunction":
384
- deployable["type"] = "server-function"
385
+ deployable["type"] = "client-function"
385
386
  else:
386
387
  print_red("ERROR")
387
388
  print(f"Unsupported polyConfig type '${node.annotation.id}'")
@@ -404,6 +405,7 @@ def parse_function_code(code: str, name: Optional[str] = "", context: Optional[s
404
405
  if type(docstring) is None or (not docstring and '"""' not in self._lines[start_lineno] and "'''" not in self._lines[start_lineno]):
405
406
  return None
406
407
 
408
+ docstring = cast(str, docstring)
407
409
 
408
410
  # Support both types of triple quotation marks
409
411
  pattern = '"""'
@@ -483,7 +485,7 @@ def parse_function_code(code: str, name: Optional[str] = "", context: Optional[s
483
485
  if docstring_params[type_index]["type"] != python_type:
484
486
  deployable["dirty"] = True
485
487
  except:
486
- pass
488
+ pass
487
489
  else:
488
490
  deployable["dirty"] = True
489
491
 
@@ -497,7 +499,7 @@ def parse_function_code(code: str, name: Optional[str] = "", context: Optional[s
497
499
  deployable["types"]["returns"]["typeSchema"] = return_type_schema
498
500
  else:
499
501
  deployable["types"]["returns"]["type"] = "Any"
500
-
502
+
501
503
  def generic_visit(self, node):
502
504
  if hasattr(node, 'lineno') and hasattr(node, 'col_offset'):
503
505
  self._current_offset = self._line_offsets[node.lineno - 1] + node.col_offset
@@ -0,0 +1,95 @@
1
+ import os
2
+ from typing import Any, Dict, List, Tuple
3
+
4
+ from polyapi.schema import wrapped_generate_schema_types
5
+ from polyapi.utils import add_import_to_init, init_the_init, to_func_namespace
6
+
7
+ from .typedefs import SchemaSpecDto
8
+
9
+ SCHEMA_CODE_IMPORTS = """from typing_extensions import TypedDict, NotRequired
10
+
11
+ __all__ = []
12
+
13
+
14
+ """
15
+
16
+
17
+ FALLBACK_SPEC_TEMPLATE = """class {name}(TypedDict, total=False):
18
+ ''' unable to generate schema for {name}, defaulting to permissive type '''
19
+ pass
20
+ """
21
+
22
+
23
+ def generate_schemas(specs: List[SchemaSpecDto]):
24
+ for spec in specs:
25
+ create_schema(spec)
26
+
27
+
28
+ def add_schema_file(
29
+ full_path: str,
30
+ schema_name: str,
31
+ spec: SchemaSpecDto,
32
+ ):
33
+ # first lets add the import to the __init__
34
+ init_the_init(full_path, SCHEMA_CODE_IMPORTS)
35
+
36
+ if not spec["definition"].get("title"):
37
+ # very empty schemas like mews.Unit are possible
38
+ # add a title here to be sure they render
39
+ spec["definition"]["title"] = schema_name
40
+
41
+ schema_defs = render_poly_schema(spec)
42
+
43
+ if schema_defs:
44
+ # add function to init
45
+ init_path = os.path.join(full_path, "__init__.py")
46
+ with open(init_path, "a") as f:
47
+ f.write(f"\n\nfrom ._{to_func_namespace(schema_name)} import {schema_name}\n__all__.append('{schema_name}')\n")
48
+
49
+ # add type_defs to underscore file
50
+ file_path = os.path.join(full_path, f"_{to_func_namespace(schema_name)}.py")
51
+ with open(file_path, "w") as f:
52
+ f.write(schema_defs)
53
+
54
+
55
+ def create_schema(
56
+ spec: SchemaSpecDto
57
+ ) -> None:
58
+ full_path = os.path.dirname(os.path.abspath(__file__))
59
+ folders = f"schemas.{spec['context']}.{spec['name']}".split(".")
60
+ for idx, folder in enumerate(folders):
61
+ if idx + 1 == len(folders):
62
+ # special handling for final level
63
+ add_schema_file(
64
+ full_path,
65
+ folder,
66
+ spec,
67
+ )
68
+ else:
69
+ full_path = os.path.join(full_path, folder)
70
+ if not os.path.exists(full_path):
71
+ os.makedirs(full_path)
72
+
73
+ # append to __init__.py file if nested folders
74
+ next = folders[idx + 1] if idx + 2 < len(folders) else ""
75
+ if next:
76
+ init_the_init(full_path, SCHEMA_CODE_IMPORTS)
77
+ add_import_to_init(full_path, next)
78
+
79
+
80
+ def add_schema_to_init(full_path: str, spec: SchemaSpecDto):
81
+ init_the_init(full_path, code_imports="")
82
+ init_path = os.path.join(full_path, "__init__.py")
83
+ with open(init_path, "a") as f:
84
+ f.write(render_poly_schema(spec) + "\n\n")
85
+
86
+
87
+ def render_poly_schema(spec: SchemaSpecDto) -> str:
88
+ definition = spec["definition"]
89
+ if not definition.get("type"):
90
+ definition["type"] = "object"
91
+ root, schema_types = wrapped_generate_schema_types(
92
+ definition, root=spec["name"], fallback_type=Dict
93
+ )
94
+ return schema_types
95
+ # return FALLBACK_SPEC_TEMPLATE.format(name=spec["name"])
polyapi/schema.py CHANGED
@@ -1,11 +1,17 @@
1
+ """ NOTE: this file represents the schema parsing logic for jsonschema_gentypes
2
+ """
1
3
  import logging
2
4
  import contextlib
5
+ import re
3
6
  from typing import Dict
4
7
  from jsonschema_gentypes.cli import process_config
5
8
  from jsonschema_gentypes import configuration
9
+ import referencing
6
10
  import tempfile
7
11
  import json
8
12
 
13
+ import referencing.exceptions
14
+
9
15
  from polyapi.constants import JSONSCHEMA_TO_PYTHON_TYPE_MAP
10
16
 
11
17
 
@@ -33,8 +39,16 @@ def _temp_store_input_data(input_data: Dict) -> str:
33
39
 
34
40
 
35
41
  def wrapped_generate_schema_types(type_spec: dict, root, fallback_type):
42
+ from polyapi.utils import pascalCase
36
43
  if not root:
37
- root = "MyList" if fallback_type == "List" else "MyDict"
44
+ root = "List" if fallback_type == "List" else "Dict"
45
+ if type_spec.get("x-poly-ref") and type_spec["x-poly-ref"].get("path"):
46
+ # x-poly-ref occurs when we have an unresolved reference
47
+ # lets name the root after the reference for some level of visibility
48
+ root += pascalCase(type_spec["x-poly-ref"]["path"].replace(".", " "))
49
+ else:
50
+ # if we have no root, just add "My"
51
+ root = "My" + root
38
52
 
39
53
  root = clean_title(root)
40
54
 
@@ -44,8 +58,13 @@ def wrapped_generate_schema_types(type_spec: dict, root, fallback_type):
44
58
  # some schemas are so huge, our library cant handle it
45
59
  # TODO identify critical recursion penalty and maybe switch underlying logic to iterative?
46
60
  return fallback_type, ""
61
+ except referencing.exceptions.CannotDetermineSpecification:
62
+ # just go with fallback_type here
63
+ # we couldn't match the right $ref earlier in resolve_poly_refs
64
+ # {'$ref': '#/definitions/FinanceAccountListModel'}
65
+ return fallback_type, ""
47
66
  except:
48
- logging.exception(f"Error when generating schema type: {type_spec}")
67
+ logging.error(f"Error when generating schema type: {type_spec}\nusing fallback type '{fallback_type}'")
49
68
  return fallback_type, ""
50
69
 
51
70
 
@@ -77,9 +96,23 @@ def generate_schema_types(input_data: Dict, root=None):
77
96
  with open(tmp_output) as f:
78
97
  output = f.read()
79
98
 
99
+ output = clean_malformed_examples(output)
100
+
80
101
  return output
81
102
 
82
103
 
104
+ # Regex to match everything between "# example: {\n" and "^}$"
105
+ MALFORMED_EXAMPLES_PATTERN = re.compile(r"# example: \{\n.*?^\}$", flags=re.DOTALL | re.MULTILINE)
106
+
107
+
108
+ def clean_malformed_examples(example: str) -> str:
109
+ """ there is a bug in the `jsonschmea_gentypes` library where if an example from a jsonchema is an object,
110
+ it will break the code because the object won't be properly commented out
111
+ """
112
+ cleaned_example = MALFORMED_EXAMPLES_PATTERN.sub("", example)
113
+ return cleaned_example
114
+
115
+
83
116
  def clean_title(title: str) -> str:
84
117
  """ used by library generation, sometimes functions can be added with spaces in the title
85
118
  or other nonsense. fix them!
polyapi/server.py CHANGED
@@ -1,7 +1,7 @@
1
- from typing import Any, Dict, List, Tuple
1
+ from typing import Any, Dict, List, Tuple, cast
2
2
 
3
- from polyapi.typedefs import PropertySpecification
4
- from polyapi.utils import add_type_import_path, parse_arguments, get_type_and_def, rewrite_arg_name
3
+ from polyapi.typedefs import PropertySpecification, PropertyType
4
+ from polyapi.utils import add_type_import_path, parse_arguments, get_type_and_def, return_type_already_defined_in_args, rewrite_arg_name
5
5
 
6
6
  SERVER_DEFS_TEMPLATE = """
7
7
  from typing import List, Dict, Any, TypedDict, Callable
@@ -21,7 +21,7 @@ def {function_name}(
21
21
  try:
22
22
  return {return_action}
23
23
  except:
24
- return resp.text
24
+ return resp.text # type: ignore # fallback for debugging
25
25
 
26
26
 
27
27
  """
@@ -37,7 +37,11 @@ def render_server_function(
37
37
  ) -> Tuple[str, str]:
38
38
  arg_names = [a["name"] for a in arguments]
39
39
  args, args_def = parse_arguments(function_name, arguments)
40
- return_type_name, return_type_def = get_type_and_def(return_type) # type: ignore
40
+ return_type_name, return_type_def = get_type_and_def(cast(PropertyType, return_type), "ReturnType")
41
+
42
+ if return_type_def and return_type_already_defined_in_args(return_type_name, args_def):
43
+ return_type_def = ""
44
+
41
45
  data = "{" + ", ".join([f"'{arg}': {rewrite_arg_name(arg)}" for arg in arg_names]) + "}"
42
46
  func_type_defs = SERVER_DEFS_TEMPLATE.format(
43
47
  args_def=args_def,
polyapi/typedefs.py CHANGED
@@ -55,6 +55,19 @@ class VariableSpecDto(TypedDict):
55
55
  variable: VariableSpecification
56
56
  type: Literal['serverVariable']
57
57
 
58
+
59
+ class SchemaSpecDto(TypedDict):
60
+ id: str
61
+ context: str
62
+ name: str
63
+ contextName: str
64
+ type: Literal['schema']
65
+ definition: Dict[Any, Any]
66
+ visibilityMetadata: object
67
+ unresolvedPolySchemaRefs: List
68
+ # TODO add more
69
+
70
+
58
71
  Visibility = Union[Literal['PUBLIC'], Literal['TENANT'], Literal['ENVIRONMENT']]
59
72
 
60
73
 
@@ -69,6 +82,7 @@ class PolyServerFunction(PolyDeployable):
69
82
  always_on: NotRequired[bool]
70
83
  visibility: NotRequired[Visibility]
71
84
 
85
+
72
86
  class PolyClientFunction(PolyDeployable):
73
87
  logs_enabled: NotRequired[bool]
74
88
  visibility: NotRequired[Visibility]
polyapi/utils.py CHANGED
@@ -6,24 +6,28 @@ from typing import Tuple, List
6
6
  from colorama import Fore, Style
7
7
  from polyapi.constants import BASIC_PYTHON_TYPES
8
8
  from polyapi.typedefs import PropertySpecification, PropertyType
9
- from polyapi.schema import wrapped_generate_schema_types, clean_title, map_primitive_types
9
+ from polyapi.schema import (
10
+ wrapped_generate_schema_types,
11
+ clean_title,
12
+ map_primitive_types,
13
+ )
10
14
 
11
15
 
12
16
  # this string should be in every __init__ file.
13
17
  # it contains all the imports needed for the function or variable code to run
14
- CODE_IMPORTS = "from typing import List, Dict, Any, TypedDict, Optional, Callable\nimport logging\nimport requests\nimport socketio # type: ignore\nfrom polyapi.config import get_api_key_and_url\nfrom polyapi.execute import execute, execute_post, variable_get, variable_update\n\n"
15
- FALLBACK_TYPES = {"Dict", "List"}
18
+ CODE_IMPORTS = "from typing import List, Dict, Any, Optional, Callable\nfrom typing_extensions import TypedDict, NotRequired\nimport logging\nimport requests\nimport socketio # type: ignore\nfrom polyapi.config import get_api_key_and_url\nfrom polyapi.execute import execute, execute_post, variable_get, variable_update\n\n"
16
19
 
17
20
 
18
- def init_the_init(full_path: str) -> None:
21
+ def init_the_init(full_path: str, code_imports="") -> None:
19
22
  init_path = os.path.join(full_path, "__init__.py")
20
23
  if not os.path.exists(init_path):
24
+ code_imports = code_imports or CODE_IMPORTS
21
25
  with open(init_path, "w") as f:
22
- f.write(CODE_IMPORTS)
26
+ f.write(code_imports)
23
27
 
24
28
 
25
- def add_import_to_init(full_path: str, next: str) -> None:
26
- init_the_init(full_path)
29
+ def add_import_to_init(full_path: str, next: str, code_imports="") -> None:
30
+ init_the_init(full_path, code_imports=code_imports)
27
31
 
28
32
  init_path = os.path.join(full_path, "__init__.py")
29
33
  with open(init_path, "a+") as f:
@@ -38,16 +42,20 @@ def get_auth_headers(api_key: str):
38
42
  return {"Authorization": f"Bearer {api_key}"}
39
43
 
40
44
 
41
- def camelCase(s):
45
+ def camelCase(s: str) -> str:
42
46
  s = s.strip()
43
47
  if " " in s or "-" in s:
44
48
  s = re.sub(r"(_|-)+", " ", s).title().replace(" ", "")
45
- return ''.join([s[0].lower(), s[1:]])
49
+ return "".join([s[0].lower(), s[1:]])
46
50
  else:
47
51
  # s is already in camelcase as best as we can tell, just move on!
48
52
  return s
49
53
 
50
54
 
55
+ def pascalCase(s) -> str:
56
+ return re.sub(r"(^|_)([a-z])", lambda match: match.group(2).upper(), s)
57
+
58
+
51
59
  def print_green(s: str):
52
60
  print(Fore.GREEN + s + Style.RESET_ALL)
53
61
 
@@ -61,8 +69,7 @@ def print_red(s: str):
61
69
 
62
70
 
63
71
  def add_type_import_path(function_name: str, arg: str) -> str:
64
- """ if not basic type, coerce to camelCase and add the import path
65
- """
72
+ """if not basic type, coerce to camelCase and add the import path"""
66
73
  # for now, just treat Callables as basic types
67
74
  if arg.startswith("Callable"):
68
75
  return arg
@@ -79,12 +86,16 @@ def add_type_import_path(function_name: str, arg: str) -> str:
79
86
  sub = sub.replace('"', "")
80
87
  return f'List["{to_func_namespace(function_name)}.{camelCase(sub)}"]'
81
88
  else:
82
- return f'List[{to_func_namespace(function_name)}.{camelCase(sub)}]'
89
+ return f"List[{to_func_namespace(function_name)}.{camelCase(sub)}]"
83
90
 
84
- return f'{to_func_namespace(function_name)}.{camelCase(arg)}'
91
+ return f"{to_func_namespace(function_name)}.{camelCase(arg)}"
85
92
 
86
93
 
87
- def get_type_and_def(type_spec: PropertyType) -> Tuple[str, str]:
94
+ def get_type_and_def(
95
+ type_spec: PropertyType, title_fallback: str = ""
96
+ ) -> Tuple[str, str]:
97
+ """ returns type and type definition for a given PropertyType
98
+ """
88
99
  if type_spec["kind"] == "plain":
89
100
  value = type_spec["value"]
90
101
  if value.endswith("[]"):
@@ -111,18 +122,27 @@ def get_type_and_def(type_spec: PropertyType) -> Tuple[str, str]:
111
122
  elif type_spec["kind"] == "object":
112
123
  if type_spec.get("schema"):
113
124
  schema = type_spec["schema"]
114
- title = schema.get("title", "")
115
- if title:
125
+ title = schema.get("title", schema.get("name", title_fallback))
126
+ if title and schema.get("type") == "array":
127
+ # TODO fix me
128
+ # we don't use ReturnType as name for the list type here, we use _ReturnTypeItem
129
+ return "List", ""
130
+ elif title:
116
131
  assert isinstance(title, str)
117
132
  return wrapped_generate_schema_types(schema, title, "Dict") # type: ignore
118
-
133
+ elif schema.get("allOf") and len(schema["allOf"]):
134
+ # we are in a case of a single allOf, lets strip off the allOf and move on!
135
+ # our library doesn't handle allOf well yet
136
+ allOf = schema["allOf"][0]
137
+ title = allOf.get("title", allOf.get("name", title_fallback))
138
+ return wrapped_generate_schema_types(allOf, title, "Dict")
119
139
  elif schema.get("items"):
120
140
  # fallback to schema $ref name if no explicit title
121
141
  items = schema.get("items") # type: ignore
122
- title = items.get("title", "") # type: ignore
142
+ title = items.get("title") # type: ignore
123
143
  if not title:
124
144
  # title is actually a reference to another schema
125
- title = items.get("$ref", "") # type: ignore
145
+ title = items.get("$ref", title_fallback) # type: ignore
126
146
 
127
147
  title = title.rsplit("/", 1)[-1]
128
148
  if not title:
@@ -144,12 +164,18 @@ def get_type_and_def(type_spec: PropertyType) -> Tuple[str, str]:
144
164
  return_type = "Any"
145
165
 
146
166
  for argument in type_spec["spec"]["arguments"]:
167
+ # do NOT add this fallback here
168
+ # callable arguments don't understand the imports yet
169
+ # if it's not a basic type here, we'll just do Any
170
+ # _maybe_add_fallback_schema_name(argument)
147
171
  arg_type, arg_def = get_type_and_def(argument["type"])
148
172
  arg_types.append(arg_type)
149
173
  if arg_def:
150
174
  arg_defs.append(arg_def)
151
175
 
152
- final_arg_type = "Callable[[{}], {}]".format(", ".join(arg_types), return_type)
176
+ final_arg_type = "Callable[[{}], {}]".format(
177
+ ", ".join(arg_types), return_type
178
+ )
153
179
  return final_arg_type, "\n".join(arg_defs)
154
180
  else:
155
181
  return "Callable", ""
@@ -159,15 +185,30 @@ def get_type_and_def(type_spec: PropertyType) -> Tuple[str, str]:
159
185
  return "Any", ""
160
186
 
161
187
 
162
- def parse_arguments(function_name: str, arguments: List[PropertySpecification]) -> Tuple[str, str]:
188
+ def _maybe_add_fallback_schema_name(a: PropertySpecification):
189
+ if a["type"]["kind"] == "object" and a["type"].get("schema"):
190
+ schema = a["type"].get("schema", {})
191
+ if not schema.get("title") and not schema.get("name") and a["name"]:
192
+ schema["title"] = a["name"].title()
193
+
194
+
195
+ def parse_arguments(
196
+ function_name: str, arguments: List[PropertySpecification]
197
+ ) -> Tuple[str, str]:
163
198
  args_def = []
164
199
  arg_string = ""
165
200
  for idx, a in enumerate(arguments):
201
+ _maybe_add_fallback_schema_name(a)
166
202
  arg_type, arg_def = get_type_and_def(a["type"])
167
203
  if arg_def:
168
204
  args_def.append(arg_def)
169
205
  a["name"] = rewrite_arg_name(a["name"])
170
- arg_string += f" {a['name']}: {add_type_import_path(function_name, arg_type)}"
206
+ arg_string += (
207
+ f" {a['name']}: {add_type_import_path(function_name, arg_type)}"
208
+ )
209
+ if not a["required"]:
210
+ arg_string += " = None"
211
+
171
212
  description = a.get("description", "")
172
213
  description = description.replace("\n", " ")
173
214
  if description:
@@ -193,7 +234,7 @@ RESERVED_WORDS = {"List", "Dict", "Any", "Optional", "Callable"} | set(keyword.k
193
234
 
194
235
 
195
236
  def to_func_namespace(s: str) -> str:
196
- """ convert a function name to some function namespace
237
+ """convert a function name to some function namespace
197
238
  by default it is
198
239
  """
199
240
  rv = s[0].upper() + s[1:]
@@ -212,6 +253,10 @@ def rewrite_arg_name(s: str):
212
253
  return rewrite_reserved(camelCase(s))
213
254
 
214
255
 
256
+ # def get_return_type_name(function_name: str) -> str:
257
+ # return function_name[0].upper() + function_name[1:] + "ReturnType"
258
+
259
+
215
260
  valid_subdomains = ["na[1-2]", "eu[1-2]", "dev"]
216
261
 
217
262
 
@@ -229,3 +274,21 @@ def is_valid_uuid(uuid_string, version=4):
229
274
  return False
230
275
 
231
276
  return str(uuid_obj) == uuid_string
277
+
278
+
279
+ def return_type_already_defined_in_args(return_type_name: str, args_def: str) -> bool:
280
+ """
281
+ Checks if the return_type_name preceded optionally by 'class ' and followed by ' =' exists in args_def.
282
+
283
+ Args:
284
+ return_type_name (str): The name of the return type to check.
285
+ args_def (str): The string containing argument definitions.
286
+
287
+ Returns:
288
+ bool: True if the pattern exists, False otherwise.
289
+ """
290
+ basic_pattern = rf"^{re.escape(return_type_name)}\s="
291
+ basic_match = bool(re.search(basic_pattern, args_def, re.MULTILINE))
292
+ class_pattern = rf"^class {re.escape(return_type_name)}\(TypedDict"
293
+ class_match = bool(re.search(class_pattern, args_def, re.MULTILINE))
294
+ return basic_match or class_match
@@ -1,11 +1,11 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: polyapi-python
3
- Version: 0.3.2.dev2
3
+ Version: 0.3.3.dev9
4
4
  Summary: The Python Client for PolyAPI, the IPaaS by Developers for Developers
5
5
  Author-email: Dan Fellin <dan@polyapi.io>
6
6
  License: MIT License
7
7
 
8
- Copyright (c) 2023 PolyAPI Inc.
8
+ Copyright (c) 2025 PolyAPI Inc.
9
9
 
10
10
  Permission is hereby granted, free of charge, to any person obtaining a copy
11
11
  of this software and associated documentation files (the "Software"), to deal
@@ -28,14 +28,15 @@ Project-URL: Homepage, https://github.com/polyapi/polyapi-python
28
28
  Requires-Python: >=3.10
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
- Requires-Dist: requests==2.31.0
32
- Requires-Dist: typing_extensions>=4.10.0
31
+ Requires-Dist: requests>=2.32.3
32
+ Requires-Dist: typing_extensions>=4.12.2
33
33
  Requires-Dist: jsonschema-gentypes==2.6.0
34
34
  Requires-Dist: pydantic==2.6.4
35
35
  Requires-Dist: stdlib_list==0.10.0
36
36
  Requires-Dist: colorama==0.4.4
37
37
  Requires-Dist: python-socketio[asyncio_client]==5.11.1
38
38
  Requires-Dist: truststore==0.8.0
39
+ Dynamic: license-file
39
40
 
40
41
  # PolyAPI Python Library
41
42
 
@@ -182,6 +183,26 @@ To run this library's unit tests, please clone the repo then run:
182
183
  python -m unittest discover
183
184
  ```
184
185
 
186
+ ## Linting
187
+
188
+ The flake8 config is at the root of this repo at `.flake8`.
189
+
190
+ When hacking on this library, please enable flake8 and add this line to your flake8 args (e.g., in your VSCode Workspace Settings):
191
+
192
+ ```
193
+ --config=.flake8
194
+ ```
195
+
196
+ ## Mypy Type Improvements
197
+
198
+ This script is handy for checking for any mypy types:
199
+
200
+ ```bash
201
+ ./check_mypy.sh
202
+ ```
203
+
204
+ Please ignore \[name-defined\] errors for now. This is a known bug we are working to fix!
205
+
185
206
  ## Support
186
207
 
187
208
  If you run into any issues or want help getting started with this project, please contact support@polyapi.io
@@ -0,0 +1,31 @@
1
+ polyapi/__init__.py,sha256=a1Poy1kaTncYnUg6nWRcTjVm-R1CUQk12UX7VYQ9d5k,616
2
+ polyapi/__main__.py,sha256=V4zhAh_YGxno5f_KSrlkELxcuDh9bR3WSd0n-2r-qQQ,93
3
+ polyapi/api.py,sha256=f1037HFJF7DtQSSypM4PE5AmmxWxjd0JiW6ARZqrgac,1879
4
+ polyapi/auth.py,sha256=zrIGatjba5GwUTNjKj1GHQWTEDP9B-HrSzCKbLFoqvc,5336
5
+ polyapi/cli.py,sha256=AKsWVHZPKGnypOdnzIpoZOsTuwcAuDGQajXhLe9OQKI,8239
6
+ polyapi/client.py,sha256=CoFDYvyKsqL4wPQbUDIr0Qb8Q5eD92xN4OEEcJEVuGQ,1296
7
+ polyapi/config.py,sha256=Vgc_q9FYXWGCOTr13EbpD0AwHks0Nflimy1NtZxgynA,3088
8
+ polyapi/constants.py,sha256=sc-FnS0SngBLvSu1ZWMs0UCf9EYD1u1Yhfr-sZXGLns,607
9
+ polyapi/deployables.py,sha256=WVcNNB6W5ZW_-ukf_kK3moRcnwIkC-O4te6vLepjcco,11936
10
+ polyapi/error_handler.py,sha256=I_e0iz6VM23FLVQWJljxs2NGcl_OODbi43OcbnqBlp8,2398
11
+ polyapi/exceptions.py,sha256=Zh7i7eCUhDuXEdUYjatkLFTeZkrx1BJ1P5ePgbJ9eIY,89
12
+ polyapi/execute.py,sha256=T9lXtiOz-JZTJgBKvJptA5_mz31qvYa6-O4NzM52mq4,2118
13
+ polyapi/function_cli.py,sha256=htgmcx_dPmw4_5NKRgIivcwS7D8bkOsxCTOrJhzV3pU,3989
14
+ polyapi/generate.py,sha256=IIbU4Kc8Ut-N3cPI1qzgV3M4r_GHi39dgU0ngpUY86Q,10473
15
+ polyapi/parser.py,sha256=mdoh4pNq8pyiHE0-i6Coqj8frEXfBLRk6itpAXMrrgI,20373
16
+ polyapi/poly_schemas.py,sha256=KFVmpB047pWQaTkiCJ3A9sUTNplTS8JETon1Sm2lnQs,2969
17
+ polyapi/prepare.py,sha256=Q8CWV4kmZ2dbXYVsud34AgJkj5ymcQ_IcYhLuikc9yk,6659
18
+ polyapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ polyapi/rendered_spec.py,sha256=uaNzBhP4cX7iGfKwzZv0dxMagWzsGeDr0cQYx_AyIhQ,2153
20
+ polyapi/schema.py,sha256=ZSzeUjpqigLvE4tFKB7y4AaZG-W5N5Z9wMH-F-vjMBU,4616
21
+ polyapi/server.py,sha256=YXWxhYBx-hluwDQ8Jvfpy2s8ogz0GsNTMcZVNcP5ca8,2147
22
+ polyapi/sync.py,sha256=PGdC0feBBjEVrF3d9EluW_OAxbWuzSrfh84czma8kWg,6476
23
+ polyapi/typedefs.py,sha256=KniVl7vwcDOhgAJmHSgTJKkP0rKWvSLIPOGsWuf9jRU,2239
24
+ polyapi/utils.py,sha256=K6QMKEf2fgmh3AswyNBADfv53sIOSAbXmGx2MaW5vy8,10261
25
+ polyapi/variables.py,sha256=d36-trnfTL_8m2NkorMiImb4O3UrJbiFV38CHxV5i0A,4200
26
+ polyapi/webhook.py,sha256=LWv28c2MLz_OKBI_Nn7WR4C-gs1SWgbdXsoxIIf-9UI,4886
27
+ polyapi_python-0.3.3.dev9.dist-info/licenses/LICENSE,sha256=6b_I7aPVp8JXhqQwdw7_B84Ca0S4JGjHj0sr_1VOdB4,1068
28
+ polyapi_python-0.3.3.dev9.dist-info/METADATA,sha256=fd2KtyjZKHie7x1Ua2p-QshQIE4Rc8gb5CMczxqGqgg,5782
29
+ polyapi_python-0.3.3.dev9.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
30
+ polyapi_python-0.3.3.dev9.dist-info/top_level.txt,sha256=CEFllOnzowci_50RYJac-M54KD2IdAptFsayVVF_f04,8
31
+ polyapi_python-0.3.3.dev9.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 PolyAPI Inc.
3
+ Copyright (c) 2025 PolyAPI Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,30 +0,0 @@
1
- polyapi/__init__.py,sha256=a1Poy1kaTncYnUg6nWRcTjVm-R1CUQk12UX7VYQ9d5k,616
2
- polyapi/__main__.py,sha256=V4zhAh_YGxno5f_KSrlkELxcuDh9bR3WSd0n-2r-qQQ,93
3
- polyapi/api.py,sha256=e-8nOzq6SXD7_YPVBlW82_9wVxDCc8XD9ioMv_QvnH0,1877
4
- polyapi/auth.py,sha256=zrIGatjba5GwUTNjKj1GHQWTEDP9B-HrSzCKbLFoqvc,5336
5
- polyapi/cli.py,sha256=w0SOiRHR7gJRoEwkfyGC1sdBT76sgx8MUcF8_GnGuJU,8324
6
- polyapi/client.py,sha256=CoFDYvyKsqL4wPQbUDIr0Qb8Q5eD92xN4OEEcJEVuGQ,1296
7
- polyapi/config.py,sha256=uvEvOfWYZTLmBmZX-5jJxCzWPpwzVmEOIiQIdi98P4Y,3015
8
- polyapi/constants.py,sha256=sc-FnS0SngBLvSu1ZWMs0UCf9EYD1u1Yhfr-sZXGLns,607
9
- polyapi/deployables.py,sha256=qKoyuPUv46yZoLBvSOmk6JsBWqomwJAkadhItp8Lx40,11956
10
- polyapi/error_handler.py,sha256=I_e0iz6VM23FLVQWJljxs2NGcl_OODbi43OcbnqBlp8,2398
11
- polyapi/exceptions.py,sha256=Zh7i7eCUhDuXEdUYjatkLFTeZkrx1BJ1P5ePgbJ9eIY,89
12
- polyapi/execute.py,sha256=kXnvlNQ7nz9cRlV2_5gXH09UCmyiDP5zi3wiAw0uDuk,1943
13
- polyapi/function_cli.py,sha256=-Fjv_DECZB5LK8rT222B62GPw4vksAi7KsIqT-ss5FM,4185
14
- polyapi/generate.py,sha256=SIRfN7RF3Z7eQ8kSNg4H70LeT7Hmh-Mn5maibvM7kAM,7969
15
- polyapi/parser.py,sha256=noi3Tjf1vVNm5gjTzgvOlJ6IMaoZ9DheklibVWfPPhk,20311
16
- polyapi/prepare.py,sha256=Q8CWV4kmZ2dbXYVsud34AgJkj5ymcQ_IcYhLuikc9yk,6659
17
- polyapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- polyapi/rendered_spec.py,sha256=uaNzBhP4cX7iGfKwzZv0dxMagWzsGeDr0cQYx_AyIhQ,2153
19
- polyapi/schema.py,sha256=VVMHAT5yU47cRC7xF44BrmUSemlk5oIKSxH2HTVPaQ8,3169
20
- polyapi/server.py,sha256=NzQCZFSAJK7XiRw1kiU_i9uMvgYK7i8qh7UX2xjytJU,1908
21
- polyapi/sync.py,sha256=PGdC0feBBjEVrF3d9EluW_OAxbWuzSrfh84czma8kWg,6476
22
- polyapi/typedefs.py,sha256=U0i30Y9CgoUBWeMUbpM28S8eVOEU9NfdckK1VAop3A0,1994
23
- polyapi/utils.py,sha256=jzCh-ivKMcgp5fIXynhYmP9UyzsISr9bGGEzdPP8n3w,7644
24
- polyapi/variables.py,sha256=d36-trnfTL_8m2NkorMiImb4O3UrJbiFV38CHxV5i0A,4200
25
- polyapi/webhook.py,sha256=LWv28c2MLz_OKBI_Nn7WR4C-gs1SWgbdXsoxIIf-9UI,4886
26
- polyapi_python-0.3.2.dev2.dist-info/LICENSE,sha256=Hi0kDr56Dsy0uYIwNt4r9G7tI8x8miXRTlyvbeplCP8,1068
27
- polyapi_python-0.3.2.dev2.dist-info/METADATA,sha256=YKwIhWvsJJ3qnsGIa1LqH7fu86jCs2a9pA5lrBtzfF8,5326
28
- polyapi_python-0.3.2.dev2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
29
- polyapi_python-0.3.2.dev2.dist-info/top_level.txt,sha256=CEFllOnzowci_50RYJac-M54KD2IdAptFsayVVF_f04,8
30
- polyapi_python-0.3.2.dev2.dist-info/RECORD,,