Coverage for sources/classcore/standard/decorators.py: 100%

132 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2025-06-08 04:17 +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''' Standard decorators. ''' 

22# TODO? Add attribute value transformer as standard decorator argument. 

23 

24# ruff: noqa: F401 

25 

26 

27from .. import factories as _factories 

28from .. import utilities as _utilities 

29from ..decorators import ( 

30 decoration_by, 

31 produce_class_construction_decorator, 

32 produce_class_initialization_decorator, 

33) 

34from . import __ 

35from . import behaviors as _behaviors 

36from . import dynadoc as _dynadoc 

37from . import nomina as _nomina 

38 

39 

40_dataclass_core = __.dcls.dataclass( kw_only = True, slots = True ) 

41_dynadoc_configuration = _dynadoc.produce_dynadoc_configuration( ) 

42 

43 

44def prepare_dataclass_for_instances( 

45 cls: type, 

46 decorators: _nomina.DecoratorsMutable[ __.U ], /, *, 

47 attributes_namer: _nomina.AttributesNamer, 

48) -> None: 

49 ''' Annotates dataclass in support of instantiation machinery. ''' 

50 annotations = __.inspect.get_annotations( cls ) 

51 behaviors_name = attributes_namer( 'instance', 'behaviors' ) 

52 behaviors_name_m = _utilities.mangle_name( cls, behaviors_name ) 

53 annotations[ behaviors_name_m ] = set[ str ] 

54 setattr( cls, '__annotations__', annotations ) # in case of absence 

55 setattr( cls, behaviors_name_m, __.dcls.field( init = False ) ) 

56 

57 

58def apply_cfc_dynadoc_configuration( 

59 clscls: type[ __.T ], /, 

60 attributes_namer: _nomina.AttributesNamer, 

61 configuration: _nomina.DynadocConfiguration, 

62) -> None: 

63 ''' Stores Dynadoc configuration on metaclass. ''' 

64 configuration_name = attributes_namer( 'classes', 'dynadoc_configuration' ) 

65 setattr( clscls, configuration_name, configuration ) 

66 

67 

68def apply_cfc_constructor( 

69 clscls: type[ __.T ], /, 

70 attributes_namer: _nomina.AttributesNamer, 

71 error_class_provider: _nomina.ErrorClassProvider, 

72) -> None: 

73 ''' Injects '__new__' method into metaclass. ''' 

74 preprocessors = ( 

75 _behaviors.produce_class_construction_preprocessor( 

76 attributes_namer = attributes_namer ), ) 

77 postprocessors = ( 

78 _behaviors.produce_class_construction_postprocessor( 

79 attributes_namer = attributes_namer, 

80 error_class_provider = error_class_provider ), ) 

81 constructor: _nomina.ClassConstructor[ __.T ] = ( 

82 _factories.produce_class_constructor( 

83 attributes_namer = attributes_namer, 

84 preprocessors = preprocessors, 

85 postprocessors = postprocessors ) ) 

86 decorator = produce_class_construction_decorator( 

87 attributes_namer = attributes_namer, constructor = constructor ) 

88 decorator( clscls ) 

89 

90 

91def apply_cfc_initializer( 

92 clscls: type[ __.T ], attributes_namer: _nomina.AttributesNamer 

93) -> None: 

94 ''' Injects '__init__' method into metaclass. ''' 

95 completers = ( 

96 _behaviors.produce_class_initialization_completer( 

97 attributes_namer = attributes_namer ), ) 

98 initializer = ( 

99 _factories.produce_class_initializer( 

100 attributes_namer = attributes_namer, 

101 completers = completers ) ) 

102 decorator = produce_class_initialization_decorator( 

103 attributes_namer = attributes_namer, initializer = initializer ) 

104 decorator( clscls ) 

105 

106 

107def apply_cfc_attributes_assigner( 

108 clscls: type[ __.T ], /, 

109 attributes_namer: _nomina.AttributesNamer, 

110 error_class_provider: _nomina.ErrorClassProvider, 

111 implementation_core: _nomina.AssignerCore, 

112) -> None: 

113 ''' Injects '__setattr__' method into metaclass. ''' 

114 decorator = produce_attributes_assignment_decorator( 

115 level = 'class', 

116 attributes_namer = attributes_namer, 

117 error_class_provider = error_class_provider, 

118 implementation_core = implementation_core ) 

119 decorator( clscls ) 

120 

121 

122def apply_cfc_attributes_deleter( 

123 clscls: type[ __.T ], /, 

124 attributes_namer: _nomina.AttributesNamer, 

125 error_class_provider: _nomina.ErrorClassProvider, 

126 implementation_core: _nomina.DeleterCore, 

127) -> None: 

128 ''' Injects '__delattr__' method into metaclass. ''' 

129 decorator = produce_attributes_deletion_decorator( 

130 level = 'class', 

131 attributes_namer = attributes_namer, 

132 error_class_provider = error_class_provider, 

133 implementation_core = implementation_core ) 

134 decorator( clscls ) 

135 

136 

137def apply_cfc_attributes_surveyor( 

138 clscls: type[ __.T ], 

139 attributes_namer: _nomina.AttributesNamer, 

140 implementation_core: _nomina.SurveyorCore, 

141) -> None: 

142 ''' Injects '__dir__' method into metaclass. ''' 

143 decorator = produce_attributes_surveillance_decorator( 

144 level = 'class', 

145 attributes_namer = attributes_namer, 

146 implementation_core = implementation_core ) 

147 decorator( clscls ) 

148 

149 

150def class_factory( # noqa: PLR0913 

151 attributes_namer: _nomina.AttributesNamer = __.calculate_attrname, 

152 error_class_provider: _nomina.ErrorClassProvider = __.provide_error_class, 

153 assigner_core: _nomina.AssignerCore = ( 

154 _behaviors.assign_attribute_if_mutable ), 

155 deleter_core: _nomina.DeleterCore = ( 

156 _behaviors.delete_attribute_if_mutable ), 

157 surveyor_core: _nomina.SurveyorCore = ( 

158 _behaviors.survey_visible_attributes ), 

159 dynadoc_configuration: __.cabc.Mapping[ str, __.typx.Any ] = ( 

160 _dynadoc_configuration ), 

161) -> _nomina.Decorator[ __.T ]: 

162 ''' Produces decorator to apply standard behaviors to metaclass. ''' 

163 def decorate( clscls: type[ __.T ] ) -> type[ __.T ]: 

164 apply_cfc_dynadoc_configuration( 

165 clscls, 

166 attributes_namer = attributes_namer, 

167 configuration = dynadoc_configuration ) 

168 apply_cfc_constructor( 

169 clscls, 

170 attributes_namer = attributes_namer, 

171 error_class_provider = error_class_provider ) 

172 apply_cfc_initializer( clscls, attributes_namer = attributes_namer ) 

173 apply_cfc_attributes_assigner( 

174 clscls, 

175 attributes_namer = attributes_namer, 

176 error_class_provider = error_class_provider, 

177 implementation_core = assigner_core ) 

178 apply_cfc_attributes_deleter( 

179 clscls, 

180 attributes_namer = attributes_namer, 

181 error_class_provider = error_class_provider, 

182 implementation_core = deleter_core ) 

183 apply_cfc_attributes_surveyor( 

184 clscls, 

185 attributes_namer = attributes_namer, 

186 implementation_core = surveyor_core ) 

187 return clscls 

188 

189 return decorate 

190 

191 

192def produce_instances_initialization_decorator( 

193 attributes_namer: _nomina.AttributesNamer, 

194 mutables: _nomina.BehaviorExclusionVerifiersOmni, 

195 visibles: _nomina.BehaviorExclusionVerifiersOmni, 

196) -> _nomina.Decorator[ __.U ]: 

197 ''' Produces decorator to inject '__init__' method into class. ''' 

198 def decorate( cls: type[ __.U ] ) -> type[ __.U ]: 

199 initializer_name = attributes_namer( 'instances', 'initializer' ) 

200 extant = getattr( cls, initializer_name, None ) 

201 original = getattr( cls, '__init__' ) 

202 if extant is original: return cls 

203 behaviors: set[ str ] = set( ) 

204 behaviors_name = attributes_namer( 'instance', 'behaviors' ) 

205 behaviors_name_m = _utilities.mangle_name( cls, behaviors_name ) 

206 _behaviors.record_behavior( 

207 cls, attributes_namer = attributes_namer, 

208 level = 'instances', basename = 'mutables', 

209 label = _nomina.immutability_label, behaviors = behaviors, 

210 verifiers = mutables ) 

211 _behaviors.record_behavior( 

212 cls, attributes_namer = attributes_namer, 

213 level = 'instances', basename = 'visibles', 

214 label = _nomina.concealment_label, behaviors = behaviors, 

215 verifiers = visibles ) 

216 

217 @__.funct.wraps( original ) 

218 def initialize( 

219 self: object, *posargs: __.typx.Any, **nomargs: __.typx.Any 

220 ) -> None: 

221 original( self, *posargs, **nomargs ) 

222 behaviors_: set[ str ] = getattr( self, behaviors_name_m, set( ) ) 

223 behaviors_.update( behaviors ) 

224 setattr( self, behaviors_name_m, frozenset( behaviors_ ) ) 

225 

226 setattr( cls, initializer_name, initialize ) 

227 cls.__init__ = initialize 

228 return cls 

229 

230 return decorate 

231 

232 

233def produce_attributes_assignment_decorator( 

234 level: str, 

235 attributes_namer: _nomina.AttributesNamer, 

236 error_class_provider: _nomina.ErrorClassProvider, 

237 implementation_core: _nomina.AssignerCore, 

238) -> _nomina.Decorator[ __.U ]: 

239 ''' Produces decorator to inject '__setattr__' method into class. ''' 

240 def decorate( cls: type[ __.U ] ) -> type[ __.U ]: 

241 assigner_name = attributes_namer( level, 'assigner' ) 

242 extant = getattr( cls, assigner_name, None ) 

243 original = getattr( cls, '__setattr__' ) 

244 if extant is original: return cls 

245 

246 @__.funct.wraps( original ) 

247 def assign( self: object, name: str, value: __.typx.Any ) -> None: 

248 implementation_core( 

249 self, 

250 ligation = __.funct.partial( original, self ), 

251 attributes_namer = attributes_namer, 

252 error_class_provider = error_class_provider, 

253 level = level, 

254 name = name, value = value ) 

255 

256 setattr( cls, assigner_name, assign ) 

257 cls.__setattr__ = assign 

258 return cls 

259 

260 return decorate 

261 

262 

263def produce_attributes_deletion_decorator( 

264 level: str, 

265 attributes_namer: _nomina.AttributesNamer, 

266 error_class_provider: _nomina.ErrorClassProvider, 

267 implementation_core: _nomina.DeleterCore, 

268) -> _nomina.Decorator[ __.U ]: 

269 ''' Produces decorator to inject '__delattr__' method into class. ''' 

270 def decorate( cls: type[ __.U ] ) -> type[ __.U ]: 

271 deleter_name = attributes_namer( level, 'deleter' ) 

272 extant = getattr( cls, deleter_name, None ) 

273 original = getattr( cls, '__delattr__' ) 

274 if extant is original: return cls 

275 

276 @__.funct.wraps( original ) 

277 def delete( self: object, name: str ) -> None: 

278 implementation_core( 

279 self, 

280 ligation = __.funct.partial( original, self ), 

281 attributes_namer = attributes_namer, 

282 error_class_provider = error_class_provider, 

283 level = level, 

284 name = name ) 

285 

286 setattr( cls, deleter_name, delete ) 

287 cls.__delattr__ = delete 

288 return cls 

289 

290 return decorate 

291 

292 

293def produce_attributes_surveillance_decorator( 

294 level: str, 

295 attributes_namer: _nomina.AttributesNamer, 

296 implementation_core: _nomina.SurveyorCore, 

297) -> _nomina.Decorator[ __.U ]: 

298 ''' Produces decorator to inject '__dir__' method into class. ''' 

299 def decorate( cls: type[ __.U ] ) -> type[ __.U ]: 

300 surveyor_name = attributes_namer( level, 'surveyor' ) 

301 extant = getattr( cls, surveyor_name, None ) 

302 original = getattr( cls, '__dir__' ) 

303 if extant is original: return cls 

304 

305 @__.funct.wraps( original ) 

306 def survey( self: object ) -> __.cabc.Iterable[ str ]: 

307 return implementation_core( 

308 self, 

309 ligation = __.funct.partial( original, self ), 

310 attributes_namer = attributes_namer, 

311 level = level ) 

312 

313 setattr( cls, surveyor_name, survey ) 

314 cls.__dir__ = survey 

315 return cls 

316 

317 return decorate 

318 

319 

320@__.typx.dataclass_transform( frozen_default = True, kw_only_default = True ) 

321def dataclass_with_standard_behaviors( # noqa: PLR0913 

322 attributes_namer: _nomina.AttributesNamer = __.calculate_attrname, 

323 error_class_provider: _nomina.ErrorClassProvider = __.provide_error_class, 

324 decorators: _nomina.Decorators[ __.U ] = ( ), 

325 assigner_core: _nomina.AssignerCore = ( 

326 _behaviors.assign_attribute_if_mutable ), 

327 deleter_core: _nomina.DeleterCore = ( 

328 _behaviors.delete_attribute_if_mutable ), 

329 surveyor_core: _nomina.SurveyorCore = ( 

330 _behaviors.survey_visible_attributes ), 

331 mutables: _nomina.BehaviorExclusionVerifiersOmni = __.mutables_default, 

332 visibles: _nomina.BehaviorExclusionVerifiersOmni = __.visibles_default, 

333) -> _nomina.Decorator[ __.U ]: 

334 # https://github.com/microsoft/pyright/discussions/10344 

335 ''' Dataclass decorator factory. ''' 

336 decorators_: _nomina.Decorators[ __.U ] = ( 

337 _produce_instances_decorators( 

338 attributes_namer = attributes_namer, 

339 error_class_provider = error_class_provider, 

340 assigner_core = assigner_core, 

341 deleter_core = deleter_core, 

342 surveyor_core = surveyor_core, 

343 mutables = mutables, 

344 visibles = visibles ) ) 

345 preparers: _nomina.DecorationPreparers[ __.U ] = ( 

346 _produce_instances_decoration_preparers( 

347 attributes_namer = attributes_namer, 

348 error_class_provider = error_class_provider, 

349 class_preparer = prepare_dataclass_for_instances ) ) 

350 return decoration_by( 

351 *decorators, _dataclass_core, *decorators_, preparers = preparers ) 

352 

353 

354def with_standard_behaviors( # noqa: PLR0913 

355 attributes_namer: _nomina.AttributesNamer = __.calculate_attrname, 

356 error_class_provider: _nomina.ErrorClassProvider = __.provide_error_class, 

357 decorators: _nomina.Decorators[ __.U ] = ( ), 

358 assigner_core: _nomina.AssignerCore = ( 

359 _behaviors.assign_attribute_if_mutable ), 

360 deleter_core: _nomina.DeleterCore = ( 

361 _behaviors.delete_attribute_if_mutable ), 

362 surveyor_core: _nomina.SurveyorCore = ( 

363 _behaviors.survey_visible_attributes ), 

364 mutables: _nomina.BehaviorExclusionVerifiersOmni = __.mutables_default, 

365 visibles: _nomina.BehaviorExclusionVerifiersOmni = __.visibles_default, 

366) -> _nomina.Decorator[ __.U ]: 

367 ''' Class decorator factory. ''' 

368 decorators_: _nomina.Decorators[ __.U ] = ( 

369 _produce_instances_decorators( 

370 attributes_namer = attributes_namer, 

371 error_class_provider = error_class_provider, 

372 assigner_core = assigner_core, 

373 deleter_core = deleter_core, 

374 surveyor_core = surveyor_core, 

375 mutables = mutables, 

376 visibles = visibles ) ) 

377 preparers: _nomina.DecorationPreparers[ __.U ] = ( 

378 _produce_instances_decoration_preparers( 

379 attributes_namer = attributes_namer, 

380 error_class_provider = error_class_provider ) ) 

381 return decoration_by( *decorators, *decorators_, preparers = preparers ) 

382 

383 

384def _produce_instances_decoration_preparers( 

385 attributes_namer: _nomina.AttributesNamer, 

386 error_class_provider: _nomina.ErrorClassProvider, 

387 class_preparer: __.typx.Optional[ _nomina.ClassPreparer ] = None, 

388) -> _nomina.DecorationPreparers[ __.U ]: 

389 ''' Produces processors for standard decorators. ''' 

390 preprocessors: list[ _nomina.DecorationPreparer[ __.U ] ] = [ ] 

391 if class_preparer is not None: 

392 preprocessors.append( 

393 __.funct.partial( 

394 class_preparer, attributes_namer = attributes_namer ) ) 

395 return tuple( preprocessors ) 

396 

397 

398def _produce_instances_decorators( # noqa: PLR0913 

399 attributes_namer: _nomina.AttributesNamer, 

400 error_class_provider: _nomina.ErrorClassProvider, 

401 assigner_core: _nomina.AssignerCore, 

402 deleter_core: _nomina.DeleterCore, 

403 surveyor_core: _nomina.SurveyorCore, 

404 mutables: _nomina.BehaviorExclusionVerifiersOmni, 

405 visibles: _nomina.BehaviorExclusionVerifiersOmni, 

406) -> _nomina.Decorators[ __.U ]: 

407 ''' Produces standard decorators. ''' 

408 decorators: list[ _nomina.Decorator[ __.U ] ] = [ ] 

409 decorators.append( 

410 produce_instances_initialization_decorator( 

411 attributes_namer = attributes_namer, 

412 mutables = mutables, visibles = visibles ) ) 

413 if mutables != '*': 

414 decorators.append( 

415 produce_attributes_assignment_decorator( 

416 level = 'instances', 

417 attributes_namer = attributes_namer, 

418 error_class_provider = error_class_provider, 

419 implementation_core = assigner_core ) ) 

420 decorators.append( 

421 produce_attributes_deletion_decorator( 

422 level = 'instances', 

423 attributes_namer = attributes_namer, 

424 error_class_provider = error_class_provider, 

425 implementation_core = deleter_core ) ) 

426 if visibles != '*': 

427 decorators.append( 

428 produce_attributes_surveillance_decorator( 

429 level = 'instances', 

430 attributes_namer = attributes_namer, 

431 implementation_core = surveyor_core ) ) 

432 return decorators