Source code for lineapy.utils.deprecation_utils

import functools
import weakref
from functools import singledispatch, update_wrapper


# Descriptor version
[docs]class singledispatchmethod: """Single-dispatch generic method descriptor. Supports wrapping existing descriptors and handles non-descriptor callables as instance methods. """ def __init__(self, func): if not callable(func) and not hasattr(func, "__get__"): raise TypeError(f"{func!r} is not callable or a descriptor") self.dispatcher = singledispatch(func) self.func = func
[docs] def register(self, cls, method=None): """generic_method.register(cls, func) -> func Registers a new implementation for the given *cls* on a *generic_method*. """ return self.dispatcher.register(cls, func=method)
def __get__(self, obj, cls=None): def _method(*args, **kwargs): method = self.dispatcher.dispatch(args[0].__class__) return method.__get__(obj, cls)(*args, **kwargs) # type: ignore _method.__isabstractmethod__ = self.__isabstractmethod__ # type: ignore _method.register = self.register # type: ignore update_wrapper(_method, self.func) return _method @property def __isabstractmethod__(self): return getattr(self.func, "__isabstractmethod__", False)
def _splitlines_no_ff(source): """Split a string into lines ignoring form feed and other chars. This mimics how the Python parser splits source code. """ idx = 0 lines = [] next_line = "" while idx < len(source): c = source[idx] next_line += c idx += 1 # Keep \r\n together if c == "\r" and idx < len(source) and source[idx] == "\n": next_line += "\n" idx += 1 if c in "\r\n": lines.append(next_line) next_line = "" if next_line: lines.append(next_line) return lines def _pad_whitespace(source): r"""Replace all chars except '\f\t' in a line with spaces.""" result = "" for c in source: if c in "\f\t": result += c else: result += " " return result
[docs]def get_source_segment(source, node, padded=False): """Get source code segment of the *source* that generated *node*. .. note:: This is a polyfill for the ast.get_source_segment function that was introduced in python 3.8. If some location information (`lineno`, `end_lineno`, `col_offset`, or `end_col_offset`) is missing, return None. If *padded* is `True`, the first line of a multi-line statement will be padded with spaces to match its original position. """ try: if node.end_lineno is None or node.end_col_offset is None: return None lineno = node.lineno - 1 end_lineno = node.end_lineno - 1 col_offset = node.col_offset end_col_offset = node.end_col_offset except AttributeError: return None lines = _splitlines_no_ff(source) if end_lineno == lineno: return lines[lineno].encode()[col_offset:end_col_offset].decode() if padded: padding = _pad_whitespace(lines[lineno].encode()[:col_offset].decode()) else: padding = "" first = padding + lines[lineno].encode()[col_offset:].decode() last = lines[end_lineno].encode()[:end_col_offset].decode() lines = lines[lineno + 1 : end_lineno] lines.insert(0, first) lines.append(last) return "".join(lines)
# references # really we should just use cached_property but not supported by Python 3.7 # https://stackoverflow.com/questions/33672412/python-functools-lru-cache-with-instance-methods-release-object/33672499#33672499 def lru_cache(*lru_args, **lru_kwargs): def decorator(func): @functools.wraps(func) def wrapped_func(self, *args, **kwargs): # We're storing the wrapped method inside the instance. If we had # a strong reference to self the instance would never die. self_weak = weakref.ref(self) @functools.wraps(func) @functools.lru_cache(*lru_args, **lru_kwargs) def cached_method(*args, **kwargs): return func(self_weak(), *args, **kwargs) setattr(self, func.__name__, cached_method) return cached_method(*args, **kwargs) return wrapped_func return decorator