@@ -19,7 +19,7 @@ import sys
1919class DotNetLoader(importlib.abc.Loader):
2020
2121 def __init__(self):
22- super(DotNetLoader, self ).__init__()
22+ super().__init__()
2323
2424 @classmethod
2525 def exec_module(klass, mod):
@@ -29,21 +29,19 @@ def exec_module(klass, mod):
2929 @classmethod
3030 def create_module(klass, spec):
3131 import clr
32- return clr._LoadClrModule (spec)
32+ return clr._load_clr_module (spec)
3333
3434class DotNetFinder(importlib.abc.MetaPathFinder):
3535
3636 def __init__(self):
37- super(DotNetFinder, self ).__init__()
37+ super().__init__()
3838
3939 @classmethod
4040 def find_spec(klass, fullname, paths=None, target=None):
4141 import clr
42- if (hasattr( clr, ' _availableNamespaces') and fullname in clr._availableNamespaces) :
42+ if clr. _availableNamespaces and fullname in clr._availableNamespaces:
4343 return importlib.machinery.ModuleSpec(fullname, DotNetLoader(), is_package=True)
4444 return None
45-
46- sys.meta_path.append(DotNetFinder())
4745 " ;
4846 const string availableNsKey = "_availableNamespaces" ;
4947
@@ -69,7 +67,7 @@ internal static unsafe void Initialize()
6967
7068 // Add/create the MetaPathLoader
7169 SetupNamespaceTracking ( ) ;
72- PythonEngine . Exec ( LoaderCode ) ;
70+ SetupImportHook ( ) ;
7371 }
7472
7573
@@ -114,13 +112,66 @@ internal static void RestoreRuntimeData(RuntimeDataStorage storage)
114112 SetupNamespaceTracking ( ) ;
115113 }
116114
115+ static void SetupImportHook ( )
116+ {
117+ // Create the import hook module
118+ var import_hook_module_def = ModuleDefOffset . AllocModuleDef ( "clr.loader" ) ;
119+ var import_hook_module = Runtime . PyModule_Create2 ( import_hook_module_def , 3 ) ;
120+
121+ // Run the python code to create the module's classes.
122+ var mod_dict = Runtime . PyModule_GetDict ( new BorrowedReference ( import_hook_module ) ) ;
123+ var builtins = Runtime . PyEval_GetBuiltins ( ) ;
124+ var exec = Runtime . PyDict_GetItemString ( builtins , "exec" ) ;
125+ using var args = NewReference . DangerousFromPointer ( Runtime . PyTuple_New ( 2 ) ) ;
126+
127+ var codeStr = Runtime . PyString_FromString ( LoaderCode ) ;
128+ Runtime . PyTuple_SetItem ( args . DangerousGetAddress ( ) , 0 , codeStr ) ;
129+ // PyTuple_SetItem steals a reference, mod_dict is borrowed.
130+ Runtime . XIncref ( mod_dict . DangerousGetAddress ( ) ) ;
131+ Runtime . PyTuple_SetItem ( args . DangerousGetAddress ( ) , 1 , mod_dict . DangerousGetAddress ( ) ) ;
132+ Runtime . PyObject_Call ( exec . DangerousGetAddress ( ) , args . DangerousGetAddress ( ) , IntPtr . Zero ) ;
133+
134+ var loader = Runtime . PyDict_GetItemString ( mod_dict , "DotNetLoader" ) . DangerousGetAddressOrNull ( ) ;
135+ Runtime . XIncref ( loader ) ;
136+
137+ // Add the classes to the module
138+ // PyModule_AddObject steals a reference only on success
139+ if ( Runtime . PyModule_AddObject ( import_hook_module , "DotNetLoader" , loader ) != 0 )
140+ {
141+ Runtime . XDecref ( loader ) ;
142+ throw new PythonException ( ) ;
143+ }
144+
145+ var finder = Runtime . PyDict_GetItemString ( mod_dict , "DotNetFinder" ) . DangerousGetAddressOrNull ( ) ;
146+ Runtime . XIncref ( finder ) ;
147+ if ( Runtime . PyModule_AddObject ( import_hook_module , "DotNetFinder" , finder ) != 0 )
148+ {
149+ Runtime . XDecref ( finder ) ;
150+ throw new PythonException ( ) ;
151+ }
152+
153+ // Set as a sub-module of clr.
154+ Runtime . XIncref ( import_hook_module ) ;
155+ if ( Runtime . PyModule_AddObject ( py_clr_module , "loader" , import_hook_module ) != 0 )
156+ {
157+ Runtime . XDecref ( import_hook_module ) ;
158+ throw new PythonException ( ) ;
159+ }
160+
161+ // Finally, add the hook to the meta path
162+ var finder_inst = Runtime . PyDict_GetItemString ( mod_dict , "finder_inst" ) . DangerousGetAddressOrNull ( ) ;
163+ Runtime . XIncref ( finder ) ;
164+ var metapath = Runtime . PySys_GetObject ( "meta_path" ) ;
165+ Runtime . PyList_Append ( metapath , finder ) ;
166+ }
167+
117168 /// <summary>
118169 /// Sets up the tracking of loaded namespaces. This makes available to
119170 /// Python, as a Python object, the loaded namespaces. The set of loaded
120171 /// namespaces is used during the import to verify if we can import a
121172 /// CLR assembly as a module or not. The set is stored on the clr module.
122173 /// </summary>
123- static void SetupNamespaceTracking ( )
174+ static void SetupNamespaceTracking ( )
124175 {
125176 var newset = Runtime . PySet_New ( new BorrowedReference ( IntPtr . Zero ) ) ;
126177 try
@@ -130,7 +181,7 @@ static void SetupNamespaceTracking ()
130181 var pyNs = Runtime . PyString_FromString ( ns ) ;
131182 try
132183 {
133- if ( Runtime . PySet_Add ( newset , new BorrowedReference ( pyNs ) ) != 0 )
184+ if ( Runtime . PySet_Add ( newset , new BorrowedReference ( pyNs ) ) != 0 )
134185 {
135186 throw new PythonException ( ) ;
136187 }
@@ -141,7 +192,7 @@ static void SetupNamespaceTracking ()
141192 }
142193 }
143194
144- if ( Runtime . PyDict_SetItemString ( root . dict , availableNsKey , newset . DangerousGetAddress ( ) ) != 0 )
195+ if ( Runtime . PyDict_SetItemString ( root . dict , availableNsKey , newset . DangerousGetAddress ( ) ) != 0 )
145196 {
146197 throw new PythonException ( ) ;
147198 }
@@ -152,7 +203,7 @@ static void SetupNamespaceTracking ()
152203 }
153204
154205 AssemblyManager . namespaceAdded += OnNamespaceAdded ;
155- PythonEngine . AddShutdownHandler ( ( ) => AssemblyManager . namespaceAdded -= OnNamespaceAdded ) ;
206+ PythonEngine . AddShutdownHandler ( ( ) => AssemblyManager . namespaceAdded -= OnNamespaceAdded ) ;
156207 }
157208
158209 /// <summary>
@@ -162,27 +213,21 @@ static void SetupNamespaceTracking ()
162213 static void TeardownNameSpaceTracking ( )
163214 {
164215 AssemblyManager . namespaceAdded -= OnNamespaceAdded ;
165- // If the C# runtime isn't loaded, then there is no namespaces available
166- if ( ( Runtime . PyDict_DelItemString ( new BorrowedReference ( root . dict ) , availableNsKey ) != 0 ) &&
167- ( Exceptions . ExceptionMatches ( Exceptions . KeyError ) ) )
168- {
169- // Trying to remove a key that's not in the dictionary
170- // raises an error. We don't care about it.
171- Runtime . PyErr_Clear ( ) ;
172- }
216+ // If the C# runtime isn't loaded, then there are no namespaces available
217+ Runtime . PyDict_SetItemString ( root . dict , availableNsKey , Runtime . PyNone ) ;
173218 }
174219
175- static void OnNamespaceAdded ( string name )
220+ static void OnNamespaceAdded ( string name )
176221 {
177- using ( Py . GIL ( ) )
222+ using ( Py . GIL ( ) )
178223 {
179224 var pyNs = Runtime . PyString_FromString ( name ) ;
180225 try
181226 {
182227 var nsSet = Runtime . PyDict_GetItemString ( new BorrowedReference ( root . dict ) , availableNsKey ) ;
183- if ( ! nsSet . IsNull )
228+ if ( ! nsSet . IsNull || nsSet . DangerousGetAddress ( ) != Runtime . PyNone )
184229 {
185- if ( Runtime . PySet_Add ( nsSet , new BorrowedReference ( pyNs ) ) != 0 )
230+ if ( Runtime . PySet_Add ( nsSet , new BorrowedReference ( pyNs ) ) != 0 )
186231 {
187232 throw new PythonException ( ) ;
188233 }
@@ -225,7 +270,7 @@ public static unsafe NewReference GetCLRModule()
225270 /// <summary>
226271 /// The hook to import a CLR module into Python
227272 /// </summary>
228- public static ModuleObject __import__ ( string modname )
273+ public static ModuleObject Import ( string modname )
229274 {
230275 // Traverse the qualified module name to get the named module.
231276 // Note that if
0 commit comments