From 5070002ef1aff9eecbc3f7e755183027f8c5fa07 Mon Sep 17 00:00:00 2001 From: jessebrennan Date: Sun, 23 Feb 2020 19:54:43 -0800 Subject: [PATCH] Attempt to add Travis and make installable --- .flake8 | 3 ++ .travis.yml | 14 +++++++++ MANIFEST.in | 3 ++ Makefile | 32 +++++++++++++++++++++ VERSION | 1 + release.py | 64 ++++++++++++++++++++++++++++++++++++++++++ requirements.dev.txt | 3 ++ requirements.txt | 1 + setup.py | 40 ++++++++++++++++++++++++++ src/cutest/__init__.py | 28 ++++++++++++++++-- src/cutest/__main__.py | 15 +--------- test/sample.py | 1 - test/test_runner.py | 3 +- 13 files changed, 188 insertions(+), 20 deletions(-) create mode 100644 .flake8 create mode 100644 .travis.yml create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100644 VERSION create mode 100644 release.py create mode 100644 requirements.dev.txt create mode 100644 requirements.txt create mode 100644 setup.py diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..bcfb666 --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length=120 +ignore: E301, E302, E401, E261, E265, E226, F401, E501 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..e8fffbf --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: python +python: +- '3.6' +install: +- make develop +script: +- make test +deploy: + provider: pypi + on: + tags: true + user: jessebrennan + password: + 'foo' \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..a9bce26 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include README.md VERSION release.py +include *.txt +recursive-include src \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d751628 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +MODULES=src test + +all: test + +lint: + flake8 $(MODULES) + +check_readme: + python setup.py check -r -s + +tests:=$(wildcard test/test_*.py) + +# A pattern rule that runs a single test module, for example: +# make tests/test_gen3_input_json.py + +$(tests): %.py : mypy lint check_readme + python -m unittest --verbose $*.py + +test: $(tests) + +develop: + pip install -e . + pip install -r requirements.dev.txt + +undevelop: + python setup.py develop --uninstall + pip uninstall -y -r requirements.dev.txt + +release: test + python release.py $(VERSION) + +.PHONY: all lint mypy test develop undevelop release \ No newline at end of file diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..8a9ecc2 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.0.1 \ No newline at end of file diff --git a/release.py b/release.py new file mode 100644 index 0000000..84f4348 --- /dev/null +++ b/release.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +import re +import sys + +# +# a basic release automation script +# +# This should only be called from the Makefile +# + +VERSION_FILE = 'VERSION' + +help_message = ("The argument must be formatted as either 'major', 'minor', 'patch' or 'X.X.X'\n" + "\n" + "If one of the first three options is used than that part of the version will\n" + "be incremented and all lesser parts of the version will be reset to 0.") + + +def read_version(): + with open(VERSION_FILE, 'r') as fp: + return tuple(map(int, fp.read().split('.'))) + + +def write_version(major, minor, patch): + with open(VERSION_FILE, 'w') as fp: + fp.write(f'{major}.{minor}.{patch}') + + +def main(): + # when called from the makefile we expect only 1 arg + assert len(sys.argv) == 2 + + arg = sys.argv[1] + + major, minor, patch = read_version() + print(f'Previous version set to {major}.{minor}.{patch}') + + if arg == 'help': + print(help_message, file=sys.stderr) + exit(0) + elif arg == 'major': + major += 1 + minor = 0 + patch = 0 + elif arg == 'minor': + minor += 1 + patch = 0 + elif arg == 'patch': + patch += 1 + # of form X.X.X + elif re.match('^\d*\.\d*\.\d*$', arg): + major, minor, patch = tuple(arg.split('.')) + else: + print(help_message, file=sys.stderr) + exit(1) + + write_version(major, minor, patch) + print(f'New version successfully set to {major}.{minor}.{patch}. To finish release, commit\n' + 'changes. Then create a release on github with this version number. Travis will\n' + 'automatically upload to PyPI') + + +if __name__ == '__main__': + main() diff --git a/requirements.dev.txt b/requirements.dev.txt new file mode 100644 index 0000000..1fc1247 --- /dev/null +++ b/requirements.dev.txt @@ -0,0 +1,3 @@ +flake8 +ipython +docutils diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9c558e3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +. diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e676351 --- /dev/null +++ b/setup.py @@ -0,0 +1,40 @@ +from setuptools import setup, find_packages +from os import path + +from release import read_version + +here = path.abspath(path.dirname(__file__)) + +# Get the long description from the README file +with open(path.join(here, 'README.md'), encoding='utf-8') as f: + long_description = f.read() + + +setup( + name='cutest', + version='{}.{}.{}'.format(*read_version()), + description='A cute, composable unit test framework for Python', + long_description=long_description, + long_description_content_type='text/markdown', + url='https://github.com/jessebrennan/cutest', + author='jessebrennan', + author_email='brennan@pacific.net', + classifiers=[ + 'Development Status :: 2 - Pre-Alpha', + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Testing', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + ], + package_dir={'': 'src'}, + packages=find_packages(where='src'), + python_requires='>=3.6, <4', + install_requires=[], + entry_points={ # Optional + 'console_scripts': [ + 'cutest=cutest:main', + ], + }, +) diff --git a/src/cutest/__init__.py b/src/cutest/__init__.py index 4b127b6..bd55930 100644 --- a/src/cutest/__init__.py +++ b/src/cutest/__init__.py @@ -1,6 +1,8 @@ +import argparse import importlib import inspect import logging +import sys from abc import ABC from concurrent.futures import Executor from contextlib import contextmanager @@ -95,8 +97,11 @@ def prune_all_but(self, nodes: Iterable['Node']) -> bool: self.children = save_children return self in nodes or any(save_children) - def __eq__(self, other: 'Node'): - return self.data == other.data + def __eq__(self, other: object) -> bool: + if not isinstance(other, Node): + return NotImplemented + else: + return self.data == other.data def __hash__(self): return hash(self.data) @@ -392,7 +397,7 @@ def add_tests(self, test_ids: List[str]): obj.model.initialize() self.tests += obj.calls else: - models = [obj for obj in vars(module) if isinstance(obj, Model)] + models: List[Model] = [obj for obj in vars(module) if isinstance(obj, Model)] for model in models: model.initialize() self.models.append(model) @@ -400,3 +405,20 @@ def add_tests(self, test_ids: List[str]): class CutestError(Exception): pass + + +def main(argv=None): + if not argv: + # If called programmatically (i.e. tests), we don't want to override logging info + logging.basicConfig(level=logging.INFO) + argv = sys.argv[1:] + + parser = argparse.ArgumentParser(description='Run unit tests with cutest') + parser.add_argument('--verbose', '-v', action='count', default=0) + parser.add_argument('tests', nargs='*', + help='a list of any number of test modules, suites, and test methods') + options = parser.parse_args(argv) + collection = Collection() + collection.add_tests(options.tests) + runner = Runner() + runner.run_collection(collection) diff --git a/src/cutest/__main__.py b/src/cutest/__main__.py index c3e704b..a0d1323 100644 --- a/src/cutest/__main__.py +++ b/src/cutest/__main__.py @@ -1,19 +1,6 @@ -import argparse import sys -from cutest import Collection, Runner - - -def main(argv): - parser = argparse.ArgumentParser(description='Run unit tests with cutest') - parser.add_argument('--verbose', '-v', action='count', default=0) - parser.add_argument('tests', nargs='*', - help='a list of any number of test modules, suites, and test methods') - options = parser.parse_args(argv) - collection = Collection() - collection.add_tests(options.tests) - runner = Runner() - runner.run_collection(collection) +from cutest import main if __name__ == '__main__': diff --git a/test/sample.py b/test/sample.py index 7694513..9d123db 100644 --- a/test/sample.py +++ b/test/sample.py @@ -61,4 +61,3 @@ def __exit__(self, exc_type, exc_val, exc_tb): def fix_method(self): print('fix_2 method') - diff --git a/test/test_runner.py b/test/test_runner.py index f9942dc..4a4e63e 100644 --- a/test/test_runner.py +++ b/test/test_runner.py @@ -10,5 +10,4 @@ def test_prune(self): runner = cutest.Runner() sample.my_suite.initialize() pruned = list(runner.pruned_suites(sample.test_1.calls)) - foo = 1 - + self.assertEqual(len(pruned), 1)