include stubs for python-multipart
This commit is contained in:
parent
e6a119c871
commit
a8e7813318
0
basgi/py.typed
Normal file
0
basgi/py.typed
Normal file
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
import typing
|
||||
|
||||
from aputils import JsonBase
|
||||
from blib import random_str
|
||||
from collections.abc import Iterable
|
||||
from multidict import CIMultiDict, CIMultiDictProxy
|
||||
from multipart.multipart import Field, File, create_form_parser
|
||||
|
@ -122,7 +123,12 @@ class Request:
|
|||
|
||||
else:
|
||||
field.file_object.seek(0)
|
||||
fields[field.field_name.decode("utf-8")] = field
|
||||
|
||||
if field.field_name is None:
|
||||
fields[f"unnamed-file-{random_str(10)}"] = field
|
||||
|
||||
else:
|
||||
fields[field.field_name.decode("utf-8")] = field
|
||||
|
||||
parser = create_form_parser(self.headers, handle_field, handle_field)
|
||||
|
||||
|
|
|
@ -44,25 +44,36 @@ class Runner(ABC):
|
|||
|
||||
@abstractmethod
|
||||
def setup_module(self) -> None:
|
||||
"Import all necessary modules here and add them as attributes to the runner"
|
||||
...
|
||||
|
||||
|
||||
@abstractmethod
|
||||
def reload(self) -> None:
|
||||
"Start the runner with reloading enabled"
|
||||
...
|
||||
|
||||
|
||||
@abstractmethod
|
||||
def simple(self) -> None:
|
||||
"Start the runner without workers"
|
||||
...
|
||||
|
||||
|
||||
@abstractmethod
|
||||
def multiprocess(self) -> None:
|
||||
"Start the runner with multiple process workers"
|
||||
...
|
||||
|
||||
|
||||
class UvicornRunner(Runner):
|
||||
"""
|
||||
Start an ASGI application with Uvicorn
|
||||
|
||||
.. note:: Do not start this runner in the same process as the application unless it is
|
||||
started with the simple runner.
|
||||
"""
|
||||
|
||||
def setup_module(self) -> None:
|
||||
self.uvicorn = import_module("uvicorn")
|
||||
self.supervisors = import_module("uvicorn.supervisors")
|
||||
|
|
|
@ -55,7 +55,7 @@ class SassExtension(Extension):
|
|||
if (ext := splitext(tpl_name)[1]) not in self._exts:
|
||||
return source
|
||||
|
||||
return sass.compile( # type: ignore[no-any-return]
|
||||
return sass.compile(
|
||||
string = source,
|
||||
output_style = self.output_style.value,
|
||||
indented = ext == ".sass"
|
||||
|
|
9
dev.py
9
dev.py
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
import asyncio
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
|
@ -90,6 +91,7 @@ def cli_lint(path: Path, watch: bool) -> None:
|
|||
run_python("-m", "flake8", str(path))
|
||||
|
||||
echo("\n----- mypy -----")
|
||||
os.environ["MYPYPATH"] = str(REPO.joinpath("stubs"))
|
||||
run_python("-m", "mypy", str(path))
|
||||
|
||||
|
||||
|
@ -120,6 +122,13 @@ def cli_update_files() -> None:
|
|||
project_file.write(project)
|
||||
|
||||
|
||||
@cli.command("generate-stubs")
|
||||
def cli_generate_stubs():
|
||||
subprocess.run(
|
||||
["-m", "mypy.stubgen", "-o", "stubs", "-p", "multipart", "--export-less"]
|
||||
)
|
||||
|
||||
|
||||
def run_python(*arguments: str) -> subprocess.CompletedProcess[bytes]:
|
||||
return subprocess.run([sys.executable, *arguments])
|
||||
|
||||
|
|
|
@ -27,9 +27,9 @@ HTTP Messages
|
|||
Application Runners
|
||||
-------------------
|
||||
|
||||
:meth:`basgi.run_app`
|
||||
:class:`basgi.GranianRunner`
|
||||
|
||||
:meth:`basgi.handle_run_app`
|
||||
:class:`basgi.UvicornRunner`
|
||||
|
||||
|
||||
Templates
|
||||
|
|
|
@ -8,7 +8,9 @@ App Runners
|
|||
.. autoclass:: basgi.GranianRunner
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:exclude-members: setup_module
|
||||
|
||||
.. autoclass:: basgi.UvicornRunner
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:exclude-members: setup_module
|
||||
|
|
|
@ -69,7 +69,7 @@ dev = [
|
|||
"mypy == 1.9.0",
|
||||
"flake8 == 7.0.0",
|
||||
"pyinstaller == 6.5.0",
|
||||
"types-jinja2 == 2.11.9",
|
||||
"types-libsass == 0.23.0.20240311",
|
||||
"watchfiles == 0.21.0"
|
||||
]
|
||||
|
||||
|
|
3
stubs/multipart/__init__.pyi
Normal file
3
stubs/multipart/__init__.pyi
Normal file
|
@ -0,0 +1,3 @@
|
|||
from .multipart import FormParser as FormParser, MultipartParser as MultipartParser, OctetStreamParser as OctetStreamParser, QuerystringParser as QuerystringParser, create_form_parser as create_form_parser, parse_form as parse_form
|
||||
|
||||
__all__ = ['FormParser', 'MultipartParser', 'OctetStreamParser', 'QuerystringParser', 'create_form_parser', 'parse_form']
|
20
stubs/multipart/decoders.pyi
Normal file
20
stubs/multipart/decoders.pyi
Normal file
|
@ -0,0 +1,20 @@
|
|||
import typing
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from .multipart import Field, File
|
||||
|
||||
class Base64Decoder:
|
||||
cache: bytearray
|
||||
underlying: Field | File
|
||||
def __init__(self, underlying: Field | File) -> None: ...
|
||||
def write(self, data) -> int: ...
|
||||
def close(self) -> None: ...
|
||||
def finalize(self) -> None: ...
|
||||
|
||||
class QuotedPrintableDecoder:
|
||||
cache: bytes
|
||||
underlying: Field | File
|
||||
def __init__(self, underlying: Field | File) -> None: ...
|
||||
def write(self, data) -> int: ...
|
||||
def close(self) -> None: ...
|
||||
def finalize(self) -> None: ...
|
9
stubs/multipart/exceptions.pyi
Normal file
9
stubs/multipart/exceptions.pyi
Normal file
|
@ -0,0 +1,9 @@
|
|||
class FormParserError(ValueError): ...
|
||||
|
||||
class ParseError(FormParserError):
|
||||
offset: int
|
||||
|
||||
class MultipartParseError(ParseError): ...
|
||||
class QuerystringParseError(ParseError): ...
|
||||
class DecodeError(ParseError): ...
|
||||
class FileError(FormParserError, OSError): ...
|
184
stubs/multipart/multipart.pyi
Normal file
184
stubs/multipart/multipart.pyi
Normal file
|
@ -0,0 +1,184 @@
|
|||
import io
|
||||
import logging
|
||||
|
||||
from collections.abc import Mapping, Sequence
|
||||
from enum import IntEnum
|
||||
from typing import Any, Callable, TypedDict
|
||||
|
||||
|
||||
ParserCallback = Callable[[Any, int, int], None] | Callable[..., None]
|
||||
|
||||
class QuerystringCallbacks(TypedDict, total=False):
|
||||
on_field_start: Callable[[], None]
|
||||
on_field_name: Callable[[bytes, int, int], None]
|
||||
on_field_data: Callable[[bytes, int, int], None]
|
||||
on_field_end: Callable[[], None]
|
||||
on_end: Callable[[], None]
|
||||
|
||||
class OctetStreamCallbacks(TypedDict, total=False):
|
||||
on_start: Callable[[], None]
|
||||
on_data: Callable[[bytes, int, int], None]
|
||||
on_end: Callable[[], None]
|
||||
|
||||
class MultipartCallbacks(TypedDict, total=False):
|
||||
on_part_begin: Callable[[], None]
|
||||
on_part_data: Callable[[bytes, int, int], None]
|
||||
on_part_end: Callable[[], None]
|
||||
on_headers_begin: Callable[[], None]
|
||||
on_header_field: Callable[[bytes, int, int], None]
|
||||
on_header_value: Callable[[bytes, int, int], None]
|
||||
on_header_end: Callable[[], None]
|
||||
on_headers_finished: Callable[[], None]
|
||||
on_end: Callable[[], None]
|
||||
|
||||
class FormParserConfig(TypedDict, total=False):
|
||||
UPLOAD_DIR: str | None
|
||||
UPLOAD_KEEP_FILENAME: bool
|
||||
UPLOAD_KEEP_EXTENSIONS: bool
|
||||
UPLOAD_ERROR_ON_BAD_CTE: bool
|
||||
MAX_MEMORY_FILE_SIZE: int
|
||||
MAX_BODY_SIZE: float
|
||||
|
||||
class FileConfig(TypedDict, total=False):
|
||||
UPLOAD_DIR: str | None
|
||||
UPLOAD_DELETE_TMP: bool
|
||||
UPLOAD_KEEP_FILENAME: bool
|
||||
UPLOAD_KEEP_EXTENSIONS: bool
|
||||
MAX_MEMORY_FILE_SIZE: int
|
||||
|
||||
class QuerystringState(IntEnum):
|
||||
BEFORE_FIELD: int
|
||||
FIELD_NAME: int
|
||||
FIELD_DATA: int
|
||||
|
||||
class MultipartState(IntEnum):
|
||||
START: int
|
||||
START_BOUNDARY: int
|
||||
HEADER_FIELD_START: int
|
||||
HEADER_FIELD: int
|
||||
HEADER_VALUE_START: int
|
||||
HEADER_VALUE: int
|
||||
HEADER_VALUE_ALMOST_DONE: int
|
||||
HEADERS_ALMOST_DONE: int
|
||||
PART_DATA_START: int
|
||||
PART_DATA: int
|
||||
PART_DATA_END: int
|
||||
END: int
|
||||
|
||||
FLAG_PART_BOUNDARY: int
|
||||
FLAG_LAST_BOUNDARY: int
|
||||
CR: bytes
|
||||
LF: bytes
|
||||
COLON: bytes
|
||||
SPACE: bytes
|
||||
HYPHEN: bytes
|
||||
AMPERSAND: bytes
|
||||
SEMICOLON: bytes
|
||||
LOWER_A: bytes
|
||||
LOWER_Z: bytes
|
||||
NULL: bytes
|
||||
|
||||
def lower_char(c: bytes) -> bytes:...
|
||||
def ord_char(c: bytes) -> bytes: ...
|
||||
def join_bytes(b: Sequence[bytes]) -> bytes: ...
|
||||
def parse_options_header(value: str | bytes) -> tuple[bytes, dict[bytes, bytes]]: ...
|
||||
|
||||
class Field:
|
||||
def __init__(self, name: str) -> None: ...
|
||||
@classmethod
|
||||
def from_value(cls, name: str, value: bytes | None) -> Field: ...
|
||||
def write(self, data: bytes) -> int: ...
|
||||
def on_data(self, data: bytes) -> int: ...
|
||||
def on_end(self) -> None: ...
|
||||
def finalize(self) -> None: ...
|
||||
def close(self) -> None: ...
|
||||
def set_none(self) -> None: ...
|
||||
@property
|
||||
def field_name(self) -> bytes: ...
|
||||
@property
|
||||
def value(self) -> bytes: ...
|
||||
def __eq__(self, other: object) -> bool: ...
|
||||
|
||||
class File:
|
||||
logger: logging.Logger
|
||||
def __init__(self, file_name: bytes | None, field_name: bytes | None = None, config: FileConfig = {}) -> None: ...
|
||||
@property
|
||||
def field_name(self) -> bytes | None: ...
|
||||
@property
|
||||
def file_name(self) -> bytes | None: ...
|
||||
@property
|
||||
def actual_file_name(self) -> str | None: ...
|
||||
@property
|
||||
def file_object(self) -> io.BytesIO | io.TextIOWrapper | io.BufferedReader: ...
|
||||
@property
|
||||
def size(self) -> int: ...
|
||||
@property
|
||||
def in_memory(self) -> bool: ...
|
||||
def flush_to_disk(self) -> None: ...
|
||||
def write(self, data: bytes) -> int: ...
|
||||
def on_data(self, data: bytes) -> int: ...
|
||||
def on_end(self) -> None: ...
|
||||
def finalize(self) -> None: ...
|
||||
def close(self) -> None: ...
|
||||
|
||||
class BaseParser:
|
||||
logger: logging.Logger
|
||||
def __init__(self) -> None: ...
|
||||
def callback(self, name: str, data: Any | None = None, start: int | None = None, end: int | None = None) -> None: ...
|
||||
def set_callback(self, name: str, new_func: ParserCallback) -> None: ...
|
||||
def close(self) -> None: ...
|
||||
def finalize(self) -> None: ...
|
||||
|
||||
class OctetStreamParser(BaseParser):
|
||||
callbacks: OctetStreamCallbacks
|
||||
max_size: float
|
||||
def __init__(self, callbacks: OctetStreamCallbacks = {}, max_size: float = float("inf")) -> None: ...
|
||||
def write(self, data: bytes) -> int: ...
|
||||
def finalize(self) -> None: ...
|
||||
|
||||
class QuerystringParser(BaseParser):
|
||||
state: QuerystringState
|
||||
callbacks: QuerystringCallbacks
|
||||
max_size: float
|
||||
strict_parsing: bool
|
||||
def __init__(self, callbacks: QuerystringCallbacks = {}, strict_parsing: bool = False, max_size: float = float("inf")) -> None: ...
|
||||
def write(self, data: bytes) -> int: ...
|
||||
def finalize(self) -> None: ...
|
||||
|
||||
class MultipartParser(BaseParser):
|
||||
state: MultipartState
|
||||
index: int
|
||||
callbacks: MultipartCallbacks
|
||||
max_size: float
|
||||
marks: dict[str, int]
|
||||
boundary: bytes
|
||||
boundary_chars: frozenset[bytes]
|
||||
lookbehind: Sequence[bytes]
|
||||
def __init__(self, boundary: bytes | str, callbacks: MultipartCallbacks = {}, max_size: float = float("inf")) -> None: ...
|
||||
def write(self, data: bytes) -> int: ...
|
||||
def finalize(self) -> None: ...
|
||||
|
||||
OnFieldCallback = Callable[[Field], None]
|
||||
OnFileCallback = Callable[[File], None]
|
||||
OnEndCallback = Callable[..., Any | None]
|
||||
|
||||
class FormParser:
|
||||
DEFAULT_CONFIG: FormParserConfig
|
||||
logger: logging.Logger
|
||||
content_type: str
|
||||
boundary: bytes | None
|
||||
bytes_received: int
|
||||
parser: BaseParser
|
||||
on_field: OnFieldCallback
|
||||
on_file: OnFileCallback
|
||||
on_end: OnEndCallback | None
|
||||
FileClass: File
|
||||
FieldClass: Field
|
||||
config: FormParserConfig | None
|
||||
def __init__(self, content_type: str, on_field: OnFieldCallback, on_file: OnFileCallback, on_end: OnEndCallback | None = None, boundary: bytes | None = None, file_name: str | None = None, FileClass: type[File] = File, FieldClass: type[Field] = Field, config: FormParserConfig = {}) -> None: ...
|
||||
def write(self, data: bytes) -> int: ...
|
||||
def finalize(self) -> None: ...
|
||||
def close(self) -> None: ...
|
||||
|
||||
def create_form_parser(headers: Mapping[str, str], on_field: OnFieldCallback, on_file: OnFileCallback, trust_x_headers: bool = False, config: FormParserConfig = {}) -> FormParser: ...
|
||||
def parse_form(headers: Mapping[str, str], input_stream: io.BufferedIOBase, on_field: OnFieldCallback, on_file: OnFileCallback, chunk_size: int = 1048576, **kwargs: Any) -> None: ...
|
Loading…
Reference in a new issue