Skip to content

Commit

Permalink
Merge pull request #140 from epics-containers/dev-update
Browse files Browse the repository at this point in the history
add ioc_yaml_file_name and ioc_name globals to jinja context
  • Loading branch information
gilesknap authored Nov 16, 2023
2 parents 653c00e + 99aa4b6 commit 7c8593d
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 43 deletions.
6 changes: 6 additions & 0 deletions src/ibek/gen_scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from jinja2 import Template
from ruamel.yaml.main import YAML

from ibek.utils import UTILS

from .globals import TEMPLATES
from .ioc import IOC, Entity, clear_entity_model_ids, make_entity_models, make_ioc_model
from .render import Render
Expand Down Expand Up @@ -56,6 +58,10 @@ def ioc_deserialize(ioc_instance_yaml: Path, definition_yaml: List[Path]) -> IOC
# extract the ioc instance yaml into a dict
ioc_instance_dict = YAML(typ="safe").load(ioc_instance_yaml)

# extract the ioc name into UTILS for use in jinja renders
name = UTILS.render({}, ioc_instance_dict["ioc_name"])
UTILS.set_ioc_name(name)

# Create an IOC instance from the instance dict and the model
ioc_instance = ioc_model(**ioc_instance_dict)

Expand Down
12 changes: 0 additions & 12 deletions src/ibek/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@

import os
from pathlib import Path
from typing import Dict

from jinja2 import Template
from pydantic import BaseModel, ConfigDict
from typer.core import TyperGroup

from .utils import UTILS

# get the container paths from environment variables
EPICS_BASE = Path(os.getenv("EPICS_BASE", "/epics/epics-base"))
EPICS_ROOT = Path(os.getenv("EPICS_ROOT", "/epics/"))
Expand Down Expand Up @@ -58,11 +54,3 @@ class BaseSettings(BaseModel):
class NaturalOrderGroup(TyperGroup):
def list_commands(self, ctx):
return self.commands.keys()


def render_with_utils(context: Dict, template_text: str) -> str:
"""
Render a Jinja template with the global __utils__ object in the context
"""
jinja_template = Template(template_text)
return jinja_template.render(context, __utils__=UTILS)
5 changes: 3 additions & 2 deletions src/ibek/ioc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
from pydantic.fields import FieldInfo
from pydantic_core import PydanticUndefined

from .globals import BaseSettings, render_with_utils
from .globals import BaseSettings
from .support import Definition, EnumArg, IdArg, ObjectArg, Support
from .utils import UTILS

id_to_entity: Dict[str, Entity] = {}

Expand Down Expand Up @@ -61,7 +62,7 @@ def add_ibek_attributes(cls, entity: Entity):
id_to_entity[value] = entity
elif isinstance(value, str):
# Jinja expansion of any of the Entity's string args/values
setattr(entity, arg, render_with_utils(entity_dict, value))
setattr(entity, arg, UTILS.render(entity_dict, value))
return entity

def __str__(self):
Expand Down
9 changes: 6 additions & 3 deletions src/ibek/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

from typing import Callable, List, Optional, Union

from .globals import render_with_utils
from .ioc import IOC, Entity
from .support import Comment, Script, Text, When
from .utils import UTILS


class Render:
Expand Down Expand Up @@ -45,7 +45,7 @@ def render_text(
raise NotImplementedError("When.last not yet implemented")

# Render Jinja entries in the text
result = render_with_utils(instance, text) # type: ignore
result = UTILS.render(instance, text) # type: ignore

if result == "":
return ""
Expand Down Expand Up @@ -97,7 +97,10 @@ def render_environment_variables(self, instance: Entity) -> Optional[str]:
for variable in variables:
# Substitute the name and value of the environment variable from args
env_template = f"epicsEnvSet {variable.name} {variable.value}"
env_var_txt += render_with_utils(instance, env_template) # type: ignore
env_var_txt += UTILS.render(
instance,
env_template,
) # type: ignore
return env_var_txt + "\n"

def render_elements(
Expand Down
6 changes: 3 additions & 3 deletions src/ibek/render_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
from dataclasses import dataclass
from typing import Any, Dict, List, Mapping, Optional, Tuple

from ibek.globals import render_with_utils
from ibek.ioc import IOC, Entity
from ibek.support import Database
from ibek.utils import UTILS


class RenderDb:
Expand All @@ -29,7 +29,7 @@ def add_row(self, filename: str, args: Mapping[str, Any], entity: Entity) -> Non
Adding a new template file if it does not already exist.
Convert all arguments to strings.
"""
filename = render_with_utils(dict(entity), filename)
filename = UTILS.render(dict(entity), filename)

if filename not in self.render_templates:
# for new filenames create a new RenderDbTemplate entry
Expand All @@ -47,7 +47,7 @@ def add_row(self, filename: str, args: Mapping[str, Any], entity: Entity) -> Non

# render any Jinja fields in the arguments
for i, line in enumerate(row):
row[i] = render_with_utils(dict(entity), row[i])
row[i] = UTILS.render(dict(entity), row[i])

# save the new row
self.render_templates[filename].rows.append(row)
Expand Down
11 changes: 6 additions & 5 deletions src/ibek/runtime_cmds/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
PVI_DEFS,
RUNTIME_OUTPUT_PATH,
NaturalOrderGroup,
render_with_utils,
)
from ibek.ioc import IOC, Entity
from ibek.support import Database
from ibek.utils import UTILS

runtime_cli = typer.Typer(cls=NaturalOrderGroup)

Expand All @@ -44,9 +44,12 @@ def generate(
"""
Build a startup script for an IOC instance
"""
# the file name under of the instance definition provides the IOC name
UTILS.set_file_name(instance)

ioc_instance = ioc_deserialize(instance, definitions)

# Clear out generated files so developers know if something stops being generated
# Clear out generated files so developers know if something stop being generated
shutil.rmtree(RUNTIME_OUTPUT_PATH, ignore_errors=True)
RUNTIME_OUTPUT_PATH.mkdir(exist_ok=True)
shutil.rmtree(OPI_OUTPUT_PATH, ignore_errors=True)
Expand Down Expand Up @@ -100,9 +103,7 @@ def generate_pvi(ioc: IOC) -> Tuple[List[IndexEntry], List[Tuple[Database, Entit
device.deserialize_parents([PVI_DEFS])

# Render the prefix value for the device from the instance parameters
macros = {
"prefix": render_with_utils(entity.model_dump(), entity_pvi.prefix)
}
macros = {"prefix": UTILS.render(entity.model_dump(), entity_pvi.prefix)}

if entity_pvi.pva_template:
# Create a template with the V4 structure defining a PVI interface
Expand Down
29 changes: 29 additions & 0 deletions src/ibek/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
"""

from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict

from jinja2 import Template


@dataclass
class Counter:
Expand All @@ -35,6 +38,8 @@ class Utils:
"""

def __init__(self: "Utils"):
self.file_name: str = ""
self.ioc_name: str = ""
self.__reset__()

def __reset__(self: "Utils"):
Expand All @@ -45,6 +50,18 @@ def __reset__(self: "Utils"):
self.variables: Dict[str, Any] = {}
self.counters: Dict[str, Counter] = {}

def set_file_name(self: "Utils", file: Path):
"""
Set the ioc name based on the file name of the instance definition
"""
self.file_name = file.stem

def set_ioc_name(self: "Utils", name: str):
"""
Set the ioc name based on the file name of the instance definition
"""
self.ioc_name = name

def set_var(self, key: str, value: Any):
"""create a global variable for our jinja context"""
self.variables[key] = value
Expand Down Expand Up @@ -77,6 +94,18 @@ def counter(

return result

def render(self, context: Any, template_text: str) -> str:
"""
Render a Jinja template with the global __utils__ object in the context
"""
jinja_template = Template(template_text)
return jinja_template.render(
context,
__utils__=self,
ioc_yaml_file_name=self.file_name,
ioc_name=self.ioc_name,
)


# a singleton Utility object for sharing state across all Entity renders
UTILS: Utils = Utils()
24 changes: 12 additions & 12 deletions tests/samples/outputs/all.ioc.subst
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@

file "another_test.db" {
pattern
{ "name", "my_int_enum", "clock_rate", "db_calculated", "calculated_one" }
{ "AllObject One", "2", "dummy", "HELLO AllObject One", "AllObject One.AllObject One String.1.1.0.True" }
{ "AllObject Two", "1", "1", "HELLO AllObject Two", "AllObject Two.AllObject Two String.1.1.0.True" }
{ "name", "my_int_enum", "clock_rate", "db_calculated", "calculated_one" }
{ "ioc_name={{ ioc_name }}", "2", "dummy", "HELLO ioc_name={{ ioc_name }}", "ioc_name={{ ioc_name }}.AllObject One String.1.1.0.True" }
{ "AllObject Two", "1", "1", "HELLO AllObject Two", "AllObject Two.AllObject Two String.1.1.0.True" }
}

file "yet_another.db" {
pattern
{ "name", "my_object", "my_float", "my_bool" }
{ "AllObject One", "Ref1", "1.0", "True" }
{ "AllObject Two", "Ref1", "1.0", "True" }
{ "name", "my_object", "my_float", "my_bool" }
{ "ioc_name={{ ioc_name }}", "Ref1", "1.0", "True" }
{ "AllObject Two", "Ref1", "1.0", "True" }
}

file "jinjified1.db" {
pattern
{ "name" }
{ "AllObject One" }
{ "AllObject Two" }
{ "name" }
{ "ioc_name={{ ioc_name }}" }
{ "AllObject Two" }
}

file "test.db" {
Expand All @@ -31,7 +31,7 @@ pattern

file "simple.pvi.template" {
pattern
{ "prefix" }
{ "AllObject One" }
{ "AllObject Two" }
{ "prefix" }
{ "ioc_name=all.ibek.ioc" }
{ "AllObject Two" }
}
4 changes: 2 additions & 2 deletions tests/samples/outputs/all.st.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ TestValues Ref1.127.0.0.1
# this is a comment
# that spans multiple lines
#
testPreInit "AllObject One" "'AllObject One String'"
testPreInit "ioc_name={{ ioc_name }}" "'AllObject One String'"
my_str = AllObject One String
my_inferred_enum = third
clock_rate = dummy
Expand All @@ -36,7 +36,7 @@ dbLoadRecords $(RUNTIME_DIR)/ioc.db
iocInit


testPostInit "AllObject One" test_value:
testPostInit "ioc_name={{ ioc_name }}" test_value:
this should appear once only in the post_init section
# post init comment
testPostInit "AllObject Two" test_value:
Expand Down
4 changes: 2 additions & 2 deletions tests/samples/outputs/index.bob
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<widget type="label" version="2.0.0">
<name>Title</name>
<class>TITLE</class>
<text>test-multiple-ioc - Index</text>
<text>{{ ioc_yaml_file_name }} - Index</text>
<x use_class="true">0</x>
<y use_class="true">0</y>
<width>273</width>
Expand Down Expand Up @@ -68,7 +68,7 @@
<target>tab</target>
<description>Open Display</description>
<macros>
<prefix>AllObject One</prefix>
<prefix>ioc_name={{ ioc_name }}</prefix>
</macros>
</action>
</actions>
Expand Down
4 changes: 2 additions & 2 deletions tests/samples/yaml/all.ibek.ioc.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# yaml-language-server: $schema=../schemas/multiple.ibek.ioc.schema.json

ioc_name: test-multiple-ioc
ioc_name: "{{ ioc_yaml_file_name }}"
description: a basic example for testing multiple support definitions

entities:
- type: object_module.RefObject
name: Ref1

- type: module.AllObject
name: AllObject One
name: ioc_name={{ ioc_name }}
my_object: Ref1
my_int_enum: full_speed
my_inferred_enum: third
Expand Down

0 comments on commit 7c8593d

Please sign in to comment.