A collection of classes and commands for automated command line scripting using Python.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

262 lines
8.3 KiB

# Imports
from commonkit import any_list_item, parse_jinja_string, parse_jinja_template, pick, read_file, smart_cast, split_csv, \
File
from configparser import ParsingError, RawConfigParser
from jinja2.exceptions import TemplateError, TemplateNotFound
import logging
import os
from ...constants import EXCLUDED_KWARGS
from ..contexts import Variable
from ..snippets.mappings import MAPPINGS
log = logging.getLogger(__name__)
# Exports
__all__ = (
# "filter_snippets",
"load_variables",
"BaseLoader",
)
# Functions
# def filter_commands(commands, key, value):
# """Filter commands based on the given criteria.
#
# :param commands: The commands to be filtered.
# :type commands: list[scripttease.lib.commands.base.Command]
#
# :param key: The attribute name to be matched.
# :type key: str
#
# :param value: The value of the attribute.
#
# """
# filtered = list()
# for command in commands:
# try:
# values = getattr(command, key)
# except AttributeError:
# continue
#
# if not any_list_item(values, key):
# continue
#
# if not any_list_item()
# if environments is not None and len(snippet.environments) > 0:
# if not any_list_item(environments, snippet.environments):
# continue
#
# if tags is not None:
# if not any_list_item(tags, snippet.tags):
# continue
#
# filtered.append(snippet)
#
# return filtered
def load_variables(path, env=None):
"""Load variables from an INI file.
:param path: The path to the INI file.
:type path: str
:param env: The environment name of variables to return.
:type env: str
:rtype: list[scripttease.lib.contexts.Variable]
"""
if not os.path.exists(path):
log.warning("Variables file does not exist: %s" % path)
return list()
ini = RawConfigParser()
try:
ini.read(path)
except ParsingError as e:
log.warning("Failed to parse %s variables file: %s" % (path, str(e)))
return list()
variables = list()
for variable_name in ini.sections():
if ":" in variable_name:
variable_name, _environment = variable_name.split(":")
else:
_environment = None
variable_name = variable_name
kwargs = {
'environment': _environment,
}
_value = None
for key, value in ini.items(variable_name):
if key == "value":
_value = smart_cast(value)
continue
kwargs[key] = smart_cast(value)
variables.append(Variable(variable_name, _value, **kwargs))
if env is not None:
filtered_variables = list()
for var in variables:
if var.environment and var.environment == env or var.environment is None:
filtered_variables.append(var)
return filtered_variables
return variables
# Classes
class BaseLoader(File):
"""Base class for loading a command file."""
def __init__(self, path, context=None, excluded_kwargs=None, locations=None, mappings=None, profile="ubuntu",
**kwargs):
"""Initialize the loader.
:param path: The path to the command file.
:type path: str
:param context: Global context that may be used when to parse the command file, snippets, and templates. This is
converted to a ``dict`` when passed to a Snippet or Template.
:type context: scripttease.lib.contexts.Context
:param excluded_kwargs: For commands that support ad hoc sub-commands (like Django), this is a list of keyword
argument names that must be removed. Defaults to the names of common command attributes.
If your implementation requires custom but otherwise standard command attributes, you'll
need to import the ``EXCLUDED_KWARGS`` constant and add your attribute names before
passing it to the loader.
:type excluded_kwargs: list[str]
:param locations: A list of paths where templates and other external files may be found. The ``templates/``
directory in which the command file exists is added automatically.
:type locations: list[str]
:param mappings: A mapping of canonical command names and their snippets, organized by ``profile``. The profile
is typically an operating system such as ``centos`` or ``ubuntu``.
:type mappings: dict
:param profile: The profile (operating system or platform) to be used.
:type profile: str
kwargs are stored as ``options`` and may include any of the common options for command configuration. These may
be supplied as defaults for snippet processing.
"""
self.commands = list()
self.context = context
self.excluded_kwargs = excluded_kwargs or EXCLUDED_KWARGS
self.is_loaded = False
self.locations = locations or list()
self.mappings = mappings or MAPPINGS
self.options = kwargs
self.profile = profile
self.snippets = list()
super().__init__(path)
# Always include the path to the current file in locations.
self.locations.insert(0, os.path.join(self.directory, "templates"))
def get_context(self):
"""Get the context for parsing command files.
:rtype: dict
"""
d = self.options.copy()
if self.context is not None:
d.update(self.context.mapping().copy())
return d
def load(self):
"""Load the command file.
:rtype: bool
"""
raise NotImplementedError()
def read_file(self):
"""Get the content of the command file.
:rtype: str | None
"""
if self.context is not None:
try:
return parse_jinja_template(self.path, self.get_context())
except Exception as e:
log.error("Failed to process %s file as template: %s" % (self.path, e))
return None
return read_file(self.path)
# noinspection PyMethodMayBeStatic
def _get_key_value(self, key, value):
"""Process a key/value pair.
:param key: The key to be processed.
:type key: str
:param value: The value to be processed.
:rtype: tuple
:returns: The key and value, both of which may be modified from the originals.
This handles special names in the following manner:
- ``environments``, ``environs``, ``envs``, and ``env`` are treated as a CSV list of environment names
if provided as a string. These are normalized to the keyword ``environments``.
- ``func`` and ``function`` are normalized to the keyword ``function``. The value is the name of the function to
be defined.
- ``groups`` is assumed to be a CSV list of groups if provided as a string.
- ``items`` is assumed to be a CSV list if provided as a string. These are used to create an "itemized" command.
- ``tags`` is assumed to be a CSV list oif provided as a string.
All other keys are used as is. Values provided as a CSV list are smart cast to a Python value.
"""
if key in ("environments", "environs", "envs", "env"):
_key = "environments"
if type(value) in (list, tuple):
_value = value
else:
_value = split_csv(value)
elif key in ("func", "function"):
_key = "function"
_value = value
elif key == "groups":
_key = "groups"
if type(value) in (list, tuple):
_value = value
else:
_value = split_csv(value)
elif key == "items":
_key = "items"
if type(value) in (list, tuple):
_value = value
else:
_value = split_csv(value)
elif key == "tags":
_key = "tags"
if type(value) in (list, tuple):
_value = value
else:
_value = split_csv(value)
else:
_key = key
_value = smart_cast(value)
return _key, _value