amaranth/nmigen/tools.py
whitequark 744e33f42d hdl: make all public Value classes other than Record final.
In some cases, nMigen uses type() instead of isinstance() to dispatch
on types. Make sure all such uses of type() are robust; in addition,
make it clear that nMigen AST classes are not meant to be subclassed.
(Record is an exception.)

Fixes #65.
2019-05-12 05:40:17 +00:00

103 lines
2.4 KiB
Python

import contextlib
import functools
import warnings
from collections import OrderedDict
from collections.abc import Iterable
from contextlib import contextmanager
__all__ = ["flatten", "union", "log2_int", "bits_for", "memoize", "final", "deprecated"]
def flatten(i):
for e in i:
if isinstance(e, Iterable):
yield from flatten(e)
else:
yield e
def union(i, start=None):
r = start
for e in i:
if r is None:
r = e
else:
r |= e
return r
def log2_int(n, need_pow2=True):
if n == 0:
return 0
r = (n - 1).bit_length()
if need_pow2 and (1 << r) != n:
raise ValueError("{} is not a power of 2".format(n))
return r
def bits_for(n, require_sign_bit=False):
if n > 0:
r = log2_int(n + 1, False)
else:
require_sign_bit = True
r = log2_int(-n, False)
if require_sign_bit:
r += 1
return r
def memoize(f):
memo = OrderedDict()
@functools.wraps(f)
def g(*args):
if args not in memo:
memo[args] = f(*args)
return memo[args]
return g
def final(cls):
def init_subclass():
raise TypeError("Subclassing {}.{} is not supported"
.format(cls.__module__, cls.__name__))
cls.__init_subclass__ = init_subclass
return cls
def deprecated(message, stacklevel=2):
def decorator(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
warnings.warn(message, DeprecationWarning, stacklevel=stacklevel)
return f(*args, **kwargs)
return wrapper
return decorator
def _ignore_deprecated(f=None):
if f is None:
@contextlib.contextmanager
def context_like():
with warnings.catch_warnings():
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
yield
return context_like()
else:
@functools.wraps(f)
def decorator_like(*args, **kwargs):
with warnings.catch_warnings():
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
f(*args, **kwargs)
return decorator_like
def extend(cls):
def decorator(f):
if isinstance(f, property):
name = f.fget.__name__
else:
name = f.__name__
setattr(cls, name, f)
return decorator