(root)/
Python-3.12.0/
Tools/
c-analyzer/
c_common/
clsutil.py
       1  
       2  _NOT_SET = object()
       3  
       4  
       5  class ESC[4;38;5;81mSlot:
       6      """A descriptor that provides a slot.
       7  
       8      This is useful for types that can't have slots via __slots__,
       9      e.g. tuple subclasses.
      10      """
      11  
      12      __slots__ = ('initial', 'default', 'readonly', 'instances', 'name')
      13  
      14      def __init__(self, initial=_NOT_SET, *,
      15                   default=_NOT_SET,
      16                   readonly=False,
      17                   ):
      18          self.initial = initial
      19          self.default = default
      20          self.readonly = readonly
      21  
      22          # The instance cache is not inherently tied to the normal
      23          # lifetime of the instances.  So must do something in order to
      24          # avoid keeping the instances alive by holding a reference here.
      25          # Ideally we would use weakref.WeakValueDictionary to do this.
      26          # However, most builtin types do not support weakrefs.  So
      27          # instead we monkey-patch __del__ on the attached class to clear
      28          # the instance.
      29          self.instances = {}
      30          self.name = None
      31  
      32      def __set_name__(self, cls, name):
      33          if self.name is not None:
      34              raise TypeError('already used')
      35          self.name = name
      36          try:
      37              slotnames = cls.__slot_names__
      38          except AttributeError:
      39              slotnames = cls.__slot_names__ = []
      40          slotnames.append(name)
      41          self._ensure___del__(cls, slotnames)
      42  
      43      def __get__(self, obj, cls):
      44          if obj is None:  # called on the class
      45              return self
      46          try:
      47              value = self.instances[id(obj)]
      48          except KeyError:
      49              if self.initial is _NOT_SET:
      50                  value = self.default
      51              else:
      52                  value = self.initial
      53              self.instances[id(obj)] = value
      54          if value is _NOT_SET:
      55              raise AttributeError(self.name)
      56          # XXX Optionally make a copy?
      57          return value
      58  
      59      def __set__(self, obj, value):
      60          if self.readonly:
      61              raise AttributeError(f'{self.name} is readonly')
      62          # XXX Optionally coerce?
      63          self.instances[id(obj)] = value
      64  
      65      def __delete__(self, obj):
      66          if self.readonly:
      67              raise AttributeError(f'{self.name} is readonly')
      68          self.instances[id(obj)] = self.default  # XXX refleak?
      69  
      70      def _ensure___del__(self, cls, slotnames):  # See the comment in __init__().
      71          try:
      72              old___del__ = cls.__del__
      73          except AttributeError:
      74              old___del__ = (lambda s: None)
      75          else:
      76              if getattr(old___del__, '_slotted', False):
      77                  return
      78  
      79          def __del__(_self):
      80              for name in slotnames:
      81                  delattr(_self, name)
      82              old___del__(_self)
      83          __del__._slotted = True
      84          cls.__del__ = __del__
      85  
      86      def set(self, obj, value):
      87          """Update the cached value for an object.
      88  
      89          This works even if the descriptor is read-only.  This is
      90          particularly useful when initializing the object (e.g. in
      91          its __new__ or __init__).
      92          """
      93          self.instances[id(obj)] = value
      94  
      95  
      96  class ESC[4;38;5;81mclassonly:
      97      """A non-data descriptor that makes a value only visible on the class.
      98  
      99      This is like the "classmethod" builtin, but does not show up on
     100      instances of the class.  It may be used as a decorator.
     101      """
     102  
     103      def __init__(self, value):
     104          self.value = value
     105          self.getter = classmethod(value).__get__
     106          self.name = None
     107  
     108      def __set_name__(self, cls, name):
     109          if self.name is not None:
     110              raise TypeError('already used')
     111          self.name = name
     112  
     113      def __get__(self, obj, cls):
     114          if obj is not None:
     115              raise AttributeError(self.name)
     116          # called on the class
     117          return self.getter(None, cls)