polyapi-python 0.3.14.dev1__tar.gz → 0.3.14.dev3__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.14.dev1/polyapi_python.egg-info → polyapi_python-0.3.14.dev3}/PKG-INFO +4 -4
- polyapi_python-0.3.14.dev3/polyapi/__init__.py +163 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/cli.py +1 -3
- polyapi_python-0.3.14.dev3/polyapi/cli_constants.py +10 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/constants.py +7 -4
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3/polyapi_python.egg-info}/PKG-INFO +4 -4
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi_python.egg-info/SOURCES.txt +2 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi_python.egg-info/requires.txt +3 -3
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/pyproject.toml +4 -4
- polyapi_python-0.3.14.dev3/tests/test_poly_custom.py +130 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/tests/test_tabi.py +17 -3
- polyapi_python-0.3.14.dev1/polyapi/__init__.py +0 -101
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/LICENSE +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/README.md +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/__main__.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/api.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/auth.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/client.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/config.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/deployables.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/error_handler.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/exceptions.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/execute.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/function_cli.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/generate.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/http_client.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/parser.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/poly_schemas.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/poly_tables.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/prepare.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/py.typed +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/rendered_spec.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/schema.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/server.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/sync.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/typedefs.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/utils.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/variables.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi/webhook.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi_python.egg-info/dependency_links.txt +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi_python.egg-info/top_level.txt +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/setup.cfg +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/tests/test_api.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/tests/test_async_proof.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/tests/test_auth.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/tests/test_deployables.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/tests/test_generate.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/tests/test_parser.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/tests/test_rendered_spec.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/tests/test_schema.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/tests/test_server.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/tests/test_utils.py +0 -0
- {polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/tests/test_variables.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: polyapi-python
|
|
3
|
-
Version: 0.3.14.
|
|
3
|
+
Version: 0.3.14.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
|
|
@@ -30,9 +30,9 @@ Description-Content-Type: text/markdown
|
|
|
30
30
|
License-File: LICENSE
|
|
31
31
|
Requires-Dist: requests>=2.32.3
|
|
32
32
|
Requires-Dist: typing_extensions>=4.12.2
|
|
33
|
-
Requires-Dist: jsonschema-gentypes==2.
|
|
34
|
-
Requires-Dist: pydantic
|
|
35
|
-
Requires-Dist: stdlib_list
|
|
33
|
+
Requires-Dist: jsonschema-gentypes==2.10.0
|
|
34
|
+
Requires-Dist: pydantic<3.0.0,>=2.8.0
|
|
35
|
+
Requires-Dist: stdlib_list<1.0.0,>=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
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from contextvars import ContextVar, Token
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, Dict, Literal, Optional, overload
|
|
7
|
+
|
|
8
|
+
import truststore
|
|
9
|
+
from typing_extensions import TypedDict
|
|
10
|
+
|
|
11
|
+
from .cli_constants import CLI_COMMANDS
|
|
12
|
+
|
|
13
|
+
truststore.inject_into_ssl()
|
|
14
|
+
|
|
15
|
+
__all__ = ["poly"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if len(sys.argv) > 1 and sys.argv[1] not in CLI_COMMANDS:
|
|
19
|
+
currdir = os.path.dirname(os.path.abspath(__file__))
|
|
20
|
+
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
|
+
|
|
24
|
+
|
|
25
|
+
class PolyCustomDict(TypedDict, total=False):
|
|
26
|
+
"""Type definition for polyCustom dictionary."""
|
|
27
|
+
|
|
28
|
+
executionId: Optional[str] # Read-only unless explicitly unlocked
|
|
29
|
+
executionApiKey: Optional[str]
|
|
30
|
+
userSessionId: Optional[str]
|
|
31
|
+
responseStatusCode: Optional[int]
|
|
32
|
+
responseContentType: Optional[str]
|
|
33
|
+
responseHeaders: Dict[str, Any]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class _PolyCustomState:
|
|
38
|
+
internal_store: Dict[str, Any]
|
|
39
|
+
execution_id_locked: bool = False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class PolyCustom:
|
|
43
|
+
def __init__(self) -> None:
|
|
44
|
+
object.__setattr__(
|
|
45
|
+
self,
|
|
46
|
+
"_default_store",
|
|
47
|
+
{
|
|
48
|
+
"executionId": None,
|
|
49
|
+
"executionApiKey": None,
|
|
50
|
+
"userSessionId": None,
|
|
51
|
+
"responseStatusCode": 200,
|
|
52
|
+
"responseContentType": None,
|
|
53
|
+
"responseHeaders": {},
|
|
54
|
+
},
|
|
55
|
+
)
|
|
56
|
+
object.__setattr__(self, "_state_var", ContextVar("_poly_custom_state", default=None))
|
|
57
|
+
|
|
58
|
+
def _make_state(self) -> _PolyCustomState:
|
|
59
|
+
return _PolyCustomState(internal_store=copy.deepcopy(self._default_store))
|
|
60
|
+
|
|
61
|
+
def _get_state(self) -> _PolyCustomState:
|
|
62
|
+
state = self._state_var.get()
|
|
63
|
+
if state is None:
|
|
64
|
+
state = self._make_state()
|
|
65
|
+
self._state_var.set(state)
|
|
66
|
+
return state
|
|
67
|
+
|
|
68
|
+
def push_scope(self, initial_values: Optional[Dict[str, Any]] = None) -> Token:
|
|
69
|
+
state = self._make_state()
|
|
70
|
+
if initial_values:
|
|
71
|
+
state.internal_store.update(copy.deepcopy(initial_values))
|
|
72
|
+
if state.internal_store.get("executionId") is not None:
|
|
73
|
+
state.execution_id_locked = True
|
|
74
|
+
return self._state_var.set(state)
|
|
75
|
+
|
|
76
|
+
def pop_scope(self, token: Token) -> None:
|
|
77
|
+
self._state_var.reset(token)
|
|
78
|
+
|
|
79
|
+
def set_once(self, key: str, value: Any) -> None:
|
|
80
|
+
state = self._get_state()
|
|
81
|
+
if key == "executionId" and state.execution_id_locked:
|
|
82
|
+
return
|
|
83
|
+
state.internal_store[key] = value
|
|
84
|
+
if key == "executionId":
|
|
85
|
+
state.execution_id_locked = True
|
|
86
|
+
|
|
87
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
88
|
+
return self._get_state().internal_store.get(key, default)
|
|
89
|
+
|
|
90
|
+
def lock_execution_id(self) -> None:
|
|
91
|
+
self._get_state().execution_id_locked = True
|
|
92
|
+
|
|
93
|
+
def unlock_execution_id(self) -> None:
|
|
94
|
+
self._get_state().execution_id_locked = False
|
|
95
|
+
|
|
96
|
+
@overload
|
|
97
|
+
def __getitem__(self, key: Literal["executionId"]) -> Optional[str]: ...
|
|
98
|
+
|
|
99
|
+
@overload
|
|
100
|
+
def __getitem__(self, key: Literal["executionApiKey"]) -> Optional[str]: ...
|
|
101
|
+
|
|
102
|
+
@overload
|
|
103
|
+
def __getitem__(self, key: Literal["userSessionId"]) -> Optional[str]: ...
|
|
104
|
+
|
|
105
|
+
@overload
|
|
106
|
+
def __getitem__(self, key: Literal["responseStatusCode"]) -> Optional[int]: ...
|
|
107
|
+
|
|
108
|
+
@overload
|
|
109
|
+
def __getitem__(self, key: Literal["responseContentType"]) -> Optional[str]: ...
|
|
110
|
+
|
|
111
|
+
@overload
|
|
112
|
+
def __getitem__(self, key: Literal["responseHeaders"]) -> Dict[str, Any]: ...
|
|
113
|
+
|
|
114
|
+
def __getitem__(self, key: str) -> Any:
|
|
115
|
+
return self.get(key)
|
|
116
|
+
|
|
117
|
+
@overload
|
|
118
|
+
def __setitem__(self, key: Literal["executionApiKey"], value: Optional[str]) -> None: ...
|
|
119
|
+
|
|
120
|
+
@overload
|
|
121
|
+
def __setitem__(self, key: Literal["userSessionId"], value: Optional[str]) -> None: ...
|
|
122
|
+
|
|
123
|
+
@overload
|
|
124
|
+
def __setitem__(self, key: Literal["responseStatusCode"], value: Optional[int]) -> None: ...
|
|
125
|
+
|
|
126
|
+
@overload
|
|
127
|
+
def __setitem__(self, key: Literal["responseContentType"], value: Optional[str]) -> None: ...
|
|
128
|
+
|
|
129
|
+
@overload
|
|
130
|
+
def __setitem__(self, key: Literal["responseHeaders"], value: Dict[str, Any]) -> None: ...
|
|
131
|
+
|
|
132
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
133
|
+
self.set_once(key, value)
|
|
134
|
+
|
|
135
|
+
def __getattr__(self, key: str) -> Any:
|
|
136
|
+
if key in self._default_store:
|
|
137
|
+
return self.get(key)
|
|
138
|
+
raise AttributeError(f"{type(self).__name__!r} object has no attribute {key!r}")
|
|
139
|
+
|
|
140
|
+
def __setattr__(self, key: str, value: Any) -> None:
|
|
141
|
+
if key.startswith("_"):
|
|
142
|
+
object.__setattr__(self, key, value)
|
|
143
|
+
return
|
|
144
|
+
self.set_once(key, value)
|
|
145
|
+
|
|
146
|
+
def __repr__(self) -> str:
|
|
147
|
+
return f"PolyCustom({self._get_state().internal_store})"
|
|
148
|
+
|
|
149
|
+
def copy(self) -> "PolyCustom":
|
|
150
|
+
new = PolyCustom()
|
|
151
|
+
state = self._get_state()
|
|
152
|
+
new._state_var.set(
|
|
153
|
+
_PolyCustomState(
|
|
154
|
+
internal_store=copy.deepcopy(state.internal_store),
|
|
155
|
+
execution_id_locked=state.execution_id_locked,
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
return new
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
_PolyCustom = PolyCustom
|
|
162
|
+
|
|
163
|
+
polyCustom: PolyCustom = PolyCustom()
|
|
@@ -3,6 +3,7 @@ import argparse
|
|
|
3
3
|
|
|
4
4
|
from polyapi.utils import print_green, print_red
|
|
5
5
|
|
|
6
|
+
from .cli_constants import CLI_COMMANDS
|
|
6
7
|
from .config import initialize_config, set_api_key_and_url
|
|
7
8
|
from .generate import generate, clear
|
|
8
9
|
from .function_cli import function_add_or_update, function_execute
|
|
@@ -11,9 +12,6 @@ from .prepare import prepare_deployables
|
|
|
11
12
|
from .sync import sync_deployables
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
CLI_COMMANDS = ["setup", "generate", "function", "clear", "help", "update_rendered_spec"]
|
|
15
|
-
|
|
16
|
-
|
|
17
15
|
def _get_version_string():
|
|
18
16
|
"""Get the version string for the package."""
|
|
19
17
|
try:
|
|
@@ -3,8 +3,8 @@ JSONSCHEMA_TO_PYTHON_TYPE_MAP = {
|
|
|
3
3
|
"number": "float",
|
|
4
4
|
"string": "str",
|
|
5
5
|
"boolean": "bool",
|
|
6
|
-
"array": "
|
|
7
|
-
"object": "
|
|
6
|
+
"array": "list",
|
|
7
|
+
"object": "dict",
|
|
8
8
|
"function": "Callable",
|
|
9
9
|
"void": "None",
|
|
10
10
|
}
|
|
@@ -15,10 +15,13 @@ PYTHON_TO_JSONSCHEMA_TYPE_MAP = {
|
|
|
15
15
|
"float": "number",
|
|
16
16
|
"str": "string",
|
|
17
17
|
"bool": "boolean",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"list": "array",
|
|
19
|
+
"dict": "object",
|
|
20
20
|
"Callable": "function",
|
|
21
21
|
"None": "void",
|
|
22
|
+
# Keep uppercase aliases for backwards compatibility
|
|
23
|
+
"List": "array",
|
|
24
|
+
"Dict": "object",
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
BASIC_PYTHON_TYPES = set(PYTHON_TO_JSONSCHEMA_TYPE_MAP.keys())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: polyapi-python
|
|
3
|
-
Version: 0.3.14.
|
|
3
|
+
Version: 0.3.14.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
|
|
@@ -30,9 +30,9 @@ Description-Content-Type: text/markdown
|
|
|
30
30
|
License-File: LICENSE
|
|
31
31
|
Requires-Dist: requests>=2.32.3
|
|
32
32
|
Requires-Dist: typing_extensions>=4.12.2
|
|
33
|
-
Requires-Dist: jsonschema-gentypes==2.
|
|
34
|
-
Requires-Dist: pydantic
|
|
35
|
-
Requires-Dist: stdlib_list
|
|
33
|
+
Requires-Dist: jsonschema-gentypes==2.10.0
|
|
34
|
+
Requires-Dist: pydantic<3.0.0,>=2.8.0
|
|
35
|
+
Requires-Dist: stdlib_list<1.0.0,>=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
|
{polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi_python.egg-info/SOURCES.txt
RENAMED
|
@@ -6,6 +6,7 @@ polyapi/__main__.py
|
|
|
6
6
|
polyapi/api.py
|
|
7
7
|
polyapi/auth.py
|
|
8
8
|
polyapi/cli.py
|
|
9
|
+
polyapi/cli_constants.py
|
|
9
10
|
polyapi/client.py
|
|
10
11
|
polyapi/config.py
|
|
11
12
|
polyapi/constants.py
|
|
@@ -40,6 +41,7 @@ tests/test_auth.py
|
|
|
40
41
|
tests/test_deployables.py
|
|
41
42
|
tests/test_generate.py
|
|
42
43
|
tests/test_parser.py
|
|
44
|
+
tests/test_poly_custom.py
|
|
43
45
|
tests/test_rendered_spec.py
|
|
44
46
|
tests/test_schema.py
|
|
45
47
|
tests/test_server.py
|
{polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi_python.egg-info/requires.txt
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
requests>=2.32.3
|
|
2
2
|
typing_extensions>=4.12.2
|
|
3
|
-
jsonschema-gentypes==2.
|
|
4
|
-
pydantic
|
|
5
|
-
stdlib_list
|
|
3
|
+
jsonschema-gentypes==2.10.0
|
|
4
|
+
pydantic<3.0.0,>=2.8.0
|
|
5
|
+
stdlib_list<1.0.0,>=0.10.0
|
|
6
6
|
colorama==0.4.4
|
|
7
7
|
python-socketio[asyncio_client]==5.11.1
|
|
8
8
|
truststore>=0.8.0
|
|
@@ -4,15 +4,15 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "polyapi-python"
|
|
7
|
-
version = "0.3.14.
|
|
7
|
+
version = "0.3.14.dev3"
|
|
8
8
|
description = "The Python Client for PolyAPI, the IPaaS by Developers for Developers"
|
|
9
9
|
authors = [{ name = "Dan Fellin", email = "dan@polyapi.io" }]
|
|
10
10
|
dependencies = [
|
|
11
11
|
"requests>=2.32.3",
|
|
12
12
|
"typing_extensions>=4.12.2",
|
|
13
|
-
"jsonschema-gentypes==2.
|
|
14
|
-
"pydantic>=2.8.0",
|
|
15
|
-
"stdlib_list>=0.10.0",
|
|
13
|
+
"jsonschema-gentypes==2.10.0",
|
|
14
|
+
"pydantic>=2.8.0,<3.0.0",
|
|
15
|
+
"stdlib_list>=0.10.0,<1.0.0",
|
|
16
16
|
"colorama==0.4.4",
|
|
17
17
|
"python-socketio[asyncio_client]==5.11.1",
|
|
18
18
|
"truststore>=0.8.0",
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import importlib
|
|
3
|
+
import sys
|
|
4
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _reload_polyapi():
|
|
8
|
+
sys.modules.pop("polyapi", None)
|
|
9
|
+
sys.modules.pop("polyapi.cli", None)
|
|
10
|
+
return importlib.import_module("polyapi")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_import_polyapi_does_not_import_cli():
|
|
14
|
+
polyapi = _reload_polyapi()
|
|
15
|
+
|
|
16
|
+
assert polyapi is not None
|
|
17
|
+
assert "polyapi.cli" not in sys.modules
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_cli_constants_shared_between_runtime_and_cli():
|
|
21
|
+
cli_constants = importlib.import_module("polyapi.cli_constants")
|
|
22
|
+
cli_module = importlib.import_module("polyapi.cli")
|
|
23
|
+
|
|
24
|
+
assert tuple(cli_module.CLI_COMMANDS) == cli_constants.CLI_COMMANDS
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_poly_custom_nested_scopes_restore_previous_state():
|
|
28
|
+
polyapi = _reload_polyapi()
|
|
29
|
+
poly_custom = polyapi.polyCustom
|
|
30
|
+
|
|
31
|
+
outer_token = poly_custom.push_scope(
|
|
32
|
+
{
|
|
33
|
+
"executionId": "outer",
|
|
34
|
+
"responseHeaders": {"x-scope": "outer"},
|
|
35
|
+
"responseStatusCode": None,
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
try:
|
|
39
|
+
assert poly_custom["executionId"] == "outer"
|
|
40
|
+
|
|
41
|
+
inner_token = poly_custom.push_scope(
|
|
42
|
+
{
|
|
43
|
+
"executionId": "inner",
|
|
44
|
+
"responseHeaders": {"x-scope": "inner"},
|
|
45
|
+
"responseStatusCode": 202,
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
try:
|
|
49
|
+
assert poly_custom["executionId"] == "inner"
|
|
50
|
+
assert poly_custom["responseHeaders"] == {"x-scope": "inner"}
|
|
51
|
+
assert poly_custom.responseStatusCode == 202
|
|
52
|
+
|
|
53
|
+
poly_custom["executionId"] = "should-not-overwrite"
|
|
54
|
+
assert poly_custom["executionId"] == "inner"
|
|
55
|
+
|
|
56
|
+
poly_custom.unlock_execution_id()
|
|
57
|
+
poly_custom["executionId"] = "inner-updated"
|
|
58
|
+
assert poly_custom["executionId"] == "inner-updated"
|
|
59
|
+
finally:
|
|
60
|
+
poly_custom.pop_scope(inner_token)
|
|
61
|
+
|
|
62
|
+
assert poly_custom["executionId"] == "outer"
|
|
63
|
+
assert poly_custom["responseHeaders"] == {"x-scope": "outer"}
|
|
64
|
+
assert poly_custom["responseStatusCode"] is None
|
|
65
|
+
finally:
|
|
66
|
+
poly_custom.pop_scope(outer_token)
|
|
67
|
+
|
|
68
|
+
assert poly_custom["executionId"] is None
|
|
69
|
+
assert poly_custom["responseHeaders"] == {}
|
|
70
|
+
assert poly_custom["responseStatusCode"] == 200
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_poly_custom_isolated_across_async_tasks():
|
|
74
|
+
polyapi = _reload_polyapi()
|
|
75
|
+
poly_custom = polyapi.polyCustom
|
|
76
|
+
|
|
77
|
+
async def worker(execution_id: str) -> tuple[str, str]:
|
|
78
|
+
token = poly_custom.push_scope(
|
|
79
|
+
{
|
|
80
|
+
"executionId": execution_id,
|
|
81
|
+
"responseHeaders": {"worker": execution_id},
|
|
82
|
+
"responseStatusCode": None,
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
try:
|
|
86
|
+
await asyncio.sleep(0)
|
|
87
|
+
poly_custom["responseHeaders"]["seen"] = execution_id
|
|
88
|
+
await asyncio.sleep(0)
|
|
89
|
+
return poly_custom["executionId"], poly_custom["responseHeaders"]["seen"]
|
|
90
|
+
finally:
|
|
91
|
+
poly_custom.pop_scope(token)
|
|
92
|
+
|
|
93
|
+
async def run_workers() -> tuple[tuple[str, str], tuple[str, str]]:
|
|
94
|
+
first_result, second_result = await asyncio.gather(worker("async-a"), worker("async-b"))
|
|
95
|
+
return first_result, second_result
|
|
96
|
+
|
|
97
|
+
first, second = asyncio.run(run_workers())
|
|
98
|
+
|
|
99
|
+
assert first == ("async-a", "async-a")
|
|
100
|
+
assert second == ("async-b", "async-b")
|
|
101
|
+
assert poly_custom["executionId"] is None
|
|
102
|
+
assert poly_custom["responseHeaders"] == {}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_poly_custom_isolated_across_threads():
|
|
106
|
+
polyapi = _reload_polyapi()
|
|
107
|
+
poly_custom = polyapi.polyCustom
|
|
108
|
+
|
|
109
|
+
def worker(execution_id: str) -> tuple[str, str]:
|
|
110
|
+
token = poly_custom.push_scope(
|
|
111
|
+
{
|
|
112
|
+
"executionId": execution_id,
|
|
113
|
+
"responseHeaders": {"worker": execution_id},
|
|
114
|
+
"responseStatusCode": None,
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
try:
|
|
118
|
+
poly_custom["responseHeaders"]["seen"] = execution_id
|
|
119
|
+
return poly_custom["executionId"], poly_custom["responseHeaders"]["seen"]
|
|
120
|
+
finally:
|
|
121
|
+
poly_custom.pop_scope(token)
|
|
122
|
+
|
|
123
|
+
with ThreadPoolExecutor(max_workers=2) as executor:
|
|
124
|
+
first = executor.submit(worker, "thread-a").result()
|
|
125
|
+
second = executor.submit(worker, "thread-b").result()
|
|
126
|
+
|
|
127
|
+
assert first == ("thread-a", "thread-a")
|
|
128
|
+
assert second == ("thread-b", "thread-b")
|
|
129
|
+
assert poly_custom["executionId"] is None
|
|
130
|
+
assert poly_custom["responseHeaders"] == {}
|
|
@@ -2,12 +2,26 @@ import unittest
|
|
|
2
2
|
from unittest.mock import Mock, patch
|
|
3
3
|
from polyapi.poly_tables import _render_table, TABI_MODULE_IMPORTS, execute_query
|
|
4
4
|
from polyapi.typedefs import TableSpecDto
|
|
5
|
+
import re
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def _normalize_type_notation(value: str) -> str:
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
|
|
9
|
+
# Normalize type annotations to handle differences between Python versions
|
|
10
|
+
# and jsonschema-gentypes versions. This allows tests to pass regardless of
|
|
11
|
+
# whether the generator outputs:
|
|
12
|
+
# - dict[str, Any] vs Dict[str, Any]
|
|
13
|
+
# - list[...] vs List[...]
|
|
14
|
+
# - str | int vs Union[str, int]
|
|
15
|
+
|
|
16
|
+
# Normalize built-in generic style to typing style for comparison
|
|
17
|
+
result = value
|
|
18
|
+
# Handle dict/Dict
|
|
19
|
+
result = re.sub(r'\bdict\[', 'Dict[', result)
|
|
20
|
+
# Handle list/List
|
|
21
|
+
result = re.sub(r'\blist\[', 'List[', result)
|
|
22
|
+
# Normalize union syntax: "A | B | C" -> "Union[A, B, C]" (simplified, handles common cases)
|
|
23
|
+
# This is a basic normalization - complex nested unions may need manual attention
|
|
24
|
+
return result
|
|
11
25
|
|
|
12
26
|
|
|
13
27
|
TABLE_SPEC_SIMPLE: TableSpecDto = {
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
import copy
|
|
4
|
-
import truststore
|
|
5
|
-
from typing import Any, Dict, Optional, overload, Literal
|
|
6
|
-
from typing_extensions import TypedDict
|
|
7
|
-
truststore.inject_into_ssl()
|
|
8
|
-
from .cli import CLI_COMMANDS
|
|
9
|
-
|
|
10
|
-
__all__ = ["poly"]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if len(sys.argv) > 1 and sys.argv[1] not in CLI_COMMANDS:
|
|
14
|
-
currdir = os.path.dirname(os.path.abspath(__file__))
|
|
15
|
-
if not os.path.isdir(os.path.join(currdir, "poly")):
|
|
16
|
-
print("No 'poly' found. Please run 'python3 -m polyapi generate' to generate the 'poly' library for your tenant.")
|
|
17
|
-
sys.exit(1)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class PolyCustomDict(TypedDict, total=False):
|
|
21
|
-
"""Type definition for polyCustom dictionary."""
|
|
22
|
-
executionId: Optional[str] # Read-only
|
|
23
|
-
executionApiKey: Optional[str]
|
|
24
|
-
responseStatusCode: int
|
|
25
|
-
responseContentType: Optional[str]
|
|
26
|
-
responseHeaders: Dict[str, str]
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class _PolyCustom:
|
|
30
|
-
def __init__(self):
|
|
31
|
-
self._internal_store = {
|
|
32
|
-
"executionId": None,
|
|
33
|
-
"executionApiKey": None,
|
|
34
|
-
"responseStatusCode": 200,
|
|
35
|
-
"responseContentType": None,
|
|
36
|
-
"responseHeaders": {},
|
|
37
|
-
}
|
|
38
|
-
self._execution_id_locked = False
|
|
39
|
-
|
|
40
|
-
def set_once(self, key: str, value: Any) -> None:
|
|
41
|
-
if key == "executionId" and self._execution_id_locked:
|
|
42
|
-
# Silently ignore attempts to overwrite locked executionId
|
|
43
|
-
return
|
|
44
|
-
self._internal_store[key] = value
|
|
45
|
-
if key == "executionId":
|
|
46
|
-
# Lock executionId after setting it
|
|
47
|
-
self.lock_execution_id()
|
|
48
|
-
|
|
49
|
-
def get(self, key: str, default: Any = None) -> Any:
|
|
50
|
-
return self._internal_store.get(key, default)
|
|
51
|
-
|
|
52
|
-
def lock_execution_id(self) -> None:
|
|
53
|
-
self._execution_id_locked = True
|
|
54
|
-
|
|
55
|
-
def unlock_execution_id(self) -> None:
|
|
56
|
-
self._execution_id_locked = False
|
|
57
|
-
|
|
58
|
-
@overload
|
|
59
|
-
def __getitem__(self, key: Literal["executionId"]) -> Optional[str]: ...
|
|
60
|
-
|
|
61
|
-
@overload
|
|
62
|
-
def __getitem__(self, key: Literal["executionApiKey"]) -> Optional[str]: ...
|
|
63
|
-
|
|
64
|
-
@overload
|
|
65
|
-
def __getitem__(self, key: Literal["responseStatusCode"]) -> int: ...
|
|
66
|
-
|
|
67
|
-
@overload
|
|
68
|
-
def __getitem__(self, key: Literal["responseContentType"]) -> Optional[str]: ...
|
|
69
|
-
|
|
70
|
-
@overload
|
|
71
|
-
def __getitem__(self, key: Literal["responseHeaders"]) -> Dict[str, str]: ...
|
|
72
|
-
|
|
73
|
-
def __getitem__(self, key: str) -> Any:
|
|
74
|
-
return self.get(key)
|
|
75
|
-
|
|
76
|
-
@overload
|
|
77
|
-
def __setitem__(self, key: Literal["executionApiKey"], value: Optional[str]) -> None: ...
|
|
78
|
-
|
|
79
|
-
@overload
|
|
80
|
-
def __setitem__(self, key: Literal["responseStatusCode"], value: int) -> None: ...
|
|
81
|
-
|
|
82
|
-
@overload
|
|
83
|
-
def __setitem__(self, key: Literal["responseContentType"], value: Optional[str]) -> None: ...
|
|
84
|
-
|
|
85
|
-
@overload
|
|
86
|
-
def __setitem__(self, key: Literal["responseHeaders"], value: Dict[str, str]) -> None: ...
|
|
87
|
-
|
|
88
|
-
def __setitem__(self, key: str, value: Any) -> None:
|
|
89
|
-
self.set_once(key, value)
|
|
90
|
-
|
|
91
|
-
def __repr__(self) -> str:
|
|
92
|
-
return f"PolyCustom({self._internal_store})"
|
|
93
|
-
|
|
94
|
-
def copy(self) -> '_PolyCustom':
|
|
95
|
-
new = _PolyCustom()
|
|
96
|
-
new._internal_store = copy.deepcopy(self._internal_store)
|
|
97
|
-
new._execution_id_locked = self._execution_id_locked
|
|
98
|
-
return new
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
polyCustom: PolyCustomDict = _PolyCustom()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polyapi_python-0.3.14.dev1 → polyapi_python-0.3.14.dev3}/polyapi_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|