Skip to content

Commit b47f53f

Browse files
authored
[BUGFIX] Fix GUI (#533)
* Fix GUI Fix version parsing Add more tests for version class * Update train/_version.py Better error message * Add hello world CLI entry point to verify installation * Simplify install process for testing * Hack for CI/CD version determined by pyproject.toml * Clean up development Remove environments/requirements.txt Add docuemntation recommending to use Anaconda environment definitions.
1 parent af1c4d5 commit b47f53f

File tree

9 files changed

+176
-39
lines changed

9 files changed

+176
-39
lines changed

.github/workflows/python-package.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,16 @@ jobs:
2727
- name: Install dependencies
2828
run: |
2929
python -m pip install --upgrade pip
30-
python -m pip install flake8 pytest
31-
if [ -f environments/requirements.txt ]; then pip install -r environments/requirements.txt; fi
3230
python -m pip install .
31+
nam-hello-world
3332
- name: Lint with flake8
3433
run: |
34+
python -m pip install flake8
3535
# stop the build if there are Python syntax errors or undefined names
3636
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
3737
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
3838
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
3939
- name: Test with pytest
4040
run: |
41+
python -m pip install pytest pytest-mock
4142
xvfb-run -a pytest

docs/source/installation.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,13 @@ To update an existing installation:
3333
.. code-block:: console
3434
3535
pip install --upgrade neural-amp-modeler
36+
37+
Local development installation
38+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
39+
40+
If you're interested in developing this package, there are Anaconda environment
41+
definitions included in the ``environments/`` directory. Use the one that's
42+
appropriate for the platform you're developing on. The
43+
``.github/workflows/python-pckage.yml`` is also helpful if you want to be sure
44+
that you're testing your developments in the same way that contributions will be
45+
automatically tested (via GitHub Actions).

environments/requirements.txt

Lines changed: 0 additions & 25 deletions
This file was deleted.

nam/cli.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,26 @@ def removesuffix(s: str, suffix: str) -> str:
8282
from pathlib import Path as _Path
8383

8484
from nam.train.full import main as _nam_full
85-
from nam.train.gui import run as _nam_gui # noqa F401 Used as an entry point
85+
from nam.train.gui import run as nam_gui # noqa F401 Used as an entry point
8686
from nam.util import timestamp as _timestamp
8787

8888

89+
def nam_hello_world():
90+
"""
91+
This is a minimal CLI entry point that's meant to be used to ensure that NAM
92+
was installed successfully
93+
"""
94+
from nam import __version__
95+
msg = f"""
96+
Neural Amp Modeler
97+
98+
by Steven Atkinson
99+
100+
Version {__version__}
101+
"""
102+
print(msg)
103+
104+
89105
def nam_full():
90106
parser = _ArgumentParser()
91107
parser.add_argument("data_config_path", type=str)

nam/train/_version.py

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,68 @@
66
Version utility
77
"""
88

9+
from typing import Optional as _Optional
10+
911
from .._version import __version__
1012

1113

14+
class IncomparableVersionError(ValueError):
15+
"""
16+
Error raised when two versions can't be compared.
17+
"""
18+
19+
pass
20+
21+
1222
class Version:
13-
def __init__(self, major: int, minor: int, patch: int):
23+
def __init__(self, major: int, minor: int, patch: int, dev: _Optional[str] = None):
1424
self.major = major
1525
self.minor = minor
1626
self.patch = patch
27+
self.dev = dev
28+
self.dev_int = self._parse_dev_int(dev)
1729

1830
@classmethod
1931
def from_string(cls, s: str):
20-
major, minor, patch = [int(x) for x in s.split(".")]
21-
return cls(major, minor, patch)
32+
def special_case(s: str) -> _Optional[dict]:
33+
"""
34+
Regretful hacks
35+
"""
36+
# It seems like the git repo isn't accessible to setuptools_scm's version
37+
# guesser, so it comes up with this during install:
38+
if s == "0.1.dev1":
39+
# This will be fine.
40+
return {
41+
"major": 0,
42+
"minor": 1,
43+
"patch": 0,
44+
"dev": "dev1"
45+
}
46+
return None
47+
48+
if special_case(s) is not None:
49+
return cls(**special_case(s))
50+
51+
# Typical
52+
parts = s.split(".")
53+
if len(parts) == 3: # e.g. "0.7.1"
54+
dev = None
55+
elif len(parts) == 4: # e.g. "0.7.1.dev7"
56+
dev = parts[3]
57+
else:
58+
raise ValueError(f"Invalid version string {s}")
59+
try:
60+
major, minor, patch = [int(x) for x in parts[:3]]
61+
except ValueError as e:
62+
raise ValueError(f"Failed to parse version from string '{s}':\n{e}")
63+
return cls(major=major, minor=minor, patch=patch, dev=dev)
2264

2365
def __eq__(self, other) -> bool:
2466
return (
2567
self.major == other.major
2668
and self.minor == other.minor
2769
and self.patch == other.patch
70+
and self.dev == other.dev
2871
)
2972

3073
def __lt__(self, other) -> bool:
@@ -36,10 +79,42 @@ def __lt__(self, other) -> bool:
3679
return self.minor < other.minor
3780
if self.patch != other.patch:
3881
return self.patch < other.patch
82+
if self.dev != other.dev:
83+
# None is defined as least
84+
if self.dev is None and other.dev is not None:
85+
return True
86+
elif self.dev is not None and other.dev is None:
87+
return False
88+
assert self.dev is not None
89+
assert other.dev is not None
90+
if self.dev_int is None:
91+
raise IncomparableVersionError(
92+
f"Version {str(self)} has incomparable dev version {self.dev}"
93+
)
94+
if other.dev_int is None:
95+
raise IncomparableVersionError(
96+
f"Version {str(other)} has incomparable dev version {other.dev}"
97+
)
98+
return self.dev_int < other.dev_int
99+
raise RuntimeError(
100+
f"Unhandled comparison between versions {str(self)} and {str(other)}"
101+
)
39102

40103
def __str__(self) -> str:
41104
return f"{self.major}.{self.minor}.{self.patch}"
42105

106+
def _parse_dev_int(self, dev: _Optional[str]) -> _Optional[int]:
107+
"""
108+
Turn the string into an int that can be compared if possible.
109+
"""
110+
if dev is None:
111+
return None
112+
if not isinstance(dev, str):
113+
raise TypeError(f"Invalid dev string type {type(dev)}")
114+
if not dev.startswith("dev") or len(dev) <= 3: # "misc", "dev", etc
115+
return None
116+
return int(dev.removeprefix("dev"))
117+
43118

44119
PROTEUS_VERSION = Version(4, 0, 0)
45120

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ requires-python = ">=3.8"
1414
dependencies = [
1515
"auraloss==0.3.0",
1616
"matplotlib",
17+
"numpy<2",
1718
"pydantic>=2.0.0",
1819
"pytorch_lightning",
1920
"scipy",
2021
"sounddevice",
2122
"tensorboard",
2223
"torch",
24+
# `transformers` is not required, but if you have it, it needs to be recent
25+
# enough so I'm adding it.
26+
"transformers>=4",
2327
"tqdm",
2428
"wavio>=0.0.5",
2529
]
@@ -44,6 +48,7 @@ homepage = "https://github.com/sdatkinson/"
4448
[project.scripts]
4549
nam = "nam.cli:nam_gui"
4650
nam-full = "nam.cli:nam_full"
51+
nam-hello-world = "nam.cli:nam_hello_world"
4752

4853
[tool.setuptools_scm]
4954
version_scheme = "guess-next-dev"

tests/test_nam/test_train/test_gui/test_main.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,22 @@
99
from nam.train import gui
1010

1111

12-
class TestPathButton(object):
13-
def test_system_text_color(self):
14-
"""
15-
Issue 428
16-
"""
17-
top_level = tk.Toplevel()
18-
label = tk.Label(master=top_level, text="My text", fg=gui._SYSTEM_TEXT_COLOR)
19-
label.pack()
12+
# class TestPathButton(object):
13+
# def test_system_text_color(self):
14+
# """
15+
# Issue 428
16+
# """
17+
# top_level = tk.Toplevel()
18+
# label = tk.Label(master=top_level, text="My text", fg=gui._SYSTEM_TEXT_COLOR)
19+
# label.pack()
20+
21+
22+
def test_get_current_version():
23+
"""
24+
Make sure this at least runs!
25+
See #516
26+
"""
27+
v = gui._get_current_version()
2028

2129

2230
if __name__ == "__main__":

tests/test_nam/test_train/test_version.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,32 @@
22
# Created Date: Saturday April 29th 2023
33
# Author: Steven Atkinson (steven@atkinson.mn)
44

5+
"""
6+
Tests for version class
7+
"""
8+
9+
import pytest as _pytest
10+
511
from nam.train import _version
612

713

14+
def test_dev_int():
15+
"""
16+
Assert that dev_int is properly parsed
17+
"""
18+
assert _version.Version(0, 0, 0).dev_int is None
19+
assert _version.Version(0, 0, 0, "dev").dev_int is None
20+
assert _version.Version(0, 0, 0, "misc").dev_int is None
21+
assert _version.Version(0, 0, 0, "dev11").dev_int == 11
22+
23+
824
def test_eq():
925
assert _version.Version(0, 0, 0) == _version.Version(0, 0, 0)
1026
assert _version.Version(0, 0, 0) != _version.Version(0, 0, 1)
1127
assert _version.Version(0, 0, 0) != _version.Version(0, 1, 0)
1228
assert _version.Version(0, 0, 0) != _version.Version(1, 0, 0)
29+
assert _version.Version(0, 0, 0) != _version.Version(0, 0, 0, dev="dev0")
30+
assert _version.Version(0, 0, 0) != _version.Version(0, 0, 0, dev="dev1")
1331

1432

1533
def test_lt():
@@ -20,3 +38,31 @@ def test_lt():
2038
assert _version.Version(1, 2, 3) < _version.Version(2, 0, 0)
2139

2240
assert not _version.Version(1, 2, 3) < _version.Version(0, 4, 5)
41+
42+
43+
def test_lt_incomparable():
44+
"""
45+
Assert that the error is properly raised for incomparable versions
46+
"""
47+
with _pytest.raises(_version.IncomparableVersionError):
48+
_version.Version(0, 0, 0, "incomparable") < _version.Version(0, 0, 0, "dev1")
49+
50+
51+
def test_current_version():
52+
"""
53+
Test that the current version is valid
54+
"""
55+
from nam import __version__
56+
57+
# First off, assert that the current version can be understood by _version.Version.
58+
# Broken by PR 516 (pyproject.toml)--watch out!
59+
v = _version.Version.from_string(__version__)
60+
61+
# Check comparisons like used in GUI:
62+
assert _version.Version(0, 0, 0) != v
63+
assert _version.Version(0, 0, 0) < v
64+
# Just checking both orders. If we're actually at this version, then fine, move it up!
65+
high_major_version_that_we_will_probably_never_get_to = 1000
66+
assert v < _version.Version(
67+
high_major_version_that_we_will_probably_never_get_to, 0, 0
68+
)

tests/test_nam/test_version.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import nam as _nam
88

9+
910
def test_has_version():
1011
assert hasattr(_nam, "__version__")
1112

0 commit comments

Comments
 (0)