diff --git a/src/wireviz/DataClasses.py b/src/wireviz/DataClasses.py index 529ad30..a9ce303 100644 --- a/src/wireviz/DataClasses.py +++ b/src/wireviz/DataClasses.py @@ -1,15 +1,32 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from typing import Optional, List, Any, Union +from typing import Optional, List, Tuple, Union from dataclasses import dataclass, field, InitVar from pathlib import Path from wireviz.wv_helper import int2tuple, aspect_ratio from wireviz import wv_colors + +# Each type alias have their legal values described in comments - validation might be implemented in the future +PlainText = str # Text not containing HTML tags nor newlines +Hypertext = str # Text possibly including HTML hyperlinks that are removed in all outputs except HTML output +MultilineHypertext = str # Hypertext possibly also including newlines to break lines in diagram output +Designator = PlainText # Case insensitive unique name of connector or cable + # Literal type aliases below are commented to avoid requiring python 3.8 -ConnectorMultiplier = str # = Literal['pincount', 'populated'] -CableMultiplier = str # = Literal['wirecount', 'terminations', 'length', 'total_length'] +ConnectorMultiplier = PlainText # = Literal['pincount', 'populated'] +CableMultiplier = PlainText # = Literal['wirecount', 'terminations', 'length', 'total_length'] +ImageScale = PlainText # = Literal['false', 'true', 'width', 'height', 'both'] +Color = PlainText # Two-letter color name = Literal[wv_colors._color_hex.keys()] +ColorScheme = PlainText # Color scheme name = Literal[wv_colors.COLOR_CODES.keys()] + +# Type combinations +Colors = PlainText # One or more two-letter color names (Color) concatenated into one string +Pin = Union[int, PlainText] # Pin identifier +Wire = Union[int, PlainText] # Wire number or Literal['s'] for shield +NoneOrMorePins = Union[Pin, Tuple[Pin, ...], None] # None, one, or a tuple of pins +OneOrMoreWires = Union[Wire, Tuple[Wire, ...]] # One or a tuple of wires @dataclass @@ -17,13 +34,13 @@ class Image: gv_dir: InitVar[Path] # Directory of .gv file injected as context during parsing # Attributes of the image object : src: str - scale: Optional[str] = None # false | true | width | height | both + scale: Optional[ImageScale] = None # Attributes of the image cell containing the image: width: Optional[int] = None height: Optional[int] = None fixedsize: Optional[bool] = None # Contents of the text cell just below the image cell: - caption: Optional[str] = None + caption: Optional[MultilineHypertext] = None # See also HTML doc at https://graphviz.org/doc/info/shapes.html#html def __post_init__(self, gv_dir): @@ -47,13 +64,14 @@ class Image: if self.width: self.height = self.width / aspect_ratio(gv_dir.joinpath(self.src)) + @dataclass class AdditionalComponent: - type: str - subtype: Optional[str] = None - manufacturer: Optional[str] = None - mpn: Optional[str] = None - pn: Optional[str] = None + type: MultilineHypertext + subtype: Optional[MultilineHypertext] = None + manufacturer: Optional[MultilineHypertext] = None + mpn: Optional[MultilineHypertext] = None + pn: Optional[Hypertext] = None qty: float = 1 unit: Optional[str] = None qty_multiplier: Union[ConnectorMultiplier, CableMultiplier, None] = None @@ -65,29 +83,29 @@ class AdditionalComponent: @dataclass class Connector: - name: str - manufacturer: Optional[str] = None - mpn: Optional[str] = None - pn: Optional[str] = None + name: Designator + manufacturer: Optional[MultilineHypertext] = None + mpn: Optional[MultilineHypertext] = None + pn: Optional[Hypertext] = None style: Optional[str] = None category: Optional[str] = None - type: Optional[str] = None - subtype: Optional[str] = None + type: Optional[MultilineHypertext] = None + subtype: Optional[MultilineHypertext] = None pincount: Optional[int] = None image: Optional[Image] = None - notes: Optional[str] = None - pinlabels: List[Any] = field(default_factory=list) - pins: List[Any] = field(default_factory=list) - color: Optional[str] = None + notes: Optional[MultilineHypertext] = None + pinlabels: List[Pin] = field(default_factory=list) + pins: List[Pin] = field(default_factory=list) + color: Optional[Color] = None show_name: Optional[bool] = None show_pincount: Optional[bool] = None hide_disconnected_pins: bool = False autogenerate: bool = False - loops: List[Any] = field(default_factory=list) + loops: List[List[Pin]] = field(default_factory=list) ignore_in_bom: bool = False additional_components: List[AdditionalComponent] = field(default_factory=list) - def __post_init__(self): + def __post_init__(self) -> None: if isinstance(self.image, dict): self.image = Image(**self.image) @@ -139,7 +157,7 @@ class Connector: if isinstance(item, dict): self.additional_components[i] = AdditionalComponent(**item) - def activate_pin(self, pin): + def activate_pin(self, pin: Pin) -> None: self.visible_pins[pin] = True def get_qty_multiplier(self, qty_multiplier: Optional[ConnectorMultiplier]) -> int: @@ -155,29 +173,29 @@ class Connector: @dataclass class Cable: - name: str - manufacturer: Optional[Union[str, List[str]]] = None - mpn: Optional[Union[str, List[str]]] = None - pn: Optional[Union[str, List[str]]] = None + name: Designator + manufacturer: Union[MultilineHypertext, List[MultilineHypertext], None] = None + mpn: Union[MultilineHypertext, List[MultilineHypertext], None] = None + pn: Union[Hypertext, List[Hypertext], None] = None category: Optional[str] = None - type: Optional[str] = None + type: Optional[MultilineHypertext] = None gauge: Optional[float] = None gauge_unit: Optional[str] = None show_equiv: bool = False length: float = 0 - color: Optional[str] = None + color: Optional[Color] = None wirecount: Optional[int] = None - shield: Union[bool, str] = False # False | True | color + shield: Union[bool, Color] = False image: Optional[Image] = None - notes: Optional[str] = None - colors: List[Any] = field(default_factory=list) - color_code: Optional[str] = None + notes: Optional[MultilineHypertext] = None + colors: List[Colors] = field(default_factory=list) + color_code: Optional[ColorScheme] = None show_name: bool = True show_wirecount: bool = True ignore_in_bom: bool = False additional_components: List[AdditionalComponent] = field(default_factory=list) - def __post_init__(self): + def __post_init__(self) -> None: if isinstance(self.image, dict): self.image = Image(**self.image) @@ -237,7 +255,9 @@ class Cable: if isinstance(item, dict): self.additional_components[i] = AdditionalComponent(**item) - def connect(self, from_name, from_pin, via_pin, to_name, to_pin): + # The *_pin arguments accept a tuple, but it seems not in use with the current code. + def connect(self, from_name: Optional[Designator], from_pin: NoneOrMorePins, via_pin: OneOrMoreWires, + to_name: Optional[Designator], to_pin: NoneOrMorePins) -> None: from_pin = int2tuple(from_pin) via_pin = int2tuple(via_pin) to_pin = int2tuple(to_pin) @@ -264,8 +284,8 @@ class Cable: @dataclass class Connection: - from_name: Any - from_port: Any - via_port: Any - to_name: Any - to_port: Any + from_name: Optional[Designator] + from_port: Optional[Pin] + via_port: Wire + to_name: Optional[Designator] + to_port: Optional[Pin]