From 42594d100606cff812c315a2feacb636e42a4053 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 22 Jan 2025 15:10:18 +0100 Subject: [PATCH] First commit --- .github/workflows/python-package.yml | 103 +++++++++++++++++++++++++++ .gitignore | 3 + MANIFEST.in | 3 + README.md | 37 ++++++++++ package/__init__.py | 2 + package/__main__.py | 22 ++++++ package/module.py | 7 ++ pyproject.toml | 52 ++++++++++++++ setup.cfg | 27 +++++++ setup.py | 5 ++ test_package.py | 10 +++ tox.ini | 34 +++++++++ 12 files changed, 305 insertions(+) create mode 100644 .github/workflows/python-package.yml create mode 100644 MANIFEST.in create mode 100644 package/__init__.py create mode 100644 package/__main__.py create mode 100644 package/module.py create mode 100644 pyproject.toml create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 test_package.py create mode 100644 tox.ini diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..9be9a41 --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,103 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python package + +on: + pull_request: + push: + branches: + - main + tags: + - "v*" + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] #, windows-latest, macos-latest] + python-version: + - "3.9" + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "pypy-3.9" + + name: ${{ matrix.os }} - ${{ matrix.python-version }} + steps: + - uses: actions/checkout@v4 + - name: Get history and tags for SCM versioning to work + run: | + git fetch --prune --unshallow + git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + version: "0.5.22" + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: pip install tox tox-uv + - name: Run tox + # Run tox using the version of Python in `PATH` + run: tox -e py + + dist: + runs-on: ubuntu-latest + needs: [test] + name: Build Python packages + steps: + - uses: actions/checkout@v4 + - name: Get history and tags for SCM versioning to work + run: | + git fetch --prune --unshallow + git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - uses: actions/setup-python@v5 + with: + python-version: "3.9" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install --upgrade wheel setuptools build + - name: Build package + run: python -m build -s -w -o dist/ + - uses: actions/upload-artifact@v4 + with: + name: dist + path: dist + + dist_check: + runs-on: ubuntu-latest + needs: [dist] + name: Twine check + steps: + - uses: actions/setup-python@v5 + with: + python-version: "3.9" + - name: Install dependencies + run: pip install twine + - uses: actions/download-artifact@v4 + with: + name: dist + path: dist + - run: twine check dist/* + + dist_upload: + runs-on: ubuntu-latest + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') + needs: [dist_check] + name: PyPI upload + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v4 + with: + name: dist + path: dist + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.gitignore b/.gitignore index 15201ac..b506588 100644 --- a/.gitignore +++ b/.gitignore @@ -169,3 +169,6 @@ cython_debug/ # PyPI configuration file .pypirc + +# Generated by setuptools_scm +package/_version.py diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..70797e0 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +# Add files to be included/excluded in the source distribution +#include /path/to/file +#recursive-include directory *.csv diff --git a/README.md b/README.md index 4262b63..1c2799e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,39 @@ # python-project-starter + This is a template repo to act as a reference when starting up a new project in python. It consolidates best practices in regards to minimal level of documentation as well as the CI aspects. + +Local installation can be done using [`uv`](https://github.com/astral-sh/uv): + +```bash +$ uv venv -p python3.10 +$ uv pip install -e . +$ source .venv/bin/activate +$ python +>>> from package import square +>>> square(3) +9 +``` + +After installation a command-line tool is also available: + +```bash +$ square 4 +Square of 4 is 16 +``` + +Running the tests can be done using [`tox`](https://tox.wiki/): + +```bash +$ tox -p +``` + +Building the packages can also be done using `tox`: + +```bash +$ tox -e packages +$ ls dist/ +``` + +Packaging uses [`setuptools-scm`](https://github.com/pypa/setuptools-scm), so the version of the software is based on git tags. + +To run the linting, we recommend `ruff`, a standard configuration is in the repo in `pyproject.toml`. diff --git a/package/__init__.py b/package/__init__.py new file mode 100644 index 0000000..daf2215 --- /dev/null +++ b/package/__init__.py @@ -0,0 +1,2 @@ +from .module import * # noqa +from ._version import __version__ # noqa diff --git a/package/__main__.py b/package/__main__.py new file mode 100644 index 0000000..e923ae0 --- /dev/null +++ b/package/__main__.py @@ -0,0 +1,22 @@ +from .module import square +from ._version import __version__ + + +def main(): + import argparse + + parser = argparse.ArgumentParser(prog="Squarer of ints") + parser.add_argument("x", type=int) + parser.add_argument( + "--version", + action="version", + version=f"%(prog)s, version {__version__}", + ) + args = parser.parse_args() + print(f"Square of {args.x} is {square(args.x)}") + + +if __name__ == "__main__": + import sys + + sys.exit(main()) diff --git a/package/module.py b/package/module.py new file mode 100644 index 0000000..93dd803 --- /dev/null +++ b/package/module.py @@ -0,0 +1,7 @@ +__all__ = [ + "square", +] + + +def square(x: int) -> int: + return x**2 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..15041d9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,52 @@ +[build-system] +requires = ["setuptools>=71", "setuptools-scm>=8"] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] +write_to = "package/_version.py" +write_to_template = "__version__ = \"{version}\"\n" + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "--strict-markers --doctest-modules --doctest-glob='*.rst' --ignore=setup.py" +doctest_optionflags = [ + "NORMALIZE_WHITESPACE", + "IGNORE_EXCEPTION_DETAIL", + "ELLIPSIS", +] +norecursedirs = [ + ".*", + "__pycache__", + "build", + "dist", + "*.egg-info", + "htmlcov", + "doc", +] +filterwarnings = [] + +[tool.ruff] +line-length = 100 +target-version = "py39" +extend-exclude = ["doc"] + +[tool.ruff.lint] +select = [ + "F", # pyflakes + "E", # pycodestyle + "W", # pycodestyle + "UP", # pyupgrade + "YTT", # flake8-2020 + "B", # flake8-bugbear + "T10", # flake8-debugger + "C4", # flake8-comprehensions + "G", # flake8-logging-format + "ISC", # flake8-implicit-str-concat + "ICN", # flake8-import-conventions +] +ignore = [] + +[tool.black] +line-length = 88 +target-version = ["py39"] +extend-exclude = "doc" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..1d7526b --- /dev/null +++ b/setup.cfg @@ -0,0 +1,27 @@ +[metadata] +name = Squarer +description = A package that squares integers +long_description = file: README.md +long_description_content_type = text/markdown +url = https://github.com/AmadeusITGroup/python-project-starter +author = your_name_here +author_email = no_reply@github.com +license_files = LICENSE +classifiers = + Development Status :: 5 - Production/Stable + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: Implementation :: PyPy + +[options] +zip_safe = False +include_package_data = True +packages = find: +python_requires = >=3.9 +setup_requires = + setuptools_scm + +[options.entry_points] +console_scripts = + square = package.__main__:main diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..4ca6776 --- /dev/null +++ b/setup.py @@ -0,0 +1,5 @@ +from setuptools import setup + +setup( + use_scm_version=True, +) diff --git a/test_package.py b/test_package.py new file mode 100644 index 0000000..ea97dbe --- /dev/null +++ b/test_package.py @@ -0,0 +1,10 @@ +import pytest +from package import square + + +@pytest.mark.parametrize( + "x,res", + [(1, 1), (2, 4), (-1, 1), (0, 0)], +) +def test_square(x, res): + assert square(x) == res diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..945ef51 --- /dev/null +++ b/tox.ini @@ -0,0 +1,34 @@ +[tox] +isolated_build = true +skip_missing_interpreters = true +envlist = + py{39,310,311,312,313} + pypy{39,310} + +[testenv] +package = wheel +wheel_build_env = .pkg +deps = pytest>=8.0 +commands = py.test {posargs} + +[testenv:ruff] +basepython = python3.9 +deps = ruff +commands = ruff check . + +[testenv:black] +basepython = python3.9 +deps = black +commands = black . + +[testenv:packages] +allowlist_externals = + rm +basepython = python3.9 +deps = + build + twine +commands = + rm -rf build *.egg-info + python -m build -s -w -o dist + twine check dist/*