Conversation
200ba3d to
f502b8e
Compare
src/runtime/importhook.cs
Outdated
| var finder_inst = Runtime.PyDict_GetItemString(mod_dict, "finder_inst").DangerousGetAddressOrNull(); | ||
| Runtime.XIncref(finder); | ||
| var metapath = Runtime.PySys_GetObject("meta_path"); | ||
| Runtime.PyList_Append(metapath, finder); |
There was a problem hiding this comment.
These four lines look fishy: you don't use finder_inst and you are doing an incref on finder for no obvious reason.
There was a problem hiding this comment.
oh.. that should be finder_inst, not finder
There was a problem hiding this comment.
BTW, it makes sense to add an overload for PyList_Append with proper reference types, then avoid manual refcounting.
Same for PySys_GetObject - you can rename the original one to Dangerous_PySys_GetObject to avoid refactoring other call sites.
This is a NIT though. Should come up with a better plan to switch everything.
src/runtime/importhook.cs
Outdated
| BorrowedReference fromList = default; | ||
| var fromlist = false; | ||
| if (num_args >= 4) | ||
| var newset = Runtime.PySet_New(new BorrowedReference(IntPtr.Zero)); |
There was a problem hiding this comment.
You Dispose in a finally; should this be a using var ?
There was a problem hiding this comment.
NIT: I'd replace new BorrowedReference(IntPtr.Zero) with either BorrowedReference.Null or simply default.
src/runtime/importhook.cs
Outdated
| { | ||
| if (IsLoadAll(fromList)) | ||
| var nsSet = Runtime.PyDict_GetItemString(new BorrowedReference(root.dict), availableNsKey); | ||
| if (!nsSet.IsNull || nsSet.DangerousGetAddress() != Runtime.PyNone) |
There was a problem hiding this comment.
Should BorrowedReference have an IsNullOrNone property? It seems likely to be a common idiom.
There was a problem hiding this comment.
I'd keep separate IsNull and IsNone. Thought there's already IsNone.
0f1219b to
f9a3974
Compare
Implement a meta path loader instead
This reverts commit 4684523.
* Return ModuleObject.pyHandle, do not convert. * Write domain tests generated code to file.
2860632 to
970a189
Compare
Don't piggyback on AssemblyManager's AssemblyLoadHandler method because it may be called from other threads, leading to deadlocks if it is called while Python code is executing
src/runtime/BorrowedReference.cs
Outdated
| { | ||
| readonly IntPtr pointer; | ||
| public bool IsNull => this.pointer == IntPtr.Zero; | ||
| public bool IsNone => this.pointer == Runtime.PyNone; |
There was a problem hiding this comment.
This should not be directly on BorrowedReference. None is a concept of the currently running runtime, and BorrowedReference might be a valid non-None reference from the previous runtime incarnation.
src/runtime/importhook.cs
Outdated
| var rootHandle = storage.GetValue<IntPtr>("root"); | ||
| root = (CLRModule)ManagedType.GetManagedObject(rootHandle); | ||
| BorrowedReference dict = Runtime.PyImport_GetModuleDict(); | ||
| Runtime.PyDict_SetItemString(dict.DangerousGetAddress(), "clr", py_clr_module); |
There was a problem hiding this comment.
I'd add a property ClrModuleReference that returns a BorrowedReference of py_clr_module, and then used it in PyDict_SetItemString overload, that works with BorrowedReferences to avoid using Dangerous* methods.
src/runtime/importhook.cs
Outdated
| var mod_dict = Runtime.PyModule_GetDict(import_hook_module); | ||
| Runtime.XIncref(mod_dict.DangerousGetAddress()); | ||
| Runtime.PyTuple_SetItem(args.DangerousGetAddress(), 1, mod_dict.DangerousGetAddress()); | ||
| Runtime.PyObject_Call(exec.DangerousGetAddress(), args.DangerousGetAddress(), IntPtr.Zero); |
There was a problem hiding this comment.
It is better to make an overload with the new reference types when possible without rewriting existing code, than to call Dangerous* methods. See for example
pythonnet/src/runtime/runtime.cs
Line 996 in 7d8f754
One can also be made for PyTuple_SetItem using the new StolenReference.
There was a problem hiding this comment.
This applies to a few Runtime methods across the PR. Basically anything where you can add a new overload.
src/runtime/importhook.cs
Outdated
| /// </summary> | ||
| static void SetupNamespaceTracking() | ||
| { | ||
| var newset = Runtime.PySet_New(default); |
There was a problem hiding this comment.
using var newset = ... would remove the need for try ... finally ...
src/runtime/moduleobject.cs
Outdated
| var pyname = Runtime.PyString_FromString(name); | ||
| try | ||
| { | ||
| if (Runtime.PyList_Append(new BorrowedReference(__all__), pyname) != 0) |
There was a problem hiding this comment.
Frankly, I screwed up PyList_Append. Its only overload schizophrenically mixes references and raw pointers. Can you change it to either have two overloads with only references and with only pointers, or just one that takes only references?
There was a problem hiding this comment.
There are only a few calls to PyList_Append throughout the code base, I changed all of them to use only references.
| /// This is needed because the import mechanics need | ||
| /// to set a few attributes | ||
| /// </summary> | ||
| [ForbidPythonThreads] |
There was a problem hiding this comment.
Why do you have to forbid threads here?
There was a problem hiding this comment.
Because there's a call to Runtime.PyDict_SetItem. I might be missing something and this is always called from CPython and the GIL is guaranteed to be held.
| ModuleObject mod = null; | ||
| using (var modname = spec.GetAttr("name")) | ||
| { | ||
| mod = ImportHook.Import(modname.ToString()); | ||
| } | ||
| return mod; | ||
| } |
There was a problem hiding this comment.
NIT:
using var modname = ...;
return ...;
src/runtime/runtime.cs
Outdated
|
|
||
|
|
||
| internal static IntPtr PyImport_Import(IntPtr name) => Delegates.PyImport_Import(name); | ||
| internal static int PyModule_AddObject(BorrowedReference module, string name, BorrowedReference stolenObject) |
There was a problem hiding this comment.
Looks like stolenObject parameter should be of type StolenReference here and below (present in the latest master).
There was a problem hiding this comment.
StolenReference here and below (present in the latest master)
🎉
There was a problem hiding this comment.
Although I'm reviewing the semantics, and this function only steals on success, so by using StolenReference, we leak on failure.
There was a problem hiding this comment.
Dang you are right. Why did they not make it consistent? PyTuple_SetItem for example does steal even when there's an error. ugh...
Maybe we should make a wrapper, that always steals?
There was a problem hiding this comment.
I think that we should not use BorrowedReference for the last parameter here. It makes false suggestion, that signature is valid, and borrowed reference is always enough when it is really not. I'd keep it IntPtr for now, and explain the behavior in the parameter doc.
Or alternatively, make a wrapper, that takes either BorrowedReference or StolenReference and handles refcounting internally, and keep the real PInvoke private.
There was a problem hiding this comment.
I'm leaning towards the IntPtr parameter to be as "consistant" as possible with the CPython API.
src/runtime/converter.cs
Outdated
| // pyHandle as is, do not convert. | ||
| if (value is ModuleObject modobj) | ||
| { | ||
| return modobj.pyHandle; |
There was a problem hiding this comment.
You need to return a new reference here.
There was a problem hiding this comment.
My bad missed that one.
src/runtime/runtime.cs
Outdated
|
|
||
|
|
||
| internal static IntPtr PyImport_Import(IntPtr name) => Delegates.PyImport_Import(name); | ||
| internal static int PyModule_AddObject(BorrowedReference module, string name, BorrowedReference stolenObject) |
There was a problem hiding this comment.
I think that we should not use BorrowedReference for the last parameter here. It makes false suggestion, that signature is valid, and borrowed reference is always enough when it is really not. I'd keep it IntPtr for now, and explain the behavior in the parameter doc.
Or alternatively, make a wrapper, that takes either BorrowedReference or StolenReference and handles refcounting internally, and keep the real PInvoke private.
|
I'll review this today. |
|
great news! :) |
What does this implement/fix? Explain your changes.
This replaces the current import hook replacement with a MetaPathFinder, letting python handle non-CLR module import.
Does this close any currently open issues?
#727 #1245 #547 #111 and probably others.
Any other comments?
This fix also helps working around a mono runtime behaviour where a mono owns all threads that execute .NET code.
Checklist
Check all those that are applicable and complete.
Make sure to include one or more tests for your change
If an enhancement PR, please create docs and at best an example
Add yourself to AUTHORS
Updated the CHANGELOG