Skip to content

Commit 05220be

Browse files
committed
Add tasks.py to ease and standardize packaging
1 parent 796454d commit 05220be

File tree

4 files changed

+193
-5
lines changed

4 files changed

+193
-5
lines changed

cmd2_submenu/pylintrc

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#
2+
# pylint configuration
3+
#
4+
# $ pylint --rcfile=cmd2_submenu/pylintrc cmd2_submenu
5+
#
6+
7+
[messages control]
8+
# too-few-public-methods pylint expects a class to have at
9+
# least two public methods
10+
disable=too-few-public-methods

setup.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@
1010
with open(os.path.join(here, 'README.md'), encoding='utf-8') as f:
1111
long_description = f.read()
1212

13-
VERSION='0.1.0'
14-
1513
setuptools.setup(
1614
name='cmd2-submenu',
17-
version=VERSION,
15+
use_scm_version=True,
1816
description='A nested submenu system for cmd2',
1917
long_description=long_description,
2018
keywords='cmd2 plugin submenu',
@@ -27,7 +25,8 @@
2725
packages=['cmd2_submenu'],
2826

2927
python_requires='>=3.4',
30-
install_requires=['cmd2 >= 0.9.0, <=2'],
28+
install_requires=['cmd2 >= 0.9.4, <=2'],
29+
setup_requires=['setuptools_scm'],
3130

3231
classifiers=[
3332
'Development Status :: 4 - Beta',
@@ -45,6 +44,6 @@
4544
# dependencies for development and testing
4645
# $ pip install -e .[dev]
4746
extras_require={
48-
'dev': ['pytest']
47+
'dev': ['setuptools_scm', 'pytest', 'invoke']
4948
},
5049
)

tasks.py

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#
2+
# -*- coding: utf-8 -*-
3+
"""Development related tasks to be run with 'invoke'"""
4+
5+
import os
6+
import shutil
7+
8+
import invoke
9+
10+
# shared function
11+
def rmrf(items, verbose=True):
12+
"Silently remove a list of directories or files"
13+
if isinstance(items, str):
14+
items = [items]
15+
16+
for item in items:
17+
if verbose:
18+
print("Removing {}".format(item))
19+
shutil.rmtree(item, ignore_errors=True)
20+
# rmtree doesn't remove bare files
21+
try:
22+
os.remove(item)
23+
except FileNotFoundError:
24+
pass
25+
26+
27+
# create namespaces
28+
namespace = invoke.Collection()
29+
namespace_clean = invoke.Collection('clean')
30+
namespace.add_collection(namespace_clean, 'clean')
31+
32+
#####
33+
#
34+
# pytest, tox, pylint, and codecov
35+
#
36+
#####
37+
@invoke.task
38+
def pytest(context):
39+
"Run tests and code coverage using pytest"
40+
context.run("pytest --cov=cmd2_submenu")
41+
namespace.add_task(pytest)
42+
43+
@invoke.task
44+
def pytest_clean(context):
45+
"Remove pytest cache and code coverage files and directories"
46+
#pylint: disable=unused-argument
47+
dirs = ['.pytest_cache', '.cache', '.coverage']
48+
rmrf(dirs)
49+
namespace_clean.add_task(pytest_clean, 'pytest')
50+
51+
@invoke.task
52+
def tox(context):
53+
"Run unit and integration tests on multiple python versions using tox"
54+
context.run("tox")
55+
namespace.add_task(tox)
56+
57+
@invoke.task
58+
def tox_clean(context):
59+
"Remove tox virtualenvs and logs"
60+
#pylint: disable=unused-argument
61+
rmrf('.tox')
62+
namespace_clean.add_task(tox_clean, 'tox')
63+
64+
@invoke.task
65+
def pylint(context):
66+
"Check code quality using pylint"
67+
context.run('pylint --rcfile=cmd2_submenu/pylintrc cmd2_submenu')
68+
namespace.add_task(pylint)
69+
70+
@invoke.task
71+
def pylint_tests(context):
72+
"Check code quality of test suite using pylint"
73+
context.run('pylint --rcfile=tests/pylintrc tests')
74+
namespace.add_task(pylint_tests)
75+
76+
77+
#####
78+
#
79+
# build and distribute
80+
#
81+
#####
82+
BUILDDIR = 'build'
83+
DISTDIR = 'dist'
84+
85+
@invoke.task
86+
def build_clean(context):
87+
"Remove the build directory"
88+
#pylint: disable=unused-argument
89+
rmrf(BUILDDIR)
90+
namespace_clean.add_task(build_clean, 'build')
91+
92+
@invoke.task
93+
def dist_clean(context):
94+
"Remove the dist directory"
95+
#pylint: disable=unused-argument
96+
rmrf(DISTDIR)
97+
namespace_clean.add_task(dist_clean, 'dist')
98+
99+
@invoke.task
100+
def eggs_clean(context):
101+
"Remove egg directories"
102+
#pylint: disable=unused-argument
103+
dirs = set()
104+
dirs.add('.eggs')
105+
for name in os.listdir(os.curdir):
106+
if name.endswith('.egg-info'):
107+
dirs.add(name)
108+
if name.endswith('.egg'):
109+
dirs.add(name)
110+
rmrf(dirs)
111+
namespace_clean.add_task(eggs_clean, 'eggs')
112+
113+
@invoke.task
114+
def bytecode_clean(context):
115+
"Remove __pycache__ directories and *.pyc files"
116+
#pylint: disable=unused-argument
117+
dirs = set()
118+
for root, dirnames, files in os.walk(os.curdir):
119+
if '__pycache__' in dirnames:
120+
dirs.add(os.path.join(root, '__pycache__'))
121+
for file in files:
122+
if file.endswith(".pyc"):
123+
dirs.add(os.path.join(root,file))
124+
print("Removing __pycache__ directories and .pyc files")
125+
rmrf(dirs, verbose=False)
126+
namespace_clean.add_task(bytecode_clean, 'bytecode')
127+
128+
#
129+
# make a dummy clean task which runs all the tasks in the clean namespace
130+
clean_tasks = list(namespace_clean.tasks.values())
131+
@invoke.task(pre=list(namespace_clean.tasks.values()), default=True)
132+
def clean_all(context):
133+
"Run all clean tasks"
134+
#pylint: disable=unused-argument
135+
pass
136+
namespace_clean.add_task(clean_all, 'all')
137+
138+
@invoke.task(pre=[clean_all])
139+
def sdist(context):
140+
"Create a source distribution"
141+
context.run('python setup.py sdist')
142+
namespace.add_task(sdist)
143+
144+
@invoke.task(pre=[clean_all])
145+
def wheel(context):
146+
"Build a wheel distribution"
147+
context.run('python setup.py bdist_wheel')
148+
namespace.add_task(wheel)
149+
150+
@invoke.task(pre=[sdist, wheel])
151+
def pypi(context):
152+
"Build and upload a distribution to pypi"
153+
context.run('twine upload dist/*')
154+
namespace.add_task(pypi)
155+
156+
@invoke.task(pre=[sdist, wheel])
157+
def pypi_test(context):
158+
"Build and upload a distribution to https://test.pypi.org"
159+
context.run('twine upload --repository-url https://test.pypi.org/legacy/ dist/*')
160+
namespace.add_task(pypi_test)

tests/pylintrc

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#
2+
# pylint configuration for tests package
3+
#
4+
# $ pylint --rcfile=tests/pylintrc tests
5+
#
6+
7+
[basic]
8+
# allow for longer method and function names
9+
method-rgx=(([a-z][a-z0-9_]{2,50})|(_[a-z0-9_]*))$
10+
function-rgx=(([a-z][a-z0-9_]{2,50})|(_[a-z0-9_]*))$
11+
12+
[messages control]
13+
# too-many-public-methods -> test classes can have lots of methods, so let's ignore those
14+
# missing-docstring -> prefer method names instead of docstrings
15+
# no-self-use -> test methods part of a class hardly ever use self
16+
# unused-variable -> sometimes we are expecting exceptions
17+
# redefined-outer-name -> pylint fixtures cause these
18+
# protected-access -> we want to test private methods
19+
disable=too-many-public-methods,missing-docstring,no-self-use,unused-variable,redefined-outer-name,protected-access

0 commit comments

Comments
 (0)