emcd-projects 1.16__py3-none-any.whl → 1.17rc0__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emcd-projects
3
- Version: 1.16
3
+ Version: 1.17rc0
4
4
  Summary: Project management utilities.
5
5
  Project-URL: Homepage, https://github.com/emcd/python-project-common
6
6
  Project-URL: Documentation, https://emcd.github.io/python-project-common
@@ -1,24 +1,27 @@
1
1
  emcdproj/data/.gitignore,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ emcdproj/data/copier/answers-default.yaml,sha256=dgdxDTcZZpHjg3-uZyDALzJ0Gc7GBcSSvQ7L_OmebWM,150
3
+ emcdproj/data/copier/answers-maximum.yaml,sha256=aVAiQZgltQYZWf3wYGzw6-Syp2iZr7yjOauqd2lhYfQ,392
2
4
  emcdproj/data/templates/coverage.svg.jinja,sha256=UWJE9I1QVoo31DB4PAcf_sYFs1oOViDbnCmhzuoVuq8,1479
3
5
  emcdproj/data/templates/website.html.jinja,sha256=V958XUcGdNmvSl_RalRvPRIZ5uUcpaXgycddOLN6rfA,1513
4
6
  emcdproj/README.rst,sha256=ThJlTJWDLurnGuhCXlQDRuSYexDJrAUnHIPiTGaAmww,4495
5
- emcdproj/__init__.py,sha256=WJ5-iRl5T7069faD1eJIW0u_-jjBY8cvL6jh1ntu6dw,1582
7
+ emcdproj/__init__.py,sha256=OvqDSiaoUWKq1Xq0aASEfxXagPu8Yx3cvjUf8aN9u34,1585
6
8
  emcdproj/__main__.py,sha256=KkTeK75rYgF2yVf2HpTs-L2V1jru3j7ohDSgGXWFXjQ,1409
7
- emcdproj/cli.py,sha256=aTnDAVS5aLQjuTk0peHlZvLAUToL1JrWst470JcjoLY,4168
9
+ emcdproj/cli.py,sha256=WNAF0pmAbdxtmuCcyTBkuG1CQnWixpkDSXNuQ4KwXDE,4414
8
10
  emcdproj/exceptions.py,sha256=Y9n5pGh1-hMkKtOYherAjUw19af4-uWDNzUZjiqiU00,2641
9
11
  emcdproj/filesystem.py,sha256=YgcX5Q52r2hTYU-7_VoW2inMC4NBTzqSTINB9eNT9Hw,1708
10
- emcdproj/interfaces.py,sha256=8zpEhAjPpXZfwbQxj3TkGzeTzY8sHZUHsbmbtdb8k6c,1735
12
+ emcdproj/interfaces.py,sha256=PvNZb4G0EhXbK_yaAOhqcgp0uNR30FCRUf4Qn0e03M8,3185
11
13
  emcdproj/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- emcdproj/website.py,sha256=qBJE-wBNs3UAFMWLdMFR5V9-KK9aMwfKznhIvI9yl2g,10503
14
+ emcdproj/template.py,sha256=S_SAlrVtEco2c2eO0QNdy-Gffn_hk8zVFFT1GzqXcuc,4823
15
+ emcdproj/website.py,sha256=YfIGPp2WzoL0E9nQLJgbfU6yVbipns-I7F6alkSowb0,10657
13
16
  emcdproj/__/__init__.py,sha256=oGdDy7STF7jZlrxKzgvdbqw5-tR-RRWqurizIazVvvg,1598
14
17
  emcdproj/__/application.py,sha256=6uF2yTIlAsLWKadus7a7mPvr7q3pagplPrDAA1EUwlw,2316
15
18
  emcdproj/__/distribution.py,sha256=9TCSm8oJtIot-MPR8Ypt72gMaUh77WXtkpuxggb6tGQ,3972
16
- emcdproj/__/imports.py,sha256=hj9OReVMu5G0QeM531UhP0Qeo1ePtkVGNUBMrJTULMQ,2109
19
+ emcdproj/__/imports.py,sha256=P4r8oEkrKGQ0NuA4uTTbM-ZYUJp3QgvjuqJELdlSJH0,2340
17
20
  emcdproj/__/preparation.py,sha256=rNbA8VvrspanbP_0O4GjmwaqgzgPf0SdY7WadcINJZE,3824
18
21
  emcdproj/__/state.py,sha256=inChROB1WcJ_b9kZ6VfQZr-dLj56-MxWoMHrvB-yXu4,3596
19
22
  emcdproj/_typedecls/__builtins__.pyi,sha256=VhJfnYgggXihUzomBHtA1xEKzG7XqEHkSjhl0Ro6qgg,153
20
- emcd_projects-1.16.dist-info/METADATA,sha256=zABwcCUBJZ_2rGG0piv6V2firXqlxoyJzIrSziaum34,6085
21
- emcd_projects-1.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
- emcd_projects-1.16.dist-info/entry_points.txt,sha256=22Pc1xg3OboNBxpTnDM3gLYXo8QuY2E0SOv1djo0fdc,43
23
- emcd_projects-1.16.dist-info/licenses/LICENSE.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
24
- emcd_projects-1.16.dist-info/RECORD,,
23
+ emcd_projects-1.17rc0.dist-info/METADATA,sha256=pxUPDMV0NYl7G-ET4n3aY58R3Mjv2ZctqVMnzRpmmjM,6088
24
+ emcd_projects-1.17rc0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
25
+ emcd_projects-1.17rc0.dist-info/entry_points.txt,sha256=22Pc1xg3OboNBxpTnDM3gLYXo8QuY2E0SOv1djo0fdc,43
26
+ emcd_projects-1.17rc0.dist-info/licenses/LICENSE.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
27
+ emcd_projects-1.17rc0.dist-info/RECORD,,
emcdproj/__/imports.py CHANGED
@@ -25,14 +25,17 @@
25
25
 
26
26
  from __future__ import annotations
27
27
 
28
- import abc
29
- import collections.abc as cabc
30
- import contextlib as ctxl
31
- import json
32
- import math
33
- import os
34
- import shutil
35
- import types
28
+ import abc
29
+ import collections.abc as cabc
30
+ import contextlib as ctxl
31
+ import enum
32
+ import io
33
+ import json
34
+ import math
35
+ import os
36
+ import shutil
37
+ import sys
38
+ import types
36
39
 
37
40
  from pathlib import Path
38
41
 
emcdproj/__init__.py CHANGED
@@ -27,7 +27,7 @@ from . import exceptions
27
27
  # --- END: Injected by Copier ---
28
28
 
29
29
 
30
- __version__ = '1.16'
30
+ __version__ = '1.17rc0'
31
31
 
32
32
 
33
33
  def main( ):
emcdproj/cli.py CHANGED
@@ -25,6 +25,7 @@ from __future__ import annotations
25
25
 
26
26
  from . import __
27
27
  from . import interfaces as _interfaces
28
+ from . import template as _template
28
29
  from . import website as _website
29
30
 
30
31
 
@@ -34,7 +35,9 @@ class VersionCommand(
34
35
  ):
35
36
  ''' Prints version information. '''
36
37
 
37
- async def __call__( self, auxdata: __.Globals ) -> None:
38
+ async def __call__(
39
+ self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
40
+ ) -> None:
38
41
  from . import __version__
39
42
  print( f"{__package__} {__version__}" )
40
43
  raise SystemExit( 0 )
@@ -48,8 +51,12 @@ class Cli(
48
51
 
49
52
  application: __.ApplicationInformation
50
53
  # configfile: __.typx.Optional[ str ] = None
51
- # display: ConsoleDisplay
54
+ display: _interfaces.ConsoleDisplay
52
55
  command: __.typx.Union[
56
+ __.typx.Annotated[
57
+ _template.CommandDispatcher,
58
+ __.tyro.conf.subcommand( 'template', prefix_name = False ),
59
+ ],
53
60
  __.typx.Annotated[
54
61
  _website.CommandDispatcher,
55
62
  __.tyro.conf.subcommand( 'website', prefix_name = False ),
@@ -66,8 +73,8 @@ class Cli(
66
73
  async with __.ctxl.AsyncExitStack( ) as exits:
67
74
  auxdata = await _prepare( exits = exits, **nomargs )
68
75
  ictr( 0 )( self.command )
69
- await self.command( auxdata = auxdata )
70
- # await self.command( auxdata = auxdata, display = self.display )
76
+ # await self.command( auxdata = auxdata )
77
+ await self.command( auxdata = auxdata, display = self.display )
71
78
 
72
79
  def prepare_invocation_args(
73
80
  self,
@@ -0,0 +1,4 @@
1
+ project_name: python-test-defaults
2
+ distribution_name: emcdproj-defaults
3
+ package_name: defaults
4
+ description: 'All configuration options are defaults.'
@@ -0,0 +1,14 @@
1
+ project_name: python-test-maximum
2
+ distribution_name: emcdproj-maximum
3
+ package_name: maximum
4
+ description: 'All configuration options set.'
5
+ enable_rust_extension: true
6
+ include_data_resources: true
7
+ enable_property_tests: true
8
+ enable_publication: true
9
+ enable_cli: true
10
+ enable_executables: true
11
+ inject_foundations: true
12
+ inject_exceptions: true
13
+ inject_immutables: true
14
+ inject_docstring_utils: true
emcdproj/interfaces.py CHANGED
@@ -26,6 +26,46 @@ from __future__ import annotations
26
26
  from . import __
27
27
 
28
28
 
29
+ class DisplayStreams( __.enum.Enum ): # TODO: Python 3.11: StrEnum
30
+ # TODO: Protected class attributes.
31
+ ''' Stream upon which to place output. '''
32
+
33
+ Stderr = 'stderr'
34
+ Stdout = 'stdout'
35
+
36
+
37
+ class ConsoleDisplay(
38
+ metaclass = __.ImmutableDataclass,
39
+ ):
40
+ silence: __.typx.Annotated[
41
+ bool,
42
+ __.tyro.conf.arg(
43
+ aliases = ( '--quiet', '--silent', ), prefix_name = False ),
44
+ ] = False
45
+ file: __.typx.Annotated[
46
+ __.typx.Optional[ __.Path ],
47
+ __.tyro.conf.arg(
48
+ name = 'console-capture-file', prefix_name = False ),
49
+ ] = None
50
+ stream: __.typx.Annotated[
51
+ DisplayStreams,
52
+ __.tyro.conf.arg( name = 'console-stream', prefix_name = False ),
53
+ ] = DisplayStreams.Stderr
54
+
55
+ async def provide_stream( self ) -> __.io.TextIOWrapper:
56
+ ''' Provides output stream for display. '''
57
+ # TODO: register file stream as a process-lifetime exit
58
+ if self.file: return open( self.file, 'w' )
59
+ # TODO: async context manager for async file streams
60
+ # TODO: return async stream - need async printers
61
+ # TODO: handle non-TextIOWrapper streams
62
+ match self.stream:
63
+ case DisplayStreams.Stdout:
64
+ return __.sys.stdout # pyright: ignore[reportReturnType]
65
+ case DisplayStreams.Stderr:
66
+ return __.sys.stderr # pyright: ignore[reportReturnType]
67
+
68
+
29
69
  class CliCommand(
30
70
  __.typx.Protocol,
31
71
  metaclass = __.ImmutableProtocolDataclass,
@@ -34,7 +74,9 @@ class CliCommand(
34
74
  ''' CLI command. '''
35
75
 
36
76
  @__.abc.abstractmethod
37
- async def __call__( self, auxdata: __.Globals ) -> None:
77
+ async def __call__(
78
+ self, auxdata: __.Globals, display: ConsoleDisplay
79
+ ) -> None:
38
80
  ''' Executes command with global state. '''
39
81
  raise NotImplementedError
40
82
 
emcdproj/template.py ADDED
@@ -0,0 +1,127 @@
1
+ # vim: set filetype=python fileencoding=utf-8:
2
+ # -*- coding: utf-8 -*-
3
+
4
+ #============================================================================#
5
+ # #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); #
7
+ # you may not use this file except in compliance with the License. #
8
+ # You may obtain a copy of the License at #
9
+ # #
10
+ # http://www.apache.org/licenses/LICENSE-2.0 #
11
+ # #
12
+ # Unless required by applicable law or agreed to in writing, software #
13
+ # distributed under the License is distributed on an "AS IS" BASIS, #
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
15
+ # See the License for the specific language governing permissions and #
16
+ # limitations under the License. #
17
+ # #
18
+ #============================================================================#
19
+
20
+
21
+ ''' Copier template maintenance and validation. '''
22
+
23
+
24
+ from __future__ import annotations
25
+
26
+ import subprocess as _subprocess
27
+ import tempfile as _tempfile
28
+
29
+ from . import __
30
+ from . import interfaces as _interfaces
31
+
32
+
33
+ class CommandDispatcher(
34
+ _interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
35
+ ):
36
+ ''' Dispatches commands for static website maintenance. '''
37
+
38
+ command: __.typx.Union[
39
+ __.typx.Annotated[
40
+ SurveyCommand,
41
+ __.tyro.conf.subcommand( 'survey', prefix_name = False ),
42
+ ],
43
+ __.typx.Annotated[
44
+ ValidateCommand,
45
+ __.tyro.conf.subcommand( 'validate', prefix_name = False ),
46
+ ],
47
+ ]
48
+
49
+ async def __call__(
50
+ self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
51
+ ) -> None:
52
+ ictr( 1 )( self.command )
53
+ await self.command( auxdata = auxdata, display = display )
54
+
55
+
56
+ class SurveyCommand(
57
+ _interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
58
+ ):
59
+ ''' Surveys available configuration variants. '''
60
+
61
+ async def __call__(
62
+ self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
63
+ ) -> None:
64
+ stream = await display.provide_stream( )
65
+ for variant in survey_variants( auxdata ):
66
+ print( variant, file = stream )
67
+
68
+
69
+ class ValidateCommand(
70
+ _interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
71
+ ):
72
+ ''' Validates template against configuration variant. '''
73
+
74
+ variant: __.typx.Annotated[
75
+ str,
76
+ __.typx.Doc( ''' Configuration variant to validate. ''' ),
77
+ __.tyro.conf.Positional,
78
+ ]
79
+
80
+ async def __call__(
81
+ self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
82
+ ) -> None:
83
+ ''' Copies new project from template for configuration variant. '''
84
+ # TODO: Validate variant argument.
85
+ validate_variant( auxdata, self.variant )
86
+
87
+
88
+ def copy_template( answers_file: __.Path, projectdir: __.Path ) -> None:
89
+ ''' Copies template to target directory using answers. '''
90
+ _subprocess.run( # noqa: S603
91
+ ( 'copier', 'copy', '--data-file', str( answers_file ),
92
+ '--defaults', '--overwrite', '--vcs-ref', 'HEAD',
93
+ '.', str( projectdir ) ),
94
+ cwd = __.Path( ), check = True )
95
+
96
+
97
+ def survey_variants( auxdata: __.Globals ) -> __.cabc.Sequence[ str ]:
98
+ ''' Surveys available configuration variants. '''
99
+ location = auxdata.distribution.provide_data_location( 'copier' )
100
+ return tuple(
101
+ fsent.stem.lstrip( 'answers-' )
102
+ for fsent in location.glob( 'answers-*.yaml' )
103
+ if fsent.is_file( ) )
104
+
105
+
106
+ def validate_variant( auxdata: __.Globals, variant: str ) -> None:
107
+ ''' Validates configuration variant. '''
108
+ answers_file = (
109
+ auxdata.distribution.provide_data_location(
110
+ 'copier', f"answers-{variant}.yaml" ) )
111
+ if not answers_file.is_file( ):
112
+ # TODO: Raise error.
113
+ return
114
+ with _tempfile.TemporaryDirectory( ) as tmpdir:
115
+ projectdir = __.Path( tmpdir ) / variant
116
+ copy_template( answers_file, projectdir )
117
+ validate_variant_project( projectdir )
118
+
119
+
120
+ def validate_variant_project( projectdir: __.Path ) -> None:
121
+ ''' Validates standard project as generated from template. '''
122
+ for command in (
123
+ ( 'hatch', '--env', 'develop', 'run',
124
+ 'python', '-m', 'pip', 'install',
125
+ '--upgrade', 'pip', 'build' ),
126
+ ( 'hatch', '--env', 'develop', 'run', 'make-all' ),
127
+ ): _subprocess.run( command, cwd = str( projectdir ), check = True ) # noqa: S603
emcdproj/website.py CHANGED
@@ -33,8 +33,7 @@ from . import interfaces as _interfaces
33
33
 
34
34
 
35
35
  class CommandDispatcher(
36
- _interfaces.CliCommand,
37
- decorators = ( __.standard_tyro_class, ),
36
+ _interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
38
37
  ):
39
38
  ''' Dispatches commands for static website maintenance. '''
40
39
 
@@ -49,25 +48,27 @@ class CommandDispatcher(
49
48
  ],
50
49
  ]
51
50
 
52
- async def __call__( self, auxdata: __.Globals ) -> None:
51
+ async def __call__(
52
+ self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
53
+ ) -> None:
53
54
  ictr( 1 )( self.command )
54
- await self.command( auxdata = auxdata )
55
+ await self.command( auxdata = auxdata, display = display )
55
56
 
56
57
 
57
58
  class SurveyCommand(
58
- _interfaces.CliCommand,
59
- decorators = ( __.standard_tyro_class, ),
59
+ _interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
60
60
  ):
61
61
  ''' Surveys release versions published in static website. '''
62
62
 
63
- async def __call__( self, auxdata: __.Globals ) -> None:
63
+ async def __call__(
64
+ self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
65
+ ) -> None:
64
66
  # TODO: Implement.
65
67
  pass
66
68
 
67
69
 
68
70
  class UpdateCommand(
69
- _interfaces.CliCommand,
70
- decorators = ( __.standard_tyro_class, ),
71
+ _interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
71
72
  ):
72
73
  ''' Updates static website for particular release version. '''
73
74
 
@@ -77,7 +78,9 @@ class UpdateCommand(
77
78
  __.tyro.conf.Positional,
78
79
  ]
79
80
 
80
- async def __call__( self, auxdata: __.Globals ) -> None:
81
+ async def __call__(
82
+ self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
83
+ ) -> None:
81
84
  update( auxdata, self.version )
82
85
 
83
86