Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
6087d69
Add ColormapSet for color blending
izkgao Apr 30, 2025
d6961b2
Update Image.make_active to support CARTA 5.0.0+ API changes
izkgao Apr 30, 2025
9fa89de
Add ColorBlending class with layer management and color blending func…
izkgao May 5, 2025
85b3528
Add color blending documentation and update image handling examples
izkgao May 5, 2025
06445bc
Add ColorBlending.from_files method to create blended images directly…
izkgao Sep 10, 2025
266f1c1
Add tests for ColorBlending and Layer classes
izkgao Sep 10, 2025
606c9f3
Improve mock consistency
izkgao Sep 11, 2025
8e560b9
Merge branch 'dev' into color_blending
izkgao Apr 10, 2026
34bf41b
Fix duplicate imports and docstring formatting in colorblending and i…
izkgao Apr 10, 2026
5d4fca4
Add colorblending module to API documentation
izkgao Apr 10, 2026
032949d
Refactor layers property to use Layer.from_list method
izkgao Apr 10, 2026
2e0abce
Fix indentation in Layer class docstring
izkgao Apr 10, 2026
5c52466
Rename reorder_layers to set_layer_sequence and add support for layer…
izkgao Apr 14, 2026
06fff60
Simplify layer_list implementation by using frames.length instead of …
izkgao Apr 14, 2026
b7af8dc
Add explicit validation to prevent deletion of base layer in ColorBle…
izkgao Apr 14, 2026
fbcf72f
Add validation to prevent alpha list length mismatch in set_alpha method
izkgao Apr 14, 2026
6596ab3
Refactor ColorBlending to use store_id instead of image_id and add fr…
izkgao Apr 14, 2026
9a20cfa
Add color_blending_list method to Session class for retrieving open c…
izkgao Apr 14, 2026
d9bcab4
Update quickstart documentation to clarify ColorBlending layer termin…
izkgao Apr 14, 2026
a34e646
Drop support for carta version < 5.0 for make_active
izkgao Apr 14, 2026
4ce4429
Update documentation references to use new wcs_overlay module paths f…
izkgao Apr 14, 2026
25e0baa
Add validation test for invalid colormap name in Layer.set_colormap m…
izkgao Apr 14, 2026
cb22ed5
Fix indentation in Layer.from_layer_ids docstring return section
izkgao Apr 14, 2026
91e952d
Add validation to prevent exceeding maximum initial layer count in Co…
izkgao Apr 14, 2026
3f1c4fe
Rename set_vectoroverlay_visible method to set_vector_overlay_visible…
izkgao Apr 14, 2026
d337759
Rename ColormapSet.Rainbow to RAINBOW for consistent uppercase enum n…
izkgao Apr 14, 2026
345c17d
Add module docstring to colorblending.py describing color blending fu…
izkgao Apr 14, 2026
2bf9d1f
Replace explicit type annotations with placeholder format strings in …
izkgao Apr 14, 2026
e16c9f4
Change periods to colons in ColorBlending section introductory senten…
izkgao Apr 14, 2026
2812ffa
Update test_make_active to use setActiveImageByFileId action instead …
izkgao Apr 14, 2026
509d4e2
Fix ColorBlending initialization to rebuild layers from requested ima…
izkgao Apr 15, 2026
af48cf3
Add validation to reject duplicate layer indices in ColorBlending.set…
izkgao Apr 15, 2026
2f7d76b
Remove automatic inversion reset when applying colormap sets in Color…
izkgao Apr 15, 2026
56e019d
Fix imageview_id property to search imageList by store_id instead of …
izkgao Apr 15, 2026
6e02ba2
Convert layer_indices to list before comparing with current_layer_ind…
izkgao Apr 15, 2026
9ea1a66
Style check
izkgao Apr 15, 2026
f024c5c
Merge branch 'dev' into zhenkai/color_blending
izkgao Apr 20, 2026
50a21bc
Implement ImageBase class
izkgao Apr 20, 2026
6c2d9a1
Update Image.__repr__ to use [Closed] label instead of [Invalid] for …
izkgao Apr 20, 2026
ace85cb
Update ColorBlending and Layer to use color_blending_id and improve _…
izkgao Apr 20, 2026
566dfb0
Add get_image method and refactor image list handling to support hete…
izkgao Apr 20, 2026
07353f1
Filter image_list to return only Image instances in _images helper me…
izkgao Apr 20, 2026
4302da5
Update tests
izkgao Apr 20, 2026
d424e90
Update terminology from "file-based" to "frame-backed" in image docst…
izkgao Apr 20, 2026
5c9e334
Set default colormap set based on number of files for open_as_color_b…
izkgao Apr 20, 2026
ac58b5f
Extract ImageBase class into separate module
izkgao Apr 20, 2026
7be1e65
Add parametrized test for default colormap set selection in open_as_c…
izkgao Apr 20, 2026
4a06836
Reorganize ColorBlending methods with section comments for better cod…
izkgao Apr 20, 2026
ec1501a
Add image_view_order property to Layer class and update __repr__ to u…
izkgao Apr 20, 2026
68f6b27
Set default colormap set based on number of images for create_color_b…
izkgao Apr 20, 2026
57bdb55
Add documentation for image list inspection and update color blending…
izkgao Apr 20, 2026
e7a8370
Add validation to prevent rebasing existing color blendings when crea…
izkgao Apr 21, 2026
e13fd3a
Rename active_frame to active_image and add support for ColorBlending…
izkgao Apr 22, 2026
b47334b
Rename get_image to image_by_id and update signature to use keyword a…
izkgao Apr 22, 2026
a4630de
Update set_cursor to use activeFrame.setCursorPosition action and rem…
izkgao Apr 22, 2026
c6e8a54
Remove ColorBlending factory methods and simplify create_color_blendi…
izkgao Apr 22, 2026
d6a479e
Reorganize Session methods with section comments for color blending a…
izkgao Apr 22, 2026
9d86616
Fix default colormap set selection to use layer count instead of tota…
izkgao Apr 22, 2026
bebe9e4
Add carta_version property to Session and include it in __repr__ when…
izkgao Apr 22, 2026
76a6c37
Update documentation references to use new wcs module paths and impro…
izkgao Apr 22, 2026
49e4e78
Add zoom_to_size method to ColorBlending class with validation and de…
izkgao Apr 22, 2026
21b22f0
Refactor ImageBase to use ABC pattern, move image_view_order implemen…
izkgao Apr 22, 2026
1c5b8ab
Update quickstart documentation to use unpacked layer variables inste…
izkgao Apr 22, 2026
8940ca3
Update test regex pattern to match abstract method error messages mor…
izkgao Apr 22, 2026
f0b86ae
Fix a test warning
izkgao Apr 22, 2026
1e4d2fe
Add test coverage
izkgao Apr 22, 2026
8620e85
Improve error messages to include actual values and update tests to v…
izkgao Apr 22, 2026
c982c89
Add session.clear_spatial_reference() call before setting new spatial…
izkgao Apr 22, 2026
ab1753d
Add validation to reject empty file list in open_as_color_blending me…
izkgao Apr 22, 2026
8f6ab9e
Optimize active_image method to retrieve activeImage object once inst…
izkgao Apr 22, 2026
65eaced
Refactor _find_image_view_order to use imageViewConfigStore.getImageL…
izkgao Apr 23, 2026
298043c
Rename loop variable from 'call' to 'call_' to avoid shadowing built-…
izkgao Apr 23, 2026
9693fa5
Add connection validation during session creation with minimum CARTA …
izkgao Apr 28, 2026
5a7a924
Update uv sync command to include all extras in contributor setup ins…
izkgao Apr 28, 2026
d421fe9
Simplify __repr__ method by removing manual cache checking and relyin…
izkgao Apr 28, 2026
c69d57a
Merge branch 'dev' into zhenkai/color_blending
izkgao Apr 28, 2026
6484a3e
Merge branch 'zhenkai/color_blending' into zhenkai/check_version
izkgao Apr 28, 2026
af27b18
Implement enum validation against frontend snapshots and add correspo…
izkgao Apr 29, 2026
9b3b423
Add carta_version_requirement parameter to session creation methods t…
izkgao Apr 30, 2026
427ca22
Merge branch 'zhenkai/check_version' into zhenkai/157_validate_enums
izkgao Apr 30, 2026
8b7914d
Merge branch 'dev' into zhenkai/157_validate_enums
izkgao Aug 18, 2026
aa0d801
Refactor enum registration system to use mixin classes with automatic…
izkgao Aug 18, 2026
94e2fe0
Update enums
izkgao Aug 18, 2026
bad4e94
Revert merge zhenkai/check_version
izkgao Aug 18, 2026
779ebb6
Add ASINH and SINH scaling functions to missing_in_frontend test asse…
izkgao Aug 18, 2026
437faa6
Revert merge zhenkai/check_version
izkgao Aug 18, 2026
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
53 changes: 53 additions & 0 deletions carta/_enum_snapshots.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Validate carta.constants enums against frontend enum snapshots."""

from . import constants


def build_constant_enum_dict():
"""Build a dictionary of enums registered for external validation."""
enum_dict = {}
for registry in (constants.FrontendEnum, constants.ProtobufEnum):
for snapshot_name, enum_ in registry.ENUMS.items():
enum_dict[enum_.__name__] = {
"snapshot_name": snapshot_name,
"enum": enum_,
}
return enum_dict


def find_enum_snapshot_mismatches(enum_dict, enum_snapshots):
"""Compare frontend enum snapshots with local constants enums."""
mismatches = []

for (enum_name, enum_value), snapshot in zip(enum_dict.items(), enum_snapshots):
frontend_dict = {i["name"].upper().replace("_", ""): [i["name"], i["value"]] for i in snapshot}
python_dict = {i.name.replace("_", ""): [i.name, i.value] for i in enum_value["enum"]}
missing_in_python = [
{"frontend_name": frontend_dict[name][0], "frontend_value": frontend_dict[name][1]}
for name in sorted(frontend_dict.keys() - python_dict.keys())
]
missing_in_frontend = [
{"python_name": python_dict[name][0], "python_value": python_dict[name][1]}
for name in sorted(python_dict.keys() - frontend_dict.keys())
]
value_mismatches = [
{
"python_name": python_dict[name][0],
"frontend_name": frontend_dict[name][0],
"python_value": python_dict[name][1],
"frontend_value": frontend_dict[name][1],
}
for name in sorted(python_dict.keys() & frontend_dict.keys())
if python_dict[name][1] != frontend_dict[name][1]
]
if missing_in_python or missing_in_frontend or value_mismatches:
mismatches.append(
{
"name": enum_name,
"source": enum_value["snapshot_name"],
"missing_in_python": missing_in_python,
"missing_in_frontend": missing_in_frontend,
"value_mismatches": value_mismatches,
}
)
return mismatches
117 changes: 83 additions & 34 deletions carta/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,48 @@ class StrEnum(str, Enum):
pass


class _RegisteredEnum:
"""Mixin implementation shared by externally defined enum registries."""

ENUMS = {}

def __init_subclass__(cls, *, snapshot_name=None, **kwargs):
"""Register an enum subclass by its external snapshot name."""
super().__init_subclass__(**kwargs)
if _RegisteredEnum in cls.__bases__:
return
snapshot_name = snapshot_name or cls.__name__
cls.SNAPSHOT_NAME = snapshot_name
cls.ENUMS[snapshot_name] = cls


class FrontendEnum(_RegisteredEnum):
"""Mixin for enums defined and validated by the CARTA frontend."""

ENUMS = {}


class ProtobufEnum(_RegisteredEnum):
"""Mixin for enums defined and validated by CARTA protobuf messages."""

ENUMS = {}


def _registered_enum(registry, enum_type, enum_name, names, snapshot_name=None, **kwargs):
"""Create a functional enum and register it with the requested mixin."""
enum_ = enum_type(enum_name, names, type=registry, **kwargs)
if snapshot_name is not None:
_set_snapshot_name(enum_, registry, snapshot_name)
return enum_


def _set_snapshot_name(enum_, registry, snapshot_name):
"""Set a custom name after EnumMeta has constructed an enum class."""
del registry.ENUMS[enum_.__name__]
enum_.SNAPSHOT_NAME = snapshot_name
registry.ENUMS[snapshot_name] = enum_


class ComplexComponent(StrEnum):
"""Complex component."""
AMPLITUDE = "AMPLITUDE"
Expand All @@ -19,33 +61,31 @@ class ComplexComponent(StrEnum):
IMAG = "IMAG"


Colormap = StrEnum('Colormap', {c.upper(): c for c in ('copper', 'paired', 'gist_heat', 'brg', 'cool', 'summer', 'OrRd', 'tab20c', 'purples', 'gray', 'terrain', 'RdPu', 'set2', 'spring', 'gist_yarg', 'RdYlBu', 'reds', 'winter', 'Wistia', 'rainbow', 'dark2', 'oranges', 'BuPu', 'gist_earth', 'PuBu', 'pink', 'PuOr', 'pastel2', 'PiYG', 'gist_ncar', 'PuRd', 'plasma', 'gist_stern', 'hot', 'PuBuGn', 'YlOrRd', 'accent', 'magma', 'set1', 'GnBu', 'greens', 'CMRmap', 'gist_rainbow', 'prism', 'hsv', 'Blues', 'viridis', 'YlGn', 'spectral', 'RdBu', 'tab20', 'greys', 'flag', 'jet', 'seismic', 'PRGn', 'coolwarm', 'YlOrBr', 'RdYlGn', 'bone', 'autumn', 'BrBG', 'gnuplot2', 'RdGy', 'binary', 'gnuplot', 'BuGn', 'gist_gray', 'nipy_spectral', 'set3', 'tab20b', 'pastel1', 'afmhot', 'cubehelix', 'YlGnBu', 'ocean', 'tab10', 'bwr', 'inferno')})
Colormap = _registered_enum(FrontendEnum, StrEnum, 'Colormap', {c.upper(): c for c in ('copper', 'paired', 'gist_heat', 'brg', 'cool', 'summer', 'OrRd', 'tab20c', 'purples', 'gray', 'terrain', 'RdPu', 'set2', 'spring', 'gist_yarg', 'RdYlBu', 'reds', 'winter', 'Wistia', 'rainbow', 'dark2', 'oranges', 'BuPu', 'gist_earth', 'PuBu', 'pink', 'PuOr', 'pastel2', 'PiYG', 'gist_ncar', 'PuRd', 'plasma', 'gist_stern', 'hot', 'PuBuGn', 'YlOrRd', 'accent', 'magma', 'set1', 'GnBu', 'greens', 'CMRmap', 'gist_rainbow', 'prism', 'hsv', 'Blues', 'viridis', 'YlGn', 'spectral', 'RdBu', 'tab20', 'greys', 'flag', 'jet', 'seismic', 'PRGn', 'coolwarm', 'YlOrBr', 'RdYlGn', 'bone', 'autumn', 'BrBG', 'gnuplot2', 'RdGy', 'binary', 'gnuplot', 'BuGn', 'gist_gray', 'nipy_spectral', 'set3', 'tab20b', 'pastel1', 'afmhot', 'cubehelix', 'YlGnBu', 'ocean', 'tab10', 'bwr', 'inferno', 'Blue', 'Cyan', 'Green', 'Magenta', 'Orange', 'Red', 'Violet', 'Yellow')}, snapshot_name="ColorMap")
Colormap.__doc__ = """All available colormaps."""


class ColormapSet(StrEnum):
class ColormapSet(FrontendEnum, StrEnum):
"""Colormap sets for color blending."""
RGB = "RGB"
CMY = "CMY"
RAINBOW = "Rainbow"


class ImageType(IntEnum):
"""View item types, corresponding to the frontend ImageType enum."""
class ImageType(FrontendEnum, IntEnum):
"""Image view item types, corresponding to the frontend ImageType enum."""
FRAME = 0
COLOR_BLENDING = 1
PV_PREVIEW = 2


Scaling = IntEnum('Scaling', ('LINEAR', 'LOG', 'SQRT', 'SQUARE', 'POWER', 'GAMMA'), start=0)
Scaling = _registered_enum(FrontendEnum, IntEnum, 'Scaling', ('LINEAR', 'LOG', 'SQRT', 'SQUARE', 'POWER', 'GAMMA', 'EXP', 'CUSTOM', 'SINH', 'ASINH'), snapshot_name="FrameScaling", start=0)
Scaling.__doc__ = """Colormap scaling types."""


CoordinateSystem = StrEnum('CoordinateSystem', {c: c for c in ("AUTO", "ECLIPTIC", "FK4", "FK5", "GALACTIC", "ICRS")})
CoordinateSystem = _registered_enum(FrontendEnum, StrEnum, 'CoordinateSystem', {c: c for c in ("AUTO", "ECLIPTIC", "FK4", "FK5", "GALACTIC", "ICRS")} | {"IMAGE": "CARTESIAN"}, snapshot_name="SystemType")
CoordinateSystem.__doc__ = """Coordinate systems."""


class NumberFormat(StrEnum):
class NumberFormat(FrontendEnum, StrEnum, snapshot_name="NumberFormatType"):
"""Number formats."""
DEGREES = "d"
HMS = "hms"
Expand All @@ -58,13 +98,13 @@ class SpatialAxis(StrEnum):
Y = "y"


class LabelType(StrEnum):
class LabelType(FrontendEnum, StrEnum):
"""Label types."""
INTERIOR = "Interior"
EXTERIOR = "Exterior"


class BeamType(StrEnum):
class BeamType(FrontendEnum, StrEnum):
"""Beam types."""
OPEN = "open"
SOLID = "solid"
Expand Down Expand Up @@ -147,16 +187,14 @@ def __init__(self, value):

Member values are paths to stores corresponding to these elements, relative to the WCS overlay store.
"""


class SmoothingMode(IntEnum):
class SmoothingMode(ProtobufEnum, IntEnum, snapshot_name="protobuf:SmoothingMode"):
"""Contour smoothing modes."""
NO_SMOOTHING = 0
BLOCK_AVERAGE = 1
GAUSSIAN_BLUR = 2


VectorOverlaySource = Enum('VectorOverlaySource', ('NONE', 'CURRENT', 'COMPUTED'), type=int, start=-1)
VectorOverlaySource = _registered_enum(FrontendEnum, Enum, 'VectorOverlaySource', ('NONE', 'CURRENT', 'COMPUTED'), start=-1)
VectorOverlaySource.__doc__ = """Vector overlay source."""


Expand All @@ -165,11 +203,11 @@ class Auto(StrEnum):
AUTO = "Auto"


class ContourDashMode(StrEnum):
class ContourDashMode(FrontendEnum, StrEnum):
"""Contour dash modes."""
NONE = "None"
DASHED = "Dashed"
NEGATIVE_ONLY = "NegativeOnly"
NEGATIVE_ONLY = "Negative only"


PROTO_POLARIZATION = {
Expand All @@ -193,9 +231,8 @@ class ContourDashMode(StrEnum):
}


class Polarization(IntEnum):
"""Polarizations, corresponding to the POLARIZATIONS enum in the frontend."""

class Polarization(FrontendEnum, IntEnum, snapshot_name="Polarizations"):
"""Polarizations."""
def __init__(self, value):
self.proto_index = PROTO_POLARIZATION[self.name]

Expand Down Expand Up @@ -224,13 +261,14 @@ class PanelMode(IntEnum):
MULTIPLE = 1


class GridMode(StrEnum):
class GridMode(FrontendEnum, StrEnum, snapshot_name="ImagePanelMode"):
"""Grid modes."""
DYNAMIC = "dynamic"
FIXED = "fixed"
NONE = "none"


class FileType(IntEnum):
class FileType(ProtobufEnum, IntEnum, snapshot_name="protobuf:FileType"):
"""File types corresponding to the protobuf enum."""
CASA = 0
CRTF = 1
Expand All @@ -241,9 +279,8 @@ class FileType(IntEnum):
UNKNOWN = 6


class RegionType(IntEnum):
class RegionType(ProtobufEnum, IntEnum, snapshot_name="protobuf:RegionType"):
"""Region types corresponding to the protobuf enum."""

def __init__(self, value):
self.is_annotation = self.name.startswith("ANN")
self.label = f"{self.name[3:].title()} - Ann" if self.is_annotation else self.name.title()
Expand All @@ -267,13 +304,13 @@ def __init__(self, value):
ANNCOMPASS = 16


class CoordinateType(IntEnum):
class CoordinateType(ProtobufEnum, IntEnum, snapshot_name="protobuf:CoordinateType"):
"""Coordinate types corresponding to the protobuf enum."""
PIXEL = 0
WORLD = 1


class PointShape(IntEnum):
class PointShape(ProtobufEnum, IntEnum, snapshot_name="protobuf:PointAnnotationShape"):
"""Point annotation shapes corresponding to the protobuf enum."""
SQUARE = 0
BOX = 1
Expand All @@ -285,7 +322,7 @@ class PointShape(IntEnum):
X = 7


class TextPosition(IntEnum):
class TextPosition(ProtobufEnum, IntEnum, snapshot_name="protobuf:TextAnnotationPosition"):
"""Text annotation positions corresponding to the protobuf enum."""
CENTER = 0
UPPER_LEFT = 1
Expand All @@ -298,15 +335,15 @@ class TextPosition(IntEnum):
RIGHT = 8


class AnnotationFontStyle(StrEnum):
class AnnotationFontStyle(FrontendEnum, StrEnum, snapshot_name="FontStyle"):
"""Font styles which may be used in annotations."""
NORMAL = "Normal"
BOLD = "Bold"
ITALIC = "Italic"
BOLD_ITALIC = "Italic Bold"


class AnnotationFont(StrEnum):
class AnnotationFont(FrontendEnum, StrEnum, snapshot_name="Font"):
"""Fonts which may be used in annotations."""
HELVETICA = "Helvetica"
TIMES = "Times"
Expand Down Expand Up @@ -337,15 +374,15 @@ class ColorbarPosition(StrEnum):
BOTTOM = "bottom"


class SpectralSystem(StrEnum):
class SpectralSystem(FrontendEnum, StrEnum):
"""Spectral systems."""
LSRK = "LSRK"
LSRD = "LSRD"
BARY = "BARYCENT"
TOPO = "TOPOCENT"


class SpectralUnit(StrEnum):
class SpectralUnit(FrontendEnum, StrEnum):
"""Spectral units."""
KMS = "km/s"
MS = "m/s"
Expand All @@ -358,9 +395,16 @@ class SpectralUnit(StrEnum):
UM = "um"
NM = "nm"
ANGSTROM = "Angstrom"
M_SQUARE = "m^2"
MM_SQUARE = "mm^2"
UM_SQUARE = "um^2"
NM_SQUARE = "nm^2"
ANGSTROM_SQUARE = "Angstrom^2"


SPECTRAL_TYPE_DESCRIPTION = {
"CHANNEL": "Channel",
"NATIVE": "Native",
"VRAD": "Radio velocity",
"VOPT": "Optical velocity",
"FREQ": "Frequency",
Expand All @@ -370,6 +414,8 @@ class SpectralUnit(StrEnum):


SPECTRAL_TYPE_UNITS = {
"CHANNEL": tuple(),
"NATIVE": tuple(),
"VRAD": (SpectralUnit.KMS, SpectralUnit.MS),
"VOPT": (SpectralUnit.KMS, SpectralUnit.MS),
"FREQ": (SpectralUnit.GHZ, SpectralUnit.MHZ, SpectralUnit.KHZ, SpectralUnit.HZ),
Expand All @@ -378,7 +424,7 @@ class SpectralUnit(StrEnum):
}


class SpectralType(StrEnum):
class SpectralType(FrontendEnum, StrEnum):
"""Spectral types.

Members of this enum class have additional attributes.
Expand All @@ -394,10 +440,13 @@ class SpectralType(StrEnum):
"""

def __init__(self, value):
units = SPECTRAL_TYPE_UNITS[self.name]
self.description = SPECTRAL_TYPE_DESCRIPTION[self.name]
self.units = set(SPECTRAL_TYPE_UNITS[self.name])
self.default_unit = SPECTRAL_TYPE_UNITS[self.name][0]
self.units = set(units)
self.default_unit = units[0] if units else None

CHANNEL = "CHANNEL",
NATIVE = "NATIVE",
VRAD = "VRAD",
VOPT = "VOPT",
FREQ = "FREQ",
Expand Down
8 changes: 8 additions & 0 deletions carta/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import base64
import posixpath

from ._enum_snapshots import build_constant_enum_dict, find_enum_snapshot_mismatches
from .image import Image
from .view import View
from .color_blending import ColorBlending
Expand Down Expand Up @@ -258,6 +259,13 @@ def carta_version(self):
"""
return self.get_value("frontendVersion")

def _enum_snapshot_mismatches(self):
"""Return mismatches between local constants enums and frontend snapshots."""
enum_dict = build_constant_enum_dict()
requested_names = [info["snapshot_name"] for info in enum_dict.values()]
snapshots = self.call_action("getEnumSnapshots", requested_names, response_expected=True)
return find_enum_snapshot_mismatches(enum_dict, snapshots)

def call_action(self, path, *args, **kwargs):
"""Call an action on the frontend through the backend's scripting interface.

Expand Down
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ version = { file = ["VERSION.txt"] }
[tool.uv]
default-groups = ["dev"]

[tool.pytest.ini_options]
markers = [
"live_enum_snapshots: requires a running CARTA frontend/backend scripting session",
]

[tool.ruff]
target-version = "py310"
extend-exclude = ["docs/source/conf.py", "scripts/update_palette_colours.py"]
Expand All @@ -79,3 +84,4 @@ ignore = ["E501", "E741"]

[tool.ruff.lint.per-file-ignores]
"tests/*.py" = ["D"]
"tests/**/*.py" = ["D"]
Loading