Jellyfin(8096), OrbStack(8097) 포트 충돌으로 변경. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
231 lines
7.3 KiB
Python
231 lines
7.3 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib
|
|
import inspect
|
|
import typing
|
|
from abc import ABCMeta
|
|
from base64 import b64encode
|
|
from typing import Any
|
|
from urllib.parse import parse_qs
|
|
|
|
from ...util import parse_url
|
|
from .protocols import BaseResolver, ProtocolResolver
|
|
|
|
|
|
class ResolverFactory(metaclass=ABCMeta):
|
|
@staticmethod
|
|
def new(
|
|
protocol: ProtocolResolver,
|
|
specifier: str | None = None,
|
|
implementation: str | None = None,
|
|
**kwargs: Any,
|
|
) -> BaseResolver:
|
|
package_name: str = __name__.split(".")[0]
|
|
|
|
module_expr = f".{protocol.value.replace('-', '_')}"
|
|
|
|
if implementation:
|
|
module_expr += f"._{implementation.replace('-', '_').lower()}"
|
|
|
|
spe_msg = " " if specifier is None else f' (w/ specifier "{specifier}") '
|
|
|
|
try:
|
|
resolver_module = importlib.import_module(
|
|
module_expr, f"{package_name}.contrib.resolver"
|
|
)
|
|
except ImportError as e:
|
|
raise NotImplementedError(
|
|
f"{protocol}{spe_msg}cannot be loaded. Tried to import '{module_expr}'. Did you specify a non-existent implementation?"
|
|
) from e
|
|
|
|
implementations: list[tuple[str, type[BaseResolver]]] = inspect.getmembers(
|
|
resolver_module,
|
|
lambda e: isinstance(e, type)
|
|
and issubclass(e, BaseResolver)
|
|
and (
|
|
(specifier is None and e.specifier is None) or specifier == e.specifier
|
|
),
|
|
)
|
|
|
|
if not implementations:
|
|
raise NotImplementedError(
|
|
f"{protocol}{spe_msg}cannot be loaded. "
|
|
"No compatible implementation available. "
|
|
"Make sure your implementation inherit from BaseResolver."
|
|
)
|
|
|
|
implementation_target: type[BaseResolver] = implementations.pop()[1]
|
|
|
|
return implementation_target(**kwargs)
|
|
|
|
|
|
class ResolverDescription:
|
|
"""Describe how a BaseResolver must be instantiated."""
|
|
|
|
def __init__(
|
|
self,
|
|
protocol: ProtocolResolver,
|
|
specifier: str | None = None,
|
|
implementation: str | None = None,
|
|
server: str | None = None,
|
|
port: int | None = None,
|
|
*host_patterns: str,
|
|
**kwargs: typing.Any,
|
|
) -> None:
|
|
self.protocol = protocol
|
|
self.specifier = specifier
|
|
self.implementation = implementation
|
|
self.server = server
|
|
self.port = port
|
|
self.host_patterns = host_patterns
|
|
self.kwargs = kwargs
|
|
|
|
def __setitem__(self, key: str, value: typing.Any) -> None:
|
|
self.kwargs[key] = value
|
|
|
|
def __contains__(self, item: str) -> bool:
|
|
return item in self.kwargs
|
|
|
|
def new(self) -> BaseResolver:
|
|
kwargs = {**self.kwargs}
|
|
|
|
if self.server:
|
|
kwargs["server"] = self.server
|
|
if self.port:
|
|
kwargs["port"] = self.port
|
|
if self.host_patterns:
|
|
kwargs["patterns"] = self.host_patterns
|
|
|
|
return ResolverFactory.new(
|
|
self.protocol,
|
|
self.specifier,
|
|
self.implementation,
|
|
**kwargs,
|
|
)
|
|
|
|
@staticmethod
|
|
def from_url(url: str) -> ResolverDescription:
|
|
parsed_url = parse_url(url)
|
|
|
|
schema = parsed_url.scheme
|
|
|
|
if schema is None:
|
|
raise ValueError("Given DNS url is missing a protocol")
|
|
|
|
specifier = None
|
|
implementation = None
|
|
|
|
if "+" in schema:
|
|
schema, specifier = tuple(schema.lower().split("+", 1))
|
|
|
|
protocol = ProtocolResolver(schema)
|
|
kwargs: dict[str, typing.Any] = {}
|
|
|
|
if parsed_url.path:
|
|
kwargs["path"] = parsed_url.path
|
|
|
|
if parsed_url.auth:
|
|
kwargs["headers"] = dict()
|
|
if ":" in parsed_url.auth:
|
|
username, password = parsed_url.auth.split(":")
|
|
|
|
username = username.strip("'\"")
|
|
password = password.strip("'\"")
|
|
|
|
kwargs["headers"]["Authorization"] = (
|
|
f"Basic {b64encode(f'{username}:{password}'.encode()).decode()}"
|
|
)
|
|
else:
|
|
kwargs["headers"]["Authorization"] = f"Bearer {parsed_url.auth}"
|
|
|
|
if parsed_url.query:
|
|
parameters = parse_qs(parsed_url.query)
|
|
|
|
for parameter in parameters:
|
|
if not parameters[parameter]:
|
|
continue
|
|
|
|
parameter_insensible = parameter.lower()
|
|
|
|
if (
|
|
isinstance(parameters[parameter], list)
|
|
and len(parameters[parameter]) > 1
|
|
):
|
|
if parameter == "implementation":
|
|
raise ValueError("Only one implementation can be passed to URL")
|
|
|
|
values = []
|
|
|
|
for e in parameters[parameter]:
|
|
if "," in e:
|
|
values.extend(e.split(","))
|
|
else:
|
|
values.append(e)
|
|
|
|
if parameter_insensible in kwargs:
|
|
if isinstance(kwargs[parameter_insensible], list):
|
|
kwargs[parameter_insensible].extend(values)
|
|
else:
|
|
values.append(kwargs[parameter_insensible])
|
|
kwargs[parameter_insensible] = values
|
|
continue
|
|
|
|
kwargs[parameter_insensible] = values
|
|
continue
|
|
|
|
value: str = parameters[parameter][0].lower().strip(" ")
|
|
|
|
if parameter == "implementation":
|
|
implementation = value
|
|
continue
|
|
|
|
if "," in value:
|
|
list_of_values = value.split(",")
|
|
|
|
if parameter_insensible in kwargs:
|
|
if isinstance(kwargs[parameter_insensible], list):
|
|
kwargs[parameter_insensible].extend(list_of_values)
|
|
else:
|
|
list_of_values.append(kwargs[parameter_insensible])
|
|
continue
|
|
|
|
kwargs[parameter_insensible] = list_of_values
|
|
continue
|
|
|
|
value_converted: bool | int | float | None = None
|
|
|
|
if value in ["false", "true"]:
|
|
value_converted = True if value == "true" else False
|
|
elif value.isdigit():
|
|
value_converted = int(value)
|
|
elif (
|
|
value.count(".") == 1
|
|
and value.index(".") > 0
|
|
and value.replace(".", "").isdigit()
|
|
):
|
|
value_converted = float(value)
|
|
|
|
kwargs[parameter_insensible] = (
|
|
value if value_converted is None else value_converted
|
|
)
|
|
|
|
host_patterns: list[str] = []
|
|
|
|
if "hosts" in kwargs:
|
|
host_patterns = (
|
|
kwargs["hosts"].split(",")
|
|
if isinstance(kwargs["hosts"], str)
|
|
else kwargs["hosts"]
|
|
)
|
|
del kwargs["hosts"]
|
|
|
|
return ResolverDescription(
|
|
protocol,
|
|
specifier,
|
|
implementation,
|
|
parsed_url.host,
|
|
parsed_url.port,
|
|
*host_patterns,
|
|
**kwargs,
|
|
)
|