From 908f0e8044b091ff1a1408b5f4bb58b861ddc67c Mon Sep 17 00:00:00 2001 From: Ryan Peck Date: Sat, 10 Jan 2026 17:29:27 -0500 Subject: [PATCH 1/4] Modernize project for Python 3.9+ and migrate to GitHub Actions Update Python support from 3.5-3.7 to 3.9-3.13, replacing deprecated private ipaddress APIs with public interfaces. Migrate CI from Travis to GitHub Actions with uv-based workflows for testing and PyPI publishing. Add Makefile with test and build targets using uv. Switch default branch from master to main. Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/ci.yml | 70 +++++++++++++++++++++++++++++++ .github/workflows/publish.yml | 78 +++++++++++++++++++++++++++++++++++ .travis.yml | 25 ----------- Makefile | 48 +++++++++++++++++++++ README.md | 4 +- ipgroup.py | 12 ++++-- ipgroup_test.py | 4 +- setup.py | 10 +++-- 8 files changed, 214 insertions(+), 37 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/publish.yml delete mode 100644 .travis.yml create mode 100644 Makefile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a07d498 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,70 @@ +name: CI + +on: + push: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + version: "latest" + + - name: Set up Python ${{ matrix.python-version }} + run: uv python install ${{ matrix.python-version }} + + - name: Install dependencies + run: | + uv venv --python ${{ matrix.python-version }} + uv pip install -e . + + - name: Run tests + run: uv run python ipgroup_test.py -v + + - name: Install coverage + run: uv pip install coverage coveralls + + - name: Run tests with coverage + run: uv run coverage run ipgroup_test.py + + - name: Upload coverage to Coveralls + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + run: | + uv run coveralls --service=github + continue-on-error: true + + build: + runs-on: ubuntu-latest + needs: test + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + version: "latest" + + - name: Set up Python + run: uv python install 3.13 + + - name: Build package + run: uv build + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..a7fbedf --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,78 @@ +name: Publish to PyPI + +on: + push: + tags: + - '*' + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + version: "latest" + + - name: Set up Python ${{ matrix.python-version }} + run: uv python install ${{ matrix.python-version }} + + - name: Install dependencies + run: | + uv venv --python ${{ matrix.python-version }} + uv pip install -e . + + - name: Run tests + run: uv run python ipgroup_test.py -v + + build: + runs-on: ubuntu-latest + needs: test + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + version: "latest" + + - name: Set up Python + run: uv python install 3.13 + + - name: Build package + run: uv build + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ + + publish: + runs-on: ubuntu-latest + needs: build + permissions: + id-token: write # Required for trusted publishing to PyPI + + steps: + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: dist + path: dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 12f886b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -dist: xenial -language: python -sudo: false -python: -- '3.5' -- '3.6' -- '3.7' -- '3.8-dev' -- 'nightly' -env: -- PYTHONPATH=. -install: -- pip install coveralls -script: coverage run ipgroup_test.py -after_success: coveralls -deploy: - provider: pypi - user: rypeck - password: - secure: ITMC3cQXglwDp8jvQxyduOwWdHtu5lT5YSuSkw1AwD4LxnWcH+sgnOM6+USAjS9ug43DvzuRHZ6v3auMcZ2tKwWLceaahSHzIsZOcUe5mPp/fXCqkxwLnT+W++LsBCQUajo6aGIReKOo+q9CdQcSzpyCvgo8xLNX/syXwPkrHJ4= - on: - branch: master - tags: true - python: 3.7 - repo: RyPeck/python-ipgroup diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..db6822b --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +.PHONY: help test test-verbose build clean install dev lint format check-format + +help: + @echo "Available targets:" + @echo " test - Run tests using uv" + @echo " test-verbose - Run tests with verbose output" + @echo " build - Build the package using uv" + @echo " clean - Remove build artifacts and cache files" + @echo " install - Install package in development mode" + @echo " dev - Install development dependencies" + @echo " lint - Run linting (if configured)" + @echo " format - Format code (if configured)" + @echo " check-format - Check code formatting" + +test: + uv run python ipgroup_test.py + +test-verbose: + uv run python ipgroup_test.py -v + +build: clean + uv build + +clean: + rm -rf build/ + rm -rf dist/ + rm -rf *.egg-info + rm -rf .pytest_cache + rm -rf __pycache__ + find . -type d -name "__pycache__" -exec rm -rf {} + + find . -type f -name "*.pyc" -delete + find . -type f -name "*.pyo" -delete + find . -type f -name "*~" -delete + +install: + uv pip install -e . + +dev: + uv pip install -e ".[dev]" + +lint: + @echo "No linting configured yet. Install ruff or flake8 to enable linting." + +format: + @echo "No formatting configured yet. Install ruff or black to enable formatting." + +check-format: + @echo "No format checking configured yet." diff --git a/README.md b/README.md index 06069f8..c709057 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ IP Grouping Python ================== -[![Build -Status](https://travis-ci.org/RyPeck/python-ipgroup.svg?branch=master)](https://travis-ci.org/RyPeck/python-ipgroup) [![Coverage -Status](https://coveralls.io/repos/RyPeck/python-ipgroup/badge.png)](https://coveralls.io/r/RyPeck/python-ipgroup) [![Downloads](https://pypip.in/download/ipgroup/badge.svg)](https://pypi.python.org/pypi/ipgroup/ ) [![License](https://pypip.in/license/ipgroup/badge.svg)](https://pypi.python.org/pypi/ipgroup/) [![Supported Python versions](https://pypip.in/py_versions/ipgroup/badge.svg)](https://pypi.python.org/pypi/ipgroup/) [![Development Status](https://pypip.in/status/ipgroup/badge.svg)](https://pypi.python.org/pypi/ipgroup/) +[![Coverage Status](https://coveralls.io/repos/RyPeck/python-ipgroup/badge.png)](https://coveralls.io/r/RyPeck/python-ipgroup) [![Downloads](https://pypip.in/download/ipgroup/badge.svg)](https://pypi.python.org/pypi/ipgroup/ ) [![License](https://pypip.in/license/ipgroup/badge.svg)](https://pypi.python.org/pypi/ipgroup/) [![Supported Python versions](https://pypip.in/py_versions/ipgroup/badge.svg)](https://pypi.python.org/pypi/ipgroup/) [![Development Status](https://pypip.in/status/ipgroup/badge.svg)](https://pypi.python.org/pypi/ipgroup/) ## Usage diff --git a/ipgroup.py b/ipgroup.py index a711c41..3a29a22 100644 --- a/ipgroup.py +++ b/ipgroup.py @@ -116,12 +116,16 @@ def _validate_ips_param(self, ips): """ # Acceptable inputs - assert(isinstance(ips, (str, list, self.IPVersion))) + valid_types = (str, list) + if self.IPVersion is not None: + valid_types = valid_types + self.IPVersion + assert(isinstance(ips, valid_types)) # Unpack a list if isinstance(ips, list): for i in ips: - assert(isinstance(i, (str, ipaddress._IPAddressBase))) + assert(isinstance(i, (str, ipaddress.IPv4Address, ipaddress.IPv6Address, + ipaddress.IPv4Network, ipaddress.IPv6Network))) if isinstance(i, str): assert(self._validate_IPNetwork_str(i)) @@ -197,14 +201,14 @@ class IPv4Group(_BaseGroup): """Group of IPv4 Addresses""" def __init__(self, ip_objs, net_bits=24): - _BaseGroup.__init__(self, ip_objs, net_bits, ipaddress._BaseV4) + _BaseGroup.__init__(self, ip_objs, net_bits, (ipaddress.IPv4Network, ipaddress.IPv4Address)) class IPv6Group(_BaseGroup): """Group of IPv6 Addresses""" def __init__(self, ip_objs, net_bits=48): - _BaseGroup.__init__(self, ip_objs, net_bits, ipaddress._BaseV6) + _BaseGroup.__init__(self, ip_objs, net_bits, (ipaddress.IPv6Network, ipaddress.IPv6Address)) def totalAddresses(ips): diff --git a/ipgroup_test.py b/ipgroup_test.py index 40179a2..854ab29 100755 --- a/ipgroup_test.py +++ b/ipgroup_test.py @@ -61,7 +61,7 @@ def test_group3(self): # So out sample size is never bigger than the population of hosts random_int = random.randint(1, 2**(32 - random_cidr - 1)) - IPs = random.sample(set(network.hosts()), random_int) + IPs = random.sample(list(network.hosts()), random_int) expected_results = {("129.21.0.0/%s" % random_cidr): random_int} @@ -81,7 +81,7 @@ def test_IPv6(self): # So out sample size is never bigger than the population of hosts random_int = random.randint(1, 2**(128 - random_cidr - 1)) - IPs = random.sample(set(network.hosts()), random_int) + IPs = random.sample(list(network.hosts()), random_int) expected_results = {("2607:f8b0:4009:803::/%s" % random_cidr): random_int} diff --git a/setup.py b/setup.py index 6d3963d..7e8b8d6 100644 --- a/setup.py +++ b/setup.py @@ -40,9 +40,12 @@ "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Internet", "Topic :: Internet :: Log Analysis", "Topic :: Software Development :: Libraries", @@ -53,4 +56,5 @@ py_modules=["ipgroup"], long_description=long_description, long_description_content_type="text/markdown", + python_requires=">=3.9", ) From 2644ce81349735302f174b6467c0d54894adba98 Mon Sep 17 00:00:00 2001 From: Ryan Peck Date: Sat, 10 Jan 2026 17:37:36 -0500 Subject: [PATCH 2/4] Add caching to GitHub Actions workflows Enable uv cache in all workflow jobs to speed up CI runs by caching Python installations and dependencies. Co-Authored-By: Claude Sonnet 4.5 --- .github/workflows/ci.yml | 2 ++ .github/workflows/publish.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a07d498..7d2ab03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,7 @@ jobs: uses: astral-sh/setup-uv@v4 with: version: "latest" + enable-cache: true - name: Set up Python ${{ matrix.python-version }} run: uv python install ${{ matrix.python-version }} @@ -56,6 +57,7 @@ jobs: uses: astral-sh/setup-uv@v4 with: version: "latest" + enable-cache: true - name: Set up Python run: uv python install 3.13 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a7fbedf..0c18c7e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,6 +23,7 @@ jobs: uses: astral-sh/setup-uv@v4 with: version: "latest" + enable-cache: true - name: Set up Python ${{ matrix.python-version }} run: uv python install ${{ matrix.python-version }} @@ -46,6 +47,7 @@ jobs: uses: astral-sh/setup-uv@v4 with: version: "latest" + enable-cache: true - name: Set up Python run: uv python install 3.13 From 19350df7c6889ed1bdab742cd386a96b066951a3 Mon Sep 17 00:00:00 2001 From: Ryan Peck Date: Sat, 10 Jan 2026 19:55:19 -0500 Subject: [PATCH 3/4] Switch to uv --- .github/workflows/publish.yml | 2 -- .gitignore | 7 ++++ pyproject.toml | 46 +++++++++++++++++++++++++++ setup.py | 60 ----------------------------------- 4 files changed, 53 insertions(+), 62 deletions(-) create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0c18c7e..a7fbedf 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,7 +23,6 @@ jobs: uses: astral-sh/setup-uv@v4 with: version: "latest" - enable-cache: true - name: Set up Python ${{ matrix.python-version }} run: uv python install ${{ matrix.python-version }} @@ -47,7 +46,6 @@ jobs: uses: astral-sh/setup-uv@v4 with: version: "latest" - enable-cache: true - name: Set up Python run: uv python install 3.13 diff --git a/.gitignore b/.gitignore index 2ecb5f8..c00289c 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,10 @@ nosetests.xml *,cover .idea/ + +# Virtual environments +.venv/ +venv/ + +# uv lock file (not needed for libraries) +uv.lock diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..62dada9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,46 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "ipgroup" +version = "0.0.5" +description = "Functions to gather info on a group of IPv4 or IPv6 Networks" +readme = "README.md" +requires-python = ">=3.9" +license = "Apache-2.0" +authors = [ + { name = "Ryan Peck", email = "ryan@rypeck.com" }, +] +maintainers = [ + { name = "Ryan Peck", email = "ryan@rypeck.com" }, +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "Intended Audience :: System Administrators", + "Intended Audience :: Telecommunications Industry", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Internet", + "Topic :: Internet :: Log Analysis", + "Topic :: Software Development :: Libraries", + "Topic :: System :: Networking", + "Topic :: Utilities", +] + +[project.urls] +Homepage = "https://github.com/RyPeck/python-ipgroup" +Repository = "https://github.com/RyPeck/python-ipgroup" +Issues = "https://github.com/RyPeck/python-ipgroup/issues" + +[tool.hatch.build.targets.wheel] +packages = ["ipgroup.py"] diff --git a/setup.py b/setup.py deleted file mode 100644 index 7e8b8d6..0000000 --- a/setup.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2019 Ryan Peck -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from setuptools import setup -from os import path - -import ipgroup - - -this_directory = path.abspath(path.dirname(__file__)) -with open(path.join(this_directory, "README.md"), encoding="utf-8") as f: - long_description = f.read() - -setup( - name="ipgroup", - maintainer="Ryan Peck", - maintainer_email="ryan@rypeck.com", - version=ipgroup.__version__, - url="https://github.com/RyPeck/python-ipgroup", - license="Apache License, Version 2.0", - classifiers=[ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "Intended Audience :: Information Technology", - "Intended Audience :: System Administrators", - "Intended Audience :: Telecommunications Industry", - "License :: OSI Approved :: Apache Software License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Topic :: Internet", - "Topic :: Internet :: Log Analysis", - "Topic :: Software Development :: Libraries", - "Topic :: System :: Networking", - "Topic :: Utilities", - ], - description="Functions to gather info on a group of IPv4 or IPv6 Networks", - py_modules=["ipgroup"], - long_description=long_description, - long_description_content_type="text/markdown", - python_requires=">=3.9", -) From b599e76ddf8651625ebec9d8eef76c86f91d6f5b Mon Sep 17 00:00:00 2001 From: Ryan Peck Date: Sat, 10 Jan 2026 20:36:46 -0500 Subject: [PATCH 4/4] Add daily cache expiration to CI workflow Add date-based cache suffix to uv setup in both test and build jobs to ensure dependency cache refreshes daily, preventing stale packages. Co-Authored-By: Claude Opus 4.5 --- .github/workflows/ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d2ab03..4375d89 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,11 +15,17 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Get current date + id: date + run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT + - name: Install uv uses: astral-sh/setup-uv@v4 with: version: "latest" enable-cache: true + cache-dependency-glob: "pyproject.toml" + cache-suffix: ${{ steps.date.outputs.date }} - name: Set up Python ${{ matrix.python-version }} run: uv python install ${{ matrix.python-version }} @@ -53,11 +59,17 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Get current date + id: date + run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT + - name: Install uv uses: astral-sh/setup-uv@v4 with: version: "latest" enable-cache: true + cache-dependency-glob: "pyproject.toml" + cache-suffix: ${{ steps.date.outputs.date }} - name: Set up Python run: uv python install 3.13