Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type Annotations for Before Hook #2234

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions falcon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,60 @@
# need to log something that we can't communicate any other way.
_logger = _logging.getLogger('falcon')
_logger.addHandler(_logging.NullHandler())


__all__ = (
'API',
'App',
'after',
'before',
'HTTPError',
'HTTPStatus',
'CORSMiddleware',
'HTTPFound',
'HTTPMovedPermanently',
'HTTPPermanentRedirect',
'HTTPSeeOther',
'HTTPTemporaryRedirect',
'Forwarded',
'Request',
'RequestOptions',
'Response',
'ResponseOptions',
'BoundedStream',
'async_to_sync',
'BufferedReader',
'CaseInsensitiveDict',
'code_to_http_status',
'Context',
'create_task',
'deprecated',
'dt_to_http',
'ETag',
'get_argnames',
'get_bound_method',
'get_http_status',
'get_running_loop',
'http_cookies',
'http_date_to_dt',
'http_now',
'http_status_to_code',
'IS_64_BITS',
'is_python_func',
'misc',
'parse_header',
'reader',
'runs_sync',
'secure_filename',
'structures',
'sync',
'sync_to_async',
'sys',
'time',
'TimezoneGMT',
'to_query_str',
'uri',
'wrap_sync_to_async',
'wrap_sync_to_async_unsafe',
'__version__',
)
22 changes: 18 additions & 4 deletions falcon/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,33 @@
"""Hook decorators."""

from functools import wraps
from inspect import getmembers
from inspect import iscoroutinefunction
from inspect import getmembers, iscoroutinefunction
import re

import typing_extensions as te

from falcon.constants import COMBINED_METHODS
from falcon.request import Request
from falcon.response import Response
from falcon.util.misc import get_argnames
from falcon.util.sync import _wrap_non_coroutine_unsafe


_DECORABLE_METHOD_NAME = re.compile(
r'^on_({})(_\w+)?$'.format('|'.join(method.lower() for method in COMBINED_METHODS))
)


def before(action, *args, is_async=False, **kwargs):
P = te.ParamSpec('P')
R = te.TypeVar('R', bound=Request)
S = te.TypeVar('S', bound=Response)


def before(
action: te.Callable[te.Concatenate[R, S, te.Any, te.Dict[str, te.Any], P], None],
*args: P.args,
is_async: bool = False, # type: ignore
**kwargs: P.kwargs,
) -> te.Callable[[te.Any], te.Any]:
"""Execute the given action function *before* the responder.

The `params` argument that is passed to the hook
Expand Down Expand Up @@ -79,6 +91,8 @@ def do_something(req, resp, resource, params):
*action*.
"""

is_async = kwargs.get('is_async', False)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we keep is_async: bool = False, listed explicitly this line is not needed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to deprecate is_async now that Cython 3.0 properly marks coroutine functions if run on Python 3.10+. I will create a separate issue for this.

Copy link
Author

@nZac nZac Jun 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can drop is_async this PR becomes possible. After I pushed this branch I continued my testing and realized this wont work the way I thought it would. I'm not seeing a way to actually type before and after methods in their current state.

I could be missing something though 🤷🏼‍♂️


def _before(responder_or_resource):
if isinstance(responder_or_resource, type):
resource = responder_or_resource
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"setuptools>=47",
"wheel>=0.34",
"cython>=0.29.21; python_implementation == 'CPython'", # Skip cython when using pypy
"typing-extensions>=4"
]

[tool.mypy]
Expand Down
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ project_urls =
zip_safe = False
include_package_data = True
packages = find:
python_requires = >=3.7
python_requires = >=3.8
install_requires =
typing_extensions;python_version<'3.10'
tests_require =
testtools
requests
Expand Down
Loading