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.
Files changed (59) hide show
  1. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/PKG-INFO +35 -2
  2. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/README.md +34 -1
  3. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/__init__.py +18 -7
  4. polyapi_python-0.3.20/polyapi/ai_function.py +96 -0
  5. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/generate.py +13 -2
  6. polyapi_python-0.3.20/polyapi/http_client.py +414 -0
  7. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/typedefs.py +1 -1
  8. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/utils.py +22 -4
  9. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi_python.egg-info/PKG-INFO +35 -2
  10. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi_python.egg-info/SOURCES.txt +2 -0
  11. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/pyproject.toml +2 -2
  12. polyapi_python-0.3.20/tests/test_async_proof.py +1560 -0
  13. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_generate.py +214 -1
  14. polyapi_python-0.3.20/tests/test_generated_poly_boundary.py +47 -0
  15. polyapi_python-0.3.19.dev3/polyapi/http_client.py +0 -83
  16. polyapi_python-0.3.19.dev3/tests/test_async_proof.py +0 -498
  17. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/LICENSE +0 -0
  18. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/__main__.py +0 -0
  19. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/api.py +0 -0
  20. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/auth.py +0 -0
  21. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/cli.py +0 -0
  22. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/cli_constants.py +0 -0
  23. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/client.py +0 -0
  24. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/config.py +0 -0
  25. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/constants.py +0 -0
  26. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/deployables.py +0 -0
  27. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/error_handler.py +0 -0
  28. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/exceptions.py +0 -0
  29. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/execute.py +0 -0
  30. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/function_cli.py +0 -0
  31. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/parser.py +0 -0
  32. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/poly_schemas.py +0 -0
  33. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/poly_tables.py +0 -0
  34. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/prepare.py +0 -0
  35. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/py.typed +0 -0
  36. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/rendered_spec.py +0 -0
  37. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/schema.py +0 -0
  38. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/server.py +0 -0
  39. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/sync.py +0 -0
  40. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/variables.py +0 -0
  41. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi/webhook.py +0 -0
  42. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi_python.egg-info/dependency_links.txt +0 -0
  43. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi_python.egg-info/requires.txt +0 -0
  44. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/polyapi_python.egg-info/top_level.txt +0 -0
  45. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/setup.cfg +0 -0
  46. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_api.py +0 -0
  47. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_auth.py +0 -0
  48. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_cli.py +0 -0
  49. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_dependency_pins.py +0 -0
  50. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_deployables.py +0 -0
  51. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_parser.py +0 -0
  52. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_poly_custom.py +0 -0
  53. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_poly_schemas.py +0 -0
  54. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_rendered_spec.py +0 -0
  55. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_schema.py +0 -0
  56. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_server.py +0 -0
  57. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_tabi.py +0 -0
  58. {polyapi_python-0.3.19.dev3 → polyapi_python-0.3.20}/tests/test_utils.py +0 -0
  59. {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.19.dev3
4
- Summary: The Python Client for PolyAPI, the IPaaS by Developers for Developers
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 typing import Any, Dict, Literal, Optional, overload
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
- if len(sys.argv) > 1 and sys.argv[1] not in CLI_COMMANDS:
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
- print("No 'poly' found. Please run 'python3 -m polyapi generate' to generate the 'poly' library for your tenant.")
22
- sys.exit(1)
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