diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3dc4fb389178fd7837d06121806d5a1f78f495b4
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/__init__.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/_compat.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/_compat.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1b5a708cb7736892776f13b439c57b2736ca28f4
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/_compat.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/cli.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/cli.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..109fdb1cb894a7b951f9dc3c12e84282421160de
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/cli.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/concurrency.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/concurrency.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..803b372f3dc669bb331703cfac4c4111bbdf9305
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/concurrency.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/encoders.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/encoders.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1910a2e5a6a58360b70efbce7dd7e5bd006c5b7d
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/encoders.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/exception_handlers.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/exception_handlers.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c50ad8ed8e24065726ef2eb4c548f19e5364cb24
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/exception_handlers.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/exceptions.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/exceptions.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..066d754c9d95a77bfc8ff1ae42a6c13ec13e4272
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/exceptions.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/params.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/params.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9d4e94695b495595a0d81aca11d217dedb35223d
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/params.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/responses.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/responses.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..052871a6374f1da0625413846a18d4e01dc9f829
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/responses.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/routing.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/routing.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ba057dbcd07ef8f7b0ff6fe97b801e3f2c8e19a4
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/routing.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/staticfiles.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/staticfiles.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c8be3e41758480b73589830e558c108715720a9a
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/staticfiles.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/types.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/types.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..26260028f8d2264439957a480726dd7f2027f697
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/types.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/__pycache__/utils.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1e472c8b4f9c6f177ba8774457c1f169e93ff4c2
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/__pycache__/utils.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/dependencies/__init__.py b/.venv/lib/python3.11/site-packages/fastapi/dependencies/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/.venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..fc9f1a61e7a12ffcb9c62cefa9119611de237eba
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/__init__.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/models.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/models.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..16ba68589de13251f7afb4e8fb72eb7ee5ebb985
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/models.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/utils.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c4a970f14643b1e3f1caafc54b52710f95a0cebc
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/utils.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/dependencies/models.py b/.venv/lib/python3.11/site-packages/fastapi/dependencies/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..418c117259aa31de5c9d9bf6a4e5cfcbb5dab2d8
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/dependencies/models.py
@@ -0,0 +1,37 @@
+from dataclasses import dataclass, field
+from typing import Any, Callable, List, Optional, Sequence, Tuple
+
+from fastapi._compat import ModelField
+from fastapi.security.base import SecurityBase
+
+
+@dataclass
+class SecurityRequirement:
+ security_scheme: SecurityBase
+ scopes: Optional[Sequence[str]] = None
+
+
+@dataclass
+class Dependant:
+ path_params: List[ModelField] = field(default_factory=list)
+ query_params: List[ModelField] = field(default_factory=list)
+ header_params: List[ModelField] = field(default_factory=list)
+ cookie_params: List[ModelField] = field(default_factory=list)
+ body_params: List[ModelField] = field(default_factory=list)
+ dependencies: List["Dependant"] = field(default_factory=list)
+ security_requirements: List[SecurityRequirement] = field(default_factory=list)
+ name: Optional[str] = None
+ call: Optional[Callable[..., Any]] = None
+ request_param_name: Optional[str] = None
+ websocket_param_name: Optional[str] = None
+ http_connection_param_name: Optional[str] = None
+ response_param_name: Optional[str] = None
+ background_tasks_param_name: Optional[str] = None
+ security_scopes_param_name: Optional[str] = None
+ security_scopes: Optional[List[str]] = None
+ use_cache: bool = True
+ path: Optional[str] = None
+ cache_key: Tuple[Optional[Callable[..., Any]], Tuple[str, ...]] = field(init=False)
+
+ def __post_init__(self) -> None:
+ self.cache_key = (self.call, tuple(sorted(set(self.security_scopes or []))))
diff --git a/.venv/lib/python3.11/site-packages/fastapi/dependencies/utils.py b/.venv/lib/python3.11/site-packages/fastapi/dependencies/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2866b48894b794c9fe7029a2c128b1751ff2754
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/dependencies/utils.py
@@ -0,0 +1,972 @@
+import inspect
+from contextlib import AsyncExitStack, contextmanager
+from copy import copy, deepcopy
+from dataclasses import dataclass
+from typing import (
+ Any,
+ Callable,
+ Coroutine,
+ Dict,
+ ForwardRef,
+ List,
+ Mapping,
+ Optional,
+ Sequence,
+ Tuple,
+ Type,
+ Union,
+ cast,
+)
+
+import anyio
+from fastapi import params
+from fastapi._compat import (
+ PYDANTIC_V2,
+ ErrorWrapper,
+ ModelField,
+ RequiredParam,
+ Undefined,
+ _regenerate_error_with_loc,
+ copy_field_info,
+ create_body_model,
+ evaluate_forwardref,
+ field_annotation_is_scalar,
+ get_annotation_from_field_info,
+ get_cached_model_fields,
+ get_missing_field_error,
+ is_bytes_field,
+ is_bytes_sequence_field,
+ is_scalar_field,
+ is_scalar_sequence_field,
+ is_sequence_field,
+ is_uploadfile_or_nonable_uploadfile_annotation,
+ is_uploadfile_sequence_annotation,
+ lenient_issubclass,
+ sequence_types,
+ serialize_sequence_value,
+ value_is_sequence,
+)
+from fastapi.background import BackgroundTasks
+from fastapi.concurrency import (
+ asynccontextmanager,
+ contextmanager_in_threadpool,
+)
+from fastapi.dependencies.models import Dependant, SecurityRequirement
+from fastapi.logger import logger
+from fastapi.security.base import SecurityBase
+from fastapi.security.oauth2 import OAuth2, SecurityScopes
+from fastapi.security.open_id_connect_url import OpenIdConnect
+from fastapi.utils import create_model_field, get_path_param_names
+from pydantic import BaseModel
+from pydantic.fields import FieldInfo
+from starlette.background import BackgroundTasks as StarletteBackgroundTasks
+from starlette.concurrency import run_in_threadpool
+from starlette.datastructures import (
+ FormData,
+ Headers,
+ ImmutableMultiDict,
+ QueryParams,
+ UploadFile,
+)
+from starlette.requests import HTTPConnection, Request
+from starlette.responses import Response
+from starlette.websockets import WebSocket
+from typing_extensions import Annotated, get_args, get_origin
+
+multipart_not_installed_error = (
+ 'Form data requires "python-multipart" to be installed. \n'
+ 'You can install "python-multipart" with: \n\n'
+ "pip install python-multipart\n"
+)
+multipart_incorrect_install_error = (
+ 'Form data requires "python-multipart" to be installed. '
+ 'It seems you installed "multipart" instead. \n'
+ 'You can remove "multipart" with: \n\n'
+ "pip uninstall multipart\n\n"
+ 'And then install "python-multipart" with: \n\n'
+ "pip install python-multipart\n"
+)
+
+
+def ensure_multipart_is_installed() -> None:
+ try:
+ from python_multipart import __version__
+
+ # Import an attribute that can be mocked/deleted in testing
+ assert __version__ > "0.0.12"
+ except (ImportError, AssertionError):
+ try:
+ # __version__ is available in both multiparts, and can be mocked
+ from multipart import __version__ # type: ignore[no-redef,import-untyped]
+
+ assert __version__
+ try:
+ # parse_options_header is only available in the right multipart
+ from multipart.multipart import ( # type: ignore[import-untyped]
+ parse_options_header,
+ )
+
+ assert parse_options_header
+ except ImportError:
+ logger.error(multipart_incorrect_install_error)
+ raise RuntimeError(multipart_incorrect_install_error) from None
+ except ImportError:
+ logger.error(multipart_not_installed_error)
+ raise RuntimeError(multipart_not_installed_error) from None
+
+
+def get_param_sub_dependant(
+ *,
+ param_name: str,
+ depends: params.Depends,
+ path: str,
+ security_scopes: Optional[List[str]] = None,
+) -> Dependant:
+ assert depends.dependency
+ return get_sub_dependant(
+ depends=depends,
+ dependency=depends.dependency,
+ path=path,
+ name=param_name,
+ security_scopes=security_scopes,
+ )
+
+
+def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> Dependant:
+ assert callable(
+ depends.dependency
+ ), "A parameter-less dependency must have a callable dependency"
+ return get_sub_dependant(depends=depends, dependency=depends.dependency, path=path)
+
+
+def get_sub_dependant(
+ *,
+ depends: params.Depends,
+ dependency: Callable[..., Any],
+ path: str,
+ name: Optional[str] = None,
+ security_scopes: Optional[List[str]] = None,
+) -> Dependant:
+ security_requirement = None
+ security_scopes = security_scopes or []
+ if isinstance(depends, params.Security):
+ dependency_scopes = depends.scopes
+ security_scopes.extend(dependency_scopes)
+ if isinstance(dependency, SecurityBase):
+ use_scopes: List[str] = []
+ if isinstance(dependency, (OAuth2, OpenIdConnect)):
+ use_scopes = security_scopes
+ security_requirement = SecurityRequirement(
+ security_scheme=dependency, scopes=use_scopes
+ )
+ sub_dependant = get_dependant(
+ path=path,
+ call=dependency,
+ name=name,
+ security_scopes=security_scopes,
+ use_cache=depends.use_cache,
+ )
+ if security_requirement:
+ sub_dependant.security_requirements.append(security_requirement)
+ return sub_dependant
+
+
+CacheKey = Tuple[Optional[Callable[..., Any]], Tuple[str, ...]]
+
+
+def get_flat_dependant(
+ dependant: Dependant,
+ *,
+ skip_repeats: bool = False,
+ visited: Optional[List[CacheKey]] = None,
+) -> Dependant:
+ if visited is None:
+ visited = []
+ visited.append(dependant.cache_key)
+
+ flat_dependant = Dependant(
+ path_params=dependant.path_params.copy(),
+ query_params=dependant.query_params.copy(),
+ header_params=dependant.header_params.copy(),
+ cookie_params=dependant.cookie_params.copy(),
+ body_params=dependant.body_params.copy(),
+ security_requirements=dependant.security_requirements.copy(),
+ use_cache=dependant.use_cache,
+ path=dependant.path,
+ )
+ for sub_dependant in dependant.dependencies:
+ if skip_repeats and sub_dependant.cache_key in visited:
+ continue
+ flat_sub = get_flat_dependant(
+ sub_dependant, skip_repeats=skip_repeats, visited=visited
+ )
+ flat_dependant.path_params.extend(flat_sub.path_params)
+ flat_dependant.query_params.extend(flat_sub.query_params)
+ flat_dependant.header_params.extend(flat_sub.header_params)
+ flat_dependant.cookie_params.extend(flat_sub.cookie_params)
+ flat_dependant.body_params.extend(flat_sub.body_params)
+ flat_dependant.security_requirements.extend(flat_sub.security_requirements)
+ return flat_dependant
+
+
+def _get_flat_fields_from_params(fields: List[ModelField]) -> List[ModelField]:
+ if not fields:
+ return fields
+ first_field = fields[0]
+ if len(fields) == 1 and lenient_issubclass(first_field.type_, BaseModel):
+ fields_to_extract = get_cached_model_fields(first_field.type_)
+ return fields_to_extract
+ return fields
+
+
+def get_flat_params(dependant: Dependant) -> List[ModelField]:
+ flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
+ path_params = _get_flat_fields_from_params(flat_dependant.path_params)
+ query_params = _get_flat_fields_from_params(flat_dependant.query_params)
+ header_params = _get_flat_fields_from_params(flat_dependant.header_params)
+ cookie_params = _get_flat_fields_from_params(flat_dependant.cookie_params)
+ return path_params + query_params + header_params + cookie_params
+
+
+def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
+ signature = inspect.signature(call)
+ globalns = getattr(call, "__globals__", {})
+ typed_params = [
+ inspect.Parameter(
+ name=param.name,
+ kind=param.kind,
+ default=param.default,
+ annotation=get_typed_annotation(param.annotation, globalns),
+ )
+ for param in signature.parameters.values()
+ ]
+ typed_signature = inspect.Signature(typed_params)
+ return typed_signature
+
+
+def get_typed_annotation(annotation: Any, globalns: Dict[str, Any]) -> Any:
+ if isinstance(annotation, str):
+ annotation = ForwardRef(annotation)
+ annotation = evaluate_forwardref(annotation, globalns, globalns)
+ return annotation
+
+
+def get_typed_return_annotation(call: Callable[..., Any]) -> Any:
+ signature = inspect.signature(call)
+ annotation = signature.return_annotation
+
+ if annotation is inspect.Signature.empty:
+ return None
+
+ globalns = getattr(call, "__globals__", {})
+ return get_typed_annotation(annotation, globalns)
+
+
+def get_dependant(
+ *,
+ path: str,
+ call: Callable[..., Any],
+ name: Optional[str] = None,
+ security_scopes: Optional[List[str]] = None,
+ use_cache: bool = True,
+) -> Dependant:
+ path_param_names = get_path_param_names(path)
+ endpoint_signature = get_typed_signature(call)
+ signature_params = endpoint_signature.parameters
+ dependant = Dependant(
+ call=call,
+ name=name,
+ path=path,
+ security_scopes=security_scopes,
+ use_cache=use_cache,
+ )
+ for param_name, param in signature_params.items():
+ is_path_param = param_name in path_param_names
+ param_details = analyze_param(
+ param_name=param_name,
+ annotation=param.annotation,
+ value=param.default,
+ is_path_param=is_path_param,
+ )
+ if param_details.depends is not None:
+ sub_dependant = get_param_sub_dependant(
+ param_name=param_name,
+ depends=param_details.depends,
+ path=path,
+ security_scopes=security_scopes,
+ )
+ dependant.dependencies.append(sub_dependant)
+ continue
+ if add_non_field_param_to_dependency(
+ param_name=param_name,
+ type_annotation=param_details.type_annotation,
+ dependant=dependant,
+ ):
+ assert (
+ param_details.field is None
+ ), f"Cannot specify multiple FastAPI annotations for {param_name!r}"
+ continue
+ assert param_details.field is not None
+ if isinstance(param_details.field.field_info, params.Body):
+ dependant.body_params.append(param_details.field)
+ else:
+ add_param_to_fields(field=param_details.field, dependant=dependant)
+ return dependant
+
+
+def add_non_field_param_to_dependency(
+ *, param_name: str, type_annotation: Any, dependant: Dependant
+) -> Optional[bool]:
+ if lenient_issubclass(type_annotation, Request):
+ dependant.request_param_name = param_name
+ return True
+ elif lenient_issubclass(type_annotation, WebSocket):
+ dependant.websocket_param_name = param_name
+ return True
+ elif lenient_issubclass(type_annotation, HTTPConnection):
+ dependant.http_connection_param_name = param_name
+ return True
+ elif lenient_issubclass(type_annotation, Response):
+ dependant.response_param_name = param_name
+ return True
+ elif lenient_issubclass(type_annotation, StarletteBackgroundTasks):
+ dependant.background_tasks_param_name = param_name
+ return True
+ elif lenient_issubclass(type_annotation, SecurityScopes):
+ dependant.security_scopes_param_name = param_name
+ return True
+ return None
+
+
+@dataclass
+class ParamDetails:
+ type_annotation: Any
+ depends: Optional[params.Depends]
+ field: Optional[ModelField]
+
+
+def analyze_param(
+ *,
+ param_name: str,
+ annotation: Any,
+ value: Any,
+ is_path_param: bool,
+) -> ParamDetails:
+ field_info = None
+ depends = None
+ type_annotation: Any = Any
+ use_annotation: Any = Any
+ if annotation is not inspect.Signature.empty:
+ use_annotation = annotation
+ type_annotation = annotation
+ # Extract Annotated info
+ if get_origin(use_annotation) is Annotated:
+ annotated_args = get_args(annotation)
+ type_annotation = annotated_args[0]
+ fastapi_annotations = [
+ arg
+ for arg in annotated_args[1:]
+ if isinstance(arg, (FieldInfo, params.Depends))
+ ]
+ fastapi_specific_annotations = [
+ arg
+ for arg in fastapi_annotations
+ if isinstance(arg, (params.Param, params.Body, params.Depends))
+ ]
+ if fastapi_specific_annotations:
+ fastapi_annotation: Union[FieldInfo, params.Depends, None] = (
+ fastapi_specific_annotations[-1]
+ )
+ else:
+ fastapi_annotation = None
+ # Set default for Annotated FieldInfo
+ if isinstance(fastapi_annotation, FieldInfo):
+ # Copy `field_info` because we mutate `field_info.default` below.
+ field_info = copy_field_info(
+ field_info=fastapi_annotation, annotation=use_annotation
+ )
+ assert (
+ field_info.default is Undefined or field_info.default is RequiredParam
+ ), (
+ f"`{field_info.__class__.__name__}` default value cannot be set in"
+ f" `Annotated` for {param_name!r}. Set the default value with `=` instead."
+ )
+ if value is not inspect.Signature.empty:
+ assert not is_path_param, "Path parameters cannot have default values"
+ field_info.default = value
+ else:
+ field_info.default = RequiredParam
+ # Get Annotated Depends
+ elif isinstance(fastapi_annotation, params.Depends):
+ depends = fastapi_annotation
+ # Get Depends from default value
+ if isinstance(value, params.Depends):
+ assert depends is None, (
+ "Cannot specify `Depends` in `Annotated` and default value"
+ f" together for {param_name!r}"
+ )
+ assert field_info is None, (
+ "Cannot specify a FastAPI annotation in `Annotated` and `Depends` as a"
+ f" default value together for {param_name!r}"
+ )
+ depends = value
+ # Get FieldInfo from default value
+ elif isinstance(value, FieldInfo):
+ assert field_info is None, (
+ "Cannot specify FastAPI annotations in `Annotated` and default value"
+ f" together for {param_name!r}"
+ )
+ field_info = value
+ if PYDANTIC_V2:
+ field_info.annotation = type_annotation
+
+ # Get Depends from type annotation
+ if depends is not None and depends.dependency is None:
+ # Copy `depends` before mutating it
+ depends = copy(depends)
+ depends.dependency = type_annotation
+
+ # Handle non-param type annotations like Request
+ if lenient_issubclass(
+ type_annotation,
+ (
+ Request,
+ WebSocket,
+ HTTPConnection,
+ Response,
+ StarletteBackgroundTasks,
+ SecurityScopes,
+ ),
+ ):
+ assert depends is None, f"Cannot specify `Depends` for type {type_annotation!r}"
+ assert (
+ field_info is None
+ ), f"Cannot specify FastAPI annotation for type {type_annotation!r}"
+ # Handle default assignations, neither field_info nor depends was not found in Annotated nor default value
+ elif field_info is None and depends is None:
+ default_value = value if value is not inspect.Signature.empty else RequiredParam
+ if is_path_param:
+ # We might check here that `default_value is RequiredParam`, but the fact is that the same
+ # parameter might sometimes be a path parameter and sometimes not. See
+ # `tests/test_infer_param_optionality.py` for an example.
+ field_info = params.Path(annotation=use_annotation)
+ elif is_uploadfile_or_nonable_uploadfile_annotation(
+ type_annotation
+ ) or is_uploadfile_sequence_annotation(type_annotation):
+ field_info = params.File(annotation=use_annotation, default=default_value)
+ elif not field_annotation_is_scalar(annotation=type_annotation):
+ field_info = params.Body(annotation=use_annotation, default=default_value)
+ else:
+ field_info = params.Query(annotation=use_annotation, default=default_value)
+
+ field = None
+ # It's a field_info, not a dependency
+ if field_info is not None:
+ # Handle field_info.in_
+ if is_path_param:
+ assert isinstance(field_info, params.Path), (
+ f"Cannot use `{field_info.__class__.__name__}` for path param"
+ f" {param_name!r}"
+ )
+ elif (
+ isinstance(field_info, params.Param)
+ and getattr(field_info, "in_", None) is None
+ ):
+ field_info.in_ = params.ParamTypes.query
+ use_annotation_from_field_info = get_annotation_from_field_info(
+ use_annotation,
+ field_info,
+ param_name,
+ )
+ if isinstance(field_info, params.Form):
+ ensure_multipart_is_installed()
+ if not field_info.alias and getattr(field_info, "convert_underscores", None):
+ alias = param_name.replace("_", "-")
+ else:
+ alias = field_info.alias or param_name
+ field_info.alias = alias
+ field = create_model_field(
+ name=param_name,
+ type_=use_annotation_from_field_info,
+ default=field_info.default,
+ alias=alias,
+ required=field_info.default in (RequiredParam, Undefined),
+ field_info=field_info,
+ )
+ if is_path_param:
+ assert is_scalar_field(
+ field=field
+ ), "Path params must be of one of the supported types"
+ elif isinstance(field_info, params.Query):
+ assert (
+ is_scalar_field(field)
+ or is_scalar_sequence_field(field)
+ or (
+ lenient_issubclass(field.type_, BaseModel)
+ # For Pydantic v1
+ and getattr(field, "shape", 1) == 1
+ )
+ )
+
+ return ParamDetails(type_annotation=type_annotation, depends=depends, field=field)
+
+
+def add_param_to_fields(*, field: ModelField, dependant: Dependant) -> None:
+ field_info = field.field_info
+ field_info_in = getattr(field_info, "in_", None)
+ if field_info_in == params.ParamTypes.path:
+ dependant.path_params.append(field)
+ elif field_info_in == params.ParamTypes.query:
+ dependant.query_params.append(field)
+ elif field_info_in == params.ParamTypes.header:
+ dependant.header_params.append(field)
+ else:
+ assert (
+ field_info_in == params.ParamTypes.cookie
+ ), f"non-body parameters must be in path, query, header or cookie: {field.name}"
+ dependant.cookie_params.append(field)
+
+
+def is_coroutine_callable(call: Callable[..., Any]) -> bool:
+ if inspect.isroutine(call):
+ return inspect.iscoroutinefunction(call)
+ if inspect.isclass(call):
+ return False
+ dunder_call = getattr(call, "__call__", None) # noqa: B004
+ return inspect.iscoroutinefunction(dunder_call)
+
+
+def is_async_gen_callable(call: Callable[..., Any]) -> bool:
+ if inspect.isasyncgenfunction(call):
+ return True
+ dunder_call = getattr(call, "__call__", None) # noqa: B004
+ return inspect.isasyncgenfunction(dunder_call)
+
+
+def is_gen_callable(call: Callable[..., Any]) -> bool:
+ if inspect.isgeneratorfunction(call):
+ return True
+ dunder_call = getattr(call, "__call__", None) # noqa: B004
+ return inspect.isgeneratorfunction(dunder_call)
+
+
+async def solve_generator(
+ *, call: Callable[..., Any], stack: AsyncExitStack, sub_values: Dict[str, Any]
+) -> Any:
+ if is_gen_callable(call):
+ cm = contextmanager_in_threadpool(contextmanager(call)(**sub_values))
+ elif is_async_gen_callable(call):
+ cm = asynccontextmanager(call)(**sub_values)
+ return await stack.enter_async_context(cm)
+
+
+@dataclass
+class SolvedDependency:
+ values: Dict[str, Any]
+ errors: List[Any]
+ background_tasks: Optional[StarletteBackgroundTasks]
+ response: Response
+ dependency_cache: Dict[Tuple[Callable[..., Any], Tuple[str]], Any]
+
+
+async def solve_dependencies(
+ *,
+ request: Union[Request, WebSocket],
+ dependant: Dependant,
+ body: Optional[Union[Dict[str, Any], FormData]] = None,
+ background_tasks: Optional[StarletteBackgroundTasks] = None,
+ response: Optional[Response] = None,
+ dependency_overrides_provider: Optional[Any] = None,
+ dependency_cache: Optional[Dict[Tuple[Callable[..., Any], Tuple[str]], Any]] = None,
+ async_exit_stack: AsyncExitStack,
+ embed_body_fields: bool,
+) -> SolvedDependency:
+ values: Dict[str, Any] = {}
+ errors: List[Any] = []
+ if response is None:
+ response = Response()
+ del response.headers["content-length"]
+ response.status_code = None # type: ignore
+ dependency_cache = dependency_cache or {}
+ sub_dependant: Dependant
+ for sub_dependant in dependant.dependencies:
+ sub_dependant.call = cast(Callable[..., Any], sub_dependant.call)
+ sub_dependant.cache_key = cast(
+ Tuple[Callable[..., Any], Tuple[str]], sub_dependant.cache_key
+ )
+ call = sub_dependant.call
+ use_sub_dependant = sub_dependant
+ if (
+ dependency_overrides_provider
+ and dependency_overrides_provider.dependency_overrides
+ ):
+ original_call = sub_dependant.call
+ call = getattr(
+ dependency_overrides_provider, "dependency_overrides", {}
+ ).get(original_call, original_call)
+ use_path: str = sub_dependant.path # type: ignore
+ use_sub_dependant = get_dependant(
+ path=use_path,
+ call=call,
+ name=sub_dependant.name,
+ security_scopes=sub_dependant.security_scopes,
+ )
+
+ solved_result = await solve_dependencies(
+ request=request,
+ dependant=use_sub_dependant,
+ body=body,
+ background_tasks=background_tasks,
+ response=response,
+ dependency_overrides_provider=dependency_overrides_provider,
+ dependency_cache=dependency_cache,
+ async_exit_stack=async_exit_stack,
+ embed_body_fields=embed_body_fields,
+ )
+ background_tasks = solved_result.background_tasks
+ dependency_cache.update(solved_result.dependency_cache)
+ if solved_result.errors:
+ errors.extend(solved_result.errors)
+ continue
+ if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache:
+ solved = dependency_cache[sub_dependant.cache_key]
+ elif is_gen_callable(call) or is_async_gen_callable(call):
+ solved = await solve_generator(
+ call=call, stack=async_exit_stack, sub_values=solved_result.values
+ )
+ elif is_coroutine_callable(call):
+ solved = await call(**solved_result.values)
+ else:
+ solved = await run_in_threadpool(call, **solved_result.values)
+ if sub_dependant.name is not None:
+ values[sub_dependant.name] = solved
+ if sub_dependant.cache_key not in dependency_cache:
+ dependency_cache[sub_dependant.cache_key] = solved
+ path_values, path_errors = request_params_to_args(
+ dependant.path_params, request.path_params
+ )
+ query_values, query_errors = request_params_to_args(
+ dependant.query_params, request.query_params
+ )
+ header_values, header_errors = request_params_to_args(
+ dependant.header_params, request.headers
+ )
+ cookie_values, cookie_errors = request_params_to_args(
+ dependant.cookie_params, request.cookies
+ )
+ values.update(path_values)
+ values.update(query_values)
+ values.update(header_values)
+ values.update(cookie_values)
+ errors += path_errors + query_errors + header_errors + cookie_errors
+ if dependant.body_params:
+ (
+ body_values,
+ body_errors,
+ ) = await request_body_to_args( # body_params checked above
+ body_fields=dependant.body_params,
+ received_body=body,
+ embed_body_fields=embed_body_fields,
+ )
+ values.update(body_values)
+ errors.extend(body_errors)
+ if dependant.http_connection_param_name:
+ values[dependant.http_connection_param_name] = request
+ if dependant.request_param_name and isinstance(request, Request):
+ values[dependant.request_param_name] = request
+ elif dependant.websocket_param_name and isinstance(request, WebSocket):
+ values[dependant.websocket_param_name] = request
+ if dependant.background_tasks_param_name:
+ if background_tasks is None:
+ background_tasks = BackgroundTasks()
+ values[dependant.background_tasks_param_name] = background_tasks
+ if dependant.response_param_name:
+ values[dependant.response_param_name] = response
+ if dependant.security_scopes_param_name:
+ values[dependant.security_scopes_param_name] = SecurityScopes(
+ scopes=dependant.security_scopes
+ )
+ return SolvedDependency(
+ values=values,
+ errors=errors,
+ background_tasks=background_tasks,
+ response=response,
+ dependency_cache=dependency_cache,
+ )
+
+
+def _validate_value_with_model_field(
+ *, field: ModelField, value: Any, values: Dict[str, Any], loc: Tuple[str, ...]
+) -> Tuple[Any, List[Any]]:
+ if value is None:
+ if field.required:
+ return None, [get_missing_field_error(loc=loc)]
+ else:
+ return deepcopy(field.default), []
+ v_, errors_ = field.validate(value, values, loc=loc)
+ if isinstance(errors_, ErrorWrapper):
+ return None, [errors_]
+ elif isinstance(errors_, list):
+ new_errors = _regenerate_error_with_loc(errors=errors_, loc_prefix=())
+ return None, new_errors
+ else:
+ return v_, []
+
+
+def _get_multidict_value(
+ field: ModelField, values: Mapping[str, Any], alias: Union[str, None] = None
+) -> Any:
+ alias = alias or field.alias
+ if is_sequence_field(field) and isinstance(values, (ImmutableMultiDict, Headers)):
+ value = values.getlist(alias)
+ else:
+ value = values.get(alias, None)
+ if (
+ value is None
+ or (
+ isinstance(field.field_info, params.Form)
+ and isinstance(value, str) # For type checks
+ and value == ""
+ )
+ or (is_sequence_field(field) and len(value) == 0)
+ ):
+ if field.required:
+ return
+ else:
+ return deepcopy(field.default)
+ return value
+
+
+def request_params_to_args(
+ fields: Sequence[ModelField],
+ received_params: Union[Mapping[str, Any], QueryParams, Headers],
+) -> Tuple[Dict[str, Any], List[Any]]:
+ values: Dict[str, Any] = {}
+ errors: List[Dict[str, Any]] = []
+
+ if not fields:
+ return values, errors
+
+ first_field = fields[0]
+ fields_to_extract = fields
+ single_not_embedded_field = False
+ if len(fields) == 1 and lenient_issubclass(first_field.type_, BaseModel):
+ fields_to_extract = get_cached_model_fields(first_field.type_)
+ single_not_embedded_field = True
+
+ params_to_process: Dict[str, Any] = {}
+
+ processed_keys = set()
+
+ for field in fields_to_extract:
+ alias = None
+ if isinstance(received_params, Headers):
+ # Handle fields extracted from a Pydantic Model for a header, each field
+ # doesn't have a FieldInfo of type Header with the default convert_underscores=True
+ convert_underscores = getattr(field.field_info, "convert_underscores", True)
+ if convert_underscores:
+ alias = (
+ field.alias
+ if field.alias != field.name
+ else field.name.replace("_", "-")
+ )
+ value = _get_multidict_value(field, received_params, alias=alias)
+ if value is not None:
+ params_to_process[field.name] = value
+ processed_keys.add(alias or field.alias)
+ processed_keys.add(field.name)
+
+ for key, value in received_params.items():
+ if key not in processed_keys:
+ params_to_process[key] = value
+
+ if single_not_embedded_field:
+ field_info = first_field.field_info
+ assert isinstance(
+ field_info, params.Param
+ ), "Params must be subclasses of Param"
+ loc: Tuple[str, ...] = (field_info.in_.value,)
+ v_, errors_ = _validate_value_with_model_field(
+ field=first_field, value=params_to_process, values=values, loc=loc
+ )
+ return {first_field.name: v_}, errors_
+
+ for field in fields:
+ value = _get_multidict_value(field, received_params)
+ field_info = field.field_info
+ assert isinstance(
+ field_info, params.Param
+ ), "Params must be subclasses of Param"
+ loc = (field_info.in_.value, field.alias)
+ v_, errors_ = _validate_value_with_model_field(
+ field=field, value=value, values=values, loc=loc
+ )
+ if errors_:
+ errors.extend(errors_)
+ else:
+ values[field.name] = v_
+ return values, errors
+
+
+def _should_embed_body_fields(fields: List[ModelField]) -> bool:
+ if not fields:
+ return False
+ # More than one dependency could have the same field, it would show up as multiple
+ # fields but it's the same one, so count them by name
+ body_param_names_set = {field.name for field in fields}
+ # A top level field has to be a single field, not multiple
+ if len(body_param_names_set) > 1:
+ return True
+ first_field = fields[0]
+ # If it explicitly specifies it is embedded, it has to be embedded
+ if getattr(first_field.field_info, "embed", None):
+ return True
+ # If it's a Form (or File) field, it has to be a BaseModel to be top level
+ # otherwise it has to be embedded, so that the key value pair can be extracted
+ if isinstance(first_field.field_info, params.Form) and not lenient_issubclass(
+ first_field.type_, BaseModel
+ ):
+ return True
+ return False
+
+
+async def _extract_form_body(
+ body_fields: List[ModelField],
+ received_body: FormData,
+) -> Dict[str, Any]:
+ values = {}
+ first_field = body_fields[0]
+ first_field_info = first_field.field_info
+
+ for field in body_fields:
+ value = _get_multidict_value(field, received_body)
+ if (
+ isinstance(first_field_info, params.File)
+ and is_bytes_field(field)
+ and isinstance(value, UploadFile)
+ ):
+ value = await value.read()
+ elif (
+ is_bytes_sequence_field(field)
+ and isinstance(first_field_info, params.File)
+ and value_is_sequence(value)
+ ):
+ # For types
+ assert isinstance(value, sequence_types) # type: ignore[arg-type]
+ results: List[Union[bytes, str]] = []
+
+ async def process_fn(
+ fn: Callable[[], Coroutine[Any, Any, Any]],
+ ) -> None:
+ result = await fn()
+ results.append(result) # noqa: B023
+
+ async with anyio.create_task_group() as tg:
+ for sub_value in value:
+ tg.start_soon(process_fn, sub_value.read)
+ value = serialize_sequence_value(field=field, value=results)
+ if value is not None:
+ values[field.alias] = value
+ for key, value in received_body.items():
+ if key not in values:
+ values[key] = value
+ return values
+
+
+async def request_body_to_args(
+ body_fields: List[ModelField],
+ received_body: Optional[Union[Dict[str, Any], FormData]],
+ embed_body_fields: bool,
+) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]:
+ values: Dict[str, Any] = {}
+ errors: List[Dict[str, Any]] = []
+ assert body_fields, "request_body_to_args() should be called with fields"
+ single_not_embedded_field = len(body_fields) == 1 and not embed_body_fields
+ first_field = body_fields[0]
+ body_to_process = received_body
+
+ fields_to_extract: List[ModelField] = body_fields
+
+ if single_not_embedded_field and lenient_issubclass(first_field.type_, BaseModel):
+ fields_to_extract = get_cached_model_fields(first_field.type_)
+
+ if isinstance(received_body, FormData):
+ body_to_process = await _extract_form_body(fields_to_extract, received_body)
+
+ if single_not_embedded_field:
+ loc: Tuple[str, ...] = ("body",)
+ v_, errors_ = _validate_value_with_model_field(
+ field=first_field, value=body_to_process, values=values, loc=loc
+ )
+ return {first_field.name: v_}, errors_
+ for field in body_fields:
+ loc = ("body", field.alias)
+ value: Optional[Any] = None
+ if body_to_process is not None:
+ try:
+ value = body_to_process.get(field.alias)
+ # If the received body is a list, not a dict
+ except AttributeError:
+ errors.append(get_missing_field_error(loc))
+ continue
+ v_, errors_ = _validate_value_with_model_field(
+ field=field, value=value, values=values, loc=loc
+ )
+ if errors_:
+ errors.extend(errors_)
+ else:
+ values[field.name] = v_
+ return values, errors
+
+
+def get_body_field(
+ *, flat_dependant: Dependant, name: str, embed_body_fields: bool
+) -> Optional[ModelField]:
+ """
+ Get a ModelField representing the request body for a path operation, combining
+ all body parameters into a single field if necessary.
+
+ Used to check if it's form data (with `isinstance(body_field, params.Form)`)
+ or JSON and to generate the JSON Schema for a request body.
+
+ This is **not** used to validate/parse the request body, that's done with each
+ individual body parameter.
+ """
+ if not flat_dependant.body_params:
+ return None
+ first_param = flat_dependant.body_params[0]
+ if not embed_body_fields:
+ return first_param
+ model_name = "Body_" + name
+ BodyModel = create_body_model(
+ fields=flat_dependant.body_params, model_name=model_name
+ )
+ required = any(True for f in flat_dependant.body_params if f.required)
+ BodyFieldInfo_kwargs: Dict[str, Any] = {
+ "annotation": BodyModel,
+ "alias": "body",
+ }
+ if not required:
+ BodyFieldInfo_kwargs["default"] = None
+ if any(isinstance(f.field_info, params.File) for f in flat_dependant.body_params):
+ BodyFieldInfo: Type[params.Body] = params.File
+ elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params):
+ BodyFieldInfo = params.Form
+ else:
+ BodyFieldInfo = params.Body
+
+ body_param_media_types = [
+ f.field_info.media_type
+ for f in flat_dependant.body_params
+ if isinstance(f.field_info, params.Body)
+ ]
+ if len(set(body_param_media_types)) == 1:
+ BodyFieldInfo_kwargs["media_type"] = body_param_media_types[0]
+ final_field = create_model_field(
+ name="body",
+ type_=BodyModel,
+ required=required,
+ alias="body",
+ field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
+ )
+ return final_field
diff --git a/.venv/lib/python3.11/site-packages/fastapi/openapi/__init__.py b/.venv/lib/python3.11/site-packages/fastapi/openapi/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..482f36a73f4e0b97391665be53701d8b28e178d7
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/__init__.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/constants.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/constants.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..561091aa425753592948eda02b349e50a2b434e5
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/constants.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/docs.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/docs.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..fe8bde32791a5939221fc9e3e172d74c6899e44a
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/docs.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/models.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/models.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b720c53335c1da892fb1ac36f08394eb76374ebf
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/models.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/utils.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..fd96e19a22670d9373817f31e3b8e8bf99ad519a
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/utils.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/openapi/constants.py b/.venv/lib/python3.11/site-packages/fastapi/openapi/constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..d724ee3cfdbcda1c39f39511046c7a884186ca98
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/openapi/constants.py
@@ -0,0 +1,3 @@
+METHODS_WITH_BODY = {"GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"}
+REF_PREFIX = "#/components/schemas/"
+REF_TEMPLATE = "#/components/schemas/{model}"
diff --git a/.venv/lib/python3.11/site-packages/fastapi/openapi/docs.py b/.venv/lib/python3.11/site-packages/fastapi/openapi/docs.py
new file mode 100644
index 0000000000000000000000000000000000000000..c2ec358d2fed946d19b47dc02560e1a79819dd12
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/openapi/docs.py
@@ -0,0 +1,344 @@
+import json
+from typing import Any, Dict, Optional
+
+from fastapi.encoders import jsonable_encoder
+from starlette.responses import HTMLResponse
+from typing_extensions import Annotated, Doc
+
+swagger_ui_default_parameters: Annotated[
+ Dict[str, Any],
+ Doc(
+ """
+ Default configurations for Swagger UI.
+
+ You can use it as a template to add any other configurations needed.
+ """
+ ),
+] = {
+ "dom_id": "#swagger-ui",
+ "layout": "BaseLayout",
+ "deepLinking": True,
+ "showExtensions": True,
+ "showCommonExtensions": True,
+}
+
+
+def get_swagger_ui_html(
+ *,
+ openapi_url: Annotated[
+ str,
+ Doc(
+ """
+ The OpenAPI URL that Swagger UI should load and use.
+
+ This is normally done automatically by FastAPI using the default URL
+ `/openapi.json`.
+ """
+ ),
+ ],
+ title: Annotated[
+ str,
+ Doc(
+ """
+ The HTML `
` content, normally shown in the browser tab.
+ """
+ ),
+ ],
+ swagger_js_url: Annotated[
+ str,
+ Doc(
+ """
+ The URL to use to load the Swagger UI JavaScript.
+
+ It is normally set to a CDN URL.
+ """
+ ),
+ ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
+ swagger_css_url: Annotated[
+ str,
+ Doc(
+ """
+ The URL to use to load the Swagger UI CSS.
+
+ It is normally set to a CDN URL.
+ """
+ ),
+ ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
+ swagger_favicon_url: Annotated[
+ str,
+ Doc(
+ """
+ The URL of the favicon to use. It is normally shown in the browser tab.
+ """
+ ),
+ ] = "https://fastapi.tiangolo.com/img/favicon.png",
+ oauth2_redirect_url: Annotated[
+ Optional[str],
+ Doc(
+ """
+ The OAuth2 redirect URL, it is normally automatically handled by FastAPI.
+ """
+ ),
+ ] = None,
+ init_oauth: Annotated[
+ Optional[Dict[str, Any]],
+ Doc(
+ """
+ A dictionary with Swagger UI OAuth2 initialization configurations.
+ """
+ ),
+ ] = None,
+ swagger_ui_parameters: Annotated[
+ Optional[Dict[str, Any]],
+ Doc(
+ """
+ Configuration parameters for Swagger UI.
+
+ It defaults to [swagger_ui_default_parameters][fastapi.openapi.docs.swagger_ui_default_parameters].
+ """
+ ),
+ ] = None,
+) -> HTMLResponse:
+ """
+ Generate and return the HTML that loads Swagger UI for the interactive
+ API docs (normally served at `/docs`).
+
+ You would only call this function yourself if you needed to override some parts,
+ for example the URLs to use to load Swagger UI's JavaScript and CSS.
+
+ Read more about it in the
+ [FastAPI docs for Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/)
+ and the [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
+ """
+ current_swagger_ui_parameters = swagger_ui_default_parameters.copy()
+ if swagger_ui_parameters:
+ current_swagger_ui_parameters.update(swagger_ui_parameters)
+
+ html = f"""
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+ """
+ return HTMLResponse(html)
+
+
+def get_redoc_html(
+ *,
+ openapi_url: Annotated[
+ str,
+ Doc(
+ """
+ The OpenAPI URL that ReDoc should load and use.
+
+ This is normally done automatically by FastAPI using the default URL
+ `/openapi.json`.
+ """
+ ),
+ ],
+ title: Annotated[
+ str,
+ Doc(
+ """
+ The HTML `` content, normally shown in the browser tab.
+ """
+ ),
+ ],
+ redoc_js_url: Annotated[
+ str,
+ Doc(
+ """
+ The URL to use to load the ReDoc JavaScript.
+
+ It is normally set to a CDN URL.
+ """
+ ),
+ ] = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js",
+ redoc_favicon_url: Annotated[
+ str,
+ Doc(
+ """
+ The URL of the favicon to use. It is normally shown in the browser tab.
+ """
+ ),
+ ] = "https://fastapi.tiangolo.com/img/favicon.png",
+ with_google_fonts: Annotated[
+ bool,
+ Doc(
+ """
+ Load and use Google Fonts.
+ """
+ ),
+ ] = True,
+) -> HTMLResponse:
+ """
+ Generate and return the HTML response that loads ReDoc for the alternative
+ API docs (normally served at `/redoc`).
+
+ You would only call this function yourself if you needed to override some parts,
+ for example the URLs to use to load ReDoc's JavaScript and CSS.
+
+ Read more about it in the
+ [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
+ """
+ html = f"""
+
+
+
+ {title}
+
+
+
+ """
+ if with_google_fonts:
+ html += """
+
+ """
+ html += f"""
+
+
+
+
+
+
+
+
+
+
+ """
+ return HTMLResponse(html)
+
+
+def get_swagger_ui_oauth2_redirect_html() -> HTMLResponse:
+ """
+ Generate the HTML response with the OAuth2 redirection for Swagger UI.
+
+ You normally don't need to use or change this.
+ """
+ # copied from https://github.com/swagger-api/swagger-ui/blob/v4.14.0/dist/oauth2-redirect.html
+ html = """
+
+
+
+ Swagger UI: OAuth2 Redirect
+
+
+
+
+
+ """
+ return HTMLResponse(content=html)
diff --git a/.venv/lib/python3.11/site-packages/fastapi/openapi/models.py b/.venv/lib/python3.11/site-packages/fastapi/openapi/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed07b40f576109585f7e2b3f872edd90f718c320
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/openapi/models.py
@@ -0,0 +1,445 @@
+from enum import Enum
+from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Type, Union
+
+from fastapi._compat import (
+ PYDANTIC_V2,
+ CoreSchema,
+ GetJsonSchemaHandler,
+ JsonSchemaValue,
+ _model_rebuild,
+ with_info_plain_validator_function,
+)
+from fastapi.logger import logger
+from pydantic import AnyUrl, BaseModel, Field
+from typing_extensions import Annotated, Literal, TypedDict
+from typing_extensions import deprecated as typing_deprecated
+
+try:
+ import email_validator
+
+ assert email_validator # make autoflake ignore the unused import
+ from pydantic import EmailStr
+except ImportError: # pragma: no cover
+
+ class EmailStr(str): # type: ignore
+ @classmethod
+ def __get_validators__(cls) -> Iterable[Callable[..., Any]]:
+ yield cls.validate
+
+ @classmethod
+ def validate(cls, v: Any) -> str:
+ logger.warning(
+ "email-validator not installed, email fields will be treated as str.\n"
+ "To install, run: pip install email-validator"
+ )
+ return str(v)
+
+ @classmethod
+ def _validate(cls, __input_value: Any, _: Any) -> str:
+ logger.warning(
+ "email-validator not installed, email fields will be treated as str.\n"
+ "To install, run: pip install email-validator"
+ )
+ return str(__input_value)
+
+ @classmethod
+ def __get_pydantic_json_schema__(
+ cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler
+ ) -> JsonSchemaValue:
+ return {"type": "string", "format": "email"}
+
+ @classmethod
+ def __get_pydantic_core_schema__(
+ cls, source: Type[Any], handler: Callable[[Any], CoreSchema]
+ ) -> CoreSchema:
+ return with_info_plain_validator_function(cls._validate)
+
+
+class BaseModelWithConfig(BaseModel):
+ if PYDANTIC_V2:
+ model_config = {"extra": "allow"}
+
+ else:
+
+ class Config:
+ extra = "allow"
+
+
+class Contact(BaseModelWithConfig):
+ name: Optional[str] = None
+ url: Optional[AnyUrl] = None
+ email: Optional[EmailStr] = None
+
+
+class License(BaseModelWithConfig):
+ name: str
+ identifier: Optional[str] = None
+ url: Optional[AnyUrl] = None
+
+
+class Info(BaseModelWithConfig):
+ title: str
+ summary: Optional[str] = None
+ description: Optional[str] = None
+ termsOfService: Optional[str] = None
+ contact: Optional[Contact] = None
+ license: Optional[License] = None
+ version: str
+
+
+class ServerVariable(BaseModelWithConfig):
+ enum: Annotated[Optional[List[str]], Field(min_length=1)] = None
+ default: str
+ description: Optional[str] = None
+
+
+class Server(BaseModelWithConfig):
+ url: Union[AnyUrl, str]
+ description: Optional[str] = None
+ variables: Optional[Dict[str, ServerVariable]] = None
+
+
+class Reference(BaseModel):
+ ref: str = Field(alias="$ref")
+
+
+class Discriminator(BaseModel):
+ propertyName: str
+ mapping: Optional[Dict[str, str]] = None
+
+
+class XML(BaseModelWithConfig):
+ name: Optional[str] = None
+ namespace: Optional[str] = None
+ prefix: Optional[str] = None
+ attribute: Optional[bool] = None
+ wrapped: Optional[bool] = None
+
+
+class ExternalDocumentation(BaseModelWithConfig):
+ description: Optional[str] = None
+ url: AnyUrl
+
+
+class Schema(BaseModelWithConfig):
+ # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu
+ # Core Vocabulary
+ schema_: Optional[str] = Field(default=None, alias="$schema")
+ vocabulary: Optional[str] = Field(default=None, alias="$vocabulary")
+ id: Optional[str] = Field(default=None, alias="$id")
+ anchor: Optional[str] = Field(default=None, alias="$anchor")
+ dynamicAnchor: Optional[str] = Field(default=None, alias="$dynamicAnchor")
+ ref: Optional[str] = Field(default=None, alias="$ref")
+ dynamicRef: Optional[str] = Field(default=None, alias="$dynamicRef")
+ defs: Optional[Dict[str, "SchemaOrBool"]] = Field(default=None, alias="$defs")
+ comment: Optional[str] = Field(default=None, alias="$comment")
+ # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-a-vocabulary-for-applying-s
+ # A Vocabulary for Applying Subschemas
+ allOf: Optional[List["SchemaOrBool"]] = None
+ anyOf: Optional[List["SchemaOrBool"]] = None
+ oneOf: Optional[List["SchemaOrBool"]] = None
+ not_: Optional["SchemaOrBool"] = Field(default=None, alias="not")
+ if_: Optional["SchemaOrBool"] = Field(default=None, alias="if")
+ then: Optional["SchemaOrBool"] = None
+ else_: Optional["SchemaOrBool"] = Field(default=None, alias="else")
+ dependentSchemas: Optional[Dict[str, "SchemaOrBool"]] = None
+ prefixItems: Optional[List["SchemaOrBool"]] = None
+ # TODO: uncomment and remove below when deprecating Pydantic v1
+ # It generales a list of schemas for tuples, before prefixItems was available
+ # items: Optional["SchemaOrBool"] = None
+ items: Optional[Union["SchemaOrBool", List["SchemaOrBool"]]] = None
+ contains: Optional["SchemaOrBool"] = None
+ properties: Optional[Dict[str, "SchemaOrBool"]] = None
+ patternProperties: Optional[Dict[str, "SchemaOrBool"]] = None
+ additionalProperties: Optional["SchemaOrBool"] = None
+ propertyNames: Optional["SchemaOrBool"] = None
+ unevaluatedItems: Optional["SchemaOrBool"] = None
+ unevaluatedProperties: Optional["SchemaOrBool"] = None
+ # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-structural
+ # A Vocabulary for Structural Validation
+ type: Optional[str] = None
+ enum: Optional[List[Any]] = None
+ const: Optional[Any] = None
+ multipleOf: Optional[float] = Field(default=None, gt=0)
+ maximum: Optional[float] = None
+ exclusiveMaximum: Optional[float] = None
+ minimum: Optional[float] = None
+ exclusiveMinimum: Optional[float] = None
+ maxLength: Optional[int] = Field(default=None, ge=0)
+ minLength: Optional[int] = Field(default=None, ge=0)
+ pattern: Optional[str] = None
+ maxItems: Optional[int] = Field(default=None, ge=0)
+ minItems: Optional[int] = Field(default=None, ge=0)
+ uniqueItems: Optional[bool] = None
+ maxContains: Optional[int] = Field(default=None, ge=0)
+ minContains: Optional[int] = Field(default=None, ge=0)
+ maxProperties: Optional[int] = Field(default=None, ge=0)
+ minProperties: Optional[int] = Field(default=None, ge=0)
+ required: Optional[List[str]] = None
+ dependentRequired: Optional[Dict[str, Set[str]]] = None
+ # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-vocabularies-for-semantic-c
+ # Vocabularies for Semantic Content With "format"
+ format: Optional[str] = None
+ # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-the-conten
+ # A Vocabulary for the Contents of String-Encoded Data
+ contentEncoding: Optional[str] = None
+ contentMediaType: Optional[str] = None
+ contentSchema: Optional["SchemaOrBool"] = None
+ # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-basic-meta
+ # A Vocabulary for Basic Meta-Data Annotations
+ title: Optional[str] = None
+ description: Optional[str] = None
+ default: Optional[Any] = None
+ deprecated: Optional[bool] = None
+ readOnly: Optional[bool] = None
+ writeOnly: Optional[bool] = None
+ examples: Optional[List[Any]] = None
+ # Ref: OpenAPI 3.1.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema-object
+ # Schema Object
+ discriminator: Optional[Discriminator] = None
+ xml: Optional[XML] = None
+ externalDocs: Optional[ExternalDocumentation] = None
+ example: Annotated[
+ Optional[Any],
+ typing_deprecated(
+ "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
+ "although still supported. Use examples instead."
+ ),
+ ] = None
+
+
+# Ref: https://json-schema.org/draft/2020-12/json-schema-core.html#name-json-schema-documents
+# A JSON Schema MUST be an object or a boolean.
+SchemaOrBool = Union[Schema, bool]
+
+
+class Example(TypedDict, total=False):
+ summary: Optional[str]
+ description: Optional[str]
+ value: Optional[Any]
+ externalValue: Optional[AnyUrl]
+
+ if PYDANTIC_V2: # type: ignore [misc]
+ __pydantic_config__ = {"extra": "allow"}
+
+ else:
+
+ class Config:
+ extra = "allow"
+
+
+class ParameterInType(Enum):
+ query = "query"
+ header = "header"
+ path = "path"
+ cookie = "cookie"
+
+
+class Encoding(BaseModelWithConfig):
+ contentType: Optional[str] = None
+ headers: Optional[Dict[str, Union["Header", Reference]]] = None
+ style: Optional[str] = None
+ explode: Optional[bool] = None
+ allowReserved: Optional[bool] = None
+
+
+class MediaType(BaseModelWithConfig):
+ schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema")
+ example: Optional[Any] = None
+ examples: Optional[Dict[str, Union[Example, Reference]]] = None
+ encoding: Optional[Dict[str, Encoding]] = None
+
+
+class ParameterBase(BaseModelWithConfig):
+ description: Optional[str] = None
+ required: Optional[bool] = None
+ deprecated: Optional[bool] = None
+ # Serialization rules for simple scenarios
+ style: Optional[str] = None
+ explode: Optional[bool] = None
+ allowReserved: Optional[bool] = None
+ schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema")
+ example: Optional[Any] = None
+ examples: Optional[Dict[str, Union[Example, Reference]]] = None
+ # Serialization rules for more complex scenarios
+ content: Optional[Dict[str, MediaType]] = None
+
+
+class Parameter(ParameterBase):
+ name: str
+ in_: ParameterInType = Field(alias="in")
+
+
+class Header(ParameterBase):
+ pass
+
+
+class RequestBody(BaseModelWithConfig):
+ description: Optional[str] = None
+ content: Dict[str, MediaType]
+ required: Optional[bool] = None
+
+
+class Link(BaseModelWithConfig):
+ operationRef: Optional[str] = None
+ operationId: Optional[str] = None
+ parameters: Optional[Dict[str, Union[Any, str]]] = None
+ requestBody: Optional[Union[Any, str]] = None
+ description: Optional[str] = None
+ server: Optional[Server] = None
+
+
+class Response(BaseModelWithConfig):
+ description: str
+ headers: Optional[Dict[str, Union[Header, Reference]]] = None
+ content: Optional[Dict[str, MediaType]] = None
+ links: Optional[Dict[str, Union[Link, Reference]]] = None
+
+
+class Operation(BaseModelWithConfig):
+ tags: Optional[List[str]] = None
+ summary: Optional[str] = None
+ description: Optional[str] = None
+ externalDocs: Optional[ExternalDocumentation] = None
+ operationId: Optional[str] = None
+ parameters: Optional[List[Union[Parameter, Reference]]] = None
+ requestBody: Optional[Union[RequestBody, Reference]] = None
+ # Using Any for Specification Extensions
+ responses: Optional[Dict[str, Union[Response, Any]]] = None
+ callbacks: Optional[Dict[str, Union[Dict[str, "PathItem"], Reference]]] = None
+ deprecated: Optional[bool] = None
+ security: Optional[List[Dict[str, List[str]]]] = None
+ servers: Optional[List[Server]] = None
+
+
+class PathItem(BaseModelWithConfig):
+ ref: Optional[str] = Field(default=None, alias="$ref")
+ summary: Optional[str] = None
+ description: Optional[str] = None
+ get: Optional[Operation] = None
+ put: Optional[Operation] = None
+ post: Optional[Operation] = None
+ delete: Optional[Operation] = None
+ options: Optional[Operation] = None
+ head: Optional[Operation] = None
+ patch: Optional[Operation] = None
+ trace: Optional[Operation] = None
+ servers: Optional[List[Server]] = None
+ parameters: Optional[List[Union[Parameter, Reference]]] = None
+
+
+class SecuritySchemeType(Enum):
+ apiKey = "apiKey"
+ http = "http"
+ oauth2 = "oauth2"
+ openIdConnect = "openIdConnect"
+
+
+class SecurityBase(BaseModelWithConfig):
+ type_: SecuritySchemeType = Field(alias="type")
+ description: Optional[str] = None
+
+
+class APIKeyIn(Enum):
+ query = "query"
+ header = "header"
+ cookie = "cookie"
+
+
+class APIKey(SecurityBase):
+ type_: SecuritySchemeType = Field(default=SecuritySchemeType.apiKey, alias="type")
+ in_: APIKeyIn = Field(alias="in")
+ name: str
+
+
+class HTTPBase(SecurityBase):
+ type_: SecuritySchemeType = Field(default=SecuritySchemeType.http, alias="type")
+ scheme: str
+
+
+class HTTPBearer(HTTPBase):
+ scheme: Literal["bearer"] = "bearer"
+ bearerFormat: Optional[str] = None
+
+
+class OAuthFlow(BaseModelWithConfig):
+ refreshUrl: Optional[str] = None
+ scopes: Dict[str, str] = {}
+
+
+class OAuthFlowImplicit(OAuthFlow):
+ authorizationUrl: str
+
+
+class OAuthFlowPassword(OAuthFlow):
+ tokenUrl: str
+
+
+class OAuthFlowClientCredentials(OAuthFlow):
+ tokenUrl: str
+
+
+class OAuthFlowAuthorizationCode(OAuthFlow):
+ authorizationUrl: str
+ tokenUrl: str
+
+
+class OAuthFlows(BaseModelWithConfig):
+ implicit: Optional[OAuthFlowImplicit] = None
+ password: Optional[OAuthFlowPassword] = None
+ clientCredentials: Optional[OAuthFlowClientCredentials] = None
+ authorizationCode: Optional[OAuthFlowAuthorizationCode] = None
+
+
+class OAuth2(SecurityBase):
+ type_: SecuritySchemeType = Field(default=SecuritySchemeType.oauth2, alias="type")
+ flows: OAuthFlows
+
+
+class OpenIdConnect(SecurityBase):
+ type_: SecuritySchemeType = Field(
+ default=SecuritySchemeType.openIdConnect, alias="type"
+ )
+ openIdConnectUrl: str
+
+
+SecurityScheme = Union[APIKey, HTTPBase, OAuth2, OpenIdConnect, HTTPBearer]
+
+
+class Components(BaseModelWithConfig):
+ schemas: Optional[Dict[str, Union[Schema, Reference]]] = None
+ responses: Optional[Dict[str, Union[Response, Reference]]] = None
+ parameters: Optional[Dict[str, Union[Parameter, Reference]]] = None
+ examples: Optional[Dict[str, Union[Example, Reference]]] = None
+ requestBodies: Optional[Dict[str, Union[RequestBody, Reference]]] = None
+ headers: Optional[Dict[str, Union[Header, Reference]]] = None
+ securitySchemes: Optional[Dict[str, Union[SecurityScheme, Reference]]] = None
+ links: Optional[Dict[str, Union[Link, Reference]]] = None
+ # Using Any for Specification Extensions
+ callbacks: Optional[Dict[str, Union[Dict[str, PathItem], Reference, Any]]] = None
+ pathItems: Optional[Dict[str, Union[PathItem, Reference]]] = None
+
+
+class Tag(BaseModelWithConfig):
+ name: str
+ description: Optional[str] = None
+ externalDocs: Optional[ExternalDocumentation] = None
+
+
+class OpenAPI(BaseModelWithConfig):
+ openapi: str
+ info: Info
+ jsonSchemaDialect: Optional[str] = None
+ servers: Optional[List[Server]] = None
+ # Using Any for Specification Extensions
+ paths: Optional[Dict[str, Union[PathItem, Any]]] = None
+ webhooks: Optional[Dict[str, Union[PathItem, Reference]]] = None
+ components: Optional[Components] = None
+ security: Optional[List[Dict[str, List[str]]]] = None
+ tags: Optional[List[Tag]] = None
+ externalDocs: Optional[ExternalDocumentation] = None
+
+
+_model_rebuild(Schema)
+_model_rebuild(Operation)
+_model_rebuild(Encoding)
diff --git a/.venv/lib/python3.11/site-packages/fastapi/openapi/utils.py b/.venv/lib/python3.11/site-packages/fastapi/openapi/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..947eca948e2691e91735ed446fbed5e8e6bf194b
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/openapi/utils.py
@@ -0,0 +1,548 @@
+import http.client
+import inspect
+import warnings
+from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union, cast
+
+from fastapi import routing
+from fastapi._compat import (
+ GenerateJsonSchema,
+ JsonSchemaValue,
+ ModelField,
+ Undefined,
+ get_compat_model_name_map,
+ get_definitions,
+ get_schema_from_model_field,
+ lenient_issubclass,
+)
+from fastapi.datastructures import DefaultPlaceholder
+from fastapi.dependencies.models import Dependant
+from fastapi.dependencies.utils import (
+ _get_flat_fields_from_params,
+ get_flat_dependant,
+ get_flat_params,
+)
+from fastapi.encoders import jsonable_encoder
+from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX, REF_TEMPLATE
+from fastapi.openapi.models import OpenAPI
+from fastapi.params import Body, ParamTypes
+from fastapi.responses import Response
+from fastapi.types import ModelNameMap
+from fastapi.utils import (
+ deep_dict_update,
+ generate_operation_id_for_path,
+ is_body_allowed_for_status_code,
+)
+from starlette.responses import JSONResponse
+from starlette.routing import BaseRoute
+from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
+from typing_extensions import Literal
+
+validation_error_definition = {
+ "title": "ValidationError",
+ "type": "object",
+ "properties": {
+ "loc": {
+ "title": "Location",
+ "type": "array",
+ "items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
+ },
+ "msg": {"title": "Message", "type": "string"},
+ "type": {"title": "Error Type", "type": "string"},
+ },
+ "required": ["loc", "msg", "type"],
+}
+
+validation_error_response_definition = {
+ "title": "HTTPValidationError",
+ "type": "object",
+ "properties": {
+ "detail": {
+ "title": "Detail",
+ "type": "array",
+ "items": {"$ref": REF_PREFIX + "ValidationError"},
+ }
+ },
+}
+
+status_code_ranges: Dict[str, str] = {
+ "1XX": "Information",
+ "2XX": "Success",
+ "3XX": "Redirection",
+ "4XX": "Client Error",
+ "5XX": "Server Error",
+ "DEFAULT": "Default Response",
+}
+
+
+def get_openapi_security_definitions(
+ flat_dependant: Dependant,
+) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]:
+ security_definitions = {}
+ operation_security = []
+ for security_requirement in flat_dependant.security_requirements:
+ security_definition = jsonable_encoder(
+ security_requirement.security_scheme.model,
+ by_alias=True,
+ exclude_none=True,
+ )
+ security_name = security_requirement.security_scheme.scheme_name
+ security_definitions[security_name] = security_definition
+ operation_security.append({security_name: security_requirement.scopes})
+ return security_definitions, operation_security
+
+
+def _get_openapi_operation_parameters(
+ *,
+ dependant: Dependant,
+ schema_generator: GenerateJsonSchema,
+ model_name_map: ModelNameMap,
+ field_mapping: Dict[
+ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
+ ],
+ separate_input_output_schemas: bool = True,
+) -> List[Dict[str, Any]]:
+ parameters = []
+ flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
+ path_params = _get_flat_fields_from_params(flat_dependant.path_params)
+ query_params = _get_flat_fields_from_params(flat_dependant.query_params)
+ header_params = _get_flat_fields_from_params(flat_dependant.header_params)
+ cookie_params = _get_flat_fields_from_params(flat_dependant.cookie_params)
+ parameter_groups = [
+ (ParamTypes.path, path_params),
+ (ParamTypes.query, query_params),
+ (ParamTypes.header, header_params),
+ (ParamTypes.cookie, cookie_params),
+ ]
+ for param_type, param_group in parameter_groups:
+ for param in param_group:
+ field_info = param.field_info
+ # field_info = cast(Param, field_info)
+ if not getattr(field_info, "include_in_schema", True):
+ continue
+ param_schema = get_schema_from_model_field(
+ field=param,
+ schema_generator=schema_generator,
+ model_name_map=model_name_map,
+ field_mapping=field_mapping,
+ separate_input_output_schemas=separate_input_output_schemas,
+ )
+ parameter = {
+ "name": param.alias,
+ "in": param_type.value,
+ "required": param.required,
+ "schema": param_schema,
+ }
+ if field_info.description:
+ parameter["description"] = field_info.description
+ openapi_examples = getattr(field_info, "openapi_examples", None)
+ example = getattr(field_info, "example", None)
+ if openapi_examples:
+ parameter["examples"] = jsonable_encoder(openapi_examples)
+ elif example != Undefined:
+ parameter["example"] = jsonable_encoder(example)
+ if getattr(field_info, "deprecated", None):
+ parameter["deprecated"] = True
+ parameters.append(parameter)
+ return parameters
+
+
+def get_openapi_operation_request_body(
+ *,
+ body_field: Optional[ModelField],
+ schema_generator: GenerateJsonSchema,
+ model_name_map: ModelNameMap,
+ field_mapping: Dict[
+ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
+ ],
+ separate_input_output_schemas: bool = True,
+) -> Optional[Dict[str, Any]]:
+ if not body_field:
+ return None
+ assert isinstance(body_field, ModelField)
+ body_schema = get_schema_from_model_field(
+ field=body_field,
+ schema_generator=schema_generator,
+ model_name_map=model_name_map,
+ field_mapping=field_mapping,
+ separate_input_output_schemas=separate_input_output_schemas,
+ )
+ field_info = cast(Body, body_field.field_info)
+ request_media_type = field_info.media_type
+ required = body_field.required
+ request_body_oai: Dict[str, Any] = {}
+ if required:
+ request_body_oai["required"] = required
+ request_media_content: Dict[str, Any] = {"schema": body_schema}
+ if field_info.openapi_examples:
+ request_media_content["examples"] = jsonable_encoder(
+ field_info.openapi_examples
+ )
+ elif field_info.example != Undefined:
+ request_media_content["example"] = jsonable_encoder(field_info.example)
+ request_body_oai["content"] = {request_media_type: request_media_content}
+ return request_body_oai
+
+
+def generate_operation_id(
+ *, route: routing.APIRoute, method: str
+) -> str: # pragma: nocover
+ warnings.warn(
+ "fastapi.openapi.utils.generate_operation_id() was deprecated, "
+ "it is not used internally, and will be removed soon",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ if route.operation_id:
+ return route.operation_id
+ path: str = route.path_format
+ return generate_operation_id_for_path(name=route.name, path=path, method=method)
+
+
+def generate_operation_summary(*, route: routing.APIRoute, method: str) -> str:
+ if route.summary:
+ return route.summary
+ return route.name.replace("_", " ").title()
+
+
+def get_openapi_operation_metadata(
+ *, route: routing.APIRoute, method: str, operation_ids: Set[str]
+) -> Dict[str, Any]:
+ operation: Dict[str, Any] = {}
+ if route.tags:
+ operation["tags"] = route.tags
+ operation["summary"] = generate_operation_summary(route=route, method=method)
+ if route.description:
+ operation["description"] = route.description
+ operation_id = route.operation_id or route.unique_id
+ if operation_id in operation_ids:
+ message = (
+ f"Duplicate Operation ID {operation_id} for function "
+ + f"{route.endpoint.__name__}"
+ )
+ file_name = getattr(route.endpoint, "__globals__", {}).get("__file__")
+ if file_name:
+ message += f" at {file_name}"
+ warnings.warn(message, stacklevel=1)
+ operation_ids.add(operation_id)
+ operation["operationId"] = operation_id
+ if route.deprecated:
+ operation["deprecated"] = route.deprecated
+ return operation
+
+
+def get_openapi_path(
+ *,
+ route: routing.APIRoute,
+ operation_ids: Set[str],
+ schema_generator: GenerateJsonSchema,
+ model_name_map: ModelNameMap,
+ field_mapping: Dict[
+ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
+ ],
+ separate_input_output_schemas: bool = True,
+) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]:
+ path = {}
+ security_schemes: Dict[str, Any] = {}
+ definitions: Dict[str, Any] = {}
+ assert route.methods is not None, "Methods must be a list"
+ if isinstance(route.response_class, DefaultPlaceholder):
+ current_response_class: Type[Response] = route.response_class.value
+ else:
+ current_response_class = route.response_class
+ assert current_response_class, "A response class is needed to generate OpenAPI"
+ route_response_media_type: Optional[str] = current_response_class.media_type
+ if route.include_in_schema:
+ for method in route.methods:
+ operation = get_openapi_operation_metadata(
+ route=route, method=method, operation_ids=operation_ids
+ )
+ parameters: List[Dict[str, Any]] = []
+ flat_dependant = get_flat_dependant(route.dependant, skip_repeats=True)
+ security_definitions, operation_security = get_openapi_security_definitions(
+ flat_dependant=flat_dependant
+ )
+ if operation_security:
+ operation.setdefault("security", []).extend(operation_security)
+ if security_definitions:
+ security_schemes.update(security_definitions)
+ operation_parameters = _get_openapi_operation_parameters(
+ dependant=route.dependant,
+ schema_generator=schema_generator,
+ model_name_map=model_name_map,
+ field_mapping=field_mapping,
+ separate_input_output_schemas=separate_input_output_schemas,
+ )
+ parameters.extend(operation_parameters)
+ if parameters:
+ all_parameters = {
+ (param["in"], param["name"]): param for param in parameters
+ }
+ required_parameters = {
+ (param["in"], param["name"]): param
+ for param in parameters
+ if param.get("required")
+ }
+ # Make sure required definitions of the same parameter take precedence
+ # over non-required definitions
+ all_parameters.update(required_parameters)
+ operation["parameters"] = list(all_parameters.values())
+ if method in METHODS_WITH_BODY:
+ request_body_oai = get_openapi_operation_request_body(
+ body_field=route.body_field,
+ schema_generator=schema_generator,
+ model_name_map=model_name_map,
+ field_mapping=field_mapping,
+ separate_input_output_schemas=separate_input_output_schemas,
+ )
+ if request_body_oai:
+ operation["requestBody"] = request_body_oai
+ if route.callbacks:
+ callbacks = {}
+ for callback in route.callbacks:
+ if isinstance(callback, routing.APIRoute):
+ (
+ cb_path,
+ cb_security_schemes,
+ cb_definitions,
+ ) = get_openapi_path(
+ route=callback,
+ operation_ids=operation_ids,
+ schema_generator=schema_generator,
+ model_name_map=model_name_map,
+ field_mapping=field_mapping,
+ separate_input_output_schemas=separate_input_output_schemas,
+ )
+ callbacks[callback.name] = {callback.path: cb_path}
+ operation["callbacks"] = callbacks
+ if route.status_code is not None:
+ status_code = str(route.status_code)
+ else:
+ # It would probably make more sense for all response classes to have an
+ # explicit default status_code, and to extract it from them, instead of
+ # doing this inspection tricks, that would probably be in the future
+ # TODO: probably make status_code a default class attribute for all
+ # responses in Starlette
+ response_signature = inspect.signature(current_response_class.__init__)
+ status_code_param = response_signature.parameters.get("status_code")
+ if status_code_param is not None:
+ if isinstance(status_code_param.default, int):
+ status_code = str(status_code_param.default)
+ operation.setdefault("responses", {}).setdefault(status_code, {})[
+ "description"
+ ] = route.response_description
+ if route_response_media_type and is_body_allowed_for_status_code(
+ route.status_code
+ ):
+ response_schema = {"type": "string"}
+ if lenient_issubclass(current_response_class, JSONResponse):
+ if route.response_field:
+ response_schema = get_schema_from_model_field(
+ field=route.response_field,
+ schema_generator=schema_generator,
+ model_name_map=model_name_map,
+ field_mapping=field_mapping,
+ separate_input_output_schemas=separate_input_output_schemas,
+ )
+ else:
+ response_schema = {}
+ operation.setdefault("responses", {}).setdefault(
+ status_code, {}
+ ).setdefault("content", {}).setdefault(route_response_media_type, {})[
+ "schema"
+ ] = response_schema
+ if route.responses:
+ operation_responses = operation.setdefault("responses", {})
+ for (
+ additional_status_code,
+ additional_response,
+ ) in route.responses.items():
+ process_response = additional_response.copy()
+ process_response.pop("model", None)
+ status_code_key = str(additional_status_code).upper()
+ if status_code_key == "DEFAULT":
+ status_code_key = "default"
+ openapi_response = operation_responses.setdefault(
+ status_code_key, {}
+ )
+ assert isinstance(
+ process_response, dict
+ ), "An additional response must be a dict"
+ field = route.response_fields.get(additional_status_code)
+ additional_field_schema: Optional[Dict[str, Any]] = None
+ if field:
+ additional_field_schema = get_schema_from_model_field(
+ field=field,
+ schema_generator=schema_generator,
+ model_name_map=model_name_map,
+ field_mapping=field_mapping,
+ separate_input_output_schemas=separate_input_output_schemas,
+ )
+ media_type = route_response_media_type or "application/json"
+ additional_schema = (
+ process_response.setdefault("content", {})
+ .setdefault(media_type, {})
+ .setdefault("schema", {})
+ )
+ deep_dict_update(additional_schema, additional_field_schema)
+ status_text: Optional[str] = status_code_ranges.get(
+ str(additional_status_code).upper()
+ ) or http.client.responses.get(int(additional_status_code))
+ description = (
+ process_response.get("description")
+ or openapi_response.get("description")
+ or status_text
+ or "Additional Response"
+ )
+ deep_dict_update(openapi_response, process_response)
+ openapi_response["description"] = description
+ http422 = str(HTTP_422_UNPROCESSABLE_ENTITY)
+ all_route_params = get_flat_params(route.dependant)
+ if (all_route_params or route.body_field) and not any(
+ status in operation["responses"]
+ for status in [http422, "4XX", "default"]
+ ):
+ operation["responses"][http422] = {
+ "description": "Validation Error",
+ "content": {
+ "application/json": {
+ "schema": {"$ref": REF_PREFIX + "HTTPValidationError"}
+ }
+ },
+ }
+ if "ValidationError" not in definitions:
+ definitions.update(
+ {
+ "ValidationError": validation_error_definition,
+ "HTTPValidationError": validation_error_response_definition,
+ }
+ )
+ if route.openapi_extra:
+ deep_dict_update(operation, route.openapi_extra)
+ path[method.lower()] = operation
+ return path, security_schemes, definitions
+
+
+def get_fields_from_routes(
+ routes: Sequence[BaseRoute],
+) -> List[ModelField]:
+ body_fields_from_routes: List[ModelField] = []
+ responses_from_routes: List[ModelField] = []
+ request_fields_from_routes: List[ModelField] = []
+ callback_flat_models: List[ModelField] = []
+ for route in routes:
+ if getattr(route, "include_in_schema", None) and isinstance(
+ route, routing.APIRoute
+ ):
+ if route.body_field:
+ assert isinstance(
+ route.body_field, ModelField
+ ), "A request body must be a Pydantic Field"
+ body_fields_from_routes.append(route.body_field)
+ if route.response_field:
+ responses_from_routes.append(route.response_field)
+ if route.response_fields:
+ responses_from_routes.extend(route.response_fields.values())
+ if route.callbacks:
+ callback_flat_models.extend(get_fields_from_routes(route.callbacks))
+ params = get_flat_params(route.dependant)
+ request_fields_from_routes.extend(params)
+
+ flat_models = callback_flat_models + list(
+ body_fields_from_routes + responses_from_routes + request_fields_from_routes
+ )
+ return flat_models
+
+
+def get_openapi(
+ *,
+ title: str,
+ version: str,
+ openapi_version: str = "3.1.0",
+ summary: Optional[str] = None,
+ description: Optional[str] = None,
+ routes: Sequence[BaseRoute],
+ webhooks: Optional[Sequence[BaseRoute]] = None,
+ tags: Optional[List[Dict[str, Any]]] = None,
+ servers: Optional[List[Dict[str, Union[str, Any]]]] = None,
+ terms_of_service: Optional[str] = None,
+ contact: Optional[Dict[str, Union[str, Any]]] = None,
+ license_info: Optional[Dict[str, Union[str, Any]]] = None,
+ separate_input_output_schemas: bool = True,
+) -> Dict[str, Any]:
+ info: Dict[str, Any] = {"title": title, "version": version}
+ if summary:
+ info["summary"] = summary
+ if description:
+ info["description"] = description
+ if terms_of_service:
+ info["termsOfService"] = terms_of_service
+ if contact:
+ info["contact"] = contact
+ if license_info:
+ info["license"] = license_info
+ output: Dict[str, Any] = {"openapi": openapi_version, "info": info}
+ if servers:
+ output["servers"] = servers
+ components: Dict[str, Dict[str, Any]] = {}
+ paths: Dict[str, Dict[str, Any]] = {}
+ webhook_paths: Dict[str, Dict[str, Any]] = {}
+ operation_ids: Set[str] = set()
+ all_fields = get_fields_from_routes(list(routes or []) + list(webhooks or []))
+ model_name_map = get_compat_model_name_map(all_fields)
+ schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)
+ field_mapping, definitions = get_definitions(
+ fields=all_fields,
+ schema_generator=schema_generator,
+ model_name_map=model_name_map,
+ separate_input_output_schemas=separate_input_output_schemas,
+ )
+ for route in routes or []:
+ if isinstance(route, routing.APIRoute):
+ result = get_openapi_path(
+ route=route,
+ operation_ids=operation_ids,
+ schema_generator=schema_generator,
+ model_name_map=model_name_map,
+ field_mapping=field_mapping,
+ separate_input_output_schemas=separate_input_output_schemas,
+ )
+ if result:
+ path, security_schemes, path_definitions = result
+ if path:
+ paths.setdefault(route.path_format, {}).update(path)
+ if security_schemes:
+ components.setdefault("securitySchemes", {}).update(
+ security_schemes
+ )
+ if path_definitions:
+ definitions.update(path_definitions)
+ for webhook in webhooks or []:
+ if isinstance(webhook, routing.APIRoute):
+ result = get_openapi_path(
+ route=webhook,
+ operation_ids=operation_ids,
+ schema_generator=schema_generator,
+ model_name_map=model_name_map,
+ field_mapping=field_mapping,
+ separate_input_output_schemas=separate_input_output_schemas,
+ )
+ if result:
+ path, security_schemes, path_definitions = result
+ if path:
+ webhook_paths.setdefault(webhook.path_format, {}).update(path)
+ if security_schemes:
+ components.setdefault("securitySchemes", {}).update(
+ security_schemes
+ )
+ if path_definitions:
+ definitions.update(path_definitions)
+ if definitions:
+ components["schemas"] = {k: definitions[k] for k in sorted(definitions)}
+ if components:
+ output["components"] = components
+ output["paths"] = paths
+ if webhook_paths:
+ output["webhooks"] = webhook_paths
+ if tags:
+ output["tags"] = tags
+ return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True) # type: ignore
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/__init__.py b/.venv/lib/python3.11/site-packages/fastapi/security/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3aa6bf21e44f3069adb94242fbba5c8160532a1c
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/security/__init__.py
@@ -0,0 +1,15 @@
+from .api_key import APIKeyCookie as APIKeyCookie
+from .api_key import APIKeyHeader as APIKeyHeader
+from .api_key import APIKeyQuery as APIKeyQuery
+from .http import HTTPAuthorizationCredentials as HTTPAuthorizationCredentials
+from .http import HTTPBasic as HTTPBasic
+from .http import HTTPBasicCredentials as HTTPBasicCredentials
+from .http import HTTPBearer as HTTPBearer
+from .http import HTTPDigest as HTTPDigest
+from .oauth2 import OAuth2 as OAuth2
+from .oauth2 import OAuth2AuthorizationCodeBearer as OAuth2AuthorizationCodeBearer
+from .oauth2 import OAuth2PasswordBearer as OAuth2PasswordBearer
+from .oauth2 import OAuth2PasswordRequestForm as OAuth2PasswordRequestForm
+from .oauth2 import OAuth2PasswordRequestFormStrict as OAuth2PasswordRequestFormStrict
+from .oauth2 import SecurityScopes as SecurityScopes
+from .open_id_connect_url import OpenIdConnect as OpenIdConnect
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3e6d429c49e762c6c0cb186396e4b24d7c316ba3
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/__init__.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/api_key.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/api_key.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..541cf5cf028322f85ae0af8e23c79a81d01556d8
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/api_key.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/base.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/base.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d774636e0e6d24977a8d5dabd08819df73b1328d
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/base.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/http.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/http.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c3c061ac978188adcd4c68897355f507b3433e83
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/http.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/oauth2.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/oauth2.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0ac5c1f8902ccf5af24caace9ad09609d3ae6f82
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/oauth2.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/open_id_connect_url.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/open_id_connect_url.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..413c6fc0eac7188f7b18a529ff9f3e8f256c88cb
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/open_id_connect_url.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/utils.cpython-311.pyc b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/utils.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2c96eb281a5599f70205e06fe9fc20914bf4c1dd
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/utils.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/api_key.py b/.venv/lib/python3.11/site-packages/fastapi/security/api_key.py
new file mode 100644
index 0000000000000000000000000000000000000000..70c2dca8a81fea5620185374ce22ba77c356027f
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/security/api_key.py
@@ -0,0 +1,288 @@
+from typing import Optional
+
+from fastapi.openapi.models import APIKey, APIKeyIn
+from fastapi.security.base import SecurityBase
+from starlette.exceptions import HTTPException
+from starlette.requests import Request
+from starlette.status import HTTP_403_FORBIDDEN
+from typing_extensions import Annotated, Doc
+
+
+class APIKeyBase(SecurityBase):
+ @staticmethod
+ def check_api_key(api_key: Optional[str], auto_error: bool) -> Optional[str]:
+ if not api_key:
+ if auto_error:
+ raise HTTPException(
+ status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
+ )
+ return None
+ return api_key
+
+
+class APIKeyQuery(APIKeyBase):
+ """
+ API key authentication using a query parameter.
+
+ This defines the name of the query parameter that should be provided in the request
+ with the API key and integrates that into the OpenAPI documentation. It extracts
+ the key value sent in the query parameter automatically and provides it as the
+ dependency result. But it doesn't define how to send that API key to the client.
+
+ ## Usage
+
+ Create an instance object and use that object as the dependency in `Depends()`.
+
+ The dependency result will be a string containing the key value.
+
+ ## Example
+
+ ```python
+ from fastapi import Depends, FastAPI
+ from fastapi.security import APIKeyQuery
+
+ app = FastAPI()
+
+ query_scheme = APIKeyQuery(name="api_key")
+
+
+ @app.get("/items/")
+ async def read_items(api_key: str = Depends(query_scheme)):
+ return {"api_key": api_key}
+ ```
+ """
+
+ def __init__(
+ self,
+ *,
+ name: Annotated[
+ str,
+ Doc("Query parameter name."),
+ ],
+ scheme_name: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme name.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ description: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme description.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ auto_error: Annotated[
+ bool,
+ Doc(
+ """
+ By default, if the query parameter is not provided, `APIKeyQuery` will
+ automatically cancel the request and send the client an error.
+
+ If `auto_error` is set to `False`, when the query parameter is not
+ available, instead of erroring out, the dependency result will be
+ `None`.
+
+ This is useful when you want to have optional authentication.
+
+ It is also useful when you want to have authentication that can be
+ provided in one of multiple optional ways (for example, in a query
+ parameter or in an HTTP Bearer token).
+ """
+ ),
+ ] = True,
+ ):
+ self.model: APIKey = APIKey(
+ **{"in": APIKeyIn.query}, # type: ignore[arg-type]
+ name=name,
+ description=description,
+ )
+ self.scheme_name = scheme_name or self.__class__.__name__
+ self.auto_error = auto_error
+
+ async def __call__(self, request: Request) -> Optional[str]:
+ api_key = request.query_params.get(self.model.name)
+ return self.check_api_key(api_key, self.auto_error)
+
+
+class APIKeyHeader(APIKeyBase):
+ """
+ API key authentication using a header.
+
+ This defines the name of the header that should be provided in the request with
+ the API key and integrates that into the OpenAPI documentation. It extracts
+ the key value sent in the header automatically and provides it as the dependency
+ result. But it doesn't define how to send that key to the client.
+
+ ## Usage
+
+ Create an instance object and use that object as the dependency in `Depends()`.
+
+ The dependency result will be a string containing the key value.
+
+ ## Example
+
+ ```python
+ from fastapi import Depends, FastAPI
+ from fastapi.security import APIKeyHeader
+
+ app = FastAPI()
+
+ header_scheme = APIKeyHeader(name="x-key")
+
+
+ @app.get("/items/")
+ async def read_items(key: str = Depends(header_scheme)):
+ return {"key": key}
+ ```
+ """
+
+ def __init__(
+ self,
+ *,
+ name: Annotated[str, Doc("Header name.")],
+ scheme_name: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme name.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ description: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme description.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ auto_error: Annotated[
+ bool,
+ Doc(
+ """
+ By default, if the header is not provided, `APIKeyHeader` will
+ automatically cancel the request and send the client an error.
+
+ If `auto_error` is set to `False`, when the header is not available,
+ instead of erroring out, the dependency result will be `None`.
+
+ This is useful when you want to have optional authentication.
+
+ It is also useful when you want to have authentication that can be
+ provided in one of multiple optional ways (for example, in a header or
+ in an HTTP Bearer token).
+ """
+ ),
+ ] = True,
+ ):
+ self.model: APIKey = APIKey(
+ **{"in": APIKeyIn.header}, # type: ignore[arg-type]
+ name=name,
+ description=description,
+ )
+ self.scheme_name = scheme_name or self.__class__.__name__
+ self.auto_error = auto_error
+
+ async def __call__(self, request: Request) -> Optional[str]:
+ api_key = request.headers.get(self.model.name)
+ return self.check_api_key(api_key, self.auto_error)
+
+
+class APIKeyCookie(APIKeyBase):
+ """
+ API key authentication using a cookie.
+
+ This defines the name of the cookie that should be provided in the request with
+ the API key and integrates that into the OpenAPI documentation. It extracts
+ the key value sent in the cookie automatically and provides it as the dependency
+ result. But it doesn't define how to set that cookie.
+
+ ## Usage
+
+ Create an instance object and use that object as the dependency in `Depends()`.
+
+ The dependency result will be a string containing the key value.
+
+ ## Example
+
+ ```python
+ from fastapi import Depends, FastAPI
+ from fastapi.security import APIKeyCookie
+
+ app = FastAPI()
+
+ cookie_scheme = APIKeyCookie(name="session")
+
+
+ @app.get("/items/")
+ async def read_items(session: str = Depends(cookie_scheme)):
+ return {"session": session}
+ ```
+ """
+
+ def __init__(
+ self,
+ *,
+ name: Annotated[str, Doc("Cookie name.")],
+ scheme_name: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme name.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ description: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme description.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ auto_error: Annotated[
+ bool,
+ Doc(
+ """
+ By default, if the cookie is not provided, `APIKeyCookie` will
+ automatically cancel the request and send the client an error.
+
+ If `auto_error` is set to `False`, when the cookie is not available,
+ instead of erroring out, the dependency result will be `None`.
+
+ This is useful when you want to have optional authentication.
+
+ It is also useful when you want to have authentication that can be
+ provided in one of multiple optional ways (for example, in a cookie or
+ in an HTTP Bearer token).
+ """
+ ),
+ ] = True,
+ ):
+ self.model: APIKey = APIKey(
+ **{"in": APIKeyIn.cookie}, # type: ignore[arg-type]
+ name=name,
+ description=description,
+ )
+ self.scheme_name = scheme_name or self.__class__.__name__
+ self.auto_error = auto_error
+
+ async def __call__(self, request: Request) -> Optional[str]:
+ api_key = request.cookies.get(self.model.name)
+ return self.check_api_key(api_key, self.auto_error)
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/base.py b/.venv/lib/python3.11/site-packages/fastapi/security/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..c43555deb8ea83b14241a5631c9ea451c96f6e7f
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/security/base.py
@@ -0,0 +1,6 @@
+from fastapi.openapi.models import SecurityBase as SecurityBaseModel
+
+
+class SecurityBase:
+ model: SecurityBaseModel
+ scheme_name: str
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/http.py b/.venv/lib/python3.11/site-packages/fastapi/security/http.py
new file mode 100644
index 0000000000000000000000000000000000000000..e06f3d66d884c9040686da057565e3aa866e2d8d
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/security/http.py
@@ -0,0 +1,420 @@
+import binascii
+from base64 import b64decode
+from typing import Optional
+
+from fastapi.exceptions import HTTPException
+from fastapi.openapi.models import HTTPBase as HTTPBaseModel
+from fastapi.openapi.models import HTTPBearer as HTTPBearerModel
+from fastapi.security.base import SecurityBase
+from fastapi.security.utils import get_authorization_scheme_param
+from pydantic import BaseModel
+from starlette.requests import Request
+from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
+from typing_extensions import Annotated, Doc
+
+
+class HTTPBasicCredentials(BaseModel):
+ """
+ The HTTP Basic credentials given as the result of using `HTTPBasic` in a
+ dependency.
+
+ Read more about it in the
+ [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/).
+ """
+
+ username: Annotated[str, Doc("The HTTP Basic username.")]
+ password: Annotated[str, Doc("The HTTP Basic password.")]
+
+
+class HTTPAuthorizationCredentials(BaseModel):
+ """
+ The HTTP authorization credentials in the result of using `HTTPBearer` or
+ `HTTPDigest` in a dependency.
+
+ The HTTP authorization header value is split by the first space.
+
+ The first part is the `scheme`, the second part is the `credentials`.
+
+ For example, in an HTTP Bearer token scheme, the client will send a header
+ like:
+
+ ```
+ Authorization: Bearer deadbeef12346
+ ```
+
+ In this case:
+
+ * `scheme` will have the value `"Bearer"`
+ * `credentials` will have the value `"deadbeef12346"`
+ """
+
+ scheme: Annotated[
+ str,
+ Doc(
+ """
+ The HTTP authorization scheme extracted from the header value.
+ """
+ ),
+ ]
+ credentials: Annotated[
+ str,
+ Doc(
+ """
+ The HTTP authorization credentials extracted from the header value.
+ """
+ ),
+ ]
+
+
+class HTTPBase(SecurityBase):
+ def __init__(
+ self,
+ *,
+ scheme: str,
+ scheme_name: Optional[str] = None,
+ description: Optional[str] = None,
+ auto_error: bool = True,
+ ):
+ self.model = HTTPBaseModel(scheme=scheme, description=description)
+ self.scheme_name = scheme_name or self.__class__.__name__
+ self.auto_error = auto_error
+
+ async def __call__(
+ self, request: Request
+ ) -> Optional[HTTPAuthorizationCredentials]:
+ authorization = request.headers.get("Authorization")
+ scheme, credentials = get_authorization_scheme_param(authorization)
+ if not (authorization and scheme and credentials):
+ if self.auto_error:
+ raise HTTPException(
+ status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
+ )
+ else:
+ return None
+ return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials)
+
+
+class HTTPBasic(HTTPBase):
+ """
+ HTTP Basic authentication.
+
+ ## Usage
+
+ Create an instance object and use that object as the dependency in `Depends()`.
+
+ The dependency result will be an `HTTPBasicCredentials` object containing the
+ `username` and the `password`.
+
+ Read more about it in the
+ [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/).
+
+ ## Example
+
+ ```python
+ from typing import Annotated
+
+ from fastapi import Depends, FastAPI
+ from fastapi.security import HTTPBasic, HTTPBasicCredentials
+
+ app = FastAPI()
+
+ security = HTTPBasic()
+
+
+ @app.get("/users/me")
+ def read_current_user(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
+ return {"username": credentials.username, "password": credentials.password}
+ ```
+ """
+
+ def __init__(
+ self,
+ *,
+ scheme_name: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme name.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ realm: Annotated[
+ Optional[str],
+ Doc(
+ """
+ HTTP Basic authentication realm.
+ """
+ ),
+ ] = None,
+ description: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme description.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ auto_error: Annotated[
+ bool,
+ Doc(
+ """
+ By default, if the HTTP Basic authentication is not provided (a
+ header), `HTTPBasic` will automatically cancel the request and send the
+ client an error.
+
+ If `auto_error` is set to `False`, when the HTTP Basic authentication
+ is not available, instead of erroring out, the dependency result will
+ be `None`.
+
+ This is useful when you want to have optional authentication.
+
+ It is also useful when you want to have authentication that can be
+ provided in one of multiple optional ways (for example, in HTTP Basic
+ authentication or in an HTTP Bearer token).
+ """
+ ),
+ ] = True,
+ ):
+ self.model = HTTPBaseModel(scheme="basic", description=description)
+ self.scheme_name = scheme_name or self.__class__.__name__
+ self.realm = realm
+ self.auto_error = auto_error
+
+ async def __call__( # type: ignore
+ self, request: Request
+ ) -> Optional[HTTPBasicCredentials]:
+ authorization = request.headers.get("Authorization")
+ scheme, param = get_authorization_scheme_param(authorization)
+ if self.realm:
+ unauthorized_headers = {"WWW-Authenticate": f'Basic realm="{self.realm}"'}
+ else:
+ unauthorized_headers = {"WWW-Authenticate": "Basic"}
+ if not authorization or scheme.lower() != "basic":
+ if self.auto_error:
+ raise HTTPException(
+ status_code=HTTP_401_UNAUTHORIZED,
+ detail="Not authenticated",
+ headers=unauthorized_headers,
+ )
+ else:
+ return None
+ invalid_user_credentials_exc = HTTPException(
+ status_code=HTTP_401_UNAUTHORIZED,
+ detail="Invalid authentication credentials",
+ headers=unauthorized_headers,
+ )
+ try:
+ data = b64decode(param).decode("ascii")
+ except (ValueError, UnicodeDecodeError, binascii.Error):
+ raise invalid_user_credentials_exc # noqa: B904
+ username, separator, password = data.partition(":")
+ if not separator:
+ raise invalid_user_credentials_exc
+ return HTTPBasicCredentials(username=username, password=password)
+
+
+class HTTPBearer(HTTPBase):
+ """
+ HTTP Bearer token authentication.
+
+ ## Usage
+
+ Create an instance object and use that object as the dependency in `Depends()`.
+
+ The dependency result will be an `HTTPAuthorizationCredentials` object containing
+ the `scheme` and the `credentials`.
+
+ ## Example
+
+ ```python
+ from typing import Annotated
+
+ from fastapi import Depends, FastAPI
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
+
+ app = FastAPI()
+
+ security = HTTPBearer()
+
+
+ @app.get("/users/me")
+ def read_current_user(
+ credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
+ ):
+ return {"scheme": credentials.scheme, "credentials": credentials.credentials}
+ ```
+ """
+
+ def __init__(
+ self,
+ *,
+ bearerFormat: Annotated[Optional[str], Doc("Bearer token format.")] = None,
+ scheme_name: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme name.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ description: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme description.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ auto_error: Annotated[
+ bool,
+ Doc(
+ """
+ By default, if the HTTP Bearer token is not provided (in an
+ `Authorization` header), `HTTPBearer` will automatically cancel the
+ request and send the client an error.
+
+ If `auto_error` is set to `False`, when the HTTP Bearer token
+ is not available, instead of erroring out, the dependency result will
+ be `None`.
+
+ This is useful when you want to have optional authentication.
+
+ It is also useful when you want to have authentication that can be
+ provided in one of multiple optional ways (for example, in an HTTP
+ Bearer token or in a cookie).
+ """
+ ),
+ ] = True,
+ ):
+ self.model = HTTPBearerModel(bearerFormat=bearerFormat, description=description)
+ self.scheme_name = scheme_name or self.__class__.__name__
+ self.auto_error = auto_error
+
+ async def __call__(
+ self, request: Request
+ ) -> Optional[HTTPAuthorizationCredentials]:
+ authorization = request.headers.get("Authorization")
+ scheme, credentials = get_authorization_scheme_param(authorization)
+ if not (authorization and scheme and credentials):
+ if self.auto_error:
+ raise HTTPException(
+ status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
+ )
+ else:
+ return None
+ if scheme.lower() != "bearer":
+ if self.auto_error:
+ raise HTTPException(
+ status_code=HTTP_403_FORBIDDEN,
+ detail="Invalid authentication credentials",
+ )
+ else:
+ return None
+ return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials)
+
+
+class HTTPDigest(HTTPBase):
+ """
+ HTTP Digest authentication.
+
+ ## Usage
+
+ Create an instance object and use that object as the dependency in `Depends()`.
+
+ The dependency result will be an `HTTPAuthorizationCredentials` object containing
+ the `scheme` and the `credentials`.
+
+ ## Example
+
+ ```python
+ from typing import Annotated
+
+ from fastapi import Depends, FastAPI
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPDigest
+
+ app = FastAPI()
+
+ security = HTTPDigest()
+
+
+ @app.get("/users/me")
+ def read_current_user(
+ credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
+ ):
+ return {"scheme": credentials.scheme, "credentials": credentials.credentials}
+ ```
+ """
+
+ def __init__(
+ self,
+ *,
+ scheme_name: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme name.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ description: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme description.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ auto_error: Annotated[
+ bool,
+ Doc(
+ """
+ By default, if the HTTP Digest is not provided, `HTTPDigest` will
+ automatically cancel the request and send the client an error.
+
+ If `auto_error` is set to `False`, when the HTTP Digest is not
+ available, instead of erroring out, the dependency result will
+ be `None`.
+
+ This is useful when you want to have optional authentication.
+
+ It is also useful when you want to have authentication that can be
+ provided in one of multiple optional ways (for example, in HTTP
+ Digest or in a cookie).
+ """
+ ),
+ ] = True,
+ ):
+ self.model = HTTPBaseModel(scheme="digest", description=description)
+ self.scheme_name = scheme_name or self.__class__.__name__
+ self.auto_error = auto_error
+
+ async def __call__(
+ self, request: Request
+ ) -> Optional[HTTPAuthorizationCredentials]:
+ authorization = request.headers.get("Authorization")
+ scheme, credentials = get_authorization_scheme_param(authorization)
+ if not (authorization and scheme and credentials):
+ if self.auto_error:
+ raise HTTPException(
+ status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
+ )
+ else:
+ return None
+ if scheme.lower() != "digest":
+ raise HTTPException(
+ status_code=HTTP_403_FORBIDDEN,
+ detail="Invalid authentication credentials",
+ )
+ return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials)
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/oauth2.py b/.venv/lib/python3.11/site-packages/fastapi/security/oauth2.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ffad59862ba942a26dc27ce77c66578659918d3
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/security/oauth2.py
@@ -0,0 +1,638 @@
+from typing import Any, Dict, List, Optional, Union, cast
+
+from fastapi.exceptions import HTTPException
+from fastapi.openapi.models import OAuth2 as OAuth2Model
+from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
+from fastapi.param_functions import Form
+from fastapi.security.base import SecurityBase
+from fastapi.security.utils import get_authorization_scheme_param
+from starlette.requests import Request
+from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
+
+# TODO: import from typing when deprecating Python 3.9
+from typing_extensions import Annotated, Doc
+
+
+class OAuth2PasswordRequestForm:
+ """
+ This is a dependency class to collect the `username` and `password` as form data
+ for an OAuth2 password flow.
+
+ The OAuth2 specification dictates that for a password flow the data should be
+ collected using form data (instead of JSON) and that it should have the specific
+ fields `username` and `password`.
+
+ All the initialization parameters are extracted from the request.
+
+ Read more about it in the
+ [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/).
+
+ ## Example
+
+ ```python
+ from typing import Annotated
+
+ from fastapi import Depends, FastAPI
+ from fastapi.security import OAuth2PasswordRequestForm
+
+ app = FastAPI()
+
+
+ @app.post("/login")
+ def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
+ data = {}
+ data["scopes"] = []
+ for scope in form_data.scopes:
+ data["scopes"].append(scope)
+ if form_data.client_id:
+ data["client_id"] = form_data.client_id
+ if form_data.client_secret:
+ data["client_secret"] = form_data.client_secret
+ return data
+ ```
+
+ Note that for OAuth2 the scope `items:read` is a single scope in an opaque string.
+ You could have custom internal logic to separate it by colon characters (`:`) or
+ similar, and get the two parts `items` and `read`. Many applications do that to
+ group and organize permissions, you could do it as well in your application, just
+ know that that it is application specific, it's not part of the specification.
+ """
+
+ def __init__(
+ self,
+ *,
+ grant_type: Annotated[
+ Union[str, None],
+ Form(pattern="^password$"),
+ Doc(
+ """
+ The OAuth2 spec says it is required and MUST be the fixed string
+ "password". Nevertheless, this dependency class is permissive and
+ allows not passing it. If you want to enforce it, use instead the
+ `OAuth2PasswordRequestFormStrict` dependency.
+ """
+ ),
+ ] = None,
+ username: Annotated[
+ str,
+ Form(),
+ Doc(
+ """
+ `username` string. The OAuth2 spec requires the exact field name
+ `username`.
+ """
+ ),
+ ],
+ password: Annotated[
+ str,
+ Form(),
+ Doc(
+ """
+ `password` string. The OAuth2 spec requires the exact field name
+ `password".
+ """
+ ),
+ ],
+ scope: Annotated[
+ str,
+ Form(),
+ Doc(
+ """
+ A single string with actually several scopes separated by spaces. Each
+ scope is also a string.
+
+ For example, a single string with:
+
+ ```python
+ "items:read items:write users:read profile openid"
+ ````
+
+ would represent the scopes:
+
+ * `items:read`
+ * `items:write`
+ * `users:read`
+ * `profile`
+ * `openid`
+ """
+ ),
+ ] = "",
+ client_id: Annotated[
+ Union[str, None],
+ Form(),
+ Doc(
+ """
+ If there's a `client_id`, it can be sent as part of the form fields.
+ But the OAuth2 specification recommends sending the `client_id` and
+ `client_secret` (if any) using HTTP Basic auth.
+ """
+ ),
+ ] = None,
+ client_secret: Annotated[
+ Union[str, None],
+ Form(),
+ Doc(
+ """
+ If there's a `client_password` (and a `client_id`), they can be sent
+ as part of the form fields. But the OAuth2 specification recommends
+ sending the `client_id` and `client_secret` (if any) using HTTP Basic
+ auth.
+ """
+ ),
+ ] = None,
+ ):
+ self.grant_type = grant_type
+ self.username = username
+ self.password = password
+ self.scopes = scope.split()
+ self.client_id = client_id
+ self.client_secret = client_secret
+
+
+class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
+ """
+ This is a dependency class to collect the `username` and `password` as form data
+ for an OAuth2 password flow.
+
+ The OAuth2 specification dictates that for a password flow the data should be
+ collected using form data (instead of JSON) and that it should have the specific
+ fields `username` and `password`.
+
+ All the initialization parameters are extracted from the request.
+
+ The only difference between `OAuth2PasswordRequestFormStrict` and
+ `OAuth2PasswordRequestForm` is that `OAuth2PasswordRequestFormStrict` requires the
+ client to send the form field `grant_type` with the value `"password"`, which
+ is required in the OAuth2 specification (it seems that for no particular reason),
+ while for `OAuth2PasswordRequestForm` `grant_type` is optional.
+
+ Read more about it in the
+ [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/).
+
+ ## Example
+
+ ```python
+ from typing import Annotated
+
+ from fastapi import Depends, FastAPI
+ from fastapi.security import OAuth2PasswordRequestForm
+
+ app = FastAPI()
+
+
+ @app.post("/login")
+ def login(form_data: Annotated[OAuth2PasswordRequestFormStrict, Depends()]):
+ data = {}
+ data["scopes"] = []
+ for scope in form_data.scopes:
+ data["scopes"].append(scope)
+ if form_data.client_id:
+ data["client_id"] = form_data.client_id
+ if form_data.client_secret:
+ data["client_secret"] = form_data.client_secret
+ return data
+ ```
+
+ Note that for OAuth2 the scope `items:read` is a single scope in an opaque string.
+ You could have custom internal logic to separate it by colon characters (`:`) or
+ similar, and get the two parts `items` and `read`. Many applications do that to
+ group and organize permissions, you could do it as well in your application, just
+ know that that it is application specific, it's not part of the specification.
+
+
+ grant_type: the OAuth2 spec says it is required and MUST be the fixed string "password".
+ This dependency is strict about it. If you want to be permissive, use instead the
+ OAuth2PasswordRequestForm dependency class.
+ username: username string. The OAuth2 spec requires the exact field name "username".
+ password: password string. The OAuth2 spec requires the exact field name "password".
+ scope: Optional string. Several scopes (each one a string) separated by spaces. E.g.
+ "items:read items:write users:read profile openid"
+ client_id: optional string. OAuth2 recommends sending the client_id and client_secret (if any)
+ using HTTP Basic auth, as: client_id:client_secret
+ client_secret: optional string. OAuth2 recommends sending the client_id and client_secret (if any)
+ using HTTP Basic auth, as: client_id:client_secret
+ """
+
+ def __init__(
+ self,
+ grant_type: Annotated[
+ str,
+ Form(pattern="^password$"),
+ Doc(
+ """
+ The OAuth2 spec says it is required and MUST be the fixed string
+ "password". This dependency is strict about it. If you want to be
+ permissive, use instead the `OAuth2PasswordRequestForm` dependency
+ class.
+ """
+ ),
+ ],
+ username: Annotated[
+ str,
+ Form(),
+ Doc(
+ """
+ `username` string. The OAuth2 spec requires the exact field name
+ `username`.
+ """
+ ),
+ ],
+ password: Annotated[
+ str,
+ Form(),
+ Doc(
+ """
+ `password` string. The OAuth2 spec requires the exact field name
+ `password".
+ """
+ ),
+ ],
+ scope: Annotated[
+ str,
+ Form(),
+ Doc(
+ """
+ A single string with actually several scopes separated by spaces. Each
+ scope is also a string.
+
+ For example, a single string with:
+
+ ```python
+ "items:read items:write users:read profile openid"
+ ````
+
+ would represent the scopes:
+
+ * `items:read`
+ * `items:write`
+ * `users:read`
+ * `profile`
+ * `openid`
+ """
+ ),
+ ] = "",
+ client_id: Annotated[
+ Union[str, None],
+ Form(),
+ Doc(
+ """
+ If there's a `client_id`, it can be sent as part of the form fields.
+ But the OAuth2 specification recommends sending the `client_id` and
+ `client_secret` (if any) using HTTP Basic auth.
+ """
+ ),
+ ] = None,
+ client_secret: Annotated[
+ Union[str, None],
+ Form(),
+ Doc(
+ """
+ If there's a `client_password` (and a `client_id`), they can be sent
+ as part of the form fields. But the OAuth2 specification recommends
+ sending the `client_id` and `client_secret` (if any) using HTTP Basic
+ auth.
+ """
+ ),
+ ] = None,
+ ):
+ super().__init__(
+ grant_type=grant_type,
+ username=username,
+ password=password,
+ scope=scope,
+ client_id=client_id,
+ client_secret=client_secret,
+ )
+
+
+class OAuth2(SecurityBase):
+ """
+ This is the base class for OAuth2 authentication, an instance of it would be used
+ as a dependency. All other OAuth2 classes inherit from it and customize it for
+ each OAuth2 flow.
+
+ You normally would not create a new class inheriting from it but use one of the
+ existing subclasses, and maybe compose them if you want to support multiple flows.
+
+ Read more about it in the
+ [FastAPI docs for Security](https://fastapi.tiangolo.com/tutorial/security/).
+ """
+
+ def __init__(
+ self,
+ *,
+ flows: Annotated[
+ Union[OAuthFlowsModel, Dict[str, Dict[str, Any]]],
+ Doc(
+ """
+ The dictionary of OAuth2 flows.
+ """
+ ),
+ ] = OAuthFlowsModel(),
+ scheme_name: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme name.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ description: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme description.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ auto_error: Annotated[
+ bool,
+ Doc(
+ """
+ By default, if no HTTP Authorization header is provided, required for
+ OAuth2 authentication, it will automatically cancel the request and
+ send the client an error.
+
+ If `auto_error` is set to `False`, when the HTTP Authorization header
+ is not available, instead of erroring out, the dependency result will
+ be `None`.
+
+ This is useful when you want to have optional authentication.
+
+ It is also useful when you want to have authentication that can be
+ provided in one of multiple optional ways (for example, with OAuth2
+ or in a cookie).
+ """
+ ),
+ ] = True,
+ ):
+ self.model = OAuth2Model(
+ flows=cast(OAuthFlowsModel, flows), description=description
+ )
+ self.scheme_name = scheme_name or self.__class__.__name__
+ self.auto_error = auto_error
+
+ async def __call__(self, request: Request) -> Optional[str]:
+ authorization = request.headers.get("Authorization")
+ if not authorization:
+ if self.auto_error:
+ raise HTTPException(
+ status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
+ )
+ else:
+ return None
+ return authorization
+
+
+class OAuth2PasswordBearer(OAuth2):
+ """
+ OAuth2 flow for authentication using a bearer token obtained with a password.
+ An instance of it would be used as a dependency.
+
+ Read more about it in the
+ [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/).
+ """
+
+ def __init__(
+ self,
+ tokenUrl: Annotated[
+ str,
+ Doc(
+ """
+ The URL to obtain the OAuth2 token. This would be the *path operation*
+ that has `OAuth2PasswordRequestForm` as a dependency.
+ """
+ ),
+ ],
+ scheme_name: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme name.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ scopes: Annotated[
+ Optional[Dict[str, str]],
+ Doc(
+ """
+ The OAuth2 scopes that would be required by the *path operations* that
+ use this dependency.
+ """
+ ),
+ ] = None,
+ description: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme description.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ auto_error: Annotated[
+ bool,
+ Doc(
+ """
+ By default, if no HTTP Authorization header is provided, required for
+ OAuth2 authentication, it will automatically cancel the request and
+ send the client an error.
+
+ If `auto_error` is set to `False`, when the HTTP Authorization header
+ is not available, instead of erroring out, the dependency result will
+ be `None`.
+
+ This is useful when you want to have optional authentication.
+
+ It is also useful when you want to have authentication that can be
+ provided in one of multiple optional ways (for example, with OAuth2
+ or in a cookie).
+ """
+ ),
+ ] = True,
+ ):
+ if not scopes:
+ scopes = {}
+ flows = OAuthFlowsModel(
+ password=cast(Any, {"tokenUrl": tokenUrl, "scopes": scopes})
+ )
+ super().__init__(
+ flows=flows,
+ scheme_name=scheme_name,
+ description=description,
+ auto_error=auto_error,
+ )
+
+ async def __call__(self, request: Request) -> Optional[str]:
+ authorization = request.headers.get("Authorization")
+ scheme, param = get_authorization_scheme_param(authorization)
+ if not authorization or scheme.lower() != "bearer":
+ if self.auto_error:
+ raise HTTPException(
+ status_code=HTTP_401_UNAUTHORIZED,
+ detail="Not authenticated",
+ headers={"WWW-Authenticate": "Bearer"},
+ )
+ else:
+ return None
+ return param
+
+
+class OAuth2AuthorizationCodeBearer(OAuth2):
+ """
+ OAuth2 flow for authentication using a bearer token obtained with an OAuth2 code
+ flow. An instance of it would be used as a dependency.
+ """
+
+ def __init__(
+ self,
+ authorizationUrl: str,
+ tokenUrl: Annotated[
+ str,
+ Doc(
+ """
+ The URL to obtain the OAuth2 token.
+ """
+ ),
+ ],
+ refreshUrl: Annotated[
+ Optional[str],
+ Doc(
+ """
+ The URL to refresh the token and obtain a new one.
+ """
+ ),
+ ] = None,
+ scheme_name: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme name.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ scopes: Annotated[
+ Optional[Dict[str, str]],
+ Doc(
+ """
+ The OAuth2 scopes that would be required by the *path operations* that
+ use this dependency.
+ """
+ ),
+ ] = None,
+ description: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme description.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ auto_error: Annotated[
+ bool,
+ Doc(
+ """
+ By default, if no HTTP Authorization header is provided, required for
+ OAuth2 authentication, it will automatically cancel the request and
+ send the client an error.
+
+ If `auto_error` is set to `False`, when the HTTP Authorization header
+ is not available, instead of erroring out, the dependency result will
+ be `None`.
+
+ This is useful when you want to have optional authentication.
+
+ It is also useful when you want to have authentication that can be
+ provided in one of multiple optional ways (for example, with OAuth2
+ or in a cookie).
+ """
+ ),
+ ] = True,
+ ):
+ if not scopes:
+ scopes = {}
+ flows = OAuthFlowsModel(
+ authorizationCode=cast(
+ Any,
+ {
+ "authorizationUrl": authorizationUrl,
+ "tokenUrl": tokenUrl,
+ "refreshUrl": refreshUrl,
+ "scopes": scopes,
+ },
+ )
+ )
+ super().__init__(
+ flows=flows,
+ scheme_name=scheme_name,
+ description=description,
+ auto_error=auto_error,
+ )
+
+ async def __call__(self, request: Request) -> Optional[str]:
+ authorization = request.headers.get("Authorization")
+ scheme, param = get_authorization_scheme_param(authorization)
+ if not authorization or scheme.lower() != "bearer":
+ if self.auto_error:
+ raise HTTPException(
+ status_code=HTTP_401_UNAUTHORIZED,
+ detail="Not authenticated",
+ headers={"WWW-Authenticate": "Bearer"},
+ )
+ else:
+ return None # pragma: nocover
+ return param
+
+
+class SecurityScopes:
+ """
+ This is a special class that you can define in a parameter in a dependency to
+ obtain the OAuth2 scopes required by all the dependencies in the same chain.
+
+ This way, multiple dependencies can have different scopes, even when used in the
+ same *path operation*. And with this, you can access all the scopes required in
+ all those dependencies in a single place.
+
+ Read more about it in the
+ [FastAPI docs for OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/).
+ """
+
+ def __init__(
+ self,
+ scopes: Annotated[
+ Optional[List[str]],
+ Doc(
+ """
+ This will be filled by FastAPI.
+ """
+ ),
+ ] = None,
+ ):
+ self.scopes: Annotated[
+ List[str],
+ Doc(
+ """
+ The list of all the scopes required by dependencies.
+ """
+ ),
+ ] = scopes or []
+ self.scope_str: Annotated[
+ str,
+ Doc(
+ """
+ All the scopes required by all the dependencies in a single string
+ separated by spaces, as defined in the OAuth2 specification.
+ """
+ ),
+ ] = " ".join(self.scopes)
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/open_id_connect_url.py b/.venv/lib/python3.11/site-packages/fastapi/security/open_id_connect_url.py
new file mode 100644
index 0000000000000000000000000000000000000000..c8cceb911cf6690c2f5b1630718ef39c8f602ea0
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/security/open_id_connect_url.py
@@ -0,0 +1,84 @@
+from typing import Optional
+
+from fastapi.openapi.models import OpenIdConnect as OpenIdConnectModel
+from fastapi.security.base import SecurityBase
+from starlette.exceptions import HTTPException
+from starlette.requests import Request
+from starlette.status import HTTP_403_FORBIDDEN
+from typing_extensions import Annotated, Doc
+
+
+class OpenIdConnect(SecurityBase):
+ """
+ OpenID Connect authentication class. An instance of it would be used as a
+ dependency.
+ """
+
+ def __init__(
+ self,
+ *,
+ openIdConnectUrl: Annotated[
+ str,
+ Doc(
+ """
+ The OpenID Connect URL.
+ """
+ ),
+ ],
+ scheme_name: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme name.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ description: Annotated[
+ Optional[str],
+ Doc(
+ """
+ Security scheme description.
+
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
+ """
+ ),
+ ] = None,
+ auto_error: Annotated[
+ bool,
+ Doc(
+ """
+ By default, if no HTTP Authorization header is provided, required for
+ OpenID Connect authentication, it will automatically cancel the request
+ and send the client an error.
+
+ If `auto_error` is set to `False`, when the HTTP Authorization header
+ is not available, instead of erroring out, the dependency result will
+ be `None`.
+
+ This is useful when you want to have optional authentication.
+
+ It is also useful when you want to have authentication that can be
+ provided in one of multiple optional ways (for example, with OpenID
+ Connect or in a cookie).
+ """
+ ),
+ ] = True,
+ ):
+ self.model = OpenIdConnectModel(
+ openIdConnectUrl=openIdConnectUrl, description=description
+ )
+ self.scheme_name = scheme_name or self.__class__.__name__
+ self.auto_error = auto_error
+
+ async def __call__(self, request: Request) -> Optional[str]:
+ authorization = request.headers.get("Authorization")
+ if not authorization:
+ if self.auto_error:
+ raise HTTPException(
+ status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
+ )
+ else:
+ return None
+ return authorization
diff --git a/.venv/lib/python3.11/site-packages/fastapi/security/utils.py b/.venv/lib/python3.11/site-packages/fastapi/security/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..fa7a450b74e813e66fd6e9a140d48c29215503bb
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/fastapi/security/utils.py
@@ -0,0 +1,10 @@
+from typing import Optional, Tuple
+
+
+def get_authorization_scheme_param(
+ authorization_header_value: Optional[str],
+) -> Tuple[str, str]:
+ if not authorization_header_value:
+ return "", ""
+ scheme, _, param = authorization_header_value.partition(" ")
+ return scheme, param
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/__init__.py b/.venv/lib/python3.11/site-packages/partial_json_parser/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..72bfd3a9c9d13ea9ed8b9b0be4f265feaa036461
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/partial_json_parser/__init__.py
@@ -0,0 +1,7 @@
+from .core.api import JSON, ensure_json, parse_json
+from .core.complete import fix
+from .core.exceptions import *
+from .core.myelin import fix_fast
+from .core.options import *
+
+loads = decode = parse_json
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/__pycache__/version.cpython-311.pyc b/.venv/lib/python3.11/site-packages/partial_json_parser/__pycache__/version.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bd11eb35618ccccb1e8d30c77c1202085d5b3fc3
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/partial_json_parser/__pycache__/version.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/api.cpython-311.pyc b/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/api.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..785c2dd56dd04b3d2f50b2e87aeca5fc77a50233
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/api.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/complete.cpython-311.pyc b/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/complete.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b863feb40499d109759336b5a5dbf27e24a99cda
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/complete.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/exceptions.cpython-311.pyc b/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/exceptions.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9914322bd3145f59893654a872c2f22451d9857c
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/exceptions.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/myelin.cpython-311.pyc b/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/myelin.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..54f23f3332ae35cd0854dad39b8ff04085c5493a
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/myelin.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/options.cpython-311.pyc b/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/options.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3ca283e9deb1777029086bc8152389f6eff1bcd5
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/options.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/core/api.py b/.venv/lib/python3.11/site-packages/partial_json_parser/core/api.py
new file mode 100644
index 0000000000000000000000000000000000000000..4587aff86b6aacb2d1c5ddd869755803795c3230
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/partial_json_parser/core/api.py
@@ -0,0 +1,26 @@
+from typing import Callable, Dict, List, Optional, Union
+
+from .complete import fix
+from .myelin import fix_fast
+from .options import *
+
+Number = Union[int, float]
+JSON = Union[str, bool, Number, List["JSON"], Dict[str, "JSON"], None]
+
+
+def parse_json(json_string: str, allow_partial: Union[Allow, int] = ALL, parser: Optional[Callable[[str], JSON]] = None, use_fast_fix=True) -> JSON:
+ if parser is None:
+ from json import loads as parser
+
+ return parser(ensure_json(json_string, allow_partial, use_fast_fix))
+
+
+def ensure_json(json_string: str, allow_partial: Union[Allow, int] = ALL, use_fast_fix=True) -> str:
+ """get the completed JSON string"""
+
+ if use_fast_fix:
+ head, tail = fix_fast(json_string, allow_partial)
+ else:
+ head, tail = fix(json_string, allow_partial)
+
+ return head + tail
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/core/complete.py b/.venv/lib/python3.11/site-packages/partial_json_parser/core/complete.py
new file mode 100644
index 0000000000000000000000000000000000000000..98dba5bcb864d94eb8be5cb45fbe15506158e5b9
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/partial_json_parser/core/complete.py
@@ -0,0 +1,237 @@
+from typing import TYPE_CHECKING, Tuple, Union
+
+from .exceptions import MalformedJSON, PartialJSON
+from .options import *
+
+if TYPE_CHECKING:
+ from typing import Literal
+
+CompleteResult = Union[Tuple[int, Union[str, "Literal[True]"]], "Literal[False]"] # (length, complete_string / already completed) / partial
+
+
+def fix(json_string: str, allow_partial: Union[Allow, int] = ALL):
+ """get the original slice and the trailing suffix separately"""
+
+ return _fix(json_string, Allow(allow_partial), True)
+
+
+def _fix(json_string: str, allow: Allow, is_top_level=False):
+ try:
+ result = complete_any(json_string.strip(), allow, is_top_level)
+ if result is False:
+ raise PartialJSON
+
+ index, completion = result
+ return json_string[:index], ("" if completion is True else completion)
+
+ except (AssertionError, IndexError) as err:
+ raise MalformedJSON(*err.args) from err
+
+
+def skip_blank(text: str, index: int):
+ try:
+ while text[index].isspace():
+ index += 1
+ finally:
+ return index
+
+
+def complete_any(json_string: str, allow: Allow, is_top_level=False) -> CompleteResult:
+ i = skip_blank(json_string, 0)
+ char = json_string[i]
+
+ if char == '"':
+ return complete_str(json_string, allow)
+
+ if char in "1234567890":
+ return complete_num(json_string, allow, is_top_level)
+
+ if char == "[":
+ return complete_arr(json_string, allow)
+
+ if char == "{":
+ return complete_obj(json_string, allow)
+
+ if json_string.startswith("null"):
+ return (4, True)
+ if "null".startswith(json_string):
+ return (0, "null") if NULL in allow else False
+
+ if json_string.startswith("true"):
+ return (4, True)
+ if "true".startswith(json_string):
+ return (0, "true") if BOOL in allow else False
+
+ if json_string.startswith("false"):
+ return (5, True)
+ if "false".startswith(json_string):
+ return (0, "false") if BOOL in allow else False
+
+ if json_string.startswith("Infinity"):
+ return (8, True)
+ if "Infinity".startswith(json_string):
+ return (0, "Infinity") if INFINITY in allow else False
+
+ if char == "-":
+ if len(json_string) == 1:
+ return False
+ elif json_string[1] != "I":
+ return complete_num(json_string, allow, is_top_level)
+
+ if json_string.startswith("-Infinity"):
+ return (9, True)
+ if "-Infinity".startswith(json_string):
+ return (0, "-Infinity") if _INFINITY in allow else False
+
+ if json_string.startswith("NaN"):
+ return (3, True)
+ if "NaN".startswith(json_string):
+ return (0, "NaN") if NAN in allow else False
+
+ raise MalformedJSON(f"Unexpected character {char}")
+
+
+def complete_str(json_string: str, allow: Allow) -> CompleteResult:
+ assert json_string[0] == '"'
+
+ length = len(json_string)
+
+ i = 1
+
+ try:
+ while True:
+ char = json_string[i]
+
+ if char == "\\":
+ if i + 1 == length:
+ raise IndexError
+ i += 2
+ continue
+ if char == '"':
+ return i + 1, True
+
+ i += 1
+
+ except IndexError:
+ if STR not in allow:
+ return False
+
+ def not_escaped(index: int):
+ text_before = json_string[:index]
+ count = index - len(text_before.rstrip("\\"))
+ return count % 2 == 0
+
+ # \uXXXX
+ _u = json_string.rfind("\\u", max(0, i - 5), i)
+ if _u != -1 and not_escaped(_u):
+ return _u, '"'
+
+ # \UXXXXXXXX
+ _U = json_string.rfind("\\U", max(0, i - 9), i)
+ if _U != -1 and not_escaped(_U):
+ return _U, '"'
+
+ # \xXX
+ _x = json_string.rfind("\\x", max(0, i - 3), i)
+ if _x != -1 and not_escaped(_x):
+ return _x, '"'
+
+ return i, '"'
+
+
+def complete_arr(json_string: str, allow: Allow) -> CompleteResult:
+ assert json_string[0] == "["
+ i = j = 1
+
+ try:
+ while True:
+ j = skip_blank(json_string, j)
+
+ if json_string[j] == "]":
+ return j + 1, True
+
+ result = complete_any(json_string[j:], allow)
+
+ if result is False: # incomplete
+ return (i, "]") if ARR in allow else False
+ if result[1] is True: # complete
+ i = j = j + result[0]
+ else: # incomplete
+ return (j + result[0], result[1] + "]") if ARR in allow else False
+
+ j = skip_blank(json_string, j)
+
+ if json_string[j] == ",":
+ j += 1
+ elif json_string[j] == "]":
+ return j + 1, True
+ else:
+ raise MalformedJSON(f"Expected ',' or ']', got {json_string[j]}")
+ except IndexError:
+ return (i, "]") if ARR in allow else False
+
+
+def complete_obj(json_string: str, allow: Allow) -> CompleteResult:
+ assert json_string[0] == "{"
+ i = j = 1
+
+ try:
+ while True:
+ j = skip_blank(json_string, j)
+
+ if json_string[j] == "}":
+ return j + 1, True
+
+ result = complete_str(json_string[j:], allow)
+ if result and result[1] is True: # complete
+ j += result[0]
+ else: # incomplete
+ return (i, "}") if OBJ in allow else False
+
+ j = skip_blank(json_string, j)
+
+ if json_string[j] != ":":
+ raise MalformedJSON(f"Expected ':', got {json_string[j]}")
+ j += 1
+
+ j = skip_blank(json_string, j)
+
+ result = complete_any(json_string[j:], allow)
+ if result is False: # incomplete
+ return (i, "}") if OBJ in allow else False
+ if result[1] is True: # complete
+ i = j = j + result[0]
+ else: # incomplete
+ return (j + result[0], result[1] + "}") if OBJ in allow else False
+
+ j = skip_blank(json_string, j)
+
+ if json_string[j] == ",":
+ j += 1
+ elif json_string[j] == "}":
+ return j + 1, True
+ else:
+ raise MalformedJSON(f"Expected ',' or '}}', got {json_string[j]}")
+ except IndexError:
+ return (i, "}") if OBJ in allow else False
+
+
+def complete_num(json_string: str, allow: Allow, is_top_level=False) -> CompleteResult:
+ i = 1
+ length = len(json_string)
+
+ # forward
+ while i < length and json_string[i] in "1234567890.-+eE":
+ i += 1
+
+ modified = False
+
+ # backward
+ while json_string[i - 1] in ".-+eE":
+ modified = True
+ i -= 1
+
+ if modified or i == length and not is_top_level:
+ return (i, "") if NUM in allow else False
+ else:
+ return i, True
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/core/exceptions.py b/.venv/lib/python3.11/site-packages/partial_json_parser/core/exceptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..12f917e46abbad33b1a18e3408dd28866c20c5e6
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/partial_json_parser/core/exceptions.py
@@ -0,0 +1,10 @@
+class JSONDecodeError(ValueError):
+ pass
+
+
+class PartialJSON(JSONDecodeError):
+ pass
+
+
+class MalformedJSON(JSONDecodeError):
+ pass
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/core/myelin.py b/.venv/lib/python3.11/site-packages/partial_json_parser/core/myelin.py
new file mode 100644
index 0000000000000000000000000000000000000000..963867c93e07dd5a365dfd261c3ca94147665db1
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/partial_json_parser/core/myelin.py
@@ -0,0 +1,226 @@
+"""Myelin acts as the highway among neurons, epitomizing the leapfrog methodology within this algorithm."""
+
+from re import compile
+from typing import List, Tuple, Union
+
+from .complete import _fix
+from .exceptions import PartialJSON
+from .options import *
+
+finditer = compile(r'["\[\]{}]').finditer
+
+
+def scan(json_string: str):
+ return [(match.start(), match.group()) for match in finditer(json_string)]
+
+
+def join_closing_tokens(stack: List[Tuple[int, str]]):
+ return "".join("}" if char == "{" else "]" for _, char in reversed(stack))
+
+
+def fix_fast(json_string: str, allow_partial: Union[Allow, int] = ALL):
+ allow = Allow(allow_partial)
+
+ def is_escaped(index: int):
+ text_before = json_string[:index]
+ count = index - len(text_before.rstrip("\\"))
+ return count % 2
+
+ stack = []
+ in_string = False
+ last_string_start = -1
+ last_string_end = -1
+
+ tokens = scan(json_string)
+
+ if not tokens or tokens[0][1] == '"':
+ return _fix(json_string, allow, True)
+
+ for i, char in tokens:
+ if char == '"':
+ if not in_string:
+ in_string = True
+ last_string_start = i
+ elif not is_escaped(i):
+ in_string = False
+ last_string_end = i
+
+ elif not in_string:
+ if char == "}":
+ _i, _char = stack.pop()
+ assert _char == "{", f"Expected '{{' at index {_i}, got '{_char}'"
+ elif char == "]":
+ _i, _char = stack.pop()
+ assert _char == "[", f"Expected '[' at index {_i}, got '{_char}'"
+ else:
+ stack.append((i, char))
+
+ if not stack:
+ return json_string, ""
+
+ # check if the opening tokens are allowed
+
+ if (STR | COLLECTION) not in allow:
+
+ def truncate_before_last_key_start(container_start: int, last_string_end: int, stack):
+ last_key_start = last_string_end # backtrace the last key's start and retry finding the last comma
+ while True:
+ last_key_start = json_string.rfind('"', container_start, last_key_start)
+ if last_key_start == -1: # this is the only key
+ # { "key": "v
+ return json_string[: container_start + 1], join_closing_tokens(stack)
+ if is_escaped(last_key_start):
+ last_key_start -= 1
+ else:
+ last_comma = json_string.rfind(",", container_start, last_key_start)
+ if last_comma == -1:
+ # { "key": "
+ return json_string[: container_start + 1], join_closing_tokens(stack)
+ # # { ... "key": ... , "
+ return json_string[:last_comma], join_closing_tokens(stack)
+
+ if COLLECTION not in allow:
+ for index, [_i, _char] in enumerate(stack):
+ if _char == "{" and OBJ not in allow or _char == "[" and ARR not in allow:
+ if index == 0:
+ raise PartialJSON
+
+ # to truncate before the last container token and the last comma (if exists) of its parent container
+
+ # reset `last_string_end` to before `_i`
+ if _i < last_string_start:
+ if last_string_start < _i: # ... { "k
+ last_string_end = json_string.rfind('"', last_string_end, _i)
+ else: # ... { "" ...
+ last_string_end = json_string.rfind('"', None, _i)
+
+ last_comma = json_string.rfind(",", max(stack[index - 1][0], last_string_end) + 1, _i)
+
+ if last_comma == -1:
+ if stack[index - 1][1] == "[":
+ # [ ... [
+ return json_string[:_i], join_closing_tokens(stack[:index])
+
+ # { "key": [ 1, 2, "v
+ # { "key": [ 1, 2, "value"
+ if last_string_start > last_string_end:
+ return truncate_before_last_key_start(stack[index - 1][0], last_string_end, stack[:index])
+
+ last_comma = json_string.rfind(",", stack[index - 1][0] + 1, last_string_start)
+ if last_comma == -1:
+ return json_string[: stack[index - 1][0] + 1], join_closing_tokens(stack[:index])
+ return json_string[:last_comma], join_closing_tokens(stack[:index])
+
+ # { ..., "key": {
+ # ..., {
+ return json_string[:last_comma], join_closing_tokens(stack[:index])
+
+ if STR not in allow and in_string: # truncate before the last key
+ if stack[-1][0] > last_string_end and stack[-1][1] == "{":
+ # { "k
+ return json_string[: stack[-1][0] + 1], join_closing_tokens(stack)
+
+ last_comma = json_string.rfind(",", max(stack[-1][0], last_string_end) + 1, last_string_start - 1)
+ if last_comma != -1:
+ # { "key": "v", "k
+ # { "key": 123, "k
+ # [ 1, 2, 3, "k
+ return json_string[:last_comma], join_closing_tokens(stack)
+
+ # { ... "key": "v
+ return truncate_before_last_key_start(stack[-1][0], last_string_end, stack)
+
+ # only fix the rest of the container in O(1) time complexity
+
+ assert in_string == (last_string_end < last_string_start)
+
+ if in_string:
+ if stack[-1][1] == "[": # [ ... "val
+ head, tail = _fix(json_string[last_string_start:], allow) # fix the last string
+ return json_string[:last_string_start] + head, tail + join_closing_tokens(stack)
+
+ assert stack[-1][1] == "{" # { ... "val
+
+ start = max(last_string_end, stack[-1][0])
+
+ if "," in json_string[start + 1 : last_string_start]:
+ # { ... "k": "v", "key
+ # { ... "k": 123, "key
+ last_comma = json_string.rindex(",", start, last_string_start)
+ head, tail = _fix(stack[-1][1] + json_string[last_comma + 1 :], allow)
+ return json_string[:last_comma] + head[1:], tail + join_closing_tokens(stack[:-1])
+
+ if ":" in json_string[start + 1 : last_string_start]:
+ # { ... ": "val
+ head, tail = _fix(json_string[last_string_start:], allow) # fix the last string (same as array)
+ return json_string[:last_string_start] + head, tail + join_closing_tokens(stack)
+
+ # {"key
+ return json_string[:last_string_start], join_closing_tokens(stack)
+
+ last_comma = json_string.rfind(",", max(last_string_end, i) + 1)
+
+ if last_comma != -1:
+ i, char = stack[-1]
+
+ if not json_string[last_comma + 1 :].strip(): # comma at the end
+ # { ... "key": "value",
+ return json_string[:last_comma], join_closing_tokens(stack)
+
+ assert char == "[", json_string # array with many non-string literals
+
+ # [ ..., 1, 2, 3, 4
+
+ head, tail = _fix(char + json_string[last_comma + 1 :], allow)
+ if not head[1:] + tail[:-1].strip(): # empty, so trim the last comma
+ return json_string[:last_comma] + head[1:], tail + join_closing_tokens(stack[:-1])
+ return json_string[: last_comma + 1] + head[1:], tail + join_closing_tokens(stack[:-1])
+
+ # can't find comma after the last string and after the last container token
+
+ if char in "]}":
+ # ... [ ... ]
+ # ... { ... }
+ assert not json_string[i + 1 :].strip()
+ return json_string, join_closing_tokens(stack)
+
+ if char in "[{":
+ # ... [ ...
+ # ... { ...
+ head, tail = _fix(json_string[i:], allow)
+ return json_string[:i] + head, tail + join_closing_tokens(stack[:-1])
+
+ assert char == '"'
+
+ i, char = stack[-1]
+
+ if char == "[": # [ ... "val"
+ return json_string, join_closing_tokens(stack)
+
+ assert char == "{"
+ last_colon = json_string.rfind(":", last_string_end)
+ last_comma = json_string.rfind(",", i + 1, last_string_start)
+
+ if last_comma == -1: # only 1 key
+ # ... { "key"
+ # ... { "key": "value"
+ head, tail = _fix(json_string[i:], allow)
+ return json_string[:i] + head, tail + join_closing_tokens(stack[:-1])
+
+ if last_colon == -1:
+ if json_string.rfind(":", max(i, last_comma) + 1, last_string_start) != -1:
+ # { ... , "key": "value"
+ return json_string, join_closing_tokens(stack)
+
+ # { ... , "key"
+ head, tail = _fix("{" + json_string[last_comma + 1 :], allow)
+ if not head[1:] + tail[:-1].strip():
+ return json_string[:last_comma] + head[1:], tail + join_closing_tokens(stack[:-1])
+ return json_string[: last_comma + 1] + head[1:], tail + join_closing_tokens(stack)
+
+ assert last_colon > last_comma # { ... , "key":
+
+ head, tail = _fix("{" + json_string[last_comma + 1 :], allow)
+ if not head[1:] + tail[:-1].strip():
+ return json_string[:last_comma] + head[1:], tail + join_closing_tokens(stack[:-1])
+ return json_string[: last_comma + 1] + head[1:], tail + join_closing_tokens(stack[:-1])
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/core/options.py b/.venv/lib/python3.11/site-packages/partial_json_parser/core/options.py
new file mode 100644
index 0000000000000000000000000000000000000000..4563bffbaa5f92ab7d12755f3f0fd635954935aa
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/partial_json_parser/core/options.py
@@ -0,0 +1,56 @@
+from enum import IntFlag, auto
+
+
+class Allow(IntFlag):
+ """Specify what kind of partialness is allowed during JSON parsing"""
+
+ STR = auto()
+ NUM = auto()
+ ARR = auto()
+ OBJ = auto()
+ NULL = auto()
+ BOOL = auto()
+ NAN = auto()
+ INFINITY = auto()
+ _INFINITY = auto()
+
+ INF = INFINITY | _INFINITY
+ SPECIAL = NULL | BOOL | INF | NAN
+ ATOM = STR | NUM | SPECIAL
+ COLLECTION = ARR | OBJ
+ ALL = ATOM | COLLECTION
+
+
+STR = Allow.STR
+NUM = Allow.NUM
+ARR = Allow.ARR
+OBJ = Allow.OBJ
+NULL = Allow.NULL
+BOOL = Allow.BOOL
+NAN = Allow.NAN
+INFINITY = Allow.INFINITY
+_INFINITY = Allow._INFINITY
+INF = Allow.INF
+SPECIAL = Allow.SPECIAL
+ATOM = Allow.ATOM
+COLLECTION = Allow.COLLECTION
+ALL = Allow.ALL
+
+
+__all__ = [
+ "Allow",
+ "STR",
+ "NUM",
+ "ARR",
+ "OBJ",
+ "NULL",
+ "BOOL",
+ "NAN",
+ "INFINITY",
+ "_INFINITY",
+ "INF",
+ "SPECIAL",
+ "ATOM",
+ "COLLECTION",
+ "ALL",
+]
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/options.py b/.venv/lib/python3.11/site-packages/partial_json_parser/options.py
new file mode 100644
index 0000000000000000000000000000000000000000..9f94c0f7f71382e82ac5bbfcf73a8be4c2a0d21f
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/partial_json_parser/options.py
@@ -0,0 +1,3 @@
+"""For backward compatibility."""
+
+from .core.options import *
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/playground.py b/.venv/lib/python3.11/site-packages/partial_json_parser/playground.py
new file mode 100644
index 0000000000000000000000000000000000000000..3dd78a86d54a3bfcef5ab261dd55119d01e38a1a
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/partial_json_parser/playground.py
@@ -0,0 +1,30 @@
+from rich.console import Console
+from rich.highlighter import JSONHighlighter
+from rich.style import Style
+from rich.text import Span
+
+from partial_json_parser import fix_fast
+
+console = Console()
+highlight = JSONHighlighter()
+
+
+def main():
+ while True:
+ try:
+ input_str = console.input("[d]>>> ")
+
+ head, tail = fix_fast(input_str)
+ json = head + tail
+
+ rich_text = highlight(json)
+ if tail:
+ rich_text.spans.append(Span(len(head), len(json), Style(dim=True)))
+
+ console.print(" " * 3, rich_text)
+
+ except KeyboardInterrupt:
+ return
+ except Exception as err:
+ console.print(f"{err.__class__.__name__}:", style="bold red", highlight=False, end=" ")
+ console.print(" ".join(map(str, err.args)), style="yellow", highlight=False)
diff --git a/.venv/lib/python3.11/site-packages/partial_json_parser/version.py b/.venv/lib/python3.11/site-packages/partial_json_parser/version.py
new file mode 100644
index 0000000000000000000000000000000000000000..25de752701cdee60c2c26222b867a1d856f5b5cc
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/partial_json_parser/version.py
@@ -0,0 +1 @@
+__version__ = "0.2.1.1.post5"
diff --git a/.venv/lib/python3.11/site-packages/wrapt/__init__.py b/.venv/lib/python3.11/site-packages/wrapt/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb00b5a295b4c7bae2a6630c7f12b44bd94c1ddd
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/wrapt/__init__.py
@@ -0,0 +1,30 @@
+__version_info__ = ('1', '17', '2')
+__version__ = '.'.join(__version_info__)
+
+from .__wrapt__ import (ObjectProxy, CallableObjectProxy, FunctionWrapper,
+ BoundFunctionWrapper, PartialCallableObjectProxy)
+
+from .patches import (resolve_path, apply_patch, wrap_object, wrap_object_attribute,
+ function_wrapper, wrap_function_wrapper, patch_function_wrapper,
+ transient_function_wrapper)
+
+from .weakrefs import WeakFunctionProxy
+
+from .decorators import (adapter_factory, AdapterFactory, decorator,
+ synchronized)
+
+from .importer import (register_post_import_hook, when_imported,
+ notify_module_loaded, discover_post_import_hooks)
+
+# Import of inspect.getcallargs() included for backward compatibility. An
+# implementation of this was previously bundled and made available here for
+# Python <2.7. Avoid using this in future.
+
+from inspect import getcallargs
+
+# Variant of inspect.formatargspec() included here for forward compatibility.
+# This is being done because Python 3.11 dropped inspect.formatargspec() but
+# code for handling signature changing decorators relied on it. Exposing the
+# bundled implementation here in case any user of wrapt was also needing it.
+
+from .arguments import formatargspec
diff --git a/.venv/lib/python3.11/site-packages/wrapt/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9aefbf36037f5593573f01192ac95b2e679c3b50
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/__init__.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/wrapt/__pycache__/__wrapt__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/__wrapt__.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e47db68112751f95a8e9d9a1178dcb551309bbf1
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/__wrapt__.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/wrapt/__pycache__/arguments.cpython-311.pyc b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/arguments.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..fbebf7d9c154393a636e45ba6561b96dbdec7dad
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/arguments.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/wrapt/__pycache__/decorators.cpython-311.pyc b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/decorators.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..96cd52e17625765b95b16eac556d7f191014859f
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/decorators.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/wrapt/__pycache__/importer.cpython-311.pyc b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/importer.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1406032b102b1a953ad158f1026915e89c637c3b
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/importer.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/wrapt/__pycache__/patches.cpython-311.pyc b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/patches.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4d3093a8a9f8999fb732a87abddd26f5b1578ab0
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/patches.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/wrapt/__pycache__/weakrefs.cpython-311.pyc b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/weakrefs.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bdd78d2cfdc737c52e7edec0d7350abfdfc17dd5
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/weakrefs.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/wrapt/__pycache__/wrappers.cpython-311.pyc b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/wrappers.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..662403855a4949f9e3f62948433e1c2fe17d6a17
Binary files /dev/null and b/.venv/lib/python3.11/site-packages/wrapt/__pycache__/wrappers.cpython-311.pyc differ
diff --git a/.venv/lib/python3.11/site-packages/wrapt/__wrapt__.py b/.venv/lib/python3.11/site-packages/wrapt/__wrapt__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9933b2c972f0cb3895ac6709a460db8482b9fe5a
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/wrapt/__wrapt__.py
@@ -0,0 +1,14 @@
+import os
+
+from .wrappers import (ObjectProxy, CallableObjectProxy,
+ PartialCallableObjectProxy, FunctionWrapper,
+ BoundFunctionWrapper, _FunctionWrapperBase)
+
+try:
+ if not os.environ.get('WRAPT_DISABLE_EXTENSIONS'):
+ from ._wrappers import (ObjectProxy, CallableObjectProxy,
+ PartialCallableObjectProxy, FunctionWrapper,
+ BoundFunctionWrapper, _FunctionWrapperBase)
+
+except ImportError:
+ pass
diff --git a/.venv/lib/python3.11/site-packages/wrapt/arguments.py b/.venv/lib/python3.11/site-packages/wrapt/arguments.py
new file mode 100644
index 0000000000000000000000000000000000000000..032bc059e003c43b871d4dfe523ceb60881d2140
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/wrapt/arguments.py
@@ -0,0 +1,38 @@
+# The inspect.formatargspec() function was dropped in Python 3.11 but we need
+# need it for when constructing signature changing decorators based on result of
+# inspect.getargspec() or inspect.getfullargspec(). The code here implements
+# inspect.formatargspec() base on Parameter and Signature from inspect module,
+# which were added in Python 3.6. Thanks to Cyril Jouve for the implementation.
+
+try:
+ from inspect import Parameter, Signature
+except ImportError:
+ from inspect import formatargspec
+else:
+ def formatargspec(args, varargs=None, varkw=None, defaults=None,
+ kwonlyargs=(), kwonlydefaults={}, annotations={}):
+ if kwonlydefaults is None:
+ kwonlydefaults = {}
+ ndefaults = len(defaults) if defaults else 0
+ parameters = [
+ Parameter(
+ arg,
+ Parameter.POSITIONAL_OR_KEYWORD,
+ default=defaults[i] if i >= 0 else Parameter.empty,
+ annotation=annotations.get(arg, Parameter.empty),
+ ) for i, arg in enumerate(args, ndefaults - len(args))
+ ]
+ if varargs:
+ parameters.append(Parameter(varargs, Parameter.VAR_POSITIONAL))
+ parameters.extend(
+ Parameter(
+ kwonlyarg,
+ Parameter.KEYWORD_ONLY,
+ default=kwonlydefaults.get(kwonlyarg, Parameter.empty),
+ annotation=annotations.get(kwonlyarg, Parameter.empty),
+ ) for kwonlyarg in kwonlyargs
+ )
+ if varkw:
+ parameters.append(Parameter(varkw, Parameter.VAR_KEYWORD))
+ return_annotation = annotations.get('return', Signature.empty)
+ return str(Signature(parameters, return_annotation=return_annotation))
\ No newline at end of file
diff --git a/.venv/lib/python3.11/site-packages/wrapt/decorators.py b/.venv/lib/python3.11/site-packages/wrapt/decorators.py
new file mode 100644
index 0000000000000000000000000000000000000000..c80a4bb72e80d5113ba0330ec2bf4b17488b5997
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/wrapt/decorators.py
@@ -0,0 +1,541 @@
+"""This module implements decorators for implementing other decorators
+as well as some commonly used decorators.
+
+"""
+
+import sys
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+ string_types = basestring,
+
+ def exec_(_code_, _globs_=None, _locs_=None):
+ """Execute code in a namespace."""
+ if _globs_ is None:
+ frame = sys._getframe(1)
+ _globs_ = frame.f_globals
+ if _locs_ is None:
+ _locs_ = frame.f_locals
+ del frame
+ elif _locs_ is None:
+ _locs_ = _globs_
+ exec("""exec _code_ in _globs_, _locs_""")
+
+else:
+ string_types = str,
+
+ import builtins
+
+ exec_ = getattr(builtins, "exec")
+ del builtins
+
+from functools import partial
+from inspect import isclass
+from threading import Lock, RLock
+
+from .arguments import formatargspec
+
+try:
+ from inspect import signature
+except ImportError:
+ pass
+
+from .__wrapt__ import (FunctionWrapper, BoundFunctionWrapper, ObjectProxy,
+ CallableObjectProxy)
+
+# Adapter wrapper for the wrapped function which will overlay certain
+# properties from the adapter function onto the wrapped function so that
+# functions such as inspect.getargspec(), inspect.getfullargspec(),
+# inspect.signature() and inspect.getsource() return the correct results
+# one would expect.
+
+class _AdapterFunctionCode(CallableObjectProxy):
+
+ def __init__(self, wrapped_code, adapter_code):
+ super(_AdapterFunctionCode, self).__init__(wrapped_code)
+ self._self_adapter_code = adapter_code
+
+ @property
+ def co_argcount(self):
+ return self._self_adapter_code.co_argcount
+
+ @property
+ def co_code(self):
+ return self._self_adapter_code.co_code
+
+ @property
+ def co_flags(self):
+ return self._self_adapter_code.co_flags
+
+ @property
+ def co_kwonlyargcount(self):
+ return self._self_adapter_code.co_kwonlyargcount
+
+ @property
+ def co_varnames(self):
+ return self._self_adapter_code.co_varnames
+
+class _AdapterFunctionSurrogate(CallableObjectProxy):
+
+ def __init__(self, wrapped, adapter):
+ super(_AdapterFunctionSurrogate, self).__init__(wrapped)
+ self._self_adapter = adapter
+
+ @property
+ def __code__(self):
+ return _AdapterFunctionCode(self.__wrapped__.__code__,
+ self._self_adapter.__code__)
+
+ @property
+ def __defaults__(self):
+ return self._self_adapter.__defaults__
+
+ @property
+ def __kwdefaults__(self):
+ return self._self_adapter.__kwdefaults__
+
+ @property
+ def __signature__(self):
+ if 'signature' not in globals():
+ return self._self_adapter.__signature__
+ else:
+ return signature(self._self_adapter)
+
+ if PY2:
+ func_code = __code__
+ func_defaults = __defaults__
+
+class _BoundAdapterWrapper(BoundFunctionWrapper):
+
+ @property
+ def __func__(self):
+ return _AdapterFunctionSurrogate(self.__wrapped__.__func__,
+ self._self_parent._self_adapter)
+
+ @property
+ def __signature__(self):
+ if 'signature' not in globals():
+ return self.__wrapped__.__signature__
+ else:
+ return signature(self._self_parent._self_adapter)
+
+ if PY2:
+ im_func = __func__
+
+class AdapterWrapper(FunctionWrapper):
+
+ __bound_function_wrapper__ = _BoundAdapterWrapper
+
+ def __init__(self, *args, **kwargs):
+ adapter = kwargs.pop('adapter')
+ super(AdapterWrapper, self).__init__(*args, **kwargs)
+ self._self_surrogate = _AdapterFunctionSurrogate(
+ self.__wrapped__, adapter)
+ self._self_adapter = adapter
+
+ @property
+ def __code__(self):
+ return self._self_surrogate.__code__
+
+ @property
+ def __defaults__(self):
+ return self._self_surrogate.__defaults__
+
+ @property
+ def __kwdefaults__(self):
+ return self._self_surrogate.__kwdefaults__
+
+ if PY2:
+ func_code = __code__
+ func_defaults = __defaults__
+
+ @property
+ def __signature__(self):
+ return self._self_surrogate.__signature__
+
+class AdapterFactory(object):
+ def __call__(self, wrapped):
+ raise NotImplementedError()
+
+class DelegatedAdapterFactory(AdapterFactory):
+ def __init__(self, factory):
+ super(DelegatedAdapterFactory, self).__init__()
+ self.factory = factory
+ def __call__(self, wrapped):
+ return self.factory(wrapped)
+
+adapter_factory = DelegatedAdapterFactory
+
+# Decorator for creating other decorators. This decorator and the
+# wrappers which they use are designed to properly preserve any name
+# attributes, function signatures etc, in addition to the wrappers
+# themselves acting like a transparent proxy for the original wrapped
+# function so the wrapper is effectively indistinguishable from the
+# original wrapped function.
+
+def decorator(wrapper=None, enabled=None, adapter=None, proxy=FunctionWrapper):
+ # The decorator should be supplied with a single positional argument
+ # which is the wrapper function to be used to implement the
+ # decorator. This may be preceded by a step whereby the keyword
+ # arguments are supplied to customise the behaviour of the
+ # decorator. The 'adapter' argument is used to optionally denote a
+ # separate function which is notionally used by an adapter
+ # decorator. In that case parts of the function '__code__' and
+ # '__defaults__' attributes are used from the adapter function
+ # rather than those of the wrapped function. This allows for the
+ # argument specification from inspect.getfullargspec() and similar
+ # functions to be overridden with a prototype for a different
+ # function than what was wrapped. The 'enabled' argument provides a
+ # way to enable/disable the use of the decorator. If the type of
+ # 'enabled' is a boolean, then it is evaluated immediately and the
+ # wrapper not even applied if it is False. If not a boolean, it will
+ # be evaluated when the wrapper is called for an unbound wrapper,
+ # and when binding occurs for a bound wrapper. When being evaluated,
+ # if 'enabled' is callable it will be called to obtain the value to
+ # be checked. If False, the wrapper will not be called and instead
+ # the original wrapped function will be called directly instead.
+ # The 'proxy' argument provides a way of passing a custom version of
+ # the FunctionWrapper class used in decorating the function.
+
+ if wrapper is not None:
+ # Helper function for creating wrapper of the appropriate
+ # time when we need it down below.
+
+ def _build(wrapped, wrapper, enabled=None, adapter=None):
+ if adapter:
+ if isinstance(adapter, AdapterFactory):
+ adapter = adapter(wrapped)
+
+ if not callable(adapter):
+ ns = {}
+
+ # Check if the signature argument specification has
+ # annotations. If it does then we need to remember
+ # it but also drop it when attempting to manufacture
+ # a standin adapter function. This is necessary else
+ # it will try and look up any types referenced in
+ # the annotations in the empty namespace we use,
+ # which will fail.
+
+ annotations = {}
+
+ if not isinstance(adapter, string_types):
+ if len(adapter) == 7:
+ annotations = adapter[-1]
+ adapter = adapter[:-1]
+ adapter = formatargspec(*adapter)
+
+ exec_('def adapter{}: pass'.format(adapter), ns, ns)
+ adapter = ns['adapter']
+
+ # Override the annotations for the manufactured
+ # adapter function so they match the original
+ # adapter signature argument specification.
+
+ if annotations:
+ adapter.__annotations__ = annotations
+
+ return AdapterWrapper(wrapped=wrapped, wrapper=wrapper,
+ enabled=enabled, adapter=adapter)
+
+ return proxy(wrapped=wrapped, wrapper=wrapper, enabled=enabled)
+
+ # The wrapper has been provided so return the final decorator.
+ # The decorator is itself one of our function wrappers so we
+ # can determine when it is applied to functions, instance methods
+ # or class methods. This allows us to bind the instance or class
+ # method so the appropriate self or cls attribute is supplied
+ # when it is finally called.
+
+ def _wrapper(wrapped, instance, args, kwargs):
+ # We first check for the case where the decorator was applied
+ # to a class type.
+ #
+ # @decorator
+ # class mydecoratorclass(object):
+ # def __init__(self, arg=None):
+ # self.arg = arg
+ # def __call__(self, wrapped, instance, args, kwargs):
+ # return wrapped(*args, **kwargs)
+ #
+ # @mydecoratorclass(arg=1)
+ # def function():
+ # pass
+ #
+ # In this case an instance of the class is to be used as the
+ # decorator wrapper function. If args was empty at this point,
+ # then it means that there were optional keyword arguments
+ # supplied to be used when creating an instance of the class
+ # to be used as the wrapper function.
+
+ if instance is None and isclass(wrapped) and not args:
+ # We still need to be passed the target function to be
+ # wrapped as yet, so we need to return a further function
+ # to be able to capture it.
+
+ def _capture(target_wrapped):
+ # Now have the target function to be wrapped and need
+ # to create an instance of the class which is to act
+ # as the decorator wrapper function. Before we do that,
+ # we need to first check that use of the decorator
+ # hadn't been disabled by a simple boolean. If it was,
+ # the target function to be wrapped is returned instead.
+
+ _enabled = enabled
+ if type(_enabled) is bool:
+ if not _enabled:
+ return target_wrapped
+ _enabled = None
+
+ # Now create an instance of the class which is to act
+ # as the decorator wrapper function. Any arguments had
+ # to be supplied as keyword only arguments so that is
+ # all we pass when creating it.
+
+ target_wrapper = wrapped(**kwargs)
+
+ # Finally build the wrapper itself and return it.
+
+ return _build(target_wrapped, target_wrapper,
+ _enabled, adapter)
+
+ return _capture
+
+ # We should always have the target function to be wrapped at
+ # this point as the first (and only) value in args.
+
+ target_wrapped = args[0]
+
+ # Need to now check that use of the decorator hadn't been
+ # disabled by a simple boolean. If it was, then target
+ # function to be wrapped is returned instead.
+
+ _enabled = enabled
+ if type(_enabled) is bool:
+ if not _enabled:
+ return target_wrapped
+ _enabled = None
+
+ # We now need to build the wrapper, but there are a couple of
+ # different cases we need to consider.
+
+ if instance is None:
+ if isclass(wrapped):
+ # In this case the decorator was applied to a class
+ # type but optional keyword arguments were not supplied
+ # for initialising an instance of the class to be used
+ # as the decorator wrapper function.
+ #
+ # @decorator
+ # class mydecoratorclass(object):
+ # def __init__(self, arg=None):
+ # self.arg = arg
+ # def __call__(self, wrapped, instance,
+ # args, kwargs):
+ # return wrapped(*args, **kwargs)
+ #
+ # @mydecoratorclass
+ # def function():
+ # pass
+ #
+ # We still need to create an instance of the class to
+ # be used as the decorator wrapper function, but no
+ # arguments are pass.
+
+ target_wrapper = wrapped()
+
+ else:
+ # In this case the decorator was applied to a normal
+ # function, or possibly a static method of a class.
+ #
+ # @decorator
+ # def mydecoratorfuntion(wrapped, instance,
+ # args, kwargs):
+ # return wrapped(*args, **kwargs)
+ #
+ # @mydecoratorfunction
+ # def function():
+ # pass
+ #
+ # That normal function becomes the decorator wrapper
+ # function.
+
+ target_wrapper = wrapper
+
+ else:
+ if isclass(instance):
+ # In this case the decorator was applied to a class
+ # method.
+ #
+ # class myclass(object):
+ # @decorator
+ # @classmethod
+ # def decoratorclassmethod(cls, wrapped,
+ # instance, args, kwargs):
+ # return wrapped(*args, **kwargs)
+ #
+ # instance = myclass()
+ #
+ # @instance.decoratorclassmethod
+ # def function():
+ # pass
+ #
+ # This one is a bit strange because binding was actually
+ # performed on the wrapper created by our decorator
+ # factory. We need to apply that binding to the decorator
+ # wrapper function that the decorator factory
+ # was applied to.
+
+ target_wrapper = wrapper.__get__(None, instance)
+
+ else:
+ # In this case the decorator was applied to an instance
+ # method.
+ #
+ # class myclass(object):
+ # @decorator
+ # def decoratorclassmethod(self, wrapped,
+ # instance, args, kwargs):
+ # return wrapped(*args, **kwargs)
+ #
+ # instance = myclass()
+ #
+ # @instance.decoratorclassmethod
+ # def function():
+ # pass
+ #
+ # This one is a bit strange because binding was actually
+ # performed on the wrapper created by our decorator
+ # factory. We need to apply that binding to the decorator
+ # wrapper function that the decorator factory
+ # was applied to.
+
+ target_wrapper = wrapper.__get__(instance, type(instance))
+
+ # Finally build the wrapper itself and return it.
+
+ return _build(target_wrapped, target_wrapper, _enabled, adapter)
+
+ # We first return our magic function wrapper here so we can
+ # determine in what context the decorator factory was used. In
+ # other words, it is itself a universal decorator. The decorator
+ # function is used as the adapter so that linters see a signature
+ # corresponding to the decorator and not the wrapper it is being
+ # applied to.
+
+ return _build(wrapper, _wrapper, adapter=decorator)
+
+ else:
+ # The wrapper still has not been provided, so we are just
+ # collecting the optional keyword arguments. Return the
+ # decorator again wrapped in a partial using the collected
+ # arguments.
+
+ return partial(decorator, enabled=enabled, adapter=adapter,
+ proxy=proxy)
+
+# Decorator for implementing thread synchronization. It can be used as a
+# decorator, in which case the synchronization context is determined by
+# what type of function is wrapped, or it can also be used as a context
+# manager, where the user needs to supply the correct synchronization
+# context. It is also possible to supply an object which appears to be a
+# synchronization primitive of some sort, by virtue of having release()
+# and acquire() methods. In that case that will be used directly as the
+# synchronization primitive without creating a separate lock against the
+# derived or supplied context.
+
+def synchronized(wrapped):
+ # Determine if being passed an object which is a synchronization
+ # primitive. We can't check by type for Lock, RLock, Semaphore etc,
+ # as the means of creating them isn't the type. Therefore use the
+ # existence of acquire() and release() methods. This is more
+ # extensible anyway as it allows custom synchronization mechanisms.
+
+ if hasattr(wrapped, 'acquire') and hasattr(wrapped, 'release'):
+ # We remember what the original lock is and then return a new
+ # decorator which accesses and locks it. When returning the new
+ # decorator we wrap it with an object proxy so we can override
+ # the context manager methods in case it is being used to wrap
+ # synchronized statements with a 'with' statement.
+
+ lock = wrapped
+
+ @decorator
+ def _synchronized(wrapped, instance, args, kwargs):
+ # Execute the wrapped function while the original supplied
+ # lock is held.
+
+ with lock:
+ return wrapped(*args, **kwargs)
+
+ class _PartialDecorator(CallableObjectProxy):
+
+ def __enter__(self):
+ lock.acquire()
+ return lock
+
+ def __exit__(self, *args):
+ lock.release()
+
+ return _PartialDecorator(wrapped=_synchronized)
+
+ # Following only apply when the lock is being created automatically
+ # based on the context of what was supplied. In this case we supply
+ # a final decorator, but need to use FunctionWrapper directly as we
+ # want to derive from it to add context manager methods in case it is
+ # being used to wrap synchronized statements with a 'with' statement.
+
+ def _synchronized_lock(context):
+ # Attempt to retrieve the lock for the specific context.
+
+ lock = vars(context).get('_synchronized_lock', None)
+
+ if lock is None:
+ # There is no existing lock defined for the context we
+ # are dealing with so we need to create one. This needs
+ # to be done in a way to guarantee there is only one
+ # created, even if multiple threads try and create it at
+ # the same time. We can't always use the setdefault()
+ # method on the __dict__ for the context. This is the
+ # case where the context is a class, as __dict__ is
+ # actually a dictproxy. What we therefore do is use a
+ # meta lock on this wrapper itself, to control the
+ # creation and assignment of the lock attribute against
+ # the context.
+
+ with synchronized._synchronized_meta_lock:
+ # We need to check again for whether the lock we want
+ # exists in case two threads were trying to create it
+ # at the same time and were competing to create the
+ # meta lock.
+
+ lock = vars(context).get('_synchronized_lock', None)
+
+ if lock is None:
+ lock = RLock()
+ setattr(context, '_synchronized_lock', lock)
+
+ return lock
+
+ def _synchronized_wrapper(wrapped, instance, args, kwargs):
+ # Execute the wrapped function while the lock for the
+ # desired context is held. If instance is None then the
+ # wrapped function is used as the context.
+
+ with _synchronized_lock(instance if instance is not None else wrapped):
+ return wrapped(*args, **kwargs)
+
+ class _FinalDecorator(FunctionWrapper):
+
+ def __enter__(self):
+ self._self_lock = _synchronized_lock(self.__wrapped__)
+ self._self_lock.acquire()
+ return self._self_lock
+
+ def __exit__(self, *args):
+ self._self_lock.release()
+
+ return _FinalDecorator(wrapped=wrapped, wrapper=_synchronized_wrapper)
+
+synchronized._synchronized_meta_lock = Lock()
diff --git a/.venv/lib/python3.11/site-packages/wrapt/importer.py b/.venv/lib/python3.11/site-packages/wrapt/importer.py
new file mode 100644
index 0000000000000000000000000000000000000000..23fcbd2f6358cfd72111dd0e5c1270e382c69b30
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/wrapt/importer.py
@@ -0,0 +1,295 @@
+"""This module implements a post import hook mechanism styled after what is
+described in PEP-369. Note that it doesn't cope with modules being reloaded.
+
+"""
+
+import sys
+import threading
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+ string_types = basestring,
+ find_spec = None
+else:
+ string_types = str,
+ from importlib.util import find_spec
+
+from .__wrapt__ import ObjectProxy
+
+# The dictionary registering any post import hooks to be triggered once
+# the target module has been imported. Once a module has been imported
+# and the hooks fired, the list of hooks recorded against the target
+# module will be truncated but the list left in the dictionary. This
+# acts as a flag to indicate that the module had already been imported.
+
+_post_import_hooks = {}
+_post_import_hooks_init = False
+_post_import_hooks_lock = threading.RLock()
+
+# Register a new post import hook for the target module name. This
+# differs from the PEP-369 implementation in that it also allows the
+# hook function to be specified as a string consisting of the name of
+# the callback in the form 'module:function'. This will result in a
+# proxy callback being registered which will defer loading of the
+# specified module containing the callback function until required.
+
+def _create_import_hook_from_string(name):
+ def import_hook(module):
+ module_name, function = name.split(':')
+ attrs = function.split('.')
+ __import__(module_name)
+ callback = sys.modules[module_name]
+ for attr in attrs:
+ callback = getattr(callback, attr)
+ return callback(module)
+ return import_hook
+
+def register_post_import_hook(hook, name):
+ # Create a deferred import hook if hook is a string name rather than
+ # a callable function.
+
+ if isinstance(hook, string_types):
+ hook = _create_import_hook_from_string(hook)
+
+ with _post_import_hooks_lock:
+ # Automatically install the import hook finder if it has not already
+ # been installed.
+
+ global _post_import_hooks_init
+
+ if not _post_import_hooks_init:
+ _post_import_hooks_init = True
+ sys.meta_path.insert(0, ImportHookFinder())
+
+ # Check if the module is already imported. If not, register the hook
+ # to be called after import.
+
+ module = sys.modules.get(name, None)
+
+ if module is None:
+ _post_import_hooks.setdefault(name, []).append(hook)
+
+ # If the module is already imported, we fire the hook right away. Note that
+ # the hook is called outside of the lock to avoid deadlocks if code run as a
+ # consequence of calling the module import hook in turn triggers a separate
+ # thread which tries to register an import hook.
+
+ if module is not None:
+ hook(module)
+
+# Register post import hooks defined as package entry points.
+
+def _create_import_hook_from_entrypoint(entrypoint):
+ def import_hook(module):
+ __import__(entrypoint.module_name)
+ callback = sys.modules[entrypoint.module_name]
+ for attr in entrypoint.attrs:
+ callback = getattr(callback, attr)
+ return callback(module)
+ return import_hook
+
+def discover_post_import_hooks(group):
+ try:
+ import pkg_resources
+ except ImportError:
+ return
+
+ for entrypoint in pkg_resources.iter_entry_points(group=group):
+ callback = _create_import_hook_from_entrypoint(entrypoint)
+ register_post_import_hook(callback, entrypoint.name)
+
+# Indicate that a module has been loaded. Any post import hooks which
+# were registered against the target module will be invoked. If an
+# exception is raised in any of the post import hooks, that will cause
+# the import of the target module to fail.
+
+def notify_module_loaded(module):
+ name = getattr(module, '__name__', None)
+
+ with _post_import_hooks_lock:
+ hooks = _post_import_hooks.pop(name, ())
+
+ # Note that the hook is called outside of the lock to avoid deadlocks if
+ # code run as a consequence of calling the module import hook in turn
+ # triggers a separate thread which tries to register an import hook.
+
+ for hook in hooks:
+ hook(module)
+
+# A custom module import finder. This intercepts attempts to import
+# modules and watches out for attempts to import target modules of
+# interest. When a module of interest is imported, then any post import
+# hooks which are registered will be invoked.
+
+class _ImportHookLoader:
+
+ def load_module(self, fullname):
+ module = sys.modules[fullname]
+ notify_module_loaded(module)
+
+ return module
+
+class _ImportHookChainedLoader(ObjectProxy):
+
+ def __init__(self, loader):
+ super(_ImportHookChainedLoader, self).__init__(loader)
+
+ if hasattr(loader, "load_module"):
+ self.__self_setattr__('load_module', self._self_load_module)
+ if hasattr(loader, "create_module"):
+ self.__self_setattr__('create_module', self._self_create_module)
+ if hasattr(loader, "exec_module"):
+ self.__self_setattr__('exec_module', self._self_exec_module)
+
+ def _self_set_loader(self, module):
+ # Set module's loader to self.__wrapped__ unless it's already set to
+ # something else. Import machinery will set it to spec.loader if it is
+ # None, so handle None as well. The module may not support attribute
+ # assignment, in which case we simply skip it. Note that we also deal
+ # with __loader__ not existing at all. This is to future proof things
+ # due to proposal to remove the attribue as described in the GitHub
+ # issue at https://github.com/python/cpython/issues/77458. Also prior
+ # to Python 3.3, the __loader__ attribute was only set if a custom
+ # module loader was used. It isn't clear whether the attribute still
+ # existed in that case or was set to None.
+
+ class UNDEFINED: pass
+
+ if getattr(module, "__loader__", UNDEFINED) in (None, self):
+ try:
+ module.__loader__ = self.__wrapped__
+ except AttributeError:
+ pass
+
+ if (getattr(module, "__spec__", None) is not None
+ and getattr(module.__spec__, "loader", None) is self):
+ module.__spec__.loader = self.__wrapped__
+
+ def _self_load_module(self, fullname):
+ module = self.__wrapped__.load_module(fullname)
+ self._self_set_loader(module)
+ notify_module_loaded(module)
+
+ return module
+
+ # Python 3.4 introduced create_module() and exec_module() instead of
+ # load_module() alone. Splitting the two steps.
+
+ def _self_create_module(self, spec):
+ return self.__wrapped__.create_module(spec)
+
+ def _self_exec_module(self, module):
+ self._self_set_loader(module)
+ self.__wrapped__.exec_module(module)
+ notify_module_loaded(module)
+
+class ImportHookFinder:
+
+ def __init__(self):
+ self.in_progress = {}
+
+ def find_module(self, fullname, path=None):
+ # If the module being imported is not one we have registered
+ # post import hooks for, we can return immediately. We will
+ # take no further part in the importing of this module.
+
+ with _post_import_hooks_lock:
+ if fullname not in _post_import_hooks:
+ return None
+
+ # When we are interested in a specific module, we will call back
+ # into the import system a second time to defer to the import
+ # finder that is supposed to handle the importing of the module.
+ # We set an in progress flag for the target module so that on
+ # the second time through we don't trigger another call back
+ # into the import system and cause a infinite loop.
+
+ if fullname in self.in_progress:
+ return None
+
+ self.in_progress[fullname] = True
+
+ # Now call back into the import system again.
+
+ try:
+ if not find_spec:
+ # For Python 2 we don't have much choice but to
+ # call back in to __import__(). This will
+ # actually cause the module to be imported. If no
+ # module could be found then ImportError will be
+ # raised. Otherwise we return a loader which
+ # returns the already loaded module and invokes
+ # the post import hooks.
+
+ __import__(fullname)
+
+ return _ImportHookLoader()
+
+ else:
+ # For Python 3 we need to use find_spec().loader
+ # from the importlib.util module. It doesn't actually
+ # import the target module and only finds the
+ # loader. If a loader is found, we need to return
+ # our own loader which will then in turn call the
+ # real loader to import the module and invoke the
+ # post import hooks.
+
+ loader = getattr(find_spec(fullname), "loader", None)
+
+ if loader and not isinstance(loader, _ImportHookChainedLoader):
+ return _ImportHookChainedLoader(loader)
+
+ finally:
+ del self.in_progress[fullname]
+
+ def find_spec(self, fullname, path=None, target=None):
+ # Since Python 3.4, you are meant to implement find_spec() method
+ # instead of find_module() and since Python 3.10 you get deprecation
+ # warnings if you don't define find_spec().
+
+ # If the module being imported is not one we have registered
+ # post import hooks for, we can return immediately. We will
+ # take no further part in the importing of this module.
+
+ with _post_import_hooks_lock:
+ if fullname not in _post_import_hooks:
+ return None
+
+ # When we are interested in a specific module, we will call back
+ # into the import system a second time to defer to the import
+ # finder that is supposed to handle the importing of the module.
+ # We set an in progress flag for the target module so that on
+ # the second time through we don't trigger another call back
+ # into the import system and cause a infinite loop.
+
+ if fullname in self.in_progress:
+ return None
+
+ self.in_progress[fullname] = True
+
+ # Now call back into the import system again.
+
+ try:
+ # This should only be Python 3 so find_spec() should always
+ # exist so don't need to check.
+
+ spec = find_spec(fullname)
+ loader = getattr(spec, "loader", None)
+
+ if loader and not isinstance(loader, _ImportHookChainedLoader):
+ spec.loader = _ImportHookChainedLoader(loader)
+
+ return spec
+
+ finally:
+ del self.in_progress[fullname]
+
+# Decorator for marking that a function should be called as a post
+# import hook when the target module is imported.
+
+def when_imported(name):
+ def register(hook):
+ register_post_import_hook(hook, name)
+ return hook
+ return register
diff --git a/.venv/lib/python3.11/site-packages/wrapt/patches.py b/.venv/lib/python3.11/site-packages/wrapt/patches.py
new file mode 100644
index 0000000000000000000000000000000000000000..e22adf7ca835d25f66f9bd51d3ab5ba1f1b52b23
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/wrapt/patches.py
@@ -0,0 +1,141 @@
+import inspect
+import sys
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+ string_types = basestring,
+else:
+ string_types = str,
+
+from .__wrapt__ import FunctionWrapper
+
+# Helper functions for applying wrappers to existing functions.
+
+def resolve_path(module, name):
+ if isinstance(module, string_types):
+ __import__(module)
+ module = sys.modules[module]
+
+ parent = module
+
+ path = name.split('.')
+ attribute = path[0]
+
+ # We can't just always use getattr() because in doing
+ # that on a class it will cause binding to occur which
+ # will complicate things later and cause some things not
+ # to work. For the case of a class we therefore access
+ # the __dict__ directly. To cope though with the wrong
+ # class being given to us, or a method being moved into
+ # a base class, we need to walk the class hierarchy to
+ # work out exactly which __dict__ the method was defined
+ # in, as accessing it from __dict__ will fail if it was
+ # not actually on the class given. Fallback to using
+ # getattr() if we can't find it. If it truly doesn't
+ # exist, then that will fail.
+
+ def lookup_attribute(parent, attribute):
+ if inspect.isclass(parent):
+ for cls in inspect.getmro(parent):
+ if attribute in vars(cls):
+ return vars(cls)[attribute]
+ else:
+ return getattr(parent, attribute)
+ else:
+ return getattr(parent, attribute)
+
+ original = lookup_attribute(parent, attribute)
+
+ for attribute in path[1:]:
+ parent = original
+ original = lookup_attribute(parent, attribute)
+
+ return (parent, attribute, original)
+
+def apply_patch(parent, attribute, replacement):
+ setattr(parent, attribute, replacement)
+
+def wrap_object(module, name, factory, args=(), kwargs={}):
+ (parent, attribute, original) = resolve_path(module, name)
+ wrapper = factory(original, *args, **kwargs)
+ apply_patch(parent, attribute, wrapper)
+ return wrapper
+
+# Function for applying a proxy object to an attribute of a class
+# instance. The wrapper works by defining an attribute of the same name
+# on the class which is a descriptor and which intercepts access to the
+# instance attribute. Note that this cannot be used on attributes which
+# are themselves defined by a property object.
+
+class AttributeWrapper(object):
+
+ def __init__(self, attribute, factory, args, kwargs):
+ self.attribute = attribute
+ self.factory = factory
+ self.args = args
+ self.kwargs = kwargs
+
+ def __get__(self, instance, owner):
+ value = instance.__dict__[self.attribute]
+ return self.factory(value, *self.args, **self.kwargs)
+
+ def __set__(self, instance, value):
+ instance.__dict__[self.attribute] = value
+
+ def __delete__(self, instance):
+ del instance.__dict__[self.attribute]
+
+def wrap_object_attribute(module, name, factory, args=(), kwargs={}):
+ path, attribute = name.rsplit('.', 1)
+ parent = resolve_path(module, path)[2]
+ wrapper = AttributeWrapper(attribute, factory, args, kwargs)
+ apply_patch(parent, attribute, wrapper)
+ return wrapper
+
+# Functions for creating a simple decorator using a FunctionWrapper,
+# plus short cut functions for applying wrappers to functions. These are
+# for use when doing monkey patching. For a more featured way of
+# creating decorators see the decorator decorator instead.
+
+def function_wrapper(wrapper):
+ def _wrapper(wrapped, instance, args, kwargs):
+ target_wrapped = args[0]
+ if instance is None:
+ target_wrapper = wrapper
+ elif inspect.isclass(instance):
+ target_wrapper = wrapper.__get__(None, instance)
+ else:
+ target_wrapper = wrapper.__get__(instance, type(instance))
+ return FunctionWrapper(target_wrapped, target_wrapper)
+ return FunctionWrapper(wrapper, _wrapper)
+
+def wrap_function_wrapper(module, name, wrapper):
+ return wrap_object(module, name, FunctionWrapper, (wrapper,))
+
+def patch_function_wrapper(module, name, enabled=None):
+ def _wrapper(wrapper):
+ return wrap_object(module, name, FunctionWrapper, (wrapper, enabled))
+ return _wrapper
+
+def transient_function_wrapper(module, name):
+ def _decorator(wrapper):
+ def _wrapper(wrapped, instance, args, kwargs):
+ target_wrapped = args[0]
+ if instance is None:
+ target_wrapper = wrapper
+ elif inspect.isclass(instance):
+ target_wrapper = wrapper.__get__(None, instance)
+ else:
+ target_wrapper = wrapper.__get__(instance, type(instance))
+ def _execute(wrapped, instance, args, kwargs):
+ (parent, attribute, original) = resolve_path(module, name)
+ replacement = FunctionWrapper(original, target_wrapper)
+ setattr(parent, attribute, replacement)
+ try:
+ return wrapped(*args, **kwargs)
+ finally:
+ setattr(parent, attribute, original)
+ return FunctionWrapper(target_wrapped, _execute)
+ return FunctionWrapper(wrapper, _wrapper)
+ return _decorator
diff --git a/.venv/lib/python3.11/site-packages/wrapt/wrappers.py b/.venv/lib/python3.11/site-packages/wrapt/wrappers.py
new file mode 100644
index 0000000000000000000000000000000000000000..62da8a30e31c14c09b64d092ec7521ec705bf830
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/wrapt/wrappers.py
@@ -0,0 +1,821 @@
+import sys
+import operator
+import inspect
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+ string_types = basestring,
+else:
+ string_types = str,
+
+def with_metaclass(meta, *bases):
+ """Create a base class with a metaclass."""
+ return meta("NewBase", bases, {})
+
+class _ObjectProxyMethods(object):
+
+ # We use properties to override the values of __module__ and
+ # __doc__. If we add these in ObjectProxy, the derived class
+ # __dict__ will still be setup to have string variants of these
+ # attributes and the rules of descriptors means that they appear to
+ # take precedence over the properties in the base class. To avoid
+ # that, we copy the properties into the derived class type itself
+ # via a meta class. In that way the properties will always take
+ # precedence.
+
+ @property
+ def __module__(self):
+ return self.__wrapped__.__module__
+
+ @__module__.setter
+ def __module__(self, value):
+ self.__wrapped__.__module__ = value
+
+ @property
+ def __doc__(self):
+ return self.__wrapped__.__doc__
+
+ @__doc__.setter
+ def __doc__(self, value):
+ self.__wrapped__.__doc__ = value
+
+ # We similar use a property for __dict__. We need __dict__ to be
+ # explicit to ensure that vars() works as expected.
+
+ @property
+ def __dict__(self):
+ return self.__wrapped__.__dict__
+
+ # Need to also propagate the special __weakref__ attribute for case
+ # where decorating classes which will define this. If do not define
+ # it and use a function like inspect.getmembers() on a decorator
+ # class it will fail. This can't be in the derived classes.
+
+ @property
+ def __weakref__(self):
+ return self.__wrapped__.__weakref__
+
+class _ObjectProxyMetaType(type):
+ def __new__(cls, name, bases, dictionary):
+ # Copy our special properties into the class so that they
+ # always take precedence over attributes of the same name added
+ # during construction of a derived class. This is to save
+ # duplicating the implementation for them in all derived classes.
+
+ dictionary.update(vars(_ObjectProxyMethods))
+
+ return type.__new__(cls, name, bases, dictionary)
+
+class ObjectProxy(with_metaclass(_ObjectProxyMetaType)):
+
+ __slots__ = '__wrapped__'
+
+ def __init__(self, wrapped):
+ object.__setattr__(self, '__wrapped__', wrapped)
+
+ # Python 3.2+ has the __qualname__ attribute, but it does not
+ # allow it to be overridden using a property and it must instead
+ # be an actual string object instead.
+
+ try:
+ object.__setattr__(self, '__qualname__', wrapped.__qualname__)
+ except AttributeError:
+ pass
+
+ # Python 3.10 onwards also does not allow itself to be overridden
+ # using a property and it must instead be set explicitly.
+
+ try:
+ object.__setattr__(self, '__annotations__', wrapped.__annotations__)
+ except AttributeError:
+ pass
+
+ def __self_setattr__(self, name, value):
+ object.__setattr__(self, name, value)
+
+ @property
+ def __name__(self):
+ return self.__wrapped__.__name__
+
+ @__name__.setter
+ def __name__(self, value):
+ self.__wrapped__.__name__ = value
+
+ @property
+ def __class__(self):
+ return self.__wrapped__.__class__
+
+ @__class__.setter
+ def __class__(self, value):
+ self.__wrapped__.__class__ = value
+
+ def __dir__(self):
+ return dir(self.__wrapped__)
+
+ def __str__(self):
+ return str(self.__wrapped__)
+
+ if not PY2:
+ def __bytes__(self):
+ return bytes(self.__wrapped__)
+
+ def __repr__(self):
+ return '<{} at 0x{:x} for {} at 0x{:x}>'.format(
+ type(self).__name__, id(self),
+ type(self.__wrapped__).__name__,
+ id(self.__wrapped__))
+
+ def __format__(self, format_spec):
+ return format(self.__wrapped__, format_spec)
+
+ def __reversed__(self):
+ return reversed(self.__wrapped__)
+
+ if not PY2:
+ def __round__(self, ndigits=None):
+ return round(self.__wrapped__, ndigits)
+
+ if sys.hexversion >= 0x03070000:
+ def __mro_entries__(self, bases):
+ return (self.__wrapped__,)
+
+ def __lt__(self, other):
+ return self.__wrapped__ < other
+
+ def __le__(self, other):
+ return self.__wrapped__ <= other
+
+ def __eq__(self, other):
+ return self.__wrapped__ == other
+
+ def __ne__(self, other):
+ return self.__wrapped__ != other
+
+ def __gt__(self, other):
+ return self.__wrapped__ > other
+
+ def __ge__(self, other):
+ return self.__wrapped__ >= other
+
+ def __hash__(self):
+ return hash(self.__wrapped__)
+
+ def __nonzero__(self):
+ return bool(self.__wrapped__)
+
+ def __bool__(self):
+ return bool(self.__wrapped__)
+
+ def __setattr__(self, name, value):
+ if name.startswith('_self_'):
+ object.__setattr__(self, name, value)
+
+ elif name == '__wrapped__':
+ object.__setattr__(self, name, value)
+ try:
+ object.__delattr__(self, '__qualname__')
+ except AttributeError:
+ pass
+ try:
+ object.__setattr__(self, '__qualname__', value.__qualname__)
+ except AttributeError:
+ pass
+ try:
+ object.__delattr__(self, '__annotations__')
+ except AttributeError:
+ pass
+ try:
+ object.__setattr__(self, '__annotations__', value.__annotations__)
+ except AttributeError:
+ pass
+
+ elif name == '__qualname__':
+ setattr(self.__wrapped__, name, value)
+ object.__setattr__(self, name, value)
+
+ elif name == '__annotations__':
+ setattr(self.__wrapped__, name, value)
+ object.__setattr__(self, name, value)
+
+ elif hasattr(type(self), name):
+ object.__setattr__(self, name, value)
+
+ else:
+ setattr(self.__wrapped__, name, value)
+
+ def __getattr__(self, name):
+ # If we are being to lookup '__wrapped__' then the
+ # '__init__()' method cannot have been called.
+
+ if name == '__wrapped__':
+ raise ValueError('wrapper has not been initialised')
+
+ return getattr(self.__wrapped__, name)
+
+ def __delattr__(self, name):
+ if name.startswith('_self_'):
+ object.__delattr__(self, name)
+
+ elif name == '__wrapped__':
+ raise TypeError('__wrapped__ must be an object')
+
+ elif name == '__qualname__':
+ object.__delattr__(self, name)
+ delattr(self.__wrapped__, name)
+
+ elif hasattr(type(self), name):
+ object.__delattr__(self, name)
+
+ else:
+ delattr(self.__wrapped__, name)
+
+ def __add__(self, other):
+ return self.__wrapped__ + other
+
+ def __sub__(self, other):
+ return self.__wrapped__ - other
+
+ def __mul__(self, other):
+ return self.__wrapped__ * other
+
+ def __div__(self, other):
+ return operator.div(self.__wrapped__, other)
+
+ def __truediv__(self, other):
+ return operator.truediv(self.__wrapped__, other)
+
+ def __floordiv__(self, other):
+ return self.__wrapped__ // other
+
+ def __mod__(self, other):
+ return self.__wrapped__ % other
+
+ def __divmod__(self, other):
+ return divmod(self.__wrapped__, other)
+
+ def __pow__(self, other, *args):
+ return pow(self.__wrapped__, other, *args)
+
+ def __lshift__(self, other):
+ return self.__wrapped__ << other
+
+ def __rshift__(self, other):
+ return self.__wrapped__ >> other
+
+ def __and__(self, other):
+ return self.__wrapped__ & other
+
+ def __xor__(self, other):
+ return self.__wrapped__ ^ other
+
+ def __or__(self, other):
+ return self.__wrapped__ | other
+
+ def __radd__(self, other):
+ return other + self.__wrapped__
+
+ def __rsub__(self, other):
+ return other - self.__wrapped__
+
+ def __rmul__(self, other):
+ return other * self.__wrapped__
+
+ def __rdiv__(self, other):
+ return operator.div(other, self.__wrapped__)
+
+ def __rtruediv__(self, other):
+ return operator.truediv(other, self.__wrapped__)
+
+ def __rfloordiv__(self, other):
+ return other // self.__wrapped__
+
+ def __rmod__(self, other):
+ return other % self.__wrapped__
+
+ def __rdivmod__(self, other):
+ return divmod(other, self.__wrapped__)
+
+ def __rpow__(self, other, *args):
+ return pow(other, self.__wrapped__, *args)
+
+ def __rlshift__(self, other):
+ return other << self.__wrapped__
+
+ def __rrshift__(self, other):
+ return other >> self.__wrapped__
+
+ def __rand__(self, other):
+ return other & self.__wrapped__
+
+ def __rxor__(self, other):
+ return other ^ self.__wrapped__
+
+ def __ror__(self, other):
+ return other | self.__wrapped__
+
+ def __iadd__(self, other):
+ self.__wrapped__ += other
+ return self
+
+ def __isub__(self, other):
+ self.__wrapped__ -= other
+ return self
+
+ def __imul__(self, other):
+ self.__wrapped__ *= other
+ return self
+
+ def __idiv__(self, other):
+ self.__wrapped__ = operator.idiv(self.__wrapped__, other)
+ return self
+
+ def __itruediv__(self, other):
+ self.__wrapped__ = operator.itruediv(self.__wrapped__, other)
+ return self
+
+ def __ifloordiv__(self, other):
+ self.__wrapped__ //= other
+ return self
+
+ def __imod__(self, other):
+ self.__wrapped__ %= other
+ return self
+
+ def __ipow__(self, other):
+ self.__wrapped__ **= other
+ return self
+
+ def __ilshift__(self, other):
+ self.__wrapped__ <<= other
+ return self
+
+ def __irshift__(self, other):
+ self.__wrapped__ >>= other
+ return self
+
+ def __iand__(self, other):
+ self.__wrapped__ &= other
+ return self
+
+ def __ixor__(self, other):
+ self.__wrapped__ ^= other
+ return self
+
+ def __ior__(self, other):
+ self.__wrapped__ |= other
+ return self
+
+ def __neg__(self):
+ return -self.__wrapped__
+
+ def __pos__(self):
+ return +self.__wrapped__
+
+ def __abs__(self):
+ return abs(self.__wrapped__)
+
+ def __invert__(self):
+ return ~self.__wrapped__
+
+ def __int__(self):
+ return int(self.__wrapped__)
+
+ def __long__(self):
+ return long(self.__wrapped__)
+
+ def __float__(self):
+ return float(self.__wrapped__)
+
+ def __complex__(self):
+ return complex(self.__wrapped__)
+
+ def __oct__(self):
+ return oct(self.__wrapped__)
+
+ def __hex__(self):
+ return hex(self.__wrapped__)
+
+ def __index__(self):
+ return operator.index(self.__wrapped__)
+
+ def __len__(self):
+ return len(self.__wrapped__)
+
+ def __contains__(self, value):
+ return value in self.__wrapped__
+
+ def __getitem__(self, key):
+ return self.__wrapped__[key]
+
+ def __setitem__(self, key, value):
+ self.__wrapped__[key] = value
+
+ def __delitem__(self, key):
+ del self.__wrapped__[key]
+
+ def __getslice__(self, i, j):
+ return self.__wrapped__[i:j]
+
+ def __setslice__(self, i, j, value):
+ self.__wrapped__[i:j] = value
+
+ def __delslice__(self, i, j):
+ del self.__wrapped__[i:j]
+
+ def __enter__(self):
+ return self.__wrapped__.__enter__()
+
+ def __exit__(self, *args, **kwargs):
+ return self.__wrapped__.__exit__(*args, **kwargs)
+
+ def __iter__(self):
+ return iter(self.__wrapped__)
+
+ def __copy__(self):
+ raise NotImplementedError('object proxy must define __copy__()')
+
+ def __deepcopy__(self, memo):
+ raise NotImplementedError('object proxy must define __deepcopy__()')
+
+ def __reduce__(self):
+ raise NotImplementedError(
+ 'object proxy must define __reduce__()')
+
+ def __reduce_ex__(self, protocol):
+ raise NotImplementedError(
+ 'object proxy must define __reduce_ex__()')
+
+class CallableObjectProxy(ObjectProxy):
+
+ def __call__(*args, **kwargs):
+ def _unpack_self(self, *args):
+ return self, args
+
+ self, args = _unpack_self(*args)
+
+ return self.__wrapped__(*args, **kwargs)
+
+class PartialCallableObjectProxy(ObjectProxy):
+
+ def __init__(*args, **kwargs):
+ def _unpack_self(self, *args):
+ return self, args
+
+ self, args = _unpack_self(*args)
+
+ if len(args) < 1:
+ raise TypeError('partial type takes at least one argument')
+
+ wrapped, args = args[0], args[1:]
+
+ if not callable(wrapped):
+ raise TypeError('the first argument must be callable')
+
+ super(PartialCallableObjectProxy, self).__init__(wrapped)
+
+ self._self_args = args
+ self._self_kwargs = kwargs
+
+ def __call__(*args, **kwargs):
+ def _unpack_self(self, *args):
+ return self, args
+
+ self, args = _unpack_self(*args)
+
+ _args = self._self_args + args
+
+ _kwargs = dict(self._self_kwargs)
+ _kwargs.update(kwargs)
+
+ return self.__wrapped__(*_args, **_kwargs)
+
+class _FunctionWrapperBase(ObjectProxy):
+
+ __slots__ = ('_self_instance', '_self_wrapper', '_self_enabled',
+ '_self_binding', '_self_parent', '_self_owner')
+
+ def __init__(self, wrapped, instance, wrapper, enabled=None,
+ binding='callable', parent=None, owner=None):
+
+ super(_FunctionWrapperBase, self).__init__(wrapped)
+
+ object.__setattr__(self, '_self_instance', instance)
+ object.__setattr__(self, '_self_wrapper', wrapper)
+ object.__setattr__(self, '_self_enabled', enabled)
+ object.__setattr__(self, '_self_binding', binding)
+ object.__setattr__(self, '_self_parent', parent)
+ object.__setattr__(self, '_self_owner', owner)
+
+ def __get__(self, instance, owner):
+ # This method is actually doing double duty for both unbound and bound
+ # derived wrapper classes. It should possibly be broken up and the
+ # distinct functionality moved into the derived classes. Can't do that
+ # straight away due to some legacy code which is relying on it being
+ # here in this base class.
+ #
+ # The distinguishing attribute which determines whether we are being
+ # called in an unbound or bound wrapper is the parent attribute. If
+ # binding has never occurred, then the parent will be None.
+ #
+ # First therefore, is if we are called in an unbound wrapper. In this
+ # case we perform the binding.
+ #
+ # We have two special cases to worry about here. These are where we are
+ # decorating a class or builtin function as neither provide a __get__()
+ # method to call. In this case we simply return self.
+ #
+ # Note that we otherwise still do binding even if instance is None and
+ # accessing an unbound instance method from a class. This is because we
+ # need to be able to later detect that specific case as we will need to
+ # extract the instance from the first argument of those passed in.
+
+ if self._self_parent is None:
+ # Technically can probably just check for existence of __get__ on
+ # the wrapped object, but this is more explicit.
+
+ if self._self_binding == 'builtin':
+ return self
+
+ if self._self_binding == "class":
+ return self
+
+ binder = getattr(self.__wrapped__, '__get__', None)
+
+ if binder is None:
+ return self
+
+ descriptor = binder(instance, owner)
+
+ return self.__bound_function_wrapper__(descriptor, instance,
+ self._self_wrapper, self._self_enabled,
+ self._self_binding, self, owner)
+
+ # Now we have the case of binding occurring a second time on what was
+ # already a bound function. In this case we would usually return
+ # ourselves again. This mirrors what Python does.
+ #
+ # The special case this time is where we were originally bound with an
+ # instance of None and we were likely an instance method. In that case
+ # we rebind against the original wrapped function from the parent again.
+
+ if self._self_instance is None and self._self_binding in ('function', 'instancemethod', 'callable'):
+ descriptor = self._self_parent.__wrapped__.__get__(
+ instance, owner)
+
+ return self._self_parent.__bound_function_wrapper__(
+ descriptor, instance, self._self_wrapper,
+ self._self_enabled, self._self_binding,
+ self._self_parent, owner)
+
+ return self
+
+ def __call__(*args, **kwargs):
+ def _unpack_self(self, *args):
+ return self, args
+
+ self, args = _unpack_self(*args)
+
+ # If enabled has been specified, then evaluate it at this point
+ # and if the wrapper is not to be executed, then simply return
+ # the bound function rather than a bound wrapper for the bound
+ # function. When evaluating enabled, if it is callable we call
+ # it, otherwise we evaluate it as a boolean.
+
+ if self._self_enabled is not None:
+ if callable(self._self_enabled):
+ if not self._self_enabled():
+ return self.__wrapped__(*args, **kwargs)
+ elif not self._self_enabled:
+ return self.__wrapped__(*args, **kwargs)
+
+ # This can occur where initial function wrapper was applied to
+ # a function that was already bound to an instance. In that case
+ # we want to extract the instance from the function and use it.
+
+ if self._self_binding in ('function', 'instancemethod', 'classmethod', 'callable'):
+ if self._self_instance is None:
+ instance = getattr(self.__wrapped__, '__self__', None)
+ if instance is not None:
+ return self._self_wrapper(self.__wrapped__, instance,
+ args, kwargs)
+
+ # This is generally invoked when the wrapped function is being
+ # called as a normal function and is not bound to a class as an
+ # instance method. This is also invoked in the case where the
+ # wrapped function was a method, but this wrapper was in turn
+ # wrapped using the staticmethod decorator.
+
+ return self._self_wrapper(self.__wrapped__, self._self_instance,
+ args, kwargs)
+
+ def __set_name__(self, owner, name):
+ # This is a special method use to supply information to
+ # descriptors about what the name of variable in a class
+ # definition is. Not wanting to add this to ObjectProxy as not
+ # sure of broader implications of doing that. Thus restrict to
+ # FunctionWrapper used by decorators.
+
+ if hasattr(self.__wrapped__, "__set_name__"):
+ self.__wrapped__.__set_name__(owner, name)
+
+ def __instancecheck__(self, instance):
+ # This is a special method used by isinstance() to make checks
+ # instance of the `__wrapped__`.
+ return isinstance(instance, self.__wrapped__)
+
+ def __subclasscheck__(self, subclass):
+ # This is a special method used by issubclass() to make checks
+ # about inheritance of classes. We need to upwrap any object
+ # proxy. Not wanting to add this to ObjectProxy as not sure of
+ # broader implications of doing that. Thus restrict to
+ # FunctionWrapper used by decorators.
+
+ if hasattr(subclass, "__wrapped__"):
+ return issubclass(subclass.__wrapped__, self.__wrapped__)
+ else:
+ return issubclass(subclass, self.__wrapped__)
+
+class BoundFunctionWrapper(_FunctionWrapperBase):
+
+ def __call__(*args, **kwargs):
+ def _unpack_self(self, *args):
+ return self, args
+
+ self, args = _unpack_self(*args)
+
+ # If enabled has been specified, then evaluate it at this point and if
+ # the wrapper is not to be executed, then simply return the bound
+ # function rather than a bound wrapper for the bound function. When
+ # evaluating enabled, if it is callable we call it, otherwise we
+ # evaluate it as a boolean.
+
+ if self._self_enabled is not None:
+ if callable(self._self_enabled):
+ if not self._self_enabled():
+ return self.__wrapped__(*args, **kwargs)
+ elif not self._self_enabled:
+ return self.__wrapped__(*args, **kwargs)
+
+ # We need to do things different depending on whether we are likely
+ # wrapping an instance method vs a static method or class method.
+
+ if self._self_binding == 'function':
+ if self._self_instance is None and args:
+ instance, newargs = args[0], args[1:]
+ if isinstance(instance, self._self_owner):
+ wrapped = PartialCallableObjectProxy(self.__wrapped__, instance)
+ return self._self_wrapper(wrapped, instance, newargs, kwargs)
+
+ return self._self_wrapper(self.__wrapped__, self._self_instance,
+ args, kwargs)
+
+ elif self._self_binding == 'callable':
+ if self._self_instance is None:
+ # This situation can occur where someone is calling the
+ # instancemethod via the class type and passing the instance as
+ # the first argument. We need to shift the args before making
+ # the call to the wrapper and effectively bind the instance to
+ # the wrapped function using a partial so the wrapper doesn't
+ # see anything as being different.
+
+ if not args:
+ raise TypeError('missing 1 required positional argument')
+
+ instance, args = args[0], args[1:]
+ wrapped = PartialCallableObjectProxy(self.__wrapped__, instance)
+ return self._self_wrapper(wrapped, instance, args, kwargs)
+
+ return self._self_wrapper(self.__wrapped__, self._self_instance,
+ args, kwargs)
+
+ else:
+ # As in this case we would be dealing with a classmethod or
+ # staticmethod, then _self_instance will only tell us whether
+ # when calling the classmethod or staticmethod they did it via an
+ # instance of the class it is bound to and not the case where
+ # done by the class type itself. We thus ignore _self_instance
+ # and use the __self__ attribute of the bound function instead.
+ # For a classmethod, this means instance will be the class type
+ # and for a staticmethod it will be None. This is probably the
+ # more useful thing we can pass through even though we loose
+ # knowledge of whether they were called on the instance vs the
+ # class type, as it reflects what they have available in the
+ # decoratored function.
+
+ instance = getattr(self.__wrapped__, '__self__', None)
+
+ return self._self_wrapper(self.__wrapped__, instance, args,
+ kwargs)
+
+class FunctionWrapper(_FunctionWrapperBase):
+
+ __bound_function_wrapper__ = BoundFunctionWrapper
+
+ def __init__(self, wrapped, wrapper, enabled=None):
+ # What it is we are wrapping here could be anything. We need to
+ # try and detect specific cases though. In particular, we need
+ # to detect when we are given something that is a method of a
+ # class. Further, we need to know when it is likely an instance
+ # method, as opposed to a class or static method. This can
+ # become problematic though as there isn't strictly a fool proof
+ # method of knowing.
+ #
+ # The situations we could encounter when wrapping a method are:
+ #
+ # 1. The wrapper is being applied as part of a decorator which
+ # is a part of the class definition. In this case what we are
+ # given is the raw unbound function, classmethod or staticmethod
+ # wrapper objects.
+ #
+ # The problem here is that we will not know we are being applied
+ # in the context of the class being set up. This becomes
+ # important later for the case of an instance method, because in
+ # that case we just see it as a raw function and can't
+ # distinguish it from wrapping a normal function outside of
+ # a class context.
+ #
+ # 2. The wrapper is being applied when performing monkey
+ # patching of the class type afterwards and the method to be
+ # wrapped was retrieved direct from the __dict__ of the class
+ # type. This is effectively the same as (1) above.
+ #
+ # 3. The wrapper is being applied when performing monkey
+ # patching of the class type afterwards and the method to be
+ # wrapped was retrieved from the class type. In this case
+ # binding will have been performed where the instance against
+ # which the method is bound will be None at that point.
+ #
+ # This case is a problem because we can no longer tell if the
+ # method was a static method, plus if using Python3, we cannot
+ # tell if it was an instance method as the concept of an
+ # unnbound method no longer exists.
+ #
+ # 4. The wrapper is being applied when performing monkey
+ # patching of an instance of a class. In this case binding will
+ # have been perfomed where the instance was not None.
+ #
+ # This case is a problem because we can no longer tell if the
+ # method was a static method.
+ #
+ # Overall, the best we can do is look at the original type of the
+ # object which was wrapped prior to any binding being done and
+ # see if it is an instance of classmethod or staticmethod. In
+ # the case where other decorators are between us and them, if
+ # they do not propagate the __class__ attribute so that the
+ # isinstance() checks works, then likely this will do the wrong
+ # thing where classmethod and staticmethod are used.
+ #
+ # Since it is likely to be very rare that anyone even puts
+ # decorators around classmethod and staticmethod, likelihood of
+ # that being an issue is very small, so we accept it and suggest
+ # that those other decorators be fixed. It is also only an issue
+ # if a decorator wants to actually do things with the arguments.
+ #
+ # As to not being able to identify static methods properly, we
+ # just hope that that isn't something people are going to want
+ # to wrap, or if they do suggest they do it the correct way by
+ # ensuring that it is decorated in the class definition itself,
+ # or patch it in the __dict__ of the class type.
+ #
+ # So to get the best outcome we can, whenever we aren't sure what
+ # it is, we label it as a 'callable'. If it was already bound and
+ # that is rebound later, we assume that it will be an instance
+ # method and try and cope with the possibility that the 'self'
+ # argument it being passed as an explicit argument and shuffle
+ # the arguments around to extract 'self' for use as the instance.
+
+ binding = None
+
+ if isinstance(wrapped, _FunctionWrapperBase):
+ binding = wrapped._self_binding
+
+ if not binding:
+ if inspect.isbuiltin(wrapped):
+ binding = 'builtin'
+
+ elif inspect.isfunction(wrapped):
+ binding = 'function'
+
+ elif inspect.isclass(wrapped):
+ binding = 'class'
+
+ elif isinstance(wrapped, classmethod):
+ binding = 'classmethod'
+
+ elif isinstance(wrapped, staticmethod):
+ binding = 'staticmethod'
+
+ elif hasattr(wrapped, '__self__'):
+ if inspect.isclass(wrapped.__self__):
+ binding = 'classmethod'
+ elif inspect.ismethod(wrapped):
+ binding = 'instancemethod'
+ else:
+ binding = 'callable'
+
+ else:
+ binding = 'callable'
+
+ super(FunctionWrapper, self).__init__(wrapped, None, wrapper,
+ enabled, binding)
diff --git a/.venv/lib/python3.11/site-packages/zipp/_functools.py b/.venv/lib/python3.11/site-packages/zipp/_functools.py
new file mode 100644
index 0000000000000000000000000000000000000000..f75ae2b0b439147179169dd85ab54021c2bbe084
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/zipp/_functools.py
@@ -0,0 +1,20 @@
+import collections
+import functools
+
+
+# from jaraco.functools 4.0.2
+def save_method_args(method):
+ """
+ Wrap a method such that when it is called, the args and kwargs are
+ saved on the method.
+ """
+ args_and_kwargs = collections.namedtuple('args_and_kwargs', 'args kwargs')
+
+ @functools.wraps(method)
+ def wrapper(self, /, *args, **kwargs):
+ attr_name = '_saved_' + method.__name__
+ attr = args_and_kwargs(args, kwargs)
+ setattr(self, attr_name, attr)
+ return method(self, *args, **kwargs)
+
+ return wrapper
diff --git a/.venv/lib/python3.11/site-packages/zipp/glob.py b/.venv/lib/python3.11/site-packages/zipp/glob.py
new file mode 100644
index 0000000000000000000000000000000000000000..4ed74cc48d9a91689280608f954e52150f5536f5
--- /dev/null
+++ b/.venv/lib/python3.11/site-packages/zipp/glob.py
@@ -0,0 +1,113 @@
+import os
+import re
+
+_default_seps = os.sep + str(os.altsep) * bool(os.altsep)
+
+
+class Translator:
+ """
+ >>> Translator('xyz')
+ Traceback (most recent call last):
+ ...
+ AssertionError: Invalid separators
+
+ >>> Translator('')
+ Traceback (most recent call last):
+ ...
+ AssertionError: Invalid separators
+ """
+
+ seps: str
+
+ def __init__(self, seps: str = _default_seps):
+ assert seps and set(seps) <= set(_default_seps), "Invalid separators"
+ self.seps = seps
+
+ def translate(self, pattern):
+ """
+ Given a glob pattern, produce a regex that matches it.
+ """
+ return self.extend(self.match_dirs(self.translate_core(pattern)))
+
+ def extend(self, pattern):
+ r"""
+ Extend regex for pattern-wide concerns.
+
+ Apply '(?s:)' to create a non-matching group that
+ matches newlines (valid on Unix).
+
+ Append '\Z' to imply fullmatch even when match is used.
+ """
+ return rf'(?s:{pattern})\Z'
+
+ def match_dirs(self, pattern):
+ """
+ Ensure that zipfile.Path directory names are matched.
+
+ zipfile.Path directory names always end in a slash.
+ """
+ return rf'{pattern}[/]?'
+
+ def translate_core(self, pattern):
+ r"""
+ Given a glob pattern, produce a regex that matches it.
+
+ >>> t = Translator()
+ >>> t.translate_core('*.txt').replace('\\\\', '')
+ '[^/]*\\.txt'
+ >>> t.translate_core('a?txt')
+ 'a[^/]txt'
+ >>> t.translate_core('**/*').replace('\\\\', '')
+ '.*/[^/][^/]*'
+ """
+ self.restrict_rglob(pattern)
+ return ''.join(map(self.replace, separate(self.star_not_empty(pattern))))
+
+ def replace(self, match):
+ """
+ Perform the replacements for a match from :func:`separate`.
+ """
+ return match.group('set') or (
+ re.escape(match.group(0))
+ .replace('\\*\\*', r'.*')
+ .replace('\\*', rf'[^{re.escape(self.seps)}]*')
+ .replace('\\?', r'[^/]')
+ )
+
+ def restrict_rglob(self, pattern):
+ """
+ Raise ValueError if ** appears in anything but a full path segment.
+
+ >>> Translator().translate('**foo')
+ Traceback (most recent call last):
+ ...
+ ValueError: ** must appear alone in a path segment
+ """
+ seps_pattern = rf'[{re.escape(self.seps)}]+'
+ segments = re.split(seps_pattern, pattern)
+ if any('**' in segment and segment != '**' for segment in segments):
+ raise ValueError("** must appear alone in a path segment")
+
+ def star_not_empty(self, pattern):
+ """
+ Ensure that * will not match an empty segment.
+ """
+
+ def handle_segment(match):
+ segment = match.group(0)
+ return '?*' if segment == '*' else segment
+
+ not_seps_pattern = rf'[^{re.escape(self.seps)}]+'
+ return re.sub(not_seps_pattern, handle_segment, pattern)
+
+
+def separate(pattern):
+ """
+ Separate out character sets to avoid translating their contents.
+
+ >>> [m.group(0) for m in separate('*.txt')]
+ ['*.txt']
+ >>> [m.group(0) for m in separate('a[?]txt')]
+ ['a', '[?]', 'txt']
+ """
+ return re.finditer(r'([^\[]+)|(?P[\[].*?[\]])|([\[][^\]]*$)', pattern)