Coverage for sources/emcdproj/template.py: 43%
38 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-28 23:18 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-28 23:18 +0000
1# vim: set filetype=python fileencoding=utf-8:
2# -*- coding: utf-8 -*-
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#============================================================================#
21''' Copier template maintenance and validation. '''
24import subprocess as _subprocess
26from . import __
27from . import interfaces as _interfaces
30class SurveyCommand(
31 _interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
32):
33 ''' Surveys available configuration variants. '''
35 async def __call__(
36 self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
37 ) -> None:
38 stream = await display.provide_stream( )
39 for variant in survey_variants( auxdata ):
40 print( variant, file = stream )
43class ValidateCommand(
44 _interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
45):
46 ''' Validates template against configuration variant. '''
48 variant: __.typx.Annotated[
49 str,
50 __.typx.Doc( ''' Configuration variant to validate. ''' ),
51 __.tyro.conf.Positional,
52 ]
53 preserve: __.typx.Annotated[
54 bool,
55 __.typx.Doc( ''' Preserve generated project for inspection? ''' ),
56 ] = False
58 async def __call__(
59 self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
60 ) -> None:
61 ''' Copies new project from template for configuration variant. '''
62 # TODO: Validate variant argument.
63 validate_variant(
64 auxdata, self.variant, preserve = self.preserve )
67class CommandDispatcher(
68 _interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
69):
70 ''' Dispatches commands for Copier template maintenance. '''
72 command: __.typx.Union[
73 __.typx.Annotated[
74 SurveyCommand,
75 __.tyro.conf.subcommand( 'survey', prefix_name = False ),
76 ],
77 __.typx.Annotated[
78 ValidateCommand,
79 __.tyro.conf.subcommand( 'validate', prefix_name = False ),
80 ],
81 ]
83 async def __call__(
84 self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
85 ) -> None:
86 ictr( 1 )( self.command )
87 await self.command( auxdata = auxdata, display = display )
90def copy_template( answers_file: __.Path, projectdir: __.Path ) -> None:
91 ''' Copies template to target directory using answers. '''
92 _subprocess.run( # noqa: S603
93 ( 'copier', 'copy', # noqa: S607
94 '--data-file', str( answers_file ),
95 '--defaults', '--overwrite', '--vcs-ref', 'HEAD',
96 '.', str( projectdir ) ),
97 cwd = __.Path( ), check = True )
100def survey_variants( auxdata: __.Globals ) -> __.cabc.Sequence[ str ]:
101 ''' Surveys available configuration variants. '''
102 location = auxdata.distribution.provide_data_location( 'copier' )
103 return tuple(
104 fsent.stem.lstrip( 'answers-' )
105 for fsent in location.glob( 'answers-*.yaml' )
106 if fsent.is_file( ) )
109def validate_variant(
110 auxdata: __.Globals, variant: str, preserve: bool
111) -> None:
112 ''' Validates configuration variant. '''
113 answers_file = (
114 auxdata.distribution.provide_data_location(
115 'copier', f"answers-{variant}.yaml" ) )
116 if not answers_file.is_file( ):
117 # TODO: Raise error.
118 return
119 with _manage_temporary_directory( preserve = preserve ) as tmpdir:
120 projectdir = tmpdir / variant
121 copy_template( answers_file, projectdir )
122 validate_variant_project( projectdir )
125def validate_variant_project( projectdir: __.Path ) -> None:
126 ''' Validates standard project as generated from template. '''
127 for command in (
128 ( 'hatch', '--env', 'develop', 'run',
129 'python', '-m', 'pip', 'install',
130 '--upgrade', 'pip', 'build' ),
131 ( 'hatch', '--env', 'develop', 'run', 'make-all' ),
132 ): _subprocess.run( command, cwd = str( projectdir ), check = True ) # noqa: S603
135@__.ctxl.contextmanager
136def _manage_temporary_directory(
137 preserve: bool
138) -> __.cabc.Iterator[ __.Path ]:
139 # TODO: Python 3.12: Replace with tempfile.TemporaryDirectory,
140 # ( delete = not preserve )
141 location = __.Path( __.tempfile.mkdtemp( ) )
142 yield location
143 if not preserve: __.shutil.rmtree( location )