Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Custom Errors #384

Merged
merged 21 commits into from
Sep 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: "3.10"
python-version: "3.9"
- name: Install Poetry
uses: snok/install-poetry@v1
with:
Expand Down Expand Up @@ -100,7 +100,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: "3.10"
python-version: "3.9"
- name: Install Poetry
uses: snok/install-poetry@v1
with:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ def test_example_func_dry_run(proj_path):
assert " ".join(script) == " ".join(
[
"dvc",
"run",
"stage",
"add",
"-n",
"example_func",
"--no-exec",
"--force",
"--params",
"params.yaml:example_func",
Expand Down
83 changes: 83 additions & 0 deletions tests/integration_tests/test_direct_usage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""Test using the ZnTrack Nodes without DVC"""
import random

import pandas as pd
import pytest

from zntrack import Node, dvc, exceptions, zn


class GenerateRandomNumber(Node):
seed: int = zn.params()
output: float = zn.outs()

def run(self):
random.seed(self.seed)
self.output = random.randint(0, 100)


class MetricsAndPlots(Node):
metrics = zn.metrics()
plots = zn.plots()

def run(self):
self.metrics = {"a": 100}
self.plots = pd.DataFrame([{"b": 1}, {"b": 2}])


class DVCOuts(Node):
file: str = dvc.outs()

def run(self):
pass


def test_GenerateRandomNumber():
rn = GenerateRandomNumber(seed=1234)

with pytest.raises(exceptions.DataNotAvailableError):
_ = rn.output

assert rn.seed == 1234
rn.run()
assert rn.output == 99


def test_run_and_save(proj_path):
rn = GenerateRandomNumber(seed=1234)
rn.run_and_save()

assert rn.seed == 1234
assert rn.output == 99

rn_loaded = GenerateRandomNumber.load()

assert rn_loaded.output == 99
assert rn_loaded.seed == 1234


def test_MetricsAndPlots():
mp = MetricsAndPlots()
with pytest.raises(exceptions.DataNotAvailableError):
_ = mp.plots

with pytest.raises(exceptions.DataNotAvailableError):
_ = mp.metrics

mp.run()

assert mp.metrics == {"a": 100}
assert pd.DataFrame([{"b": 1}, {"b": 2}]).equals(mp.plots)


def test_run_and_save_mp(proj_path):
mp = MetricsAndPlots()
mp.run_and_save()

assert mp.metrics == {"a": 100}
assert pd.DataFrame([{"b": 1}, {"b": 2}]).equals(mp.plots)

mp_loaded = MetricsAndPlots.load()

assert mp_loaded.metrics == {"a": 100}
assert pd.DataFrame([{"b": 1}, {"b": 2}]).equals(mp_loaded.plots)
3 changes: 2 additions & 1 deletion tests/integration_tests/test_getdeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ def test_err_get_origin(proj_path):
sd.write_graph()
ModifyNumber(inputs=getdeps(sd, "number")).write_graph()

with pytest.raises(AttributeError):
with pytest.raises(utils.exceptions.DataNotAvailableError):
# Try to access the 'outputs' data which is not available at this point in time
get_origin(ModifyNumber.load(), "outputs")

with pytest.raises(AttributeError):
Expand Down
44 changes: 44 additions & 0 deletions tests/integration_tests/test_loading_exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import pytest

import zntrack
from zntrack import Node, dvc, zn


class WriteNumber(Node):
input = zn.params()
output = zn.outs()

def run(self):
self.output = self.input


class WriteNumberDVC(Node):
input = zn.params()
output = dvc.outs("test.txt")

def run(self):
with open(self.output) as f:
f.write("Hello World")


@pytest.mark.parametrize("model", [WriteNumber, WriteNumberDVC])
def test_GraphNotAvailableError(model):
node_obj = model.load()
with pytest.raises(zntrack.exceptions.GraphNotAvailableError):
_ = node_obj.input

with pytest.raises(zntrack.exceptions.GraphNotAvailableError):
# TODO is this really GraphNotAvailable or DataNotAvailable?
_ = node_obj.output


@pytest.mark.parametrize("load", (True, False))
def test_DataNotAvailableError(proj_path, load):
node_obj = WriteNumber(input=5)
node_obj.write_graph()

if load:
node_obj = WriteNumber.load()
assert node_obj.input == 5
with pytest.raises(zntrack.exceptions.DataNotAvailableError):
_ = node_obj.output
4 changes: 2 additions & 2 deletions tests/integration_tests/test_node_to_node_dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import pytest

from zntrack import dvc, getdeps, zn
from zntrack import dvc, getdeps, utils, zn
from zntrack.core.base import Node


Expand Down Expand Up @@ -94,7 +94,7 @@ def test_dvc_outs_no_load(proj_path):

def test_dvc_reversed(proj_path):
"""Create the instances first and at the end call write_graph"""
with pytest.raises(AttributeError):
with pytest.raises(utils.exceptions.GraphNotAvailableError):
# this can not work, because DVCOuts affected files is not now at the stage
# where DependenciesCollector writes its DVC stage
DependenciesCollector(dependencies=DVCOuts.load()).write_graph()
Expand Down
10 changes: 10 additions & 0 deletions tests/integration_tests/test_single_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,9 @@ def test_load_named_nodes(proj_path):
assert ExampleNode01[{"name": "Node01", "lazy": True}].outputs == 42
assert ExampleNode01[{"name": "Node01", "lazy": False}].outputs == 42

with pytest.raises(ValueError):
_ = ExampleNode01[1]


class NodeCustomFileName(Node):
output_std = zn.outs()
Expand Down Expand Up @@ -359,3 +362,10 @@ def test_collect(proj_path):

with pytest.raises(ValueError):
ExampleNode01["TestNode"].zntrack.collect((zn.params, zn.outs))


def test__graph_entry_exists(proj_path):
ExampleNode01(inputs="Hello World", name="TestNode").write_graph()

assert ExampleNode01.load()._graph_entry_exists is False
assert ExampleNode01["TestNode"]._graph_entry_exists
4 changes: 2 additions & 2 deletions tests/integration_tests/test_timeit.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ def test_timeit_loop(proj_path):
sleep_class = SleepClassLoop()
sleep_class.run_and_save()
assert (
pytest.approx(SleepClassLoop.load().timeit_metrics["sleep:timeit"]["mean"], 0.01)
pytest.approx(SleepClassLoop.load().timeit_metrics["sleep:timeit"]["mean"], 0.1)
== 0.1
)
assert SleepClassLoop.load().timeit_metrics["sleep:timeit"]["std"] < 1e-3
assert SleepClassLoop.load().timeit_metrics["sleep:timeit"]["std"] < 1e-2
assert len(SleepClassLoop.load().timeit_metrics["sleep:timeit"]["values"]) == 30
21 changes: 17 additions & 4 deletions tests/unit_tests/core/test_core_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def test__load():
)
params_mock = mock_open(read_data=yaml.safe_dump({"ExampleFullNode": {"params": 42}}))
zn_outs_mock = mock_open(read_data=json.dumps({"zn_outs": "outs_"}))
dvc_mock = mock_open(read_data=yaml.safe_dump({"stages": {"ExampleFullNode": None}}))

example = ExampleFullNode()

Expand All @@ -103,6 +104,9 @@ def pathlib_open(*args, **kwargs):
return params_mock(*args, **kwargs)
elif args[0] == pathlib.Path("nodes/ExampleFullNode/outs.json"):
return zn_outs_mock(*args, **kwargs)
elif args[0] == pathlib.Path("dvc.yaml"):
# required for logging with __repr__ which uses '_graph_entry_exists'
return dvc_mock(*args, **kwargs)
else:
raise ValueError(args)

Expand Down Expand Up @@ -150,10 +154,12 @@ def run(self):
self.outs = 42


def test_run_and_save():
@pytest.mark.parametrize("is_loaded", (True, False))
def test_run_and_save(is_loaded):
open_mock = mock_open(read_data="{}")

example = RunTestNode()
example.is_loaded = is_loaded

def pathlib_open(*args, **kwargs):
return open_mock(*args, **kwargs)
Expand All @@ -163,9 +169,16 @@ def pathlib_open(*args, **kwargs):

assert example.outs == 42

assert open_mock().write.mock_calls == [
call(json.dumps({"outs": 42}, indent=4)),
]
if example.is_loaded:
assert open_mock().write.mock_calls == [
call(json.dumps({"outs": 42}, indent=4)),
]
else:
assert open_mock().write.mock_calls == [
call("{}\n"), # clear_config_file(utils.Files.params) in save
call("{}"), # clear_config_file(utils.Files.zntrack) in save
call(json.dumps({"outs": 42}, indent=4)),
]


class WrongInit(Node):
Expand Down
14 changes: 6 additions & 8 deletions tests/unit_tests/core/test_dvcgraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,12 @@ def test_get_dvc_arguments():
dvc_options = DVCRunOptions(
force=True,
always_changed=False,
no_exec=True,
external=False,
external=True,
no_commit=False,
no_run_cache=False,
)

assert dvc_options.dvc_args == ["--no-exec", "--force"]
assert dvc_options.dvc_args == ["--external", "--force"]


def test_handle_deps():
Expand Down Expand Up @@ -144,7 +143,6 @@ def test_prepare_dvc_script():
external=True,
always_changed=True,
no_run_cache=False,
no_exec=True,
force=True,
)

Expand All @@ -160,12 +158,12 @@ def test_prepare_dvc_script():

assert script == [
"dvc",
"run",
"stage",
"add",
"-n",
"node01",
"--external",
"--always-changed",
"--no-exec",
"--force",
"--deps",
"file.txt",
Expand All @@ -187,12 +185,12 @@ def test_prepare_dvc_script():

assert script == [
"dvc",
"run",
"stage",
"add",
"-n",
"node01",
"--external",
"--always-changed",
"--no-exec",
"--force",
"--deps",
"file.txt",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ def pathlib_open(*args, **kwargs):
with patch.object(pathlib.Path, "open", pathlib_open):
assert file_io.read_file(pathlib.Path("example.yaml")) == {"a": "b"}

open_mock.assert_called_with(pathlib.Path("example.yaml"), "r")
args, kwargs = open_mock.call_args
assert args[0] == pathlib.Path("example.yaml")


def test_read_file_yml():
Expand All @@ -100,7 +101,8 @@ def pathlib_open(*args, **kwargs):
with patch.object(pathlib.Path, "open", pathlib_open):
assert file_io.read_file(pathlib.Path("example.yml")) == {"a": "b"}

open_mock.assert_called_with(pathlib.Path("example.yml"), "r")
args, kwargs = open_mock.call_args
assert args[0] == pathlib.Path("example.yml")


def test_read_file_txt():
Expand Down
File renamed without changes.
5 changes: 3 additions & 2 deletions tests/unit_tests/zn/test_zn_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import pytest
import znjson

from zntrack import zn
from zntrack import exceptions, zn
from zntrack.zn.dependencies import NodeAttribute, getdeps
from zntrack.zn.split_option import combine_values, split_value

Expand Down Expand Up @@ -39,7 +39,8 @@ class ExamplePlots:
def test_zn_plots():
example = ExamplePlots()
# test save and load if there is nothing to save or load
assert ExamplePlots.plots.save(example) is None
with pytest.raises(exceptions.DataNotAvailableError):
_ = ExamplePlots.plots.save(example)
with pytest.raises(FileNotFoundError):
_ = ExamplePlots.plots.get_data_from_files(example)

Expand Down
2 changes: 2 additions & 0 deletions zntrack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from zntrack.core.functions.decorator import NodeConfig, nodify
from zntrack.interface.base import DVCInterface
from zntrack.project.zntrack_project import ZnTrackProject
from zntrack.utils import exceptions
from zntrack.utils.config import config
from zntrack.utils.serializer import (
MethodConverter,
Expand All @@ -29,6 +30,7 @@
NodeConfig.__name__,
"getdeps",
"utils",
"exceptions",
]

__version__ = importlib.metadata.version("zntrack")
Expand Down
Loading