Repository navigation
build(deps): make polars, dask and tqdm optional, drop dead dependencies - #1032
Open
olivier-lacroix wants to merge 1 commit into
Open
olivier-lacroix wants to merge 1 commit into
olivier-lacroix wants to merge 1 commit into
Conversation
Merging this PR will not alter performance
Comparing Footnotes
|
The declared dependency list had 11 entries. Instrumenting the test suite with a plugin that wraps every public callable of bottleneck and numexpr showed which ones the code actually reaches. Removed: - toolz, never imported. It is a hard requirement of dask, so it arrives with the new dask extra. - numexpr, 0 calls across 8054 tests. linopy never calls pd.eval or Dataset.query, and xarray's accel extra does not list it. - deprecation, which backed a single decorator. The other 30 deprecation sites in the codebase call warnings.warn directly. Moved behind extras: - polars backs to_polars() and the LP file writer, so it becomes linopy[polars]. - tqdm backs the LP writer's progress bars, so it becomes linopy[progress]. - dask is never imported by linopy, only reached through xarray's .chunk(), so it becomes linopy[dask]. Kept: - bottleneck, with 286 nanmax, 284 nanmin and 16 push calls measured. xarray's duck_array_ops._push imports bottleneck directly with no numpy fallback, so Variable.ffill and Variable.bfill raised ModuleNotFoundError on a core-only install. The no-extras CI job now calls both, so a packaging change that drops bottleneck fails the build. Added: pandas, which 18 modules import and which previously arrived only through xarray. Core dependencies are now numpy, scipy, xarray, pandas, packaging and bottleneck. polars is loaded through a placeholder that raises ImportError with an install hint on first use, so import linopy, model building, direct-API solves, MPS export and Variable.ffill all work without it. The default Model.solve() writes an LP file and still needs polars; to_lp_file raises a message naming linopy[polars] and pointing at io_api="direct". get_printers_scalar drops polars for numpy, which removes polars from the direct-API solve path shared by the Highs, Gurobi, Cplex and Xpress backends. Generated LP files and printed variable and constraint names are byte-identical to master. The dev and docs extras pull the new extras in through a self-reference, so the extra list lives in one place. New tests in test/test_io.py cover the tqdm fallback, the LP guard with polars hidden from sys.modules, the placeholder, and the dunder probe that pytest's doctest collection makes. Co-Authored-By: Claude Code <noreply@anthropic.com>
olivier-lacroix
force-pushed
the
dep-trim
branch
from
October 18, 2026 05:07
9565cc9 to
5194de8
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linopy currently requires some unused dependencies, and some which are used only in a select number of methods. This PR removes unused dependencies, and make some others optional
Note
The following content was generated by AI.
What
The declared dependency list had 11 entries. Instrumenting the test suite showed which ones the code actually reaches.
toolzdaskextranumexprdask.chunk()linopy[dask]tqdmlinopy[progress]deprecationwarnings.warnpolarsto_polars()and the LP writerlinopy[polars]bottlenecknanmax, 284nanmin, 16pushcallsCore dependencies:
numpy,scipy,xarray,pandas,packaging,bottleneck.pandasis declared explicitly, since 18 modules import it and it previously arrived only through xarray.Why bottleneck stays
xarray's
duck_array_ops._pushimports bottleneck directly, with no numpy fallback.Variable.ffillandVariable.bfillgo through it, so a core-only install raisedModuleNotFoundErrorfrom both. A core-only venv confirmed it, and the no-extras CI job now callsffillandbfillso this cannot regress silently.The
nanmin/nanmaxcalls come from xarray's nanops behind.min()/.max()on NaN-carrying label and coefficient arrays, as used byConstraints.coefficientrangeand the objective range.numexprstays out: linopy never callspd.evalorDataset.query, and xarray'saccelextra does not list it.How polars became optional
polarsis loaded through a placeholder that raisesImportErrorwith an install hint on first use.import linopy, model building, direct-API solves, MPS export andVariable.ffillall work without it. The defaultModel.solve()writes an LP file and still needs polars;to_lp_fileraises a message naminglinopy[polars]and pointing atio_api="direct".get_printers_scalardrops polars for numpy, which removes polars from the direct-API solve path shared by the Highs, Gurobi, Cplex and Xpress backends.New extras:
polars,dask,progress. Thedevanddocsextras pull them in through a self-reference, so the extra list lives in one place.Tests
test/test_io.pycover the tqdm fallback, the LP guard with polars hidden fromsys.modules, the placeholder, and the dunder probe that pytest's doctest collection makes.master.How the usage was measured, and the full suite results
A pytest plugin wrapped every public callable of
bottleneckandnumexprbefore collection and counted calls, recording the first-call stack for each. Running it over the whole suite gave 586 bottleneck calls and 0 numexpr calls.Stack traces pointed at:
xarray/computation/nanops.pynanmin/nanmax, reached fromconstraints.py:2627coefficientrangexarray/core/duck_array_ops.py:892_push, reached fromxarray/core/dataarray.py:3858bfill