blob: 30b3d9de8912fa8265ae0408db3f88a613f671fd [file] [log] [blame]
#!/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()