From 45cfc32f4a0c7922abe89950766bfe59a9414bf9 Mon Sep 17 00:00:00 2001 From: Plom Heller Date: Sun, 9 Aug 2026 06:50:52 +0200 Subject: [PATCH] Allow selective ignorance of LDRAW guesses. --- src/bricksplom/misc.py | 44 +++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/bricksplom/misc.py b/src/bricksplom/misc.py index cbad122..39e22e9 100644 --- a/src/bricksplom/misc.py +++ b/src/bricksplom/misc.py @@ -2,9 +2,9 @@ # standard libs from abc import ABC, abstractmethod -from dataclasses import dataclass +from dataclasses import dataclass, field as dc_field from pathlib import Path -from typing import Callable, Optional, Self +from typing import Any, Callable, Optional, Self # ourselves from bricksplom.constants import CHAR_NEWLINE from bricksplom.ldraw import LdrawDb, LdrawPathFailure, LdrawRefsTree @@ -269,6 +269,29 @@ class BrickDesignData: n_studs: int = -1 description: str = '?' ldraw: str = '' + _secured: set = dc_field(default_factory=set) + + @classmethod + def sortables( + cls + ) -> dict[str, Any]: + 'Any .__annotations__ whose key does not start with an underscore.' + return {k: v for k, v in cls.__annotations__.items() + if not k.startswith('_')} + + def secure( + self, + attr_key: str + ) -> None: + 'Declare knowledge of attribute as certain (overpower ldraw guesses).' + self._secured.add(attr_key) + + def secured( + self, + attr_key: str + ) -> bool: + 'Is knowledge of attribute certain (to owerpower ldraw guesses)?' + return attr_key in self._secured class BrickDesign(Textfiled, WithDb, Lookupable): @@ -288,7 +311,7 @@ class BrickDesign(Textfiled, WithDb, Lookupable): super().__init__(**kwargs) def __getattribute__(self, key: str): - if key in BrickDesignData.__annotations__: + if key in BrickDesignData.__annotations__ or key == 'secured': if self.direct_attrs: attrs = self.direct_attrs else: @@ -365,7 +388,7 @@ class BrickDesign(Textfiled, WithDb, Lookupable): return 'by ' + ATTR_DESCS[attr_name], match return super().matchers() | { f'{attr_name}{CHAR_ATTR_EQ}': attr_matcher(attr_name) - for attr_name, attr_type in BrickDesignData.__annotations__.items() + for attr_name, attr_type in BrickDesignData.sortables().items() if attr_type is int} @classmethod @@ -388,7 +411,7 @@ class BrickDesign(Textfiled, WithDb, Lookupable): return super().sorters()\ | {TOK_SORT_BOX: cls._by_box_sorter('design')}\ | {attr_name: attr_sorter(attr_name) - for attr_name in BrickDesignData.__annotations__} + for attr_name in BrickDesignData.sortables().keys()} @classmethod def from_textfile( @@ -416,6 +439,9 @@ class BrickDesign(Textfiled, WithDb, Lookupable): 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) + if a_val_str[:1] == CHAR_ATTR_EQ: + a_val_str = a_val_str[1:] + attrs.secure((a_key)) assert a_key in annos if annos[a_key] is int: assert a_val_str.isdigit(), a_val_str @@ -436,7 +462,7 @@ class BrickDesign(Textfiled, WithDb, Lookupable): if self.alternate_to: return f'{raw}{CHAR_DESIGN_ALT}{self.alternate_to.id_}' attrs = [] - for attr_key in [k for k in BrickDesignData.__annotations__ + for attr_key in [k for k in BrickDesignData.sortables() if k != 'description']: attr_val = getattr(self, attr_key) if attr_val != getattr(BrickDesignData, attr_key): @@ -828,9 +854,9 @@ class BricksDb: f'{design.id_} ({design.ldraw})'] continue # compare n_studs calculations to explicit records - if LDRAW_MODE_VERIFY_N_STUDS in self._ldraw_modes: - if design.n_studs <= -1: - continue + if LDRAW_MODE_VERIFY_N_STUDS in self._ldraw_modes\ + and design.n_studs > -1\ + and not design.secured('n_studs'): studs_counted = self.ldraw.count_studs(design.ldraw) if design.n_studs != studs_counted: fails += ['n_studs count differs between record and ldraw ' -- 2.30.2