mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Codegen: Make public AST classes final
This commit is contained in:
@@ -2,18 +2,19 @@
|
||||
QL code generation
|
||||
|
||||
`generate(opts, renderer)` will generate in the library directory:
|
||||
* generated/Raw.qll with thin class wrappers around DB types
|
||||
* generated/Synth.qll with the base algebraic datatypes for AST entities
|
||||
* generated/<group>/<Class>.qll with generated properties for each class
|
||||
* if not already modified, a elements/<group>/<Class>.qll stub to customize the above classes
|
||||
* elements.qll importing all the above stubs
|
||||
* if not already modified, a elements/<group>/<Class>Constructor.qll stub to customize the algebraic datatype
|
||||
* `generated/Raw.qll` with thin class wrappers around DB types
|
||||
* `generated/Synth.qll` with the base algebraic datatypes for AST entities
|
||||
* `generated/<group>/<Class>.qll` with generated properties for each class
|
||||
* if not already modified, an `elements/<group>/<Class>Impl.qll` stub to customize the above classes
|
||||
* `elements/<group>/<Class>.qll` that wraps the internal `<Class>Impl.qll` file in a public `final` class.
|
||||
* `elements.qll` importing all the above public classes
|
||||
* if not already modified, an `elements/<group>/<Class>Constructor.qll` stub to customize the algebraic datatype
|
||||
characteristic predicate
|
||||
* generated/SynthConstructors.qll importing all the above constructor stubs
|
||||
* generated/PureSynthConstructors.qll importing constructor stubs for pure synthesized types (that is, not
|
||||
* `generated/SynthConstructors.qll` importing all the above constructor stubs
|
||||
* `generated/PureSynthConstructors.qll` importing constructor stubs for pure synthesized types (that is, not
|
||||
corresponding to raw types)
|
||||
Moreover in the test directory for each <Class> in <group> it will generate beneath the
|
||||
extractor-tests/generated/<group>/<Class> directory either
|
||||
`extractor-tests/generated/<group>/<Class>` directory either
|
||||
* a `MISSING_SOURCE.txt` explanation file if no source is present, or
|
||||
* one `<Class>.ql` test query for all single properties and on `<Class>_<property>.ql` test query for each optional or
|
||||
repeated property
|
||||
@@ -164,6 +165,7 @@ def get_ql_class(cls: schema.Class, lookup: typing.Dict[str, schema.Class]) -> q
|
||||
return ql.Class(
|
||||
name=cls.name,
|
||||
bases=cls.bases,
|
||||
bases_impl=[base + "Impl::" + base for base in cls.bases],
|
||||
final=not cls.derived,
|
||||
properties=properties,
|
||||
dir=pathlib.Path(cls.group or ""),
|
||||
@@ -210,15 +212,17 @@ def get_import(file: pathlib.Path, root_dir: pathlib.Path):
|
||||
return str(stem).replace("/", ".")
|
||||
|
||||
|
||||
def get_types_used_by(cls: ql.Class) -> typing.Iterable[str]:
|
||||
def get_types_used_by(cls: ql.Class, is_impl: bool) -> typing.Iterable[str]:
|
||||
for b in cls.bases:
|
||||
yield b.base
|
||||
yield b.base + "Impl" if is_impl else b.base
|
||||
for p in cls.properties:
|
||||
yield p.type
|
||||
if cls.root:
|
||||
yield cls.name # used in `getResolveStep` and `resolve`
|
||||
|
||||
|
||||
def get_classes_used_by(cls: ql.Class) -> typing.List[str]:
|
||||
return sorted(set(t for t in get_types_used_by(cls) if t[0].isupper() and t != cls.name))
|
||||
def get_classes_used_by(cls: ql.Class, is_impl: bool) -> typing.List[str]:
|
||||
return sorted(set(t for t in get_types_used_by(cls, is_impl) if t[0].isupper() and (is_impl or t != cls.name)))
|
||||
|
||||
|
||||
def format(codeql, files):
|
||||
@@ -239,6 +243,10 @@ def _get_path(cls: schema.Class) -> pathlib.Path:
|
||||
return pathlib.Path(cls.group or "", cls.name).with_suffix(".qll")
|
||||
|
||||
|
||||
def _get_path_impl(cls: schema.Class) -> pathlib.Path:
|
||||
return pathlib.Path(cls.group or "", cls.name+"Impl").with_suffix(".qll")
|
||||
|
||||
|
||||
def _get_all_properties(cls: schema.Class, lookup: typing.Dict[str, schema.Class],
|
||||
already_seen: typing.Optional[typing.Set[int]] = None) -> \
|
||||
typing.Iterable[typing.Tuple[schema.Class, schema.Property]]:
|
||||
@@ -315,11 +323,13 @@ def _get_stub(cls: schema.Class, base_import: str, generated_import_prefix: str)
|
||||
else:
|
||||
accessors = []
|
||||
return ql.Stub(name=cls.name, base_import=base_import, import_prefix=generated_import_prefix,
|
||||
doc=cls.doc, synth_accessors=accessors,
|
||||
internal="ql_internal" in cls.pragmas)
|
||||
doc=cls.doc, synth_accessors=accessors)
|
||||
|
||||
def _get_class_public(cls: schema.Class) -> ql.ClassPublic:
|
||||
return ql.ClassPublic(name=cls.name, doc=cls.doc, internal="ql_internal" in cls.pragmas)
|
||||
|
||||
|
||||
_stub_qldoc_header = "// the following QLdoc is generated: if you need to edit it, do it in the schema file\n"
|
||||
_stub_qldoc_header = "// the following QLdoc is generated: if you need to edit it, do it in the schema file\n "
|
||||
|
||||
_class_qldoc_re = re.compile(
|
||||
rf"(?P<qldoc>(?:{re.escape(_stub_qldoc_header)})?/\*\*.*?\*/\s*|^\s*)(?:class\s+(?P<class>\w+))?",
|
||||
@@ -330,13 +340,13 @@ def _patch_class_qldoc(cls: str, qldoc: str, stub_file: pathlib.Path):
|
||||
""" Replace or insert `qldoc` as the QLdoc of class `cls` in `stub_file` """
|
||||
if not qldoc or not stub_file.exists():
|
||||
return
|
||||
qldoc = "\n".join(l.rstrip() for l in qldoc.splitlines())
|
||||
qldoc = "\n ".join(l.rstrip() for l in qldoc.splitlines())
|
||||
with open(stub_file) as input:
|
||||
contents = input.read()
|
||||
for match in _class_qldoc_re.finditer(contents):
|
||||
if match["class"] == cls:
|
||||
qldoc_start, qldoc_end = match.span("qldoc")
|
||||
contents = f"{contents[:qldoc_start]}{_stub_qldoc_header}{qldoc}\n{contents[qldoc_end:]}"
|
||||
contents = f"{contents[:qldoc_start]}{_stub_qldoc_header}{qldoc}\n {contents[qldoc_end:]}"
|
||||
tmp = stub_file.with_suffix(f"{stub_file.suffix}.bkp")
|
||||
with open(tmp, "w") as out:
|
||||
out.write(contents)
|
||||
@@ -370,6 +380,8 @@ def generate(opts, renderer):
|
||||
raise RootElementHasChildren(root)
|
||||
|
||||
imports = {}
|
||||
imports_impl = {}
|
||||
classes_used_by = {}
|
||||
generated_import_prefix = get_import(out, opts.root_dir)
|
||||
registry = opts.generated_registry or pathlib.Path(
|
||||
os.path.commonpath((out, stub_out, test_out)), ".generated.list")
|
||||
@@ -382,24 +394,33 @@ def generate(opts, renderer):
|
||||
|
||||
classes_by_dir_and_name = sorted(classes.values(), key=lambda cls: (cls.dir, cls.name))
|
||||
for c in classes_by_dir_and_name:
|
||||
imports[c.name] = get_import(stub_out / c.path, opts.root_dir)
|
||||
path = get_import(stub_out / c.path, opts.root_dir)
|
||||
imports[c.name] = path
|
||||
imports_impl[c.name + "Impl"] = path + "Impl"
|
||||
|
||||
for c in classes.values():
|
||||
qll = out / c.path.with_suffix(".qll")
|
||||
c.imports = [imports[t] for t in get_classes_used_by(c)]
|
||||
c.imports = [imports[t] if t in imports else imports_impl[t]+ "::Impl as " + t for t in get_classes_used_by(c, is_impl=True)]
|
||||
classes_used_by[c.name] = get_classes_used_by(c, is_impl=False)
|
||||
c.import_prefix = generated_import_prefix
|
||||
renderer.render(c, qll)
|
||||
|
||||
for c in data.classes.values():
|
||||
path = _get_path(c)
|
||||
stub_file = stub_out / path
|
||||
path_impl = _get_path_impl(c)
|
||||
stub_file = stub_out / path_impl
|
||||
base_import = get_import(out / path, opts.root_dir)
|
||||
stub = _get_stub(c, base_import, generated_import_prefix)
|
||||
|
||||
if not renderer.is_customized_stub(stub_file):
|
||||
renderer.render(stub, stub_file)
|
||||
else:
|
||||
qldoc = renderer.render_str(stub, template='ql_stub_class_qldoc')
|
||||
_patch_class_qldoc(c.name, qldoc, stub_file)
|
||||
class_public = _get_class_public(c)
|
||||
class_public_file = stub_out / path
|
||||
class_public.imports = [imports[t] for t in classes_used_by[c.name]]
|
||||
renderer.render(class_public, class_public_file)
|
||||
|
||||
# for example path/to/elements -> path/to/elements.qll
|
||||
renderer.render(ql.ImportList([i for name, i in imports.items() if not classes[name].internal]),
|
||||
|
||||
@@ -101,6 +101,7 @@ class Class:
|
||||
|
||||
name: str
|
||||
bases: List[Base] = field(default_factory=list)
|
||||
bases_impl: List[Base] = field(default_factory=list)
|
||||
final: bool = False
|
||||
properties: List[Property] = field(default_factory=list)
|
||||
dir: pathlib.Path = pathlib.Path()
|
||||
@@ -115,6 +116,8 @@ class Class:
|
||||
|
||||
def __post_init__(self):
|
||||
self.bases = [Base(str(b), str(prev)) for b, prev in zip(self.bases, itertools.chain([""], self.bases))]
|
||||
self.bases_impl = [Base(str(b), str(prev)) for b, prev in zip(
|
||||
self.bases_impl, itertools.chain([""], self.bases_impl))]
|
||||
if self.properties:
|
||||
self.properties[0].first = True
|
||||
|
||||
@@ -159,13 +162,26 @@ class Stub:
|
||||
base_import: str
|
||||
import_prefix: str
|
||||
synth_accessors: List[SynthUnderlyingAccessor] = field(default_factory=list)
|
||||
internal: bool = False
|
||||
doc: List[str] = field(default_factory=list)
|
||||
|
||||
@property
|
||||
def has_synth_accessors(self) -> bool:
|
||||
return bool(self.synth_accessors)
|
||||
|
||||
@property
|
||||
def has_qldoc(self) -> bool:
|
||||
return bool(self.doc)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ClassPublic:
|
||||
template: ClassVar = 'ql_class_public'
|
||||
|
||||
name: str
|
||||
imports: List[str] = field(default_factory=list)
|
||||
internal: bool = False
|
||||
doc: List[str] = field(default_factory=list)
|
||||
|
||||
@property
|
||||
def has_qldoc(self) -> bool:
|
||||
return bool(self.doc) or self.internal
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// generated by {{generator}}
|
||||
// generated by {{generator}}, do not edit
|
||||
{{#includes}}
|
||||
|
||||
// from {{src}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// generated by {{generator}}
|
||||
// generated by {{generator}}, do not edit
|
||||
/**
|
||||
* This module provides the generated definition of `{{name}}`.
|
||||
* INTERNAL: Do not import directly.
|
||||
@@ -22,7 +22,7 @@ module Generated {
|
||||
* INTERNAL: Do not reference the `Generated::{{name}}` class directly.
|
||||
* Use the subclass `{{name}}`, where the following predicates are available.
|
||||
*/
|
||||
class {{name}} extends Synth::T{{name}}{{#bases}}, {{.}}{{/bases}} {
|
||||
class {{name}} extends Synth::T{{name}}{{#bases_impl}}, {{.}}{{/bases_impl}} {
|
||||
{{#root}}
|
||||
/**
|
||||
* Gets the string representation of this element.
|
||||
|
||||
12
misc/codegen/templates/ql_class_public.mustache
Normal file
12
misc/codegen/templates/ql_class_public.mustache
Normal file
@@ -0,0 +1,12 @@
|
||||
// generated by {{generator}}, do not edit
|
||||
/**
|
||||
* This module provides the public class `{{name}}`.
|
||||
*/
|
||||
|
||||
private import {{name}}Impl
|
||||
{{#imports}}
|
||||
import {{.}}
|
||||
{{/imports}}
|
||||
|
||||
{{>ql_stub_class_qldoc}}
|
||||
final class {{name}} = Impl::{{name}};
|
||||
@@ -1,4 +1,4 @@
|
||||
// generated by {{generator}}
|
||||
// generated by {{generator}}, do not edit
|
||||
/**
|
||||
* This module exports all modules providing `Element` subclasses.
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// generated by {{generator}}
|
||||
// generated by {{generator}}, do not edit
|
||||
/**
|
||||
* This module provides the generated parent/child relationship.
|
||||
*/
|
||||
|
||||
@@ -6,10 +6,16 @@ private import {{import_prefix}}.Raw
|
||||
private import {{import_prefix}}.Synth
|
||||
{{/has_synth_accessors}}
|
||||
|
||||
{{>ql_stub_class_qldoc}}
|
||||
class {{name}} extends Generated::{{name}} {
|
||||
/**
|
||||
* INTERNAL: This module contains the customizable definition of `{{name}}` and should not
|
||||
* be referenced directly.
|
||||
*/
|
||||
module Impl {
|
||||
{{>ql_stub_class_qldoc}}
|
||||
class {{name}} extends Generated::{{name}} {
|
||||
{{#synth_accessors}}
|
||||
private
|
||||
cached {{type}} getUnderlying{{argument}}() { this = Synth::T{{name}}({{#constructorparams}}{{^first}},{{/first}}{{param}}{{/constructorparams}})}
|
||||
{{/synth_accessors}}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
/**
|
||||
* This module provides a hand-modifiable wrapper around the generated class `{{name}}`.
|
||||
{{#internal}}
|
||||
*
|
||||
* INTERNAL: Do not use.
|
||||
{{/internal}}
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// generated by {{generator}}
|
||||
// generated by {{generator}}, do not edit
|
||||
|
||||
import {{elements_module}}
|
||||
import TestUtils
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// generated by {{generator}}
|
||||
// generated by {{generator}}, do not edit
|
||||
|
||||
After a source file is added in this directory and {{generator}} is run again, test queries
|
||||
will appear and this file will be deleted
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// generated by {{generator}}
|
||||
// generated by {{generator}}, do not edit
|
||||
|
||||
import {{elements_module}}
|
||||
import TestUtils
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// generated by {{generator}}
|
||||
// generated by {{generator}}, do not edit
|
||||
|
||||
#![cfg_attr(any(), rustfmt::skip)]
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// generated by {{generator}}
|
||||
// generated by {{generator}}, do not edit
|
||||
{{#modules}}
|
||||
|
||||
mod {{.}};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// generated by {{generator}}
|
||||
// generated by {{generator}}, do not edit
|
||||
|
||||
{{#function}}
|
||||
fn {{name}}{{signature}} {
|
||||
|
||||
@@ -147,15 +147,14 @@ def test_class_with_children():
|
||||
assert cls.has_children is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("doc,internal,expected",
|
||||
@pytest.mark.parametrize("doc,expected",
|
||||
[
|
||||
(["foo", "bar"], False, True),
|
||||
(["foo", "bar"], True, True),
|
||||
([], False, False),
|
||||
([], True, True),
|
||||
(["foo", "bar"], True),
|
||||
(["foo", "bar"], True),
|
||||
([], False)
|
||||
])
|
||||
def test_has_doc(doc, internal, expected):
|
||||
stub = ql.Stub("Class", base_import="foo", import_prefix="bar", doc=doc, internal=internal)
|
||||
def test_has_doc(doc, expected):
|
||||
stub = ql.Stub("Class", base_import="foo", import_prefix="bar", doc=doc)
|
||||
assert stub.has_qldoc is expected
|
||||
|
||||
|
||||
|
||||
@@ -114,7 +114,9 @@ def _filter_generated_classes(ret, output_test_files=False):
|
||||
("Raw", "Synth", "SynthConstructors", "PureSynthConstructors")}
|
||||
assert base_files <= stub_files
|
||||
return {
|
||||
str(f): (ret[stub_path() / f], ret[ql_output_path() / f])
|
||||
str(f): (ret[stub_path() / f],
|
||||
ret[stub_path() / pathlib.Path(f.parent, f.stem + "Impl.qll")],
|
||||
ret[ql_output_path() / f])
|
||||
for f in base_files
|
||||
}
|
||||
|
||||
@@ -144,12 +146,17 @@ def a_ql_stub(*, name, import_prefix="", **kwargs):
|
||||
base_import=f"{gen_import_prefix}{import_prefix}{name}")
|
||||
|
||||
|
||||
def a_ql_class_public(*, name, **kwargs):
|
||||
return ql.ClassPublic(name=name, **kwargs)
|
||||
|
||||
|
||||
def test_one_empty_class(generate_classes):
|
||||
assert generate_classes([
|
||||
schema.Class("A")
|
||||
]) == {
|
||||
"A.qll": (a_ql_stub(name="A"),
|
||||
a_ql_class(name="A", final=True)),
|
||||
"A.qll": (a_ql_class_public(name="A"),
|
||||
a_ql_stub(name="A"),
|
||||
a_ql_class(name="A", final=True, imports=[stub_import_prefix + "A"]))
|
||||
}
|
||||
|
||||
|
||||
@@ -157,8 +164,9 @@ def test_one_empty_internal_class(generate_classes):
|
||||
assert generate_classes([
|
||||
schema.Class("A", pragmas=["ql_internal"])
|
||||
]) == {
|
||||
"A.qll": (a_ql_stub(name="A", internal=True),
|
||||
a_ql_class(name="A", final=True, internal=True)),
|
||||
"A.qll": (a_ql_class_public(name="A", internal=True),
|
||||
a_ql_stub(name="A"),
|
||||
a_ql_class(name="A", final=True, internal=True, imports=[stub_import_prefix + "A"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -169,11 +177,11 @@ def test_hierarchy(generate_classes):
|
||||
schema.Class("B", bases=["A"], derived={"D"}),
|
||||
schema.Class("A", derived={"B", "C"}),
|
||||
]) == {
|
||||
"A.qll": (a_ql_stub(name="A"), a_ql_class(name="A")),
|
||||
"B.qll": (a_ql_stub(name="B"), a_ql_class(name="B", bases=["A"], imports=[stub_import_prefix + "A"])),
|
||||
"C.qll": (a_ql_stub(name="C"), a_ql_class(name="C", bases=["A"], imports=[stub_import_prefix + "A"])),
|
||||
"D.qll": (a_ql_stub(name="D"), a_ql_class(name="D", final=True, bases=["B", "C"],
|
||||
imports=[stub_import_prefix + cls for cls in "BC"])),
|
||||
"A.qll": (a_ql_class_public(name="A"), a_ql_stub(name="A"), a_ql_class(name="A", imports=[stub_import_prefix + "A"])),
|
||||
"B.qll": (a_ql_class_public(name="B", imports=[stub_import_prefix + "A"]), a_ql_stub(name="B"), a_ql_class(name="B", bases=["A"], bases_impl=["AImpl::A"], imports=[stub_import_prefix + "AImpl::Impl as AImpl"])),
|
||||
"C.qll": (a_ql_class_public(name="C", imports=[stub_import_prefix + "A"]), a_ql_stub(name="C"), a_ql_class(name="C", bases=["A"], bases_impl=["AImpl::A"], imports=[stub_import_prefix + "AImpl::Impl as AImpl"])),
|
||||
"D.qll": (a_ql_class_public(name="D", imports=[stub_import_prefix + "B", stub_import_prefix + "C"]), a_ql_stub(name="D"), a_ql_class(name="D", final=True, bases=["B", "C"], bases_impl=["BImpl::B", "CImpl::C"],
|
||||
imports=[stub_import_prefix + cls + "Impl::Impl as " + cls + "Impl" for cls in "BC"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -202,13 +210,13 @@ def test_hierarchy_children(generate_children_implementations):
|
||||
schema.Class("C", bases=["A"], derived={"D"}, pragmas=["ql_internal"]),
|
||||
schema.Class("D", bases=["B", "C"]),
|
||||
]) == ql.GetParentImplementation(
|
||||
classes=[a_ql_class(name="A", internal=True),
|
||||
a_ql_class(name="B", bases=["A"], imports=[
|
||||
stub_import_prefix + "A"]),
|
||||
a_ql_class(name="C", bases=["A"], imports=[
|
||||
stub_import_prefix + "A"], internal=True),
|
||||
a_ql_class(name="D", final=True, bases=["B", "C"],
|
||||
imports=[stub_import_prefix + cls for cls in "BC"]),
|
||||
classes=[a_ql_class(name="A", internal=True, imports=[stub_import_prefix + "A"]),
|
||||
a_ql_class(name="B", bases=["A"], bases_impl=["AImpl::A"], imports=[
|
||||
stub_import_prefix + "AImpl::Impl as AImpl"]),
|
||||
a_ql_class(name="C", bases=["A"], bases_impl=["AImpl::A"], imports=[
|
||||
stub_import_prefix + "AImpl::Impl as AImpl"], internal=True),
|
||||
a_ql_class(name="D", final=True, bases=["B", "C"], bases_impl=["BImpl::B", "CImpl::C"],
|
||||
imports=[stub_import_prefix + cls + "Impl::Impl as " + cls + "Impl" for cls in "BC"]),
|
||||
],
|
||||
imports=[stub_import] + [stub_import_prefix + cls for cls in "AC"],
|
||||
)
|
||||
@@ -219,12 +227,14 @@ def test_single_property(generate_classes):
|
||||
schema.Class("MyObject", properties=[
|
||||
schema.SingleProperty("foo", "bar")]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_objects",
|
||||
tableparams=["this", "result"], doc="foo of this my object"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -233,13 +243,15 @@ def test_internal_property(generate_classes):
|
||||
schema.Class("MyObject", properties=[
|
||||
schema.SingleProperty("foo", "bar", pragmas=["ql_internal"])]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_objects",
|
||||
tableparams=["this", "result"], doc="foo of this my object",
|
||||
internal=True),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -257,8 +269,9 @@ def test_children(generate_classes):
|
||||
schema.RepeatedOptionalProperty("child_4", "int", is_child=True),
|
||||
]),
|
||||
]) == {
|
||||
"FakeRoot.qll": (a_ql_stub(name="FakeRoot"), a_ql_class(name="FakeRoot", final=True)),
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"FakeRoot.qll": (a_ql_class_public(name="FakeRoot"), a_ql_stub(name="FakeRoot"), a_ql_class(name="FakeRoot", final=True, imports=[stub_import_prefix + "FakeRoot"])),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="A", type="int", tablename="my_objects",
|
||||
@@ -294,7 +307,8 @@ def test_children(generate_classes):
|
||||
tableparams=["this", "index", "result"], is_optional=True,
|
||||
prev_child="Child3", doc="child 4 of this my object",
|
||||
doc_plural="child 4s of this my object"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -306,7 +320,8 @@ def test_single_properties(generate_classes):
|
||||
schema.SingleProperty("three", "z"),
|
||||
]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="One", type="x", tablename="my_objects",
|
||||
@@ -318,7 +333,8 @@ def test_single_properties(generate_classes):
|
||||
ql.Property(singular="Three", type="z", tablename="my_objects",
|
||||
tableparams=["this", "_", "_", "result"],
|
||||
doc="three of this my object"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -329,13 +345,15 @@ def test_optional_property(generate_classes, is_child, prev_child):
|
||||
schema.Class("MyObject", properties=[
|
||||
schema.OptionalProperty("foo", "bar", is_child=is_child)]),
|
||||
]) == {
|
||||
"FakeRoot.qll": (a_ql_stub(name="FakeRoot"), a_ql_class(name="FakeRoot", final=True)),
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"FakeRoot.qll": (a_ql_class_public(name="FakeRoot"), a_ql_stub(name="FakeRoot"), a_ql_class(name="FakeRoot", final=True, imports=[stub_import_prefix + "FakeRoot"])),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True, properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_object_foos",
|
||||
tableparams=["this", "result"],
|
||||
is_optional=True, prev_child=prev_child, doc="foo of this my object"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -346,13 +364,15 @@ def test_repeated_property(generate_classes, is_child, prev_child):
|
||||
schema.Class("MyObject", properties=[
|
||||
schema.RepeatedProperty("foo", "bar", is_child=is_child)]),
|
||||
]) == {
|
||||
"FakeRoot.qll": (a_ql_stub(name="FakeRoot"), a_ql_class(name="FakeRoot", final=True)),
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"FakeRoot.qll": (a_ql_class_public(name="FakeRoot"), a_ql_stub(name="FakeRoot"), a_ql_class(name="FakeRoot", final=True, imports=[stub_import_prefix + "FakeRoot"])),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True, properties=[
|
||||
ql.Property(singular="Foo", plural="Foos", type="bar", tablename="my_object_foos",
|
||||
tableparams=["this", "index", "result"], prev_child=prev_child,
|
||||
doc="foo of this my object", doc_plural="foos of this my object"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -362,13 +382,15 @@ def test_repeated_unordered_property(generate_classes):
|
||||
schema.Class("MyObject", properties=[
|
||||
schema.RepeatedUnorderedProperty("foo", "bar")]),
|
||||
]) == {
|
||||
"FakeRoot.qll": (a_ql_stub(name="FakeRoot"), a_ql_class(name="FakeRoot", final=True)),
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"FakeRoot.qll": (a_ql_class_public(name="FakeRoot"), a_ql_stub(name="FakeRoot"), a_ql_class(name="FakeRoot", final=True, imports=[stub_import_prefix + "FakeRoot"])),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True, properties=[
|
||||
ql.Property(singular="Foo", plural="Foos", type="bar", tablename="my_object_foos",
|
||||
tableparams=["this", "result"], is_unordered=True,
|
||||
doc="foo of this my object", doc_plural="foos of this my object"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -380,14 +402,16 @@ def test_repeated_optional_property(generate_classes, is_child, prev_child):
|
||||
schema.RepeatedOptionalProperty("foo", "bar", is_child=is_child)]),
|
||||
]) == {
|
||||
|
||||
"FakeRoot.qll": (a_ql_stub(name="FakeRoot"), a_ql_class(name="FakeRoot", final=True)),
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"FakeRoot.qll": (a_ql_class_public(name="FakeRoot"), a_ql_stub(name="FakeRoot"), a_ql_class(name="FakeRoot", final=True, imports=[stub_import_prefix + "FakeRoot"])),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True, properties=[
|
||||
ql.Property(singular="Foo", plural="Foos", type="bar", tablename="my_object_foos",
|
||||
tableparams=["this", "index", "result"], is_optional=True,
|
||||
prev_child=prev_child, doc="foo of this my object",
|
||||
doc_plural="foos of this my object"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -396,11 +420,13 @@ def test_predicate_property(generate_classes):
|
||||
schema.Class("MyObject", properties=[
|
||||
schema.PredicateProperty("is_foo")]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True, properties=[
|
||||
ql.Property(singular="isFoo", type="predicate", tablename="my_object_is_foo",
|
||||
tableparams=["this"], is_predicate=True, doc="this my object is foo"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -411,16 +437,17 @@ def test_single_class_property(generate_classes, is_child, prev_child):
|
||||
schema.Class("MyObject", properties=[
|
||||
schema.SingleProperty("foo", "Bar", is_child=is_child)]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject", imports=[stub_import_prefix + "Bar"]),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(
|
||||
name="MyObject", final=True, imports=[stub_import_prefix + "Bar"], properties=[
|
||||
name="MyObject", final=True, imports=[stub_import_prefix + "Bar", stub_import_prefix + "MyObject"], properties=[
|
||||
ql.Property(singular="Foo", type="Bar", tablename="my_objects",
|
||||
tableparams=[
|
||||
"this", "result"],
|
||||
prev_child=prev_child, doc="foo of this my object"),
|
||||
],
|
||||
)),
|
||||
"Bar.qll": (a_ql_stub(name="Bar"), a_ql_class(name="Bar", final=True)),
|
||||
"Bar.qll": (a_ql_class_public(name="Bar"), a_ql_stub(name="Bar"), a_ql_class(name="Bar", final=True, imports=[stub_import_prefix + "Bar"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -429,7 +456,7 @@ def test_class_with_doc(generate_classes):
|
||||
assert generate_classes([
|
||||
schema.Class("A", doc=doc),
|
||||
]) == {
|
||||
"A.qll": (a_ql_stub(name="A", doc=doc), a_ql_class(name="A", final=True, doc=doc)),
|
||||
"A.qll": (a_ql_class_public(name="A", doc=doc), a_ql_stub(name="A", doc=doc), a_ql_class(name="A", final=True, doc=doc, imports=[stub_import_prefix + "A"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -440,10 +467,11 @@ def test_class_dir(generate_classes):
|
||||
schema.Class("B", bases=["A"]),
|
||||
]) == {
|
||||
f"{dir}/A.qll": (
|
||||
a_ql_stub(name="A", import_prefix="another.rel.path."), a_ql_class(name="A", dir=pathlib.Path(dir))),
|
||||
"B.qll": (a_ql_stub(name="B"),
|
||||
a_ql_class(name="B", final=True, bases=["A"],
|
||||
imports=[stub_import_prefix + "another.rel.path.A"])),
|
||||
a_ql_class_public(name="A"), a_ql_stub(name="A", import_prefix="another.rel.path."), a_ql_class(name="A", dir=pathlib.Path(dir), imports=[stub_import_prefix + "another.rel.path.A"])),
|
||||
"B.qll": (a_ql_class_public(name="B", imports=[stub_import_prefix + "another.rel.path.A"]),
|
||||
a_ql_stub(name="B"),
|
||||
a_ql_class(name="B", final=True, bases=["A"], bases_impl=["AImpl::A"],
|
||||
imports=[stub_import_prefix + "another.rel.path.AImpl::Impl as AImpl"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -518,7 +546,7 @@ def test_manage_parameters(opts, generate, renderer, force):
|
||||
|
||||
|
||||
def test_modified_stub_skipped(qlgen_opts, generate, render_manager):
|
||||
stub = qlgen_opts.ql_stub_output / "A.qll"
|
||||
stub = qlgen_opts.ql_stub_output / "AImpl.qll"
|
||||
render_manager.is_customized_stub.side_effect = lambda f: f == stub
|
||||
assert stub not in generate([schema.Class('A')])
|
||||
|
||||
@@ -732,14 +760,16 @@ def test_property_description(generate_classes):
|
||||
schema.SingleProperty("foo", "bar", description=description),
|
||||
]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_objects",
|
||||
tableparams=["this", "result"],
|
||||
doc="foo of this my object",
|
||||
description=description),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -748,12 +778,14 @@ def test_property_doc_override(generate_classes):
|
||||
schema.Class("MyObject", properties=[
|
||||
schema.SingleProperty("foo", "bar", doc="baz")]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_objects",
|
||||
tableparams=["this", "result"], doc="baz"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -763,7 +795,8 @@ def test_repeated_property_doc_override(generate_classes):
|
||||
schema.RepeatedProperty("x", "int", doc="children of this"),
|
||||
schema.RepeatedOptionalProperty("y", "int", doc="child of this")]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="X", plural="Xes", type="int",
|
||||
@@ -774,7 +807,8 @@ def test_repeated_property_doc_override(generate_classes):
|
||||
tablename="my_object_ies", is_optional=True,
|
||||
tableparams=["this", "index", "result"],
|
||||
doc="child of this", doc_plural="children of this"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -785,13 +819,15 @@ def test_property_doc_abbreviations(generate_classes, abbr, expected):
|
||||
schema.Class("Object", properties=[
|
||||
schema.SingleProperty(f"foo_{abbr}_bar", "baz")]),
|
||||
]) == {
|
||||
"Object.qll": (a_ql_stub(name="Object"),
|
||||
"Object.qll": (a_ql_class_public(name="Object"),
|
||||
a_ql_stub(name="Object"),
|
||||
a_ql_class(name="Object", final=True,
|
||||
properties=[
|
||||
ql.Property(singular=f"Foo{abbr.capitalize()}Bar", type="baz",
|
||||
tablename="objects",
|
||||
tableparams=["this", "result"], doc=expected_doc),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "Object"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -802,13 +838,15 @@ def test_property_doc_abbreviations_ignored_if_within_word(generate_classes, abb
|
||||
schema.Class("Object", properties=[
|
||||
schema.SingleProperty(f"foo_{abbr}acadabra_bar", "baz")]),
|
||||
]) == {
|
||||
"Object.qll": (a_ql_stub(name="Object"),
|
||||
"Object.qll": (a_ql_class_public(name="Object"),
|
||||
a_ql_stub(name="Object"),
|
||||
a_ql_class(name="Object", final=True,
|
||||
properties=[
|
||||
ql.Property(singular=f"Foo{abbr.capitalize()}acadabraBar", type="baz",
|
||||
tablename="objects",
|
||||
tableparams=["this", "result"], doc=expected_doc),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "Object"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -818,7 +856,8 @@ def test_repeated_property_doc_override_with_format(generate_classes):
|
||||
schema.RepeatedProperty("x", "int", doc="special {children} of this"),
|
||||
schema.RepeatedOptionalProperty("y", "int", doc="special {child} of this")]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="X", plural="Xes", type="int",
|
||||
@@ -831,7 +870,8 @@ def test_repeated_property_doc_override_with_format(generate_classes):
|
||||
tableparams=["this", "index", "result"],
|
||||
doc="special child of this",
|
||||
doc_plural="special children of this"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -841,7 +881,8 @@ def test_repeated_property_doc_override_with_multiple_formats(generate_classes):
|
||||
schema.RepeatedProperty("x", "int", doc="{cat} or {dog}"),
|
||||
schema.RepeatedOptionalProperty("y", "int", doc="{cats} or {dogs}")]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="X", plural="Xes", type="int",
|
||||
@@ -852,7 +893,8 @@ def test_repeated_property_doc_override_with_multiple_formats(generate_classes):
|
||||
tablename="my_object_ies", is_optional=True,
|
||||
tableparams=["this", "index", "result"],
|
||||
doc="cat or dog", doc_plural="cats or dogs"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -861,12 +903,14 @@ def test_property_doc_override_with_format(generate_classes):
|
||||
schema.Class("MyObject", properties=[
|
||||
schema.SingleProperty("foo", "bar", doc="special {baz} of this")]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_objects",
|
||||
tableparams=["this", "result"], doc="special baz of this"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -876,12 +920,14 @@ def test_property_on_class_with_default_doc_name(generate_classes):
|
||||
schema.SingleProperty("foo", "bar")],
|
||||
default_doc_name="baz"),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_objects",
|
||||
tableparams=["this", "result"], doc="foo of this baz"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -890,13 +936,13 @@ def test_stub_on_class_with_synth_from_class(generate_classes):
|
||||
schema.Class("MyObject", synth=schema.SynthInfo(from_class="A"),
|
||||
properties=[schema.SingleProperty("foo", "bar")]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject", synth_accessors=[
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"), a_ql_stub(name="MyObject", synth_accessors=[
|
||||
ql.SynthUnderlyingAccessor(argument="Entity", type="Raw::A", constructorparams=["result"]),
|
||||
]),
|
||||
a_ql_class(name="MyObject", final=True, properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_objects", synth=True,
|
||||
tableparams=["this", "result"], doc="foo of this my object"),
|
||||
])),
|
||||
], imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -905,7 +951,7 @@ def test_stub_on_class_with_synth_on_arguments(generate_classes):
|
||||
schema.Class("MyObject", synth=schema.SynthInfo(on_arguments={"base": "A", "index": "int", "label": "string"}),
|
||||
properties=[schema.SingleProperty("foo", "bar")]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject", synth_accessors=[
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"), a_ql_stub(name="MyObject", synth_accessors=[
|
||||
ql.SynthUnderlyingAccessor(argument="Base", type="Raw::A", constructorparams=["result", "_", "_"]),
|
||||
ql.SynthUnderlyingAccessor(argument="Index", type="int", constructorparams=["_", "result", "_"]),
|
||||
ql.SynthUnderlyingAccessor(argument="Label", type="string", constructorparams=["_", "_", "result"]),
|
||||
@@ -913,7 +959,7 @@ def test_stub_on_class_with_synth_on_arguments(generate_classes):
|
||||
a_ql_class(name="MyObject", final=True, properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_objects", synth=True,
|
||||
tableparams=["this", "result"], doc="foo of this my object"),
|
||||
])),
|
||||
], imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -922,13 +968,15 @@ def test_synth_property(generate_classes):
|
||||
schema.Class("MyObject", properties=[
|
||||
schema.SingleProperty("foo", "bar", synth=True)]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"),
|
||||
a_ql_stub(name="MyObject"),
|
||||
a_ql_class(name="MyObject", final=True,
|
||||
properties=[
|
||||
ql.Property(singular="Foo", type="bar", tablename="my_objects",
|
||||
synth=True,
|
||||
tableparams=["this", "result"], doc="foo of this my object"),
|
||||
])),
|
||||
],
|
||||
imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -936,7 +984,7 @@ def test_hideable_class(generate_classes):
|
||||
assert generate_classes([
|
||||
schema.Class("MyObject", hideable=True),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"), a_ql_class(name="MyObject", final=True, hideable=True)),
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"), a_ql_stub(name="MyObject"), a_ql_class(name="MyObject", final=True, hideable=True, imports=[stub_import_prefix + "MyObject"])),
|
||||
}
|
||||
|
||||
|
||||
@@ -947,9 +995,10 @@ def test_hideable_property(generate_classes):
|
||||
schema.SingleProperty("x", "MyObject"),
|
||||
]),
|
||||
]) == {
|
||||
"MyObject.qll": (a_ql_stub(name="MyObject"), a_ql_class(name="MyObject", final=True, hideable=True)),
|
||||
"Other.qll": (a_ql_stub(name="Other"),
|
||||
a_ql_class(name="Other", imports=[stub_import_prefix + "MyObject"],
|
||||
"MyObject.qll": (a_ql_class_public(name="MyObject"), a_ql_stub(name="MyObject"), a_ql_class(name="MyObject", final=True, hideable=True, imports=[stub_import_prefix + "MyObject"])),
|
||||
"Other.qll": (a_ql_class_public(name="Other", imports=[stub_import_prefix + "MyObject"]),
|
||||
a_ql_stub(name="Other"),
|
||||
a_ql_class(name="Other", imports=[stub_import_prefix + "MyObject", stub_import_prefix + "Other"],
|
||||
final=True, properties=[
|
||||
ql.Property(singular="X", type="MyObject", tablename="others",
|
||||
type_is_hideable=True,
|
||||
|
||||
Reference in New Issue
Block a user