MCPcopy Create free account
hub / github.com/bpython/bpython / AttrCleaner

Class AttrCleaner

bpython/inspection.py:77–130  ·  view source on GitHub ↗

A context manager that tries to make an object not exhibit side-effects on attribute lookup. Unless explicitly required, prefer `getattr_safe`.

Source from the content-addressed store, hash-verified

75
76
77class AttrCleaner(ContextManager[None]):
78 """A context manager that tries to make an object not exhibit side-effects
79 on attribute lookup.
80
81 Unless explicitly required, prefer `getattr_safe`."""
82
83 def __init__(self, obj: Any) -> None:
84 self._obj = obj
85
86 def __enter__(self) -> None:
87 """Try to make an object not exhibit side-effects on attribute
88 lookup."""
89 type_ = type(self._obj)
90 # Dark magic:
91 # If __getattribute__ doesn't exist on the class and __getattr__ does
92 # then __getattr__ will be called when doing
93 # getattr(type_, '__getattribute__', None)
94 # so we need to first remove the __getattr__, then the
95 # __getattribute__, then look up the attributes and then restore the
96 # original methods. :-(
97 # The upshot being that introspecting on an object to display its
98 # attributes will avoid unwanted side-effects.
99 __getattr__ = getattr(type_, "__getattr__", None)
100 if __getattr__ is not None:
101 try:
102 setattr(type_, "__getattr__", (lambda *_, **__: None))
103 except (TypeError, AttributeError):
104 __getattr__ = None
105 __getattribute__ = getattr(type_, "__getattribute__", None)
106 if __getattribute__ is not None:
107 try:
108 setattr(type_, "__getattribute__", object.__getattribute__)
109 except (TypeError, AttributeError):
110 # XXX: This happens for e.g. built-in types
111 __getattribute__ = None
112 self._attribs = (__getattribute__, __getattr__)
113 # /Dark magic
114
115 def __exit__(
116 self,
117 exc_type: type[BaseException] | None,
118 exc_val: BaseException | None,
119 exc_tb: TracebackType | None,
120 ) -> Literal[False]:
121 """Restore an object's magic methods."""
122 type_ = type(self._obj)
123 __getattribute__, __getattr__ = self._attribs
124 # Dark magic:
125 if __getattribute__ is not None:
126 setattr(type_, "__getattribute__", __getattribute__)
127 if __getattr__ is not None:
128 setattr(type_, "__getattr__", __getattr__)
129 # /Dark magic
130 return False
131
132
133def parsekeywordpairs(signature: str) -> dict[str, str]:

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected