Skip to content

fix(model): stop using removed peewee.basestring in Model._validate - #69

Open
hello-wn wants to merge 6 commits into
shanbay:masterfrom
hello-wn:fix/peewee4-basestring-compat
Open

fix(model): stop using removed peewee.basestring in Model._validate#69
hello-wn wants to merge 6 commits into
shanbay:masterfrom
hello-wn:fix/peewee4-basestring-compat

Conversation

@hello-wn

Copy link
Copy Markdown

Summary

  • peewee>=4 dropped the py2-compat basestring alias (peewee.basestring). Model._validate() in peeweext/model.py used isinstance(field, pw.basestring) when handling save(only=[...]), which raises AttributeError: module 'peewee' has no attribute 'basestring' on any environment resolving to peewee 4.x — since requirements.txt doesn't cap the peewee version, a plain pip install can pick up peewee 4 today.
  • Reported downstream by MinerU (Sentry BACKEND-EXAM-21H2): a cache-hit path called task.save(only=[...]) and crashed, so the business side never got notified.
  • Fix replaces pw.basestring with the builtin str — correct for both peewee 3.x and 4.x, and peeweext already requires python_requires='>=3' so no py2 compat is needed.

Test plan

  • Added tests/test_model.py::test_save_only_without_peewee_basestring, which patches the pw name inside peeweext.model with a proxy that raises AttributeError for basestring (simulating peewee>=4) without touching peewee's own internals (which still reference basestring elsewhere on 3.x), then calls save(only=[...]) with both a field-name string and a Field object.
  • Verified RED → GREEN manually: reverting the one-line fix makes the new test fail with the exact reported AttributeError; with the fix it passes.
  • Reproduced the original bug end-to-end in an isolated venv with peewee==4.3.0 installed against pre-fix code, confirming the traceback matches the Sentry report; confirmed the fix resolves it in the same venv.
  • Note: couldn't run the full existing suite locally (test_datetime, test_json_field_mysql, Postgres-backed tests, etc.) since this environment has no MySQL/Postgres/Docker available — those are pre-existing environment requirements, unrelated to this change.

🤖 Generated with Claude Code

Neng Wan and others added 6 commits August 14, 2026 14:49
peewee>=4 dropped the py2-compat `basestring` alias, so `save(only=[...])`
raised AttributeError on any environment with peewee 4 installed since
Model._validate() checked `isinstance(field, pw.basestring)`. Use the
builtin str instead, which is correct for both peewee 3.x and 4.x given
peeweext already requires python_requires='>=3'.

Adds a regression test that simulates a peewee>=4 environment (no
peewee.basestring) without touching peewee's own internals.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
The repo currently has no CI that actually runs the test suite
(.github/workflows only covers PyPI publish on release and stale-issue
triage; the legacy .travis.yml required check is no longer reported by
any active integration). Add a workflow that spins up MySQL and
Postgres services and runs `pytest tests` on both sides of the peewee
3/4 compatibility line this branch fixes.

Co-Authored-By: Claude <noreply@anthropic.com>
pip's resolver was backtracking into sea==3.1.5's old, source-only
grpcio<1.49.0 pin, which fails to build on modern Python/setuptools
(setuptools dropped pkg_resources, which grpcio's legacy setup.py still
imports). sea>=4.0.0 pulls grpcio<1.69.0,>=1.49.0, which has prebuilt
wheels. Verified tests/test_sea.py passes locally against sea==4.0.0.

Co-Authored-By: Claude <noreply@anthropic.com>
…ance dependents

Model.dependencies(search_nullable) controls which dependent rows peewee
even discovers, not just how they get handled. delete_instance() was
passing delete_nullable (False by default) as search_nullable, so a
nullable FK dependent was never discovered at all, never got nulled out,
and the parent delete then failed with a foreign key constraint error.
Reproduced against real MySQL 8; the existing
test_instance_delete::delete_instance(recursive=True) case now passes.

Also fix URLValidator: Python 3.9+'s urlsplit() raises ValueError itself
for malformed bracketed IPv6 hosts (e.g. "[::1:2::3]") instead of letting
our own ipaddress.IPv6Address check catch it, so that call needs the same
try/except ValueError guard already used elsewhere in this validator.

Both were long-standing bugs invisible until the new CI workflow (added
earlier in this branch) actually ran the suite against real MySQL and
Python 3.11 for the first time.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
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.

1 participant