-
Notifications
You must be signed in to change notification settings - Fork 578
Remove requests #1175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove requests #1175
Changes from all commits
6eb4ccd
a30fb38
7cb6bb4
e354353
0e03ec8
9461e55
80713a6
b7025f5
3c63d05
b8a2647
67b8058
f473263
4665306
1ddb2a3
c923eb5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,13 @@ | ||
| import logging | ||
| import threading | ||
| import requests | ||
|
|
||
| import os | ||
| from urllib.request import urlopen, Request | ||
| from urllib.parse import urlencode | ||
| from urllib.error import HTTPError, URLError | ||
| import base64 | ||
|
|
||
| from io import BytesIO | ||
|
|
||
| from rdflib.query import Result | ||
| from rdflib import BNode | ||
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
|
|
@@ -26,7 +27,6 @@ class SPARQLConnectorException(Exception): | |
|
|
||
|
|
||
| class SPARQLConnector(object): | ||
|
|
||
| """ | ||
| this class deals with nitty gritty details of talking to a SPARQL server | ||
| """ | ||
|
|
@@ -37,29 +37,26 @@ def __init__( | |
| update_endpoint=None, | ||
| returnFormat="xml", | ||
| method="GET", | ||
| auth=None, | ||
| **kwargs | ||
| ): | ||
| """ | ||
| Any additional keyword arguments will be passed to requests, and can be used to setup timesouts, basic auth, etc. | ||
| auth, if present, must be a tuple of (username, password) used for Basic Authentication | ||
|
|
||
| Any additional keyword arguments will be passed to to the request, and can be used to setup timesouts etc. | ||
| """ | ||
|
|
||
| self.returnFormat = returnFormat | ||
| self.query_endpoint = query_endpoint | ||
| self.update_endpoint = update_endpoint | ||
| self.kwargs = kwargs | ||
| self.method = method | ||
|
|
||
| # it is recommended to have one session object per thread/process. This assures that is the case. | ||
| # https://github.com/kennethreitz/requests/issues/1871 | ||
|
|
||
| self._session = threading.local() | ||
|
|
||
| @property | ||
| def session(self): | ||
| k = "session_%d" % os.getpid() | ||
| self._session.__dict__.setdefault(k, requests.Session()) | ||
| log.debug("Session %s %s", os.getpid(), id(self._session.__dict__[k])) | ||
| return self._session.__dict__[k] | ||
| if auth is not None: | ||
| assert type(auth) == tuple, "auth must be a tuple" | ||
| assert len(auth) == 2, "auth must be a tuple (user, password)" | ||
| base64string = base64.b64encode(bytes('%s:%s' % auth, 'ascii')) | ||
| self.kwargs.setdefault("headers", {}) | ||
| self.kwargs["headers"].update({"Authorization": "Basic %s" % base64string.decode('utf-8')}) | ||
|
|
||
| @property | ||
| def method(self): | ||
|
|
@@ -72,19 +69,18 @@ def method(self, method): | |
|
|
||
| self._method = method | ||
|
|
||
| def query(self, query, default_graph=None): | ||
|
|
||
| def query(self, query, default_graph: str = None, named_graph: str = None): | ||
| if not self.query_endpoint: | ||
| raise SPARQLConnectorException("Query endpoint not set!") | ||
|
|
||
| params = {"query": query} | ||
| if default_graph: | ||
| # this test ensures we don't have a useless (BNode) default graph URI, which calls to Graph().query() will add | ||
| if default_graph is not None and type(default_graph) != BNode: | ||
| params["default-graph-uri"] = default_graph | ||
|
|
||
| headers = {"Accept": _response_mime_types[self.returnFormat]} | ||
|
|
||
| args = dict(self.kwargs) | ||
| args.update(url=self.query_endpoint) | ||
|
|
||
| # merge params/headers dicts | ||
| args.setdefault("params", {}) | ||
|
|
@@ -94,47 +90,46 @@ def query(self, query, default_graph=None): | |
|
|
||
| if self.method == "GET": | ||
| args["params"].update(params) | ||
| qsa = "?" + urlencode(args["params"]) | ||
| try: | ||
| res = urlopen(Request(self.query_endpoint + qsa, headers=args["headers"])) | ||
| except Exception as e: | ||
| raise ValueError("You did something wrong formulating either the URI or your SPARQL query") | ||
| elif self.method == "POST": | ||
| args["headers"].update({"Content-Type": "application/sparql-query"}) | ||
| args["data"] = params | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why don't we need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We still need to submit Also I think we should support both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The If you consider the following: Load some context aware quad store (e.g. the QuitStore) with the following data: graph <http://example.org/> {
<http://example.org/ExampleInstance> a <http://example.org/Example>
}
graph <http://othergraph.org/> {
<http://example.org/OtherInstance> a <http://example.org/Example>
}The query endpoint is assumed to be available at class SPARQLStoreQuitStoreTestCase(unittest.TestCase):
store_name = "SPARQLStore"
path = "http://localhost:5000/sparql"
create = False
def setUp(self):
store = SPARQLStore(query_endpoint=self.path, method="POST")
self.conjunctivegraph = ConjunctiveGraph(store=store)
def tearDown(self):
self.conjunctivegraph.close()
def test_Query(self):
query = "select distinct ?inst where {?inst a <http://example.org/Example>}"
graph = self.conjunctivegraph.get_context(URIRef("http://example.org/"))
res = graph.query(query, initNs={})
assert len(res) == 1, len(res)
for i in res:
assert type(i[0]) == URIRef, i[0].n3()
assert i[0] == URIRef("http://example.org/ExampleInstance"), i[0].n3()The query is executed as POST request but does not convey the information about the default graph. This issue is a combination of removing this line and sending the wrong Content-Type. |
||
| try: | ||
| res = urlopen(Request(self.query_endpoint, data=query.encode(), headers=args["headers"])) | ||
white-gecko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| except HTTPError as e: | ||
| return e.code, str(e), None | ||
| else: | ||
| raise SPARQLConnectorException("Unknown method %s" % self.method) | ||
|
|
||
| res = self.session.request(self.method, **args) | ||
|
|
||
| res.raise_for_status() | ||
|
|
||
| return Result.parse( | ||
| BytesIO(res.content), content_type=res.headers["Content-type"] | ||
| BytesIO(res.read()), content_type=res.headers["Content-Type"].split(";")[0] | ||
| ) | ||
|
|
||
| def update(self, update, default_graph=None): | ||
| def update(self, query, default_graph: str = None, named_graph: str = None): | ||
| if not self.update_endpoint: | ||
| raise SPARQLConnectorException("Query endpoint not set!") | ||
|
|
||
| params = {} | ||
|
|
||
| if default_graph: | ||
| if default_graph is not None: | ||
| params["using-graph-uri"] = default_graph | ||
|
|
||
| if named_graph is not None: | ||
| params["using-named-graph-uri"] = default_graph | ||
|
|
||
| headers = { | ||
| "Accept": _response_mime_types[self.returnFormat], | ||
| "Content-Type": "application/sparql-update", | ||
| } | ||
|
|
||
| args = dict(self.kwargs) | ||
|
|
||
| args.update(url=self.update_endpoint, data=update.encode("utf-8")) | ||
| args = dict(self.kwargs) # other QSAs | ||
|
|
||
| # merge params/headers dicts | ||
| args.setdefault("params", {}) | ||
| args["params"].update(params) | ||
| args.setdefault("headers", {}) | ||
| args["headers"].update(headers) | ||
|
|
||
| res = self.session.post(**args) | ||
|
|
||
| res.raise_for_status() | ||
|
|
||
| def close(self): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because the Store is actually stateless with each operation and there's nothing to close. I originally dropped |
||
| self.session.close() | ||
| qsa = "?" + urlencode(args["params"]) | ||
| res = urlopen(Request(self.update_endpoint + qsa, data=query.encode(), headers=args["headers"])) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my eyes it is no good practice to have
assertin production code, so it should not be in a library.The Python reference states:
So I think it should be replaced by if and raise and exception.