Coverage for sources/dynadoc/context.py: 100%
83 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-05-30 03:34 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2025-05-30 03:34 +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''' Data transfer objects for execution context. '''
24from . import __
25from . import interfaces as _interfaces
26from . import nomina as _nomina
29fragments_name_default = '_dynadoc_fragments_'
30introspection_limit_name_default = '_dynadoc_introspection_limit_'
33@__.dcls.dataclass( frozen = True, kw_only = True, slots = True )
34class Context:
36 _dynadoc_fragments_: __.typx.ClassVar[
37 _interfaces.Fragments ] = ( 'context', )
39 notifier: __.typx.Annotated[
40 _interfaces.Notifier,
41 _interfaces.Fname( 'notifier' ),
42 ]
43 fragment_rectifier: __.typx.Annotated[
44 _interfaces.FragmentRectifier,
45 _interfaces.Fname( 'fragment rectifier' ),
46 ]
47 visibility_decider: __.typx.Annotated[
48 _interfaces.VisibilityDecider,
49 _interfaces.Fname( 'visibility decider' ),
50 ]
51 fragments_name: __.typx.Annotated[
52 str,
53 _interfaces.Fname( 'fragments name' ),
54 ] = fragments_name_default
55 introspection_limit_name: __.typx.Annotated[
56 str,
57 _interfaces.Fname( 'introspection limit name' ),
58 ] = introspection_limit_name_default
59 invoker_globals: __.typx.Annotated[
60 __.typx.Optional[ _nomina.Variables ],
61 _interfaces.Fname( 'invoker globals' ),
62 ] = None
63 resolver_globals: __.typx.Annotated[
64 __.typx.Optional[ _nomina.Variables ],
65 _interfaces.Fname( 'resolver globals' ),
66 ] = None
67 resolver_locals: __.typx.Annotated[
68 __.typx.Optional[ _nomina.Variables ],
69 _interfaces.Fname( 'resolver locals' ),
70 ] = None
72 def with_invoker_globals(
73 self,
74 level: _interfaces.GlobalsLevelArgument = 2
75 ) -> __.typx.Self:
76 ''' Returns new context with invoker globals from stack frame. '''
77 iglobals = __.inspect.stack( )[ level ].frame.f_globals
78 return type( self )(
79 notifier = self.notifier,
80 fragment_rectifier = self.fragment_rectifier,
81 visibility_decider = self.visibility_decider,
82 fragments_name = self.fragments_name,
83 introspection_limit_name = self.introspection_limit_name,
84 invoker_globals = iglobals,
85 resolver_globals = self.resolver_globals,
86 resolver_locals = self.resolver_locals )
89ContextArgument: __.typx.TypeAlias = __.typx.Annotated[
90 Context, _interfaces.Fname( 'context' ) ]
91IntrospectionArgumentFref: __.typx.TypeAlias = __.typx.Annotated[
92 'IntrospectionControl', _interfaces.Fname( 'introspection' ) ]
95class ClassIntrospector( __.typx.Protocol ):
96 ''' Custom introspector for class annotations and attributes. '''
98 @staticmethod
99 def __call__( # noqa: PLR0913
100 possessor: _interfaces.PossessorClassArgument, /,
101 context: ContextArgument,
102 introspection: IntrospectionArgumentFref,
103 annotations: _interfaces.AnnotationsArgument,
104 cache: _interfaces.AnnotationsCacheArgument,
105 table: _interfaces.FragmentsTableArgument,
106 ) -> __.typx.Optional[ _interfaces.Informations ]:
107 ''' Introspects class and returns information about its members. '''
108 raise NotImplementedError # pragma: no cover
110ClassIntrospectors: __.typx.TypeAlias = __.cabc.Sequence[ ClassIntrospector ]
113@__.dcls.dataclass( frozen = True, kw_only = True, slots = True )
114class ClassIntrospectionLimit:
115 ''' Limits on class introspection behavior. '''
117 avoid_inheritance: __.typx.Annotated[
118 bool,
119 _interfaces.Doc( ''' Avoid introspecting inherited members? ''' ),
120 ] = False
121 ignore_attributes: __.typx.Annotated[
122 bool,
123 _interfaces.Doc(
124 ''' Ignore attributes not covered by annotations? ''' ),
125 ] = False
128@__.dcls.dataclass( frozen = True, kw_only = True, slots = True )
129class ClassIntrospectionControl:
130 ''' Controls on class introspection behavior. '''
132 inheritance: __.typx.Annotated[
133 bool, _interfaces.Doc( ''' Inherit annotations? ''' )
134 ] = False
135 introspectors: __.typx.Annotated[
136 ClassIntrospectors,
137 _interfaces.Doc( ''' Custom introspectors to apply. ''' ),
138 ] = ( )
139 scan_attributes: __.typx.Annotated[
140 bool,
141 _interfaces.Doc( ''' Scan attributes not covered by annotations? ''' ),
142 ] = False
144 def with_limit(
145 self,
146 limit: __.typx.Annotated[
147 ClassIntrospectionLimit,
148 _interfaces.Doc(
149 ''' Limits to apply to this introspection control. ''' ),
150 ]
151 ) -> __.typx.Self:
152 ''' Returns new control with applied limits. '''
153 inheritance = self.inheritance and not limit.avoid_inheritance
154 scan_attributes = self.scan_attributes and not limit.ignore_attributes
155 return type( self )(
156 inheritance = inheritance,
157 introspectors = self.introspectors,
158 scan_attributes = scan_attributes )
161@__.dcls.dataclass( frozen = True, kw_only = True, slots = True )
162class ModuleIntrospectionLimit:
163 ''' Limits on module introspection behavior. '''
165 ignore_attributes: __.typx.Annotated[
166 bool,
167 _interfaces.Doc(
168 ''' Ignore attributes not covered by annotations? ''' ),
169 ] = False
172@__.dcls.dataclass( frozen = True, kw_only = True, slots = True )
173class ModuleIntrospectionControl:
174 ''' Controls on module introspection behavior. '''
176 scan_attributes: __.typx.Annotated[
177 bool,
178 _interfaces.Doc( ''' Scan attributes not covered by annotations? ''' ),
179 ] = False
181 def with_limit(
182 self,
183 limit: __.typx.Annotated[
184 ModuleIntrospectionLimit,
185 _interfaces.Doc(
186 ''' Limits to apply to this introspection control. ''' ),
187 ]
188 ) -> __.typx.Self:
189 ''' Returns new control with applied limits. '''
190 scan_attributes = self.scan_attributes and not limit.ignore_attributes
191 return type( self )( scan_attributes = scan_attributes )
194class IntrospectionLimiter( __.typx.Protocol ):
195 ''' Can return modified introspection control for attribute. '''
197 @staticmethod
198 def __call__(
199 objct: __.typx.Annotated[
200 object,
201 _interfaces.Doc(
202 ''' Object being evaluated for introspection limits. ''' ),
203 ],
204 introspection: IntrospectionArgumentFref,
205 ) -> 'IntrospectionControl':
206 ''' Returns modified introspection control with limits applied. '''
207 raise NotImplementedError # pragma: no cover
209IntrospectionLimiters: __.typx.TypeAlias = __.typx.Annotated[
210 __.cabc.Sequence[ IntrospectionLimiter ],
211 _interfaces.Doc(
212 ''' Functions which can apply limits to introspection control. ''' ),
213]
216class IntrospectionTargets( __.enum.IntFlag ):
217 ''' Kinds of objects to recursively document. '''
219 Null = 0
220 Class = __.enum.auto( )
221 Descriptor = __.enum.auto( )
222 Function = __.enum.auto( )
223 Module = __.enum.auto( )
226IntrospectionTargetsSansModule: __.typx.Annotated[
227 IntrospectionTargets,
228 _interfaces.Doc( ''' All introspection targets except modules. ''' ),
229] = ( IntrospectionTargets.Class
230 | IntrospectionTargets.Descriptor
231 | IntrospectionTargets.Function )
232IntrospectionTargetsOmni: __.typx.Annotated[
233 IntrospectionTargets,
234 _interfaces.Doc(
235 ''' All available introspection targets including modules. ''' ),
236] = IntrospectionTargetsSansModule | IntrospectionTargets.Module
239@__.dcls.dataclass( frozen = True, kw_only = True, slots = True )
240class IntrospectionLimit:
241 ''' Limits on introspection behavior. '''
243 class_limit: __.typx.Annotated[
244 ClassIntrospectionLimit,
245 _interfaces.Doc( ''' Limits specific to class introspection. ''' ),
246 ] = ClassIntrospectionLimit( )
247 module_limit: __.typx.Annotated[
248 ModuleIntrospectionLimit,
249 _interfaces.Doc( ''' Limits specific to module introspection. ''' ),
250 ] = ModuleIntrospectionLimit( )
251 targets_exclusions: __.typx.Annotated[
252 IntrospectionTargets,
253 _interfaces.Doc( ''' Target types to exclude from introspection. ''' ),
254 ] = IntrospectionTargets.Null
257@__.dcls.dataclass( frozen = True, kw_only = True, slots = True )
258class IntrospectionControl:
260 _dynadoc_fragments_ = ( 'introspection', )
262 enable: __.typx.Annotated[
263 bool,
264 _interfaces.Doc( ''' Whether introspection is enabled at all. ''' ),
265 ] = True
266 class_control: __.typx.Annotated[
267 ClassIntrospectionControl,
268 _interfaces.Doc( ''' Controls specific to class introspection. ''' ),
269 ] = ClassIntrospectionControl( )
270 module_control: __.typx.Annotated[
271 ModuleIntrospectionControl,
272 _interfaces.Doc( ''' Controls specific to module introspection. ''' ),
273 ] = ModuleIntrospectionControl( )
274 limiters: __.typx.Annotated[
275 IntrospectionLimiters,
276 _interfaces.Doc(
277 ''' Functions that can apply limits to introspection. ''' ),
278 ] = ( )
279 targets: __.typx.Annotated[
280 IntrospectionTargets,
281 _interfaces.Doc(
282 ''' Which types of objects to recursively document. ''' ),
283 ] = IntrospectionTargets.Null
284 # TODO? Maximum depth.
285 # (Suggested by multiple LLMs; not convinced that it is needed.)
287 def evaluate_limits_for(
288 self,
289 objct: __.typx.Annotated[
290 object,
291 _interfaces.Doc( ''' Object to evaluate limits for. ''' ),
292 ]
293 ) -> 'IntrospectionControl':
294 ''' Determine which introspection limits apply to object. '''
295 introspection_ = self
296 for limiter in self.limiters:
297 introspection_ = limiter( objct, introspection_ )
298 return introspection_
300 def with_limit(
301 self,
302 limit: __.typx.Annotated[
303 IntrospectionLimit,
304 _interfaces.Doc(
305 ''' Limits to apply to this introspection control. ''' ),
306 ]
307 ) -> __.typx.Self:
308 ''' Returns new control with applied limits. '''
309 class_control = self.class_control.with_limit( limit.class_limit )
310 module_control = self.module_control.with_limit( limit.module_limit )
311 targets = self.targets & ~limit.targets_exclusions
312 return type( self )(
313 enable = self.enable,
314 class_control = class_control,
315 module_control = module_control,
316 limiters = self.limiters,
317 targets = targets )
320IntrospectionArgument: __.typx.TypeAlias = __.typx.Annotated[
321 IntrospectionControl, _interfaces.Fname( 'introspection' ) ]
324# def avoid_enum_inheritance(
325# objct: object, recursion: RecursionControl
326# ) -> RecursionControl:
327# ''' Enums inherit copious amounts of documentation. Avoids this. '''
328# if isinstance( objct, __.enum.EnumMeta ):
329# return recursion.with_limit(
330# RecursionLimit( avoid_inheritance = True ) )
331# return recursion