Skip to content

Commit 18d174f

Browse files
✨ Added an extra argument in Client/Application methods to pass into listeners and included additional logging.
1 parent 4523aeb commit 18d174f

File tree

5 files changed

+363
-183
lines changed

5 files changed

+363
-183
lines changed

squarecloud/app.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
)
2020
from .file import File
2121
from .http import Endpoint, HTTPClient, Response
22+
from .listeners import Listener, ListenerConfig
2223
from .listeners.capture_listener import CaptureListenerManager
2324

2425
# avoid circular imports
@@ -321,9 +322,10 @@ def _notify_listener(endpoint: Endpoint):
321322
:param endpoint: the endpoint for witch the listener will fetch
322323
:return: a callable
323324
"""
325+
324326
def wrapper(func: Callable):
325327
@wraps(func)
326-
async def decorator(self, *args, **kwargs):
328+
async def decorator(self: Application, *args, **kwargs) -> Any:
327329
result = await func(self, *args, **kwargs)
328330
avoid_listener = kwargs.pop('avoid_listener', False)
329331
if not (avoid_listener or self.always_avoid_listeners):
@@ -348,6 +350,7 @@ def _update_cache(func: Callable):
348350
:param func:
349351
:return: a callable
350352
"""
353+
351354
@wraps(func)
352355
async def wrapper(self, *args, **kwargs):
353356
update_cache = kwargs.pop('update_cache', True)
@@ -358,7 +361,7 @@ async def wrapper(self, *args, **kwargs):
358361

359362
return wrapper
360363

361-
def capture(self, endpoint: Endpoint) -> Callable:
364+
def capture(self, endpoint: Endpoint, **kwargs) -> Callable:
362365
"""
363366
The capture function is a decorator that can be used to add a callable
364367
to be called when a request is made to the specified endpoint.
@@ -371,7 +374,7 @@ def capture(self, endpoint: Endpoint) -> Callable:
371374
"""
372375

373376
def wrapper(
374-
func: Callable[[Endpoint, Endpoint], Any]
377+
call: Callable[[Endpoint, Endpoint], Any]
375378
) -> Callable[[Endpoint, Endpoint], Any]:
376379
"""
377380
The wrapper function is a decorator that takes in the endpoint as
@@ -382,13 +385,27 @@ def wrapper(
382385
with the function passed to wrapper().
383386
Otherwise, it raises another SquareException.
384387
385-
:param func: Pass the function to be wrapped
388+
:param call: Pass the function to be wrapped
386389
:return: The wrapper function itself
387390
:rtype: None
388-
:raises InvalidListener: Raised if the endpoint is already registered
391+
:raises InvalidListener: Raised if the endpoint is already
392+
registered
389393
"""
390-
self.include_listener(endpoint, func)
391-
return func
394+
for key, value in kwargs.items():
395+
if key not in ListenerConfig.__annotations__:
396+
raise ValueError(
397+
f'Invalid listener configuration: "{key}={value}"'
398+
)
399+
config = ListenerConfig(**kwargs)
400+
listener = Listener(
401+
app=self,
402+
client=self.client,
403+
endpoint=endpoint,
404+
callback=call,
405+
config=config,
406+
)
407+
self.include_listener(listener)
408+
return call
392409

393410
return wrapper
394411

squarecloud/listeners/__init__.py

Lines changed: 162 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,182 @@
1+
import inspect
12
import types
2-
from inspect import Parameter
3-
from typing import Callable, Union
3+
from dataclasses import dataclass
4+
from types import MappingProxyType
5+
from typing import TYPE_CHECKING, Any, Callable, Optional, Type, Union
46

5-
from .. import data
7+
import pydantic
8+
from pydantic import BaseModel
9+
10+
from .. import data, errors
611
from ..http.endpoints import Endpoint
712

13+
if TYPE_CHECKING:
14+
from ..app import Application
15+
from ..client import Client
16+
17+
18+
@dataclass(frozen=False)
19+
class ListenerConfig:
20+
force_raise: bool = False
21+
822

923
class Listener:
10-
__slots__ = ('endpoint', 'callback', 'callback_args')
24+
__slots__ = (
25+
'_app',
26+
'_client',
27+
'_endpoint',
28+
'_callback',
29+
'_callback_params',
30+
'config',
31+
)
1132

1233
def __init__(
1334
self,
1435
endpoint: Endpoint,
1536
callback: Callable,
16-
callback_args: types.MappingProxyType[str, Parameter],
37+
config: ListenerConfig = ListenerConfig(),
38+
app: Optional['Application'] = None,
39+
client: Optional['Client'] = None,
1740
):
18-
self.endpoint = endpoint
19-
self.callback = callback
20-
self.callback_args = callback_args
41+
self._app = app
42+
self._client = client
43+
self._endpoint = endpoint
44+
self._callback = callback
45+
self._callback_params = inspect.signature(callback).parameters
46+
self.config = config
47+
48+
@property
49+
def app(self) -> 'Application':
50+
return self._app
51+
52+
@property
53+
def endpoint(self) -> Endpoint:
54+
return self._endpoint
55+
56+
@property
57+
def callback(self) -> Callable:
58+
return self._callback
59+
60+
@property
61+
def callback_params(self) -> MappingProxyType[str, inspect.Parameter]:
62+
return self._callback_params
2163

2264
def __repr__(self):
2365
return f'{self.__class__.__name__}(endpoint={self.endpoint})'
2466

2567

68+
class ListenerManager:
69+
def __init__(self):
70+
"""
71+
The __init__ method is called when the class is instantiated.
72+
It sets up the instance variables that will be used by other methods
73+
in the class.
74+
75+
76+
:param self: Refer to the class instance
77+
:return: A dictionary of the capture listeners and request listeners
78+
"""
79+
self.listeners: dict[str, Callable] = {}
80+
81+
def get_listener(self, endpoint: Endpoint) -> Listener | None:
82+
"""
83+
The get_listener method is used to get the capture listener
84+
for a given endpoint.
85+
86+
:param self: Refer to the class instance
87+
:param endpoint: Endpoint: Get the capture listener from the endpoint
88+
name
89+
:return: The capture listener for the given endpoint
90+
"""
91+
return self.listeners.get(endpoint.name)
92+
93+
def include_listener(self, listener: Listener) -> Listener:
94+
"""
95+
The include_listener method adds a listener to the
96+
capture_listeners dictionary.
97+
The key is the name of an endpoint, and the value is a callable
98+
function that will be called when
99+
the endpoint's data has been captured.
100+
101+
:param listener: the listener that will be included
102+
:param self: Refer to the class instance
103+
listen to
104+
request is made to the endpoint
105+
:return: None
106+
:raises InvalidListener: Raised if the endpoint is already registered
107+
"""
108+
if self.get_listener(listener.endpoint):
109+
raise errors.InvalidListener(
110+
message='Already exists an capture_listener for '
111+
f'{listener.endpoint}',
112+
listener=listener.callback,
113+
)
114+
self.listeners.update({listener.endpoint.name: listener})
115+
return listener
116+
117+
def remove_listener(self, endpoint: Endpoint) -> Callable:
118+
"""
119+
The remove_listener method removes a capture listener from
120+
the list of listeners.
121+
122+
:param self: Refer to the class instance
123+
:param endpoint: Endpoint: Identify the endpoint to remove
124+
:return: The capture_listener that was removed from the dictionary
125+
"""
126+
if self.get_listener(endpoint):
127+
return self.listeners.pop(endpoint.name)
128+
129+
def clear_listeners(self) -> None:
130+
"""
131+
The clear_listeners function clears the capture_listeners list.
132+
133+
:param self: Refer to the class instance
134+
:return: None
135+
"""
136+
self.listeners = None
137+
138+
@classmethod
139+
def cast_to_pydantic_model(
140+
cls, model: Type[BaseModel], values: dict[Any, Any]
141+
):
142+
result: BaseModel | None | dict = values
143+
if isinstance(model, types.UnionType):
144+
for ty in model.__args__:
145+
if ty is None:
146+
continue
147+
elif not issubclass(ty, BaseModel):
148+
continue
149+
try:
150+
a = ty(**values)
151+
return a
152+
except pydantic.ValidationError:
153+
continue
154+
return None
155+
if issubclass(model, BaseModel):
156+
try:
157+
result = model(**values)
158+
except pydantic.ValidationError as e:
159+
print(e)
160+
result = None
161+
return result
162+
163+
# async def notify(
164+
# self,
165+
# endpoint: Endpoint,
166+
# extra: Any = None,
167+
# **kwargs,
168+
# ) -> Any | Exception:
169+
# """
170+
# The notify method is called when a capture event occurs.
171+
#
172+
# :param self: Refer to the class instance
173+
# :param endpoint: Endpoint: Get the endpoint that is being called
174+
# :param extra:
175+
# :return: The result of the call function
176+
# """
177+
# pass
178+
179+
26180
ListenerDataTypes = Union[
27181
data.AppData,
28182
data.StatusData,

0 commit comments

Comments
 (0)