diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9084dbc..74f0507 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,12 +89,68 @@ jobs: python -m build shell: bash + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + allow-prereleases: true + + - name: Build package 3.14 + run: | + pip install build wheel + python -m build + shell: bash + - name: Upload Artifact uses: actions/upload-artifact@v4 with: name: pyosmium-linux-x64-dist path: dist + build-free-threaded: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v4 + + - name: Install packages + run: | + sudo apt-get update -y -qq + sudo apt-get install -y -qq libboost-dev libexpat1-dev zlib1g-dev libbz2-dev libproj-dev libgeos-dev liblz4-dev pipx + pipx install mypy + pipx inject mypy types-requests + pipx install flake8 + + - name: Set up Python 3.13t + uses: actions/setup-python@v5 + with: + python-version: "3.13t" + + - name: Build package 3.13t + run: | + pip install build wheel + python -m build + shell: bash + + - name: Set up Python 3.14t + uses: actions/setup-python@v5 + with: + python-version: "3.14t" + allow-prereleases: true + + - name: Build package 3.14t + run: | + pip install build wheel + python -m build + shell: bash + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: pyosmium-linux-x64-dist-t + path: dist + + test-default: runs-on: ubuntu-22.04 needs: build-default @@ -136,6 +192,78 @@ jobs: ./osmium-test/bin/pyosmium-get-changes -h ./osmium-test/bin/pyosmium-up-to-date -h + test-314: + runs-on: ubuntu-22.04 + needs: build-default + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: 3.14 + allow-prereleases: true + + - uses: actions/download-artifact@v4 + with: + name: pyosmium-linux-x64-dist + + - name: Install osmium + run: | + pip install virtualenv + virtualenv osmium-test + WHEEL=`ls osmium*314-*.whl` + ./osmium-test/bin/pip install ${WHEEL} + ./osmium-test/bin/pip install pytest pytest-httpserver pytest-run-parallel + shell: bash + + - name: Run tests + run: ./osmium-test/bin/pytest test + shell: bash + + - name: Check tool availability + run: | + ./osmium-test/bin/pyosmium-get-changes -h + ./osmium-test/bin/pyosmium-up-to-date -h + + test-free-threaded: + runs-on: ubuntu-22.04 + needs: build-free-threaded + + strategy: + fail-fast: false + matrix: + python-version: ["3.13t", "3.14t"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + allow-prereleases: true + + - uses: actions/download-artifact@v4 + with: + name: pyosmium-linux-x64-dist-t + + - name: Install osmium + run: | + pip install virtualenv + virtualenv osmium-test + WHEEL=`ls osmium*${PYVER/./}-*.whl` + ./osmium-test/bin/pip install ${WHEEL} + ./osmium-test/bin/pip install pytest pytest-httpserver pytest-run-parallel + shell: bash + env: + PYVER: ${{ matrix.python-version }} + + - name: Run tests + run: ./osmium-test/bin/pytest test --parallel-threads 10 --iterations 10 + shell: bash + build-platform: runs-on: ${{ matrix.platform }} @@ -334,12 +462,83 @@ jobs: env: CMAKE_TOOLCHAIN_FILE: C:/vcpkg/scripts/buildsystems/vcpkg.cmake + - name: Set up Python 3.14 + uses: actions/setup-python@v5 + with: + python-version: "3.14" + allow-prereleases: true + + - name: Build package 3.14 + run: | + pip install build wheel + python -m build + shell: bash + env: + CMAKE_TOOLCHAIN_FILE: C:/vcpkg/scripts/buildsystems/vcpkg.cmake + - name: 'Upload Artifact' uses: actions/upload-artifact@v4 with: name: pyosmium-win64-dist path: dist + build-windows-free-threaded: + runs-on: windows-2022 + + env: + VCPKG_DEFAULT_BINARY_CACHE: C:/vcpkg_binary_cache + + steps: + - uses: actions/checkout@v4 + + - uses: actions/cache@v4 + with: + path: | + C:/vcpkg_binary_cache + key: vcpkg-binary-cache-windows-2022 + + - name: Prepare cache + run: if [ ! -d C:/vcpkg_binary_cache ]; then mkdir C:/vcpkg_binary_cache; fi + shell: bash + + - name: Install packages + run: vcpkg install bzip2:x64-windows expat:x64-windows zlib:x64-windows boost-variant:x64-windows boost-iterator:x64-windows lz4:x86-windows + shell: bash + + - name: Set up Python 3.13t + uses: actions/setup-python@v5 + with: + python-version: "3.13t" + + - name: Build package 3.13t + run: | + pip install build wheel + python -m build + shell: bash + env: + CMAKE_TOOLCHAIN_FILE: C:/vcpkg/scripts/buildsystems/vcpkg.cmake + + - name: Set up Python 3.14t + uses: actions/setup-python@v5 + with: + python-version: "3.14t" + allow-prereleases: true + + - name: Build package 3.14t + run: | + pip install build wheel + python -m build + shell: bash + env: + CMAKE_TOOLCHAIN_FILE: C:/vcpkg/scripts/buildsystems/vcpkg.cmake + + - name: 'Upload Artifact' + uses: actions/upload-artifact@v4 + with: + name: pyosmium-win64-dist-t + path: dist + + test-windows: runs-on: windows-2022 needs: build-windows @@ -347,7 +546,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] env: PYTEST_ADDOPTS: ${{ matrix.test-args }} @@ -365,15 +564,28 @@ jobs: with: name: pyosmium-win64-dist - - name: Install osmium + - name: Install osmium (with shapely) run: | pip install virtualenv virtualenv osmium-test - WHEEL=`ls osmium*${PYVER/./}*.whl` + WHEEL=`ls osmium*${PYVER/./}-*.whl` ./osmium-test/Scripts/pip install ${WHEEL}[tests] shell: bash env: PYVER: ${{ matrix.python-version }} + if: matrix.python-version != '3.14' + + - name: Install osmium (without shapely) + run: | + pip install virtualenv + virtualenv osmium-test + WHEEL=`ls osmium*${PYVER/./}-*.whl` + ./osmium-test/Scripts/pip install ${WHEEL} + ./osmium-test/Scripts/pip install pytest pytest-httpserver pytest-run-parallel + shell: bash + env: + PYVER: ${{ matrix.python-version }} + if: matrix.python-version == '3.14' - name: Run tests run: ./osmium-test/Scripts/pytest test @@ -384,3 +596,61 @@ jobs: ./osmium-test/Scripts/pyosmium-get-changes -h ./osmium-test/Scripts/pyosmium-up-to-date -h shell: bash + + test-windows-free-threaded: + runs-on: windows-2022 + needs: build-windows + + strategy: + fail-fast: false + matrix: + python-version: ["3.13t", "3.14t"] + + env: + PYTEST_ADDOPTS: ${{ matrix.test-args }} + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + allow-prereleases: true + + - uses: actions/download-artifact@v4 + with: + name: pyosmium-win64-dist-t + + - name: Install osmium (with shapely) + run: | + pip install virtualenv + virtualenv osmium-test + WHEEL=`ls osmium*${PYVER/./}-*.whl` + ./osmium-test/Scripts/pip install ${WHEEL}[tests] + shell: bash + env: + PYVER: ${{ matrix.python-version }} + if: matrix.python-version != '3.14t' + + - name: Install osmium (without shapely) + run: | + pip install virtualenv + virtualenv osmium-test + WHEEL=`ls osmium*${PYVER/./}-*.whl` + ./osmium-test/Scripts/pip install ${WHEEL} + ./osmium-test/Scripts/pip install pytest pytest-httpserver pytest-run-parallel + shell: bash + env: + PYVER: ${{ matrix.python-version }} + if: matrix.python-version == '3.14t' + + - name: Run tests + run: ./osmium-test/Scripts/pytest test --parallel-threads 5 --iterations 5 + shell: bash + + - name: Check tool availability + run: | + ./osmium-test/Scripts/pyosmium-get-changes -h + ./osmium-test/Scripts/pyosmium-up-to-date -h + shell: bash diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ff278c..c37874f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,8 +35,15 @@ message(STATUS "Building in C++${CMAKE_CXX_STANDARD} mode") find_package(Python COMPONENTS Interpreter Development) +# Check for abiflags, so we can check for free-threaded later. +execute_process(COMMAND ${Python_EXECUTABLE} -c "import sys; print(sys.abiflags, end='')" + OUTPUT_VARIABLE PYTHON_ABIFLAGS) + if(PYBIND11_PREFIX) add_subdirectory(${PYBIND11_PREFIX} contrib/pybind11) +elseif(PYTHON_ABIFLAGS STREQUAL "t") + message(STATUS "Free-threading Python found. Enabling support (needs pybind11 2.13+).") + find_package(pybind11 2.13 REQUIRED) else() find_package(pybind11 2.9 REQUIRED) endif() diff --git a/docs/reference/Thread-Safety.md b/docs/reference/Thread-Safety.md new file mode 100644 index 0000000..d260773 --- /dev/null +++ b/docs/reference/Thread-Safety.md @@ -0,0 +1,18 @@ +# Thread safety + +Object instances of pyosmium are not thread-safe to modify. If you share +objects like an index, you have to protect write accesses to these objects. +Concurrent reads are safe. + +The library functions themselves are all reentrant and may be used safely from +different threads. + +### Free-threaded Python + +Starting with version 4.1, Pyosmium has experimental support for Python +runtimes with GIL disabled. See the +[Python Free-Threading Guide](https://py-free-threading.github.io/) +for more information. + +The restrictions mentioned above still apply: write accesses on object need +to be protected by exclusive locks when using them in multi-threaded context. diff --git a/lib/area.cc b/lib/area.cc index 19e38b0..2f2d12b 100644 --- a/lib/area.cc +++ b/lib/area.cc @@ -2,7 +2,7 @@ * * This file is part of pyosmium. (https://osmcode.org/pyosmium/) * - * Copyright (C) 2024 Sarah Hoffmann and others. + * Copyright (C) 2025 Sarah Hoffmann and others. * For a full list of authors see the git log. */ @@ -127,7 +127,11 @@ class AreaManager : public pyosmium::BaseHandler } // namespace +#ifdef Py_GIL_DISABLED +PYBIND11_MODULE(area, m, py::mod_gil_not_used()) +#else PYBIND11_MODULE(area, m) +#endif { py::class_(m, "AreaManagerSecondPassHandler"); diff --git a/lib/filter.cc b/lib/filter.cc index 5eef376..6665224 100644 --- a/lib/filter.cc +++ b/lib/filter.cc @@ -2,7 +2,7 @@ * * This file is part of pyosmium. (https://osmcode.org/pyosmium/) * - * Copyright (C) 2024 Sarah Hoffmann and others. + * Copyright (C) 2025 Sarah Hoffmann and others. * For a full list of authors see the git log. */ #include @@ -12,7 +12,12 @@ namespace py = pybind11; -PYBIND11_MODULE(filter, m) { +#ifdef Py_GIL_DISABLED +PYBIND11_MODULE(filter, m, py::mod_gil_not_used()) +#else +PYBIND11_MODULE(filter, m) +#endif +{ pyosmium::init_empty_tag_filter(m); pyosmium::init_key_filter(m); pyosmium::init_tag_filter(m); diff --git a/lib/geom.cc b/lib/geom.cc index ec11a08..f2e9076 100644 --- a/lib/geom.cc +++ b/lib/geom.cc @@ -2,7 +2,7 @@ * * This file is part of pyosmium. (https://osmcode.org/pyosmium/) * - * Copyright (C) 2024 Sarah Hoffmann and others. + * Copyright (C) 2025 Sarah Hoffmann and others. * For a full list of authors see the git log. */ #include @@ -75,7 +75,11 @@ void make_factory_class(py::module_ &m, char const *name) } // namespace +#ifdef Py_GIL_DISABLED +PYBIND11_MODULE(geom, m, py::mod_gil_not_used()) +#else PYBIND11_MODULE(geom, m) +#endif { py::enum_(m, "use_nodes") .value("UNIQUE", og::use_nodes::unique) diff --git a/lib/index.cc b/lib/index.cc index 33ad58c..ec4f759 100644 --- a/lib/index.cc +++ b/lib/index.cc @@ -2,7 +2,7 @@ * * This file is part of pyosmium. (https://osmcode.org/pyosmium/) * - * Copyright (C) 2024 Sarah Hoffmann and others. + * Copyright (C) 2025 Sarah Hoffmann and others. * For a full list of authors see the git log. */ #include @@ -14,7 +14,11 @@ namespace py = pybind11; +#ifdef Py_GIL_DISABLED +PYBIND11_MODULE(index, m, py::mod_gil_not_used()) +#else PYBIND11_MODULE(index, m) +#endif { using LocationTable = osmium::index::map::Map; diff --git a/lib/io.cc b/lib/io.cc index 5b4a545..d36a2dc 100644 --- a/lib/io.cc +++ b/lib/io.cc @@ -2,7 +2,7 @@ * * This file is part of pyosmium. (https://osmcode.org/pyosmium/) * - * Copyright (C) 2024 Sarah Hoffmann and others. + * Copyright (C) 2025 Sarah Hoffmann and others. * For a full list of authors see the git log. */ #include @@ -25,7 +25,11 @@ class FileBuffer : public osmium::io::File } // namespace +#ifdef Py_GIL_DISABLED +PYBIND11_MODULE(io, m, py::mod_gil_not_used()) +#else PYBIND11_MODULE(io, m) +#endif { py::class_(m, "File") .def(py::init()) diff --git a/lib/osm.cc b/lib/osm.cc index 03e4218..e7e7717 100644 --- a/lib/osm.cc +++ b/lib/osm.cc @@ -2,7 +2,7 @@ * * This file is part of pyosmium. (https://osmcode.org/pyosmium/) * - * Copyright (C) 2024 Sarah Hoffmann and others. + * Copyright (C) 2025 Sarah Hoffmann and others. * For a full list of authors see the git log. */ #include @@ -151,8 +151,12 @@ py::class_ make_osm_object_class(py::module_ &m, char const *class_n } // namespace - -PYBIND11_MODULE(_osm, m) { +#ifdef Py_GIL_DISABLED +PYBIND11_MODULE(_osm, m, py::mod_gil_not_used()) +#else +PYBIND11_MODULE(_osm, m) +#endif +{ py::enum_(m, "osm_entity_bits") .value("NOTHING", osmium::osm_entity_bits::nothing) .value("NODE", osmium::osm_entity_bits::node) diff --git a/lib/osmium.cc b/lib/osmium.cc index 1d7c0cf..084f10d 100644 --- a/lib/osmium.cc +++ b/lib/osmium.cc @@ -2,7 +2,7 @@ * * This file is part of pyosmium. (https://osmcode.org/pyosmium/) * - * Copyright (C) 2024 Sarah Hoffmann and others. + * Copyright (C) 2025 Sarah Hoffmann and others. * For a full list of authors see the git log. */ #include @@ -72,8 +72,12 @@ void pyosmium::apply(osmium::io::Reader &reader, pyosmium::BaseHandler &handler) handler.flush(); } - -PYBIND11_MODULE(_osmium, m) { +#ifdef Py_GIL_DISABLED +PYBIND11_MODULE(_osmium, m, py::mod_gil_not_used()) +#else +PYBIND11_MODULE(_osmium, m) +#endif +{ py::register_exception(m, "InvalidLocationError"); py::register_exception_translator([](std::exception_ptr p) { try { diff --git a/lib/replication.cc b/lib/replication.cc index 05c12f0..8a9c189 100644 --- a/lib/replication.cc +++ b/lib/replication.cc @@ -2,7 +2,7 @@ * * This file is part of pyosmium. (https://osmcode.org/pyosmium/) * - * Copyright (C) 2024 Sarah Hoffmann and others. + * Copyright (C) 2025 Sarah Hoffmann and others. * For a full list of authors see the git log. */ #include @@ -31,7 +31,11 @@ struct LastChangeHandler : public osmium::handler::Handler } // namespace +#ifdef Py_GIL_DISABLED +PYBIND11_MODULE(_replication, m, py::mod_gil_not_used()) +#else PYBIND11_MODULE(_replication, m) +#endif { m.def("newest_change_from_file", [](char const *filename) { diff --git a/mkdocs.yaml b/mkdocs.yaml index 3c9d3f1..da08115 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -47,6 +47,7 @@ nav: - Indexes: 'reference/Indexes.md' - Replication: 'reference/Replication.md' - Exceptions: 'reference/Exceptions.md' + - Thread Safety: 'reference/Thread-Safety.md' exclude_docs: | .*.swp diff --git a/pyproject.toml b/pyproject.toml index d2fe957..a996ae0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,8 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: Free Threading :: 2 - Beta", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: C++", ] @@ -53,6 +55,7 @@ Issues = "https://github.com/osmcode/pyosmium/issues" tests = [ 'pytest', 'pytest-httpserver', + 'pytest-run-parallel', 'werkzeug', 'shapely' ] diff --git a/test/conftest.py b/test/conftest.py index ccb5dbf..b194df5 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -19,10 +19,10 @@ BUILD_DIR = "build/lib.{}-{}.{}".format(sysconfig.get_platform(), sys.version_info[0], sys.version_info[1]) - if not (SRC_DIR / BUILD_DIR).exists(): - BUILD_DIR = "build/lib.{}-{}".format(sysconfig.get_platform(), - sys.implementation.cache_tag) + BUILD_DIR = "build/lib.{}-{}{}".format(sysconfig.get_platform(), + sys.implementation.cache_tag, + getattr(sys, 'abiflags', '')) if (SRC_DIR / BUILD_DIR).exists(): sys.path.insert(0, str(SRC_DIR)) diff --git a/test/test_area.py b/test/test_area.py index de837b4..41f3dc3 100644 --- a/test/test_area.py +++ b/test/test_area.py @@ -6,6 +6,7 @@ # For a full list of authors see the git log. from pathlib import Path +import pytest import osmium from helpers import CountingHandler @@ -14,6 +15,7 @@ TEST_FILE = str((Path(__file__) / '..' / 'example-test.pbf').resolve()) +@pytest.mark.thread_unsafe def test_area_handler(): area = osmium.area.AreaManager() diff --git a/test/test_back_reference_writer.py b/test/test_back_reference_writer.py index c906a43..a39a2cf 100644 --- a/test/test_back_reference_writer.py +++ b/test/test_back_reference_writer.py @@ -5,6 +5,7 @@ # Copyright (C) 2025 Sarah Hoffmann and others. # For a full list of authors see the git log. import pytest +import uuid import osmium @@ -18,7 +19,7 @@ class TestWay: id = 34 nodes = [3, 6, 5] - outfile = str(tmp_path / 'test.osm') + outfile = str(tmp_path / f'{uuid.uuid4()}.osm') with osmium.BackReferenceWriter(outfile, ref_file) as writer: writer.add_way(TestWay()) @@ -33,7 +34,7 @@ class TestWay: def test_do_not_write_on_exception(test_data, tmp_path): ref_file = test_data('\n'.join((f"n{i} x2 y3" for i in range(10)))) - outfile = tmp_path / 'test.osm' + outfile = tmp_path / f'{uuid.uuid4()}.osm' with pytest.raises(RuntimeError, match="inner error"): with osmium.BackReferenceWriter(str(outfile), ref_file): diff --git a/test/test_dangling_references.py b/test/test_dangling_references.py index c949e28..2b75605 100644 --- a/test/test_dangling_references.py +++ b/test/test_dangling_references.py @@ -9,6 +9,8 @@ import pytest import osmium +pytestmark = pytest.mark.iterations(1) + TEST_DIR = (Path(__file__) / '..').resolve() diff --git a/test/test_empty_tag_filter.py b/test/test_empty_tag_filter.py index e0ca92e..83ad29c 100644 --- a/test/test_empty_tag_filter.py +++ b/test/test_empty_tag_filter.py @@ -12,7 +12,8 @@ @pytest.fixture def reader(opl_reader): - return opl_reader("""\ + def _mk(): + return opl_reader("""\ n1 x1 y1 n2 x1 y1 Tfoo=bar w1 Nn1,n2 Thighway=road @@ -23,11 +24,13 @@ def reader(opl_reader): c223 """) + return _mk + def test_filter_default_config(reader): pre = IDCollector() post = IDCollector() - osmium.apply(reader, pre, osmium.filter.EmptyTagFilter(), post) + osmium.apply(reader(), pre, osmium.filter.EmptyTagFilter(), post) assert pre.nodes == [1, 2] assert post.nodes == [2] @@ -42,7 +45,7 @@ def test_filter_default_config(reader): def test_filter_restrict_entity(reader): pre = IDCollector() post = IDCollector() - osmium.apply(reader, pre, + osmium.apply(reader(), pre, osmium.filter.EmptyTagFilter().enable_for(osmium.osm.WAY | osmium.osm.RELATION), post) @@ -57,7 +60,7 @@ def test_filter_restrict_entity(reader): def test_filter_chained(reader): pre = IDCollector() post = IDCollector() - osmium.apply(reader, pre, + osmium.apply(reader(), pre, osmium.filter.EmptyTagFilter().enable_for(osmium.osm.NODE), osmium.filter.EmptyTagFilter().enable_for(osmium.osm.WAY), post) diff --git a/test/test_examples.py b/test/test_examples.py index b3f1bf2..7651739 100644 --- a/test/test_examples.py +++ b/test/test_examples.py @@ -9,6 +9,7 @@ """ from pathlib import Path +import pytest TEST_DIR = (Path(__file__) / '..').resolve() TEST_FILE = TEST_DIR / 'example-test.pbf' @@ -26,6 +27,7 @@ def run_example(name, *args): def test_amenity_list(capsys): + pytest.importorskip("shapely") assert 0 == run_example("amenity_list", TEST_FILE) output = capsys.readouterr().out.splitlines() diff --git a/test/test_file_processor.py b/test/test_file_processor.py index 32f25e0..3e04445 100644 --- a/test/test_file_processor.py +++ b/test/test_file_processor.py @@ -5,6 +5,7 @@ # Copyright (C) 2025 Sarah Hoffmann and others. # For a full list of authors see the git log. import pytest +import uuid import osmium from helpers import IDCollector @@ -125,7 +126,7 @@ def test_generator_with_filter(opl_buffer): def test_file_processor_header(tmp_path): - fn = tmp_path / 'empty.xml' + fn = tmp_path / f"{uuid.uuid4()}.xml" fn.write_text(""" @@ -234,7 +235,7 @@ def test_filtered_handler_basehandler(opl_buffer, tmp_path): r4 """) - testf = tmp_path / 'test.opl' + testf = tmp_path / f"{uuid.uuid4()}.opl" with osmium.SimpleWriter(str(testf)) as writer: fp = osmium.FileProcessor(data)\ diff --git a/test/test_forward_reference_writer.py b/test/test_forward_reference_writer.py index 5de61ee..da6ab30 100644 --- a/test/test_forward_reference_writer.py +++ b/test/test_forward_reference_writer.py @@ -4,6 +4,7 @@ # # Copyright (C) 2025 Sarah Hoffmann and others. # For a full list of authors see the git log. +import uuid import pytest import osmium @@ -32,7 +33,7 @@ def __init__(self, nid): def test_simple_forward_no_back_reference(ref_file, tmp_path): - outfile = str(tmp_path / 'test.osm') + outfile = tmp_path / f"{uuid.uuid4()}.osm" with osmium.ForwardReferenceWriter(outfile, ref_file, back_references=False) as writer: writer.add_node(DummyNode(2)) @@ -47,7 +48,7 @@ def test_simple_forward_no_back_reference(ref_file, tmp_path): def test_simple_forward_with_back_reference(ref_file, tmp_path): - outfile = str(tmp_path / 'test.osm') + outfile = str(tmp_path / f"{uuid.uuid4()}.osm") with osmium.ForwardReferenceWriter(outfile, ref_file) as writer: writer.add_node(DummyNode(2)) diff --git a/test/test_geom.py b/test/test_geom.py index 2e97c6f..8a54a56 100644 --- a/test/test_geom.py +++ b/test/test_geom.py @@ -7,12 +7,8 @@ import json import pytest - -import osmium import osmium.geom -wkbfab = osmium.geom.WKBFactory() - @pytest.fixture def node_geom(test_data): diff --git a/test/test_id_tracker.py b/test/test_id_tracker.py index 0e17ec1..a9cccc5 100644 --- a/test/test_id_tracker.py +++ b/test/test_id_tracker.py @@ -5,6 +5,7 @@ # Copyright (C) 2025 Sarah Hoffmann and others. # For a full list of authors see the git log. import pytest +import uuid import osmium @@ -169,7 +170,7 @@ def test_complete_backward_references(tmp_path, depth): if depth == 0: data_file = osmium.io.FileBuffer(REF_SRC.encode('utf-8'), 'opl') else: - data_file = tmp_path / 'test.opl' + data_file = tmp_path / f"{uuid.uuid4()}.opl" data_file.write_text(REF_SRC) ids = osmium.IdTracker() @@ -191,7 +192,7 @@ def test_complete_forward_references(tmp_path, depth): if depth == 0: data_file = osmium.io.FileBuffer(REF_SRC.encode('utf-8'), 'opl') else: - data_file = tmp_path / 'test.opl' + data_file = tmp_path / f"{uuid.uuid4()}.opl" data_file.write_text(REF_SRC) ids = osmium.IdTracker() diff --git a/test/test_index_location.py b/test/test_index_location.py index cb5d1f4..8e6f005 100644 --- a/test/test_index_location.py +++ b/test/test_index_location.py @@ -14,14 +14,10 @@ def test_list_types(): assert ml -@pytest.fixture -def table(): - return osmium.index.create_map("flex_mem") - - @pytest.mark.parametrize('use_get', [True, False]) @pytest.mark.parametrize('use_set', [True, False]) -def test_set_get(table, use_set, use_get): +def test_set_get(use_set, use_get): + table = osmium.index.create_map("flex_mem") if use_set: table.set(4, osmium.osm.Location(3.4, -5.6)) else: @@ -34,33 +30,39 @@ def test_set_get(table, use_set, use_get): assert loc.lat == pytest.approx(-5.6) -def test_get_unset(table): +def test_get_unset(): + table = osmium.index.create_map("flex_mem") with pytest.raises(KeyError): table.get(56) -def test_array_get_unset(table): +def test_array_get_unset(): + table = osmium.index.create_map("flex_mem") with pytest.raises(KeyError): table[56] -def test_set_negative(table): +def test_set_negative(): + table = osmium.index.create_map("flex_mem") with pytest.raises(TypeError): table.set(-4, osmium.osm.Location(3.4, -5.6)) -def test_array_set_negative(table): +def test_array_set_negative(): + table = osmium.index.create_map("flex_mem") with pytest.raises(TypeError): table[-4] = osmium.osm.Location(3.4, -5.6) -def test_used_memory(table): +def test_used_memory(): + table = osmium.index.create_map("flex_mem") table.set(4, osmium.osm.Location(3.4, -5.6)) assert table.used_memory() > 0 -def test_clear(table): +def test_clear(): + table = osmium.index.create_map("flex_mem") table.set(593, osmium.osm.Location(0.35, 45.3)) table.get(593) table.clear() diff --git a/test/test_io.py b/test/test_io.py index 4677b00..31d6d65 100644 --- a/test/test_io.py +++ b/test/test_io.py @@ -5,6 +5,7 @@ # Copyright (C) 2025 Sarah Hoffmann and others. # For a full list of authors see the git log. import pytest +import uuid import osmium from helpers import CountingHandler @@ -23,7 +24,7 @@ def _run_file(fn): @pytest.mark.parametrize('as_string', [True, False]) def test_file_simple(tmp_path, as_string): - fn = tmp_path / 'text.opl' + fn = tmp_path / f"{uuid.uuid4()}.opl" fn.write_text('n1') if as_string: @@ -36,7 +37,7 @@ def test_file_simple(tmp_path, as_string): @pytest.mark.parametrize('as_string', [True, False]) def test_file_with_format(tmp_path, as_string): - fn = tmp_path / 'text.txt' + fn = tmp_path / f"{uuid.uuid4()}.txt" fn.write_text('n1') if as_string: @@ -81,7 +82,7 @@ def test_broken_timestamp(test_data): @pytest.mark.parametrize('as_string', [True, False]) def test_file_header(tmp_path, as_string): - fn = tmp_path / 'empty.xml' + fn = tmp_path / f"{uuid.uuid4()}.xml" fn.write_text(""" diff --git a/test/test_pyosmium_get_changes.py b/test/test_pyosmium_get_changes.py index 59168bd..a9578ed 100644 --- a/test/test_pyosmium_get_changes.py +++ b/test/test_pyosmium_get_changes.py @@ -8,6 +8,7 @@ """ from pathlib import Path from textwrap import dedent +import uuid import pytest import osmium.replication.server @@ -57,13 +58,13 @@ def test_init_date(self, capsys, httpserver): assert output == '-1' def test_init_to_file(self, tmp_path, httpserver): - fname = tmp_path / 'db.seq' + fname = tmp_path / f"{uuid.uuid4()}.seq" assert 0 == self.main(httpserver, '-I', '453', '-f', str(fname)) assert fname.read_text() == '453' def test_init_from_seq_file(self, tmp_path, httpserver): - fname = tmp_path / 'db.seq' + fname = tmp_path / f"{uuid.uuid4()}.seq" fname.write_text('453') assert 0 == self.main(httpserver, '-f', str(fname)) @@ -91,7 +92,7 @@ def test_init_date_with_cookie(self, capsys, tmp_path, httpserver): assert output == '-1' def test_get_simple_update(self, tmp_path, httpserver): - outfile = tmp_path / 'outfile.opl' + outfile = tmp_path / f"{uuid.uuid4()}.opl" httpserver.expect_request('/state.txt').respond_with_data(dedent("""\ sequenceNumber=454 diff --git a/test/test_replication.py b/test/test_replication.py index 2917fc6..9fc903e 100644 --- a/test/test_replication.py +++ b/test/test_replication.py @@ -7,6 +7,7 @@ import logging import time from textwrap import dedent +import uuid import pytest @@ -17,6 +18,8 @@ import osmium.replication.server as rserv import osmium.replication +pytestmark = [pytest.mark.thread_unsafe, pytest.mark.iterations(1)] + @pytest.mark.parametrize("inp,outp", [ (None, 'https://text.org/state.txt'), @@ -45,7 +48,7 @@ def test_get_diff_url(inp, outp): def test_get_newest_change_from_file(tmp_path): - fn = tmp_path / 'change.opl' + fn = tmp_path / f"{uuid.uuid4()}.opl" fn.write_text('n6365 v1 c63965061 t2018-10-29T03:56:07Z i8369524 ux x1 y7') val = osmium.replication.newest_change_from_file(str(fn)) diff --git a/test/test_writer.py b/test/test_writer.py index d9cd7a8..53ea613 100644 --- a/test/test_writer.py +++ b/test/test_writer.py @@ -26,7 +26,7 @@ def _WriteExpect(filename, expected): assert filename.read_text().strip() == expected def _create(expected): - filename = tmp_path / (str(uuid.uuid4()) + '.opl') + filename = tmp_path / f"{uuid.uuid4()}.opl" return _WriteExpect(filename, expected) return _create @@ -234,7 +234,7 @@ def test_member_object(test_writer, simple_handler): def test_set_custom_header(tmp_path): - fn = str(tmp_path / 'test.xml') + fn = tmp_path / f"{uuid.uuid4()}.xml" h = osmium.io.Header() h.set('generator', 'foo') h.add_box(osmium.osm.Box(0.1, -4, 10, 45)) @@ -257,7 +257,7 @@ def test_set_custom_header(tmp_path): def test_add_node_after_close(tmp_path, simple_handler): node_opl = "n235 v1 dV c0 t i0 u Telephant=yes x98.7 y-3.45" - filename = tmp_path / (str(uuid.uuid4()) + '.opl') + filename = tmp_path / f"{uuid.uuid4()}.opl" writer = osmium.SimpleWriter(str(filename), 1024*1024) writer.close() @@ -268,7 +268,7 @@ def test_add_node_after_close(tmp_path, simple_handler): def test_add_way_after_close(tmp_path, simple_handler): node_opl = "w1 Nn1" - filename = tmp_path / (str(uuid.uuid4()) + '.opl') + filename = tmp_path / f"{uuid.uuid4()}.opl" writer = osmium.SimpleWriter(str(filename), 1024*1024) writer.close() @@ -279,7 +279,7 @@ def test_add_way_after_close(tmp_path, simple_handler): def test_add_relation_after_close(tmp_path, simple_handler): node_opl = "r54 Mn1@,w3@foo" - filename = tmp_path / (str(uuid.uuid4()) + '.opl') + filename = tmp_path / f"{uuid.uuid4()}.opl" writer = osmium.SimpleWriter(str(filename), 1024*1024) writer.close() @@ -289,7 +289,7 @@ def test_add_relation_after_close(tmp_path, simple_handler): @pytest.mark.parametrize("final_item", (True, False)) def test_catch_errors_in_add_node(tmp_path, final_item): - test_file = tmp_path / 'test.opl' + test_file = tmp_path / f"{uuid.uuid4()}.opl" with osmium.SimpleWriter(str(test_file), 4000) as writer: writer.add_node(osmium.osm.mutable.Node(id=123)) @@ -309,7 +309,7 @@ def test_catch_errors_in_add_node(tmp_path, final_item): @pytest.mark.parametrize("final_item", (True, False)) def test_catch_errors_in_add_way(tmp_path, final_item): - test_file = tmp_path / 'test.opl' + test_file = tmp_path / f"{uuid.uuid4()}.opl" with osmium.SimpleWriter(test_file, 4000) as writer: writer.add_way(osmium.osm.mutable.Way(id=123, nodes=[1, 2, 3])) @@ -329,7 +329,7 @@ def test_catch_errors_in_add_way(tmp_path, final_item): @pytest.mark.parametrize("final_item", (True, False)) def test_catch_errors_in_add_relation(tmp_path, final_item): - test_file = tmp_path / 'test.opl' + test_file = tmp_path / f"{uuid.uuid4()}.opl" with osmium.SimpleWriter(filename=str(test_file), bufsz=4000) as writer: writer.add_relation(osmium.osm.mutable.Relation(id=123)) @@ -348,7 +348,7 @@ def test_catch_errors_in_add_relation(tmp_path, final_item): def test_do_not_overwrite_by_default(tmp_path): - test_file = tmp_path / 'test.opl' + test_file = tmp_path / f"{uuid.uuid4()}.opl" with osmium.SimpleWriter(filename=str(test_file), bufsz=4000) as writer: writer.add_node(osmium.osm.mutable.Node(id=123)) @@ -359,7 +359,7 @@ def test_do_not_overwrite_by_default(tmp_path): def test_do_overwrite(tmp_path): - test_file = tmp_path / 'test.opl' + test_file = tmp_path / f"{uuid.uuid4()}.opl" with osmium.SimpleWriter(filename=str(test_file), bufsz=4000) as writer: writer.add_node(osmium.osm.mutable.Node(id=123)) @@ -369,7 +369,7 @@ def test_do_overwrite(tmp_path): def test_write_to_file(tmp_path): - test_file = tmp_path / 'test.txt' + test_file = tmp_path / f"{uuid.uuid4()}.txt" with osmium.SimpleWriter(osmium.io.File(test_file, 'opl'), bufsz=4000) as writer: writer.add_node(osmium.osm.mutable.Node(id=123))