Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ build --java_language_version=17
build --tool_java_language_version=17
build --tool_java_runtime_version=remotejdk_17
build --java_runtime_version=remotejdk_17
build --@rules_python//python/config_settings:python_version=3.12

try-import %workspace%/local.bazelrc
2 changes: 2 additions & 0 deletions .bazelrc.internal
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ common --registry=https://bcr.bazel.build
# its implementation packages without providing any code itself.
# We either can depend on internal implementation details, or turn of strict deps.
common --@rules_dotnet//dotnet/settings:strict_deps=false

build --@rules_python//python/config_settings:python_version=3.12
2 changes: 1 addition & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ use_repo(csharp_main_extension, "paket.main")
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
pip.parse(
hub_name = "codegen_deps",
python_version = "3.11",
python_version = "3.12",
requirements_lock = "//misc/codegen:requirements_lock.txt",
)
use_repo(pip, "codegen_deps")
Expand Down
2 changes: 1 addition & 1 deletion misc/codegen/.python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.11
3.12
19 changes: 13 additions & 6 deletions misc/codegen/generators/cppgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,27 @@ def _get_type(t: str, add_or_none_except: typing.Optional[str] = None) -> str:
return t


def _get_trap_name(cls: schema.Class, p: schema.Property) -> str | None:
if p.is_single:
return None
overridden_trap_name = p.pragmas.get("ql_db_table_name")
if overridden_trap_name:
return inflection.camelize(overridden_trap_name)
trap_name = inflection.camelize(f"{cls.name}_{p.name}")
if p.is_predicate:
return trap_name
return inflection.pluralize(trap_name)


def _get_field(cls: schema.Class, p: schema.Property, add_or_none_except: typing.Optional[str] = None) -> cpp.Field:
trap_name = None
if not p.is_single:
trap_name = inflection.camelize(f"{cls.name}_{p.name}")
if not p.is_predicate:
trap_name = inflection.pluralize(trap_name)
args = dict(
field_name=p.name + ("_" if p.name in cpp.cpp_keywords else ""),
base_type=_get_type(p.type, add_or_none_except),
is_optional=p.is_optional,
is_repeated=p.is_repeated,
is_predicate=p.is_predicate,
is_unordered=p.is_unordered,
trap_name=trap_name,
trap_name=_get_trap_name(cls, p),
)
args.update(cpp.get_field_override(p.name))
return cpp.Field(**args)
Expand Down
25 changes: 21 additions & 4 deletions misc/codegen/generators/dbschemegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
log = logging.getLogger(__name__)


class Error(Exception):
pass


def dbtype(typename: str, add_or_none_except: typing.Optional[str] = None) -> str:
""" translate a type to a dbscheme counterpart, using `@lower_underscore` format for classes.
For class types, appends an underscore followed by `null` if provided
Expand Down Expand Up @@ -65,11 +69,12 @@ def cls_to_dbscheme(cls: schema.Class, lookup: typing.Dict[str, schema.Class], a
)
# use property-specific tables for 1-to-many and 1-to-at-most-1 properties
for f in cls.properties:
overridden_table_name = f.pragmas.get("ql_db_table_name")
if f.synth:
continue
if f.is_unordered:
yield Table(
name=inflection.tableize(f"{cls.name}_{f.name}"),
name=overridden_table_name or inflection.tableize(f"{cls.name}_{f.name}"),
columns=[
Column("id", type=dbtype(cls.name)),
Column(inflection.singularize(f.name), dbtype(f.type, add_or_none_except)),
Expand All @@ -79,7 +84,7 @@ def cls_to_dbscheme(cls: schema.Class, lookup: typing.Dict[str, schema.Class], a
elif f.is_repeated:
yield Table(
keyset=KeySet(["id", "index"]),
name=inflection.tableize(f"{cls.name}_{f.name}"),
name=overridden_table_name or inflection.tableize(f"{cls.name}_{f.name}"),
columns=[
Column("id", type=dbtype(cls.name)),
Column("index", type="int"),
Expand All @@ -90,7 +95,7 @@ def cls_to_dbscheme(cls: schema.Class, lookup: typing.Dict[str, schema.Class], a
elif f.is_optional:
yield Table(
keyset=KeySet(["id"]),
name=inflection.tableize(f"{cls.name}_{f.name}"),
name=overridden_table_name or inflection.tableize(f"{cls.name}_{f.name}"),
columns=[
Column("id", type=dbtype(cls.name)),
Column(f.name, dbtype(f.type, add_or_none_except)),
Expand All @@ -100,14 +105,25 @@ def cls_to_dbscheme(cls: schema.Class, lookup: typing.Dict[str, schema.Class], a
elif f.is_predicate:
yield Table(
keyset=KeySet(["id"]),
name=inflection.underscore(f"{cls.name}_{f.name}"),
name=overridden_table_name or inflection.underscore(f"{cls.name}_{f.name}"),
columns=[
Column("id", type=dbtype(cls.name)),
],
dir=dir,
)


def check_name_conflicts(decls: list[Table | Union]):
names = set()
for decl in decls:
match decl:
case Table(name=name):
if name in names:
raise Error(f"Duplicate table name: {
name}, you can use `@ql.db_table_name` on a property to resolve this")
names.add(name)


def get_declarations(data: schema.Schema):
add_or_none_except = data.root_class.name if data.null else None
declarations = [d for cls in data.classes.values() if not cls.imported for d in cls_to_dbscheme(cls,
Expand All @@ -120,6 +136,7 @@ def get_declarations(data: schema.Schema):
declarations += [
Union(dbtype(t, data.null), [dbtype(t), dbtype(data.null)]) for t in sorted(property_classes)
]
check_name_conflicts(declarations)
return declarations


Expand Down
9 changes: 6 additions & 3 deletions misc/codegen/generators/qlgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ def get_ql_property(cls: schema.Class, prop: schema.Property, lookup: typing.Dic
internal="ql_internal" in prop.pragmas,
)
ql_name = prop.pragmas.get("ql_name", prop.name)
db_table_name = prop.pragmas.get("ql_db_table_name")
if db_table_name and prop.is_single:
raise Error(f"`db_table_name` pragma is not supported for single properties, but {cls.name}.{prop.name} has it")
if prop.is_single:
args.update(
singular=inflection.camelize(ql_name),
Expand All @@ -141,22 +144,22 @@ def get_ql_property(cls: schema.Class, prop: schema.Property, lookup: typing.Dic
args.update(
singular=inflection.singularize(inflection.camelize(ql_name)),
plural=inflection.pluralize(inflection.camelize(ql_name)),
tablename=inflection.tableize(f"{cls.name}_{prop.name}"),
tablename=db_table_name or inflection.tableize(f"{cls.name}_{prop.name}"),
tableparams=["this", "index", "result"] if not prop.is_unordered else ["this", "result"],
doc=_get_doc(cls, prop, plural=False),
doc_plural=_get_doc(cls, prop, plural=True),
)
elif prop.is_optional:
args.update(
singular=inflection.camelize(ql_name),
tablename=inflection.tableize(f"{cls.name}_{prop.name}"),
tablename=db_table_name or inflection.tableize(f"{cls.name}_{prop.name}"),
tableparams=["this", "result"],
doc=_get_doc(cls, prop),
)
elif prop.is_predicate:
args.update(
singular=inflection.camelize(ql_name, uppercase_first_letter=False),
tablename=inflection.underscore(f"{cls.name}_{prop.name}"),
tablename=db_table_name or inflection.underscore(f"{cls.name}_{prop.name}"),
tableparams=["this"],
doc=_get_doc(cls, prop),
)
Expand Down
22 changes: 14 additions & 8 deletions misc/codegen/generators/rustgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,28 @@ def _get_type(t: str) -> str:
return t


def _get_table_name(cls: schema.Class, p: schema.Property) -> str:
if p.is_single:
return inflection.tableize(cls.name)
overridden_table_name = p.pragmas.get("ql_db_table_name")
if overridden_table_name:
return overridden_table_name
table_name = f"{cls.name}_{p.name}"
if p.is_predicate:
return inflection.underscore(table_name)
else:
return inflection.tableize(table_name)


def _get_field(cls: schema.Class, p: schema.Property) -> rust.Field:
table_name = inflection.tableize(cls.name)
if not p.is_single:
table_name = f"{cls.name}_{p.name}"
if p.is_predicate:
table_name = inflection.underscore(table_name)
else:
table_name = inflection.tableize(table_name)
args = dict(
field_name=rust.avoid_keywords(p.name),
base_type=_get_type(p.type),
is_optional=p.is_optional,
is_repeated=p.is_repeated,
is_predicate=p.is_predicate,
is_unordered=p.is_unordered,
table_name=table_name,
table_name=_get_table_name(cls, p),
)
args.update(rust.get_field_override(p.name))
return rust.Field(**args)
Expand Down
86 changes: 47 additions & 39 deletions misc/codegen/lib/schemadefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
Callable as _Callable,
Dict as _Dict,
Iterable as _Iterable,
ClassVar as _ClassVar,
Union as _Union,
)
from copy import deepcopy as _deepcopy
from misc.codegen.lib import schema as _schema
import inspect as _inspect
from dataclasses import dataclass as _dataclass
Expand Down Expand Up @@ -75,7 +76,7 @@ class _Namespace:
""" simple namespacing mechanism """
_name: str

def add(self, pragma: "_PragmaBase", key: str | None = None):
def add(self, pragma: _Union["_PragmaBase", "_Parametrized"], key: str | None = None):
self.__dict__[pragma.pragma] = pragma
pragma.pragma = key or f"{self._name}_{pragma.pragma}"

Expand All @@ -101,6 +102,10 @@ def negate(self) -> _schema.PropertyModifier:
@_dataclass
class _PragmaBase:
pragma: str
value: object = None

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


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

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

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


@_dataclass
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 `@`.
class _PropertyPragma(_PragmaBase, _schema.PropertyModifier):
""" A property pragma.
It functions similarly to a `_PropertyModifier` with `|`, adding the pragma.
"""
remove: bool = False

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

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

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


@_dataclass
class _ParametrizedClassPragma(_PragmaBase):
""" A class parametrized pragma.
Needs to be applied to a parameter to give a class pragma.
class _Pragma(_ClassPragma, _PropertyPragma):
""" 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 `@`.
"""
_pragma_class: _ClassVar[type] = _ClassPragma

inherited: bool = False
factory: _Callable[..., object] = None

def __post_init__(self):
self.__signature__ = _inspect.signature(self.factory).replace(return_annotation=self._pragma_class)
class _Parametrized[P, **Q, T]:
""" A parametrized pragma.
Needs to be applied to a parameter to give a pragma.
"""

def __init__(self, pragma_instance: P, factory: _Callable[Q, T]):
self.pragma_instance = pragma_instance
self.factory = factory
self.__signature__ = _inspect.signature(self.factory).replace(return_annotation=type(self.pragma_instance))

def __call__(self, *args, **kwargs) -> _pragma_class:
return self._pragma_class(self.pragma, self.inherited, value=self.factory(*args, **kwargs))
@property
def pragma(self):
return self.pragma_instance.pragma

@pragma.setter
def pragma(self, value):
self.pragma_instance.pragma = value

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

def __invert__(self) -> _Pragma:
return _Pragma(self.pragma, remove=True)
def __call__(self, *args: Q.args, **kwargs: Q.kwargs) -> T:
ret = _deepcopy(self.pragma_instance)
ret.value = self.factory(*args, **kwargs)
return ret


class _Optionalizer(_schema.PropertyModifier):
Expand Down Expand Up @@ -232,30 +239,31 @@ def __getitem__(self, item):

use_for_null = _ClassPragma("null")

qltest.add(_Pragma("skip"))
qltest.add(_ClassPragma("skip"))
qltest.add(_ClassPragma("collapse_hierarchy"))
qltest.add(_ClassPragma("uncollapse_hierarchy"))
qltest.add(_ParametrizedClassPragma("test_with", inherited=True, factory=_schema.get_type_name))
qltest.add(_Parametrized(_ClassPragma("test_with", inherited=True), factory=_schema.get_type_name))

ql.add(_ParametrizedClassPragma("default_doc_name", factory=lambda doc: doc))
ql.add(_Parametrized(_ClassPragma("default_doc_name"), factory=lambda doc: doc))
ql.add(_ClassPragma("hideable", inherited=True))
ql.add(_Pragma("internal"))
ql.add(_ParametrizedPragma("name", factory=lambda name: name))
ql.add(_Parametrized(_Pragma("name"), factory=lambda name: name))
ql.add(_Parametrized(_PropertyPragma("db_table_name"), factory=lambda name: name))

cpp.add(_Pragma("skip"))

rust.add(_Pragma("detach"))
rust.add(_PropertyPragma("detach"))
rust.add(_Pragma("skip_doc_test"))

rust.add(_ParametrizedClassPragma("doc_test_signature", factory=lambda signature: signature))
rust.add(_Parametrized(_ClassPragma("doc_test_signature"), factory=lambda signature: signature))

group = _ParametrizedClassPragma("group", inherited=True, factory=lambda group: group)
group = _Parametrized(_ClassPragma("group", inherited=True), factory=lambda group: group)


synth.add(_ParametrizedClassPragma("from_class", factory=lambda ref: _schema.SynthInfo(
synth.add(_Parametrized(_ClassPragma("from_class"), factory=lambda ref: _schema.SynthInfo(
from_class=_schema.get_type_name(ref))), key="synth")
synth.add(_ParametrizedClassPragma("on_arguments", factory=lambda **kwargs:
_schema.SynthInfo(on_arguments={k: _schema.get_type_name(t) for k, t in kwargs.items()})), key="synth")
synth.add(_Parametrized(_ClassPragma("on_arguments"), factory=lambda **kwargs:
_schema.SynthInfo(on_arguments={k: _schema.get_type_name(t) for k, t in kwargs.items()})), key="synth")


@_dataclass(frozen=True)
Expand Down
Loading
Loading