Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ uv run --env-file .env pytest -n 1 tests/pyathena/test_cursor.py -v
#### Test Conventions

- **Class-based tests** for integration tests that use fixtures (cursors, engines): `class TestCursor:` with methods like `def test_fetchone(self, cursor):`
- **Standalone functions** for unit tests of pure logic (converters, parsers, utils): `def test_to_struct_json_formats(input_value, expected):`
- **Standalone functions** are the default for stateless helpers: `def test_to_struct_json_formats(input_value, expected):`. Unit tests may use a class when it groups the behavior of one object or meaningful common setup, such as `TestTypeSignatureParser`.
- Function-oriented utility tests may remain standalone even when they use AWS fixtures, as in `tests/pyathena/pandas/test_util.py`. Fixture use alone does not determine the grouping.
- Test file naming mirrors source: `pyathena/parser.py` → `tests/pyathena/test_parser.py`
- **Fixtures**: Cursor/engine fixtures are defined in `conftest.py` and injected by name (e.g., `cursor`, `engine`, `async_cursor`). Use `indirect=True` parametrization to pass connection options:

Expand All @@ -93,6 +94,9 @@ uv run --env-file .env pytest -n 1 tests/pyathena/test_cursor.py -v
```

- **Parametrize** with `@pytest.mark.parametrize(("input", "expected"), [...])` for data-driven tests
- Keep parameter definitions declarative. Build mocks, configured result sets, and one-shot readers during fixture setup or test execution. Pure values and framework type/expression objects may be constructed in parameter definitions.
- Make expected values independent of the implementation under test. A library comparison is appropriate when matching that library is the contract; describe intentional differences with explicit expected values.
- Preserve test IDs, parameter coverage, marks, fixture scopes, and resource cleanup when reorganizing tests. SQLAlchemy compliance tests retain their upstream class and plugin conventions and applicable attribution.
- **Integration tests** (need AWS) use cursor/engine fixtures with real Athena queries; **unit tests** (no AWS) call functions directly with test data

### Markdown Lint
Expand Down
54 changes: 54 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,38 @@ uv run --env-file .env pytest -n 1 tests/pyathena/test_cursor.py -v
```

A targeted run helps during development but does not replace other coverage required by the affected callers or features.

(testing-offline)=

### Run self-contained tests offline

The pandas and Polars result-set modules have self-contained tests that can run without AWS access when the session hooks are excluded.
Reuse the `.env` file described in the AWS environment section if it is already configured.
For an offline-only setup, create a gitignored `.env` file in the repository root with these placeholder values:

```ini
AWS_DEFAULT_REGION=us-east-1
AWS_ATHENA_S3_STAGING_DIR=s3://pyathena-offline-placeholder/
AWS_ATHENA_WORKGROUP=offline
AWS_ATHENA_SPARK_WORKGROUP=offline
AWS_EC2_METADATA_DISABLED=true
```

After `just lint`, load `.env` and run:

```bash
uv run --env-file .env pytest --noconftest -p no:rerunfailures -q \

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relayed bounded independent .env documentation confirmation: CLEAN.
No actionable defect found in this follow-up.

Reviewer: Claude Code, claude-opus-5-5, max profile, effort high; verified first-party Max authentication, no Enterprise/API/provider override.
Actual result modelUsage contains only claude-opus-5-5 (firstParty).
Session: 81e3300a-4349-4024-a0fb-816b28d21fd1
Merge-base: 0c19c8d
Previous published head: 03beabd
Published reviewed head: e72974b

The reviewer inspected all final repair hunks and the literal range-diff; the four preceding entries remain unchanged.
Both real and offline .env examples supply the four import-time values required by tests/init.py; the placeholder staging URL parses correctly.
The metadata-disabling claim is properly limited to the offline-only example.
The documented modules have no static client/session/filesystem creation path, so loading an existing AWS configuration does not introduce an identified AWS call.
uv run --env-file .env agrees with the existing guide and worktree-env script; .env is ignored, conftest/rerun-plugin exclusions are retained, and no pytest addopts/dotenv plugin alters the flow.
The final prose reference avoids an unresolved MyST heading target while preserving the offline label and valid code fences.

Non-defect boundary: offline placeholders persist in .env and are unsuitable for an AWS suite; the existing real-configuration instructions and explicit offline-only/integration boundary already explain the distinction.
No additional finding was raised.

Static source review only: Read/Glob/Grep in exported tracked sources; no commands, edits, tests, collection, lint/build, AWS, GitHub/web/MCP/memory access or subagents.
The reviewer did not independently execute uv or measure botocore behavior.
All 861 package-file fingerprints and the PR worktree remained unchanged.
The author separately ran the exact .env invocation with inherited AWS variables cleared (132 passed), required lint, and final current-source rendering without testing-page warnings; standard documentation-build CI also passed.
No independent finding remains unresolved.

tests/pyathena/pandas/test_result_set.py \
tests/pyathena/polars/test_result_set.py
```

The four AWS configuration values are required by `tests/__init__.py`, which pytest still imports with `--noconftest`.
The offline-only `.env` example disables EC2 metadata to prevent implicit credential lookup through that service.
`--noconftest` excludes the AWS session hooks and fixtures; disabling the rerun plugin also avoids its local socket setup in restricted environments.
Use this invocation only for self-contained modules; integration tests need their normal fixtures and a real AWS environment.

### SQLAlchemy suites

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relayed bounded independent follow-up: FINDINGS, one new Low documentation-structure issue.

Reviewer: Claude Code, claude-opus-5-5, max profile, effort high; first-party Max authentication verified, no Enterprise/API/provider override. Actual result modelUsage contains only claude-opus-5-5 (firstParty). Session: 984dcf63-5640-4ee6-a8e6-51028003e814.
Base: 2007620
Previous head: aef3ae1
Reviewed head: 6a25777

All four original dispositions verified: the async directory-entry hypothesis is rejected against actual fsspec 2026.9.0 _du/_find sources; pandas parity coverage, historical/offline instructions, and cleanup diagnostics are resolved. Explicit pandas positional selections reproduce the baseline’s direct comparisons, including category, date override, duplicate and headerless names. Headerless mapped-string values remain literal-only comparisons, a narrow coverage limit the reviewer identified as non-failing. The runtime word 'validated' cannot be established by this static review; the author separately ran the documented command successfully.

New Low finding at docs/testing.md:155-174: adding the SQLAlchemy suites subsection nests general tox and result-reporting guidance beneath it. A documentation reader could interpret required version/command/skip reporting as specific to SQLAlchemy and omit it for other changes. Author disposition: accepted; separate Run tox and Record results headings restore their general scope.

Covered every repair hunk, direct pandas/fsspec contracts and dependency sources, env/fixture/plugin configuration, and documentation structure. Static review only: no test/lint/build/command execution, GitHub/web/MCP/memory access, or edits. All 857 review-package file fingerprints and the PR worktree remained unchanged during review. PyArrow inference source and pytest/plugin implementation were not included, so those semantics were not independently traced.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 8c42428 by adding sibling Run tox and Record results headings. Required format/lint, Markdown lint, and actual current-source Sphinx build passed; the rendered HTML confirms the general hierarchy. Both author self-review perspectives passed. The final bounded independent confirmation is CLEAN (claude-opus-5-5, Max, high, session 1cb5a6b7-0340-427b-8a2a-ce7e8029b938).


The SQLAlchemy compliance suites under `tests/sqlalchemy/` run with different configurations: use `sqla` for synchronous dialects and `sqla-async` for native asyncio dialects.
They do not run PyAthena's own dialect regression tests under `tests/pyathena/sqlalchemy/` and `tests/pyathena/aio/sqlalchemy/`.
Run the relevant PyAthena tests too, either through `just test pyathena` or a focused selection during development:
Expand All @@ -136,17 +168,39 @@ Run the relevant PyAthena tests too, either through `just test pyathena` or a fo
uv run --env-file .env pytest -n 1 tests/pyathena/sqlalchemy/ tests/pyathena/aio/sqlalchemy/ -v
```

### Run tox

To invoke the configured tox environments locally:

```bash
uv run --env-file .env just tox
```

### Record results

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relayed final bounded independent confirmation: CLEAN. The prior Low documentation-heading finding is resolved, with no new actionable issue.

Reviewer: Claude Code, claude-opus-5-5, max profile, effort high; first-party Max authentication verified, no Enterprise/API/provider override. Actual result modelUsage contains only claude-opus-5-5. Session: 1cb5a6b7-0340-427b-8a2a-ce7e8029b938.
Base: 2007620
Previous head: 6a25777
Published reviewed head: 8c42428

Reviewed the literal four-line repair and range-diff, the full current testing guide and corresponding previous region, and directly affected audit/contribution/AGENTS requirements, MyST configuration, Markdown heading rules, and cross-reference uses. Run tox and Record results are now H3 siblings of SQLAlchemy suites under Run tests. Body text and the offline label remain unchanged; no existing link depends on the new headings. Earlier patch-series entries are unchanged, so the three resolved original findings and the rejected async hypothesis were not reopened.

Static review only: no commands, tests, lint/build, GitHub/web/MCP, memory, edits, or external context. Rendering and lint conclusions were inferred from source; the author separately validated actual HTML and lint. All 567 package-file fingerprints and the PR worktree remained unchanged during review. Combined with the initial full review and completed repair follow-up, no required independent finding remains unresolved.


Record the tested commit, Python and relevant dependency versions, exact commands, and results in the pull request.
Include failed and skipped tests and explain any unrun coverage.
Separate real AWS results from mock-based tests and static checks.
Sanitize logs before sharing them.

## Organize tests

Group cursor and engine integration tests in classes, and use standalone functions for stateless helpers.
A unit-test class can group the behavior of one object or common setup, as in `TestTypeSignatureParser`.
Function-oriented utility tests can remain standalone when they use AWS fixtures; `tests/pyathena/pandas/test_util.py` follows this pattern.
SQLAlchemy compliance tests retain the classes, decorators, and plugin setup required by the upstream suite.
Preserve attribution for adapted tests as documented in `NOTICE`.

Parameter definitions should make inputs, options, and expected behavior visible.
Build mocks, configured result sets, and one-shot readers during fixture setup or test execution.
Pure values and framework type or expression objects can be constructed in parameters.
Choose explicit parameter IDs when generated IDs obscure the case, and keep existing IDs, marks, and fixture scopes when reorganizing tests.

Use literal expected values for contract-specific behavior.
A library comparison is useful when matching that library is the contract, such as joining pandas chunks versus reading the whole file.
Express intentional differences directly rather than recreating the implementation in the expected-value builder.
Retain dtype and schema checks, option contexts, resource cleanup, and equivalent synchronous and asynchronous scenarios where applicable.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relayed bounded independent documentation-relocation confirmation: CLEAN.
No actionable defect found within this follow-up.

Reviewer: Claude Code, claude-opus-5-5, max profile, effort high; first-party Max authentication verified, no Enterprise/API/provider override.
Actual result modelUsage contains only claude-opus-5-5 (firstParty).
Session: 9cde76a8-2c03-404d-b3a7-a49451a8bf09
Merge-base: 0c19c8d
Previous published head: 60be248
Published reviewed head: 03beabd

Reviewed the literal git range-diff, complete repair diff, remaining testing guidance and directly affected documentation/configuration references.
The three previous patch-series entries are unchanged; the new commit only removes the historical report and its introduction/hidden toctree.
No reference to the deleted page remains. Permanent conventions, the offline command, and heading/fence structure are intact.
The retained audit artifact matches the deleted report apart from its Sphinx-only offline reference, now a normal Markdown link to the previous head's existing unique heading.
Its 83-file inventory, evidence, priorities, exceptions, validation boundaries and 27 baseline-pinned references are preserved.
The unused testing-offline label remains a valid stable target and is not a defect.

Static review only: Read/Glob/Grep in exported tracked sources, no commands, edits, tests, collection, lint/build, GitHub/web/MCP/memory or subagents.
The reviewer did not establish actual posting or runtime link/build success. The author separately verified the exact GitHub-returned report body and ran required lint plus current-source rendering; standard documentation-build CI also passed.
All 863 review-package file fingerprints and the PR worktree remained unchanged during review.
Combined with the completed initial review and preceding confirmations, no required independent finding remains unresolved.


## GitHub Actions

The Test workflow runs for pull requests that change files other than `docs/` and Markdown.
Expand Down
23 changes: 20 additions & 3 deletions tests/pyathena/filesystem/test_s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -4819,9 +4819,26 @@ def test_find_withdirs(self, fs):
result = fs.find(dir_, withdirs=False)
assert len(result) == 4 # Only files

def test_du(self):
# TODO
pass
def test_du(self, fs):
"""Disk usage reports file sizes, their total, and the requested depth."""
directory = (
f"s3://{ENV.s3_staging_bucket}/{ENV.s3_staging_key}{ENV.schema}/filesystem/test_du"
)
first = f"{directory}/first"
second = f"{directory}/nested/second"
try:
fs.pipe_file(first, b"abc")
fs.pipe_file(second, b"12345")
assert fs.du(directory) == 8

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relayed bounded independent filesystem-base confirmation: CLEAN.

Reviewer: Claude Code, claude-opus-5-5, max profile, effort high; first-party Max authentication verified, no Enterprise/API/provider override. Actual result modelUsage contains only claude-opus-5-5. Session: bd4c4c07-a6d9-40e6-82fc-cd5ad84f0677.
Previous base/head: 6888f4d / 1f08a42
Current merge-base/published head: 0c19c8d / 60be248

All three patch-series entries remain unchanged. The reviewer checked the test_du hunks, newly merged filesystem diff, direct write/find/info/delete paths, imports and class fixtures, and the boundary to multipart/checksum/cleanup changes. The 3-/5-byte writes use the unchanged single PutObject path, including native-async delegation. Listing, disk usage, path normalization, recursive deletion and missing-prefix cleanup do not call the changed multipart primitives. New upstream tests use distinct UUID prefixes; mock/monkeypatch scopes restore shared state, and permanent assignments target uncached throwaway filesystems. No fixture leak or integration regression found. Historical audit pins remain accurate; pandas/Polars sources are unaffected.

Static review only: no tests, S3 access, collection, lint/build, commands, edits, GitHub/web/MCP/memory or external context. Unchanged fsspec total/maxdepth semantics were not reopened, and default block size was read rather than measured. All 858 package-file fingerprints and the PR worktree remained unchanged during review. The author separately reran both du cases on real AWS (2 passed) and preserved all 812 collected IDs.

assert fs.du(directory, total=False) == {
fs._strip_protocol(first): 3,
fs._strip_protocol(second): 5,
}
assert fs.du(directory, maxdepth=1) == 3
assert fs.du(first) == 3
finally:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relayed independent static review by Claude Code, model claude-opus-5-5, profile max, effort high. Verified first-party Claude.ai Max authentication; no Enterprise/API/provider override. Session: 113e6092-a1e7-47d0-96ad-f51235d7fe7f.
Base: 2007620
Head: aef3ae1

Low finding (also tests/pyathena/filesystem/test_s3_async.py:1507): if the first write fails before the prefix exists, cleanup can raise FileNotFoundError and become the reported failure, obscuring the original write error.

Author disposition: accepted. Suppress only FileNotFoundError during finally cleanup in both execution styles; continue surfacing other cleanup failures. Validate normal AWS assertions and the missing-prefix failure path.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repaired in 6a25777 in both sync and async test_du. Finally cleanup suppresses only FileNotFoundError. Separate offline probes invoke the actual test methods with a first-write failure and absent-prefix cleanup; both preserve the original exception. Both real-AWS cases passed again after repair (2 passed, no skips), so normal cleanup and assertions are also validated. Independent follow-up is in progress.

with contextlib.suppress(FileNotFoundError):
fs.rm(directory, recursive=True)

def test_glob(self, fs):
dir_ = f"s3://{ENV.s3_staging_bucket}/{ENV.s3_staging_key}{ENV.schema}/filesystem/test_glob"
Expand Down
22 changes: 19 additions & 3 deletions tests/pyathena/filesystem/test_s3_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -1775,9 +1775,25 @@ async def test_find_withdirs(self, fs):
result = await fs._find(dir_, withdirs=False)
assert len(result) == 4 # Only files

def test_du(self):
# TODO
pass
@pytest.mark.asyncio
async def test_du(self, fs):
"""Disk usage reports file sizes, their total, and the requested depth."""
directory = f"s3://{ENV.s3_staging_bucket}/{ENV.s3_staging_key}{ENV.schema}/filesystem/test_async_du"
first = f"{directory}/first"
second = f"{directory}/nested/second"
try:
await fs._pipe_file(first, b"abc")
await fs._pipe_file(second, b"12345")
assert await fs._du(directory) == 8
assert await fs._du(directory, total=False) == {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relayed independent static review by Claude Code, model claude-opus-5-5, profile max, effort high. Verified first-party Claude.ai Max authentication; no Enterprise/API/provider override. Session: 113e6092-a1e7-47d0-96ad-f51235d7fe7f.
Base: 2007620
Head: aef3ae1

Review result: FINDINGS (four items). High hypothesis: inherited AsyncFileSystem._du might include directory entries, making this two-file mapping fail. The reviewer explicitly lacked the installed dependency source and based this item on recalled fsspec behavior.

Author verification: rejected. In the locked fsspec 2026.9.0, asyn.py:1002-1011 calls _find directly without withdirs=True; _find defaults to withdirs=False. The actual source does not use the hypothesized _expand_path here. Both synchronous and asynchronous cases passed against real AWS (2 passed, no skips). The dependency sources will be supplied for the bounded follow-up.

The reviewer inspected all seven changed files, relevant pandas/Polars/filesystem contracts and fixtures, source references, environment/configuration, and provenance. Review tools were restricted to Read/Glob/Grep on the exported tracked snapshots and diff; no edits, commands, tests, GitHub, memory, or web access. All original package-file fingerprints remained unchanged. The author’s separate option comparison generated one temporary pyc file in the baseline export; it did not change a reviewed source. Runtime validation above is author evidence, not reviewer execution.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The completed independent follow-up confirms rejection against the provided locked dependency source: fsspec asyn.py:1002-1011 calls _find without withdirs=True, and AioS3FileSystem._find defaults withdirs=False and delegates that value. The two-file mapping is correct. Review session: 984dcf63-5640-4ee6-a8e6-51028003e814 (claude-opus-5-5, Max, high). Both real-AWS cases also passed again after the separate cleanup repair. No production behavior was changed for this hypothesis.

fs._strip_protocol(first): 3,
fs._strip_protocol(second): 5,
}
assert await fs._du(directory, maxdepth=1) == 3
assert await fs._du(first) == 3
finally:
with contextlib.suppress(FileNotFoundError):
await fs._rm(directory, recursive=True)

@pytest.mark.asyncio
async def test_glob(self, fs):
Expand Down
Loading
Loading