Added support for Context and Variable(s).

development
Shawn Davis 4 years ago
parent c0dcea5ae5
commit 277ec1cf6b
  1. 217
      scripttease/parsers/utils.py
  2. 15
      tests/examples/variables.ini
  3. 25
      tests/test_library_commands_base.py

@ -1,7 +1,9 @@
# Imports # Imports
from commonkit import any_list_item from commonkit import any_list_item, smart_cast, split_csv
from configparser import RawConfigParser
import logging import logging
import os
from ..constants import LOGGER_NAME from ..constants import LOGGER_NAME
from .ini import Config from .ini import Config
@ -12,6 +14,10 @@ log = logging.getLogger(LOGGER_NAME)
__all__ = ( __all__ = (
"filter_commands", "filter_commands",
"load_commands", "load_commands",
"load_config",
"load_variables",
"Context",
"Variable",
) )
# Functions # Functions
@ -108,3 +114,212 @@ def load_config(path, overlay="ubuntu", **kwargs):
return None return None
return _config return _config
def load_variables(path, environment=None):
"""Load variables from a file.
:param path: The path to the file.
:type path: str
:param environment: Filter variables by the given environment name.
:type environment: str
:rtype: list[scripttease.parsers.utils.Variable]
"""
if not os.path.exists(path):
log.warning("Path to variables file does not exist: %s" % path)
return list()
if path.endswith(".ini"):
return _load_variables_ini(path, environment=environment)
else:
log.warning("Variable file format is not currently supports: %s" % path)
return list()
def _load_variables_ini(path, environment=None):
"""Load variables from an INI file. See ``load_variables()``."""
ini = RawConfigParser()
ini.read(path)
a = list()
for section in ini.sections():
if ":" in section:
variable_name, _environment = section.split(":")
else:
_environment = None
variable_name = section
_kwargs = {
'environment': _environment,
}
for key, value in ini.items(section):
if key == "tags":
value = split_csv(value)
else:
value = smart_cast(value)
_kwargs[key] = value
a.append(Variable(variable_name, **_kwargs))
if environment is not None:
b = list()
for var in a:
if var.environment and var.environment == environment or var.environment is None:
b.append(var)
return b
return a
# Classes
class Context(object):
"""A collection of variables."""
def __init__(self, **kwargs):
"""Initialize the context.
kwargs are added as variable instances.
"""
self.variables = dict()
for key, value in kwargs.items():
self.add(key, value)
def __getattr__(self, item):
if item in self.variables:
return self.variables[item].value
return None
def __repr__(self):
return "<%s (%s)>" % (self.__class__.__name__, len(self.variables))
def add(self, name, value, environment=None, tags=None):
"""Add a variable to the context.
:param name: The name of the variable.
:type name: str
:param value: The value of the variable in this context.
:param environment: The environment name to which the variable applies. ``None`` applies to all environments.
:type environment: str
:param tags: A list of tags that describe the variable.
:type tags: list[str]
:rtype: scripttease.parsers.utils.Variable
:raise: RuntimeError
:raises: ``RuntimeError`` if the variable already exists.
"""
if name in self.variables:
raise RuntimeError("Variable already exists: %s" % name)
v = Variable(name, value, environment=environment, tags=tags)
self.variables[name] = v
return v
def get(self, name, default=None):
"""Get a the value of the variable from the context.
:param name: The name of the variable.
:type name: str
:param default: The default value to return.
"""
if not self.has(name):
return default
return self.variables[name].value
def has(self, name):
"""Indicates whether the named variable exists in this context, and whether the value is not ``None``.
:rtype: bool
"""
if name not in self.variables:
return False
return self.variables[name].value is not None
def join(self, variables):
"""Join a list of variables to the context.
:param variables: the list of variables to be added.
:type variables: list[scripttease.parsers.utils.Variable]
.. note::
This *replaces* a variable if it already exists.
"""
for v in variables:
self.variables[v.name] = v
def mapping(self):
"""Export the context as a dictionary.
:rtype: dict
"""
values = dict()
for key, var in self.variables.items():
values[key] = var.value or var.default
return values
def merge(self, context):
"""Merge another context with this one.
:param context: The context to be merged.
:type context: scripttease.parser.utils.Context
.. note::
Variables that exist in the current context are *not* replaced with variables from the provided context.
"""
for name, var in context.variables.items():
if not self.has(name):
self.variables[name] = var
class Variable(object):
"""Represents a variable to be used in the context of pre-processing a config file."""
def __init__(self, name, value, **kwargs):
"""Initialize a variable.
:param name: The variable name.
:type name: str
:param value: The value of the variable.
kwargs are added as attributes of the instance.
"""
self.name = name
self.value = value
kwargs.setdefault("tags", list())
self._attributes = kwargs
def __eq__(self, other):
return self.value == other
def __getattr__(self, item):
return self._attributes.get(item)
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, self.name)

@ -0,0 +1,15 @@
[domain_name]
value = example.com
[domain_tld]
value = example_com
[debug_enabled:testing]
value = True
[postgres_version]
value = 11
tags = postgres
[mailgun_domain:live]
value = "mg.example.com

@ -57,6 +57,10 @@ class TestCommand(object):
assert c.sudo.user == "root" assert c.sudo.user == "root"
assert c.sudo.enabled is False assert c.sudo.enabled is False
def test_is_itemized(self):
c = Command("ls -ls")
assert c.is_itemized is False
def test_repr(self): def test_repr(self):
c = Command("ls -ls", comment="listing") c = Command("ls -ls", comment="listing")
assert repr(c) == "<Command listing>" assert repr(c) == "<Command listing>"
@ -94,6 +98,10 @@ class TestItemizedCommand(object):
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item") c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item")
assert c.has_attribute("testing") is False assert c.has_attribute("testing") is False
def test_is_itemized(self):
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item")
assert c.is_itemized is True
def test_repr(self): def test_repr(self):
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item") c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item")
assert repr(c) == "<ItemizedCommand python_pip>" assert repr(c) == "<ItemizedCommand python_pip>"
@ -103,3 +111,20 @@ class TestItemizedCommand(object):
assert c.testing is None assert c.testing is None
c.set_attribute("testing", True) c.set_attribute("testing", True)
assert c.testing is True assert c.testing is True
class TestSudo(object):
def test_bool(self):
s = Sudo()
assert bool(s) is False
s = Sudo(True)
assert bool(s) is True
def test_str(self):
s = Sudo()
assert str(s) == ""
s = Sudo(True)
assert str(s) == "sudo -u root"

Loading…
Cancel
Save