polyapi-python 0.3.19.dev3__tar.gz → 0.3.20__tar.gz
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_python-0.3.19.dev3 → polyapi_python-0.3.20}/PKG-INFO +35 -2
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/README.md +34 -1
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/__init__.py +18 -7
- polyapi_python-0.3.20/polyapi/ai_function.py +96 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/generate.py +13 -2
- polyapi_python-0.3.20/polyapi/http_client.py +414 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/typedefs.py +1 -1
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/utils.py +22 -4
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi_python.egg-info/PKG-INFO +35 -2
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi_python.egg-info/SOURCES.txt +2 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/pyproject.toml +2 -2
- polyapi_python-0.3.20/tests/test_async_proof.py +1560 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_generate.py +214 -1
- polyapi_python-0.3.20/tests/test_generated_poly_boundary.py +47 -0
- polyapi_python-0.3.19.dev3/polyapi/http_client.py +0 -83
- polyapi_python-0.3.19.dev3/tests/test_async_proof.py +0 -498
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/LICENSE +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/__main__.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/api.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/auth.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/cli.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/cli_constants.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/client.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/config.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/constants.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/deployables.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/error_handler.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/exceptions.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/execute.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/function_cli.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/parser.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/poly_schemas.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/poly_tables.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/prepare.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/py.typed +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/rendered_spec.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/schema.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/server.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/sync.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/variables.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/webhook.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi_python.egg-info/dependency_links.txt +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi_python.egg-info/requires.txt +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi_python.egg-info/top_level.txt +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/setup.cfg +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_api.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_auth.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_cli.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_dependency_pins.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_deployables.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_parser.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_poly_custom.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_poly_schemas.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_rendered_spec.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_schema.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_server.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_tabi.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_utils.py +0 -0
- {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_variables.py +0 -0
|
@@ -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.20
|
|
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
|
|
|
@@ -84,6 +84,39 @@ POLY_API_KEY='your_key'
|
|
|
84
84
|
POLY_API_BASE_URL='your_server' # e.g. na1.polyapi.io
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
### HTTP Client Configuration
|
|
88
|
+
|
|
89
|
+
The SDK reuses one synchronous HTTP client per process and one asynchronous
|
|
90
|
+
client per event loop. New clients read these optional environment variables:
|
|
91
|
+
|
|
92
|
+
| Variable | Default | Description |
|
|
93
|
+
| --- | --- | --- |
|
|
94
|
+
| `POLY_HTTP_MAX_CONNECTIONS` | `200` | Maximum connections per client |
|
|
95
|
+
| `POLY_HTTP_MAX_KEEPALIVE_CONNECTIONS` | `none` | Maximum idle keep-alive connections per client |
|
|
96
|
+
| `POLY_HTTP_KEEPALIVE_EXPIRY` | `30` | Idle keep-alive expiry in seconds |
|
|
97
|
+
| `POLY_HTTP_CONNECT_TIMEOUT` | `none` | Connection timeout in seconds |
|
|
98
|
+
| `POLY_HTTP_READ_TIMEOUT` | `none` | Read timeout in seconds |
|
|
99
|
+
| `POLY_HTTP_WRITE_TIMEOUT` | `none` | Write timeout in seconds |
|
|
100
|
+
| `POLY_HTTP_POOL_TIMEOUT` | `none` | Connection-pool timeout in seconds |
|
|
101
|
+
| `POLY_HTTP_CLOSE_TIMEOUT` | `5` | Deadline in seconds for closing a client's pool |
|
|
102
|
+
|
|
103
|
+
An empty value, `none`, or `null` means unbounded. Limits are per client, so
|
|
104
|
+
processes with multiple event loops can create one configured pool per loop.
|
|
105
|
+
|
|
106
|
+
`POLY_HTTP_CLOSE_TIMEOUT` is read at shutdown rather than at client creation.
|
|
107
|
+
It bounds pool teardown so a single unresponsive socket cannot stall shutdown:
|
|
108
|
+
if the deadline passes, the SDK logs a warning, abandons the remaining
|
|
109
|
+
connections to the operating system, and retires the client so it is never
|
|
110
|
+
reused. Set it to `none` to wait indefinitely.
|
|
111
|
+
|
|
112
|
+
Call `polyapi.http_client.close()` during synchronous shutdown or await
|
|
113
|
+
`polyapi.http_client.close_async()` on each owning event loop during
|
|
114
|
+
asynchronous shutdown. The shutdown functions wait for active SDK requests
|
|
115
|
+
before closing their client. Clients on standard asyncio loops are closed
|
|
116
|
+
automatically when the loop closes. If a custom event-loop implementation
|
|
117
|
+
reports that its close hook could not be installed, explicitly await
|
|
118
|
+
`close_async()` before closing that loop.
|
|
119
|
+
|
|
87
120
|
### 3. Test
|
|
88
121
|
|
|
89
122
|
That's it! Now open up a test file and you can run some code like so:
|
|
@@ -43,6 +43,39 @@ POLY_API_KEY='your_key'
|
|
|
43
43
|
POLY_API_BASE_URL='your_server' # e.g. na1.polyapi.io
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
### HTTP Client Configuration
|
|
47
|
+
|
|
48
|
+
The SDK reuses one synchronous HTTP client per process and one asynchronous
|
|
49
|
+
client per event loop. New clients read these optional environment variables:
|
|
50
|
+
|
|
51
|
+
| Variable | Default | Description |
|
|
52
|
+
| --- | --- | --- |
|
|
53
|
+
| `POLY_HTTP_MAX_CONNECTIONS` | `200` | Maximum connections per client |
|
|
54
|
+
| `POLY_HTTP_MAX_KEEPALIVE_CONNECTIONS` | `none` | Maximum idle keep-alive connections per client |
|
|
55
|
+
| `POLY_HTTP_KEEPALIVE_EXPIRY` | `30` | Idle keep-alive expiry in seconds |
|
|
56
|
+
| `POLY_HTTP_CONNECT_TIMEOUT` | `none` | Connection timeout in seconds |
|
|
57
|
+
| `POLY_HTTP_READ_TIMEOUT` | `none` | Read timeout in seconds |
|
|
58
|
+
| `POLY_HTTP_WRITE_TIMEOUT` | `none` | Write timeout in seconds |
|
|
59
|
+
| `POLY_HTTP_POOL_TIMEOUT` | `none` | Connection-pool timeout in seconds |
|
|
60
|
+
| `POLY_HTTP_CLOSE_TIMEOUT` | `5` | Deadline in seconds for closing a client's pool |
|
|
61
|
+
|
|
62
|
+
An empty value, `none`, or `null` means unbounded. Limits are per client, so
|
|
63
|
+
processes with multiple event loops can create one configured pool per loop.
|
|
64
|
+
|
|
65
|
+
`POLY_HTTP_CLOSE_TIMEOUT` is read at shutdown rather than at client creation.
|
|
66
|
+
It bounds pool teardown so a single unresponsive socket cannot stall shutdown:
|
|
67
|
+
if the deadline passes, the SDK logs a warning, abandons the remaining
|
|
68
|
+
connections to the operating system, and retires the client so it is never
|
|
69
|
+
reused. Set it to `none` to wait indefinitely.
|
|
70
|
+
|
|
71
|
+
Call `polyapi.http_client.close()` during synchronous shutdown or await
|
|
72
|
+
`polyapi.http_client.close_async()` on each owning event loop during
|
|
73
|
+
asynchronous shutdown. The shutdown functions wait for active SDK requests
|
|
74
|
+
before closing their client. Clients on standard asyncio loops are closed
|
|
75
|
+
automatically when the loop closes. If a custom event-loop implementation
|
|
76
|
+
reports that its close hook could not be installed, explicitly await
|
|
77
|
+
`close_async()` before closing that loop.
|
|
78
|
+
|
|
46
79
|
### 3. Test
|
|
47
80
|
|
|
48
81
|
That's it! Now open up a test file and you can run some code like so:
|
|
@@ -156,4 +189,4 @@ Of course all this is in addition to the changes passing through normal unittest
|
|
|
156
189
|
## Support
|
|
157
190
|
|
|
158
191
|
If you run into any issues or want help getting started with this project, please contact support@polyapi.io
|
|
159
|
-
.
|
|
192
|
+
.
|
|
@@ -1,25 +1,36 @@
|
|
|
1
1
|
import copy
|
|
2
|
+
import importlib
|
|
2
3
|
import os
|
|
3
|
-
import sys
|
|
4
4
|
from contextvars import ContextVar, Token
|
|
5
5
|
from dataclasses import dataclass
|
|
6
|
-
from
|
|
6
|
+
from types import ModuleType
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, overload
|
|
7
8
|
|
|
8
9
|
import truststore
|
|
9
10
|
from typing_extensions import TypedDict
|
|
10
11
|
|
|
11
|
-
from .cli_constants import CLI_COMMANDS
|
|
12
|
-
|
|
13
12
|
truststore.inject_into_ssl()
|
|
14
13
|
|
|
15
14
|
__all__ = ["poly"]
|
|
16
15
|
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
poly: ModuleType
|
|
18
|
+
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
def _load_generated_poly() -> ModuleType:
|
|
19
21
|
currdir = os.path.dirname(os.path.abspath(__file__))
|
|
20
22
|
if not os.path.isdir(os.path.join(currdir, "poly")):
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
raise ModuleNotFoundError(
|
|
24
|
+
"No 'poly' found. Please run 'python3 -m polyapi generate' "
|
|
25
|
+
"to generate the 'poly' library for your tenant."
|
|
26
|
+
)
|
|
27
|
+
return importlib.import_module(".poly", __name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def __getattr__(name: str) -> ModuleType:
|
|
31
|
+
if name == "poly":
|
|
32
|
+
return _load_generated_poly()
|
|
33
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
23
34
|
|
|
24
35
|
|
|
25
36
|
class PolyCustomDict(TypedDict, total=False):
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Tuple, cast
|
|
2
|
+
|
|
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
|
+
|
|
6
|
+
AI_DEFS_TEMPLATE = """
|
|
7
|
+
from typing import List, Dict, Any, TypedDict, Callable
|
|
8
|
+
{args_def}
|
|
9
|
+
{return_type_def}
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
AI_FUNCTION_TEMPLATE = """
|
|
13
|
+
def {function_name}(
|
|
14
|
+
{args}
|
|
15
|
+
) -> {return_type_name}:
|
|
16
|
+
\"""{function_description}
|
|
17
|
+
|
|
18
|
+
Function ID: {function_id}
|
|
19
|
+
\"""
|
|
20
|
+
resp = execute("{function_type}", "{function_id}", {data})
|
|
21
|
+
try:
|
|
22
|
+
return {return_action}
|
|
23
|
+
except:
|
|
24
|
+
return resp.text # type: ignore # fallback for debugging
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
async def {function_name}_async(
|
|
28
|
+
{args}
|
|
29
|
+
) -> {return_type_name}:
|
|
30
|
+
\"""{function_description}
|
|
31
|
+
|
|
32
|
+
Function ID: {function_id}
|
|
33
|
+
\"""
|
|
34
|
+
resp = await execute_async("{function_type}", "{function_id}", {data})
|
|
35
|
+
try:
|
|
36
|
+
return {return_action}
|
|
37
|
+
except:
|
|
38
|
+
return resp.text # type: ignore # fallback for debugging
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def render_ai_function(
|
|
45
|
+
function_type: str,
|
|
46
|
+
function_name: str,
|
|
47
|
+
function_id: str,
|
|
48
|
+
function_description: str,
|
|
49
|
+
arguments: List[PropertySpecification],
|
|
50
|
+
return_type: Dict[str, Any],
|
|
51
|
+
) -> Tuple[str, str]:
|
|
52
|
+
arg_names = [a["name"] for a in arguments]
|
|
53
|
+
args, args_def = parse_arguments(function_name, arguments)
|
|
54
|
+
return_type_name, return_type_def = get_type_and_def(cast(PropertyType, return_type), "ReturnType")
|
|
55
|
+
|
|
56
|
+
if return_type_def and return_type_already_defined_in_args(return_type_name, args_def):
|
|
57
|
+
return_type_def = ""
|
|
58
|
+
|
|
59
|
+
data = "{" + ", ".join([f"'{arg}': {rewrite_arg_name(arg)}" for arg in arg_names]) + "}"
|
|
60
|
+
func_type_defs = AI_DEFS_TEMPLATE.format(
|
|
61
|
+
args_def=args_def,
|
|
62
|
+
return_type_def=return_type_def,
|
|
63
|
+
)
|
|
64
|
+
func_str = AI_FUNCTION_TEMPLATE.format(
|
|
65
|
+
return_type_name=_normalize_return_type_for_annotation(function_name, return_type_name),
|
|
66
|
+
function_type="ai",
|
|
67
|
+
function_name=function_name,
|
|
68
|
+
function_id=function_id,
|
|
69
|
+
function_description=function_description.replace('"', "'"),
|
|
70
|
+
args=args,
|
|
71
|
+
return_action=_get_ai_return_action(return_type_name),
|
|
72
|
+
data=data,
|
|
73
|
+
)
|
|
74
|
+
return func_str, func_type_defs
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _normalize_return_type_for_annotation(function_name: str, return_type_name: str) -> str:
|
|
78
|
+
if return_type_name == "ReturnType":
|
|
79
|
+
return "ReturnType"
|
|
80
|
+
return add_type_import_path(function_name, return_type_name)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _get_ai_return_action(return_type_name: str) -> str:
|
|
84
|
+
normalized_type = return_type_name.replace(" ", "")
|
|
85
|
+
|
|
86
|
+
if normalized_type in {"str", "Optional[str]"}:
|
|
87
|
+
return_action = "resp.text"
|
|
88
|
+
elif "|" in normalized_type:
|
|
89
|
+
union_parts = {part for part in normalized_type.split("|") if part}
|
|
90
|
+
if union_parts == {"str", "None"}:
|
|
91
|
+
return_action = "resp.text"
|
|
92
|
+
else:
|
|
93
|
+
return_action = "resp.json()"
|
|
94
|
+
else:
|
|
95
|
+
return_action = "resp.json()"
|
|
96
|
+
return return_action
|
|
@@ -17,6 +17,7 @@ from .webhook import render_webhook_handle
|
|
|
17
17
|
from .typedefs import PropertySpecification, SchemaSpecDto, SpecificationDto, VariableSpecDto, TableSpecDto
|
|
18
18
|
from .api import render_api_function
|
|
19
19
|
from .server import render_server_function
|
|
20
|
+
from .ai_function import render_ai_function
|
|
20
21
|
from .utils import add_import_to_init, get_auth_headers, init_the_init, print_green, to_func_namespace, to_type_module_alias
|
|
21
22
|
from .variables import generate_variables
|
|
22
23
|
from .poly_tables import generate_tables
|
|
@@ -29,6 +30,7 @@ SUPPORTED_FUNCTION_TYPES = {
|
|
|
29
30
|
"customFunction", # client function - this is badly named in /specs atm
|
|
30
31
|
"serverFunction",
|
|
31
32
|
"webhookHandle",
|
|
33
|
+
"aiFunction",
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
SUPPORTED_TYPES = SUPPORTED_FUNCTION_TYPES | {"serverVariable", "schema", "snippet", "table"}
|
|
@@ -51,7 +53,7 @@ def get_specs(contexts: Optional[List[str]] = None, names: Optional[List[str]] =
|
|
|
51
53
|
params: Any = {"noTypes": str(no_types).lower()}
|
|
52
54
|
|
|
53
55
|
if contexts:
|
|
54
|
-
params["contexts"] = contexts
|
|
56
|
+
params["contexts"] = ",".join(contexts)
|
|
55
57
|
|
|
56
58
|
if names:
|
|
57
59
|
params["names"] = names
|
|
@@ -98,7 +100,7 @@ def resolve_poly_refs(obj, schema_index):
|
|
|
98
100
|
def replace_poly_refs_in_functions(specs: List[SpecificationDto], schema_index):
|
|
99
101
|
spec_idxs_to_remove = []
|
|
100
102
|
for idx, spec in enumerate(specs):
|
|
101
|
-
if spec.get("type") in ("apiFunction", "customFunction", "serverFunction"):
|
|
103
|
+
if spec.get("type") in ("apiFunction", "customFunction", "serverFunction", "aiFunction"):
|
|
102
104
|
func = spec.get("function")
|
|
103
105
|
if func:
|
|
104
106
|
try:
|
|
@@ -501,6 +503,15 @@ def render_spec(spec: SpecificationDto) -> Tuple[str, str]:
|
|
|
501
503
|
arguments,
|
|
502
504
|
return_type,
|
|
503
505
|
)
|
|
506
|
+
elif function_type == "aiFunction":
|
|
507
|
+
func_str, func_type_defs = render_ai_function(
|
|
508
|
+
function_type,
|
|
509
|
+
function_name,
|
|
510
|
+
function_id,
|
|
511
|
+
function_description,
|
|
512
|
+
arguments,
|
|
513
|
+
return_type,
|
|
514
|
+
)
|
|
504
515
|
|
|
505
516
|
if X_POLY_REF_WARNING in func_type_defs:
|
|
506
517
|
# this indicates that jsonschema_gentypes has detected an x-poly-ref
|