Skip to content

Commit

Permalink
types: fixed type errors reported by pyright
Browse files Browse the repository at this point in the history
  • Loading branch information
CaselIT committed Oct 18, 2024
1 parent 2aefca7 commit b444a26
Show file tree
Hide file tree
Showing 18 changed files with 131 additions and 137 deletions.
12 changes: 7 additions & 5 deletions falcon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
app = falcon.App()
"""

import logging as _logging

__all__ = (
# API interface
'API',
Expand Down Expand Up @@ -407,11 +409,6 @@
from falcon.hooks import before
from falcon.http_error import HTTPError
from falcon.http_status import HTTPStatus

# NOTE(jkmnt): Moved logger to leaf module to avoid possible circular imports.
# the _logging symbol is reexported too - maybe it was used by test or smth.
from falcon.logger import _logger
from falcon.logger import logging as _logging
from falcon.middleware import CORSMiddleware
from falcon.redirects import HTTPFound
from falcon.redirects import HTTPMovedPermanently
Expand Down Expand Up @@ -644,3 +641,8 @@

# Package version
from falcon.version import __version__ # NOQA: F401

# NOTE(kgriffs): Only to be used internally on the rare occasion that we
# need to log something that we can't communicate any other way.
_logger = _logging.getLogger('falcon')
_logger.addHandler(_logging.NullHandler())
8 changes: 2 additions & 6 deletions falcon/_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
Optional,
Pattern,
Protocol,
Sequence,
Tuple,
TYPE_CHECKING,
TypeVar,
Expand Down Expand Up @@ -105,9 +104,6 @@ async def __call__(
HeaderMapping = Mapping[str, str]
HeaderIter = Iterable[Tuple[str, str]]
HeaderArg = Union[HeaderMapping, HeaderIter]

NarrowHeaderArg = Union[Mapping[str, str], Sequence[Tuple[str, str]]]

ResponseStatus = Union[http.HTTPStatus, str, int]
StoreArg = Optional[Dict[str, Any]]
Resource = object
Expand Down Expand Up @@ -168,8 +164,8 @@ async def __call__(
AsgiProcessResponseMethod = Callable[
['AsgiRequest', 'AsgiResponse', Resource, bool], Awaitable[None]
]
AsgiProcessStartupMethod = Callable[[Dict[str, Any], 'AsgiEvent'], Awaitable[None]]
AsgiProcessShutdownMethod = Callable[[Dict[str, Any], 'AsgiEvent'], Awaitable[None]]
# AsgiProcessStartupMethod = Callable[[Dict[str, Any], 'AsgiEvent'], Awaitable[None]]
# AsgiProcessShutdownMethod = Callable[[Dict[str, Any], 'AsgiEvent'], Awaitable[None]]

AsgiProcessRequestWsMethod = Callable[['AsgiRequest', 'WebSocket'], Awaitable[None]]
AsgiProcessResourceWsMethod = Callable[
Expand Down
18 changes: 11 additions & 7 deletions falcon/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ def __call__( # noqa: C901
break

if not resp.complete:
responder(req, resp, **params)
responder(req, resp, **params) # pyright: ignore[reportPossiblyUnboundVariable]

req_succeeded = True
except Exception as ex:
Expand Down Expand Up @@ -1071,7 +1071,7 @@ def _get_responder(

if resource is not None:
try:
responder = method_map[method]
responder = method_map[method] # pyright: ignore[reportPossiblyUnboundVariable]
except KeyError:
# NOTE(kgriffs): Dirty hack! We use __class__ here to avoid
# binding self to the default responder method. We could
Expand All @@ -1094,7 +1094,7 @@ def _get_responder(
else:
responder = self.__class__._default_responder_path_not_found

return (responder, params, resource, uri_template)
return (responder, params, resource, uri_template) # pyright: ignore[reportPossiblyUnboundVariable]

def _compose_status_response(
self, req: Request, resp: Response, http_status: HTTPStatus
Expand Down Expand Up @@ -1258,17 +1258,21 @@ def _get_body(
self._STREAM_BLOCK_SIZE,
)
else:
iterable = cast(Iterable[bytes], stream)
iterable = stream

return iterable, None
return iterable, None # pyright: ignore[reportReturnType]

return [], 0

def _update_sink_and_static_routes(self) -> None:
if self._sink_before_static_route:
self._sink_and_static_routes = (*self._sinks, *self._static_routes)
self._sink_and_static_routes = tuple(self._sinks) + tuple(
self._static_routes
)
else:
self._sink_and_static_routes = (*self._static_routes, *self._sinks)
self._sink_and_static_routes = tuple(self._static_routes) + tuple(
self._sinks
)


# TODO(myusko): This class is a compatibility alias, and should be removed
Expand Down
25 changes: 6 additions & 19 deletions falcon/app_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@

from inspect import iscoroutinefunction
from typing import (
Any,
Awaitable,
Callable,
IO,
Iterable,
List,
Literal,
Expand All @@ -45,7 +43,6 @@
from falcon.errors import HTTPError
from falcon.request import Request
from falcon.response import Response
from falcon.typing import ReadableIO
from falcon.util.sync import _wrap_non_coroutine_unsafe

__all__ = (
Expand Down Expand Up @@ -379,7 +376,7 @@ class CloseableStreamIterator:
block_size (int): Number of bytes to read per iteration.
"""

def __init__(self, stream: ReadableIO, block_size: int) -> None:
def __init__(self, stream: IO[bytes], block_size: int) -> None:
self._stream = stream
self._block_size = block_size

Expand All @@ -395,17 +392,7 @@ def __next__(self) -> bytes:
return data

def close(self) -> None:
close_maybe(self._stream)


# TODO(jkmnt): Move these to some other module, they don't belong here
def close_maybe(stream: Any) -> None:
close: Optional[Callable[[], None]] = getattr(stream, 'close', None)
if close:
close()


async def async_close_maybe(stream: Any) -> None:
close: Optional[Callable[[], Awaitable[None]]] = getattr(stream, 'close', None)
if close:
await close()
try:
self._stream.close()
except (AttributeError, TypeError):
pass
45 changes: 17 additions & 28 deletions falcon/asgi/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,19 @@
Union,
)

from falcon import _logger
from falcon import constants
from falcon import responders
from falcon import routing
from falcon._typing import _UNSET
from falcon._typing import AsgiErrorHandler
from falcon._typing import AsgiProcessShutdownMethod
from falcon._typing import AsgiProcessStartupMethod
from falcon._typing import AsgiReceive
from falcon._typing import AsgiResponderCallable
from falcon._typing import AsgiResponderWsCallable
from falcon._typing import AsgiSend
from falcon._typing import AsgiSinkCallable
from falcon._typing import SinkPrefix
import falcon.app
from falcon.app_helpers import async_close_maybe
from falcon.app_helpers import AsyncPreparedMiddlewareResult
from falcon.app_helpers import AsyncPreparedMiddlewareWsResult
from falcon.app_helpers import prepare_middleware
Expand All @@ -66,7 +64,6 @@
from falcon.errors import WebSocketDisconnected
from falcon.http_error import HTTPError
from falcon.http_status import HTTPStatus
from falcon.logger import _logger
from falcon.media.multipart import MultipartFormHandler
from falcon.util import get_argnames
from falcon.util.misc import is_python_func
Expand Down Expand Up @@ -343,10 +340,10 @@ async def process_resource_ws(
# without having to import falcon.asgi.
_ASGI: ClassVar[bool] = True

_default_responder_bad_request: ClassVar[AsgiResponderCallable] = (
_default_responder_bad_request: ClassVar[AsgiResponderCallable] = ( # pyright: ignore[reportIncompatibleVariableOverride]# noqa: E501
responders.bad_request_async # type: ignore[assignment]
)
_default_responder_path_not_found: ClassVar[AsgiResponderCallable] = (
_default_responder_path_not_found: ClassVar[AsgiResponderCallable] = ( # pyright: ignore[reportIncompatibleVariableOverride] # noqa: E501
responders.path_not_found_async # type: ignore[assignment]
)

Expand All @@ -359,8 +356,8 @@ async def process_resource_ws(
_error_handlers: Dict[Type[BaseException], AsgiErrorHandler] # type: ignore[assignment]
_middleware: AsyncPreparedMiddlewareResult # type: ignore[assignment]
_middleware_ws: AsyncPreparedMiddlewareWsResult
_request_type: Type[Request]
_response_type: Type[Response]
_request_type: Type[Request] # pyright: ignore[reportIncompatibleVariableOverride]
_response_type: Type[Response] # pyright: ignore[reportIncompatibleVariableOverride]

ws_options: WebSocketOptions
"""A set of behavioral options related to WebSocket connections.
Expand Down Expand Up @@ -526,7 +523,7 @@ async def __call__( # type: ignore[override] # noqa: C901
break

if not resp.complete:
await responder(req, resp, **params)
await responder(req, resp, **params) # pyright: ignore[reportPossiblyUnboundVariable]

req_succeeded = True

Expand Down Expand Up @@ -773,13 +770,10 @@ async def watch_disconnect() -> None:
# (c) async iterator
#

read_meth: Optional[Callable[[int], Awaitable[bytes]]] = getattr(
stream, 'read', None
)
if read_meth:
if hasattr(stream, 'read'):
try:
while True:
data = await read_meth(self._STREAM_BLOCK_SIZE)
data = await stream.read(self._STREAM_BLOCK_SIZE) # pyright: ignore[reportAttributeAccessIssue]
if data == b'':
break
else:
Expand All @@ -793,7 +787,8 @@ async def watch_disconnect() -> None:
}
)
finally:
await async_close_maybe(stream)
if hasattr(stream, 'close'):
await stream.close() # pyright: ignore[reportAttributeAccessIssue]
else:
# NOTE(kgriffs): Works for both async generators and iterators
try:
Expand Down Expand Up @@ -832,7 +827,8 @@ async def watch_disconnect() -> None:
# NOTE(vytas): This could be DRYed with the above identical
# twoliner in a one large block, but OTOH we would be
# unable to reuse the current try.. except.
await async_close_maybe(stream)
if hasattr(stream, 'close'):
await stream.close() # pyright: ignore

await send(_EVT_RESP_EOF)

Expand Down Expand Up @@ -1009,8 +1005,7 @@ async def handle(req, resp, ex, params):
'The handler must be an awaitable coroutine function in order '
'to be used safely with an ASGI app.'
)
assert handler
handler_callable: AsgiErrorHandler = handler
handler_callable: AsgiErrorHandler = handler # pyright: ignore[reportAssignmentType]

exception_tuple: Tuple[type[BaseException], ...]
try:
Expand Down Expand Up @@ -1082,12 +1077,9 @@ async def _call_lifespan_handlers(
return

for handler in self._unprepared_middleware:
process_startup: Optional[AsgiProcessStartupMethod] = getattr(
handler, 'process_startup', None
)
if process_startup:
if hasattr(handler, 'process_startup'):
try:
await process_startup(scope, event)
await handler.process_startup(scope, event) # pyright: ignore[reportAttributeAccessIssue]
except Exception:
await send(
{
Expand All @@ -1101,12 +1093,9 @@ async def _call_lifespan_handlers(

elif event['type'] == 'lifespan.shutdown':
for handler in reversed(self._unprepared_middleware):
process_shutdown: Optional[AsgiProcessShutdownMethod] = getattr(
handler, 'process_shutdown', None
)
if process_shutdown:
if hasattr(handler, 'process_shutdown'):
try:
await process_shutdown(scope, event)
await handler.process_shutdown(scope, event) # pyright: ignore[reportAttributeAccessIssue]
except Exception:
await send(
{
Expand Down
4 changes: 2 additions & 2 deletions falcon/asgi/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ async def emitter():
def sse(self, value: Optional[SSEEmitter]) -> None:
self._sse = value

def set_stream(
def set_stream( # pyright: ignore[reportIncompatibleMethodOverride]
self,
stream: Union[AsyncReadableIO, AsyncIterator[bytes]], # type: ignore[override]
content_length: int,
Expand Down Expand Up @@ -175,7 +175,7 @@ def set_stream(
Content-Length header in the response.
"""

self.stream = stream
self.stream = stream # pyright: ignore[reportIncompatibleVariableOverride]

# PERF(kgriffs): Set directly rather than incur the overhead of
# the self.content_length property.
Expand Down
2 changes: 1 addition & 1 deletion falcon/asgi/ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ class WebSocketOptions:

@classmethod
def _init_default_close_reasons(cls) -> Dict[int, str]:
reasons: dict[int, str] = dict(cls._STANDARD_CLOSE_REASONS)
reasons = dict(cls._STANDARD_CLOSE_REASONS)
for status_constant in dir(status_codes):
if 'HTTP_100' <= status_constant < 'HTTP_599':
status_line = getattr(status_codes, status_constant)
Expand Down
Loading

0 comments on commit b444a26

Please sign in to comment.