From 218f9d9b1f0c28969a339beead4a059a46f728dc Mon Sep 17 00:00:00 2001 From: jaseg Date: Tue, 21 Jun 2022 12:26:38 +0200 Subject: Make gerbonara python3.8 compatible. --- gerbonara/apertures.py | 32 +++++++++++++++++++++----------- gerbonara/graphic_objects.py | 10 ++++++++-- gerbonara/graphic_primitives.py | 16 +++++++++++++--- gerbonara/ipc356.py | 5 +---- 4 files changed, 43 insertions(+), 20 deletions(-) (limited to 'gerbonara') 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 -- cgit