aiogram_tool 1.1.0__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/PKG-INFO +1 -3
  2. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/README.md +1 -3
  3. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/depend/__init__.py +6 -1
  4. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/depend/components/__init__.py +2 -2
  5. aiogram_tool-1.2.0/aiogram_tool/depend/components/inner_middleware.py +42 -0
  6. aiogram_tool-1.2.0/aiogram_tool/depend/depend.py +63 -0
  7. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/depend/setup.py +8 -16
  8. aiogram_tool-1.2.0/aiogram_tool/depend/types/enums.py +11 -0
  9. aiogram_tool-1.2.0/aiogram_tool/depend/types/schema.py +10 -0
  10. aiogram_tool-1.2.0/aiogram_tool/depend/utils/__init__.py +0 -0
  11. aiogram_tool-1.2.0/aiogram_tool/depend/utils/depend_scope.py +22 -0
  12. aiogram_tool-1.2.0/aiogram_tool/depend/utils/explore_obj.py +58 -0
  13. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/pyproject.toml +1 -1
  14. aiogram_tool-1.1.0/aiogram_tool/depend/components/filter.py +0 -35
  15. aiogram_tool-1.1.0/aiogram_tool/depend/components/outer_middleware.py +0 -19
  16. aiogram_tool-1.1.0/aiogram_tool/depend/depend.py +0 -104
  17. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/LICENSE.md +0 -0
  18. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/__init__.py +0 -0
  19. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/depend/components/exit.py +0 -0
  20. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/depend/components/handler.py +0 -0
  21. {aiogram_tool-1.1.0/aiogram_tool/depend/utils → aiogram_tool-1.2.0/aiogram_tool/depend/types}/__init__.py +0 -0
  22. /aiogram_tool-1.1.0/aiogram_tool/depend/utils/inject.py → /aiogram_tool-1.2.0/aiogram_tool/depend/utils/inject_callback.py +0 -0
  23. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/limit/__init__.py +0 -0
  24. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/limit/limit.py +0 -0
  25. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/limit/setup.py +0 -0
  26. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/limit/storage/__init__.py +0 -0
  27. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/limit/storage/abstract_storage.py +0 -0
  28. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/limit/storage/memory_storage.py +0 -0
  29. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/limit/storage/redis_storage.py +0 -0
  30. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/limit/utils/__init__.py +0 -0
  31. {aiogram_tool-1.1.0 → aiogram_tool-1.2.0}/aiogram_tool/limit/utils/callback.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: aiogram_tool
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Tools which doing work with aiogram simple
5
5
  License: MIT
6
6
  Keywords: aiogram,rate,throttling,limit,dependency-injection,depends,cooldown,delay,DI
@@ -48,5 +48,3 @@ poetry add aiogram_tool
48
48
  ## Features `Depend`
49
49
 
50
50
  - Depend for Router
51
- - cache
52
- - scopes
@@ -28,6 +28,4 @@ poetry add aiogram_tool
28
28
 
29
29
  ## Features `Depend`
30
30
 
31
- - Depend for Router
32
- - cache
33
- - scopes
31
+ - Depend for Router
@@ -1,11 +1,16 @@
1
1
  from .depend import Depend
2
2
  from .setup import setup_depend_tool
3
3
  from .components import DependExit, DependHandler
4
+ from .utils.depend_scope import dependency_scope
5
+ from .types.enums import Scope
4
6
 
5
7
 
6
8
  __all__ = [
7
9
  "Depend",
8
10
  "setup_depend_tool",
9
11
  "DependExit",
10
- "DependFilter"
12
+ "DependFilter",
13
+ "DependHandler",
14
+ "dependency_scope",
15
+ "Scope"
11
16
  ]
@@ -1,7 +1,7 @@
1
1
  from .exit import DependExit
2
2
  from .handler import DependHandler
3
- from .filter import DependFilter
4
- from .outer_middleware import StackMiddleware
3
+ from .inner_middleware import DependInnerMiddleware
4
+
5
5
 
6
6
  __all__ = [
7
7
  "DependExit",
@@ -0,0 +1,42 @@
1
+ from typing import Awaitable, Callable, Any
2
+ from contextlib import AsyncExitStack
3
+
4
+ from aiogram import Dispatcher
5
+ from aiogram.dispatcher.event.handler import HandlerObject
6
+ from aiogram.types.base import TelegramObject
7
+ from aiogram.dispatcher.middlewares.base import BaseMiddleware
8
+
9
+ from .exit import DependExit
10
+ from ..depend import Depend
11
+ from ..utils.inject_callback import inject_parametrs
12
+
13
+
14
+
15
+ class DependInnerMiddleware(BaseMiddleware):
16
+
17
+ async def __call__(
18
+ self,
19
+ handler: Callable[[TelegramObject, dict[str, Any]], Awaitable[Any]],
20
+ event: TelegramObject,
21
+ data: dict[str, Any]
22
+ ) -> Any:
23
+ data.update({"event": event})
24
+
25
+ dispatcher: Dispatcher = data.get("dispatcher")
26
+ handler_object: HandlerObject = data.get("handler")
27
+
28
+ dependency_override: dict[str, Depend] = dispatcher.get("dependency_override", {})
29
+
30
+ async with AsyncExitStack() as stack:
31
+ inject = await inject_parametrs(
32
+ callback=handler_object.callback,
33
+ dependency_override=dependency_override,
34
+ middleware_data=data,
35
+ stack=stack
36
+ )
37
+ for value in inject.values():
38
+ if isinstance(value, DependExit):
39
+ return await value.event_answer()
40
+
41
+ data.update(inject)
42
+ return await handler(event, data)
@@ -0,0 +1,63 @@
1
+ import inspect
2
+
3
+ from typing import Callable, Any
4
+ from contextlib import AsyncExitStack
5
+
6
+ from .utils.explore_obj import ExploreObj
7
+ from .types.schema import ScopeObject
8
+ from .types.enums import Scope
9
+
10
+
11
+
12
+ class Depend(ExploreObj):
13
+ app_container = {}
14
+
15
+ def __init__(self, obj: Callable) -> None:
16
+ self.scope = Scope.REQUEST
17
+
18
+ explore_obj = obj
19
+ if not inspect.iscoroutinefunction(obj):
20
+ try:
21
+ scope_object = obj()
22
+ if isinstance(scope_object, ScopeObject):
23
+ explore_obj = scope_object.obj
24
+ self.scope = scope_object.scope
25
+ except:
26
+ pass
27
+
28
+ super().__init__(obj=explore_obj)
29
+
30
+ async def call(
31
+ self,
32
+ middleware_data: dict[str, Any],
33
+ stack: AsyncExitStack
34
+ ) -> Any:
35
+ if self.scope == Scope.APP:
36
+ if self.obj_name in self.app_container:
37
+ return self.app_container[self.obj_name]
38
+
39
+ inject_obj_parameters = {}
40
+ for key, value in self.obj_parameters.items():
41
+ if isinstance(value, Depend):
42
+ inject_obj_parameters[key] = await value.call(middleware_data, stack)
43
+
44
+ elif value is inspect._empty:
45
+ inject_obj_parameters[key] = middleware_data.get(key, None)
46
+
47
+ obj_returning = None
48
+ call_obj_with_params = self.obj(**inject_obj_parameters)
49
+ if self.isasync is False:
50
+ if self.isgenerator is True:
51
+ obj_returning = stack.enter_context(call_obj_with_params)
52
+ else:
53
+ obj_returning = call_obj_with_params
54
+ else:
55
+ if self.isgenerator is True:
56
+ obj_returning = await stack.enter_async_context(call_obj_with_params)
57
+ else:
58
+ obj_returning = await call_obj_with_params
59
+
60
+ if self.scope == Scope.APP:
61
+ if self.obj_name not in self.app_container:
62
+ self.app_container[self.obj_name] = obj_returning
63
+ return obj_returning
@@ -1,10 +1,8 @@
1
1
  from aiogram import Dispatcher
2
2
  from aiogram.dispatcher.event.telegram import TelegramEventObserver
3
- from aiogram.dispatcher.event.handler import FilterObject
4
-
5
3
 
6
4
  from .depend import Depend
7
- from .components import DependFilter, StackMiddleware
5
+ from .components import DependInnerMiddleware
8
6
 
9
7
 
10
8
 
@@ -25,25 +23,19 @@ def setup_depend_tool(
25
23
  dispatcher["dependency_override"] = dependency_override
26
24
 
27
25
  if allowed_updates:
28
- for allow in allowed_updates:
29
- if allow not in dispatcher.observers:
30
- raise TypeError(f"Invalid observer {allow}")
26
+ for update in allowed_updates:
27
+ if update not in dispatcher.observers:
28
+ raise TypeError(f"Invalid observer {update}")
31
29
  else:
32
- observer: TelegramEventObserver = getattr(dispatcher, allow)
33
-
34
- if observer.handlers:
35
- observer.outer_middleware(StackMiddleware())
36
- for handler in observer.handlers:
37
- handler.filters.append(FilterObject(DependFilter()))
30
+ observer: TelegramEventObserver = dispatcher.observers.get(update)
31
+ observer.middleware(DependInnerMiddleware())
38
32
 
39
33
  else:
40
34
  for allow in dispatcher.resolve_used_update_types():
41
35
  observer: TelegramEventObserver = getattr(dispatcher, allow)
36
+ observer.middleware(DependInnerMiddleware())
37
+
42
38
 
43
- if observer.handlers:
44
- observer.outer_middleware(StackMiddleware())
45
- for handler in observer.handlers:
46
- handler.filters.append(FilterObject(DependFilter()))
47
39
 
48
40
 
49
41
 
@@ -0,0 +1,11 @@
1
+ from enum import Enum, auto
2
+
3
+
4
+ class Scope(Enum):
5
+ """
6
+ APP: Caching the result for the entire duration of the application
7
+ \nREQUEST: New result for every request
8
+ """
9
+ APP = auto()
10
+ REQUEST = auto()
11
+
@@ -0,0 +1,10 @@
1
+ from typing import Callable
2
+ from dataclasses import dataclass
3
+
4
+ from .enums import Scope
5
+
6
+
7
+ @dataclass
8
+ class ScopeObject:
9
+ obj: Callable
10
+ scope: Scope
@@ -0,0 +1,22 @@
1
+ from typing import Callable, ParamSpec, TypeVar
2
+ from functools import wraps
3
+
4
+ from ..types.schema import ScopeObject
5
+ from ..types.enums import Scope
6
+
7
+
8
+ P = ParamSpec("P")
9
+ R = TypeVar("R")
10
+
11
+
12
+
13
+ def dependency_scope(scope: Scope) -> Callable[[Callable[P, R]], Callable[P, R]]:
14
+ def decorator(obj: Callable[P, R]) -> Callable[P, R]:
15
+ @wraps(obj)
16
+ def wrapper() -> ScopeObject:
17
+ return ScopeObject(
18
+ obj=obj,
19
+ scope=scope
20
+ )
21
+ return wrapper
22
+ return decorator
@@ -0,0 +1,58 @@
1
+ import inspect
2
+
3
+ from typing import Callable, _AnnotatedAlias
4
+ from contextlib import asynccontextmanager, contextmanager
5
+
6
+ from aiogram_tool import depend
7
+
8
+
9
+
10
+
11
+ class ExploreObj:
12
+ def __init__(self, obj: Callable):
13
+ self.obj = obj
14
+ self.isgenerator = False
15
+ self.isasync = False
16
+ self.obj_parameters: dict[str, depend.Depend] = {}
17
+ self.obj_name = getattr(obj, "__name__", "")
18
+
19
+ if isinstance(self.obj, type):
20
+ raise TypeError("dont valid type. Initialize class")
21
+
22
+ if not callable(self.obj):
23
+ raise TypeError("obj must be a callable")
24
+
25
+ if not inspect.isfunction(self.obj):
26
+ self.obj_name = self.obj.__class__.__name__
27
+ self.obj = getattr(self.obj, "__call__")
28
+
29
+ self.__rule_for_call_obj()
30
+ self.__find_obj_parameters()
31
+
32
+
33
+ def __rule_for_call_obj(self) -> None:
34
+ if inspect.iscoroutinefunction(self.obj):
35
+ self.isasync = True
36
+
37
+ else:
38
+ if inspect.isasyncgenfunction(self.obj):
39
+ self.isgenerator = True
40
+ self.isasync = True
41
+ self.obj = asynccontextmanager(self.obj)
42
+
43
+ elif inspect.isgeneratorfunction(self.obj):
44
+ self.isgenerator = True
45
+ self.obj = contextmanager(self.obj)
46
+
47
+
48
+ def __find_obj_parameters(self) -> None:
49
+ signature = inspect.signature(self.obj)
50
+ for param, param_spec in signature.parameters.items():
51
+ if isinstance(param_spec.annotation, _AnnotatedAlias):
52
+ types = getattr(param_spec.annotation, "__metadata__")
53
+ for dependency in types:
54
+ if isinstance(dependency, depend.Depend):
55
+ self.obj_parameters[param] = dependency
56
+ else:
57
+ self.obj_parameters[param] = param_spec.default
58
+
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aiogram_tool"
3
- version = "1.1.0"
3
+ version = "1.2.0"
4
4
  description = "Tools which doing work with aiogram simple"
5
5
  authors = [
6
6
  {name = "Vlad_shayzi1",email = "tecnomega@mail.ru"}
@@ -1,35 +0,0 @@
1
- from contextlib import AsyncExitStack
2
-
3
- from aiogram.filters import Filter
4
- from aiogram.types.base import TelegramObject
5
- from aiogram import Dispatcher
6
- from aiogram.dispatcher.event.handler import HandlerObject
7
-
8
- from .exit import DependExit
9
- from ..depend import Depend
10
- from ..utils.inject import inject_parametrs
11
-
12
-
13
-
14
- class DependFilter(Filter):
15
-
16
- async def __call__(self, event: TelegramObject, **data):
17
- data.update({"event": event})
18
-
19
- dispatcher: Dispatcher = data.get("dispatcher")
20
- handler_object: HandlerObject = data.get("handler")
21
-
22
- dependency_override: dict[str, Depend] = dispatcher.get("dependency_override", {})
23
- stack: AsyncExitStack = data.get("async_stack")
24
-
25
- inject = await inject_parametrs(
26
- callback=handler_object.callback,
27
- dependency_override=dependency_override,
28
- middleware_data=data,
29
- stack=stack
30
- )
31
- for value in inject.values():
32
- if isinstance(value, DependExit):
33
- await value.event_answer()
34
- return False
35
- return inject
@@ -1,19 +0,0 @@
1
- from typing import Any, Callable, Awaitable
2
- from contextlib import AsyncExitStack
3
-
4
- from aiogram.dispatcher.middlewares.base import BaseMiddleware
5
- from aiogram.types.base import TelegramObject
6
-
7
-
8
-
9
- class StackMiddleware(BaseMiddleware):
10
-
11
- async def __call__(
12
- self,
13
- handler: Callable[[TelegramObject, dict[str, Any]], Awaitable],
14
- event: TelegramObject,
15
- data: dict[str, Any]
16
- ):
17
- async with AsyncExitStack() as stack:
18
- data.update({"async_stack": stack})
19
- return await handler(event, data)
@@ -1,104 +0,0 @@
1
- import inspect
2
-
3
- from contextlib import (
4
- asynccontextmanager,
5
- contextmanager,
6
- AsyncExitStack
7
- )
8
- from typing import (
9
- Callable,
10
- Any,
11
- _AnnotatedAlias
12
- )
13
-
14
-
15
-
16
- class Depend:
17
- def __init__(self, obj: Callable) -> None:
18
- self.obj = obj
19
- self.isgenerator = False
20
- self.isasync = False
21
- self.obj_name = getattr(obj, "__name__", "")
22
- self.arguments = {}
23
-
24
- if isinstance(self.obj, type):
25
- raise TypeError("dont valid type. Initialize class")
26
-
27
- if not callable(self.obj):
28
- raise TypeError("obj must be a callable")
29
-
30
- if not inspect.isfunction(self.obj):
31
- self.obj_name = self.obj.__class__.__name__
32
- self.obj = getattr(self.obj, "__call__")
33
-
34
- self.__explore_obj()
35
- self.__explore_signature()
36
-
37
-
38
- def __explore_obj(self) -> None:
39
- if inspect.iscoroutinefunction(self.obj):
40
- self.isasync = True
41
-
42
- else:
43
- if inspect.isasyncgenfunction(self.obj):
44
- self.isgenerator = True
45
- self.isasync = True
46
- self.obj = asynccontextmanager(self.obj)
47
-
48
- elif inspect.isgeneratorfunction(self.obj):
49
- self.isgenerator = True
50
- self.obj = contextmanager(self.obj)
51
-
52
- else:
53
- types = []
54
- for closure in inspect.getclosurevars(self.obj):
55
- if isinstance(closure, dict):
56
- types.extend(list(closure.values()))
57
-
58
- for type_ in types:
59
- name = getattr(type_, "__name__", None)
60
- if name in "_AsyncGeneratorContextManager":
61
- self.isasync = True
62
- self.isgenerator = True
63
-
64
- elif name == "_GeneratorContextManager":
65
- self.isgenerator = True
66
-
67
-
68
- def __explore_signature(self) -> None:
69
- signature = inspect.signature(self.obj)
70
- for key, annotation in signature.parameters.items():
71
- default_value = annotation.default
72
-
73
- if isinstance(annotation.annotation, _AnnotatedAlias):
74
- types = getattr(annotation.annotation, "__metadata__")
75
- for dep in types:
76
- if isinstance(dep, Depend):
77
- self.arguments[key] = dep
78
-
79
- elif isinstance(default_value, Depend):
80
- self.arguments[key] = default_value
81
-
82
- else:
83
- self.arguments[key] = annotation.annotation
84
-
85
- async def call(
86
- self,
87
- middleware_data: dict[str, Any],
88
- stack: AsyncExitStack
89
- ) -> Any:
90
- kwargs = {}
91
- for key, value in self.arguments.items():
92
- if isinstance(value, Depend):
93
- kwargs[key] = await value.call(middleware_data, stack)
94
- else:
95
- kwargs[key] = middleware_data.get(key)
96
-
97
- if self.isasync is False:
98
- if self.isgenerator is True:
99
- return stack.enter_context(self.obj(**kwargs))
100
- return self.obj(**kwargs)
101
- else:
102
- if self.isgenerator is True:
103
- return await stack.enter_async_context(self.obj(**kwargs))
104
- return await self.obj(**kwargs)
File without changes