75 lines
2.1 KiB
Python
75 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
import dataclasses
|
|
import urllib.parse
|
|
from abc import ABCMeta
|
|
|
|
from limits.errors import ConfigurationError
|
|
|
|
SCHEMES: dict[str, StorageRegistry] = {}
|
|
|
|
|
|
class StorageRegistry(ABCMeta):
|
|
def __new__(
|
|
mcs, name: str, bases: tuple[type, ...], dct: dict[str, str | list[str]]
|
|
) -> StorageRegistry:
|
|
storage_scheme = dct.get("STORAGE_SCHEME", None)
|
|
cls = super().__new__(mcs, name, bases, dct)
|
|
|
|
if storage_scheme:
|
|
if isinstance(storage_scheme, str): # noqa
|
|
schemes = [storage_scheme]
|
|
else:
|
|
schemes = storage_scheme
|
|
|
|
for scheme in schemes:
|
|
SCHEMES[scheme] = cls
|
|
|
|
return cls
|
|
|
|
|
|
@dataclasses.dataclass
|
|
class StorageURIOptions:
|
|
scheme: str
|
|
username: str | None
|
|
password: str | None
|
|
locations: list[tuple[str, int]]
|
|
path: str | None
|
|
query: dict[str, list[str]]
|
|
|
|
@property
|
|
def empty(self) -> bool:
|
|
"""
|
|
whether this is just a scheme:// uri without any information
|
|
that might be useful when constructing the actual storage
|
|
instance
|
|
"""
|
|
return bool(
|
|
self.username is None
|
|
and self.password is None
|
|
and not (self.locations or self.path or self.query)
|
|
)
|
|
|
|
|
|
def parse_storage_uri(uri: str) -> StorageURIOptions:
|
|
parsed = urllib.parse.urlparse(uri)
|
|
sep = parsed.netloc.find("@") + 1
|
|
locations = []
|
|
try:
|
|
if parsed.netloc[sep:]:
|
|
for loc in parsed.netloc[sep:].split(","):
|
|
sub = urllib.parse.urlparse(f"fake://{loc.strip()}")
|
|
hostname, port = sub.hostname, sub.port
|
|
if hostname is None or port is None:
|
|
raise ConfigurationError(f"Missing host or port in location {loc}")
|
|
locations.append((hostname, port))
|
|
except ValueError as err:
|
|
raise ConfigurationError(f"Unable to parse storage uri {uri}") from err
|
|
return StorageURIOptions(
|
|
parsed.scheme,
|
|
parsed.username,
|
|
parsed.password,
|
|
locations,
|
|
parsed.path,
|
|
urllib.parse.parse_qs(parsed.query),
|
|
)
|