dsw-tdk 4.26.1__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.
Files changed (41) hide show
  1. {dsw_tdk-4.26.1/dsw_tdk.egg-info → dsw_tdk-4.27.0}/PKG-INFO +12 -12
  2. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/__init__.py +4 -3
  3. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/__main__.py +1 -0
  4. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/api_client.py +23 -25
  5. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/build_info.py +4 -4
  6. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/cli.py +15 -10
  7. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/config.py +5 -8
  8. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/consts.py +2 -1
  9. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/core.py +27 -22
  10. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/model.py +44 -42
  11. dsw_tdk-4.27.0/dsw/tdk/py.typed +0 -0
  12. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/utils.py +8 -8
  13. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/validation.py +33 -36
  14. dsw_tdk-4.27.0/pyproject.toml +57 -0
  15. dsw_tdk-4.26.1/CHANGELOG.md +0 -666
  16. dsw_tdk-4.26.1/LICENSE +0 -201
  17. dsw_tdk-4.26.1/MANIFEST.in +0 -2
  18. dsw_tdk-4.26.1/PKG-INFO +0 -154
  19. dsw_tdk-4.26.1/dsw_tdk.egg-info/SOURCES.txt +0 -37
  20. dsw_tdk-4.26.1/dsw_tdk.egg-info/dependency_links.txt +0 -1
  21. dsw_tdk-4.26.1/dsw_tdk.egg-info/entry_points.txt +0 -2
  22. dsw_tdk-4.26.1/dsw_tdk.egg-info/not-zip-safe +0 -1
  23. dsw_tdk-4.26.1/dsw_tdk.egg-info/requires.txt +0 -13
  24. dsw_tdk-4.26.1/dsw_tdk.egg-info/top_level.txt +0 -1
  25. dsw_tdk-4.26.1/pyproject.toml +0 -64
  26. dsw_tdk-4.26.1/setup.cfg +0 -4
  27. dsw_tdk-4.26.1/setup.py +0 -3
  28. dsw_tdk-4.26.1/tests/test_basic.py +0 -26
  29. dsw_tdk-4.26.1/tests/test_cmd_dot-env.py +0 -78
  30. dsw_tdk-4.26.1/tests/test_cmd_get.py +0 -76
  31. dsw_tdk-4.26.1/tests/test_cmd_list.py +0 -53
  32. dsw_tdk-4.26.1/tests/test_cmd_new.py +0 -77
  33. dsw_tdk-4.26.1/tests/test_cmd_package.py +0 -57
  34. dsw_tdk-4.26.1/tests/test_cmd_put.py +0 -56
  35. dsw_tdk-4.26.1/tests/test_cmd_unpackage.py +0 -57
  36. dsw_tdk-4.26.1/tests/test_cmd_verify.py +0 -46
  37. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/README.md +0 -0
  38. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/templates/LICENSE.j2 +0 -0
  39. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/templates/README.md.j2 +0 -0
  40. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/templates/env.j2 +0 -0
  41. {dsw_tdk-4.26.1 → dsw_tdk-4.27.0}/dsw/tdk/templates/starter.j2 +0 -0
@@ -1,13 +1,11 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.3
2
2
  Name: dsw-tdk
3
- Version: 4.26.1
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: Jinja2
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
- Requires-Dist: pytest; extra == "test"
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
- __app__ = APP
13
- __version__ = VERSION
12
+
13
+ __app__ = consts.APP
14
+ __version__ = consts.VERSION
14
15
 
15
16
  __all__ = ['__app__', '__version__', 'main']
@@ -1,4 +1,5 @@
1
1
  from .cli import main
2
2
 
3
+
3
4
  # pylint: disable-next=no-value-for-parameter
4
5
  main(ctx=None)
@@ -5,7 +5,7 @@ import urllib.parse
5
5
  import aiohttp
6
6
  import aiohttp.client_exceptions
7
7
 
8
- from .consts import DEFAULT_ENCODING, APP, VERSION
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', None)
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', None)
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', None)
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
- file = TemplateFile(
388
- remote_id=data.get('uuid', None),
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
- asset = TemplateFile(
399
- remote_id=data.get('uuid', None),
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', None),
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.26.1~2e1c502',
13
- built_at='2026-01-10 17:15:33Z',
14
- sha='2e1c502c11e1e2fd885a1c696488008a1ca9b343',
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.26.1',
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, FormatSpec, safe_utf8, create_dot_env
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 = datetime.datetime.now().isoformat(timespec='milliseconds')
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 = datetime.datetime.now().isoformat(timespec='milliseconds')
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 if env.api_url else not_set}')
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 .consts import DEFAULT_ENCODING
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
- try:
48
- if path.exists():
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(output, 'w', encoding=DEFAULT_ENCODING) as configfile:
151
- config.write(configfile)
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()
@@ -3,8 +3,9 @@ import re
3
3
 
4
4
  import pathspec
5
5
 
6
+
6
7
  APP = 'dsw-tdk'
7
- VERSION = '4.26.1'
8
+ VERSION = '4.27.0'
8
9
  METAMODEL_VERSION_MAJOR = 17
9
10
  METAMODEL_VERSION_MINOR = 1
10
11
  METAMODEL_VERSION = f'{METAMODEL_VERSION_MAJOR}.{METAMODEL_VERSION_MINOR}'
@@ -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 .consts import DEFAULT_ENCODING
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 ValidationError, TemplateValidator
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}', hint
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 len(self.project.template.tdk_config.files) == 0:
159
- self.project.template.tdk_config.use_default_files()
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 file in self.safe_project.safe_template.files.values():
268
- self.logger.debug('Cleaning up remote %s', file.filename.as_posix())
269
- for asset in remote_assets:
270
- if asset.filename == file.filename:
271
- await self._delete_template_file(file=asset, project_update=False)
272
- for file in remote_files:
273
- if file.filename == file.filename:
274
- await self._delete_template_file(file=file, project_update=False)
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], include_descriptor=True, include_readme=True
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: