diff --git a/overpy/__init__.py b/overpy/__init__.py index 78072e9..13de583 100644 --- a/overpy/__init__.py +++ b/overpy/__init__.py @@ -84,6 +84,22 @@ def __init__(self, read_chunk_size=None, url=None, xml_parser=XML_PARSER_SAX, ma self.xml_parser = xml_parser + def _handle_remark_msg(self, msg): + """ + Try to parse the message provided with the remark tag or element. + + :param str msg: The message + :raises overpy.exception.OverpassRuntimeError: If message starts with 'runtime error:' + :raises overpy.exception.OverpassRuntimeRemark: If message starts with 'runtime remark:' + :raises overpy.exception.OverpassUnknownError: If we are unable to identify the error + """ + msg = msg.strip() + if msg.startswith("runtime error:"): + raise exception.OverpassRuntimeError(msg=msg) + elif msg.startswith("runtime remark:"): + raise exception.OverpassRuntimeRemark(msg=msg) + raise exception.OverpassUnknownError(msg=msg) + def query(self, query): """ Query the Overpass API @@ -189,6 +205,8 @@ def parse_json(self, data, encoding="utf-8"): if isinstance(data, bytes): data = data.decode(encoding) data = json.loads(data, parse_float=Decimal) + if "remark" in data: + self._handle_remark_msg(msg=data.get("remark")) return Result.from_json(data, api=self) def parse_xml(self, data, encoding="utf-8", parser=None): @@ -210,6 +228,10 @@ def parse_xml(self, data, encoding="utf-8", parser=None): # Python 2.x: Convert unicode strings data = data.encode(encoding) + m = re.compile("(?P[^<>]*)").search(data) + if m: + self._handle_remark_msg(m.group("msg")) + return Result.from_xml(data, api=self, parser=parser) diff --git a/overpy/exception.py b/overpy/exception.py index c529364..3d8416a 100644 --- a/overpy/exception.py +++ b/overpy/exception.py @@ -74,6 +74,29 @@ def __str__(self): return "\n".join(tmp_msgs) +class OverpassError(OverPyException): + """ + Base exception to report errors if the response returns a remark tag or element. + + .. note:: + If you are not sure which of the subexceptions you should use, use this one and try to parse the message. + + For more information have a look at https://github.com/DinoTools/python-overpy/issues/62 + + :param str msg: The message from the remark tag or element + """ + def __init__(self, msg=None): + #: The message from the remark tag or element + self.msg = msg + + def __str__(self): + if self.msg is None: + return "No error message provided" + if not isinstance(self.msg, str): + return str(self.msg) + return self.msg + + class OverpassGatewayTimeout(OverPyException): """ Raised if load of the Overpass API service is too high and it can't handle the request. @@ -82,6 +105,22 @@ def __init__(self): OverPyException.__init__(self, "Server load too high") +class OverpassRuntimeError(OverpassError): + """ + Raised if the server returns a remark-tag(xml) or remark element(json) with a message starting with + 'runtime error:'. + """ + pass + + +class OverpassRuntimeRemark(OverpassError): + """ + Raised if the server returns a remark-tag(xml) or remark element(json) with a message starting with + 'runtime remark:'. + """ + pass + + class OverpassTooManyRequests(OverPyException): """ Raised if the Overpass API service returns a 429 status code. @@ -106,6 +145,13 @@ def __str__(self): return "Unknown content type: %s" % self.content_type +class OverpassUnknownError(OverpassError): + """ + Raised if the server returns a remark-tag(xml) or remark element(json) and we are unable to find any reason. + """ + pass + + class OverpassUnknownHTTPStatusCode(OverPyException): """ Raised if the returned HTTP status code isn't handled by OverPy. diff --git a/tests/json/remark-runtime-error-01.json b/tests/json/remark-runtime-error-01.json new file mode 100644 index 0000000..83b54cb --- /dev/null +++ b/tests/json/remark-runtime-error-01.json @@ -0,0 +1,15 @@ +{ + "version": 0.6, + "generator": "Overpass API", + "osm3s": { + "timestamp_osm_base": "2017-03-17T22:05:02Z", + "timestamp_areas_base": "2017-03-17T18:38:02Z", + "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." + }, + "elements": [ + + + + ], +"remark": "runtime error: Query timed out in \"query\" at line 4 after 2 seconds." +} diff --git a/tests/json/remark-runtime-remark-01.json b/tests/json/remark-runtime-remark-01.json new file mode 100644 index 0000000..fc21c88 --- /dev/null +++ b/tests/json/remark-runtime-remark-01.json @@ -0,0 +1,15 @@ +{ + "version": 0.6, + "generator": "Overpass API", + "osm3s": { + "timestamp_osm_base": "2017-03-17T22:05:02Z", + "timestamp_areas_base": "2017-03-17T18:38:02Z", + "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." + }, + "elements": [ + + + + ], +"remark": "runtime remark: Test" +} diff --git a/tests/json/remark-unknown-01.json b/tests/json/remark-unknown-01.json new file mode 100644 index 0000000..815b770 --- /dev/null +++ b/tests/json/remark-unknown-01.json @@ -0,0 +1,15 @@ +{ + "version": 0.6, + "generator": "Overpass API", + "osm3s": { + "timestamp_osm_base": "2017-03-17T22:05:02Z", + "timestamp_areas_base": "2017-03-17T18:38:02Z", + "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL." + }, + "elements": [ + + + + ], +"remark": "Test remark" +} diff --git a/tests/test_exception.py b/tests/test_exception.py index 0c79064..4b4dd18 100644 --- a/tests/test_exception.py +++ b/tests/test_exception.py @@ -39,4 +39,26 @@ def test_overpass_unknown_content_type(self): def test_overpass_unknown_http_status_code(self): e = overpy.exception.OverpassUnknownHTTPStatusCode(123) assert e.code == 123 - assert str(e).endswith("123") \ No newline at end of file + assert str(e).endswith("123") + + def test_overpass_error(self): + exceptions = [ + overpy.exception.OverpassError, + overpy.exception.OverpassRuntimeError, + overpy.exception.OverpassRuntimeRemark, + overpy.exception.OverpassUnknownError + ] + for cls in exceptions: + e = cls(msg="Test message") + assert e.msg == "Test message" + assert str(e) == "Test message" + + for cls in exceptions: + e = cls() + assert e.msg is None + assert str(e) == "No error message provided" + + for cls in exceptions: + e = cls(msg=123) + assert e.msg == 123 + assert str(e) == "123" diff --git a/tests/test_json.py b/tests/test_json.py index 60a1a0a..287250a 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -98,4 +98,21 @@ def test_element_wrong_type(self): { "type": "foo" } - ) \ No newline at end of file + ) + + +class TestRemark(object): + def test_remark_runtime_error(self): + api = overpy.Overpass() + with pytest.raises(overpy.exception.OverpassRuntimeError): + api.parse_json(read_file("json/remark-runtime-error-01.json")) + + def test_remark_runtime_remark(self): + api = overpy.Overpass() + with pytest.raises(overpy.exception.OverpassRuntimeRemark): + api.parse_json(read_file("json/remark-runtime-remark-01.json")) + + def test_remark_unknown(self): + api = overpy.Overpass() + with pytest.raises(overpy.exception.OverpassUnknownError): + api.parse_json(read_file("json/remark-unknown-01.json")) \ No newline at end of file diff --git a/tests/test_xml.py b/tests/test_xml.py index c25e836..d81f003 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -169,3 +169,20 @@ def test_way_missing_data(self): node = ET.fromstring(data) with pytest.raises(ValueError): overpy.Way.from_xml(node) + + +class TestRemark(object): + def test_remark_runtime_error(self): + api = overpy.Overpass() + with pytest.raises(overpy.exception.OverpassRuntimeError): + api.parse_xml(read_file("xml/remark-runtime-error-01.xml")) + + def test_remark_runtime_remark(self): + api = overpy.Overpass() + with pytest.raises(overpy.exception.OverpassRuntimeRemark): + api.parse_xml(read_file("xml/remark-runtime-remark-01.xml")) + + def test_remark_unknown(self): + api = overpy.Overpass() + with pytest.raises(overpy.exception.OverpassUnknownError): + api.parse_xml(read_file("xml/remark-unknown-01.xml")) diff --git a/tests/xml/remark-runtime-error-01.xml b/tests/xml/remark-runtime-error-01.xml new file mode 100644 index 0000000..5aa0888 --- /dev/null +++ b/tests/xml/remark-runtime-error-01.xml @@ -0,0 +1,8 @@ + + +The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. + + + runtime error: Query timed out in "query" at line 4 after 2 seconds. + + diff --git a/tests/xml/remark-runtime-remark-01.xml b/tests/xml/remark-runtime-remark-01.xml new file mode 100644 index 0000000..254fe97 --- /dev/null +++ b/tests/xml/remark-runtime-remark-01.xml @@ -0,0 +1,8 @@ + + +The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. + + + runtime remark: Test + + diff --git a/tests/xml/remark-unknown-01.xml b/tests/xml/remark-unknown-01.xml new file mode 100644 index 0000000..ad8a047 --- /dev/null +++ b/tests/xml/remark-unknown-01.xml @@ -0,0 +1,8 @@ + + +The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. + + + Test remark + +