mercurial/thirdparty/attr/_compat.py
changeset 49643 e1c586b9a43c
parent 34397 765eb17a7eb8
equal deleted inserted replaced
49642:7e6f3c69c0fb 49643:e1c586b9a43c
     1 from __future__ import absolute_import, division, print_function
     1 # SPDX-License-Identifier: MIT
     2 
       
     3 import sys
       
     4 import types
       
     5 
     2 
     6 
     3 
     7 PY2 = sys.version_info[0] == 2
     4 import inspect
       
     5 import platform
       
     6 import sys
       
     7 import threading
       
     8 import types
       
     9 import warnings
       
    10 
       
    11 from collections.abc import Mapping, Sequence  # noqa
     8 
    12 
     9 
    13 
    10 if PY2:
    14 PYPY = platform.python_implementation() == "PyPy"
    11     from UserDict import IterableUserDict
    15 PY36 = sys.version_info[:2] >= (3, 6)
       
    16 HAS_F_STRINGS = PY36
       
    17 PY310 = sys.version_info[:2] >= (3, 10)
    12 
    18 
    13     # We 'bundle' isclass instead of using inspect as importing inspect is
       
    14     # fairly expensive (order of 10-15 ms for a modern machine in 2016)
       
    15     def isclass(klass):
       
    16         return isinstance(klass, (type, types.ClassType))
       
    17 
    19 
    18     # TYPE is used in exceptions, repr(int) is different on Python 2 and 3.
    20 if PYPY or PY36:
    19     TYPE = "type"
    21     ordered_dict = dict
       
    22 else:
       
    23     from collections import OrderedDict
    20 
    24 
    21     def iteritems(d):
    25     ordered_dict = OrderedDict
    22         return d.iteritems()
       
    23 
    26 
    24     def iterkeys(d):
       
    25         return d.iterkeys()
       
    26 
    27 
    27     # Python 2 is bereft of a read-only dict proxy, so we make one!
    28 def just_warn(*args, **kw):
    28     class ReadOnlyDict(IterableUserDict):
    29     warnings.warn(
       
    30         "Running interpreter doesn't sufficiently support code object "
       
    31         "introspection.  Some features like bare super() or accessing "
       
    32         "__class__ will not work with slotted classes.",
       
    33         RuntimeWarning,
       
    34         stacklevel=2,
       
    35     )
       
    36 
       
    37 
       
    38 class _AnnotationExtractor:
       
    39     """
       
    40     Extract type annotations from a callable, returning None whenever there
       
    41     is none.
       
    42     """
       
    43 
       
    44     __slots__ = ["sig"]
       
    45 
       
    46     def __init__(self, callable):
       
    47         try:
       
    48             self.sig = inspect.signature(callable)
       
    49         except (ValueError, TypeError):  # inspect failed
       
    50             self.sig = None
       
    51 
       
    52     def get_first_param_type(self):
    29         """
    53         """
    30         Best-effort read-only dict wrapper.
    54         Return the type annotation of the first argument if it's not empty.
    31         """
    55         """
       
    56         if not self.sig:
       
    57             return None
    32 
    58 
    33         def __setitem__(self, key, val):
    59         params = list(self.sig.parameters.values())
    34             # We gently pretend we're a Python 3 mappingproxy.
    60         if params and params[0].annotation is not inspect.Parameter.empty:
    35             raise TypeError("'mappingproxy' object does not support item "
    61             return params[0].annotation
    36                             "assignment")
       
    37 
    62 
    38         def update(self, _):
    63         return None
    39             # We gently pretend we're a Python 3 mappingproxy.
       
    40             raise AttributeError("'mappingproxy' object has no attribute "
       
    41                                  "'update'")
       
    42 
    64 
    43         def __delitem__(self, _):
    65     def get_return_type(self):
    44             # We gently pretend we're a Python 3 mappingproxy.
    66         """
    45             raise TypeError("'mappingproxy' object does not support item "
    67         Return the return type if it's not empty.
    46                             "deletion")
    68         """
       
    69         if (
       
    70             self.sig
       
    71             and self.sig.return_annotation is not inspect.Signature.empty
       
    72         ):
       
    73             return self.sig.return_annotation
    47 
    74 
    48         def clear(self):
    75         return None
    49             # We gently pretend we're a Python 3 mappingproxy.
       
    50             raise AttributeError("'mappingproxy' object has no attribute "
       
    51                                  "'clear'")
       
    52 
    76 
    53         def pop(self, key, default=None):
       
    54             # We gently pretend we're a Python 3 mappingproxy.
       
    55             raise AttributeError("'mappingproxy' object has no attribute "
       
    56                                  "'pop'")
       
    57 
    77 
    58         def popitem(self):
    78 def make_set_closure_cell():
    59             # We gently pretend we're a Python 3 mappingproxy.
    79     """Return a function of two arguments (cell, value) which sets
    60             raise AttributeError("'mappingproxy' object has no attribute "
    80     the value stored in the closure cell `cell` to `value`.
    61                                  "'popitem'")
    81     """
       
    82     # pypy makes this easy. (It also supports the logic below, but
       
    83     # why not do the easy/fast thing?)
       
    84     if PYPY:
    62 
    85 
    63         def setdefault(self, key, default=None):
    86         def set_closure_cell(cell, value):
    64             # We gently pretend we're a Python 3 mappingproxy.
    87             cell.__setstate__((value,))
    65             raise AttributeError("'mappingproxy' object has no attribute "
       
    66                                  "'setdefault'")
       
    67 
    88 
    68         def __repr__(self):
    89         return set_closure_cell
    69             # Override to be identical to the Python 3 version.
       
    70             return "mappingproxy(" + repr(self.data) + ")"
       
    71 
    90 
    72     def metadata_proxy(d):
    91     # Otherwise gotta do it the hard way.
    73         res = ReadOnlyDict()
       
    74         res.data.update(d)  # We blocked update, so we have to do it like this.
       
    75         return res
       
    76 
    92 
    77 else:
    93     # Create a function that will set its first cellvar to `value`.
    78     def isclass(klass):
    94     def set_first_cellvar_to(value):
    79         return isinstance(klass, type)
    95         x = value
       
    96         return
    80 
    97 
    81     TYPE = "class"
    98         # This function will be eliminated as dead code, but
       
    99         # not before its reference to `x` forces `x` to be
       
   100         # represented as a closure cell rather than a local.
       
   101         def force_x_to_be_a_cell():  # pragma: no cover
       
   102             return x
    82 
   103 
    83     def iteritems(d):
   104     try:
    84         return d.items()
   105         # Extract the code object and make sure our assumptions about
       
   106         # the closure behavior are correct.
       
   107         co = set_first_cellvar_to.__code__
       
   108         if co.co_cellvars != ("x",) or co.co_freevars != ():
       
   109             raise AssertionError  # pragma: no cover
    85 
   110 
    86     def iterkeys(d):
   111         # Convert this code object to a code object that sets the
    87         return d.keys()
   112         # function's first _freevar_ (not cellvar) to the argument.
       
   113         if sys.version_info >= (3, 8):
    88 
   114 
    89     def metadata_proxy(d):
   115             def set_closure_cell(cell, value):
    90         return types.MappingProxyType(dict(d))
   116                 cell.cell_contents = value
       
   117 
       
   118         else:
       
   119             args = [co.co_argcount]
       
   120             args.append(co.co_kwonlyargcount)
       
   121             args.extend(
       
   122                 [
       
   123                     co.co_nlocals,
       
   124                     co.co_stacksize,
       
   125                     co.co_flags,
       
   126                     co.co_code,
       
   127                     co.co_consts,
       
   128                     co.co_names,
       
   129                     co.co_varnames,
       
   130                     co.co_filename,
       
   131                     co.co_name,
       
   132                     co.co_firstlineno,
       
   133                     co.co_lnotab,
       
   134                     # These two arguments are reversed:
       
   135                     co.co_cellvars,
       
   136                     co.co_freevars,
       
   137                 ]
       
   138             )
       
   139             set_first_freevar_code = types.CodeType(*args)
       
   140 
       
   141             def set_closure_cell(cell, value):
       
   142                 # Create a function using the set_first_freevar_code,
       
   143                 # whose first closure cell is `cell`. Calling it will
       
   144                 # change the value of that cell.
       
   145                 setter = types.FunctionType(
       
   146                     set_first_freevar_code, {}, "setter", (), (cell,)
       
   147                 )
       
   148                 # And call it to set the cell.
       
   149                 setter(value)
       
   150 
       
   151         # Make sure it works on this interpreter:
       
   152         def make_func_with_cell():
       
   153             x = None
       
   154 
       
   155             def func():
       
   156                 return x  # pragma: no cover
       
   157 
       
   158             return func
       
   159 
       
   160         cell = make_func_with_cell().__closure__[0]
       
   161         set_closure_cell(cell, 100)
       
   162         if cell.cell_contents != 100:
       
   163             raise AssertionError  # pragma: no cover
       
   164 
       
   165     except Exception:
       
   166         return just_warn
       
   167     else:
       
   168         return set_closure_cell
       
   169 
       
   170 
       
   171 set_closure_cell = make_set_closure_cell()
       
   172 
       
   173 # Thread-local global to track attrs instances which are already being repr'd.
       
   174 # This is needed because there is no other (thread-safe) way to pass info
       
   175 # about the instances that are already being repr'd through the call stack
       
   176 # in order to ensure we don't perform infinite recursion.
       
   177 #
       
   178 # For instance, if an instance contains a dict which contains that instance,
       
   179 # we need to know that we're already repr'ing the outside instance from within
       
   180 # the dict's repr() call.
       
   181 #
       
   182 # This lives here rather than in _make.py so that the functions in _make.py
       
   183 # don't have a direct reference to the thread-local in their globals dict.
       
   184 # If they have such a reference, it breaks cloudpickle.
       
   185 repr_context = threading.local()