dataquery-sdk 0.1.1__tar.gz → 0.1.2__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.
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/CHANGELOG.md +3 -1
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/PKG-INFO +3 -7
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/__init__.py +2 -2
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/auth.py +7 -25
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/auto_download.py +22 -31
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/cli.py +11 -33
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/client.py +101 -255
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/config.py +6 -16
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/connection_pool.py +17 -61
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/dataquery.py +82 -227
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/exceptions.py +6 -18
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/logging_config.py +6 -18
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/models.py +113 -334
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/rate_limiter.py +5 -16
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/retry.py +8 -25
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/utils.py +11 -25
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery_sdk.egg-info/PKG-INFO +3 -7
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery_sdk.egg-info/requires.txt +2 -6
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/pyproject.toml +23 -35
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/CONTRIBUTING.md +0 -0
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/LICENSE +0 -0
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/MANIFEST.in +0 -0
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/README.md +0 -0
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery/py.typed +0 -0
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery_sdk.egg-info/SOURCES.txt +0 -0
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery_sdk.egg-info/dependency_links.txt +0 -0
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery_sdk.egg-info/entry_points.txt +0 -0
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/dataquery_sdk.egg-info/top_level.txt +0 -0
- {dataquery_sdk-0.1.1 → dataquery_sdk-0.1.2}/setup.cfg +0 -0
|
@@ -18,4 +18,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
18
18
|
## [0.1.0] - 2025-12-10
|
|
19
19
|
- Time series data made optional
|
|
20
20
|
## [0.1.1] - 2026-02-18
|
|
21
|
-
- Non blocking IO
|
|
21
|
+
- Non blocking IO
|
|
22
|
+
## [0.1.2] - 2026-02-18
|
|
23
|
+
- File already exists status added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataquery-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Python SDK for DATAQUERY Data API - Query, download, and check availability of economic data files
|
|
5
5
|
Author-email: DATAQUERY SDK Team <support@dataquery.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/dataquery/dataquery-sdk
|
|
@@ -34,10 +34,8 @@ Provides-Extra: dev
|
|
|
34
34
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
35
35
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
36
36
|
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
37
|
-
Requires-Dist:
|
|
38
|
-
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
39
38
|
Requires-Dist: mypy>=1.5.0; extra == "dev"
|
|
40
|
-
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
41
39
|
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
42
40
|
Requires-Dist: pip-audit>=2.7.0; extra == "dev"
|
|
43
41
|
Requires-Dist: build>=1.3.0; extra == "dev"
|
|
@@ -53,10 +51,8 @@ Provides-Extra: all
|
|
|
53
51
|
Requires-Dist: pytest>=7.0.0; extra == "all"
|
|
54
52
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "all"
|
|
55
53
|
Requires-Dist: pytest-cov>=4.0.0; extra == "all"
|
|
56
|
-
Requires-Dist:
|
|
57
|
-
Requires-Dist: flake8>=6.0.0; extra == "all"
|
|
54
|
+
Requires-Dist: ruff>=0.4.0; extra == "all"
|
|
58
55
|
Requires-Dist: mypy>=1.5.0; extra == "all"
|
|
59
|
-
Requires-Dist: isort>=5.12.0; extra == "all"
|
|
60
56
|
Requires-Dist: pre-commit>=3.0.0; extra == "all"
|
|
61
57
|
Requires-Dist: mkdocs>=1.5.0; extra == "all"
|
|
62
58
|
Requires-Dist: mkdocs-material>=9.0.0; extra == "all"
|
|
@@ -14,9 +14,9 @@ Quick Start:
|
|
|
14
14
|
For more information, visit: https://github.com/dataquery/dataquery-sdk
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
__version__ = "0.1.
|
|
17
|
+
__version__ = "0.1.2"
|
|
18
18
|
__author__ = "DATAQUERY SDK Team"
|
|
19
|
-
__email__ = "
|
|
19
|
+
__email__ = "dataquery_support@jpmorgan.com"
|
|
20
20
|
__license__ = "MIT"
|
|
21
21
|
__url__ = "https://github.com/jpmorganchase/dataquery-sdk"
|
|
22
22
|
|
|
@@ -32,9 +32,7 @@ class TokenManager:
|
|
|
32
32
|
# Prefer explicit token_storage_dir only when token storage is enabled,
|
|
33
33
|
# else fallback to download_dir/.tokens if download_dir is provided.
|
|
34
34
|
base_dir: Optional[Path] = None
|
|
35
|
-
token_storage_enabled = bool(
|
|
36
|
-
getattr(self.config, "token_storage_enabled", False)
|
|
37
|
-
)
|
|
35
|
+
token_storage_enabled = bool(getattr(self.config, "token_storage_enabled", False))
|
|
38
36
|
token_storage_dir = getattr(self.config, "token_storage_dir", None)
|
|
39
37
|
if token_storage_enabled and token_storage_dir:
|
|
40
38
|
base_dir = Path(token_storage_dir)
|
|
@@ -97,9 +95,7 @@ class TokenManager:
|
|
|
97
95
|
|
|
98
96
|
# Validate required fields
|
|
99
97
|
if not self.config.client_id or not self.config.client_secret:
|
|
100
|
-
raise ConfigurationError(
|
|
101
|
-
"client_id and client_secret are required for OAuth"
|
|
102
|
-
)
|
|
98
|
+
raise ConfigurationError("client_id and client_secret are required for OAuth")
|
|
103
99
|
|
|
104
100
|
# Create token request
|
|
105
101
|
token_request = TokenRequest(
|
|
@@ -142,9 +138,7 @@ class TokenManager:
|
|
|
142
138
|
status=response.status,
|
|
143
139
|
error=error_data,
|
|
144
140
|
)
|
|
145
|
-
raise AuthenticationError(
|
|
146
|
-
f"OAuth token request failed: {response.status}"
|
|
147
|
-
)
|
|
141
|
+
raise AuthenticationError(f"OAuth token request failed: {response.status}")
|
|
148
142
|
|
|
149
143
|
except Exception as e:
|
|
150
144
|
logger.error("Error getting OAuth token", error=str(e))
|
|
@@ -220,9 +214,7 @@ class TokenManager:
|
|
|
220
214
|
|
|
221
215
|
# Convert timestamp back to datetime
|
|
222
216
|
if "issued_at" in token_data:
|
|
223
|
-
token_data["issued_at"] = datetime.fromisoformat(
|
|
224
|
-
token_data["issued_at"]
|
|
225
|
-
)
|
|
217
|
+
token_data["issued_at"] = datetime.fromisoformat(token_data["issued_at"])
|
|
226
218
|
|
|
227
219
|
self.current_token = OAuthToken(**token_data)
|
|
228
220
|
|
|
@@ -232,9 +224,7 @@ class TokenManager:
|
|
|
232
224
|
self.current_token = None
|
|
233
225
|
return None
|
|
234
226
|
|
|
235
|
-
logger.info(
|
|
236
|
-
"Token loaded from storage", expires_at=self.current_token.expires_at
|
|
237
|
-
)
|
|
227
|
+
logger.info("Token loaded from storage", expires_at=self.current_token.expires_at)
|
|
238
228
|
return self.current_token
|
|
239
229
|
|
|
240
230
|
except Exception as e:
|
|
@@ -297,16 +287,8 @@ class TokenManager:
|
|
|
297
287
|
return {
|
|
298
288
|
"status": self.current_token.status.value,
|
|
299
289
|
"token_type": self.current_token.token_type,
|
|
300
|
-
"issued_at": (
|
|
301
|
-
|
|
302
|
-
if self.current_token.issued_at
|
|
303
|
-
else None
|
|
304
|
-
),
|
|
305
|
-
"expires_at": (
|
|
306
|
-
self.current_token.expires_at.isoformat()
|
|
307
|
-
if self.current_token.expires_at
|
|
308
|
-
else None
|
|
309
|
-
),
|
|
290
|
+
"issued_at": (self.current_token.issued_at.isoformat() if self.current_token.issued_at else None),
|
|
291
|
+
"expires_at": (self.current_token.expires_at.isoformat() if self.current_token.expires_at else None),
|
|
310
292
|
"is_expired": self.current_token.is_expired,
|
|
311
293
|
"has_refresh_token": self.current_token.refresh_token is not None,
|
|
312
294
|
}
|
|
@@ -121,8 +121,7 @@ class AutoDownloadManager:
|
|
|
121
121
|
self._stop_event.clear()
|
|
122
122
|
|
|
123
123
|
self.logger.info(
|
|
124
|
-
f"Starting auto-download for group '{self.group_id}' "
|
|
125
|
-
f"(interval: {self.interval_minutes} minutes)"
|
|
124
|
+
f"Starting auto-download for group '{self.group_id}' (interval: {self.interval_minutes} minutes)"
|
|
126
125
|
)
|
|
127
126
|
|
|
128
127
|
# Start the monitoring task
|
|
@@ -145,9 +144,7 @@ class AutoDownloadManager:
|
|
|
145
144
|
try:
|
|
146
145
|
await asyncio.wait_for(self._task, timeout=5.0)
|
|
147
146
|
except asyncio.TimeoutError:
|
|
148
|
-
self.logger.warning(
|
|
149
|
-
"Monitoring task did not stop gracefully, cancelling..."
|
|
150
|
-
)
|
|
147
|
+
self.logger.warning("Monitoring task did not stop gracefully, cancelling...")
|
|
151
148
|
self._task.cancel()
|
|
152
149
|
try:
|
|
153
150
|
await self._task
|
|
@@ -184,9 +181,7 @@ class AutoDownloadManager:
|
|
|
184
181
|
|
|
185
182
|
# Wait for the specified interval or until stopped
|
|
186
183
|
try:
|
|
187
|
-
await asyncio.wait_for(
|
|
188
|
-
self._stop_event.wait(), timeout=self.interval_minutes * 60
|
|
189
|
-
)
|
|
184
|
+
await asyncio.wait_for(self._stop_event.wait(), timeout=self.interval_minutes * 60)
|
|
190
185
|
# If we reach here, stop was requested
|
|
191
186
|
break
|
|
192
187
|
except asyncio.TimeoutError:
|
|
@@ -220,19 +215,13 @@ class AutoDownloadManager:
|
|
|
220
215
|
)
|
|
221
216
|
|
|
222
217
|
if not isinstance(available_files, list) or not available_files:
|
|
223
|
-
self.logger.debug(
|
|
224
|
-
f"No available files for group '{self.group_id}' between {start_date} and {end_date}"
|
|
225
|
-
)
|
|
218
|
+
self.logger.debug(f"No available files for group '{self.group_id}' between {start_date} and {end_date}")
|
|
226
219
|
return
|
|
227
220
|
|
|
228
|
-
self.logger.debug(
|
|
229
|
-
f"Found {len(available_files)} available file entries for group '{self.group_id}'"
|
|
230
|
-
)
|
|
221
|
+
self.logger.debug(f"Found {len(available_files)} available file entries for group '{self.group_id}'")
|
|
231
222
|
|
|
232
223
|
# Build eligible downloads
|
|
233
|
-
eligible: List[tuple[str, str, str]] = (
|
|
234
|
-
[]
|
|
235
|
-
) # (file_group_id, date_str, file_key)
|
|
224
|
+
eligible: List[tuple[str, str, str]] = [] # (file_group_id, date_str, file_key)
|
|
236
225
|
for item in available_files:
|
|
237
226
|
if not self._running:
|
|
238
227
|
break
|
|
@@ -267,10 +256,7 @@ class AutoDownloadManager:
|
|
|
267
256
|
async with semaphore:
|
|
268
257
|
await self._download_file(fid, dstr, fkey)
|
|
269
258
|
|
|
270
|
-
tasks = [
|
|
271
|
-
asyncio.create_task(worker(fid, dstr, fkey))
|
|
272
|
-
for fid, dstr, fkey in eligible
|
|
273
|
-
]
|
|
259
|
+
tasks = [asyncio.create_task(worker(fid, dstr, fkey)) for fid, dstr, fkey in eligible]
|
|
274
260
|
await asyncio.gather(*tasks, return_exceptions=True)
|
|
275
261
|
|
|
276
262
|
except Exception as e:
|
|
@@ -336,6 +322,7 @@ class AutoDownloadManager:
|
|
|
336
322
|
)
|
|
337
323
|
|
|
338
324
|
succeeded = False
|
|
325
|
+
already_exists = False
|
|
339
326
|
if result is not None and getattr(result, "status", None) is not None:
|
|
340
327
|
try:
|
|
341
328
|
from .models import ( # import inside to avoid cycles in some tools
|
|
@@ -343,21 +330,29 @@ class AutoDownloadManager:
|
|
|
343
330
|
)
|
|
344
331
|
|
|
345
332
|
succeeded = result.status == DownloadStatus.COMPLETED
|
|
333
|
+
already_exists = result.status == DownloadStatus.ALREADY_EXISTS
|
|
346
334
|
except Exception:
|
|
347
335
|
succeeded = False
|
|
348
336
|
|
|
349
|
-
if
|
|
337
|
+
if already_exists:
|
|
350
338
|
self.logger.info(
|
|
351
|
-
"
|
|
339
|
+
"File already exists for '%s' for %s, skipping",
|
|
340
|
+
file_group_id,
|
|
341
|
+
date_str,
|
|
352
342
|
)
|
|
343
|
+
self.stats["files_skipped"] += 1
|
|
344
|
+
self._downloaded_files.add(file_key)
|
|
345
|
+
if file_key in self._failed_files:
|
|
346
|
+
del self._failed_files[file_key]
|
|
347
|
+
|
|
348
|
+
elif succeeded:
|
|
349
|
+
self.logger.info("Successfully downloaded '%s' for %s", file_group_id, date_str)
|
|
353
350
|
self.stats["files_downloaded"] += 1
|
|
354
351
|
self._downloaded_files.add(file_key)
|
|
355
352
|
# Accumulate total bytes if provided
|
|
356
353
|
try:
|
|
357
354
|
if getattr(result, "file_size", None):
|
|
358
|
-
self.stats["total_bytes_downloaded"] += (
|
|
359
|
-
int(result.file_size) or 0
|
|
360
|
-
)
|
|
355
|
+
self.stats["total_bytes_downloaded"] += int(result.file_size) or 0
|
|
361
356
|
except Exception:
|
|
362
357
|
pass
|
|
363
358
|
|
|
@@ -366,11 +361,7 @@ class AutoDownloadManager:
|
|
|
366
361
|
del self._failed_files[file_key]
|
|
367
362
|
|
|
368
363
|
else:
|
|
369
|
-
error_msg = (
|
|
370
|
-
getattr(result, "error_message", "Unknown error")
|
|
371
|
-
if result
|
|
372
|
-
else "No result"
|
|
373
|
-
)
|
|
364
|
+
error_msg = getattr(result, "error_message", "Unknown error") if result else "No result"
|
|
374
365
|
self.logger.error(
|
|
375
366
|
"Download failed for '%s' for %s: %s",
|
|
376
367
|
file_group_id,
|
|
@@ -11,18 +11,14 @@ from dataquery import DataQuery
|
|
|
11
11
|
|
|
12
12
|
def create_parser() -> argparse.ArgumentParser:
|
|
13
13
|
"""Create the top-level CLI parser with subcommands."""
|
|
14
|
-
parser = argparse.ArgumentParser(
|
|
15
|
-
description="Command Line Interface for the DataQuery SDK"
|
|
16
|
-
)
|
|
14
|
+
parser = argparse.ArgumentParser(description="Command Line Interface for the DataQuery SDK")
|
|
17
15
|
parser.add_argument("--env-file", type=str, default=None, help="Path to .env file")
|
|
18
16
|
subparsers = parser.add_subparsers(dest="command")
|
|
19
17
|
|
|
20
18
|
# groups
|
|
21
19
|
p_groups = subparsers.add_parser("groups", help="List or search groups")
|
|
22
20
|
p_groups.add_argument("--json", action="store_true", help="Output JSON")
|
|
23
|
-
p_groups.add_argument(
|
|
24
|
-
"--limit", type=int, default=None, help="Limit number of results"
|
|
25
|
-
)
|
|
21
|
+
p_groups.add_argument("--limit", type=int, default=None, help="Limit number of results")
|
|
26
22
|
p_groups.add_argument("--search", type=str, default=None, help="Search keywords")
|
|
27
23
|
|
|
28
24
|
# files
|
|
@@ -43,22 +39,14 @@ def create_parser() -> argparse.ArgumentParser:
|
|
|
43
39
|
p_dl.add_argument("--file-group-id", default=None)
|
|
44
40
|
p_dl.add_argument("--file-datetime", default=None)
|
|
45
41
|
p_dl.add_argument("--destination", type=str, default=None)
|
|
46
|
-
p_dl.add_argument(
|
|
47
|
-
"--watch", action="store_true", help="Watch a group for new files"
|
|
48
|
-
)
|
|
42
|
+
p_dl.add_argument("--watch", action="store_true", help="Watch a group for new files")
|
|
49
43
|
p_dl.add_argument("--group-id", default=None, help="Required with --watch")
|
|
50
44
|
p_dl.add_argument("--json", action="store_true")
|
|
51
|
-
p_dl.add_argument(
|
|
52
|
-
|
|
53
|
-
)
|
|
54
|
-
p_dl.add_argument(
|
|
55
|
-
"--chunk-size", type=int, default=None, help="Chunk size in bytes"
|
|
56
|
-
)
|
|
45
|
+
p_dl.add_argument("--num-parts", type=int, default=5, help="Number of parallel parts")
|
|
46
|
+
p_dl.add_argument("--chunk-size", type=int, default=None, help="Chunk size in bytes")
|
|
57
47
|
|
|
58
48
|
# download-group
|
|
59
|
-
p_dlg = subparsers.add_parser(
|
|
60
|
-
"download-group", help="Download all files in a group"
|
|
61
|
-
)
|
|
49
|
+
p_dlg = subparsers.add_parser("download-group", help="Download all files in a group")
|
|
62
50
|
p_dlg.add_argument("--group-id", required=True)
|
|
63
51
|
p_dlg.add_argument("--start-date", required=True)
|
|
64
52
|
p_dlg.add_argument("--end-date", required=True)
|
|
@@ -101,9 +89,7 @@ async def cmd_groups(args: argparse.Namespace) -> int:
|
|
|
101
89
|
for g in items:
|
|
102
90
|
try:
|
|
103
91
|
d = g.model_dump()
|
|
104
|
-
print(
|
|
105
|
-
f"{d.get('group_id') or d.get('group-id')}\t{d.get('group_name') or d.get('group-name')}"
|
|
106
|
-
)
|
|
92
|
+
print(f"{d.get('group_id') or d.get('group-id')}\t{d.get('group_name') or d.get('group-name')}")
|
|
107
93
|
except Exception:
|
|
108
94
|
print(str(g))
|
|
109
95
|
return 0
|
|
@@ -125,9 +111,7 @@ async def cmd_files(args: argparse.Namespace) -> int:
|
|
|
125
111
|
for f in files:
|
|
126
112
|
try:
|
|
127
113
|
d = f.model_dump()
|
|
128
|
-
print(
|
|
129
|
-
f"{d.get('file_group_id') or d.get('file-group-id')}\t{d.get('file_type')}"
|
|
130
|
-
)
|
|
114
|
+
print(f"{d.get('file_group_id') or d.get('file-group-id')}\t{d.get('file_type')}")
|
|
131
115
|
except Exception:
|
|
132
116
|
print(str(f))
|
|
133
117
|
return 0
|
|
@@ -135,9 +119,7 @@ async def cmd_files(args: argparse.Namespace) -> int:
|
|
|
135
119
|
|
|
136
120
|
async def cmd_availability(args: argparse.Namespace) -> int:
|
|
137
121
|
async with DataQuery(args.env_file) as dq:
|
|
138
|
-
avail = await dq.check_availability_async(
|
|
139
|
-
args.file_group_id, args.file_datetime
|
|
140
|
-
)
|
|
122
|
+
avail = await dq.check_availability_async(args.file_group_id, args.file_datetime)
|
|
141
123
|
if args.json:
|
|
142
124
|
try:
|
|
143
125
|
print(json.dumps(getattr(avail, "model_dump")(), indent=2))
|
|
@@ -210,9 +192,7 @@ async def cmd_download_group(args: argparse.Namespace) -> int:
|
|
|
210
192
|
if args.json:
|
|
211
193
|
print(json.dumps(results, indent=2))
|
|
212
194
|
else:
|
|
213
|
-
print(
|
|
214
|
-
f"Downloaded {results.get('successful', 0)} files to {args.destination}"
|
|
215
|
-
)
|
|
195
|
+
print(f"Downloaded {results.get('successful', 0)} files to {args.destination}")
|
|
216
196
|
if results.get("failed", 0) > 0:
|
|
217
197
|
print(f"Failed: {results.get('failed', 0)}")
|
|
218
198
|
return 0
|
|
@@ -221,9 +201,7 @@ async def cmd_download_group(args: argparse.Namespace) -> int:
|
|
|
221
201
|
def cmd_config_show(args: argparse.Namespace) -> int:
|
|
222
202
|
from dataquery.config import EnvConfig
|
|
223
203
|
|
|
224
|
-
EnvConfig.create_client_config(
|
|
225
|
-
env_file=Path(args.env_file) if getattr(args, "env_file", None) else None
|
|
226
|
-
)
|
|
204
|
+
EnvConfig.create_client_config(env_file=Path(args.env_file) if getattr(args, "env_file", None) else None)
|
|
227
205
|
print("Configuration loaded")
|
|
228
206
|
return 0
|
|
229
207
|
|