Skip to content

Commit

Permalink
Major refactor of template generation
Browse files Browse the repository at this point in the history
- jinja templates now should generate only from dataclasses defined in
  j2_context.py
- Make some logic clearer and more efficient
  • Loading branch information
virtuald committed Sep 11, 2023
1 parent 51959cf commit fe7c5f7
Show file tree
Hide file tree
Showing 14 changed files with 1,626 additions and 748 deletions.
6 changes: 4 additions & 2 deletions robotpy_build/config/autowrap_yml.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ class PropData(Model):
ignore: bool = False

#: Set the python name of this property to the specified string
rename: Optional[str]
rename: Optional[str] = None

#: Python code access to this property
access: PropAccess = PropAccess.AUTOMATIC
Expand Down Expand Up @@ -278,6 +278,7 @@ class EnumData(Model):
#: Set the python name of this enum to the specified string
rename: Optional[str] = None

#: Remove this prefix from autogenerated enum value name
value_prefix: Optional[str] = None

#: If specified, put the enum in a sub.pack.age (ignored for
Expand Down Expand Up @@ -356,7 +357,8 @@ class ClassData(Model):
#: wrapping scope
typealias: List[str] = []

#: Extra constexpr to insert into the trampoline and wrapping scopes
#: Fully-qualified pre-existing constant that will be inserted into the
#: trampoline and wrapping scopes as a constexpr
constants: List[str] = []

#: If this is a template class, a list of the parameters if it can't
Expand Down
90 changes: 55 additions & 35 deletions robotpy_build/generator_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,30 @@
PropData,
FunctionData,
)
from .j2_context import OverloadTracker

from typing import Dict, Optional
import dataclasses
from typing import Dict, Optional, Tuple


@dataclasses.dataclass
class FnReportData:
missing: bool = False
overloads: Dict[str, bool] = dataclasses.field(default_factory=dict)
tracker: OverloadTracker = dataclasses.field(default_factory=OverloadTracker)


AttrMissingData = Dict[str, bool]
EnumMissingData = Dict[str, bool]
FnMissingData = Dict[str, FnReportData]


@dataclasses.dataclass
class ClsReportData:
missing: bool
attributes: AttrMissingData = dataclasses.field(default_factory=dict)
enums: EnumMissingData = dataclasses.field(default_factory=dict)
functions: FnMissingData = dataclasses.field(default_factory=dict)


class GeneratorData:
Expand All @@ -23,23 +45,18 @@ def __init__(self, data: AutowrapConfigYaml):
self.data = data

# report data
self.functions: Dict[str, bool] = {}
self.classes: Dict[str, Dict] = {}
self.enums: Dict[str, bool] = {}
self.attributes: Dict[str, bool] = {}
self.functions: FnMissingData = {}
self.classes: Dict[str, ClsReportData] = {}
self.enums: EnumMissingData = {}
self.attributes: AttrMissingData = {}

def get_class_data(self, name: str) -> ClassData:
data = self.data.classes.get(name)
missing = data is None
if missing:
data = ClassData()

self.classes[name] = {
"attributes": {},
"enums": {},
"functions": {},
"missing": missing,
}
self.classes[name] = ClsReportData(missing=missing)
return data

def get_cls_enum_data(
Expand All @@ -50,7 +67,7 @@ def get_cls_enum_data(
return EnumData()
data = cls_data.enums.get(name)
if data is None:
self.classes[cls_key]["enums"][name] = False
self.classes[cls_key].enums[name] = False
data = EnumData()

return data
Expand All @@ -64,23 +81,26 @@ def get_enum_data(self, name: str) -> EnumData:

def get_function_data(
self,
fn: dict,
name: str,
signature: str,
cls_key: Optional[str] = None,
cls_data: Optional[ClassData] = None,
is_private: bool = False,
) -> FunctionData:
name = fn["name"]
) -> Tuple[FunctionData, OverloadTracker]:
if cls_data and cls_key:
data = cls_data.methods.get(name)
report_base = self.classes[cls_key]["functions"]
report_base = self.classes[cls_key].functions
else:
data = self.data.functions.get(name)
report_base = self.functions

report_base = report_base.setdefault(name, {"overloads": {}, "first": fn})
report_data = report_base.get(name)
if not report_data:
report_data = FnReportData()
report_base[name] = report_data

missing = data is None
report_base["missing"] = missing and not is_private
report_data.missing = missing and not is_private

if missing:
data = FunctionData()
Expand All @@ -94,22 +114,16 @@ def get_function_data(
data.update(overload.dict(exclude_unset=True))
data = FunctionData(**data)

report_base["overloads"][signature] = is_private or not missing

# TODO: doesn't belong here
is_overloaded = len(report_base["overloads"]) > 1
if is_overloaded:
report_base["first"]["x_overloaded"] = True
fn["x_overloaded"] = is_overloaded

return data
report_data.overloads[signature] = is_private or not missing
report_data.tracker.add(signature)
return data, report_data.tracker

def get_cls_prop_data(
self, name: str, cls_key: str, cls_data: ClassData
) -> PropData:
data = cls_data.attributes.get(name)
if data is None:
self.classes[cls_key]["attributes"][name] = False
self.classes[cls_key].attributes[name] = False
data = PropData()

return data
Expand Down Expand Up @@ -138,12 +152,12 @@ def report_missing(self, name: str, reporter: "MissingReporter"):
all_cls_data = {}
for cls_key, cls_data in self.classes.items():
result = self._process_missing(
cls_data["attributes"],
cls_data["functions"],
cls_data["enums"],
cls_data.attributes,
cls_data.functions,
cls_data.enums,
"methods",
)
if result or cls_data["missing"]:
if result or cls_data.missing:
all_cls_data[str(cls_key)] = result
if all_cls_data:
data["classes"] = all_cls_data
Expand All @@ -153,7 +167,13 @@ def report_missing(self, name: str, reporter: "MissingReporter"):

return data

def _process_missing(self, attrs, fns, enums, fn_key: str):
def _process_missing(
self,
attrs: AttrMissingData,
fns: FnMissingData,
enums: EnumMissingData,
fn_key: str,
):
data: Dict[str, Dict[str, Dict]] = {}

# attributes
Expand All @@ -171,12 +191,12 @@ def _process_missing(self, attrs, fns, enums, fn_key: str):
fn_report = {}
for fn, fndata in fns.items():
fn = str(fn)
overloads = fndata["overloads"]
overloads = fndata.overloads
overloads_count = len(overloads)
if overloads_count > 1:
has_data = all(overloads.values())
else:
has_data = not fndata["missing"]
has_data = not fndata.missing

if not has_data:
d = {}
Expand Down
Loading

0 comments on commit fe7c5f7

Please sign in to comment.