From ab6dc48a97f367cd52b8bdd9a0dff923fa64af79 Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Thu, 10 Jul 2025 21:45:28 -0700 Subject: [PATCH 1/3] fix test discovery for expected test and coverage tools * help pytest find the tests without mutating the python path * add a skiptest for miscstdin on macos Signed-off-by: Stephen L Arnold --- tests/{test.py => test_tftpy.py} | 2 ++ 1 file changed, 2 insertions(+) rename tests/{test.py => test_tftpy.py} (99%) diff --git a/tests/test.py b/tests/test_tftpy.py similarity index 99% rename from tests/test.py rename to tests/test_tftpy.py index 718d0ac..142fcea 100644 --- a/tests/test.py +++ b/tests/test_tftpy.py @@ -4,6 +4,7 @@ import logging import os +import sys import threading import time import unittest @@ -643,6 +644,7 @@ def testDirectoriesWithSpaces(self): else: server.listen("localhost", 20001) + @unittest.skipUnless(sys.platform == "linux", "Linux only fcntl") def testStdin(self): cdir = os.path.dirname(os.path.abspath(__file__)) script = os.path.join(cdir, "stdin.py") From 216a057db0870ae0f85d39747c3a78f99744a1f9 Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Thu, 10 Jul 2025 18:57:07 -0700 Subject: [PATCH 2/3] chg: dev: add pytest/coverage configs to pyproject.toml, plus a tox file * re-enable setuptools_scm in build-requires, set version in setup.cfg * fix rpm builds and remove toml warnings with setup.cfg, fix .pylintrc * make sure setuptools_scm dep is old enough for el9 packages Signed-off-by: Stephen L Arnold --- .pylintrc | 2 +- pyproject.toml | 61 +++++++---- setup.cfg | 82 +++++++++++++++ tox.ini | 275 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 398 insertions(+), 22 deletions(-) create mode 100644 setup.cfg create mode 100644 tox.ini diff --git a/.pylintrc b/.pylintrc index a5f8803..9a5d551 100644 --- a/.pylintrc +++ b/.pylintrc @@ -536,4 +536,4 @@ valid-metaclass-classmethod-first-arg=mcs # Exceptions that will emit a warning when being caught. Defaults to # "Exception" -overgeneral-exceptions=Exception +overgeneral-exceptions=builtins.Exception diff --git a/pyproject.toml b/pyproject.toml index b781964..4a40345 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,29 +1,48 @@ [build-system] -requires = ["setuptools>=61.0", "wheel"] +requires = [ + "setuptools>=45", + "setuptools_scm[toml]>=6.0", +] build-backend = "setuptools.build_meta" -[tool.setuptools.packages.find] -where = ["."] -include = ["tftpy"] +[tool.setuptools_scm] +version_scheme = "post-release" + +[tool.pytest.ini_options] +minversion = "6.0" +log_cli = false +doctest_optionflags = ["ELLIPSIS", "NORMALIZE_WHITESPACE",] +addopts = "--strict-markers -rxX --tb=long --color=yes" +markers = "subscript" +testpaths = [ + "tests/", +] -[project] -name = "tftpy" -version = "0.8.6" -authors = [ - { name="Michael Soulier", email="msoulier@digitaltorque.ca" }, +[tool.coverage.run] +branch = true +source = [ + "tftpy/", + ".tox/py*/lib/python*/site-packages/", ] -description = "A TFTP protocol library for Python" -readme = "README.md" -requires-python = ">=3.8" -classifiers = [ - "Programming Language :: Python :: 3", - "Operating System :: OS Independent", +omit = [ + "tests/*", + ".tox", ] -license = { text = "MIT" } -[project.urls] -Homepage = "https://github.com/msoulier/tftpy" -Issues = "https://github.com/msoulier/tftpy/issues" +[tool.coverage.paths] +source = ["tftpy"] -[tools.setuptools] -script-files = ["bin/tftpy_client.py", "bin/tftpy_server.py"] +[tool.coverage.report] +fail_under = 70 +show_missing = true +exclude_lines = [ + "pragma: no cover", + "raise NotImplementedError", + "raise AssertionError", + "if typing.TYPE_CHECKING:", + "if TYPE_CHECKING:", +] +omit = [ + "doc*", + "tests/*", +] diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..54dff5b --- /dev/null +++ b/setup.cfg @@ -0,0 +1,82 @@ +[metadata] +name = tftpy +version = attr: setuptools_scm.get_version +description = A TFTP protocol library for Python +url = https://github.com/msoulier/tftpy +author = Michael Soulier +author_email = msoulier@digitaltorque.ca +long_description = file: README.md +long_description_content_type = text/markdown +license_expression = MIT +license_files = LICENSE +classifiers = + Development Status :: 4 - Beta + Intended Audience :: Developers + Programming Language :: Python :: 3 + Environment :: Console + Environment :: No Input/Output (Daemon) + Operating System :: OS Independent + Topic :: Internet + Topic :: Software Development + Topic :: Software Development :: Testing + +project_urls = + Source = https://github.com/msoulier/tftpy.git + Issues = https://github.com/msoulier/tftpy/issues + +[options] +python_requires = + >= 3.8 + +setup_requires = + setuptools_scm[toml] + +install_requires = + +packages = + tftpy + +scripts = + bin/tftpy_client.py + bin/tftpy_server.py + +[options.extras_require] +doc = + sphinx + sphinx_git + myst-parser + sphinx_rtd_theme<3.0 + sphinxcontrib-apidoc +test = + pytest + pytest-cov +cov = + coverage[toml] + coverage_python_version +all = + %(cov)s + %(doc)s + %(test)s + +[check] +metadata = true +restructuredtext = true +strict = false + +[check-manifest] +ignore = + .gitattributes + .gitignore + .pre-commit-config.yaml + +[flake8] +exclude = + .git, + __pycache__, + build, + dist, + doc, + docs, + tests + +max-line-length = 90 diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..c9716f2 --- /dev/null +++ b/tox.ini @@ -0,0 +1,275 @@ +[tox] +envlist = py3{9,10,11,12,13}-{linux,macos,windows},coverage +skip_missing_interpreters = true +isolated_build = true +skipsdist = true + +[gh-actions] +python = + 3.9: py39 + 3.10: py310 + 3.11: py311 + 3.12: py312 + 3.13: py313 + +[gh-actions:env] +PLATFORM = + ubuntu-latest: linux + macos-latest: macos + windows-latest: windows + +[base] +deps = + pip>=21.1 + setuptools_scm[toml] + +[build] +deps = + pip>=21.1 + build + twine + +[testenv] +skip_install = true +install_command = pip install {opts} {packages} + +passenv = + DISPLAY + XAUTHORITY + HOME + USERNAME + USER + XDG_* + TWINE_* + CI + OS + PYTHONIOENCODING + PIP_DOWNLOAD_CACHE + +setenv = + COVERAGE_FILE = .coverage.{envname} + +deps = + {[base]deps} + pytest + pytest-cov + -e . + +commands = + python -m pytest -v tests/ --capture={posargs:fd} --cov=tftpy --cov-branch --cov-report term-missing + +[testenv:coverage] +basepython = + python3 + +skip_install = + true + +allowlist_externals = + bash + +deps = + coverage[toml] + coverage_python_version + +commands = + bash -c 'coverage combine .coverage.py*' + coverage xml + +depends = + py39 + py310 + py311 + py312 + py313 + +[coverage:run] +parallel=True +omit = + */tests/* + */_version.py + +[testenv:lint] +passenv = + CI + OS + PIP_DOWNLOAD_CACHE + PYTHONIOENCODING + +allowlist_externals = + bash + +deps = + {[base]deps} + pylint + -e . + +commands = + pylint --fail-under=7 tftpy/ + +[testenv:build] +passenv = + pythonLocation + CI + GITHUB* + PYTHONIOENCODING + PIP_DOWNLOAD_CACHE + +allowlist_externals = bash + +deps = + {[build]deps} + +commands = + python -m build . + twine check dist/* + +[testenv:check] +skip_install = true + +passenv = + pythonLocation + CI + PYTHONIOENCODING + PIP_DOWNLOAD_CACHE + +deps = + pip>=21.2 + +commands = + python -m pip install tftpy --pre --force-reinstall --prefer-binary -f dist/ + python -m pip show -f tftpy + +[testenv:docs] +skip_install = true +allowlist_externals = + bash + make + +deps = + {[base]deps} + sphinx + recommonmark + sphinx_rtd_theme + sphinx_git + -e . + +commands = make -C doc html + +[testenv:docs-lint] +skip_install = true +allowlist_externals = + {[testenv:docs]allowlist_externals} + +deps = + {[testenv:docs]deps} + +commands = make -C doc linkcheck + +[testenv:changes] +skip_install = true +allowlist_externals = + {[testenv:docs]allowlist_externals} + +passenv = + CI + OS + PIP_DOWNLOAD_CACHE + VERSION + +deps = + {[base]deps} + git+https://github.com/sarnold/gitchangelog.git@master + +commands = + bash -c 'gitchangelog {posargs} > CHANGELOG.rst' + +[testenv:style] +passenv = + CI + OS + PIP_DOWNLOAD_CACHE + +deps = + {[base]deps} + flake8 + flake8-bugbear + +commands_pre = + {[testenv:lint]commands_pre} + +commands = + flake8 tftpy/ + +[testenv:mypy] +skip_install = true + +deps = + {[base]deps} + six + mypy + +commands_pre = + {[testenv:lint]commands_pre} + +commands = + python -m mypy --follow-imports=normal --install-types \ + --non-interactive tftpy/ + +[testenv:black] +skip_install = true + +deps = + {[base]deps} + black + +commands = + black -v -S tftpy/ {posargs} + +[testenv:isort] +skip_install = true + +deps = + {[base]deps} + isort + +commands = + python -m isort tftpy/ + +[testenv:sec] +skip_install = true +passenv = + PYTHON + CI + OS + PYTHONIOENCODING + PIP_DOWNLOAD_CACHE + +allowlist_externals = bash + +deps = + {[base]deps} + bandit + +commands = + bandit -r tftpy/ + +[testenv:publish] +description = Publish package +envdir = {toxworkdir}/build +deps = {[testenv:build]deps} +commands = + {[testenv:build]commands} + twine upload dist/* + +[testenv:clean] +skip_install = true +allowlist_externals = + bash + +deps = + pip>=21.2 + +commands = + bash -c 'make -C doc/ clean' + bash -c 'rm -rf build dist .coverage* coverage.xml tftpy/_version.py *.egg-info' From d8987740e011d7e0a448391769d6bdd7043db6fd Mon Sep 17 00:00:00 2001 From: Stephen L Arnold Date: Fri, 11 Jul 2025 20:51:45 -0700 Subject: [PATCH 3/3] new: dev: add baseline test and release workflows Signed-off-by: Stephen L Arnold --- .github/workflows/release.yml | 94 +++++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 56 +++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..edf4410 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,94 @@ +name: Release + +on: + push: + # release on tag push + tags: + - '*' + +jobs: + wheels: + + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + env: + PYTHONIOENCODING: utf-8 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + python-version: [3.9, '3.10', '3.11', '3.12', '3.13'] + + steps: + - name: Set git crlf/eol + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel + pip install tox + + - name: Build dist pkgs + run: | + tox -e build,check + + - name: Upload artifacts + if: matrix.python-version == 3.9 && runner.os == 'Linux' + uses: actions/upload-artifact@v4 + with: + name: wheels + path: | + ./dist/*.whl + ./dist/*.tar.gz + + create_release: + name: Create Release + needs: [wheels] + runs-on: ubuntu-latest + + steps: + - name: Get version + id: get_version + run: | + echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV + echo ${{ env.VERSION }} + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # download all artifacts to project dir + - uses: actions/download-artifact@v4 + + - name: Generate changes file + uses: sarnold/gitchangelog-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN}} + + - name: Create release + id: create_release + uses: softprops/action-gh-release@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.VERSION }} + name: Release v${{ env.VERSION }} + body_path: CHANGES.md + draft: false + prerelease: false + files: | + wheels/*.whl + wheels/*.tar.gz diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..7f9dca2 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,56 @@ +name: Test + +on: + workflow_dispatch: + pull_request: + push: + branches: [ master ] + +jobs: + build: + + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + env: + OS: ${{ matrix.os }} + PYTHON: ${{ matrix.python-version }} + PYTHONIOENCODING: utf-8 + PIP_DOWNLOAD_CACHE: ${{ github.workspace }}/../.pip_download_cache + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + python-version: [3.9, '3.10', '3.11', '3.12', '3.13'] + steps: + - name: Set git crlf/eol + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox tox-gh-actions + + - name: Lint with tox + run: tox -e lint + + - name: Run tests + run: | + tox + env: + PLATFORM: ${{ matrix.os }} + + - name: Build pkg + run: tox -e build,check