diff options
| -rw-r--r-- | .git_archival.txt | 3 | ||||
| -rw-r--r-- | .gitattributes | 1 | ||||
| -rw-r--r-- | README.md | 11 | ||||
| -rw-r--r-- | ffi/pyouroboros_build_dev.py | 6 | ||||
| -rw-r--r-- | ffi/pyouroboros_build_irm.py | 6 | ||||
| -rw-r--r-- | ouroboros/__init__.py | 21 | ||||
| -rw-r--r-- | ouroboros/dev.py | 24 | ||||
| -rw-r--r-- | ouroboros/irm.py | 24 | ||||
| -rw-r--r-- | pyproject.toml | 23 | ||||
| -rwxr-xr-x | setup.py | 67 |
10 files changed, 164 insertions, 22 deletions
diff --git a/.git_archival.txt b/.git_archival.txt new file mode 100644 index 0000000..3e26627 --- /dev/null +++ b/.git_archival.txt @@ -0,0 +1,3 @@ +node: $Format:%H$ +node-date: $Format:%cI$ +describe-name: $Format:%(describe:tags=true)$ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..00a7b00 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +.git_archival.txt export-subst @@ -388,5 +388,16 @@ unbind_process(pid, "my_name") Some example code is in the examples folder. +## Versioning + +pyOuroboros uses `setuptools_scm` to derive its version from git tags. + +**Compatibility contract across Ouroboros repositories:** + +| Scope | Rule | +|---|---| +| ouroboros (C) ↔ pyouroboros / rumba | Shared `major.minor` — pyouroboros requires at least the same ouroboros `major.minor` | +| pyouroboros ↔ rumba ↔ ouroboros-integration | Strict lockstep `major.minor.patch` — always released together | + ## License pyOuroboros is LGPLv2.1. The examples are 3-clause BSD. diff --git a/ffi/pyouroboros_build_dev.py b/ffi/pyouroboros_build_dev.py index 751b492..778cc40 100644 --- a/ffi/pyouroboros_build_dev.py +++ b/ffi/pyouroboros_build_dev.py @@ -33,6 +33,11 @@ struct timespec { ...; }; +/* OUROBOROS VERSION.H */ +#define OUROBOROS_VERSION_MAJOR ... +#define OUROBOROS_VERSION_MINOR ... +#define OUROBOROS_VERSION_PATCH ... + /* OUROBOROS QOS.H */ typedef struct qos_spec { uint32_t delay; @@ -139,6 +144,7 @@ ssize_t fevent(fset_t * set, ffibuilder.set_source("_ouroboros_dev_cffi", """ +#include "ouroboros/version.h" #include "ouroboros/qos.h" #include "ouroboros/dev.h" #include "fccntl_wrap.h" diff --git a/ffi/pyouroboros_build_irm.py b/ffi/pyouroboros_build_irm.py index e29287e..1fc515e 100644 --- a/ffi/pyouroboros_build_irm.py +++ b/ffi/pyouroboros_build_irm.py @@ -34,6 +34,11 @@ struct timespec { ...; }; +/* OUROBOROS VERSION.H */ +#define OUROBOROS_VERSION_MAJOR ... +#define OUROBOROS_VERSION_MINOR ... +#define OUROBOROS_VERSION_PATCH ... + /* Network types */ struct in_addr { ...; }; struct in6_addr { ...; }; @@ -281,6 +286,7 @@ void free(void *ptr); ffibuilder.set_source("_ouroboros_irm_cffi", """ +#include "ouroboros/version.h" #include "ouroboros/qos.h" #include "irm_wrap.h" """, diff --git a/ouroboros/__init__.py b/ouroboros/__init__.py new file mode 100644 index 0000000..6034f7d --- /dev/null +++ b/ouroboros/__init__.py @@ -0,0 +1,21 @@ +# +# Ouroboros - Copyright (C) 2016 - 2026 +# +# Python API for Ouroboros +# +# Dimitri Staessens <dimitri@ouroboros.rocks> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# version 2.1 as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., http://www.fsf.org/about/contact/. +# + diff --git a/ouroboros/dev.py b/ouroboros/dev.py index a2b58cf..d36a3ef 100644 --- a/ouroboros/dev.py +++ b/ouroboros/dev.py @@ -27,6 +27,30 @@ from typing import Optional from _ouroboros_dev_cffi import ffi, lib from ouroboros.qos import * + +def _check_ouroboros_version(): + ouro_major = lib.OUROBOROS_VERSION_MAJOR + ouro_minor = lib.OUROBOROS_VERSION_MINOR + try: + from importlib.metadata import version, PackageNotFoundError + try: + pyouro_parts = version('PyOuroboros').split('.') + except PackageNotFoundError: + return # running from source, skip check + if ouro_major != int(pyouro_parts[0]) or \ + ouro_minor != int(pyouro_parts[1]): + raise RuntimeError( + f"Ouroboros version mismatch: library is " + f"{ouro_major}.{ouro_minor}, " + f"pyouroboros is " + f"{pyouro_parts[0]}.{pyouro_parts[1]}" + ) + except ImportError: + pass # Python < 3.8 + + +_check_ouroboros_version() + # Some constants MILLION = 1000 * 1000 BILLION = 1000 * 1000 * 1000 diff --git a/ouroboros/irm.py b/ouroboros/irm.py index 1e4fc2e..5c23aaa 100644 --- a/ouroboros/irm.py +++ b/ouroboros/irm.py @@ -26,6 +26,30 @@ from _ouroboros_irm_cffi import ffi, lib from ouroboros.qos import QoSSpec +def _check_ouroboros_version(): + ouro_major = lib.OUROBOROS_VERSION_MAJOR + ouro_minor = lib.OUROBOROS_VERSION_MINOR + try: + from importlib.metadata import version, PackageNotFoundError + try: + pyouro_parts = version('PyOuroboros').split('.') + except PackageNotFoundError: + return # running from source, skip check + if ouro_major != int(pyouro_parts[0]) or \ + ouro_minor != int(pyouro_parts[1]): + raise RuntimeError( + f"Ouroboros version mismatch: library is " + f"{ouro_major}.{ouro_minor}, " + f"pyouroboros is " + f"{pyouro_parts[0]}.{pyouro_parts[1]}" + ) + except ImportError: + pass # Python < 3.8 + + +_check_ouroboros_version() + + # Intentionally duplicated: irm uses a separate FFI (ouroboros-irm). def _qos_to_qosspec(qos: QoSSpec): if qos is None: diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..247142c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,23 @@ +[build-system] +requires = ["setuptools>=64", "setuptools-scm>=8", "cffi>=1.0.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "PyOuroboros" +dynamic = ["version"] +description = "Python API for Ouroboros" +license = "LGPL-2.1-only" +requires-python = ">=3.8" +authors = [ + { name = "Dimitri Staessens", email = "dimitri@ouroboros.rocks" } +] +keywords = ["ouroboros", "IPC"] +dependencies = ["cffi>=1.0.0"] + +[project.urls] +Homepage = "https://ouroboros.rocks" + +[tool.setuptools] +packages = ["ouroboros"] + +[tool.setuptools_scm] @@ -1,26 +1,49 @@ -#!/usr/bin/env python - -import setuptools - -setuptools.setup( - name='PyOuroboros', - version='0.23.0', - url='https://ouroboros.rocks', - keywords='ouroboros IPC subsystem', - author='Dimitri Staessens', - author_email='dimitri@ouroboros.rocks', - license='LGPLv2.1', - description='Python API for Ouroboros', - packages=[ - 'ouroboros' - ], - setup_requires=[ - "cffi>=1.0.0" - ], +import subprocess +import sys + +from setuptools import setup + + +def _get_ouroboros_version(): + try: + out = subprocess.check_output( + ['pkg-config', '--modversion', 'ouroboros-dev'], + stderr=subprocess.DEVNULL + ) + return out.decode().strip() + except (subprocess.CalledProcessError, FileNotFoundError): + sys.exit("ERROR: ouroboros-dev not found via pkg-config. " + "Is Ouroboros installed?") + + +def _check_build_version_compat(): + try: + from setuptools_scm import get_version + pyouro_ver = get_version(root='.', relative_to=__file__) + except Exception: + return # no SCM info, skip check + + ouro_ver = _get_ouroboros_version() + + # setuptools_scm: '0.23.1.dev3+g<hash>' or '0.23.0' + # pkg-config: '0.23.0' + # Compare major.minor only. + ouro_parts = ouro_ver.split('.') + pyouro_parts = pyouro_ver.split('.') + + if ouro_parts[0] != pyouro_parts[0] or ouro_parts[1] != pyouro_parts[1]: + sys.exit( + f"ERROR: Version mismatch: ouroboros {ouro_ver} " + f"vs pyouroboros {pyouro_ver} " + f"(major.minor must match)" + ) + + +_check_build_version_compat() + +setup( cffi_modules=[ "ffi/pyouroboros_build_dev.py:ffibuilder", "ffi/pyouroboros_build_irm.py:ffibuilder" ], - install_requires=[ - "cffi>=1.0.0" - ]) +) |
