diff --git a/Makefile b/Makefile index 907ba08..465b8e9 100644 --- a/Makefile +++ b/Makefile @@ -11,12 +11,12 @@ BINDIR = $(PWD)/.state/env/bin $(BINDIR)/python -m pip install -r requirements/dev.txt build: .state/env/pyvenv.cfg - $(BINDIR)/python _internal/generator.py + $(BINDIR)/python -m _internal.generator test: .state/env/pyvenv.cfg $(BINDIR)/pytest $(eval TMPDIR := $(shell mktemp -d)) - $(BINDIR)/python _internal/generator.py --output $(TMPDIR)/test.py + $(BINDIR)/python -m _internal.generator --output $(TMPDIR)/test.py diff trove_classifiers/__init__.py $(TMPDIR)/test.py lint: .state/env/pyvenv.cfg diff --git a/_internal/classifiers.py b/_internal/classifiers.py index 5687f6b..84ed928 100644 --- a/_internal/classifiers.py +++ b/_internal/classifiers.py @@ -1,4 +1,4 @@ -from models import ClassifierRoot, ClassifierNode +from .models import ClassifierRoot, ClassifierNode classifiers = ClassifierRoot( children=[ diff --git a/_internal/exceptions.py b/_internal/exceptions.py new file mode 100644 index 0000000..f1d15ef --- /dev/null +++ b/_internal/exceptions.py @@ -0,0 +1,2 @@ +class InvalidClassifier(Exception): + pass diff --git a/_internal/generator.py b/_internal/generator.py index 910a085..1a904bd 100644 --- a/_internal/generator.py +++ b/_internal/generator.py @@ -4,7 +4,7 @@ from jinja2 import Template -from classifiers import classifiers +from .classifiers import classifiers parser = argparse.ArgumentParser() parser.add_argument( diff --git a/_internal/models.py b/_internal/models.py index bafbb18..9d3b8fa 100644 --- a/_internal/models.py +++ b/_internal/models.py @@ -1,3 +1,6 @@ +from .exceptions import InvalidClassifier + + class ClassifierRoot: def __init__(self, children): self.children = children @@ -23,10 +26,21 @@ def __init__( skip=False, ): if deprecated_by and not deprecated: - raise Exception( + raise InvalidClassifier( "Using deprecated_by, but not marking the classifier as deprecated" ) + if short_name.lower().startswith("private"): + raise InvalidClassifier("Classifiers starting with 'Private' are invalid") + + if short_name.strip().rstrip() != short_name: + raise InvalidClassifier( + "Classifiers starting or ending with whitespace are invalid" + ) + + if ":" in short_name: + raise InvalidClassifier("Classifiers containing ':' are invalid") + self.short_name = short_name self.prefix_list = [] self.deprecated = deprecated diff --git a/setup.py b/setup.py index 5a6d2fa..deb5a01 100644 --- a/setup.py +++ b/setup.py @@ -3,8 +3,6 @@ from setuptools import setup, find_packages -from trove_classifiers import classifiers - here = path.abspath(path.dirname(__file__)) # Get the long description from the README file diff --git a/tests/test_classifiers.py b/tests/test_classifiers.py index d2a7ff0..c11cb6b 100644 --- a/tests/test_classifiers.py +++ b/tests/test_classifiers.py @@ -1,6 +1,7 @@ import pytest from _internal.models import ClassifierRoot, ClassifierNode +from _internal.exceptions import InvalidClassifier def test_nested_prefixes(): @@ -13,10 +14,10 @@ def test_nested_prefixes(): ] ) - assert root.generate() == [ - ClassifierNode("Foo"), - ClassifierNode("Foo :: Bar"), - ClassifierNode("Foo :: Bar :: Baz"), + assert [node.full_name for node in root.generate()] == [ + "Foo", + "Foo :: Bar", + "Foo :: Bar :: Baz", ] @@ -31,12 +32,47 @@ def test_skip(): ] ) - assert root.generate() == [ - ClassifierNode("Foo :: Bar"), - ClassifierNode("Foo :: Bar :: Baz"), + assert [node.full_name for node in root.generate()] == [ + "Foo :: Bar", + "Foo :: Bar :: Baz", ] def test_bad_deprecation_failure(): - with pytest.raises(Exception): + with pytest.raises(InvalidClassifier) as excinfo: ClassifierNode("blah", deprecated_by=["spam"]) + + assert excinfo.value.args == ( + "Using deprecated_by, but not marking the classifier as deprecated", + ) + + +@pytest.mark.parametrize( + "parent, child", + [("Private", "Foo"), ("private", "Foo"), ("Foo", "Private"), ("Foo", "private"),], +) +def test_private_classifier_failure(parent, child): + with pytest.raises(InvalidClassifier) as excinfo: + ClassifierNode( + parent, children=[ClassifierNode(child)], + ) + + assert excinfo.value.args == ("Classifiers starting with 'Private' are invalid",) + + +@pytest.mark.parametrize("classifier", [" Foo", "Foo "]) +def test_whitespace_classifier_failure(classifier): + with pytest.raises(InvalidClassifier) as excinfo: + ClassifierNode(classifier) + + assert excinfo.value.args == ( + "Classifiers starting or ending with whitespace are invalid", + ) + + +@pytest.mark.parametrize("classifier", ["Foo:", "Foo :: Bar"]) +def test_colon_classifier_failure(classifier): + with pytest.raises(InvalidClassifier) as excinfo: + ClassifierNode(classifier) + + assert excinfo.value.args == ("Classifiers containing ':' are invalid",)