mirror of
https://github.com/github/codeql.git
synced 2025-12-17 09:13:20 +01:00
154 lines
3.8 KiB
Python
154 lines
3.8 KiB
Python
"""
|
|
QL files generation
|
|
|
|
`generate(opts, renderer)` will generate QL classes and manage stub files out of a `yml` schema file.
|
|
|
|
Each class (for example, `Foo`) in the schema triggers:
|
|
* generation of a `FooBase` class implementation translating all properties into appropriate getters
|
|
* if not created or already customized, generation of a stub file which defines `Foo` as extending `FooBase`. This can
|
|
be used to add hand-written code to `Foo`, which requires removal of the `// generated` header comment in that file.
|
|
All generated base classes actually import these customizations when referencing other classes.
|
|
Generated files that do not correspond any more to any class in the schema are deleted. Customized stubs are however
|
|
left behind and must be dealt with by hand.
|
|
"""
|
|
|
|
import pathlib
|
|
from dataclasses import dataclass, field
|
|
from typing import List, ClassVar
|
|
|
|
import inflection
|
|
|
|
|
|
@dataclass
|
|
class Param:
|
|
param: str
|
|
first: bool = False
|
|
|
|
|
|
@dataclass
|
|
class Property:
|
|
singular: str
|
|
type: str = None
|
|
tablename: str = None
|
|
tableparams: List[Param] = field(default_factory=list)
|
|
plural: str = None
|
|
first: bool = False
|
|
local_var: str = "x"
|
|
is_optional: bool = False
|
|
is_predicate: bool = False
|
|
is_child: bool = False
|
|
qltest_skip: bool = False
|
|
|
|
def __post_init__(self):
|
|
if self.tableparams:
|
|
if self.type_is_class:
|
|
self.tableparams = [x if x != "result" else self.local_var for x in self.tableparams]
|
|
self.tableparams = [Param(x) for x in self.tableparams]
|
|
self.tableparams[0].first = True
|
|
|
|
@property
|
|
def getter(self):
|
|
return f"get{self.singular}" if not self.is_predicate else self.singular
|
|
|
|
@property
|
|
def indefinite_getter(self):
|
|
if self.plural:
|
|
article = "An" if self.singular[0] in "AEIO" else "A"
|
|
return f"get{article}{self.singular}"
|
|
|
|
@property
|
|
def type_is_class(self):
|
|
return bool(self.type) and self.type[0].isupper()
|
|
|
|
@property
|
|
def is_repeated(self):
|
|
return bool(self.plural)
|
|
|
|
@property
|
|
def is_single(self):
|
|
return not (self.is_optional or self.is_repeated or self.is_predicate)
|
|
|
|
|
|
@dataclass
|
|
class Class:
|
|
template: ClassVar = 'ql_class'
|
|
|
|
name: str
|
|
bases: List[str] = field(default_factory=list)
|
|
final: bool = False
|
|
properties: List[Property] = field(default_factory=list)
|
|
dir: pathlib.Path = pathlib.Path()
|
|
imports: List[str] = field(default_factory=list)
|
|
qltest_skip: bool = False
|
|
qltest_collapse_hierarchy: bool = False
|
|
qltest_uncollapse_hierarchy: bool = False
|
|
|
|
def __post_init__(self):
|
|
self.bases = sorted(self.bases)
|
|
if self.properties:
|
|
self.properties[0].first = True
|
|
|
|
@property
|
|
def db_id(self):
|
|
return "@" + inflection.underscore(self.name)
|
|
|
|
@property
|
|
def root(self):
|
|
return not self.bases
|
|
|
|
@property
|
|
def path(self):
|
|
return self.dir / self.name
|
|
|
|
|
|
@dataclass
|
|
class Stub:
|
|
template: ClassVar = 'ql_stub'
|
|
|
|
name: str
|
|
base_import: str
|
|
|
|
|
|
@dataclass
|
|
class ImportList:
|
|
template: ClassVar = 'ql_imports'
|
|
|
|
imports: List[str] = field(default_factory=list)
|
|
|
|
|
|
@dataclass
|
|
class GetParentImplementation:
|
|
template: ClassVar = 'ql_parent'
|
|
|
|
classes: List[Class] = field(default_factory=list)
|
|
|
|
|
|
@dataclass
|
|
class PropertyForTest:
|
|
getter: str
|
|
type: str = None
|
|
is_single: bool = False
|
|
is_predicate: bool = False
|
|
is_repeated: bool = False
|
|
|
|
|
|
@dataclass
|
|
class ClassTester:
|
|
template: ClassVar = 'ql_test_class'
|
|
|
|
class_name: str
|
|
properties: List[PropertyForTest] = field(default_factory=list)
|
|
|
|
|
|
@dataclass
|
|
class PropertyTester:
|
|
template: ClassVar = 'ql_test_property'
|
|
|
|
class_name: str
|
|
property: PropertyForTest
|
|
|
|
|
|
@dataclass
|
|
class MissingTestInstructions:
|
|
template: ClassVar = 'ql_test_missing'
|