Skip to content

Commit

Permalink
Merge pull request #417 from zestsoftware/reinout-precommit
Browse files Browse the repository at this point in the history
Precommit for automatic black/flake8/isort
  • Loading branch information
mauritsvanrees committed Jul 12, 2023
2 parents ac641d7 + b7fa0da commit 9bc6d16
Show file tree
Hide file tree
Showing 18 changed files with 181 additions and 93 deletions.
3 changes: 3 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[flake8]
max-line-length = 88
ignore = E203, E501, W503
21 changes: 21 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Lint

on:
push:
branches:
- master
pull_request:

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"

- name: Run black, flake8, isort
uses: pre-commit/[email protected]
22 changes: 22 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
default_language_version:
python: python3

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-toml
- repo: https://github.com/pycqa/isort
rev: '5.12.0'
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: '23.7.0'
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: '6.0.0'
hooks:
- id: flake8
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Changelog for zest.releaser

- Zest.releaser's settings can now also be placed in ``pyproject.toml``.

- Added pre-commit config for neater code (black, flake8, isort).


8.0.0 (2023-05-05)
------------------
Expand Down
2 changes: 1 addition & 1 deletion doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
("index", "%s.tex" % project, u"%s Documentation" % project, author, "manual"),
("index", "%s.tex" % project, "%s Documentation" % project, author, "manual"),
]

# The name of an image file (relative to this directory) to place at the top of
Expand Down
35 changes: 24 additions & 11 deletions doc/source/developing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,31 @@ Running tests

Actually, this should be easy now, because we use tox.
So ``pip install tox`` somewhere, probably in a virtualenv, maybe the current directory,
and call it:
and call it::

tox
$ tox

You probably want to run the tests for all environments in parallel::

tox -p auto
$ tox -p auto

To run a specific environment and a specific test file::

tox -e py38 -- utils.txt
$ tox -e py38 -- utils.txt


Code formatting
---------------

We use black/flake8/isort. To make it easy to configure and run, there's a
pre-commit config. Enable it with::

$ pre-commit install

That will run it before every commit. You can also run it periodically when
developing::

$ pre-commit run --all


Python versions
Expand All @@ -64,15 +78,14 @@ Necessary programs
------------------

To run the tests, you need to have the supported versioning systems installed.
Since version 7, we only support ``git``.
On ubuntu::

$ sudo apt-get install git
Since version 7, we only support ``git``, which you already have installed
probably :-)

There may be test failures when you have different versions of these programs.
In that case, please investigate as these may be genuine errors.
In the past, ``git`` commands would give slightly different output.
If the output of a command changes again, we may need extra compatibility code in ``test_setup.py``
In that case, please investigate as these *may* be genuine errors. In the
past, ``git`` commands would give slightly different output. If the output of
a command changes again, we may need extra compatibility code in
``test_setup.py``.


Building the documentation locally
Expand Down
2 changes: 1 addition & 1 deletion doc/source/versions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ A version number can come from various different locations:

[zest.releaser]
python-file-with-version = mypackage/__init__.py

Alternatively, in ``pyproject.toml``, you can use the following::

[tool.zest-releaser]
Expand Down
5 changes: 3 additions & 2 deletions zest/releaser/baserelease.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ def __init__(self, vcs=None):
"zest.releaser.tests", "pypirc_old.txt"
)
self.pypiconfig = pypi.PypiConfig(pypirc_old)
self.zest_releaser_config = pypi.ZestReleaserConfig(pypirc_config_filename=pypirc_old)
self.zest_releaser_config = pypi.ZestReleaserConfig(
pypirc_config_filename=pypirc_old
)
else:
self.pypiconfig = pypi.PypiConfig()
self.zest_releaser_config = pypi.ZestReleaserConfig()
Expand All @@ -86,7 +88,6 @@ def __init__(self, vcs=None):

@property
def history_format(self):
default = "rst"
config_value = self.zest_releaser_config.history_format()
history_file = self.data.get("history_file") or ""
return utils.history_format(config_value, history_file)
Expand Down
3 changes: 2 additions & 1 deletion zest/releaser/longtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ def main():
action="store_true",
dest="headless",
default=False,
help="Do not open a browser window with the HTML result")
help="Do not open a browser window with the HTML result",
)
options = utils.parse_options(parser)
utils.configure_logging()
code = show_longdesc(headless=options.headless)
Expand Down
5 changes: 4 additions & 1 deletion zest/releaser/prerelease.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# non-Python projects.
from pep440 import is_canonical
except ImportError:

def is_canonical(version):
logger.debug("Using dummy is_canonical that always returns True.")
return True
Expand Down Expand Up @@ -109,7 +110,9 @@ def _grab_version(self, initial=False):
while new_version is None:
new_version = utils.ask_version("Enter version", default=suggestion)
if not is_canonical(new_version):
logger.warning(f"'{new_version}' is not a canonical Python package version.")
logger.warning(
f"'{new_version}' is not a canonical Python package version."
)
question = "Do you want to use this version anyway?"
if not utils.ask(question):
# Set to None: we will ask to enter a new version.
Expand Down
29 changes: 20 additions & 9 deletions zest/releaser/pypi.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from configparser import ConfigParser, NoOptionError, NoSectionError
from .utils import extract_zestreleaser_configparser
from configparser import ConfigParser
from configparser import NoOptionError
from configparser import NoSectionError

import logging
import os
import pkg_resources
import sys

from .utils import string_to_bool, extract_zestreleaser_configparser

try:
# Python 3.11+
Expand Down Expand Up @@ -50,8 +52,7 @@ def _get_boolean(self, section, key, default=False):
return result

def _get_text(self, section, key, default=None, raw=False):
"""Get a text from the config.
"""
"""Get a text from the config."""
result = default
if self.config is not None:
try:
Expand Down Expand Up @@ -142,7 +143,7 @@ def zest_releaser_config(self):

class PypiConfig(BaseConfig):
"""Wrapper around the pypi config file.
Contains functions which return information about
the pypi configuration.
"""
Expand All @@ -166,7 +167,7 @@ def reload(self):
settings, and tell release to retry the command.
"""
self._read_configfile()

def zest_releaser_config(self):
return extract_zestreleaser_configparser(self.config, self.config_filename)

Expand Down Expand Up @@ -262,7 +263,9 @@ def zest_releaser_config(self):
try:
result = self.config["tool"]["zest-releaser"]
except KeyError:
logger.debug(f"No [tool.zest-releaser] section found in the {self.config_filename}")
logger.debug(
f"No [tool.zest-releaser] section found in the {self.config_filename}"
)
return None
return result

Expand All @@ -283,13 +286,21 @@ def load_configs(self, pypirc_config_filename=DIST_CONFIG_FILE):
combined_config.update(zest_config)

# store which config file contained entrypoint hooks
if any([x for x in zest_config.keys() if x.lower().startswith(("prereleaser.", "releaser.", "postreleaser."))]):
if any(
[
x
for x in zest_config.keys()
if x.lower().startswith(
("prereleaser.", "releaser.", "postreleaser.")
)
]
):
self.hooks_filename = config.config_filename
self.config = combined_config

def __init__(self, pypirc_config_filename=DIST_CONFIG_FILE):
self.load_configs(pypirc_config_filename=pypirc_config_filename)

def want_release(self):
"""Does the user normally want to release this package.
Expand Down
70 changes: 41 additions & 29 deletions zest/releaser/release.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# GPL, (c) Reinout van Rees

from build import ProjectBuilder
from colorama import Fore
from subprocess import CalledProcessError
from subprocess import check_output
from subprocess import STDOUT
from urllib import request
from urllib.error import HTTPError
from build import ProjectBuilder
from subprocess import check_output, CalledProcessError, STDOUT

import logging
import os
Expand Down Expand Up @@ -59,30 +61,35 @@ def package_in_pypi(package):
logger.debug("Package not found on pypi: %s", e)
return False


def _project_builder_runner(cmd, cwd=None, extra_environ=None):
"""Run the build command and format warnings and errors.
It runs the build command in a subprocess. Warnings and errors are formatted
in red so that they will work correctly with utils.show_interesting_lines(). We
mimic the setuptools/wheels output that way.
"""
env = os.environ.copy()
if extra_environ:
env.update(extra_environ)
"""Run the build command and format warnings and errors.
try:
result = check_output(cmd, cwd=cwd, env=env, stderr=STDOUT)
except CalledProcessError as e:
raise SystemExit(f"Build failed with the following error:\n{e.output.decode()}\nExiting") from e
result_split = result.split(b"\n")
formatted_result = []
for line in result_split:
line = line.decode()
if line.lower().startswith(("warning", "error")):
line = Fore.RED + line + Fore.RESET # reset so that not all the lines after a warning are red
formatted_result.append(line)
formatted_result_joined = "\n".join(formatted_result)
utils.show_interesting_lines(formatted_result_joined)
It runs the build command in a subprocess. Warnings and errors are formatted
in red so that they will work correctly with utils.show_interesting_lines(). We
mimic the setuptools/wheels output that way.
"""
env = os.environ.copy()
if extra_environ:
env.update(extra_environ)

try:
result = check_output(cmd, cwd=cwd, env=env, stderr=STDOUT)
except CalledProcessError as e:
raise SystemExit(
f"Build failed with the following error:\n{e.output.decode()}\nExiting"
) from e
result_split = result.split(b"\n")
formatted_result = []
for line in result_split:
line = line.decode()
if line.lower().startswith(("warning", "error")):
line = (
Fore.RED + line + Fore.RESET
) # reset so that not all the lines after a warning are red
formatted_result.append(line)
formatted_result_joined = "\n".join(formatted_result)
utils.show_interesting_lines(formatted_result_joined)


class Releaser(baserelease.Basereleaser):
Expand All @@ -102,7 +109,9 @@ def prepare(self):
self._grab_version()
tag = self.zest_releaser_config.tag_format(self.data["version"])
self.data["tag"] = tag
self.data["tag-message"] = self.zest_releaser_config.tag_message(self.data["version"])
self.data["tag-message"] = self.zest_releaser_config.tag_message(
self.data["version"]
)
self.data["tag-signing"] = self.zest_releaser_config.tag_signing()
self.data["tag_already_exists"] = self.vcs.tag_exists(tag)

Expand Down Expand Up @@ -158,14 +167,14 @@ def _upload_distributions(self, package):
"Making a source distribution of a fresh tag checkout (in %s).",
self.data["tagworkingdir"],
)
builder = ProjectBuilder(srcdir='.', runner=_project_builder_runner)
builder.build('sdist', './dist/')
builder = ProjectBuilder(srcdir=".", runner=_project_builder_runner)
builder.build("sdist", "./dist/")
if self.zest_releaser_config.create_wheel():
logger.info(
"Making a wheel of a fresh tag checkout (in %s).",
self.data["tagworkingdir"],
)
builder.build('wheel', './dist/')
builder.build("wheel", "./dist/")
if not self.pypiconfig.is_pypi_configured():
logger.error(
"You must have a properly configured %s file in "
Expand Down Expand Up @@ -344,7 +353,10 @@ def _release(self):
# Run extra entry point
self._run_hooks("after_checkout")

if any(filename in os.listdir(self.data["tagworkingdir"]) for filename in ["setup.py", "pyproject.toml"]):
if any(
filename in os.listdir(self.data["tagworkingdir"])
for filename in ["setup.py", "pyproject.toml"]
):
self._upload_distributions(package)

# Make sure we are in the expected directory again.
Expand Down
2 changes: 1 addition & 1 deletion zest/releaser/tests/baserelease.txt
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,4 @@ history file ends with ``.md``, we consider it Markdown::
>>> base = baserelease.Basereleaser()
>>> base._grab_history()
>>> base.history_format
'md'
'md'
1 change: 1 addition & 0 deletions zest/releaser/tests/cmd_error.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Python script to test some corner cases that print warnings to stderr.
import sys


print(sys.argv[1], file=sys.stderr)
Loading

0 comments on commit 9bc6d16

Please sign in to comment.