Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PEP 518 pyproject.toml, black, isort #22

Merged
merged 10 commits into from
Jul 5, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions .coveragerc

This file was deleted.

2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -14,6 +14,8 @@ matrix:
env: TOXENV=py38
- python: 3.8
env: TOXENV=pep8
- python: 3.8
env: TOXENV=fmt-check
- python: 3.8
env: TOXENV=mypy
- python: 3.8
45 changes: 40 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
@@ -113,6 +113,8 @@ as a single compressed file.

Packaging is configured by:

- ``pyproject.toml``

- ``setup.py``

- ``MANIFEST.in``
@@ -189,7 +191,7 @@ generated in the ``htmlcov`` folder showing each source file and which lines wer
unit testing. Open ``htmlcov/index.html`` in a web browser to view the report. Code coverage
reports help identify areas of the project that are currently not tested.

Code coverage is configured in the ``.coveragerc`` file.
Code coverage is configured in ``pyproject.toml``.

To pass arguments to ``pytest`` through ``tox``:

@@ -202,12 +204,32 @@ Code Style Checking

`PEP8 <https://www.python.org/dev/peps/pep-0008/>`_ is the universally accepted style
guide for Python code. PEP8 code compliance is verified using `flake8 <http://flake8.pycqa.org/>`_.
flake8 is configured in the ``[flake8]`` section of ``tox.ini``. Three extra flake8 plugins
flake8 is configured in the ``[flake8]`` section of ``tox.ini``. Extra flake8 plugins
are also included:

- ``pep8-naming``: Ensure functions, classes, and variables are named with correct casing.
- ``flake8-quotes``: Ensure that ``' '`` style string quoting is used consistently.
- ``flake8-import-order``: Ensure consistency in the way imports are grouped and sorted.

Automated Code Formatting
^^^^^^^^^^^^^^^^^^^^^^^^^

Code is automatically formatted using `black <https://github.com/psf/black>`_. Imports are
automatically sorted and grouped using `isort <https://github.com/timothycrosley/isort/>`_.

These tools are configured by:

- ``pyproject.toml``

To automatically format code, run:

.. code-block:: bash

(venv) $ tox -e fmt

To verify code has been formatted, such as in a CI job:

.. code-block:: bash

(venv) $ tox -e fmt-check

Generated Documentation
^^^^^^^^^^^^^^^^^^^^^^^
@@ -429,8 +451,21 @@ To configure PyCharm 2018.3 and newer to align to the code style used in this pr

- Tools | Python Integrated Tools | Docstrings | Docstring Format: Google

- (Optional) Settings | Search "Force parentheses"
- Settings | Search "Force parentheses"

- Editor | Code Style | Python | Wrapping and Braces | "From" Import Statements

- ☑ Force parentheses if multiline

Integrate Code Formatters
^^^^^^^^^^^^^^^^^^^^^^^^^

To integrate automatic code formatters into PyCharm, reference the following instructions:

- `black integration <https://black.readthedocs.io/en/stable/editor_integration.html#pycharm-intellij-idea>`_

- The File Watchers method (step 3) is recommended. This will run ``black`` on every save.

- `isort integration <https://github.com/timothycrosley/isort/wiki/isort-Plugins>`_

- The File Watchers method (option 1) is recommended. This will run ``isort`` on every save.
4 changes: 2 additions & 2 deletions dev-requirements.in
Original file line number Diff line number Diff line change
@@ -4,6 +4,6 @@ pytest-cov
mypy
flake8 >= 3.3.0
pep8-naming >= 0.4.1
flake8-quotes >= 0.8.1
flake8-import-order >= 0.9.0
black
isort
Sphinx
70 changes: 35 additions & 35 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -5,60 +5,60 @@
# pip-compile dev-requirements.in
#
alabaster==0.7.12 # via sphinx
appdirs==1.4.3 # via virtualenv
atomicwrites==1.3.0 # via pytest
attrs==19.3.0 # via pytest
appdirs==1.4.4 # via black, virtualenv
atomicwrites==1.4.0 # via pytest
attrs==19.3.0 # via black, pytest
babel==2.8.0 # via sphinx
certifi==2019.11.28 # via requests
black==19.10b0 # via -r dev-requirements.in
certifi==2020.6.20 # via requests
chardet==3.0.4 # via requests
click==7.1.2 # via black
colorama==0.4.3 # via pytest, sphinx, tox
coverage==5.0.4 # via pytest-cov
distlib==0.3.0 # via virtualenv
coverage==5.1 # via pytest-cov
distlib==0.3.1 # via virtualenv
docutils==0.16 # via sphinx
entrypoints==0.3 # via flake8
filelock==3.0.12 # via tox, virtualenv
flake8-import-order==0.18.1 # via -r dev-requirements.in
flake8-polyfill==1.0.2 # via pep8-naming
flake8-quotes==3.0.0 # via -r dev-requirements.in
flake8==3.7.9 # via -r dev-requirements.in, flake8-polyfill, flake8-quotes
idna==2.9 # via requests
flake8==3.8.3 # via -r dev-requirements.in, flake8-polyfill
idna==2.10 # via requests
imagesize==1.2.0 # via sphinx
importlib-metadata==1.6.0 # via pluggy, pytest, tox, virtualenv
jinja2==2.11.1 # via sphinx
isort==5.0.3 # via -r dev-requirements.in
jinja2==2.11.2 # via sphinx
markupsafe==1.1.1 # via jinja2
mccabe==0.6.1 # via flake8
more-itertools==8.2.0 # via pytest
more-itertools==8.4.0 # via pytest
mypy-extensions==0.4.3 # via mypy
mypy==0.770 # via -r dev-requirements.in
packaging==20.3 # via pytest, sphinx, tox
pep8-naming==0.10.0 # via -r dev-requirements.in
mypy==0.782 # via -r dev-requirements.in
packaging==20.4 # via pytest, sphinx, tox
pathspec==0.8.0 # via black
pep8-naming==0.11.1 # via -r dev-requirements.in
pluggy==0.13.1 # via pytest, tox
py==1.8.1 # via pytest, tox
pycodestyle==2.5.0 # via flake8, flake8-import-order
pyflakes==2.1.1 # via flake8
py==1.9.0 # via pytest, tox
pycodestyle==2.6.0 # via flake8
pyflakes==2.2.0 # via flake8
pygments==2.6.1 # via sphinx
pyparsing==2.4.6 # via packaging
pytest-cov==2.8.1 # via -r dev-requirements.in
pytest==5.4.1 # via -r dev-requirements.in, pytest-cov
pytz==2019.3 # via babel
requests==2.23.0 # via sphinx
six==1.14.0 # via packaging, tox, virtualenv
pyparsing==2.4.7 # via packaging
pytest-cov==2.10.0 # via -r dev-requirements.in
pytest==5.4.3 # via -r dev-requirements.in, pytest-cov
pytz==2020.1 # via babel
regex==2020.6.8 # via black
requests==2.24.0 # via sphinx
six==1.15.0 # via packaging, tox, virtualenv
snowballstemmer==2.0.0 # via sphinx
sphinx==2.4.4 # via -r dev-requirements.in
sphinx==3.1.2 # via -r dev-requirements.in
sphinxcontrib-applehelp==1.0.2 # via sphinx
sphinxcontrib-devhelp==1.0.2 # via sphinx
sphinxcontrib-htmlhelp==1.0.3 # via sphinx
sphinxcontrib-jsmath==1.0.1 # via sphinx
sphinxcontrib-qthelp==1.0.3 # via sphinx
sphinxcontrib-serializinghtml==1.1.4 # via sphinx
toml==0.10.0 # via tox
tox==3.14.6 # via -r dev-requirements.in
typed-ast==1.4.1 # via mypy
typing-extensions==3.7.4.1 # via mypy
urllib3==1.25.8 # via requests
virtualenv==20.0.15 # via tox
wcwidth==0.1.9 # via pytest
zipp==3.1.0 # via importlib-metadata
toml==0.10.1 # via black, tox
tox==3.16.1 # via -r dev-requirements.in
typed-ast==1.4.1 # via black, mypy
typing-extensions==3.7.4.2 # via mypy
urllib3==1.25.9 # via requests
virtualenv==20.0.25 # via tox
wcwidth==0.2.5 # via pytest

# The following packages are considered to be unsafe in a requirements file:
# setuptools
4 changes: 2 additions & 2 deletions docs/clean_docs.py
Original file line number Diff line number Diff line change
@@ -12,11 +12,11 @@

def main() -> None:
docs_dir = Path(__file__).resolve().parent
for folder in ('_build', 'apidoc'):
for folder in ("_build", "apidoc"):
delete_dir = docs_dir / folder
if delete_dir.exists():
shutil.rmtree(delete_dir)


if __name__ == '__main__':
if __name__ == "__main__":
main()
64 changes: 32 additions & 32 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -14,19 +14,20 @@

import os
import sys
sys.path.insert(0, os.path.abspath('../src'))

sys.path.insert(0, os.path.abspath("../src"))


# -- Project information -----------------------------------------------------

project = 'fact'
copyright = ''
author = 'a'
project = "fact"
copyright = ""
author = "a"

# The short X.Y version
version = ''
version = ""
# The full version, including alpha/beta/rc tags
release = ''
release = ""


# -- General configuration ---------------------------------------------------
@@ -39,24 +40,24 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
'sphinx.ext.todo',
'sphinx.ext.viewcode',
'sphinx.ext.napoleon',
"sphinx.ext.autodoc",
"sphinx.ext.doctest",
"sphinx.ext.todo",
"sphinx.ext.viewcode",
"sphinx.ext.napoleon",
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
source_suffix = ".rst"

# The master toctree document.
master_doc = 'index'
master_doc = "index"

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -68,7 +69,7 @@
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = None
@@ -79,21 +80,21 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
html_theme = "alabaster"

# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {
# Override the default alabaster line wrap, which wraps tightly at 940px.
'page_width': 'auto',
"page_width": "auto",
}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
@@ -109,7 +110,7 @@
# -- Options for HTMLHelp output ---------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = 'adoc'
htmlhelp_basename = "adoc"


# -- Options for LaTeX output ------------------------------------------------
@@ -118,15 +119,12 @@
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',

# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',

# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',

# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
@@ -136,19 +134,15 @@
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'a.tex', 'a Documentation',
'a', 'manual'),
(master_doc, "a.tex", "a Documentation", "a", "manual"),
]


# -- Options for manual page output ------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'a', 'a Documentation',
[author], 1)
]
man_pages = [(master_doc, "a", "a Documentation", [author], 1)]


# -- Options for Texinfo output ----------------------------------------------
@@ -157,9 +151,15 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'a', 'a Documentation',
author, 'a', 'One line description of project.',
'Miscellaneous'),
(
master_doc,
"a",
"a Documentation",
author,
"a",
"One line description of project.",
"Miscellaneous",
),
]


@@ -178,7 +178,7 @@
# epub_uid = ''

# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
epub_exclude_files = ["search.html"]


# -- Extension configuration -------------------------------------------------
41 changes: 41 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[build-system]
requires = ["setuptools>=46.0", "wheel"]
build-backend = "setuptools.build_meta"

[tool.black]
# Use the more relaxed max line length permitted in PEP8.
line-length = 99
target-version = ["py36", "py37", "py38"]
exclude = '''
/(
\.eggs
| \.git
| \.mypy_cache
| \.tox
| \venv
| build
| dist
| htmlcov
)/
'''

[tool.isort]
profile = "black"
line_length = 99
force_sort_within_sections = true
order_by_type = false
src_paths = ["docs", "src", "tests", "setup.py"]

[tool.coverage.run]
branch = true

[tool.coverage.paths]
# Files with these prefixes are treated as identical for the purposes of coverage combine.
source = [
# The first path is the name to which all paths get unified
"src/",
# tox on Linux
".tox/py*/lib/python*/site-packages/",
# tox on Windows
".tox/py*/Lib/site-packages/",
]
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile
# pip-compile requirements.in
#
colorama==0.4.3 # via -r requirements.in
exitstatus==1.4.0 # via -r requirements.in
exitstatus==2.0.1 # via -r requirements.in
57 changes: 23 additions & 34 deletions setup.py
Original file line number Diff line number Diff line change
@@ -7,56 +7,45 @@
project_dir = Path(__file__).parent

setuptools.setup(
name='fact',
version='1.0.0',

description='Example Python project',

name="fact",
version="1.0.0",
description="Example Python project",
# Allow UTF-8 characters in README with encoding argument.
long_description=project_dir.joinpath('README.rst').read_text(encoding='utf-8'),
keywords=['python'],

author='',
url='https://github.com/johnthagen/python-blueprint',

packages=setuptools.find_packages('src'),
package_dir={'': 'src'},

long_description=project_dir.joinpath("README.rst").read_text(encoding="utf-8"),
keywords=["python"],
author="",
url="https://github.com/johnthagen/python-blueprint",
packages=setuptools.find_packages("src"),
package_dir={"": "src"},
# pip 9.0+ will inspect this field when installing to help users install a
# compatible version of the library for their Python version.
python_requires='>=3.6',

python_requires=">=3.6",
# There are some peculiarities on how to include package data for source
# distributions using setuptools. You also need to add entries for package
# data to MANIFEST.in.
# See https://stackoverflow.com/questions/7522250/
include_package_data=True,

# This is a trick to avoid duplicating dependencies between both setup.py and
# requirements.txt.
# requirements.txt must be included in MANIFEST.in for this to work.
# It does not work for all types of dependencies (e.g. VCS dependencies).
# For VCS dependencies, use pip >= 19 and the PEP 508 syntax.
# Example: 'requests @ git+https://github.com/requests/requests.git@branch_or_tag'
# See: https://github.com/pypa/pip/issues/6162
install_requires=project_dir.joinpath('requirements.txt').read_text().split('\n'),
install_requires=project_dir.joinpath("requirements.txt").read_text().split("\n"),
zip_safe=False,

license='MIT',
license_files=['LICENSE.txt'],
license="MIT",
license_files=["LICENSE.txt"],
classifiers=[
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
],

entry_points={
'console_scripts': ['fact=fact.cli:main'],
}
entry_points={"console_scripts": ["fact=fact.cli:main"]},
)
19 changes: 10 additions & 9 deletions src/fact/cli.py
Original file line number Diff line number Diff line change
@@ -11,12 +11,11 @@

def parse_args() -> argparse.Namespace:
"""Parse user command line arguments."""
parser = argparse.ArgumentParser(description='Compute factorial of a given input.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-n',
type=int,
required=True,
help='The input n of fact(n).')
parser = argparse.ArgumentParser(
description="Compute factorial of a given input.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument("-n", type=int, required=True, help="The input n of fact(n).")
return parser.parse_args()


@@ -25,11 +24,13 @@ def main() -> ExitStatus:
colorama.init(autoreset=True, strip=False)
args = parse_args()

print(f'fact({colorama.Fore.CYAN}{args.n}{colorama.Fore.RESET}) = '
f'{colorama.Fore.GREEN}{factorial(args.n)}{colorama.Fore.RESET}')
print(
f"fact({colorama.Fore.CYAN}{args.n}{colorama.Fore.RESET}) = "
f"{colorama.Fore.GREEN}{factorial(args.n)}{colorama.Fore.RESET}"
)
return ExitStatus.success


# Allow the script to be run standalone (useful during development in PyCharm).
if __name__ == '__main__':
if __name__ == "__main__":
sys.exit(main())
2 changes: 1 addition & 1 deletion src/fact/lib.py
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ def factorial(n: int) -> int:
Computed factorial.
"""
if n < 0:
raise InvalidFactorialError(f'n is less than zero: {n}')
raise InvalidFactorialError(f"n is less than zero: {n}")
elif n == 0:
return 1

4 changes: 4 additions & 0 deletions tests/test_fact.py
Original file line number Diff line number Diff line change
@@ -3,20 +3,24 @@
from fact.lib import factorial, InvalidFactorialError


# fmt: off
@pytest.mark.parametrize('n,expected', [
(1, 1),
(2, 2),
(3, 6),
(10, 3628800),
])
# fmt: on
def test_factorial(n: int, expected: int) -> None:
assert factorial(n) == expected


# fmt: off
@pytest.mark.parametrize('n', [
(-1),
(-100),
])
# fmt: on
def test_invalid_factorial(n: int) -> None:
with pytest.raises(InvalidFactorialError):
factorial(n)
31 changes: 24 additions & 7 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -5,21 +5,20 @@ envlist =
py{36,37,38,py3}
coverage
mypy
fmt
pep8
docs
skip_missing_interpreters = true

# Activate isolated build environment. tox will use a virtual environment
# to build a source distribution from the source tree. For build tools and
# arguments use the pyproject.toml file as specified in PEP-517 and PEP-518.
isolated_build = true

[flake8]
# Use the more relaxed max line length permitted in PEP8.
max-line-length = 99

# Enforce the Google Python style for grouping and sorting imports:
# https://github.com/google/styleguide/blob/gh-pages/pyguide.md#313-imports-formatting
import-order-style = google

# Inform flake8-import-order plugin that `fact` should be treated as a local package name.
application-import-names = fact

[testenv]
setenv =
COVERAGE_FILE = .coverage.{envname}
@@ -71,6 +70,24 @@ commands =
sphinx-apidoc --force --output-dir apidoc {toxinidir}/src/fact
sphinx-build -a -W . _build

[testenv:fmt]
skip_install = true
setenv =
deps =
-r{toxinidir}/dev-requirements.txt
commands =
isort .
black .

[testenv:fmt-check]
skip_install = true
setenv =
deps =
-r{toxinidir}/dev-requirements.txt
commands =
isort --check-only .
black --check .

[testenv:licenses]
skip_install = true
recreate = true