python-cqrs 4.10.0__tar.gz → 4.10.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.
Files changed (88) hide show
  1. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/PKG-INFO +4 -2
  2. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/pyproject.toml +3 -2
  3. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/outbox/sqlalchemy.py +64 -21
  4. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/storage/sqlalchemy.py +13 -6
  5. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/python_cqrs.egg-info/PKG-INFO +4 -2
  6. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/python_cqrs.egg-info/requires.txt +4 -1
  7. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/LICENSE +0 -0
  8. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/README.md +0 -0
  9. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/setup.cfg +0 -0
  10. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/__init__.py +0 -0
  11. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/adapters/__init__.py +0 -0
  12. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/adapters/amqp.py +0 -0
  13. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/adapters/circuit_breaker.py +0 -0
  14. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/adapters/kafka.py +0 -0
  15. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/adapters/protocol.py +0 -0
  16. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/circuit_breaker.py +0 -0
  17. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/compressors/__init__.py +0 -0
  18. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/compressors/protocol.py +0 -0
  19. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/compressors/zlib.py +0 -0
  20. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/container/__init__.py +0 -0
  21. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/container/dependency_injector.py +0 -0
  22. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/container/di.py +0 -0
  23. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/container/protocol.py +0 -0
  24. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/deserializers/__init__.py +0 -0
  25. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/deserializers/exceptions.py +0 -0
  26. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/deserializers/json.py +0 -0
  27. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/dispatcher/__init__.py +0 -0
  28. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/dispatcher/event.py +0 -0
  29. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/dispatcher/exceptions.py +0 -0
  30. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/dispatcher/models.py +0 -0
  31. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/dispatcher/request.py +0 -0
  32. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/dispatcher/saga.py +0 -0
  33. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/dispatcher/streaming.py +0 -0
  34. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/events/__init__.py +0 -0
  35. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/events/bootstrap.py +0 -0
  36. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/events/event.py +0 -0
  37. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/events/event_emitter.py +0 -0
  38. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/events/event_handler.py +0 -0
  39. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/events/event_processor.py +0 -0
  40. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/events/fallback.py +0 -0
  41. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/events/map.py +0 -0
  42. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/generic_utils.py +0 -0
  43. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/mediator.py +0 -0
  44. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/message_brokers/__init__.py +0 -0
  45. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/message_brokers/amqp.py +0 -0
  46. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/message_brokers/devnull.py +0 -0
  47. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/message_brokers/kafka.py +0 -0
  48. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/message_brokers/protocol.py +0 -0
  49. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/middlewares/__init__.py +0 -0
  50. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/middlewares/base.py +0 -0
  51. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/middlewares/logging.py +0 -0
  52. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/outbox/__init__.py +0 -0
  53. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/outbox/map.py +0 -0
  54. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/outbox/mock.py +0 -0
  55. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/outbox/repository.py +0 -0
  56. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/producer.py +0 -0
  57. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/requests/__init__.py +0 -0
  58. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/requests/bootstrap.py +0 -0
  59. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/requests/cor_request_handler.py +0 -0
  60. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/requests/fallback.py +0 -0
  61. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/requests/map.py +0 -0
  62. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/requests/mermaid.py +0 -0
  63. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/requests/request.py +0 -0
  64. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/requests/request_handler.py +0 -0
  65. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/response.py +0 -0
  66. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/__init__.py +0 -0
  67. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/bootstrap.py +0 -0
  68. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/circuit_breaker.py +0 -0
  69. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/compensation.py +0 -0
  70. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/execution.py +0 -0
  71. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/fallback.py +0 -0
  72. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/mermaid.py +0 -0
  73. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/models.py +0 -0
  74. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/recovery.py +0 -0
  75. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/saga.py +0 -0
  76. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/step.py +0 -0
  77. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/storage/__init__.py +0 -0
  78. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/storage/enums.py +0 -0
  79. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/storage/memory.py +0 -0
  80. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/storage/models.py +0 -0
  81. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/storage/protocol.py +0 -0
  82. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/saga/validation.py +0 -0
  83. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/serializers/__init__.py +0 -0
  84. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/serializers/default.py +0 -0
  85. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/cqrs/types.py +0 -0
  86. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/python_cqrs.egg-info/SOURCES.txt +0 -0
  87. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/python_cqrs.egg-info/dependency_links.txt +0 -0
  88. {python_cqrs-4.10.0 → python_cqrs-4.10.1}/src/python_cqrs.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-cqrs
3
- Version: 4.10.0
3
+ Version: 4.10.1
4
4
  Summary: Event-Driven Architecture Framework for Distributed Systems
5
5
  Author-email: Vadim Kozyrevskiy <vadikko2@mail.ru>, Dmitry Kutlubaev <kutlubaev00@mail.ru>
6
6
  Maintainer-email: Vadim Kozyrevskiy <vadikko2@mail.ru>
@@ -21,9 +21,9 @@ Requires-Dist: di[anyio]==0.*
21
21
  Requires-Dist: dependency-injector>=4.0
22
22
  Requires-Dist: orjson==3.*
23
23
  Requires-Dist: pydantic==2.*
24
+ Requires-Dist: sqlalchemy[asyncio]==2.0.*
24
25
  Requires-Dist: python-dotenv==1.*
25
26
  Requires-Dist: retry-async==0.1.*
26
- Requires-Dist: sqlalchemy[asyncio]==2.0.*
27
27
  Requires-Dist: typing-extensions>=4.0
28
28
  Provides-Extra: aiobreaker
29
29
  Requires-Dist: aiobreaker>=0.3.0; extra == "aiobreaker"
@@ -57,6 +57,8 @@ Provides-Extra: kafka
57
57
  Requires-Dist: aiokafka==0.10.0; extra == "kafka"
58
58
  Provides-Extra: rabbit
59
59
  Requires-Dist: aio-pika==9.3.0; extra == "rabbit"
60
+ Provides-Extra: sqlalchemy
61
+ Requires-Dist: sqlalchemy[asyncio]==2.0.*; extra == "sqlalchemy"
60
62
  Dynamic: license-file
61
63
 
62
64
  <div align="center">
@@ -21,9 +21,9 @@ dependencies = [
21
21
  "dependency-injector>=4.0",
22
22
  "orjson==3.*",
23
23
  "pydantic==2.*",
24
+ "sqlalchemy[asyncio]==2.0.*",
24
25
  "python-dotenv==1.*",
25
26
  "retry-async==0.1.*",
26
- "sqlalchemy[asyncio]==2.0.*",
27
27
  "typing-extensions>=4.0"
28
28
  ]
29
29
  description = "Event-Driven Architecture Framework for Distributed Systems"
@@ -31,7 +31,7 @@ maintainers = [{name = "Vadim Kozyrevskiy", email = "vadikko2@mail.ru"}]
31
31
  name = "python-cqrs"
32
32
  readme = "README.md"
33
33
  requires-python = ">=3.10"
34
- version = "4.10.0"
34
+ version = "4.10.1"
35
35
 
36
36
  [project.optional-dependencies]
37
37
  aiobreaker = ["aiobreaker>=0.3.0"]
@@ -68,6 +68,7 @@ examples = [
68
68
  ]
69
69
  kafka = ["aiokafka==0.10.0"]
70
70
  rabbit = ["aio-pika==9.3.0"]
71
+ sqlalchemy = ["sqlalchemy[asyncio]==2.0.*"]
71
72
 
72
73
  [project.urls]
73
74
  Documentation = "https://mkdocs.python-cqrs.dev/"
@@ -1,18 +1,29 @@
1
+ import datetime
1
2
  import logging
2
3
  import typing
3
4
 
4
5
  import dotenv
5
6
  import orjson
6
- import sqlalchemy
7
- from sqlalchemy import func
8
- from sqlalchemy.dialects import mysql
9
- from sqlalchemy.ext.asyncio import session as sql_session
10
- from sqlalchemy.orm import DeclarativeMeta, registry
11
-
12
7
  import cqrs
8
+ import uuid
13
9
  from cqrs import compressors
14
10
  from cqrs.outbox import map, repository
15
11
 
12
+ try:
13
+ import sqlalchemy
14
+
15
+ from sqlalchemy import func
16
+ from sqlalchemy.orm import Mapped, mapped_column, DeclarativeMeta, registry
17
+ from sqlalchemy.ext.asyncio import session as sql_session
18
+ from sqlalchemy.dialects import postgresql
19
+ except ImportError:
20
+ raise ImportError(
21
+ "You are trying to use SQLAlchemy outbox implementation, "
22
+ "but 'sqlalchemy' is not installed. "
23
+ "Please install it using: pip install python-cqrs[sqlalchemy]",
24
+ ) from None
25
+
26
+
16
27
  Base = registry().generate_base()
17
28
 
18
29
  logger = logging.getLogger(__name__)
@@ -24,6 +35,39 @@ DEFAULT_OUTBOX_TABLE_NAME = "outbox"
24
35
  MAX_FLUSH_COUNTER_VALUE = 5
25
36
 
26
37
 
38
+ class BinaryUUID(sqlalchemy.TypeDecorator):
39
+ """Stores the UUID as a native UUID in Postgres and as BINARY(16) in other databases (MySQL)."""
40
+
41
+ impl = sqlalchemy.BINARY(16)
42
+ cache_ok = True
43
+
44
+ def load_dialect_impl(self, dialect):
45
+ if dialect.name == "postgresql":
46
+ return dialect.type_descriptor(postgresql.UUID())
47
+ else:
48
+ return dialect.type_descriptor(sqlalchemy.BINARY(16))
49
+
50
+ def process_bind_param(self, value, dialect):
51
+ if value is None:
52
+ return value
53
+ if isinstance(value, str):
54
+ value = uuid.UUID(value)
55
+ if dialect.name == "postgresql":
56
+ return value # asyncpg works with uuid.UUID
57
+ if isinstance(value, uuid.UUID):
58
+ return value.bytes # For MySQL return 16 bytes
59
+ return value
60
+
61
+ def process_result_value(self, value, dialect):
62
+ if value is None:
63
+ return value
64
+ if dialect.name == "postgresql":
65
+ return value # asyncpg return uuid.UUID
66
+ if isinstance(value, bytes):
67
+ return uuid.UUID(bytes=value) # From MySQL got bytes, make UUID
68
+ return value
69
+
70
+
27
71
  class OutboxModel(Base):
28
72
  __tablename__ = DEFAULT_OUTBOX_TABLE_NAME
29
73
 
@@ -34,57 +78,56 @@ class OutboxModel(Base):
34
78
  name="event_id_unique_index",
35
79
  ),
36
80
  )
37
- id = sqlalchemy.Column(
38
- sqlalchemy.BigInteger(),
81
+ id: Mapped[int] = mapped_column(
82
+ sqlalchemy.BigInteger,
39
83
  sqlalchemy.Identity(),
40
84
  primary_key=True,
41
85
  nullable=False,
42
86
  autoincrement=True,
43
87
  comment="Identity",
44
88
  )
45
- event_id = sqlalchemy.Column(
46
- sqlalchemy.Uuid,
89
+ event_id: Mapped[uuid.UUID] = mapped_column(
90
+ BinaryUUID,
47
91
  nullable=False,
48
92
  comment="Event idempotency id",
49
93
  )
50
- event_id_bin = sqlalchemy.Column(
94
+ event_id_bin: Mapped[bytes] = mapped_column(
51
95
  sqlalchemy.BINARY(16),
52
96
  nullable=False,
53
97
  comment="Event idempotency id in 16 bit presentation",
54
98
  )
55
- event_status = sqlalchemy.Column(
99
+ event_status: Mapped[repository.EventStatus] = mapped_column(
56
100
  sqlalchemy.Enum(repository.EventStatus),
57
101
  nullable=False,
58
102
  default=repository.EventStatus.NEW,
59
103
  comment="Event producing status",
60
104
  )
61
- flush_counter = sqlalchemy.Column(
62
- sqlalchemy.SmallInteger(),
105
+ flush_counter: Mapped[int] = mapped_column(
106
+ sqlalchemy.SmallInteger,
63
107
  nullable=False,
64
108
  default=0,
65
109
  comment="Event producing flush counter",
66
110
  )
67
- event_name = sqlalchemy.Column(
111
+ event_name: Mapped[typing.Text] = mapped_column(
68
112
  sqlalchemy.String(255),
69
113
  nullable=False,
70
114
  comment="Event name",
71
115
  )
72
- topic = sqlalchemy.Column(
116
+ topic: Mapped[typing.Text] = mapped_column(
73
117
  sqlalchemy.String(255),
74
118
  nullable=False,
75
119
  comment="Event topic",
76
120
  default="",
77
121
  )
78
- created_at = sqlalchemy.Column(
122
+ created_at: Mapped[datetime.datetime] = mapped_column(
79
123
  sqlalchemy.DateTime,
80
124
  nullable=False,
81
125
  server_default=func.now(),
82
126
  comment="Event creation timestamp",
83
127
  )
84
- payload = sqlalchemy.Column(
85
- mysql.BLOB,
128
+ payload: Mapped[bytes] = mapped_column(
129
+ sqlalchemy.LargeBinary,
86
130
  nullable=False,
87
- default={},
88
131
  comment="Event payload",
89
132
  )
90
133
 
@@ -174,7 +217,7 @@ class SqlAlchemyOutboxedEventRepository(repository.OutboxedEventRepository):
174
217
  self.session.add(
175
218
  OutboxModel(
176
219
  event_id=event.event_id,
177
- event_id_bin=func.UUID_TO_BIN(event.event_id),
220
+ event_id_bin=event.event_id.bytes,
178
221
  event_name=event.event_name,
179
222
  created_at=event.event_timestamp,
180
223
  payload=bytes_payload,
@@ -6,17 +6,24 @@ import typing
6
6
  import uuid
7
7
 
8
8
  import dotenv
9
- import sqlalchemy
10
- from sqlalchemy import func
11
- from sqlalchemy.exc import SQLAlchemyError
12
- from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
13
- from sqlalchemy.orm import registry
14
-
15
9
  from cqrs.dispatcher.exceptions import SagaConcurrencyError
16
10
  from cqrs.saga.storage.enums import SagaStatus, SagaStepStatus
17
11
  from cqrs.saga.storage.models import SagaLogEntry
18
12
  from cqrs.saga.storage.protocol import ISagaStorage, SagaStorageRun
19
13
 
14
+ try:
15
+ import sqlalchemy
16
+ from sqlalchemy import func
17
+ from sqlalchemy.exc import SQLAlchemyError
18
+ from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
19
+ from sqlalchemy.orm import registry
20
+ except ImportError:
21
+ raise ImportError(
22
+ "You are trying to use SQLAlchemy saga storage implementation, "
23
+ "but 'sqlalchemy' is not installed. "
24
+ "Please install it using: pip install python-cqrs[sqlalchemy]",
25
+ ) from None
26
+
20
27
  Base = registry().generate_base()
21
28
  logger = logging.getLogger(__name__)
22
29
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-cqrs
3
- Version: 4.10.0
3
+ Version: 4.10.1
4
4
  Summary: Event-Driven Architecture Framework for Distributed Systems
5
5
  Author-email: Vadim Kozyrevskiy <vadikko2@mail.ru>, Dmitry Kutlubaev <kutlubaev00@mail.ru>
6
6
  Maintainer-email: Vadim Kozyrevskiy <vadikko2@mail.ru>
@@ -21,9 +21,9 @@ Requires-Dist: di[anyio]==0.*
21
21
  Requires-Dist: dependency-injector>=4.0
22
22
  Requires-Dist: orjson==3.*
23
23
  Requires-Dist: pydantic==2.*
24
+ Requires-Dist: sqlalchemy[asyncio]==2.0.*
24
25
  Requires-Dist: python-dotenv==1.*
25
26
  Requires-Dist: retry-async==0.1.*
26
- Requires-Dist: sqlalchemy[asyncio]==2.0.*
27
27
  Requires-Dist: typing-extensions>=4.0
28
28
  Provides-Extra: aiobreaker
29
29
  Requires-Dist: aiobreaker>=0.3.0; extra == "aiobreaker"
@@ -57,6 +57,8 @@ Provides-Extra: kafka
57
57
  Requires-Dist: aiokafka==0.10.0; extra == "kafka"
58
58
  Provides-Extra: rabbit
59
59
  Requires-Dist: aio-pika==9.3.0; extra == "rabbit"
60
+ Provides-Extra: sqlalchemy
61
+ Requires-Dist: sqlalchemy[asyncio]==2.0.*; extra == "sqlalchemy"
60
62
  Dynamic: license-file
61
63
 
62
64
  <div align="center">
@@ -3,9 +3,9 @@ di[anyio]==0.*
3
3
  dependency-injector>=4.0
4
4
  orjson==3.*
5
5
  pydantic==2.*
6
+ sqlalchemy[asyncio]==2.0.*
6
7
  python-dotenv==1.*
7
8
  retry-async==0.1.*
8
- sqlalchemy[asyncio]==2.0.*
9
9
  typing-extensions>=4.0
10
10
 
11
11
  [aiobreaker]
@@ -44,3 +44,6 @@ aiokafka==0.10.0
44
44
 
45
45
  [rabbit]
46
46
  aio-pika==9.3.0
47
+
48
+ [sqlalchemy]
49
+ sqlalchemy[asyncio]==2.0.*
File without changes
File without changes
File without changes