├── .coveragerc ├── .flake8 ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md ├── config.yml ├── stale.yml └── workflows │ ├── ci.yml │ ├── codeql-analysis.yml │ └── pypi.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── CHANGELOG.rst ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── README.rst ├── docs ├── Makefile ├── make.bat ├── requirements.txt └── source │ ├── _static │ └── .keep │ ├── api.rst │ ├── changelog.rst │ ├── conf.py │ ├── contributing.rst │ ├── example.rst │ ├── faq.rst │ ├── index.rst │ └── introduction.rst ├── examples ├── get_areas.py ├── get_nodes.py └── get_ways.py ├── overpy ├── __about__.py ├── __init__.py ├── exception.py └── helper.py ├── setup.cfg ├── setup.py ├── tests ├── README.md ├── __init__.py ├── base_class.py ├── json │ ├── area-01.json │ ├── node-01.json │ ├── relation-01.json │ ├── relation-02.json │ ├── relation-03.json │ ├── relation-04.json │ ├── remark-runtime-error-01.json │ ├── remark-runtime-remark-01.json │ ├── remark-unknown-01.json │ ├── result-expand-01.json │ ├── result-expand-02.json │ ├── result-way-01.json │ ├── result-way-02.json │ ├── result-way-03.json │ ├── way-01.json │ ├── way-02.json │ ├── way-03.json │ └── way-04.json ├── response │ ├── bad-request-encoding.html │ └── bad-request.html ├── test_exception.py ├── test_json.py ├── test_request.py ├── test_result.py ├── test_result_way.py ├── test_xml.py └── xml │ ├── area-01.xml │ ├── node-01.xml │ ├── relation-01.xml │ ├── relation-02.xml │ ├── relation-03.xml │ ├── relation-04.xml │ ├── remark-runtime-error-01.xml │ ├── remark-runtime-remark-01.xml │ ├── remark-unknown-01.xml │ ├── way-01.xml │ ├── way-02.xml │ ├── way-03.xml │ └── way-04.xml └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = overpy 3 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: none 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | [flake8] 5 | max-line-length = 120 6 | extend-exclude = 7 | venv/ 8 | docs/source/conf.py 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ##### Issue type 4 | 5 | - Bug Report 6 | - Feature Idea 7 | - Documentation Report 8 | 9 | ##### OverPy version 10 | 11 | ``` 12 | 13 | ``` 14 | 15 | ##### OS 16 | 20 | - Ubuntu 14.04 21 | - Ubuntu 16.04 22 | - Debian 8.0 23 | 24 | #### Python version 25 | 26 | 27 | - Python 2.7 28 | - Python 3.2 29 | - Python 3.3 30 | - Python 3.4 31 | - Python 3.5 32 | 33 | ##### Summary 34 | 35 | 36 | ##### Steps to reproduce 37 | 40 | 41 | 42 | 43 | ##### Expected results 44 | 45 | 46 | ##### Actual results 47 | 48 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ##### Issue type 2 | 3 | - Feature 4 | - Bugfix 5 | - Documentation 6 | - ... 7 | 8 | ##### Summary 9 | 10 | 11 | 15 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: none 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | # Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome 5 | 6 | # Comment to be posted to on first time issues 7 | newIssueWelcomeComment: > 8 | Thanks for opening your first issue here! Be sure to follow the issue template! 9 | 10 | # Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome 11 | 12 | # Comment to be posted to on PRs from first time contributors in your repository 13 | newPRWelcomeComment: > 14 | Welcome to the community and thanks for opening your first pull request. 15 | Feel free to look for more issues to tackle at https://github.com/DinoTools/python-ssdeep/issues 16 | 17 | # Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge 18 | 19 | # Comment to be posted to on pull requests merged by a first time user 20 | # firstPRMergeComment: > 21 | # Congrats on merging your first pull request! We here at behaviorbot are proud of you! 22 | 23 | # It is recommend to include as many gifs and emojis as possible 24 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: none 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | # Number of days of inactivity before an issue becomes stale 5 | daysUntilStale: 14 6 | 7 | # Number of days of inactivity before a stale issue is closed 8 | daysUntilClose: 7 9 | 10 | # Only issues that requires info from the op 11 | onlyLabels: 12 | - "Status: Feedback needed" 13 | 14 | # Issues with these labels will never be considered stale 15 | exemptLabels: 16 | - "Priority: Critical" 17 | - "Priority: Security" 18 | - "Status: Confirmed" 19 | - "Status: Complete" 20 | - "Status: Review Needed" 21 | - "Status: On Hold" 22 | - "Status: In Progress" 23 | - security 24 | 25 | # Label to use when marking an issue as stale 26 | staleLabel: "Status: Stale" 27 | 28 | # Comment to post when marking an issue as stale. Set to `false` to disable 29 | markComment: > 30 | This issue has been automatically marked as stale because it has not had 31 | recent activity and seems to be missing some essential information. It will 32 | be closed if no further activity occurs. Thank you for your contributions. 33 | 34 | # Comment to post when closing a stale issue. Set to `false` to disable 35 | # closeComment: false 36 | 37 | # Limit the number of actions per hour, from 1-30. Default is 30 38 | limitPerRun: 30 39 | 40 | # Limit to only `issues` or `pulls` 41 | only: issues 42 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: none 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | name: Python CI Tests 5 | 6 | on: 7 | push: 8 | branches: 9 | - master 10 | paths-ignore: 11 | - 'docs/**' 12 | - '*.md' 13 | - '*.rst' 14 | pull_request: 15 | branches: 16 | - master 17 | paths-ignore: 18 | - 'docs/**' 19 | - '*.md' 20 | - '*.rst' 21 | 22 | jobs: 23 | tests: 24 | name: "Python ${{ matrix.name }} on ${{ matrix.os }}" 25 | runs-on: "${{ matrix.os }}" 26 | 27 | strategy: 28 | fail-fast: false 29 | matrix: 30 | include: 31 | - {name: Linux, python: '3.9', os: ubuntu-latest, tox: py39} 32 | - {name: Windows, python: '3.9', os: windows-latest, tox: py39} 33 | # ToDo: There are errors on Mac, but I don't know why: Connection timeout or reset 34 | # - {name: Mac, python: '3.9', os: macos-latest, tox: py39} 35 | - {name: '3.11', python: '3.11', os: ubuntu-latest, tox: py311} 36 | - {name: '3.10', python: '3.10', os: ubuntu-latest, tox: py310} 37 | - {name: '3.9', python: '3.9', os: ubuntu-latest, tox: py39} 38 | - {name: '3.8', python: '3.8', os: ubuntu-latest, tox: py38} 39 | - {name: '3.7', python: '3.7', os: ubuntu-latest, tox: py37} 40 | - {name: 'PyPy 3.9', python: pypy3.9, os: ubuntu-latest, tox: pypy39} 41 | 42 | steps: 43 | - uses: "actions/checkout@v3" 44 | 45 | - uses: "actions/setup-python@v4" 46 | with: 47 | python-version: ${{ matrix.python }} 48 | cache: 'pip' # caching pip dependencies 49 | 50 | - name: Install tox and any other packages 51 | run: pip install tox 52 | 53 | - name: "Run tox targets for ${{ matrix.python }}" 54 | run: tox -e ${{ matrix.tox }} 55 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '36 17 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'python' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /.github/workflows/pypi.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: none 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | name: Publish Python distributions to PyPI and TestPyPI 5 | 6 | on: 7 | pull_request: 8 | push: 9 | branches: 10 | - main 11 | - master 12 | tags: 13 | - 0.* 14 | - 1.* 15 | 16 | jobs: 17 | build-n-publish: 18 | name: Build and publish Python distributions to PyPI and TestPyPI 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@master 22 | 23 | - name: Get history and tags for SCM versioning to work 24 | run: | 25 | git fetch --prune --unshallow 26 | git fetch --depth=1 origin +refs/tags/*:refs/tags/* 27 | 28 | - name: Set up Python 3.10 29 | uses: actions/setup-python@v3 30 | with: 31 | python-version: "3.10" 32 | 33 | - name: Install pypa/build 34 | run: python -m pip install build twine --user 35 | 36 | - name: Build binary wheel and a source tarball 37 | run: python -m build --sdist --wheel --outdir dist/ . 38 | 39 | - name: Check wheels and source tarballs 40 | run: python -m twine check dist/* 41 | 42 | # The project name os blocked by an other test project 43 | # - name: Publish distribution to Test PyPI 44 | # if: >- 45 | # github.event_name == 'push' && 46 | # github.ref == 'refs/heads/main' 47 | # uses: pypa/gh-action-pypi-publish@release/v1 48 | # with: 49 | # password: ${{ secrets.TEST_PYPI_API_TOKEN }} 50 | # repository_url: https://test.pypi.org/legacy/ 51 | # skip-existing: true 52 | # 53 | - name: Publish distribution to PyPI 54 | if: >- 55 | github.event_name == 'push' && 56 | startsWith(github.ref, 'refs/tags') 57 | uses: pypa/gh-action-pypi-publish@release/v1 58 | with: 59 | password: ${{ secrets.PYPI_API_TOKEN }} 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Python template 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | .env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | 135 | # pytype static type analyzer 136 | .pytype/ 137 | 138 | # Cython debug symbols 139 | cython_debug/ 140 | 141 | ### JetBrains template 142 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 143 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 144 | 145 | # User-specific stuff 146 | .idea/**/workspace.xml 147 | .idea/**/tasks.xml 148 | .idea/**/usage.statistics.xml 149 | .idea/**/dictionaries 150 | .idea/**/shelf 151 | 152 | # Generated files 153 | .idea/**/contentModel.xml 154 | 155 | # Sensitive or high-churn files 156 | .idea/**/dataSources/ 157 | .idea/**/dataSources.ids 158 | .idea/**/dataSources.local.xml 159 | .idea/**/sqlDataSources.xml 160 | .idea/**/dynamic.xml 161 | .idea/**/uiDesigner.xml 162 | .idea/**/dbnavigator.xml 163 | 164 | # Gradle 165 | .idea/**/gradle.xml 166 | .idea/**/libraries 167 | 168 | # Gradle and Maven with auto-import 169 | # When using Gradle or Maven with auto-import, you should exclude module files, 170 | # since they will be recreated, and may cause churn. Uncomment if using 171 | # auto-import. 172 | # .idea/artifacts 173 | # .idea/compiler.xml 174 | # .idea/jarRepositories.xml 175 | # .idea/modules.xml 176 | # .idea/*.iml 177 | # .idea/modules 178 | # *.iml 179 | # *.ipr 180 | 181 | # CMake 182 | cmake-build-*/ 183 | 184 | # Mongo Explorer plugin 185 | .idea/**/mongoSettings.xml 186 | 187 | # File-based project format 188 | *.iws 189 | 190 | # IntelliJ 191 | out/ 192 | 193 | # mpeltonen/sbt-idea plugin 194 | .idea_modules/ 195 | 196 | # JIRA plugin 197 | atlassian-ide-plugin.xml 198 | 199 | # Cursive Clojure plugin 200 | .idea/replstate.xml 201 | 202 | # Crashlytics plugin (for Android Studio and IntelliJ) 203 | com_crashlytics_export_strings.xml 204 | crashlytics.properties 205 | crashlytics-build.properties 206 | fabric.properties 207 | 208 | # Editor-based Rest Client 209 | .idea/httpRequests 210 | 211 | # Android studio 3.1+ serialized cache file 212 | .idea/caches/build_file_checksums.ser 213 | 214 | ### Vim template 215 | # Swap 216 | [._]*.s[a-v][a-z] 217 | !*.svg # comment out if you don't need vector files 218 | [._]*.sw[a-p] 219 | [._]s[a-rt-v][a-z] 220 | [._]ss[a-gi-z] 221 | [._]sw[a-p] 222 | 223 | # Session 224 | Session.vim 225 | Sessionx.vim 226 | 227 | # Temporary 228 | .netrwhist 229 | *~ 230 | # Auto-generated tag files 231 | tags 232 | # Persistent undo 233 | [._]*.un~ 234 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: none 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | repos: 5 | - repo: https://github.com/pre-commit/pre-commit-hooks 6 | rev: v5.0.0 7 | hooks: 8 | # Prevent giant files from being committed 9 | - id: check-added-large-files 10 | # Simply check whether files parse as valid python 11 | - id: check-ast 12 | # Require literal syntax when initializing empty or zero Python builtin types 13 | - id: check-builtin-literals 14 | # Check for files that would conflict in case-insensitive filesystems 15 | - id: check-case-conflict 16 | # Checks for a common error of placing code before the docstring 17 | - id: check-docstring-first 18 | # Ensures that (non-binary) executables have a shebang 19 | - id: check-executables-have-shebangs 20 | # Check for files that contain merge conflict strings 21 | - id: check-merge-conflict 22 | # Checks that scripts with shebangs are executable 23 | - id: check-shebang-scripts-are-executable 24 | # Checks for symlinks which do not point to anything 25 | - id: check-symlinks 26 | # This hook checks yaml files for parseable syntax 27 | - id: check-yaml 28 | # Check for debugger imports and py37+ breakpoint() calls in python source 29 | - id: debug-statements 30 | # Detects the presence of private keys 31 | - id: detect-private-key 32 | # Ensures that a file is either empty, or ends with one newline 33 | - id: end-of-file-fixer 34 | exclude: ^tests/(json|response|xml)/ 35 | # Replaces or checks mixed line ending 36 | - id: mixed-line-ending 37 | exclude: ^tests/(json|response|xml)/ 38 | # This hook trims trailing whitespace 39 | - id: trailing-whitespace 40 | exclude: ^tests/(json|response|xml)/ 41 | 42 | - repo: https://github.com/pre-commit/pygrep-hooks 43 | rev: v1.10.0 44 | hooks: 45 | # Enforce that `noqa` annotations always occur with specific codes 46 | - id: python-check-blanket-noqa 47 | # Enforce that # type: ignore annotations always occur with specific codes 48 | - id: python-check-blanket-type-ignore 49 | # Prevent common mistakes of `assert mck.not_called()`, `assert mck.called_once_with(...)` and `mck.assert_called` 50 | - id: python-check-mock-methods 51 | # A quick check for the `eval()` built-in function 52 | - id: python-no-eval 53 | # A quick check for the deprecated `.warn()` method of python loggers 54 | - id: python-no-log-warn 55 | # Enforce that python3.6+ type annotations are used instead of type comments 56 | - id: python-use-type-annotations 57 | # Detect common mistake of using single backticks when writing rst 58 | - id: rst-backticks 59 | # Detect mistake of rst directive not ending with double colon 60 | - id: rst-directive-colons 61 | # Detect mistake of inline code touching normal text in rst 62 | - id: rst-inline-touching-normal 63 | # Forbid files which have a UTF-8 Unicode replacement character 64 | - id: text-unicode-replacement-char 65 | 66 | - repo: https://github.com/PyCQA/flake8 67 | rev: 7.1.1 68 | hooks: 69 | - id: flake8 70 | additional_dependencies: 71 | - flake8-bugbear 72 | - flake8-implicit-str-concat 73 | 74 | - repo: https://github.com/pre-commit/mirrors-mypy 75 | rev: v1.15.0 76 | hooks: 77 | - id: mypy 78 | args: [--no-strict-optional, --ignore-missing-imports, --allow-untyped-global] 79 | additional_dependencies: [types-simplejson] 80 | exclude: ^(docs/) 81 | 82 | # # Check for missing licensing and copyright information. 83 | # # The REUSE helper tool assists with achieving and confirming REUSE compliance. 84 | # # See: https://reuse.software/ 85 | # - repo: https://github.com/fsfe/reuse-tool 86 | # rev: v1.1.2 87 | # hooks: 88 | # - id: reuse 89 | # exclude: ^tests/(json|response|xml)/ 90 | 91 | ci: 92 | autofix_commit_msg: | 93 | * - auto fixes from pre-commit hooks 94 | 95 | for more information, see https://pre-commit.ci 96 | autofix_prs: false 97 | autoupdate_commit_msg: 'ci - pre-commit autoupdate' 98 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Build documentation in the docs/ directory with Sphinx 9 | sphinx: 10 | configuration: docs/source/conf.py 11 | 12 | # Optionally build your docs in additional formats such as PDF 13 | formats: all 14 | 15 | # Optionally set the version of Python and requirements required to build your docs 16 | python: 17 | version: 3.7 18 | install: 19 | - requirements: docs/requirements.txt 20 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | 0.x (`master`_) 5 | ~~~~~~~~~~~~~~~ 6 | 7 | .. note:: This version is not yet released and is under development. 8 | 9 | 0.7 (2023-12-04) 10 | ~~~~~~~~~~~~~~~~ 11 | 12 | * Add type hints 13 | * Update requirements to 3.6.2+ 14 | * Update docs to use type hints 15 | * Update CI pipelines 16 | * Add automated upload to PyPI 17 | * Build wheels 18 | 19 | 0.6 (2021-04-20) 20 | ~~~~~~~~~~~~~~~~ 21 | 22 | * Drop Python 2.7 support 23 | * Replace Travis CI with GitHub Actions 24 | * Change ThreadedHTTPServer to HTTPServer for tests 25 | * Add CodeQL CI tests 26 | * Add pre-commit 27 | * Improve build steps 28 | 29 | 30 | 0.5 (2021-04-14) 31 | ~~~~~~~~~~~~~~~~ 32 | 33 | * Add support to retry a failed request 34 | * Improve test handlers to fix issues with Py2.7 and PyPy 35 | * Improve tests 36 | * Add Support for Python 3.6 37 | * Add function to handle remark tags and elements 38 | * Add support to parse data from ET.Element 39 | * Move attribute modifiers from class to package 40 | * Change to use Exception instead of BaseException 41 | 42 | 0.4 (2016-12-08) 43 | ~~~~~~~~~~~~~~~~ 44 | 45 | * Add SAX parser 46 | * Add option to choose DOM or SAX parser 47 | * Fix issues with CI builds with Python 3.2 48 | * Add Python 3.5 to CI builds 49 | * Fix issues (Thanks to all contributors) 50 | * Add property for default API URL 51 | * Add examples 52 | * Build Fixes 53 | * GitHub templates 54 | * Parse center information 55 | * Parse geometry information 56 | * Support Areas 57 | 58 | 0.3.1 (2015-04-30) 59 | ~~~~~~~~~~~~~~~~~~ 60 | 61 | * Improve example 62 | 63 | 0.3.0 (2015-04-30) 64 | ~~~~~~~~~~~~~~~~~~ 65 | 66 | * Improve internal data handling (Dominik) 67 | * Add helper functions (Morris Jobke) 68 | 69 | 0.2.0 (2014-12-27) 70 | ~~~~~~~~~~~~~~~~~~ 71 | 72 | * Added support for xml response data 73 | * Added support for exceptions 74 | * Added tests with 100% code coverage 75 | * Removed Python 2.6 support 76 | * Added more examples to the documentation 77 | 78 | 0.1.0 (2014-12-14) 79 | ~~~~~~~~~~~~~~~~~~ 80 | 81 | Proof of concept 82 | 83 | * Initial release. 84 | 85 | .. _`master`: https://github.com/DinoTools/python-overpy 86 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | First of all, thank you for your interest in contributing to OverPy! 5 | 6 | 7 | Filing bug reports 8 | ------------------ 9 | 10 | Bug reports are very welcome. 11 | Please fill them on the `GitHub issue tracker`_. 12 | Good bug reports come with extensive descriptions of the error and how to reproduce it. 13 | 14 | 15 | Patches 16 | ------- 17 | 18 | All patches to OverPy should be submitted in the form of pull requests to the main OverPy repository, `DinoTools/python-overpy`_. 19 | These pull requests should satisfy the following properties: 20 | 21 | Code 22 | ^^^^ 23 | 24 | - The pull request should focus on one particular improvement to OverPy. 25 | - Create different pull requests for unrelated features or bugfixes. 26 | - Python code should follow `PEP 8`_, especially in the "do what code around you does" sense. 27 | 28 | Documentation 29 | ^^^^^^^^^^^^^ 30 | 31 | When introducing new functionality, please remember to write documentation. 32 | 33 | Tests 34 | ^^^^^ 35 | 36 | It is recommended to add tests for new code you add. 37 | 38 | Review 39 | ------ 40 | 41 | Finally, pull requests must be reviewed before merging. 42 | Everyone can perform reviews; this is a very valuable way to contribute, and is highly encouraged. 43 | 44 | 45 | .. _GitHub issue tracker: https://github.com/DinoTools/python-overpy/issues 46 | .. _DinoTools/python-overpy: https://github.com/DinoTools/python-overpy 47 | .. _PEP 8: https://www.python.org/dev/peps/pep-0008/ 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 PhiBo (DinoTools) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include CHANGELOG.rst CONTRIBUTING.rst README.rst 2 | include LICENSE 3 | include MANIFEST.in 4 | include docs/make.bat docs/Makefile 5 | include docs/source/conf.py 6 | include docs/source/*.rst 7 | include examples/*.py 8 | include tests/*.py 9 | include tests/json/*.json 10 | include tests/response/*.html 11 | include tests/xml/*.xml 12 | include tox.ini 13 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Python Overpass Wrapper 2 | ======================= 3 | 4 | A Python Wrapper to access the Overpass API. 5 | 6 | Have a look at the `documentation`_ to find additional information. 7 | 8 | .. image:: https://img.shields.io/pypi/v/overpy.svg 9 | :target: https://pypi.python.org/pypi/overpy/ 10 | :alt: Latest Version 11 | 12 | .. image:: https://img.shields.io/pypi/l/overpy.svg 13 | :target: https://pypi.python.org/pypi/overpy/ 14 | :alt: License 15 | 16 | .. image:: https://github.com/DinoTools/python-overpy/actions/workflows/ci.yml/badge.svg?branch=master 17 | :target: https://github.com/DinoTools/python-overpy/actions/workflows/ci.yml?query=branch%3Amaster+ 18 | 19 | .. image:: https://coveralls.io/repos/DinoTools/python-overpy/badge.png?branch=master 20 | :target: https://coveralls.io/r/DinoTools/python-overpy?branch=master 21 | 22 | Features 23 | -------- 24 | 25 | * Query Overpass API 26 | * Parse JSON and XML response data 27 | * Additional helper functions 28 | 29 | Install 30 | ------- 31 | 32 | **Requirements:** 33 | 34 | Supported Python versions: 35 | 36 | * Python >= 3.7 37 | * PyPy3 38 | 39 | **Install:** 40 | 41 | .. code-block:: console 42 | 43 | $ pip install overpy 44 | 45 | Examples 46 | -------- 47 | 48 | Additional examples can be found in the `documentation`_ and in the *examples* directory. 49 | 50 | .. code-block:: python 51 | 52 | import overpy 53 | 54 | api = overpy.Overpass() 55 | 56 | # fetch all ways and nodes 57 | result = api.query(""" 58 | way(50.746,7.154,50.748,7.157) ["highway"]; 59 | (._;>;); 60 | out body; 61 | """) 62 | 63 | for way in result.ways: 64 | print("Name: %s" % way.tags.get("name", "n/a")) 65 | print(" Highway: %s" % way.tags.get("highway", "n/a")) 66 | print(" Nodes:") 67 | for node in way.nodes: 68 | print(" Lat: %f, Lon: %f" % (node.lat, node.lon)) 69 | 70 | 71 | Helper 72 | ~~~~~~ 73 | 74 | Helper methods are available to provide easy access to often used requests. 75 | 76 | .. code-block:: python 77 | 78 | import overpy.helper 79 | 80 | # 3600062594 is the OSM id of Chemnitz and is the bounding box for the request 81 | street = overpy.helper.get_street( 82 | "Straße der Nationen", 83 | "3600062594" 84 | ) 85 | 86 | # this finds an intersection between Straße der Nationen and Carolastraße in Chemnitz 87 | intersection = overpy.helper.get_intersection( 88 | "Straße der Nationen", 89 | "Carolastraße", 90 | "3600062594" 91 | ) 92 | 93 | 94 | License 95 | ------- 96 | 97 | Published under the MIT (see LICENSE for more information) 98 | 99 | .. _`documentation`: http://python-overpy.readthedocs.org/ 100 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PythonOverpassAPI.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PythonOverpassAPI.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/PythonOverpassAPI" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PythonOverpassAPI" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PythonOverpassAPI.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PythonOverpassAPI.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | sphinx-autodoc-typehints 3 | -------------------------------------------------------------------------------- /docs/source/_static/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DinoTools/python-overpy/a8acf84e7182e2e315fd3627529b238f8688ee77/docs/source/_static/.keep -------------------------------------------------------------------------------- /docs/source/api.rst: -------------------------------------------------------------------------------- 1 | API Reference 2 | ============= 3 | 4 | .. currentmodule:: overpy 5 | 6 | Overpass API 7 | ------------ 8 | 9 | .. autoclass:: Overpass 10 | :members: 11 | 12 | 13 | Result 14 | ------ 15 | 16 | .. autoclass:: Result 17 | :members: 18 | 19 | 20 | Elements 21 | -------- 22 | 23 | .. autoclass:: Element 24 | :members: 25 | 26 | .. autoclass:: Area 27 | :members: 28 | 29 | .. autoclass:: Node 30 | :members: 31 | 32 | .. autoclass:: Relation 33 | :members: 34 | 35 | .. autoclass:: Way 36 | :members: 37 | 38 | 39 | Relation Members 40 | ---------------- 41 | 42 | .. autoclass:: RelationMember 43 | :members: 44 | 45 | .. autoclass:: RelationArea 46 | :members: 47 | 48 | .. autoclass:: RelationNode 49 | :members: 50 | 51 | .. autoclass:: RelationWay 52 | :members: 53 | 54 | 55 | Exceptions 56 | ---------- 57 | 58 | .. automodule:: overpy.exception 59 | :members: 60 | 61 | 62 | Helper 63 | ------ 64 | 65 | .. automodule:: overpy.helper 66 | :members: 67 | -------------------------------------------------------------------------------- /docs/source/changelog.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../CHANGELOG.rst 2 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # All configuration values have a default; values that are commented out 5 | # serve to show the default. 6 | 7 | import sys 8 | from pathlib import Path 9 | 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | #sys.path.insert(0, os.path.abspath('.')) 15 | sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent)) 16 | 17 | # -- General configuration ------------------------------------------------ 18 | 19 | # If your documentation needs a minimal Sphinx version, state it here. 20 | #needs_sphinx = '1.0' 21 | 22 | # Add any Sphinx extension module names here, as strings. They can be 23 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 24 | # ones. 25 | extensions = [ 26 | 'sphinx.ext.autodoc', 27 | 'sphinx_autodoc_typehints', 28 | 'sphinx.ext.todo', 29 | 'sphinx.ext.coverage', 30 | 'sphinx.ext.viewcode', 31 | ] 32 | 33 | # Add any paths that contain templates here, relative to this directory. 34 | templates_path = ['_templates'] 35 | 36 | # The suffix of source filenames. 37 | source_suffix = '.rst' 38 | 39 | # The encoding of source files. 40 | #source_encoding = 'utf-8-sig' 41 | 42 | # The master toctree document. 43 | master_doc = 'index' 44 | 45 | # General information about the project. 46 | project = 'Python Overpass API' 47 | copyright = '2014, PhiBo' 48 | 49 | # The version info for the project you're documenting, acts as replacement for 50 | # |version| and |release|, also used in various other places throughout the 51 | # built documents. 52 | # 53 | try: 54 | from overpy import __about__ as overpy_about 55 | # The short X.Y version. 56 | version = overpy_about.__version__ 57 | # The full version, including alpha/beta/rc tags. 58 | release = overpy_about.__version__ 59 | except ImportError: 60 | version = "unknown" 61 | release = version 62 | 63 | # The language for content autogenerated by Sphinx. Refer to documentation 64 | # for a list of supported languages. 65 | #language = None 66 | 67 | # There are two options for replacing |today|: either, you set today to some 68 | # non-false value, then it is used: 69 | #today = '' 70 | # Else, today_fmt is used as the format for a strftime call. 71 | #today_fmt = '%B %d, %Y' 72 | 73 | # List of patterns, relative to source directory, that match files and 74 | # directories to ignore when looking for source files. 75 | exclude_patterns = [] 76 | 77 | # The reST default role (used for this markup: `text`) to use for all 78 | # documents. 79 | #default_role = None 80 | 81 | # If true, '()' will be appended to :func: etc. cross-reference text. 82 | #add_function_parentheses = True 83 | 84 | # If true, the current module name will be prepended to all description 85 | # unit titles (such as .. function::). 86 | #add_module_names = True 87 | 88 | # If true, sectionauthor and moduleauthor directives will be shown in the 89 | # output. They are ignored by default. 90 | #show_authors = False 91 | 92 | # The name of the Pygments (syntax highlighting) style to use. 93 | pygments_style = 'sphinx' 94 | 95 | # A list of ignored prefixes for module index sorting. 96 | #modindex_common_prefix = [] 97 | 98 | # If true, keep warnings as "system message" paragraphs in the built documents. 99 | #keep_warnings = False 100 | 101 | # sphinx-autodoc-typehints options 102 | # If True, add stub documentation for undocumented parameters to be able to add type info. 103 | always_document_param_types = True 104 | 105 | # -- Options for HTML output ---------------------------------------------- 106 | 107 | # The theme to use for HTML and HTML Help pages. See the documentation for 108 | # a list of builtin themes. 109 | html_theme = 'default' 110 | 111 | # Theme options are theme-specific and customize the look and feel of a theme 112 | # further. For a list of options available for each theme, see the 113 | # documentation. 114 | #html_theme_options = {} 115 | 116 | # Add any paths that contain custom themes here, relative to this directory. 117 | #html_theme_path = [] 118 | 119 | # The name for this set of Sphinx documents. If None, it defaults to 120 | # " v documentation". 121 | #html_title = None 122 | 123 | # A shorter title for the navigation bar. Default is the same as html_title. 124 | #html_short_title = None 125 | 126 | # The name of an image file (relative to this directory) to place at the top 127 | # of the sidebar. 128 | #html_logo = None 129 | 130 | # The name of an image file (within the static path) to use as favicon of the 131 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 132 | # pixels large. 133 | #html_favicon = None 134 | 135 | # Add any paths that contain custom static files (such as style sheets) here, 136 | # relative to this directory. They are copied after the builtin static files, 137 | # so a file named "default.css" will overwrite the builtin "default.css". 138 | html_static_path = ['_static'] 139 | 140 | # Add any extra paths that contain custom files (such as robots.txt or 141 | # .htaccess) here, relative to this directory. These files are copied 142 | # directly to the root of the documentation. 143 | #html_extra_path = [] 144 | 145 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 146 | # using the given strftime format. 147 | #html_last_updated_fmt = '%b %d, %Y' 148 | 149 | # If true, SmartyPants will be used to convert quotes and dashes to 150 | # typographically correct entities. 151 | #html_use_smartypants = True 152 | 153 | # Custom sidebar templates, maps document names to template names. 154 | #html_sidebars = {} 155 | 156 | # Additional templates that should be rendered to pages, maps page names to 157 | # template names. 158 | #html_additional_pages = {} 159 | 160 | # If false, no module index is generated. 161 | #html_domain_indices = True 162 | 163 | # If false, no index is generated. 164 | #html_use_index = True 165 | 166 | # If true, the index is split into individual pages for each letter. 167 | #html_split_index = False 168 | 169 | # If true, links to the reST sources are added to the pages. 170 | #html_show_sourcelink = True 171 | 172 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 173 | #html_show_sphinx = True 174 | 175 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 176 | #html_show_copyright = True 177 | 178 | # If true, an OpenSearch description file will be output, and all pages will 179 | # contain a tag referring to it. The value of this option must be the 180 | # base URL from which the finished HTML is served. 181 | #html_use_opensearch = '' 182 | 183 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 184 | #html_file_suffix = None 185 | 186 | # Output file base name for HTML help builder. 187 | htmlhelp_basename = 'PythonOverpassAPIdoc' 188 | 189 | 190 | # -- Options for LaTeX output --------------------------------------------- 191 | 192 | latex_elements = { 193 | # The paper size ('letterpaper' or 'a4paper'). 194 | #'papersize': 'letterpaper', 195 | 196 | # The font size ('10pt', '11pt' or '12pt'). 197 | #'pointsize': '10pt', 198 | 199 | # Additional stuff for the LaTeX preamble. 200 | #'preamble': '', 201 | } 202 | 203 | # Grouping the document tree into LaTeX files. List of tuples 204 | # (source start file, target name, title, 205 | # author, documentclass [howto, manual, or own class]). 206 | latex_documents = [ 207 | ('index', 'PythonOverpassAPI.tex', 'Python Overpass API Documentation', 208 | 'PhiBo', 'manual'), 209 | ] 210 | 211 | # The name of an image file (relative to this directory) to place at the top of 212 | # the title page. 213 | #latex_logo = None 214 | 215 | # For "manual" documents, if this is true, then toplevel headings are parts, 216 | # not chapters. 217 | #latex_use_parts = False 218 | 219 | # If true, show page references after internal links. 220 | #latex_show_pagerefs = False 221 | 222 | # If true, show URL addresses after external links. 223 | #latex_show_urls = False 224 | 225 | # Documents to append as an appendix to all manuals. 226 | #latex_appendices = [] 227 | 228 | # If false, no module index is generated. 229 | #latex_domain_indices = True 230 | 231 | 232 | # -- Options for manual page output --------------------------------------- 233 | 234 | # One entry per manual page. List of tuples 235 | # (source start file, name, description, authors, manual section). 236 | man_pages = [ 237 | ('index', 'pythonoverpassapi', 'Python Overpass API Documentation', 238 | ['PhiBo'], 1) 239 | ] 240 | 241 | # If true, show URL addresses after external links. 242 | #man_show_urls = False 243 | 244 | 245 | # -- Options for Texinfo output ------------------------------------------- 246 | 247 | # Grouping the document tree into Texinfo files. List of tuples 248 | # (source start file, target name, title, author, 249 | # dir menu entry, description, category) 250 | texinfo_documents = [ 251 | ('index', 'PythonOverpassAPI', 'Python Overpass API Documentation', 252 | 'PhiBo', 'PythonOverpassAPI', 'One line description of project.', 253 | 'Miscellaneous'), 254 | ] 255 | 256 | # Documents to append as an appendix to all manuals. 257 | #texinfo_appendices = [] 258 | 259 | # If false, no module index is generated. 260 | #texinfo_domain_indices = True 261 | 262 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 263 | #texinfo_show_urls = 'footnote' 264 | 265 | # If true, do not generate a @detailmenu in the "Top" node's menu. 266 | #texinfo_no_detailmenu = False 267 | 268 | 269 | # -- Options for Epub output ---------------------------------------------- 270 | 271 | # Bibliographic Dublin Core info. 272 | epub_title = 'Python Overpass API' 273 | epub_author = 'PhiBo' 274 | epub_publisher = 'PhiBo' 275 | epub_copyright = '2014, PhiBo' 276 | 277 | # The basename for the epub file. It defaults to the project name. 278 | #epub_basename = 'Python Overpass API' 279 | 280 | # The HTML theme for the epub output. Since the default themes are not optimized 281 | # for small screen space, using the same theme for HTML and epub output is 282 | # usually not wise. This defaults to 'epub', a theme designed to save visual 283 | # space. 284 | #epub_theme = 'epub' 285 | 286 | # The language of the text. It defaults to the language option 287 | # or en if the language is not set. 288 | #epub_language = '' 289 | 290 | # The scheme of the identifier. Typical schemes are ISBN or URL. 291 | #epub_scheme = '' 292 | 293 | # The unique identifier of the text. This can be a ISBN number 294 | # or the project homepage. 295 | #epub_identifier = '' 296 | 297 | # A unique identification for the text. 298 | #epub_uid = '' 299 | 300 | # A tuple containing the cover image and cover page html template filenames. 301 | #epub_cover = () 302 | 303 | # A sequence of (type, uri, title) tuples for the guide element of content.opf. 304 | #epub_guide = () 305 | 306 | # HTML files that should be inserted before the pages created by sphinx. 307 | # The format is a list of tuples containing the path and title. 308 | #epub_pre_files = [] 309 | 310 | # HTML files shat should be inserted after the pages created by sphinx. 311 | # The format is a list of tuples containing the path and title. 312 | #epub_post_files = [] 313 | 314 | # A list of files that should not be packed into the epub file. 315 | epub_exclude_files = ['search.html'] 316 | 317 | # The depth of the table of contents in toc.ncx. 318 | #epub_tocdepth = 3 319 | 320 | # Allow duplicate toc entries. 321 | #epub_tocdup = True 322 | 323 | # Choose between 'default' and 'includehidden'. 324 | #epub_tocscope = 'default' 325 | 326 | # Fix unsupported image types using the PIL. 327 | #epub_fix_images = False 328 | 329 | # Scale large images. 330 | #epub_max_image_width = 0 331 | 332 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 333 | #epub_show_urls = 'inline' 334 | 335 | # If false, no index is generated. 336 | #epub_use_index = True 337 | -------------------------------------------------------------------------------- /docs/source/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /docs/source/example.rst: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | Basic example 5 | ------------- 6 | 7 | Lets start with an example from the Overpass API documentation. 8 | 9 | **Query String:** 10 | 11 | .. code-block:: text 12 | :linenos: 13 | 14 | node(50.745,7.17,50.75,7.18); 15 | out; 16 | 17 | **Use OverPy:** 18 | 19 | .. code-block:: pycon 20 | :linenos: 21 | 22 | >>> import overpy 23 | >>> api = overpy.Overpass() 24 | >>> result = api.query("node(50.745,7.17,50.75,7.18);out;") 25 | >>> len(result.nodes) 26 | 1984 27 | >>> len(result.ways) 28 | 0 29 | >>> len(result.relations) 30 | 0 31 | >>> node = result.nodes[2] 32 | >>> node.id 33 | 100792806 34 | >>> node.tags 35 | {} 36 | 37 | Line 1: 38 | Import the required Python module 39 | 40 | Line 2: 41 | Create a new instance of the Overpass() class. 42 | This instance is used to query the Overpass API. 43 | 44 | Line 3: 45 | Use the Query-String from above to query the Overpass API service. 46 | 47 | Line 4,5: 48 | Get the number of nodes in the result set. 49 | 50 | Line 6-9: 51 | Get the number of ways and relations available in the result set. 52 | 53 | Line 10-14: 54 | Get the third node from the list. 55 | Display the ID and the tags of this node. 56 | 57 | 58 | Use Overpass QL or Overpass XML 59 | ------------------------------- 60 | 61 | Queries are passed directly to the Overpass API service without any modification. 62 | So it is possible to use Overpass QL and Overpass XML. 63 | 64 | Overpass QL 65 | ~~~~~~~~~~~ 66 | 67 | **Query:** 68 | 69 | .. code-block:: text 70 | :linenos: 71 | 72 | node["name"="Gielgen"]; 73 | out body; 74 | 75 | 76 | **Use OverPy:** 77 | 78 | .. code-block:: pycon 79 | :linenos: 80 | 81 | >>> import overpy 82 | >>> api = overpy.Overpass() 83 | >>> result = api.query("""node["name"="Gielgen"];out body;""") 84 | >>> len(result.nodes) 85 | 6 86 | >>> len(result.ways) 87 | 0 88 | >>> len(result.relations) 89 | 0 90 | 91 | 92 | Overpass XML 93 | ~~~~~~~~~~~~ 94 | 95 | **Query:** 96 | 97 | .. code-block:: xml 98 | :linenos: 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | **Use OverPy:** 108 | 109 | .. code-block:: pycon 110 | :linenos: 111 | 112 | >>> import overpy 113 | >>> api = overpy.Overpass() 114 | >>> result = api.query(""" 115 | ... 116 | ... 117 | ... 118 | ... 119 | ... """) 120 | >>> len(result.nodes) 121 | 6 122 | >>> len(result.ways) 123 | 0 124 | >>> len(result.relations) 125 | 0 126 | 127 | 128 | Parse JSON or XML responses 129 | --------------------------- 130 | 131 | On a request OverPy detects the content type from the response. 132 | 133 | JSON response 134 | ~~~~~~~~~~~~~ 135 | 136 | **Query String:** 137 | 138 | .. code-block:: text 139 | :linenos: 140 | 141 | [out:json]; 142 | node(50.745,7.17,50.75,7.18); 143 | out; 144 | 145 | **Use OverPy:** 146 | 147 | .. code-block:: pycon 148 | :linenos: 149 | 150 | >>> import overpy 151 | >>> api = overpy.Overpass() 152 | >>> result = api.query("[out:json];node(50.745,7.17,50.75,7.18);out;") 153 | >>> len(result.nodes) 154 | 1984 155 | >>> len(result.ways) 156 | 0 157 | >>> len(result.relations) 158 | 0 159 | 160 | XML response 161 | ~~~~~~~~~~~~ 162 | 163 | **Query String:** 164 | 165 | .. code-block:: text 166 | :linenos: 167 | 168 | [out:xml]; 169 | node(50.745,7.17,50.75,7.18); 170 | out; 171 | 172 | **Use OverPy:** 173 | 174 | .. code-block:: pycon 175 | :linenos: 176 | 177 | >>> import overpy 178 | >>> api = overpy.Overpass() 179 | >>> result = api.query("[out:xml];node(50.745,7.17,50.75,7.18);out;") 180 | >>> len(result.nodes) 181 | 1984 182 | >>> len(result.ways) 183 | 0 184 | >>> len(result.relations) 185 | 0 186 | 187 | Ways 188 | ---- 189 | 190 | Get all nodes of a way 191 | ~~~~~~~~~~~~~~~~~~~~~~ 192 | 193 | In this example the Overpass API will only return the Way elements with the name "Gielgenstraße". 194 | But there will be no Node elements in the result set. 195 | 196 | OverPy provides a way to resolve missing nodes. 197 | 198 | **Query String:** 199 | 200 | .. code-block:: text 201 | :linenos: 202 | 203 | way 204 | ["name"="Gielgenstraße"] 205 | (50.7,7.1,50.8,7.25); 206 | out; 207 | 208 | **Use OverPy:** 209 | 210 | .. code-block:: pycon 211 | :linenos: 212 | 213 | >>> import overpy 214 | >>> api = overpy.Overpass() 215 | >>> result = api.query("""way["name"="Gielgenstraße"](50.7,7.1,50.8,7.25);out;""") 216 | >>> len(result.nodes) 217 | 0 218 | >>> len(result.ways) 219 | 4 220 | >>> way = result.ways[0] 221 | >>> way.nodes 222 | Traceback (most recent call last): 223 | File "", line 1, in 224 | [...] 225 | raise exception.DataIncomplete("Resolve missing nodes is disabled") 226 | overpy.exception.DataIncomplete: ('Data incomplete try to improve the query to resolve the missing data', 'Resolve missing nodes is disabled') 227 | >>> way.get_nodes() 228 | Traceback (most recent call last): 229 | File "", line 1, in 230 | [...] 231 | raise exception.DataIncomplete("Resolve missing nodes is disabled") 232 | overpy.exception.DataIncomplete: ('Data incomplete try to improve the query to resolve the missing data', 'Resolve missing nodes is disabled') 233 | >>> nodes = way.get_nodes(resolve_missing=True) 234 | >>> len(nodes) 235 | 13 236 | >>> len(result.nodes) 237 | 13 238 | >>> len(way.nodes) 239 | 13 240 | 241 | 242 | Line 1-3: 243 | Send a query to the Overpass API service. 244 | 245 | Line 4-6: 246 | There are 4 Way elements and 0 Node elements in the result set. 247 | 248 | Line 7: 249 | Get the first way. 250 | 251 | Line 8-19: 252 | Use :attr:`overpy.Way.nodes` class attribute and the :func:`overpy.Way.get_nodes()` function to get the nodes for the way. 253 | Both raise an exception because the nodes are not in the result set and auto resolving missing nodes is disabled. 254 | 255 | Line 20-21: 256 | Use the :func:`overpy.Way.get_nodes()` function and let OverPy try to resolve the missing nodes. 257 | The function will return all Node elements connected with the Way element. 258 | 259 | Line 22-25: 260 | The resolved nodes have been added to the result set and are available to be used again later. 261 | 262 | Serialization 263 | ------------- 264 | 265 | Result objects can be converted to a dictionary, in the same format as the 266 | Overpass API ``json`` output format. 267 | 268 | .. code-block:: pycon 269 | :linenos: 270 | 271 | >>> import overpy, simplejson 272 | >>> api = overpy.Overpass() 273 | >>> result = api.query("[out:xml];node(50.745,7.17,50.75,7.18);out;") 274 | >>> other_result = overpy.Result.from_json(result.to_json()) 275 | >>> assert other_result != result 276 | >>> assert other_result.to_json() == result.to_json() 277 | >>> assert len(result.nodes) == len(other_result.nodes) 278 | >>> assert len(result.ways) == len(other_result.ways) 279 | 280 | Serializing the dictionary to JSON requires rendering Decimal values as JSON 281 | numbers, and then parsing with ``Overpass.parse_json()``. The third-party 282 | package ``simplejson`` works for this application: 283 | 284 | .. code-block:: pycon 285 | :linenos: 286 | 287 | >>> result_str = simplejson.dumps(result.to_json()) 288 | >>> new_result = api.parse_json(result_str) 289 | >>> assert len(result.nodes) == len(new_result.nodes) 290 | -------------------------------------------------------------------------------- /docs/source/faq.rst: -------------------------------------------------------------------------------- 1 | Frequently Asked Questions 2 | ========================== 3 | 4 | 429 Too Many Requests 5 | --------------------- 6 | 7 | If too many requests are send from the same IP address the server blocks some requests to avoid that a user uses up all resources. 8 | For more information have a look at the `Overpass API documentation `_. 9 | 10 | OverPy tries to fetch missing information automatically. 11 | To limit the number of requests you should try to fetch all required information/data(relations, ways, nodes, tags, ...) with the initial query. 12 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to Python Overpass API's documentation! 2 | =============================================== 3 | 4 | Contents: 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | introduction 10 | example 11 | faq 12 | api 13 | contributing 14 | changelog 15 | 16 | Indices and tables 17 | ================== 18 | 19 | * :ref:`genindex` 20 | * :ref:`modindex` 21 | * :ref:`search` 22 | -------------------------------------------------------------------------------- /docs/source/introduction.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | Requirements 5 | ------------ 6 | 7 | Supported Python versions: 8 | 9 | * Python 3.7+ 10 | * PyPy3 11 | 12 | Installation 13 | ------------ 14 | 15 | As a Python egg 16 | ~~~~~~~~~~~~~~~ 17 | 18 | You can install the most recent version using ``pip`` 19 | 20 | .. code-block:: console 21 | 22 | $ pip install overpy 23 | 24 | 25 | From a tarball release 26 | ~~~~~~~~~~~~~~~~~~~~~~ 27 | 28 | Download the most recent tarball from github, unpack it and run the following command on the command-line. 29 | 30 | .. code-block:: console 31 | 32 | $ python setup.py install 33 | 34 | 35 | Install the development version 36 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 37 | 38 | Install git and run the following commands on the command-line. 39 | 40 | .. code-block:: console 41 | 42 | $ git clone https://github.com/DinoTools/python-overpy.git 43 | $ cd python-overpy 44 | $ python setup.py install 45 | 46 | Usage 47 | ----- 48 | 49 | It is recommended to have a look at the documentation of the `Overpass API`_ before using OverPy. 50 | For more examples have a look at the :doc:`examples page ` or in the examples directory. 51 | 52 | .. code-block:: python 53 | 54 | import overpy 55 | 56 | api = overpy.Overpass() 57 | 58 | # fetch all ways and nodes 59 | result = api.query(""" 60 | way(50.746,7.154,50.748,7.157) ["highway"]; 61 | (._;>;); 62 | out body; 63 | """) 64 | 65 | for way in result.ways: 66 | print("Name: %s" % way.tags.get("name", "n/a")) 67 | print(" Highway: %s" % way.tags.get("highway", "n/a")) 68 | print(" Nodes:") 69 | for node in way.nodes: 70 | print(" Lat: %f, Lon: %f" % (node.lat, node.lon)) 71 | 72 | 73 | .. _Overpass API: https://wiki.openstreetmap.org/wiki/Overpass_API 74 | -------------------------------------------------------------------------------- /examples/get_areas.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import overpy 3 | 4 | api = overpy.Overpass() 5 | 6 | # fetch all areas 7 | # More info on http://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_API_by_Example 8 | result = api.query(""" 9 | area[name="Troisdorf"]; 10 | out; 11 | """) 12 | 13 | for area in result.areas: 14 | print( 15 | "Name: %s (%i)" % ( 16 | area.tags.get("name", "n/a"), 17 | area.id 18 | ) 19 | ) 20 | for n, v in area.tags.items(): 21 | print(f" Tag: {n} = {v}") 22 | -------------------------------------------------------------------------------- /examples/get_nodes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import overpy 3 | 4 | api = overpy.Overpass() 5 | 6 | # We can also see a node's metadata: 7 | jet_deau = 60018172 8 | result = api.query(f"node({jet_deau}); out meta;") 9 | node = result.get_node(jet_deau) 10 | 11 | print(f"The node for the famous Geneva {node.tags['name']} ({node.lat},{node.lon}) was:") 12 | attrs = node.attributes 13 | 14 | print(f"* last modified {attrs['timestamp']}") 15 | print(f"* by {attrs['user']} (uid: {attrs['uid']})") 16 | print(f"* in changeset {attrs['changeset']}") 17 | -------------------------------------------------------------------------------- /examples/get_ways.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import overpy 3 | 4 | api = overpy.Overpass() 5 | 6 | # fetch all ways and nodes 7 | result = api.query(""" 8 | way(50.746,7.154,50.748,7.157) ["highway"]; 9 | (._;>;); 10 | out body; 11 | """) 12 | 13 | for way in result.ways: 14 | print(f"Name: {way.tags.get('name', 'n/a')}") 15 | print(f" Highway: {way.tags.get('highway', 'n/a')}") 16 | print(" Nodes:") 17 | for node in way.nodes: 18 | print(f" Lat: {node.lat:f}, Lon: {node.lon:f}") 19 | -------------------------------------------------------------------------------- /overpy/__about__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "__author__", 3 | "__copyright__", 4 | "__email__", 5 | "__license__", 6 | "__summary__", 7 | "__title__", 8 | "__uri__", 9 | "__version__", 10 | ] 11 | 12 | __title__ = "overpy" 13 | __summary__ = "Python Wrapper to access the OpenStreepMap Overpass API" 14 | __uri__ = "https://github.com/DinoTools/python-overpy" 15 | 16 | __version__ = "0.7" 17 | 18 | __author__ = "PhiBo (DinoTools)" 19 | __email__ = "" 20 | 21 | __license__ = "MIT" 22 | __copyright__ = f"Copyright 2014-2023 {__author__}" 23 | -------------------------------------------------------------------------------- /overpy/exception.py: -------------------------------------------------------------------------------- 1 | class OverPyException(Exception): 2 | """OverPy base exception""" 3 | pass 4 | 5 | 6 | class DataIncomplete(OverPyException): 7 | """ 8 | Raised if the requested data isn't available in the result. 9 | Try to improve the query or to resolve the missing data. 10 | """ 11 | def __init__(self, *args, **kwargs): 12 | OverPyException.__init__( 13 | self, 14 | "Data incomplete try to improve the query to resolve the missing data", 15 | *args, 16 | **kwargs 17 | ) 18 | 19 | 20 | class ElementDataWrongType(OverPyException): 21 | """ 22 | Raised if the provided element does not match the expected type. 23 | 24 | :param type_expected: The expected element type 25 | :type type_expected: String 26 | :param type_provided: The provided element type 27 | :type type_provided: String|None 28 | """ 29 | def __init__(self, type_expected, type_provided=None): 30 | self.type_expected = type_expected 31 | self.type_provided = type_provided 32 | 33 | def __str__(self) -> str: 34 | return f"Type expected {self.type_expected!r} but {self.type_provided!r} provided" 35 | 36 | 37 | class MaxRetriesReached(OverPyException): 38 | """ 39 | Raised if max retries reached and the Overpass server didn't respond with a result. 40 | """ 41 | def __init__(self, retry_count, exceptions): 42 | self.exceptions = exceptions 43 | self.retry_count = retry_count 44 | 45 | def __str__(self) -> str: 46 | return f"Unable get any result from the Overpass API server after {self.retry_count} retries." 47 | 48 | 49 | class OverpassBadRequest(OverPyException): 50 | """ 51 | Raised if the Overpass API service returns a syntax error. 52 | 53 | :param query: The encoded query how it was send to the server 54 | :type query: Bytes 55 | :param msgs: List of error messages 56 | :type msgs: List 57 | """ 58 | def __init__(self, query, msgs=None): 59 | self.query = query 60 | if msgs is None: 61 | msgs = [] 62 | self.msgs = msgs 63 | 64 | def __str__(self) -> str: 65 | tmp_msgs = [] 66 | for tmp_msg in self.msgs: 67 | if not isinstance(tmp_msg, str): 68 | tmp_msg = str(tmp_msg) 69 | tmp_msgs.append(tmp_msg) 70 | 71 | return "\n".join(tmp_msgs) 72 | 73 | 74 | class OverpassError(OverPyException): 75 | """ 76 | Base exception to report errors if the response returns a remark tag or element. 77 | 78 | .. note:: 79 | If you are not sure which of the subexceptions you should use, use this one and try to parse the message. 80 | 81 | For more information have a look at https://github.com/DinoTools/python-overpy/issues/62 82 | 83 | :param str msg: The message from the remark tag or element 84 | """ 85 | def __init__(self, msg=None): 86 | #: The message from the remark tag or element 87 | self.msg = msg 88 | 89 | def __str__(self) -> str: 90 | if self.msg is None: 91 | return "No error message provided" 92 | if not isinstance(self.msg, str): 93 | return str(self.msg) 94 | return self.msg 95 | 96 | 97 | class OverpassGatewayTimeout(OverPyException): 98 | """ 99 | Raised if load of the Overpass API service is too high and it can't handle the request. 100 | """ 101 | def __init__(self): 102 | OverPyException.__init__(self, "Server load too high") 103 | 104 | 105 | class OverpassRuntimeError(OverpassError): 106 | """ 107 | Raised if the server returns a remark-tag(xml) or remark element(json) with a message starting with 108 | 'runtime error:'. 109 | """ 110 | pass 111 | 112 | 113 | class OverpassRuntimeRemark(OverpassError): 114 | """ 115 | Raised if the server returns a remark-tag(xml) or remark element(json) with a message starting with 116 | 'runtime remark:'. 117 | """ 118 | pass 119 | 120 | 121 | class OverpassTooManyRequests(OverPyException): 122 | """ 123 | Raised if the Overpass API service returns a 429 status code. 124 | """ 125 | def __init__(self): 126 | OverPyException.__init__(self, "Too many requests") 127 | 128 | 129 | class OverpassUnknownContentType(OverPyException): 130 | """ 131 | Raised if the reported content type isn't handled by OverPy. 132 | 133 | :param content_type: The reported content type 134 | :type content_type: None or String 135 | """ 136 | def __init__(self, content_type): 137 | self.content_type = content_type 138 | 139 | def __str__(self) -> str: 140 | if self.content_type is None: 141 | return "No content type returned" 142 | return f"Unknown content type: {self.content_type}" 143 | 144 | 145 | class OverpassUnknownError(OverpassError): 146 | """ 147 | Raised if the server returns a remark-tag(xml) or remark element(json) and we are unable to find any reason. 148 | """ 149 | pass 150 | 151 | 152 | class OverpassUnknownHTTPStatusCode(OverPyException): 153 | """ 154 | Raised if the returned HTTP status code isn't handled by OverPy. 155 | 156 | :param code: The HTTP status code 157 | :type code: Integer 158 | """ 159 | def __init__(self, code): 160 | self.code = code 161 | 162 | def __str__(self) -> str: 163 | return f"Unknown/Unhandled status code: {self.code}" 164 | -------------------------------------------------------------------------------- /overpy/helper.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | __author__ = 'mjob' 3 | 4 | import overpy 5 | 6 | 7 | def get_street( 8 | street: str, 9 | areacode: str, 10 | api: Optional[overpy.Overpass] = None) -> overpy.Result: 11 | """ 12 | Retrieve streets in a given bounding area 13 | 14 | :param street: Name of street 15 | :param areacode: The OSM id of the bounding area 16 | :param api: API object to fetch missing elements 17 | :return: Parsed result 18 | :raises overpy.exception.OverPyException: If something bad happens. 19 | """ 20 | if api is None: 21 | api = overpy.Overpass() 22 | 23 | query = f""" 24 | area({areacode})->.location; 25 | ( 26 | way[highway][name="{street}"](area.location); 27 | - ( 28 | way[highway=service](area.location); 29 | way[highway=track](area.location); 30 | ); 31 | ); 32 | out body; 33 | >; 34 | out skel qt; 35 | """ 36 | 37 | data = api.query(query) 38 | 39 | return data 40 | 41 | 42 | def get_intersection( 43 | street1: str, 44 | street2: str, 45 | areacode: str, 46 | api: Optional[overpy.Overpass] = None) -> List[overpy.Node]: 47 | """ 48 | Retrieve intersection of two streets in a given bounding area 49 | 50 | :param street1: Name of first street of intersection 51 | :param street2: Name of second street of intersection 52 | :param areacode: The OSM id of the bounding area 53 | :param api: API object to fetch missing elements 54 | :return: List of intersections 55 | :raises overpy.exception.OverPyException: If something bad happens. 56 | """ 57 | if api is None: 58 | api = overpy.Overpass() 59 | 60 | query = f""" 61 | area({areacode}->.location; 62 | ( 63 | way[highway][name="{street1}"](area.location); node(w)->.n1; 64 | way[highway][name="{street2}"](area.location); node(w)->.n2; 65 | ); 66 | node.n1.n2; 67 | out meta; 68 | """ 69 | 70 | data = api.query(query) 71 | 72 | return data.get_nodes() 73 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | license-file = LICENSE 3 | long_description = file: README.rst 4 | long_description_content_type = text/x-rst 5 | classifiers = 6 | Development Status :: 4 - Beta 7 | License :: OSI Approved :: MIT License 8 | Operating System :: OS Independent 9 | Programming Language :: Python 10 | Programming Language :: Python :: 3 11 | Programming Language :: Python :: 3.7 12 | Programming Language :: Python :: 3.8 13 | Programming Language :: Python :: 3.9 14 | Programming Language :: Python :: Implementation :: CPython 15 | Programming Language :: Python :: Implementation :: PyPy 16 | project_urls = 17 | Documentation = https://python-overpy.readthedocs.io/ 18 | Source = https://github.com/DinoTools/python-overpy 19 | Issue Tracker = https://github.com/DinoTools/python-overpy/issues 20 | 21 | [options] 22 | python_requires = >=3.7 23 | include_package_data = true 24 | zip_safe = false 25 | 26 | [aliases] 27 | test=pytest 28 | 29 | [tool:pytest] 30 | python_files = tests/*.py 31 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pathlib import Path 3 | from setuptools import setup, find_packages 4 | 5 | HERE = Path(__file__).resolve().parent 6 | 7 | about = {} 8 | exec((HERE / "overpy" / "__about__.py").read_text(), about) 9 | 10 | setup( 11 | name=about["__title__"], 12 | version=about["__version__"], 13 | 14 | description=about["__summary__"], 15 | license=about["__license__"], 16 | url=about["__uri__"], 17 | author=about["__author__"], 18 | keywords="OverPy Overpass OSM OpenStreetMap", 19 | install_requires=[], 20 | packages=find_packages(exclude=["*.tests", "*.tests.*", "tests"]), 21 | package_data={ 22 | # "": ["README"], 23 | }, 24 | tests_require=["pytest"], 25 | ) 26 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | Data 2 | ==== 3 | 4 | Queries to get test data. 5 | 6 | Have a look at https://overpass-turbo.eu/ to test the queries. 7 | 8 | area-01 (2016-11-22) 9 | -------------------- 10 | 11 | ``` 12 | area[name="Troisdorf"]; 13 | out; 14 | ``` 15 | 16 | relation-03 (2016-11-23) 17 | ------------------------ 18 | 19 | ``` 20 | (rel["ref"="A 555"];); 21 | out center; 22 | ``` 23 | 24 | relation-04 (2016-11-24) 25 | ------------------------ 26 | 27 | ``` 28 | (rel["ref"="A 555"];); 29 | out geom; 30 | ``` 31 | 32 | way-03.xml (2016-11-22) 33 | ----------------------- 34 | 35 | ``` 36 | (way(225576797);>;); 37 | out meta center; 38 | ``` 39 | 40 | way-04 (2016-11-22) 41 | ------------------- 42 | 43 | ``` 44 | (way(225576797);); 45 | out center; 46 | ``` 47 | 48 | * With empty center information to test exception, this should never happen 49 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import threading 3 | from pathlib import Path 4 | from threading import Lock 5 | 6 | from socketserver import BaseRequestHandler, TCPServer 7 | from http.server import HTTPServer 8 | 9 | TCPServer.allow_reuse_address = True 10 | 11 | HOST = "127.0.0.1" 12 | PORT_START = sys.version_info[0] * 10000 + sys.version_info[1] * 100 13 | 14 | current_port = PORT_START 15 | test_lock = Lock() 16 | 17 | 18 | class OverpyBaseRequestHandler(BaseRequestHandler): 19 | def handle(self): 20 | for data in self.get_response(self): 21 | self.request.send(data) 22 | 23 | @staticmethod 24 | def get_response(): 25 | yield b"" 26 | 27 | 28 | def read_file(filename, mode="r"): 29 | return (Path(__file__).resolve().parent / filename).open(mode).read() 30 | 31 | 32 | def new_server_thread(handle_cls, port=None): 33 | global current_port 34 | if port is None: 35 | test_lock.acquire() 36 | port = current_port 37 | current_port += 1 38 | test_lock.release() 39 | 40 | server = HTTPServer( 41 | (HOST, port), 42 | handle_cls 43 | ) 44 | server_thread = threading.Thread(target=server.serve_forever) 45 | server_thread.daemon = True 46 | server_thread.start() 47 | return ( 48 | f"http://{HOST}:{port}", 49 | server 50 | ) 51 | 52 | 53 | def stop_server_thread(server): 54 | server.shutdown() 55 | server.server_close() 56 | -------------------------------------------------------------------------------- /tests/base_class.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from datetime import datetime 3 | 4 | import pytest 5 | 6 | import overpy 7 | 8 | 9 | class BaseTestAreas: 10 | @staticmethod 11 | def _test_area01(result): 12 | assert len(result.areas) == 4 13 | assert len(result.nodes) == 0 14 | assert len(result.relations) == 0 15 | assert len(result.ways) == 0 16 | 17 | area = result.areas[0] 18 | 19 | assert isinstance(area, overpy.Area) 20 | assert isinstance(area.id, int) 21 | assert area.id == 2448756446 22 | 23 | assert isinstance(area.tags, dict) 24 | assert len(area.tags) == 12 25 | 26 | area = result.areas[1] 27 | 28 | assert isinstance(area, overpy.Area) 29 | assert isinstance(area.id, int) 30 | assert area.id == 3600055060 31 | 32 | assert isinstance(area.tags, dict) 33 | assert len(area.tags) == 13 34 | 35 | area = result.areas[2] 36 | assert isinstance(area, overpy.Area) 37 | assert isinstance(area.id, int) 38 | assert area.id == 3605945175 39 | 40 | assert isinstance(area.tags, dict) 41 | assert len(area.tags) == 12 42 | 43 | area = result.areas[3] 44 | assert isinstance(area, overpy.Area) 45 | assert isinstance(area.id, int) 46 | assert area.id == 3605945176 47 | 48 | assert isinstance(area.tags, dict) 49 | assert len(area.tags) == 14 50 | 51 | # try to get a single area by id 52 | area = result.get_area(3605945175) 53 | assert area.id == 3605945175 54 | 55 | # try to get a single area by id not available in the result 56 | with pytest.raises(overpy.exception.DataIncomplete): 57 | result.get_area(123456) 58 | 59 | # area_ids is an alias for get_node_ids() and should return the same data 60 | for area_ids in (result.area_ids, result.get_area_ids()): 61 | assert len(area_ids) == 4 62 | assert area_ids[0] == 2448756446 63 | assert area_ids[1] == 3600055060 64 | assert area_ids[2] == 3605945175 65 | assert area_ids[3] == 3605945176 66 | 67 | assert len(result.node_ids) == 0 68 | assert len(result.get_node_ids()) == 0 69 | 70 | assert len(result.relation_ids) == 0 71 | assert len(result.get_relation_ids()) == 0 72 | 73 | assert len(result.way_ids) == 0 74 | assert len(result.get_way_ids()) == 0 75 | 76 | 77 | class BaseTestNodes: 78 | @staticmethod 79 | def _test_node01(result): 80 | assert len(result.nodes) == 3 81 | assert len(result.relations) == 0 82 | assert len(result.ways) == 0 83 | 84 | node = result.nodes[0] 85 | 86 | assert isinstance(node, overpy.Node) 87 | assert isinstance(node.id, int) 88 | assert isinstance(node.lat, Decimal) 89 | assert isinstance(node.lon, Decimal) 90 | assert node.id == 50878400 91 | assert node.lat == Decimal("50.7461788") 92 | assert node.lon == Decimal("7.1742257") 93 | 94 | assert isinstance(node.tags, dict) 95 | assert len(node.tags) == 0 96 | 97 | node = result.nodes[1] 98 | 99 | assert isinstance(node, overpy.Node) 100 | assert isinstance(node.id, int) 101 | assert isinstance(node.lat, Decimal) 102 | assert isinstance(node.lon, Decimal) 103 | assert node.id == 100793192 104 | assert node.lat == Decimal("50.7468472") 105 | assert node.lon == Decimal("7.1709376") 106 | 107 | assert isinstance(node.tags, dict) 108 | assert len(node.tags) == 1 109 | 110 | assert node.tags["highway"] == "turning_circle" 111 | 112 | node = result.nodes[2] 113 | assert isinstance(node, overpy.Node) 114 | assert isinstance(node.id, int) 115 | assert isinstance(node.lat, Decimal) 116 | assert isinstance(node.lon, Decimal) 117 | assert node.id == 3233854234 118 | assert node.lat == Decimal("50.7494236") 119 | assert node.lon == Decimal("7.1757664") 120 | 121 | assert isinstance(node.attributes, dict) 122 | assert len(node.attributes) == 5 123 | 124 | assert node.attributes["changeset"] == 23456789 125 | assert node.attributes["timestamp"] == datetime(2014, 12, 14, 7, 27, 19, 0, None) 126 | assert node.attributes["uid"] == 345678 127 | assert node.attributes["user"] == "TestUser" 128 | assert node.attributes["version"] == 1 129 | 130 | # try to get a single node by id 131 | node = result.get_node(50878400) 132 | assert node.id == 50878400 133 | 134 | # try to get a single node by id not available in the result 135 | with pytest.raises(overpy.exception.DataIncomplete): 136 | result.get_node(123456) 137 | 138 | # node_ids is an alias for get_node_ids() and should return the same data 139 | for node_ids in (result.node_ids, result.get_node_ids()): 140 | assert len(node_ids) == 3 141 | assert node_ids[0] == 50878400 142 | assert node_ids[1] == 100793192 143 | assert node_ids[2] == 3233854234 144 | 145 | assert len(result.relation_ids) == 0 146 | assert len(result.get_relation_ids()) == 0 147 | 148 | assert len(result.way_ids) == 0 149 | assert len(result.get_way_ids()) == 0 150 | 151 | 152 | class BaseTestRelation: 153 | @staticmethod 154 | def _test_relation01(result): 155 | assert len(result.nodes) == 0 156 | assert len(result.relations) == 1 157 | assert len(result.ways) == 0 158 | 159 | relation = result.relations[0] 160 | 161 | assert isinstance(relation, overpy.Relation) 162 | assert isinstance(relation.id, int) 163 | assert relation.id == 2046898 164 | 165 | assert isinstance(relation.tags, dict) 166 | assert len(relation.tags) == 6 167 | assert relation.tags["from"] == "Here" 168 | assert relation.tags["name"] == "Test relation" 169 | assert relation.tags["ref"] == "609" 170 | assert relation.tags["route"] == "bus" 171 | assert relation.tags["to"] == "There" 172 | assert relation.tags["type"] == "route" 173 | 174 | assert isinstance(relation.attributes, dict) 175 | assert len(relation.attributes) == 5 176 | 177 | assert relation.attributes["changeset"] == 17433822 178 | assert relation.attributes["timestamp"] == datetime(2014, 12, 15, 13, 13, 11, 0, None) 179 | assert relation.attributes["uid"] == 12345 180 | assert relation.attributes["user"] == "Username" 181 | assert relation.attributes["version"] == 12 182 | 183 | assert len(relation.members) == 5 184 | assert isinstance(relation.members[0], overpy.RelationNode) 185 | assert isinstance(relation.members[1], overpy.RelationNode) 186 | assert isinstance(relation.members[2], overpy.RelationNode) 187 | assert isinstance(relation.members[3], overpy.RelationNode) 188 | assert isinstance(relation.members[4], overpy.RelationWay) 189 | 190 | @staticmethod 191 | def _test_relation02(result): 192 | assert len(result.nodes) == 3 193 | assert len(result.relations) == 1 194 | assert len(result.ways) == 1 195 | 196 | relation = result.relations[0] 197 | 198 | assert isinstance(relation, overpy.Relation) 199 | assert isinstance(relation.id, int) 200 | assert relation.id == 2046898 201 | 202 | assert isinstance(relation.tags, dict) 203 | assert len(relation.tags) == 6 204 | assert relation.tags["from"] == "Here" 205 | assert relation.tags["name"] == "Test relation" 206 | assert relation.tags["ref"] == "609" 207 | assert relation.tags["route"] == "bus" 208 | assert relation.tags["to"] == "There" 209 | assert relation.tags["type"] == "route" 210 | 211 | assert isinstance(relation.attributes, dict) 212 | assert len(relation.attributes) == 5 213 | 214 | assert len(relation.members) == 4 215 | 216 | member = relation.members[0] 217 | assert isinstance(member, overpy.RelationNode) 218 | node = member.resolve() 219 | assert isinstance(node, overpy.Node) 220 | assert node.id == 3233854233 221 | assert member.ref == node.id 222 | 223 | member = relation.members[1] 224 | assert isinstance(member, overpy.RelationNode) 225 | node = member.resolve() 226 | assert isinstance(node, overpy.Node) 227 | assert node.id == 3233854234 228 | assert member.ref == node.id 229 | 230 | member = relation.members[2] 231 | assert isinstance(member, overpy.RelationNode) 232 | node = member.resolve() 233 | assert isinstance(node, overpy.Node) 234 | assert node.id == 3233854235 235 | assert member.ref == node.id 236 | 237 | member = relation.members[3] 238 | assert isinstance(member, overpy.RelationWay) 239 | way = member.resolve() 240 | assert isinstance(way, overpy.Way) 241 | assert way.id == 317146078 242 | assert member.ref == way.id 243 | 244 | @staticmethod 245 | def _test_relation03(result): 246 | assert len(result.nodes) == 0 247 | assert len(result.relations) == 1 248 | assert len(result.ways) == 0 249 | 250 | relation = result.relations[0] 251 | 252 | assert isinstance(relation, overpy.Relation) 253 | assert isinstance(relation.id, int) 254 | assert relation.id == 23092 255 | 256 | assert isinstance(relation.tags, dict) 257 | assert len(relation.tags) == 10 258 | 259 | assert isinstance(relation.center_lat, Decimal) 260 | assert isinstance(relation.center_lon, Decimal) 261 | assert relation.center_lat == Decimal("50.8176646") 262 | assert relation.center_lon == Decimal("7.0208539") 263 | 264 | @staticmethod 265 | def _test_relation04(result): 266 | assert len(result.nodes) == 0 267 | assert len(result.relations) == 1 268 | assert len(result.ways) == 0 269 | 270 | relation = result.relations[0] 271 | 272 | assert isinstance(relation, overpy.Relation) 273 | assert isinstance(relation.id, int) 274 | assert relation.id == 23092 275 | 276 | assert isinstance(relation.tags, dict) 277 | assert len(relation.tags) == 10 278 | 279 | way = relation.members[2] 280 | 281 | assert isinstance(way, overpy.RelationWay) 282 | assert len(way.attributes) == 0 283 | assert isinstance(way.attributes, dict) 284 | 285 | assert isinstance(way.geometry, list) 286 | assert len(way.geometry) == 2 287 | assert isinstance(way.geometry[0], overpy.RelationWayGeometryValue) 288 | assert isinstance(way.geometry[0].lat, Decimal) 289 | assert isinstance(way.geometry[0].lon, Decimal) 290 | assert way.geometry[0].lat == Decimal("50.8137408") 291 | assert way.geometry[0].lon == Decimal("6.9813352") 292 | 293 | 294 | class BaseTestWay: 295 | @staticmethod 296 | def _test_way01(result): 297 | assert len(result.nodes) == 0 298 | assert len(result.relations) == 0 299 | assert len(result.ways) == 2 300 | 301 | way = result.ways[0] 302 | 303 | assert isinstance(way, overpy.Way) 304 | assert isinstance(way.id, int) 305 | assert way.id == 317146077 306 | 307 | assert isinstance(way.tags, dict) 308 | assert len(way.tags) == 1 309 | assert way.tags["building"] == "yes" 310 | 311 | assert isinstance(way.attributes, dict) 312 | assert len(way.attributes) == 0 313 | 314 | way = result.ways[1] 315 | 316 | assert isinstance(way, overpy.Way) 317 | assert isinstance(way.id, int) 318 | assert way.id == 317146078 319 | 320 | assert isinstance(way.tags, dict) 321 | assert len(way.tags) == 0 322 | 323 | assert isinstance(way.attributes, dict) 324 | assert len(way.attributes) == 5 325 | 326 | assert way.attributes["changeset"] == 23456789 327 | assert way.attributes["timestamp"] == datetime(2014, 12, 14, 7, 27, 21, 0, None) 328 | assert way.attributes["uid"] == 345678 329 | assert way.attributes["user"] == "TestUser" 330 | assert way.attributes["version"] == 1 331 | 332 | # try to get a single way by id 333 | way = result.get_way(317146077) 334 | assert way.id == 317146077 335 | 336 | # try to get a single way by id not available in the result 337 | with pytest.raises(overpy.exception.DataIncomplete): 338 | result.get_way(123456) 339 | 340 | assert len(result.node_ids) == 0 341 | assert len(result.get_node_ids()) == 0 342 | 343 | assert len(result.relation_ids) == 0 344 | assert len(result.get_relation_ids()) == 0 345 | 346 | # way_ids is an alias for get_way_ids() and should return the same data 347 | for way_ids in (result.way_ids, result.get_way_ids()): 348 | assert len(way_ids) == 2 349 | assert way_ids[0] == 317146077 350 | assert way_ids[1] == 317146078 351 | 352 | @staticmethod 353 | def _test_way02(result): 354 | assert len(result.nodes) == 6 355 | assert len(result.relations) == 0 356 | assert len(result.ways) == 1 357 | 358 | node = result.nodes[0] 359 | 360 | assert isinstance(node, overpy.Node) 361 | assert isinstance(node.id, int) 362 | assert isinstance(node.lat, Decimal) 363 | assert isinstance(node.lon, Decimal) 364 | assert node.id == 3233854233 365 | assert node.lat == Decimal("50.7494187") 366 | assert node.lon == Decimal("7.1758731") 367 | 368 | way = result.ways[0] 369 | 370 | assert isinstance(way, overpy.Way) 371 | assert isinstance(way.id, int) 372 | assert way.id == 317146077 373 | 374 | assert isinstance(way.tags, dict) 375 | assert len(way.tags) == 1 376 | assert way.tags["building"] == "yes" 377 | 378 | nodes = way.nodes 379 | 380 | assert len(nodes) == 7 381 | 382 | node = nodes[0] 383 | 384 | assert isinstance(node, overpy.Node) 385 | assert node.id == 3233854241 386 | 387 | # try to get a single way by id 388 | way = result.get_way(317146077) 389 | assert way.id == 317146077 390 | 391 | # try to get a single way by id not available in the result 392 | with pytest.raises(overpy.exception.DataIncomplete): 393 | result.get_way(123456) 394 | 395 | # node_ids is an alias for get_node_ids() and should return the same data 396 | for node_ids in (result.node_ids, result.get_node_ids()): 397 | assert len(node_ids) == 6 398 | assert node_ids[0] == 3233854233 399 | assert node_ids[1] == 3233854234 400 | assert node_ids[2] == 3233854236 401 | assert node_ids[3] == 3233854237 402 | assert node_ids[4] == 3233854238 403 | assert node_ids[5] == 3233854241 404 | 405 | assert len(result.relation_ids) == 0 406 | assert len(result.get_relation_ids()) == 0 407 | 408 | # way_ids is an alias for get_way_ids() and should return the same data 409 | for way_ids in (result.way_ids, result.get_way_ids()): 410 | assert len(way_ids) == 1 411 | assert way_ids[0] == 317146077 412 | 413 | @staticmethod 414 | def _test_way03(result): 415 | assert len(result.nodes) == 4 416 | assert len(result.relations) == 0 417 | assert len(result.ways) == 1 418 | 419 | way = result.ways[0] 420 | 421 | assert isinstance(way, overpy.Way) 422 | assert isinstance(way.id, int) 423 | assert way.id == 225576797 424 | 425 | assert isinstance(way.tags, dict) 426 | assert len(way.tags) == 2 427 | assert way.tags["building"] == "kiosk" 428 | assert way.tags["shop"] == "florist" 429 | 430 | assert isinstance(way.center_lat, Decimal) 431 | assert isinstance(way.center_lon, Decimal) 432 | assert way.center_lat == Decimal("41.8954998") 433 | assert way.center_lon == Decimal("12.5032265") 434 | 435 | for nodes in (way.nodes, way.get_nodes()): 436 | assert len(nodes) == 5 437 | for node in nodes: 438 | assert isinstance(node, overpy.Node) 439 | assert isinstance(node.id, int) 440 | 441 | assert nodes[0].id == 2343425525 442 | assert nodes[1].id == 2343425528 443 | assert nodes[2].id == 2343425526 444 | assert nodes[3].id == 2343425523 445 | assert nodes[4].id == 2343425525 446 | 447 | # try to get a single way by id 448 | way = result.get_way(225576797) 449 | assert way.id == 225576797 450 | 451 | # try to get a single way by id not available in the result 452 | with pytest.raises(overpy.exception.DataIncomplete): 453 | result.get_way(123456) 454 | 455 | # node_ids is an alias for get_node_ids() and should return the same data 456 | for node_ids in (result.node_ids, result.get_node_ids()): 457 | assert len(node_ids) == 4 458 | assert node_ids[0] == 2343425523 459 | assert node_ids[1] == 2343425525 460 | assert node_ids[2] == 2343425526 461 | assert node_ids[3] == 2343425528 462 | 463 | assert len(result.relation_ids) == 0 464 | assert len(result.get_relation_ids()) == 0 465 | 466 | # way_ids is an alias for get_way_ids() and should return the same data 467 | for way_ids in (result.way_ids, result.get_way_ids()): 468 | assert len(way_ids) == 1 469 | assert way_ids[0] == 225576797 470 | -------------------------------------------------------------------------------- /tests/json/area-01.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2016-11-22T21:04:02Z", 6 | "timestamp_areas_base": "2016-11-22T20:25:03Z", 7 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 8 | }, 9 | "elements": [ 10 | 11 | { 12 | "type": "area", 13 | "id": 2448756446, 14 | "tags": { 15 | "addr:city": "Troisdorf", 16 | "addr:postcode": "53840", 17 | "area": "yes", 18 | "description": "Troisdorf Bahnsteig Gleis 9", 19 | "name": "Troisdorf", 20 | "public_transport": "platform", 21 | "railway": "platform", 22 | "ref": "9", 23 | "train": "yes", 24 | "wheelchair": "no", 25 | "wheelchair:description": "Plattformlift ist vorhanden, Betriebsbereitschaft nach 8 Jahren stillstand fraglich.", 26 | "width": "5" 27 | } 28 | } 29 | , 30 | { 31 | "type": "area", 32 | "id": 3600055060, 33 | "tags": { 34 | "TMC:cid_58:tabcd_1:Class": "Area", 35 | "TMC:cid_58:tabcd_1:LCLversion": "8.00", 36 | "TMC:cid_58:tabcd_1:LocationCode": "2550", 37 | "admin_level": "8", 38 | "boundary": "administrative", 39 | "de:amtlicher_gemeindeschluessel": "05382068", 40 | "de:place": "town", 41 | "de:regionalschluessel": "053820068068", 42 | "name": "Troisdorf", 43 | "name:prefix": "Stadt", 44 | "type": "boundary", 45 | "wikidata": "Q3900", 46 | "wikipedia": "de:Troisdorf" 47 | } 48 | } 49 | , 50 | { 51 | "type": "area", 52 | "id": 3605945175, 53 | "tags": { 54 | "addr:city": "Troisdorf", 55 | "addr:postcode": "53840", 56 | "description": "Troisdorf Bahnsteig Gleis 1+2", 57 | "local_ref": "1;2", 58 | "name": "Troisdorf", 59 | "public_transport": "platform", 60 | "railway": "platform", 61 | "ref": "1/2", 62 | "tactile_paving": "yes", 63 | "train": "yes", 64 | "type": "multipolygon", 65 | "wheelchair": "yes" 66 | } 67 | } 68 | , 69 | { 70 | "type": "area", 71 | "id": 3605945176, 72 | "tags": { 73 | "addr:city": "Troisdorf", 74 | "addr:postcode": "53840", 75 | "description": "Troisdorf Bahnsteig Gleis 5+6", 76 | "name": "Troisdorf", 77 | "phone": "+49 221 1411055", 78 | "public_transport": "platform", 79 | "railway": "platform", 80 | "ref": "5/6", 81 | "tactile_paving": "yes", 82 | "train": "yes", 83 | "type": "multipolygon", 84 | "wheelchair": "yes", 85 | "wheelchair:description": "Der Aufzug zu diesem Bahnsteig ist oft defekt, bitte informieren. Betriebszustand kann bei 3S-Zentrale erfragt werden (siehe Telefonnummer, aber Vorsicht, die sind nicht immer informiert!)", 86 | "width": "6" 87 | } 88 | } 89 | 90 | 91 | ] 92 | } 93 | -------------------------------------------------------------------------------- /tests/json/node-01.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2014-12-14T12:58:02Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | 10 | { 11 | "type": "node", 12 | "id": 50878400, 13 | "lat": 50.7461788, 14 | "lon": 7.1742257 15 | }, 16 | { 17 | "type": "node", 18 | "id": 100793192, 19 | "lat": 50.7468472, 20 | "lon": 7.1709376, 21 | "tags": { 22 | "highway": "turning_circle" 23 | } 24 | }, 25 | { 26 | "type": "node", 27 | "id": 3233854234, 28 | "lat": 50.7494236, 29 | "lon": 7.1757664, 30 | "timestamp": "2014-12-14T07:27:19Z", 31 | "version": 1, 32 | "changeset": 23456789, 33 | "user": "TestUser", 34 | "uid": 345678 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /tests/json/relation-01.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2014-12-21T20:00:03Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | { 10 | "type": "relation", 11 | "id": 2046898, 12 | "timestamp": "2014-12-15T13:13:11Z", 13 | "version": 12, 14 | "changeset": 17433822, 15 | "user": "Username", 16 | "uid": 12345, 17 | "members": [ 18 | { 19 | "type": "node", 20 | "ref": 507464632, 21 | "role": "platform" 22 | }, 23 | { 24 | "type": "node", 25 | "ref": 2252681768, 26 | "role": "stop" 27 | }, 28 | { 29 | "type": "node", 30 | "ref": 507464636, 31 | "role": "platform" 32 | }, 33 | { 34 | "type": "node", 35 | "ref": 1620886108, 36 | "role": "stop" 37 | }, 38 | { 39 | "type": "way", 40 | "ref": 4893348, 41 | "role": "" 42 | } 43 | ], 44 | "tags": { 45 | "from": "Here", 46 | "name": "Test relation", 47 | "ref": "609", 48 | "route": "bus", 49 | "to": "There", 50 | "type": "route" 51 | } 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /tests/json/relation-02.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2014-12-14T13:34:02Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | { 10 | "type": "node", 11 | "id": 3233854233, 12 | "lat": 50.7494187, 13 | "lon": 7.1758731 14 | }, 15 | { 16 | "type": "node", 17 | "id": 3233854234, 18 | "lat": 50.7494287, 19 | "lon": 7.1758731 20 | }, 21 | { 22 | "type": "node", 23 | "id": 3233854235, 24 | "lat": 50.7494287, 25 | "lon": 7.1758731 26 | }, 27 | { 28 | "type": "way", 29 | "id": 317146078, 30 | "nodes": [ 31 | 3233854233, 32 | 3233854234, 33 | 3233854235 34 | ] 35 | }, 36 | { 37 | "type": "relation", 38 | "id": 2046898, 39 | "timestamp": "2014-12-15T13:13:11Z", 40 | "version": 12, 41 | "changeset": 17433822, 42 | "user": "Username", 43 | "uid": 12345, 44 | "members": [ 45 | { 46 | "type": "node", 47 | "ref": 3233854233, 48 | "role": "platform" 49 | }, 50 | { 51 | "type": "node", 52 | "ref": 3233854234, 53 | "role": "stop" 54 | }, 55 | { 56 | "type": "node", 57 | "ref": 3233854235, 58 | "role": "platform" 59 | }, 60 | { 61 | "type": "way", 62 | "ref": 317146078, 63 | "role": "" 64 | } 65 | ], 66 | "tags": { 67 | "from": "Here", 68 | "name": "Test relation", 69 | "ref": "609", 70 | "route": "bus", 71 | "to": "There", 72 | "type": "route" 73 | } 74 | } 75 | ] 76 | } -------------------------------------------------------------------------------- /tests/json/relation-03.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2016-11-23T20:28:03Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | 10 | { 11 | "type": "relation", 12 | "id": 23092, 13 | "center": { 14 | "lat": 50.8176646, 15 | "lon": 7.0208539 16 | }, 17 | "members": [ 18 | { 19 | "type": "way", 20 | "ref": 4334856, 21 | "role": "" 22 | }, 23 | { 24 | "type": "way", 25 | "ref": 234434903, 26 | "role": "" 27 | }, 28 | { 29 | "type": "way", 30 | "ref": 37881522, 31 | "role": "" 32 | }, 33 | { 34 | "type": "way", 35 | "ref": 37881521, 36 | "role": "" 37 | }, 38 | { 39 | "type": "way", 40 | "ref": 4356491, 41 | "role": "" 42 | }, 43 | { 44 | "type": "way", 45 | "ref": 235363211, 46 | "role": "" 47 | }, 48 | { 49 | "type": "way", 50 | "ref": 48290877, 51 | "role": "" 52 | }, 53 | { 54 | "type": "way", 55 | "ref": 210683519, 56 | "role": "" 57 | }, 58 | { 59 | "type": "way", 60 | "ref": 42743961, 61 | "role": "" 62 | }, 63 | { 64 | "type": "way", 65 | "ref": 210683522, 66 | "role": "" 67 | }, 68 | { 69 | "type": "way", 70 | "ref": 48290881, 71 | "role": "" 72 | }, 73 | { 74 | "type": "way", 75 | "ref": 235363214, 76 | "role": "" 77 | }, 78 | { 79 | "type": "way", 80 | "ref": 4400137, 81 | "role": "" 82 | }, 83 | { 84 | "type": "way", 85 | "ref": 230940375, 86 | "role": "" 87 | }, 88 | { 89 | "type": "way", 90 | "ref": 4400140, 91 | "role": "" 92 | }, 93 | { 94 | "type": "way", 95 | "ref": 4400142, 96 | "role": "" 97 | }, 98 | { 99 | "type": "way", 100 | "ref": 235824476, 101 | "role": "" 102 | }, 103 | { 104 | "type": "way", 105 | "ref": 4400143, 106 | "role": "" 107 | }, 108 | { 109 | "type": "way", 110 | "ref": 235824475, 111 | "role": "" 112 | }, 113 | { 114 | "type": "way", 115 | "ref": 178797123, 116 | "role": "" 117 | }, 118 | { 119 | "type": "way", 120 | "ref": 233553030, 121 | "role": "" 122 | }, 123 | { 124 | "type": "way", 125 | "ref": 4829989, 126 | "role": "" 127 | }, 128 | { 129 | "type": "way", 130 | "ref": 99834112, 131 | "role": "" 132 | }, 133 | { 134 | "type": "way", 135 | "ref": 133081219, 136 | "role": "" 137 | }, 138 | { 139 | "type": "way", 140 | "ref": 234434905, 141 | "role": "" 142 | }, 143 | { 144 | "type": "way", 145 | "ref": 20661567, 146 | "role": "" 147 | }, 148 | { 149 | "type": "way", 150 | "ref": 230941900, 151 | "role": "" 152 | }, 153 | { 154 | "type": "way", 155 | "ref": 20661568, 156 | "role": "" 157 | }, 158 | { 159 | "type": "way", 160 | "ref": 20661572, 161 | "role": "" 162 | }, 163 | { 164 | "type": "way", 165 | "ref": 230940374, 166 | "role": "" 167 | }, 168 | { 169 | "type": "way", 170 | "ref": 20661573, 171 | "role": "" 172 | }, 173 | { 174 | "type": "way", 175 | "ref": 20662817, 176 | "role": "" 177 | }, 178 | { 179 | "type": "way", 180 | "ref": 230941903, 181 | "role": "" 182 | }, 183 | { 184 | "type": "way", 185 | "ref": 20662819, 186 | "role": "" 187 | }, 188 | { 189 | "type": "way", 190 | "ref": 20662824, 191 | "role": "" 192 | }, 193 | { 194 | "type": "way", 195 | "ref": 20662827, 196 | "role": "" 197 | }, 198 | { 199 | "type": "way", 200 | "ref": 27492515, 201 | "role": "" 202 | }, 203 | { 204 | "type": "way", 205 | "ref": 129125122, 206 | "role": "" 207 | }, 208 | { 209 | "type": "way", 210 | "ref": 234434901, 211 | "role": "" 212 | }, 213 | { 214 | "type": "way", 215 | "ref": 234434904, 216 | "role": "" 217 | }, 218 | { 219 | "type": "way", 220 | "ref": 27492542, 221 | "role": "" 222 | }, 223 | { 224 | "type": "way", 225 | "ref": 27492543, 226 | "role": "" 227 | }, 228 | { 229 | "type": "way", 230 | "ref": 29233646, 231 | "role": "" 232 | }, 233 | { 234 | "type": "way", 235 | "ref": 235820561, 236 | "role": "" 237 | }, 238 | { 239 | "type": "way", 240 | "ref": 29233647, 241 | "role": "" 242 | }, 243 | { 244 | "type": "way", 245 | "ref": 235820559, 246 | "role": "" 247 | }, 248 | { 249 | "type": "way", 250 | "ref": 235820562, 251 | "role": "" 252 | }, 253 | { 254 | "type": "way", 255 | "ref": 29233648, 256 | "role": "" 257 | }, 258 | { 259 | "type": "way", 260 | "ref": 29382960, 261 | "role": "" 262 | }, 263 | { 264 | "type": "way", 265 | "ref": 271008229, 266 | "role": "" 267 | }, 268 | { 269 | "type": "way", 270 | "ref": 271008238, 271 | "role": "" 272 | }, 273 | { 274 | "type": "way", 275 | "ref": 271008230, 276 | "role": "" 277 | }, 278 | { 279 | "type": "way", 280 | "ref": 271008226, 281 | "role": "" 282 | }, 283 | { 284 | "type": "way", 285 | "ref": 40033062, 286 | "role": "" 287 | }, 288 | { 289 | "type": "way", 290 | "ref": 40033063, 291 | "role": "" 292 | }, 293 | { 294 | "type": "way", 295 | "ref": 29384561, 296 | "role": "" 297 | }, 298 | { 299 | "type": "way", 300 | "ref": 29384563, 301 | "role": "" 302 | }, 303 | { 304 | "type": "way", 305 | "ref": 271008224, 306 | "role": "" 307 | }, 308 | { 309 | "type": "way", 310 | "ref": 271008227, 311 | "role": "" 312 | }, 313 | { 314 | "type": "way", 315 | "ref": 271008237, 316 | "role": "" 317 | }, 318 | { 319 | "type": "way", 320 | "ref": 271008232, 321 | "role": "" 322 | }, 323 | { 324 | "type": "way", 325 | "ref": 271008239, 326 | "role": "" 327 | }, 328 | { 329 | "type": "way", 330 | "ref": 29384564, 331 | "role": "" 332 | }, 333 | { 334 | "type": "way", 335 | "ref": 31282638, 336 | "role": "" 337 | }, 338 | { 339 | "type": "way", 340 | "ref": 31282787, 341 | "role": "" 342 | }, 343 | { 344 | "type": "way", 345 | "ref": 383262762, 346 | "role": "" 347 | }, 348 | { 349 | "type": "way", 350 | "ref": 31304443, 351 | "role": "" 352 | }, 353 | { 354 | "type": "way", 355 | "ref": 128541594, 356 | "role": "" 357 | }, 358 | { 359 | "type": "way", 360 | "ref": 31304503, 361 | "role": "" 362 | }, 363 | { 364 | "type": "way", 365 | "ref": 31363881, 366 | "role": "" 367 | }, 368 | { 369 | "type": "way", 370 | "ref": 31363891, 371 | "role": "" 372 | }, 373 | { 374 | "type": "way", 375 | "ref": 31363892, 376 | "role": "" 377 | }, 378 | { 379 | "type": "way", 380 | "ref": 31363898, 381 | "role": "" 382 | }, 383 | { 384 | "type": "way", 385 | "ref": 31363910, 386 | "role": "" 387 | }, 388 | { 389 | "type": "way", 390 | "ref": 31364152, 391 | "role": "" 392 | }, 393 | { 394 | "type": "way", 395 | "ref": 31364158, 396 | "role": "" 397 | }, 398 | { 399 | "type": "way", 400 | "ref": 31364178, 401 | "role": "" 402 | }, 403 | { 404 | "type": "way", 405 | "ref": 31364384, 406 | "role": "" 407 | }, 408 | { 409 | "type": "way", 410 | "ref": 31364387, 411 | "role": "" 412 | }, 413 | { 414 | "type": "way", 415 | "ref": 31364484, 416 | "role": "" 417 | }, 418 | { 419 | "type": "way", 420 | "ref": 31364512, 421 | "role": "" 422 | }, 423 | { 424 | "type": "way", 425 | "ref": 31364656, 426 | "role": "" 427 | }, 428 | { 429 | "type": "way", 430 | "ref": 31364667, 431 | "role": "" 432 | }, 433 | { 434 | "type": "way", 435 | "ref": 271008234, 436 | "role": "" 437 | }, 438 | { 439 | "type": "way", 440 | "ref": 31364888, 441 | "role": "" 442 | }, 443 | { 444 | "type": "way", 445 | "ref": 122158730, 446 | "role": "" 447 | }, 448 | { 449 | "type": "way", 450 | "ref": 31364985, 451 | "role": "" 452 | }, 453 | { 454 | "type": "way", 455 | "ref": 271008235, 456 | "role": "" 457 | }, 458 | { 459 | "type": "way", 460 | "ref": 271008231, 461 | "role": "" 462 | }, 463 | { 464 | "type": "way", 465 | "ref": 271008233, 466 | "role": "" 467 | }, 468 | { 469 | "type": "way", 470 | "ref": 31365377, 471 | "role": "" 472 | }, 473 | { 474 | "type": "way", 475 | "ref": 31365425, 476 | "role": "" 477 | }, 478 | { 479 | "type": "way", 480 | "ref": 271008236, 481 | "role": "" 482 | }, 483 | { 484 | "type": "way", 485 | "ref": 36447898, 486 | "role": "" 487 | }, 488 | { 489 | "type": "way", 490 | "ref": 271008228, 491 | "role": "" 492 | }, 493 | { 494 | "type": "way", 495 | "ref": 31366812, 496 | "role": "" 497 | }, 498 | { 499 | "type": "way", 500 | "ref": 31366880, 501 | "role": "" 502 | }, 503 | { 504 | "type": "way", 505 | "ref": 31367017, 506 | "role": "" 507 | }, 508 | { 509 | "type": "way", 510 | "ref": 31367443, 511 | "role": "" 512 | }, 513 | { 514 | "type": "way", 515 | "ref": 31367449, 516 | "role": "" 517 | }, 518 | { 519 | "type": "way", 520 | "ref": 31367526, 521 | "role": "" 522 | }, 523 | { 524 | "type": "way", 525 | "ref": 31367534, 526 | "role": "" 527 | }, 528 | { 529 | "type": "way", 530 | "ref": 31369852, 531 | "role": "" 532 | }, 533 | { 534 | "type": "way", 535 | "ref": 449397558, 536 | "role": "" 537 | }, 538 | { 539 | "type": "way", 540 | "ref": 31797389, 541 | "role": "" 542 | }, 543 | { 544 | "type": "way", 545 | "ref": 31798039, 546 | "role": "" 547 | }, 548 | { 549 | "type": "way", 550 | "ref": 235363212, 551 | "role": "" 552 | }, 553 | { 554 | "type": "way", 555 | "ref": 156492546, 556 | "role": "" 557 | }, 558 | { 559 | "type": "way", 560 | "ref": 156492545, 561 | "role": "" 562 | }, 563 | { 564 | "type": "way", 565 | "ref": 130749935, 566 | "role": "" 567 | }, 568 | { 569 | "type": "way", 570 | "ref": 32472877, 571 | "role": "" 572 | }, 573 | { 574 | "type": "way", 575 | "ref": 31798988, 576 | "role": "" 577 | }, 578 | { 579 | "type": "way", 580 | "ref": 31798989, 581 | "role": "" 582 | }, 583 | { 584 | "type": "way", 585 | "ref": 235820560, 586 | "role": "" 587 | }, 588 | { 589 | "type": "way", 590 | "ref": 235820563, 591 | "role": "" 592 | }, 593 | { 594 | "type": "way", 595 | "ref": 235820564, 596 | "role": "" 597 | }, 598 | { 599 | "type": "way", 600 | "ref": 235820570, 601 | "role": "" 602 | }, 603 | { 604 | "type": "way", 605 | "ref": 32472855, 606 | "role": "" 607 | }, 608 | { 609 | "type": "way", 610 | "ref": 32472856, 611 | "role": "" 612 | }, 613 | { 614 | "type": "way", 615 | "ref": 37881520, 616 | "role": "" 617 | }, 618 | { 619 | "type": "way", 620 | "ref": 37881519, 621 | "role": "" 622 | }, 623 | { 624 | "type": "way", 625 | "ref": 234434902, 626 | "role": "" 627 | }, 628 | { 629 | "type": "way", 630 | "ref": 32472875, 631 | "role": "" 632 | }, 633 | { 634 | "type": "way", 635 | "ref": 32473071, 636 | "role": "" 637 | }, 638 | { 639 | "type": "way", 640 | "ref": 128522486, 641 | "role": "" 642 | }, 643 | { 644 | "type": "way", 645 | "ref": 36447896, 646 | "role": "" 647 | }, 648 | { 649 | "type": "way", 650 | "ref": 32473075, 651 | "role": "" 652 | }, 653 | { 654 | "type": "way", 655 | "ref": 32473086, 656 | "role": "" 657 | }, 658 | { 659 | "type": "way", 660 | "ref": 32473087, 661 | "role": "" 662 | }, 663 | { 664 | "type": "way", 665 | "ref": 40033064, 666 | "role": "" 667 | }, 668 | { 669 | "type": "way", 670 | "ref": 189189059, 671 | "role": "" 672 | }, 673 | { 674 | "type": "way", 675 | "ref": 271008225, 676 | "role": "" 677 | }, 678 | { 679 | "type": "way", 680 | "ref": 41550921, 681 | "role": "" 682 | } 683 | ], 684 | "tags": { 685 | "TMC:cid_58:tabcd_1:Class": "Road", 686 | "TMC:cid_58:tabcd_1:LCLversion": "8.00", 687 | "TMC:cid_58:tabcd_1:LocationCode": "7142", 688 | "name": "Bundesautobahn 555", 689 | "network": "BAB", 690 | "operator": "Bundesrepublik Deutschland", 691 | "ref": "A 555", 692 | "route": "road", 693 | "type": "route", 694 | "wikipedia": "de:Bundesautobahn 555" 695 | } 696 | } 697 | 698 | ] 699 | } 700 | -------------------------------------------------------------------------------- /tests/json/remark-runtime-error-01.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2017-03-17T22:05:02Z", 6 | "timestamp_areas_base": "2017-03-17T18:38:02Z", 7 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 8 | }, 9 | "elements": [ 10 | 11 | 12 | 13 | ], 14 | "remark": "runtime error: Query timed out in \"query\" at line 4 after 2 seconds." 15 | } 16 | -------------------------------------------------------------------------------- /tests/json/remark-runtime-remark-01.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2017-03-17T22:05:02Z", 6 | "timestamp_areas_base": "2017-03-17T18:38:02Z", 7 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 8 | }, 9 | "elements": [ 10 | 11 | 12 | 13 | ], 14 | "remark": "runtime remark: Test" 15 | } 16 | -------------------------------------------------------------------------------- /tests/json/remark-unknown-01.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2017-03-17T22:05:02Z", 6 | "timestamp_areas_base": "2017-03-17T18:38:02Z", 7 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 8 | }, 9 | "elements": [ 10 | 11 | 12 | 13 | ], 14 | "remark": "Test remark" 15 | } 16 | -------------------------------------------------------------------------------- /tests/json/result-expand-01.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2014-12-14T13:34:02Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | { 10 | "type": "node", 11 | "id": 3233854233, 12 | "lat": 50.7494187, 13 | "lon": 7.1758731 14 | }, 15 | { 16 | "type": "node", 17 | "id": 3233854234, 18 | "lat": 50.7494287, 19 | "lon": 7.1758731 20 | }, 21 | { 22 | "type": "way", 23 | "id": 317146077, 24 | "nodes": [ 25 | 3233854233, 26 | 3233854234 27 | ], 28 | "tags": { 29 | "building": "yes" 30 | } 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /tests/json/result-expand-02.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2014-12-14T13:34:02Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | { 10 | "type": "area", 11 | "id": 3605945176 12 | }, 13 | { 14 | "type": "node", 15 | "id": 3233854233, 16 | "lat": 50.7494187, 17 | "lon": 7.1758731 18 | }, 19 | { 20 | "type": "node", 21 | "id": 3233854235, 22 | "lat": 50.7494287, 23 | "lon": 7.1758731 24 | }, 25 | { 26 | "type": "way", 27 | "id": 317146078, 28 | "nodes": [ 29 | 3233854233, 30 | 3233854235 31 | ], 32 | "tags": { 33 | "building": "yes" 34 | } 35 | }, 36 | { 37 | "type": "relation", 38 | "id": 2046898, 39 | "timestamp": "2014-12-15T13:13:11Z", 40 | "version": 12, 41 | "changeset": 17433822, 42 | "user": "Username", 43 | "uid": 12345, 44 | "members": [ 45 | { 46 | "type": "node", 47 | "ref": 3233854233, 48 | "role": "platform" 49 | }, 50 | { 51 | "type": "node", 52 | "ref": 3233854234, 53 | "role": "stop" 54 | }, 55 | { 56 | "type": "node", 57 | "ref": 3233854235, 58 | "role": "platform" 59 | }, 60 | { 61 | "type": "way", 62 | "ref": 317146078, 63 | "role": "" 64 | } 65 | ], 66 | "tags": { 67 | "from": "Here", 68 | "name": "Test relation", 69 | "ref": "609", 70 | "route": "bus", 71 | "to": "There", 72 | "type": "route" 73 | } 74 | } 75 | ] 76 | } -------------------------------------------------------------------------------- /tests/json/result-way-01.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2014-12-14T13:34:02Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | { 10 | "type": "way", 11 | "id": 317146077, 12 | "nodes": [ 13 | 3233854233, 14 | 3233854234 15 | ], 16 | "tags": { 17 | "building": "yes" 18 | } 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /tests/json/result-way-02.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2014-12-14T13:34:02Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | { 10 | "type": "node", 11 | "id": 3233854233, 12 | "lat": 50.7494187, 13 | "lon": 7.1758731 14 | }, 15 | { 16 | "type": "way", 17 | "id": 317146077, 18 | "nodes": [ 19 | 3233854233, 20 | 3233854234 21 | ], 22 | "tags": { 23 | "building": "yes" 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /tests/json/result-way-03.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2014-12-14T13:34:02Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | { 10 | "type": "node", 11 | "id": 3233854233, 12 | "lat": 50.7494187, 13 | "lon": 7.1758731 14 | }, 15 | { 16 | "type": "node", 17 | "id": 3233854234, 18 | "lat": 50.7494287, 19 | "lon": 7.1758731 20 | }, 21 | { 22 | "type": "way", 23 | "id": 317146077, 24 | "nodes": [ 25 | 3233854233, 26 | 3233854234 27 | ], 28 | "tags": { 29 | "building": "yes" 30 | } 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /tests/json/way-01.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2014-12-14T13:33:02Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | 10 | { 11 | "type": "way", 12 | "id": 317146077, 13 | "nodes": [ 14 | 3233854241, 15 | 3233854238, 16 | 3233854233, 17 | 3233854234, 18 | 3233854236, 19 | 3233854237, 20 | 3233854241 21 | ], 22 | "tags": { 23 | "building": "yes" 24 | } 25 | }, 26 | { 27 | "type": "way", 28 | "id": 317146078, 29 | "timestamp": "2014-12-14T07:27:21Z", 30 | "version": 1, 31 | "changeset": 23456789, 32 | "user": "TestUser", 33 | "uid": 345678, 34 | "nodes": [ 35 | 3233854241, 36 | 3233854238, 37 | 3233854233, 38 | 3233854234, 39 | 3233854236, 40 | 3233854237, 41 | 3233854241 42 | ] 43 | } 44 | 45 | ] 46 | } -------------------------------------------------------------------------------- /tests/json/way-02.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2014-12-14T13:34:02Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | 10 | { 11 | "type": "node", 12 | "id": 3233854233, 13 | "lat": 50.7494187, 14 | "lon": 7.1758731 15 | }, 16 | { 17 | "type": "node", 18 | "id": 3233854234, 19 | "lat": 50.7494236, 20 | "lon": 7.1757664 21 | }, 22 | { 23 | "type": "node", 24 | "id": 3233854236, 25 | "lat": 50.7494909, 26 | "lon": 7.1757741 27 | }, 28 | { 29 | "type": "node", 30 | "id": 3233854237, 31 | "lat": 50.7494985, 32 | "lon": 7.1756064 33 | }, 34 | { 35 | "type": "node", 36 | "id": 3233854238, 37 | "lat": 50.7495391, 38 | "lon": 7.1758868 39 | }, 40 | { 41 | "type": "node", 42 | "id": 3233854241, 43 | "lat": 50.7495516, 44 | "lon": 7.1756125 45 | }, 46 | { 47 | "type": "way", 48 | "id": 317146077, 49 | "nodes": [ 50 | 3233854241, 51 | 3233854238, 52 | 3233854233, 53 | 3233854234, 54 | 3233854236, 55 | 3233854237, 56 | 3233854241 57 | ], 58 | "tags": { 59 | "building": "yes" 60 | } 61 | } 62 | 63 | ] 64 | } -------------------------------------------------------------------------------- /tests/json/way-03.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2016-11-22T22:33:02Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | 10 | { 11 | "type": "node", 12 | "id": 2343425523, 13 | "lat": 41.8954452, 14 | "lon": 12.5032169, 15 | "timestamp": "2013-06-13T15:34:01Z", 16 | "version": 1, 17 | "changeset": 16539126, 18 | "user": "Giardia", 19 | "uid": 113909 20 | }, 21 | { 22 | "type": "node", 23 | "id": 2343425525, 24 | "lat": 41.8954752, 25 | "lon": 12.5031604, 26 | "timestamp": "2013-06-13T15:34:01Z", 27 | "version": 1, 28 | "changeset": 16539126, 29 | "user": "Giardia", 30 | "uid": 113909 31 | }, 32 | { 33 | "type": "node", 34 | "id": 2343425526, 35 | "lat": 41.8955244, 36 | "lon": 12.5032926, 37 | "timestamp": "2013-06-13T15:34:01Z", 38 | "version": 1, 39 | "changeset": 16539126, 40 | "user": "Giardia", 41 | "uid": 113909 42 | }, 43 | { 44 | "type": "node", 45 | "id": 2343425528, 46 | "lat": 41.8955543, 47 | "lon": 12.5032362, 48 | "timestamp": "2013-06-13T15:34:01Z", 49 | "version": 1, 50 | "changeset": 16539126, 51 | "user": "Giardia", 52 | "uid": 113909 53 | }, 54 | { 55 | "type": "way", 56 | "id": 225576797, 57 | "timestamp": "2013-06-13T15:34:01Z", 58 | "version": 1, 59 | "changeset": 16539126, 60 | "user": "Giardia", 61 | "uid": 113909, 62 | "center": { 63 | "lat": 41.8954998, 64 | "lon": 12.5032265 65 | }, 66 | "nodes": [ 67 | 2343425525, 68 | 2343425528, 69 | 2343425526, 70 | 2343425523, 71 | 2343425525 72 | ], 73 | "tags": { 74 | "building": "kiosk", 75 | "shop": "florist" 76 | } 77 | } 78 | 79 | ] 80 | } 81 | -------------------------------------------------------------------------------- /tests/json/way-04.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.6, 3 | "generator": "Overpass API", 4 | "osm3s": { 5 | "timestamp_osm_base": "2016-11-22T23:25:02Z", 6 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." 7 | }, 8 | "elements": [ 9 | 10 | { 11 | "type": "way", 12 | "id": 225576797, 13 | "center": {}, 14 | "nodes": [ 15 | 2343425525, 16 | 2343425528, 17 | 2343425526, 18 | 2343425523, 19 | 2343425525 20 | ], 21 | "tags": { 22 | "building": "kiosk", 23 | "shop": "florist" 24 | } 25 | } 26 | 27 | ] 28 | } -------------------------------------------------------------------------------- /tests/response/bad-request-encoding.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DinoTools/python-overpy/a8acf84e7182e2e315fd3627529b238f8688ee77/tests/response/bad-request-encoding.html -------------------------------------------------------------------------------- /tests/response/bad-request.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | OSM3S Response 8 | 9 | 10 | 11 |

The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.

12 |

Error: line 2: parse error: ';' expected - 'out' found.

13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/test_exception.py: -------------------------------------------------------------------------------- 1 | import overpy 2 | 3 | 4 | class TestExceptions: 5 | def test_element_data_wrong_type(self): 6 | e = overpy.exception.ElementDataWrongType("from1") 7 | assert e.type_expected == "from1" 8 | assert e.type_provided is None 9 | assert isinstance(str(e), str) 10 | 11 | e = overpy.exception.ElementDataWrongType("from2", "to2") 12 | assert e.type_expected == "from2" 13 | assert e.type_provided == "to2" 14 | assert isinstance(str(e), str) 15 | 16 | def test_overpass_bad_request(self): 17 | e = overpy.exception.OverpassBadRequest("query") 18 | assert e.query == "query" 19 | assert isinstance(e.msgs, list) 20 | assert len(e.msgs) == 0 21 | assert str(e) == "" 22 | 23 | e = overpy.exception.OverpassBadRequest("test\nquery\n123", ["abc", 1]) 24 | assert e.query == "test\nquery\n123" 25 | assert isinstance(e.msgs, list) 26 | assert len(e.msgs) == 2 27 | assert str(e) == "abc\n1" 28 | 29 | def test_overpass_unknown_content_type(self): 30 | e = overpy.exception.OverpassUnknownContentType(None) 31 | assert e.content_type is None 32 | assert str(e).startswith("No content") 33 | 34 | e = overpy.exception.OverpassUnknownContentType("content") 35 | assert e.content_type == "content" 36 | assert str(e).startswith("Unknown content") 37 | assert str(e).endswith("content") 38 | 39 | def test_overpass_unknown_http_status_code(self): 40 | e = overpy.exception.OverpassUnknownHTTPStatusCode(123) 41 | assert e.code == 123 42 | assert str(e).endswith("123") 43 | 44 | def test_overpass_error(self): 45 | exceptions = [ 46 | overpy.exception.OverpassError, 47 | overpy.exception.OverpassRuntimeError, 48 | overpy.exception.OverpassRuntimeRemark, 49 | overpy.exception.OverpassUnknownError 50 | ] 51 | for cls in exceptions: 52 | e = cls(msg="Test message") 53 | assert e.msg == "Test message" 54 | assert str(e) == "Test message" 55 | 56 | for cls in exceptions: 57 | e = cls() 58 | assert e.msg is None 59 | assert str(e) == "No error message provided" 60 | 61 | for cls in exceptions: 62 | e = cls(msg=123) 63 | assert e.msg == 123 64 | assert str(e) == "123" 65 | -------------------------------------------------------------------------------- /tests/test_json.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import simplejson 3 | 4 | import overpy 5 | 6 | from tests import read_file 7 | from tests.base_class import BaseTestAreas, BaseTestNodes, BaseTestRelation, BaseTestWay 8 | 9 | 10 | def reparse(api: overpy.Overpass, r: overpy.Result): 11 | # we need `simplejson` because core `json` can't serialize Decimals in the way 12 | # that we would like without enormous hacks 13 | return api.parse_json(simplejson.dumps(r.to_json())) 14 | 15 | 16 | class TestAreas(BaseTestAreas): 17 | def test_area01(self): 18 | api = overpy.Overpass() 19 | result = api.parse_json(read_file("json/area-01.json")) 20 | self._test_area01(result) 21 | self._test_area01(reparse(api, result)) 22 | 23 | 24 | class TestNodes(BaseTestNodes): 25 | def test_node01(self): 26 | api = overpy.Overpass() 27 | result = api.parse_json(read_file("json/node-01.json")) 28 | self._test_node01(result) 29 | self._test_node01(reparse(api, result)) 30 | 31 | 32 | class TestRelation(BaseTestRelation): 33 | def test_relation01(self): 34 | api = overpy.Overpass() 35 | result = api.parse_json(read_file("json/relation-01.json")) 36 | self._test_relation01(result) 37 | self._test_relation01(reparse(api, result)) 38 | 39 | def test_relation02(self): 40 | api = overpy.Overpass() 41 | result = api.parse_json(read_file("json/relation-02.json")) 42 | self._test_relation02(result) 43 | self._test_relation02(reparse(api, result)) 44 | 45 | def test_relation03(self): 46 | api = overpy.Overpass() 47 | result = api.parse_json(read_file("json/relation-03.json")) 48 | self._test_relation03(result) 49 | self._test_relation03(reparse(api, result)) 50 | 51 | def test_relation04(self): 52 | api = overpy.Overpass() 53 | result = api.parse_json(read_file("json/relation-04.json")) 54 | self._test_relation04(result) 55 | self._test_relation04(reparse(api, result)) 56 | 57 | 58 | class TestWay(BaseTestWay): 59 | def test_way01(self): 60 | api = overpy.Overpass() 61 | result = api.parse_json(read_file("json/way-01.json")) 62 | self._test_way01(result) 63 | self._test_way01(reparse(api, result)) 64 | 65 | def test_way02(self): 66 | api = overpy.Overpass() 67 | result = api.parse_json(read_file("json/way-02.json")) 68 | self._test_way02(result) 69 | self._test_way02(reparse(api, result)) 70 | 71 | def test_way03(self): 72 | api = overpy.Overpass() 73 | result = api.parse_json(read_file("json/way-03.json")) 74 | self._test_way03(result) 75 | self._test_way03(reparse(api, result)) 76 | 77 | def test_way04(self): 78 | api = overpy.Overpass() 79 | with pytest.raises(ValueError): 80 | api.parse_json(read_file("json/way-04.json")) 81 | 82 | 83 | class TestDataError: 84 | def test_element_wrong_type(self): 85 | with pytest.raises(overpy.exception.ElementDataWrongType): 86 | overpy.Node.from_json( 87 | { 88 | "type": "foo" 89 | } 90 | ) 91 | 92 | with pytest.raises(overpy.exception.ElementDataWrongType): 93 | overpy.Relation.from_json( 94 | { 95 | "type": "foo" 96 | } 97 | ) 98 | 99 | with pytest.raises(overpy.exception.ElementDataWrongType): 100 | overpy.RelationNode.from_json( 101 | { 102 | "type": "foo" 103 | } 104 | ) 105 | 106 | with pytest.raises(overpy.exception.ElementDataWrongType): 107 | overpy.RelationWay.from_json( 108 | { 109 | "type": "foo" 110 | } 111 | ) 112 | with pytest.raises(overpy.exception.ElementDataWrongType): 113 | overpy.Way.from_json( 114 | { 115 | "type": "foo" 116 | } 117 | ) 118 | 119 | 120 | class TestRemark: 121 | def test_remark_runtime_error(self): 122 | api = overpy.Overpass() 123 | with pytest.raises(overpy.exception.OverpassRuntimeError): 124 | api.parse_json(read_file("json/remark-runtime-error-01.json")) 125 | 126 | def test_remark_runtime_remark(self): 127 | api = overpy.Overpass() 128 | with pytest.raises(overpy.exception.OverpassRuntimeRemark): 129 | api.parse_json(read_file("json/remark-runtime-remark-01.json")) 130 | 131 | def test_remark_unknown(self): 132 | api = overpy.Overpass() 133 | with pytest.raises(overpy.exception.OverpassUnknownError): 134 | api.parse_json(read_file("json/remark-unknown-01.json")) 135 | -------------------------------------------------------------------------------- /tests/test_request.py: -------------------------------------------------------------------------------- 1 | from http.server import BaseHTTPRequestHandler 2 | 3 | import pytest 4 | 5 | import overpy 6 | 7 | from tests import read_file, new_server_thread, stop_server_thread 8 | 9 | 10 | def handle_bad_request(request): 11 | request.send_response(400, "Bad Request") 12 | request.send_header("Content-Type", "text/html; charset=utf-8") 13 | request.end_headers() 14 | request.wfile.write(read_file("response/bad-request.html", "rb")) 15 | 16 | 17 | def handle_bad_request_encoding(request): 18 | request.send_response(400, "Bad Request") 19 | request.send_header("Content-Type", "text/html; charset=utf-8") 20 | request.end_headers() 21 | request.wfile.write(read_file("response/bad-request-encoding.html", "rb")) 22 | 23 | 24 | def handle_too_many_requests(request): 25 | request.send_response(429, "Too Many Requests") 26 | request.send_header("Content-Type", "text/html; charset=utf-8") 27 | request.end_headers() 28 | request.wfile.write(b"Too Many Requests") 29 | 30 | 31 | def handle_gateway_timeout(request): 32 | request.send_response(504, "Gateway Timeout") 33 | request.send_header("Content-Type", "text/html; charset=utf-8") 34 | request.end_headers() 35 | request.wfile.write(b"Gateway Timeout") 36 | 37 | 38 | def handle_unknown_content_type(request): 39 | request.send_response(200, "OK") 40 | request.send_header("Content-Type", "application/foobar") 41 | request.end_headers() 42 | request.wfile.write(read_file("xml/way-02.xml", "rb")) 43 | 44 | 45 | def handle_unknown_http_status_code(request): 46 | request.send_response(123, "Unknown") 47 | request.send_header("Content-Type", "text/html; charset=utf-8") 48 | request.end_headers() 49 | request.wfile.write(b"Unknown status code") 50 | 51 | 52 | class HandleOverpassBadRequest(BaseHTTPRequestHandler): 53 | """ 54 | Simulate the response if the query string has syntax errors 55 | """ 56 | def do_POST(self): 57 | handle_bad_request(self) 58 | 59 | 60 | class HandleOverpassBadRequestEncoding(BaseHTTPRequestHandler): 61 | """ 62 | """ 63 | def do_POST(self): 64 | handle_bad_request_encoding(self) 65 | 66 | 67 | class HandleOverpassTooManyRequests(BaseHTTPRequestHandler): 68 | """ 69 | """ 70 | def do_POST(self): 71 | handle_too_many_requests(self) 72 | 73 | 74 | class HandleOverpassGatewayTimeout(BaseHTTPRequestHandler): 75 | """ 76 | """ 77 | def do_POST(self): 78 | handle_gateway_timeout(self) 79 | 80 | 81 | class HandleOverpassUnknownHTTPStatusCode(BaseHTTPRequestHandler): 82 | """ 83 | """ 84 | def do_POST(self): 85 | handle_unknown_http_status_code(self) 86 | 87 | 88 | class HandleResponseJSON(BaseHTTPRequestHandler): 89 | """ 90 | """ 91 | def do_POST(self): 92 | self.send_response(200, "OK") 93 | self.send_header("Content-Type", "application/json") 94 | self.end_headers() 95 | self.wfile.write(read_file("json/way-02.json", "rb")) 96 | 97 | 98 | class HandleResponseXML(BaseHTTPRequestHandler): 99 | """ 100 | """ 101 | def do_POST(self): 102 | self.send_response(200, "OK") 103 | self.send_header("Content-Type", "application/osm3s+xml") 104 | self.end_headers() 105 | self.wfile.write(read_file("xml/way-02.xml", "rb")) 106 | 107 | 108 | class HandleResponseUnknown(BaseHTTPRequestHandler): 109 | """ 110 | """ 111 | def do_POST(self): 112 | handle_unknown_content_type(self) 113 | 114 | 115 | class HandleRetry(BaseHTTPRequestHandler): 116 | default_handler_func = [ 117 | handle_bad_request, 118 | handle_bad_request_encoding, 119 | handle_too_many_requests, 120 | handle_gateway_timeout, 121 | handle_unknown_content_type, 122 | handle_unknown_http_status_code 123 | ] 124 | 125 | def do_POST(self): 126 | f = self.default_handler_func.pop(0) 127 | f(self) 128 | 129 | 130 | class TestQuery: 131 | def test_chunk_size(self): 132 | url, server = new_server_thread(HandleResponseJSON) 133 | 134 | api = overpy.Overpass(read_chunk_size=128) 135 | api.url = url 136 | result = api.query("[out:json];node(50.745,7.17,50.75,7.18);out;") 137 | stop_server_thread(server) 138 | assert len(result.nodes) > 0 139 | 140 | def test_overpass_syntax_error(self): 141 | url, server = new_server_thread(HandleOverpassBadRequest) 142 | 143 | api = overpy.Overpass() 144 | api.url = url 145 | with pytest.raises(overpy.exception.OverpassBadRequest): 146 | # Missing ; after way(1) 147 | api.query( 148 | "way(1)" 149 | "out body;" 150 | ) 151 | stop_server_thread(server) 152 | 153 | def test_overpass_syntax_error_encoding_error(self): 154 | with pytest.raises(UnicodeDecodeError): 155 | # File should be encoded with iso8859-15 and will raise an exception 156 | tmp = read_file("response/bad-request-encoding.html", "rb") 157 | tmp.decode("utf-8") 158 | 159 | url, server = new_server_thread(HandleOverpassBadRequestEncoding) 160 | 161 | api = overpy.Overpass() 162 | api.url = url 163 | with pytest.raises(overpy.exception.OverpassBadRequest): 164 | # Missing ; after way(1) 165 | api.query( 166 | "way(1)" 167 | "out body;" 168 | ) 169 | stop_server_thread(server) 170 | 171 | def test_overpass_too_many_requests(self): 172 | url, server = new_server_thread(HandleOverpassTooManyRequests) 173 | 174 | api = overpy.Overpass() 175 | api.url = url 176 | with pytest.raises(overpy.exception.OverpassTooManyRequests): 177 | api.query( 178 | "way(1);" 179 | "out body;" 180 | ) 181 | stop_server_thread(server) 182 | 183 | def test_overpass_gateway_timeout(self): 184 | url, server = new_server_thread(HandleOverpassGatewayTimeout) 185 | 186 | api = overpy.Overpass() 187 | api.url = url 188 | with pytest.raises(overpy.exception.OverpassGatewayTimeout): 189 | api.query( 190 | "way(1);" 191 | "out body;" 192 | ) 193 | stop_server_thread(server) 194 | 195 | def test_overpass_unknown_status_code(self): 196 | url, server = new_server_thread(HandleOverpassUnknownHTTPStatusCode) 197 | 198 | api = overpy.Overpass() 199 | api.url = url 200 | with pytest.raises(overpy.exception.OverpassUnknownHTTPStatusCode): 201 | api.query( 202 | "way(1);" 203 | "out body;" 204 | ) 205 | stop_server_thread(server) 206 | 207 | def test_response_json(self): 208 | url, server = new_server_thread(HandleResponseJSON) 209 | 210 | api = overpy.Overpass() 211 | api.url = url 212 | result = api.query("[out:json];node(50.745,7.17,50.75,7.18);out;") 213 | stop_server_thread(server) 214 | assert len(result.nodes) > 0 215 | 216 | def test_response_unknown(self): 217 | url, server = new_server_thread(HandleResponseUnknown) 218 | 219 | api = overpy.Overpass() 220 | api.url = url 221 | with pytest.raises(overpy.exception.OverpassUnknownContentType): 222 | api.query("[out:xml];node(50.745,7.17,50.75,7.18);out;") 223 | stop_server_thread(server) 224 | 225 | def test_response_xml(self): 226 | url, server = new_server_thread(HandleResponseXML) 227 | 228 | api = overpy.Overpass() 229 | api.url = url 230 | result = api.query("[out:xml];node(50.745,7.17,50.75,7.18);out;") 231 | stop_server_thread(server) 232 | assert len(result.nodes) > 0 233 | 234 | def test_retry(self): 235 | url, server = new_server_thread(HandleRetry) 236 | 237 | api = overpy.Overpass() 238 | # HandleRetry.default_handler_cls should contain at least 2 classes to process 239 | api.max_retry_count = len(HandleRetry.default_handler_func) - 1 240 | api.url = url 241 | with pytest.raises(overpy.exception.MaxRetriesReached): 242 | api.query( 243 | "way(1);" 244 | "out body;" 245 | ) 246 | stop_server_thread(server) 247 | -------------------------------------------------------------------------------- /tests/test_result.py: -------------------------------------------------------------------------------- 1 | from http.server import BaseHTTPRequestHandler 2 | 3 | import pytest 4 | 5 | import overpy 6 | 7 | from tests import read_file, new_server_thread, stop_server_thread 8 | from tests.base_class import BaseTestWay 9 | 10 | 11 | class HandleResponseJSON02(BaseHTTPRequestHandler): 12 | """ 13 | """ 14 | def do_POST(self): 15 | self.send_response(200, "OK") 16 | self.send_header("Content-Type", "application/json") 17 | self.end_headers() 18 | self.wfile.write(read_file("json/result-expand-02.json", "rb")) 19 | 20 | 21 | class TestResult: 22 | def test_expand_error(self): 23 | api = overpy.Overpass() 24 | result = api.parse_json(read_file("json/result-expand-01.json")) 25 | with pytest.raises(ValueError): 26 | result.expand(123) 27 | with pytest.raises(ValueError): 28 | result.expand(1.23) 29 | with pytest.raises(ValueError): 30 | result.expand("abc") 31 | 32 | def test_expand_01(self): 33 | api = overpy.Overpass() 34 | result1 = api.parse_json(read_file("json/result-expand-01.json")) 35 | 36 | assert len(result1.nodes) == 2 37 | assert len(result1.ways) == 1 38 | 39 | result2 = api.parse_json(read_file("json/result-expand-02.json")) 40 | 41 | assert len(result2.nodes) == 2 42 | assert len(result2.ways) == 1 43 | 44 | result1.expand(result2) 45 | 46 | # Don't overwrite existing elements 47 | assert len(result1.nodes) == 3 48 | assert len(result1.ways) == 2 49 | 50 | 51 | class TestArea: 52 | def test_missing_unresolvable(self): 53 | url, server = new_server_thread(HandleResponseJSON02) 54 | 55 | api = overpy.Overpass() 56 | api.url = url 57 | result1 = api.parse_json(read_file("json/result-expand-01.json")) 58 | 59 | with pytest.raises(overpy.exception.DataIncomplete): 60 | result1.get_area(123, resolve_missing=True) 61 | stop_server_thread(server) 62 | 63 | def test_missing_resolvable(self): 64 | url, server = new_server_thread(HandleResponseJSON02) 65 | 66 | api = overpy.Overpass() 67 | api.url = url 68 | result1 = api.parse_json(read_file("json/result-expand-01.json")) 69 | 70 | # Node must not be available 71 | with pytest.raises(overpy.exception.DataIncomplete): 72 | result1.get_area(3605945176) 73 | 74 | # Node must be available 75 | area = result1.get_area(3605945176, resolve_missing=True) 76 | 77 | assert isinstance(area, overpy.Area) 78 | assert area.id == 3605945176 79 | 80 | stop_server_thread(server) 81 | 82 | 83 | class TestNode: 84 | def test_missing_unresolvable(self): 85 | url, server = new_server_thread(HandleResponseJSON02) 86 | 87 | api = overpy.Overpass() 88 | api.url = url 89 | result1 = api.parse_json(read_file("json/result-expand-01.json")) 90 | 91 | with pytest.raises(overpy.exception.DataIncomplete): 92 | result1.get_node(123, resolve_missing=True) 93 | stop_server_thread(server) 94 | 95 | def test_missing_resolvable(self): 96 | url, server = new_server_thread(HandleResponseJSON02) 97 | 98 | api = overpy.Overpass() 99 | api.url = url 100 | result1 = api.parse_json(read_file("json/result-expand-01.json")) 101 | 102 | # Node must not be available 103 | with pytest.raises(overpy.exception.DataIncomplete): 104 | result1.get_node(3233854235) 105 | 106 | # Node must be available 107 | node = result1.get_node(3233854235, resolve_missing=True) 108 | 109 | assert isinstance(node, overpy.Node) 110 | assert node.id == 3233854235 111 | 112 | stop_server_thread(server) 113 | 114 | 115 | class TestPickle(BaseTestWay): 116 | def test_way02(self): 117 | """ 118 | Try to pickle and unpickle the result object 119 | """ 120 | import pickle 121 | 122 | api = overpy.Overpass() 123 | result = api.parse_json(read_file("json/way-02.json")) 124 | self._test_way02(result) 125 | # do pickle and unpickle 126 | result_string = pickle.dumps(result) 127 | new_result = pickle.loads(result_string) 128 | # test new result 129 | self._test_way02(new_result) 130 | 131 | 132 | class TestRelation: 133 | def test_missing_unresolvable(self): 134 | url, server = new_server_thread(HandleResponseJSON02) 135 | 136 | api = overpy.Overpass() 137 | api.url = url 138 | result1 = api.parse_json(read_file("json/result-expand-01.json")) 139 | 140 | with pytest.raises(overpy.exception.DataIncomplete): 141 | result1.get_relation(123, resolve_missing=True) 142 | stop_server_thread(server) 143 | 144 | def test_missing_resolvable(self): 145 | url, server = new_server_thread(HandleResponseJSON02) 146 | 147 | api = overpy.Overpass() 148 | api.url = url 149 | result1 = api.parse_json(read_file("json/result-expand-01.json")) 150 | 151 | # Relation must not be available 152 | with pytest.raises(overpy.exception.DataIncomplete): 153 | result1.get_relation(2046898) 154 | 155 | # Relation must be available 156 | relation = result1.get_relation(2046898, resolve_missing=True) 157 | 158 | assert isinstance(relation, overpy.Relation) 159 | assert relation.id == 2046898 160 | 161 | stop_server_thread(server) 162 | 163 | 164 | class TestWay: 165 | def test_missing_unresolvable(self): 166 | url, server = new_server_thread(HandleResponseJSON02) 167 | 168 | api = overpy.Overpass() 169 | api.url = url 170 | result1 = api.parse_json(read_file("json/result-expand-01.json")) 171 | 172 | with pytest.raises(overpy.exception.DataIncomplete): 173 | result1.get_way(123, resolve_missing=True) 174 | stop_server_thread(server) 175 | 176 | def test_missing_resolvable(self): 177 | url, server = new_server_thread(HandleResponseJSON02) 178 | 179 | api = overpy.Overpass() 180 | api.url = url 181 | result1 = api.parse_json(read_file("json/result-expand-01.json")) 182 | 183 | # Way must not be available 184 | with pytest.raises(overpy.exception.DataIncomplete): 185 | result1.get_way(317146078) 186 | 187 | # Way must be available 188 | way = result1.get_way(317146078, resolve_missing=True) 189 | 190 | assert isinstance(way, overpy.Way) 191 | assert way.id == 317146078 192 | 193 | stop_server_thread(server) 194 | -------------------------------------------------------------------------------- /tests/test_result_way.py: -------------------------------------------------------------------------------- 1 | from http.server import BaseHTTPRequestHandler 2 | 3 | import pytest 4 | 5 | import overpy 6 | 7 | from tests import read_file, new_server_thread, stop_server_thread 8 | 9 | 10 | class HandleResponseJSON01(BaseHTTPRequestHandler): 11 | """ 12 | """ 13 | def do_POST(self): 14 | self.send_response(200, "OK") 15 | self.send_header("Content-Type", "application/json") 16 | self.end_headers() 17 | self.wfile.write(read_file("json/result-way-01.json", "rb")) 18 | 19 | 20 | class HandleResponseJSON02(BaseHTTPRequestHandler): 21 | """ 22 | """ 23 | def do_POST(self): 24 | self.send_response(200, "OK") 25 | self.send_header("Content-Type", "application/json") 26 | self.end_headers() 27 | self.wfile.write(read_file("json/result-way-02.json", "rb")) 28 | 29 | 30 | class HandleResponseJSON03(BaseHTTPRequestHandler): 31 | """ 32 | """ 33 | def do_POST(self): 34 | self.send_response(200, "OK") 35 | self.send_header("Content-Type", "application/json") 36 | self.end_headers() 37 | self.wfile.write(read_file("json/result-way-03.json", "rb")) 38 | 39 | 40 | class TestNodes: 41 | def test_missing_unresolvable(self): 42 | url, server = new_server_thread(HandleResponseJSON01) 43 | 44 | api = overpy.Overpass() 45 | api.url = url 46 | result = api.parse_json(read_file("json/result-way-01.json")) 47 | 48 | assert len(result.nodes) == 0 49 | assert len(result.ways) == 1 50 | 51 | way = result.ways[0] 52 | assert isinstance(way, overpy.Way) 53 | 54 | with pytest.raises(overpy.exception.DataIncomplete): 55 | way.get_nodes() 56 | 57 | with pytest.raises(overpy.exception.DataIncomplete): 58 | way.get_nodes(resolve_missing=True) 59 | 60 | assert len(result.nodes) == 0 61 | stop_server_thread(server) 62 | 63 | def test_missing_partly_unresolvable(self): 64 | url, server = new_server_thread(HandleResponseJSON02) 65 | 66 | api = overpy.Overpass() 67 | api.url = url 68 | result = api.parse_json(read_file("json/result-way-01.json")) 69 | 70 | assert len(result.nodes) == 0 71 | assert len(result.ways) == 1 72 | 73 | way = result.ways[0] 74 | assert isinstance(way, overpy.Way) 75 | 76 | with pytest.raises(overpy.exception.DataIncomplete): 77 | way.get_nodes() 78 | 79 | with pytest.raises(overpy.exception.DataIncomplete): 80 | way.get_nodes(resolve_missing=True) 81 | 82 | assert len(result.nodes) == 1 83 | stop_server_thread(server) 84 | 85 | def test_missing_resolvable(self): 86 | url, server = new_server_thread(HandleResponseJSON03) 87 | 88 | api = overpy.Overpass() 89 | api.url = url 90 | result = api.parse_json(read_file("json/result-way-01.json")) 91 | 92 | assert len(result.nodes) == 0 93 | assert len(result.ways) == 1 94 | 95 | way = result.ways[0] 96 | assert isinstance(way, overpy.Way) 97 | 98 | with pytest.raises(overpy.exception.DataIncomplete): 99 | way.get_nodes() 100 | 101 | nodes = way.get_nodes(resolve_missing=True) 102 | 103 | assert len(nodes) == 2 104 | 105 | stop_server_thread(server) 106 | -------------------------------------------------------------------------------- /tests/test_xml.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import overpy 4 | 5 | from tests import read_file 6 | from tests.base_class import BaseTestAreas, BaseTestNodes, BaseTestRelation, BaseTestWay 7 | 8 | 9 | class TestAreas(BaseTestAreas): 10 | def test_node01(self): 11 | api = overpy.Overpass() 12 | # DOM 13 | result = api.parse_xml(read_file("xml/area-01.xml"), parser=overpy.XML_PARSER_DOM) 14 | self._test_area01(result) 15 | # SAX 16 | result = api.parse_xml(read_file("xml/area-01.xml"), parser=overpy.XML_PARSER_SAX) 17 | self._test_area01(result) 18 | 19 | 20 | class TestNodes(BaseTestNodes): 21 | def test_node01(self): 22 | api = overpy.Overpass() 23 | # DOM 24 | result = api.parse_xml(read_file("xml/node-01.xml"), parser=overpy.XML_PARSER_DOM) 25 | self._test_node01(result) 26 | # SAX 27 | result = api.parse_xml(read_file("xml/node-01.xml"), parser=overpy.XML_PARSER_SAX) 28 | self._test_node01(result) 29 | 30 | 31 | class TestRelation(BaseTestRelation): 32 | def test_relation01(self): 33 | api = overpy.Overpass() 34 | # DOM 35 | result = api.parse_xml(read_file("xml/relation-01.xml"), parser=overpy.XML_PARSER_DOM) 36 | self._test_relation01(result) 37 | # SAX 38 | result = api.parse_xml(read_file("xml/relation-01.xml"), parser=overpy.XML_PARSER_SAX) 39 | self._test_relation01(result) 40 | 41 | def test_relation02(self): 42 | api = overpy.Overpass() 43 | # DOM 44 | result = api.parse_xml(read_file("xml/relation-02.xml"), parser=overpy.XML_PARSER_DOM) 45 | self._test_relation02(result) 46 | # SAX 47 | result = api.parse_xml(read_file("xml/relation-02.xml"), parser=overpy.XML_PARSER_SAX) 48 | self._test_relation02(result) 49 | 50 | def test_relation03(self): 51 | api = overpy.Overpass() 52 | # DOM 53 | result = api.parse_xml(read_file("xml/relation-03.xml"), parser=overpy.XML_PARSER_DOM) 54 | self._test_relation03(result) 55 | # SAX 56 | result = api.parse_xml(read_file("xml/relation-03.xml"), parser=overpy.XML_PARSER_SAX) 57 | self._test_relation03(result) 58 | 59 | def test_relation04(self): 60 | api = overpy.Overpass() 61 | # DOM 62 | result = api.parse_xml(read_file("xml/relation-04.xml"), parser=overpy.XML_PARSER_DOM) 63 | self._test_relation04(result) 64 | # SAX 65 | result = api.parse_xml(read_file("xml/relation-04.xml"), parser=overpy.XML_PARSER_SAX) 66 | self._test_relation04(result) 67 | 68 | 69 | class TestWay(BaseTestWay): 70 | def test_way01(self): 71 | api = overpy.Overpass() 72 | # DOM 73 | result = api.parse_xml(read_file("xml/way-01.xml"), parser=overpy.XML_PARSER_DOM) 74 | self._test_way01(result) 75 | # SAX 76 | result = api.parse_xml(read_file("xml/way-01.xml"), parser=overpy.XML_PARSER_SAX) 77 | self._test_way01(result) 78 | 79 | def test_way02(self): 80 | api = overpy.Overpass() 81 | # DOM 82 | result = api.parse_xml(read_file("xml/way-02.xml"), parser=overpy.XML_PARSER_DOM) 83 | self._test_way02(result) 84 | # SAX 85 | result = api.parse_xml(read_file("xml/way-02.xml"), parser=overpy.XML_PARSER_SAX) 86 | self._test_way02(result) 87 | 88 | def test_way03(self): 89 | api = overpy.Overpass() 90 | # DOM 91 | result = api.parse_xml(read_file("xml/way-03.xml"), parser=overpy.XML_PARSER_DOM) 92 | self._test_way03(result) 93 | # SAX 94 | result = api.parse_xml(read_file("xml/way-03.xml"), parser=overpy.XML_PARSER_SAX) 95 | self._test_way03(result) 96 | 97 | def test_way04(self): 98 | api = overpy.Overpass() 99 | # DOM 100 | with pytest.raises(ValueError): 101 | api.parse_xml(read_file("xml/way-04.xml"), parser=overpy.XML_PARSER_DOM) 102 | 103 | # SAX 104 | with pytest.raises(ValueError): 105 | api.parse_xml(read_file("xml/way-04.xml"), parser=overpy.XML_PARSER_SAX) 106 | 107 | 108 | class TestDataError: 109 | @staticmethod 110 | def _get_element_wrong_type(): 111 | data = "" 112 | import xml.etree.ElementTree as ET 113 | return ET.fromstring(data) 114 | 115 | def test_element_wrong_type(self): 116 | with pytest.raises(overpy.exception.ElementDataWrongType): 117 | overpy.Node.from_xml( 118 | self._get_element_wrong_type() 119 | ) 120 | 121 | with pytest.raises(overpy.exception.ElementDataWrongType): 122 | overpy.Relation.from_xml( 123 | self._get_element_wrong_type() 124 | ) 125 | 126 | with pytest.raises(overpy.exception.ElementDataWrongType): 127 | overpy.RelationNode.from_xml( 128 | self._get_element_wrong_type() 129 | ) 130 | 131 | with pytest.raises(overpy.exception.ElementDataWrongType): 132 | overpy.RelationWay.from_xml( 133 | self._get_element_wrong_type() 134 | ) 135 | 136 | with pytest.raises(overpy.exception.ElementDataWrongType): 137 | overpy.Way.from_xml( 138 | self._get_element_wrong_type() 139 | ) 140 | 141 | def test_node_missing_data(self): 142 | import xml.etree.ElementTree as ET 143 | 144 | # Tag without k attribute 145 | data = """""" 146 | node = ET.fromstring(data) 147 | with pytest.raises(ValueError): 148 | overpy.Node.from_xml(node) 149 | 150 | def test_relation_missing_data(self): 151 | import xml.etree.ElementTree as ET 152 | 153 | # Tag without k attribute 154 | data = """""" 155 | node = ET.fromstring(data) 156 | with pytest.raises(ValueError): 157 | overpy.Relation.from_xml(node) 158 | 159 | def test_way_missing_data(self): 160 | import xml.etree.ElementTree as ET 161 | 162 | # Node without ref attribute 163 | data = """""" 164 | node = ET.fromstring(data) 165 | with pytest.raises(ValueError): 166 | overpy.Way.from_xml(node) 167 | 168 | # Tag without k attribute 169 | data = """""" 170 | node = ET.fromstring(data) 171 | with pytest.raises(ValueError): 172 | overpy.Way.from_xml(node) 173 | 174 | 175 | class TestParser(BaseTestNodes): 176 | def test_exception(self): 177 | with pytest.raises(overpy.exception.OverPyException): 178 | overpy.Result.from_xml(123) 179 | 180 | def test_xml_element(self): 181 | import xml.etree.ElementTree as ET 182 | data = read_file("xml/node-01.xml") 183 | root = ET.fromstring(data) 184 | result = overpy.Result.from_xml(root) 185 | 186 | assert isinstance(result, overpy.Result) 187 | self._test_node01(result) 188 | 189 | def test_xml_autodetect_parser(self): 190 | data = read_file("xml/node-01.xml") 191 | result = overpy.Result.from_xml(data) 192 | 193 | assert isinstance(result, overpy.Result) 194 | self._test_node01(result) 195 | 196 | 197 | class TestRemark: 198 | def test_remark_runtime_error(self): 199 | api = overpy.Overpass() 200 | with pytest.raises(overpy.exception.OverpassRuntimeError): 201 | api.parse_xml(read_file("xml/remark-runtime-error-01.xml")) 202 | 203 | def test_remark_runtime_remark(self): 204 | api = overpy.Overpass() 205 | with pytest.raises(overpy.exception.OverpassRuntimeRemark): 206 | api.parse_xml(read_file("xml/remark-runtime-remark-01.xml")) 207 | 208 | def test_remark_unknown(self): 209 | api = overpy.Overpass() 210 | with pytest.raises(overpy.exception.OverpassUnknownError): 211 | api.parse_xml(read_file("xml/remark-unknown-01.xml")) 212 | -------------------------------------------------------------------------------- /tests/xml/area-01.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /tests/xml/node-01.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/xml/relation-01.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/xml/relation-02.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/xml/relation-03.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /tests/xml/relation-04.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | -------------------------------------------------------------------------------- /tests/xml/remark-runtime-error-01.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | runtime error: Query timed out in "query" at line 4 after 2 seconds. 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/xml/remark-runtime-remark-01.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | runtime remark: Test 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/xml/remark-unknown-01.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | Test remark 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/xml/way-01.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/xml/way-02.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/xml/way-03.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/xml/way-04.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: none 2 | # SPDX-License-Identifier: CC0-1.0 3 | 4 | [tox] 5 | envlist = py37,py38,py39,py310,py311,pypy39 6 | 7 | [testenv] 8 | deps = 9 | pytest 10 | simplejson 11 | pytest-cov 12 | commands = pytest --cov overpy --cov-report=term-missing -v tests/ 13 | 14 | [gh-actions] 15 | python = 16 | 3.7: py37 17 | 3.8: py38 18 | 3.9: py39 19 | 3.10: py310 20 | 3.11: py311 21 | pypy3.9: pypy39 22 | --------------------------------------------------------------------------------