Skip to content

Commit 3e2f886

Browse files
author
Paolo Tranquilli
committed
Codegen: allow inheritable pragmas
1 parent 1bffc2a commit 3e2f886

File tree

3 files changed

+35
-20
lines changed

3 files changed

+35
-20
lines changed

misc/codegen/lib/schema.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,6 @@ def split_doc(doc):
211211
while trimmed and not trimmed[0]:
212212
trimmed.pop(0)
213213
return trimmed
214+
215+
216+
inheritable_pragma_prefix = "_inheritable_pragma_"

misc/codegen/lib/schemadefs.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
from misc.codegen.lib.schema import Property
77

8+
_set = set
9+
810

911
@_dataclass
1012
class _ChildModifier(_schema.PropertyModifier):
@@ -79,7 +81,7 @@ class _SynthModifier(_schema.PropertyModifier, _Namespace):
7981
def modify(self, prop: _schema.Property):
8082
prop.synth = self.synth
8183

82-
def negate(self) -> "PropertyModifier":
84+
def negate(self) -> _schema.PropertyModifier:
8385
return _SynthModifier(self.name, False)
8486

8587

@@ -100,14 +102,18 @@ class _ClassPragma(_PragmaBase):
100102
""" A class pragma.
101103
For schema classes it acts as a python decorator with `@`.
102104
"""
105+
inherited: bool = False
103106
value: object = None
104107

105108
def __call__(self, cls: type) -> type:
106109
""" use this pragma as a decorator on classes """
107-
# not using hasattr as we don't want to land on inherited pragmas
108-
if "_pragmas" not in cls.__dict__:
109-
cls._pragmas = {}
110-
self._apply(cls._pragmas)
110+
if self.inherited:
111+
setattr(cls, f"{_schema.inheritable_pragma_prefix}{self.pragma}", self.value)
112+
else:
113+
# not using hasattr as we don't want to land on inherited pragmas
114+
if "_pragmas" not in cls.__dict__:
115+
cls._pragmas = {}
116+
self._apply(cls._pragmas)
111117
return cls
112118

113119
def _apply(self, pragmas: _Dict[str, object]) -> None:
@@ -125,7 +131,7 @@ class _Pragma(_ClassPragma, _schema.PropertyModifier):
125131
def modify(self, prop: _schema.Property):
126132
self._apply(prop.pragmas)
127133

128-
def negate(self) -> "PropertyModifier":
134+
def negate(self) -> _schema.PropertyModifier:
129135
return _Pragma(self.pragma, remove=True)
130136

131137
def _apply(self, pragmas: _Dict[str, object]) -> None:
@@ -142,13 +148,14 @@ class _ParametrizedClassPragma(_PragmaBase):
142148
"""
143149
_pragma_class: _ClassVar[type] = _ClassPragma
144150

145-
function: _Callable[..., object] = None
151+
inherited: bool = False
152+
factory: _Callable[..., object] = None
146153

147154
def __post_init__(self):
148-
self.__signature__ = _inspect.signature(self.function).replace(return_annotation=self._pragma_class)
155+
self.__signature__ = _inspect.signature(self.factory).replace(return_annotation=self._pragma_class)
149156

150157
def __call__(self, *args, **kwargs) -> _pragma_class:
151-
return self._pragma_class(self.pragma, value=self.function(*args, **kwargs))
158+
return self._pragma_class(self.pragma, self.inherited, value=self.factory(*args, **kwargs))
152159

153160

154161
@_dataclass
@@ -233,24 +240,24 @@ def f(cls: type) -> type:
233240
qltest.add(_ClassPragma("uncollapse_hierarchy"))
234241
qltest.test_with = lambda cls: _annotate(test_with=cls) # inheritable
235242

236-
ql.add(_ParametrizedClassPragma("default_doc_name", lambda doc: doc))
243+
ql.add(_ParametrizedClassPragma("default_doc_name", factory=lambda doc: doc))
237244
ql.hideable = _annotate(hideable=True) # inheritable
238245
ql.add(_Pragma("internal"))
239246

240247
cpp.add(_Pragma("skip"))
241248

242249
rust.add(_Pragma("skip_doc_test"))
243250

244-
rust.add(_ParametrizedClassPragma("doc_test_signature", lambda signature: signature))
251+
rust.add(_ParametrizedClassPragma("doc_test_signature", factory=lambda signature: signature))
245252

246253

247254
def group(name: str = "") -> _ClassDecorator:
248255
return _annotate(group=name)
249256

250257

251-
synth.add(_ParametrizedClassPragma("from_class", lambda ref: _schema.SynthInfo(
258+
synth.add(_ParametrizedClassPragma("from_class", factory=lambda ref: _schema.SynthInfo(
252259
from_class=_schema.get_type_name(ref))), key="synth")
253-
synth.add(_ParametrizedClassPragma("on_arguments", lambda **kwargs:
260+
synth.add(_ParametrizedClassPragma("on_arguments", factory=lambda **kwargs:
254261
_schema.SynthInfo(on_arguments={k: _schema.get_type_name(t) for k, t in kwargs.items()})), key="synth")
255262

256263

@@ -288,12 +295,11 @@ def decorator(cls: type) -> _PropertyAnnotation:
288295
raise _schema.Error("Annotation classes must be named _")
289296
if cls.__doc__ is not None:
290297
annotated_cls.__doc__ = cls.__doc__
291-
old_pragmas = getattr(annotated_cls, "_pragmas", None)
292-
new_pragmas = getattr(cls, "_pragmas", {})
293-
if old_pragmas:
294-
old_pragmas.update(new_pragmas)
295-
else:
296-
annotated_cls._pragmas = new_pragmas
298+
for p, v in cls.__dict__.get("_pragmas", {}).items():
299+
_ClassPragma(p, value=v)(annotated_cls)
300+
for a in dir(cls):
301+
if a.startswith(_schema.inheritable_pragma_prefix):
302+
setattr(annotated_cls, a, getattr(cls, a))
297303
for a, v in cls.__dict__.items():
298304
# transfer annotations
299305
if a.startswith("_") and not a.startswith("__") and a != "_pragmas":

misc/codegen/loaders/schemaloader.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,21 @@ def _get_class(cls: type) -> schema.Class:
4141
raise schema.Error(f"Bases with mixed groups for {cls.__name__}")
4242
if any(getattr(b, "_null", False) for b in cls.__bases__):
4343
raise schema.Error(f"Null class cannot be derived")
44+
pragmas = {
45+
# dir and getattr inherit from bases
46+
a[len(schema.inheritable_pragma_prefix):]: getattr(cls, a)
47+
for a in dir(cls) if a.startswith(schema.inheritable_pragma_prefix)
48+
}
49+
pragmas |= cls.__dict__.get("_pragmas", {})
4450
return schema.Class(name=cls.__name__,
4551
bases=[b.__name__ for b in cls.__bases__ if b is not object],
4652
derived={d.__name__ for d in cls.__subclasses__()},
4753
# getattr to inherit from bases
4854
group=getattr(cls, "_group", ""),
4955
hideable=getattr(cls, "_hideable", False),
5056
test_with=_get_name(getattr(cls, "_test_with", None)),
57+
pragmas=pragmas,
5158
# in the following we don't use `getattr` to avoid inheriting
52-
pragmas=cls.__dict__.get("_pragmas", {}),
5359
properties=[
5460
a | _PropertyNamer(n)
5561
for n, a in cls.__dict__.get("__annotations__", {}).items()

0 commit comments

Comments
 (0)