diff --git a/.github/workflows/benchmark_release.yml b/.github/workflows/benchmark_release.yml index 1f436c05..d8b07398 100644 --- a/.github/workflows/benchmark_release.yml +++ b/.github/workflows/benchmark_release.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install dependencies run: | diff --git a/.github/workflows/binding.yml b/.github/workflows/binding.yml index 3d383fe3..568e3b44 100644 --- a/.github/workflows/binding.yml +++ b/.github/workflows/binding.yml @@ -21,10 +21,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -177,10 +177,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Python 3.12 - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: "3.12" diff --git a/.github/workflows/clang_format.yml b/.github/workflows/clang_format.yml index b22719f7..434fa8d4 100644 --- a/.github/workflows/clang_format.yml +++ b/.github/workflows/clang_format.yml @@ -17,7 +17,7 @@ jobs: - 'test' - 'examples' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Run clang-format style check uses: jidicula/clang-format-action@v4.16.0 with: diff --git a/.github/workflows/cmake_examples.yml b/.github/workflows/cmake_examples.yml index 327d07cb..27e94c9d 100644 --- a/.github/workflows/cmake_examples.yml +++ b/.github/workflows/cmake_examples.yml @@ -28,7 +28,7 @@ jobs: brew install tbb fi - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Build DSF with examples and install working-directory: ${{github.workspace}} diff --git a/.github/workflows/cmake_tests.yml b/.github/workflows/cmake_tests.yml index a133047a..538db7a8 100644 --- a/.github/workflows/cmake_tests.yml +++ b/.github/workflows/cmake_tests.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install dependencies on Ubuntu if: matrix.os == 'ubuntu-latest' diff --git a/.github/workflows/codacy.yml b/.github/workflows/codacy.yml index b2ac336d..1b542296 100644 --- a/.github/workflows/codacy.yml +++ b/.github/workflows/codacy.yml @@ -36,7 +36,7 @@ jobs: steps: # Checkout the repository to the GitHub Actions runner - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis - name: Run Codacy Analysis CLI diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2912f58c..ec51b3d9 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/.github/workflows/flawfinder.yml b/.github/workflows/flawfinder.yml index d7302db2..a4fad949 100644 --- a/.github/workflows/flawfinder.yml +++ b/.github/workflows/flawfinder.yml @@ -19,7 +19,7 @@ jobs: security-events: write steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: flawfinder_scan uses: david-a-wheeler/flawfinder@8e4a779ad59dbfaee5da586aa9210853b701959c diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 3e1ebc89..53c6bd2a 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: true diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 8e1ecf95..8b5a6148 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Extract version from dsf.hpp id: extract @@ -78,9 +78,57 @@ jobs: echo "should_build=true" >> $GITHUB_OUTPUT fi + uv-validate: + name: Validate uv workflows + needs: [check-version] + runs-on: ubuntu-latest + if: needs.check-version.outputs.should_build == 'true' + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Set up uv + uses: astral-sh/setup-uv@v5 + + - name: Install system dependencies + run: | + sudo apt update + sudo apt install -y cmake build-essential doxygen libtbb-dev + + - name: Validate uv build + env: + CMAKE_ARGS: "-DDSF_OPTIMIZE_ARCH=OFF" + DSF_OPTIMIZE_ARCH: "OFF" + DSF_PACKAGE_VERSION: ${{ needs.check-version.outputs.publish_version }} + run: uv build + + - name: Validate uv source install + env: + DSF_PACKAGE_VERSION: ${{ needs.check-version.outputs.publish_version }} + run: | + uv venv .venv-uv-install + source .venv-uv-install/bin/activate + uv pip install . + python -c "import dsf; print(dsf.__version__)" + + - name: Validate uv editable install + env: + DSF_PACKAGE_VERSION: ${{ needs.check-version.outputs.publish_version }} + run: | + uv venv .venv-uv-editable + source .venv-uv-editable/bin/activate + uv pip install -e . + python -c "import dsf; print(dsf.__version__)" + build-wheels-linux: name: Build wheels on Linux (Python ${{ matrix.python-version }}) - needs: [check-version] + needs: [check-version, uv-validate] runs-on: ubuntu-latest if: needs.check-version.outputs.should_build == 'true' strategy: @@ -89,10 +137,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -125,14 +173,14 @@ jobs: python -c "import dsf; print('DSF wheel installation successful')" - name: Upload wheels as artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: wheel-linux-${{ matrix.python-version }} path: wheelhouse/*.whl build-wheels-macos: name: Build wheels on macOS (Python ${{ matrix.python-version }}) - needs: [check-version] + needs: [check-version, uv-validate] runs-on: macos-latest if: needs.check-version.outputs.should_build == 'true' strategy: @@ -141,10 +189,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -174,14 +222,14 @@ jobs: delocate-wheel -w wheelhouse -v --require-archs $(uname -m) dist/*.whl - name: Upload wheels as artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: wheel-macos-${{ matrix.python-version }} path: wheelhouse/*.whl build-wheels-windows: name: Build wheels on Windows (Python ${{ matrix.python-version }}) - needs: [check-version] + needs: [check-version, uv-validate] runs-on: windows-latest if: needs.check-version.outputs.should_build == 'true' strategy: @@ -190,10 +238,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -220,23 +268,23 @@ jobs: run: python -m build --wheel - name: Upload wheels as artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: wheel-windows-${{ matrix.python-version }} path: dist/*.whl build-sdist: name: Build source distribution - needs: [check-version] + needs: [check-version, uv-validate] runs-on: ubuntu-latest if: needs.check-version.outputs.should_build == 'true' steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: "3.12" @@ -256,14 +304,14 @@ jobs: run: python -m build --sdist - name: Upload sdist as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: sdist path: dist/*.tar.gz publish: name: Publish to PyPI - needs: [check-version, build-wheels-linux, build-wheels-macos, build-wheels-windows, build-sdist] + needs: [check-version, uv-validate, build-wheels-linux, build-wheels-macos, build-wheels-windows, build-sdist] runs-on: ubuntu-latest if: needs.check-version.outputs.should_build == 'true' @@ -282,7 +330,7 @@ jobs: ls -la dist/ - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: "3.12" diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 7f34a151..a70ff397 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -14,7 +14,7 @@ jobs: python-version: ["3.10", "3.12"] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index ed5491bc..a839577a 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -10,9 +10,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python 3.12 - uses: actions/setup-python@v3 + uses: actions/setup-python@v6 with: python-version: "3.12" - name: Install dependencies diff --git a/pyproject.toml b/pyproject.toml index ae64a1fa..66090572 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,65 @@ [build-system] -requires = ["setuptools>=61.0", "wheel", "pybind11-stubgen"] -build-backend = "setuptools.build_meta" \ No newline at end of file +requires = ["setuptools>=77.0.0", "wheel", "cmake>=3.16", "pybind11-stubgen"] +build-backend = "setuptools.build_meta" + +[project] +name = "dsf-mobility" +description = "DSF C++ core with Python bindings via pybind11" +readme = { file = "README.md", content-type = "text/markdown" } +requires-python = ">=3.10" +license = "CC-BY-NC-SA-4.0" +license-files = ["LICENSE"] +authors = [ + { name = "Grufoony", email = "gregorio.berselli@studio.unibo.it" }, +] +keywords = [ + "traffic", + "simulation", + "dynamics", + "network", + "modeling", + "transportation", + "mobility", + "congestion", + "flow", + "optimization", +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Science/Research", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.12", + "Programming Language :: C++", + "Topic :: Scientific/Engineering :: Physics", + "Topic :: Scientific/Engineering :: Information Analysis", + "Topic :: Software Development :: Libraries :: Python Modules", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS", +] +dependencies = [ + "osmnx>=2.0.6", + "networkx>=3", + "numpy", + "geopandas", + "shapely", + "folium", +] +dynamic = ["version"] + +[project.urls] +Homepage = "https://github.com/physycom/DynamicalSystemFramework" +Documentation = "https://physycom.github.io/DynamicalSystemFramework/" +Repository = "https://github.com/physycom/DynamicalSystemFramework" +Issues = "https://github.com/physycom/DynamicalSystemFramework/issues" + +[tool.setuptools] +include-package-data = true + +[tool.setuptools.packages.find] +where = ["src"] +include = ["dsf*"] +namespaces = true + +[tool.setuptools.package-data] +dsf = ["*.pyi", "py.typed", "**/*.pyi"] \ No newline at end of file diff --git a/setup.py b/setup.py index 99221703..3ba5eaa3 100644 --- a/setup.py +++ b/setup.py @@ -15,12 +15,17 @@ import sys import xml.etree.ElementTree as ET -from setuptools import setup, Extension, find_namespace_packages +from setuptools import setup, Extension from setuptools.command.build_ext import build_ext def get_version_from_header(): - """Extract version from C++ header file""" + """Extract version from C++ header file. + + Raises RuntimeError if version cannot be extracted, unless + DSF_PACKAGE_VERSION env var is set (CI builds) or + DSF_ALLOW_MISSING_VERSION is set to allow local dev fallback. + """ header_path = Path(__file__).parent / "src" / "dsf" / "dsf.hpp" try: with open(header_path, "r", encoding="UTF-8") as header_file: @@ -34,10 +39,22 @@ def get_version_from_header(): return ( f"{major_match.group(1)}.{minor_match.group(1)}.{patch_match.group(1)}" ) - return "unknown" - except (FileNotFoundError, AttributeError): - # Fallback version if header can't be read - return "unknown" + + # Version regex failed to match + error_msg = ( + f"Failed to extract version from {header_path}. " + "Expected DSF_VERSION_MAJOR, DSF_VERSION_MINOR, DSF_VERSION_PATCH defines." + ) + if os.environ.get("DSF_ALLOW_MISSING_VERSION"): + print(f"WARNING: {error_msg}. Using 0.0.0.dev0 for local build.") + return "0.0.0.dev0" + raise RuntimeError(error_msg) + except FileNotFoundError as e: + error_msg = f"Version header file not found: {header_path}" + if os.environ.get("DSF_ALLOW_MISSING_VERSION"): + print(f"WARNING: {error_msg}. Using 0.0.0.dev0 for local build.") + return "0.0.0.dev0" + raise RuntimeError(error_msg) from e class CMakeExtension(Extension): # pylint: disable=too-few-public-methods @@ -479,78 +496,12 @@ def run_stubgen(self): shutil.copy2(py_typed_src, py_typed_dest) -# Read long description from README.md if available -LONG_DESCRIPTION = "" -if Path("README.md").exists(): - with open("README.md", "r", encoding="utf-8") as f: - LONG_DESCRIPTION = f.read() - # Get version from header file, unless explicitly overridden for CI pre-releases. PROJECT_VERSION = os.environ.get("DSF_PACKAGE_VERSION", get_version_from_header()) + setup( - name="dsf-mobility", version=PROJECT_VERSION, - author="Grufoony", - author_email="gregorio.berselli@studio.unibo.it", - description="DSF C++ core with Python bindings via pybind11", - long_description=LONG_DESCRIPTION, - long_description_content_type="text/markdown", - license="CC-BY-NC-SA-4.0", - url="https://github.com/physycom/DynamicalSystemFramework", - project_urls={ - "Homepage": "https://github.com/physycom/DynamicalSystemFramework", - "Documentation": "https://physycom.github.io/DynamicalSystemFramework/", - "Repository": "https://github.com/physycom/DynamicalSystemFramework", - "Issues": "https://github.com/physycom/DynamicalSystemFramework/issues", - }, - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Science/Research", - "Intended Audience :: Developers", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.12", - "Programming Language :: C++", - "Topic :: Scientific/Engineering :: Physics", - "Topic :: Scientific/Engineering :: Information Analysis", - "Topic :: Software Development :: Libraries :: Python Modules", - "Operating System :: POSIX :: Linux", - "Operating System :: MacOS", - ], - keywords=[ - "traffic", - "simulation", - "dynamics", - "network", - "modeling", - "transportation", - "mobility", - "congestion", - "flow", - "optimization", - ], ext_modules=[CMakeExtension("dsf_cpp")], - # Use namespace-aware discovery under the `src/` directory so any subpackages - # (including implicit/namespace packages) such as `dsf.mobility` are picked up - # automatically for distribution. - # packages=find_packages(where="src", include=["dsf", "dsf.mobility"]), - packages=find_namespace_packages(where="src"), - package_dir={"": "src"}, cmdclass={"build_ext": CMakeBuild}, - package_data={ - "dsf": ["*.pyi", "py.typed", "**/*.pyi"], - "": ["*.pyi"], - }, - include_package_data=True, - zip_safe=False, - python_requires=">=3.10", - install_requires=[ - "pybind11-stubgen", - "osmnx>=2.0.6", - "networkx>=3", - "numpy", - "geopandas", - "shapely", - "folium", - ], )