Skip to content

Commit

Permalink
Move checking types of endpoints from __call__ -> handle_http() to …
Browse files Browse the repository at this point in the history
…`__init__ -> load_configs()`
  • Loading branch information
AliRn76 committed Aug 22, 2024
1 parent cb2de17 commit ab5ebe6
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 22 deletions.
2 changes: 1 addition & 1 deletion panther/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from panther.main import Panther # noqa: F401

Check warning on line 1 in panther/__init__.py

View workflow job for this annotation

GitHub Actions / Qodana for Python

Unsatisfied package requirements

Package requirements 'python-jose', 'faker', 'coverage', 'pytest', 'cryptography\~=42.0', 'websockets', 'pymongo' are not satisfied

__version__ = '4.3.0'
__version__ = '4.3.1'


def version():
Expand Down
13 changes: 12 additions & 1 deletion panther/_load_configs.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import logging

Check warning on line 1 in panther/_load_configs.py

View workflow job for this annotation

GitHub Actions / Qodana for Python

Unsatisfied package requirements

Package requirements 'python-jose', 'faker', 'coverage', 'pytest', 'cryptography\~=42.0', 'websockets', 'pymongo' are not satisfied
import sys
import types
from importlib import import_module
from multiprocessing import Manager

from panther._utils import import_class
from panther._utils import import_class, check_function_type_endpoint, check_class_type_endpoint
from panther.background_tasks import background_tasks
from panther.base_websocket import WebsocketConnections
from panther.cli.utils import import_error
Expand Down Expand Up @@ -36,6 +37,7 @@
'load_authentication_class',
'load_urls',
'load_websocket_connections',
'check_endpoints_inheritance',
)

logger = logging.getLogger('panther')
Expand Down Expand Up @@ -265,5 +267,14 @@ def load_websocket_connections():
config.WEBSOCKET_CONNECTIONS = WebsocketConnections(pubsub_connection=pubsub_connection)


def check_endpoints_inheritance():
"""Should be after `load_urls()`"""
for _, endpoint in config.FLAT_URLS.items():
if isinstance(endpoint, types.FunctionType):
check_function_type_endpoint(endpoint=endpoint)
else:
check_class_type_endpoint(endpoint=endpoint)


def _exception_handler(field: str, error: str | Exception) -> PantherError:
return PantherError(f"Invalid '{field}': {error}")
18 changes: 9 additions & 9 deletions panther/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
import re
import subprocess
import types
from typing import Any, Generator, Iterator, AsyncGenerator
from collections.abc import Callable
from traceback import TracebackException
from typing import Any, Generator, Iterator, AsyncGenerator

from panther.exceptions import PantherError
from panther.file_handler import File
from panther.websocket import GenericWebsocket

logger = logging.getLogger('panther')

Expand Down Expand Up @@ -99,19 +100,18 @@ def reformat_code(base_dir):
def check_function_type_endpoint(endpoint: types.FunctionType) -> Callable:

Check warning on line 100 in panther/_utils.py

View workflow job for this annotation

GitHub Actions / Qodana for Python

Incorrect type

Expected to return '(...) -\> Any', got no return
# Function Doesn't Have @API Decorator
if not hasattr(endpoint, '__wrapped__'):
logger.critical(f'You may have forgotten to use @API() on the {endpoint.__name__}()')
raise TypeError
return endpoint
raise PantherError(
f'You may have forgotten to use `@API()` on the `{endpoint.__module__}.{endpoint.__name__}()`')


def check_class_type_endpoint(endpoint: Callable) -> Callable:

Check warning on line 107 in panther/_utils.py

View workflow job for this annotation

GitHub Actions / Qodana for Python

Incorrect type

Expected to return '(...) -\> Any', got no return
from panther.app import GenericAPI

if not issubclass(endpoint, GenericAPI):
logger.critical(f'You may have forgotten to inherit from GenericAPI on the {endpoint.__name__}()')
raise TypeError

return endpoint().call_method
if not issubclass(endpoint, (GenericAPI, GenericWebsocket)):

Check warning on line 110 in panther/_utils.py

View workflow job for this annotation

GitHub Actions / Qodana for Python

Incorrect type

Expected type 'type', got '(...) -\> Any' instead
raise PantherError(
f'You may have forgotten to inherit from `panther.app.GenericAPI` or `panther.app.GenericWebsocket` '
f'on the `{endpoint.__module__}.{endpoint.__name__}()`'
)


def async_next(iterator: Iterator):
Expand Down
19 changes: 8 additions & 11 deletions panther/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ def load_configs(self) -> None:
load_urls(self._configs_module, urls=self._urls)
load_websocket_connections()

check_endpoints_inheritance()

async def __call__(self, scope: dict, receive: Callable, send: Callable) -> None:
if scope['type'] == 'lifespan':
message = await receive()
Expand Down Expand Up @@ -160,19 +162,10 @@ async def handle_http(self, scope: dict, receive: Callable, send: Callable) -> N
await request.read_body()

# Find Endpoint
_endpoint, found_path = find_endpoint(path=request.path)
if _endpoint is None:
endpoint, found_path = find_endpoint(path=request.path)
if endpoint is None:
return await self._raise(send, monitoring=monitoring, status_code=status.HTTP_404_NOT_FOUND)

# Check Endpoint Type
try:
if isinstance(_endpoint, types.FunctionType):
endpoint = check_function_type_endpoint(endpoint=_endpoint)
else:
endpoint = check_class_type_endpoint(endpoint=_endpoint)
except TypeError:
return await self._raise(send, monitoring=monitoring, status_code=status.HTTP_501_NOT_IMPLEMENTED)

# Collect Path Variables
request.collect_path_variables(found_path=found_path)

Expand All @@ -186,6 +179,10 @@ async def handle_http(self, scope: dict, receive: Callable, send: Callable) -> N
f'Make sure to return the `request` at the end of `{middleware.__class__.__name__}.before()`')
return await self._raise(send, monitoring=monitoring)

# Prepare the method
if not isinstance(endpoint, types.FunctionType):
endpoint = endpoint().call_method

# Call Endpoint
response = await endpoint(request=request)

Expand Down
4 changes: 4 additions & 0 deletions tests/sample_project/app/apis.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from panther.app import API


@API()
def first_api():
pass


@API()
def second_api():
pass
54 changes: 54 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,60 @@ def test_jwt_auth_with_secret_key(self):
AUTHENTICATION = None
SECRET_KEY = None

def test_check_function_endpoint_decorator(self):
with self.assertLogs(level='ERROR') as captured:
try:
Panther(name=__name__, configs=__name__, urls={'/': invalid_api})
except SystemExit:
assert True
else:
assert False

assert len(captured.records) == 1
assert captured.records[0].getMessage() == 'You may have forgotten to use `@API()` on the `tests.test_utils.invalid_api()`'

def test_check_class_endpoint_inheritance(self):
with self.assertLogs(level='ERROR') as captured:
try:
Panther(name=__name__, configs=__name__, urls={'/': InvalidAPI})
except SystemExit:
assert True
else:
assert False

assert len(captured.records) == 1
assert captured.records[0].getMessage() == (
f'You may have forgotten to inherit from `panther.app.GenericAPI` or `panther.app.GenericWebsocket` '
f'on the `tests.test_utils.InvalidAPI()`'
)

def test_check_websocket_inheritance(self):
with self.assertLogs(level='ERROR') as captured:
try:
Panther(name=__name__, configs=__name__, urls={'/': InvalidWebsocket})
except SystemExit:
assert True
else:
assert False

assert len(captured.records) == 1
assert captured.records[0].getMessage() == (
f'You may have forgotten to inherit from `panther.app.GenericAPI` or `panther.app.GenericWebsocket` '
f'on the `tests.test_utils.InvalidWebsocket()`'
)


def invalid_api():
pass


class InvalidAPI:
pass


class InvalidWebsocket:
pass


class CorrectTestMiddleware(BaseMiddleware):
pass
Expand Down

0 comments on commit ab5ebe6

Please sign in to comment.