Skip to content

Commit

Permalink
Feature!: Ninja Command Macro (#21)
Browse files Browse the repository at this point in the history
* feat!: command macro

Remove implicit removal of line breaks.
Add macro to do so explicitly.
Execute commands line by line.
Remove need for command seperator `&&`.

* docs(README): add explanation for new feature

* tests!: new feature

Change tests to support new feature.
Change tests to support altered return values.
Remove test for function which was removed.
  • Loading branch information
WyvernIXTL authored Sep 6, 2024
1 parent d068891 commit 66e6432
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 96 deletions.
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ properties:
- pipx
- pip
----------------------------------
{% if method == "pip" %}
python -m pip
{% else %}
pipx
{% endif %}
install installation-instruction
{% call command() %}
{% if method == "pip" %}
python -m pip
{% else %}
pipx
{% endif %}
install installation-instruction
{% endcall %}
```


Expand Down Expand Up @@ -122,8 +124,8 @@ __os__:

### Template

* You can have as much whitespace and line breaks as you wish in and inbetween your commands.
* Commands must be seperated by `&&`! (`pip install installation-instruction && pip uninstall installation-instruction`.)
* Non empty lines are executed one by one.
* Wrapping a command with `{% call command() %}` and `{% endcall %}` essentially removes all line breaks for convenience.
* If you wish to stop the render from within the template you can use the macro `raise`. (`{{ raise("no support!") }}`.)


Expand Down
6 changes: 5 additions & 1 deletion examples/pytorch/pytorch-instruction.schema.yml.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ pretty:

------

{# `command` is macro which simply removes line breaks. #}
{% call command() %}

{# Adams spaghetti code #}
{% if package == "conda" %}
conda install
Expand Down Expand Up @@ -122,4 +125,5 @@ pretty:
{% endif %}
{% endif %}
{% endif %}
{% endif %}
{% endif %}
{% endcall %}
16 changes: 8 additions & 8 deletions examples/scikit-learn/scikit-learn-instruction.schema.yml.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,23 @@ additionalProperties: false
------

{% if package == "conda" %}
conda create -n sklearn-env -c conda-forge scikit-learn &&
conda activate sklearn-env &&
conda create -n sklearn-env -c conda-forge scikit-learn
conda activate sklearn-env
{% else %}
{% if os == "Linux"%}
{% if virtualenv%}
python3 -m venv sklearn-venv &&
source sklearn-venv/bin/activate &&
python3 -m venv sklearn-venv
source sklearn-venv/bin/activate
{% endif %}
pip3 install -U scikit-learn
{% else %}
{% if virtualenv%}
{% if os == "macOS"%}
python -m venv sklearn-venv &&
source sklearn-venv/bin/activate &&
python -m venv sklearn-venv
source sklearn-venv/bin/activate
{% else %}
python -m venv sklearn-venv &&
sklearn-venv\Scripts\activate &&
python -m venv sklearn-venv
sklearn-venv\Scripts\activate
{% endif %}
{% endif %}
pip install -U scikit-learn
Expand Down
50 changes: 28 additions & 22 deletions examples/spacy/spacy-instruction.schema.yml.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,11 @@ pretty:
------

{% if package == "pip" %}
pip install -U pip setuptools wheel &&
pip install -U spacy
pip install -U pip setuptools wheel

{% call command() %}
pip install -U spacy

{% if hardware == "cpu" %}
{% if os == "mac"%}
{% if platform == "arm"%}
Expand All @@ -108,10 +110,11 @@ pretty:
{% if os == "linux"%}
[cuda111]
{% endif %}
{%if os == "mac"%}
{% if os == "mac" %}
[apple]
{%endif%}
{%endif%}
{% endif %}
{% endif %}
{% endcall %}
{%endif%}

{% if package == "conda" %}
Expand All @@ -122,26 +125,29 @@ pretty:
{%endif%}

{%if package == "source"%}
pip install -U pip setuptools wheel &&
git clone https://github.com/explosion/spaCy &&
cd spaCy &&
pip install -r requirements.txt &&
pip install --no-build-isolation --editable .
pip install -U pip setuptools wheel
git clone https://github.com/explosion/spaCy
cd spaCy
pip install -r requirements.txt

{% if hardware == "gpu"%}
{% if platform == "x86"%}
'[cuda111]'
{%endif%}
{% if platform == "arm"%}
{%if os == "windows"%}
'[]'
{%endif%}
{% if os == "linux"%}
{% call command() %}
pip install --no-build-isolation --editable .

{% if hardware == "gpu"%}
{% if platform == "x86"%}
'[cuda111]'
{%endif%}
{% if os == "mac"%}
'[apple]'
{% if platform == "arm"%}
{%if os == "windows"%}
'[]'
{%endif%}
{% if os == "linux"%}
'[cuda111]'
{%endif%}
{% if os == "mac"%}
'[apple]'
{%endif%}
{%endif%}
{%endif%}
{%endif%}
{% endcall %}
{%endif%}
30 changes: 13 additions & 17 deletions installation_instruction/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from .__init__ import __version__, __description__, __repository__, __author__, __author_email__, __license__
from .get_flags_and_options_from_schema import _get_flags_and_options
from .installation_instruction import InstallationInstruction
from .helpers import _make_pretty_print_line_breaks, _red_echo, _get_install_config_file
from .helpers import _red_echo, _get_install_config_file


VERSION_STRING = f"""Version: installation-instruction {__version__}
Expand Down Expand Up @@ -89,22 +89,20 @@ def get_command(self, ctx, config_file: str) -> click.Command|None:
def callback(**kwargs):
inst = instruction.validate_and_render(kwargs)
if inst[1]:
_red_echo("Error: " + inst[0])
_red_echo("Error: " + "\n".join(inst[0]))
exit(1)
if ctx.obj["MODE"] == "show":
if ctx.obj["RAW"]:
click.echo(inst[0])
else:
click.echo(_make_pretty_print_line_breaks(inst[0]))
click.echo("\n".join(inst[0]))
elif ctx.obj["MODE"] == "install":
result = run(inst[0], shell=True, text=True, capture_output=True)
if result.returncode != 0:
_red_echo("Installation failed with:\n" + str(result.stdout) + "\n" + str(result.stderr))
exit(1)
else:
if ctx.obj["INSTALL_VERBOSE"]:
click.echo(str(result.stdout))
click.echo(click.style("Installation successful.", fg="green"))
for command in inst[0]:
result = run(command, shell=True, text=True, capture_output=True)
if result.returncode != 0:
_red_echo("Installation failed with:\n" + command + "\n\n" + result.stdout + "\n" + result.stderr)
exit(1)
else:
if ctx.obj["INSTALL_VERBOSE"]:
click.echo(result.stdout.strip())
click.echo(click.style("Installation successful.", fg="green"))

exit(0)

Expand All @@ -124,11 +122,9 @@ def cat(path):
print(config_string)

@click.command(cls=ConfigReadCommand, help="Shows installation instructions for your specified config file and parameters.")
@click.option("--raw", is_flag=True, help="Show installation instructions without pretty print.", default=False)
@click.pass_context
def show(ctx, raw):
def show(ctx):
ctx.obj['MODE'] = "show"
ctx.obj['RAW'] = raw

@click.command(cls=ConfigReadCommand, help="Installs with config and parameters given.")
@click.option("-v", "--verbose", is_flag=True, help="Show verbose output.", default=False)
Expand Down
25 changes: 10 additions & 15 deletions installation_instruction/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,6 @@ def _get_install_config_file(path: str) -> tuple[TemporaryDirectory|None, str]:

return (temp_dir, config_file)

def _make_pretty_print_line_breaks(string: str) -> str:
"""
Replaces `&& ` with a newline character.
:param string: String to be processed.
:type string: str
:return: String with `&& ` replaced with newline character.
:rtype: str
"""
return re.sub(r"\s?&&\s?", "\n", string, 0, re.S)

def _get_error_message_from_string(string: str) -> str | None:
"""
Parses error message of error given by using jinja macro `RAISE_JINJA_MACRO_STRING`. If no error message is found returns `None`.
Expand All @@ -129,16 +118,22 @@ def _get_error_message_from_string(string: str) -> str | None:
return None
return matches.group("errmsg")

def _replace_whitespace_in_string(string: str) -> str:
def _replace_whitespace_in_string_and_split_it(string: str) -> list[str]:
"""
Replaces eol and whitespaces of a string with a single whitespace.
Replaces eol whitespaces of a string with a single whitespace or none.
:param string: String to be processed.
:type string: str
:return: String where whitespace and eol is replaced with one whitespace and whitspace before and after are stripped.
:return: String where whitespace is replaced with one whitespace and whitspace before and after are stripped.
:rtype: str
"""
return re.sub(r"\s{1,}", " ", string, 0, re.S).strip()
multiline_string = str.splitlines(string)
string_list = []
for s in multiline_string:
s = s.strip()
if s:
string_list += [re.sub(r"\s{2,}", " ", s, 0)]
return string_list

def _split_string_at_delimiter(string: str) -> tuple[str, str]:
"""
Expand Down
21 changes: 17 additions & 4 deletions installation_instruction/installation_instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,26 @@
{% endmacro %}
"""

COMMAND_JINJA_MACRO_STRING = """
{% macro command() %}
{% filter replace("\n", "") %}
{{ caller() }}
{% endfilter %}
{% endmacro %}
"""

MACROS = [
RAISE_JINJA_MACRO_STRING,
COMMAND_JINJA_MACRO_STRING,
]


class InstallationInstruction:
"""
Class holding schema and template for validating and rendering installation instruction.
"""

def validate_and_render(self, input: dict) -> tuple[str, bool]:
def validate_and_render(self, input: dict) -> tuple[list[str], bool]:
"""
Validates user input against schema and renders with the template.
Returns installation instructions and False.
Expand All @@ -51,11 +64,11 @@ def validate_and_render(self, input: dict) -> tuple[str, bool]:
instruction = self.template.render(input)
except UndefinedError as e:
if errmsg := helpers._get_error_message_from_string(str(e)):
return (errmsg, True)
return ([errmsg], True)
else:
raise e

instruction = helpers._replace_whitespace_in_string(instruction)
instruction = helpers._replace_whitespace_in_string_and_split_it(instruction)

return (instruction, False)

Expand Down Expand Up @@ -127,7 +140,7 @@ def __init__(self, config: str) -> None:
Draft202012Validator.check_schema(self.schema)
except exceptions.SchemaError as e:
raise Exception(f"The given schema file is not a valid json schema.\n\n{e}")
self.template = helpers._load_template_from_string(RAISE_JINJA_MACRO_STRING+template)
self.template = helpers._load_template_from_string("".join(MACROS) + template)


@classmethod
Expand Down
12 changes: 6 additions & 6 deletions tests/data/example_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"package": "pip",
"compute_platform": "ro60"
},
"expected_output": "Windows does not support ROCm!\nTrue",
"expected_message": ["Windows does not support ROCm!"],
"expected_error": true,
"schema_path":"examples/pytorch/pytorch-instruction.schema.yml.jinja"
},
{
Expand All @@ -17,7 +18,8 @@
"packager": "pip",
"virtualenv": false
},
"expected_output":"pip install -U scikit-learn\nFalse",
"expected_message": ["pip install -U scikit-learn"],
"expected_error": false,
"schema_path":"examples/scikit-learn/scikit-learn-instruction.schema.yml.jinja"
},
{
Expand All @@ -28,7 +30,8 @@
"package": "pip",
"hardware": "cpu"
},
"expected_output":"pip install -U pip setuptools wheel && pip install -U spacy\nFalse",
"expected_message": ["pip install -U pip setuptools wheel", "pip install -U spacy"],
"expected_error": false,
"schema_path":"examples/spacy/spacy-instruction.schema.yml.jinja"
},
{
Expand All @@ -39,7 +42,6 @@
"package": "piiip",
"compute_platform": "ro60"
},
"expected_output":null,
"schema_path":"examples/pytorch/pytorch-instruction.schema.yml.jinja"
},
{
Expand All @@ -49,7 +51,6 @@
"packager": "pip",
"virtualenv": false
},
"expected_output":null,
"schema_path":"examples/scikit-learn/scikit-learn-instruction.schema.yml.jinja"

},
Expand All @@ -61,7 +62,6 @@
"package": "forge",
"hardware": "cpu"
},
"expected_output":null,
"schema_path":"examples/spacy/spacy-instruction.schema.yml.jinja"
}
]
10 changes: 5 additions & 5 deletions tests/data/test_install/install.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ required:
- error_install
- error_template
------
echo "start" &&
{%- if error_install %}
echo "start"
{% if error_install %}
abcdefghijklmnop
{%- endif %}
{%- if error_template %}
{% endif %}
{% if error_template %}
{{ raise("error error error") }}
{%- endif %}
{% endif %}
echo "end"
Loading

0 comments on commit 66e6432

Please sign in to comment.