From 4a8c7a5a2b62c86c29c70d529df7768215ccec33 Mon Sep 17 00:00:00 2001 From: Florian Strzelecki Date: Sat, 23 Jul 2022 00:13:28 +0200 Subject: [PATCH 1/7] ci, packaging: modernize build system and CI with pyproject.toml --- .github/workflows/ci.yml | 10 +++---- .github/workflows/pypi.yml | 23 ++++++++------- MANIFEST.in | 4 ++- pyproject.toml | 57 ++++++++++++++++++++++++++++++++++++++ requirements.txt | 9 ------ setup.cfg | 36 ------------------------ setup.py | 51 ---------------------------------- 7 files changed, 76 insertions(+), 114 deletions(-) create mode 100644 pyproject.toml delete mode 100644 requirements.txt delete mode 100755 setup.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff2220d4b9..9a50527c7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,19 +24,19 @@ jobs: - 3.8 - 3.9 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt -r dev-requirements.txt + python -m pip install --upgrade wheel + python -m pip install --upgrade -r dev-requirements.txt + python -m pip install -e . - name: Check code style run: make quality - - name: Install test package - run: python setup.py develop - name: Run pytest run: make test_norecord - name: Upload coverage data to coveralls.io diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 013794eede..bdb7c90750 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -9,20 +9,19 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - name: Set up Python 3.9 + uses: actions/setup-python@v4 with: python-version: '3.9' - - name: Install dependencies + - name: Install PyPA/build run: | python -m pip install --upgrade pip - pip install setuptools wheel twine - pip install -r requirements.txt -r dev-requirements.txt - - name: Build and publish - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + python -m pip install build --user + - name: Build a binary wheel and a source tarball run: | - python setup.py sdist bdist_wheel - twine upload dist/* + python -m build --sdist --wheel --outdir dist/ . + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@v1 + with: + password: ${{ secrets.PYPI_TOKEN }} diff --git a/MANIFEST.in b/MANIFEST.in index 9a526b2fd6..efe7351bef 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,6 @@ -include *requirements.txt include setup.cfg +include pyproject.toml +recursive-exclude docs * +recursive-exclude test * recursive-exclude * __pycache__ recursive-exclude * *.py[co] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..83d2bd0293 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,57 @@ +[build-system] +requires = ["setuptools>=63.0", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +platforms = ["Linux x86, x86-64"] + +[tool.setuptools.packages.find] +include = ["sopel", "sopel.*"] +namespaces = false + +[project] +name = "sopel" +version = "8.0.0.dev0" +description = "Simple and extensible IRC bot" +authors = [ + { name="dgw", email="dgw@technobabbl.es" }, +] +readme = "README.rst" +license = { file="COPYING" } +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + "License :: Eiffel Forum License (EFL)", + "License :: OSI Approved :: Eiffel Forum License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Topic :: Communications :: Chat :: Internet Relay Chat", +] +requires-python = ">=3.7" +dependencies = [ + "xmltodict==0.12", + "pytz", + "praw>=4.0.0,<8.0.0", + "geoip2>=4.0,<5.0", + "requests>=2.24.0,<3.0.0", + "dnspython<3.0", + "sqlalchemy>=1.4,<1.5", + "importlib_metadata>=3.6", + "packaging", +] + +[project.urls] +"Homepage" = "https://sopel.chat/" +"Documentation" = "https://sopel.chat/docs/" +"Bug Tracker" = "https://github.com/sopel-irc/sopel/issues" + +[project.scripts] +sopel = "sopel.cli.run:main" +sopel-config = "sopel.cli.config:main" +sopel-plugins = "sopel.cli.plugins:main" + +[project.entry-points.pytest11] +pytest-sopel = "sopel.tests.pytest_plugin" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index dca27f31ca..0000000000 --- a/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -xmltodict==0.12 -pytz -praw>=4.0.0,<8.0.0 -geoip2>=4.0,<5.0 -requests>=2.24.0,<3.0.0 -dnspython<3.0 -sqlalchemy>=1.4,<1.5 -importlib_metadata>=3.6 -packaging diff --git a/setup.cfg b/setup.cfg index b64a8e4b29..bfc8544ce3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,39 +1,3 @@ -[metadata] -name = sopel -version = 8.0.0-dev -description = Simple and extensible IRC bot -long_description = file: README.rst -long_description_content_type = text/x-rst -author = dgw -author_email = dgw@technobabbl.es -url = https://sopel.chat/ -license = Eiffel Forum License, version 2 -platforms = Linux x86, x86-64 -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Developers - Intended Audience :: System Administrators - License :: Eiffel Forum License (EFL) - License :: OSI Approved :: Eiffel Forum License - Operating System :: POSIX :: Linux - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Topic :: Communications :: Chat :: Internet Relay Chat - -[options] -python_requires = >=3.7, <4 -packages = find: -zip_safe = false - -[options.entry_points] -console_scripts = - sopel = sopel.cli.run:main - sopel-config = sopel.cli.config:main - sopel-plugins = sopel.cli.plugins:main -pytest11 = - pytest-sopel = sopel.tests.pytest_plugin - [flake8] max-line-length = 79 application-import-names = sopel diff --git a/setup.py b/setup.py deleted file mode 100755 index 13e0a89195..0000000000 --- a/setup.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -from __future__ import annotations - -import sys - -try: - from setuptools import setup, __version__ as setuptools_version -except ImportError: - print( - 'You do not have setuptools, and can not install Sopel. The easiest ' - 'way to fix this is to install pip by following the instructions at ' - 'https://pip.readthedocs.io/en/latest/installing/\n' - 'Alternately, you can run Sopel without installing it by running ' - '"python sopel.py"', - file=sys.stderr, - ) - sys.exit(1) -else: - version_info = setuptools_version.split('.') - major = int(version_info[0]) - minor = int(version_info[1]) - - if major < 30 or (major == 30 and minor < 3): - print( - 'Your version of setuptools is outdated: version 30.3 or above ' - 'is required to install Sopel. You can do that with ' - '"pip install -U setuptools"\n' - 'Alternately, you can run Sopel without installing it by running ' - '"python sopel.py"', - file=sys.stderr, - ) - sys.exit(1) - -# We check Python's version ourselves in case someone installed Sopel on an -# old version of pip (<9.0.0), which doesn't know about `python_requires`. -if sys.version_info < (3, 7): - # Maybe not the best way to do this, but this question is tiring. - raise ImportError('Sopel requires Python 3.7+.') - - -def read_reqs(path): - with open(path, 'r') as fil: - return list(fil.readlines()) - - -requires = read_reqs('requirements.txt') -dev_requires = requires + read_reqs('dev-requirements.txt') - -setup( - install_requires=requires, extras_require={"dev": dev_requires}, -) From 866c339c7285a0d729d12b97b2cadd6e8e754d4b Mon Sep 17 00:00:00 2001 From: Florian Strzelecki Date: Sat, 23 Jul 2022 00:23:53 +0200 Subject: [PATCH 2/7] meta: remove obsolete files and update githook --- conftest.py | 2 +- contrib/githooks/main-hook.sh | 4 ++-- pytest_run.py | 19 ------------------- sopel.py | 9 --------- 4 files changed, 3 insertions(+), 31 deletions(-) delete mode 100755 pytest_run.py delete mode 100755 sopel.py diff --git a/conftest.py b/conftest.py index a14bf76c8a..af9e027b47 100644 --- a/conftest.py +++ b/conftest.py @@ -3,7 +3,7 @@ import pytest # This file lists files which should be ignored by pytest -collect_ignore = ["setup.py", "sopel.py"] +collect_ignore = [] def pytest_addoption(parser): diff --git a/contrib/githooks/main-hook.sh b/contrib/githooks/main-hook.sh index 41c43e5009..6d779b77a1 100755 --- a/contrib/githooks/main-hook.sh +++ b/contrib/githooks/main-hook.sh @@ -67,9 +67,9 @@ RUN_PYTEST=$([ "${SKIP_PYTEST}" = "1" ] && echo "0" || echo "1") # pytest [ "${RUN_PYTEST}" -eq "1" ] \ && { - echo -e "\033[32mRunning \033[33mpytest_run.py \033[32mbefore ${_GIT_COMMAND}... \033[0m" + echo -e "\033[32mRunning \033[33mpytest \033[32mbefore ${_GIT_COMMAND}... \033[0m" - python pytest_run.py + python -m pytest sopel test/ pt="${?}" if [ "${pt}" -ne "0" ]; then echo -e '\033[91mFAILED.\033[0m' diff --git a/pytest_run.py b/pytest_run.py deleted file mode 100755 index 0932223400..0000000000 --- a/pytest_run.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python -"""This is a script for running pytest from the command line. - -This script exists so that the project directory gets added to sys.path, which -prevents us from accidentally testing the globally installed sopel version. - -pytest_run.py -Copyright 2013, Ari Koivula, -Licensed under the Eiffel Forum License 2. - -https://sopel.chat -""" -from __future__ import annotations - -if __name__ == "__main__": - import sys - import pytest - returncode = pytest.main() - sys.exit(returncode) diff --git a/sopel.py b/sopel.py deleted file mode 100755 index e107fb866e..0000000000 --- a/sopel.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python3 -from __future__ import annotations - -import sys - -# Different from setuptools script, because we want the one in this dir. -from sopel.cli import run - -sys.exit(run.main()) From 2128a138d6c0f883563b92c4a33c3358f533f4ff Mon Sep 17 00:00:00 2001 From: Florian Strzelecki Date: Sat, 23 Jul 2022 15:19:52 +0200 Subject: [PATCH 3/7] CONTRIBUTING: local dev setup Editing by dgw. Co-authored-by: dgw --- CONTRIBUTING.md | 85 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 32f62d3a2d..f0f195033d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ that you do the following: 1. Describe your issue clearly and concisely. 2. Give Sopel the `.version` command, and include the output in your issue. 3. Note the OS you're running Sopel on, and how you installed Sopel (via your - package manager, pip, `setup.py install`, or running straight from source) + package manager, pip, `pip install .` straight from source) 4. Include relevant output from the log files in `~/.sopel/logs/`. Committing Code @@ -45,9 +45,90 @@ include your changes. the name of the thing you're changing in at the beginning of the message, followed by a colon: the plugin name for plugins, "docs" for documentation files, "coretasks" for `coretasks.py`, "db" for the database feature, etc. -* Python files should always have `from __future__ import generator_stop` +* Python files should always have `from __future__ import annotations` as the first line after the module docstring. +Running from source +------------------- + +Sopel is your typical Python project: you can install it from source, edit +the code using standard tooling, and take advantage of your favorite code +editor. + +Assuming you are using standard tools: + +* Create a virtualenv. + * You can use `virtualenv` (or the `venv` built-in) directly, + `virtualenvwrapper`, or any tool that allows you to create and manage your + virtualenvs. + * Your project manager may create the virtualenv for you; be sure to check + the documentation of the tools you are using. +* Fork Sopel's repository, and clone your fork locally. +* Activate your virtualenv, and `cd` into your clone's folder. +* Ensure you have the latest version of `pip`, and install `wheel`. +* Install Sopel from source as an editable install. + * If you don't use a project manager (like Poetry or Hatch), you can do + that with `pip install -e .` from within the clone's directory, where + there is a `pyproject.toml` file. +* Install development tools used to run tests and linters; there is a specific + requirement file you can use with `pip install -U -r dev-requirements.txt`. +* Run `make qa` to run linters and tests. +* Run `make cleandoc` or just `make docs` to build the documentation locally. + +At this point, you are all set, at least for the Python environment; all that's +left is for you to configure your code editor the way you like, and read and +write some code and documentation. + +Using branches +-------------- + +As previously stated, you should fork Sopel's repository to implement your +changes and commit them. Moreover, we advise you to work from your own branch. + +To setup your local clone, we suggest the following steps: + +``` +$ cd path/to/your/dev/folder +$ git clone git@github.com:/sopel.git +$ cd sopel/ +$ git remote add upstream git@github.com:sopel-irc/sopel.git +$ git checkout -b +``` + +With that workflow, you have your local clone with a link to the upstream +repository, and a branch to work on. Once you are done with your work, you can +commit, and push to your repository. + +You'll probably need, at some point, to update your repository with the new +commits from upstream. We suggest the following steps: + +``` +$ git checkout master +$ git fetch upstream +$ git rebase upstream/master +$ git push +``` + +If you never pushed on your own `master` branch, you should not need to force +the push, which is why we recommend to work on your own branch. + +Said branch can be updated with rebase: + +``` +$ git checkout +$ git rebase master +``` + +To squash your commits, you can use interactive mode with +`git rebase -i master`. + +In both cases, if you configured +[a GPG key](https://docs.github.com/en/authentication/managing-commit-signature-verification) +to sign your commits, don't forget the `-S` option: + +* `git rebase -S master` +* `git rebase -S -i master` + Documenting Code ---------------- From 356524a343be61770dc3205866c63e9850694f5c Mon Sep 17 00:00:00 2001 From: Florian Strzelecki Date: Fri, 29 Jul 2022 23:59:53 +0200 Subject: [PATCH 4/7] meta: more URLs (release notes, sponsor, source, coverage) Suggested URLs by dgw. Co-authored-by: dgw --- conftest.py | 3 --- pyproject.toml | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/conftest.py b/conftest.py index af9e027b47..ee89a5163c 100644 --- a/conftest.py +++ b/conftest.py @@ -2,9 +2,6 @@ import pytest -# This file lists files which should be ignored by pytest -collect_ignore = [] - def pytest_addoption(parser): parser.addoption('--offline', action='store_true', default=False) diff --git a/pyproject.toml b/pyproject.toml index 83d2bd0293..c429d1c486 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,8 +45,14 @@ dependencies = [ [project.urls] "Homepage" = "https://sopel.chat/" +"Release notes" = "https://github.com/sopel-irc/sopel/releases" "Documentation" = "https://sopel.chat/docs/" "Bug Tracker" = "https://github.com/sopel-irc/sopel/issues" +"Funding on Open Collective" = "https://opencollective.com/sopel" +"Sponsor on GitHub" = "https://github.com/sponsors/sopel-irc" +"Source" = "https://github.com/sopel-irc/sopel" +"Coverage" = "https://coveralls.io/github/sopel-irc/sopel" + [project.scripts] sopel = "sopel.cli.run:main" From 290258ea0ece24074dca2a4fe910d3b956176126 Mon Sep 17 00:00:00 2001 From: dgw Date: Fri, 12 Aug 2022 18:04:48 -0500 Subject: [PATCH 5/7] pyproject.toml: harmonize donation links PyPI alphabetizes these. Chose to use "Donate" for both because that leaves "Source" at the end. Which looks nice, to me. --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c429d1c486..a4ce150c2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,8 +48,8 @@ dependencies = [ "Release notes" = "https://github.com/sopel-irc/sopel/releases" "Documentation" = "https://sopel.chat/docs/" "Bug Tracker" = "https://github.com/sopel-irc/sopel/issues" -"Funding on Open Collective" = "https://opencollective.com/sopel" -"Sponsor on GitHub" = "https://github.com/sponsors/sopel-irc" +"Donate on Open Collective" = "https://opencollective.com/sopel" +"Donate on GitHub" = "https://github.com/sponsors/sopel-irc" "Source" = "https://github.com/sopel-irc/sopel" "Coverage" = "https://coveralls.io/github/sopel-irc/sopel" From 0330dd065b2a21d603295729a37c3ea872369a49 Mon Sep 17 00:00:00 2001 From: dgw Date: Fri, 12 Aug 2022 18:17:35 -0500 Subject: [PATCH 6/7] pyproject.toml: fix full license appearing in PyPI sidebar We can't go full PEP 639 yet because it isn't finalized. This will have to suffice. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a4ce150c2d..c24290613e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ authors = [ { name="dgw", email="dgw@technobabbl.es" }, ] readme = "README.rst" -license = { file="COPYING" } +license = { text="EFL-2.0" } classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", From 8982190fe0aa3713597a7be8bfee03d43b733f32 Mon Sep 17 00:00:00 2001 From: dgw Date: Fri, 12 Aug 2022 18:29:08 -0500 Subject: [PATCH 7/7] pyproject.toml: Point "Release notes" to website "Changelog" section Effectively the same thing, but more future-proof. Website is automatic based on the NEWS file, but GitHub releases only contain what someone remembers to *put* in the description when creating one. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c24290613e..9865a842b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,7 @@ dependencies = [ [project.urls] "Homepage" = "https://sopel.chat/" -"Release notes" = "https://github.com/sopel-irc/sopel/releases" +"Release notes" = "https://sopel.chat/changelog/" "Documentation" = "https://sopel.chat/docs/" "Bug Tracker" = "https://github.com/sopel-irc/sopel/issues" "Donate on Open Collective" = "https://opencollective.com/sopel"