dsw-tdk 4.26.0rc1__tar.gz → 4.27.0__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.
- {dsw_tdk-4.26.0rc1/dsw_tdk.egg-info → dsw_tdk-4.27.0}/PKG-INFO +12 -12
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/__init__.py +4 -3
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/__main__.py +1 -0
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/api_client.py +23 -25
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/build_info.py +4 -4
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/cli.py +15 -10
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/config.py +5 -8
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/consts.py +2 -1
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/core.py +27 -22
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/model.py +44 -42
- dsw_tdk-4.27.0/dsw/tdk/py.typed +0 -0
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/utils.py +8 -8
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/validation.py +33 -36
- dsw_tdk-4.27.0/pyproject.toml +57 -0
- dsw_tdk-4.26.0rc1/CHANGELOG.md +0 -661
- dsw_tdk-4.26.0rc1/LICENSE +0 -201
- dsw_tdk-4.26.0rc1/MANIFEST.in +0 -2
- dsw_tdk-4.26.0rc1/PKG-INFO +0 -154
- dsw_tdk-4.26.0rc1/dsw_tdk.egg-info/SOURCES.txt +0 -37
- dsw_tdk-4.26.0rc1/dsw_tdk.egg-info/dependency_links.txt +0 -1
- dsw_tdk-4.26.0rc1/dsw_tdk.egg-info/entry_points.txt +0 -2
- dsw_tdk-4.26.0rc1/dsw_tdk.egg-info/not-zip-safe +0 -1
- dsw_tdk-4.26.0rc1/dsw_tdk.egg-info/requires.txt +0 -13
- dsw_tdk-4.26.0rc1/dsw_tdk.egg-info/top_level.txt +0 -1
- dsw_tdk-4.26.0rc1/pyproject.toml +0 -64
- dsw_tdk-4.26.0rc1/setup.cfg +0 -4
- dsw_tdk-4.26.0rc1/setup.py +0 -3
- dsw_tdk-4.26.0rc1/tests/test_basic.py +0 -26
- dsw_tdk-4.26.0rc1/tests/test_cmd_dot-env.py +0 -78
- dsw_tdk-4.26.0rc1/tests/test_cmd_get.py +0 -76
- dsw_tdk-4.26.0rc1/tests/test_cmd_list.py +0 -53
- dsw_tdk-4.26.0rc1/tests/test_cmd_new.py +0 -77
- dsw_tdk-4.26.0rc1/tests/test_cmd_package.py +0 -57
- dsw_tdk-4.26.0rc1/tests/test_cmd_put.py +0 -56
- dsw_tdk-4.26.0rc1/tests/test_cmd_unpackage.py +0 -57
- dsw_tdk-4.26.0rc1/tests/test_cmd_verify.py +0 -46
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/README.md +0 -0
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/templates/LICENSE.j2 +0 -0
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/templates/README.md.j2 +0 -0
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/templates/env.j2 +0 -0
- {dsw_tdk-4.26.0rc1 → dsw_tdk-4.27.0}/dsw/tdk/templates/starter.j2 +0 -0
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: dsw-tdk
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.27.0
|
|
4
4
|
Summary: Data Stewardship Wizard Template Development Toolkit
|
|
5
|
+
Keywords: documents,dsw,jinja2,template,toolkit
|
|
6
|
+
Author: Marek Suchánek
|
|
5
7
|
Author-email: Marek Suchánek <marek.suchanek@ds-wizard.org>
|
|
6
8
|
License: Apache License 2.0
|
|
7
|
-
Project-URL: Homepage, https://ds-wizard.org
|
|
8
|
-
Project-URL: Repository, https://github.com/ds-wizard/engine-tools
|
|
9
|
-
Project-URL: Documentation, https://guide.ds-wizard.org
|
|
10
|
-
Keywords: documents,dsw,jinja2,template,toolkit
|
|
11
9
|
Classifier: Framework :: AsyncIO
|
|
12
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
11
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -18,22 +16,24 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
18
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
17
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
20
18
|
Classifier: Topic :: Utilities
|
|
21
|
-
Requires-Python: <4,>=3.10
|
|
22
|
-
Description-Content-Type: text/markdown
|
|
23
|
-
License-File: LICENSE
|
|
24
19
|
Requires-Dist: aiohttp
|
|
25
20
|
Requires-Dist: click
|
|
26
21
|
Requires-Dist: colorama
|
|
27
22
|
Requires-Dist: humanize
|
|
28
|
-
Requires-Dist:
|
|
23
|
+
Requires-Dist: jinja2
|
|
29
24
|
Requires-Dist: multidict
|
|
30
25
|
Requires-Dist: pathspec
|
|
31
26
|
Requires-Dist: python-dotenv
|
|
32
27
|
Requires-Dist: python-slugify
|
|
33
28
|
Requires-Dist: watchfiles
|
|
29
|
+
Requires-Dist: pytest ; extra == 'test'
|
|
30
|
+
Requires-Python: >=3.10, <4
|
|
31
|
+
Project-URL: Homepage, https://ds-wizard.org
|
|
32
|
+
Project-URL: Repository, https://github.com/ds-wizard/engine-tools
|
|
33
|
+
Project-URL: Documentation, https://guide.ds-wizard.org
|
|
34
|
+
Project-URL: Issues, https://github.com/ds-wizard/ds-wizard/issues
|
|
34
35
|
Provides-Extra: test
|
|
35
|
-
|
|
36
|
-
Dynamic: license-file
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
37
|
|
|
38
38
|
# dsw-tdk
|
|
39
39
|
|
|
@@ -6,10 +6,11 @@ Template Development Kit for `Data Stewardship Wizard`_.
|
|
|
6
6
|
https://ds-wizard.org
|
|
7
7
|
|
|
8
8
|
"""
|
|
9
|
+
from . import consts
|
|
9
10
|
from .cli import main
|
|
10
|
-
from .consts import APP, VERSION
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
|
|
13
|
+
__app__ = consts.APP
|
|
14
|
+
__version__ = consts.VERSION
|
|
14
15
|
|
|
15
16
|
__all__ = ['__app__', '__version__', 'main']
|
|
@@ -5,7 +5,7 @@ import urllib.parse
|
|
|
5
5
|
import aiohttp
|
|
6
6
|
import aiohttp.client_exceptions
|
|
7
7
|
|
|
8
|
-
from .
|
|
8
|
+
from . import consts
|
|
9
9
|
from .model import Template, TemplateFile, TemplateFileType
|
|
10
10
|
|
|
11
11
|
|
|
@@ -34,27 +34,27 @@ def handle_client_errors(func):
|
|
|
34
34
|
except aiohttp.client_exceptions.ContentTypeError as e:
|
|
35
35
|
raise WizardCommunicationError(
|
|
36
36
|
reason='Unexpected response type',
|
|
37
|
-
message=e.message
|
|
37
|
+
message=e.message,
|
|
38
38
|
) from e
|
|
39
39
|
except aiohttp.client_exceptions.ClientResponseError as e:
|
|
40
40
|
raise WizardCommunicationError(
|
|
41
41
|
reason='Error response status',
|
|
42
|
-
message=f'Server responded with error HTTP status {e.status}: {e.message}'
|
|
42
|
+
message=f'Server responded with error HTTP status {e.status}: {e.message}',
|
|
43
43
|
) from e
|
|
44
44
|
except aiohttp.client_exceptions.InvalidURL as e:
|
|
45
45
|
raise WizardCommunicationError(
|
|
46
46
|
reason='Invalid URL',
|
|
47
|
-
message=f'Provided API URL seems invalid: {e.url}'
|
|
47
|
+
message=f'Provided API URL seems invalid: {e.url}',
|
|
48
48
|
) from e
|
|
49
49
|
except aiohttp.client_exceptions.ClientConnectorError as e:
|
|
50
50
|
raise WizardCommunicationError(
|
|
51
51
|
reason='Server unreachable',
|
|
52
|
-
message=f'Desired server is not reachable (errno {e.os_error.errno})'
|
|
52
|
+
message=f'Desired server is not reachable (errno {e.os_error.errno})',
|
|
53
53
|
) from e
|
|
54
54
|
except Exception as e:
|
|
55
55
|
raise WizardCommunicationError(
|
|
56
56
|
reason='Communication error',
|
|
57
|
-
message=f'Communication with server failed ({e})'
|
|
57
|
+
message=f'Communication with server failed ({e})',
|
|
58
58
|
) from e
|
|
59
59
|
return handled_client_call
|
|
60
60
|
|
|
@@ -65,7 +65,7 @@ class WizardAPIClient:
|
|
|
65
65
|
def _headers(self, extra=None):
|
|
66
66
|
headers = {
|
|
67
67
|
'Authorization': f'Bearer {self.token}',
|
|
68
|
-
'User-Agent': f'{APP}/{VERSION}'
|
|
68
|
+
'User-Agent': f'{consts.APP}/{consts.VERSION}',
|
|
69
69
|
}
|
|
70
70
|
if extra is not None:
|
|
71
71
|
headers.update(extra)
|
|
@@ -78,7 +78,7 @@ class WizardAPIClient:
|
|
|
78
78
|
raise WizardCommunicationError(
|
|
79
79
|
reason='Unexpected response status',
|
|
80
80
|
message=f'Server responded with unexpected HTTP status {r.status}: '
|
|
81
|
-
f'{r.reason} (expecting {expected_status})'
|
|
81
|
+
f'{r.reason} (expecting {expected_status})',
|
|
82
82
|
)
|
|
83
83
|
|
|
84
84
|
def __init__(self, api_url: str, api_key: str, session=None):
|
|
@@ -158,11 +158,11 @@ class WizardAPIClient:
|
|
|
158
158
|
async def login(self, email: str, password: str) -> str | None:
|
|
159
159
|
req = {'email': email, 'password': password}
|
|
160
160
|
body = await self._post_json('/tokens', json=req)
|
|
161
|
-
token_value = body.get('token'
|
|
161
|
+
token_value = body.get('token')
|
|
162
162
|
if not isinstance(token_value, str):
|
|
163
163
|
raise WizardCommunicationError(
|
|
164
164
|
reason='Invalid response',
|
|
165
|
-
message='Server did not return a valid token'
|
|
165
|
+
message='Server did not return a valid token',
|
|
166
166
|
)
|
|
167
167
|
self.token = token_value
|
|
168
168
|
return self.token
|
|
@@ -263,7 +263,7 @@ class WizardAPIClient:
|
|
|
263
263
|
raise RuntimeError('Organization ID changed during the process')
|
|
264
264
|
body = await self._put_json(
|
|
265
265
|
endpoint=f'/document-template-drafts/{remote_id}',
|
|
266
|
-
json=template.serialize_for_update()
|
|
266
|
+
json=template.serialize_for_update(),
|
|
267
267
|
)
|
|
268
268
|
return _load_remote_template(body)
|
|
269
269
|
|
|
@@ -281,8 +281,8 @@ class WizardAPIClient:
|
|
|
281
281
|
endpoint=f'/document-template-drafts/{remote_id}/files',
|
|
282
282
|
json={
|
|
283
283
|
'fileName': file.filename.as_posix(),
|
|
284
|
-
'content': file.content.decode(DEFAULT_ENCODING)
|
|
285
|
-
}
|
|
284
|
+
'content': file.content.decode(consts.DEFAULT_ENCODING),
|
|
285
|
+
},
|
|
286
286
|
)
|
|
287
287
|
return _load_remote_file(data)
|
|
288
288
|
|
|
@@ -338,7 +338,7 @@ class WizardAPIClient:
|
|
|
338
338
|
f'{self.api_url}/document-template-drafts/{remote_id}'
|
|
339
339
|
f'/assets/{file.remote_id}/content',
|
|
340
340
|
data=data,
|
|
341
|
-
headers=self._headers()
|
|
341
|
+
headers=self._headers(),
|
|
342
342
|
) as r:
|
|
343
343
|
self._check_status(r, expected_status=200)
|
|
344
344
|
body = await r.json()
|
|
@@ -363,15 +363,15 @@ class WizardAPIClient:
|
|
|
363
363
|
@handle_client_errors
|
|
364
364
|
async def get_api_version(self) -> tuple[str, str | None]:
|
|
365
365
|
body = await self._get_json('/')
|
|
366
|
-
version = body.get('version'
|
|
366
|
+
version = body.get('version')
|
|
367
367
|
metamodel_version = None
|
|
368
368
|
for item in body.get('metamodelVersions', []):
|
|
369
369
|
if item.get('name', '') == 'Document Template':
|
|
370
|
-
metamodel_version = item.get('version'
|
|
370
|
+
metamodel_version = item.get('version')
|
|
371
371
|
if version is None:
|
|
372
372
|
raise WizardCommunicationError(
|
|
373
373
|
reason='Invalid response',
|
|
374
|
-
message='Server did not return valid API version information (incompatible TDK?)'
|
|
374
|
+
message='Server did not return valid API version information (incompatible TDK?)',
|
|
375
375
|
)
|
|
376
376
|
return version, metamodel_version
|
|
377
377
|
|
|
@@ -384,25 +384,23 @@ class WizardAPIClient:
|
|
|
384
384
|
def _load_remote_file(data: dict) -> TemplateFile:
|
|
385
385
|
content: str = data.get('content', '')
|
|
386
386
|
filename: str = str(data.get('fileName', ''))
|
|
387
|
-
|
|
388
|
-
remote_id=data.get('uuid'
|
|
387
|
+
return TemplateFile(
|
|
388
|
+
remote_id=data.get('uuid'),
|
|
389
389
|
remote_type=TemplateFileType.FILE,
|
|
390
390
|
filename=pathlib.Path(urllib.parse.unquote(filename)),
|
|
391
|
-
content=content.encode(encoding=DEFAULT_ENCODING),
|
|
391
|
+
content=content.encode(encoding=consts.DEFAULT_ENCODING),
|
|
392
392
|
)
|
|
393
|
-
return file
|
|
394
393
|
|
|
395
394
|
|
|
396
395
|
def _load_remote_asset(data: dict, content: bytes) -> TemplateFile:
|
|
397
396
|
filename = str(data.get('fileName', ''))
|
|
398
|
-
|
|
399
|
-
remote_id=data.get('uuid'
|
|
397
|
+
return TemplateFile(
|
|
398
|
+
remote_id=data.get('uuid'),
|
|
400
399
|
remote_type=TemplateFileType.ASSET,
|
|
401
400
|
filename=pathlib.Path(urllib.parse.unquote(filename)),
|
|
402
|
-
content_type=data.get('contentType'
|
|
401
|
+
content_type=data.get('contentType'),
|
|
403
402
|
content=content,
|
|
404
403
|
)
|
|
405
|
-
return asset
|
|
406
404
|
|
|
407
405
|
|
|
408
406
|
def _load_remote_template(data: dict) -> Template:
|
|
@@ -9,9 +9,9 @@ BuildInfo = namedtuple(
|
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
BUILD_INFO = BuildInfo(
|
|
12
|
-
version='v4.
|
|
13
|
-
built_at='2026-
|
|
14
|
-
sha='
|
|
12
|
+
version='v4.27.0~8ec71bd',
|
|
13
|
+
built_at='2026-02-03 08:45:12Z',
|
|
14
|
+
sha='8ec71bd85dfbea66adedb6590f7d76ae5143bbaa',
|
|
15
15
|
branch='HEAD',
|
|
16
|
-
tag='v4.
|
|
16
|
+
tag='v4.27.0',
|
|
17
17
|
)
|
|
@@ -12,14 +12,15 @@ import humanize
|
|
|
12
12
|
import slugify
|
|
13
13
|
import watchfiles
|
|
14
14
|
|
|
15
|
+
from . import consts
|
|
15
16
|
from .api_client import WizardCommunicationError
|
|
16
17
|
from .config import CONFIG
|
|
17
|
-
from .consts import VERSION, DEFAULT_LIST_FORMAT, DEFAULT_ENCODING
|
|
18
18
|
from .core import TDKCore, TDKProcessingError
|
|
19
19
|
from .model import Template
|
|
20
|
-
from .utils import TemplateBuilder,
|
|
20
|
+
from .utils import FormatSpec, TemplateBuilder, create_dot_env, safe_utf8
|
|
21
21
|
from .validation import ValidationError
|
|
22
22
|
|
|
23
|
+
|
|
23
24
|
CURRENT_DIR = pathlib.Path.cwd()
|
|
24
25
|
DIR_TYPE = click.Path(exists=True, dir_okay=True, file_okay=False, resolve_path=True,
|
|
25
26
|
readable=True, writable=True)
|
|
@@ -29,6 +30,10 @@ NEW_DIR_TYPE = click.Path(dir_okay=True, file_okay=False, resolve_path=True,
|
|
|
29
30
|
readable=True, writable=True)
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
def _now() -> datetime.datetime:
|
|
34
|
+
return datetime.datetime.now(tz=datetime.UTC)
|
|
35
|
+
|
|
36
|
+
|
|
32
37
|
class ClickPrinter:
|
|
33
38
|
|
|
34
39
|
CHANGE_SIGNS = {
|
|
@@ -64,7 +69,7 @@ class ClickPrinter:
|
|
|
64
69
|
@classmethod
|
|
65
70
|
def watch_change(cls, change_type: watchfiles.Change, filepath: pathlib.Path,
|
|
66
71
|
root: pathlib.Path):
|
|
67
|
-
timestamp =
|
|
72
|
+
timestamp = _now().isoformat(timespec='milliseconds')
|
|
68
73
|
sign = cls.CHANGE_SIGNS[change_type]
|
|
69
74
|
click.secho('WATCH', fg='blue', bold=True, nl=False)
|
|
70
75
|
click.echo(f'@{timestamp} {sign} {filepath.relative_to(root)}')
|
|
@@ -149,7 +154,7 @@ class ClickLogger(logging.Logger):
|
|
|
149
154
|
|
|
150
155
|
def _print_message(self, level, message):
|
|
151
156
|
if self.show_timestamp:
|
|
152
|
-
timestamp =
|
|
157
|
+
timestamp = _now().isoformat(timespec='milliseconds')
|
|
153
158
|
click.echo(timestamp + ' | ', nl=False)
|
|
154
159
|
if self.show_level:
|
|
155
160
|
sep = ' | ' if self.show_timestamp else ': '
|
|
@@ -204,7 +209,7 @@ def interact_formats() -> dict[str, FormatSpec]:
|
|
|
204
209
|
format_spec = FormatSpec()
|
|
205
210
|
prompt_fill('Format name', obj=format_spec, attr='name', default='HTML')
|
|
206
211
|
if format_spec.name not in formats or click.confirm(
|
|
207
|
-
'There is already a format with this name. Do you want to change it?'
|
|
212
|
+
'There is already a format with this name. Do you want to change it?',
|
|
208
213
|
):
|
|
209
214
|
prompt_fill('File extension', obj=format_spec, attr='file_extension',
|
|
210
215
|
default=format_spec.name.lower() if ' ' not in format_spec.name else None)
|
|
@@ -267,7 +272,7 @@ def dir_from_id(template_id: str) -> pathlib.Path:
|
|
|
267
272
|
help='Hide additional information logs.')
|
|
268
273
|
@click.option('--debug', is_flag=True,
|
|
269
274
|
help='Enable debug logging.')
|
|
270
|
-
@click.version_option(version=VERSION)
|
|
275
|
+
@click.version_option(version=consts.VERSION)
|
|
271
276
|
@click.pass_context
|
|
272
277
|
def main(ctx, quiet, debug, dot_env, environment, no_dot_env, no_config):
|
|
273
278
|
if not no_config:
|
|
@@ -499,7 +504,7 @@ def extract_package(ctx, template_package, output, force: bool):
|
|
|
499
504
|
help='URL of Wizard server API.')
|
|
500
505
|
@click.option('-k', '--api-key', metavar='API-KEY', envvar='DSW_API_KEY',
|
|
501
506
|
help='API key for Wizard instance.')
|
|
502
|
-
@click.option('--output-format', default=DEFAULT_LIST_FORMAT,
|
|
507
|
+
@click.option('--output-format', default=consts.DEFAULT_LIST_FORMAT,
|
|
503
508
|
metavar='FORMAT', help='Entry format string for printing.')
|
|
504
509
|
@click.option('-r', '--released-only', is_flag=True, help='List only released templates')
|
|
505
510
|
@click.option('-d', '--drafts-only', is_flag=True, help='List only template drafts')
|
|
@@ -652,7 +657,7 @@ def config_check():
|
|
|
652
657
|
else:
|
|
653
658
|
env_out = click.style(env_name, fg='blue', bold=True)
|
|
654
659
|
click.echo(env_out)
|
|
655
|
-
click.echo(f' API URL: {env.api_url
|
|
660
|
+
click.echo(f' API URL: {env.api_url or not_set}')
|
|
656
661
|
click.echo(f' API Key: {hidden if env.api_key else not_set}')
|
|
657
662
|
click.echo('')
|
|
658
663
|
click.secho('Project-local configuration:', bold=True)
|
|
@@ -687,7 +692,7 @@ def config_create_dotenv(ctx, template_dir, api_url, api_key, force):
|
|
|
687
692
|
try:
|
|
688
693
|
if output.exists():
|
|
689
694
|
if force:
|
|
690
|
-
ClickPrinter.warning(f'Overwriting {output.as_posix()} (forced)'
|
|
695
|
+
ClickPrinter.warning(f'Overwriting {output.as_posix()} (forced)')
|
|
691
696
|
else:
|
|
692
697
|
raise FileExistsError(f'File {output.as_posix()} already exists (not forced)')
|
|
693
698
|
output.write_text(
|
|
@@ -695,7 +700,7 @@ def config_create_dotenv(ctx, template_dir, api_url, api_key, force):
|
|
|
695
700
|
api_url=CONFIG.env.api_url,
|
|
696
701
|
api_key=CONFIG.env.api_key,
|
|
697
702
|
),
|
|
698
|
-
encoding=DEFAULT_ENCODING,
|
|
703
|
+
encoding=consts.DEFAULT_ENCODING,
|
|
699
704
|
)
|
|
700
705
|
except Exception as e:
|
|
701
706
|
ClickPrinter.failure('Failed to create dot-env file')
|
|
@@ -5,7 +5,7 @@ import pathlib
|
|
|
5
5
|
|
|
6
6
|
import dotenv
|
|
7
7
|
|
|
8
|
-
from .
|
|
8
|
+
from . import consts
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def _rectify_api_url(api_url: str | None) -> str:
|
|
@@ -44,11 +44,8 @@ class TDKConfig:
|
|
|
44
44
|
self.default_env_name = None # type: str | None
|
|
45
45
|
|
|
46
46
|
def load_dotenv(self, path: pathlib.Path):
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
dotenv.load_dotenv(path)
|
|
50
|
-
except Exception as e:
|
|
51
|
-
print(f"Error loading .env file: {e}")
|
|
47
|
+
if path.exists():
|
|
48
|
+
dotenv.load_dotenv(path)
|
|
52
49
|
api_url = os.getenv('DSW_API_URL', '')
|
|
53
50
|
api_key = os.getenv('DSW_API_KEY', '')
|
|
54
51
|
|
|
@@ -147,8 +144,8 @@ class TDKConfig:
|
|
|
147
144
|
config.set(section_name, 'api_url', env.api_url)
|
|
148
145
|
config.set(section_name, 'api_key', env.api_key)
|
|
149
146
|
|
|
150
|
-
with open(
|
|
151
|
-
config.write(
|
|
147
|
+
with output.open(mode='w', encoding=consts.DEFAULT_ENCODING) as config_file:
|
|
148
|
+
config.write(config_file)
|
|
152
149
|
|
|
153
150
|
|
|
154
151
|
CONFIG = TDKConfig()
|
|
@@ -10,11 +10,11 @@ import zipfile
|
|
|
10
10
|
|
|
11
11
|
import watchfiles
|
|
12
12
|
|
|
13
|
+
from . import consts
|
|
13
14
|
from .api_client import WizardAPIClient, WizardCommunicationError
|
|
14
|
-
from .
|
|
15
|
-
from .model import TemplateProject, Template, TemplateFile, TemplateFileType
|
|
15
|
+
from .model import Template, TemplateFile, TemplateFileType, TemplateProject
|
|
16
16
|
from .utils import UUIDGen
|
|
17
|
-
from .validation import
|
|
17
|
+
from .validation import TemplateValidator, ValidationError
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
ChangeItem = tuple[watchfiles.Change, pathlib.Path]
|
|
@@ -65,7 +65,8 @@ class TDKCore:
|
|
|
65
65
|
'but still compatible', mm_ver, mmr_ver)
|
|
66
66
|
else:
|
|
67
67
|
raise TDKProcessingError(
|
|
68
|
-
f'Unsupported metamodel version: local {mm_ver}, remote {mmr_ver}',
|
|
68
|
+
f'Unsupported metamodel version: local {mm_ver}, remote {mmr_ver}',
|
|
69
|
+
hint,
|
|
69
70
|
)
|
|
70
71
|
|
|
71
72
|
def __init__(self, template: Template | None = None, project: TemplateProject | None = None,
|
|
@@ -155,8 +156,10 @@ class TDKCore:
|
|
|
155
156
|
if self.project is None:
|
|
156
157
|
raise RuntimeError('No template project is initialized')
|
|
157
158
|
self.project.template = self.safe_template
|
|
158
|
-
if
|
|
159
|
-
|
|
159
|
+
if self.project.template is None:
|
|
160
|
+
raise RuntimeError('No template is loaded in the project')
|
|
161
|
+
if len(self.project.safe_template.tdk_config.files) == 0:
|
|
162
|
+
self.project.safe_template.tdk_config.use_default_files()
|
|
160
163
|
self.logger.warning('Using default _tdk.files in template.json, you may want '
|
|
161
164
|
'to change it to include relevant files')
|
|
162
165
|
self.logger.debug('Initiating storing local template project (force=%s)', force)
|
|
@@ -264,14 +267,14 @@ class TDKCore:
|
|
|
264
267
|
|
|
265
268
|
async def cleanup_remote_files(self, remote_assets: list[TemplateFile],
|
|
266
269
|
remote_files: list[TemplateFile]):
|
|
267
|
-
for
|
|
268
|
-
self.logger.debug('Cleaning up remote %s',
|
|
269
|
-
for
|
|
270
|
-
if
|
|
271
|
-
await self._delete_template_file(file=
|
|
272
|
-
for
|
|
273
|
-
if
|
|
274
|
-
await self._delete_template_file(file=
|
|
270
|
+
for local_file in self.safe_project.safe_template.files.values():
|
|
271
|
+
self.logger.debug('Cleaning up remote %s', local_file.filename.as_posix())
|
|
272
|
+
for remote_asset in remote_assets:
|
|
273
|
+
if remote_asset.filename == local_file.filename:
|
|
274
|
+
await self._delete_template_file(file=remote_asset, project_update=False)
|
|
275
|
+
for remote_file in remote_files:
|
|
276
|
+
if remote_file.filename == local_file.filename:
|
|
277
|
+
await self._delete_template_file(file=remote_file, project_update=False)
|
|
275
278
|
|
|
276
279
|
async def _create_template_file(self, file: TemplateFile, project_update: bool = False):
|
|
277
280
|
try:
|
|
@@ -318,7 +321,7 @@ class TDKCore:
|
|
|
318
321
|
self.logger.info('Adding template file %s', file.filename.as_posix())
|
|
319
322
|
files.append({
|
|
320
323
|
'uuid': str(UUIDGen.generate()),
|
|
321
|
-
'content': file.content.decode(encoding=DEFAULT_ENCODING),
|
|
324
|
+
'content': file.content.decode(encoding=consts.DEFAULT_ENCODING),
|
|
322
325
|
'fileName': str(file.filename.as_posix()),
|
|
323
326
|
})
|
|
324
327
|
else:
|
|
@@ -359,7 +362,7 @@ class TDKCore:
|
|
|
359
362
|
self.logger.debug('Extracting template data')
|
|
360
363
|
if not file.exists():
|
|
361
364
|
raise RuntimeError('Malformed package: missing template.json file')
|
|
362
|
-
data = json.loads(file.read_text(encoding=DEFAULT_ENCODING))
|
|
365
|
+
data = json.loads(file.read_text(encoding=consts.DEFAULT_ENCODING))
|
|
363
366
|
template = Template.load_local(data)
|
|
364
367
|
template.tdk_config.use_default_files()
|
|
365
368
|
self.logger.warning('Using default _tdk.files in template.json, you may want '
|
|
@@ -377,13 +380,13 @@ class TDKCore:
|
|
|
377
380
|
local_template_json = template_dir / 'template.json'
|
|
378
381
|
local_template_json.write_text(
|
|
379
382
|
data=json.dumps(template.serialize_local_new(), indent=2),
|
|
380
|
-
encoding=DEFAULT_ENCODING,
|
|
383
|
+
encoding=consts.DEFAULT_ENCODING,
|
|
381
384
|
)
|
|
382
385
|
self.logger.debug('Extracting README.md from package')
|
|
383
386
|
local_readme = template_dir / 'README.md'
|
|
384
387
|
local_readme.write_text(
|
|
385
388
|
data=data['readme'].replace('\r\n', '\n'),
|
|
386
|
-
encoding=DEFAULT_ENCODING,
|
|
389
|
+
encoding=consts.DEFAULT_ENCODING,
|
|
387
390
|
)
|
|
388
391
|
self.logger.debug('Extracting assets from package')
|
|
389
392
|
for asset_file in assets_dir.rglob('*'):
|
|
@@ -399,7 +402,7 @@ class TDKCore:
|
|
|
399
402
|
target_file = template_dir / filename
|
|
400
403
|
target_dir = target_file.parent
|
|
401
404
|
target_dir.mkdir(parents=True, exist_ok=True)
|
|
402
|
-
target_file.write_text(data=content, encoding=DEFAULT_ENCODING)
|
|
405
|
+
target_file.write_text(data=content, encoding=consts.DEFAULT_ENCODING)
|
|
403
406
|
self.logger.debug('Extracting package done')
|
|
404
407
|
|
|
405
408
|
async def watch_project(self, callback, stop_event: asyncio.Event):
|
|
@@ -407,12 +410,14 @@ class TDKCore:
|
|
|
407
410
|
self.safe_project.template_dir,
|
|
408
411
|
stop_event=stop_event,
|
|
409
412
|
):
|
|
410
|
-
await callback(
|
|
413
|
+
await callback(
|
|
411
414
|
change for change in ((change[0], pathlib.Path(change[1])) for change in changes)
|
|
412
415
|
if self.safe_project.is_template_file(
|
|
413
|
-
change[1],
|
|
416
|
+
change[1],
|
|
417
|
+
include_descriptor=True,
|
|
418
|
+
include_readme=True,
|
|
414
419
|
)
|
|
415
|
-
)
|
|
420
|
+
)
|
|
416
421
|
|
|
417
422
|
async def update_descriptor(self):
|
|
418
423
|
try:
|