sqlalchemy-events-lib 0.3.1__tar.gz → 0.3.2__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.
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/PKG-INFO +1 -1
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/pyproject.toml +1 -1
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/__init__.py +1 -1
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/callbacks_strategies/postgres_callback.py +1 -1
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/handlers.py +10 -13
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/registry.py +1 -1
- sqlalchemy_events_lib-0.3.2/src/sqlalchemy_events/types.py +40 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events_lib.egg-info/PKG-INFO +1 -1
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/tests/conftest.py +9 -2
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/tests/test_autodiscover.py +4 -5
- sqlalchemy_events_lib-0.3.2/src/tests/test_handlers.py +79 -0
- sqlalchemy_events_lib-0.3.1/src/sqlalchemy_events/types.py +0 -26
- sqlalchemy_events_lib-0.3.1/src/tests/test_handlers.py +0 -17
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/LICENSE +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/README.md +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/setup.cfg +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/callbacks_strategies/__init__.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/callbacks_strategies/base.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/core.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/decorators.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/default_logger.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/discovery.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/events.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/exceptions.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/init_triggers_strategies/__init__.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/init_triggers_strategies/base.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/init_triggers_strategies/postgres_init_triggers.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/utils.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events_lib.egg-info/SOURCES.txt +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events_lib.egg-info/dependency_links.txt +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events_lib.egg-info/requires.txt +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events_lib.egg-info/top_level.txt +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/tests/test_is_created_triggers.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/tests/test_models.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/tests/test_sa_events.py +0 -0
- {sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/tests/test_with_events_deco.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlalchemy-events-lib
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Event-driven extension for SQLAlchemy that enables listening to database CUD events. This library allows you to react to database changes in real time using a clean, declarative API.
|
|
5
5
|
Author-email: Alexey Kostarev <normjkeeewm@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -10,7 +10,7 @@ where = ["src"]
|
|
|
10
10
|
|
|
11
11
|
[project]
|
|
12
12
|
name = "sqlalchemy-events-lib"
|
|
13
|
-
version = "0.3.
|
|
13
|
+
version = "0.3.2"
|
|
14
14
|
description = "Event-driven extension for SQLAlchemy that enables listening to database CUD events. This library allows you to react to database changes in real time using a clean, declarative API."
|
|
15
15
|
requires-python = ">=3.12"
|
|
16
16
|
authors = [
|
{sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/handlers.py
RENAMED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import inspect
|
|
2
|
-
from pathlib import Path
|
|
3
1
|
from typing import Callable, Type
|
|
4
2
|
|
|
5
3
|
from sqlalchemy.orm import DeclarativeBase
|
|
@@ -13,21 +11,20 @@ def __inner(func: Callable, sa_event: SaEvent, model: Type[DeclarativeBase]):
|
|
|
13
11
|
if not issubclass(model, DeclarativeBase):
|
|
14
12
|
raise RuntimeError('Model must inherit from DeclarativeBase')
|
|
15
13
|
|
|
14
|
+
if '.' in func.__qualname__ and not isinstance(func, staticmethod):
|
|
15
|
+
raise RuntimeError(f'Handler must be a regular function or staticmethod, not method or classmethod of '
|
|
16
|
+
f'{func.__qualname__.split('.')[0]}')
|
|
17
|
+
|
|
18
|
+
if isinstance(func, staticmethod):
|
|
19
|
+
func = func.__func__
|
|
20
|
+
|
|
16
21
|
event_handlers = get_event_handlers()
|
|
17
22
|
trig_name = f'sa_{model.__tablename__}_{sa_event.lower()}_notify'
|
|
18
|
-
func_path = inspect.getsourcefile(func) or inspect.getfile(func)
|
|
19
|
-
func_path = Path(func_path)
|
|
20
|
-
func_path_name = f'{func_path.parent.name}/{func_path.name}/{func.__name__}'
|
|
21
23
|
handlers: list[Handler] | None = event_handlers.get(trig_name)
|
|
24
|
+
|
|
22
25
|
obj_handler = Handler(func=func, args={'model': model})
|
|
23
|
-
if handlers:
|
|
24
|
-
|
|
25
|
-
handler_path = inspect.getsourcefile(handler.func) or inspect.getfile(handler.func)
|
|
26
|
-
handler_path = Path(handler_path)
|
|
27
|
-
handler_path_name = f'{handler_path.parent.name}/{handler_path.name}/{handler.func.__name__}'
|
|
28
|
-
if func_path_name == handler_path_name:
|
|
29
|
-
continue
|
|
30
|
-
handlers.append(obj_handler)
|
|
26
|
+
if handlers and obj_handler not in handlers:
|
|
27
|
+
handlers.append(obj_handler)
|
|
31
28
|
else:
|
|
32
29
|
event_handlers[trig_name] = [obj_handler]
|
|
33
30
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Awaitable, Callable, TypeAlias, Union
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SaEvent(StrEnum):
|
|
9
|
+
INSERT = 'INSERT'
|
|
10
|
+
UPDATE = 'UPDATE'
|
|
11
|
+
DELETE = 'DELETE'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Dialect(StrEnum):
|
|
15
|
+
POSTGRESQL = 'postgresql'
|
|
16
|
+
SQLITE = 'sqlite'
|
|
17
|
+
MYSQL = 'mysql'
|
|
18
|
+
MSSQL = 'mssql'
|
|
19
|
+
ORACLE = 'oracle'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class Handler:
|
|
24
|
+
func: Union[Callable, Awaitable]
|
|
25
|
+
args: dict[str, Any]
|
|
26
|
+
full_path: str | None = None
|
|
27
|
+
|
|
28
|
+
def __post_init__(self):
|
|
29
|
+
func_path = inspect.getsourcefile(self.func) or inspect.getfile(self.func)
|
|
30
|
+
func_path = Path(func_path)
|
|
31
|
+
self.full_path = f'{func_path.parent.name}/{func_path.name}/{self.func.__name__}'
|
|
32
|
+
|
|
33
|
+
def __hash__(self):
|
|
34
|
+
return hash(self.full_path)
|
|
35
|
+
|
|
36
|
+
def __eq__(self, other):
|
|
37
|
+
return self.full_path == other.full_path
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
DB_ID: TypeAlias = str | int
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlalchemy-events-lib
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Event-driven extension for SQLAlchemy that enables listening to database CUD events. This library allows you to react to database changes in real time using a clean, declarative API.
|
|
5
5
|
Author-email: Alexey Kostarev <normjkeeewm@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import asyncio
|
|
2
1
|
import os
|
|
3
2
|
|
|
4
3
|
import pytest_asyncio
|
|
@@ -7,6 +6,8 @@ from dotenv import load_dotenv
|
|
|
7
6
|
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
|
|
8
7
|
from sqlalchemy import text
|
|
9
8
|
from sqlalchemy_events import SQLAlchemyEvents
|
|
9
|
+
from sqlalchemy_events.events import sa_events_strategy
|
|
10
|
+
from sqlalchemy_events.utils import dialect_resolver
|
|
10
11
|
|
|
11
12
|
from tests.test_models import Base
|
|
12
13
|
|
|
@@ -45,4 +46,10 @@ async def session(engine):
|
|
|
45
46
|
|
|
46
47
|
async with async_session() as session:
|
|
47
48
|
yield session
|
|
48
|
-
await session.rollback()
|
|
49
|
+
await session.rollback()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest_asyncio.fixture
|
|
53
|
+
async def mock_callback(engine):
|
|
54
|
+
dialect = dialect_resolver(engine)
|
|
55
|
+
yield sa_events_strategy[dialect].callback
|
|
@@ -117,18 +117,17 @@ async def test_discover_handlers():
|
|
|
117
117
|
assert handlers
|
|
118
118
|
result_handlers = []
|
|
119
119
|
for key, handler_list in handlers.items():
|
|
120
|
-
assert len(handler_list) == 1
|
|
121
120
|
if 'insert' in key:
|
|
122
121
|
assert handler_list[0].func.__name__ == 'insert_handler'
|
|
123
|
-
result_handlers.
|
|
122
|
+
result_handlers.append(handler_list[0])
|
|
124
123
|
if 'update' in key:
|
|
125
124
|
assert handler_list[0].func.__name__ == 'update_handler'
|
|
126
|
-
result_handlers.
|
|
125
|
+
result_handlers.append(handler_list[0])
|
|
127
126
|
if 'delete' in key:
|
|
128
127
|
assert handler_list[0].func.__name__ == 'delete_handler'
|
|
129
|
-
result_handlers.
|
|
128
|
+
result_handlers.append(handler_list[0])
|
|
130
129
|
|
|
131
|
-
assert len(result_handlers) ==
|
|
130
|
+
assert len(result_handlers) == 3
|
|
132
131
|
|
|
133
132
|
|
|
134
133
|
def test_invalid_autodiscover_path():
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from sqlalchemy_events import sa_delete_handler, sa_insert_handler, sa_update_handler
|
|
5
|
+
from tests.test_models import UserModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_invalid_model_in_handler():
|
|
9
|
+
with pytest.raises(RuntimeError):
|
|
10
|
+
@sa_insert_handler(object)
|
|
11
|
+
def test_func():
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_handler_signature():
|
|
16
|
+
with pytest.raises(TypeError):
|
|
17
|
+
@sa_insert_handler()
|
|
18
|
+
def test_func():
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Test:
|
|
23
|
+
def test_method(self):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def test_class_method(cls):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def test_static_method():
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_handler_in_class():
|
|
36
|
+
with pytest.raises(RuntimeError):
|
|
37
|
+
sa_insert_handler(UserModel)(Test().test_method)
|
|
38
|
+
|
|
39
|
+
with pytest.raises(RuntimeError):
|
|
40
|
+
sa_update_handler(UserModel)(Test.test_class_method)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@sa_insert_handler(UserModel)
|
|
44
|
+
async def insert_user_handler(rows):
|
|
45
|
+
assert rows == [1, 2, 3]
|
|
46
|
+
return rows
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@sa_update_handler(UserModel)
|
|
50
|
+
async def update_user_handler(rows):
|
|
51
|
+
assert rows == [4, 5, 6]
|
|
52
|
+
return rows
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@sa_delete_handler(UserModel)
|
|
56
|
+
async def delete_user_handler(rows):
|
|
57
|
+
assert rows == [7, 8, 9]
|
|
58
|
+
return rows
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@pytest.mark.parametrize(
|
|
62
|
+
'op,rows,resp',
|
|
63
|
+
[
|
|
64
|
+
('insert', [1, 2, 3], [None, [1, 2, 3]]),
|
|
65
|
+
('update', [4, 5, 6], [None, [4, 5, 6]]),
|
|
66
|
+
('delete', [7, 8, 9], [None, [7, 8, 9]]),
|
|
67
|
+
]
|
|
68
|
+
)
|
|
69
|
+
async def test_handlers(mock_callback, op, rows, resp):
|
|
70
|
+
response = await mock_callback.handle(
|
|
71
|
+
None, None, None, json.dumps(
|
|
72
|
+
{
|
|
73
|
+
'op': op,
|
|
74
|
+
'table': 'users',
|
|
75
|
+
'rows': rows
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
assert response == resp
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from enum import StrEnum
|
|
3
|
-
from typing import Any, Awaitable, Callable, TypeAlias, Union
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class SaEvent(StrEnum):
|
|
7
|
-
INSERT = 'INSERT'
|
|
8
|
-
UPDATE = 'UPDATE'
|
|
9
|
-
DELETE = 'DELETE'
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class Dialect(StrEnum):
|
|
13
|
-
POSTGRESQL = 'postgresql'
|
|
14
|
-
SQLITE = 'sqlite'
|
|
15
|
-
MYSQL = 'mysql'
|
|
16
|
-
MSSQL = 'mssql'
|
|
17
|
-
ORACLE = 'oracle'
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@dataclass
|
|
21
|
-
class Handler:
|
|
22
|
-
func: Union[Callable, Awaitable]
|
|
23
|
-
args: dict[str, Any]
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
DB_ID: TypeAlias = str | int
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
|
-
from sqlalchemy_events import sa_insert_handler
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_invalid_model_in_handler():
|
|
7
|
-
with pytest.raises(RuntimeError):
|
|
8
|
-
@sa_insert_handler(object)
|
|
9
|
-
def test_func():
|
|
10
|
-
pass
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def test_handler_signature():
|
|
14
|
-
with pytest.raises(TypeError):
|
|
15
|
-
@sa_insert_handler()
|
|
16
|
-
def test_func():
|
|
17
|
-
pass
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/decorators.py
RENAMED
|
File without changes
|
{sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/default_logger.py
RENAMED
|
File without changes
|
{sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/discovery.py
RENAMED
|
File without changes
|
|
File without changes
|
{sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/sqlalchemy_events/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/tests/test_is_created_triggers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sqlalchemy_events_lib-0.3.1 → sqlalchemy_events_lib-0.3.2}/src/tests/test_with_events_deco.py
RENAMED
|
File without changes
|