dominus-sdk-python 4.1.0__tar.gz → 4.1.1__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.
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/PKG-INFO +2 -1
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/README.md +1 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/__init__.py +1 -1
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/helpers/sse.py +74 -17
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/start.py +6 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus_sdk_python.egg-info/PKG-INFO +2 -1
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/pyproject.toml +1 -1
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_control_plane_namespaces.py +2 -2
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_transport_compat.py +65 -1
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/config/__init__.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/config/endpoints.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/errors.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/helpers/__init__.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/helpers/auth.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/helpers/cache.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/helpers/console_capture.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/helpers/core.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/helpers/crypto.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/helpers/trace.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/__init__.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/admin.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/ai.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/artifacts.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/auth.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/authority.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/browser.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/courier.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/db.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/ddl.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/deployer.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/fastapi.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/files.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/health.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/jobs.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/logs.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/portal.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/processor.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/redis.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/secrets.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/secure.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/sync.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/warden.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/namespaces/workflow.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus/services/__init__.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus_sdk_python.egg-info/SOURCES.txt +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus_sdk_python.egg-info/requires.txt +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus_sdk_python.egg-info/top_level.txt +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/setup.cfg +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_auth.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_authority_public_vocabulary.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_browser_namespace.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_errors.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_flat_commands.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_health.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_logs.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_provisioning_parity.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_public_exports.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_workflow_lifecycle.py +0 -0
- {dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_workflow_refs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dominus-sdk-python
|
|
3
|
-
Version: 4.1.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: Python SDK for the Dominus gateway-first platform
|
|
5
5
|
Author-email: CareBridge Systems <dev@carebridge.io>
|
|
6
6
|
License: Proprietary
|
|
@@ -171,6 +171,7 @@ JWT and selected scope headers directly through Gateway.
|
|
|
171
171
|
- Service JWTs are cached for 14 minutes with a 60-second refresh window
|
|
172
172
|
- Auth-required worker routes still send base64-encoded JSON bodies as `text/plain`
|
|
173
173
|
- Responses may arrive as legacy base64 JSON or raw JSON; the SDK accepts both
|
|
174
|
+
- Finite workflow/orchestration replay routes may return `text/event-stream`; the SDK normalizes them into event lists
|
|
174
175
|
- Gateway-routed namespaces translate `/api/*` to `/svc/*`
|
|
175
176
|
- SSE and binary helpers bypass JSON decoding where appropriate
|
|
176
177
|
|
|
@@ -138,6 +138,7 @@ JWT and selected scope headers directly through Gateway.
|
|
|
138
138
|
- Service JWTs are cached for 14 minutes with a 60-second refresh window
|
|
139
139
|
- Auth-required worker routes still send base64-encoded JSON bodies as `text/plain`
|
|
140
140
|
- Responses may arrive as legacy base64 JSON or raw JSON; the SDK accepts both
|
|
141
|
+
- Finite workflow/orchestration replay routes may return `text/event-stream`; the SDK normalizes them into event lists
|
|
141
142
|
- Gateway-routed namespaces translate `/api/*` to `/svc/*`
|
|
142
143
|
- SSE and binary helpers bypass JSON decoding where appropriate
|
|
143
144
|
|
|
@@ -1,14 +1,36 @@
|
|
|
1
1
|
"""
|
|
2
2
|
SSE (Server-Sent Events) streaming helper.
|
|
3
3
|
|
|
4
|
-
Provides async
|
|
5
|
-
Used by dominus.ai.stream_agent(), dominus.ai.complete_stream(),
|
|
4
|
+
Provides async and buffered parsing helpers for SSE responses.
|
|
5
|
+
Used by dominus.ai.stream_agent(), dominus.ai.complete_stream(), and finite
|
|
6
|
+
execution replay surfaces that return `text/event-stream`.
|
|
6
7
|
"""
|
|
7
8
|
import json
|
|
8
|
-
from typing import Any, AsyncGenerator, Callable, Dict, Optional
|
|
9
|
+
from typing import Any, AsyncGenerator, Callable, Dict, List, Optional
|
|
9
10
|
import httpx
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
def _decode_sse_payload(
|
|
14
|
+
data_buffer: List[str],
|
|
15
|
+
event_type: Optional[str],
|
|
16
|
+
) -> Optional[Dict[str, Any]]:
|
|
17
|
+
if not data_buffer:
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
data_str = "\n".join(data_buffer)
|
|
21
|
+
if data_str == "[DONE]":
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
data = json.loads(data_str)
|
|
26
|
+
except json.JSONDecodeError:
|
|
27
|
+
data = {"raw": data_str}
|
|
28
|
+
|
|
29
|
+
if event_type:
|
|
30
|
+
data["_event"] = event_type
|
|
31
|
+
return data
|
|
32
|
+
|
|
33
|
+
|
|
12
34
|
async def stream_sse(
|
|
13
35
|
response: httpx.Response,
|
|
14
36
|
on_event: Optional[Callable[[Dict[str, Any]], None]] = None
|
|
@@ -42,24 +64,13 @@ async def stream_sse(
|
|
|
42
64
|
# Empty line = end of event
|
|
43
65
|
if not line:
|
|
44
66
|
if data_buffer:
|
|
45
|
-
|
|
67
|
+
data = _decode_sse_payload(data_buffer, event_type)
|
|
46
68
|
data_buffer = []
|
|
69
|
+
event_type = None
|
|
47
70
|
|
|
48
|
-
|
|
49
|
-
if data_str == "[DONE]":
|
|
71
|
+
if data is None:
|
|
50
72
|
break
|
|
51
73
|
|
|
52
|
-
try:
|
|
53
|
-
data = json.loads(data_str)
|
|
54
|
-
except json.JSONDecodeError:
|
|
55
|
-
# Non-JSON data - wrap in raw field
|
|
56
|
-
data = {"raw": data_str}
|
|
57
|
-
|
|
58
|
-
# Add event type if present
|
|
59
|
-
if event_type:
|
|
60
|
-
data["_event"] = event_type
|
|
61
|
-
event_type = None
|
|
62
|
-
|
|
63
74
|
if on_event:
|
|
64
75
|
on_event(data)
|
|
65
76
|
yield data
|
|
@@ -85,6 +96,52 @@ async def stream_sse(
|
|
|
85
96
|
pass
|
|
86
97
|
|
|
87
98
|
|
|
99
|
+
def parse_sse_text(payload: str) -> List[Dict[str, Any]]:
|
|
100
|
+
"""
|
|
101
|
+
Parse a finite SSE payload that has already been fully buffered.
|
|
102
|
+
|
|
103
|
+
Used by replay-style GET routes that return `text/event-stream` but are not
|
|
104
|
+
long-lived subscriptions.
|
|
105
|
+
"""
|
|
106
|
+
events: List[Dict[str, Any]] = []
|
|
107
|
+
event_type: Optional[str] = None
|
|
108
|
+
data_buffer: List[str] = []
|
|
109
|
+
|
|
110
|
+
for raw_line in payload.splitlines():
|
|
111
|
+
line = raw_line.strip()
|
|
112
|
+
|
|
113
|
+
if not line:
|
|
114
|
+
if data_buffer:
|
|
115
|
+
data = _decode_sse_payload(data_buffer, event_type)
|
|
116
|
+
data_buffer = []
|
|
117
|
+
event_type = None
|
|
118
|
+
if data is None:
|
|
119
|
+
break
|
|
120
|
+
events.append(data)
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
if line.startswith("event:"):
|
|
124
|
+
event_type = line[6:].strip()
|
|
125
|
+
elif line.startswith("data:"):
|
|
126
|
+
data_str = line[5:].strip()
|
|
127
|
+
if data_str == "[DONE]":
|
|
128
|
+
break
|
|
129
|
+
data_buffer.append(data_str)
|
|
130
|
+
elif line.startswith("id:"):
|
|
131
|
+
pass
|
|
132
|
+
elif line.startswith("retry:"):
|
|
133
|
+
pass
|
|
134
|
+
elif line.startswith(":"):
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
if data_buffer:
|
|
138
|
+
data = _decode_sse_payload(data_buffer, event_type)
|
|
139
|
+
if data is not None:
|
|
140
|
+
events.append(data)
|
|
141
|
+
|
|
142
|
+
return events
|
|
143
|
+
|
|
144
|
+
|
|
88
145
|
async def collect_stream(
|
|
89
146
|
response: httpx.Response,
|
|
90
147
|
on_event: Optional[Callable[[Dict[str, Any]], None]] = None
|
|
@@ -468,6 +468,12 @@ class Dominus:
|
|
|
468
468
|
endpoint,
|
|
469
469
|
)
|
|
470
470
|
|
|
471
|
+
content_type = str(response.headers.get("content-type", "")).lower()
|
|
472
|
+
if "text/event-stream" in content_type:
|
|
473
|
+
from .helpers.sse import parse_sse_text
|
|
474
|
+
|
|
475
|
+
return parse_sse_text(response.text)
|
|
476
|
+
|
|
471
477
|
from .helpers.core import _decode_json_or_b64_json
|
|
472
478
|
|
|
473
479
|
result = _decode_json_or_b64_json(response.text)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dominus-sdk-python
|
|
3
|
-
Version: 4.1.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: Python SDK for the Dominus gateway-first platform
|
|
5
5
|
Author-email: CareBridge Systems <dev@carebridge.io>
|
|
6
6
|
License: Proprietary
|
|
@@ -171,6 +171,7 @@ JWT and selected scope headers directly through Gateway.
|
|
|
171
171
|
- Service JWTs are cached for 14 minutes with a 60-second refresh window
|
|
172
172
|
- Auth-required worker routes still send base64-encoded JSON bodies as `text/plain`
|
|
173
173
|
- Responses may arrive as legacy base64 JSON or raw JSON; the SDK accepts both
|
|
174
|
+
- Finite workflow/orchestration replay routes may return `text/event-stream`; the SDK normalizes them into event lists
|
|
174
175
|
- Gateway-routed namespaces translate `/api/*` to `/svc/*`
|
|
175
176
|
- SSE and binary helpers bypass JSON decoding where appropriate
|
|
176
177
|
|
{dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_control_plane_namespaces.py
RENAMED
|
@@ -23,11 +23,11 @@ async def test_deployer_namespace_uses_gateway_fetch(monkeypatch, sdk):
|
|
|
23
23
|
|
|
24
24
|
monkeypatch.setattr(sdk, "gateway_fetch", fake_gateway_fetch)
|
|
25
25
|
|
|
26
|
-
result = await sdk.deployer.request("/
|
|
26
|
+
result = await sdk.deployer.request("/configs", method="GET")
|
|
27
27
|
|
|
28
28
|
assert result == {"ok": True}
|
|
29
29
|
assert calls == [
|
|
30
|
-
("/svc/deployer/
|
|
30
|
+
("/svc/deployer/configs", {"method": "GET", "body": None, "headers": None, "timeout": 30.0})
|
|
31
31
|
]
|
|
32
32
|
|
|
33
33
|
|
|
@@ -16,9 +16,10 @@ from dominus.namespaces.auth import AuthNamespace # noqa: E402
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class FakeResponse:
|
|
19
|
-
def __init__(self, text: str, *, status_code: int = 200):
|
|
19
|
+
def __init__(self, text: str, *, status_code: int = 200, headers=None):
|
|
20
20
|
self.text = text
|
|
21
21
|
self.status_code = status_code
|
|
22
|
+
self.headers = headers or {}
|
|
22
23
|
|
|
23
24
|
@property
|
|
24
25
|
def is_error(self) -> bool:
|
|
@@ -81,6 +82,46 @@ class UnwrappedRequestClient:
|
|
|
81
82
|
return FakeResponse(json.dumps({"secret": {"key": "PROVISION_REDIS_URL_REST", "value": "redis-url"}}))
|
|
82
83
|
|
|
83
84
|
|
|
85
|
+
class SseReplayClient:
|
|
86
|
+
calls = []
|
|
87
|
+
|
|
88
|
+
def __init__(self, *, base_url, headers, timeout):
|
|
89
|
+
self.base_url = base_url
|
|
90
|
+
self.headers = headers
|
|
91
|
+
self.timeout = timeout
|
|
92
|
+
|
|
93
|
+
async def __aenter__(self):
|
|
94
|
+
return self
|
|
95
|
+
|
|
96
|
+
async def __aexit__(self, exc_type, exc, tb):
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
async def get(self, endpoint):
|
|
100
|
+
self.calls.append(
|
|
101
|
+
{
|
|
102
|
+
"endpoint": endpoint,
|
|
103
|
+
"base_url": self.base_url,
|
|
104
|
+
"headers": self.headers,
|
|
105
|
+
"timeout": self.timeout,
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
return FakeResponse(
|
|
109
|
+
"\n".join(
|
|
110
|
+
[
|
|
111
|
+
'event: workflow_start',
|
|
112
|
+
'data: {"id":"1-0","type":"workflow_start"}',
|
|
113
|
+
"",
|
|
114
|
+
'event: workflow_complete',
|
|
115
|
+
'data: {"id":"2-0","type":"workflow_complete"}',
|
|
116
|
+
"",
|
|
117
|
+
'data: [DONE]',
|
|
118
|
+
"",
|
|
119
|
+
]
|
|
120
|
+
),
|
|
121
|
+
headers={"content-type": "text/event-stream; charset=utf-8"},
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
84
125
|
class ServiceJwtClient:
|
|
85
126
|
def __init__(self, *, base_url, headers, timeout, proxy):
|
|
86
127
|
self.base_url = base_url
|
|
@@ -174,6 +215,29 @@ async def test_request_accepts_unwrapped_control_plane_success(monkeypatch, sdk)
|
|
|
174
215
|
assert UnwrappedRequestClient.calls[0]["endpoint"] == "/svc/warden/secrets/PROVISION_REDIS_URL_REST"
|
|
175
216
|
|
|
176
217
|
|
|
218
|
+
@pytest.mark.asyncio
|
|
219
|
+
async def test_request_replays_finite_sse_payloads(monkeypatch, sdk):
|
|
220
|
+
SseReplayClient.calls = []
|
|
221
|
+
|
|
222
|
+
async def fake_ensure_valid_jwt(psk_token, base_url):
|
|
223
|
+
return "jwt-abc"
|
|
224
|
+
|
|
225
|
+
monkeypatch.setattr(core_module, "_ensure_valid_jwt", fake_ensure_valid_jwt)
|
|
226
|
+
monkeypatch.setattr(start_module.httpx, "AsyncClient", SseReplayClient)
|
|
227
|
+
|
|
228
|
+
result = await sdk._request(
|
|
229
|
+
"/api/workflow/executions/exec-123/events?count=2",
|
|
230
|
+
method="GET",
|
|
231
|
+
use_gateway=True,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
assert result == [
|
|
235
|
+
{"id": "1-0", "type": "workflow_start", "_event": "workflow_start"},
|
|
236
|
+
{"id": "2-0", "type": "workflow_complete", "_event": "workflow_complete"},
|
|
237
|
+
]
|
|
238
|
+
assert SseReplayClient.calls[0]["endpoint"] == "/svc/workflow/executions/exec-123/events?count=2"
|
|
239
|
+
|
|
240
|
+
|
|
177
241
|
def test_dominus_constructor_accepts_gateway_context_keywords():
|
|
178
242
|
sdk = start_module.Dominus(
|
|
179
243
|
gateway_user_token="user-jwt",
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus_sdk_python.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus_sdk_python.egg-info/requires.txt
RENAMED
|
File without changes
|
{dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/dominus_sdk_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-4.1.0 → dominus_sdk_python-4.1.1}/tests/test_authority_public_vocabulary.py
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
|