From 7ecdb3c1f3a4ae5556d8fc4401867c0c1e9856b5 Mon Sep 17 00:00:00 2001 From: Shawn Davis Date: Tue, 1 Feb 2022 21:46:38 -0600 Subject: [PATCH] Worked on new docs/tutorial output. --- sandbox/cli.py | 111 ++++++++++++++-- scripttease/data/inventory/radicale/steps.ini | 1 + scripttease/lib/contexts.py | 52 +------- scripttease/lib/loaders/__init__.py | 5 +- scripttease/lib/loaders/base.py | 122 +++++++++++++++++- scripttease/lib/snippets/messages.py | 18 +-- 6 files changed, 232 insertions(+), 77 deletions(-) diff --git a/sandbox/cli.py b/sandbox/cli.py index 81054fb..79ca964 100755 --- a/sandbox/cli.py +++ b/sandbox/cli.py @@ -1,17 +1,17 @@ #! /usr/bin/env python from argparse import ArgumentParser, RawDescriptionHelpFormatter -from commonkit import highlight_code, smart_cast +from commonkit import highlight_code, indent, smart_cast from commonkit.logging import LoggingHelper from commonkit.shell import EXIT +from markdown import markdown import sys sys.path.insert(0, "../") from scripttease.constants import LOGGER_NAME -from scripttease.lib.contexts import load_variables, Context -from scripttease.lib.loaders.ini import INILoader -from scripttease.lib.loaders.yaml import YMLLoader +from scripttease.lib.contexts import Context +from scripttease.lib.loaders import load_variables, INILoader, YMLLoader from scripttease.version import DATE as VERSION_DATE, VERSION DEBUG = 10 @@ -59,10 +59,11 @@ This command is used to parse configuration files and output the commands. ) parser.add_argument( - "-d", - "--docs", - action="store_true", - dest="docs_enabled", + "-d=", + "--docs=", + choices=["html", "markdown", "plain", "rst"], + # default="markdown", + dest="docs", help="Output documentation instead of code." ) @@ -172,7 +173,7 @@ This command is used to parse configuration files and output the commands. filters[key].append(value) - # Handle options. + # Handle global command options. options = dict() if args.options: for token in args.options: @@ -208,8 +209,96 @@ This command is used to parse configuration files and output the commands. exit(EXIT.ERROR) # Generate output. - if args.docs_enabled: - pass + if args.docs: + output = list() + for snippet in loader.get_snippets(): + if snippet is None: + continue + + if snippet.name == "explain": + output.append(snippet.args[0]) + output.append("") + elif snippet.name == "screenshot": + if args.docs == "html": + b = list() + b.append('") + elif args.docs == "plain": + output.append(snippet.args[0]) + output.append("") + elif args.docs == "rst": + output.append(".. figure:: %s" % snippet.args[0]) + + if snippet.caption: + output.append(indent(":alt: %s" % snippet.caption)) + + if snippet.height: + output.append(indent(":height: %s" % snippet.height)) + + if snippet.width: + output.append(indent(":width: %s" % snippet.width)) + + output.append("") + else: + if snippet.caption: + output.append("![%s](%s)" % (snippet.caption, snippet.args[0])) + else: + output.append("![](%s)" % (snippet.args[0])) + + output.append("") + elif snippet.name == "template": + if args.docs == "plain": + output.append("+++") + output.append(snippet.get_content()) + output.append("+++") + elif args.docs == "rst": + output.append(".. code-block:: %s" % snippet.get_target_language()) + output.append("") + output.append(indent(snippet.get_content())) + output.append("") + else: + output.append("```%s" % snippet.get_target_language()) + output.append(snippet.get_content()) + output.append("```") + else: + statement = snippet.get_statement(include_comment=False, include_register=False, include_stop=False) + if statement is not None: + line = snippet.comment.replace("#", "") + output.append("%s:" % line.capitalize()) + output.append("") + if args.docs == "plain": + output.append("---") + output.append(statement) + output.append("---") + output.append("") + elif args.docs == "rst": + output.append(".. code-block:: bash") + output.append("") + output.append(indent(statement)) + output.append("") + else: + output.append("```bash") + output.append(statement) + output.append("```") + output.append("") + + if args.docs == "html": + print(markdown("\n".join(output), extensions=['fenced_code'])) + else: + print("\n".join(output)) else: commands = list() for snippet in loader.get_snippets(): diff --git a/scripttease/data/inventory/radicale/steps.ini b/scripttease/data/inventory/radicale/steps.ini index d4f5e2e..bd23eb8 100644 --- a/scripttease/data/inventory/radicale/steps.ini +++ b/scripttease/data/inventory/radicale/steps.ini @@ -24,6 +24,7 @@ system: yes [create the systemd service file for radicale] template: radicale.service /etc/systemd/system/radicale.service +lang: ini [start the radicale service] start: radicale diff --git a/scripttease/lib/contexts.py b/scripttease/lib/contexts.py index ebc8e5c..632d2c7 100644 --- a/scripttease/lib/contexts.py +++ b/scripttease/lib/contexts.py @@ -1,58 +1,16 @@ # Imports -from commonkit import smart_cast -from configparser import ParsingError, RawConfigParser import logging -import os log = logging.getLogger(__name__) # Exports __all__ = ( - "load_variables", "Context", "Variable", ) -# Functions - - -def load_variables(path): - """Load variables from an INI file. - - :param path: The path to the INI file. - :type path: 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(): - _value = None - kwargs = dict() - 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)) - - return variables - # Classes @@ -108,11 +66,10 @@ class Context(object): return d - class Variable(object): """An individual variable.""" - def __init__(self, name, value, **kwargs): + def __init__(self, name, value, environment=None, **kwargs): """Initialize a variable. :param name: The name of the variable. @@ -120,13 +77,18 @@ class Variable(object): :param value: The value of the variable. + :param environment: The environment in which the variable is used. + :type environment: str + kwargs are available as dynamic attributes. """ - self.attributes = kwargs + self.environment = environment self.name = name self.value = value + self.attributes = kwargs + def __getattr__(self, item): return self.attributes.get(item) diff --git a/scripttease/lib/loaders/__init__.py b/scripttease/lib/loaders/__init__.py index 4a0d831..527b4ef 100644 --- a/scripttease/lib/loaders/__init__.py +++ b/scripttease/lib/loaders/__init__.py @@ -1,3 +1,6 @@ """ The job of a loader is to collect commands and their arguments from a text file. -""" \ No newline at end of file +""" +from .base import filter_snippets, load_variables +from .ini import INILoader +from .yaml import YMLLoader diff --git a/scripttease/lib/loaders/base.py b/scripttease/lib/loaders/base.py index 7335b5f..96661b2 100644 --- a/scripttease/lib/loaders/base.py +++ b/scripttease/lib/loaders/base.py @@ -1,21 +1,111 @@ # Imports -from commonkit import parse_jinja_string, parse_jinja_template, pick, read_file, smart_cast, split_csv, File +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 ..contexts import Variable from ..snippets.mappings import MAPPINGS log = logging.getLogger(__name__) # Exports + __all__ = ( + "filter_snippets", + "load_variables", "BaseLoader", "Snippet", "Sudo", "Template", ) +# Functions + + +def filter_snippets(snippets, environments=None, tags=None): + """Filter snippets based on the given criteria. + + :param snippets: The snippets to be filtered. + :type snippets: list[scripttease.lib.loaders.base.Snippet] + + :param environments: Environment names to be matched. + :type environments: list[str] + + :param tags: Tag names to be matched. + :type tags: list[str] + + """ + filtered = list() + for snippet in snippets: + 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 @@ -305,6 +395,9 @@ class Snippet(object): self.kwargs = kwargs or dict() self.name = name + self.environments = kwargs.pop("environments", list()) + self.tags = kwargs.pop("tags", list()) + sudo = self.kwargs.pop("sudo", None) if isinstance(sudo, Sudo): self.sudo = sudo @@ -509,14 +602,14 @@ class Template(object): def __init__(self, source, target, backup=True, parser=PARSER_JINJA, **kwargs): self.backup_enabled = backup self.context = kwargs.pop("context", dict()) - self.kwargs = kwargs - + self.name = "template" self.parser = parser + self.language = kwargs.pop("lang", None) self.locations = kwargs.pop("locations", list()) self.source = os.path.expanduser(source) self.target = target - sudo = self.kwargs.pop("sudo", None) + sudo = kwargs.pop("sudo", None) if isinstance(sudo, Sudo): self.sudo = sudo elif type(sudo) is str: @@ -526,6 +619,8 @@ class Template(object): else: self.sudo = Sudo() + self.kwargs = kwargs + def __getattr__(self, item): return self.kwargs.get(item) @@ -603,6 +698,25 @@ class Template(object): return "\n".join(lines) + def get_target_language(self): + if self.language is not None: + return self.language + + if self.target.endswith(".conf"): + return "conf" + elif self.target.endswith(".ini"): + return "ini" + elif self.target.endswith(".php"): + return "php" + elif self.target.endswith(".py"): + return "python" + elif self.target.endswith(".sh"): + return "bash" + elif self.target.endswith(".yml"): + return "yaml" + else: + return "text" + def get_template(self): """Get the template path. diff --git a/scripttease/lib/snippets/messages.py b/scripttease/lib/snippets/messages.py index 861979b..f01447a 100644 --- a/scripttease/lib/snippets/messages.py +++ b/scripttease/lib/snippets/messages.py @@ -7,22 +7,8 @@ messages = { 'clear;' ], 'echo': 'echo "{{ args[0] }}"', - 'explain': "{{ args[0] }} ", - 'screenshot': [ - '{% if output == "md" %}', - "![{% if caption %}{{ caption }}]({{ args[0] }})", - '{% elif output == "rst" %}', - '.. figure:: {{ args[0] }}', - '{% if caption %}\n :alt: {{ caption }}{% endif %}', - '{% if height %}\n :height: {{ height }}{% endif %}', - '{% if width %}\n :width: {{ width }}{% endif %}' - '\n', - '{% else %}', - '{{ caption }}{% endif %}'
-        '{% if classes %} class={{ classes }}{% endif %}'
-        '{% if height %} height=', - '{% endif %}', - ], + 'explain': None, + 'screenshot': None, 'slack': [ "curl -X POST -H 'Content-type: application/json' --data", '{"text": "{{ args[0] }}"}',