Coverage for sources/classcore/utilities.py: 100%
44 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-08 03:00 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-08 03:00 +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 . import __
27def describe_object( obj: object ) -> str:
28 ''' Returns object type with fully-qualified name. '''
29 if __.inspect.isclass( obj ):
30 return "class '{}'".format( qualify_class_name( obj ) )
31 # TODO? functions, methods, etc...
32 return "instance of {}".format( describe_object( type( obj ) ) )
35def getattr0( obj: object, name: str, default: __.typx.Any ) -> __.typx.Any:
36 ''' Returns private attribute from object.
38 Uses mangled attribute name which is unique to the class.
39 '''
40 name_m = mangle_name( obj, name )
41 return getattr( obj, name_m, default )
44def delattr0( obj: object, name: str ) -> None:
45 ''' Deletes private attribute on object.
47 Uses mangled attribute name which is unique to the class.
48 '''
49 name_m = mangle_name( obj, name )
50 delattr( obj, name_m )
53def setattr0( obj: object, name: str, value: __.typx.Any ) -> None:
54 ''' Assigns private attribute to object.
56 Uses mangled attribute name which is unique to the class.
57 '''
58 name_m = mangle_name( obj, name )
59 setattr( obj, name_m, value )
62def mangle_name( obj: object, name: str ) -> str:
63 ''' Mangles attribute name so that it is unique.
65 Effectively provides name of private member attribute,
66 which is unique across class inheritance.
67 '''
68 if not __.inspect.isclass( obj ):
69 return mangle_name( type( obj ), name )
70 namehash = __.hashlib.sha256( )
71 namehash.update( qualify_class_name( obj ).encode( ) )
72 namehash_hex = namehash.hexdigest( )
73 return f"{name}_{namehash_hex}"
76def qualify_class_name( cls: type ) -> str:
77 ''' Returns fully-qualified class name. '''
78 return f"{cls.__module__}.{cls.__qualname__}"
81def repair_class_reproduction( original: type, reproduction: type ) -> None:
82 ''' Repairs a class reproduction, if necessary. '''
83 match __.platform.python_implementation( ):
84 case 'CPython': # pragma: no branch
85 _repair_cpython_class_closures( original, reproduction )
86 case _: pass # pragma: no cover
89def _repair_cpython_class_closures(
90 original: type, reproduction: type
91) -> None:
92 # Adapted from https://github.com/python/cpython/pull/124455/files
93 def try_repair_closure(
94 function: __.cabc.Callable[ ..., __.typx.Any ]
95 ) -> bool:
96 try: index = function.__code__.co_freevars.index( '__class__' )
97 except ValueError: return False
98 if not function.__closure__: return False # pragma: no branch
99 closure = function.__closure__[ index ]
100 if original is closure.cell_contents: # pragma: no branch
101 closure.cell_contents = reproduction
102 return True
103 return False # pragma: no cover
105 for attribute in reproduction.__dict__.values( ):
106 attribute_ = __.inspect.unwrap( attribute )
107 if ( __.inspect.isfunction( attribute_ )
108 and try_repair_closure( attribute_ )
109 ): return
110 if isinstance( attribute_, property ):
111 for aname in ( 'fget', 'fset', 'fdel' ):
112 accessor = getattr( attribute_, aname )
113 if None is accessor: continue
114 if try_repair_closure( accessor ): return