modified: netbox_services/engine/allocators.py modified: netbox_services/engine/context.py new file: netbox_services/engine/mappers.py modified: netbox_services/engine/resolver.py new file: netbox_services/engine/templating.py modified: netbox_services/forms.py modified: netbox_services/models.py modified: netbox_services/tests/test_allocators.py new file: netbox_services/tests/test_mappers.py modified: netbox_services/tests/test_resolver.py new file: netbox_services/tests/test_templating.py Simplify layout to template and service instances
60 lines
1.8 KiB
Python
60 lines
1.8 KiB
Python
"""Resolution context — carries service and ancestor chain during tree walk."""
|
|
|
|
|
|
class Context:
|
|
"""Evaluation frame for the resolver.
|
|
|
|
Top-level context has ``service`` set, ``parent_object`` as None,
|
|
and ``ancestors`` as an empty list.
|
|
|
|
Each selected/allocated object pushes a child context so children
|
|
can reference ``parent`` (immediate) and ``ancestors`` (full chain).
|
|
|
|
``ancestors[0]`` is the immediate parent (``p1`` in Jinja2),
|
|
``ancestors[1]`` is the grandparent (``p2``), etc.
|
|
"""
|
|
|
|
def __init__(self, service, parent_object=None, ancestors=None):
|
|
self.service = service
|
|
self.parent_object = parent_object
|
|
self.ancestors = ancestors if ancestors is not None else []
|
|
|
|
def child(self, obj):
|
|
return Context(
|
|
service=self.service,
|
|
parent_object=obj,
|
|
ancestors=[obj] + self.ancestors,
|
|
)
|
|
|
|
@property
|
|
def parent(self):
|
|
return self.parent_object
|
|
|
|
def resolve_path(self, path):
|
|
"""Resolve a dotted reference like ``parent.site.management_prefix``.
|
|
|
|
Supported roots:
|
|
- ``service`` → the Service instance
|
|
- ``parent`` → the parent object (or None at service scope)
|
|
Returns None if any segment is None or missing.
|
|
"""
|
|
parts = path.split(".")
|
|
root = parts[0]
|
|
|
|
if root == "service":
|
|
obj = self.service
|
|
elif root == "parent":
|
|
obj = self.parent_object
|
|
else:
|
|
raise KeyError(f"Unknown context root: {root!r}")
|
|
|
|
for part in parts[1:]:
|
|
if obj is None:
|
|
return None
|
|
if isinstance(obj, dict):
|
|
obj = obj.get(part)
|
|
else:
|
|
obj = getattr(obj, part, None)
|
|
|
|
return obj
|