home · contact · privacy
Simplify BrickDesign attributes linkage.
authorPlom Heller <plom@plomlompom.com>
Sat, 13 Jun 2026 16:39:49 +0000 (18:39 +0200)
committerPlom Heller <plom@plomlompom.com>
Sat, 13 Jun 2026 16:39:49 +0000 (18:39 +0200)
bricksplom.py

index cfe67b83ed438bd2264ac83f304822e09379b919..eaff95ccf62b96e80a95997ad538a3703045ae01 100755 (executable)
@@ -2,6 +2,7 @@
 'Data structures for managing/sorting bricks of a certain kind.'
 from abc import ABC, abstractmethod
 from argparse import ArgumentError, ArgumentParser
+from dataclasses import dataclass
 from os import environ
 from pathlib import Path
 from typing import Callable, Optional, Self
@@ -227,16 +228,11 @@ class BrickColor(Textfiled, Lookupable):
                 + self.wavelength)
 
 
+@dataclass
 class BrickDesignData:
-    'Whatever be available in BrickDesign.attrs.'
-
-    def __init__(
-            self,
-            n_studs=-1,
-            description=''
-            ) -> None:
-        self.description = description
-        self.n_studs = n_studs
+    'Lookupables for BrickDesign besides .id.'
+    n_studs: int = -1
+    description: str = ''
 
 
 class BrickDesign(Textfiled, Lookupable):
@@ -250,18 +246,19 @@ class BrickDesign(Textfiled, Lookupable):
             attrs: Optional[BrickDesignData] = None
             ) -> None:
         self.id_ = id_
-        self._attrs = attrs
+        self.direct_attrs = attrs
         self.alternate_ids: set[str] = set()
 
-    @property
-    def attrs(
-            self
-            ) -> BrickDesignData:
-        'Collect from either .alternate_to or ._attrs.'
-        if self.alternate_to:
-            return self.alternate_to.attrs
-        assert self._attrs
-        return self._attrs
+    def __getattribute__(self, key: str):
+        if key in BrickDesignData.__annotations__:
+            if self.direct_attrs:
+                attrs = self.direct_attrs
+            else:
+                assert self.alternate_to is not None
+                assert self.alternate_to.direct_attrs is not None
+                attrs = self.alternate_to.direct_attrs
+            return getattr(attrs, key)
+        return super().__getattribute__(key)
 
     @property
     def all_ids(
@@ -278,7 +275,7 @@ class BrickDesign(Textfiled, Lookupable):
                 q_body
                 ) -> bool:
             assert q_body.isdigit()
-            return self.attrs.n_studs == int(q_body)
+            return self.n_studs == int(q_body)
         return super().matchers | {PARAM_Q_STUDS: q_studs}
 
     @property
@@ -289,7 +286,7 @@ class BrickDesign(Textfiled, Lookupable):
         return super().sorters | {
             TOK_SORT_BOX: self._by_box_sorter('design'),
             TOK_SORT_STUDS: lambda _, pre_sorted: tuple(
-                sorted(pre_sorted, key=lambda item: item.attrs.n_studs))
+                sorted(pre_sorted, key=lambda item: item.n_studs))
                 }
 
     @classmethod
@@ -312,14 +309,15 @@ class BrickDesign(Textfiled, Lookupable):
             else:
                 assert SEP_DESIGN_DESC in body
                 metadata, desc = body.split(SEP_DESIGN_DESC, maxsplit=1)
-                kwargs: dict[str, str | int] = {'description': desc}
+                attrs = BrickDesignData(description=desc)
+                annos = BrickDesignData.__annotations__
                 for attr in metadata.split(SEP_DESIGN_ATTR):
                     assert CHAR_ATTR_EQ in attr
                     a_key, a_val_str = attr.split(CHAR_ATTR_EQ, maxsplit=1)
+                    assert a_key in annos and annos[a_key] is int
                     assert a_val_str.isdigit()
-                    kwargs[a_key] = int(a_val_str)
-                collected[design_id] = cls(design_id,
-                                           BrickDesignData(**kwargs))
+                    setattr(attrs, a_key, int(a_val_str))
+                collected[design_id] = cls(design_id, attrs)
         for id_, alternate_ids in alts.items():
             collected[id_].alternate_ids = alternate_ids
             for alt_id in alternate_ids:
@@ -332,9 +330,9 @@ class BrickDesign(Textfiled, Lookupable):
         return (
             f'{self.id_indented()} '
             + (f'{CHAR_DESIGN_ALT}{self.alternate_to.id_}' if self.alternate_to
-               else (('' if self.attrs.n_studs < 0
-                      else f'n_studs{CHAR_ATTR_EQ}{self.attrs.n_studs}')
-                     + f'{SEP_DESIGN_DESC}{self.attrs.description}')))
+               else (('' if self.n_studs < 0
+                      else f'n_studs{CHAR_ATTR_EQ}{self.n_studs}')
+                     + f'{SEP_DESIGN_DESC}{self.description}')))
 
 
 class Brick(Textfiled, WithDb, Lookupable):
@@ -396,7 +394,7 @@ class Brick(Textfiled, WithDb, Lookupable):
         comment = f' # {self.comment}' if self.comment else ''
         return (f'{self.id_indented()} '
                 f'{BrickDesign.indent_id(self.design_id)} '
-                f'{design.attrs.description} ({color}){comment}')
+                f'{design.description} ({color}){comment}')
 
 
 class BrickSet(Textfiled, WithDb, Lookupable):
@@ -546,7 +544,7 @@ class BrickSet(Textfiled, WithDb, Lookupable):
             return (
                 f'{count:>2}× {Brick.indent_id(brick_id)}:'
                 f'{BrickDesign.indent_id(design_id)} {box_listing} {color} '
-                + self._db.designs[design_id].attrs.description + tail_comment)
+                + self._db.designs[design_id].description + tail_comment)
 
         return f'{self}{CHAR_NEWLINE}{self._format_paginated(format_line)}'
 
@@ -596,8 +594,7 @@ class Box(WithDb, Lookupable):
         lines = []
         for design, listings in self.designs_to_listings:
             lines += [
-                f'=== {" / ".join(design.all_ids)}: '
-                f'{design.attrs.description} ===']
+                f'=== {" / ".join(design.all_ids)}: {design.description} ===']
             for count, brick_id, comment in listings:
                 color = self._db.colors[self._db.bricks[brick_id].color_id]
                 lines += [f'{count:>2}× {Brick.indent_id(brick_id)} / {color} '