- CLAUDE.md with architecture overview, conventions, and strict commit rules - docs/architecture-registries.md: registry pattern details - docs/color-spaces.md: color space system guide - docs/testing.md: BDD testing guide with mamba - docs/nodes-comfyui.md: ComfyUI nodes reference - docs/data-models.md: PixelColor and PixelPalette models
7.2 KiB
Architecture des Registries
Philosophie
Le projet utilise le pattern Registry comme pilier architectural pour permettre l'ajout de nouveaux systemes de couleurs, formats d'export et formats de parsing sans modifier le code existant (principe Open/Closed).
Chaque registry fonctionne comme un annuaire : les composants s'y inscrivent a l'import, et le reste du code les retrouve par leur nom.
Les trois registries
1. ColorSpaceRegistry
Fichier : lib/color/color_space_registry.py
Gere les espaces colorimétriques. Chaque espace peut avoir un mixer (mélange de couleurs) et/ou un exporter (conversion de format).
class ColorSpaceRegistry:
_spaces = {} # {"rgb": {"exporter": RGBExporter, "mixer": RGBMixer}, ...}
@classmethod
def register(cls, name, exporter_class=None, mixer_class=None)
@classmethod
def get_mixer_class(cls, space_name) -> MixerClass
@classmethod
def get_exporter_class(cls, space_name) -> ExporterClass
@classmethod
def available_spaces(cls) -> List[str]
Particularités :
- Accepte l'enregistrement partiel (mixer seul OU exporter seul)
- Fusionne les enregistrements multiples (on peut enregistrer le mixer puis l'exporter séparément)
- Noms normalisés en minuscules (insensible a la casse)
- Leve
ValueErrorsi on demande un espace inconnu ou un mixer absent
Espaces enregistrés :
| Espace | Mixer | Exporter |
|---|---|---|
| rgb | RGBMixer | RGBExporter |
| hsv | HSVMixer | - |
2. ExporterRegistry
Fichier : lib/palette/exporters/exporter_registry.py
Gere les formats d'export de palettes entieres.
class ExporterRegistry:
_exporters = {} # {"gimp": GimpExporter, "hex": HexExporter, ...}
@classmethod
def register(cls, name, exporter_class)
@classmethod
def get_exporter_class(cls, format_name) -> ExporterClass
@classmethod
def available_formats(cls) -> List[str]
Formats enregistrés :
| Format | Classe | Description |
|---|---|---|
| gimp | GimpExporter | Format GIMP .gpl |
| hex | HexExporter | Couleurs hexadécimales |
| rgb | RGBExporter | Format rgb(r,g,b) |
Chaque exporter hérite de BaseExporter (ABC) et implémente export(colors, **kwargs) -> str.
3. ParserRegistry
Fichier : lib/parsers/parser_registry.py
Gere les formats de parsing (lecture) de palettes.
class ParserRegistry:
_parsers = {} # {"gimp": GimpParser, "generic": GenericParser, ...}
@classmethod
def register(cls, name, parser_class)
@classmethod
def get_parser_class(cls, format_name) -> ParserClass
@classmethod
def available_formats(cls) -> List[str]
Parsers enregistrés :
| Format | Classe | Détection |
|---|---|---|
| gimp | GimpParser | Premiere ligne = "GIMP Palette" |
| generic | GenericParser | Fallback : hex (#RRGGBB) ou rgb(r,g,b) |
| adobe_aco | AdobeAcoParser | Format binaire Adobe (stub) |
Chaque parser hérite de BaseParser (ABC) et implémente :
can_parse(content) -> bool: détection du formatparse(content) -> List[PixelColor]: extraction des couleurs
Mécanisme d'auto-enregistrement
L'enregistrement se fait dans les __init__.py de chaque module, exécutés au moment de l'import.
Chaine d'initialisation
__init__.py (racine ComfyUI)
└── import nodes
└── import lib
└── lib/__init__.py
├── import color
│ └── color/__init__.py
│ └── import color_spaces
│ └── color_spaces/__init__.py
│ ├── import RGBMixer → s'auto-enregistre
│ └── import HSVMixer → s'auto-enregistre
├── import palette.exporters
│ └── exporters/__init__.py
│ ├── ExporterRegistry.register("gimp", GimpExporter)
│ ├── ExporterRegistry.register("hex", HexExporter)
│ └── ExporterRegistry.register("rgb", RGBExporter)
└── import parsers
└── parsers/__init__.py
├── ParserRegistry.register("gimp", GimpParser)
├── ParserRegistry.register("generic", GenericParser)
└── ParserRegistry.register("adobe_aco", AdobeAcoParser)
Deux styles d'enregistrement
Style 1 - Auto-enregistrement dans le composant (ColorSpaceRegistry) : Les mixers s'enregistrent eux-memes dans leur propre fichier.
# lib/color/color_spaces/rgb/rgb_mixer.py
from ...color_space_registry import ColorSpaceRegistry
class RGBMixer:
@staticmethod
def mix_with(color_a, color_b, ratio):
...
ColorSpaceRegistry.register('rgb', mixer_class=RGBMixer)
Style 2 - Enregistrement centralisé dans __init__.py (ExporterRegistry, ParserRegistry) :
# lib/palette/exporters/__init__.py
ExporterRegistry.register("gimp", GimpExporter)
ExporterRegistry.register("hex", HexExporter)
Ajouter un composant
Nouveau color space (ex: HSL)
lib/color/color_spaces/hsl/
├── __init__.py
├── hsl_mixer.py # class HSLMixer avec mix_with() + auto-register
└── hsl_exporter.py # class HSLExporter (optionnel)
- Créer le dossier et les fichiers
- Le mixer doit appeler
ColorSpaceRegistry.register('hsl', mixer_class=HSLMixer)en fin de fichier - Importer le mixer dans
lib/color/color_spaces/__init__.py - Tester dans
spec/color/color_spaces/hsl/
Nouvel exporter de palette (ex: CSS)
- Créer
lib/palette/exporters/css_exporter.pyhéritant deBaseExporter - Implémenter
export(colors, **kwargs) -> str - Ajouter dans
lib/palette/exporters/__init__.py:ExporterRegistry.register("css", CSSExporter) - Tester dans
spec/palette/exporters/
Nouveau parser (ex: ASE Adobe Swatch Exchange)
- Créer
lib/parsers/ase_parser.pyhéritant deBaseParser - Implémenter
can_parse()etparse() - Ajouter dans
lib/parsers/__init__.py:ParserRegistry.register("ase", AseParser) - Tester dans
spec/parsers/
Context Managers associés
ColorSpaceContext
Permet de changer temporairement l'espace de couleur d'un PixelColor :
color = PixelColor(255, 0, 0) # color_space = "rgb"
with color.using_color_space("hsv"):
mixed = color.mix_with(other, 0.5) # utilise HSVMixer
# color_space restauré a "rgb" automatiquement
PaletteExporter
Gere le cycle de vie de l'export d'une palette :
with PaletteExporter(palette, "hex") as ctx:
text = ctx.export(separator="\n", include_names=True)
colors_list = ctx.export_list()
tuples = ctx.export_tuples()
Considérations pour les tests
Les registries utilisent des variables de classe (_spaces, _exporters, _parsers) qui persistent entre les tests. Il faut les réinitialiser :
# Dans before.each
ColorSpaceRegistry._spaces = {}
# ou
ColorSpaceRegistry.reset()