Coverage for sources/classcore/utilities.py: 100%
35 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-29 23:23 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-29 23:23 +0000
1# vim: set filetype=python fileencoding=utf-8:
2# -*- coding: utf-8 -*-
4#============================================================================#
5# #
6# Licensed under the Apache License, Version 2.0 (the "License"); #
7# you may not use this file except in compliance with the License. #
8# You may obtain a copy of the License at #
9# #
10# http://www.apache.org/licenses/LICENSE-2.0 #
11# #
12# Unless required by applicable law or agreed to in writing, software #
13# distributed under the License is distributed on an "AS IS" BASIS, #
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
15# See the License for the specific language governing permissions and #
16# limitations under the License. #
17# #
18#============================================================================#
21''' Various utilities for class manipulation. '''
24from __future__ import annotations
26from . import __
29def describe_object( obj: object ) -> str:
30 if __.inspect.isclass( obj ):
31 return "class '{}'".format( qualify_class_name( obj ) )
32 # TODO? functions, methods, etc...
33 return "instance of {}".format( describe_object( type( obj ) ) )
36def getattr0( obj: object, name: str, default: __.typx.Any ) -> __.typx.Any:
37 ''' Returns attribute from object without inheritance. '''
38 # Inspect object dictionary directly to suppress getattr inheritance.
39 attrsdict = getattr( obj, '__dict__', { } )
40 if name in attrsdict: return attrsdict[ name ]
41 slots = getattr( obj, '__slots__', ( ) )
42 # Name may be in slots but not yet assigned.
43 if name in slots: return getattr( obj, name, default )
44 return default
47def qualify_class_name( cls: type ) -> str:
48 return f"{cls.__module__}.{cls.__qualname__}"
51def repair_class_reproduction( original: type, reproduction: type ) -> None:
52 ''' Repairs a class reproduction, if necessary. '''
53 match __.platform.python_implementation( ):
54 case 'CPython': # pragma: no branch
55 _repair_cpython_class_closures( original, reproduction )
56 case _: pass # pragma: no cover
59def _repair_cpython_class_closures(
60 original: type, reproduction: type
61) -> None:
62 # Adapted from https://github.com/python/cpython/pull/124455/files
63 def try_repair_closure(
64 function: __.cabc.Callable[ ..., __.typx.Any ]
65 ) -> bool:
66 try: index = function.__code__.co_freevars.index( '__class__' )
67 except ValueError: return False
68 if not function.__closure__: return False # pragma: no branch
69 closure = function.__closure__[ index ]
70 if original is closure.cell_contents: # pragma: no branch
71 closure.cell_contents = reproduction
72 return True
73 return False # pragma: no cover
75 for attribute in reproduction.__dict__.values( ):
76 attribute_ = __.inspect.unwrap( attribute )
77 if ( __.inspect.isfunction( attribute_ )
78 and try_repair_closure( attribute_ )
79 ): return
80 if isinstance( attribute_, property ):
81 for aname in ( 'fget', 'fset', 'fdel' ):
82 accessor = getattr( attribute_, aname )
83 if None is accessor: continue
84 if try_repair_closure( accessor ): return