1
- from typing import Callable as _Callable
1
+ from typing import Callable as _Callable , List as _List
2
2
from misc .codegen .lib import schema as _schema
3
3
import inspect as _inspect
4
4
from dataclasses import dataclass as _dataclass
5
5
6
+ from misc .codegen .lib .schema import Property
6
7
8
+
9
+ @_dataclass
7
10
class _ChildModifier (_schema .PropertyModifier ):
11
+ child : bool = True
12
+
8
13
def modify (self , prop : _schema .Property ):
9
14
if prop .type is None or prop .type [0 ].islower ():
10
15
raise _schema .Error ("Non-class properties cannot be children" )
11
16
if prop .is_unordered :
12
17
raise _schema .Error ("Set properties cannot be children" )
13
- prop .is_child = True
18
+ prop .is_child = self .child
19
+
20
+ def negate (self ) -> _schema .PropertyModifier :
21
+ return _ChildModifier (False )
22
+
23
+ # make ~doc same as doc(None)
24
+
25
+
26
+ class _DocModifierMetaclass (type (_schema .PropertyModifier )):
27
+ def __invert__ (self ) -> _schema .PropertyModifier :
28
+ return _DocModifier (None )
14
29
15
30
16
31
@_dataclass
17
- class _DocModifier (_schema .PropertyModifier ):
18
- doc : str
32
+ class _DocModifier (_schema .PropertyModifier , metaclass = _DocModifierMetaclass ):
33
+ doc : str | None
19
34
20
35
def modify (self , prop : _schema .Property ):
21
- if "\n " in self .doc or self .doc [- 1 ] == "." :
36
+ if self . doc and ( "\n " in self .doc or self .doc [- 1 ] == "." ) :
22
37
raise _schema .Error ("No newlines or trailing dots are allowed in doc, did you intend to use desc?" )
23
38
prop .doc = self .doc
24
39
40
+ def negate (self ) -> _schema .PropertyModifier :
41
+ return _DocModifier (None )
42
+
43
+
44
+ # make ~desc same as desc(None)
45
+ class _DescModifierMetaclass (type (_schema .PropertyModifier )):
46
+ def __invert__ (self ) -> _schema .PropertyModifier :
47
+ return _DescModifier (None )
48
+
25
49
26
50
@_dataclass
27
- class _DescModifier (_schema .PropertyModifier ):
28
- description : str
51
+ class _DescModifier (_schema .PropertyModifier , metaclass = _DescModifierMetaclass ):
52
+ description : str | None
29
53
30
54
def modify (self , prop : _schema .Property ):
31
55
prop .description = _schema .split_doc (self .description )
32
56
57
+ def negate (self ) -> _schema .PropertyModifier :
58
+ return _DescModifier (None )
59
+
33
60
34
61
def include (source : str ):
35
62
# add to `includes` variable in calling context
36
- _inspect .currentframe ().f_back .f_locals .setdefault (
37
- "__includes" , []).append (source )
63
+ _inspect .currentframe ().f_back .f_locals .setdefault ("includes" , []).append (source )
38
64
39
65
40
66
class _Namespace :
@@ -44,9 +70,15 @@ def __init__(self, **kwargs):
44
70
self .__dict__ .update (kwargs )
45
71
46
72
73
+ @_dataclass
47
74
class _SynthModifier (_schema .PropertyModifier , _Namespace ):
75
+ synth : bool = True
76
+
48
77
def modify (self , prop : _schema .Property ):
49
- prop .synth = True
78
+ prop .synth = self .synth
79
+
80
+ def negate (self ) -> "PropertyModifier" :
81
+ return _SynthModifier (False )
50
82
51
83
52
84
qltest = _Namespace ()
@@ -63,22 +95,35 @@ class _Pragma(_schema.PropertyModifier):
63
95
For schema classes it acts as a python decorator with `@`.
64
96
"""
65
97
pragma : str
98
+ remove : bool = False
66
99
67
100
def __post_init__ (self ):
68
101
namespace , _ , name = self .pragma .partition ('_' )
69
102
setattr (globals ()[namespace ], name , self )
70
103
71
104
def modify (self , prop : _schema .Property ):
72
- prop .pragmas .append (self .pragma )
105
+ self ._apply (prop .pragmas )
106
+
107
+ def negate (self ) -> "PropertyModifier" :
108
+ return _Pragma (self .pragma , remove = True )
73
109
74
110
def __call__ (self , cls : type ) -> type :
75
111
""" use this pragma as a decorator on classes """
76
112
if "_pragmas" in cls .__dict__ : # not using hasattr as we don't want to land on inherited pragmas
77
- cls . _pragmas . append ( self . pragma )
78
- else :
113
+ self . _apply ( cls . _pragmas )
114
+ elif not self . remove :
79
115
cls ._pragmas = [self .pragma ]
80
116
return cls
81
117
118
+ def _apply (self , pragmas : _List [str ]) -> None :
119
+ if self .remove :
120
+ try :
121
+ pragmas .remove (self .pragma )
122
+ except ValueError :
123
+ pass
124
+ else :
125
+ pragmas .append (self .pragma )
126
+
82
127
83
128
class _Optionalizer (_schema .PropertyModifier ):
84
129
def modify (self , prop : _schema .Property ):
@@ -172,17 +217,46 @@ def group(name: str = "") -> _ClassDecorator:
172
217
synth = _schema .SynthInfo (on_arguments = {k : _schema .get_type_name (t ) for k , t in kwargs .items ()}))
173
218
174
219
175
- def annotate (annotated_cls : type ) -> _Callable [[type ], None ]:
220
+ class _PropertyModifierList (_schema .PropertyModifier ):
221
+ def __init__ (self ):
222
+ self ._mods = []
223
+
224
+ def __or__ (self , other : _schema .PropertyModifier ):
225
+ self ._mods .append (other )
226
+ return self
227
+
228
+ def modify (self , prop : Property ):
229
+ for m in self ._mods :
230
+ m .modify (prop )
231
+
232
+
233
+ class _PropertyAnnotation :
234
+ def __or__ (self , other : _schema .PropertyModifier ):
235
+ return _PropertyModifierList () | other
236
+
237
+
238
+ _ = _PropertyAnnotation ()
239
+
240
+
241
+ def annotate (annotated_cls : type ) -> _Callable [[type ], _PropertyAnnotation ]:
176
242
"""
177
243
Add or modify schema annotations after a class has been defined
178
244
For the moment, only docstring annotation is supported. In the future, any kind of
179
245
modification will be allowed.
180
246
181
247
The name of the class used for annotation must be `_`
182
248
"""
183
- def decorator (cls : type ) -> None :
249
+ def decorator (cls : type ) -> _PropertyAnnotation :
184
250
if cls .__name__ != "_" :
185
251
raise _schema .Error ("Annotation classes must be named _" )
186
252
annotated_cls .__doc__ = cls .__doc__
187
- return None
253
+ for p , a in cls .__annotations__ .items ():
254
+ if p in annotated_cls .__annotations__ :
255
+ annotated_cls .__annotations__ [p ] |= a
256
+ elif isinstance (a , (_PropertyAnnotation , _PropertyModifierList )):
257
+ raise _schema .Error (f"annotated property { p } not present in annotated class "
258
+ f"{ annotated_cls .__name__ } " )
259
+ else :
260
+ annotated_cls .__annotations__ [p ] = a
261
+ return _
188
262
return decorator
0 commit comments