Package urilibplus
urilibplus
A python module containing classes used to interact and manipulate uris in python in a easier and consistent manner.
Sub-modules
urilibplus.characters-
characters …
urilibplus.tests-
__init__… urilibplus.toolsurilibplus.typingsurilibplus.uriurilibplus.uri_pathurilibplus.uri_query
Classes
class CharacterSets-
Holds the character literals and related functions for the
urilibplusmodule.Sources: - https://www.rfc-editor.org/rfc/rfc3986 - https://en.wikipedia.org/wiki/Uniform_Resource_Identifier
Expand source code
class CharacterSets: """ `CharacterSets` Holds the character literals and related functions for the `urilibplus` module. Sources: - https://www.rfc-editor.org/rfc/rfc3986 - https://en.wikipedia.org/wiki/Uniform_Resource_Identifier """ HEXDIGITS: LiteralString = hexdigits DIGITS: LiteralString = digits LETTERS: LiteralString = ascii_letters GENERIC_DELIMITERS: LiteralString = ":/?#[]@" SPECIFIC_DELIMITERS: LiteralString = "!$&'()*+,;=" PERCENT_ENCODING: LiteralString = singlify_str(HEXDIGITS, "%") UNRESERVED: LiteralString = singlify_str(LETTERS, DIGITS, "-._~") P_CHARS: LiteralString = singlify_str(UNRESERVED, PERCENT_ENCODING, SPECIFIC_DELIMITERS, ":@") ALL: LiteralString = singlify_str(GENERIC_DELIMITERS, SPECIFIC_DELIMITERS, PERCENT_ENCODING, UNRESERVED) SEGMENT: LiteralString = P_CHARS SCHEME: LiteralString = cast(LiteralString, uri_scheme_chars) USERINFO: LiteralString = singlify_str(UNRESERVED, PERCENT_ENCODING, SPECIFIC_DELIMITERS, ":") HOST: LiteralString = singlify_str(DIGITS, HEXDIGITS, UNRESERVED, PERCENT_ENCODING, SPECIFIC_DELIMITERS, ".-:[]") PORT: LiteralString = DIGITS PATH: LiteralString = singlify_str(SEGMENT, "/") QUERY: LiteralString = singlify_str(P_CHARS, "/?") FRAGMENT: LiteralString = QUERY @staticmethod def invalid_check(character_set: LiteralString, *validate_all: str) -> bool: """ `invalid_check` Checks that all the `*validate_all` strings only use characters from the given `character-set` Arguments: `character_set` -- The character set to check the strings with. `*validate_all` -- The strings to check. Returns: True if *all* `*validate_all` strings only use the characters in the given `character_set`. """ return any(any((c not in character_set) for c in s) for s in validate_all if s != "")Class variables
var ALL : LiteralStringvar DIGITS : LiteralStringvar FRAGMENT : LiteralStringvar GENERIC_DELIMITERS : LiteralStringvar HEXDIGITS : LiteralStringvar HOST : LiteralStringvar LETTERS : LiteralStringvar PATH : LiteralStringvar PERCENT_ENCODING : LiteralStringvar PORT : LiteralStringvar P_CHARS : LiteralStringvar QUERY : LiteralStringvar SCHEME : LiteralStringvar SEGMENT : LiteralStringvar SPECIFIC_DELIMITERS : LiteralStringvar UNRESERVED : LiteralStringvar USERINFO : LiteralString
Static methods
def invalid_check(character_set: LiteralString, *validate_all: str) ‑> bool-
invalid_checkChecks that all the
*validate_allstrings only use characters from the givencharacter-setArguments
character_set– The character set to check the strings with.*validate_all– The strings to check.Returns
True if all
*validate_allstrings only use the characters in the givencharacter_set.
class URI (contents: Union[str, ForwardRef('URI'), urllib.parse.SplitResult, Tuple[str, str, str, str, str]], default_scheme: Optional[str] = None, *, unquote: bool = False, requote: bool = False, quote_safe: str = '')-
A python class used to interact and manipulate with uris in python in a easier and consistent manner.
Sources: - https://www.rfc-editor.org/rfc/rfc3986 - https://en.wikipedia.org/wiki/Uniform_Resource_Identifier
Expand source code
class URI: """ `URI` A python class used to interact and manipulate with uris in python in a easier and consistent manner. Sources: - https://www.rfc-editor.org/rfc/rfc3986 - https://en.wikipedia.org/wiki/Uniform_Resource_Identifier """ CHARACTER_SETS = CharacterSets quotestr = staticmethod(uriquote) unquotestr = staticmethod(uriunquote) @property def authority(self) -> str: """ `authority` Returns: The authority of the uri; ie. the user, password, host, and port; formatted as required. """ authority = "" if self.user_info != "": authority = f"{self.user_info}@" authority += f"{self.host}" if self.port is not None and self.port > 0: authority += f":{self.port}" return authority @authority.setter def authority(self, value: str): at_index = value.find("@") if at_index < 0: self.user_info = "" else: self.user_info = value[:at_index] value = value.replace(self.user_info, "", 1).lstrip("@") col_index = value.rfind(":") if col_index < 0: self.port = None else: self.port = int(value[col_index+1:]) if self.port < 0: self.port = None if self.port is not None: value = value.replace(str(self.port), "", 1).rstrip(":") self.host = value @property def username(self) -> Union[None, str]: """ `username` Returns: The username in the uri, or None if no username in included. """ if self.userinfo.count(":") == 1: return self.userinfo[:self.userinfo.find(":")] else: return None @username.setter def username(self, value: Union[None, str]): if value is None: self.userinfo = self.userinfo[self.userinfo.find(":")+1:] else: if self.userinfo[0] != ":" and self.username is None: value += ":" self.userinfo = value + \ self.userinfo[max(self.userinfo.find(":"), 0):] @property def password(self) -> Union[None, str]: """ `password` Returns: The password in the uri, or None if no password in included. """ if self.userinfo_string.count(":") == 1: return self.userinfo_string[self.userinfo_string.find(":")+1:] else: return None @password.setter def password(self, value: Union[None, str]): if value is None: self.userinfo_string = self.userinfo_string[:self.userinfo_string.find( ":")] else: if self.userinfo_string[-1] != ":" and self.password is None: value = ":" + value self.userinfo_string = self.userinfo_string[:self.userinfo_string.find( ":")] + value def __init__(self, contents:Union[str, 'URI', SplitResult, Tuple[str, str, str, str, str]], default_scheme: Optional[str] = None, *, unquote:bool = False, requote:bool = False, quote_safe:str = ""): self.scheme: str self.user_info: str = "" self.host: str = "" self.port: Optional[int] = None self.path: Optional['URIPath'] = None self.query: Optional['URIQuery'] = None self.fragment: Optional['URIQuery'] = None if default_scheme is None: default_scheme = "" parsed = None if isinstance(contents, SplitResult): parsed = contents elif isinstance(contents, tuple) and len(contents) <= 5 and len(contents) >= 1: parsed = SplitResult(*contents) else: if isinstance(contents, URI): contents = str(contents) contents = contents.strip() contents = uriunwrap(contents) parsed = urisplit(contents, scheme=default_scheme, allow_fragments=True) self.scheme = uriunquote(parsed.scheme) if unquote else parsed.scheme self.authority = uriunquote(parsed.netloc) if unquote else parsed.netloc self.path = URIPath(parsed.path.lstrip("/"), unquote=unquote) self.query = URIQuery(parsed.query, unquote=unquote) self.fragment = URIQuery(parsed.fragment, unquote=unquote) self.default_scheme = default_scheme self.requote = requote self.quote_safe = quote_safe def __repr__(self): return f"<URI object (url = {self.encode()}, valid = {self.validate()})>" def __len__(self): return len(self.encode()) def __contains__(self, value:str): return value in self.encode() def __iter__(self): return iter(self.tupled()) def copy(self) -> 'URI': """ `copy` Returns: A complete, deep, copy of this object. """ return URI(self.splitted(False), default_scheme=self.default_scheme, unquote=False, requote=self.requote, quote_safe=self.quote_safe ) __copy__ = copy __deepcopy__ = copy def tupled(self, quote: Optional[bool] = None, quote_safe:Optional[str] = None ) -> Tuple[str, str, str, str, str]: """ `tupled` Returns this object formated as a `tuple`, a ordered in the commonly used order used in native python uri related functions. 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. Returns: This object as a `tuple`. """ if quote is None: quote = self.requote if quote_safe is None: quote_safe = self.quote_safe return (uriquote(self.scheme, quote_safe) if quote else self.scheme, uriquote(self.authority, quote_safe) if quote else self.authority, "" if self.path is None else self.path.encode(quote, quote_safe), "" if self.query is None else self.query.encode(quote, quote_safe), "" if self.fragment is None else self.fragment.encode(quote, quote_safe) ) def splitted(self, quote: Optional[bool] = None, quote_safe:Optional[str] = None ) -> SplitResult: """ `splitted` Returns this object formated as a `SplitResult`, a native object commonly used with native python uri related functions. 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. Returns: This object as a `SplitResult`. """ return SplitResult(*self.tupled(quote, quote_safe)) def encode(self, quote: Optional[bool] = None, quote_safe:Optional[str] = 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. Returns: The URI object, encoded as a string. """ if quote is None: quote = self.requote if quote_safe is None: quote_safe = self.quote_safe encoded = uriunsplit(SplitResult(*self.tupled(None, None))) if quote is True: encoded = uriquote(encoded, safe = quote_safe) elif quote is False: encoded = uriunquote(encoded) return encoded __str__ = encode __repr__ = encode def stripped(self, quote: Optional[bool] = None, quote_safe:Optional[str] = None) -> str: """ `stripped` 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. Returns: The URI without any fragment or query, ie. the URI with only its scheme and authority (host, user, password, and port) and path """ if quote is None: quote = self.requote if quote_safe is None: quote_safe = self.quote_safe encoded = uriunsplit(SplitResult(*self.tupled(quote, quote_safe)[:3], "", "")) if quote is True: encoded = uriquote(encoded, safe = quote_safe) elif quote is False: encoded = uriunquote(encoded) return encoded def root(self, quote: Optional[bool] = None, quote_safe:Optional[str] = None) -> str: """ `root` 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. Returns: The URI's root path, ie. the URI with only its scheme and authority (host, user, password, and port) """ if quote is None: quote = self.requote if quote_safe is None: quote_safe = self.quote_safe encoded = uriunsplit(SplitResult(*self.tupled(quote, quote_safe)[:2], "", "", "")) if quote is True: encoded = uriquote(encoded, safe = quote_safe) elif quote is False: encoded = uriunquote(encoded) return encoded def validate(self) -> bool: """ `validate` Returns: True if all characters in this object are allowed in a URI. """ for char in self.scheme: if char not in self.CHARACTER_SETS.SCHEME: return False for char in self.user_info: if char not in self.CHARACTER_SETS.USERINFO: return False for char in self.host: if char not in self.CHARACTER_SETS.HOST: return False if not (isinstance(self.port, int) or self.port is None): return False if self.path is None: return False elif isinstance(self.path, URIPath) and not self.path.validate(): return False else: for char in self.host: if char not in self.CHARACTER_SETS.PATH: return False if isinstance(self.query, URIQuery) and not self.query.validate(): return False else: for char in self.host: if char not in self.CHARACTER_SETS.QUERY: return False for char in str(self.fragment): if char not in self.CHARACTER_SETS.FRAGMENT: return False # This is specifically stated as invalid in the specs if self.authority == "" and self.path[0] == "": return False return True # NOTE this doesn't test all parts fo the URI, just the required parts! def isempty(self) -> bool: """ `isempty` Returns: True if the given url's scheme or path are blank. """ return self.scheme == "" or (self.path is None or self.path.isempty()) __bool__ = isempty def search(self, pattern:Union[str, Pattern], quoted: Optional[bool] = None, quote_safe:Optional[str] = None ) -> Optional[Match]: """ `search` A simple shorcut to regex `search` this url, returning the `Match`, or `None` if there was no matches. Arguments: `pattern` -- The pattern to check the url with. 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. Returns: `None` if not matches where found, or a `Match` object otherwise. """ if isinstance(pattern, str): pattern = regexcompile(pattern) return pattern.search(self.encode(quoted, quote_safe)) def pathappend(self, *parts:Union[str, PathLike]): """ `pathappend` A simple shorcut to append to the path directly from the this object, if a path is defined. Will initialise `path` if `path` is None. Arguments: `*parts` -- The parts to be appended to the path, in order. """ if self.path is None: self.path = URIPath() self.path.append(parts) def __truediv__(self, value:Union[str, PathLike]) -> 'URI': cpy = self.copy() cpy.pathappend(value) return cpy def __floordiv__(self, value:Union[str, PathLike]) -> 'URI': cpy = self.copy() cpy.pathappend("", value) return cpyClass variables
var CHARACTER_SETS-
Holds the character literals and related functions for the
urilibplusmodule.Sources: - https://www.rfc-editor.org/rfc/rfc3986 - https://en.wikipedia.org/wiki/Uniform_Resource_Identifier
Static methods
def quotestr(string, safe='/', encoding=None, errors=None)-
quote('abc def') -> 'abc%20def'
Each part of a URL, e.g. the path info, the query, etc., has a different set of reserved characters that must be quoted. The quote function offers a cautious (not minimal) way to quote a string for most of these parts.
RFC 3986 Uniform Resource Identifier (URI): Generic Syntax lists the following (un)reserved characters.
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" reserved = gen-delims / sub-delims gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
Each of the reserved characters is reserved in some component of a URL, but not necessarily in all of them.
The quote function %-escapes all characters that are neither in the unreserved chars ("always safe") nor the additional chars set via the safe arg.
The default for the safe arg is '/'. The character is reserved, but in typical usage the quote function is being called on a path where the existing slash characters are to be preserved.
Python 3.7 updates from using RFC 2396 to RFC 3986 to quote URL strings. Now, "~" is included in the set of unreserved characters.
string and safe may be either str or bytes objects. encoding and errors must not be specified if string is a bytes object.
The optional encoding and errors parameters specify how to deal with non-ASCII characters, as accepted by the str.encode method. By default, encoding='utf-8' (characters are encoded with UTF-8), and errors='strict' (unsupported characters raise a UnicodeEncodeError).
def unquotestr(string, encoding='utf-8', errors='replace')-
Replace %xx escapes by their single-character equivalent. The optional encoding and errors parameters specify how to decode percent-encoded sequences into Unicode characters, as accepted by the bytes.decode() method. By default, percent-encoded sequences are decoded with UTF-8, and invalid sequences are replaced by a placeholder character.
unquote('abc%20def') -> 'abc def'.
Instance variables
-
authorityReturns
The authority of the uri; ie. the user, password, host, and port; formatted as required.
Expand source code
@property def authority(self) -> str: """ `authority` Returns: The authority of the uri; ie. the user, password, host, and port; formatted as required. """ authority = "" if self.user_info != "": authority = f"{self.user_info}@" authority += f"{self.host}" if self.port is not None and self.port > 0: authority += f":{self.port}" return authority prop password : Optional[str]-
passwordReturns
The password in the uri, or None if no password in included.
Expand source code
@property def password(self) -> Union[None, str]: """ `password` Returns: The password in the uri, or None if no password in included. """ if self.userinfo_string.count(":") == 1: return self.userinfo_string[self.userinfo_string.find(":")+1:] else: return None prop username : Optional[str]-
usernameReturns
The username in the uri, or None if no username in included.
Expand source code
@property def username(self) -> Union[None, str]: """ `username` Returns: The username in the uri, or None if no username in included. """ if self.userinfo.count(":") == 1: return self.userinfo[:self.userinfo.find(":")] else: return None
Methods
def copy(self) ‑> URI-
copyReturns
A complete, deep, copy of this object.
def encode(self, quote: Optional[bool] = None, quote_safe: Optional[str] = None) ‑> str-
encodeKeyword Arguments: quoted – If not
None, the URI will be quoted, ifTrue; or unquoted, ifFalse, withNonedefaulting to the objectsquoteattribute. quote_safe – If quoting, these characters will be excluded when quoting.Returns
The URI object, encoded as a string.
def isempty(self) ‑> bool-
isemptyReturns
True if the given url's scheme or path are blank.
def pathappend(self, *parts: Union[str, os.PathLike])-
pathappendA simple shorcut to append to the path directly from the this object, if a path is defined.
Will initialise
pathifpathis None.Arguments
*parts– The parts to be appended to the path, in order. def root(self, quote: Optional[bool] = None, quote_safe: Optional[str] = None) ‑> str-
rootKeyword Arguments: quoted – If not
None, the URI will be quoted, ifTrue; or unquoted, ifFalse, withNonedefaulting to the objectsquoteattribute. quote_safe – If quoting, these characters will be excluded when quoting.Returns
The URI's root path, ie. the URI with only its scheme and authority (host, user, password, and port)
def search(self, pattern: Union[str, re.Pattern], quoted: Optional[bool] = None, quote_safe: Optional[str] = None) ‑> Optional[re.Match]-
searchA simple shorcut to regex
searchthis url, returning theMatch, orNoneif there was no matches.Arguments
pattern– The pattern to check the url with.Keyword Arguments: quoted – If not
None, the URI will be quoted, ifTrue; or unquoted, ifFalse, withNonedefaulting to the objectsquoteattribute. quote_safe – If quoting, these characters will be excluded when quoting.Returns
Noneif not matches where found, or aMatchobject otherwise. def splitted(self, quote: Optional[bool] = None, quote_safe: Optional[str] = None) ‑> urllib.parse.SplitResult-
splittedReturns this object formated as a
SplitResult, a native object commonly used with native python uri related functions.Keyword Arguments: quoted – If not
None, the URI will be quoted, ifTrue; or unquoted, ifFalse, withNonedefaulting to the objectsquoteattribute. quote_safe – If quoting, these characters will be excluded when quoting.Returns
This object as a
SplitResult. def stripped(self, quote: Optional[bool] = None, quote_safe: Optional[str] = None) ‑> str-
strippedKeyword Arguments: quoted – If not
None, the URI will be quoted, ifTrue; or unquoted, ifFalse, withNonedefaulting to the objectsquoteattribute. quote_safe – If quoting, these characters will be excluded when quoting.Returns
The URI without any fragment or query, ie. the URI with only its scheme and authority (host, user, password, and port) and path
def tupled(self, quote: Optional[bool] = None, quote_safe: Optional[str] = None) ‑> Tuple[str, str, str, str, str]-
tupledReturns this object formated as a
tuple, a ordered in the commonly used order used in native python uri related functions.Keyword Arguments: quoted – If not
None, the URI will be quoted, ifTrue; or unquoted, ifFalse, withNonedefaulting to the objectsquoteattribute. quote_safe – If quoting, these characters will be excluded when quoting.Returns
This object as a
tuple. def validate(self) ‑> bool-
validateReturns
True if all characters in this object are allowed in a URI.
class URIPath (*path: Union[str, os.PathLike, Iterable[Union[str, os.PathLike]]], unquote: bool = False, requote: bool = False, quote_safe: str = '')-
A class used to manipulate and use a URI path in python easily.
Expand source code
class URIPath(PurePosixPath, PathLike, MutableSequenceABC[str]): """ `URIPath` A class used to manipulate and use a URI path in python easily. """ _USE_NEW_PUREPATH_INIT_METHOD:bool = version_info.major > 3 or (version_info.major == 3 and version_info.minor >= 12) _CACHEING_ANCESTORS:Tuple[type, ...] = (PurePosixPath, PurePath, PureWindowsPath) _CACHE_ATTR_NAMES:Iterator[LiteralString] = cast(Iterator[LiteralString], iter_flatten( (getattr(c, "__slots__", []) for c in _CACHEING_ANCESTORS), #pylint:disable=line-too-long str ) ) @property def raw(self) -> List[str]: """ `__raw` Used internally, do not modify without express intent. Returns: The internal list of strings used by `Path` types to create paths from. """ return self._raw_paths @raw.setter def raw(self, value:List[str]): #without this, the caches parts of the path wont update when we manually change the content for attr in self._CACHE_ATTR_NAMES: if hasattr(self, attr): delattr(self, attr) self._raw_paths = value @raw.deleter def raw(self): # you don't just delete raw, but you can clear it... self.raw = [] def __new__(cls, *path:Union[str, PathLike, Iterable[Union[str, PathLike]]], unquote: bool = False, requote:bool = False, quote_safe:str = "" ) -> 'URIPath': # don't run the inherited __new__ methods, # it automatically makes the path in the OS local type and confuses type checkers if cls._USE_NEW_PUREPATH_INIT_METHOD or TYPE_CHECKING: return object.__new__(cls) else: return cls._from_parts(tuple(iter_flatten(path, str))) def __init__(self, *path:Union[str, PathLike, Iterable[Union[str, PathLike]]], unquote:bool = False, requote:bool = False, quote_safe:str = "" ): self.unquote:bool = unquote self.requote:bool = requote self.quote_safe = quote_safe if self._USE_NEW_PUREPATH_INIT_METHOD: super().__init__(*tuple(iter_flatten(path, str))) def __iter__(self): return iter(self.parts) @overload def __getitem__(self, index:int) -> str: ... @overload def __getitem__(self, index:slice) -> List[str]: ... def __getitem__(self, index:Union[int, slice]) -> Union[str, List[str]]: return list(self.parts[index]) def __setitem__(self, index:Union[int, slice], value:Union[str, PathLike, Iterable[Union[str, PathLike]]] ): c = list(self) value = self.copy(value) if isinstance(index, int): c[index] = str(value) else: c[index] = list(value) self.raw = c def __delitem__(self, index:Union[int, slice]): c = list(self) del c[index] self.raw = c def __bool__(self): return not self.isempty() def __len__(self): return len(self.parts) def __truediv__(self, other:Union[str, PathLike, Iterable[Union[str, PathLike]]]): #self / other c = self.copy() c.append(other) return c @overload def __contains__(self, #type:ignore other:Union[str, PathLike, Iterable[Union[str, PathLike]]] ) -> bool: ... @overload def __contains__(self, other:object) -> NotImplementedType: ... def __contains__(self, other:Union[str, PathLike, Iterable[Union[str, PathLike]], object] ) -> Union[bool, NotImplementedType]: if not isinstance(other, (str, PathLike, Iterable)): return NotImplemented return all((x in list(self)) for x in self.copy(other)) def copy(self, *content_override:Union[str, PathLike, Iterable[Union[str, PathLike]]] ) -> 'URIPath': """ `copy` `*content_override` -- When any of these arguments are set, the copy will instead be initialised with these values as the path's content. Returns: A complete, deep, copy of this object; with the contents optionally overridden. """ kwargs = { "unquote":self.unquote, "requote":self.requote, "quote_safe":self.quote_safe } return URIPath(*((self,) if len(content_override) <= 0 else content_override), **kwargs) __copy__ = copy __deepcopy__ = copy def append(self, value:Union[str, PathLike, Iterable[Union[str, PathLike]]]): appendage = [] if isinstance(value, URIPath) : if all(not x.startswith("/") for x in value.raw): appendage = value.raw else: appendage = list(value[1:] if value[0] == "/" else value[:]) else: appendage = self.copy(value).raw self.raw = self.raw + appendage def count(self, value:str) -> int: return list(self).count(value) def index(self, value:str, start:int = 0, stop:int = sys_maxsize) -> int: return list(self).index(value, start, stop) def rindex(self, value:str, start:int = 0, stop:Optional[int] = None) -> int: """ `rindex` Similar to `index`, but searching in reverse order. Arguments: value -- The value to search for. Keyword Arguments: start -- The lower index to limit searches at, defaults to 0. stop -- The higher index to limit searches at, defaults to `None`, which equates to stopping at the end of the list. Returns: The index found. """ return (len(self) - self[start:stop][-1::-1].index(value) - 1) + abs(start) def insert(self, index:int, value:Union[str, PathLike, Iterable[Union[str, PathLike]]]): value = self.copy(value).raw self.raw = self.raw[:index] + value + self.raw[index:] def remove(self, value:str): newraw = list(self.copy(value)) newraw.remove(value) self.raw = newraw def isempty(self) -> bool: """ `isempty` Returns: `True` if the path contains no segments, otherwise `False`. """ return len(self.raw) <= 0 def encode(self, quote: Optional[bool] = None, quote_safe:Optional[str] = None) -> str: """ `encode` Keyword Arguments: quoted -- If not `None`, the path 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. Returns: The path object, encoded as a string. """ if quote is None: quote = self.requote if quote_safe is None: quote_safe = cast(str, self.quote_safe) quote_safe += "/" if not quote: return super().__str__() else: seg = tuple(uriquote(x, quote_safe) for x in self) if self[0] == "/": #the root slash will be consitered a part, we need to preserve that seg = ("/", ) + seg[1:] return super(URIPath, self.copy(*seg)).__str__() __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's path. """ if encoded: return not CharacterSets.invalid_check(CharacterSets.PATH, self.encode(True)) else: return not CharacterSets.invalid_check(CharacterSets.SEGMENT, *tuple(self)) def segafter(self, value: str, last:bool = False) -> Union[str, None]: """ `segafter` Retrieves the content of the after the first (or last) path segment matching `value`, if any; otherwise None. Arguments: value -- The exact value of a segment to find. Keyword Arguments: last -- Instead of finding the first possible segment matching `value`, find the last. Returns: The content of the segment comming after the found `value`, or `None` if `value` was not found. """ if value not in self: return None ind = (self.rindex if last else self.index)(value) if ind >= len(self): return None return cast(str, self[ind + 1]) def segbefore(self, value: str, last:bool = False) -> Union[str, None]: """ `segbefore` Retrieves the content of the before the first (or last) path segment matching `value`, if any; otherwise None. Arguments: value -- The exact value of a segment to find. Keyword Arguments: last -- Instead of finding the first possible segment matching `value`, find the last. Returns: The content of the segment comming before the found `value`, or `None` if `value` was not found. """ if value not in self: return None ind = (self.rindex if last else self.index)(value) if ind <= 0: return None return cast(str, self[ ind - 1]) def segsearch(self, pattern:Union[str, Pattern], include_all:bool = False ) -> Iterable[Match]: """ `segsearch` Searches every segment in the path for the given regex `pattern`, returning any found as an iterable. Arguments: `pattern` -- The pattern to search for in each segment. Keyword Arguments: `include_all` -- Return every match found in a single segment, not just the first one. Returns: A iterable of found matches, in the same rough order of the segment they where found in. Yields: A match found. """ if isinstance(pattern, str): pattern = regexcompile(pattern) for s in self: m = pattern.search(s) if include_all or m is not None: yield cast(Match, m)Ancestors
- pathlib.PurePosixPath
- pathlib.PurePath
- os.PathLike
- abc.ABC
- collections.abc.MutableSequence
- collections.abc.Sequence
- collections.abc.Reversible
- collections.abc.Collection
- collections.abc.Sized
- collections.abc.Iterable
- collections.abc.Container
Instance variables
prop raw : List[str]-
__rawUsed internally, do not modify without express intent.
Returns
The internal list of strings used by
Pathtypes to create paths from.Expand source code
@property def raw(self) -> List[str]: """ `__raw` Used internally, do not modify without express intent. Returns: The internal list of strings used by `Path` types to create paths from. """ return self._raw_paths
Methods
def append(self, value: Union[str, os.PathLike, Iterable[Union[str, os.PathLike]]])-
S.append(value) – append value to the end of the sequence
def copy(self, *content_override: Union[str, os.PathLike, Iterable[Union[str, os.PathLike]]]) ‑> URIPath-
copy*content_override– When any of these arguments are set, the copy will instead be initialised with these values as the path's content.Returns
A complete, deep, copy of this object; with the contents optionally overridden.
def count(self, value: str) ‑> int-
S.count(value) -> integer – return number of occurrences of value
def encode(self, quote: Optional[bool] = None, quote_safe: Optional[str] = None) ‑> str-
encodeKeyword Arguments: quoted – If not
None, the path will be quoted, ifTrue; or unquoted, ifFalse, withNonedefaulting to the objectsquoteattribute. quote_safe – If quoting, these characters will be excluded when quoting.Returns
The path object, encoded as a string.
def index(self, value: str, start: int = 0, stop: int = 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, index: int, value: Union[str, os.PathLike, Iterable[Union[str, os.PathLike]]])-
S.insert(index, value) – insert value before index
def isempty(self) ‑> bool-
isemptyReturns
Trueif the path contains no segments, otherwiseFalse. def remove(self, value: str)-
S.remove(value) – remove first occurrence of value. Raise ValueError if the value is not present.
def rindex(self, value: str, start: int = 0, stop: Optional[int] = None) ‑> int-
rindexSimilar to
index, but searching in reverse order.Arguments
value – The value to search for.
Keyword Arguments: start – The lower index to limit searches at, defaults to 0. stop – The higher index to limit searches at, defaults to
None, which equates to stopping at the end of the list.Returns
The index found.
def segafter(self, value: str, last: bool = False) ‑> Optional[str]-
segafterRetrieves the content of the after the first (or last) path segment matching
value, if any; otherwise None.Arguments
value – The exact value of a segment to find.
Keyword Arguments: last – Instead of finding the first possible segment matching
value, find the last.Returns
The content of the segment comming after the found
value, orNoneifvaluewas not found. def segbefore(self, value: str, last: bool = False) ‑> Optional[str]-
segbeforeRetrieves the content of the before the first (or last) path segment matching
value, if any; otherwise None.Arguments
value – The exact value of a segment to find.
Keyword Arguments: last – Instead of finding the first possible segment matching
value, find the last.Returns
The content of the segment comming before the found
value, orNoneifvaluewas not found. def segsearch(self, pattern: Union[str, re.Pattern], include_all: bool = False) ‑> Iterable[re.Match]-
segsearchSearches every segment in the path for the given regex
pattern, returning any found as an iterable.Arguments
pattern– The pattern to search for in each segment.Keyword Arguments:
include_all– Return every match found in a single segment, not just the first one.Returns
A iterable of found matches, in the same rough order of the segment they where found in.
Yields
A match found.
def validate(self, encoded: bool = False) ‑> bool-
validateReturns
True if all characters in this object are allowed in a URI's path.
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.