| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright 2020 SkyWater PDK Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| import dataclasses |
| import dataclasses_json |
| import functools |
| import random |
| import re |
| import sys |
| |
| from dataclasses import dataclass |
| from dataclasses_json import dataclass_json |
| from enum import Flag |
| from typing import Optional, Tuple, Any |
| |
| |
| def dataclass_json_passthru_config(*args, **kw): |
| return dataclasses.field( |
| *args, |
| metadata=dataclasses_json.config( |
| encoder=lambda x: x.to_json(), |
| #decoder=lambda x: x.from_json(), |
| ), |
| **kw, |
| ) |
| |
| def dataclass_json_passthru_sequence_config(*args, **kw): |
| def to_json_sequence(s): |
| if s is None: |
| return None |
| o = [] |
| for i in s: |
| if hasattr(i, 'to_json'): |
| o.append(i.to_json()) |
| else: |
| o.append(i) |
| return o |
| |
| return dataclasses.field( |
| *args, |
| metadata=dataclasses_json.config( |
| encoder=to_json_sequence, |
| #decoder=lambda x: x.from_json(), |
| ), |
| **kw, |
| ) |
| |
| |
| |
| def comparable_to_none(cls): |
| """ |
| |
| Examples |
| -------- |
| |
| >>> @comparable_to_none |
| ... @dataclass(order=True) |
| ... class A: |
| ... a: int = 0 |
| >>> @comparable_to_none |
| ... @dataclass(order=True) |
| ... class B: |
| ... b: Optional[A] = None |
| >>> b0 = B() |
| >>> repr(b0) |
| 'B(b=None)' |
| >>> str(b0) |
| 'B(b=None)' |
| >>> b1 = B(A()) |
| >>> repr(b1) |
| 'B(b=A(a=0))' |
| >>> str(b1) |
| 'B(b=A(a=0))' |
| >>> b2 = B(A(2)) |
| >>> repr(b2) |
| 'B(b=A(a=2))' |
| >>> str(b2) |
| 'B(b=A(a=2))' |
| >>> l = [b0, b1, b2, None] |
| >>> for i in range(0, 3): |
| ... random.shuffle(l) |
| ... l.sort() |
| ... print(l) |
| [None, B(b=None), B(b=A(a=0)), B(b=A(a=2))] |
| [None, B(b=None), B(b=A(a=0)), B(b=A(a=2))] |
| [None, B(b=None), B(b=A(a=0)), B(b=A(a=2))] |
| |
| """ |
| class ComparableToNoneVersion(cls): |
| def __ge__(self, other): |
| if other is None: |
| return True |
| return super().__ge__(other) |
| def __gt__(self, other): |
| if other is None: |
| return True |
| return super().__gt__(other) |
| def __le__(self, other): |
| if other is None: |
| return False |
| return super().__le__(other) |
| def __lt__(self, other): |
| if other is None: |
| return False |
| return super().__lt__(other) |
| def __eq__(self, other): |
| if other is None: |
| return False |
| return super().__eq__(other) |
| def __hash__(self): |
| return super().__hash__() |
| def __repr__(self): |
| s = super().__repr__() |
| return s.replace('comparable_to_none.<locals>.ComparableToNoneVersion', cls.__name__) |
| |
| for a in functools.WRAPPER_ASSIGNMENTS: |
| if not hasattr(cls, a): |
| continue |
| setattr(ComparableToNoneVersion, a, getattr(cls, a)) |
| |
| return ComparableToNoneVersion |
| |
| |
| def _is_optional_type(t): |
| """ |
| |
| Examples |
| -------- |
| |
| >>> _is_optional_type(Optional[int]) |
| True |
| >>> _is_optional_type(Optional[Tuple]) |
| True |
| >>> _is_optional_type(Any) |
| False |
| """ |
| return hasattr(t, "__args__") and len(t.__args__) == 2 and t.__args__[-1] is type(None) |
| |
| |
| def _get_the_optional_type(t): |
| """ |
| |
| Examples |
| -------- |
| |
| >>> _get_the_optional_type(Optional[int]) |
| <class 'int'> |
| >>> _get_the_optional_type(Optional[Tuple]) |
| typing.Tuple |
| >>> class A: |
| ... pass |
| >>> _get_the_optional_type(Optional[A]) |
| <class '__main__.A'> |
| >>> _get_type_name(_get_the_optional_type(Optional[A])) |
| 'A' |
| """ |
| assert _is_optional_type(t), t |
| return t.__args__[0] |
| |
| |
| def _get_type_name(ot): |
| """ |
| |
| Examples |
| -------- |
| |
| >>> _get_type_name(int) |
| 'int' |
| >>> _get_type_name(Tuple) |
| 'Tuple' |
| >>> _get_type_name(Optional[Tuple]) |
| 'typing.Union[typing.Tuple, NoneType]' |
| """ |
| if hasattr(ot, "_name") and ot._name: |
| return ot._name |
| elif hasattr(ot, "__name__") and ot.__name__: |
| return ot.__name__ |
| else: |
| return str(ot) |
| |
| |
| class OrderedFlag(Flag): |
| def __ge__(self, other): |
| if other is None: |
| return True |
| if self.__class__ is other.__class__: |
| return self.value >= other.value |
| return NotImplemented |
| def __gt__(self, other): |
| if other is None: |
| return True |
| if self.__class__ is other.__class__: |
| return self.value > other.value |
| return NotImplemented |
| def __le__(self, other): |
| if other is None: |
| return False |
| if self.__class__ is other.__class__: |
| return self.value <= other.value |
| return NotImplemented |
| def __lt__(self, other): |
| if other is None: |
| return False |
| if self.__class__ is other.__class__: |
| return self.value < other.value |
| return NotImplemented |
| def __eq__(self, other): |
| if other is None: |
| return False |
| if self.__class__ is other.__class__: |
| return self.value == other.value |
| return NotImplemented |
| def __hash__(self): |
| return hash(self._name_) |
| |
| |
| def extract_numbers(s): |
| """Create tuple with sequences of numbers converted to ints. |
| |
| >>> extract_numbers("pwr_template13x10") |
| ('pwr_template', 13, 'x', 10) |
| >>> extract_numbers("vio_10_10_1") |
| ('vio_', 10, '_', 10, '_', 1) |
| """ |
| bits = [] |
| for m in re.finditer("([^0-9]*)([0-9]*)", s): |
| if m.group(1): |
| bits.append(m.group(1)) |
| if m.group(2): |
| bits.append(int(m.group(2))) |
| return tuple(bits) |
| |
| |
| def sortable_extracted_numbers(s): |
| """Create output which is sortable by numeric values in string. |
| |
| >>> sortable_extracted_numbers("pwr_template13x10") |
| ('pwr_template', '0000000013', 'x', '0000000010') |
| >>> sortable_extracted_numbers("vio_10_10_1") |
| ('vio_', '0000000010', '_', '0000000010', '_', '0000000001') |
| |
| >>> l = ['a1', 'a2b2', 'a10b10', 'b2', 'a8b50', 'a10b1'] |
| >>> l.sort() |
| >>> print('\\n'.join(l)) |
| a1 |
| a10b1 |
| a10b10 |
| a2b2 |
| a8b50 |
| b2 |
| >>> l.sort(key=sortable_extracted_numbers) |
| >>> print('\\n'.join(l)) |
| a1 |
| a2b2 |
| a8b50 |
| a10b1 |
| a10b10 |
| b2 |
| |
| """ |
| zero_pad_str = '%010i' |
| bits = extract_numbers(s) |
| o = [] |
| |
| for b in bits: |
| if not isinstance(b, str): |
| assert isinstance(b, int), (b, bits) |
| assert len(str(b)) < len(zero_pad_str % 0) |
| b = zero_pad_str % b |
| o.append(b) |
| return tuple(o) |
| |
| |
| |
| if __name__ == "__main__": |
| import doctest |
| doctest.testmod() |