Template
Module for TIM templating logic and classes.
        BaseFileObject
  
      pydantic-model
  
      ¶
Base model for file/directory type template objects
        
delete: bool        
  
      pydantic-field
  
      ¶
If the template object should be deleted after instantiating the TSM
        
dest: str        
  
      pydantic-field
      required
  
      ¶
The templates destination path (relative to the containing directories destination path)
        
src: str        
  
      pydantic-field
      required
  
      ¶
The template source path (relative to the containing directory)
        BaseObject
  
      pydantic-model
  
      ¶
Base model for template configuration models
        
extra: Any        
  
      pydantic-field
  
      ¶
Dict that can be used to bind additional context info to a template object
        
name: str        
  
      pydantic-field
      required
  
      ¶
A descriptive name for the file template directive
        
dict(self, **kwargs)        
      ¶
Generate a dictionary representation of the model, optionally specifying which fields to include or exclude.
Source code in generator/template.py
def dict(self, **kwargs):
    # set by alias to true by default
    # to ensure that template object fields are always serialized based
    # on their alias names
    kwargs.setdefault("by_alias", True)
    return super().dict(**kwargs)
        Directory
  
      pydantic-model
  
      ¶
        File
  
      pydantic-model
  
      ¶
File template model for configuring TIM template files
        
alpha(string)        
      ¶
Returns a string only containing letters.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
string | 
str | 
 String containing all kinds of characters.  | 
required | 
Returns:
| Type | Description | 
|---|---|
str | 
 The string without any non-alpha characters.  | 
Source code in generator/template.py
def alpha(string: str) -> str:
    """Returns a string only containing letters.
    Args:
        string: String containing all kinds of characters.
    Returns:
        The string without any non-alpha characters.
    """
    return "".join(
        [
            x
            for x in string
            if x in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
        ]
    )
        
create_context_environment(seed_store, generators=[], template_dirs=PosixPath('.'))        
      ¶
Creates the Jinja2 context for rendering the TIM context configuration.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
seed_store | 
SeedStore | 
 The seed store to use for generating PRNG seeds.  | 
required | 
generators | 
List[cr_kyoushi.generator.plugin.Generator] | 
 The random data generators to make available in the Jinja2 context  | 
[] | 
template_dirs | 
Union[str, pathlib.Path, List[Union[str, pathlib.Path]]] | 
 The Jinja2 template directory.  | 
PosixPath('.') | 
Returns:
| Type | Description | 
|---|---|
NativeEnvironment | 
 Jinja2 NativeEnvironment for rendering the TIM context.  | 
Source code in generator/template.py
def create_context_environment(
    seed_store: SeedStore,
    generators: List[Generator] = [],
    template_dirs: Union[Text, Path, List[Union[Text, Path]]] = Path("./"),
) -> NativeEnvironment:
    """Creates the Jinja2 context for rendering the TIM context configuration.
    Args:
        seed_store: The seed store to use for generating PRNG seeds.
        generators: The random data generators to make available in the Jinja2 context
        template_dirs: The Jinja2 template directory.
    Returns:
        Jinja2 NativeEnvironment for rendering the TIM context.
    """
    env = create_template_object_environment(template_dirs)
    _env_add_generators(env, seed_store, generators)
    return env
        
create_environment(seed_store, config, template_dirs=PosixPath('.'), generators=[])        
      ¶
Creates the Jinja2 context for rendering the TIM templates.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
config | 
JinjaConfig | 
 The Jinja2 configuration.  | 
required | 
template_dirs | 
Union[str, pathlib.Path, List[Union[str, pathlib.Path]]] | 
 The Jinja2 template directory.  | 
PosixPath('.') | 
generators | 
List[cr_kyoushi.generator.plugin.Generator] | 
 The random data generators to make available in the Jinja2 context  | 
[] | 
Returns:
| Type | Description | 
|---|---|
NativeEnvironment | 
 Jinja2 NativeEnvironment for rendering the TIM.  | 
Source code in generator/template.py
def create_environment(
    seed_store: SeedStore,
    config: JinjaConfig,
    template_dirs: Union[Text, Path, List[Union[Text, Path]]] = Path("./"),
    generators: List[Generator] = [],
) -> NativeEnvironment:
    """Creates the Jinja2 context for rendering the TIM templates.
    Args:
        config: The Jinja2 configuration.
        template_dirs: The Jinja2 template directory.
        generators: The random data generators to make available in the Jinja2 context
    Returns:
        Jinja2 NativeEnvironment for rendering the TIM.
    """
    env = NativeEnvironment(
        loader=FileSystemLoader(template_dirs),
        block_start_string=config.block_start,
        block_end_string=config.block_end,
        variable_start_string=config.variable_start,
        variable_end_string=config.variable_end,
        comment_start_string=config.comment_start,
        comment_end_string=config.comment_end,
        line_statement_prefix=config.line_statement,
        line_comment_prefix=config.line_comment,
        keep_trailing_newline=True,
        undefined=StrictUndefined,
        extensions=["jinja2.ext.do", "jinja2.ext.loopcontrols"],
    )
    _add_env_options(env)
    _env_add_generators(env, seed_store, generators)
    return env
        
create_template_object_environment(template_dirs=PosixPath('.'))        
      ¶
Creates the Jinja2 context for rendering the templates.yml.j2 configuration.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
template_dirs | 
Union[str, pathlib.Path, List[Union[str, pathlib.Path]]] | 
 The Jinja2 template directory.  | 
PosixPath('.') | 
Returns:
| Type | Description | 
|---|---|
NativeEnvironment | 
 Jinja2 NativeEnvironment for rendering the templates configuration.  | 
Source code in generator/template.py
def create_template_object_environment(
    template_dirs: Union[Text, Path, List[Union[Text, Path]]] = Path("./")
) -> NativeEnvironment:
    """Creates the Jinja2 context for rendering the `templates.yml.j2` configuration.
    Args:
        template_dirs: The Jinja2 template directory.
    Returns:
        Jinja2 NativeEnvironment for rendering the templates configuration.
    """
    env = NativeEnvironment(
        loader=FileSystemLoader(template_dirs),
        undefined=StrictUndefined,
        extensions=["jinja2.ext.do", "jinja2.ext.loopcontrols"],
    )
    _add_env_options(env)
    return env
        
get_max_hosts(mask)        
      ¶
Returns the number of maximum hosts to use.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
mask | 
int | 
 network mask.  | 
required | 
Returns:
| Type | Description | 
|---|---|
int | 
 An integer representing the maximum amount of hosts.  | 
Source code in generator/template.py
def get_max_hosts(mask: int) -> int:
    """Returns the number of maximum hosts to use.
    Args:
        mask: network mask.
    Returns:
        An integer representing the maximum amount of hosts.
    """
    host_bits = 32 - mask
    return 2**host_bits - 2
        
get_yaml()        
      ¶
Utility function for creating a YAML parser and serializer.
Returns:
| Type | Description | 
|---|---|
YAML | 
 A ruamel.yaml.YAML object  | 
Source code in generator/template.py
def get_yaml() -> YAML:
    """Utility function for creating a YAML parser and serializer.
    Returns:
        A ruamel.yaml.YAML object
    """
    yaml = YAML(typ="safe")
    for g in [File, Directory]:
        yaml.register_class(g)
    return yaml
        
normalize_probabilities_map(container, ignore=['extra'], skip=[])        
      ¶
Normalizes a dictionary of distributions.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
container | 
Dict[str, Union[Dict[str, float], List[float]]] | 
 Dictionary containing multiple distributions  | 
required | 
ignore | 
List[str] | 
 Distribution dictionary keys to ignore during normalization  | 
['extra'] | 
skip | 
List[str] | 
 Dictionary keys to ignore (i.e., incase the given dict contains keys which are not distributions.)  | 
[] | 
Exceptions:
| Type | Description | 
|---|---|
Exception | 
 If the given distributions have an invalid container type, sum to 0 or contain negative numbers.  | 
Returns:
| Type | Description | 
|---|---|
Dict[str, Union[Dict[str, float], List[float]]] | 
 The dictionary with all its distributions normalized.  | 
Source code in generator/template.py
def normalize_probabilities_map(
    container: Dict[str, Union[Dict[str, float], List[float]]],
    ignore: List[str] = ["extra"],
    skip: List[str] = [],
) -> Dict[str, Union[Dict[str, float], List[float]]]:
    """Normalizes a dictionary of distributions.
    Args:
        container: Dictionary containing multiple distributions
        ignore: Distribution dictionary keys to ignore during normalization
        skip: Dictionary keys to ignore (i.e., incase the given dict contains keys
                                    which are not distributions.)
    Raises:
        Exception: If the given distributions have an invalid container type, sum to 0
                   or contain negative numbers.
    Returns:
        The dictionary with all its distributions normalized.
    """
    if isinstance(container, dict):
        # normalize each distribution
        for k, v in container.items():
            # but do nothing with those in the skip list
            if k not in skip:
                container[k] = normalize_propabilities(v, ignore=ignore)
        return container
    raise Exception(f"Normalize map target must be a dictionary, but got '{container}'")
        
normalize_propabilities(propabilities, ignore=['extra'])        
      ¶
Accepts probability distribution dicts and lists and normalizes them.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
propabilities | 
Union[Dict[str, float], List[float]] | 
 A probability distribution as dict or list  | 
required | 
ignore | 
List[str] | 
 Additional dict keys to ignore during normalization.  | 
['extra'] | 
Exceptions:
| Type | Description | 
|---|---|
Exception | 
 If the given distribution has an invalid container type, sums to 0 or contains negative numbers.  | 
Returns:
| Type | Description | 
|---|---|
Union[Dict[str, float], List[float]] | 
 The normalized distribution.  | 
Source code in generator/template.py
def normalize_propabilities(
    propabilities: Union[Dict[str, float], List[float]], ignore: List[str] = ["extra"]
) -> Union[Dict[str, float], List[float]]:
    """Accepts probability distribution dicts and lists and normalizes them.
    Args:
        propabilities: A probability distribution as dict or list
        ignore: Additional dict keys to ignore during normalization.
    Raises:
        Exception: If the given distribution has an invalid container type, sums to 0
                   or contains negative numbers.
    Returns:
        The normalized distribution.
    """
    if isinstance(propabilities, dict):
        keys = []
        values = []
        # only consider non ignored fields as part of the distribution
        for k, p in propabilities.items():
            if k not in ignore:
                keys.append(k)
                values.append(p)
        # normalize the probabilities
        values = _normalize_propabilities(values)
        # the order is preserved so we can simply zip them
        # back together
        norm = dict(zip(keys, values))
        # add the ignored fields back
        for e in ignore:
            if e in propabilities:
                norm[e] = propabilities[e]
        return norm
    elif isinstance(propabilities, list):
        # ensure we work on a copy (we don't want to modify the original)
        propabilities = list(propabilities)
        return _normalize_propabilities(propabilities)
    raise Exception(
        f"Normalize target must be a dictionary or list, but got '{propabilities}'"
    )
        
render_template(env, template, context)        
      ¶
Utility function for rendering Jinja2 text or file templates.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
env | 
NativeEnvironment | 
 The Jinja2 environment to use for rendering  | 
required | 
template | 
Union[str, pathlib.Path] | 
 The template string or file to render  | 
required | 
context | 
Any | 
 The context variables to use for rendering  | 
required | 
Returns:
| Type | Description | 
|---|---|
Any | 
 The rendered template string or data structure  | 
Source code in generator/template.py
def render_template(
    env: NativeEnvironment,
    template: Union[Text, Path],
    context: Any,
) -> Any:
    """Utility function for rendering Jinja2 text or file templates.
    Args:
        env: The Jinja2 environment to use for rendering
        template: The template string or file to render
        context: The context variables to use for rendering
    Returns:
        The rendered template string or data structure
    """
    # convert strings to template
    if isinstance(template, Path):
        _template = env.get_template(str(template))
    else:
        _template = env.from_string(template)
    value = _template.render(**context)
    if isinstance(value, Undefined):
        value._fail_with_undefined_error()
    return value
        
render_tim(env, object_list, src_dir, dest_dir, inputs, global_context, parent_context={}, delete_dirs=None, delete_files=None)        
      ¶
Function for rendering a TIM using the context and template configuration.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
env | 
NativeEnvironment | 
 The TIM Jinja2 environment.  | 
required | 
object_list | 
List[Union[cr_kyoushi.generator.template.File, cr_kyoushi.generator.template.Directory]] | 
 The list of template objects to render for the TIM.  | 
required | 
src_dir | 
Path | 
 The current source directory for template objects.  | 
required | 
dest_dir | 
Path | 
 The current destination directory for rendered templates.  | 
required | 
global_context | 
Dict[str, Any] | 
 The global TIM context.  | 
required | 
parent_context | 
Dict[str, Any] | 
 The TIM context specific to the parent template objects.  | 
{} | 
delete_dirs | 
Optional[Deque[pathlib.Path]] | 
 LiFo queue of directories to delete at the end of render process.  | 
None | 
delete_files | 
Optional[Deque[pathlib.Path]] | 
 LiFo queue of files to delete at the end of render process.  | 
None | 
Exceptions:
| Type | Description | 
|---|---|
NotImplementedError | 
 If the raw object_list contains an unknown template object type  | 
Returns:
| Type | Description | 
|---|---|
Tuple[Deque[pathlib.Path], Deque[pathlib.Path]] | 
 The final   | 
Source code in generator/template.py
def render_tim(
    env: NativeEnvironment,
    object_list: List[Union[File, Directory]],
    src_dir: Path,
    dest_dir: Path,
    inputs: Dict[str, Any],
    global_context: Dict[str, Any],
    parent_context: Dict[str, Any] = {},
    delete_dirs: Optional[Deque[Path]] = None,
    delete_files: Optional[Deque[Path]] = None,
) -> Tuple[Deque[Path], Deque[Path]]:
    """Function for rendering a TIM using the context and template configuration.
    Args:
        env: The TIM Jinja2 environment.
        object_list: The list of template objects to render for the TIM.
        src_dir: The current source directory for template objects.
        dest_dir: The current destination directory for rendered templates.
        global_context: The global TIM context.
        parent_context: The TIM context specific to the parent template objects.
        delete_dirs: LiFo queue of directories to delete at the end of render process.
        delete_files: LiFo queue of files to delete at the end of render process.
    Raises:
        NotImplementedError: If the raw object_list contains an unknown template object type
    Returns:
        The final `delete_dirs` and `delete_files` queues as tuple `(delete_dirs, delete_files)`
    """
    if delete_dirs is None:
        delete_dirs = deque()
    if delete_files is None:
        delete_files = deque()
    for obj in object_list:
        src: Path = src_dir.joinpath(obj.src)
        dest: Path = dest_dir.joinpath(obj.dest)
        if isinstance(obj, Directory):
            # create the directory
            dest.mkdir(parents=True, exist_ok=True)
            # handle the copy configuration
            # i.e., resolve all globs and copy them to the destination
            for glb in obj.copy_:
                for copy_src in src.glob(glb):
                    copy_src_relative = copy_src.relative_to(src)
                    copy_dest = dest.joinpath(copy_src_relative)
                    # make sure the containing directory exists (might be needed if glob is for some sub dir)
                    copy_dest.parent.mkdir(parents=True, exist_ok=True)
                    shutil.copy(copy_src, copy_dest)
            # handle all sub template objects (dirs and files)
            new_parent_context = parent_context.copy()
            new_parent_context.update(obj.extra)
            if obj.delete and src not in delete_files:
                delete_dirs.append(src)
            render_tim(
                env,
                obj.contents,
                src,
                dest,
                inputs,
                global_context,
                new_parent_context,
                delete_dirs,
                delete_files,
            )
        elif isinstance(obj, File):
            context = {
                "inputs": inputs,
                "context": global_context,
                "parent_context": parent_context,
                "local_context": obj.extra,
            }
            if obj.delete and src not in delete_files:
                delete_files.append(src)
            write_template(env, src, dest, context)
        else:
            raise NotImplementedError()
    return (delete_dirs, delete_files)
        
validate_object_list(object_list)        
      ¶
Utility function for validating template object model lists.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
object_list | 
 | 
 List of unvalidated template object models.  | 
required | 
Returns:
| Type | Description | 
|---|---|
List[Union[cr_kyoushi.generator.template.File, cr_kyoushi.generator.template.Directory]] | 
 A validated list of template object models converted to the correct Python classes.  | 
Source code in generator/template.py
def validate_object_list(object_list) -> List[Union[File, Directory]]:
    """Utility function for validating template object model lists.
    Args:
        object_list: List of unvalidated template object models.
    Returns:
        A validated list of template object models converted to the correct
        Python classes.
    """
    return parse_obj_as(
        Annotated[List[Union[File, Directory]], Field()],  # type: ignore[arg-type]
        object_list,
    )
        
write_template(env, src, dest, context)        
      ¶
Utility function for rendering and writing a template file.
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
env | 
NativeEnvironment | 
 The Jinja2 environment to use for rendering  | 
required | 
src | 
Path | 
 The template file path  | 
required | 
dest | 
Path | 
 The path to write the rendered template to  | 
required | 
context | 
Any | 
 The context variables to use for rendering  | 
required | 
Source code in generator/template.py
def write_template(env: NativeEnvironment, src: Path, dest: Path, context: Any):
    """Utility function for rendering and writing a template file.
    Args:
        env: The Jinja2 environment to use for rendering
        src: The template file path
        dest: The path to write the rendered template to
        context: The context variables to use for rendering
    """
    template_rendered = render_template(env, src, context)
    with open(dest, "w") as f:
        # mappings and lists are output as yaml files
        if isinstance(template_rendered, Mapping) or (
            # need to exclude str types since they are also sequences
            not isinstance(template_rendered, Text)
            and isinstance(template_rendered, Sequence)
        ):
            yaml = YAML(typ="safe")
            yaml.dump(template_rendered, f)
        else:
            f.write(str(template_rendered))