This document describes how to test daffy's optional dependency support for DataFrame libraries (Pandas, Polars, Modin, PyArrow).
Daffy supports optional dependencies: you can install it with pandas, polars, pyarrow, modin, or combinations of these. This testing setup ensures that supported combinations work correctly.
The GitHub Actions workflow includes three separate jobs:
- Standard tests - Run with both pandas and polars installed (full functionality)
- Pytest optional dependency tests - Run pytest tests that work with available libraries (always pass locally and in CI)
- Isolation scenario tests - Test each scenario in true isolation using built wheels:
pandas-only- Only pandas is availablepolars-only- Only polars is availablepyarrow-only- Only pyarrow is availablemodin-only- Modin is available (with pandas as dependency)both- Both libraries availablenone- No DataFrame libraries (should fail gracefully)
The file tests/test_optional_dependencies.py contains tests that:
- Verify library detection flags work correctly
- Test that error messages reflect available libraries
- Ensure decorators work with whatever is installed
These tests are designed to always pass regardless of which DataFrame libraries are installed. They run as part of the regular test suite and should succeed when you run uv run pytest locally.
The scripts/test_isolated_deps.py script allows manual testing of different scenarios:
Note: The pandas-only and polars-only tests will likely "fail" in local development environments because both libraries are typically installed. These tests are designed to work in CI environments with truly isolated environments using built wheel packages. The test failure messages will explain this.
# First build a wheel to avoid dev dependencies
uv build --wheel
# Test with pandas only
WHEEL=$(ls dist/daffy-*.whl | head -n1)
uv run --no-project --with "pandas>=1.5.1" --with "$WHEEL" python scripts/test_isolated_deps.py pandas
# Test with polars only
WHEEL=$(ls dist/daffy-*.whl | head -n1)
uv run --no-project --with "polars>=1.7.0" --with "$WHEEL" python scripts/test_isolated_deps.py polars
# Test with both
WHEEL=$(ls dist/daffy-*.whl | head -n1)
uv run --no-project --with "pandas>=1.5.1" --with "polars>=1.7.0" --with "$WHEEL" python scripts/test_isolated_deps.py both
# Test with pyarrow only
WHEEL=$(ls dist/daffy-*.whl | head -n1)
uv run --no-project --with "pyarrow>=14.0.0" --with "$WHEEL" python scripts/test_isolated_deps.py pyarrow
# Test with modin only (modin installs pandas as a dependency)
WHEEL=$(ls dist/daffy-*.whl | head -n1)
uv run --no-project --with "modin[ray]>=0.30.0" --with "$WHEEL" python scripts/test_isolated_deps.py modin
# Test with neither (should fail gracefully)
WHEEL=$(ls dist/daffy-*.whl | head -n1)
uv run --no-project --with "$WHEEL" python scripts/test_isolated_deps.py noneHAS_PANDAS = True,HAS_POLARS = False- Only pandas DataFrames are accepted
- Error messages mention "Pandas DataFrame"
HAS_PANDAS = False,HAS_POLARS = True- Only polars DataFrames are accepted
- Error messages mention "Polars DataFrame"
HAS_PANDAS = True,HAS_POLARS = True- Both DataFrame types work
- Error messages mention available DataFrame types
HAS_PYARROW = True(otherHAS_*flags False)- PyArrow tables are accepted as DataFrame inputs/outputs
- Error messages mention "PyArrow DataFrame"
HAS_MODIN = TrueHAS_PANDAS = Trueis expected because Modin depends on pandasHAS_POLARSshould be False in this scenarioHAS_PYARROWmay be True or False depending on selected Modin extras/transitive dependencies- Modin DataFrames are accepted as DataFrame inputs/outputs
- Error messages mention "Modin DataFrame"
- Import should fail with:
ImportError: No supported DataFrame library found...
The optional dependency support works through:
- Module detection in
daffy/dataframe_types.pyusingimportlib.util.find_spec - Narwhals runtime compatibility checks in
daffy/narwhals_compat.py - Conditional type hints using
TYPE_CHECKINGfor static analysis - Dynamic error messages that reflect available libraries
When adding tests for optional dependencies:
- Use the simple approach in
test_optional_dependencies.py - Check
HAS_PANDAS,HAS_POLARS,HAS_MODIN, andHAS_PYARROWflags to conditionally run tests - Use
pytest.mark.skipiffor tests requiring specific libraries - Test error message content to ensure it reflects available libraries
When working on optional dependency features:
- Run standard tests:
uv run pytest - Test specific scenarios:
uv run python scripts/test_isolated_deps.py <scenario> - Verify CI passes with all dependency combinations
- Ensure mypy type checking works:
uv run mypy daffy