diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1a4c3f2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: build + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + # By default, GitHub will maximize the number of jobs run in parallel + # depending on the available runners on GitHub-hosted virtual machines. + # max-parallel: 8 + fail-fast: false + matrix: + python-version: + - "3.8" + - "3.9" + - "3.10" + - "3.11" + - "3.12" + tox-env: + - "dj42" # LTS + - "dj50" + exclude: + # Python 3.8/3.9 is incompatible with Django 5.0+ + - python-version: "3.8" + tox-env: "dj50" + - python-version: "3.9" + tox-env: "dj50" + + env: + TOXENV: ${{ matrix.tox-env }} + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Upgrade pip version + run: | + python -m pip install -U pip + + - name: Install tox and coverage packages + run: | + pip install tox tox-gh-actions 'coverage<5' coveralls + + - name: Run tox and coverage + run: | + tox -e $TOXENV + coverage report + coverage html + + # - name: Upload coverage to coveralls + # run: | + # coveralls + + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install tox and flake8 packages + run: pip install tox tox-gh-actions flake8 + + - name: Lint + run: tox -e flake8 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..dc55ee1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,102 @@ +name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI + +on: + push: + tags: + # Order matters, the last rule that applies to a tag + # is the one that takes effect: + # https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#example-including-and-excluding-branches-and-tags + - '*' + # There should be no dev tags created, but to be safe, + # let's not publish them. + - '!*.dev*' + +env: + # Change these for your project's URLs + PYPI_URL: https://pypi.org/p/django-click + +jobs: + + build: + name: Build distribution 📦 + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + - name: Install pypa/build + run: + python3 -m pip install build --user + - name: Build a binary wheel and a source tarball + run: python3 -m build + - name: Store the distribution packages + uses: actions/upload-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + publish-to-pypi: + name: >- + Publish Python 🐍 distribution 📦 to PyPI + needs: + - build + runs-on: ubuntu-latest + environment: + name: pypi + url: ${{ env.PYPI_URL }} + permissions: + id-token: write # IMPORTANT: mandatory for trusted publishing + steps: + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@release/v1.10 + + github-release: + name: >- + Sign the Python 🐍 distribution 📦 with Sigstore + and upload them to GitHub Release + needs: + - publish-to-pypi + runs-on: ubuntu-latest + + permissions: + contents: write # IMPORTANT: mandatory for making GitHub Releases + id-token: write # IMPORTANT: mandatory for sigstore + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + - name: Sign the dists with Sigstore + uses: sigstore/gh-action-sigstore-python@v3.0.0 + with: + inputs: >- + ./dist/*.tar.gz + ./dist/*.whl + - name: Create GitHub Release + env: + GITHUB_TOKEN: ${{ github.token }} + run: >- + gh release create + '${{ github.ref_name }}' + --repo '${{ github.repository }}' + --notes "" + - name: Upload artifact signatures to GitHub Release + env: + GITHUB_TOKEN: ${{ github.token }} + # Upload to GitHub Release using the `gh` CLI. + # `dist/` contains the built packages, and the + # sigstore-produced signatures and certificates. + run: >- + gh release upload + '${{ github.ref_name }}' dist/** + --repo '${{ github.repository }}' diff --git a/.travis.yml b/.travis.yml index b8d08f8..951b471 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ python: env: - TOXENV=dj22 - TOXENV=dj30 + - TOXENV=dj31 cache: directories: diff --git a/AUTHORS.rst b/AUTHORS.rst deleted file mode 100644 index 111a962..0000000 --- a/AUTHORS.rst +++ /dev/null @@ -1,9 +0,0 @@ -==================== -Project contributors -==================== - - * Jonathan Stoppani - * Ulrich Petri - * Timothy Allen (https://github.com/FlipperPA) - * Bastien Vallet (https://github.com/Djailla) - diff --git a/HISTORY.rst b/HISTORY.rst deleted file mode 100644 index 7ba4f3a..0000000 --- a/HISTORY.rst +++ /dev/null @@ -1,67 +0,0 @@ -======= -History -======= - - -Unreleased -========== - -* ... - - -2.2.0 - 2020-04-14 -================== - -* Fix compatibility with latest `click`: remove `__all__`. - - -2.1.0 - 2018-04-20 -================== - -* Add experimental support for Django 2.0 - - -2.0.0 - 2017-06-30 -================== - -* Drop support for unsupported Django versions (1.4, 1.5, 1.6, and 1.7). -* Add official support for Django 1.10 and 1.11. -* Add official support for python 3.5 (all Django versions) and 3.6 - (Django 1.11 only). -* Correctly handle click errors by outputting the formatted messages instead - of a stack trace (#4). - - -1.2.0 - 2016-05-19 -================== - -* Allow custom lookups on ``ModelInstance`` parameter types. - - -1.1.0 - 2016-02-04 -================== - -* Add a ``ModelInstance`` parameter type to automatically retrieve model - instances by their primary key - - -1.0.0 – 2015-09-14 -================== - -* Support for command groups -* Added a ``pass_verbosity`` decorator -* Improved test suite - - -0.1.1 – 2015-09-11 -================== - -* Django 1.4, 1.5, 1.6, 1.7 and 1.8 compatibility -* Python 2.7 and 3.4 compatibility -* 100% coverage test suite - - -0.1.0 – 2015-09-10 -================== - -* Initial release diff --git a/MANIFEST.in b/MANIFEST.in index 3f088d0..2c7de92 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,4 @@ -include AUTHORS.rst include CONTRIBUTING.rst -include HISTORY.rst include LICENSE include README.rst diff --git a/README.rst b/README.rst index 7aa880c..da34f16 100644 --- a/README.rst +++ b/README.rst @@ -18,24 +18,15 @@ Project information: Automated code metrics: -.. image:: https://img.shields.io/travis/GaretJax/django-click.svg - :target: https://travis-ci.org/GaretJax/django-click - .. image:: https://img.shields.io/coveralls/GaretJax/django-click/master.svg :target: https://coveralls.io/r/GaretJax/django-click?branch=master -.. image:: https://img.shields.io/codeclimate/github/GaretJax/django-click.svg - :target: https://codeclimate.com/github/GaretJax/django-click - -.. image:: https://img.shields.io/requires/github/GaretJax/django-click.svg - :target: https://requires.io/github/GaretJax/django-click/requirements/?branch=master - ``django-click`` is a library to easily write Django management commands using the ``click`` command line library. * Free software: MIT license -* Documentation for the Click command line library: http://click.pocoo.org/7/ -* Compatible with Django 2.2 or 3.0 running on Python 3.6, 3.7, 3.8, and PyPy. +* Documentation for the Click command line library: https://click.palletsprojects.com/en/8.0.x/ +* Compatible with Django 4.2 and 5.0 running on Python 3.8, 3.9, 3.10, 3.11, and 3.12 (note: 3.10+ required for Django 5.0). Installation @@ -70,3 +61,11 @@ And then call the command with:: Check out the `test commands `_ for additional example commands and advanced usage. + +Release Notes and Contributors +============================== + +* `Release Notes on GitHub `_ +* `Our Wonderful Contributors `_ + +This package is a member of `Django Commons `_ and adheres to the community's `Code of Conduct `_. It is maintained by `Jonathan Stoppani `_ and `Timothy Allen `_, who have many professional responsibilities. We are thrilled that our employers allow us a certain amount of time to contribute to open-source projects. We add features as they are necessary for our projects, and try to keep up with Issues and Pull Requests as best we can. Due to constraints of time (our full time jobs!), Feature Requests without a Pull Request may not be implemented, but we are always open to new ideas and grateful for contributions and our users. diff --git a/djclick/__init__.py b/djclick/__init__.py index 1e6d6f4..372413a 100644 --- a/djclick/__init__.py +++ b/djclick/__init__.py @@ -9,7 +9,7 @@ # The RegEx in setup.py requires single quotes. Rather than change it, turn off Black. # fmt: off -__version__ = '2.2.0' +__version__ = '2.4.1' __url__ = 'https://github.com/GaretJax/django-click' __author__ = 'Jonathan Stoppani' __email__ = 'jonathan@stoppani.name' diff --git a/djclick/adapter.py b/djclick/adapter.py index e528d37..3d802fd 100644 --- a/djclick/adapter.py +++ b/djclick/adapter.py @@ -4,17 +4,10 @@ import click -from django import get_version, VERSION as DJANGO_VERSION +from django import get_version from django.core.management import CommandError -class OptionParseAdapter(object): - """Django pre-1.10-compatible adapter, deprecated""" - - def parse_args(self, args): - return (self, None) # NOCOV - - class ArgumentParserDefaults(object): def __init__(self, args): self._args = args @@ -71,7 +64,7 @@ def run_from_argv(self, argv): if exit_code: sys.exit(exit_code) except click.ClickException as e: - if getattr(e.ctx, "traceback", False): # NOCOV + if getattr(e, "ctx", False) and getattr(e.ctx, "traceback", False): # NOCOV raise e.show() sys.exit(e.exit_code) @@ -80,10 +73,7 @@ def create_parser(self, progname, subcommand): """ Called when run through `call_command`. """ - if DJANGO_VERSION >= (1, 10): - return ArgumentParserAdapter() - else: # NOCOV - return OptionParseAdapter() + return ArgumentParserAdapter() def print_help(self, prog_name, subcommand): prog_name = "{} {}".format(prog_name, subcommand) diff --git a/djclick/test/test_adapter.py b/djclick/test/test_adapter.py index 7eb4bd7..0ec4337 100644 --- a/djclick/test/test_adapter.py +++ b/djclick/test/test_adapter.py @@ -122,7 +122,7 @@ def test_django_verbosity(capsys, manage): b"Usage: manage.py ctxverbositycmd [OPTIONS]\n" b"\n" b'Error: Invalid value for "-v" / "--verbosity": 4 is not in the ' - b"valid range of 0 to 3.\n" + b"range 0<=x<=3.\n" ) # Default (option) @@ -147,9 +147,6 @@ def test_django_pythonpath(manage): ) == b"1" -@pytest.mark.xfail( - reason="Looks like CommandError no longer " "results in non-zero exit status" -) def test_django_traceback(manage): with pytest.raises(subprocess.CalledProcessError) as e: manage("errcmd") @@ -170,6 +167,13 @@ def test_django_traceback(manage): assert e.returncode == 1 +def test_click_exception(manage): + with pytest.raises(subprocess.CalledProcessError) as e: + manage("clickexceptioncmd") + assert e.value.output == b"Error: Raised error description\n" + assert e.value.returncode == 1 + + def test_django_settings(manage): # The --settings switch only works from the command line (or if the django # settings where not setup before)... this means that we have to call it diff --git a/djclick/test/testprj/testapp/management/commands/clickexceptioncmd.py b/djclick/test/testprj/testapp/management/commands/clickexceptioncmd.py new file mode 100644 index 0000000..6d66705 --- /dev/null +++ b/djclick/test/testprj/testapp/management/commands/clickexceptioncmd.py @@ -0,0 +1,6 @@ +import djclick as click + + +@click.command(version="20.0") +def command(): + raise click.ClickException("Raised error description") diff --git a/requirements.txt b/requirements.txt index d28a871..9f65be1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -click>=7.1,<7.2 +click>=7.1 diff --git a/setup.py b/setup.py index 90eb1e1..eacb30b 100755 --- a/setup.py +++ b/setup.py @@ -19,9 +19,11 @@ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", "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", ] @@ -114,7 +116,7 @@ def license(cls): @staticmethod def longdesc(): - return Setup.read("README.rst") + "\n\n" + Setup.read("HISTORY.rst") + return Setup.read("README.rst") @staticmethod def test_links(): diff --git a/tox.ini b/tox.ini index f95f9f0..10adc01 100644 --- a/tox.ini +++ b/tox.ini @@ -1,21 +1,22 @@ [tox] -# Having the .tox directory in the project directory slows down the -# `pip install -e .` step required by `usedevelop = true` considerably. -# By moving it out of the way (~500MB), we trim test execution time by > 80%. -toxworkdir = {homedir}/.toxenvs/django-click envlist = - dj{22,30},flake8 + dj{42,50},flake8 + +[gh-actions] +django = + 4.2: dj42 + 5.0: dj50 [testenv] -usedevelop = true +package = editable passenv = LC_ALL, LANG, LC_CTYPE setenv = DJANGO_SETTINGS_MODULE=testprj.settings PYTHONPATH={toxinidir}/djclick/test/testprj deps = -rrequirements-test.txt - dj22: django>=2.2,<2.3 - dj30: django>=3.0,<3.1 + dj42: django>=4.2,<4.3 + dj50: django>=5.0,<5.1 commands = py.test -rxs --cov-report= --cov-append --cov djclick {posargs:djclick}