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 check to prevent .dev dependencies from being released #2

Merged
merged 5 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ source_pkgs = icrs.releaser
# Perhaps this obsoletes the source section in [paths]?
relative_files = True
branch = true
parallel = true
concurrency = thread

[report]
# Coverage is run on Linux under cPython 2 and 3,
Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
test:
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', 'pypy-3.10', '3.11', '3.12']
python-version: ['3.9', '3.10', 'pypy-3.10', '3.11', '3.12']

runs-on: ubuntu-latest
steps:
Expand All @@ -29,8 +29,10 @@ jobs:
python -m pip install -U -e ".[test,docs]"
- name: Test
run: |
coverage run -m zope.testrunner --test-path=src --auto-color --auto-progress
coverage run -a -m sphinx -b doctest -d docs/_build/doctrees docs docs/_build/doctests
coverage run -m zope.testrunner --package-path src/icrs/releaser icrs.releaser --auto-color --auto-progress
coverage run -m sphinx -b doctest -d docs/_build/doctrees docs docs/_build/doctests
coverage combine
coverage report
- name: Submit to Coveralls
# This is a container action, which only runs on Linux.
uses: AndreMiras/coveralls-python-action@develop
Expand Down
8 changes: 6 additions & 2 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@
==================

- Add support for Python 3.11 and 3.12.
- Depend on newer ``zest.releaser >. 9.1.1``.
- Remove dependency on setuptools; now uses the poorly desgined
- Drop support for Python 3.8. The minimum supported version is now 3.9.
- Depend on newer ``zest.releaser >= 9.1.1``.
- Remove dependency on setuptools; now uses the so-called
"native" namespace packages.
- Add a new release check that forbids having development dependencies
(e.g., "icrs.releaser >= 3.0.dev0" would be forbidden). This only
works for ``setuptools`` projects that have dependencies listed in setup.py.


1.1.0 (2022-03-03)
Expand Down
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ recursive-include docs *.py
recursive-include docs *.rst
recursive-include docs *.css
recursive-include docs Makefile

recursive-include src *.zcml
recursive-include src *.txt
9 changes: 4 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@
from setuptools import find_namespace_packages


version = '1.1.1.dev0'
version = '1.2.0'

entry_points = {
'zest.releaser.prereleaser.before': [
'dev_middle = icrs.releaser.devremover:prereleaser_before',
# XXX: This only works doing fullrelease
'rm_cflags = icrs.releaser.removecflags:prereleaser_before',
],
'zest.releaser.prereleaser.middle': [
'version_next = icrs.releaser.versionreplacer:prereleaser_middle',
# XXX: This only works doing fullrelease
'scm_middle = icrs.releaser.setuptools_scm_versionfixer:prereleaser_middle',
# XXX: Add check for .dev.

],
'console_scripts': [
'icrs_release = icrs.releaser.fullrelease:main',
Expand Down Expand Up @@ -50,7 +51,6 @@ def _read(fname):
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
Expand All @@ -64,7 +64,6 @@ def _read(fname):
package_dir={'': 'src'},
namespace_packages=['icrs'],
install_requires=[
'setuptools_scm',
'zest.releaser >= 9.1.1',
],
entry_points=entry_points,
Expand All @@ -80,5 +79,5 @@ def _read(fname):
'zest.releaser[recommended]',
],
},
python_requires=">=3.8",
python_requires=">=3.9",
)
86 changes: 86 additions & 0 deletions src/icrs/releaser/devremover.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
"""
See `prereleaser_before`.

"""

import re
import sys

from pathlib import Path

class DevelopmentDependency(Exception):
"""
Raised when a development dependency is detected.
"""

# An expression that matches dependency specification lines that have
# ".dev" versions.
#
# TODO: The ``packaging`` library
# (https://packaging.pypa.io/en/stable/index.html) has support to
# actually parse all of these complicated specifiers; the problem is
# extracting which lines need to be parsed like that. We're just
# matching the entire file
_SETUP_PY_DEV_REQUIREMENT_MATCHER = re.compile(
# open single or double quote
br"['\"]"
# one or more alphanumeric characters, underscores, periods
# commas, spaces or square brackets (for extras)
# and zero or more spaces
br"[\w.,\[\] ]+\s*"
# followed by an operator.
# (recall that 'foo < 3, >2, !=2.1' is valid syntax; the clauses can come in
# any order). See https://peps.python.org/pep-0508/
br"[>=<!~]"
# followed by any number of characters of any sort, but with at least one number.
br".*\d+.*"
# followed by the ".dev" sequence
br"\.dev"
# followed by any number of characters
br".*"
# ended with the closing string quote
# (if we wanted to be super smart, we would use a capture group to match
# the actual opening quote)
br"['\"]"
)

def prereleaser_before(data):
"""
Checks for development dependencies and raise an error
if found.

A non-released dependency is something in setup.py that has a version number
containing ".dev". An exception is raised if this is found.

This is a "prereleaser before" hook so that it runs before anything
tries to commit to the project (e.g., to update the version number).

.. todo::

This only supports traditional setuptools-style
projects and only supports setup.py. Ideally, it would
support pyproject.toml, setup.cfg and generic build backends.
We'd need to be able to get the project metadata (``.egg-info`` in setuptools,
or part of the records recorded in the wheel file) to make that
work reliably, though, and I'm not sure how to do that.
"""
report = data.get('icrs.releaser:report', print)
report('Checking for the existence of development dependencies.')
root_dir = Path(data['reporoot'])

for p in (
root_dir / 'setup.py',
):
if not p.is_file():
report('Project file', p, "does not exist; not checking for development deps.",
file=sys.stderr)
continue

contents = p.read_bytes()
if (match := _SETUP_PY_DEV_REQUIREMENT_MATCHER.search(contents)):
raise DevelopmentDependency(
"Project file %s had development dependency: %s" % (
p, match.group().decode('utf-8', errors='ignore')
)
)
85 changes: 85 additions & 0 deletions src/icrs/releaser/tests/example_setup.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# pragma: no cover
# Copyright 2020 NextThought
# Released under the terms of the LICENSE file.
import codecs
from setuptools import setup
from setuptools import find_namespace_packages


version = '1.1.1.dev0'

entry_points = {
'zest.releaser.prereleaser.before': [
# XXX: This only works doing fullrelease
'rm_cflags = icrs.releaser.removecflags:prereleaser_before',
],
'zest.releaser.prereleaser.middle': [
'version_next = icrs.releaser.versionreplacer:prereleaser_middle',
# XXX: This only works doing fullrelease
'scm_middle = icrs.releaser.setuptools_scm_versionfixer:prereleaser_middle',
'dev_middle = icrs.releaser.devremover:prereleaser_middle',
],
'console_scripts': [
'icrs_release = icrs.releaser.fullrelease:main',
]
}

TESTS_REQUIRE = [
'coverage',
'zope.testrunner',
]

def _read(fname):
with codecs.open(fname, encoding='utf-8') as f:
return f.read()

setup(
name='icrs.releaser',
version=version,
author='Jason Madden',
author_email='jason@seecoresoftware.com',
description="zest.releaser/setuptools_scm plugin.",
long_description=_read('README.rst') + '\n\n' + _read('CHANGES.rst'),
license='Apache',
keywords='zest.releaser release automation setuptools_scm git',
url='https://github.com/jamadden/icrs.releaser',
project_urls={
'Documentation': 'https://icrsreleaser.readthedocs.io/en/latest/',
},
classifiers=[
'Intended Audience :: Developers',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Development Status :: 1 - Planning',
],
zip_safe=False,
packages=find_namespace_packages(where='src'),
package_dir={'': 'src'},
namespace_packages=['icrs'],
install_requires=[
# Placeholder
'zest.releaser >= 9.1.1',
],
entry_points=entry_points,
include_package_data=True,
extras_require={
'test': TESTS_REQUIRE,
'docs': [
'Sphinx',
'furo',
'sphinxcontrib-programoutput',
] + TESTS_REQUIRE,
'recommended': [
'zest.releaser[recommended]',
],
},
python_requires=">=3.8",
)