diff --git a/gerbonara/apertures.py b/gerbonara/apertures.py index 33b78df..0a4bf37 100644 --- a/gerbonara/apertures.py +++ b/gerbonara/apertures.py @@ -17,7 +17,7 @@ # import math -from dataclasses import dataclass, replace, field, fields, InitVar, KW_ONLY +from dataclasses import dataclass, replace, field, fields, InitVar from .aperture_macros.parse import GenericMacros from .utils import MM, Inch @@ -60,16 +60,26 @@ class Length: @dataclass class Aperture: """ Base class for all apertures. """ - _ : KW_ONLY - #: :py:class:`gerbonara.utils.LengthUnit` used for all length fields of this aperture. - unit : str = None - #: GerberX2 attributes of this aperture. Note that this will only contain aperture attributes, not file attributes. - #: File attributes are stored in the :py:attr:`~.GerberFile.attrs` of the :py:class:`.GerberFile`. - attrs : dict = field(default_factory=dict) - #: Aperture index this aperture had when it was read from the Gerber file. This field is purely informational since - #: apertures are de-duplicated and re-numbered when writing a Gerber file. For `D10`, this field would be `10`. When - #: you programmatically create a new aperture, you do not have to set this. - original_number : int = None + + # hackety hack: Work around python < 3.10 not having dataclasses.KW_ONLY. + # + # For details, refer to graphic_objects.py + def __init_subclass__(cls): + #: :py:class:`gerbonara.utils.LengthUnit` used for all length fields of this aperture. + cls.unit = None + #: GerberX2 attributes of this aperture. Note that this will only contain aperture attributes, not file attributes. + #: File attributes are stored in the :py:attr:`~.GerberFile.attrs` of the :py:class:`.GerberFile`. + cls.attrs = field(default_factory=dict) + #: Aperture index this aperture had when it was read from the Gerber file. This field is purely informational since + #: apertures are de-duplicated and re-numbered when writing a Gerber file. For `D10`, this field would be `10`. When + #: you programmatically create a new aperture, you do not have to set this. + cls.original_number = None + + d = {'unit': str, 'attrs': dict, 'original_number': int} + if hasattr(cls, '__annotations__'): + cls.__annotations__.update(d) + else: + cls.__annotations__ = d @property def hole_shape(self): diff --git a/gerbonara/graphic_objects.py b/gerbonara/graphic_objects.py index 3136b39..35c08f3 100644 --- a/gerbonara/graphic_objects.py +++ b/gerbonara/graphic_objects.py @@ -18,7 +18,7 @@ import math import copy -from dataclasses import dataclass, KW_ONLY, astuple, replace, field, fields +from dataclasses import dataclass, astuple, replace, field, fields from .utils import MM, InterpMode, to_unit, rotate_point from . import graphic_primitives as gp @@ -45,6 +45,8 @@ class GraphicObject: # hackety hack: Work around python < 3.10 not having dataclasses.KW_ONLY. Once we drop python 3.8 and 3.9, we can # get rid of this, just set these as normal fields, and decorate GraphicObject with @dataclass. + # + # See also: apertures.py, graphic_primitives.py def __init_subclass__(cls): #: bool representing the *color* of this feature: whether this is a *dark* or *clear* feature. Clear and dark are #: meant in the sense that they are used in the Gerber spec and refer to whether the transparency film that this @@ -61,7 +63,11 @@ class GraphicObject: #: which are stored in the :py:class:`.GerberFile` object instead. cls.attrs = field(default_factory=dict) - cls.__annotations__.update({'polarity_dark' : bool, 'unit' : str, 'attrs': dict}) + d = {'polarity_dark' : bool, 'unit' : str, 'attrs': dict} + if hasattr(cls, '__annotations__'): + cls.__annotations__.update(d) + else: + cls.__annotations__ = d def converted(self, unit): diff --git a/gerbonara/graphic_primitives.py b/gerbonara/graphic_primitives.py index dd421a7..072a98a 100644 --- a/gerbonara/graphic_primitives.py +++ b/gerbonara/graphic_primitives.py @@ -19,7 +19,7 @@ import math import itertools -from dataclasses import dataclass, KW_ONLY, replace +from dataclasses import dataclass, replace from .utils import * @@ -28,8 +28,18 @@ prec = lambda x: f'{float(x):.6}' @dataclass class GraphicPrimitive: - _ : KW_ONLY - polarity_dark : bool = True + + # hackety hack: Work around python < 3.10 not having dataclasses.KW_ONLY. + # + # For details, refer to graphic_objects.py + def __init_subclass__(cls): + cls.polarity_dark = True + + d = {'polarity_dark': bool} + if hasattr(cls, '__annotations__'): + cls.__annotations__.update(d) + else: + cls.__annotations__ = d def bounding_box(self): """ Return the axis-aligned bounding box of this feature. diff --git a/gerbonara/ipc356.py b/gerbonara/ipc356.py index 06a87f0..435a805 100644 --- a/gerbonara/ipc356.py +++ b/gerbonara/ipc356.py @@ -23,7 +23,7 @@ import math import re from enum import Enum import warnings -from dataclasses import dataclass, KW_ONLY +from dataclasses import dataclass from pathlib import Path from .cam import CamFile, FileSettings @@ -414,7 +414,6 @@ class TestRecord: rotation : float = 0 solder_mask : SoldermaskInfo = None lefover : str = None - _ : KW_ONLY unit : LengthUnit = None def __str__(self): @@ -563,7 +562,6 @@ def format_coord_chain(line, settings, coords, cont, unit): class Outline: outline_type : OutlineType outline : [(float,)] - _ : KW_ONLY unit : LengthUnit = None @classmethod @@ -596,7 +594,6 @@ class Conductor: layer : int aperture : (float,) coords : [(float,)] - _ : KW_ONLY unit : LengthUnit = None @classmethod diff --git a/run-tests.sh b/run-tests.sh index 1ed2b66..996c1e7 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -24,7 +24,7 @@ mkdir -p podman/testdata/git git ls-tree --full-tree -r HEAD --name-only | rsync -lptgoDv --delete . --files-from - podman/testdata/git/ #git clone --depth 1 . podman/testdata/git -for distro in ubuntu arch +for distro in ubuntu-old ubuntu arch do podman build $NO_CACHE -t gerbonara-$distro-testenv -f podman/$distro-testenv mkdir -p /tmp/gerbonara-test-out