Skip to content
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

Judge exact query type #213

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 55 additions & 45 deletions SPARQLWrapper/Wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,31 @@
import urllib.request
import warnings
from http.client import HTTPResponse
from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional, Tuple, Union, cast
from urllib.request import (
urlopen as urlopener,
) # don't change the name: tests override it
from typing import (
TYPE_CHECKING,
Any,
Dict,
Iterator,
List,
Optional,
Tuple,
Union,
cast,
)

# -- don't change the name: tests override it
from urllib.request import urlopen as urlopener
# -- don't change the name: tests override it

from xml.dom.minidom import Document, parse

from rdflib.plugins.sparql import parser

from SPARQLWrapper import __agent__

if TYPE_CHECKING:
from rdflib import Graph



from .KeyCaseInsensitiveDict import KeyCaseInsensitiveDict
from .SPARQLExceptions import (
EndPointInternalError,
Expand Down Expand Up @@ -131,7 +143,6 @@
MOVE,
ADD,
]

# Possible methods to perform requests
URLENCODED = "urlencoded"
"""to be used to set **URL encode** as the encoding method for the request.
Expand Down Expand Up @@ -248,26 +259,8 @@ class SPARQLWrapper(object):
:ivar _defaultReturnFormat: The default return format. It is used in case the same class instance is reused for
subsequent queries.
:vartype _defaultReturnFormat: string

:cvar prefix_pattern: regular expression used to remove base/prefixes in the process of determining the query type.
:vartype prefix_pattern: :class:`re.RegexObject`, a compiled regular expression. See the :mod:`re` module of Python
:cvar pattern: regular expression used to determine whether a query (without base/prefixes) is of type
:data:`CONSTRUCT`, :data:`SELECT`, :data:`ASK`, :data:`DESCRIBE`, :data:`INSERT`, :data:`DELETE`, :data:`CREATE`,
:data:`CLEAR`, :data:`DROP`, :data:`LOAD`, :data:`COPY`, :data:`MOVE` or :data:`ADD`.
:vartype pattern: :class:`re.RegexObject`, a compiled regular expression. See the :mod:`re` module of Python
:cvar comments_pattern: regular expression used to remove comments from a query.
:vartype comments_pattern: :class:`re.RegexObject`, a compiled regular expression. See the :mod:`re` module of
Python
"""

prefix_pattern = re.compile(
r"((?P<base>(\s*BASE\s*<.*?>)\s*)|(?P<prefixes>(\s*PREFIX\s+.+:\s*<.*?>)\s*))*"
)
# Maybe the future name could be queryType_pattern
pattern = re.compile(
r"(?P<queryType>(CONSTRUCT|SELECT|ASK|DESCRIBE|INSERT|DELETE|CREATE|CLEAR|DROP|LOAD|COPY|MOVE|ADD))",
re.VERBOSE | re.IGNORECASE,
)
comments_pattern = re.compile(r"(^|\n)\s*#.*?\n")

def __init__(
Expand Down Expand Up @@ -594,7 +587,7 @@ def setQuery(self, query: Union[str, bytes]) -> None:
self.queryString = query
self.queryType = self._parseQueryType(query)

def _parseQueryType(self, query: str) -> Optional[str]:
def _parseQueryType(self, query: str) -> str:
"""
Internal method for parsing the SPARQL query and return its type (ie, :data:`SELECT`, :data:`ASK`, etc).

Expand All @@ -609,28 +602,45 @@ def _parseQueryType(self, query: str) -> Optional[str]:
:return: the type of SPARQL query (aka SPARQL query form).
:rtype: string
"""
query = query if (isinstance(query, str)) else query.encode("ascii", "ignore")
tokens = None
r_queryTypes = []
try:
query = (
query if (isinstance(query, str)) else query.encode("ascii", "ignore")
)
query = self._cleanComments(query)
query_for_queryType = re.sub(self.prefix_pattern, "", query.strip())
# type error: Item "None" of "Optional[Match[str]]" has no attribute "group"
r_queryType = (
self.pattern.search(query_for_queryType).group("queryType").upper() # type: ignore[union-attr]
)
except AttributeError:
warnings.warn(
"not detected query type for query '%r'" % query.replace("\n", " "),
RuntimeWarning,
)
r_queryType = None
tokens = [
token.name.upper().replace("QUERY", "")
for token in parser.parseQuery(query)
] # type: ignore[no-untyped-call]
r_queryTypes = [token for token in tokens if token in _allowedQueryTypes]
except Exception:
try:
tokens = [
token.name.upper()
.replace("DATA", "")
.replace("WHERE", "")
.replace("CLAUSE", "")
for token in parser.parseUpdate(query).get("request", [])
] # type: ignore[no-untyped-call]
r_queryTypes = [
token for token in tokens if token in _allowedQueryTypes
]

except Exception as e:
warnings.warn(
(
"not detected query type for query '%r' "
% query.replace("\n", " ")
+ "(%s)" % e
),
RuntimeWarning,
)

if r_queryType in _allowedQueryTypes:
return r_queryType
if len(r_queryTypes) > 0:
return str(r_queryTypes[0])
else:
# raise Exception("Illegal SPARQL Query; must be one of SELECT, ASK, DESCRIBE, or CONSTRUCT")
warnings.warn("unknown query type '%s'" % r_queryType, RuntimeWarning)
warnings.warn(
"query type is not allowed or cannot be detected", RuntimeWarning
)
return SELECT

def setMethod(self, method: str) -> None:
Expand Down Expand Up @@ -693,7 +703,6 @@ def isSparqlQueryRequest(self) -> bool:
def _cleanComments(self, query: str) -> str:
"""Internal method for returning the query after all occurrence of singleline comments are removed
(issues #32 and #77).

:param query: The query.
:type query: string
:return: the query after all occurrence of singleline comments are removed.
Expand Down Expand Up @@ -1083,6 +1092,7 @@ def _convertRDF(self) -> "Graph":
:rtype: :class:`rdflib.graph.Graph`
"""
from rdflib import ConjunctiveGraph

retval = ConjunctiveGraph()
retval.parse(self.response, format="xml") # type: ignore[no-untyped-call]
return retval
Expand Down