feat: add async PostgreSQL fixture support and configurable fixture scope#1295
feat: add async PostgreSQL fixture support and configurable fixture scope#1295tboy1337 wants to merge 22 commits intodbfixtures:mainfrom
Conversation
- Introduced AsyncDatabaseJanitor for managing database state asynchronously. - Added async loading capabilities with build_loader_async and sql_async functions. - Updated factories to include async versions of PostgreSQL fixtures. - Enhanced tests to cover async functionality for janitor and loader. - Updated pyproject.toml to include optional dependencies for async support.
- Added tests for custom database name handling in AsyncDatabaseJanitor. - Implemented tests for database initialization and dropping with various configurations. - Included tests for callable resolution in build_loader and build_loader_async with dot-separated paths. - Improved error handling tests for sql_async when aiofiles is not installed.
…c functions - Updated the loader and client to check for aiofiles and pytest_asyncio imports at runtime, raising ImportError with clear messages if they are not installed. - Refactored tests to mock the absence of these dependencies more effectively, ensuring proper error handling is validated.
… postgresql_async fixture Made-with: Cursor
… default postgresql_async fixture" This reverts commit 5c92310.
for more information, see https://pre-commit.ci
📝 WalkthroughWalkthroughThis PR adds asynchronous PostgreSQL fixture support (postgresql_async), an AsyncDatabaseJanitor, async loader and retry utilities, makes fixture scope configurable via a new FixtureScopeT, and introduces an optional Changes
Sequence DiagramsequenceDiagram
participant Test as Test Function
participant Fixture as postgresql_async<br/>factory
participant Janitor as AsyncDatabaseJanitor
participant Loader as Loader (async)
participant Conn as AsyncConnection
participant DB as PostgreSQL
Test->>Fixture: request async fixture
Fixture->>Janitor: instantiate async janitor
Janitor->>DB: connect via AsyncConnection.connect()
DB-->>Conn: AsyncConnection
Janitor-->>Fixture: yield AsyncConnection
alt loading required
Fixture->>Loader: read SQL (aiofiles)
Loader->>Conn: execute SQL (async)
Conn->>DB: apply statements
DB-->>Conn: results
end
Test->>Conn: run async queries
Conn-->>Test: query results
Test->>Fixture: teardown
Fixture->>Janitor: cleanup (async)
Janitor->>Conn: close
Conn->>DB: disconnect
DB-->>Janitor: closed
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
pre-commit broke coderabbit @fizyk |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pytest_postgresql/factories/process.py (1)
85-110:⚠️ Potential issue | 🟠 MajorConfigurable process scopes currently leak the port lock file.
Line 110 now allows repeated setup/teardown cycles, but the
postgresql-<port>.portsentinel created on Line 140 is never removed if the fixture later tears down or fails. Withscope="function"and a fixed port or narrow range, the second invocation can fail withPortForExceptioneven though the previous server has already stopped.Possible fix
- postgresql_executor = PostgreSQLExecutor( - executable=postgresql_ctl, - host=host or config.host, - port=pg_port, - user=user or config.user, - password=password or config.password, - dbname=pg_dbname, - options=options or config.options, - datadir=str(datadir), - unixsocketdir=unixsocketdir or config.unixsocketdir, - logfile=str(logfile_path), - startparams=startparams or config.startparams, - postgres_options=postgres_options or config.postgres_options, - ) - # start server - with postgresql_executor: - postgresql_executor.wait_for_postgres() - janitor = DatabaseJanitor( - user=postgresql_executor.user, - host=postgresql_executor.host, - port=postgresql_executor.port, - dbname=postgresql_executor.template_dbname, - as_template=True, - version=postgresql_executor.version, - password=postgresql_executor.password, - ) - if config.drop_test_database: - janitor.drop() - with janitor: - for load_element in pg_load: - janitor.load(load_element) - yield postgresql_executor + try: + postgresql_executor = PostgreSQLExecutor( + executable=postgresql_ctl, + host=host or config.host, + port=pg_port, + user=user or config.user, + password=password or config.password, + dbname=pg_dbname, + options=options or config.options, + datadir=str(datadir), + unixsocketdir=unixsocketdir or config.unixsocketdir, + logfile=str(logfile_path), + startparams=startparams or config.startparams, + postgres_options=postgres_options or config.postgres_options, + ) + # start server + with postgresql_executor: + postgresql_executor.wait_for_postgres() + janitor = DatabaseJanitor( + user=postgresql_executor.user, + host=postgresql_executor.host, + port=postgresql_executor.port, + dbname=postgresql_executor.template_dbname, + as_template=True, + version=postgresql_executor.version, + password=postgresql_executor.password, + ) + if config.drop_test_database: + janitor.drop() + with janitor: + for load_element in pg_load: + janitor.load(load_element) + yield postgresql_executor + finally: + port_filename_path.unlink(missing_ok=True)Also applies to: 133-141, 155-186
🧹 Nitpick comments (1)
tests/test_retry.py (1)
18-32: Avoid the real backoff in the failure-path tests.These two tests add roughly three seconds of wall-clock delay and can become timing-sensitive on busy runners. Patching
pytest_postgresql.retry.asyncio.sleep(and the clock helper for the timeout case) would keep the async retry coverage fast and deterministic.Also applies to: 35-43
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_retry.py` around lines 18 - 32, The tests test_retry_async_succeeds_after_failures (and the related timeout test) are incurring real backoff delays; patch the sleep and clock used by retry_async to make them deterministic and fast: in the tests monkeypatch pytest_postgresql.retry.asyncio.sleep to an async no-op (or a function that records calls) and also patch the retry module's clock/timer helper used for timeout handling so the timeout case can advance without waiting; update the tests to use these monkeypatched replacements while calling retry_async to eliminate wall-clock backoff and keep behavior identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pytest_postgresql/factories/client.py`:
- Around line 102-119: The async fixture decorator call for
postgresql_async_factory must set loop_scope to match the fixture cache scope to
avoid event-loop lifetime misconfiguration; update the `@pytest_asyncio.fixture`
invocation (the decorator on postgresql_async_factory) to pass loop_scope=scope
so the event loop scope is at least as broad as the fixture scope (keeping the
existing scope=scope argument).
In `@tests/test_postgresql.py`:
- Around line 109-127: The async test test_postgres_terminate_connection_async
is missing the pytest-xdist isolation marker; add the same
`@pytest.mark.xdist_group`(name="terminate_connection") decorator to the async
test (alongside the existing `@pytest.mark.asyncio` and `@pytest.mark.parametrize`)
so it gets run in the same xdist group as the synchronous counterpart and avoids
cross-test interference.
---
Nitpick comments:
In `@tests/test_retry.py`:
- Around line 18-32: The tests test_retry_async_succeeds_after_failures (and the
related timeout test) are incurring real backoff delays; patch the sleep and
clock used by retry_async to make them deterministic and fast: in the tests
monkeypatch pytest_postgresql.retry.asyncio.sleep to an async no-op (or a
function that records calls) and also patch the retry module's clock/timer
helper used for timeout handling so the timeout case can advance without
waiting; update the tests to use these monkeypatched replacements while calling
retry_async to eliminate wall-clock backoff and keep behavior identical.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 821e8573-8a73-4dae-a6fc-dde60894e840
📒 Files selected for processing (18)
newsfragments/1235.feature.rstpyproject.tomlpytest_postgresql/factories/__init__.pypytest_postgresql/factories/client.pypytest_postgresql/factories/noprocess.pypytest_postgresql/factories/process.pypytest_postgresql/janitor.pypytest_postgresql/loader.pypytest_postgresql/retry.pypytest_postgresql/types.pytests/conftest.pytests/docker/test_noproc_docker.pytests/test_factory_errors.pytests/test_janitor.pytests/test_loader.pytests/test_postgresql.pytests/test_retry.pytests/test_template_database.py
- Updated the postgresql_async fixture to include loop_scope for better async handling. - Added xdist_group marker to the test_postgres_terminate_connection_async for improved test organization. - Refactored retry tests to mock sleep and current time, ensuring accurate timeout handling and retry logic validation.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/test_retry.py (1)
49-64: Optionally harden timeout assertions.You may want to assert no sleep occurred and only one failing call happened in this scenario, to lock in current timeout behaviour more explicitly.
Optional tightening
- async def always_fail() -> None: - raise ValueError("boom") + always_fail = AsyncMock(side_effect=ValueError("boom")) @@ with pytest.raises(TimeoutError, match="Failed after"): await retry_async(always_fail, timeout=1, possible_exception=ValueError) + sleep_mock.assert_not_awaited() + assert always_fail.await_count == 1🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_retry.py` around lines 49 - 64, Add explicit assertions that no sleep was awaited and that only the expected number of time-checks/calls happened: after the with-block raising TimeoutError, call sleep_mock.assert_not_awaited() to ensure no backoff was used and assert call_count == 2 (verifying get_current_datetime was called exactly once to capture the start and once to detect the timeout) to lock the current timeout behavior for retry_async when always_fail raises immediately.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/test_retry.py`:
- Around line 49-64: Add explicit assertions that no sleep was awaited and that
only the expected number of time-checks/calls happened: after the with-block
raising TimeoutError, call sleep_mock.assert_not_awaited() to ensure no backoff
was used and assert call_count == 2 (verifying get_current_datetime was called
exactly once to capture the start and once to detect the timeout) to lock the
current timeout behavior for retry_async when always_fail raises immediately.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5bf34b69-b624-4e9d-8cb3-1fdaeffb1f94
📒 Files selected for processing (3)
pytest_postgresql/factories/client.pytests/test_postgresql.pytests/test_retry.py
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/test_postgresql.py
- Changed isolation level and autocommit settings to direct attribute assignment instead of using methods. - Removed unnecessary mock methods in test for improved clarity and performance.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/test_janitor.py (1)
124-143: Consider adding test coverage for async loaders.This test validates that
AsyncDatabaseJanitor.load()works with synchronous loaders via theinspect.isawaitablecheck. Consider adding a companion test that exercises the async loader path (e.g., using aPathargument to triggersql_async), ensuring theawait resultbranch is also covered.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_janitor.py` around lines 124 - 143, Add a companion async test that exercises the AsyncDatabaseJanitor.load() path where the loader is asynchronous (so the inspect.isawaitable branch and the await result path are executed): create an async loader (or point to an async loader via a Path to trigger sql_async), patch tests.loader.psycopg.connect like in test_janitor_populate_async, instantiate AsyncDatabaseJanitor(version=10, **call_kwargs), await janitor.load(async_loader) and assert connect_mock.called and connect_mock.call_args.kwargs == call_kwargs; ensure the test uses `@pytest.mark.asyncio` and param or fixture to provide the async loader so sql_async/inspect.isawaitable logic in AsyncDatabaseJanitor.load is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pytest_postgresql/janitor.py`:
- Around line 252-259: The SQL string concatenation in the async helper
_terminate_connection (async def _terminate_connection(cur: AsyncCursor, dbname:
str)) currently joins two literals without a space, producing "...pid)FROM..."
and causing a syntax error; fix it by ensuring there's whitespace between ")”
and "FROM" (e.g., include a trailing space on the first literal or a leading
space on the second) so the executed query becomes "...pid) FROM
pg_stat_activity ...", and apply the same whitespace fix to the synchronous
DatabaseJanitor._terminate_connection query to avoid the identical runtime
error.
---
Nitpick comments:
In `@tests/test_janitor.py`:
- Around line 124-143: Add a companion async test that exercises the
AsyncDatabaseJanitor.load() path where the loader is asynchronous (so the
inspect.isawaitable branch and the await result path are executed): create an
async loader (or point to an async loader via a Path to trigger sql_async),
patch tests.loader.psycopg.connect like in test_janitor_populate_async,
instantiate AsyncDatabaseJanitor(version=10, **call_kwargs), await
janitor.load(async_loader) and assert connect_mock.called and
connect_mock.call_args.kwargs == call_kwargs; ensure the test uses
`@pytest.mark.asyncio` and param or fixture to provide the async loader so
sql_async/inspect.isawaitable logic in AsyncDatabaseJanitor.load is covered.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 951653a7-eb09-47eb-a5cc-adc4bbea59c1
📒 Files selected for processing (2)
pytest_postgresql/janitor.pytests/test_janitor.py
- Added a space at the end of the SQL query string in both DatabaseJanitor and AsyncDatabaseJanitor to ensure proper formatting.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pytest_postgresql/janitor.py`:
- Around line 224-231: The CREATE DATABASE DDL currently builds SQL with
f-strings using self.dbname and self.template_dbname (in the method that calls
cur.execute with query), which can break on embedded quotes; replace this string
interpolation with psycopg.sql.Identifier composition: import psycopg.sql as
sql, build the base sql.SQL("CREATE DATABASE
{}").format(sql.Identifier(self.dbname)), conditionally append sql.SQL("
TEMPLATE {}").format(sql.Identifier(self.template_dbname)) when present and
sql.SQL(" IS_TEMPLATE = true") when self.as_template is true, then pass the
composed sql.SQL object to await cur.execute(...) instead of the f-string.
Ensure all occurrences that construct database identifiers (the blocks using
self.dbname, self.template_dbname, and as_template) use sql.Identifier.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3dceab04-c112-4e1c-8a96-71c78d839bfd
📒 Files selected for processing (1)
pytest_postgresql/janitor.py
- Updated PostgreSQLExecutor to conditionally omit Unix socket parameters on Windows. - Enhanced error handling in PostgreSQLExecutor for Windows compatibility. - Refactored AsyncDatabaseJanitor to use async methods for setting isolation level and autocommit. - Updated tests to mock async connection methods for better compatibility.
for more information, see https://pre-commit.ci
Remove Windows executor compatibility changes (log_destination quoting, unix_socket_directories platform guard, os.killpg AttributeError catch) and the Windows-only event_loop_policy fixture from conftest.py. These belong in the separate Windows compatibility PR dbfixtures#1182. Made-with: Cursor
… for PR-1182) Made-with: Cursor
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
pytest_postgresql/janitor.py (1)
223-250:⚠️ Potential issue | 🟠 MajorCompose database identifiers with
psycopg.sql.Identifier.
self.dbname,self.template_dbname, anddbnameare still interpolated straight into DDL here. Any identifier containing"will generate invalid SQL, so the new async path inherits the same quoting bug as the sync janitor. Please build these statements withsql.Identifier(...)instead of f-strings.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pytest_postgresql/janitor.py` around lines 223 - 250, Replace all f-string interpolation of database identifiers with psycopg's composable SQL identifiers: build queries using psycopg.sql.SQL(...) and psycopg.sql.Identifier(...) instead of f-strings for the code paths that use self.dbname and self.template_dbname (the block that constructs query and calls await cur.execute(f"{query};")), the drop method (ALTER DATABASE ... with is_template false; DROP DATABASE IF EXISTS ...), and the helper _dont_datallowconn (which runs ALTER DATABASE ... with allow_connections false;). Use sql.Identifier(dbname) / sql.Identifier(self.template_dbname) to compose the identifier parts and sql.SQL(...) to join fragments so the executed statements are safe even when identifiers contain quotes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pytest_postgresql/plugin.py`:
- Line 138: The module-level call to
factories.postgresql_async("postgresql_proc") (assigned to postgresql_async)
triggers ImportError when pytest_asyncio is not installed; change this to defer
creation so the ImportError only occurs if the async fixture is actually
requested: replace the module-level invocation of factories.postgresql_async
with a lazy registration pattern — either wrap the call in a try/except
ImportError and register a stub fixture named postgresql_async that raises a
clear ImportError when used, or implement a small wrapper function/fixture that
calls factories.postgresql_async("postgresql_proc") on first access; update
references to the postgresql_async symbol accordingly so the optional [async]
extra remains optional.
---
Duplicate comments:
In `@pytest_postgresql/janitor.py`:
- Around line 223-250: Replace all f-string interpolation of database
identifiers with psycopg's composable SQL identifiers: build queries using
psycopg.sql.SQL(...) and psycopg.sql.Identifier(...) instead of f-strings for
the code paths that use self.dbname and self.template_dbname (the block that
constructs query and calls await cur.execute(f"{query};")), the drop method
(ALTER DATABASE ... with is_template false; DROP DATABASE IF EXISTS ...), and
the helper _dont_datallowconn (which runs ALTER DATABASE ... with
allow_connections false;). Use sql.Identifier(dbname) /
sql.Identifier(self.template_dbname) to compose the identifier parts and
sql.SQL(...) to join fragments so the executed statements are safe even when
identifiers contain quotes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d9ce58b1-1939-4f2d-abc2-0e5fabc5dc59
📒 Files selected for processing (5)
pytest_postgresql/executor.pypytest_postgresql/janitor.pypytest_postgresql/plugin.pytests/conftest.pytests/test_janitor.py
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/conftest.py
…baseJanitor - Updated SQL query construction to use psycopg.sql for better safety and readability. - Refactored database creation, alteration, and dropping methods to utilize composable SQL objects. - Improved test assertions by rendering SQL commands for clarity in test outputs.
for more information, see https://pre-commit.ci
- Updated the postgresql_async fixture to conditionally use pytest_asyncio if available, enhancing compatibility with synchronous fixtures. - Added tests to ensure that the fixture does not raise errors at creation time when pytest_asyncio is absent, while still raising an ImportError during usage without pytest_asyncio.
for more information, see https://pre-commit.ci
|
@coderabbitai resume |
✅ Actions performedReviews resumed. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/test_janitor.py (1)
173-175: Make the patched cursor helper signature explicit.When patched onto
AsyncDatabaseJanitor.cursor, the first bound argument is the instance. Declaringselfexplicitly in_ctxavoids brittle behaviour if future tests passdbname.♻️ Suggested tweak
- async def _ctx(dbname: str = "postgres") -> AsyncIterator[AsyncMock]: + async def _ctx(self: AsyncDatabaseJanitor, dbname: str = "postgres") -> AsyncIterator[AsyncMock]: yield cur🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_janitor.py` around lines 173 - 175, The helper asynccontextmanager _ctx used to patch AsyncDatabaseJanitor.cursor must declare the bound instance explicitly; change the signature to include self as the first parameter (e.g. async def _ctx(self, dbname: str = "postgres") -> AsyncIterator[AsyncMock]) so the patched method receives the instance correctly when called on AsyncDatabaseJanitor, then yield cur as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pytest_postgresql/factories/client.py`:
- Around line 116-127: The async fixture postgresql_async_factory currently gets
decorated even when pytest_asyncio is None, causing an async def to be passed to
pytest and leading to "coroutine was never awaited" errors; change the
implementation so that when pytest_asyncio is None you register a synchronous
fixture function (not async) that immediately raises ImportError with the same
message, and only define the async def postgresql_async_factory when
pytest_asyncio is present; update usage of fixture_decorator/pytest.fixture to
wrap the sync stub in that branch so the guard executes at fixture call time and
surfaces the clear ImportError instead of an unawaited coroutine.
---
Nitpick comments:
In `@tests/test_janitor.py`:
- Around line 173-175: The helper asynccontextmanager _ctx used to patch
AsyncDatabaseJanitor.cursor must declare the bound instance explicitly; change
the signature to include self as the first parameter (e.g. async def _ctx(self,
dbname: str = "postgres") -> AsyncIterator[AsyncMock]) so the patched method
receives the instance correctly when called on AsyncDatabaseJanitor, then yield
cur as before.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f6aae3db-164c-4f28-99f3-850e516c8115
📒 Files selected for processing (5)
pytest_postgresql/factories/client.pypytest_postgresql/janitor.pypytest_postgresql/plugin.pytests/test_factory_errors.pytests/test_janitor.py
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/test_factory_errors.py
…n pytest-asyncio is absent - Updated the postgresql_async fixture to return a synchronous stub that raises ImportError if pytest-asyncio is not available, preventing coroutine-related warnings. - Modified tests to verify that the synchronous stub is correctly registered and raises the appropriate error when used without pytest-asyncio.
… guard Made-with: Cursor
for more information, see https://pre-commit.ci
There was a problem hiding this comment.
🧹 Nitpick comments (1)
pytest_postgresql/factories/client.py (1)
65-80: Consider extracting shared connection/janitor setup to reduce drift.The sync and async branches duplicate proc-fixture field extraction and janitor argument construction; a tiny helper would make future changes safer.
♻️ Refactor sketch
+def _janitor_kwargs( + proc_fixture: PostgreSQLExecutor | NoopExecutor, + dbname: str | None, + isolation_level: "psycopg.IsolationLevel | None", +) -> dict[str, object]: + return { + "user": proc_fixture.user, + "host": proc_fixture.host, + "port": proc_fixture.port, + "dbname": dbname or proc_fixture.dbname, + "template_dbname": proc_fixture.template_dbname, + "version": proc_fixture.version, + "password": proc_fixture.password, + "isolation_level": isolation_level, + } - janitor = DatabaseJanitor( - user=pg_user, - host=pg_host, - port=pg_port, - dbname=pg_db, - template_dbname=proc_fixture.template_dbname, - version=proc_fixture.version, - password=pg_password, - isolation_level=isolation_level, - ) + janitor = DatabaseJanitor(**_janitor_kwargs(proc_fixture, dbname, isolation_level)) - janitor = AsyncDatabaseJanitor( - user=pg_user, - host=pg_host, - port=pg_port, - dbname=pg_db, - template_dbname=proc_fixture.template_dbname, - version=proc_fixture.version, - password=pg_password, - isolation_level=isolation_level, - ) + janitor = AsyncDatabaseJanitor(**_janitor_kwargs(proc_fixture, dbname, isolation_level))Also applies to: 134-149
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pytest_postgresql/factories/client.py` around lines 65 - 80, The sync and async branches duplicate extraction of proc_fixture fields and construction of the DatabaseJanitor; create a small helper (e.g., build_database_janitor or _make_janitor) that accepts proc_fixture, dbname (or None) and isolation_level and returns the DatabaseJanitor (or a tuple of janitor and resolved pg_db) so both branches call that helper instead of repeating the pg_host/pg_port/pg_user/pg_password/pg_options/pg_db and DatabaseJanitor(...) construction; update the usages in the functions that currently reference proc_fixture and DatabaseJanitor to use the helper (refactor occurrences around the code that create DatabaseJanitor in the sync branch and the async branch originally using proc_fixture.template_dbname and proc_fixture.version).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@pytest_postgresql/factories/client.py`:
- Around line 65-80: The sync and async branches duplicate extraction of
proc_fixture fields and construction of the DatabaseJanitor; create a small
helper (e.g., build_database_janitor or _make_janitor) that accepts
proc_fixture, dbname (or None) and isolation_level and returns the
DatabaseJanitor (or a tuple of janitor and resolved pg_db) so both branches call
that helper instead of repeating the
pg_host/pg_port/pg_user/pg_password/pg_options/pg_db and DatabaseJanitor(...)
construction; update the usages in the functions that currently reference
proc_fixture and DatabaseJanitor to use the helper (refactor occurrences around
the code that create DatabaseJanitor in the sync branch and the async branch
originally using proc_fixture.template_dbname and proc_fixture.version).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5186e2cc-e165-4017-90d5-8d87a3bc5437
📒 Files selected for processing (2)
pytest_postgresql/factories/client.pytests/test_factory_errors.py
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/test_factory_errors.py
postgresql_asyncfixture factory andAsyncDatabaseJanitorclass for testing async code against PostgreSQL usingpsycopg.AsyncConnection.asyncextra (pip install pytest-postgresql[async]) that pulls inpytest-asyncioandaiofilesas dependencies.scopeparameter to thepostgresql,postgresql_async,postgresql_proc, andpostgresql_noprocfactories, allowing consumers to override the fixture scope. Defaults are preserved ("function"for client fixtures,"session"for process fixtures).Changes
pytest_postgresql/factories/client.py: newpostgresql_asyncfactory usingpytest_asyncio.fixture;scopeparameter added topostgresqlpytest_postgresql/factories/process.py/noprocess.py:scopeparameter added topostgresql_procandpostgresql_noprocpytest_postgresql/janitor.py: newAsyncDatabaseJanitorwithinit,drop,load,cursor, and async context manager supportpytest_postgresql/loader.py: newbuild_loader_asyncandsql_asyncfor async SQL file loading viaaiofilespytest_postgresql/retry.py: newretry_asynccoroutine mirroring the existingretryhelperpytest_postgresql/types.py: newFixtureScopeTliteral type shared across factoriespyproject.toml: new[async]optional dependency grouptests/: comprehensive unit and integration tests covering all new async paths, including mock-based janitor tests and docker-based fixture testsSummary by CodeRabbit
New Features
Dependencies
Tests