Module urilibplus.uri_query
uri_query
Holds the URIQuery class and reated imports.
Classes
class URIQuery (content: Union[str, List[Tuple[str, str]], Dict[str, str], ForwardRef('URIQuery'), ForwardRef(None)] = None, *, unquote: bool = False, requote: bool = False, force_case: Literal['upper', 'lower', 'preserve'] = 'preserve', quote_safe: str = '')-
A class used to manipulate and use a URI query in python easily.
NOTE: while this class acts and appears very similar to a tuple of dictionary items, it is not, as it allows for duplacate keys, and requires the ordering to be preserved.
Expand source code
class URIQuery(UserList): """ `URIQuery` A class used to manipulate and use a URI query in python easily. NOTE: while this class acts and appears very similar to a tuple of dictionary items, it is not, as it allows for duplacate keys, and requires the ordering to be preserved. """ def __parse(self, querystr:str) -> List[Tuple[str, str]]: parsed = uriqueryparse(querystr, keep_blank_values=True, strict_parsing = True) return parsed if not self.unquote else [(uriunquote(k), uriunquote(v)) for k,v in parsed] def __init__(self, content:Union[str, List[Tuple[str, str]], Dict[str, str], 'URIQuery', None]= None, *, unquote:bool = False, #unquote values when unencoding strings requote: bool = False, #requote values when normalizing to string force_case: Literal["upper", "lower", "preserve"] = "preserve", quote_safe: str = "" ): self.unquote:bool = unquote self.requote:bool = requote self.quote_safe:str = quote_safe self.force_case: Literal["upper", "lower", "preserve"] = force_case self.data:List[Tuple[str, str]] if isinstance(content, (dict, URIQuery)): content = list(content.items()) elif isinstance(content, str): content = content.strip().lstrip("?").lstrip() if content == "": content = None else: if "=" not in content: #normaize a nonstandard query with a empty value while still in string form content += "=" content = self.__parse(content) super().__init__(content) def __bool__(self): return not self.isempty() def __lshift__(self, count:int) -> 'URIQuery': c = self.copy() if count > 0: for _ in range(count): c.append(c.pop(0)) else: for _ in range(abs(count)): c.insert(0, c.pop(-1)) return c def __rshift__(self, count:int) -> 'URIQuery': return self << -count def __add__(self, other:Union[str, Tuple[str, str], Iterable[Tuple[str, str]], 'URIQuery'] ) -> 'URIQuery': c = self.copy() if isinstance(other, str): other = self.__parse(other) elif isinstance(other, URIQuery): other = other.data elif (isinstance(other, tuple) and len(other) == 2 and isinstance(other[0], str) and isinstance(other[1], str) ): other = [cast(Tuple[str, str], other)] else: other = list(cast(Iterable[Tuple[str, str]], other)) c.data += other return c def __len__(self): return len(self.data) def append(self, item:Union[str, Tuple[str, str], Iterable[Tuple[str, str]]]): #we can use the __add__ opperator, as this appcompishes the same thing self.data = list(self + item) def count(self, item:Union[str, Tuple[str, str]]) -> int: if isinstance(item, str): parsed = self.__parse(item) if len(parsed) == 1: item = parsed[0] else: raise TypeError(item) return super().count(item) def index(self, #pylint:disable=arguments-differ item:Union[str, Tuple[str, str]], start:Union[int, SupportsIndex] = 0, stop:Union[int, SupportsIndex] = sys_maxsize ) -> int: if isinstance(item, str): parsed = self.__parse(item) if len(parsed) == 1: item = parsed[0] else: raise TypeError(item) return super().index(item, start, stop) def insert(self, i:int, item:Union[str, Tuple[str, str]]): if isinstance(item, str): parsed = self.__parse(item) if len(parsed) == 1: item = parsed[0] else: raise TypeError(item) super().insert(i, item) def remove(self, item:Union[str, Tuple[str, str]]): if isinstance(item, str): parsed = self.__parse(item) if len(parsed) == 1: item = parsed[0] else: raise TypeError(item) super().remove(item) def querykeys(self, *values:str) -> Iterable[str]: """ `querykeys` Returns: An iterable with all keys of this query, or only the keys with one of the exact `*values` given, if any `*values` are given. """ return (k for k,v in self if len(values) <= 0 or v in values) def queryvalues(self, *keys:str) -> Iterable[str]: """ `queryvalues` Returns: An iterable with all values of this query, or only the values with one of the exact `*keys` given, if any `*keys` are given. """ return (v for k,v in self if len(keys) <= 0 or k in keys) def items(self, *kvpairs:Tuple[str,str]) -> Iterable[Tuple[str, str]]: """ `items` Returns: An iterable with all items of this query (formated as a tuple of `("key", "value")`), or only the values with one of the exact `*kvpairs` given, if any `*kvpairs` are given. """ return (self.data if len(kvpairs) <= 0 else((k,v) for k,v in self if (k,v) in kvpairs)) def keyindexes(self, *keys:str) -> Tuple[int, ...]: """ `keyindexes` Returns: An iterable with all indexes of all entries with one of the exact `*keys` in this query. """ return tuple(i for i,(k,v) in enumerate(self) if k in keys) def valueindexes(self, *values:str) -> Tuple[int, ...]: """ `valueindexes` Returns: An iterable with all indexes of all entries with one of the exact `*value` in this query. """ return tuple(i for i,(k,v) in enumerate(self) if v in values) #dictionary like methods def getvalues(self, key:str) -> Tuple[str, ...]: """ `getvalues` A dictionary like get method. Returns: A tuple with all values found with the given `key`. """ return tuple(self.queryvalues(key)) #dictionary like methods def setvalues(self, key:str, *values:str): """ `setvalues` A dictionary like get method. Sets every value in query with the key `key` to the single given value in `*values`, or if multiple `*values` are given, then it will set every key in order with a value from `*values`. With multiple `*values`, it's expected that the quantity of `*values` match the quantity of entries in the query with `key`. """ inds = self.keyindexes(key) if len(inds) != len(values) and len(values) != 1: raise TypeError(values) for vi, qi in enumerate(inds): if len(values) != 1: value = values[vi] else: value = values[0] self[qi] = (key, value) def delkey(self, key:str): """ `delkey` Deletes every entry in the query with key `key`. """ for i in reversed(self.keyindexes(key)): del self[i] def isempty(self) -> bool: """ `isempty` Returns: `True` if there are no keys or values in this query, otherwise `False`. """ return len(self) <= 0 def iskeyempty(self, key:str, all_values:bool = False) -> bool: """ `iskeyempty` Arguments: `key` -- The key to check the value of. Keyword Arguments: `all_values` -- If `True`, `True` will be returned only if *all* values are empty, not just one. """ return (all if all_values else any)(v == "" for v in self.queryvalues(key)) def encode(self, quote: Optional[bool] = None, quote_safe:Optional[str] = None, force_case: Optional[Literal["upper", "lower", "preserve"]] = None ) -> str: """ `encode` Keyword Arguments: `quoted` -- If not `None`, the URI will be quoted, if `True`; or unquoted, if `False`, with `None` defaulting to the objects `quote` attribute. `quote_safe` -- If quoting, these characters will be excluded when quoting. `force_case` -- If not `None`, the query's case will be converted to the given case, with `None` defaulting to the objects `force_case` attribute for how case should be handled. Returns: This `URIQuery` object, encoded as a string. """ #pylint:disable=unnecessary-lambda-assignment if force_case is not None and force_case not in ("upper", "lower", "preserve"): raise AttributeError if quote is None: quote = self.requote if force_case is None: force_case = self.force_case if quote_safe is None: quote_safe = self.quote_safe qmet = passthrough_first if quote: qmet = lambda s: uriquote(s, quote_safe) cmet = passthrough_first if force_case != "preserve": cmet = (lambda s: s.upper()) if force_case == "upper" else (lambda s: s.lower()) quote_via=lambda *p :cmet(qmet(passthrough_first(*p))) #we must use items instead of the query dict to ensure that the order remains intace encoded = uriqueryunparse(tuple(self.items()), doseq=True, quote_via=quote_via) _self_tup = tuple(self.items()) if len(_self_tup) == 1 and _self_tup[0][0] != "" and _self_tup[0][1] == "": #reformat a single, valueless query as just a string of the query key encoded = encoded.rstrip("=") return encoded __str__ = encode __repr__ = encode def validate(self, encoded:bool = False) -> bool: """ `validate` Returns: True if all characters in this object are allowed in a URI query. """ if encoded: return not CharacterSets.invalid_check(CharacterSets.QUERY, self.encode(True)) else: return not CharacterSets.invalid_check(CharacterSets.QUERY, *self.querykeys(), *self.queryvalues() ) def search(self, keymatch:Optional[Union[str, Pattern]] = None, valmatch:Optional[Union[str, Pattern]] = None ) -> Iterable[Tuple[Optional[Match], Optional[Match]]]: """ `search` Keyword Arguments: keymatch -- A optional regex pattern to match keys with. valmatch -- A optional regex pattern to match values with. Returns: An iterable of tuples, each with a match either for a key and/or value, with the format of `(Optional[Match], Optional[Match])`, the first being matches for keys, the second for values. """ #pylint:disable=unnecessary-lambda, unnecessary-lambda-assignment ksch = lambda *a, **k: None if isinstance(keymatch, str): keymatch = cast(Pattern, regexcompile(keymatch)) if keymatch is not None: ksch = lambda s: keymatch.search(s) vsch = lambda *a, **k: None if isinstance(valmatch, str): valmatch = cast(Pattern, regexcompile(valmatch)) if valmatch is not None: vsch = lambda s: valmatch.search(s) return ((ksch(k),vsch(v)) for k,v in self if ksch(k) is not None or vsch(v) is not None)Ancestors
- collections.UserList
- collections.abc.MutableSequence
- collections.abc.Sequence
- collections.abc.Reversible
- collections.abc.Collection
- collections.abc.Sized
- collections.abc.Iterable
- collections.abc.Container
Methods
def append(self, item: Union[str, Tuple[str, str], Iterable[Tuple[str, str]]])-
S.append(value) – append value to the end of the sequence
def count(self, item: Union[str, Tuple[str, str]]) ‑> int-
S.count(value) -> integer – return number of occurrences of value
def delkey(self, key: str)-
delkeyDeletes every entry in the query with key
key. def encode(self, quote: Optional[bool] = None, quote_safe: Optional[str] = None, force_case: Optional[Literal['upper', 'lower', 'preserve']] = None) ‑> str-
encodeKeyword Arguments:
quoted– If notNone, the URI will be quoted, ifTrue; or unquoted, ifFalse, withNonedefaulting to the objectsquoteattribute.quote_safe– If quoting, these characters will be excluded when quoting.force_case– If notNone, the query's case will be converted to the given case, withNonedefaulting to the objectsforce_caseattribute for how case should be handled.Returns
This
URIQueryobject, encoded as a string. def getvalues(self, key: str) ‑> Tuple[str, ...]-
getvaluesA dictionary like get method.
Returns
A tuple with all values found with the given
key. def index(self, item: Union[str, Tuple[str, str]], start: Union[int, SupportsIndex] = 0, stop: Union[int, SupportsIndex] = 9223372036854775807) ‑> int-
S.index(value, [start, [stop]]) -> integer – return first index of value. Raises ValueError if the value is not present.
Supporting start and stop arguments is optional, but recommended.
def insert(self, i: int, item: Union[str, Tuple[str, str]])-
S.insert(index, value) – insert value before index
def isempty(self) ‑> bool-
isemptyReturns
Trueif there are no keys or values in this query, otherwiseFalse. def iskeyempty(self, key: str, all_values: bool = False) ‑> bool-
iskeyemptyArguments
key– The key to check the value of.Keyword Arguments:
all_values– IfTrue,Truewill be returned only if all values are empty, not just one. def items(self, *kvpairs: Tuple[str, str]) ‑> Iterable[Tuple[str, str]]-
itemsReturns
An iterable with all items of this query (formated as a tuple of
("key", "value")), or only the values with one of the exact*kvpairsgiven, if any*kvpairsare given. def keyindexes(self, *keys: str) ‑> Tuple[int, ...]-
keyindexesReturns
An iterable with all indexes of all entries with one of the exact
*keysin this query. def querykeys(self, *values: str) ‑> Iterable[str]-
querykeysReturns
An iterable with all keys of this query, or only the keys with one of the exact
*valuesgiven, if any*valuesare given. def queryvalues(self, *keys: str) ‑> Iterable[str]-
queryvaluesReturns
An iterable with all values of this query, or only the values with one of the exact
*keysgiven, if any*keysare given. def remove(self, item: Union[str, Tuple[str, str]])-
S.remove(value) – remove first occurrence of value. Raise ValueError if the value is not present.
def search(self, keymatch: Union[str, re.Pattern, ForwardRef(None)] = None, valmatch: Union[str, re.Pattern, ForwardRef(None)] = None) ‑> Iterable[Tuple[Optional[re.Match], Optional[re.Match]]]-
searchKeyword Arguments: keymatch – A optional regex pattern to match keys with. valmatch – A optional regex pattern to match values with.
Returns
An iterable of tuples, each with a match either for a key and/or value, with the format of
(Optional[Match], Optional[Match]), the first being matches for keys, the second for values. def setvalues(self, key: str, *values: str)-
setvaluesA dictionary like get method.
Sets every value in query with the key
keyto the single given value in*values, or if multiple*valuesare given, then it will set every key in order with a value from*values. With multiple*values, it's expected that the quantity of*valuesmatch the quantity of entries in the query withkey. def validate(self, encoded: bool = False) ‑> bool-
validateReturns
True if all characters in this object are allowed in a URI query.
def valueindexes(self, *values: str) ‑> Tuple[int, ...]-
valueindexesReturns
An iterable with all indexes of all entries with one of the exact
*valuein this query.