Skip to content

Commit 841214f

Browse files
author
Paolo Tranquilli
committed
Codegen: introduce property-only pragmas
1 parent 91b7329 commit 841214f

File tree

3 files changed

+63
-45
lines changed

3 files changed

+63
-45
lines changed

misc/codegen/lib/schemadefs.py

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
Callable as _Callable,
33
Dict as _Dict,
44
Iterable as _Iterable,
5-
ClassVar as _ClassVar,
5+
Union as _Union,
66
)
7+
from copy import deepcopy as _deepcopy
78
from misc.codegen.lib import schema as _schema
89
import inspect as _inspect
910
from dataclasses import dataclass as _dataclass
@@ -75,7 +76,7 @@ class _Namespace:
7576
""" simple namespacing mechanism """
7677
_name: str
7778

78-
def add(self, pragma: "_PragmaBase", key: str | None = None):
79+
def add(self, pragma: _Union["_PragmaBase", "_Parametrized"], key: str | None = None):
7980
self.__dict__[pragma.pragma] = pragma
8081
pragma.pragma = key or f"{self._name}_{pragma.pragma}"
8182

@@ -101,6 +102,10 @@ def negate(self) -> _schema.PropertyModifier:
101102
@_dataclass
102103
class _PragmaBase:
103104
pragma: str
105+
value: object = None
106+
107+
def _apply(self, pragmas: _Dict[str, object]) -> None:
108+
pragmas[self.pragma] = self.value
104109

105110

106111
@_dataclass
@@ -109,7 +114,6 @@ class _ClassPragma(_PragmaBase):
109114
For schema classes it acts as a python decorator with `@`.
110115
"""
111116
inherited: bool = False
112-
value: object = None
113117

114118
def __call__(self, cls: type) -> type:
115119
""" use this pragma as a decorator on classes """
@@ -122,23 +126,19 @@ def __call__(self, cls: type) -> type:
122126
self._apply(cls._pragmas)
123127
return cls
124128

125-
def _apply(self, pragmas: _Dict[str, object]) -> None:
126-
pragmas[self.pragma] = self.value
127-
128129

129130
@_dataclass
130-
class _Pragma(_ClassPragma, _schema.PropertyModifier):
131-
""" A class or property pragma.
132-
For properties, it functions similarly to a `_PropertyModifier` with `|`, adding the pragma.
133-
For schema classes it acts as a python decorator with `@`.
131+
class _PropertyPragma(_PragmaBase, _schema.PropertyModifier):
132+
""" A property pragma.
133+
It functions similarly to a `_PropertyModifier` with `|`, adding the pragma.
134134
"""
135135
remove: bool = False
136136

137137
def modify(self, prop: _schema.Property):
138138
self._apply(prop.pragmas)
139139

140140
def negate(self) -> _schema.PropertyModifier:
141-
return _Pragma(self.pragma, remove=True)
141+
return _PropertyPragma(self.pragma, remove=not self.remove)
142142

143143
def _apply(self, pragmas: _Dict[str, object]) -> None:
144144
if self.remove:
@@ -148,31 +148,38 @@ def _apply(self, pragmas: _Dict[str, object]) -> None:
148148

149149

150150
@_dataclass
151-
class _ParametrizedClassPragma(_PragmaBase):
152-
""" A class parametrized pragma.
153-
Needs to be applied to a parameter to give a class pragma.
151+
class _Pragma(_ClassPragma, _PropertyPragma):
152+
""" A class or property pragma.
153+
For properties, it functions similarly to a `_PropertyModifier` with `|`, adding the pragma.
154+
For schema classes it acts as a python decorator with `@`.
154155
"""
155-
_pragma_class: _ClassVar[type] = _ClassPragma
156156

157-
inherited: bool = False
158-
factory: _Callable[..., object] = None
159157

160-
def __post_init__(self):
161-
self.__signature__ = _inspect.signature(self.factory).replace(return_annotation=self._pragma_class)
158+
class _Parametrized[P, **Q, T]:
159+
""" A parametrized pragma.
160+
Needs to be applied to a parameter to give a pragma.
161+
"""
162+
163+
def __init__(self, pragma_instance: P, factory: _Callable[Q, T]):
164+
self.pragma_instance = pragma_instance
165+
self.factory = factory
166+
self.__signature__ = _inspect.signature(self.factory).replace(return_annotation=type(self.pragma_instance))
162167

163-
def __call__(self, *args, **kwargs) -> _pragma_class:
164-
return self._pragma_class(self.pragma, self.inherited, value=self.factory(*args, **kwargs))
168+
@property
169+
def pragma(self):
170+
return self.pragma_instance.pragma
165171

172+
@pragma.setter
173+
def pragma(self, value):
174+
self.pragma_instance.pragma = value
166175

167-
@_dataclass
168-
class _ParametrizedPragma(_ParametrizedClassPragma):
169-
""" A class or property parametrized pragma.
170-
Needs to be applied to a parameter to give a pragma.
171-
"""
172-
_pragma_class: _ClassVar[type] = _Pragma
176+
def __invert__(self) -> "_Parametrized[P, Q, T]":
177+
return _Parametrized(~self.pragma_instance, factory=self.factory)
173178

174-
def __invert__(self) -> _Pragma:
175-
return _Pragma(self.pragma, remove=True)
179+
def __call__(self, *args: Q.args, **kwargs: Q.kwargs) -> T:
180+
ret = _deepcopy(self.pragma_instance)
181+
ret.value = self.factory(*args, **kwargs)
182+
return ret
176183

177184

178185
class _Optionalizer(_schema.PropertyModifier):
@@ -232,30 +239,30 @@ def __getitem__(self, item):
232239

233240
use_for_null = _ClassPragma("null")
234241

235-
qltest.add(_Pragma("skip"))
242+
qltest.add(_ClassPragma("skip"))
236243
qltest.add(_ClassPragma("collapse_hierarchy"))
237244
qltest.add(_ClassPragma("uncollapse_hierarchy"))
238-
qltest.add(_ParametrizedClassPragma("test_with", inherited=True, factory=_schema.get_type_name))
245+
qltest.add(_Parametrized(_ClassPragma("test_with", inherited=True), factory=_schema.get_type_name))
239246

240-
ql.add(_ParametrizedClassPragma("default_doc_name", factory=lambda doc: doc))
247+
ql.add(_Parametrized(_ClassPragma("default_doc_name"), factory=lambda doc: doc))
241248
ql.add(_ClassPragma("hideable", inherited=True))
242249
ql.add(_Pragma("internal"))
243-
ql.add(_ParametrizedPragma("name", factory=lambda name: name))
250+
ql.add(_Parametrized(_Pragma("name"), factory=lambda name: name))
244251

245252
cpp.add(_Pragma("skip"))
246253

247-
rust.add(_Pragma("detach"))
254+
rust.add(_PropertyPragma("detach"))
248255
rust.add(_Pragma("skip_doc_test"))
249256

250-
rust.add(_ParametrizedClassPragma("doc_test_signature", factory=lambda signature: signature))
257+
rust.add(_Parametrized(_ClassPragma("doc_test_signature"), factory=lambda signature: signature))
251258

252-
group = _ParametrizedClassPragma("group", inherited=True, factory=lambda group: group)
259+
group = _Parametrized(_ClassPragma("group", inherited=True), factory=lambda group: group)
253260

254261

255-
synth.add(_ParametrizedClassPragma("from_class", factory=lambda ref: _schema.SynthInfo(
262+
synth.add(_Parametrized(_ClassPragma("from_class"), factory=lambda ref: _schema.SynthInfo(
256263
from_class=_schema.get_type_name(ref))), key="synth")
257-
synth.add(_ParametrizedClassPragma("on_arguments", factory=lambda **kwargs:
258-
_schema.SynthInfo(on_arguments={k: _schema.get_type_name(t) for k, t in kwargs.items()})), key="synth")
264+
synth.add(_Parametrized(_ClassPragma("on_arguments"), factory=lambda **kwargs:
265+
_schema.SynthInfo(on_arguments={k: _schema.get_type_name(t) for k, t in kwargs.items()})), key="synth")
259266

260267

261268
@_dataclass(frozen=True)

misc/codegen/test/test_dbschemegen.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -593,13 +593,13 @@ def test_synth_properties_ignored(generate):
593593
)
594594

595595

596-
def test_table_conflict(generate, dir_param):
596+
def test_table_conflict(generate):
597597
with pytest.raises(dbschemegen.Error):
598598
generate([
599-
schema.Class("Foo", pragmas={"group": dir_param.input}, properties=[
599+
schema.Class("Foo", properties=[
600600
schema.OptionalProperty("bar", "FooBar"),
601601
]),
602-
schema.Class("FooBar", pragmas={"group": dir_param.input}),
602+
schema.Class("FooBar"),
603603
])
604604

605605

misc/codegen/test/test_schemaloader.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,10 @@ class A:
272272
_class_pragmas = [
273273
(defs.qltest.collapse_hierarchy, "qltest_collapse_hierarchy"),
274274
(defs.qltest.uncollapse_hierarchy, "qltest_uncollapse_hierarchy"),
275+
(defs.qltest.skip, "qltest_skip"),
275276
]
276277

277278
_property_pragmas = [
278-
(defs.qltest.skip, "qltest_skip"),
279279
(defs.cpp.skip, "cpp_skip"),
280280
(defs.ql.internal, "ql_internal"),
281281
]
@@ -646,6 +646,17 @@ class A:
646646
}
647647

648648

649+
def test_db_table_name():
650+
@load
651+
class data:
652+
class A:
653+
x: optional[int] | defs.ql.db_table_name("foo")
654+
655+
assert data.classes == {
656+
'A': schema.Class('A', properties=[schema.OptionalProperty("x", "int", pragmas={"ql_db_table_name": "foo"})]),
657+
}
658+
659+
649660
def test_null_class():
650661
@load
651662
class data:
@@ -838,7 +849,7 @@ def test_annotate_fields_negations():
838849
@load
839850
class data:
840851
class Root:
841-
x: defs.int | defs.ql.internal | defs.qltest.skip
852+
x: defs.int | defs.ql.internal
842853
y: defs.optional["Root"] | defs.child | defs.desc("foo\nbar\n")
843854
z: defs.string | defs.synth | defs.doc("foo")
844855

@@ -850,7 +861,7 @@ class _:
850861

851862
assert data.classes == {
852863
"Root": schema.Class("Root", properties=[
853-
schema.SingleProperty("x", "int", pragmas=["qltest_skip"]),
864+
schema.SingleProperty("x", "int"),
854865
schema.OptionalProperty("y", "Root"),
855866
schema.SingleProperty("z", "string"),
856867
]),

0 commit comments

Comments
 (0)