polyapi-python 0.3.14.dev2__tar.gz → 0.3.14.dev4__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 (53) hide show
  1. {polyapi_python-0.3.14.dev2/polyapi_python.egg-info → polyapi_python-0.3.14.dev4}/PKG-INFO +1 -1
  2. polyapi_python-0.3.14.dev4/polyapi/__init__.py +163 -0
  3. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/cli.py +1 -3
  4. polyapi_python-0.3.14.dev4/polyapi/cli_constants.py +10 -0
  5. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4/polyapi_python.egg-info}/PKG-INFO +1 -1
  6. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi_python.egg-info/SOURCES.txt +2 -0
  7. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/pyproject.toml +1 -1
  8. polyapi_python-0.3.14.dev4/tests/test_poly_custom.py +130 -0
  9. polyapi_python-0.3.14.dev2/polyapi/__init__.py +0 -101
  10. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/LICENSE +0 -0
  11. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/README.md +0 -0
  12. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/__main__.py +0 -0
  13. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/api.py +0 -0
  14. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/auth.py +0 -0
  15. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/client.py +0 -0
  16. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/config.py +0 -0
  17. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/constants.py +0 -0
  18. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/deployables.py +0 -0
  19. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/error_handler.py +0 -0
  20. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/exceptions.py +0 -0
  21. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/execute.py +0 -0
  22. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/function_cli.py +0 -0
  23. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/generate.py +0 -0
  24. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/http_client.py +0 -0
  25. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/parser.py +0 -0
  26. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/poly_schemas.py +0 -0
  27. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/poly_tables.py +0 -0
  28. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/prepare.py +0 -0
  29. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/py.typed +0 -0
  30. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/rendered_spec.py +0 -0
  31. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/schema.py +0 -0
  32. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/server.py +0 -0
  33. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/sync.py +0 -0
  34. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/typedefs.py +0 -0
  35. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/utils.py +0 -0
  36. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/variables.py +0 -0
  37. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi/webhook.py +0 -0
  38. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi_python.egg-info/dependency_links.txt +0 -0
  39. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi_python.egg-info/requires.txt +0 -0
  40. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/polyapi_python.egg-info/top_level.txt +0 -0
  41. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/setup.cfg +0 -0
  42. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/tests/test_api.py +0 -0
  43. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/tests/test_async_proof.py +0 -0
  44. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/tests/test_auth.py +0 -0
  45. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/tests/test_deployables.py +0 -0
  46. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/tests/test_generate.py +0 -0
  47. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/tests/test_parser.py +0 -0
  48. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/tests/test_rendered_spec.py +0 -0
  49. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/tests/test_schema.py +0 -0
  50. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/tests/test_server.py +0 -0
  51. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/tests/test_tabi.py +0 -0
  52. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/tests/test_utils.py +0 -0
  53. {polyapi_python-0.3.14.dev2 → polyapi_python-0.3.14.dev4}/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.dev2
3
+ Version: 0.3.14.dev4
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
@@ -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:
@@ -0,0 +1,10 @@
1
+ CLI_COMMANDS = (
2
+ "setup",
3
+ "generate",
4
+ "function",
5
+ "clear",
6
+ "help",
7
+ "update_rendered_spec",
8
+ "prepare",
9
+ "sync",
10
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polyapi-python
3
- Version: 0.3.14.dev2
3
+ Version: 0.3.14.dev4
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
@@ -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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "polyapi-python"
7
- version = "0.3.14.dev2"
7
+ version = "0.3.14.dev4"
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 = [
@@ -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"] == {}
@@ -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()