Codegen: move default_doc_name to parametrized pragma

This commit is contained in:
Paolo Tranquilli 2024-09-20 09:50:22 +02:00
Родитель 9f1d50ebd1
Коммит 70997e8189
6 изменённых файлов: 86 добавлений и 57 удалений

Просмотреть файл

@ -96,7 +96,7 @@ def _get_doc(cls: schema.Class, prop: schema.Property, plural=None):
return format.format(**{noun: transform(noun) for noun in nouns})
prop_name = _humanize(prop.name)
class_name = cls.default_doc_name or _humanize(inflection.underscore(cls.name))
class_name = cls.pragmas.get("ql_default_doc_name", _humanize(inflection.underscore(cls.name)))
if prop.is_predicate:
return f"this {class_name} {prop_name}"
if plural is not None:

Просмотреть файл

@ -96,7 +96,6 @@ class Class:
synth: Optional[Union[SynthInfo, bool]] = None
"""^^^ filled with `True` for non-final classes with only synthesized final descendants """
doc: List[str] = field(default_factory=list)
default_doc_name: Optional[str] = None
hideable: bool = False
test_with: Optional[str] = None
rust_doc_test_function: Optional["FunctionInfo"] = "() -> ()" # TODO: parametrized pragmas

Просмотреть файл

@ -1,4 +1,4 @@
from typing import Callable as _Callable, Dict as _Dict
from typing import Callable as _Callable, Dict as _Dict, ClassVar as _ClassVar
from misc.codegen.lib import schema as _schema
import inspect as _inspect
from dataclasses import dataclass as _dataclass
@ -62,11 +62,14 @@ def include(source: str):
_inspect.currentframe().f_back.f_locals.setdefault("includes", []).append(source)
@_dataclass
class _Namespace:
""" simple namespacing mechanism """
name: str
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def add(self, pragma: "_PragmaBase"):
self.__dict__[pragma.pragma] = pragma
pragma.pragma = f"{self.name}_{pragma.pragma}"
@_dataclass
@ -77,35 +80,27 @@ class _SynthModifier(_schema.PropertyModifier, _Namespace):
prop.synth = self.synth
def negate(self) -> "PropertyModifier":
return _SynthModifier(False)
return _SynthModifier(self.name, False)
qltest = _Namespace()
ql = _Namespace()
cpp = _Namespace()
rust = _Namespace()
synth = _SynthModifier()
qltest = _Namespace("qltest")
ql = _Namespace("ql")
cpp = _Namespace("cpp")
rust = _Namespace("rust")
synth = _SynthModifier("synth")
@_dataclass
class _Pragma(_schema.PropertyModifier):
""" A class or property pragma.
For properties, it functions similarly to a `_PropertyModifier` with `|`, adding the pragma.
class _PragmaBase:
pragma: str
@_dataclass
class _ClassPragma(_PragmaBase):
""" A class pragma.
For schema classes it acts as a python decorator with `@`.
"""
pragma: str
value: object = None
remove: bool = False
def __post_init__(self):
namespace, _, name = self.pragma.partition('_')
setattr(globals()[namespace], name, self)
def modify(self, prop: _schema.Property):
self._apply(prop.pragmas)
def negate(self) -> "PropertyModifier":
return _Pragma(self.pragma, remove=True)
def __call__(self, cls: type) -> type:
""" use this pragma as a decorator on classes """
@ -116,22 +111,52 @@ class _Pragma(_schema.PropertyModifier):
return cls
def _apply(self, pragmas: _Dict[str, object]) -> None:
if self.remove:
pragmas.pop(self.pragma, None)
else:
pragmas[self.pragma] = self.value
pragmas[self.pragma] = self.value
@_dataclass
class _ParametrizedPragma:
class _Pragma(_ClassPragma, _schema.PropertyModifier):
""" A class or property pragma.
For properties, it functions similarly to a `_PropertyModifier` with `|`, adding the pragma.
For schema classes it acts as a python decorator with `@`.
"""
remove: bool = False
def modify(self, prop: _schema.Property):
self._apply(prop.pragmas)
def negate(self) -> "PropertyModifier":
return _Pragma(self.pragma, remove=True)
def _apply(self, pragmas: _Dict[str, object]) -> None:
if self.remove:
pragmas.pop(self.pragma, None)
else:
super()._apply(pragmas)
@_dataclass
class _ParametrizedClassPragma(_PragmaBase):
""" A class parametrized pragma.
Needs to be applied to a parameter to give a class pragma.
"""
_pragma_class: _ClassVar[type] = _ClassPragma
function: _Callable[[...], object] = None
def __post_init__(self):
self.__signature__ = _inspect.signature(self.function).replace(return_annotation=self._pragma_class)
def __call__(self, *args, **kwargs) -> _pragma_class:
return self._pragma_class(self.pragma, value=self.function(*args, **kwargs))
@_dataclass
class _ParametrizedPragma(_ParametrizedClassPragma):
""" A class or property parametrized pragma.
Needs to be applied to a parameter to give a pragma.
"""
pragma: str
function: _Callable[[...], object] = None
def __call__(self, *args, **kwargs) -> _Pragma:
return _Pragma(self.pragma, value=self.function(*args, **kwargs))
_pragma_class: _ClassVar[type] = _Pragma
def __invert__(self) -> _Pragma:
return _Pragma(self.pragma, remove=True)
@ -203,18 +228,18 @@ desc = _DescModifier
use_for_null = _annotate(null=True)
_Pragma("qltest_skip")
_Pragma("qltest_collapse_hierarchy")
_Pragma("qltest_uncollapse_hierarchy")
qltest.test_with = lambda cls: _annotate(test_with=cls)
qltest.add(_Pragma("skip"))
qltest.add(_ClassPragma("collapse_hierarchy"))
qltest.add(_ClassPragma("uncollapse_hierarchy"))
qltest.test_with = lambda cls: _annotate(test_with=cls) # inheritable
ql.default_doc_name = lambda doc: _annotate(doc_name=doc)
ql.hideable = _annotate(hideable=True)
_Pragma("ql_internal")
ql.add(_ParametrizedClassPragma("default_doc_name", lambda doc: doc))
ql.hideable = _annotate(hideable=True) # inheritable
ql.add(_Pragma("internal"))
_Pragma("cpp_skip")
cpp.add(_Pragma("skip"))
_Pragma("rust_skip_doc_test")
rust.add(_Pragma("skip_doc_test"))
rust.doc_test_signature = lambda signature: _annotate(rust_doc_test_function=signature)

Просмотреть файл

@ -56,7 +56,6 @@ def _get_class(cls: type) -> schema.Class:
for n, a in cls.__dict__.get("__annotations__", {}).items()
],
doc=schema.split_doc(cls.__doc__),
default_doc_name=cls.__dict__.get("_doc_name"),
rust_doc_test_function=cls.__dict__.get("_rust_doc_test_function",
schema.Class.rust_doc_test_function)
)

Просмотреть файл

@ -922,7 +922,7 @@ def test_property_on_class_with_default_doc_name(generate_classes):
assert generate_classes([
schema.Class("MyObject", properties=[
schema.SingleProperty("foo", "bar")],
default_doc_name="baz"),
pragmas={"ql_default_doc_name": "baz"}),
]) == {
"MyObject.qll": (a_ql_class_public(name="MyObject"),
a_ql_stub(name="MyObject"),

Просмотреть файл

@ -269,15 +269,21 @@ def test_builtin_predicate_and_set_children_not_allowed(spec):
x: spec | defs.child
_pragmas = [(defs.qltest.skip, "qltest_skip"),
(defs.qltest.collapse_hierarchy, "qltest_collapse_hierarchy"),
(defs.qltest.uncollapse_hierarchy, "qltest_uncollapse_hierarchy"),
(defs.cpp.skip, "cpp_skip"),
(defs.ql.internal, "ql_internal"),
]
_class_pragmas = [
(defs.qltest.collapse_hierarchy, "qltest_collapse_hierarchy"),
(defs.qltest.uncollapse_hierarchy, "qltest_uncollapse_hierarchy"),
]
_property_pragmas = [
(defs.qltest.skip, "qltest_skip"),
(defs.cpp.skip, "cpp_skip"),
(defs.ql.internal, "ql_internal"),
]
_pragmas = _class_pragmas + _property_pragmas
@pytest.mark.parametrize("pragma,expected", _pragmas)
@pytest.mark.parametrize("pragma,expected", _property_pragmas)
def test_property_with_pragma(pragma, expected):
@load
class data:
@ -293,7 +299,7 @@ def test_property_with_pragma(pragma, expected):
def test_property_with_pragmas():
spec = defs.string
for pragma, _ in _pragmas:
for pragma, _ in _property_pragmas:
spec |= pragma
@load
@ -303,7 +309,7 @@ def test_property_with_pragmas():
assert data.classes == {
'A': schema.Class('A', properties=[
schema.SingleProperty('x', 'string', pragmas=[expected for _, expected in _pragmas]),
schema.SingleProperty('x', 'string', pragmas=[expected for _, expected in _property_pragmas]),
]),
}
@ -636,7 +642,7 @@ def test_class_default_doc_name():
pass
assert data.classes == {
'A': schema.Class('A', default_doc_name="b"),
'A': schema.Class('A', pragmas={"ql_default_doc_name": "b"}),
}