polyapi-python 0.3.7.dev2__py3-none-any.whl → 0.3.7.dev3__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/function_cli.py CHANGED
@@ -86,7 +86,7 @@ def function_add_or_update(
86
86
 
87
87
  headers = get_auth_headers(api_key)
88
88
  resp = requests.post(url, headers=headers, json=data)
89
- if resp.status_code == 201:
89
+ if resp.status_code in [200, 201]:
90
90
  print_green("DEPLOYED")
91
91
  function_id = resp.json()["id"]
92
92
  print(f"Function ID: {function_id}")
polyapi/generate.py CHANGED
@@ -129,24 +129,26 @@ def parse_function_specs(
129
129
  ) -> List[SpecificationDto]:
130
130
  functions = []
131
131
  for spec in specs:
132
- if not spec or "function" not in spec:
133
- continue
134
-
135
- if not spec["function"]:
136
- continue
137
-
138
- if limit_ids and spec["id"] not in limit_ids:
132
+ if not spec:
139
133
  continue
140
134
 
135
+ # For no_types mode, we might not have function data, but we still want to include the spec
136
+ # if it's a supported function type
141
137
  if spec["type"] not in SUPPORTED_FUNCTION_TYPES:
142
138
  continue
143
139
 
144
- if spec["type"] == "customFunction" and spec["language"] != "python":
145
- # poly libraries only support client functions of same language
140
+ # Skip if we have a limit and this spec is not in it
141
+ if limit_ids and spec.get("id") not in limit_ids:
146
142
  continue
147
143
 
144
+ # For customFunction, check language if we have function data
145
+ if spec["type"] == "customFunction":
146
+ if spec.get("language") and spec["language"] != "python":
147
+ # poly libraries only support client functions of same language
148
+ continue
149
+
148
150
  # Functions with serverSideAsync True will always return a Dict with execution ID
149
- if spec.get('serverSideAsync'):
151
+ if spec.get('serverSideAsync') and spec.get("function"):
150
152
  spec['function']['returnType'] = {'kind': 'plain', 'value': 'object'}
151
153
 
152
154
  functions.append(spec)
@@ -205,6 +207,63 @@ def remove_old_library():
205
207
  shutil.rmtree(path)
206
208
 
207
209
 
210
+ def create_empty_schemas_module():
211
+ """Create an empty schemas module for no-types mode so user code can still import from polyapi.schemas"""
212
+ currdir = os.path.dirname(os.path.abspath(__file__))
213
+ schemas_path = os.path.join(currdir, "schemas")
214
+
215
+ # Create the schemas directory
216
+ if not os.path.exists(schemas_path):
217
+ os.makedirs(schemas_path)
218
+
219
+ # Create an __init__.py file with dynamic schema resolution
220
+ init_path = os.path.join(schemas_path, "__init__.py")
221
+ with open(init_path, "w") as f:
222
+ f.write('''"""Empty schemas module for no-types mode"""
223
+ from typing import Any, Dict
224
+
225
+ class _GenericSchema(Dict[str, Any]):
226
+ """Generic schema type that acts like a Dict for no-types mode"""
227
+ def __init__(self, *args, **kwargs):
228
+ super().__init__(*args, **kwargs)
229
+
230
+ class _SchemaModule:
231
+ """Dynamic module that returns itself for attribute access, allowing infinite nesting"""
232
+
233
+ def __getattr__(self, name: str):
234
+ # For callable access (like schemas.Response()), return the generic schema class
235
+ # For further attribute access (like schemas.random.random2), return self to allow nesting
236
+ return _NestedSchemaAccess()
237
+
238
+ def __call__(self, *args, **kwargs):
239
+ # If someone tries to call the module itself, return a generic schema
240
+ return _GenericSchema(*args, **kwargs)
241
+
242
+ def __dir__(self):
243
+ # Return common schema names for introspection
244
+ return ['Response', 'Request', 'Error', 'Data', 'Result']
245
+
246
+ class _NestedSchemaAccess:
247
+ """Handles nested attribute access and final callable resolution"""
248
+
249
+ def __getattr__(self, name: str):
250
+ # Continue allowing nested access
251
+ return _NestedSchemaAccess()
252
+
253
+ def __call__(self, *args, **kwargs):
254
+ # When finally called, return a generic schema instance
255
+ return _GenericSchema(*args, **kwargs)
256
+
257
+ def __class_getitem__(cls, item):
258
+ # Support type annotations like schemas.Response[str]
259
+ return _GenericSchema
260
+
261
+ # Replace this module with our dynamic module
262
+ import sys
263
+ sys.modules[__name__] = _SchemaModule()
264
+ ''')
265
+
266
+
208
267
  def generate(contexts: Optional[List[str]] = None, no_types: bool = False) -> None:
209
268
  generate_msg = f"Generating Poly Python SDK for contexts ${contexts}..." if contexts else "Generating Poly Python SDK..."
210
269
  print(generate_msg, end="", flush=True)
@@ -216,14 +275,23 @@ def generate(contexts: Optional[List[str]] = None, no_types: bool = False) -> No
216
275
  limit_ids: List[str] = [] # useful for narrowing down generation to a single function to debug
217
276
  functions = parse_function_specs(specs, limit_ids=limit_ids)
218
277
 
219
- schemas = get_schemas()
220
- schema_index = build_schema_index(schemas)
221
- if schemas:
222
- schema_limit_ids: List[str] = [] # useful for narrowing down generation to a single function to debug
223
- schemas = replace_poly_refs_in_schemas(schemas, schema_index)
224
- generate_schemas(schemas, limit_ids=schema_limit_ids)
225
-
226
- functions = replace_poly_refs_in_functions(functions, schema_index)
278
+ # Only process schemas if no_types is False
279
+ if not no_types:
280
+ schemas = get_schemas()
281
+ schema_index = build_schema_index(schemas)
282
+ if schemas:
283
+ schema_limit_ids: List[str] = [] # useful for narrowing down generation to a single function to debug
284
+ schemas = replace_poly_refs_in_schemas(schemas, schema_index)
285
+ generate_schemas(schemas, limit_ids=schema_limit_ids)
286
+
287
+ functions = replace_poly_refs_in_functions(functions, schema_index)
288
+ else:
289
+ # When no_types is True, we still need to process functions but without schema resolution
290
+ # Use an empty schema index to avoid poly-ref resolution
291
+ schema_index = {}
292
+
293
+ # Create an empty schemas module so user code can still import from polyapi.schemas
294
+ create_empty_schemas_module()
227
295
 
228
296
  if functions:
229
297
  generate_functions(functions)
@@ -233,10 +301,11 @@ def generate(contexts: Optional[List[str]] = None, no_types: bool = False) -> No
233
301
  )
234
302
  exit()
235
303
 
236
- variables = get_variables()
237
- if variables:
238
- generate_variables(variables)
239
-
304
+ # Only process variables if no_types is False
305
+ if not no_types:
306
+ variables = get_variables()
307
+ if variables:
308
+ generate_variables(variables)
240
309
 
241
310
  # indicator to vscode extension that this is a polyapi-python project
242
311
  file_path = os.path.join(os.getcwd(), ".polyapi-python")
@@ -266,11 +335,19 @@ def render_spec(spec: SpecificationDto) -> Tuple[str, str]:
266
335
 
267
336
  arguments: List[PropertySpecification] = []
268
337
  return_type = {}
269
- if spec["function"]:
270
- arguments = [
271
- arg for arg in spec["function"]["arguments"]
272
- ]
273
- return_type = spec["function"]["returnType"]
338
+ if spec.get("function"):
339
+ # Handle cases where arguments might be missing or None
340
+ if spec["function"].get("arguments"):
341
+ arguments = [
342
+ arg for arg in spec["function"]["arguments"]
343
+ ]
344
+
345
+ # Handle cases where returnType might be missing or None
346
+ if spec["function"].get("returnType"):
347
+ return_type = spec["function"]["returnType"]
348
+ else:
349
+ # Provide a fallback return type when missing
350
+ return_type = {"kind": "any"}
274
351
 
275
352
  if function_type == "apiFunction":
276
353
  func_str, func_type_defs = render_api_function(
@@ -284,7 +361,7 @@ def render_spec(spec: SpecificationDto) -> Tuple[str, str]:
284
361
  elif function_type == "customFunction":
285
362
  func_str, func_type_defs = render_client_function(
286
363
  function_name,
287
- spec["code"],
364
+ spec.get("code", ""),
288
365
  arguments,
289
366
  return_type,
290
367
  )
polyapi/utils.py CHANGED
@@ -97,20 +97,32 @@ def get_type_and_def(
97
97
  ) -> Tuple[str, str]:
98
98
  """ returns type and type definition for a given PropertyType
99
99
  """
100
+ # Handle cases where type_spec might be None or empty
101
+ if not type_spec:
102
+ return "Any", ""
103
+
104
+ # Handle cases where kind might be missing
105
+ if "kind" not in type_spec:
106
+ return "Any", ""
107
+
100
108
  if type_spec["kind"] == "plain":
101
- value = type_spec["value"]
109
+ value = type_spec.get("value", "")
102
110
  if value.endswith("[]"):
103
111
  primitive = map_primitive_types(value[:-2])
104
112
  return f"List[{primitive}]", ""
105
113
  else:
106
114
  return map_primitive_types(value), ""
107
115
  elif type_spec["kind"] == "primitive":
108
- return map_primitive_types(type_spec["type"]), ""
116
+ return map_primitive_types(type_spec.get("type", "any")), ""
109
117
  elif type_spec["kind"] == "array":
110
118
  if type_spec.get("items"):
111
119
  items = type_spec["items"]
112
120
  if items.get("$ref"):
113
- return wrapped_generate_schema_types(type_spec, "ResponseType", "Dict") # type: ignore
121
+ # For no-types mode, avoid complex schema generation
122
+ try:
123
+ return wrapped_generate_schema_types(type_spec, "ResponseType", "Dict") # type: ignore
124
+ except:
125
+ return "List[Dict]", ""
114
126
  else:
115
127
  item_type, _ = get_type_and_def(items)
116
128
  title = f"List[{item_type}]"
@@ -130,13 +142,20 @@ def get_type_and_def(
130
142
  return "List", ""
131
143
  elif title:
132
144
  assert isinstance(title, str)
133
- return wrapped_generate_schema_types(schema, title, "Dict") # type: ignore
145
+ # For no-types mode, avoid complex schema generation
146
+ try:
147
+ return wrapped_generate_schema_types(schema, title, "Dict") # type: ignore
148
+ except:
149
+ return "Dict", ""
134
150
  elif schema.get("allOf") and len(schema["allOf"]):
135
151
  # we are in a case of a single allOf, lets strip off the allOf and move on!
136
152
  # our library doesn't handle allOf well yet
137
153
  allOf = schema["allOf"][0]
138
154
  title = allOf.get("title", allOf.get("name", title_fallback))
139
- return wrapped_generate_schema_types(allOf, title, "Dict")
155
+ try:
156
+ return wrapped_generate_schema_types(allOf, title, "Dict")
157
+ except:
158
+ return "Dict", ""
140
159
  elif schema.get("items"):
141
160
  # fallback to schema $ref name if no explicit title
142
161
  items = schema.get("items") # type: ignore
@@ -150,9 +169,15 @@ def get_type_and_def(
150
169
  return "List", ""
151
170
 
152
171
  title = f"List[{title}]"
153
- return wrapped_generate_schema_types(schema, title, "List")
172
+ try:
173
+ return wrapped_generate_schema_types(schema, title, "List")
174
+ except:
175
+ return "List[Dict]", ""
176
+ elif schema.get("properties"):
177
+ result = wrapped_generate_schema_types(schema, "ResponseType", "Dict") # type: ignore
178
+ return result
154
179
  else:
155
- return "Any", ""
180
+ return "Dict", ""
156
181
  else:
157
182
  return "Dict", ""
158
183
  elif type_spec["kind"] == "function":
@@ -187,9 +212,13 @@ def get_type_and_def(
187
212
 
188
213
 
189
214
  def _maybe_add_fallback_schema_name(a: PropertySpecification):
190
- if a["type"]["kind"] == "object" and a["type"].get("schema"):
215
+ # Handle cases where type might be missing
216
+ if not a.get("type"):
217
+ return
218
+
219
+ if a["type"].get("kind") == "object" and a["type"].get("schema"):
191
220
  schema = a["type"].get("schema", {})
192
- if not schema.get("title") and not schema.get("name") and a["name"]:
221
+ if not schema.get("title") and not schema.get("name") and a.get("name"):
193
222
  schema["title"] = a["name"].title()
194
223
 
195
224
 
@@ -200,14 +229,23 @@ def parse_arguments(
200
229
  arg_string = ""
201
230
  for idx, a in enumerate(arguments):
202
231
  _maybe_add_fallback_schema_name(a)
203
- arg_type, arg_def = get_type_and_def(a["type"])
232
+
233
+ # Handle cases where type might be missing
234
+ arg_type_spec = a.get("type", {"kind": "any"})
235
+ arg_type, arg_def = get_type_and_def(arg_type_spec)
204
236
  if arg_def:
205
237
  args_def.append(arg_def)
206
- a["name"] = rewrite_arg_name(a["name"])
238
+
239
+ # Handle cases where name might be missing
240
+ arg_name = a.get("name", f"arg{idx}")
241
+ a["name"] = rewrite_arg_name(arg_name)
242
+
207
243
  arg_string += (
208
244
  f" {a['name']}: {add_type_import_path(function_name, arg_type)}"
209
245
  )
210
- if not a["required"]:
246
+
247
+ # Handle cases where required might be missing
248
+ if not a.get("required", True):
211
249
  arg_string += " = None"
212
250
 
213
251
  description = a.get("description", "")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polyapi-python
3
- Version: 0.3.7.dev2
3
+ Version: 0.3.7.dev3
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
@@ -10,8 +10,8 @@ polyapi/deployables.py,sha256=WVcNNB6W5ZW_-ukf_kK3moRcnwIkC-O4te6vLepjcco,11936
10
10
  polyapi/error_handler.py,sha256=I_e0iz6VM23FLVQWJljxs2NGcl_OODbi43OcbnqBlp8,2398
11
11
  polyapi/exceptions.py,sha256=Zh7i7eCUhDuXEdUYjatkLFTeZkrx1BJ1P5ePgbJ9eIY,89
12
12
  polyapi/execute.py,sha256=sjI6BMBYPSCD6UngV9DzpJIRSU6p02aShNaTXhDExtY,3457
13
- polyapi/function_cli.py,sha256=wDbgWGSQjMpzmZoPAcU57ZwD8EUTKE8sF9dgZLUInMk,4118
14
- polyapi/generate.py,sha256=ZTLtiMPYUcRygJ-s--2IzLY2FQ1W7idLJnbwh2UngKU,12089
13
+ polyapi/function_cli.py,sha256=hv5K5niegqitT6VwbS7M5ec3nEMyqwVtE6tcpwHoxIk,4125
14
+ polyapi/generate.py,sha256=1ZwcUEfhYLc64Wh5aYhvU6P594HOL2LYycPKkH39frc,15394
15
15
  polyapi/parser.py,sha256=mdoh4pNq8pyiHE0-i6Coqj8frEXfBLRk6itpAXMrrgI,20373
16
16
  polyapi/poly_schemas.py,sha256=T4kfZyfgVLiqLD28GmYNiHnrNx77J_HO4uzk8LUAhlo,3137
17
17
  polyapi/prepare.py,sha256=Q8CWV4kmZ2dbXYVsud34AgJkj5ymcQ_IcYhLuikc9yk,6659
@@ -21,11 +21,11 @@ polyapi/schema.py,sha256=ZSzeUjpqigLvE4tFKB7y4AaZG-W5N5Z9wMH-F-vjMBU,4616
21
21
  polyapi/server.py,sha256=YXWxhYBx-hluwDQ8Jvfpy2s8ogz0GsNTMcZVNcP5ca8,2147
22
22
  polyapi/sync.py,sha256=PGdC0feBBjEVrF3d9EluW_OAxbWuzSrfh84czma8kWg,6476
23
23
  polyapi/typedefs.py,sha256=MGDwWaijLNqokXF9UCHGAP-yKixOzztrH4Lsj800AJs,2328
24
- polyapi/utils.py,sha256=hW_H207-aHB22UFFYtUjxZvW13TUXTR786-3LaL4OLc,10351
24
+ polyapi/utils.py,sha256=ehRD5o0fGJPtXEEeX8rXPmbzPzVLfLimHC2lzBkNUGs,11672
25
25
  polyapi/variables.py,sha256=j7WWrGLr2O5SkWGxnsusnnfl25kVL3b6SQYcVGEoC8c,4277
26
26
  polyapi/webhook.py,sha256=LWv28c2MLz_OKBI_Nn7WR4C-gs1SWgbdXsoxIIf-9UI,4886
27
- polyapi_python-0.3.7.dev2.dist-info/licenses/LICENSE,sha256=6b_I7aPVp8JXhqQwdw7_B84Ca0S4JGjHj0sr_1VOdB4,1068
28
- polyapi_python-0.3.7.dev2.dist-info/METADATA,sha256=rx9IKDlG3qPk8IGDAn3m7Z2BCL5gS43uTtQEYnUHPm8,5782
29
- polyapi_python-0.3.7.dev2.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
30
- polyapi_python-0.3.7.dev2.dist-info/top_level.txt,sha256=CEFllOnzowci_50RYJac-M54KD2IdAptFsayVVF_f04,8
31
- polyapi_python-0.3.7.dev2.dist-info/RECORD,,
27
+ polyapi_python-0.3.7.dev3.dist-info/licenses/LICENSE,sha256=6b_I7aPVp8JXhqQwdw7_B84Ca0S4JGjHj0sr_1VOdB4,1068
28
+ polyapi_python-0.3.7.dev3.dist-info/METADATA,sha256=qoFbCNu-kAltunwz4xR5hYvBAm9ErCMaLPHKvg5TjUQ,5782
29
+ polyapi_python-0.3.7.dev3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
+ polyapi_python-0.3.7.dev3.dist-info/top_level.txt,sha256=CEFllOnzowci_50RYJac-M54KD2IdAptFsayVVF_f04,8
31
+ polyapi_python-0.3.7.dev3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5