Skip to content

Commit

Permalink
Bump version + zntrack init (#472)
Browse files Browse the repository at this point in the history
* ZnTrack v0.5.0

* add CLI from #301

* poetry update

* add cli test
  • Loading branch information
PythonFZ authored Dec 20, 2022
1 parent d0adc8e commit 8399902
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 79 deletions.
148 changes: 72 additions & 76 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "ZnTrack"
version = "0.4.5"
version = "0.5.0"
description = "Create, Run and Benchmark DVC Pipelines in Python"
authors = ["zincwarecode <zincwarecode@gmail.com>"]
license = "Apache-2.0"
Expand Down
108 changes: 108 additions & 0 deletions tests/integration_tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import os
import pathlib

import pytest
from typer.testing import CliRunner

from zntrack import Node, NodeConfig, nodify, zn
from zntrack.cli import app


@pytest.fixture()
def runner() -> CliRunner:
return CliRunner()


def test_version(runner):
result = runner.invoke(app, ["--version"])
assert "ZnTrack " in result.stdout # ZnTrack v.x.y


def test_init(tmp_path, runner):
os.chdir(tmp_path)
result = runner.invoke(app, ["init"])
assert "Creating new project" in result.stdout

assert pathlib.Path("src").is_dir()
assert (pathlib.Path("src") / "__init__.py").is_file()
assert pathlib.Path(".git").is_dir()
assert pathlib.Path(".dvc").is_dir()
assert pathlib.Path("main.py").is_file()
assert pathlib.Path("README.md").is_file()


def test_init_gitignore(tmp_path, runner):
os.chdir(tmp_path)
_ = runner.invoke(app, ["init", "--gitignore"])

assert pathlib.Path("src").is_dir()
assert (pathlib.Path("src") / "__init__.py").is_file()
assert pathlib.Path(".git").is_dir()
assert pathlib.Path(".dvc").is_dir()
assert pathlib.Path(".gitignore").is_file()
assert pathlib.Path("main.py").is_file()
assert pathlib.Path("README.md").is_file()


@pytest.mark.parametrize("force", (True, False))
def test_init_force(tmp_path, runner, force):
os.chdir(tmp_path)
pathlib.Path("file.txt").touch()

if force:
_ = runner.invoke(app, ["init", "--force"])
assert pathlib.Path("src").is_dir()
assert (pathlib.Path("src") / "__init__.py").is_file()
assert pathlib.Path(".git").is_dir()
assert pathlib.Path(".dvc").is_dir()
assert pathlib.Path("main.py").is_file()
assert pathlib.Path("README.md").is_file()
else:
_ = runner.invoke(app, ["init"])
assert not pathlib.Path("src").exists()
assert not pathlib.Path(".git").exists()
assert not pathlib.Path(".dvc").exists()
assert not pathlib.Path("main.py").exists()
assert not pathlib.Path("README.md").exists()


class InputsToOutputs(Node):
inputs = zn.params()
outputs = zn.outs()

def run(self):
self.outputs = self.inputs


@nodify(outs="test.txt", params={"text": "Lorem Ipsum"})
def example_func(cfg: NodeConfig) -> NodeConfig:
out_file = pathlib.Path(cfg.outs)
out_file.write_text(cfg.params.text)
return cfg


def test_run(proj_path, runner):
node = InputsToOutputs(inputs=15)
node.write_graph()
result = runner.invoke(app, ["run", "test_cli.InputsToOutputs"])
assert result.exit_code == 0

node = InputsToOutputs.load()
assert node.outputs == 15


def test_run_nodify(proj_path, runner):
example_func()
result = runner.invoke(app, ["run", "test_cli.example_func"])
assert result.exit_code == 0
assert pathlib.Path("test.txt").read_text() == "Lorem Ipsum"


def test_run_w_name(proj_path, runner):
node = InputsToOutputs(inputs=15, name="TestNode")
node.write_graph()
result = runner.invoke(app, ["run", "test_cli.InputsToOutputs", "--name", "TestNode"])
assert result.exit_code == 0

node = InputsToOutputs["TestNode"]
assert node.outputs == 15
2 changes: 1 addition & 1 deletion tests/test_zntrack.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

def test_version():
"""Test 'ZnTrack' version."""
assert __version__ == "0.4.5"
assert __version__ == "0.5.0"
17 changes: 17 additions & 0 deletions zntrack/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

import typer

from zntrack import utils

app = typer.Typer()


Expand Down Expand Up @@ -46,3 +48,18 @@ def run(node: str, name: str = None, hash_only: bool = False) -> None:
except AttributeError:
# @nodify function
cls(exec_func=True)


@app.command()
def init(
name: str = "New Project",
gitignore: bool = typer.Option(
False, help="Create a gitignore file with all the usual files excluded."
),
force: bool = typer.Option(
False, "--force", "-f", help="Initialize Project if the directory is not empty."
),
):
"""Initialize a new ZnTrack Project."""
initializer = utils.cli.Initializer(name=name, gitignore=gitignore, force=force)
initializer.run()
3 changes: 2 additions & 1 deletion zntrack/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Standard python init file for the utils directory."""

from zntrack.utils import exceptions, file_io, helpers
from zntrack.utils import cli, exceptions, file_io, helpers
from zntrack.utils.config import Files, config
from zntrack.utils.nwd import move_nwd, nwd
from zntrack.utils.structs import (
Expand Down Expand Up @@ -48,4 +48,5 @@
"GIT_TRACKED",
"update_gitignore",
"move_nwd",
"cli",
]
72 changes: 72 additions & 0 deletions zntrack/utils/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""CLI Helpers."""
import dataclasses
import pathlib
import subprocess
import urllib.request

import typer


@dataclasses.dataclass
class Initializer:
"""Initialize a new ZnTrack Project."""

name: str
gitignore: bool
force: bool
src: pathlib.Path = pathlib.Path("src")

_gitignore_url: str = (
"https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore"
)

def run(self):
"""Run the initializer."""
self.check_empty()
typer.echo(f"Creating new project: {self.name}")
self.make_src()
self.make_repo()
self.write_main_file()
self.write_readme()
if self.gitignore:
self.write_gitignore()

def check_empty(self):
"""Check if the project directory is empty.
Raises
------
typer.Exit: if the directory is not empty and force is false
"""
is_empty = not any(pathlib.Path(".").iterdir())
if not is_empty and not self.force:
typer.echo(
"The current working directory is not empty. If you want to initialize a"
" project anyway use 'zntrack init --force'."
)
raise typer.Exit()

def make_src(self):
"""Create a src directory that contains the Node configurations."""
self.src.mkdir()
init_file = self.src / "__init__.py"
init_file.write_text(r'"""ZnTrack Node module."""')

def write_gitignore(self):
"""Create a gitignore file based on the Python gitignore template."""
gitignore = pathlib.Path(".gitignore")
with urllib.request.urlopen(self._gitignore_url) as url:
gitignore.write_text(url.read().decode("utf-8"))

def write_main_file(self):
"""Create a 'main.py' file."""
pathlib.Path("main.py").touch()

def write_readme(self):
"""Create a README.md file."""
pathlib.Path("README.md").write_text(f"# Welcome to {self.name} \n")

def make_repo(self):
"""Initialize the repository."""
subprocess.check_call(["git", "init"])
subprocess.check_call(["dvc", "init"])

6 comments on commit 8399902

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Report for Python 3.8

name min max mean stddev median iqr outliers ops rounds iterations
0 tests/benchmark/test_benchmark.py::test_InputOutput_write_graph 8.94e-05 0.000555502 9.2805e-05 1.04687e-05 9.17e-05 2.5e-06 24;117 10775.3 2145 1
1 tests/benchmark/test_benchmark.py::test_InputOutput_load 0.0001025 0.000198401 0.000108517 3.50853e-06 0.000108101 7.7525e-07 64;99 9215.13 1223 1
2 tests/benchmark/test_benchmark.py::test_NodeCollector_load 0.0001066 0.00704593 0.000121516 0.000238998 0.000111201 8.99e-07 2;112 8229.35 1199 1
3 tests/benchmark/test_benchmark.py::test_InputOutput_load_lazy 0.0001071 0.000221901 0.000109834 3.87952e-06 0.0001094 8e-07 42;79 9104.63 1287 1
4 tests/benchmark/test_benchmark.py::test_NodeCollector_load_lazy 0.0001083 0.00865174 0.000125892 0.000267819 0.000113801 9e-07 2;198 7943.33 1242 1
5 tests/benchmark/test_benchmark.py::test_NodeCollector_run_and_save 0.000380502 0.00250351 0.000443182 0.000282397 0.000396652 2.91e-05 1;4 2256.41 56 1
6 tests/benchmark/test_benchmark.py::test_InputOutput_run_and_save 0.00161201 0.00297391 0.00174552 0.00014013 0.00172666 4.355e-05 13;64 572.894 284 1
7 tests/benchmark/test_benchmark.py::test_NodeCollector_write_graph 0.138725 0.315732 0.221702 0.0584242 0.219806 0.041257 2;1 4.51056 6 1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Report for Python 3.10

name min max mean stddev median iqr outliers ops rounds iterations
0 tests/benchmark/test_benchmark.py::test_InputOutput_write_graph 8.9e-05 0.000534304 9.19687e-05 9.98197e-06 9.0801e-05 1.801e-06 35;146 10873.3 2240 1
1 tests/benchmark/test_benchmark.py::test_InputOutput_load 9.58e-05 0.000153402 9.89337e-05 3.67464e-06 9.8201e-05 1.2e-06 57;89 10107.8 1147 1
2 tests/benchmark/test_benchmark.py::test_InputOutput_load_lazy 9.65e-05 0.00526614 0.000106064 0.000161283 9.9301e-05 2.201e-06 3;57 9428.25 1035 1
3 tests/benchmark/test_benchmark.py::test_NodeCollector_load 9.9301e-05 0.000202902 0.000101345 3.94469e-06 0.000100801 9e-07 38;74 9867.24 1067 1
4 tests/benchmark/test_benchmark.py::test_NodeCollector_load_lazy 0.000100101 0.000204102 0.000102043 4.01643e-06 0.000101501 7e-07 45;92 9799.82 1079 1
5 tests/benchmark/test_benchmark.py::test_NodeCollector_run_and_save 0.000365003 0.00910627 0.000681143 0.00127565 0.000408803 3.625e-05 2;10 1468.12 53 1
6 tests/benchmark/test_benchmark.py::test_InputOutput_run_and_save 0.00153951 0.12358 0.00221617 0.00732601 0.00165666 4.42005e-05 2;67 451.23 308 1
7 tests/benchmark/test_benchmark.py::test_NodeCollector_write_graph 0.123457 0.457886 0.226689 0.132883 0.174156 0.11574 1;1 4.41134 5 1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Report for Python 3.9

name min max mean stddev median iqr outliers ops rounds iterations
0 tests/benchmark/test_benchmark.py::test_InputOutput_write_graph 8.6999e-05 0.00151049 9.11817e-05 2.95532e-05 8.96e-05 1.7e-06 8;355 10967.1 2341 1
1 tests/benchmark/test_benchmark.py::test_InputOutput_load 9.7899e-05 0.000122899 0.000100452 1.87302e-06 0.000100099 1.1e-06 91;79 9955.04 1552 1
2 tests/benchmark/test_benchmark.py::test_NodeCollector_load_lazy 9.9699e-05 0.000122999 0.000102176 1.85217e-06 0.000101899 1.099e-06 80;66 9787.08 1441 1
3 tests/benchmark/test_benchmark.py::test_InputOutput_load_lazy 9.9799e-05 0.000187198 0.000102047 3.02994e-06 0.000101599 1.1e-06 62;91 9799.43 1499 1
4 tests/benchmark/test_benchmark.py::test_NodeCollector_load 0.000101399 0.00765904 0.000111007 0.000214425 0.000103199 1e-06 2;91 9008.45 1463 1
5 tests/benchmark/test_benchmark.py::test_NodeCollector_run_and_save 0.000342097 0.00434496 0.000560364 0.000675343 0.000364997 0.000125474 2;5 1784.56 41 1
6 tests/benchmark/test_benchmark.py::test_InputOutput_run_and_save 0.00139449 0.00220578 0.00149531 8.13788e-05 0.00148699 8.6399e-05 69;13 668.758 365 1
7 tests/benchmark/test_benchmark.py::test_NodeCollector_write_graph 0.178219 0.319447 0.240725 0.0485825 0.225707 0.0479557 2;0 4.15411 6 1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Report for Python 3.8

name min max mean stddev median iqr outliers ops rounds iterations
0 tests/benchmark/test_benchmark.py::test_InputOutput_write_graph 8.6999e-05 0.000504494 9.05512e-05 8.75326e-06 8.9899e-05 1.4e-06 35;154 11043.5 2406 1
1 tests/benchmark/test_benchmark.py::test_InputOutput_load_lazy 9.5499e-05 0.00441624 0.000103056 0.000116124 9.8098e-05 1.299e-06 4;113 9703.46 1469 1
2 tests/benchmark/test_benchmark.py::test_InputOutput_load 9.5798e-05 0.000141698 9.87119e-05 2.78633e-06 9.8199e-05 1.7e-06 82;69 10130.5 1447 1
3 tests/benchmark/test_benchmark.py::test_NodeCollector_load 9.7598e-05 0.00422384 0.00010455 0.00011621 0.000100498 1.701e-06 2;65 9564.78 1262 1
4 tests/benchmark/test_benchmark.py::test_NodeCollector_load_lazy 9.8499e-05 0.0108067 0.000110417 0.000292576 0.000100899 1.3505e-06 2;75 9056.6 1364 1
5 tests/benchmark/test_benchmark.py::test_NodeCollector_run_and_save 0.000348895 0.00829889 0.000885011 0.00150055 0.000429394 0.000277322 5;9 1129.93 83 1
6 tests/benchmark/test_benchmark.py::test_InputOutput_run_and_save 0.00148188 0.00337995 0.00161478 0.000170526 0.00156978 0.000100498 21;20 619.28 347 1
7 tests/benchmark/test_benchmark.py::test_NodeCollector_write_graph 0.159702 0.296448 0.219168 0.0473954 0.219859 0.05401 2;0 4.56271 6 1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Report for Python 3.9

name min max mean stddev median iqr outliers ops rounds iterations
0 tests/benchmark/test_benchmark.py::test_InputOutput_write_graph 9.4401e-05 0.00319393 0.000129727 8.97096e-05 0.000115001 2.72753e-05 53;144 7708.52 2451 1
1 tests/benchmark/test_benchmark.py::test_NodeCollector_load_lazy 0.000103601 0.00134091 0.000135897 5.00554e-05 0.000125951 2.81505e-05 62;62 7358.51 1228 1
2 tests/benchmark/test_benchmark.py::test_InputOutput_load_lazy 0.000105101 0.00823407 0.000148222 0.000335105 0.000123901 2.445e-05 3;59 6746.63 1093 1
3 tests/benchmark/test_benchmark.py::test_InputOutput_load 0.000108701 0.00280672 0.00013759 0.000103328 0.000124251 2.61005e-05 9;41 7267.95 880 1
4 tests/benchmark/test_benchmark.py::test_NodeCollector_load 0.000111701 0.00140901 0.000140891 7.12234e-05 0.000123551 2.56e-05 32;72 7097.68 930 1
5 tests/benchmark/test_benchmark.py::test_NodeCollector_run_and_save 0.000503105 0.0104144 0.00135163 0.00232033 0.000562255 0.000122101 4;5 739.848 34 1
6 tests/benchmark/test_benchmark.py::test_InputOutput_run_and_save 0.00211142 0.0113733 0.00261191 0.000826061 0.00247647 0.000212952 9;17 382.862 240 1
7 tests/benchmark/test_benchmark.py::test_NodeCollector_write_graph 0.225535 0.332832 0.285278 0.0409782 0.295744 0.0563555 2;0 3.50536 5 1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Report for Python 3.10

name min max mean stddev median iqr outliers ops rounds iterations
0 tests/benchmark/test_benchmark.py::test_InputOutput_write_graph 0.000103904 0.000529916 0.000107451 1.03805e-05 0.000106003 2.901e-06 43;102 9306.53 2045 1
1 tests/benchmark/test_benchmark.py::test_InputOutput_load 0.000113403 0.000150305 0.000117224 4.20701e-06 0.000116004 2.8995e-06 83;69 8530.67 1013 1
2 tests/benchmark/test_benchmark.py::test_InputOutput_load_lazy 0.000113504 0.000156605 0.000116377 3.79672e-06 0.000115603 1.301e-06 46;82 8592.73 1027 1
3 tests/benchmark/test_benchmark.py::test_NodeCollector_load 0.000114504 0.00228097 0.00012099 6.89867e-05 0.000117803 2.599e-06 1;58 8265.17 987 1
4 tests/benchmark/test_benchmark.py::test_NodeCollector_load_lazy 0.000115004 0.0100485 0.000128548 0.000321713 0.000117103 1.7e-06 1;85 7779.19 953 1
5 tests/benchmark/test_benchmark.py::test_NodeCollector_run_and_save 0.000429813 0.009708 0.00111153 0.00213886 0.000463815 5.98768e-05 4;6 899.659 47 1
6 tests/benchmark/test_benchmark.py::test_InputOutput_run_and_save 0.00183916 0.00598448 0.00201702 0.000319099 0.00198296 0.000127879 9;11 495.78 281 1
7 tests/benchmark/test_benchmark.py::test_NodeCollector_write_graph 0.173766 0.486841 0.263943 0.126841 0.227357 0.103804 1;1 3.78869 5 1

Please sign in to comment.