Add fixes to setup.py for the inability to import pip for parsing requirements files. Update tox with new coverage commands and only test on a single Python3 version. Create venv if one doesn't exist with direnv. Remove encoding pragma as this is no longer necessary as of ~Python3.6? Remove _compat.py as six does a better job and Python2.7 shouldn't be in use anymore. Add the requirements directory text files to the Manifest.in. Update versions of pre-commit repos.master
if [ ! -d venv ]; then | |||||
python3 -m venv venv | |||||
fi | |||||
source venv/bin/activate | source venv/bin/activate | ||||
unset PS1 | unset PS1 |
exclude: '^$' | |||||
exclude: '^(.*\.(svg))?$' | |||||
fail_fast: false | fail_fast: false | ||||
default_language_version: | default_language_version: | ||||
python: python3.9 | python: python3.9 | ||||
repos: | repos: | ||||
- repo: https://github.com/pre-commit/pre-commit-hooks | - repo: https://github.com/pre-commit/pre-commit-hooks | ||||
rev: v3.3.0 | |||||
rev: v4.1.0 | |||||
hooks: | hooks: | ||||
- id: check-added-large-files | - id: check-added-large-files | ||||
- id: check-ast | - id: check-ast | ||||
types: [file, text] | types: [file, text] | ||||
- id: end-of-file-fixer | - id: end-of-file-fixer | ||||
types: [file, python] | types: [file, python] | ||||
- id: fix-encoding-pragma | |||||
types: [file, python] | |||||
- id: requirements-txt-fixer | - id: requirements-txt-fixer | ||||
types: [file, text] | types: [file, text] | ||||
- id: trailing-whitespace | - id: trailing-whitespace | ||||
types: [file, text] | types: [file, text] | ||||
exclude: ^docs/.*/coverage/.*\.html$ | exclude: ^docs/.*/coverage/.*\.html$ | ||||
- repo: https://github.com/PyCQA/isort | - repo: https://github.com/PyCQA/isort | ||||
rev: 5.6.4 | |||||
rev: 5.10.1 | |||||
hooks: | hooks: | ||||
- id: isort | - id: isort | ||||
types: [file, python] | types: [file, python] | ||||
rev: stable | rev: stable | ||||
hooks: | hooks: | ||||
- id: black | - id: black | ||||
language_version: python3.8 | |||||
language_version: python3.9 | |||||
- repo: https://gitlab.com/pycqa/flake8 | - repo: https://gitlab.com/pycqa/flake8 | ||||
rev: 3.8.4 | |||||
rev: 4.0.1 | |||||
hooks: | hooks: | ||||
- id: flake8 | - id: flake8 | ||||
exclude: ^setup.py$ | exclude: ^setup.py$ |
recursive-include . *.gitkeep | recursive-include . *.gitkeep | ||||
recursive-include docs/source *.rst | recursive-include docs/source *.rst | ||||
recursive-include docs/source/coverage *.source.html | recursive-include docs/source/coverage *.source.html | ||||
recursive-include requirements *.txt | |||||
recursive-include tests * | recursive-include tests * | ||||
recursive-exclude * __pycache__ | recursive-exclude * __pycache__ | ||||
recursive-exclude * *.py[co] | recursive-exclude * *.py[co] |
#!/usr/bin/env python | #!/usr/bin/env python | ||||
# -*- coding: utf-8 -*- | |||||
import os | import os | ||||
import re | import re |
-r requirements/dev.txt | |||||
-r requirements/prod.txt | |||||
-r requirements/test.txt |
pylint; python_version >= "3.4" | pylint; python_version >= "3.4" | ||||
pylint-fail-under | pylint-fail-under | ||||
pytest | pytest | ||||
pytest-asyncio | |||||
pytest-cov | pytest-cov | ||||
pytest-mock | pytest-mock | ||||
pytest-runner | pytest-runner |
#!/usr/bin/env python | #!/usr/bin/env python | ||||
# -*- coding: utf-8 -*- | |||||
import pip | |||||
try: # for pip >= 10 | |||||
from pip._internal.req import parse_requirements | |||||
except ImportError: # for pip <= 9.0.3 | |||||
from pip.req import parse_requirements | |||||
try: | try: | ||||
from setuptools import setup | from setuptools import setup | ||||
except ImportError: | except ImportError: | ||||
from distutils.core import setup | from distutils.core import setup | ||||
with open("README.md") as readme_file: | |||||
readme = readme_file.read() | |||||
with open("HISTORY.md") as history_file: | |||||
history = history_file.read() | |||||
github_url = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}" | |||||
## workaround derived from: https://github.com/pypa/pip/issues/7645#issuecomment-578210649 | |||||
parsed_requirements = parse_requirements("requirements/prod.txt", session="workaround") | |||||
parsed_dev_requirements = parse_requirements( | |||||
"requirements/dev.txt", session="workaround" | |||||
) | |||||
parsed_test_requirements = parse_requirements( | |||||
"requirements/test.txt", session="workaround" | |||||
) | |||||
requirements = [str(ir.req) for ir in parsed_requirements] | |||||
dev_requirements = [str(ir.req) for ir in parsed_dev_requirements] | |||||
test_requirements = [str(tr.req) for tr in parsed_test_requirements] | |||||
DESCRIPTION = "{{ cookiecutter.project_short_description }}" | |||||
GITHUB_URL = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}" | |||||
with open("requirements/prod.txt", encoding="utf-8") as file: | |||||
requirements = file.read().split("\n") | |||||
with open("requirements/test.txt", encoding="utf-8") as file: | |||||
test_requirements = list( | |||||
filter( | |||||
lambda req: not req.startswith("-"), | |||||
file.read().split("\n"), | |||||
) | |||||
) | |||||
with open("requirements/dev.txt", encoding="utf-8") as file: | |||||
dev_requirements = list( | |||||
filter( | |||||
lambda req: not req.startswith("-"), | |||||
file.read().split("\n"), | |||||
) | |||||
) | |||||
test_requirements.extend(requirements) | |||||
dev_requirements.extend(test_requirements) | |||||
with open("README.md", encoding="utf-8") as file: | |||||
readme = file.read() | |||||
with open("HISTORY.md", encoding="utf-8") as file: | |||||
history = file.read() | |||||
setup( | setup( | ||||
name="{{ cookiecutter.project_slug }}", | name="{{ cookiecutter.project_slug }}", | ||||
version="{{ cookiecutter.version }}", | version="{{ cookiecutter.version }}", | ||||
description="{{ cookiecutter.project_short_description }}", | |||||
description=DESCRIPTION, | |||||
long_description=readme + "\n\n" + history, | long_description=readme + "\n\n" + history, | ||||
long_description_content_type="text/markdown", | long_description_content_type="text/markdown", | ||||
author="{{ cookiecutter.full_name }}", | author="{{ cookiecutter.full_name }}", | ||||
author_email="{{ cookiecutter.email }}", | author_email="{{ cookiecutter.email }}", | ||||
url=github_url, | |||||
url=GITHUB_URL, | |||||
license="LGPL-3", | license="LGPL-3", | ||||
keywords="{{ cookiecutter.project_slug }}", | keywords="{{ cookiecutter.project_slug }}", | ||||
project_urls={"Source": github_url}, | |||||
project_urls={"Source": GITHUB_URL}, | |||||
packages=[ | packages=[ | ||||
"{{ cookiecutter.project_slug }}", | "{{ cookiecutter.project_slug }}", | ||||
], | ], |
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- | |||||
""" | """ | ||||
Conftest Fixtures | Conftest Fixtures | ||||
absolute_import, | absolute_import, | ||||
division, | division, | ||||
print_function, | print_function, | ||||
with_statement, | |||||
unicode_literals, | unicode_literals, | ||||
with_statement, | |||||
) | ) | ||||
import logging | import logging | ||||
import mock | import mock | ||||
import pytest | import pytest | ||||
logger = logging.getLogger(__name__) # pylint: disable=invalid-name | logger = logging.getLogger(__name__) # pylint: disable=invalid-name | ||||
FIXTURE_PATH = "{}/fixtures".format(os.path.dirname(os.path.abspath(__file__))) | FIXTURE_PATH = "{}/fixtures".format(os.path.dirname(os.path.abspath(__file__))) |
# -*- coding: utf-8 -*- | |||||
""" | """ | ||||
Tests for `{{ cookiecutter.project_slug }}` module. | Tests for `{{ cookiecutter.project_slug }}` module. | ||||
""" | """ | ||||
absolute_import, | absolute_import, | ||||
division, | division, | ||||
print_function, | print_function, | ||||
unicode_literals, | |||||
with_statement, | with_statement, | ||||
unicode_literals | |||||
) | ) | ||||
import logging | import logging | ||||
import pytest | |||||
from {{ cookiecutter.project_slug }} import {{ (cookiecutter.project_slug.title()).replace('_', '') }} | |||||
logger = logging.getLogger(__name__) # pylint: disable=invalid-name | |||||
import pytest | |||||
from {{cookiecutter.project_slug}} import ( # pylint: disable=invalid-name | |||||
'' }}, | |||||
'_', | |||||
.replace, | |||||
=, | |||||
__name__, | |||||
logger, | |||||
logging.getLogger, | |||||
{{ cookiecutter.project_slug.title, | |||||
) | |||||
FIXTURE_PATH = "{}/fixtures".format(os.path.dirname(os.path.abspath(__file__))) | FIXTURE_PATH = "{}/fixtures".format(os.path.dirname(os.path.abspath(__file__))) | ||||
[tox] | [tox] | ||||
envlist = cov_init, py{37,38,39}, cov_report, docs, bandit, pylint | |||||
envlist = cov_init, py3, cov_report, docs, bandit, pylint | |||||
[testenv] | [testenv] | ||||
basepython = | basepython = | ||||
py3: {env:TOXPYTHON:python3} | |||||
py37: {env:TOXPYTHON:python3.7} | py37: {env:TOXPYTHON:python3.7} | ||||
py38: {env:TOXPYTHON:python3.8} | py38: {env:TOXPYTHON:python3.8} | ||||
py39: {env:TOXPYTHON:python3.9} | py39: {env:TOXPYTHON:python3.9} | ||||
{bandit,cov_init,cov_report,docs,lint,pur,pylint}: {env:TOXPYTHON:python3} | {bandit,cov_init,cov_report,docs,lint,pur,pylint}: {env:TOXPYTHON:python3} | ||||
setenv = | setenv = | ||||
COVERAGE_FILE=.coverage.{envname} | |||||
PYTHONPATH={toxinidir}:{toxinidir}/{{ cookiecutter.project_slug }} | PYTHONPATH={toxinidir}:{toxinidir}/{{ cookiecutter.project_slug }} | ||||
PYTHONUNBUFFERED=yes | PYTHONUNBUFFERED=yes | ||||
HOME={env:HOME:/tmp} | HOME={env:HOME:/tmp} | ||||
--basetemp={envtmpdir} \ | --basetemp={envtmpdir} \ | ||||
--confcutdir=.. \ | --confcutdir=.. \ | ||||
--cov \ | --cov \ | ||||
--cov-append \ | |||||
-n 0 \ | -n 0 \ | ||||
{posargs} | {posargs} | ||||
coverage | coverage | ||||
skip_install = True | skip_install = True | ||||
commands = | commands = | ||||
coverage combine | |||||
coverage html | coverage html | ||||
{toxinidir}/docs/source/coverage/extract_source.py | {toxinidir}/docs/source/coverage/extract_source.py | ||||
description = | description = | ||||
Invoke sphinx-build to build the HTML docs | Invoke sphinx-build to build the HTML docs | ||||
commands = | commands = | ||||
sphinx-build -b linkcheck | |||||
sphinx-build -b html -q {posargs} | |||||
python setup.py build_sphinx -q {posargs} |
#!/usr/bin/env python | #!/usr/bin/env python | ||||
# -*- coding: utf-8 -*- | |||||
""" | """ | ||||
{{ cookiecutter.project_slug }} | {{ cookiecutter.project_slug }} | ||||
absolute_import, | absolute_import, | ||||
division, | division, | ||||
print_function, | print_function, | ||||
with_statement, | |||||
unicode_literals, | unicode_literals, | ||||
with_statement, | |||||
) | ) | ||||
from pkgutil import extend_path | from pkgutil import extend_path | ||||
__path__ = extend_path(__path__, __name__) | __path__ = extend_path(__path__, __name__) | ||||
from .{{ cookiecutter.project_slug }} import {{ (cookiecutter.project_slug.title()).replace('_', '') }} | |||||
__all__ = ["{{ (cookiecutter.project_slug.title()).replace('_', '') }}"] | |||||
from .{{cookiecutter.project_slug}} import ( | |||||
'' }}, | |||||
'' }}"], | |||||
'_', | |||||
.replace, | |||||
=, | |||||
["{{ cookiecutter.project_slug.title, | |||||
__all__, | |||||
{{ cookiecutter.project_slug.title, | |||||
) |
import sys | |||||
# https://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/ | |||||
# https://github.com/tony/cookiecutter-pypackage-pythonic/blob/master/%7B%7Bcookiecutter.repo_name%7D%7D/%7B%7Bcookiecutter.repo_name%7D%7D/_compat.py | |||||
PY2 = sys.version_info[0] == 2 | |||||
_identity = lambda x: x | |||||
if PY2: | |||||
unichr = unichr | |||||
text_type = unicode | |||||
string_types = (str, unicode) | |||||
integer_types = (int, long) | |||||
from urllib import urlretrieve | |||||
text_to_native = lambda s, enc: s.encode(enc) | |||||
iterkeys = lambda d: d.iterkeys() | |||||
itervalues = lambda d: d.itervalues() | |||||
iteritems = lambda d: d.iteritems() | |||||
from cStringIO import StringIO as BytesIO | |||||
from StringIO import StringIO | |||||
import cPickle as pickle | |||||
import ConfigParser as configparser | |||||
from itertools import izip, imap | |||||
range_type = xrange | |||||
cmp = cmp | |||||
input = raw_input | |||||
from string import lower as ascii_lowercase | |||||
import urlparse | |||||
def console_to_str(s): | |||||
return s.decode("utf_8") | |||||
exec("def reraise(tp, value, tb=None):\n raise tp, value, tb") | |||||
else: | |||||
unichr = chr | |||||
text_type = str | |||||
string_types = (str,) | |||||
integer_types = (int,) | |||||
text_to_native = lambda s, enc: s | |||||
iterkeys = lambda d: iter(d.keys()) | |||||
itervalues = lambda d: iter(d.values()) | |||||
iteritems = lambda d: iter(d.items()) | |||||
from io import StringIO, BytesIO | |||||
import pickle | |||||
import configparser | |||||
izip = zip | |||||
imap = map | |||||
range_type = range | |||||
cmp = lambda a, b: (a > b) - (a < b) | |||||
input = input | |||||
from string import ascii_lowercase | |||||
import urllib.parse as urllib | |||||
import urllib.parse as urlparse | |||||
from urllib.request import urlretrieve | |||||
console_encoding = sys.__stdout__.encoding | |||||
def console_to_str(s): | |||||
""" From pypa/pip project, pip.backwardwardcompat. License MIT. """ | |||||
try: | |||||
return s.decode(console_encoding) | |||||
except UnicodeDecodeError: | |||||
return s.decode("utf_8") | |||||
def reraise(tp, value, tb=None): | |||||
if value.__traceback__ is not tb: | |||||
raise (value.with_traceback(tb)) | |||||
raise value | |||||
number_types = integer_types + (float,) |
#!/usr/bin/env python | #!/usr/bin/env python | ||||
# -*- coding: utf-8 -*- | |||||
from __future__ import ( | from __future__ import ( | ||||
absolute_import, | absolute_import, | ||||
division, | division, | ||||
print_function, | print_function, | ||||
unicode_literals, | |||||
with_statement, | with_statement, | ||||
unicode_literals | |||||
) | ) | ||||
import logging | import logging |