Skip to content

Commit 270735d

Browse files
committed
Switch to use compas_invocations for tasks.py
1 parent fac33fe commit 270735d

File tree

4 files changed

+39
-299
lines changed

4 files changed

+39
-299
lines changed

docs/devguide.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ The procedure for submitting a PR is the following.
7474
Style guide
7575
===========
7676

77-
Please run `black <path_to_source_file>` to auto-format your code.
77+
Please run ``invoke format`` to auto-format your code.
7878

79-
The command ``invoke lint`` runs the entire codebase through ``flake8``.
79+
The command ``invoke lint`` runs the entire codebase through both ``black`` and ``flake8``.
8080
As described in the `docs <https://flake8.pycqa.org/en/latest/manpage.html>`_,
8181
``flake8`` includes lint checks provided by the PyFlakes project,
8282
PEP-0008 inspired style checks provided by the PyCodeStyle project,

requirements-dev.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1-
black
21
attrs >=17.4
2+
black
33
bump2version >=1.0.1
44
check-manifest >=0.36
5+
compas_invocations
56
doc8
67
flake8
78
graphviz
89
invoke >=0.14
910
ipykernel
1011
ipython >=5.8
1112
isort
13+
jinja2 >= 3.0
1214
m2r2
1315
nbsphinx
1416
pydocstyle
1517
pytest <7.1
16-
sphinx_compas_theme >=0.15.18
1718
sphinx ==4.5
19+
sphinx_compas_theme >=0.15.18
1820
twine
1921
wheel
20-
jinja2 >= 3.0
2122
-e .

setup.cfg

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ universal = 1
33

44
[flake8]
55
max-line-length = 120
6-
exclude = */migrations/*
7-
extend-ignore = E203
6+
extend-ignore =
7+
# See https://github.com/PyCQA/pycodestyle/issues/373
8+
E203,
9+
# Only keep black line length check because flake8 forces it on comments as well
10+
E501,
811

912
[doc8]
1013
max-line-length = 120

tasks.py

Lines changed: 28 additions & 292 deletions
Original file line numberDiff line numberDiff line change
@@ -1,299 +1,35 @@
1-
# -*- coding: utf-8 -*-
21
from __future__ import print_function
32

4-
import contextlib
5-
import glob
63
import os
7-
import shutil
8-
import sys
9-
import tempfile
104

11-
from invoke import Exit
12-
from invoke import task
13-
14-
BASE_FOLDER = os.path.dirname(__file__)
15-
16-
17-
class Log(object):
18-
def __init__(self, out=sys.stdout, err=sys.stderr):
19-
self.out = out
20-
self.err = err
21-
22-
def flush(self):
23-
self.out.flush()
24-
self.err.flush()
25-
26-
def write(self, message):
27-
self.flush()
28-
self.out.write(message + "\n")
29-
self.out.flush()
30-
31-
def info(self, message):
32-
self.write("[INFO] %s" % message)
33-
34-
def warn(self, message):
35-
self.write("[WARN] %s" % message)
36-
37-
38-
log = Log()
39-
40-
41-
def confirm(question):
42-
while True:
43-
response = input(question).lower().strip()
44-
45-
if not response or response in ("n", "no"):
46-
return False
47-
48-
if response in ("y", "yes"):
49-
return True
50-
51-
print("Focus, kid! It is either (y)es or (n)o", file=sys.stderr)
52-
53-
54-
@task(default=True)
55-
def help(ctx):
56-
"""Lists available tasks and usage."""
57-
ctx.run("invoke --list")
58-
log.write('Use "invoke -h <taskname>" to get detailed help for a task.')
59-
60-
61-
@task(
62-
help={
63-
"docs": "True to clean up generated documentation, otherwise False",
64-
"bytecode": "True to clean up compiled python files, otherwise False.",
65-
"builds": "True to clean up build/packaging artifacts, otherwise False.",
66-
}
5+
from compas_invocations import build
6+
from compas_invocations import docs
7+
from compas_invocations import style
8+
from compas_invocations import tests
9+
from invoke import Collection
10+
11+
ns = Collection(
12+
docs.help,
13+
style.check,
14+
style.lint,
15+
style.format,
16+
docs.docs,
17+
docs.linkcheck,
18+
tests.test,
19+
tests.testdocs,
20+
tests.testcodeblocks,
21+
build.prepare_changelog,
22+
build.clean,
23+
build.release,
24+
build.build_ghuser_components,
6725
)
68-
def clean(ctx, docs=True, bytecode=True, builds=True, ghuser=True):
69-
"""Cleans the local copy from compiled artifacts."""
70-
71-
with chdir(BASE_FOLDER):
72-
if builds:
73-
ctx.run("python setup.py clean")
74-
75-
if bytecode:
76-
for root, dirs, files in os.walk(BASE_FOLDER):
77-
for f in files:
78-
if f.endswith(".pyc"):
79-
os.remove(os.path.join(root, f))
80-
if ".git" in dirs:
81-
dirs.remove(".git")
82-
83-
folders = []
84-
85-
if docs:
86-
folders.append("docs/api/generated")
87-
88-
folders.append("dist/")
89-
90-
if bytecode:
91-
for t in ("src", "tests"):
92-
folders.extend(glob.glob("{}/**/__pycache__".format(t), recursive=True))
93-
94-
if builds:
95-
folders.append("build/")
96-
folders.append("src/compas.egg-info/")
97-
98-
if ghuser:
99-
folders.append("src/compas_ghpython/components/ghuser")
100-
101-
for folder in folders:
102-
shutil.rmtree(os.path.join(BASE_FOLDER, folder), ignore_errors=True)
103-
104-
105-
@task(
106-
help={
107-
"rebuild": "True to clean all previously built docs before starting, otherwise False.",
108-
"doctest": "True to run doctests, otherwise False.",
109-
"check_links": "True to check all web links in docs for validity, otherwise False.",
26+
ns.configure(
27+
{
28+
"base_folder": os.path.dirname(__file__),
29+
"ghuser": {
30+
"source_dir": "src/compas_ghpython/components",
31+
"target_dir": "src/compas_ghpython/components/ghuser",
32+
"prefix": "(COMPAS)",
33+
},
11034
}
11135
)
112-
def docs(ctx, doctest=False, rebuild=False, check_links=False):
113-
"""Builds package's HTML documentation."""
114-
115-
if rebuild:
116-
clean(ctx)
117-
118-
with chdir(BASE_FOLDER):
119-
if doctest:
120-
testdocs(ctx, rebuild=rebuild)
121-
122-
opts = "-E" if rebuild else ""
123-
ctx.run("sphinx-build {} -b html docs dist/docs".format(opts))
124-
125-
if check_links:
126-
linkcheck(ctx, rebuild=rebuild)
127-
128-
129-
@task()
130-
def lint(ctx):
131-
"""Check the consistency of coding style."""
132-
log.write("Running black python linter...")
133-
ctx.run("black --check --diff --color src tests")
134-
135-
136-
@task()
137-
def format(ctx):
138-
"""Reformat the code base using black."""
139-
log.write("Running black python formatter...")
140-
ctx.run("black src tests")
141-
142-
143-
@task()
144-
def testdocs(ctx, rebuild=False):
145-
"""Test the examples in the docstrings."""
146-
log.write("Running doctest...")
147-
ctx.run("pytest --doctest-modules")
148-
149-
150-
@task()
151-
def linkcheck(ctx, rebuild=False):
152-
"""Check links in documentation."""
153-
log.write("Running link check...")
154-
opts = "-E" if rebuild else ""
155-
ctx.run("sphinx-build {} -b linkcheck docs dist/docs".format(opts))
156-
157-
158-
@task()
159-
def check(ctx):
160-
"""Check the consistency of documentation, coding style and a few other things."""
161-
162-
with chdir(BASE_FOLDER):
163-
lint(ctx)
164-
165-
log.write("Checking MANIFEST.in...")
166-
ctx.run("check-manifest")
167-
168-
log.write("Checking metadata...")
169-
ctx.run("python setup.py check --strict --metadata")
170-
171-
172-
@task(
173-
help={
174-
"checks": "True to run all checks before testing, otherwise False.",
175-
"doctest": "True to run doctest on all modules, otherwise False.",
176-
}
177-
)
178-
def test(ctx, checks=False, doctest=False):
179-
"""Run all tests."""
180-
if checks:
181-
check(ctx)
182-
183-
with chdir(BASE_FOLDER):
184-
cmd = ["pytest"]
185-
if doctest:
186-
cmd.append("--doctest-modules")
187-
188-
ctx.run(" ".join(cmd))
189-
190-
191-
@task
192-
def prepare_changelog(ctx):
193-
"""Prepare changelog for next release."""
194-
UNRELEASED_CHANGELOG_TEMPLATE = (
195-
"## Unreleased\n\n### Added\n\n### Changed\n\n### Removed\n\n\n## "
196-
)
197-
198-
with chdir(BASE_FOLDER):
199-
# Preparing changelog for next release
200-
with open("CHANGELOG.md", "r+") as changelog:
201-
content = changelog.read()
202-
changelog.seek(0)
203-
changelog.write(content.replace("## ", UNRELEASED_CHANGELOG_TEMPLATE, 1))
204-
205-
ctx.run(
206-
'git add CHANGELOG.md && git commit -m "Prepare changelog for next release"'
207-
)
208-
209-
210-
@task(
211-
help={
212-
"gh_io_folder": "Folder where GH_IO.dll is located. If not specified, it will try to download from NuGet.",
213-
"ironpython": "Command for running the IronPython executable. Defaults to `ipy`.",
214-
}
215-
)
216-
def build_ghuser_components(ctx, gh_io_folder=None, ironpython=None):
217-
"""Build Grasshopper user objects from source"""
218-
clean(ctx, docs=False, bytecode=False, builds=False, ghuser=True)
219-
with chdir(BASE_FOLDER):
220-
with tempfile.TemporaryDirectory("actions.ghcomponentizer") as action_dir:
221-
source_dir = os.path.abspath("src/compas_ghpython/components")
222-
target_dir = os.path.join(source_dir, "ghuser")
223-
ctx.run(
224-
"git clone https://github.com/compas-dev/compas-actions.ghpython_components.git {}".format(
225-
action_dir
226-
)
227-
)
228-
229-
if not gh_io_folder:
230-
gh_io_folder = "temp"
231-
import compas_ghpython
232-
233-
compas_ghpython.fetch_ghio_lib(gh_io_folder)
234-
235-
if not ironpython:
236-
ironpython = "ipy"
237-
238-
gh_io_folder = os.path.abspath(gh_io_folder)
239-
componentizer_script = os.path.join(action_dir, "componentize.py")
240-
241-
ctx.run(
242-
'{} {} {} {} --ghio "{}"'.format(
243-
ironpython,
244-
componentizer_script,
245-
source_dir,
246-
target_dir,
247-
gh_io_folder,
248-
)
249-
)
250-
251-
252-
@task(
253-
help={
254-
"release_type": "Type of release follows semver rules. Must be one of: major, minor, patch."
255-
}
256-
)
257-
def release(ctx, release_type):
258-
"""Releases the project in one swift command!"""
259-
if release_type not in ("patch", "minor", "major"):
260-
raise Exit(
261-
"The release type parameter is invalid.\nMust be one of: major, minor, patch"
262-
)
263-
264-
# Run formmatter
265-
ctx.run("invoke format")
266-
267-
# Run checks
268-
ctx.run("invoke check test")
269-
270-
# Bump version and git tag it
271-
ctx.run("bump2version %s --verbose" % release_type)
272-
273-
# Build project
274-
ctx.run("python setup.py clean --all sdist bdist_wheel")
275-
276-
# Prepare changelog for next release
277-
prepare_changelog(ctx)
278-
279-
# Clean up local artifacts
280-
clean(ctx)
281-
282-
# Upload to pypi
283-
if confirm(
284-
"Everything is ready. You are about to push to git which will trigger a release to pypi.org. Are you sure? [y/N]"
285-
):
286-
ctx.run("git push --tags && git push")
287-
else:
288-
raise Exit("You need to manually revert the tag/commits created.")
289-
290-
291-
@contextlib.contextmanager
292-
def chdir(dirname=None):
293-
current_dir = os.getcwd()
294-
try:
295-
if dirname is not None:
296-
os.chdir(dirname)
297-
yield
298-
finally:
299-
os.chdir(current_dir)

0 commit comments

Comments
 (0)