Skip to content

Commit

Permalink
Merge pull request #72 from zincware/development
Browse files Browse the repository at this point in the history
Update to [PyTrack 0.1.2]
  • Loading branch information
PythonFZ authored Oct 20, 2021
2 parents c35a660 + 6bde8d7 commit afcf46a
Show file tree
Hide file tree
Showing 30 changed files with 3,233 additions and 2,446 deletions.
50 changes: 50 additions & 0 deletions CI/integration_tests/test_ValueError_in_Stage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""
This program and the accompanying materials are made available under the terms of the
Eclipse Public License v2.0 which accompanies this distribution, and is available at
https://www.eclipse.org/legal/epl-v20.html
SPDX-License-Identifier: EPL-2.0
Copyright Contributors to the Zincware Project.
Description:
"""
import pytest
from pytrack import PyTrack, PyTrackProject
import shutil
import os
from subprocess import CalledProcessError


@PyTrack()
class RaiseValueError:
"""BasicTest class"""

def run(self):
"""Run method of the PyTrack test instance"""
raise ValueError("Testing ValueError")


def test_project(tmp_path):
"""Test that an ValueError is raised
Currently it is not checked, which Error occurs in the subprocess but just that the subprocess call fails.
It only works within `project.repro` and not with `project.run`
"""
shutil.copy(__file__, tmp_path)
os.chdir(tmp_path)

project = PyTrackProject()
project.create_dvc_repository()

error_stage = RaiseValueError()
error_stage()

with pytest.raises(CalledProcessError):
project.repro()

# with pytest.raises(CalledProcessError):
# project.run()

with pytest.raises(ValueError):
error_stage.run()
66 changes: 66 additions & 0 deletions CI/integration_tests/test_call_stage_multiple_times.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""
This program and the accompanying materials are made available under the terms of the
Eclipse Public License v2.0 which accompanies this distribution, and is available at
https://www.eclipse.org/legal/epl-v20.html
SPDX-License-Identifier: EPL-2.0
Copyright Contributors to the Zincware Project.
Description:
"""
from pytrack import PyTrack, DVC, PyTrackProject
import os


@PyTrack()
class HelloWorld:
def __init__(self):
self.argument_1 = DVC.params()

def __call__(self, argument_1):
self.argument_1 = argument_1

def run(self):
pass


@PyTrack()
class HelloWorldwDefault:
def __init__(self):
self.argument_1 = DVC.params(314159)

def __call__(self, argument_1):
self.argument_1 = argument_1

def run(self):
pass


def test_init_without_overwriting(tmp_path):
"""Test that initializing empty DVC.params does not result in changing values"""
os.chdir(tmp_path)
project = PyTrackProject()
project.create_dvc_repository()

hello_world_1 = HelloWorld()
hello_world_1(argument_1=11235)

hello_world_2 = HelloWorld()

# it should not overwrite the given param values,
# when they are not set explicitly!

assert hello_world_1.argument_1 == 11235


def test_load_works(tmp_path):
"""Test that pre-initializing DVC.params does result in changing values"""
os.chdir(tmp_path)
project = PyTrackProject()
project.create_dvc_repository()

hello_world_1 = HelloWorldwDefault()
hello_world_1(argument_1=11235)

assert HelloWorldwDefault().argument_1 == 314159
assert HelloWorldwDefault(load=True).argument_1 == 11235
16 changes: 4 additions & 12 deletions CI/integration_tests/test_class.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from unittest import TestCase
from pytrack import PyTrack, DVC, PyTrackProject
from pathlib import Path
import json
import subprocess
import os
import shutil
from tempfile import TemporaryDirectory
Expand Down Expand Up @@ -49,35 +47,29 @@ def prepare_env():
json.dump({"id": idx}, f)

project.name = "Test1"
project.run()
project.load()
project.repro()

yield

os.chdir(cwd)
temp_dir.cleanup()


def test_query_by_id():
base = BasicTest(id_=0)
assert base.pytrack.id == "0"


def test_parameters():
"""Test that the parameters are read correctly"""
base = BasicTest(id_=0)
base = BasicTest(load=True)
assert base.parameters == dict(
name="PyTest", values=[2, 4, 8, 16, 32, 64, 128, 256]
)


def test_results():
"""Test that the results are read correctly"""
base = BasicTest(id_=0)
base = BasicTest(load=True)
assert base.results == {"name": "PyTest"}


def test_deps():
"""Test that the dependencies are stored correctly"""
base = BasicTest(id_=0)
base = BasicTest(load=True)
assert base.deps == [Path("deps1", "input.json"), Path("deps2", "input.json")]
32 changes: 21 additions & 11 deletions CI/integration_tests/test_graph_01.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ class ComputeABNamed:
"""PyTrack stage AB, depending on A&B with a custom stage name"""

def __init__(self):
self.a = DVC.deps(ComputeANamed(id_=0))
self.b = DVC.deps(ComputeB(id_=0))
self.a: ComputeANamed = DVC.deps(ComputeANamed(id_=0))
self.b: ComputeB = DVC.deps(ComputeB(id_=0))
self.out = DVC.result()

self.param = DVC.params()
Expand All @@ -102,9 +102,7 @@ def __call__(self):
self.param = "default"

def run(self):
a = ComputeANamed(id_=0).out
b = ComputeB(id_=0).out
self.out = a + b
self.out = self.a.out + self.b.out


@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -152,8 +150,7 @@ def test_stage_addition_named():
ab = ComputeABNamed()
ab()

project.run()
project.load()
project.repro()
finished_stage = ComputeABNamed(id_=0)
assert finished_stage.out == 31

Expand Down Expand Up @@ -192,9 +189,22 @@ def test_stage_addition_named_run():
ab = ComputeABNamed()
ab()

a.run()
b.run()
ab.run()
ComputeANamed(load=True).run()
ComputeB(load=True).run()
ComputeABNamed(load=True).run()

finished_stage = ComputeABNamed(id_=0)
finished_stage = ComputeABNamed(load=True)
assert finished_stage.out == 31


def test_named_single_stage():
"""Test a single named stage"""
project = PyTrackProject()
project.create_dvc_repository()

a = ComputeANamed()
a(2)

project.repro()

assert ComputeANamed(load=True).out == 4
61 changes: 61 additions & 0 deletions CI/integration_tests/test_stage_stage_dependency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""
This program and the accompanying materials are made available under the terms of the
Eclipse Public License v2.0 which accompanies this distribution, and is available at
https://www.eclipse.org/legal/epl-v20.html
SPDX-License-Identifier: EPL-2.0
Copyright Contributors to the Zincware Project.
Description:
"""
from pytrack import PyTrack, DVC
import shutil
import os


@PyTrack()
class Stage1:
args = DVC.params()

def __call__(self, args):
self.args = args

def run(self):
pass


@PyTrack()
class Stage2:
stage_1: Stage1 = DVC.deps(Stage1(load=True))

def __call__(self, *args, **kwargs):
pass

def run(self):
pass


def test_stage_stage_dependency(tmp_path):
"""Test that stage dependencies including load work as expected"""
shutil.copy(__file__, tmp_path)
os.chdir(tmp_path)

stage_1 = Stage1()
stage_1(args="Test01")
# Need to call the stage, to create the config file
# it does not make sense to access the results of a stage
# that has not at least been called
stage_2 = Stage2()
stage_2()

stage_2a = Stage2(load=True)

# changing the value of Stage1 in the config file
stage_1 = Stage1()
stage_1(args="Test02")

# Loading the stage it should now have the new value
stage_2b = Stage2(load=True)

assert stage_2a.stage_1.args == "Test01"
assert stage_2b.stage_1.args == "Test02"
49 changes: 19 additions & 30 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,40 @@ For more information on DVC visit their `homepage <https://dvc.org/doc>`_.

Example
========
PyTrack allows you to convert most Python classes into a DVC stage, including parameters, dependencies and all DVC output types.
PyTrack allows you to convert most Python classes into a DVC stage, including
parameters, dependencies and all DVC output types.

.. code-block:: py
from pytrack import PyTrack, DVC
from random import randrange
@PyTrack()
class Linear:
def __init__(self):
"""Define all DVC parameter, dependencies and outputs"""
self.values_file = DVC.deps()
self.a1 = DVC.params()
self.b = DVC.params()
self.out = DVC.result()
def __call__(self, a1, b, values_file):
"""
Parameters
----------
a1: float
Any a1 for calculating a1 * x + b
b: float
Any b for calculating a1 * x + b
values_file: str
Path to a comma seperated file of values for x
"""
self.values_file = values_file
self.a1 = a1
self.b = b
class HelloWorld:
"""Define a PyTrack Stage"""
# parameter to be tracked
max_number = DVC.params()
# parameter to store as output
random_number = DVC.result()
def __call__(self, max_number):
"""Pass tracked arguments"""
self.max_number = max_number
def run(self):
"""Command that is run by DVC"""
values = [float(x) for x in self.values_file.read_text().split(",")]
self.out = [self.a1 * x + self.b for x in values]
"""Command to be run by DVC"""
self.random_number = randrange(self.max_number)
This stage can be used via

.. code-block:: py
linear = Linear()
linear(3, 7, "values.csv")
hello_world = HelloWorld()
hello_world(max_number=512)
which builds the DVC stage and can be used e.g., through :code:`dvc repro`.
The results can then be accessed easily via :code:`Linear(id_=0).out`.
The results can then be accessed easily via :code:`HelloWorld(load=True).random_number`.


Installation
Expand Down Expand Up @@ -84,7 +73,7 @@ Or you can install from source with:

.. |license| image:: https://img.shields.io/badge/License-EPL-purple.svg?style=flat
:alt: Project license
:target: https://www.eclipse.org/legal/eplfaq.php
:target: https://www.eclipse.org/legal/epl-2.0/faq.php

.. |code style| image:: https://img.shields.io/badge/code%20style-black-black
:alt: Code style: black
Expand Down
Loading

0 comments on commit afcf46a

Please sign in to comment.