Commit ca37afb6 authored by nimrod's avatar nimrod
Browse files

Small refactor and cleanup.

- Change the `run` filter to be a function. Usage should be nicer now:
  `{{ run(['id', '-u'])['stdout'] }}`. Keep the filter for a few
releases to avoid breakage.
- A few filters were missing from the README, add the documentation.
parent 58970d36
Loading
Loading
Loading
Loading
Loading
+16 −2
Original line number Diff line number Diff line
@@ -81,6 +81,8 @@ Jinja filters

The following Jinja filters were added:

- :code:`combine`: Merges 2 dictionaries with the 2nd overriding the 1st.
  Returns the result.
- :code:`to_yaml`: Convert to yaml (requires the :code:`yaml` package
  specifier).
- :code:`from_yaml`: Convert from yaml (requires the :code:`yaml` package
@@ -94,8 +96,8 @@ The following Jinja filters were added:
- :code:`jmespath`: Queries data using the `JMESPath <http://jmespath.org/>`_
  query language (requires the :code:`jmespath` package specifier).
- :code:`run`: Runs a command and returns the stdout, stderr and returncode
  using `run
  <https://docs.python.org/3.6/library/subprocess.html?highlight=popen#subprocess.run>`_.
  using run_. This filter is replaced with the :code:`run` function and will
  be removed in the 0.10 release.
- :code:`ipaddress`: Returns an IPAddress object from the netaddr_ library
  (requires the :code:`netaddr` package specifier).
- :code:`ipnetwork`: Returns an IPNetwork object from the netaddr_ library
@@ -104,10 +106,21 @@ The following Jinja filters were added:
  (requires the :code:`netaddr` package specifier).
- :code:`ipglob`: Returns an IPGlob object from the netaddr_ library (requires
  the :code:`netaddr` package specifier).
- :code:`ipset`: Returns an IPSet object from the netaddr_ library (requires
  the :code:`netaddr` package specifier).

Example usage can be seen in :code:`tests` and for specific filters in the
docstrings in :code:`template/filters.py`.

Jinja functions
---------------

- :code:`run`: Runs a command and returns the stdout, stderr and returncode
  using run_. This function replaces the :code:`run` filter.

Example usage can be seen in :code:`tests` and for specific filters in the
docstrings in :code:`template/functions.py`.

Testing
-------

@@ -152,3 +165,4 @@ at: https://git.shore.co.il/nimrod/.

.. _netaddr: https://netaddr.readthedocs.io/
.. _Pipenv: https://docs.pipenv.org
.. _run: https://docs.python.org/3.6/library/subprocess.html?highlight=popen#subprocess.run
+12 −2
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ from os import environ
import sys
import argparse
import template.filters
import template.functions

# I ignore import errors here and fail on them later in the main function so
# the module can be imported by the setup.py with jinja missing so the
@@ -31,9 +32,18 @@ def render(template_string):
    env = Environment(autoescape=True)
    # Add all functions in template.filters as Jinja filters.
    # pylint: disable=invalid-name
    for tf in filter(lambda x: not x.startswith("_"), dir(template.filters)):
    for tf in filter(
        lambda x: callable(getattr(template.filters, x))
        and not x.startswith("_"),
        dir(template.filters),
    ):
        env.filters[tf] = getattr(template.filters, tf)
    t = env.from_string(template_string)
    functions = {
        x: getattr(template.functions, x)
        for x in dir(template.functions)
        if callable(getattr(template.functions, x)) and not x.startswith("_")
    }
    t = env.from_string(template_string, globals=functions)
    return t.render(environ)


+2 −30
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@ from __future__ import (
    unicode_literals,
)  # pylint: disable=duplicate-code

from template.functions import run  # noqa: F401 pylint: disable=unused-import


def to_yaml(value):
    r"""
@@ -145,36 +147,6 @@ def jmespath(value, query):
    return jp.search(query, value)


def run(*argv, **kwargs):
    """
    Runs a command and returns the stdout, stderr and returncode
    using `run
    <https://docs.python.org/3.5/library/subprocess.html?highlight=popen#subprocess.run>`_.
    >>> run('ls')["returncode"] == 0
    True
    >>> 'SHELL' not in run('echo $SHELL', shell=True)['stdout']
    True
    >>> run(['ls', 'foo'])['returncode'] > 0
    True
    """
    import sys

    if sys.version_info[0] < 3:  # nosec
        import subprocess32 as subprocess
    else:
        import subprocess  # nosemgrep: rules.bandit.B40

    defaults = {"stdout": subprocess.PIPE, "stderr": subprocess.PIPE}
    defaults.update(kwargs)
    proc = subprocess.run(  # nosec, pylint: disable=subprocess-run-check
        *argv, **defaults
    ).__dict__
    if "text" not in kwargs or kwargs["text"]:
        proc["stdout"] = proc["stdout"].decode()
        proc["stderr"] = proc["stderr"].decode()
    return proc


def ipaddress(addr, version=None, flags=0):
    """
    Returns an IPAddress object from the netaddr library.

template/functions.py

0 → 100644
+41 −0
Original line number Diff line number Diff line
#!/usr/bin/env python
"""Filters for the template CLI."""
# pylint: disable=import-error, import-outside-toplevel


from __future__ import (
    absolute_import,
    division,
    print_function,
    unicode_literals,
)  # pylint: disable=duplicate-code


def run(*argv, **kwargs):
    """
    Runs a command and returns the stdout, stderr and returncode
    using `run
    <https://docs.python.org/3.5/library/subprocess.html?highlight=popen#subprocess.run>`_.
    >>> run('ls')["returncode"] == 0
    True
    >>> 'SHELL' not in run('echo $SHELL', shell=True)['stdout']
    True
    >>> run(['ls', 'foo'])['returncode'] > 0
    True
    """
    import sys

    if sys.version_info[0] < 3:  # nosec
        import subprocess32 as subprocess
    else:
        import subprocess  # nosemgrep: rules.bandit.B40

    defaults = {"stdout": subprocess.PIPE, "stderr": subprocess.PIPE}
    defaults.update(kwargs)
    proc = subprocess.run(  # nosec, pylint: disable=subprocess-run-check
        *argv, **defaults
    ).__dict__
    if "text" not in kwargs or kwargs["text"]:
        proc["stdout"] = proc["stdout"].decode()
        proc["stderr"] = proc["stderr"].decode()
    return proc