/
tasks.py
192 lines (145 loc) · 4.92 KB
/
tasks.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# Invoke tasks file
# Python 2/3 Makefile replacement
# To do:
# - Add commands to build docs
# Dependencies:
# six, packaging, pathlib, invoke
# Python2 (optional): subprocess32
from __future__ import (division, print_function,
absolute_import, unicode_literals)
import os
import shutil
import platform
import pathlib
import packaging.version
from six.moves import input
from invoke import task
# Use subprocess32 if available
try:
import subprocess32 as subprocess
except:
import subprocess as subprocess
cwd = pathlib.Path('.')
def check_output(*args, **kwargs):
"""Subprocess check_output, but prints commands and output by default.
Also allows printing of error message for helpful debugging.
Use print_all=False to turn off all printing."""
print_all = kwargs.pop('print_all', None)
if print_all is not None:
print_in = print_all
print_out = print_all
else:
print_in = kwargs.pop('print_in', True)
print_out = kwargs.pop('print_out', True)
if print_in:
print('')
print(' '.join(args[0]))
try:
out_bytes = subprocess.check_output(*args, **kwargs)
out_lines = out_bytes.decode('utf-8').splitlines()
except subprocess.CalledProcessError as e:
# Wrap in try/except so that check_output can print
raise e
if print_out:
for line in out_lines:
print(line)
return out_lines
windows = platform.system() == 'Windows'
def find_git_cmd(windows):
"""Determine whether the git command is git or git.cmd on Windows.
This changed in version 1.8.3"""
git = 'git'
if windows:
try:
check_output([git, '--version'])
except subprocess.CalledProcessError:
try:
git = 'git.cmd'
check_output([git, '--version'])
except subprocess.CalledProcessError:
msg = "git does not appear to be on your path."
raise subprocess.CalledProcessError(msg)
return git
git = find_git_cmd(windows)
# Helper functions for unix-like removing folders and files.
def rm_rf(*args):
"""Recursively delete directories, if they exist"""
for directory in args:
try:
shutil.rmtree(str(directory))
except OSError:
pass
def rm(*args):
"""Delete all files provided"""
for path in args:
try:
os.remove(str(args))
except OSError:
pass
# Helper functions for getting version numbers
def git_describe():
result = check_output(['git', 'describe', '--tags', '--dirty', '--always'])
return result[0]
def version():
result = check_output(['python', 'setup.py', '--version'])
return result[0]
@task(default=True)
def help():
print("""
Usage: inv[oke] [--core-opts] task1 [--task1-opts] ... taskN [--taskN-opts]
Tasks:
docs build the docs and open in a webbrowser
build_docs build the docs
clean Remove compiled python files, build directories
test Run tests for package (python setup.py test)
release Upload to PyPI after running tests and checking version number
To see more about a specific task, run invoke --help task""")
@task
def docs():
"""Use the docs subdirectory tasks.py to build and open docs."""
os.chdir('docs')
check_output(['invoke', 'html', 'open'])
@task
def build_docs():
"""Just build the docs using Sphinx, don't open in a browser"""
os.chdir('docs')
check_output(['invoke', 'html'])
@task
def clean():
"""Remove build directories, compiled python files"""
rm_rf('dist')
rm(*cwd.rglob("*.py[cod]"))
rm_rf('build', '__pycache__')
rm_rf(*cwd.glob('*.egg'))
@task
def test():
"""Test the package using python setup.py test"""
check_output(['python', 'setup.py', 'test'])
@task
def check_version():
"""Raise an error if the version number is not PEP440 compatible"""
packaging.version.Version(version())
print("Version okay.")
@task
def check_version_tag():
"""Before releasing, check that the version matches the git tag"""
git_describe_version = git_describe()
setuppy_version = version()
if setuppy_version == git_describe_version:
print("Versions match.")
elif 'dirty' in git_describe_version:
raise ValueError("""Working directory has uncommited changes.\
Commit before releasing.""")
else:
choice = input("Release without tagging\n(version {0})? [y/N]\n".format(
setuppy_version)).lower()
if choice[0] == 'y':
print("Continuing")
else:
raise ValueError("Stopping.")
@task(clean, test, check_version, check_version_tag)
def release():
"""Check that tests pass, the version is correct, then build source, wheel
distributions, upload to PyPI using twine."""
check_output(['python', 'setup.py', 'sdist', 'bdist_wheel'])
check_output(['twine', 'upload', 'dist/*'])