Skip to content

Enable PLC0415 so function-level imports need an explicit noqa - #850

Merged
d-chambers merged 2 commits into
devfrom
plc0415-lint
Aug 10, 2026
Merged

Enable PLC0415 so function-level imports need an explicit noqa#850
d-chambers merged 2 commits into
devfrom
plc0415-lint

Conversation

@d-chambers

@d-chambers d-chambers commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Description

Function-level imports were invisible to lint: E402 only covers module-level imports that appear after other top-level statements, and the pylint (PL) rules were not selected, so ruff never ran PLC0415 (import-outside-top-level).

This enables PLC0415 in lint.select and marks the existing sites with # noqa: PLC0415 directives, inserted mechanically with ruff check --add-noqa (45 in dascore/, mostly circular-import avoidance; 204 in tests/; 1 in scripts/). Three long import lines were re-wrapped by the ruff hook to stay under the line length; there are no semantic changes. New function-level imports now require an explicit opt-out, and the existing directives are visible debt that can be hoisted or kept deliberately over time.

Changelog

none

Checklist

I have (if applicable):

  • referenced the GitHub issue this PR closes.
  • documented the new feature with docstrings and/or appropriate doc page.
  • included tests (lint-config and comment-only change; enforced by the existing pre-commit CI job).
  • added the "ready_for_review" tag once the PR is ready to be reviewed.

Summary by CodeRabbit

  • Refactor

    • Standardized import organization throughout the application and test suite.
    • Reduced repeated in-function imports while preserving existing behavior and public APIs.
  • Chores

    • Expanded linting rules to detect imports inside functions.
    • Added targeted lint exceptions where deferred imports remain necessary.
  • Tests

    • Consolidated test dependencies without changing test coverage or assertions.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Summary

The PR standardizes import placement across production and test modules. It moves reusable imports to module scope, preserves selected deferred imports with Ruff suppressions, and enables Ruff rule PLC0415.

Changes

Import organization

Layer / File(s) Summary
Production import organization
dascore/core/spool.py, dascore/io/..., dascore/proc/coords.py, dascore/utils/..., dascore/viz/spectrogram.py, pyproject.toml, scripts/test_render_api.py
Production imports are consolidated at module scope. Intentional deferred imports receive PLC0415 suppressions.
Core and index test imports
tests/conftest.py, tests/test_core/*, tests/test_io/test_index/*, tests/test_io/test_indexer.py, tests/test_io/test_io_core.py
Repeated test imports move to module scope. Existing test behavior and assertions remain unchanged.
Supporting test imports
tests/test_integrations/*, tests/test_io/test_*, tests/test_proc/*, tests/test_units.py, tests/test_utils/*, tests/test_viz/*
Additional test modules consolidate imports and annotate intentional optional-dependency imports.

Possibly related PRs

  • DASDAE/dascore#743: Both PRs modify import placement and deferred import behavior in overlapping modules.
  • DASDAE/dascore#751: This PR cleans up imports in modules and areas introduced or changed by PR #751.

Suggested labels: CI

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: enabling PLC0415 and requiring explicit noqa directives for function-level imports.
Description check ✅ Passed The description explains the linting change, its scope, validation, and checklist status; optional items are marked as not applicable.
Docstring Coverage ✅ Passed Docstring coverage is 94.24% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch plc0415-lint

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@d-chambers d-chambers added the ready_for_review PR is ready for review label Aug 9, 2026
@codecov

codecov Bot commented Aug 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (94d149b) to head (2e244b8).

Additional details and impacted files
@@            Coverage Diff            @@
##               dev      #850   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          164       164           
  Lines        18192     18178   -14     
=========================================
- Hits         18192     18178   -14     
Flag Coverage Δ
network 48.38% <58.82%> (+0.08%) ⬆️
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Imports inside functions were previously invisible to lint: E402 only
covers module-level imports after other statements, and the pylint
rules were not selected. Enable PLC0415 and mark the existing lazy
imports (circular-import avoidance in dascore/, optional-dependency
and monkeypatching sites in tests/) with noqa directives so new
function-level imports require an explicit opt-out.
Enabling PLC0415 froze 249 existing function-level imports in place behind
noqa directives. Most of them were not deferred for any reason: 28 were exact
duplicates of an import already at the top of the same file, and many others
were the same import repeated across a dozen functions in one module.

Hoist the 228 that can move and drop their noqa directives, keeping the 21
that are load-bearing:

- 13 in core/spool.py, plus io/index/backend.py, utils/coordmanager.py and
  utils/patch.py, avoid circular imports.
- utils/jit.py defers the optional numba dependency.
- utils/docs.py must read _PLUGIN_REGISTRY_DIR at call time so tests can
  monkeypatch it.
- test_io_utils.py defers obspy and xarray so the module still collects when
  those optional packages are absent.
- test_index_edge_cases.py imports a sibling test module that is only on
  sys.path at call time.

Each hoist was verified by moving the import and checking that the module
still imports in a fresh interpreter; the full test suite and doctests pass.
@coderabbitai coderabbitai Bot added the CI continuous integration label Aug 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_integrations/test_attr_coord_independence.py`:
- Line 23: Move the dascore.utils.downloader fetch import out of module scope
and into test_legacy_fixture_reads(), marking the local import with # noqa:
PLC0415. Preserve the existing Pooch availability guard so the legacy test is
collected and skipped without importing downloader when Pooch is unavailable.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 39513824-860c-4b34-870d-ca15842dc032

📥 Commits

Reviewing files that changed from the base of the PR and between 94d149b and 2e244b8.

📒 Files selected for processing (53)
  • dascore/core/spool.py
  • dascore/io/core.py
  • dascore/io/index/backend.py
  • dascore/io/index/catalog.py
  • dascore/io/index/indexer.py
  • dascore/io/index/planned.py
  • dascore/io/index/query.py
  • dascore/proc/coords.py
  • dascore/utils/chunk_plan.py
  • dascore/utils/coordmanager.py
  • dascore/utils/docs.py
  • dascore/utils/jit.py
  • dascore/utils/patch.py
  • dascore/utils/patch_assembly.py
  • dascore/viz/spectrogram.py
  • pyproject.toml
  • scripts/test_render_api.py
  • tests/conftest.py
  • tests/test_core/test_coord_segmented.py
  • tests/test_core/test_directory_spool.py
  • tests/test_core/test_patch_chunk.py
  • tests/test_core/test_spool.py
  • tests/test_core/test_spool_contracts.py
  • tests/test_core/test_spool_select_spec.py
  • tests/test_imports.py
  • tests/test_integrations/test_attr_coord_independence.py
  • tests/test_io/test_dasdae/test_dasdae.py
  • tests/test_io/test_index/test_catalog.py
  • tests/test_io/test_index/test_hive_attrs.py
  • tests/test_io/test_index/test_index_contract.py
  • tests/test_io/test_index/test_index_edge_cases.py
  • tests/test_io/test_index/test_ordering.py
  • tests/test_io/test_index/test_plan.py
  • tests/test_io/test_index/test_planned.py
  • tests/test_io/test_index/test_union.py
  • tests/test_io/test_indexer.py
  • tests/test_io/test_io_core.py
  • tests/test_io/test_mseed/test_mseed.py
  • tests/test_io/test_netcdf/test_netcdf.py
  • tests/test_io/test_sintela/test_protobuf.py
  • tests/test_io/test_tdms/test_tdms_utils.py
  • tests/test_io/test_terra15/test_terra15.py
  • tests/test_proc/test_whiten.py
  • tests/test_units.py
  • tests/test_utils/test_array_utils.py
  • tests/test_utils/test_config.py
  • tests/test_utils/test_coordmanager_utils.py
  • tests/test_utils/test_doc_utils.py
  • tests/test_utils/test_io_utils.py
  • tests/test_utils/test_misc.py
  • tests/test_utils/test_patch_utils.py
  • tests/test_utils/test_pd.py
  • tests/test_viz/test_waterfall.py
💤 Files with no reviewable changes (2)
  • tests/test_proc/test_whiten.py
  • tests/test_utils/test_misc.py


import dascore as dc
from dascore.core.coordmanager import get_coord_manager
from dascore.utils.downloader import fetch

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 'pooch|def fetch|from dascore\.utils\.downloader import fetch' dascore tests

Repository: DASDAE/dascore

Length of output: 46206


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate file =="
wc -l tests/test_integrations/test_attr_coord_independence.py dascore/config.py dascore/utils/downloader.py dascore/utils/imports.py

echo
echo "== test module top and legacy test =="
sed -n '1,35p;250,272p' tests/test_integrations/test_attr_coord_independence.py

echo
echo "== downloader.py =="
sed -n '1,90p' dascore/utils/downloader.py

echo
echo "== config.py imports =="
sed -n '1,45p' dascore/config.py

echo
echo "== imports.py if present =="
sed -n '1,220p' dascore/utils/imports.py

echo
echo "== exact module-level downloader imports in tests =="
python3 - <<'PY'
from pathlib import Path
for path in sorted(Path("tests").rglob("*.py")):
    txt = path.read_text()
    lines = txt.splitlines()

    # print only if first import of fetch precedes first pytest.importorskip("pooch")
    for i,line in enumerate(lines,1):
        if "from dascore.utils.downloader import fetch" in line:
            first_fetch = i
            break
    else:
        continue
    first_skip = next((j for j,l in enumerate(lines,1) if "pytest.importorskip(\"pooch\")" in l), None)
    print(f"{path}:{first_fetch}:{first_skip}:fetch_before_pooch_skip={first_fetch < first_skip if first_skip else False}")
PY

Repository: DASDAE/dascore

Length of output: 9225


🌐 Web query:

GitHub dascore d-chambers dascore tests/test_integrations/test_attr_coord_independence.py import dascore from dascore.config import config_context

💡 Result:

DASCore is an open-source Python library designed for processing and analyzing distributed fiber optic sensing (DAS) data [1][2]. The official repository is maintained under the organization DASDAE (formerly referenced as d-chambers) at https://github.com/DASDAE/dascore [1][2][3]. The file path referenced in your query, tests/test_integrations/test_attr_coord_independence.py, belongs to the DASCore testing suite, which validates how coordinate and attribute metadata are handled within the library's Patch objects [4][5]. In DASCore, Patch objects manage both data arrays and their associated metadata (coordinates and attributes) [4]. The library enforces strict consistency rules; for example, it ensures that coordinate information and attribute dictionaries are correctly separated and that dimensions remain consistent when patches are processed or merged [4][5][6]. The import statement from dascore.config import config_context is used in various modules and tests to manage library-wide configurations, such as global settings or context-dependent behaviors, during execution [7]. For documentation and usage examples, you can refer to the official DASCore website at https://dascore.org [3]. The library is also available on PyPI for installation via pip or conda [8][3].

Citations:


🌐 Web query:

raw.githubusercontent.com/d-chambers/dascore/master/dascore/config.py import pooch

💡 Result:

The library dascore uses the Python library pooch to manage and download data files, rather than relying on pooch inside a config.py file [1][2]. While pooch is integrated into the dascore codebase, it is specifically utilized within utility modules—such as dascore/utils/downloader.py—to handle data caching, registry loading, and remote data fetching [1][2][3]. DASCore is an open-source Python library designed for distributed acoustic sensing (DAS), and its development is primarily housed under the DASDAE organization on GitHub [4][5]. Pooch is a well-known, independent third-party tool used by various scientific Python packages to simplify the process of downloading and caching sample data [6][7]. In the context of DASCore, pooch is configured to manage local data caches [1][3] and is not used as a configuration management tool in the library's config.py file [8].

Citations:


🌐 Web query:

raw.githubusercontent.com/d-chambers/dascore/master/dascore/config.py line 14

💡 Result:

The GitHub repository at raw.githubusercontent.com/d-chambers/dascore/master/dascore/config.py is associated with a personal repository ("dascore") owned by Derrick Chambers [1][2]. DASCore is an open-source Python library designed for distributed fiber optic sensing, managed primarily under the DASDAE (DAS Data Analysis Ecosystem) GitHub organization [3][4][5]. While Derrick Chambers is a primary contributor to the project [1][2], the official development and maintenance occur within the DASDAE/dascore repository [3][5]. The user-specified URL points to a personal fork or mirror [2] rather than the primary source code repository used for distribution and documentation (dascore.org) [6][3]. In the official DASDAE/dascore repository, there is no file named config.py at the top-level directory; configuration and constants are typically handled within specific modules (e.g., dascore/constants.py) or via standardized Python packaging configurations like pyproject.toml [7][8]. Users are encouraged to reference the official DASDAE repository for the most up-to-date and authoritative source code [3].

Citations:


🌐 Web query:

github DASDAE dascore dascore/utils/downloader.py import pooch

💡 Result:

In the DASCore library, the module dascore/utils/downloader.py uses the Pooch library to handle the downloading and caching of data files [1]. The module performs the following operations: 1. It imports the pooch library [1]. 2. It initializes a Pooch instance (named fetcher) using pooch.create to manage data retrieval, specifying a cache directory (pooch.os_cache("dascore")), a base URL, and a data registry file [1]. 3. It exposes a fetch function that checks if a file exists locally; if not, it uses the Pooch instance to fetch the file from the registry [1]. This functionality allows users to easily retrieve example datasets and other required files for use with DASCore [2][3][4]. For example, users can import fetch and use it to get local paths to data files, which can then be processed by other parts of the library, such as dc.spool [3][4].

Citations:


Keep fetch behind the existing Pooch guard.

fetch imports dascore.utils.downloader, which imports and creates a Pooch fetcher during module import. For the legacy TestLegacyDasdaeFile path, collect before that import runs and skip when Pooch is unavailable. Move fetch inside test_legacy_fixture_reads() with # noqa: PLC0415, or make fetch lazy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_integrations/test_attr_coord_independence.py` at line 23, Move the
dascore.utils.downloader fetch import out of module scope and into
test_legacy_fixture_reads(), marking the local import with # noqa: PLC0415.
Preserve the existing Pooch availability guard so the legacy test is collected
and skipped without importing downloader when Pooch is unavailable.

Source: MCP tools

@d-chambers
d-chambers merged commit 8a3b321 into dev Aug 10, 2026
29 checks passed
@d-chambers
d-chambers deleted the plc0415-lint branch August 10, 2026 11:16
@d-chambers d-chambers removed the ready_for_review PR is ready for review label Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI continuous integration

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant