dkist-processing-visp 5.7.12__tar.gz → 5.7.14__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.
- dkist_processing_visp-5.7.14/.rovodev/.review-agent.md +537 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/CHANGELOG.rst +18 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/PKG-INFO +44 -35
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/README.rst +12 -2
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/l1_output_data.py +3 -2
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp.egg-info/PKG-INFO +44 -35
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp.egg-info/SOURCES.txt +1 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp.egg-info/requires.txt +31 -32
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/pyproject.toml +33 -34
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/.gitignore +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/.pre-commit-config.yaml +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/.readthedocs.yml +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/.snyk +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/SCIENCE_CHANGELOG.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/bitbucket-pipelines.yml +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/changelog/.gitempty +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/__init__.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/config.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/fonts/Lato-Regular.ttf +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/models/__init__.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/models/constants.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/models/dataset_extras.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/models/fits_access.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/models/metric_code.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/models/parameters.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/models/tags.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/models/task_name.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/parsers/__init__.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/parsers/map_repeats.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/parsers/modulator_states.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/parsers/polarimeter_mode.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/parsers/raster_step.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/parsers/spectrograph_configuration.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/parsers/time.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/parsers/visp_l0_fits_access.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/parsers/visp_l1_fits_access.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/__init__.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/assemble_movie.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/background_light.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/dark.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/geometric.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/instrument_polarization.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/lamp.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/make_movie_frames.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/mixin/__init__.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/mixin/beam_access.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/mixin/corrections.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/mixin/downsample.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/parse.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/polcal_as_science.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/quality_metrics.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/science.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/solar.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/visp_base.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/wavelength_calibration.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/write_dataset_extras.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tasks/write_l1.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/README.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/__init__.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/conftest.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/header_models.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/local_trial_workflows/__init__.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/local_trial_workflows/l0_cals_only.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/local_trial_workflows/l0_polcals_as_science.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/local_trial_workflows/l0_solar_gain_as_science.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/local_trial_workflows/l0_to_l1.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/local_trial_workflows/local_trial_helpers.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_assemble_movie.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_assemble_quality.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_background_light.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_dark.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_downsample.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_fits_access.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_geometric.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_instrument_polarization.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_lamp.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_make_movie_frames.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_map_repeats.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_parameters.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_parse.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_polcal_as_science.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_quality.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_science.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_solar.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_trial_create_quality_report.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_visp_base.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_visp_constants.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_wavelength_calibration.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_workflows.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_write_dataset_extras.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/tests/test_write_l1.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/workflows/__init__.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/workflows/l0_processing.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/workflows/single_task_workflows.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp/workflows/trial_workflows.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp.egg-info/dependency_links.txt +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/dkist_processing_visp.egg-info/top_level.txt +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/Makefile +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/background_light.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/changelog.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/conf.py +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/dataset_extras.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/gain_correction.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/geometric.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/index.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/l0_to_l1_visp.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/l0_to_l1_visp_full-trial.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/landing_page.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/make.bat +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/polarization_calibration.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/requirements.txt +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/requirements_table.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/science_calibration.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/scientific_changelog.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/docs/wavelength_calibration.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/licenses/LICENSE.rst +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/science_towncrier.sh +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/setup.cfg +0 -0
- {dkist_processing_visp-5.7.12 → dkist_processing_visp-5.7.14}/towncrier_science.toml +0 -0
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
# Custom Code Review Instructions for dkist-processing-* Repositories
|
|
2
|
+
|
|
3
|
+
This document configures automated PR review guidance for DKIST science data processing repositories.
|
|
4
|
+
It is intended for repos in the `dkist-processing-*` family, including:
|
|
5
|
+
|
|
6
|
+
- `dkist-processing-core`: shared workflow/task abstractions used by other processing packages.
|
|
7
|
+
- `dkist-processing-common`: common processing utilities and task bases used by instrument packages.
|
|
8
|
+
- Instrument processing packages such as `dkist-processing-visp`, where most science pipeline changes occur.
|
|
9
|
+
|
|
10
|
+
Focus comments on objective, code-reviewable findings. Avoid blocking a PR on subjective preferences unless the code clearly violates one of the conventions below or introduces a maintainability/bug risk.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Review Priorities
|
|
15
|
+
|
|
16
|
+
When reviewing a PR, prioritize:
|
|
17
|
+
|
|
18
|
+
1. Correctness and data integrity bugs.
|
|
19
|
+
2. Violations of repository architecture or dependency direction.
|
|
20
|
+
3. Patterns that make future instrument-specific changes harder.
|
|
21
|
+
4. Missing tests for changed behavior.
|
|
22
|
+
5. Maintainability issues that are concrete and actionable.
|
|
23
|
+
|
|
24
|
+
Do not repeat findings that are already clearly handled by linters, formatters, or type checkers unless the issue has architectural or runtime significance.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Repository Layering and Dependency Direction
|
|
29
|
+
|
|
30
|
+
**Description**: Preserve the dependency direction across processing repositories.
|
|
31
|
+
|
|
32
|
+
- `dkist-processing-core` must remain generic and must not import from `dkist-processing-common` or any instrument package.
|
|
33
|
+
- `dkist-processing-common` may depend on core abstractions, but must not import instrument-specific packages such as `dkist-processing-visp`.
|
|
34
|
+
- Instrument packages may depend on common and core, but should not create cross-instrument dependencies.
|
|
35
|
+
|
|
36
|
+
**Flag when**:
|
|
37
|
+
|
|
38
|
+
- A lower-level package imports a higher-level package.
|
|
39
|
+
- Shared code gains instrument-specific logic that belongs in an instrument package.
|
|
40
|
+
- Instrument-specific constants, FITS keys, task names, or parser behavior are added to common/core without a clear abstraction.
|
|
41
|
+
|
|
42
|
+
**Good pattern**:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
# instrument package code may use common abstractions
|
|
46
|
+
from dkist_processing_common.tasks import WorkflowTaskBase
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Bad pattern**:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
# common/core code should not depend on an instrument package
|
|
53
|
+
from dkist_processing_visp.models.tags import VispTag
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Task Mixins
|
|
59
|
+
|
|
60
|
+
**Description**: Task mixins encapsulate reusable task functionality in small modules and are composed into concrete task classes only where needed. Prefer minimizing new mixins and concentrating them around dependencies on external/shared resources rather than using mixins as general helper-method buckets.
|
|
61
|
+
|
|
62
|
+
**Flag when**:
|
|
63
|
+
|
|
64
|
+
- A mixin class does not end with `Mixin`.
|
|
65
|
+
- A public mixin method is not prefixed with the mixin-specific namespace.
|
|
66
|
+
- Example: methods on `MetadataStoreMixin` should begin with `metadata_store_`.
|
|
67
|
+
- Example: methods on `ObjectStoreMixin` should begin with `object_store_`.
|
|
68
|
+
- A mixin imports another mixin, creating hidden coupling between reusable behaviors.
|
|
69
|
+
- A mixin contains behavior that is only meaningful for a single concrete task or instrument and would be clearer on that task/base class.
|
|
70
|
+
|
|
71
|
+
**Good example**:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
class MetadataStoreMixin:
|
|
75
|
+
def metadata_store_get_parameter(self, key: str) -> object:
|
|
76
|
+
...
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Bad example**:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
class MetadataStoreMixin:
|
|
83
|
+
def get_parameter(self, key: str) -> object:
|
|
84
|
+
...
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Bad example**:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
# In a mixin module
|
|
91
|
+
from dkist_processing_common.tasks.mixin.object_store import ObjectStoreMixin
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Task Classes and Task Imports
|
|
97
|
+
|
|
98
|
+
**Description**: Concrete task classes represent workflow/DAG nodes. Keep task dependencies explicit and avoid coupling concrete tasks to each other.
|
|
99
|
+
|
|
100
|
+
**Flag when**:
|
|
101
|
+
|
|
102
|
+
- A concrete task imports another concrete task from a `tasks` package module.
|
|
103
|
+
- Imports from `tasks` package modules are not from `*_base` modules or the `tasks.mixin` package.
|
|
104
|
+
- A class that is intended as a base class does not end with `Base`.
|
|
105
|
+
- A concrete task contains branching logic that appears to select instrument-specific behavior that should instead be represented by separate task classes or instrument-specific subclasses.
|
|
106
|
+
|
|
107
|
+
**Good example**:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from dkist_processing_common.tasks.write_l1_base import WriteL1Base
|
|
111
|
+
from dkist_processing_visp.tasks.mixin.beam_access import BeamAccessMixin
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Bad example**:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from dkist_processing_visp.tasks.dark import DarkCalibration
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Overriding Base-Class Methods
|
|
123
|
+
|
|
124
|
+
**Description**: When overriding methods from task or framework base classes, preserve base-class behavior unless there is an explicit reason not to.
|
|
125
|
+
|
|
126
|
+
**Flag when**:
|
|
127
|
+
|
|
128
|
+
- A subclass overrides a method that exists on a base class and does not call `super().method_name(...)`.
|
|
129
|
+
- The PR does not explain why the base implementation should be skipped.
|
|
130
|
+
|
|
131
|
+
**Good example**:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
class ScienceCalibration(VispTaskBase):
|
|
135
|
+
def run(self) -> None:
|
|
136
|
+
super().run()
|
|
137
|
+
self.science_calibrate_frames()
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Bad example**:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
class ScienceCalibration(VispTaskBase):
|
|
144
|
+
def run(self) -> None:
|
|
145
|
+
self.science_calibrate_frames()
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Task-Class Docstrings
|
|
151
|
+
|
|
152
|
+
**Description**: Concrete task class docstrings are user-facing narrative documentation.
|
|
153
|
+
|
|
154
|
+
**Flag when**:
|
|
155
|
+
|
|
156
|
+
- A new or changed concrete task class lacks a docstring.
|
|
157
|
+
- The docstring is only a developer note and does not explain what the task does for pipeline users/operators.
|
|
158
|
+
- The docstring repeats the class name without adding useful context.
|
|
159
|
+
|
|
160
|
+
**Good example**:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
class DarkCalibration(VispTaskBase):
|
|
164
|
+
"""Create dark calibration arrays for downstream ViSP calibration tasks."""
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Bad example**:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
class DarkCalibration(VispTaskBase):
|
|
171
|
+
"""DarkCalibration class."""
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Tracing Span Usage
|
|
177
|
+
|
|
178
|
+
**Description**: Tracing spans using `telemetry_span...` should provide useful operational visibility without overwhelming telemetry.
|
|
179
|
+
|
|
180
|
+
**Flag when**:
|
|
181
|
+
|
|
182
|
+
- A span name is vague, e.g. `run`, `process`, `loop`, `do stuff`, or merely repeats the task name.
|
|
183
|
+
- A span is created inside an unbounded loop such as raster steps, DSPS repeats, frames of arbitrary count, or input records without a fixed upper bound.
|
|
184
|
+
- A single added span mirrors the always-on run/task span but provides no additional visibility.
|
|
185
|
+
- Load, process, and write phases are mixed together in one broad span when the code clearly separates those phases.
|
|
186
|
+
- Span placement is so low in the loop structure that it risks producing excessive telemetry volume.
|
|
187
|
+
|
|
188
|
+
**Acceptable pattern**:
|
|
189
|
+
|
|
190
|
+
- Individual spans for bounded, known-small loop dimensions such as ViSP beam or VBI camera position.
|
|
191
|
+
- Separate spans for load/process/write phases when they help diagnose runtime behavior.
|
|
192
|
+
|
|
193
|
+
**Bad example**:
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
for raster_step in raster_steps:
|
|
197
|
+
with telemetry_span(f"Process raster step {raster_step}"):
|
|
198
|
+
self.process_raster_step(raster_step)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## FITS Header Access
|
|
204
|
+
|
|
205
|
+
**Description**: FITS header names and values should be abstracted through `FitsAccessBase` and instrument-specific subclasses.
|
|
206
|
+
|
|
207
|
+
**Flag when**:
|
|
208
|
+
|
|
209
|
+
- Code outside FITS access classes reads FITS header keys directly from headers or dictionaries.
|
|
210
|
+
- A raw Spec122 key is used where a translated `FitsAccessBase` attribute exists.
|
|
211
|
+
- Example: prefer `CAM_ID` over `CAM__001` when a translated attribute exists.
|
|
212
|
+
- Header-version-specific logic is added outside FITS access classes/parsers.
|
|
213
|
+
- Multiple modules duplicate the same raw header-key string.
|
|
214
|
+
|
|
215
|
+
**Good example**:
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
camera_id = fits_access.camera_id
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Bad example**:
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
camera_id = header["CAM__001"]
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Parameters and Parameter Storage
|
|
230
|
+
|
|
231
|
+
**Description**: Parameter storage format should be chosen deliberately based on size, serialization needs, and operator readability.
|
|
232
|
+
|
|
233
|
+
**Flag when**:
|
|
234
|
+
|
|
235
|
+
- A new parameter stores large array-like data as JSON instead of a file-backed format such as `.npy`.
|
|
236
|
+
- A small list or simple scalar is written as an opaque file when JSON would be clearer.
|
|
237
|
+
- A file parameter is not self-describing enough to inspect with ordinary tools where practical.
|
|
238
|
+
- The storage format appears to simply mirror the input format without considering downstream use.
|
|
239
|
+
- The same parameter key/string is duplicated across multiple locations instead of being defined once.
|
|
240
|
+
|
|
241
|
+
**Good pattern**:
|
|
242
|
+
|
|
243
|
+
- Use JSON-compatible values for small scalar/list parameters.
|
|
244
|
+
- Use `.npy` or another explicit file format for large array-like data.
|
|
245
|
+
- Prefer named constants, enums, or parameter model attributes over repeated string literals.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Logging Standards
|
|
250
|
+
|
|
251
|
+
### Instrument Pipeline Repositories (`dkist-processing-[visp|vbi|...]`)
|
|
252
|
+
Instrument-specific processing repositories should use the logging infrastructure provided by `dkist-service-configuration`.
|
|
253
|
+
|
|
254
|
+
**Expected pattern**:
|
|
255
|
+
```python
|
|
256
|
+
from dkist_service_configuration.logging import logger
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
or the repository-standard wrapper around that logger.
|
|
260
|
+
|
|
261
|
+
### Core and Shared Libraries (`dkist-processing-core`, `dkist-processing-common`)
|
|
262
|
+
Core/shared libraries should use standard library logging with module-local loggers.
|
|
263
|
+
|
|
264
|
+
**Expected pattern**:
|
|
265
|
+
```python
|
|
266
|
+
import logging
|
|
267
|
+
|
|
268
|
+
logger = logging.getLogger(__name__)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### General Logging Rules
|
|
272
|
+
- No `print()` statements in production code.
|
|
273
|
+
- Rare exceptions may exist for notebook-oriented execution/debug workflows.
|
|
274
|
+
- New modules should define a module-level logger rather than creating loggers inline repeatedly.
|
|
275
|
+
- Logging should provide actionable operational/debugging information and avoid noisy per-item logging inside large loops unless rate-limited or scoped by tracing spans.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Exceptions
|
|
280
|
+
|
|
281
|
+
**Description**: Preserve exception context when converting or re-raising errors.
|
|
282
|
+
|
|
283
|
+
**Flag when**:
|
|
284
|
+
|
|
285
|
+
- Code catches an exception and raises a new exception without `raise ... from exc`.
|
|
286
|
+
- Code catches broad exceptions without a clear recovery path.
|
|
287
|
+
- Code logs an exception and continues in a way that may hide data loss or partial processing.
|
|
288
|
+
|
|
289
|
+
**Good example**:
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
try:
|
|
293
|
+
value = parse_value(raw_value)
|
|
294
|
+
except ValueError as exc:
|
|
295
|
+
raise ParameterError("Could not parse parameter value") from exc
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**Bad example**:
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
try:
|
|
302
|
+
value = parse_value(raw_value)
|
|
303
|
+
except ValueError:
|
|
304
|
+
raise ParameterError("Could not parse parameter value")
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Type Hints
|
|
310
|
+
|
|
311
|
+
**Description**: All new or modified functions and methods should have complete type hints.
|
|
312
|
+
|
|
313
|
+
**Flag when**:
|
|
314
|
+
|
|
315
|
+
- A function parameter lacks a type annotation.
|
|
316
|
+
- A function lacks an explicit return type, including `-> None` for functions used only for side effects.
|
|
317
|
+
- New code uses `Any`, untyped containers, or `dict`/`list` without parameterization where a concrete type is practical.
|
|
318
|
+
|
|
319
|
+
**Good example**:
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
def write_frame(frame_path: Path, tags: list[str]) -> None:
|
|
323
|
+
...
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Bad example**:
|
|
327
|
+
|
|
328
|
+
```python
|
|
329
|
+
def write_frame(frame_path, tags):
|
|
330
|
+
...
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Avoid `**kwargs` for Core Behavior
|
|
336
|
+
|
|
337
|
+
**Description**: Prefer explicit parameters over catch-all keyword arguments.
|
|
338
|
+
|
|
339
|
+
**Flag when**:
|
|
340
|
+
|
|
341
|
+
- New public functions, methods, or constructors use `**kwargs` without a strong reason.
|
|
342
|
+
- `**kwargs` is used to pass required domain values such as task names, tags, parameter keys, FITS keys, or calibration settings.
|
|
343
|
+
- `**kwargs` hides the contract between task classes and their collaborators.
|
|
344
|
+
|
|
345
|
+
**Acceptable uses**:
|
|
346
|
+
|
|
347
|
+
- Compatibility with third-party framework hooks that require `**kwargs`.
|
|
348
|
+
- Thin adapters where the accepted keys are documented and validated immediately.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Avoid Python Magic That Breaks Navigation
|
|
353
|
+
|
|
354
|
+
**Description**: Avoid dynamic patterns that break IDE “Go To Definition” and “Where Used” for domain behavior.
|
|
355
|
+
|
|
356
|
+
**Flag when**:
|
|
357
|
+
|
|
358
|
+
- New code uses `getattr`, `setattr`, dynamic imports, monkeypatching, or computed method names for ordinary domain logic.
|
|
359
|
+
- String names are used to reference methods/classes that could be imported or called directly.
|
|
360
|
+
- Magic behavior makes task dependencies, parameter names, or FITS keys hard to trace.
|
|
361
|
+
|
|
362
|
+
**Acceptable uses**:
|
|
363
|
+
|
|
364
|
+
- Well-contained framework integration points.
|
|
365
|
+
- Tests that intentionally patch or inspect behavior.
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Function and Method Size / Complexity
|
|
370
|
+
|
|
371
|
+
**Description**: Large functions with multiple conceptual sections are hard to review, test, and maintain.
|
|
372
|
+
|
|
373
|
+
**Flag when**:
|
|
374
|
+
|
|
375
|
+
- A new or heavily modified function contains multiple comment-delimited sections such as “load”, “process”, and “write”.
|
|
376
|
+
- A function introduces deep nested loops/conditionals that could be split into named helpers.
|
|
377
|
+
- A long function mixes parsing, validation, processing, and output-writing logic.
|
|
378
|
+
- Complex branching is driven by instrument/task type and would be clearer in separate task classes or polymorphic methods.
|
|
379
|
+
|
|
380
|
+
**Good review suggestion**:
|
|
381
|
+
|
|
382
|
+
> Consider extracting the commented `load`, `calibrate`, and `write` sections into named helper methods. That would make each phase easier to test and would give each block a docstring/narrative name.
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Names and Common Strings
|
|
387
|
+
|
|
388
|
+
**Description**: Names should communicate domain meaning, and repeated strings should be centralized.
|
|
389
|
+
|
|
390
|
+
**Flag when**:
|
|
391
|
+
|
|
392
|
+
- Variables, methods, or functions have vague names such as `data`, `thing`, `stuff`, `foo`, `tmp`, or `val` in non-trivial code.
|
|
393
|
+
- A commonly used string appears multiple times and should be represented by an enum, model attribute, constant, or tag class.
|
|
394
|
+
- New task names, tags, metric codes, parameter names, or FITS-related strings are duplicated instead of added to the existing model/enum structure.
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## Comments, TODOs, and Docstrings
|
|
399
|
+
|
|
400
|
+
**Description**: Comments and docstrings should improve maintainability and generated documentation.
|
|
401
|
+
|
|
402
|
+
**Flag when**:
|
|
403
|
+
|
|
404
|
+
- New code adds TODO/FIXME comments without an issue reference, owner, or clear follow-up path.
|
|
405
|
+
- A comment explains what a block of code does, but the block could be extracted into a well-named helper with a docstring.
|
|
406
|
+
- A public function/method docstring is missing or too vague to help generated documentation.
|
|
407
|
+
- Inline comments duplicate obvious code behavior instead of explaining intent or constraints.
|
|
408
|
+
|
|
409
|
+
**Good example**:
|
|
410
|
+
|
|
411
|
+
```python
|
|
412
|
+
def calculate_beam_offsets(...) -> BeamOffsets:
|
|
413
|
+
"""Calculate beam offsets used to align downstream ViSP calibration arrays."""
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
**Bad example**:
|
|
417
|
+
|
|
418
|
+
```python
|
|
419
|
+
# Calculate beam offsets
|
|
420
|
+
beam_offsets = calculate_offsets(...)
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Protected Members and Object Representation
|
|
426
|
+
|
|
427
|
+
**Description**: Avoid reaching through object internals and ensure custom objects are debuggable.
|
|
428
|
+
|
|
429
|
+
**Flag when**:
|
|
430
|
+
|
|
431
|
+
- Production code accesses protected members such as `_value`, `_cache`, or `_internal_state` on objects it does not own.
|
|
432
|
+
- New domain/model classes lack a useful `repr`/Pydantic representation/dataclass representation when instances are likely to appear in logs, debugging output, or assertion failures.
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Tests and Coverage
|
|
437
|
+
|
|
438
|
+
**Description**: Changed behavior should be covered by targeted tests.
|
|
439
|
+
|
|
440
|
+
**Flag when**:
|
|
441
|
+
|
|
442
|
+
- New task behavior, parser behavior, FITS access behavior, parameter serialization, or workflow behavior is added without tests.
|
|
443
|
+
- A bug fix lacks a regression test.
|
|
444
|
+
- Tests assert only that code runs, without checking the meaningful output or side effect.
|
|
445
|
+
- Coverage decreases in important domain logic without explanation.
|
|
446
|
+
- Missing lines are justified only by “hard to test” when a smaller helper function would make the logic testable.
|
|
447
|
+
|
|
448
|
+
**Acceptable explanation**:
|
|
449
|
+
|
|
450
|
+
- Some branches require integration-test infrastructure or heavy mocking and are already covered by an integration workflow. In that case, ask for a PR note or test comment identifying the integration coverage.
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## Notebook and Structured File Handling
|
|
455
|
+
|
|
456
|
+
**Description**: Use native APIs for structured formats when accuracy matters.
|
|
457
|
+
|
|
458
|
+
**Flag when**:
|
|
459
|
+
|
|
460
|
+
- Notebook code manually reads text and then calls `nbformat.reads()` where `nbformat.read()` with a `Path` or file handle would be clearer.
|
|
461
|
+
- Structured data such as JSON, notebooks, ASDF, or XML is searched as raw text when a native parser should be used.
|
|
462
|
+
- A PR changes polling/performance-sensitive code to parse full structured files repeatedly without acknowledging the performance tradeoff.
|
|
463
|
+
|
|
464
|
+
**Good example**:
|
|
465
|
+
|
|
466
|
+
```python
|
|
467
|
+
notebook = nbformat.read(notebook_path, as_version=nbformat.NO_CONVERT)
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
**Bad example**:
|
|
471
|
+
|
|
472
|
+
```python
|
|
473
|
+
notebook = nbformat.reads(notebook_path.read_text(encoding="utf-8"), as_version=nbformat.NO_CONVERT)
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## File Creation and Partial State
|
|
479
|
+
|
|
480
|
+
**Description**: Validate inputs before creating output files to avoid partial state on errors.
|
|
481
|
+
|
|
482
|
+
**Flag when**:
|
|
483
|
+
|
|
484
|
+
- A function creates/touches output files before validating all inputs needed to populate those files.
|
|
485
|
+
- A failure path can leave orphaned or partially written files without cleanup.
|
|
486
|
+
- A write operation overwrites an existing output without an explicit overwrite policy.
|
|
487
|
+
|
|
488
|
+
**Good pattern**:
|
|
489
|
+
|
|
490
|
+
1. Collect inputs.
|
|
491
|
+
2. Validate all required values.
|
|
492
|
+
3. Build complete output content in memory or a temporary location.
|
|
493
|
+
4. Write/touch the final output.
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## Path Handling
|
|
498
|
+
|
|
499
|
+
**Description**: Use `Path` operations consistently when working with paths.
|
|
500
|
+
|
|
501
|
+
**Flag when**:
|
|
502
|
+
|
|
503
|
+
- Code mixes string path manipulation with `pathlib.Path` objects.
|
|
504
|
+
- Code uses built-in `open(path)` where `path` is already a `Path` object and `path.open(...)` would be more consistent with nearby code.
|
|
505
|
+
- Code manually joins paths with string concatenation instead of `/` or `Path` methods.
|
|
506
|
+
|
|
507
|
+
**Good example**:
|
|
508
|
+
|
|
509
|
+
```python
|
|
510
|
+
with output_path.open(mode="w", encoding="utf-8") as f:
|
|
511
|
+
f.write(contents)
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
**Bad example**:
|
|
515
|
+
|
|
516
|
+
```python
|
|
517
|
+
with open(output_path, "w") as f:
|
|
518
|
+
f.write(contents)
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
## Review Comment Style
|
|
524
|
+
|
|
525
|
+
When reporting an issue:
|
|
526
|
+
|
|
527
|
+
- Be specific about the file/line and the convention being violated.
|
|
528
|
+
- Explain the practical risk: broken dependency direction, hidden task coupling, loss of FITS-key abstraction, harder telemetry, partial file state, missing regression coverage, etc.
|
|
529
|
+
- Suggest a concrete fix.
|
|
530
|
+
- Do not ask for broad rewrites unless the changed code creates a clear maintainability or correctness risk.
|
|
531
|
+
- Do not flag purely subjective naming/style issues unless the name obscures domain meaning or conflicts with existing conventions.
|
|
532
|
+
|
|
533
|
+
**Preferred comment format**:
|
|
534
|
+
|
|
535
|
+
```text
|
|
536
|
+
This imports a concrete task from another task module. In dkist-processing-* task modules should import only from *_base modules or tasks.mixin so concrete workflow nodes do not become coupled to each other. Could this dependency move to a shared base/helper, or be expressed through the existing base class instead?
|
|
537
|
+
```
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
v5.7.14 (2026-05-19)
|
|
2
|
+
====================
|
|
3
|
+
|
|
4
|
+
Misc
|
|
5
|
+
----
|
|
6
|
+
|
|
7
|
+
- Update dkist-processing-common to 14.3.1 which migrates `_format_facet` from the quality mixin to the `QualityMetric` `BaseModel`. (`#319 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/319>`__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
v5.7.13 (2026-05-19)
|
|
11
|
+
====================
|
|
12
|
+
|
|
13
|
+
Features
|
|
14
|
+
--------
|
|
15
|
+
|
|
16
|
+
- Update 'dkist-processing-common' to incorporate notebook export functionality for manually processed pipeline runs. (`#320 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/320>`__)
|
|
17
|
+
|
|
18
|
+
|
|
1
19
|
v5.7.12 (2026-05-18)
|
|
2
20
|
====================
|
|
3
21
|
|