Warn when a standalone pyspark dependency collides with databricks-connect - #6276
Warn when a standalone pyspark dependency collides with databricks-connect#6276rugpanov wants to merge 2 commits into
Conversation
*Why* databricks-connect vendors its own pyspark (it ships the pyspark/ package tree rather than depending on the standalone distribution). A project that also declares a standalone pyspark dependency resolves it into the same namespace, the two overwrite each other, and the environment then fails to start a session — surfacing to users as a cryptic Java or protobuf gencode error long after setup reported success. The merge-warning system compared user pins against env constraints only, and the env deliberately does not pin pyspark, so this collision was never flagged. *What* - Add warning code W_STANDALONE_PYSPARK_CONFLICT. - isPysparkDep: PEP 503 name match for the standalone pyspark requirement. - standalonePysparkWarnings: report the collision once, independent of the pinned version (it is a coexistence conflict, not a version one), emitted only when the env manages databricks-connect. - Wire it into detectMergeWarnings between the databricks-connect warnings and the constraint conflicts. *Verification* - go test ./libs/localenv/ (unit + regenerated acceptance goldens) passes. - go vet ./libs/localenv/ clean. Co-authored-by: Isaac
Integration test reportCommit: 34667be
8 interesting tests: 4 SKIP, 3 KNOWN, 1 RECOVERED
Top 6 slowest tests (at least 2 minutes):
|
anton-107
left a comment
There was a problem hiding this comment.
Reviewed as part of the three-PR stack (this, #6277, and databricks/databricks-vscode#2121). I reproduced the underlying bug rather than reasoning from the description.
Verified empirically
Cloning a real working databricks-connect 18.0.6 venv and layering standalone pyspark 4.2.0 on top reproduces the failure this stack is chasing:
ImportError: cannot import name 'PythonUDFEnvironment' from
'pyspark.sql.connect.expressions' ← raised at `from databricks.connect import ...`
The premise behind the warning holds: databricks-connect ships the pyspark package tree (its top_level.txt lists pyspark) but registers no pyspark distribution. Confirmed on 13.3.12, 14.3.19, 17.3.8 and 18.0.6 — so the collision is real and the diagnosis is right.
Test coverage is good: the three new unit tests cover ordering, constraints-only, and the report-once dedup, and the acceptance goldens exercise warnings[] end to end.
Finding — constraints-only gating disagrees with #6277
This warning gates on the mode (c.DatabricksConnect != ""); #6277's hard fail gates on what is actually in the venv (dbcVer != "" && pysparkVer != ""). The mismatch is reachable.
The comment here says "in constraints-only mode the env installs no databricks-connect, so a standalone pyspark is harmless." But mergeDatabricksConnect is a no-op on an empty value, not a deletion (merge.go:351) — so a project that already pins databricks-connect itself keeps that pin, and uv installs it. That yields:
constraints-only + project's own dbconnect pin + standalone pyspark
→ merge: warning SKIPPED (gated on mode)
→ validate: HARD FAIL E_VALIDATE (gated on venv contents)
The user gets no warning at merge and then an unexplained hard failure at validate. Because dbcPin == "" on that path, they also aren't told which databricks-connect is involved.
Consider keying this warning on databricks-connect being present by any route rather than on the mode, so the two PRs agree. #6277's comment already reasons that way ("Keyed on both packages actually being present in the venv, not on the mode"); this one doesn't. No test covers constraints-only + a user's own dbconnect pin.
Smaller notes
isPysparkDepduplicatesisDatabricksConnectDepverbatim except for the compared literal — two copies of the PEP 508 name extraction plus PEP 503 normalization. OneisDepNamed(entry, name string) boolwith both as thin wrappers would avoid the drift.standalonePysparkWarningsreturns a slice but can only ever return 0 or 1 element (itreturns on first match).(Warning, bool)would state the contract honestly — though the plural name matches the siblingdbconnectWarnings, so this is a judgment call.detectMergeWarningsis only called on the non-greenfield path. That is right for the override warnings — greenfield has nothing of the user's to override — but a standalonepysparkis not an override, it's a property of the dependency list. Almost certainly nothing to flag today, since greenfield renders a fresh pyproject with no user deps; flagging it only because the reasoning that justifies the placement doesn't extend to this new warning.
Testing
I could not run the Go suites here: the repo requires Go 1.26, the local toolchain is 1.24, and the module proxy is blocked in my environment. I verified the logic by reading it and by reproducing the underlying package behaviour in Python — not by executing the tests. Taking the reported go test ./libs/localenv/ pass at face value.
Approving — a warning is advisory and cannot fail a run, so the gating question above is safe to settle in a follow-up. Worth resolving before #6277 merges, though, since that PR turns this same condition into a hard error.
*Why* Review feedback: the warning gated on the mode (c.DatabricksConnect != ""), but the validate hard-fail gates on the installed venv. That disagreement is reachable — in constraints-only mode a project that pins databricks-connect itself keeps that pin (mergeDatabricksConnect is a no-op on an empty managed value, merge.go), so uv installs it, yet the warning stayed silent while validate would hard-fail. The user got no warning at merge and then an unexplained failure at validate. Key the warning on databricks-connect being present by any route so the two agree. *What* - Gate standalonePysparkWarnings on `c.DatabricksConnect != "" || len(dbconnectPins(survivors)) > 0` instead of the mode alone. - Dedupe isDatabricksConnectDep / isPysparkDep into one isDepNamed(entry, name) with both as thin wrappers, removing the duplicated PEP 508/503 name logic. - Update the warning-code and function docs to describe the by-either-route gate. - Add a constraints-only + user-pinned-databricks-connect + pyspark test. *Verification* - go test ./libs/localenv/ and the merge-warnings acceptance goldens pass. - go vet ./libs/localenv/ clean. Co-authored-by: Isaac
|
Thanks for the review and the empirical repro. Addressed in the latest push. Finding — constraints-only gating disagreed with #6277. Fixed: the warning now gates on Nit — duplicated name extraction. Deduped Other nits. Left |
Problem
databricks-connectvendors its ownpyspark— it ships thepyspark/packagetree rather than depending on the standalone distribution. A project that also
declares a standalone
pysparkdependency resolves it into the same namespace, thetwo overwrite each other, and the environment then fails to start a session,
surfacing to users as a cryptic Java or protobuf gencode error long after
setup-localreported success.The merge-warning system compared the user's pins against the env constraints only,
and the environment deliberately does not pin
pyspark, so this collision was neverflagged.
What
W_STANDALONE_PYSPARK_CONFLICT.isPysparkDep: PEP 503 name match for the standalonepysparkrequirement.standalonePysparkWarnings: report the collision once, independent of the pinnedversion (it is a coexistence conflict, not a version one), emitted only when the
environment manages
databricks-connect.detectMergeWarningsbetween thedatabricks-connectwarnings andthe constraint conflicts.
Testing
go test ./libs/localenv/(unit tests + regeneratedmerge-warningsacceptancegoldens).
go vet ./libs/localenv/.This pull request and its description were written by Isaac.