From 9cf5f79c7777446e1cd3044b5409ecd0cf4d5146 Mon Sep 17 00:00:00 2001 From: Martijn van Exel Date: Sat, 10 Aug 2024 16:44:27 -0600 Subject: [PATCH 01/10] chore:clean up old setup files, importlib --- overpass/__init__.py | 11 ++--------- setup.cfg | 2 -- setup.py | 25 ------------------------- 3 files changed, 2 insertions(+), 36 deletions(-) delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/overpass/__init__.py b/overpass/__init__.py index d28d1cc085b..7197408fad0 100644 --- a/overpass/__init__.py +++ b/overpass/__init__.py @@ -1,14 +1,7 @@ -# -*- coding: utf-8 -*- - -# Copyright 2015-2018 Martijn van Exel. -# This file is part of the overpass-api-python-wrapper project -# which is licensed under Apache 2.0. -# See LICENSE.txt for the full license text. - -"""Thin wrapper around the OpenStreetMap Overpass API.""" +from importlib.metadata import version __title__ = "overpass" -__version__ = "0.7" +__version__ = version("overpass") __license__ = "Apache 2.0" from .api import API diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 12871ff0f0d..00000000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[metadata] -description-file=README.md diff --git a/setup.py b/setup.py deleted file mode 100644 index c0496fc9d0c..00000000000 --- a/setup.py +++ /dev/null @@ -1,25 +0,0 @@ -from setuptools import setup - -setup( - name="overpass", - packages=["overpass"], - version="0.7", - description="Python wrapper for the OpenStreetMap Overpass API", - long_description="See README.md", - author="Martijn van Exel", - author_email="m@rtijn.org", - url="https://github.com/mvexel/overpass-api-python-wrapper", - license="Apache", - keywords=["openstreetmap", "overpass", "wrapper"], - classifiers=[ - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Topic :: Scientific/Engineering :: GIS", - "Topic :: Utilities", - ], - install_requires=["requests>=2.3.0", "osm2geojson"], - extras_require={"test": ["pytest", "requests-mock[fixture]", "geojson>=1.0.9"]}, -) From 302fb0121b8ab5346ac30d8f739e04b2e6a79c6c Mon Sep 17 00:00:00 2001 From: Martijn van Exel Date: Sat, 10 Aug 2024 17:38:05 -0600 Subject: [PATCH 02/10] chore: add codecov --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce6bba03fee..17239a3869b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,3 +37,11 @@ jobs: - name: Run tests with tox run: poetry run tox + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4.0.1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + # files: ./coverage.xml # Adjust this path if necessary + # flags: unittests # Optional flags for Codecov + # fail_ci_if_error: true # Fails the CI if Codecov upload fails From 55601d04a0f5711c84ce8693f6a46238a9ab4287 Mon Sep 17 00:00:00 2001 From: Martijn van Exel Date: Sat, 10 Aug 2024 17:42:08 -0600 Subject: [PATCH 03/10] chore:codecov integration --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17239a3869b..d8a1ec8aef4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,10 +38,10 @@ jobs: - name: Run tests with tox run: poetry run tox + - name: Codecov + uses: codecov/codecov-action@v4.5.0 + - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v4.0.1 + uses: codecov/codecov-action@v4.5.0 with: token: ${{ secrets.CODECOV_TOKEN }} - # files: ./coverage.xml # Adjust this path if necessary - # flags: unittests # Optional flags for Codecov - # fail_ci_if_error: true # Fails the CI if Codecov upload fails From fa8b9bed15777da164fd74a0e37204d77857f490 Mon Sep 17 00:00:00 2001 From: Martijn van Exel Date: Sat, 10 Aug 2024 17:46:40 -0600 Subject: [PATCH 04/10] bug:duplicate codecov action --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8a1ec8aef4..a6e4b982f99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,9 +38,6 @@ jobs: - name: Run tests with tox run: poetry run tox - - name: Codecov - uses: codecov/codecov-action@v4.5.0 - - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.5.0 with: From 6447b7fc2901f370446e030f576d023f219c7253 Mon Sep 17 00:00:00 2001 From: Martijn van Exel Date: Sat, 10 Aug 2024 17:51:27 -0600 Subject: [PATCH 05/10] bug:toxify --- .github/workflows/ci.yml | 2 ++ pyproject.toml | 2 ++ tox.ini | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6e4b982f99..4c8bf56e299 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,3 +42,5 @@ jobs: uses: codecov/codecov-action@v4.5.0 with: token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage.xml + fail_ci_if_error: true diff --git a/pyproject.toml b/pyproject.toml index ae1e2eaf79a..16865457688 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,11 +16,13 @@ requests = "^2.32.3" [tool.poetry.group.dev.dependencies] pytest = "^7.4.0" +pytest-cov = "^5.0.0" geojson = "^3.1.0" requests-mock = { extras = ["fixtures"], version = "^1.12.1" } deepdiff = "^7.0.1" tox = "^4.17.1" + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/tox.ini b/tox.ini index 74c7ce73867..03e031ab57c 100644 --- a/tox.ini +++ b/tox.ini @@ -7,10 +7,10 @@ allowlist_externals = poetry commands_pre = poetry install --no-root --sync commands = - poetry run pytest tests/ --import-mode importlib + poetry run pytest --cov=overpass --cov-report=xml --cov-report=term-missing tests/ --import-mode importlib [gh-actions] python = 3.9: py39 3.10: py310 - 3.11: py311 \ No newline at end of file + 3.11: py311 From c5e379ba6be1bde74188dd5d0673739874ed675b Mon Sep 17 00:00:00 2001 From: Martijn van Exel Date: Sat, 10 Aug 2024 17:52:28 -0600 Subject: [PATCH 06/10] chore:update lockfile --- poetry.lock | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 2cde21fd885..57e80b63b29 100644 --- a/poetry.lock +++ b/poetry.lock @@ -143,6 +143,93 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "coverage" +version = "7.6.1" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, + {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, + {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, + {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, + {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, + {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, + {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, + {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, + {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, + {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, + {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, + {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, + {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, + {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, + {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, + {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, + {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + [[package]] name = "deepdiff" version = "7.0.1" @@ -400,6 +487,24 @@ tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + [[package]] name = "requests" version = "2.32.3" @@ -568,4 +673,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [metadata] lock-version = "2.0" python-versions = ">3.9, <3.12" -content-hash = "cbde58fd145ca379e13dd16d277aa51a4bebe0eb205fb9e55909cf7ddad3b2cf" +content-hash = "013c5bc26adef674c1c0af306f0b73fcdbb42d5a1b03fb595af1f5f0dae06788" From 7ae906813f92e767f258bd1409a574d4b74140bf Mon Sep 17 00:00:00 2001 From: Martijn van Exel Date: Sat, 10 Aug 2024 17:56:03 -0600 Subject: [PATCH 07/10] chore:codecov badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8c7eb9d68de..e54247207b3 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Overpass API python wrapper ![PyPI - Downloads](https://img.shields.io/pypi/dm/overpass) ![PyPI - License](https://img.shields.io/pypi/l/overpass) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/overpass) +[![codecov](https://codecov.io/gh/mvexel/overpass-api-python-wrapper/graph/badge.svg?token=7pAwXEZNCt)](https://codecov.io/gh/mvexel/overpass-api-python-wrapper) ![GitHub License](https://img.shields.io/github/license/mvexel/overpass-api-python-wrapper) ![Mastodon Follow](https://img.shields.io/mastodon/follow/17500?domain=https%3A%2F%2Fen.osm.town) From bb4ca746ef8c609c73bf55e5279f7ed11bb23faa Mon Sep 17 00:00:00 2001 From: Martijn van Exel Date: Sat, 10 Aug 2024 18:00:18 -0600 Subject: [PATCH 08/10] chore:formatting and imports --- overpass/api.py | 39 +++++++++++++++++---------------------- overpass/errors.py | 8 ++------ overpass/queries.py | 8 -------- overpass/utils.py | 6 ------ tests/test_api.py | 5 ----- 5 files changed, 19 insertions(+), 47 deletions(-) diff --git a/overpass/api.py b/overpass/api.py index a5fe645bb36..3feab290c10 100644 --- a/overpass/api.py +++ b/overpass/api.py @@ -60,6 +60,7 @@ def __init__(self, *args, **kwargs): if self.debug: import http.client as http_client + http_client.HTTPConnection.debuglevel = 1 # You must initialize logging, @@ -70,7 +71,9 @@ def __init__(self, *args, **kwargs): requests_log.setLevel(logging.DEBUG) requests_log.propagate = True - def get(self, query, responseformat="geojson", verbosity="body", build=True, date=''): + def get( + self, query, responseformat="geojson", verbosity="body", build=True, date="" + ): """Pass in an Overpass query in Overpass QL. :param query: the Overpass QL query to send to the endpoint @@ -89,7 +92,7 @@ def get(self, query, responseformat="geojson", verbosity="body", build=True, dat date = datetime.fromisoformat(date) except ValueError: # The 'Z' in a standard overpass date will throw fromisoformat() off - date = datetime.strptime(date, '%Y-%m-%dT%H:%M:%SZ') + date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ") # Construct full Overpass query if build: full_query = self._construct_ql_query( @@ -142,18 +145,12 @@ def _api_status() -> dict: r = requests.get(endpoint) lines = tuple(r.text.splitlines()) - available_re = re.compile(r'\d(?= slots? available)') + available_re = re.compile(r"\d(?= slots? available)") available_slots = int( - next( - ( - m.group() - for line in lines - if (m := available_re.search(line)) - ), 0 - ) + next((m.group() for line in lines if (m := available_re.search(line))), 0) ) - waiting_re = re.compile(r'(?<=Slot available after: )[\d\-TZ:]{20}') + waiting_re = re.compile(r"(?<=Slot available after: )[\d\-TZ:]{20}") waiting_slots = tuple( datetime.strptime(m.group(), "%Y-%m-%dT%H:%M:%S%z") for line in lines @@ -161,15 +158,13 @@ def _api_status() -> dict: ) current_idx = next( - i for i, word in enumerate(lines) - if word.startswith('Currently running queries') + i + for i, word in enumerate(lines) + if word.startswith("Currently running queries") ) - running_slots = tuple(tuple(line.split()) for line in lines[current_idx + 1:]) + running_slots = tuple(tuple(line.split()) for line in lines[current_idx + 1 :]) running_slots_datetimes = tuple( - datetime.strptime( - slot[3], "%Y-%m-%dT%H:%M:%S%z" - ) - for slot in running_slots + datetime.strptime(slot[3], "%Y-%m-%dT%H:%M:%S%z") for slot in running_slots ) return { @@ -217,11 +212,10 @@ def slot_available_countdown(self) -> int: return max( ceil( ( - self.slot_available_datetime - - datetime.now(timezone.utc) + self.slot_available_datetime - datetime.now(timezone.utc) ).total_seconds() ), - 0 + 0, ) except TypeError: # Can't subtract from None, which means slot is available now @@ -246,7 +240,8 @@ def _construct_ql_query(self, userquery, responseformat, verbosity, date): if responseformat == "geojson": template = self._GEOJSON_QUERY_TEMPLATE complete_query = template.format( - query=raw_query, verbosity=verbosity, date=date) + query=raw_query, verbosity=verbosity, date=date + ) else: template = self._QUERY_TEMPLATE complete_query = template.format( diff --git a/overpass/errors.py b/overpass/errors.py index 4dbc16c47f9..70d053d8b3d 100644 --- a/overpass/errors.py +++ b/overpass/errors.py @@ -1,12 +1,7 @@ -# Copyright 2015-2018 Martijn van Exel. -# This file is part of the overpass-api-python-wrapper project -# which is licensed under Apache 2.0. -# See LICENSE.txt for the full license text. - - class OverpassError(Exception): """An error during your request occurred. Super class for all Overpass api errors.""" + pass @@ -26,6 +21,7 @@ def __init__(self, timeout): class MultipleRequestsError(OverpassError): """You are trying to run multiple requests at the same time.""" + pass diff --git a/overpass/queries.py b/overpass/queries.py index 6bee8e0f9f8..4e7e50996a5 100644 --- a/overpass/queries.py +++ b/overpass/queries.py @@ -1,11 +1,3 @@ -# -*- coding: utf-8 -*- - -# Copyright 2015-2018 Martijn van Exel. -# This file is part of the overpass-api-python-wrapper project -# which is licensed under Apache 2.0. -# See LICENSE.txt for the full license text. - - class MapQuery(object): """Query to retrieve complete ways and relations in an area.""" diff --git a/overpass/utils.py b/overpass/utils.py index 3cc1e5d72e9..0fcfebcf437 100644 --- a/overpass/utils.py +++ b/overpass/utils.py @@ -1,9 +1,3 @@ -# Copyright 2015-2018 Martijn van Exel. -# This file is part of the overpass-api-python-wrapper project -# which is licensed under Apache 2.0. -# See LICENSE.txt for the full license text. - - class Utils(object): @staticmethod diff --git a/tests/test_api.py b/tests/test_api.py index 73f90a9152c..ee59dc65147 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,8 +1,3 @@ -# Copyright 2015-2018 Martijn van Exel. -# This file is part of the overpass-api-python-wrapper project -# which is licensed under Apache 2.0. -# See LICENSE.txt for the full license text. - import json import os from datetime import datetime, timezone From f61c289334f9ffc9f4fad1224f9ff523820905d2 Mon Sep 17 00:00:00 2001 From: Martijn van Exel Date: Sun, 11 Aug 2024 17:14:56 -0600 Subject: [PATCH 09/10] chore:modernize --- MANIFEST.in | 3 - README.rst | 144 --------------------- conftest.py | 0 overpass/api.py | 326 ++++++++++++++++++++++++++++-------------------- 4 files changed, 190 insertions(+), 283 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 README.rst delete mode 100644 conftest.py diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index fa788c141ce..00000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include LICENSE.txt -include tests/* -include examples/* \ No newline at end of file diff --git a/README.rst b/README.rst deleted file mode 100644 index adac91a4090..00000000000 --- a/README.rst +++ /dev/null @@ -1,144 +0,0 @@ -Overpass API python wrapper -=========================== - -.. image:: https://github.com/mvexel/overpass-api-python-wrapper/workflows/CI/badge.svg - :target: https://github.com/mvexel/overpass-api-python-wrapper/actions?query=workflow%3ACI - - -This is a thin wrapper around the OpenStreetMap `Overpass -API `__. - - -|Build Status| - -Install it -========== - -``pip install overpass`` - -Usage ------ - -Simplest example: - -.. code:: python - - import overpass - api = overpass.API() - response = api.get('node["name"="Salt Lake City"]') - -``response`` will be a dictionary representing the JSON output you would -get `from the Overpass API -directly `__. - -Note that the Overpass query passed to ``get()`` should not contain any -``out`` or other meta statements. - -Another example: - -.. code:: python - - >>> print [( - ... feature['properties']['name'], - ... feature['id']) for feature in response["features"]] - [(u'Salt Lake City', 150935219), (u'Salt Lake City', 585370637)] - -You can find more examples in the ``examples/`` directory of this -repository. - -Response formats -~~~~~~~~~~~~~~~~ - -You can set the response type of your query using ``get()``\ ’s -``responseformat`` parameter to GeoJSON (``geojson``, the default), -plain JSON (``json``), CSV (``csv``), and OSM XML (``xml``). - -.. code:: python - - response = api.get('node["name"="Salt Lake City"]', responseformat="xml") - -Parameters -~~~~~~~~~~ - -The API object takes a few parameters: - -``endpoint`` -^^^^^^^^^^^^ - -The default endpoint is ``https://overpass-api.de/api/interpreter`` but -you can pass in another instance: - -.. code:: python - - api = overpass.API(endpoint=https://overpass.myserver/interpreter) - -``timeout`` -^^^^^^^^^^^ - -The default timeout is 25 seconds, but you can set it to whatever you -want. - -.. code:: python - - api = overpass.API(timeout=600) - -``debug`` -^^^^^^^^^ - -Setting this to ``True`` will get you debug output. - -Simple queries -~~~~~~~~~~~~~~ - -In addition to just sending your query and parse the result, the wrapper -provides shortcuts for often used map queries. To use them, just pass -them like to normal query to the API. - -MapQuery -^^^^^^^^ - -This is a shorthand for a `complete ways and -relations `__ -query in a bounding box (the ‘map call’). You just pass the bounding box -to the constructor: - -.. code:: python - - MapQuery = overpass.MapQuery(50.746,7.154,50.748,7.157) - response = api.get(MapQuery) - -WayQuery -^^^^^^^^ - -This is shorthand for getting a set of ways and their child nodes that -satisfy certain criteria. Pass the criteria as a Overpass QL stub to the -constructor: - -.. code:: python - - WayQuery = overpass.WayQuery('[name="Highway 51"]') - response = api.get(WayQuery) - -Testing -------- - -Using ``pytest``. - -``py.test`` - -FAQ ---- - -I need help or have an idea for a feature -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Create a `new -issue `__. - -Where did the CLI tool go? -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The command line tool was deprecated in version 0.4.0. - -.. |Build Status| image:: https://travis-ci.org/mvexel/overpass-api-python-wrapper.svg?branch=master - :target: https://travis-ci.org/mvexel/overpass-api-python-wrapper diff --git a/conftest.py b/conftest.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/overpass/api.py b/overpass/api.py index 3feab290c10..131291e6658 100644 --- a/overpass/api.py +++ b/overpass/api.py @@ -1,16 +1,12 @@ -# Copyright 2015-2018 Martijn van Exel. -# This file is part of the overpass-api-python-wrapper project -# which is licensed under Apache 2.0. -# See LICENSE.txt for the full license text. - import csv import json import logging import re +from dataclasses import dataclass, field from datetime import datetime, timezone from io import StringIO from math import ceil -from typing import Optional +from typing import Dict, List, Optional, Tuple import requests from osm2geojson import json2geojson @@ -25,90 +21,84 @@ ) -class API(object): +@dataclass +class API: """A simple Python wrapper for the OpenStreetMap Overpass API. - :param timeout: If a single number, the TCP connection timeout for the request. If a tuple - of two numbers, the connection timeout and the read timeout respectively. - Timeouts can be integers or floats. - :param endpoint: URL of overpass interpreter - :param headers: HTTP headers to include when making requests to the overpass endpoint - :param debug: Boolean to turn on debugging output - :param proxies: Dictionary of proxies to pass to the request library. See - requests documentation for details. + Attributes: + timeout: The TCP connection timeout for the request. + endpoint: URL of the Overpass interpreter. + headers: HTTP headers to include when making requests to the Overpass endpoint. + debug: Boolean to turn on debugging output. + proxies: Dictionary of proxies to pass to the request library. """ - SUPPORTED_FORMATS = ["geojson", "json", "xml", "csv"] - - # defaults for the API class - _timeout = 25 # second - _endpoint = "https://overpass-api.de/api/interpreter" - _headers = {"Accept-Charset": "utf-8;q=0.7,*;q=0.7"} - _debug = False - _proxies = None - - _QUERY_TEMPLATE = "[out:{out}]{date};{query}out {verbosity};" - _GEOJSON_QUERY_TEMPLATE = "[out:json]{date};{query}out {verbosity};" - - def __init__(self, *args, **kwargs): - self.endpoint = kwargs.get("endpoint", self._endpoint) - self.headers = kwargs.get("headers", self._headers) - self.timeout = kwargs.get("timeout", self._timeout) - self.debug = kwargs.get("debug", self._debug) - self.proxies = kwargs.get("proxies", self._proxies) - self._status = None - + timeout: float = 25 # seconds + endpoint: str = "https://overpass-api.de/api/interpreter" + headers: Dict[str, str] = field( + default_factory=lambda: {"Accept-Charset": "utf-8;q=0.7,*;q=0.7"} + ) + debug: bool = False + proxies: Optional[Dict[str, str]] = None + _status: Optional[int] = field(init=False, default=None) + + SUPPORTED_FORMATS: List[str] = field( + default_factory=lambda: ["geojson", "json", "xml", "csv"] + ) + _QUERY_TEMPLATE: str = "[out:{out}]{date};{query}out {verbosity};" + _GEOJSON_QUERY_TEMPLATE: str = "[out:json]{date};{query}out {verbosity};" + + def __post_init__(self): + """Initialize logging if debugging is enabled.""" if self.debug: - import http.client as http_client - - http_client.HTTPConnection.debuglevel = 1 - - # You must initialize logging, - # otherwise you'll not see debug output. - logging.basicConfig() - logging.getLogger().setLevel(logging.DEBUG) + logging.basicConfig(level=logging.DEBUG) requests_log = logging.getLogger("requests.packages.urllib3") requests_log.setLevel(logging.DEBUG) requests_log.propagate = True def get( - self, query, responseformat="geojson", verbosity="body", build=True, date="" + self, + query: str, + responseformat: str = "geojson", + verbosity: str = "body", + build: bool = True, + date: Optional[str] = "", ): - """Pass in an Overpass query in Overpass QL. - - :param query: the Overpass QL query to send to the endpoint - :param responseformat: one of the supported output formats ["geojson", "json", "xml", "csv"] - :param verbosity: one of the supported levels out data verbosity ["ids", - "skel", "body", "tags", "meta"] and optionally modifiers ["geom", "bb", - "center"] followed by an optional sorting indicator ["asc", "qt"]. Example: - "body geom qt" - :param build: boolean to indicate whether to build the overpass query from a template (True) - or allow the programmer to specify full query manually (False) - :param date: a date with an optional time. Example: 2020-04-27 or 2020-04-27T00:00:00Z + """Send an Overpass query in Overpass QL. + + Args: + query: The Overpass QL query to send to the endpoint. + responseformat: One of the supported output formats ["geojson", "json", "xml", "csv"]. + verbosity: One of the supported levels of data verbosity ["ids", "skel", "body", "tags", "meta"]. + build: Boolean to indicate whether to build the Overpass query from a template (True) or specify full query manually (False). + date: A date with an optional time. Example: 2020-04-27 or 2020-04-27T00:00:00Z. + + Returns: + The response from the Overpass API in the specified format. + + Raises: + UnknownOverpassError: If the response is invalid. + ServerRuntimeError: If a runtime error is encountered. """ if date and isinstance(date, str): - # If date is given and is not already a datetime, attempt to parse from string try: date = datetime.fromisoformat(date) except ValueError: - # The 'Z' in a standard overpass date will throw fromisoformat() off date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ") - # Construct full Overpass query + if build: full_query = self._construct_ql_query( query, responseformat=responseformat, verbosity=verbosity, date=date ) else: full_query = query - if self.debug: - logging.getLogger().info(query) - # Get the response from Overpass + logging.info("Query: %s", query) + r = self._get_from_overpass(full_query) content_type = r.headers.get("content-type") + logging.debug("Content type: %s", content_type) - if self.debug: - print(content_type) if content_type == "text/csv": return list(csv.reader(StringIO(r.text), delimiter="\t")) elif content_type in ("text/xml", "application/xml", "application/osm3s+xml"): @@ -119,96 +109,122 @@ def get( if not build: return response - # Check for valid answer from Overpass. - # A valid answer contains an 'elements' key at the root level. if "elements" not in response: raise UnknownOverpassError("Received an invalid answer from Overpass.") - # If there is a 'remark' key, it spells trouble. - overpass_remark = response.get("remark", None) + overpass_remark = response.get("remark") if overpass_remark and overpass_remark.startswith("runtime error"): raise ServerRuntimeError(overpass_remark) if responseformat != "geojson": return response - # construct geojson return json2geojson(response) + @staticmethod @staticmethod def _api_status() -> dict: - """ - :returns: dict describing the client's status with the API + """Retrieve the API status. + + Returns: + A dictionary describing the client's status with the API. """ endpoint = "https://overpass-api.de/api/status" - r = requests.get(endpoint) - lines = tuple(r.text.splitlines()) + lines = r.text.splitlines() - available_re = re.compile(r"\d(?= slots? available)") + # Regex pattern to match the number of available slots + available_re = re.compile(r"(\d+) slots available now.") available_slots = int( - next((m.group() for line in lines if (m := available_re.search(line))), 0) + next((m.group(1) for line in lines if (m := available_re.search(line))), 0) ) - waiting_re = re.compile(r"(?<=Slot available after: )[\d\-TZ:]{20}") + # Regex pattern to match waiting slots + waiting_re = re.compile(r"Slot available after: ([\d\-TZ:]+)") waiting_slots = tuple( - datetime.strptime(m.group(), "%Y-%m-%dT%H:%M:%S%z") + datetime.strptime(m.group(1), "%Y-%m-%dT%H:%M:%SZ").replace( + tzinfo=timezone.utc + ) for line in lines if (m := waiting_re.search(line)) ) + # Find index of the running queries section current_idx = next( - i - for i, word in enumerate(lines) - if word.startswith("Currently running queries") + ( + i + for i, line in enumerate(lines) + if line.startswith("Currently running queries") + ), + len(lines), # Default to end of list if not found ) - running_slots = tuple(tuple(line.split()) for line in lines[current_idx + 1 :]) - running_slots_datetimes = tuple( - datetime.strptime(slot[3], "%Y-%m-%dT%H:%M:%S%z") for slot in running_slots + + # Parse running queries to extract start times + running_slots = tuple( + datetime.strptime(slot[3], "%Y-%m-%dT%H:%M:%SZ").replace( + tzinfo=timezone.utc + ) + for line in lines[current_idx + 1 :] + if (slot := line.split()) + and len(slot) == 4 # Ensure there are four elements in the split line ) + # Return the parsed status return { "available_slots": available_slots, "waiting_slots": waiting_slots, - "running_slots": running_slots_datetimes, + "running_slots": running_slots, } @property def slots_available(self) -> int: - """ - :returns: count of open slots the client has on the server + """Get the count of open slots the client has on the server. + + Returns: + The number of available slots. """ return self._api_status()["available_slots"] @property - def slots_waiting(self) -> tuple: - """ - :returns: tuple of datetimes representing waiting slots and when they will be available + def slots_waiting(self) -> Tuple[datetime, ...]: + """Get the waiting slots and when they will be available. + + Returns: + A tuple of datetimes representing waiting slots and when they will be available. """ return self._api_status()["waiting_slots"] @property - def slots_running(self) -> tuple: - """ - :returns: tuple of datetimes representing running slots and when they will be freed + def slots_running(self) -> Tuple[datetime, ...]: + """Get the running slots and when they will be freed. + + Returns: + A tuple of datetimes representing running slots and when they will be freed. """ return self._api_status()["running_slots"] @property def slot_available_datetime(self) -> Optional[datetime]: + """Get the datetime when the next slot will be available. + + Returns: + None if a slot is available now (no wait needed), or a datetime representing when the next slot will become available. """ - :returns: None if a slot is available now (no wait needed) or a datetime representing when the next slot will become available - """ - if self.slots_available: + if self.slots_available > 0: return None - return min(self.slots_running + self.slots_waiting) + all_slots = self.slots_running + self.slots_waiting + return min(all_slots) if all_slots else None @property def slot_available_countdown(self) -> int: + """Get the countdown for when the next slot will be available. + + Returns: + 0 if a slot is available now, or an integer of seconds until the next slot is free. """ - :returns: 0 if a slot is available now, or an int of seconds until the next slot is free - """ - try: + if self.slots_available > 0: + return 0 + if self.slot_available_datetime: return max( ceil( ( @@ -217,44 +233,71 @@ def slot_available_countdown(self) -> int: ), 0, ) - except TypeError: - # Can't subtract from None, which means slot is available now - return 0 + return 0 def search(self, feature_type, regex=False): - """Search for something.""" + """Search for a feature. + + Args: + feature_type: The type of feature to search for. + regex: Whether to use a regular expression for the search. + + Raises: + NotImplementedError: The search method is not implemented. + """ raise NotImplementedError() - # deprecation of upper case functions + # Deprecation of uppercase functions Get = get Search = search def _construct_ql_query(self, userquery, responseformat, verbosity, date): + """Construct a full Overpass QL query. + + Args: + userquery: The user-specified query string. + responseformat: The format for the response. + verbosity: The verbosity level for the response. + date: The date for the query. + + Returns: + The constructed query string. + """ raw_query = str(userquery).rstrip() if not raw_query.endswith(";"): raw_query += ";" - if date: - date = f'[date:"{date:%Y-%m-%dT%H:%M:%SZ}"]' + date_str = f'[date:"{date:%Y-%m-%dT%H:%M:%SZ}"]' if date else "" - if responseformat == "geojson": - template = self._GEOJSON_QUERY_TEMPLATE - complete_query = template.format( - query=raw_query, verbosity=verbosity, date=date - ) - else: - template = self._QUERY_TEMPLATE - complete_query = template.format( - query=raw_query, out=responseformat, verbosity=verbosity, date=date - ) + template = ( + self._GEOJSON_QUERY_TEMPLATE + if responseformat == "geojson" + else self._QUERY_TEMPLATE + ) + complete_query = template.format( + query=raw_query, out=responseformat, verbosity=verbosity, date=date_str + ) - if self.debug: - print(complete_query) + logging.debug("Complete query: %s", complete_query) return complete_query def _get_from_overpass(self, query): - payload = {"data": query} + """Send a POST request to the Overpass API. + + Args: + query: The query to send. + + Returns: + The HTTP response from the Overpass API. + Raises: + TimeoutError: If the request times out. + OverpassSyntaxError: If the query is malformed. + MultipleRequestsError: If too many requests are made. + ServerLoadError: If the server is under heavy load. + UnknownOverpassError: If an unknown error occurs. + """ + payload = {"data": query} try: r = requests.post( self.endpoint, @@ -263,22 +306,33 @@ def _get_from_overpass(self, query): proxies=self.proxies, headers=self.headers, ) - + r.raise_for_status() except requests.exceptions.Timeout: - raise TimeoutError(self._timeout) - - self._status = r.status_code - - if self._status != 200: - if self._status == 400: - raise OverpassSyntaxError(query) - elif self._status == 429: - raise MultipleRequestsError() - elif self._status == 504: - raise ServerLoadError(self._timeout) - raise UnknownOverpassError( - "The request returned status code {code}".format(code=self._status) - ) - else: - r.encoding = "utf-8" - return r + raise TimeoutError(self.timeout) + except requests.exceptions.HTTPError as e: + self._handle_http_error(e, query) + + r.encoding = "utf-8" + return r + + def _handle_http_error(self, error, query): + """Handle HTTP errors. + + Args: + error: The HTTP error that occurred. + query: The query that caused the error. + + Raises: + OverpassSyntaxError: If the query is malformed. + MultipleRequestsError: If too many requests are made. + ServerLoadError: If the server is under heavy load. + UnknownOverpassError: If an unknown error occurs. + """ + self._status = error.response.status_code + if self._status == 400: + raise OverpassSyntaxError(query) + elif self._status == 429: + raise MultipleRequestsError() + elif self._status == 504: + raise ServerLoadError(self.timeout) + raise UnknownOverpassError(f"The request returned status code {self._status}") From 39a4a12d65416b327208c1dbb9b1811ac03b0222 Mon Sep 17 00:00:00 2001 From: Martijn van Exel Date: Sun, 11 Aug 2024 17:48:27 -0600 Subject: [PATCH 10/10] chore:moderizing code --- overpass/utils.py | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 overpass/utils.py diff --git a/overpass/utils.py b/overpass/utils.py deleted file mode 100644 index 0fcfebcf437..00000000000 --- a/overpass/utils.py +++ /dev/null @@ -1,9 +0,0 @@ -class Utils(object): - - @staticmethod - def to_overpass_id(osmid, area=False): - area_base = 2400000000 - relation_base = 3600000000 - if area: - return int(osmid) + area_base - return int(osmid) + relation_base