Coverage for sources/emcdproj/template.py: 45%

34 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-30 13:40 +0000

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 

24from __future__ import annotations 

25 

26import subprocess as _subprocess 

27import tempfile as _tempfile 

28 

29from . import __ 

30from . import interfaces as _interfaces 

31 

32 

33class 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 

56class 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 

69class 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 

88def 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 

97def 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 

106def 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 

120def 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