django-queuebie 0.2.0__tar.gz → 0.3.1__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.
- django_queuebie-0.3.1/CHANGES.md +13 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/PKG-INFO +1 -1
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/queuebie/__init__.py +1 -1
- django_queuebie-0.3.1/queuebie/runner.py +63 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/handlers/commands/testapp.py +24 -6
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/messages/commands/my_commands.py +10 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/test_registry.py +15 -1
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/test_runner.py +11 -11
- django_queuebie-0.2.0/CHANGES.md +0 -7
- django_queuebie-0.2.0/queuebie/runner.py +0 -64
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/.ambient-package-update/metadata.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/.ambient-package-update/templates/snippets/content.tpl +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/.ambient-package-update/templates/snippets/tagline.tpl +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/.editorconfig +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/.github/workflows/ci.yml +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/.gitignore +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/.pre-commit-config.yaml +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/.readthedocs.yaml +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/LICENSE +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/LICENSE.md +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/MANIFEST.in +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/README.md +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/SECURITY.md +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/docs/Makefile +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/docs/conf.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/docs/features/changelog.rst +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/docs/features/getting-started.md +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/docs/features/introduction.md +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/docs/features/motivation.md +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/docs/features/settings.md +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/docs/features/setup.md +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/docs/index.rst +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/docs/make.bat +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/manage.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/pyproject.toml +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/queuebie/apps.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/queuebie/database_blocker.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/queuebie/exceptions.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/queuebie/logger.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/queuebie/management/__init__.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/queuebie/management/commands/__init__.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/queuebie/management/commands/clear_queuebie_registry.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/queuebie/messages.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/queuebie/registry.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/queuebie/settings.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/queuebie/utils.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/scripts/unix/install_requirements.sh +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/scripts/unix/publish_to_pypi.sh +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/scripts/windows/install_requirements.ps1 +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/scripts/windows/publish_to_pypi.ps1 +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/settings.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/__init__.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/handlers/__init__.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/handlers/commands/__init__.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/handlers/events/__init__.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/handlers/events/testapp.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/messages/__init__.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/messages/commands/__init__.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/messages/commands/other_commands.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/messages/events/__init__.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/messages/events/my_events.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/testapp/urls.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/__init__.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/helpers/__init__.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/helpers/commands.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/helpers/events.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/management_commands/__init__.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/management_commands/test_clear_queuebie_registry.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/test_apps.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/test_database_blocker.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/test_exceptions.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/test_logger.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/test_messages.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/test_settings.py +0 -0
- {django_queuebie-0.2.0 → django_queuebie-0.3.1}/tests/test_utils.py +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
* *0.3.1* (2025-03-19)
|
|
4
|
+
* Added a paranoid-ish test to check that the import logic isn't breaking any testing functionality
|
|
5
|
+
|
|
6
|
+
* *0.3.0* (2025-03-19)
|
|
7
|
+
* The whole queue iteration now is wrapped in a transaction atomic
|
|
8
|
+
|
|
9
|
+
* *0.2.0* (2025-03-17)
|
|
10
|
+
* Extend strict mode to prohibit event (!) handlers to talk to the database
|
|
11
|
+
|
|
12
|
+
* *0.1.0* (2025-01-29)
|
|
13
|
+
* Project init
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from contextlib import nullcontext
|
|
3
|
+
|
|
4
|
+
from django.db import transaction
|
|
5
|
+
|
|
6
|
+
from queuebie import message_registry
|
|
7
|
+
from queuebie.database_blocker import BlockDatabaseAccess
|
|
8
|
+
from queuebie.exceptions import InvalidMessageTypeError
|
|
9
|
+
from queuebie.logger import get_logger
|
|
10
|
+
from queuebie.messages import Command, Event, Message
|
|
11
|
+
from queuebie.settings import get_queuebie_strict_mode
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def handle_message(messages: Message | list[Message]) -> None:
|
|
15
|
+
queue: list[Message] = messages if isinstance(messages, list) else [messages]
|
|
16
|
+
|
|
17
|
+
for message in queue:
|
|
18
|
+
if not isinstance(message, (Command, Event)):
|
|
19
|
+
raise InvalidMessageTypeError(class_name=message.__class__.__name__)
|
|
20
|
+
|
|
21
|
+
# Run auto-registry
|
|
22
|
+
message_registry.autodiscover()
|
|
23
|
+
|
|
24
|
+
with transaction.atomic():
|
|
25
|
+
while queue:
|
|
26
|
+
message = queue.pop(0)
|
|
27
|
+
if isinstance(message, Command):
|
|
28
|
+
handler_list = message_registry.command_dict.get(message.module_path(), [])
|
|
29
|
+
block_db_access = False
|
|
30
|
+
else:
|
|
31
|
+
handler_list = message_registry.event_dict.get(message.module_path(), [])
|
|
32
|
+
block_db_access = True if get_queuebie_strict_mode() else False
|
|
33
|
+
|
|
34
|
+
new_messages = _process_message(handler_list=handler_list, message=message, block_db_access=block_db_access)
|
|
35
|
+
queue.extend(new_messages)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _process_message(*, handler_list: list, message: [Command, Event], block_db_access: bool) -> list[Message]:
|
|
39
|
+
"""
|
|
40
|
+
Handler to process messages of type "Command"
|
|
41
|
+
"""
|
|
42
|
+
logger = get_logger()
|
|
43
|
+
messages = []
|
|
44
|
+
|
|
45
|
+
for handler in handler_list:
|
|
46
|
+
try:
|
|
47
|
+
logger.debug(
|
|
48
|
+
f"Handling command '{message.module_path()}' ({message.uuid}) with handler '{handler['name']}'."
|
|
49
|
+
)
|
|
50
|
+
module = importlib.import_module(handler["module"])
|
|
51
|
+
handler_function = getattr(module, handler["name"])
|
|
52
|
+
with BlockDatabaseAccess() if block_db_access else nullcontext():
|
|
53
|
+
handler_messages = handler_function(context=message) or []
|
|
54
|
+
handler_messages = handler_messages if isinstance(handler_messages, list) else [handler_messages]
|
|
55
|
+
if len(handler_messages) > 0:
|
|
56
|
+
messages.extend(handler_messages)
|
|
57
|
+
uuid_list = [f"{m!s}" for m in handler_messages]
|
|
58
|
+
logger.debug(f"New messages: {uuid_list!s}")
|
|
59
|
+
except Exception as e:
|
|
60
|
+
logger.debug(f"Exception handling command {message.module_path()}: {e!s}")
|
|
61
|
+
raise e from e
|
|
62
|
+
|
|
63
|
+
return messages
|
|
@@ -5,7 +5,13 @@ from django.contrib.auth.models import User
|
|
|
5
5
|
from queuebie import message_registry
|
|
6
6
|
from queuebie.logger import get_logger
|
|
7
7
|
from queuebie.messages import Event
|
|
8
|
-
from testapp.messages.commands.my_commands import
|
|
8
|
+
from testapp.messages.commands.my_commands import (
|
|
9
|
+
CreateUser,
|
|
10
|
+
CriticalCommand,
|
|
11
|
+
DoSomething,
|
|
12
|
+
PersistSomething,
|
|
13
|
+
RaiseRuntimeError,
|
|
14
|
+
)
|
|
9
15
|
from testapp.messages.events.my_events import SomethingHappened, SomethingHappenedThatWantsToBePersisted
|
|
10
16
|
|
|
11
17
|
|
|
@@ -17,7 +23,7 @@ def handle_my_command(*, context: DoSomething) -> list[Event] | Event:
|
|
|
17
23
|
|
|
18
24
|
|
|
19
25
|
@message_registry.register_command(command=PersistSomething)
|
|
20
|
-
def
|
|
26
|
+
def handle_something_that_needs_persistence(*, context: PersistSomething) -> Event:
|
|
21
27
|
User.objects.create(username="testuser" + str(randint(1, 100)))
|
|
22
28
|
|
|
23
29
|
return SomethingHappenedThatWantsToBePersisted(any_var=1)
|
|
@@ -29,9 +35,21 @@ def handle_critical_command(*, context: CriticalCommand) -> None:
|
|
|
29
35
|
raise RuntimeError("Handler is broken.") # noqa: TRY003
|
|
30
36
|
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
@message_registry.register_command(command=CreateUser)
|
|
39
|
+
def create_user(context: CreateUser):
|
|
40
|
+
User.objects.create_user(username=context.username)
|
|
34
41
|
|
|
35
42
|
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
@message_registry.register_command(command=RaiseRuntimeError)
|
|
44
|
+
def raise_exception(context: RaiseRuntimeError):
|
|
45
|
+
raise RuntimeError(context.error_msg)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class MyClass:
|
|
49
|
+
"""
|
|
50
|
+
This is a test class since we observed the behaviour that in a similar setup, the "forced"
|
|
51
|
+
import was breaking mocking.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def process(self):
|
|
55
|
+
return 42
|
|
@@ -21,3 +21,13 @@ class SameNameCommand(Command):
|
|
|
21
21
|
@dataclass(kw_only=True)
|
|
22
22
|
class PersistSomething(Command):
|
|
23
23
|
any_var: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(kw_only=True)
|
|
27
|
+
class CreateUser(Command):
|
|
28
|
+
username: str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(kw_only=True)
|
|
32
|
+
class RaiseRuntimeError(Command):
|
|
33
|
+
error_msg: str
|
|
@@ -9,6 +9,7 @@ from django.test import override_settings
|
|
|
9
9
|
from queuebie import MessageRegistry
|
|
10
10
|
from queuebie.exceptions import RegisterOutOfScopeCommandError
|
|
11
11
|
from queuebie.settings import get_queuebie_cache_key
|
|
12
|
+
from testapp.handlers.commands.testapp import MyClass
|
|
12
13
|
from testapp.messages.commands.my_commands import CriticalCommand, DoSomething
|
|
13
14
|
from testapp.messages.events.my_events import (
|
|
14
15
|
SomethingHappened,
|
|
@@ -124,7 +125,7 @@ def test_message_autodiscover_regular():
|
|
|
124
125
|
message_registry.autodiscover()
|
|
125
126
|
|
|
126
127
|
# Assert one command registered
|
|
127
|
-
assert len(message_registry.command_dict) ==
|
|
128
|
+
assert len(message_registry.command_dict) == 5 # noqa: PLR2004
|
|
128
129
|
assert DoSomething.module_path() in message_registry.command_dict.keys()
|
|
129
130
|
assert CriticalCommand.module_path() in message_registry.command_dict.keys()
|
|
130
131
|
|
|
@@ -191,3 +192,16 @@ def test_message_autodiscover_load_handlers_from_cache_dummy_cache(*args):
|
|
|
191
192
|
|
|
192
193
|
assert len(commands) == 0
|
|
193
194
|
assert len(events) == 0
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@mock.patch.object(MyClass, "process")
|
|
198
|
+
def test_registry_forced_import_doesnt_break_mocking(mocked_process):
|
|
199
|
+
"""
|
|
200
|
+
This is a test for ensuring that the "forced" import isn't breaking mocking features.
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
def my_func():
|
|
204
|
+
return MyClass().process()
|
|
205
|
+
|
|
206
|
+
my_func()
|
|
207
|
+
mocked_process.assert_called_once()
|
|
@@ -8,8 +8,15 @@ from django.test import override_settings
|
|
|
8
8
|
from queuebie import MessageRegistry
|
|
9
9
|
from queuebie.database_blocker import DatabaseAccessDeniedError
|
|
10
10
|
from queuebie.exceptions import InvalidMessageTypeError
|
|
11
|
-
from queuebie.runner import
|
|
12
|
-
from testapp.messages.commands.my_commands import
|
|
11
|
+
from queuebie.runner import handle_message
|
|
12
|
+
from testapp.messages.commands.my_commands import (
|
|
13
|
+
CreateUser,
|
|
14
|
+
CriticalCommand,
|
|
15
|
+
DoSomething,
|
|
16
|
+
PersistSomething,
|
|
17
|
+
RaiseRuntimeError,
|
|
18
|
+
SameNameCommand,
|
|
19
|
+
)
|
|
13
20
|
from testapp.messages.events.my_events import (
|
|
14
21
|
SomethingHappened,
|
|
15
22
|
SomethingHappenedThatWantsToBePersistedViaEvent,
|
|
@@ -125,15 +132,8 @@ def test_handle_message_other_command_with_same_name(mocked_handle_command, *arg
|
|
|
125
132
|
|
|
126
133
|
@pytest.mark.django_db
|
|
127
134
|
@mock.patch("queuebie.registry.get_queuebie_strict_mode", return_value=False)
|
|
128
|
-
def
|
|
129
|
-
handler_list = [
|
|
130
|
-
{"module": "testapp.handlers.commands.testapp", "name": "create_user"},
|
|
131
|
-
{"module": "testapp.handlers.commands.testapp", "name": "raise_exception"},
|
|
132
|
-
]
|
|
133
|
-
|
|
134
|
-
message = DoSomething(my_var=1)
|
|
135
|
-
|
|
135
|
+
def test_handle_message_atomic_works(*args):
|
|
136
136
|
with pytest.raises(RuntimeError, match="Something is broken."):
|
|
137
|
-
|
|
137
|
+
handle_message([CreateUser(username="username"), RaiseRuntimeError(error_msg="Something is broken.")])
|
|
138
138
|
|
|
139
139
|
assert User.objects.filter(username="username").exists() is False
|
django_queuebie-0.2.0/CHANGES.md
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import importlib
|
|
2
|
-
from contextlib import nullcontext
|
|
3
|
-
|
|
4
|
-
from django.db import transaction
|
|
5
|
-
|
|
6
|
-
from queuebie import message_registry
|
|
7
|
-
from queuebie.database_blocker import BlockDatabaseAccess
|
|
8
|
-
from queuebie.exceptions import InvalidMessageTypeError
|
|
9
|
-
from queuebie.logger import get_logger
|
|
10
|
-
from queuebie.messages import Command, Event, Message
|
|
11
|
-
from queuebie.settings import get_queuebie_strict_mode
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def handle_message(messages: Message | list[Message]) -> None:
|
|
15
|
-
queue: list[Message] = messages if isinstance(messages, list) else [messages]
|
|
16
|
-
|
|
17
|
-
for message in queue:
|
|
18
|
-
if not isinstance(message, (Command, Event)):
|
|
19
|
-
raise InvalidMessageTypeError(class_name=message.__class__.__name__)
|
|
20
|
-
|
|
21
|
-
# Run auto-registry
|
|
22
|
-
message_registry.autodiscover()
|
|
23
|
-
|
|
24
|
-
while queue:
|
|
25
|
-
message = queue.pop(0)
|
|
26
|
-
if isinstance(message, Command):
|
|
27
|
-
handler_list = message_registry.command_dict.get(message.module_path(), [])
|
|
28
|
-
block_db_access = False
|
|
29
|
-
else:
|
|
30
|
-
handler_list = message_registry.event_dict.get(message.module_path(), [])
|
|
31
|
-
block_db_access = True if get_queuebie_strict_mode() else False
|
|
32
|
-
|
|
33
|
-
new_messages = _process_message(handler_list=handler_list, message=message, block_db_access=block_db_access)
|
|
34
|
-
queue.extend(new_messages)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def _process_message(*, handler_list: list, message: [Command, Event], block_db_access: bool) -> list[Message]:
|
|
38
|
-
"""
|
|
39
|
-
Handler to process messages of type "Command"
|
|
40
|
-
"""
|
|
41
|
-
logger = get_logger()
|
|
42
|
-
messages = []
|
|
43
|
-
|
|
44
|
-
# TODO: should the whole chain be atomic and not just the handler?
|
|
45
|
-
with transaction.atomic():
|
|
46
|
-
for handler in handler_list:
|
|
47
|
-
try:
|
|
48
|
-
logger.debug(
|
|
49
|
-
f"Handling command '{message.module_path()}' ({message.uuid}) with handler '{handler['name']}'."
|
|
50
|
-
)
|
|
51
|
-
module = importlib.import_module(handler["module"])
|
|
52
|
-
handler_function = getattr(module, handler["name"])
|
|
53
|
-
with BlockDatabaseAccess() if block_db_access else nullcontext():
|
|
54
|
-
handler_messages = handler_function(context=message) or []
|
|
55
|
-
handler_messages = handler_messages if isinstance(handler_messages, list) else [handler_messages]
|
|
56
|
-
if len(handler_messages) > 0:
|
|
57
|
-
messages.extend(handler_messages)
|
|
58
|
-
uuid_list = [f"{m!s}" for m in handler_messages]
|
|
59
|
-
logger.debug(f"New messages: {uuid_list!s}")
|
|
60
|
-
except Exception as e:
|
|
61
|
-
logger.debug(f"Exception handling command {message.module_path()}: {e!s}")
|
|
62
|
-
raise e from e
|
|
63
|
-
|
|
64
|
-
return messages
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|