Commit
Improvements and lessons-learned
Lessons learned (from hookmeup):
1. Use a Makefile for as many commands as possible
2. Force all dependency management through Pipenv
3. On that note, always use Pipenv
4. Add support for Tox, Travis CI, and Coveralls
Daniel Moch committed 5 years ago
(
Tree)
Diffstat
cookiecutter.json | 1
hooks/post_gen_project.py | 18 ++++++----
hooks/pre_gen_project.py | 7 ++-
{{cookiecutter.project_slug}}/.travis.yml | 41 +++++++++++++++++++++++++
{{cookiecutter.project_slug}}/Makefile | 40 ++++++++++++-----------
{{cookiecutter.project_slug}}/Pipfile | 8 +---
{{cookiecutter.project_slug}}/tox.ini | 29 +++++++++++++++++
cookiecutter.json
8 |
8 |
"project_short_description": "Flit Boilerplate contains all the boilerplate you need to create a Python Flit package.", |
9 |
9 |
"pypi_username": "{{ cookiecutter.github_username }}", |
10 |
10 |
"version": "0.1.0", |
11 |
|
- "use_pipenv": "y", |
12 |
11 |
"script_entrypoint": "y", |
13 |
12 |
"open_source_license": ["MIT license", "BSD license", "ISC license", "Apache Software License 2.0", "GNU General Public License v3", "Not open source"] |
14 |
13 |
} |
hooks/post_gen_project.py
1 |
|
-#!/usr/bin/env python3 |
|
1 |
+# -*- encoding: utf-8 *-* |
|
2 |
+"""Hook to run after template is created""" |
2 |
3 |
import os |
3 |
4 |
import subprocess |
|
5 |
+from subprocess import CalledProcessError |
4 |
6 |
|
5 |
7 |
PROJECT_DIRECTORY = os.path.realpath(os.path.curdir) |
6 |
|
- |
7 |
8 |
|
8 |
9 |
def remove_file(filepath): |
|
10 |
+ """Remove a file from the generated template""" |
9 |
11 |
os.remove(os.path.join(PROJECT_DIRECTORY, filepath)) |
10 |
|
- |
11 |
12 |
|
12 |
13 |
if __name__ == '__main__': |
13 |
|
- |
14 |
14 |
if 'Not open source' == '{{ cookiecutter.open_source_license }}': |
15 |
15 |
remove_file('LICENSE') |
16 |
16 |
|
17 |
|
- if '{{ cookiecutter.use_pipenv }}' == 'n': |
18 |
|
- remove_file('Pipfile') |
19 |
|
- else: |
|
17 |
+ try: |
|
18 |
+ subprocess.run( |
|
19 |
+ ['which', |
|
20 |
+ 'pipenv'], |
|
21 |
+ stdout=subprocess.DEVNULL).check_returncode() |
20 |
22 |
subprocess.run(['pipenv', 'lock', '--dev']) |
21 |
23 |
subprocess.run(['pipenv', 'sync', '--dev']) |
|
24 |
+ except CalledProcessError: |
|
25 |
+ print('pipenv not installed, or not available on PATH') |
hooks/pre_gen_project.py
|
1 |
+# -*- encoding: utf-8 -*- |
|
2 |
+"""Hook to run before the template is generated""" |
1 |
3 |
import re |
2 |
4 |
import sys |
3 |
5 |
|
4 |
|
- |
5 |
6 |
MODULE_REGEX = r'^[_a-zA-Z][_a-zA-Z0-9]+$' |
6 |
7 |
|
7 |
|
-module_name = '{{ cookiecutter.project_slug}}' |
|
8 |
+module_name = '{{ cookiecutter.project_slug }}' |
8 |
9 |
|
9 |
10 |
if not re.match(MODULE_REGEX, module_name): |
10 |
11 |
print('ERROR: The project slug (%s) is not a valid Python module name. Please do not use a - and use _ instead' % module_name) |
11 |
12 |
|
12 |
13 |
#Exit to cancel project |
13 |
|
- sys.exit(1) |
|
14 |
+ sys.exit(1) |
{{cookiecutter.project_slug}}/.travis.yml (created)
|
1 |
+# https://travis-ci.org/djmoch/{{ cookiecutter.project_slug }} |
|
2 |
+language: python |
|
3 |
+ |
|
4 |
+matrix: |
|
5 |
+ fast_finish: true |
|
6 |
+ |
|
7 |
+.mixins: |
|
8 |
+- &xenial-mixin |
|
9 |
+ dist: xenial |
|
10 |
+ sudo: true |
|
11 |
+ addons: |
|
12 |
+ apt: |
|
13 |
+ packages: |
|
14 |
+ - libgnutls-dev |
|
15 |
+ |
|
16 |
+env: |
|
17 |
+ - PIPENV_HIDE_EMOJIS=1 PIPENV_NO_INHERIT=1 |
|
18 |
+ |
|
19 |
+install: |
|
20 |
+ - pip install pipenv python-coveralls |
|
21 |
+ - make test-install |
|
22 |
+ |
|
23 |
+jobs: |
|
24 |
+ include: |
|
25 |
+ - stage: test |
|
26 |
+ script: make test |
|
27 |
+ python: 3.5 |
|
28 |
+ - stage: test |
|
29 |
+ script: make test |
|
30 |
+ python: 3.6 |
|
31 |
+ - stage: test |
|
32 |
+ script: make test |
|
33 |
+ <<: *xenial-mixin |
|
34 |
+ python: 3.7 |
|
35 |
+ - stage: lint |
|
36 |
+ script: make lint |
|
37 |
+ python: 3.6 |
|
38 |
+ - stage: coverage |
|
39 |
+ script: make coverage |
|
40 |
+ after_success: coveralls |
|
41 |
+ python: 3.5 |
{{cookiecutter.project_slug}}/Makefile
1 |
|
-.PHONY: clean clean-test clean-pyc clean-build help |
|
1 |
+.PHONY: clean clean-test clean-pyc clean-build help lint coverage coverage-html release dist install run debug |
2 |
2 |
.DEFAULT_GOAL := help |
3 |
3 |
|
4 |
4 |
define BROWSER_PYSCRIPT |
|
|
. . . |
26 |
26 |
|
27 |
27 |
BROWSER := python -c "$$BROWSER_PYSCRIPT" |
28 |
28 |
|
29 |
|
-# Comment out the following line to run outside of Pipenv |
30 |
|
-{%- if cookiecutter.use_pipenv == 'y' %} |
31 |
|
-PIPENV := pipenv run |
32 |
|
-{%- else %} |
33 |
|
-# PIPENV := pipenv run |
34 |
|
-{%- endif %} |
35 |
|
- |
36 |
29 |
help: |
37 |
30 |
@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) |
38 |
31 |
|
|
|
. . . |
55 |
48 |
rm -f .coverage |
56 |
49 |
rm -fr htmlcov/ |
57 |
50 |
rm -fr .pytest_cache |
|
51 |
+ rm -fr .tox |
58 |
52 |
|
59 |
53 |
lint: ## check style with pylint |
60 |
|
- $(PIPENV) pylint --rcfile tests/pylintrc {{ cookiecutter.project_slug }} tests |
|
54 |
+ pipenv run pylint --rcfile tests/pylintrc {{ cookiecutter.project_slug }} tests --disable=parse-error |
61 |
55 |
|
62 |
56 |
test: ## run tests quickly with the default Python |
63 |
|
- $(PIPENV) python -m pytest |
|
57 |
+ pipenv run python -m pytest |
|
58 |
+ |
|
59 |
+test-install: ## install dependenices from Pipfile (for tox / CI builds) |
|
60 |
+ pipenv --bare install --dev --skip-lock |
64 |
61 |
|
65 |
62 |
coverage: ## check code coverage quickly with the default Python |
66 |
|
- $(PIPENV) python -m pytest |
67 |
|
- $(PIPENV) coverage report -m |
68 |
|
- $(PIPENV) coverage html |
|
63 |
+ pipenv run python -m pytest --cov={{ cookiecutter.project_slug }} --cov-config tests/coveragerc |
|
64 |
+ pipenv run coverage report -m |
|
65 |
+ |
|
66 |
+coverage-html: coverage ## generate an HTML report and open in browser |
|
67 |
+ pipenv run coverage html |
69 |
68 |
$(BROWSER) htmlcov/index.html |
70 |
69 |
|
71 |
70 |
release: dist ## package and upload a release |
72 |
|
- $(PIPENV) flit publish |
|
71 |
+ pipenv run flit publish |
73 |
72 |
|
74 |
|
-dist: clean ## builds source and wheel package |
75 |
|
- $(PIPENV) flit build |
|
73 |
+dist: ## builds source and wheel package |
|
74 |
+ pipenv run flit build |
76 |
75 |
ls -l dist |
77 |
76 |
|
78 |
|
-install: clean ## install the package to the active Python's site-packages |
79 |
|
- $(PIPENV) flit install |
|
77 |
+install: ## install the package to the active Python's site-packages |
|
78 |
+ pipenv run flit install |
|
79 |
+ |
|
80 |
+run: install ## run the package from site-packages |
|
81 |
+ pipenv run {{ cookiecutter.project_slug }} |
80 |
82 |
|
81 |
83 |
debug: install ## debug the package from site packages |
82 |
|
- $(PIPENV) pudb3 `$(PIPENV) which {{ cookiecutter.project_slug }}` install |
|
84 |
+ pipenv run pudb3 `pipenv run which {{ cookiecutter.project_slug }}` |
{{cookiecutter.project_slug}}/Pipfile
4 |
4 |
name = "pypi" |
5 |
5 |
|
6 |
6 |
[packages] |
|
7 |
+flit = "*" |
7 |
8 |
|
8 |
9 |
[dev-packages] |
9 |
|
-pytest = "<3.7.0" |
|
10 |
+pytest = "*" |
10 |
11 |
pytest-cov = "*" |
11 |
|
-pytest-pylint = "*" |
|
12 |
+pylint = "*" |
12 |
13 |
pudb = "*" |
13 |
|
- |
14 |
|
-[requires] |
15 |
|
-python_version = "3.7" |
{{cookiecutter.project_slug}}/tox.ini (created)
|
1 |
+[tox] |
|
2 |
+envlist = py35, py36, py37, pylint, coverage |
|
3 |
+skipsdist = True |
|
4 |
+ |
|
5 |
+[testenv:pylint] |
|
6 |
+deps = pylint |
|
7 |
+whitelist_externals = make |
|
8 |
+commands = make lint |
|
9 |
+ |
|
10 |
+[testenv:coverage] |
|
11 |
+deps = pipenv |
|
12 |
+setenv = |
|
13 |
+ PIPENV_NO_INHERIT = 1 |
|
14 |
+ PIPENV_HIDE_EMOJIS = 1 |
|
15 |
+whitelist_externals = make |
|
16 |
+commands = |
|
17 |
+ make test-install |
|
18 |
+ make coverage |
|
19 |
+ |
|
20 |
+[testenv] |
|
21 |
+deps = pipenv |
|
22 |
+setenv = |
|
23 |
+ PIPENV_NO_INHERIT = 1 |
|
24 |
+ PIPENV_HIDE_EMOJIS = 1 |
|
25 |
+whitelist_externals = make |
|
26 |
+commands = |
|
27 |
+ make test-install |
|
28 |
+ make install |
|
29 |
+ make test |