From 48b5fa416f619df37e792e2d32a6bb72c03762be Mon Sep 17 00:00:00 2001 From: = Date: Wed, 4 Nov 2020 17:38:38 +0100 Subject: [PATCH 1/3] fix authenticate method --- sparql.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sparql.py b/sparql.py index 1ec2807..5f15127 100755 --- a/sparql.py +++ b/sparql.py @@ -47,7 +47,7 @@ Otherwise, the query is read from standard input. """ -from base64 import encodestring +from base64 import b64encode from six.moves import input, map from six.moves.urllib.parse import urlencode from xml.dom import pulldom @@ -416,9 +416,8 @@ def query(self, query, timeout=0, raw=False): return q.query(query, timeout, raw=raw) def authenticate(self, username, password): - # self._headers_map['Authorization'] = "Basic %s" % replace( - # encodestring("%s:%s" % (username, password)), "\012", "") - head = "Basic %s" % encodestring("%s:%s" % (username, password)).replace("\012", "") + cred = b64encode("{}:{}".format(username, password).encode()).decode() + head = "Basic {}".format(cred) self._headers_map['Authorization'] = head From 5d87e6c57f7d6a6b19fe75d5d06b255cb5f80a61 Mon Sep 17 00:00:00 2001 From: dhvandenakker Date: Fri, 18 Jun 2021 16:27:22 +0200 Subject: [PATCH 2/3] adds encoding argument when opening datafile --- sparql-client/tests/testparser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sparql-client/tests/testparser.py b/sparql-client/tests/testparser.py index 34abcc8..1c02fc2 100644 --- a/sparql-client/tests/testparser.py +++ b/sparql-client/tests/testparser.py @@ -11,7 +11,7 @@ def _open_datafile(name): - return open(os.path.join(os.path.dirname(__file__), name)) + return open(os.path.join(os.path.dirname(__file__), name), encoding="utf-8") XSD_FAO_MILLION = 'http://aims.fao.org/aos/geopolitical.owl#MillionUSD' From 8bab974a7c5dca45ba0961bd61b7d15d16cb3ae5 Mon Sep 17 00:00:00 2001 From: dhvandenakker Date: Fri, 18 Jun 2021 16:31:45 +0200 Subject: [PATCH 3/3] fix parse_n3_term for py38 --- sparql.py | 85 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/sparql.py b/sparql.py index 5f15127..c28eabc 100755 --- a/sparql.py +++ b/sparql.py @@ -58,6 +58,7 @@ import tempfile import eventlet import six + if six.PY2: from eventlet.green import urllib2 as ev_request import compiler @@ -73,19 +74,18 @@ USER_AGENT = "sparql-client/%s +https://www.eionet.europa.eu/software/sparql-client/" % __version__ CONTENT_TYPE = { - 'turtle': "application/turtle", - 'n3': "application/n3", - 'rdfxml': "application/rdf+xml", - 'ntriples': "application/n-triples", - 'xml': "application/xml" - } - + 'turtle': "application/turtle", + 'n3': "application/n3", + 'rdfxml': "application/rdf+xml", + 'ntriples': "application/n-triples", + 'xml': "application/xml" +} RESULTS_TYPES = { - 'xml': "application/sparql-results+xml", - 'xmlschema': "application/x-ms-access-export+xml", - 'json': "application/sparql-results+json" - } + 'xml': "application/sparql-results+xml", + 'xmlschema': "application/x-ms-access-export+xml", + 'json': "application/sparql-results+json" +} # The purpose of this construction is to use shared strings when # they have the same value. This way comparisons can happen on the @@ -103,25 +103,25 @@ XSD_BOOLEAN = 'http://www.w3.org/2001/XMLSchema#boolean' datatype_dict = { - '': '', - XSD_STRING: XSD_STRING, - XSD_INT: XSD_INT, - XSD_LONG: XSD_LONG, - XSD_DOUBLE: XSD_DOUBLE, - XSD_FLOAT: XSD_FLOAT, - XSD_INTEGER: XSD_INTEGER, - XSD_DECIMAL: XSD_DECIMAL, - XSD_DATETIME: XSD_DATETIME, - XSD_DATE: XSD_DATE, - XSD_TIME: XSD_TIME, - XSD_BOOLEAN: XSD_BOOLEAN - } + '': '', + XSD_STRING: XSD_STRING, + XSD_INT: XSD_INT, + XSD_LONG: XSD_LONG, + XSD_DOUBLE: XSD_DOUBLE, + XSD_FLOAT: XSD_FLOAT, + XSD_INTEGER: XSD_INTEGER, + XSD_DECIMAL: XSD_DECIMAL, + XSD_DATETIME: XSD_DATETIME, + XSD_DATE: XSD_DATE, + XSD_TIME: XSD_TIME, + XSD_BOOLEAN: XSD_BOOLEAN +} # allow import from RestrictedPython __allow_access_to_unprotected_subobjects__ = { - 'Datatype': 1, 'unpack_row': 1, - 'RDFTerm': 1, 'IRI': 1, - 'Literal': 1, 'BlankNode': 1 + 'Datatype': 1, 'unpack_row': 1, + 'RDFTerm': 1, 'IRI': 1, + 'Literal': 1, 'BlankNode': 1 } @@ -183,6 +183,7 @@ def __eq__(self, other): def n3(self): return '<%s>' % self.value + _n3_quote_char = re.compile(r'[^ -~]|["\\]') _n3_quote_map = { '"': '\\"', @@ -198,6 +199,7 @@ def escape(m): if ch in _n3_quote_map: return _n3_quote_map[ch] return "\\u%04x" % ord(ch) + return '"' + _n3_quote_char.sub(escape, string) + '"' @@ -205,6 +207,7 @@ class Literal(RDFTerm): """ Literals. These can take a data type or a language code. """ + def __init__(self, value, datatype=None, lang=None): self.value = six.text_type(value) self.lang = lang @@ -235,6 +238,7 @@ def n3(self): class BlankNode(RDFTerm): """ Blank node. Similar to `IRI` but lacks a stable identifier. """ + def __init__(self, value): self.value = value @@ -248,6 +252,7 @@ def __eq__(self, other): def n3(self): return '_:%s' % self.value + _n3parser_lang = re.compile(r'@(?P\w+)$') _n3parser_datatype = re.compile(r'\^\^<(?P[^\^"\'>]+)>$') @@ -318,12 +323,14 @@ def parse_n3_term(src): raise ValueError assign_node = ast.body[0] - if len(assign_node._fields) != 2: + # type_comment is added since 3.8, discard it from condition check + if len(assign_node._fields) != 2 and len(tuple(f for f in assign_node._fields if f != "type_comment")) != 2: raise ValueError value_node = assign_node.value if len(value_node._fields) != 1: - raise ValueError + if not isinstance(value_node, astcompiler.Constant): + raise ValueError # if value_node.__class__ != ast.Constant(): # raise ValueError @@ -334,6 +341,7 @@ def parse_n3_term(src): return Literal(value, datatype, lang) + ######################################### # # _ServiceMixin @@ -385,6 +393,7 @@ def prefixes(self): def headers(self): return self._headers_map + ######################################### # # Service @@ -398,6 +407,7 @@ class Service(_ServiceMixin): The user creates a :class:`Service`, then sends a query to it. If we want to have persistent connections, then open them here. """ + def __init__(self, endpoint, qs_encoding="utf-8", method="POST", accept="application/sparql-results+xml"): _ServiceMixin.__init__(self, endpoint, method, accept) @@ -432,13 +442,14 @@ def _parseBoolean(val): XSD_DOUBLE: float, XSD_FLOAT: float, XSD_INTEGER: int, # INTEGER is a DECIMAL, but Python `int` has no size - # limit, so it's safe to use + # limit, so it's safe to use XSD_DECIMAL: decimal.Decimal, XSD_BOOLEAN: _parseBoolean, } try: import dateutil.parser + _types[XSD_DATETIME] = dateutil.parser.parse _types[XSD_DATE] = lambda v: dateutil.parser.parse(v).date() _types[XSD_TIME] = lambda v: dateutil.parser.parse(v).time() @@ -483,6 +494,7 @@ def unpack_row(row, convert=None, convert_type={}): out.append(value) return out + ######################################### # # _Query @@ -605,6 +617,7 @@ class RedirectHandler(ev_request.HTTPRedirectHandler): """ Subclass the HTTPRedirectHandler to re-contruct request when follow redirect """ + def redirect_request(self, req, fp, code, msg, headers, newurl): if code in (301, 302, 303, 307): raise SparqlException(code, newurl) @@ -618,11 +631,11 @@ class _ResultsParser(object): """ __allow_access_to_unprotected_subobjects__ = { - 'fetchone': 1, - 'fetchmany': 1, - 'fetchall': 1, - 'hasresult': 1, - 'variables': 1 + 'fetchone': 1, + 'fetchmany': 1, + 'fetchall': 1, + 'hasresult': 1, + 'variables': 1 } def __init__(self, fp): @@ -772,10 +785,12 @@ def _interactive(endpoint): class SparqlException(Exception): """ Sparql Exceptions """ + def __init__(self, code, message): self.code = code self.message = message + if __name__ == '__main__': import sys import codecs