Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ jobs:
- name: Install adapt
run: pip install -e . --no-deps --force-reinstall

- name: Install ruff
run: pip install ruff

- name: Install import-linter
run: pip install import-linter

- name: Show environment info
run: |
python --version
pip list | head -n 30

- name: Lint with ruff
run: ruff check src tests

- name: Check import architecture
run: lint-imports --no-cache
- name: Run tests
run: |
pytest -m "not integration" \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build and Deploy Docs
name: Deploy Docs

on:
push:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pypi-release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build and Upload Adapt Release to PyPI
name: Release to PyPI
on:
release:
types:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/virus.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
name: Virus Scan
on:
pull_request:
types: [assigned, opened, synchronize, reopened, closed]
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,5 @@ data/
# ignore hidden files but not gitignore
.*
!.gitignore
!.importlinter
__pycache__/
156 changes: 156 additions & 0 deletions .importlinter
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Import Linter configuration for ADAPT
#
# Purpose:
# Enforce architectural boundaries so the codebase stays modular,
# testable, and free of accidental coupling.
#
# Run:
# lint-imports
#
# Philosophy:
# - Modules are scientific units and must remain independent.
# - Shared behaviour belongs in contracts, runtime, or persistence.
# - Prevent cross-layer imports early, before they calcify.

[importlinter]
root_packages =
adapt

include_external_packages = False

# ==========================================================
# 1. Scientific modules must remain independent
# ==========================================================
#
# No module may import from any other module — directly or
# transitively. Shared types belong in adapt.contracts.

[importlinter:contract:independent_modules]
name = Adapt modules remain independent
type = independence
modules =
adapt.modules.acquisition
adapt.modules.analysis
adapt.modules.detection
adapt.modules.ingest
adapt.modules.projection
adapt.modules.tracking

# ==========================================================
# 2. Modules do not depend on runtime orchestration
# ==========================================================
#
# Runtime coordinates modules; modules must not call back into it.
# Science is stateless. Orchestration is runtime's job.

[importlinter:contract:modules_do_not_import_runtime]
name = Modules do not depend on runtime
type = forbidden
source_modules =
adapt.modules
forbidden_modules =
adapt.runtime

# ==========================================================
# 3. Modules do not reach into infrastructure layers
# ==========================================================
#
# Modules produce data. Infrastructure stores and serves it.
# A module that imports persistence or GUI has too many responsibilities.
#
# Includes configuration: modules must receive their settings
# via dependency injection from the execution layer — not read
# configuration themselves.

[importlinter:contract:modules_do_not_import_infrastructure]
name = Modules do not import infrastructure layers
type = forbidden
source_modules =
adapt.modules
forbidden_modules =
adapt.persistence
adapt.api
adapt.gui
adapt.visualization
adapt.configuration

# ==========================================================
# 4. Persistence is infrastructure — no science, no orchestration
# ==========================================================
#
# Persistence reads and writes. It must not depend on modules
# (would create a circular science ↔ storage loop) or runtime
# (would couple storage to one execution strategy).

[importlinter:contract:persistence_is_infra]
name = Persistence does not depend on modules or runtime
type = forbidden
source_modules =
adapt.persistence
forbidden_modules =
adapt.modules
adapt.runtime

# ==========================================================
# 5. Nothing inside core depends on the CLI
# ==========================================================
#
# CLI is the outermost shell. It may import anything.
# Core packages must not import it — that would make them
# impossible to use as a library.

[importlinter:contract:no_internal_cli_dependency]
name = Internal packages do not depend on CLI
type = forbidden
source_modules =
adapt.modules
adapt.runtime
adapt.persistence
adapt.execution
forbidden_modules =
adapt.cli

# ==========================================================
# 6. Core must not depend on optional extensions
# ==========================================================
#
# Extensions add capability without modifying core.
# If core imports extensions, extensions can no longer be
# optional and the plug-in model breaks.

[importlinter:contract:core_not_depend_on_extensions]
name = Core packages do not depend on extensions
type = forbidden
source_modules =
adapt.modules
adapt.runtime
adapt.persistence
adapt.execution
adapt.configuration
forbidden_modules =
adapt.extensions

# ==========================================================
# 7. Contracts package imports no adapt internals
# ==========================================================
#
# adapt.contracts is the only layer both modules and execution
# may share. It must depend on nothing inside adapt so it
# never creates import cycles.

[importlinter:contract:contracts_are_pure]
name = Contracts package imports no adapt internals
type = forbidden
source_modules =
adapt.contracts
forbidden_modules =
adapt.modules
adapt.runtime
adapt.persistence
adapt.configuration
adapt.execution
adapt.api
adapt.gui
adapt.visualization
adapt.cli
adapt.extensions
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
# Adapt


[![CI](https://github.com/ARM-DOE/Adapt/actions/workflows/ci.yml/badge.svg)](https://github.com/ARM-DOE/Adapt/actions?query=workflow%3ACI)
[![Codecov](https://img.shields.io/codecov/c/github/ARM-DOE/Adapt.svg?logo=codecov)](https://codecov.io/gh/ARM-DOE/Adapt)
[![Docs](https://img.shields.io/badge/docs-users-4088b8.svg)](https://arm-doe.github.io/Adapt/)
[![PyPI Downloads](https://img.shields.io/pypi/dm/arm-adapt.svg)](https://pypi.org/project/arm-adapt/)
[![CodeFactor](https://www.codefactor.io/repository/github/arm-doe/adapt/badge)](https://www.codefactor.io/repository/github/arm-doe/adapt)


[![Docs](https://github.com/ARM-DOE/Adapt/actions/workflows/docs.yml/badge.svg)](https://arm-doe.github.io/Adapt/)
[![PyPi release](https://github.com/ARM-DOE/Adapt/actions/workflows/pypi-release.yml/badge.svg)](https://arm-doe.github.io/Adapt/)
[![PyPI - Version](https://img.shields.io/pypi/v/arm-adapt)](https://pypi.org/project/arm-adapt/)
[![PyPI Downloads](https://static.pepy.tech/personalized-badge/arm-adapt?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pypi.org/project/arm-adapt/)

[![Security](https://github.com/ARM-DOE/Adapt/actions/workflows/security-analysis.yml/badge.svg)](https://arm-doe.github.io/Adapt/)
[![Virus](https://github.com/ARM-DOE/Adapt/actions/workflows/virus.yml/badge.svg)](https://arm-doe.github.io/Adapt/)


[![PyPI - License](https://img.shields.io/pypi/l/arm-adapt)](https://github.com/ARM-DOE/Adapt?tab=License-1-ov-file#)
[![ARM](https://img.shields.io/badge/Sponsor-ARM-blue.svg?colorA=00c1de&colorB=00539c)](https://www.arm.gov/)




**Real-time processing for informed adaptive scanning of ARM weather radar operations and field campaigns.**

`Adapt` is a configuration-driven modular framework for near real-time analysis of convective systems designed to support the adaptive sampling and study of convective storms and their life cycles. The system implements a modular pipeline that ingests radar observations, performs gridding and segmentation to identify convective cells, and maintains their identity through time using tracking. It further derives cell-level properties and motion to characterize storm evolution and generate candidate targets for adaptive radar scanning.
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
import sys
import re
import sys
from importlib.metadata import version as _pkg_version

sys.path.insert(0, os.path.abspath("../src"))
Expand Down
18 changes: 18 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,21 @@ source = ["src/adapt"]
[tool.coverage.report]
ignore_errors = true
precision = 2

[tool.ruff]
line-length = 100
target-version = "py311"
src = ["src"]

[tool.ruff.lint]
select = [
"E", # syntax / style errors
"F", # logic bugs (undefined vars, etc.)
"I", # import hygiene
"B", # bug patterns
"UP", # modern Python upgrades
"SIM", # simplifications (often removes subtle mistakes)
]

[tool.ruff.lint.isort]
known-first-party = ["adapt"]
Loading
Loading