Skip to content

test: setting an attribute on pgc_conn or its cursors raises instead of doing nothing (#1420) - #1432

Merged
jdatcmd merged 1 commit into
mainfrom
fix/1420-proxy-setattr
Oct 15, 2026
Merged

jdatcmd merged 1 commit into
mainfrom
fix/1420-proxy-setattr

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1420. A harness-only change; no product code or suite changes.

The defect

pgc_conn and the cursors it hands out are proxies (_WatchedConnection, _WatchedCursor in pgc_vacuity.py). They forward reads through __getattr__ but had no __setattr__. So pgc_conn.autocommit = False created an attribute on the proxy and left the real connection in autocommit. Every statement committed, rollback() undid nothing, and nothing said so. I hit this writing #1419.

The fix

Both proxies get a __setattr__. Names starting with _ are the proxy's own state (_conn, _cur, _nodeid, _pgc_write) and are set on the proxy. Any other name raises AttributeError. The message names the attribute and says to use BEGIN/ROLLBACK, or a connection of the test's own.

Raise, not forward: #1420 left this choice open. I took "raise" because a test that wants a transaction should say so in SQL, as the existing twins do. Forwarding is a one-line change if you prefer it, @OffgridwithJD.

Evidence

Red first, against the unchanged proxies:

  • test_a_setting_on_the_watched_connection_is_refused_not_dropped: got 'set', want 'refused'.
  • test_a_setting_on_a_watched_cursor_is_refused_not_dropped: got 'set', want 'refused'.
  • test_setting_autocommit_on_the_connection_the_tests_use_is_refused (the wiring arm, on the real pgc_conn): got 'set', want 'refused'.

Nothing in the corpus depends on the old behaviour. An AST scan of all 114 files under test/pytest finds no assignment of a public attribute on either proxy. A grep couldn't answer this, because it matched SQL text inside strings.

Green, on PG17:

  • Guard set: 435 passed with --pgc-expect-tests 435.
  • Cluster half: 567 passed with --pgc-expect-tests 567, rc=0. It runs serially, as CI does. Under -n 4 the same tests pass, but the run exits 67: each worker checks the decline registry (expected_unrunnable.txt) against only the tests it ran, and test_format_downgrade declines under xdist. That happens with or without this change.

Counts and docs:

  • guard_tests goes 433 -> 435 and cluster_tests 566 -> 567, both by collection.
  • TESTS.md §7 and §22 gain a row for each new test.
  • No ledger change: the ledger covers suites, and these are harness guard tests.

🤖 Generated with Claude Code

…of doing nothing (#1420)

The watched connection and cursor forward reads through __getattr__ but had
no __setattr__, so `pgc_conn.autocommit = False` made an attribute on the
wrapper and left the real connection in autocommit: each statement
committed and rollback() undid nothing, with no error. Both proxies now
keep their own underscore state and refuse any other assignment with an
AttributeError that names the attribute and says to use BEGIN and ROLLBACK
or a connection of one's own.

Red first: the two stub arms in test_writes_wrote_rows.py and the wiring
arm in test_connection.py each got 'set' where they wanted 'refused'. No
test in the corpus assigns a public attribute on either proxy (an AST scan
of all 114 files), and the full corpus passes on PG17: 435 guard tests and
567 cluster tests, the latter serially as CI runs them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@OffgridwithJD OffgridwithJD left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved at 28f2e41. Raise rather than forward is the right call: a test that wants a transaction should say so in SQL.

  • The fix: both proxies get a __setattr__. Names starting with _ set the proxy's own state, which covers everything their constructors set; any other name raises AttributeError with a message that says what to do instead.
  • Red, one proxy at a time (PG18, __pycache__ cleared before each arm, the runtime checked for which class still had __setattr__):
    • Without the connection's: 2 failed, 1 passed. Both test_a_setting_on_the_watched_connection_is_refused_not_dropped and the wiring arm on the real pgc_conn fail with setting autocommit on the watched connection is refused, not kept on the wrapper: got 'set' want 'refused'. The cursor arm stays green.
    • Without the cursor's: only the cursor arm fails, setting an attribute on a watched cursor is refused, not kept on the wrapper: got 'set' want 'refused'.
    • Restored: 3 passed.
  • Nothing else depends on the old behaviour: a grep for assignments to the psycopg connection and cursor attributes (autocommit, row_factory, isolation_level, read_only, arraysize, prepare_threshold and so on) across test/pytest finds only the new tests and the fake _Conn's own __init__. That agrees with your AST scan.
  • Counts: collection gives guard 435 and cluster 567, matching expected_tests.txt.
  • CI: 11 of 15 checks green at the time of review, 4 still running.

🤖 Generated with Claude Code

@jdatcmd
jdatcmd merged commit 6b634ba into main Oct 15, 2026
15 checks passed
This was referenced Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test harness: setting .autocommit on pgc_conn silently does nothing

2 participants