sqla-fancy-core 1.1.1__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.

Potentially problematic release.


This version of sqla-fancy-core might be problematic. Click here for more details.

Files changed (22) hide show
  1. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/.gitignore +1 -0
  2. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/PKG-INFO +84 -8
  3. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/README.md +83 -7
  4. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/pyproject.toml +1 -1
  5. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/sqla_fancy_core/__init__.py +7 -1
  6. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/sqla_fancy_core/decorators.py +50 -14
  7. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/sqla_fancy_core/wrappers.py +188 -2
  8. sqla_fancy_core-1.2.0/tests/test_async_atomic.py +96 -0
  9. sqla_fancy_core-1.2.0/tests/test_atomic.py +92 -0
  10. sqla_fancy_core-1.2.0/tests/test_decorators.py +448 -0
  11. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/uv.lock +1 -1
  12. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/.github/workflows/ci.yaml +0 -0
  13. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/LICENSE +0 -0
  14. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/sqla_fancy_core/factories.py +0 -0
  15. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/tests/__init__.py +0 -0
  16. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/tests/test_async_fancy_engine.py +0 -0
  17. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/tests/test_connect.py +0 -0
  18. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/tests/test_fancy_engine.py +0 -0
  19. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/tests/test_field.py +0 -0
  20. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/tests/test_table_factory.py +0 -0
  21. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/tests/test_table_factory_async.py +0 -0
  22. {sqla_fancy_core-1.1.1 → sqla_fancy_core-1.2.0}/tests/test_transact.py +0 -0
@@ -158,3 +158,4 @@ cython_debug/
158
158
  # and can be added to the global gitignore or merged into this file. For a more nuclear
159
159
  # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
160
  #.idea/
161
+ .vscode/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqla-fancy-core
3
- Version: 1.1.1
3
+ Version: 1.2.0
4
4
  Summary: SQLAlchemy core, but fancier
5
5
  Project-URL: Homepage, https://github.com/sayanarijit/sqla-fancy-core
6
6
  Author-email: Arijit Basu <sayanarijit@gmail.com>
@@ -158,13 +158,18 @@ async with engine.begin() as txn:
158
158
 
159
159
  ## Fancy Engine Wrappers
160
160
 
161
- `sqla-fancy-core` provides `fancy` engine wrappers that simplify database interactions by automatically managing connections and transactions. The `fancy` function wraps a SQLAlchemy `Engine` or `AsyncEngine` and returns a wrapper object with two primary methods:
161
+ `sqla-fancy-core` provides `fancy` engine wrappers that simplify database interactions by automatically managing connections and transactions. The `fancy` function wraps a SQLAlchemy `Engine` or `AsyncEngine` and returns a wrapper object with the following methods:
162
162
 
163
163
  - `x(conn, query)`: Executes a query. It uses the provided `conn` if available, otherwise it creates a new connection.
164
- - `tx(conn, query)`: Executes a query within a transaction. It uses the provided `conn` if available, otherwise it creates a new connection and begins a transaction.
164
+ - `tx(conn, query)`: Executes a query within a transaction. It uses the provided `conn` if available, otherwise it tries to use the atomic context if within one, else creates a new connection and begins a transaction.
165
+ - `atomic()`: A context manager for grouping multiple operations in a single transaction scope.
166
+ - `ax(query)`: Executes a query using the connection from the active `atomic()` context. Raises `AtomicContextError` if called outside an `atomic()` block.
167
+ - `atx(query)`: Executes a query inside a transaction automatically. If already inside `atomic()`, it reuses the same connection and transaction; otherwise it opens a new transaction just for this call.
165
168
 
166
169
  This is particularly useful for writing connection-agnostic query functions.
167
170
 
171
+ ### Basic Examples
172
+
168
173
  **Sync Example:**
169
174
 
170
175
  ```python
@@ -208,6 +213,78 @@ async def main():
208
213
  assert await get_data(conn) == 1
209
214
  ```
210
215
 
216
+ ### Using the atomic() Context Manager
217
+
218
+ The `atomic()` context manager lets you group several database operations within one transactional scope. Queries executed with `ax()` inside this context all use the same connection. Nested `atomic()` contexts reuse the outer connection automatically.
219
+
220
+ **Sync Example:**
221
+
222
+ ```python
223
+ import sqlalchemy as sa
224
+ from sqla_fancy_core import fancy, TableFactory
225
+
226
+ tf = TableFactory()
227
+
228
+ class User:
229
+ id = tf.auto_id()
230
+ name = tf.string("name")
231
+ Table = tf("users")
232
+
233
+ engine = sa.create_engine("sqlite:///:memory:")
234
+ tf.metadata.create_all(engine)
235
+ fancy_engine = fancy(engine)
236
+
237
+ # Group operations in one transaction
238
+ with fancy_engine.atomic():
239
+ fancy_engine.ax(sa.insert(User.Table).values(name="Alice"))
240
+ fancy_engine.ax(sa.insert(User.Table).values(name="Bob"))
241
+ result = fancy_engine.ax(sa.select(sa.func.count()).select_from(User.Table))
242
+ count = result.scalar_one()
243
+ assert count == 2
244
+ ```
245
+
246
+ **Async Example:**
247
+
248
+ ```python
249
+ import sqlalchemy as sa
250
+ from sqlalchemy.ext.asyncio import create_async_engine
251
+ from sqla_fancy_core import fancy, TableFactory
252
+
253
+ tf = TableFactory()
254
+
255
+ class User:
256
+ id = tf.auto_id()
257
+ name = tf.string("name")
258
+ Table = tf("users")
259
+
260
+ async def run_example():
261
+ engine = create_async_engine("sqlite+aiosqlite:///:memory:")
262
+ async with engine.begin() as conn:
263
+ await conn.run_sync(tf.metadata.create_all)
264
+
265
+ fancy_engine = fancy(engine)
266
+
267
+ async with fancy_engine.atomic():
268
+ await fancy_engine.ax(sa.insert(User.Table).values(name="Alice"))
269
+ await fancy_engine.ax(sa.insert(User.Table).values(name="Bob"))
270
+ result = await fancy_engine.ax(sa.select(sa.func.count()).select_from(User.Table))
271
+ count = result.scalar_one()
272
+ assert count == 2
273
+ ```
274
+
275
+ **Key Points:**
276
+
277
+ - `ax()` must be called inside an `atomic()` context. Calling it elsewhere raises `AtomicContextError`.
278
+ - `atx()` is a safe/ergonomic helper: it will run inside the current `atomic()` transaction when present, or create a short-lived transaction otherwise.
279
+ - Nesting `atomic()` contexts is safe. Inner contexts share the outer connection instead of creating a new transaction.
280
+ - On normal exit, the transaction commits automatically. On exception, it rolls back.
281
+
282
+ ### ax vs atx vs tx
283
+
284
+ - `ax(q)`: Only valid inside `atomic()`. Uses the ambient transactional connection. Great for batch operations grouped by an outer context.
285
+ - `atx(q)`: Fire-and-forget in a transaction. Reuses the ambient `atomic()` connection if present; otherwise starts and commits its own transaction.
286
+ - `tx(conn, q)`: Low-level primitive. If `conn` is provided, it executes within it, creating a transaction when needed; if `None`, it prefers the `atomic()` connection when active or opens a new transactional connection.
287
+
211
288
  ## Decorators: Inject, connect, transact
212
289
 
213
290
  When writing plain SQLAlchemy Core code, you often pass connections around and manage transactions manually. The decorators in `sqla-fancy-core` help you keep functions connection-agnostic and composable, while remaining explicit and safe.
@@ -242,27 +319,24 @@ users = sa.Table(
242
319
  )
243
320
  metadata.create_all(engine)
244
321
 
245
- # 1) Ensure a connection is available (no implicit transaction)
246
322
  @connect
247
323
  def get_user_count(conn=Inject(engine)):
248
324
  return conn.execute(sa.select(sa.func.count()).select_from(users)).scalar_one()
249
325
 
250
326
  assert get_user_count() == 0
251
327
 
252
- # 2) Wrap in a transaction automatically
253
328
  @transact
254
329
  def create_user(name: str, conn=Inject(engine)):
255
330
  conn.execute(sa.insert(users).values(name=name))
256
331
 
332
+ # Without an explicit transaction
257
333
  create_user("alice")
258
334
  assert get_user_count() == 1
259
335
 
260
- # 3) Reuse an explicit connection or transaction
336
+ # With an explicit transaction
261
337
  with engine.begin() as txn:
262
338
  create_user("bob", conn=txn)
263
339
  assert get_user_count(conn=txn) == 2
264
-
265
- assert get_user_count() == 2
266
340
  ```
267
341
 
268
342
  ### Async examples
@@ -293,10 +367,12 @@ async def get_user_count(conn=Inject(engine)):
293
367
  async def create_user(name: str, conn=Inject(engine)):
294
368
  await conn.execute(sa.insert(users).values(name=name))
295
369
 
370
+ # Without an explicit transaction
296
371
  assert await get_user_count() == 0
297
372
  await create_user("carol")
298
373
  assert await get_user_count() == 1
299
374
 
375
+ # With an explicit transaction
300
376
  async with engine.connect() as conn:
301
377
  await create_user("dave", conn=conn)
302
378
  assert await get_user_count(conn=conn) == 2
@@ -106,13 +106,18 @@ async with engine.begin() as txn:
106
106
 
107
107
  ## Fancy Engine Wrappers
108
108
 
109
- `sqla-fancy-core` provides `fancy` engine wrappers that simplify database interactions by automatically managing connections and transactions. The `fancy` function wraps a SQLAlchemy `Engine` or `AsyncEngine` and returns a wrapper object with two primary methods:
109
+ `sqla-fancy-core` provides `fancy` engine wrappers that simplify database interactions by automatically managing connections and transactions. The `fancy` function wraps a SQLAlchemy `Engine` or `AsyncEngine` and returns a wrapper object with the following methods:
110
110
 
111
111
  - `x(conn, query)`: Executes a query. It uses the provided `conn` if available, otherwise it creates a new connection.
112
- - `tx(conn, query)`: Executes a query within a transaction. It uses the provided `conn` if available, otherwise it creates a new connection and begins a transaction.
112
+ - `tx(conn, query)`: Executes a query within a transaction. It uses the provided `conn` if available, otherwise it tries to use the atomic context if within one, else creates a new connection and begins a transaction.
113
+ - `atomic()`: A context manager for grouping multiple operations in a single transaction scope.
114
+ - `ax(query)`: Executes a query using the connection from the active `atomic()` context. Raises `AtomicContextError` if called outside an `atomic()` block.
115
+ - `atx(query)`: Executes a query inside a transaction automatically. If already inside `atomic()`, it reuses the same connection and transaction; otherwise it opens a new transaction just for this call.
113
116
 
114
117
  This is particularly useful for writing connection-agnostic query functions.
115
118
 
119
+ ### Basic Examples
120
+
116
121
  **Sync Example:**
117
122
 
118
123
  ```python
@@ -156,6 +161,78 @@ async def main():
156
161
  assert await get_data(conn) == 1
157
162
  ```
158
163
 
164
+ ### Using the atomic() Context Manager
165
+
166
+ The `atomic()` context manager lets you group several database operations within one transactional scope. Queries executed with `ax()` inside this context all use the same connection. Nested `atomic()` contexts reuse the outer connection automatically.
167
+
168
+ **Sync Example:**
169
+
170
+ ```python
171
+ import sqlalchemy as sa
172
+ from sqla_fancy_core import fancy, TableFactory
173
+
174
+ tf = TableFactory()
175
+
176
+ class User:
177
+ id = tf.auto_id()
178
+ name = tf.string("name")
179
+ Table = tf("users")
180
+
181
+ engine = sa.create_engine("sqlite:///:memory:")
182
+ tf.metadata.create_all(engine)
183
+ fancy_engine = fancy(engine)
184
+
185
+ # Group operations in one transaction
186
+ with fancy_engine.atomic():
187
+ fancy_engine.ax(sa.insert(User.Table).values(name="Alice"))
188
+ fancy_engine.ax(sa.insert(User.Table).values(name="Bob"))
189
+ result = fancy_engine.ax(sa.select(sa.func.count()).select_from(User.Table))
190
+ count = result.scalar_one()
191
+ assert count == 2
192
+ ```
193
+
194
+ **Async Example:**
195
+
196
+ ```python
197
+ import sqlalchemy as sa
198
+ from sqlalchemy.ext.asyncio import create_async_engine
199
+ from sqla_fancy_core import fancy, TableFactory
200
+
201
+ tf = TableFactory()
202
+
203
+ class User:
204
+ id = tf.auto_id()
205
+ name = tf.string("name")
206
+ Table = tf("users")
207
+
208
+ async def run_example():
209
+ engine = create_async_engine("sqlite+aiosqlite:///:memory:")
210
+ async with engine.begin() as conn:
211
+ await conn.run_sync(tf.metadata.create_all)
212
+
213
+ fancy_engine = fancy(engine)
214
+
215
+ async with fancy_engine.atomic():
216
+ await fancy_engine.ax(sa.insert(User.Table).values(name="Alice"))
217
+ await fancy_engine.ax(sa.insert(User.Table).values(name="Bob"))
218
+ result = await fancy_engine.ax(sa.select(sa.func.count()).select_from(User.Table))
219
+ count = result.scalar_one()
220
+ assert count == 2
221
+ ```
222
+
223
+ **Key Points:**
224
+
225
+ - `ax()` must be called inside an `atomic()` context. Calling it elsewhere raises `AtomicContextError`.
226
+ - `atx()` is a safe/ergonomic helper: it will run inside the current `atomic()` transaction when present, or create a short-lived transaction otherwise.
227
+ - Nesting `atomic()` contexts is safe. Inner contexts share the outer connection instead of creating a new transaction.
228
+ - On normal exit, the transaction commits automatically. On exception, it rolls back.
229
+
230
+ ### ax vs atx vs tx
231
+
232
+ - `ax(q)`: Only valid inside `atomic()`. Uses the ambient transactional connection. Great for batch operations grouped by an outer context.
233
+ - `atx(q)`: Fire-and-forget in a transaction. Reuses the ambient `atomic()` connection if present; otherwise starts and commits its own transaction.
234
+ - `tx(conn, q)`: Low-level primitive. If `conn` is provided, it executes within it, creating a transaction when needed; if `None`, it prefers the `atomic()` connection when active or opens a new transactional connection.
235
+
159
236
  ## Decorators: Inject, connect, transact
160
237
 
161
238
  When writing plain SQLAlchemy Core code, you often pass connections around and manage transactions manually. The decorators in `sqla-fancy-core` help you keep functions connection-agnostic and composable, while remaining explicit and safe.
@@ -190,27 +267,24 @@ users = sa.Table(
190
267
  )
191
268
  metadata.create_all(engine)
192
269
 
193
- # 1) Ensure a connection is available (no implicit transaction)
194
270
  @connect
195
271
  def get_user_count(conn=Inject(engine)):
196
272
  return conn.execute(sa.select(sa.func.count()).select_from(users)).scalar_one()
197
273
 
198
274
  assert get_user_count() == 0
199
275
 
200
- # 2) Wrap in a transaction automatically
201
276
  @transact
202
277
  def create_user(name: str, conn=Inject(engine)):
203
278
  conn.execute(sa.insert(users).values(name=name))
204
279
 
280
+ # Without an explicit transaction
205
281
  create_user("alice")
206
282
  assert get_user_count() == 1
207
283
 
208
- # 3) Reuse an explicit connection or transaction
284
+ # With an explicit transaction
209
285
  with engine.begin() as txn:
210
286
  create_user("bob", conn=txn)
211
287
  assert get_user_count(conn=txn) == 2
212
-
213
- assert get_user_count() == 2
214
288
  ```
215
289
 
216
290
  ### Async examples
@@ -241,10 +315,12 @@ async def get_user_count(conn=Inject(engine)):
241
315
  async def create_user(name: str, conn=Inject(engine)):
242
316
  await conn.execute(sa.insert(users).values(name=name))
243
317
 
318
+ # Without an explicit transaction
244
319
  assert await get_user_count() == 0
245
320
  await create_user("carol")
246
321
  assert await get_user_count() == 1
247
322
 
323
+ # With an explicit transaction
248
324
  async with engine.connect() as conn:
249
325
  await create_user("dave", conn=conn)
250
326
  assert await get_user_count(conn=conn) == 2
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = 'sqla-fancy-core'
3
- version = '1.1.1'
3
+ version = '1.2.0'
4
4
  description = 'SQLAlchemy core, but fancier'
5
5
  readme = 'README.md'
6
6
  requires-python = ">=3.7"
@@ -1,5 +1,11 @@
1
1
  """SQLAlchemy core, but fancier."""
2
2
 
3
3
  from sqla_fancy_core.factories import TableFactory # noqa
4
- from sqla_fancy_core.wrappers import FancyEngineWrapper, AsyncFancyEngineWrapper, fancy # noqa
4
+ from sqla_fancy_core.wrappers import ( # noqa
5
+ FancyEngineWrapper,
6
+ AsyncFancyEngineWrapper,
7
+ fancy,
8
+ FancyError,
9
+ AtomicContextError,
10
+ )
5
11
  from sqla_fancy_core.decorators import transact, Inject # noqa
@@ -7,7 +7,9 @@ from typing import Union, overload
7
7
  import sqlalchemy as sa
8
8
  from sqlalchemy.ext.asyncio import AsyncConnection, AsyncEngine
9
9
 
10
- EngineType = Union[sa.Engine, AsyncEngine]
10
+ from sqla_fancy_core.wrappers import AsyncFancyEngineWrapper, FancyEngineWrapper
11
+
12
+ EngineType = Union[sa.Engine, AsyncEngine, FancyEngineWrapper, AsyncFancyEngineWrapper]
11
13
 
12
14
 
13
15
  class _Injectable:
@@ -16,9 +18,9 @@ class _Injectable:
16
18
 
17
19
 
18
20
  @overload
19
- def Inject(engine: sa.Engine) -> sa.Connection: ...
21
+ def Inject(engine: Union[sa.Engine, FancyEngineWrapper]) -> sa.Connection: ...
20
22
  @overload
21
- def Inject(engine: AsyncEngine) -> AsyncConnection: ...
23
+ def Inject(engine: Union[AsyncEngine, AsyncFancyEngineWrapper]) -> AsyncConnection: ...
22
24
  def Inject(engine: EngineType): # type: ignore
23
25
  """A marker class for dependency injection."""
24
26
  return _Injectable(engine)
@@ -44,7 +46,6 @@ def transact(func):
44
46
  create_user(name="existing", conn=conn)
45
47
  """
46
48
 
47
- # Find the parameter with value Inject
48
49
  sig = inspect.signature(func)
49
50
  inject_param_name = None
50
51
  for name, param in sig.parameters.items():
@@ -56,8 +57,21 @@ def transact(func):
56
57
  if inject_param_name is None:
57
58
  return func # No injection needed
58
59
 
59
- engine = sig.parameters[inject_param_name].default.engine
60
- is_async = isinstance(engine, AsyncEngine)
60
+ engine_arg = sig.parameters[inject_param_name].default.engine
61
+ if isinstance(engine_arg, sa.Engine):
62
+ is_async = False
63
+ engine = engine_arg
64
+ elif isinstance(engine_arg, FancyEngineWrapper):
65
+ is_async = False
66
+ engine = engine_arg.engine
67
+ elif isinstance(engine_arg, AsyncEngine):
68
+ is_async = True
69
+ engine = engine_arg
70
+ elif isinstance(engine_arg, AsyncFancyEngineWrapper):
71
+ is_async = True
72
+ engine = engine_arg.engine
73
+ else:
74
+ raise TypeError("Unsupported engine type")
61
75
 
62
76
  if is_async:
63
77
 
@@ -70,8 +84,14 @@ def transact(func):
70
84
  else:
71
85
  async with conn.begin():
72
86
  return await func(*args, **kwargs)
87
+ elif isinstance(conn, sa.Connection):
88
+ if conn.in_transaction():
89
+ return await func(*args, **kwargs)
90
+ else:
91
+ with conn.begin():
92
+ return await func(*args, **kwargs)
73
93
  else:
74
- async with engine.begin() as conn:
94
+ async with engine.begin() as conn: # type: ignore
75
95
  kwargs[inject_param_name] = conn
76
96
  return await func(*args, **kwargs)
77
97
 
@@ -88,8 +108,10 @@ def transact(func):
88
108
  else:
89
109
  with conn.begin():
90
110
  return func(*args, **kwargs)
111
+ elif isinstance(conn, AsyncConnection):
112
+ raise TypeError("AsyncConnection cannot be used in sync function")
91
113
  else:
92
- with engine.begin() as conn:
114
+ with engine.begin() as conn: # type: ignore
93
115
  kwargs[inject_param_name] = conn
94
116
  return func(*args, **kwargs)
95
117
 
@@ -116,7 +138,6 @@ def connect(func):
116
138
  count = get_user_count(conn)
117
139
  """
118
140
 
119
- # Find the parameter with value Inject
120
141
  sig = inspect.signature(func)
121
142
  inject_param_name = None
122
143
  for name, param in sig.parameters.items():
@@ -128,18 +149,31 @@ def connect(func):
128
149
  if inject_param_name is None:
129
150
  return func # No injection needed
130
151
 
131
- engine = sig.parameters[inject_param_name].default.engine
132
- is_async = isinstance(engine, AsyncEngine)
152
+ engine_arg = sig.parameters[inject_param_name].default.engine
153
+ if isinstance(engine_arg, sa.Engine):
154
+ is_async = False
155
+ engine = engine_arg
156
+ elif isinstance(engine_arg, FancyEngineWrapper):
157
+ is_async = False
158
+ engine = engine_arg.engine
159
+ elif isinstance(engine_arg, AsyncEngine):
160
+ is_async = True
161
+ engine = engine_arg
162
+ elif isinstance(engine_arg, AsyncFancyEngineWrapper):
163
+ is_async = True
164
+ engine = engine_arg.engine
165
+ else:
166
+ raise TypeError("Unsupported engine type")
133
167
 
134
168
  if is_async:
135
169
 
136
170
  @functools.wraps(func)
137
171
  async def async_wrapper(*args, **kwargs):
138
172
  conn = kwargs.get(inject_param_name)
139
- if isinstance(conn, AsyncConnection):
173
+ if isinstance(conn, (AsyncConnection, sa.Connection)):
140
174
  return await func(*args, **kwargs)
141
175
  else:
142
- async with engine.connect() as conn:
176
+ async with engine.connect() as conn: # type: ignore
143
177
  kwargs[inject_param_name] = conn
144
178
  return await func(*args, **kwargs)
145
179
 
@@ -152,8 +186,10 @@ def connect(func):
152
186
  conn = kwargs.get(inject_param_name)
153
187
  if isinstance(conn, sa.Connection):
154
188
  return func(*args, **kwargs)
189
+ elif isinstance(conn, AsyncConnection):
190
+ raise TypeError("AsyncConnection cannot be used in sync function")
155
191
  else:
156
- with engine.connect() as conn:
192
+ with engine.connect() as conn: # type: ignore
157
193
  kwargs[inject_param_name] = conn
158
194
  return func(*args, **kwargs)
159
195