From 6f22c0d13215bdffc4ad242ae23f34f744282c5e Mon Sep 17 00:00:00 2001 From: Facundo Batista Date: Sun, 5 Jul 2026 21:09:16 -0300 Subject: [PATCH 1/2] Modernized install infrastructure. --- .github/workflows/integtests.yaml | 28 +++--- HOWTO_RELEASE.txt | 13 ++- bin/fades | 45 --------- bin/fades.cmd | 20 ---- build_readme | 3 +- fades/__main__.py | 11 +-- fades/main.py | 13 +++ setup.py | 153 ------------------------------ test | 2 +- testdev | 2 +- testdev.bat | 2 +- 11 files changed, 39 insertions(+), 253 deletions(-) delete mode 100755 bin/fades delete mode 100644 bin/fades.cmd delete mode 100755 setup.py diff --git a/.github/workflows/integtests.yaml b/.github/workflows/integtests.yaml index cd1b305..11badb5 100644 --- a/.github/workflows/integtests.yaml +++ b/.github/workflows/integtests.yaml @@ -8,8 +8,8 @@ on: jobs: - # Single source of truth: the oldest supported Python is whatever setup.py - # declares in `python_requires`. Every job below derives its cross-version + # Single source of truth: the oldest supported Python is whatever pyproject.toml + # declares in `requires-python`. Every job below derives its cross-version # target from this output instead of hardcoding a version. min-python: runs-on: ubuntu-latest @@ -17,10 +17,10 @@ jobs: version: ${{ steps.minpy.outputs.version }} steps: - uses: actions/checkout@v4 - - name: Read minimum supported Python from setup.py + - name: Read minimum supported Python from pyproject.toml id: minpy run: | - version=$(grep python_requires setup.py | grep -oE '[0-9]+\.[0-9]+' | head -1) + version=$(grep requires-python pyproject.toml | grep -oE '[0-9]+\.[0-9]+' | head -1) echo "Minimum supported Python: $version" echo "version=$version" >> "$GITHUB_OUTPUT" @@ -45,7 +45,7 @@ jobs: - name: Simple fades run (newest Python) run: | cd /fades - bin/fades -v -d pytest -x pytest --version + python3 -m fades -v -d pytest -x pytest --version - name: Using the oldest supported Python env: OLDEST: ${{ needs.min-python.outputs.version }} @@ -53,7 +53,7 @@ jobs: cd /fades uv python install "$OLDEST" OLD=$(uv python find "$OLDEST") - python bin/fades -v --python="$OLD" -d pytest -x pytest -v --integtest-pyversion="$OLDEST" tests/integtest.py + python -m fades -v --python="$OLD" -d pytest -x pytest -v --integtest-pyversion="$OLDEST" tests/integtest.py fedora: needs: min-python @@ -73,7 +73,7 @@ jobs: - name: Simple fades run run: | cd /fades - bin/fades -v -d pytest -x pytest --version + python3 -m fades -v -d pytest -x pytest --version - name: Using the oldest supported Python env: OLDEST: ${{ needs.min-python.outputs.version }} @@ -81,7 +81,7 @@ jobs: cd /fades uv python install "$OLDEST" OLD=$(uv python find "$OLDEST") - python3 bin/fades -v --python="$OLD" -d pytest -x pytest -v --integtest-pyversion="$OLDEST" tests/integtest.py + python3 -m fades -v --python="$OLD" -d pytest -x pytest -v --integtest-pyversion="$OLDEST" tests/integtest.py native-windows: needs: min-python @@ -89,7 +89,7 @@ jobs: matrix: # just a selection otherwise it's too much # - latest OS (left here even if it's only one to simplify upgrading later) - # - oldest (from setup.py) and newest Python + # - oldest (from pyproject.toml) and newest Python os: [windows-2025] python-version: ["${{ needs.min-python.outputs.version }}", "3.13"] @@ -114,11 +114,11 @@ jobs: ${{ steps.matrixpy.outputs.python-path }} -m pip install -U packaging - name: Simple fades run run: | - ${{ steps.matrixpy.outputs.python-path }} bin/fades -v -d pytest -x pytest --version + ${{ steps.matrixpy.outputs.python-path }} -m fades -v -d pytest -x pytest --version - name: Using a different Python run: | - ${{ steps.matrixpy.outputs.python-path }} bin/fades -v --python=${{ steps.otherpy.outputs.python-path }} -d pytest -x pytest -v --integtest-pyversion=${{ needs.min-python.outputs.version }} tests/integtest.py + ${{ steps.matrixpy.outputs.python-path }} -m fades -v --python=${{ steps.otherpy.outputs.python-path }} -d pytest -x pytest -v --integtest-pyversion=${{ needs.min-python.outputs.version }} tests/integtest.py native-generic: needs: min-python @@ -126,7 +126,7 @@ jobs: matrix: # just a selection otherwise it's too much # - latest OSes - # - oldest (from setup.py) and newest Python + # - oldest (from pyproject.toml) and newest Python os: [ubuntu-24.04, macos-15] python-version: ["${{ needs.min-python.outputs.version }}", "3.13"] @@ -151,8 +151,8 @@ jobs: ${{ steps.matrixpy.outputs.python-path }} -m pip install -U packaging - name: Simple fades run run: | - ${{ steps.matrixpy.outputs.python-path }} bin/fades -v -d pytest -x pytest --version + ${{ steps.matrixpy.outputs.python-path }} -m fades -v -d pytest -x pytest --version - name: Using a different Python run: | - ${{ steps.matrixpy.outputs.python-path }} bin/fades -v --python=${{ steps.otherpy.outputs.python-path }} -d pytest -x pytest -v --integtest-pyversion=${{ needs.min-python.outputs.version }} tests/integtest.py + ${{ steps.matrixpy.outputs.python-path }} -m fades -v --python=${{ steps.otherpy.outputs.python-path }} -d pytest -x pytest -v --integtest-pyversion=${{ needs.min-python.outputs.version }} tests/integtest.py diff --git a/HOWTO_RELEASE.txt b/HOWTO_RELEASE.txt index dc4d9ad..0e62e28 100644 --- a/HOWTO_RELEASE.txt +++ b/HOWTO_RELEASE.txt @@ -1,11 +1,10 @@ Steps before a release is done ------------------------------ -Check all is crispy +Check all is crispy (building needs the 'build' package: pip install build) rm -rf build dist - ./setup.py clean build - ./setup.py clean sdist + python3 -m fades -d build -m build Edit the ``fades/_version.py`` file properly, then tag and commit/push @@ -21,7 +20,7 @@ How to release it to PyPI Dead simple: rm -rf build dist - ./setup.py clean sdist + python3 -m fades -d build -m build fades -d twine -x twine upload dist/fades-* @@ -31,7 +30,7 @@ How to create a .deb Create the tarball: rm -rf build dist - ./setup.py clean sdist + python3 -m fades -d build -m build --sdist Copy this tarball to a clean dir, renaming as "orig" @@ -155,9 +154,9 @@ Read the Docs - go and login there, go to "fades" project - in "Versions" tab, activate the new version (corresponding to the release in github) - in "Administrator" tab, go to option "Advanced configuration" at the left, choose latest release as "default version" -- verify all latest is seen in +- verify all latest is seen in - http://fades.rtfd.org/ + http://fades.rtfd.org/ How to sign the files diff --git a/bin/fades b/bin/fades deleted file mode 100755 index 60ca5c5..0000000 --- a/bin/fades +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 - -# -# Copyright 2014 Facundo Batista, Nicolás Demarchi -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 3, as published -# by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranties of -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -# PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# For further info, check https://github.com/PyAr/fades - -"""Script to run the 'fades' utility.""" - -import os -import sys - -try: - import packaging -except ImportError: - print("Import failed for `packaging` dependency. Please do `pip3 install packaging` and try again") - exit(-1) - -# small hack to allow fades to be run directly from the project, using code -# from project itself, not anything already installed in the system -parent_dir = os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0]))) -if os.path.basename(parent_dir).startswith('fades'): - # inside the project or an opened tarball!! - sys.path.insert(0, parent_dir) - -from fades import main, FadesError # noqa (imports after fixing the path, not at the top) - -try: - rc = main.go() -except FadesError: - sys.exit(-1) - -sys.exit(rc) diff --git a/bin/fades.cmd b/bin/fades.cmd deleted file mode 100644 index ca77f9c..0000000 --- a/bin/fades.cmd +++ /dev/null @@ -1,20 +0,0 @@ -:: -:: Copyright 2018 Facundo Batista, Nicolás Demarchi -:: -:: This program is free software: you can redistribute it and/or modify it -:: under the terms of the GNU General Public License version 3, as published -:: by the Free Software Foundation. -:: -:: This program is distributed in the hope that it will be useful, but -:: WITHOUT ANY WARRANTY; without even the implied warranties of -:: MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -:: PURPOSE. See the GNU General Public License for more details. -:: -:: You should have received a copy of the GNU General Public License along -:: with this program. If not, see . -:: -:: For further info, check https://github.com/PyAr/fades - -:: Script to run the 'fades' utility in Windows - -python -m fades %* diff --git a/build_readme b/build_readme index be671c9..68e2614 100755 --- a/build_readme +++ b/build_readme @@ -1,2 +1 @@ -FADES='./bin/fades -r requirements.txt' -python3 setup.py --long-description | $FADES -x rst2html5 > README.html +python3 -m fades -r requirements.txt -x rst2html5 < README.rst > README.html diff --git a/fades/__main__.py b/fades/__main__.py index 66933c2..42f8716 100644 --- a/fades/__main__.py +++ b/fades/__main__.py @@ -1,12 +1,5 @@ """Init file to allow execution of fades as a module.""" -import sys +from fades.main import console_entry_point -from fades import main, FadesError - -try: - rc = main.go() -except FadesError: - sys.exit(-1) - -sys.exit(rc) +console_entry_point() diff --git a/fades/main.py b/fades/main.py index 7256691..b937b65 100644 --- a/fades/main.py +++ b/fades/main.py @@ -506,3 +506,16 @@ def _signal_handler(signum, _): if rc: logger.debug("Child process not finished correctly: returncode=%d", rc) return rc + + +def console_entry_point(): + """Run fades as a command line program, handling its own errors. + + This is the target of the 'fades' console script declared in + pyproject.toml and is also used to run fades as a module. + """ + try: + rc = go() + except FadesError: + sys.exit(-1) + sys.exit(rc) diff --git a/setup.py b/setup.py deleted file mode 100755 index 737ea0a..0000000 --- a/setup.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2014-2024 Facundo Batista, Nicolás Demarchi -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 3, as published -# by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranties of -# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -# PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# For further info, check https://github.com/PyAr/fades - -"""Build tar.gz for fades. - -Needed packages to run (using Debian/Ubuntu package names): - - python3 - python3-xdg (optional) -""" - -import os -import re -import shutil -import sys - -from distutils.core import setup -from setuptools.command.install import install - - -def get_version(): - """Retrieves package version from the file.""" - with open('fades/_version.py') as fh: - m = re.search(r"\(([^']*)\)", fh.read()) - if m is None: - raise ValueError("Unrecognized version in 'fades/_version.py'") - return m.groups()[0].replace(', ', '.') - - -# the different scripts according to the platform -SCRIPT_WIN = 'bin/fades.cmd' -SCRIPT_REST = 'bin/fades' - - -class CustomInstall(install): - """Custom installation to fix script info and install man.""" - - def initialize_options(self): - """Run parent initialization and then fix the scripts var.""" - install.initialize_options(self) - - # leave the proper script according to the platform - script = SCRIPT_WIN if sys.platform == "win32" else SCRIPT_REST - self.distribution.scripts = [script] - - def run(self): - """Run parent install, and then save the man file.""" - install.run(self) - - # man directory - if self._custom_man_dir is not None: - if not os.path.exists(self._custom_man_dir): - os.makedirs(self._custom_man_dir) - shutil.copy("man/fades.1", self._custom_man_dir) - - def finalize_options(self): - """Alter the installation path.""" - install.finalize_options(self) - if self.prefix is None: - # no place for man page (like in a 'snap') - man_dir = None - else: - man_dir = os.path.join(self.prefix, "share", "man", "man1") - - # if we have 'root', put the building path also under it (used normally - # by pbuilder) - if self.root is not None: - man_dir = os.path.join(self.root, man_dir[1:]) - self._custom_man_dir = man_dir - - -setup( - name='fades', - version=get_version(), - license='GPL-3', - author='Facundo Batista, Nicolás Demarchi', - author_email='facundo@taniquetil.com.ar, mail@gilgamezh.me', - description=( - 'A system that automatically handles the virtualenvs in the cases ' - 'normally found when writing scripts and simple programs, and ' - 'even helps to administer big projects.'), - long_description=open('README.rst').read(), - url='https://github.com/PyAr/fades', - download_url="https://github.com/PyAr/fades/releases", # Release download URL. - packages=["fades"], - scripts=[SCRIPT_WIN, SCRIPT_REST], - keywords="virtualenv utils utility scripts", # to get found easily on PyPI results, etc. - cmdclass={ - 'install': CustomInstall, - }, - install_requires=['setuptools'], - tests_require=['logassert', 'pyxdg', 'pyuca', 'pytest', 'flake8', - 'pep257', 'rst2html5'], # what unittests require - python_requires='>=3.10', # Minimum Python version supported. - extras_require={ - 'pyxdg': 'pyxdg', - 'packaging': 'packaging', - }, - - # https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - 'Development Status :: 5 - Production/Stable', - # 'Development Status :: 6 - Mature', - # 'Development Status :: 7 - Inactive', - - 'Environment :: Console', - - 'Intended Audience :: Developers', - 'Intended Audience :: System Administrators', - - 'License :: OSI Approved :: GNU General Public License (GPL)', - 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', - 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', - - 'Natural Language :: English', - 'Natural Language :: Spanish', - - 'Operating System :: MacOS', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Operating System :: POSIX :: Linux', - - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Programming Language :: Python :: 3.13', - 'Programming Language :: Python :: Implementation', - 'Programming Language :: Python :: Implementation :: CPython', - - 'Topic :: Software Development', - 'Topic :: Utilities', - ], -) diff --git a/test b/test index c9f758b..74ec9de 100755 --- a/test +++ b/test @@ -10,7 +10,7 @@ else TARGET_TESTS="" fi -./bin/fades -r requirements.txt -d pytest-cov -x pytest --cov=fades $TARGET_TESTS +python3 -m fades -r requirements.txt -d pytest-cov -x pytest --cov=fades $TARGET_TESTS # check if we are using exit() in the code. if grep -r -n ' exit(' --include="*.py" .; then echo 'Please use sys.exit() instead of exit(). https://github.com/PyAr/fades/issues/280'; fi diff --git a/testdev b/testdev index 8c38e68..8831865 100755 --- a/testdev +++ b/testdev @@ -10,4 +10,4 @@ else TARGET_TESTS="" fi -./bin/fades -r requirements.txt -x pytest -s $TARGET_TESTS +python3 -m fades -r requirements.txt -x pytest -s $TARGET_TESTS diff --git a/testdev.bat b/testdev.bat index e0c3bb7..42d2b0c 100644 --- a/testdev.bat +++ b/testdev.bat @@ -8,4 +8,4 @@ if not [%*] == [] ( set TARGET_TESTS=fades tests ) -bin\fades -r requirements.txt -x pytest -v %TARGET_TESTS% +python -m fades -r requirements.txt -x pytest -v %TARGET_TESTS% From 583fe70cc3418d0ec4444fd21ea3572fc207cd27 Mon Sep 17 00:00:00 2001 From: Facundo Batista Date: Sun, 5 Jul 2026 21:09:36 -0300 Subject: [PATCH 2/2] Added new files. --- pyproject.toml | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 31 +++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 pyproject.toml create mode 100644 setup.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..865c3b4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,90 @@ +# Copyright 2014-2026 Facundo Batista, Nicolás Demarchi +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranties of +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# For further info, check https://github.com/PyAr/fades + +[build-system] +requires = ["setuptools>=61"] +build-backend = "setuptools.build_meta" + +[project] +name = "fades" +dynamic = ["version"] +description = "A system that automatically handles the virtualenvs in the cases normally found when writing scripts and simple programs, and even helps to administer big projects." +readme = "README.rst" +license = {text = "GPL-3"} +authors = [ + {name = "Facundo Batista", email = "facundo@taniquetil.com.ar"}, + {name = "Nicolás Demarchi", email = "mail@gilgamezh.me"}, +] +requires-python = ">=3.10" +keywords = ["virtualenv", "utils", "utility", "scripts"] +dependencies = ["packaging"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + + "Environment :: Console", + + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + + "License :: OSI Approved :: GNU General Public License (GPL)", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", + + "Natural Language :: English", + "Natural Language :: Spanish", + + "Operating System :: MacOS", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX", + "Operating System :: POSIX :: Linux", + + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: Implementation", + "Programming Language :: Python :: Implementation :: CPython", + + "Topic :: Software Development", + "Topic :: Utilities", +] + +[project.optional-dependencies] +pyxdg = ["pyxdg"] + +[project.urls] +Homepage = "https://github.com/PyAr/fades" +Documentation = "https://fades.readthedocs.io/" +Download = "https://github.com/PyAr/fades/releases" + +[project.scripts] +fades = "fades.main:console_entry_point" + +[tool.setuptools] +packages = ["fades"] + +[tool.setuptools.dynamic] +version = {attr = "fades._version.__version__"} + +# NOTE: the man page is installed under /share/man/man1 via data_files +# declared in setup.py -- PEP 621 / pyproject.toml has no equivalent for it, and +# modern setuptools no longer accepts data_files in this file. That tiny setup.py +# is the only remaining piece of the legacy build; everything else lives here. diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..a615ad6 --- /dev/null +++ b/setup.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +# Copyright 2014-2026 Facundo Batista, Nicolás Demarchi +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 3, as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranties of +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# For further info, check https://github.com/PyAr/fades + +"""Shim over the declarative pyproject.toml, only for the man page. + +All the packaging metadata lives in ``pyproject.toml``. The single thing that +cannot be expressed there is installing the man page under +``/share/man/man1`` (there is no PEP 621 mechanism for ``data_files``, +and modern setuptools rejects it in ``pyproject.toml``). That's the only reason +this file still exists; it is consumed by ``pip``, the Debian ``pybuild`` flow +and the snap build. +""" + +from setuptools import setup + +setup(data_files=[("share/man/man1", ["man/fades.1"])])