lib.Uri: Add new convenience properties

Add a couple of properties and methods that come in handy when manipulating URIs:

.path .safe_full_with_username

do the obvious, and these return new Uri objects with modified paths:

.new_add_path() .new_replace_path()

Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-05-06 15:26:14 +02:00
commit 237096cac9
Signed by: Jan Lindemann
GPG key ID: 3750640C9E25DD61

View file

@ -4,6 +4,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from functools import cached_property from functools import cached_property
import copy
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Self from typing import Self
@ -13,7 +14,7 @@ if TYPE_CHECKING:
class Uri: class Uri:
def __assemble(self, scheme: bool, credentials: bool, secure: bool) -> str: def __assemble(self, scheme: bool, credentials: bool, secure: bool, path: bool) -> str:
ret = '' ret = ''
if scheme: if scheme:
ret += f'{self.protocol}://' ret += f'{self.protocol}://'
@ -26,6 +27,8 @@ class Uri:
ret += self.hostname ret += self.hostname
if self.port_str: if self.port_str:
ret += ':' + self.port_str ret += ':' + self.port_str
if path:
ret += self.path
return ret return ret
def __init__(self, string: str) -> None: def __init__(self, string: str) -> None:
@ -37,7 +40,7 @@ class Uri:
return self.full return self.full
def __str__(self) -> str: def __str__(self) -> str:
return self.id return self.safe_full_with_username
@cached_property @cached_property
def __p(self) -> urllib.parse.ParseResult: def __p(self) -> urllib.parse.ParseResult:
@ -97,13 +100,17 @@ class Uri:
return None return None
return str(self.port) return str(self.port)
@cached_property
def path(self) -> str:
return self.__p.path
@cached_property @cached_property
def authority(self) -> str: def authority(self) -> str:
return self.__assemble(scheme=False, credentials=True, secure=False) return self.__assemble(scheme=False, credentials=True, secure=False, path=False)
@cached_property @cached_property
def origin(self) -> str: def origin(self) -> str:
return self.__assemble(scheme=False, credentials=False, secure=True) return self.__assemble(scheme=False, credentials=False, secure=True, path=False)
@cached_property @cached_property
def scheme_plus_authority(self) -> str: def scheme_plus_authority(self) -> str:
@ -111,12 +118,37 @@ class Uri:
@cached_property @cached_property
def id(self) -> str: def id(self) -> str:
return self.__assemble(scheme=True, credentials=True, secure=True) return self.__assemble(scheme=True, credentials=True, secure=True, path=False)
@cached_property @cached_property
def full(self) -> str: def full(self) -> str:
return self.__assemble(scheme=True, credentials=True, secure=False) + self.path return self.__assemble(scheme=True, credentials=True, secure=False, path=True)
@cached_property @cached_property
def path(self) -> str: def safe_full_with_username(self) -> str:
return self.__p.path return self.__assemble(scheme=True, credentials=True, secure=True, path=True)
def __new_with_path(self, base: str, path: str) -> Self:
ret = copy.deepcopy(self)
ret.__string = base
ret.__username = None
ret.__password = None
if not path:
return ret
if ret.__string[-1] == '/':
if path[0] == '/':
ret.__string += path[1:]
else:
ret.__string += path
else:
if path[0] == '/':
ret.__string += path
else:
ret.__string += '/' + path
return ret
def new_add_path(self, path: str) -> Self:
return self.__new_with_path(self.__string, path)
def new_replace_path(self, path: str) -> Self:
return self.__new_with_path(self.schema_plus_authority, path)