[Git][debian-gis-team/python-osmapi][upstream] New upstream version 4.1.0+ds

Bas Couwenberg (@sebastic) gitlab at salsa.debian.org
Tue Mar 19 17:43:56 GMT 2024



Bas Couwenberg pushed to branch upstream at Debian GIS Project / python-osmapi


Commits:
7603908e by Bas Couwenberg at 2024-03-19T18:33:43+01:00
New upstream version 4.1.0+ds
- - - - -


20 changed files:

- .gitignore
- CHANGELOG.md
- Makefile
- README.md
- + examples/oauth2.py
- + examples/oauth2_backend.py
- osmapi/__init__.py
- osmapi/errors.py
- osmapi/http.py
- requirements.txt
- setup.sh
- test-requirements.txt
- tests/changeset_test.py
- tests/fixtures/test_ChangesetDownloadContainingUnicode.xml
- + tests/fixtures/test_NodeCreate_with_session_auth.xml
- + tests/fixtures/test_NodeCreate_wo_auth.xml
- tests/node_test.py
- tests/notes_test.py
- tests/osmapi_test.py
- tests/way_test.py


Changes:

=====================================
.gitignore
=====================================
@@ -7,3 +7,4 @@ MANIFEST
 .pycache/*
 .pytest_cache/*
 .env
+pyenv


=====================================
CHANGELOG.md
=====================================
@@ -4,6 +4,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
 
 ## [Unreleased]
 
+## [4.1.0] - 2024-03-19
+### Added
+- OAuth 2.0 example in README and in the `examples` directory
+
+### Changed
+- Check if a passed `session` is authenticated and use this instead of Username/Password, this enables OAuth 2.0 authentication
+
+### Removed
+- remove Python2 crumbs (see PR #159, thanks [Alexandre Detiste](https://github.com/a-detiste))
+
 ## [4.0.0] - 2023-07-15
 ### Added
 - Add Python 3.11 to build
@@ -339,7 +349,8 @@ Miroslav Šedivý
 - `Fixed` for any bug fixes.
 - `Security` to invite users to upgrade in case of vulnerabilities.
 
-[Unreleased]: https://github.com/metaodi/osmapi/compare/v4.0.0...HEAD
+[Unreleased]: https://github.com/metaodi/osmapi/compare/v4.1.0...HEAD
+[4.1.0]: https://github.com/metaodi/osmapi/compare/v4.0.0...v4.1.0
 [4.0.0]: https://github.com/metaodi/osmapi/compare/v3.1.0...v4.0.0
 [3.1.0]: https://github.com/metaodi/osmapi/compare/v3.0.0...v3.1.0
 [3.0.0]: https://github.com/metaodi/osmapi/compare/v2.0.2...v3.0.0


=====================================
Makefile
=====================================
@@ -10,6 +10,7 @@ deps:  ## Install dependencies
 	python -m pip install --upgrade pip
 	python -m pip install -r requirements.txt
 	python -m pip install -r test-requirements.txt
+	pre-commit install
 
 docs:  ## Generate documentation
 	python -m pdoc -o docs osmapi
@@ -21,8 +22,8 @@ lint:  ## Linting of source code
 	python -m black --check --diff osmapi examples tests *.py
 	python -m flake8 --statistics --show-source .
 
-test:  ## Run tests
-	python -m pytest --cov=osmapi tests/
+test:  ## Run tests (run in UTF-8 mode in Windows)
+	python -Xutf8 -m pytest --cov=osmapi tests/
 
 help: SHELL := /bin/bash
 help: ## Show help message


=====================================
README.md
=====================================
@@ -2,10 +2,12 @@ osmapi
 ======
 
 [![Build osmapi](https://github.com/metaodi/osmapi/actions/workflows/build.yml/badge.svg)](https://github.com/metaodi/osmapi/actions/workflows/build.yml)
-[![Coverage](https://img.shields.io/coveralls/metaodi/osmapi/develop.svg)](https://coveralls.io/r/metaodi/osmapi?branch=develop)
-[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
 [![Version](https://img.shields.io/pypi/v/osmapi.svg)](https://pypi.python.org/pypi/osmapi/)
 [![License](https://img.shields.io/pypi/l/osmapi.svg)](https://github.com/metaodi/osmapi/blob/master/LICENSE.txt)
+[![Coverage](https://img.shields.io/coveralls/metaodi/osmapi/develop.svg)](https://coveralls.io/r/metaodi/osmapi?branch=develop)
+[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
+[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)
+
 
 Python wrapper for the OSM API (requires Python >= 3.8)
 
@@ -30,6 +32,8 @@ To update the online documentation, you need to re-generate the documentation wi
 
 To test this library, please create an account on the [development server of OpenStreetMap (https://api06.dev.openstreetmap.org)](https://api06.dev.openstreetmap.org).
 
+Check the [examples directory](https://github.com/metaodi/osmapi/tree/develop/examples) to find more example code.
+
 ### Read from OpenStreetMap
 
 ```python
@@ -65,22 +69,81 @@ Note: Each line in the password file should have the format _user:password_
 >>> api.ChangesetClose()
 ```
 
-## Note
+### OAuth authentication
+
+Username/Password authentication will be deprecated in 2024 (see [official OWG announcemnt](https://www.openstreetmap.org/user/pnorman/diary/401157) for details).
+In order to use this library in the future, you'll need to use OAuth 2.0.
+
+To use OAuth 2.0, you must register an application with an OpenStreetMap account, either on the [development server](https://master.apis.dev.openstreetmap.org/oauth2/applications) or on the [production server](https://www.openstreetmap.org/oauth2/applications).
+Once this registration is done, you'll get a `client_id` and a `client_secret` that you can use to authenticate users.
+
+Example code using [`requests-oauth2client`](https://pypi.org/project/requests-oauth2client/):
+
+```python
+from requests_oauth2client import OAuth2Client, OAuth2AuthorizationCodeAuth
+import requests
+import webbrowser
+import osmapi
+import os
+
+client_id = "<client_id>"
+client_secret = "<client_secret>"
+
+# special value for redirect_uri for non-web applications
+redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
+
+authorization_base_url = "https://master.apis.dev.openstreetmap.org/oauth2/authorize"
+token_url = "https://master.apis.dev.openstreetmap.org/oauth2/token"
+
+oauth2client = OAuth2Client(
+    token_endpoint=token_url,
+    authorization_endpoint=authorization_base_url,
+    redirect_uri=redirect_uri,
+    client_id=client_id,
+    client_secret=client_secret,
+    code_challenge_method=None,
+)
+
+# open OSM website to authrorize user using the write_api and write_notes scope
+scope = ["write_api", "write_notes"]
+az_request = oauth2client.authorization_request(scope=scope)
+print(f"Authorize user using this URL: {az_request.uri}")
+webbrowser.open(az_request.uri)
+
+# create a new requests session using the OAuth authorization
+auth_code = input("Paste the authorization code here: ")
+auth = OAuth2AuthorizationCodeAuth(
+    oauth2client,
+    auth_code,
+    redirect_uri=redirect_uri,
+)
+oauth_session = requests.Session()
+oauth_session.auth = auth
+
+# use the custom session
+api = osmapi.OsmApi(
+    api="https://api06.dev.openstreetmap.org",
+    session=oauth_session
+)
+with api.Changeset({"comment": "My first test"}) as changeset_id:
+    print(f"Part of Changeset {changeset_id}")
+    node1 = api.NodeCreate({"lon": 1, "lat": 1, "tag": {}})
+    print(node1)
+```
+
+## Note about imports / automated edits
 
 Scripted imports and automated edits should only be carried out by those with experience and understanding of the way the OpenStreetMap community creates maps, and only with careful **planning** and **consultation** with the local community.
 
 See the [Import/Guidelines](http://wiki.openstreetmap.org/wiki/Import/Guidelines) and [Automated Edits/Code of Conduct](http://wiki.openstreetmap.org/wiki/Automated_Edits/Code_of_Conduct) for more information.
 
-### Development
+## Development
 
 If you want to help with the development of `osmapi`, you should clone this repository and install the requirements:
 
-    pip install -r requirements.txt
-    pip install -r test-requirements.txt
+    make deps
 
-After that, it is recommended to install the pre-commit-hooks (flake8, black):
-
-    pre-commit install
+Better yet use the provided [`setup.sh`](https://github.com/metaodi/osmapi/blob/develop/setup.sh) script to create a virtual env and install this package in it. 
 
 You can lint the source code using this command:
 
@@ -90,8 +153,6 @@ And if you want to reformat the files (using the black code style) simply run:
 
     make format
 
-### Tests
-
 To run the tests use the following command:
 
     make test


=====================================
examples/oauth2.py
=====================================
@@ -0,0 +1,52 @@
+# install oauthlib for requests:  pip install requests-oauth2client
+from requests_oauth2client import OAuth2Client, OAuth2AuthorizationCodeAuth
+import requests
+import webbrowser
+import osmapi
+from dotenv import load_dotenv, find_dotenv
+import os
+
+load_dotenv(find_dotenv())
+
+# Credentials you get from registering a new application
+# register here: https://master.apis.dev.openstreetmap.org/oauth2/applications
+# or on production: https://www.openstreetmap.org/oauth2/applications
+client_id = os.getenv("OSM_OAUTH_CLIENT_ID")
+client_secret = os.getenv("OSM_OAUTH_CLIENT_SECRET")
+
+# special value for redirect_uri for non-web applications
+redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
+
+authorization_base_url = "https://master.apis.dev.openstreetmap.org/oauth2/authorize"
+token_url = "https://master.apis.dev.openstreetmap.org/oauth2/token"
+
+oauth2client = OAuth2Client(
+    token_endpoint=token_url,
+    authorization_endpoint=authorization_base_url,
+    redirect_uri=redirect_uri,
+    auth=(client_id, client_secret),
+    code_challenge_method=None,
+)
+
+# open OSM website to authrorize user using the write_api and write_notes scope
+scope = ["write_api", "write_notes"]
+az_request = oauth2client.authorization_request(scope=scope)
+print(f"Authorize user using this URL: {az_request.uri}")
+webbrowser.open(az_request.uri)
+
+# create a new requests session using the OAuth authorization
+auth_code = input("Paste the authorization code here: ")
+auth = OAuth2AuthorizationCodeAuth(
+    oauth2client,
+    auth_code,
+    redirect_uri=redirect_uri,
+)
+oauth_session = requests.Session()
+oauth_session.auth = auth
+
+# use the custom session
+api = osmapi.OsmApi(api="https://api06.dev.openstreetmap.org", session=oauth_session)
+with api.Changeset({"comment": "My first test"}) as changeset_id:
+    print(f"Part of Changeset {changeset_id}")
+    node1 = api.NodeCreate({"lon": 1, "lat": 1, "tag": {}})
+    print(node1)


=====================================
examples/oauth2_backend.py
=====================================
@@ -0,0 +1,118 @@
+# This script shows how to authenticate with OAuth2 with a backend application
+# The token is saved to disk in $HOME/.osmapi/token.json
+# It can be reused until it's revoked or expired.
+
+# install oauthlib for requests:  pip install oauthlib requests-oauthlib
+from requests_oauthlib import OAuth2Session
+import json
+import webbrowser
+import osmapi
+from dotenv import load_dotenv, find_dotenv
+import os
+import sys
+
+load_dotenv(find_dotenv())
+
+# Credentials you get from registering a new application
+# register here: https://master.apis.dev.openstreetmap.org/oauth2/applications
+# or on production: https://www.openstreetmap.org/oauth2/applications
+client_id = os.getenv("OSM_OAUTH_CLIENT_ID")
+client_secret = os.getenv("OSM_OAUTH_CLIENT_SECRET")
+
+# special value for redirect_uri for non-web applications
+redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
+
+authorization_base_url = "https://master.apis.dev.openstreetmap.org/oauth2/authorize"
+token_url = "https://master.apis.dev.openstreetmap.org/oauth2/token"
+scope = ["write_api", "write_notes"]
+
+
+def get_osmapi_path():
+    base_dir = ""
+
+    if os.getenv("HOME"):
+        base_dir = os.getenv("HOME")
+    elif os.getenv("HOMEDRIVE") and os.getenv("HOMEPATH"):
+        base_dir = os.path.join(os.getenv("HOMEDRIVE"), os.getenv("HOMEPATH"))
+    elif os.getenv("USERPROFILE"):
+        base_dir = os.getenv("USERPROFILE")
+
+    if not base_dir:
+        print(
+            "Unable to find home directory (check env vars HOME, HOMEDRIVE, HOMEPATH and USERPROFILE)",  # noqa
+            file=sys.stderr,
+        )
+        raise Exception("Home directory not found")
+
+    return os.path.join(base_dir, ".osmapi")
+
+
+def token_saver(token):
+    osmapi_path = get_osmapi_path()
+    token_path = os.path.join(osmapi_path, "token.json")
+
+    with open(token_path, "w") as f:
+        print(f"Saving token {token} to {token_path}")
+        f.write(json.dumps(token))
+
+
+def token_loader():
+    osmapi_path = get_osmapi_path()
+    token_path = os.path.join(osmapi_path, "token.json")
+
+    with open(token_path, "r") as f:
+        token = json.loads(f.read())
+        print(f"Loaded token {token} from {token_path}")
+    return token
+
+
+def save_and_get_access_token(client_id, client_secret, redirect_uri, scope):
+    oauth = OAuth2Session(
+        client_id=client_id,
+        redirect_uri=redirect_uri,
+        scope=scope,
+    )
+
+    login_url, _ = oauth.authorization_url(authorization_base_url)
+
+    print(f"Authorize user using this URL: {login_url}")
+    webbrowser.open(login_url)
+
+    authorization_code = input("Paste the authorization code here: ")
+
+    token = oauth.fetch_token(
+        token_url=token_url,
+        client_secret=client_secret,
+        code=authorization_code,
+    )
+
+    token_saver(token)
+    return token
+
+
+def make_osm_change(oauth_session):
+    api = osmapi.OsmApi(
+        api="https://api06.dev.openstreetmap.org", session=oauth_session
+    )
+    with api.Changeset({"comment": "My first test"}) as changeset_id:
+        print(f"Part of Changeset {changeset_id}")
+        node1 = api.NodeCreate({"lon": 1, "lat": 1, "tag": {}})
+        print(node1)
+
+
+# load a previously saved token
+try:
+    token = token_loader()
+except FileNotFoundError:
+    print("Token not found, get a new one...")
+    token = save_and_get_access_token(client_id, client_secret, redirect_uri, scope)
+
+# test the token
+try:
+    oauth_session = OAuth2Session(client_id, token=token)
+    make_osm_change(oauth_session)
+except osmapi.errors.UnauthorizedApiError:
+    print("Token expired, let's create a new one")
+    token = save_and_get_access_token(client_id, client_secret, redirect_uri, scope)
+    oauth_session = OAuth2Session(client_id, token=token)
+    make_osm_change(oauth_session)


=====================================
osmapi/__init__.py
=====================================
@@ -1,4 +1,4 @@
-__version__ = "4.0.0"
+__version__ = "4.1.0"
 
 from .OsmApi import *  # noqa
 from .errors import *  # noqa


=====================================
osmapi/errors.py
=====================================
@@ -69,6 +69,15 @@ class ApiError(OsmApiError):
         return f"Request failed: {self.status} - {self.reason} - {self.payload}"
 
 
+class UnauthorizedApiError(ApiError):
+    """
+    Error when the API returned an Unauthorized error,
+    e.g. when the provided OAuth token is expired
+    """
+
+    pass
+
+
 class AlreadySubscribedApiError(ApiError):
     """
     Error when a user tries to subscribe to a changeset


=====================================
osmapi/http.py
=====================================
@@ -17,7 +17,13 @@ class OsmApiSession:
     def __init__(self, base_url, created_by, auth=None, session=None):
         self._api = base_url
         self._created_by = created_by
-        self._auth = auth
+
+        try:
+            self._auth = auth
+            if not auth and session.auth:
+                self._auth = session.auth
+        except AttributeError:
+            pass
 
         self._http_session = session
         self._session = self._get_http_session()
@@ -65,6 +71,10 @@ class OsmApiSession:
         response = self._session.request(method, path, data=send)
         if response.status_code != 200:
             payload = response.content.strip()
+            if response.status_code == 401:
+                raise errors.UnauthorizedApiError(
+                    response.status_code, response.reason, payload
+                )
             if response.status_code == 404:
                 raise errors.ElementNotFoundApiError(
                     response.status_code, response.reason, payload


=====================================
requirements.txt
=====================================
@@ -1,4 +1,4 @@
 pdoc==8.0.1
-Pygments==2.10.0
+Pygments==2.15.0
 requests==2.31.0
 python-dotenv


=====================================
setup.sh
=====================================
@@ -3,7 +3,5 @@
 [ ! -d pyenv ] && python -m venv pyenv
 source pyenv/bin/activate
 
-pip install --upgrade pip
-pip install -r requirements.txt
-pip install -r test-requirements.txt
+make deps
 pip install -e .


=====================================
test-requirements.txt
=====================================
@@ -1,5 +1,4 @@
 flake8
-mock
 virtualenv
 xmltodict
 pytest


=====================================
tests/changeset_test.py
=====================================
@@ -75,10 +75,10 @@ def test_ChangesetUpdate(auth_api, add_response):
     result = auth_api.ChangesetUpdate({"test": "foobar"})
     changeset_xml = xmltosorteddict(
         b'<?xml version="1.0" encoding="UTF-8"?>\n'
-        b'<osm version="0.6" generator="osmapi/4.0.0">\n'
+        b'<osm version="0.6" generator="osmapi/4.1.0">\n'
         b'  <changeset visible="true">\n'
         b'    <tag k="test" v="foobar"/>\n'
-        b'    <tag k="created_by" v="osmapi/4.0.0"/>\n'
+        b'    <tag k="created_by" v="osmapi/4.1.0"/>\n'
         b"  </changeset>\n"
         b"</osm>\n"
     )
@@ -98,7 +98,7 @@ def test_ChangesetUpdate_with_created_by(auth_api, add_response):
     result = auth_api.ChangesetUpdate({"test": "foobar", "created_by": "MyTestOSMApp"})
     changeset_xml = xmltosorteddict(
         b'<?xml version="1.0" encoding="UTF-8"?>\n'
-        b'<osm version="0.6" generator="osmapi/4.0.0">\n'
+        b'<osm version="0.6" generator="osmapi/4.1.0">\n'
         b'  <changeset visible="true">\n'
         b'    <tag k="test" v="foobar"/>\n'
         b'    <tag k="created_by" v="MyTestOSMApp"/>\n'
@@ -122,10 +122,10 @@ def test_ChangesetCreate(auth_api, add_response):
 
     changeset_xml = xmltosorteddict(
         b'<?xml version="1.0" encoding="UTF-8"?>\n'
-        b'<osm version="0.6" generator="osmapi/4.0.0">\n'
+        b'<osm version="0.6" generator="osmapi/4.1.0">\n'
         b'  <changeset visible="true">\n'
         b'    <tag k="foobar" v="A new test changeset"/>\n'
-        b'    <tag k="created_by" v="osmapi/4.0.0"/>\n'
+        b'    <tag k="created_by" v="osmapi/4.1.0"/>\n'
         b"  </changeset>\n"
         b"</osm>\n"
     )
@@ -145,7 +145,7 @@ def test_ChangesetCreate_with_created_by(auth_api, add_response):
 
     changeset_xml = xmltosorteddict(
         b'<?xml version="1.0" encoding="UTF-8"?>\n'
-        b'<osm version="0.6" generator="osmapi/4.0.0">\n'
+        b'<osm version="0.6" generator="osmapi/4.1.0">\n'
         b'  <changeset visible="true">\n'
         b'    <tag k="foobar" v="A new test changeset"/>\n'
         b'    <tag k="created_by" v="CoolTestApp"/>\n'
@@ -218,7 +218,7 @@ def test_ChangesetUpload_create_node(auth_api, add_response):
 
     upload_xml = xmltosorteddict(
         b'<?xml version="1.0" encoding="UTF-8"?>\n'
-        b'<osmChange version="0.6" generator="osmapi/4.0.0">\n'
+        b'<osmChange version="0.6" generator="osmapi/4.1.0">\n'
         b"<create>\n"
         b'  <node lat="47.123" lon="8.555" visible="true" '
         b'changeset="4444">\n'
@@ -283,7 +283,7 @@ def test_ChangesetUpload_modify_way(auth_api, add_response):
 
     upload_xml = xmltosorteddict(
         b'<?xml version="1.0" encoding="UTF-8"?>\n'
-        b'<osmChange version="0.6" generator="osmapi/4.0.0">\n'
+        b'<osmChange version="0.6" generator="osmapi/4.1.0">\n'
         b"<modify>\n"
         b'  <way id="4294967296" version="2" visible="true" '
         b'changeset="4444">\n'
@@ -354,7 +354,7 @@ def test_ChangesetUpload_delete_relation(auth_api, add_response):
 
     upload_xml = xmltosorteddict(
         b'<?xml version="1.0" encoding="UTF-8"?>\n'
-        b'<osmChange version="0.6" generator="osmapi/4.0.0">\n'
+        b'<osmChange version="0.6" generator="osmapi/4.1.0">\n'
         b"<delete>\n"
         b'  <relation id="676" version="2" visible="true" '
         b'changeset="4444">\n'
@@ -490,8 +490,8 @@ def test_ChangesetDownloadContainingUnicode(api, add_response):
                 "tag": {
                     "highway": "service",
                     # UTF-8 encoded 'LATIN SMALL LETTER O WITH STROKE'
-                    # Aka. 0xf8 in latin-1/ISO 8859-1
-                    "name": b"S\xc3\xb8nderskovvej".decode("utf-8"),
+                    # Aka. 0xf8 in latin-1/ISO 8859-1             Emoji: 😀
+                    "name": b"S\xc3\xb8nderskovvej".decode("utf-8") + " \U0001f600",
                     "service": "driveway",
                 },
                 "timestamp": datetime.datetime(2016, 2, 23, 16, 55, 35),


=====================================
tests/fixtures/test_ChangesetDownloadContainingUnicode.xml
=====================================
@@ -10,7 +10,7 @@
       <nd ref="4022271567"/>
       <nd ref="4022271565"/>
       <tag k="highway" v="service"/>
-      <tag k="name" v="Sønderskovvej"/>
+      <tag k="name" v="Sønderskovvej 😀"/>
       <tag k="service" v="driveway"/>
     </way>
   </create>


=====================================
tests/fixtures/test_NodeCreate_with_session_auth.xml
=====================================
@@ -0,0 +1 @@
+3322


=====================================
tests/fixtures/test_NodeCreate_wo_auth.xml
=====================================
@@ -0,0 +1 @@
+123


=====================================
tests/node_test.py
=====================================
@@ -2,6 +2,7 @@ from . import osmapi_test
 import osmapi
 from unittest import mock
 import datetime
+from requests.auth import HTTPBasicAuth
 
 
 class TestOsmApiNode(osmapi_test.TestOsmApi):
@@ -158,6 +159,41 @@ class TestOsmApiNode(osmapi_test.TestOsmApi):
         ):
             self.api.NodeCreate(test_node)
 
+    def test_NodeCreate_unauthorized(self):
+        self._session_mock(auth=True, status=401)
+
+        # setup mock
+        self.api.ChangesetCreate = mock.Mock(return_value=1111)
+        self.api._CurrentChangesetId = 1111
+        test_node = {
+            "lat": 47.287,
+            "lon": 8.765,
+            "tag": {"amenity": "place_of_worship", "religion": "pastafarian"},
+        }
+
+        with self.assertRaises(osmapi.UnauthorizedApiError):
+            self.api.NodeCreate(test_node)
+
+    def test_NodeCreate_with_session_auth(self):
+        self._session_mock()
+        self.session_mock.auth = HTTPBasicAuth("user", "pass")
+
+        api = osmapi.OsmApi(api=self.api_base, session=self.session_mock)
+
+        # setup mock
+        api.ChangesetCreate = mock.Mock(return_value=1111)
+        api._CurrentChangesetId = 1111
+        test_node = {
+            "lat": 47.287,
+            "lon": 8.765,
+            "tag": {"amenity": "place_of_worship", "religion": "pastafarian"},
+        }
+
+        cs = api.ChangesetCreate({"comment": "This is a test dataset"})
+        self.assertEqual(cs, 1111)
+        result = api.NodeCreate(test_node)
+        self.assertEqual(result["id"], 3322)
+
     def test_NodeCreate_with_exception(self):
         self._session_mock(auth=True)
         self.api._session._http_request = mock.Mock(side_effect=Exception)


=====================================
tests/notes_test.py
=====================================
@@ -1,11 +1,7 @@
 from . import osmapi_test
 from datetime import datetime
 import osmapi
-
-try:
-    import urlparse
-except ImportError:
-    from urllib import parse as urlparse
+from urllib import parse as urlparse
 
 
 class TestOsmApiNotes(osmapi_test.TestOsmApi):


=====================================
tests/osmapi_test.py
=====================================
@@ -2,6 +2,7 @@ from osmapi import OsmApi
 from unittest import mock
 import os
 import unittest
+import codecs
 
 __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
 
@@ -26,6 +27,7 @@ class TestOsmApi(unittest.TestCase):
 
         self.session_mock = mock.Mock()
         self.session_mock.request = mock.Mock(return_value=response_mock)
+        self.session_mock.auth = None
 
         if auth:
             self.api = OsmApi(
@@ -48,7 +50,7 @@ class TestOsmApi(unittest.TestCase):
         for filename in filenames:
             path = os.path.join(__location__, "fixtures", filename)
             try:
-                with open(path) as file:
+                with codecs.open(path, "r", "utf-8") as file:
                     return_values.append(file.read())
             except Exception:
                 pass


=====================================
tests/way_test.py
=====================================
@@ -1,7 +1,6 @@
-from __future__ import unicode_literals, absolute_import
 from . import osmapi_test
 import osmapi
-import mock
+from unittest import mock
 import datetime
 
 



View it on GitLab: https://salsa.debian.org/debian-gis-team/python-osmapi/-/commit/7603908e57fd5d1c0ef1ff71b3ce837b04ab7527

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/python-osmapi/-/commit/7603908e57fd5d1c0ef1ff71b3ce837b04ab7527
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20240319/11bee845/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list