polyapi-python 0.3.18.dev7__py3-none-any.whl → 0.3.19__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/cli.py +8 -4
- polyapi/config.py +0 -3
- polyapi/execute.py +8 -10
- polyapi/function_cli.py +16 -7
- polyapi/generate.py +30 -19
- polyapi/http_client.py +1 -1
- polyapi/parser.py +29 -8
- polyapi/poly_tables.py +4 -4
- polyapi/schema.py +25 -11
- polyapi/utils.py +10 -7
- {polyapi_python-0.3.18.dev7.dist-info → polyapi_python-0.3.19.dist-info}/METADATA +6 -6
- {polyapi_python-0.3.18.dev7.dist-info → polyapi_python-0.3.19.dist-info}/RECORD +15 -15
- {polyapi_python-0.3.18.dev7.dist-info → polyapi_python-0.3.19.dist-info}/WHEEL +1 -1
- {polyapi_python-0.3.18.dev7.dist-info → polyapi_python-0.3.19.dist-info}/licenses/LICENSE +0 -0
- {polyapi_python-0.3.18.dev7.dist-info → polyapi_python-0.3.19.dist-info}/top_level.txt +0 -0
polyapi/cli.py
CHANGED
|
@@ -45,8 +45,8 @@ def execute_from_cli():
|
|
|
45
45
|
|
|
46
46
|
# Add global --version/-v flag
|
|
47
47
|
parser.add_argument('-v', '--version', action='version',
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
version=_get_version_string(),
|
|
49
|
+
help="Show version information")
|
|
50
50
|
|
|
51
51
|
subparsers = parser.add_subparsers(help="Available commands")
|
|
52
52
|
|
|
@@ -95,7 +95,7 @@ def execute_from_cli():
|
|
|
95
95
|
# overwrite all cached values with the values passed in from the command line
|
|
96
96
|
final_contexts = contexts
|
|
97
97
|
final_names = names
|
|
98
|
-
final_ids = ids
|
|
98
|
+
# final_ids = ids
|
|
99
99
|
final_no_types = no_types
|
|
100
100
|
|
|
101
101
|
# cache the values used for this explicit generate command
|
|
@@ -127,6 +127,7 @@ def execute_from_cli():
|
|
|
127
127
|
fn_add_parser.add_argument("--logs", choices=["enabled", "disabled"], default=None, help="Enable or disable logs for the function.")
|
|
128
128
|
fn_add_parser.add_argument("--execution-api-key", required=False, default="", help="API key for execution (for server functions only).")
|
|
129
129
|
fn_add_parser.add_argument("--skip-generate", action="store_true", help="Skip running generate after function add command, especially useful if you are deploying a bunch of functions at once. Run generate manually at the end!")
|
|
130
|
+
fn_add_parser.add_argument("--image", type=str, default=None, help="Docker image to use when deploying a server function")
|
|
130
131
|
fn_add_parser.add_argument("--generate-contexts", type=str, help="Server function only – only include certain contexts to speed up function execution")
|
|
131
132
|
fn_add_parser.add_argument("--visibility", type=str, default="environment", help="Specifies the visibility of a function. Options: PUBLIC, TENANT, ENVIRONMENT. Case insensitive")
|
|
132
133
|
|
|
@@ -141,6 +142,8 @@ def execute_from_cli():
|
|
|
141
142
|
err = "You must specify `--server` or `--client`."
|
|
142
143
|
elif logs_enabled and not args.server:
|
|
143
144
|
err = "Option `logs` is only for server functions (--server)."
|
|
145
|
+
elif args.image and not args.server:
|
|
146
|
+
err = "Option `image` is only for server functions (--server)."
|
|
144
147
|
elif args.generate_contexts and not args.server:
|
|
145
148
|
err = "Option `generate-contexts` is only for server functions (--server)."
|
|
146
149
|
elif visibility not in ["PUBLIC", "TENANT", "ENVIRONMENT"]:
|
|
@@ -162,7 +165,8 @@ def execute_from_cli():
|
|
|
162
165
|
generate=not args.skip_generate,
|
|
163
166
|
execution_api_key=args.execution_api_key,
|
|
164
167
|
generate_contexts=args.generate_contexts,
|
|
165
|
-
visibility=visibility
|
|
168
|
+
visibility=visibility,
|
|
169
|
+
image=args.image,
|
|
166
170
|
)
|
|
167
171
|
|
|
168
172
|
fn_add_parser.set_defaults(command=add_function)
|
polyapi/config.py
CHANGED
|
@@ -135,7 +135,6 @@ def clear_config():
|
|
|
135
135
|
|
|
136
136
|
def get_mtls_config() -> Tuple[bool, str | None, str | None, str | None]:
|
|
137
137
|
"""Return MTLS configuration settings"""
|
|
138
|
-
global MTLS_CERT_PATH, MTLS_KEY_PATH, MTLS_CA_PATH
|
|
139
138
|
if MTLS_CERT_PATH is None or MTLS_KEY_PATH is None or MTLS_CA_PATH is None:
|
|
140
139
|
# Force a config read if values aren't cached
|
|
141
140
|
get_api_key_and_url()
|
|
@@ -144,7 +143,6 @@ def get_mtls_config() -> Tuple[bool, str | None, str | None, str | None]:
|
|
|
144
143
|
|
|
145
144
|
def get_direct_execute_config() -> bool:
|
|
146
145
|
"""Return whether direct execute is enabled"""
|
|
147
|
-
global API_FUNCTION_DIRECT_EXECUTE
|
|
148
146
|
if API_FUNCTION_DIRECT_EXECUTE is None:
|
|
149
147
|
# Force a config read if value isn't cached
|
|
150
148
|
get_api_key_and_url()
|
|
@@ -153,7 +151,6 @@ def get_direct_execute_config() -> bool:
|
|
|
153
151
|
|
|
154
152
|
def get_cached_generate_args() -> Tuple[list | None, list | None, list | None, bool]:
|
|
155
153
|
"""Return cached generate command arguments"""
|
|
156
|
-
global LAST_GENERATE_CONTEXTS, LAST_GENERATE_NAMES, LAST_GENERATE_IDS, LAST_GENERATE_NO_TYPES
|
|
157
154
|
if LAST_GENERATE_CONTEXTS is None and LAST_GENERATE_NAMES is None and LAST_GENERATE_IDS is None and LAST_GENERATE_NO_TYPES is None:
|
|
158
155
|
# Force a config read if values aren't cached
|
|
159
156
|
get_api_key_and_url()
|
polyapi/execute.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import httpx
|
|
2
2
|
import os
|
|
3
3
|
import logging
|
|
4
|
+
from typing import Any
|
|
4
5
|
from polyapi.config import get_api_key_and_url, get_mtls_config
|
|
5
6
|
from polyapi.exceptions import PolyApiException
|
|
6
7
|
from polyapi import http_client
|
|
@@ -49,14 +50,10 @@ def _sync_direct_execute(function_type, function_id, data) -> httpx.Response:
|
|
|
49
50
|
# Direct-execute hits URL that may need custom TLS
|
|
50
51
|
# settings (mTLS certs or disabled verification). httpx Client.request()
|
|
51
52
|
# doesn't accept per-request transport kwargs, so use one-off calls.
|
|
52
|
-
if has_mtls:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
verify=ca_path,
|
|
57
|
-
timeout=None,
|
|
58
|
-
**request_params
|
|
59
|
-
)
|
|
53
|
+
if has_mtls and cert_path and key_path:
|
|
54
|
+
verify_value: bool | str = ca_path if ca_path else True
|
|
55
|
+
with httpx.Client(cert=(cert_path, key_path), verify=verify_value, timeout=None) as client:
|
|
56
|
+
resp = client.request(url=endpoint_info_data["url"], **request_params)
|
|
60
57
|
else:
|
|
61
58
|
resp = httpx.request(
|
|
62
59
|
url=endpoint_info_data["url"],
|
|
@@ -83,9 +80,10 @@ async def _async_direct_execute(function_type, function_id, data) -> httpx.Respo
|
|
|
83
80
|
has_mtls, cert_path, key_path, ca_path = get_mtls_config()
|
|
84
81
|
|
|
85
82
|
# One-off async client for custom TLS settings on external URLs.
|
|
86
|
-
if has_mtls:
|
|
83
|
+
if has_mtls and cert_path and key_path:
|
|
84
|
+
verify_value: bool | str = ca_path if ca_path else True
|
|
87
85
|
async with httpx.AsyncClient(
|
|
88
|
-
cert=(cert_path, key_path), verify=
|
|
86
|
+
cert=(cert_path, key_path), verify=verify_value, timeout=None
|
|
89
87
|
) as client:
|
|
90
88
|
resp = await client.request(
|
|
91
89
|
url=endpoint_info_data["url"], **request_params
|
polyapi/function_cli.py
CHANGED
|
@@ -25,8 +25,9 @@ def function_add_or_update(
|
|
|
25
25
|
logs_enabled: Optional[bool],
|
|
26
26
|
generate_contexts: Optional[str],
|
|
27
27
|
visibility: Optional[str],
|
|
28
|
+
image: Optional[str],
|
|
28
29
|
generate: bool = True,
|
|
29
|
-
execution_api_key: str = ""
|
|
30
|
+
execution_api_key: str = "",
|
|
30
31
|
):
|
|
31
32
|
verb = "Updating" if _func_already_exists(context, name) else "Adding"
|
|
32
33
|
ftype = "server" if server else "client"
|
|
@@ -39,11 +40,12 @@ def function_add_or_update(
|
|
|
39
40
|
parsed = parse_function_code(code, name, context)
|
|
40
41
|
return_type = parsed["types"]["returns"]["type"]
|
|
41
42
|
|
|
43
|
+
if "dependencies" not in parsed:
|
|
44
|
+
parsed["dependencies"] = []
|
|
45
|
+
|
|
42
46
|
if not return_type:
|
|
43
47
|
print_red("ERROR")
|
|
44
|
-
print(
|
|
45
|
-
f"Function {name} not found as top-level function in {name}"
|
|
46
|
-
)
|
|
48
|
+
print(f"Function {name} not found as top-level function in {name}")
|
|
47
49
|
sys.exit(1)
|
|
48
50
|
|
|
49
51
|
if logs_enabled is None:
|
|
@@ -57,13 +59,19 @@ def function_add_or_update(
|
|
|
57
59
|
"language": "python",
|
|
58
60
|
"visibility": visibility or "ENVIRONMENT",
|
|
59
61
|
"returnType": get_jsonschema_type(return_type),
|
|
60
|
-
"arguments": [
|
|
61
|
-
|
|
62
|
+
"arguments": [
|
|
63
|
+
{**p, "key": p["name"], "type": get_jsonschema_type(p["type"])}
|
|
64
|
+
for p in parsed["types"]["params"]
|
|
65
|
+
],
|
|
66
|
+
"logsEnabled": logs_enabled
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
if generate_contexts:
|
|
65
70
|
data["generateContexts"] = generate_contexts.split(",")
|
|
66
71
|
|
|
72
|
+
if server and image:
|
|
73
|
+
data["image"] = image
|
|
74
|
+
|
|
67
75
|
if server and parsed["dependencies"]:
|
|
68
76
|
print_yellow(
|
|
69
77
|
"\nPlease note that deploying your functions will take a few minutes because it makes use of libraries other than polyapi."
|
|
@@ -94,6 +102,7 @@ def function_add_or_update(
|
|
|
94
102
|
if generate:
|
|
95
103
|
# Use cached generate arguments when regenerating after function deployment
|
|
96
104
|
from polyapi.generate import generate_from_cache
|
|
105
|
+
|
|
97
106
|
generate_from_cache()
|
|
98
107
|
else:
|
|
99
108
|
print("Error adding function.")
|
|
@@ -126,4 +135,4 @@ def spec_delete(function_type: str, function_id: str):
|
|
|
126
135
|
sys.exit(1)
|
|
127
136
|
headers = get_auth_headers(api_key)
|
|
128
137
|
resp = http_client.delete(url, headers=headers)
|
|
129
|
-
return resp
|
|
138
|
+
return resp
|
polyapi/generate.py
CHANGED
|
@@ -241,6 +241,9 @@ def remove_old_library():
|
|
|
241
241
|
|
|
242
242
|
def create_empty_schemas_module():
|
|
243
243
|
"""Create an empty schemas module for no-types mode so user code can still import from polyapi.schemas"""
|
|
244
|
+
import importlib
|
|
245
|
+
import sys
|
|
246
|
+
|
|
244
247
|
currdir = os.path.dirname(os.path.abspath(__file__))
|
|
245
248
|
schemas_path = os.path.join(currdir, "schemas")
|
|
246
249
|
|
|
@@ -251,50 +254,57 @@ def create_empty_schemas_module():
|
|
|
251
254
|
# Create an __init__.py file with dynamic schema resolution
|
|
252
255
|
init_path = os.path.join(schemas_path, "__init__.py")
|
|
253
256
|
with open(init_path, "w") as f:
|
|
254
|
-
f.write('''"""Empty schemas module for no-types mode"""
|
|
257
|
+
f.write('''"""Empty schemas module for no-types mode."""
|
|
255
258
|
from typing import Any, Dict
|
|
256
259
|
|
|
257
260
|
class _GenericSchema(Dict[str, Any]):
|
|
258
261
|
"""Generic schema type that acts like a Dict for no-types mode"""
|
|
262
|
+
|
|
259
263
|
def __init__(self, *args, **kwargs):
|
|
260
264
|
super().__init__(*args, **kwargs)
|
|
261
265
|
|
|
262
266
|
class _SchemaModule:
|
|
263
|
-
"""
|
|
264
|
-
|
|
267
|
+
"""Compatibility shim retained for tests that assert this symbol exists."""
|
|
268
|
+
|
|
265
269
|
def __getattr__(self, name: str):
|
|
266
|
-
# For callable access (like schemas.Response()), return
|
|
267
|
-
# For further attribute access (like schemas.random.random2), return self to allow nesting
|
|
270
|
+
# For callable access (like schemas.Response()), return nested access.
|
|
268
271
|
return _NestedSchemaAccess()
|
|
269
|
-
|
|
272
|
+
|
|
270
273
|
def __call__(self, *args, **kwargs):
|
|
271
|
-
# If someone tries to call the module itself, return a generic schema
|
|
272
274
|
return _GenericSchema(*args, **kwargs)
|
|
273
|
-
|
|
275
|
+
|
|
274
276
|
def __dir__(self):
|
|
275
|
-
|
|
276
|
-
return ['Response', 'Request', 'Error', 'Data', 'Result']
|
|
277
|
+
return ["Response", "Request", "Error", "Data", "Result"]
|
|
277
278
|
|
|
278
279
|
class _NestedSchemaAccess:
|
|
279
280
|
"""Handles nested attribute access and final callable resolution"""
|
|
280
|
-
|
|
281
|
+
|
|
281
282
|
def __getattr__(self, name: str):
|
|
282
|
-
# Continue allowing nested access
|
|
283
283
|
return _NestedSchemaAccess()
|
|
284
|
-
|
|
284
|
+
|
|
285
285
|
def __call__(self, *args, **kwargs):
|
|
286
|
-
# When finally called, return a generic schema instance
|
|
287
286
|
return _GenericSchema(*args, **kwargs)
|
|
288
|
-
|
|
287
|
+
|
|
289
288
|
def __class_getitem__(cls, item):
|
|
290
|
-
# Support type annotations like schemas.Response[str]
|
|
291
289
|
return _GenericSchema
|
|
292
290
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
291
|
+
def __getattr__(name: str):
|
|
292
|
+
if name.startswith("__") or name == "load_tests":
|
|
293
|
+
raise AttributeError(name)
|
|
294
|
+
return _NestedSchemaAccess()
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def __dir__():
|
|
298
|
+
return ["Response", "Request", "Error", "Data", "Result"]
|
|
296
299
|
''')
|
|
297
300
|
|
|
301
|
+
# Ensure subsequent imports pick up the newly generated no-types module.
|
|
302
|
+
sys.modules.pop("polyapi.schemas", None)
|
|
303
|
+
parent_module = sys.modules.get("polyapi")
|
|
304
|
+
if parent_module is not None and hasattr(parent_module, "schemas"):
|
|
305
|
+
delattr(parent_module, "schemas")
|
|
306
|
+
importlib.invalidate_caches()
|
|
307
|
+
|
|
298
308
|
|
|
299
309
|
def _generate_client_id() -> None:
|
|
300
310
|
full_path = os.path.dirname(os.path.abspath(__file__))
|
|
@@ -411,6 +421,7 @@ def clear() -> None:
|
|
|
411
421
|
def render_spec(spec: SpecificationDto) -> Tuple[str, str]:
|
|
412
422
|
function_type = spec["type"]
|
|
413
423
|
raw_description = spec.get("description", "")
|
|
424
|
+
|
|
414
425
|
def _flatten_description(value: Any) -> List[str]:
|
|
415
426
|
if value is None:
|
|
416
427
|
return []
|
polyapi/http_client.py
CHANGED
polyapi/parser.py
CHANGED
|
@@ -22,9 +22,27 @@ BASE_REQUIREMENTS = {
|
|
|
22
22
|
"typing_extensions",
|
|
23
23
|
"jsonschema-gentypes",
|
|
24
24
|
"pydantic",
|
|
25
|
+
|
|
25
26
|
"cloudevents",
|
|
26
27
|
}
|
|
27
|
-
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _safe_stdlib_symbols() -> List[str]:
|
|
31
|
+
major = sys.version_info[0]
|
|
32
|
+
minor = sys.version_info[1]
|
|
33
|
+
|
|
34
|
+
# `stdlib_list` may lag behind the newest Python minor; fall back to the
|
|
35
|
+
# nearest supported version so importing this module never crashes.
|
|
36
|
+
while minor >= 0:
|
|
37
|
+
try:
|
|
38
|
+
return stdlib_list(f"{major}.{minor}")
|
|
39
|
+
except ValueError:
|
|
40
|
+
minor -= 1
|
|
41
|
+
|
|
42
|
+
return []
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
all_stdlib_symbols = _safe_stdlib_symbols()
|
|
28
46
|
BASE_REQUIREMENTS.update(
|
|
29
47
|
all_stdlib_symbols
|
|
30
48
|
) # dont need to pip install stuff in the python standard library
|
|
@@ -397,13 +415,16 @@ def parse_function_code(code: str, name: Optional[str] = "", context: Optional[s
|
|
|
397
415
|
deployable["docStartIndex"] = start_offset
|
|
398
416
|
deployable["docEndIndex"] = end_offset
|
|
399
417
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
418
|
+
docstring = ast.get_docstring(node)
|
|
419
|
+
|
|
420
|
+
# Handle case where there is no doc string.
|
|
421
|
+
# Also handle case where docstring exists but is empty.
|
|
422
|
+
if docstring is None or (
|
|
423
|
+
not docstring
|
|
424
|
+
and '"""' not in self._lines[start_lineno]
|
|
425
|
+
and "'''" not in self._lines[start_lineno]
|
|
426
|
+
):
|
|
427
|
+
return None
|
|
407
428
|
|
|
408
429
|
docstring = cast(str, docstring)
|
|
409
430
|
|
polyapi/poly_tables.py
CHANGED
|
@@ -85,7 +85,7 @@ def execute_query(table_id, method, query):
|
|
|
85
85
|
|
|
86
86
|
auth_key = polyCustom.get("executionApiKey") or api_key
|
|
87
87
|
url = f"{base_url.rstrip('/')}/tables/{table_id}/{method}?clientId={client_id}"
|
|
88
|
-
headers = {"x-poly-execution-id": polyCustom.get("executionId")}
|
|
88
|
+
headers = {"x-poly-execution-id": polyCustom.get("executionId") or ""}
|
|
89
89
|
if auth_key:
|
|
90
90
|
headers["Authorization"] = f"Bearer {auth_key}"
|
|
91
91
|
response = http_client.post(url, json=query, headers=headers)
|
|
@@ -130,11 +130,11 @@ def _transform_keys(obj: Any) -> Any:
|
|
|
130
130
|
|
|
131
131
|
|
|
132
132
|
def transform_query(query: dict) -> dict:
|
|
133
|
-
if query
|
|
133
|
+
if query.get("where") or query.get("order_by"):
|
|
134
134
|
return {
|
|
135
135
|
**query,
|
|
136
|
-
"where": _transform_keys(query["where"]) if query
|
|
137
|
-
"orderBy": query["order_by"] if query
|
|
136
|
+
"where": _transform_keys(query["where"]) if query.get("where") else None,
|
|
137
|
+
"orderBy": query["order_by"] if query.get("order_by") else None,
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
return query
|
polyapi/schema.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"""
|
|
3
3
|
import logging
|
|
4
4
|
import contextlib
|
|
5
|
+
import os
|
|
5
6
|
import re
|
|
6
7
|
from typing import Dict
|
|
7
8
|
from jsonschema_gentypes.cli import process_config
|
|
@@ -79,9 +80,10 @@ def generate_schema_types(input_data: Dict, root=None):
|
|
|
79
80
|
"""takes in a Dict representing a schema as input then appends the resulting python code to the output file"""
|
|
80
81
|
_cleanup_input_for_gentypes(input_data)
|
|
81
82
|
tmp_input = _temp_store_input_data(input_data)
|
|
82
|
-
|
|
83
|
+
with tempfile.NamedTemporaryFile(
|
|
83
84
|
mode="w", delete=False, prefix="polyapi_", suffix=".py"
|
|
84
|
-
)
|
|
85
|
+
) as tmp_output_file:
|
|
86
|
+
tmp_output = tmp_output_file.name
|
|
85
87
|
|
|
86
88
|
config: configuration.Configuration = {
|
|
87
89
|
"python_version": None, # type: ignore
|
|
@@ -95,17 +97,23 @@ def generate_schema_types(input_data: Dict, root=None):
|
|
|
95
97
|
],
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
with open(tmp_output, encoding='utf-8') as f:
|
|
104
|
-
output = f.read()
|
|
100
|
+
try:
|
|
101
|
+
# jsonschema_gentypes prints source to stdout
|
|
102
|
+
# no option to surpress so we do this
|
|
103
|
+
with contextlib.redirect_stdout(None):
|
|
104
|
+
process_config(config, [tmp_input])
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
with open(tmp_output, encoding='utf-8') as f:
|
|
107
|
+
output = f.read()
|
|
107
108
|
|
|
108
|
-
|
|
109
|
+
output = clean_malformed_examples(output)
|
|
110
|
+
return output
|
|
111
|
+
finally:
|
|
112
|
+
for temp_path in (tmp_input, tmp_output):
|
|
113
|
+
try:
|
|
114
|
+
os.unlink(temp_path)
|
|
115
|
+
except OSError:
|
|
116
|
+
pass
|
|
109
117
|
|
|
110
118
|
|
|
111
119
|
# Matches commented example headers emitted by jsonschema-gentypes before a raw
|
|
@@ -122,6 +130,10 @@ INVALID_ESCAPE_PATTERNS = [
|
|
|
122
130
|
(re.compile(r'\\([^nrtbfav"\'\\])', re.DOTALL), r'\\\\\1'),
|
|
123
131
|
]
|
|
124
132
|
|
|
133
|
+
# jsonschema_gentypes may emit raw triple-quoted docstrings (r"""...""").
|
|
134
|
+
# Normalize these to plain docstrings to keep generated output stable.
|
|
135
|
+
RAW_TRIPLE_QUOTE_PATTERN = re.compile(r'(^\s*)r(["\']{3})', re.MULTILINE)
|
|
136
|
+
|
|
125
137
|
|
|
126
138
|
def clean_malformed_examples(example: str) -> str:
|
|
127
139
|
""" there is a bug in the `jsonschmea_gentypes` library where if an example from a jsonchema is an object,
|
|
@@ -149,6 +161,8 @@ def clean_malformed_examples(example: str) -> str:
|
|
|
149
161
|
# Fix invalid escape sequences in docstrings
|
|
150
162
|
for pattern, replacement in INVALID_ESCAPE_PATTERNS:
|
|
151
163
|
cleaned_example = pattern.sub(replacement, cleaned_example)
|
|
164
|
+
|
|
165
|
+
cleaned_example = RAW_TRIPLE_QUOTE_PATTERN.sub(r'\1\2', cleaned_example)
|
|
152
166
|
|
|
153
167
|
return cleaned_example
|
|
154
168
|
|
polyapi/utils.py
CHANGED
|
@@ -2,7 +2,7 @@ import keyword
|
|
|
2
2
|
import re
|
|
3
3
|
import os
|
|
4
4
|
from urllib.parse import urlparse
|
|
5
|
-
from typing import Tuple, List, Optional
|
|
5
|
+
from typing import Tuple, List, Optional, cast
|
|
6
6
|
from colorama import Fore, Style
|
|
7
7
|
from polyapi.constants import BASIC_PYTHON_TYPES
|
|
8
8
|
from polyapi.typedefs import PropertySpecification, PropertyType
|
|
@@ -115,9 +115,9 @@ def to_type_module_alias(function_name: str) -> str:
|
|
|
115
115
|
|
|
116
116
|
def add_type_import_path(function_name: str, arg: str) -> str:
|
|
117
117
|
"""if not basic type, coerce to camelCase and add the import path"""
|
|
118
|
-
# from now, we start qualifying non-basic types :))
|
|
118
|
+
# from now, we start qualifying non-basic types :))
|
|
119
119
|
# e.g. Callable[[EmailAddress, Dict, Dict, Dict], None]
|
|
120
|
-
|
|
120
|
+
# becomes Callable[[Set_profile_email.EmailAddress, Dict, Dict, Dict], None]
|
|
121
121
|
arg = normalize_cross_language_type(arg)
|
|
122
122
|
|
|
123
123
|
if "|" in arg:
|
|
@@ -158,7 +158,7 @@ def add_type_import_path(function_name: str, arg: str) -> str:
|
|
|
158
158
|
return f"{type_module_alias}.{camelCase(arg)}"
|
|
159
159
|
|
|
160
160
|
|
|
161
|
-
def get_type_and_def(
|
|
161
|
+
def get_type_and_def( # noqa: C901
|
|
162
162
|
type_spec: PropertyType, title_fallback: str = ""
|
|
163
163
|
) -> Tuple[str, str]:
|
|
164
164
|
""" returns type and type definition for a given PropertyType
|
|
@@ -190,7 +190,7 @@ def get_type_and_def(
|
|
|
190
190
|
return primitive, ""
|
|
191
191
|
return map_primitive_types(primitive), ""
|
|
192
192
|
elif type_spec["kind"] == "array":
|
|
193
|
-
if type_spec
|
|
193
|
+
if "items" in type_spec and isinstance(type_spec["items"], dict):
|
|
194
194
|
items = type_spec["items"]
|
|
195
195
|
if items.get("$ref"):
|
|
196
196
|
# For no-types mode, avoid complex schema generation
|
|
@@ -208,9 +208,12 @@ def get_type_and_def(
|
|
|
208
208
|
elif type_spec["kind"] == "void":
|
|
209
209
|
return "None", ""
|
|
210
210
|
elif type_spec["kind"] == "object":
|
|
211
|
-
if type_spec
|
|
211
|
+
if "schema" in type_spec and isinstance(type_spec["schema"], dict):
|
|
212
212
|
schema = type_spec["schema"]
|
|
213
|
-
title = schema.get("title", schema.get("name", title_fallback))
|
|
213
|
+
title = cast(str, schema.get("title", schema.get("name", title_fallback)))
|
|
214
|
+
if not isinstance(title, str):
|
|
215
|
+
raise ValueError("Title must be a string, this should never happen due to the fallback, but got: {}".format(title))
|
|
216
|
+
|
|
214
217
|
if title and schema.get("type") == "array":
|
|
215
218
|
# TODO fix me
|
|
216
219
|
# we don't use ReturnType as name for the list type here, we use _ReturnTypeItem
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: polyapi-python
|
|
3
|
-
Version: 0.3.
|
|
4
|
-
Summary: The Python
|
|
3
|
+
Version: 0.3.19
|
|
4
|
+
Summary: The Python SDK for the integration platform PolyAPI
|
|
5
5
|
Author-email: PolyAPI <support@polyapi.io>
|
|
6
6
|
License: MIT License
|
|
7
7
|
|
|
@@ -29,9 +29,9 @@ Requires-Python: >=3.10
|
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
License-File: LICENSE
|
|
31
31
|
Requires-Dist: requests==2.32.3
|
|
32
|
-
Requires-Dist: typing_extensions==4.
|
|
33
|
-
Requires-Dist: jsonschema-gentypes==2.
|
|
34
|
-
Requires-Dist: pydantic==2.
|
|
32
|
+
Requires-Dist: typing_extensions==4.15.0
|
|
33
|
+
Requires-Dist: jsonschema-gentypes==2.13.0
|
|
34
|
+
Requires-Dist: pydantic==2.13.4
|
|
35
35
|
Requires-Dist: stdlib_list==0.11.1
|
|
36
36
|
Requires-Dist: colorama==0.4.4
|
|
37
37
|
Requires-Dist: python-socketio[asyncio_client]==5.11.1
|
|
@@ -99,7 +99,7 @@ print(poly.polyapi.function.api.list(my_server, my_api_key))
|
|
|
99
99
|
To add a new server function, please follow the quickstart. Then you can add a server function like so:
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
|
-
python -m polyapi
|
|
102
|
+
python -m polyapi function add <function_name> foo.py --server --context mycontext --description mydesc
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
The code in `foo.py` should contain a single defined function named the same as your `<function_name>` variable.
|
|
@@ -2,33 +2,33 @@ polyapi/__init__.py,sha256=vV1N9xtIMtpi4o-2jAI3775LkzRbC9JUo87MAnW9zgo,5201
|
|
|
2
2
|
polyapi/__main__.py,sha256=V4zhAh_YGxno5f_KSrlkELxcuDh9bR3WSd0n-2r-qQQ,93
|
|
3
3
|
polyapi/api.py,sha256=CzqX99GekhQJVqAAWlL03hUenIi_5iTln9cWgQzz6Ko,2784
|
|
4
4
|
polyapi/auth.py,sha256=l0pGAoYvEWqa2TMsrM9GS8imXu3GsWvpn4eFPy1vksI,6195
|
|
5
|
-
polyapi/cli.py,sha256=
|
|
5
|
+
polyapi/cli.py,sha256=V3586ElV1927u2AhQZAo_4UYKoJPqE8lz6b3_gJfMj0,11918
|
|
6
6
|
polyapi/cli_constants.py,sha256=eZnAqAo8HLxHH3cdAoGWbrsALAtEWJFXRdSS2lvQvzE,144
|
|
7
7
|
polyapi/client.py,sha256=DW6ljG_xCwAo2yz23A9QfLooE6ZUDvSpdA4e_dCQjiQ,1418
|
|
8
|
-
polyapi/config.py,sha256=
|
|
8
|
+
polyapi/config.py,sha256=HArQg9BtZQxUCbi4OHLqvkwvjnnnDTJsuJW7niLo_5k,7328
|
|
9
9
|
polyapi/constants.py,sha256=i3j0-ptd7XEs6ovnYegEhiP0BRs1JSKAq1oE5CWbDVo,707
|
|
10
10
|
polyapi/deployables.py,sha256=njmqTU0W7d3JdDWhjjjr2UOghEv4bTKUN-3ztJvcC7o,12233
|
|
11
11
|
polyapi/error_handler.py,sha256=I_e0iz6VM23FLVQWJljxs2NGcl_OODbi43OcbnqBlp8,2398
|
|
12
12
|
polyapi/exceptions.py,sha256=Zh7i7eCUhDuXEdUYjatkLFTeZkrx1BJ1P5ePgbJ9eIY,89
|
|
13
|
-
polyapi/execute.py,sha256=
|
|
14
|
-
polyapi/function_cli.py,sha256=
|
|
15
|
-
polyapi/generate.py,sha256=
|
|
16
|
-
polyapi/http_client.py,sha256=
|
|
17
|
-
polyapi/parser.py,sha256=
|
|
13
|
+
polyapi/execute.py,sha256=4jLkQely6cM7pNN9pYpGZTbEis-DfwENi5Fl07xSiig,8966
|
|
14
|
+
polyapi/function_cli.py,sha256=gJE72lRvuWnGl6CG9GpqL78TOnUzxVe4kJMvg0PQixw,4493
|
|
15
|
+
polyapi/generate.py,sha256=9B6DbrkBAPWpMVD92ifFYV2JaKNCduMQhqJ4Gm48qq0,22051
|
|
16
|
+
polyapi/http_client.py,sha256=TcoFsIvRcNB9wWJT4YePPWFFY6lX90cdqi7C4jKbqqk,2259
|
|
17
|
+
polyapi/parser.py,sha256=t3jJI6g5F7Qg_tirPOzSkQ02VLkSVhnqs9YGapyHx2k,21901
|
|
18
18
|
polyapi/poly_schemas.py,sha256=JGMQbELPFw4V_0nA0o71WJKFTdxjCGoKqJvIid_Y-a4,10759
|
|
19
|
-
polyapi/poly_tables.py,sha256=
|
|
19
|
+
polyapi/poly_tables.py,sha256=SlP7ZwqS3nIji2uGR2VJQH6IdddZ832rRItcmoYdIBc,20293
|
|
20
20
|
polyapi/prepare.py,sha256=DBlrQu_A0PqdaQ3KlMs-C6PkV3qncatkAiBlC7j2hnk,7441
|
|
21
21
|
polyapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
22
|
polyapi/rendered_spec.py,sha256=J2Num7MXnW_mKAe2MSVIL1coWopNYZ-6nNNwQRfseV0,2262
|
|
23
|
-
polyapi/schema.py,sha256=
|
|
23
|
+
polyapi/schema.py,sha256=SPO5GDvIE1BjRfJR89vJMEGCXERfEbhNXQm2A05UAOc,6959
|
|
24
24
|
polyapi/server.py,sha256=_14lP4_BmRYMBXwaqXVWmiSTHH3h7ecCHxSalM9UiSc,3049
|
|
25
25
|
polyapi/sync.py,sha256=t0Khh8B5GylxB7KWRP_xvkrvkuyVarSLCOvtmaz7lcc,6766
|
|
26
26
|
polyapi/typedefs.py,sha256=mfll-KrngOW0wzbvDNiIDaDkFMwbsT-MY5y5hzWj9RE,5642
|
|
27
|
-
polyapi/utils.py,sha256=
|
|
27
|
+
polyapi/utils.py,sha256=dIkuIlGNsg1USiXNNkUnOtLVzie4mxBCBlTc0YsaXao,15594
|
|
28
28
|
polyapi/variables.py,sha256=hfSDPGQK6YphNCa9MqE0W88WIFfqCQWgpBDWkExMq-A,7582
|
|
29
29
|
polyapi/webhook.py,sha256=0ceLwHNjNd2Yx_8MX5jOIxiQ5Lwhy2pe81ProAuKon0,5108
|
|
30
|
-
polyapi_python-0.3.
|
|
31
|
-
polyapi_python-0.3.
|
|
32
|
-
polyapi_python-0.3.
|
|
33
|
-
polyapi_python-0.3.
|
|
34
|
-
polyapi_python-0.3.
|
|
30
|
+
polyapi_python-0.3.19.dist-info/licenses/LICENSE,sha256=6b_I7aPVp8JXhqQwdw7_B84Ca0S4JGjHj0sr_1VOdB4,1068
|
|
31
|
+
polyapi_python-0.3.19.dist-info/METADATA,sha256=69EIA_IEpETapdFInvMckQBDdcQiIu5N9EXc529-ODI,5893
|
|
32
|
+
polyapi_python-0.3.19.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
33
|
+
polyapi_python-0.3.19.dist-info/top_level.txt,sha256=CEFllOnzowci_50RYJac-M54KD2IdAptFsayVVF_f04,8
|
|
34
|
+
polyapi_python-0.3.19.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|