reflective-data-catalog 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reflective_data_catalog-0.0.1/.coverage +0 -0
- reflective_data_catalog-0.0.1/.github/workflows/build-and-push.yml +79 -0
- reflective_data_catalog-0.0.1/.github/workflows/tests.yml +46 -0
- reflective_data_catalog-0.0.1/.gitignore +56 -0
- reflective_data_catalog-0.0.1/.pre-commit-config.yaml +9 -0
- reflective_data_catalog-0.0.1/CLAUDE.md +78 -0
- reflective_data_catalog-0.0.1/CONTRIBUTING.md +163 -0
- reflective_data_catalog-0.0.1/Example.ipynb +408 -0
- reflective_data_catalog-0.0.1/LICENSE +202 -0
- reflective_data_catalog-0.0.1/PKG-INFO +208 -0
- reflective_data_catalog-0.0.1/README.md +176 -0
- reflective_data_catalog-0.0.1/pyproject.toml +155 -0
- reflective_data_catalog-0.0.1/setup.cfg +4 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog/__init__.py +20 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog/data-catalog.yaml +645 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog/esgf.py +433 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog/esm.py +558 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog/flexible_sources.py +1006 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog/help_text.py +95 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog/main.py +1409 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog/py.typed +0 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog/reflective_data.py +159 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog/storage.py +362 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog.egg-info/PKG-INFO +208 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog.egg-info/SOURCES.txt +33 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog.egg-info/dependency_links.txt +1 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog.egg-info/requires.txt +11 -0
- reflective_data_catalog-0.0.1/src/reflective_data_catalog.egg-info/top_level.txt +1 -0
- reflective_data_catalog-0.0.1/tests/__init__.py +0 -0
- reflective_data_catalog-0.0.1/tests/conftest.py +270 -0
- reflective_data_catalog-0.0.1/tests/test_catalog.py +529 -0
- reflective_data_catalog-0.0.1/tests/test_esgf.py +250 -0
- reflective_data_catalog-0.0.1/tests/test_esm.py +208 -0
- reflective_data_catalog-0.0.1/tests/test_flexible_sources.py +455 -0
- reflective_data_catalog-0.0.1/tests/test_storage.py +373 -0
|
Binary file
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI
|
|
2
|
+
|
|
3
|
+
on: push
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
name: Build distribution 📦
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v6
|
|
12
|
+
with:
|
|
13
|
+
persist-credentials: false
|
|
14
|
+
fetch-depth: 0 # full history so setuptools-scm can compute the version
|
|
15
|
+
- name: Set up Python
|
|
16
|
+
uses: actions/setup-python@v6
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.x"
|
|
19
|
+
|
|
20
|
+
- name: Install pypa/build
|
|
21
|
+
run: >-
|
|
22
|
+
python3 -m
|
|
23
|
+
pip install
|
|
24
|
+
build
|
|
25
|
+
--user
|
|
26
|
+
- name: Build a binary wheel and a source tarball
|
|
27
|
+
run: python3 -m build
|
|
28
|
+
- name: Store the distribution packages
|
|
29
|
+
uses: actions/upload-artifact@v5
|
|
30
|
+
with:
|
|
31
|
+
name: python-package-distributions
|
|
32
|
+
path: dist/
|
|
33
|
+
|
|
34
|
+
publish-to-pypi:
|
|
35
|
+
name: >-
|
|
36
|
+
Publish Python 🐍 distribution 📦 to PyPI
|
|
37
|
+
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
|
|
38
|
+
needs:
|
|
39
|
+
- build
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
environment:
|
|
42
|
+
name: pypi
|
|
43
|
+
url: https://pypi.org/p/reflective-data-catalog
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write # IMPORTANT: mandatory for trusted publishing
|
|
46
|
+
|
|
47
|
+
steps:
|
|
48
|
+
- name: Download all the dists
|
|
49
|
+
uses: actions/download-artifact@v6
|
|
50
|
+
with:
|
|
51
|
+
name: python-package-distributions
|
|
52
|
+
path: dist/
|
|
53
|
+
- name: Publish distribution 📦 to PyPI
|
|
54
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
55
|
+
|
|
56
|
+
publish-to-testpypi:
|
|
57
|
+
name: Publish Python 🐍 distribution 📦 to TestPyPI
|
|
58
|
+
needs:
|
|
59
|
+
- build
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
|
|
62
|
+
environment:
|
|
63
|
+
name: testpypi
|
|
64
|
+
url: https://test.pypi.org/p/reflective-data-catalog
|
|
65
|
+
|
|
66
|
+
permissions:
|
|
67
|
+
id-token: write # IMPORTANT: mandatory for trusted publishing
|
|
68
|
+
|
|
69
|
+
steps:
|
|
70
|
+
- name: Download all the dists
|
|
71
|
+
uses: actions/download-artifact@v6
|
|
72
|
+
with:
|
|
73
|
+
name: python-package-distributions
|
|
74
|
+
path: dist/
|
|
75
|
+
- name: Publish distribution 📦 to TestPyPI
|
|
76
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
77
|
+
with:
|
|
78
|
+
repository-url: https://test.pypi.org/legacy/
|
|
79
|
+
verbose: true
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["*"]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
name: Tests (Python ${{ matrix.python-version }})
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v6
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
uses: actions/setup-python@v6
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: |
|
|
29
|
+
python -m pip install --upgrade pip
|
|
30
|
+
pip install -e ".[dev]"
|
|
31
|
+
|
|
32
|
+
- name: Lint with Ruff
|
|
33
|
+
run: |
|
|
34
|
+
ruff check .
|
|
35
|
+
ruff format --check .
|
|
36
|
+
|
|
37
|
+
- name: Run tests with coverage
|
|
38
|
+
run: |
|
|
39
|
+
pytest --cov=reflective_data_catalog --cov-report=term-missing --cov-report=xml
|
|
40
|
+
|
|
41
|
+
- name: Upload coverage to Codecov
|
|
42
|
+
if: matrix.python-version == '3.12'
|
|
43
|
+
uses: codecov/codecov-action@v5
|
|
44
|
+
with:
|
|
45
|
+
files: ./coverage.xml
|
|
46
|
+
fail_ci_if_error: false
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Python bytecode
|
|
2
|
+
**/__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
|
|
5
|
+
# Virtual environments
|
|
6
|
+
venv/
|
|
7
|
+
env/
|
|
8
|
+
.venv/
|
|
9
|
+
|
|
10
|
+
# Environment variables
|
|
11
|
+
.env
|
|
12
|
+
|
|
13
|
+
# IDE settings
|
|
14
|
+
.vscode/
|
|
15
|
+
.idea/
|
|
16
|
+
|
|
17
|
+
# OS generated files
|
|
18
|
+
.DS_Store
|
|
19
|
+
Thumbs.db
|
|
20
|
+
|
|
21
|
+
# Data files
|
|
22
|
+
*.joblib
|
|
23
|
+
*.nc
|
|
24
|
+
dump.rdb
|
|
25
|
+
*.npy
|
|
26
|
+
*.pkl
|
|
27
|
+
*.csv
|
|
28
|
+
|
|
29
|
+
# Python packages
|
|
30
|
+
*.egg-info/
|
|
31
|
+
*.egg
|
|
32
|
+
*.eggs
|
|
33
|
+
*.eggs.zip
|
|
34
|
+
*.eggs.tar.gz
|
|
35
|
+
*.eggs.tar.bz2
|
|
36
|
+
*.eggs.tar.xz
|
|
37
|
+
*.eggs.tar.lzma
|
|
38
|
+
*.eggs.tar.lz
|
|
39
|
+
*.eggs.tar.zst
|
|
40
|
+
*.eggs.tar.zstd
|
|
41
|
+
*.eggs.tar.zstd.tar.gz
|
|
42
|
+
|
|
43
|
+
# Jupyter notebooks cruft
|
|
44
|
+
**/.ipynb_checkpoints/
|
|
45
|
+
|
|
46
|
+
# Ruff cache
|
|
47
|
+
.ruff_cache/
|
|
48
|
+
|
|
49
|
+
# Pytest cache
|
|
50
|
+
.pytest_cache/
|
|
51
|
+
|
|
52
|
+
# Pytest coverage
|
|
53
|
+
.coverage
|
|
54
|
+
coverage.xml
|
|
55
|
+
htmlcov/
|
|
56
|
+
.coverage.*
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with this codebase.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
Reflective Data Catalog is a pip-installable Python package that provides a unified interface for accessing SAI (Stratospheric Aerosol Injection) climate model data stored in cloud storage (S3, GCS, Azure, Cloudflare R2). It wraps multiple climate model sources (CESM2-WACCM, MIROC-ES2H, UKESM1, E3SMv3) behind a consistent API.
|
|
8
|
+
|
|
9
|
+
## Project Structure
|
|
10
|
+
|
|
11
|
+
- `src/reflective_data_catalog/` — Package source (src layout)
|
|
12
|
+
- `main.py` — `ReflectiveCatalog` class, the main entry point
|
|
13
|
+
- `flexibleSources.py` — `FlexibleSourceConfig`, `FlexibleSource`, `SourceDiscovery`, `FlexibleSourceRegistry` classes
|
|
14
|
+
- `reflective_data.py` — `DEFAULT_FLEXIBLE_SOURCES` list of all source configurations
|
|
15
|
+
- `storage.py` — `CloudFileSystem` class wrapping obstore for multi-cloud access
|
|
16
|
+
- `esgf.py` — `ESGFHelper` for ESGF data access
|
|
17
|
+
- `esm.py` — `ESMCatalog` and `GeoMIPCloudHelper` for Google Cloud CMIP6 catalog via intake-esm
|
|
18
|
+
- `help_text.py` — Help text utilities
|
|
19
|
+
- `tests/` — Unit tests (pytest)
|
|
20
|
+
- `conftest.py` — Shared fixtures and mocks
|
|
21
|
+
- `test_flexible_sources.py` — FlexibleSourceConfig, Registry, Discovery
|
|
22
|
+
- `test_storage.py` — CloudFileSystem (including R2)
|
|
23
|
+
- `test_esgf.py` — ESGFHelper (mocked)
|
|
24
|
+
- `test_esm.py` — ESMCatalog, GeoMIPCloudHelper (mocked)
|
|
25
|
+
- `test_catalog.py` — ReflectiveCatalog integration
|
|
26
|
+
- `pyproject.toml` — Package metadata, dependencies, and Ruff config
|
|
27
|
+
- `.github/workflows/tests.yml` — CI: runs tests and coverage on push/PR
|
|
28
|
+
|
|
29
|
+
## Key Architecture
|
|
30
|
+
|
|
31
|
+
- **`FlexibleSourceConfig`** is a frozen dataclass that defines how to build cloud storage paths and filename patterns for each data source. It supports placeholders: `{base}`, `{ensemble}`, `{table_path}`, `{variable}`, `{variant}`, `{time}`, `{ensemble_id}`.
|
|
32
|
+
- **`FlexibleSource`** wraps a config and provides `.to_dask()`, `.read()`, `.list_variables()`, `.list_ensembles()`, `.list_tables()`, and `.discover()`.
|
|
33
|
+
- **`SourceDiscovery`** handles cloud storage scanning via `obstore` (through `CloudFileSystem`) to discover available data.
|
|
34
|
+
- **`CloudFileSystem`** wraps `obstore` to provide a unified API (glob, ls, exists, open) across S3, GCS, Azure, Cloudflare R2, and other cloud providers. It auto-detects the provider from the URL scheme (`s3://`, `gs://`, `az://`, `r2://`). R2 support uses `S3Store` with the Cloudflare endpoint.
|
|
35
|
+
- **`ESMCatalog`** provides intake-esm access to the Google Cloud CMIP6 catalog (search, load, list experiments/models/variables).
|
|
36
|
+
- **`GeoMIPCloudHelper`** wraps ESMCatalog with GeoMIP-specific convenience methods (g6sulfur, g6solar, load_ensemble).
|
|
37
|
+
- **`ReflectiveCatalog`** uses `__getattr__` to dynamically expose registered sources (e.g., `catalog.cesm2_waccm_ssp245()`). Also exposes `catalog.esm` and `catalog.geomip_cloud` for cloud-optimized Zarr access.
|
|
38
|
+
- Sources are registered from `DEFAULT_FLEXIBLE_SOURCES` in `reflective_data.py`.
|
|
39
|
+
|
|
40
|
+
## Common Commands
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Install in development mode
|
|
44
|
+
pip install -e ".[dev]"
|
|
45
|
+
|
|
46
|
+
# Lint
|
|
47
|
+
ruff check .
|
|
48
|
+
|
|
49
|
+
# Auto-fix lint issues
|
|
50
|
+
ruff check --fix .
|
|
51
|
+
|
|
52
|
+
# Format
|
|
53
|
+
ruff format .
|
|
54
|
+
|
|
55
|
+
# Run tests
|
|
56
|
+
pytest
|
|
57
|
+
|
|
58
|
+
# Run tests with coverage
|
|
59
|
+
pytest --cov=reflective_data_catalog --cov-report=term-missing
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Code Style
|
|
63
|
+
|
|
64
|
+
- Uses Ruff for linting and formatting (configured in `pyproject.toml`)
|
|
65
|
+
- Line length: 88 characters
|
|
66
|
+
- Double quotes for strings
|
|
67
|
+
- All imports use relative paths within the package (e.g., `from .flexibleSources import ...`)
|
|
68
|
+
|
|
69
|
+
## Important Notes
|
|
70
|
+
|
|
71
|
+
- Cloud storage access uses `obstore` via the `CloudFileSystem` wrapper — auto-detects provider from URL scheme (`s3://`, `gs://`, `az://`, `r2://`).
|
|
72
|
+
- Cloudflare R2 uses `r2://` URLs and requires an account ID via `r2_account_id` kwarg or `CLOUDFLARE_R2_ACCOUNT_ID` / `CLOUDFLARE_ACCOUNT_ID` env var.
|
|
73
|
+
- AWS credentials are needed for S3 access; similarly for GCS and Azure.
|
|
74
|
+
- Google Cloud CMIP6/GeoMIP data is accessible via `catalog.esm` and `catalog.geomip_cloud` (requires `intake-esm`).
|
|
75
|
+
- Some sources use `ensemble_mapping` to convert ensemble names to IDs in filenames (e.g., `r1` → `001` for CESM2).
|
|
76
|
+
- MIROC sources use a `variant` parameter to distinguish between file types (e.g., `baseline` vs `G6-1.5K-SAI`).
|
|
77
|
+
- UKESM1 and E3SMv3 sources have `{variable}` in the directory pattern, not just the filename.
|
|
78
|
+
- All external services are mocked in tests — no network or cloud credentials needed to run `pytest`.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Contributing to Reflective Data Catalog
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing! This document provides guidelines for contributing to the project.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
1. Fork and clone the repository:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/ReflectiveCloud/reflective-data-catalog.git
|
|
11
|
+
cd reflective-data-catalog
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
2. Install in development mode:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install -e ".[dev]"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Development Workflow
|
|
21
|
+
|
|
22
|
+
1. Create a branch for your work:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git checkout -b feature/your-feature-name
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
2. Make your changes and ensure they pass linting and tests.
|
|
29
|
+
|
|
30
|
+
3. Commit your changes with a clear message:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
git commit -m "Add brief description of change"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
4. Push and open a pull request.
|
|
37
|
+
|
|
38
|
+
## Code Style
|
|
39
|
+
|
|
40
|
+
This project uses [Ruff](https://docs.astral.sh/ruff/) for linting and formatting. Run both before submitting:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
ruff check .
|
|
44
|
+
ruff format .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Auto-fix lint issues where possible:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
ruff check --fix .
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Key style rules:
|
|
54
|
+
|
|
55
|
+
- Line length: 88 characters
|
|
56
|
+
- Double quotes for strings
|
|
57
|
+
- Space indentation (no tabs)
|
|
58
|
+
- Imports sorted with isort rules
|
|
59
|
+
|
|
60
|
+
## Running Tests
|
|
61
|
+
|
|
62
|
+
Run the full test suite:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pytest
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Run with coverage report:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pytest --cov=reflective_data_catalog --cov-report=term-missing
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Run a specific test file or test:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pytest tests/test_flexible_sources.py
|
|
78
|
+
pytest tests/test_catalog.py::TestSearch::test_search_by_term
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
All external services (S3, ESGF, intake-esm) are mocked in the test suite — no network access or cloud credentials are needed.
|
|
82
|
+
|
|
83
|
+
### Writing Tests
|
|
84
|
+
|
|
85
|
+
- Tests live in `tests/` and use [pytest](https://docs.pytest.org/).
|
|
86
|
+
- Shared fixtures and mocks are in `tests/conftest.py`.
|
|
87
|
+
- Mock any cloud or network calls; tests must run offline.
|
|
88
|
+
- Aim for one test file per source module (e.g. `test_storage.py` for `storage.py`).
|
|
89
|
+
|
|
90
|
+
## Adding a New Data Source
|
|
91
|
+
|
|
92
|
+
To add a new flexible data source, create a `FlexibleSourceConfig` entry in `src/reflective_data_catalog/reflective_data.py`:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
FlexibleSourceConfig(
|
|
96
|
+
name='model_experiment', # Snake-case identifier
|
|
97
|
+
base='s3://bucket/path/to/data', # Cloud storage base URL (s3://, gs://, az://, r2://)
|
|
98
|
+
pattern='{base}/{ensemble}/{table_path}', # Directory pattern
|
|
99
|
+
filename_pattern='{variable}.*.nc', # Filename pattern
|
|
100
|
+
table_mapping={'Amon': 'Amon'}, # Table name mapping
|
|
101
|
+
default_table='Amon',
|
|
102
|
+
default_variable='tas',
|
|
103
|
+
default_ensemble='r1i1p1f1',
|
|
104
|
+
driver='netcdf',
|
|
105
|
+
description='Model experiment description'
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The source will automatically be available as `catalog.model_experiment()`.
|
|
110
|
+
|
|
111
|
+
### Configuration Fields
|
|
112
|
+
|
|
113
|
+
| Field | Description |
|
|
114
|
+
|-------|-------------|
|
|
115
|
+
| `name` | Unique snake_case identifier |
|
|
116
|
+
| `base` | Cloud storage base URL (`s3://`, `gs://`, `az://`, `r2://`) |
|
|
117
|
+
| `pattern` | Directory path template with `{base}`, `{ensemble}`, `{table_path}`, `{variable}`, `{time}` placeholders |
|
|
118
|
+
| `filename_pattern` | Filename template with `{variable}`, `{ensemble}`, `{variant}`, `{ensemble_id}`, `{time}`, and `*` wildcards |
|
|
119
|
+
| `table_mapping` | Dict mapping table names to S3 directory names |
|
|
120
|
+
| `ensemble_mapping` | Optional dict mapping ensemble names to filename IDs (e.g. `{'r1': '001'}`) |
|
|
121
|
+
| `default_table` | Default table when none is specified |
|
|
122
|
+
| `default_variable` | Default variable when none is specified |
|
|
123
|
+
| `default_ensemble` | Default ensemble member |
|
|
124
|
+
| `default_variant` | Optional default variant (e.g. `'baseline'`) |
|
|
125
|
+
| `default_time` | Optional default time/frequency (e.g. `'AERmon'`) |
|
|
126
|
+
| `driver` | `'netcdf'` or `'zarr'` |
|
|
127
|
+
| `combine_files` | How to combine multi-file sources: `'by_coords'`, `'nested'`, or `'first'` |
|
|
128
|
+
| `concat_dim` | Dimension to concatenate along (default: `'time'`) |
|
|
129
|
+
| `description` | Human-readable description |
|
|
130
|
+
|
|
131
|
+
## Project Structure
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
src/reflective_data_catalog/
|
|
135
|
+
├── __init__.py # Package exports
|
|
136
|
+
├── main.py # ReflectiveCatalog class
|
|
137
|
+
├── flexible_sources.py # FlexibleSourceConfig, FlexibleSource, SourceDiscovery
|
|
138
|
+
├── reflective_data.py # Default source configurations
|
|
139
|
+
├── esgf.py # ESGF data access helper
|
|
140
|
+
├── esm.py # intake-esm Google Cloud CMIP6/GeoMIP access
|
|
141
|
+
├── storage.py # CloudFileSystem (obstore wrapper)
|
|
142
|
+
└── help_text.py # Help text utilities
|
|
143
|
+
tests/
|
|
144
|
+
├── conftest.py # Shared fixtures and mocks
|
|
145
|
+
├── test_flexible_sources.py # FlexibleSourceConfig, Registry, Discovery
|
|
146
|
+
├── test_storage.py # CloudFileSystem
|
|
147
|
+
├── test_esgf.py # ESGFHelper (mocked)
|
|
148
|
+
├── test_esm.py # ESMCatalog, GeoMIPCloudHelper (mocked)
|
|
149
|
+
└── test_catalog.py # ReflectiveCatalog integration
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Reporting Issues
|
|
153
|
+
|
|
154
|
+
When reporting a bug, please include:
|
|
155
|
+
|
|
156
|
+
- Python version
|
|
157
|
+
- Package version
|
|
158
|
+
- Steps to reproduce the issue
|
|
159
|
+
- Full error traceback
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
By contributing, you agree that your contributions will be licensed under the Apache 2.0 license.
|