| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,8 +15,9 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. | |||
| 15 | 15 | - Ability to implement delegates with `ref` and `out` parameters in Python, by returning the modified parameter values in a tuple. ([#1355][i1355]) | |
| 16 | 16 | - `PyType` - a wrapper for Python type objects, that also permits creating new heap types from `TypeSpec` | |
| 17 | 17 | - Improved exception handling: | |
| 18 | - - exceptions can now be converted with codecs | ||
| 19 | - - `InnerException` and `__cause__` are propagated properly | ||
| 18 | + * exceptions can now be converted with codecs | ||
| 19 | + * `InnerException` and `__cause__` are propagated properly | ||
| 20 | + - `__name__` and `__signature__` to reflected .NET methods | ||
| 20 | 21 | - .NET collection types now implement standard Python collection interfaces from `collections.abc`. | |
| 21 | 22 | See [Mixins/collections.py](src/runtime/Mixins/collections.py). | |
| 22 | 23 | - .NET arrays implement Python buffer protocol | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -30,5 +30,30 @@ public void InstancePropertiesVisibleOnClass() | |||
| 30 | 30 | var pyProp = (PropertyObject)ManagedType.GetManagedObject(property.Reference); | |
| 31 | 31 | Assert.AreEqual(nameof(Uri.AbsoluteUri), pyProp.info.Value.Name); | |
| 32 | 32 | } | |
| 33 | + | ||
| 34 | + [Test] | ||
| 35 | + public void BoundMethodsAreInspectable() | ||
| 36 | + { | ||
| 37 | + using var scope = Py.CreateScope(); | ||
| 38 | + try | ||
| 39 | + { | ||
| 40 | + scope.Import("inspect"); | ||
| 41 | + } | ||
| 42 | + catch (PythonException) | ||
| 43 | + { | ||
| 44 | + Assert.Inconclusive("Python build does not include inspect module"); | ||
| 45 | + return; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + var obj = new Class(); | ||
| 49 | + scope.Set(nameof(obj), obj); | ||
| 50 | + using var spec = scope.Eval($"inspect.getfullargspec({nameof(obj)}.{nameof(Class.Method)})"); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + class Class | ||
| 54 | + { | ||
| 55 | + public void Method(int a, int b = 10) { } | ||
| 56 | + public void Method(int a, object b) { } | ||
| 57 | + } | ||
| 33 | 58 | } | |
| 34 | 59 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -420,6 +420,7 @@ public static variables on the Exceptions class filled in from | |||
| 420 | 420 | public static IntPtr IOError; | |
| 421 | 421 | public static IntPtr OSError; | |
| 422 | 422 | public static IntPtr ImportError; | |
| 423 | + public static IntPtr ModuleNotFoundError; | ||
| 423 | 424 | public static IntPtr IndexError; | |
| 424 | 425 | public static IntPtr KeyError; | |
| 425 | 426 | public static IntPtr KeyboardInterrupt; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,6 @@ | |||
| 1 | 1 | using System; | |
| 2 | 2 | using System.Collections.Generic; | |
| 3 | + using System.Linq; | ||
| 3 | 4 | using System.Reflection; | |
| 4 | 5 | ||
| 5 | 6 | namespace Python.Runtime | |
@@ -65,6 +66,67 @@ public static IntPtr mp_subscript(IntPtr tp, IntPtr idx) | |||
| 65 | 66 | return mb.pyHandle; | |
| 66 | 67 | } | |
| 67 | 68 | ||
| 69 | + PyObject Signature | ||
| 70 | + { | ||
| 71 | + get | ||
| 72 | + { | ||
| 73 | + var infos = this.info.Valid ? new[] { this.info.Value } : this.m.info; | ||
| 74 | + Type type = infos.Select(i => i.DeclaringType) | ||
| 75 | + .OrderByDescending(t => t, new TypeSpecificityComparer()) | ||
| 76 | + .First(); | ||
| 77 | + infos = infos.Where(info => info.DeclaringType == type).ToArray(); | ||
| 78 | + // this is a primitive version | ||
| 79 | + // the overload with the maximum number of parameters should be used | ||
| 80 | + MethodInfo primary = infos.OrderByDescending(i => i.GetParameters().Length).First(); | ||
| 81 | + var primaryParameters = primary.GetParameters(); | ||
| 82 | + PyObject signatureClass = Runtime.InspectModule.GetAttr("Signature"); | ||
| 83 | + var primaryReturn = primary.ReturnParameter; | ||
| 84 | + | ||
| 85 | + using var parameters = new PyList(); | ||
| 86 | + using var parameterClass = primaryParameters.Length > 0 ? Runtime.InspectModule.GetAttr("Parameter") : null; | ||
| 87 | + using var positionalOrKeyword = parameterClass?.GetAttr("POSITIONAL_OR_KEYWORD"); | ||
| 88 | + for (int i = 0; i < primaryParameters.Length; i++) | ||
| 89 | + { | ||
| 90 | + var parameter = primaryParameters[i]; | ||
| 91 | + var alternatives = infos.Select(info => | ||
| 92 | + { | ||
| 93 | + ParameterInfo[] altParamters = info.GetParameters(); | ||
| 94 | + return i < altParamters.Length ? altParamters[i] : null; | ||
| 95 | + }).Where(p => p != null); | ||
| 96 | + using var defaultValue = alternatives | ||
| 97 | + .Select(alternative => alternative.DefaultValue != DBNull.Value ? alternative.DefaultValue.ToPython() : null) | ||
| 98 | + .FirstOrDefault(v => v != null) ?? parameterClass.GetAttr("empty"); | ||
| 99 | + | ||
| 100 | + if (alternatives.Any(alternative => alternative.Name != parameter.Name)) | ||
| 101 | + { | ||
| 102 | + return signatureClass.Invoke(); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + using var args = new PyTuple(new[] { parameter.Name.ToPython(), positionalOrKeyword }); | ||
| 106 | + using var kw = new PyDict(); | ||
| 107 | + if (defaultValue is not null) | ||
| 108 | + { | ||
| 109 | + kw["default"] = defaultValue; | ||
| 110 | + } | ||
| 111 | + using var parameterInfo = parameterClass.Invoke(args: args, kw: kw); | ||
| 112 | + parameters.Append(parameterInfo); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + // TODO: add return annotation | ||
| 116 | + return signatureClass.Invoke(parameters); | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + struct TypeSpecificityComparer : IComparer<Type> | ||
| 121 | + { | ||
| 122 | + public int Compare(Type a, Type b) | ||
| 123 | + { | ||
| 124 | + if (a == b) return 0; | ||
| 125 | + if (a.IsSubclassOf(b)) return 1; | ||
| 126 | + if (b.IsSubclassOf(a)) return -1; | ||
| 127 | + throw new NotSupportedException(); | ||
| 128 | + } | ||
| 129 | + } | ||
| 68 | 130 | ||
| 69 | 131 | /// <summary> | |
| 70 | 132 | /// MethodBinding __getattribute__ implementation. | |
@@ -91,6 +153,15 @@ public static IntPtr tp_getattro(IntPtr ob, IntPtr key) | |||
| 91 | 153 | case "Overloads": | |
| 92 | 154 | var om = new OverloadMapper(self.m, self.target); | |
| 93 | 155 | return om.pyHandle; | |
| 156 | + case "__signature__" when Runtime.InspectModule is not null: | ||
| 157 | + var sig = self.Signature; | ||
| 158 | + if (sig is null) | ||
| 159 | + { | ||
| 160 | + return Runtime.PyObject_GenericGetAttr(ob, key); | ||
| 161 | + } | ||
| 162 | + return sig.NewReferenceOrNull().DangerousMoveToPointerOrNull(); | ||
| 163 | + case "__name__": | ||
| 164 | + return self.m.GetName().DangerousMoveToPointerOrNull(); | ||
| 94 | 165 | default: | |
| 95 | 166 | return Runtime.PyObject_GenericGetAttr(ob, key); | |
| 96 | 167 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,7 +1,7 @@ | |||
| 1 | 1 | using System; | |
| 2 | 2 | using System.Collections.Generic; | |
| 3 | - using System.Reflection; | ||
| 4 | 3 | using System.Linq; | |
| 4 | + using System.Reflection; | ||
| 5 | 5 | ||
| 6 | 6 | namespace Python.Runtime | |
| 7 | 7 | { | |
@@ -101,6 +101,16 @@ internal IntPtr GetDocString() | |||
| 101 | 101 | return doc; | |
| 102 | 102 | } | |
| 103 | 103 | ||
| 104 | + internal NewReference GetName() | ||
| 105 | + { | ||
| 106 | + var names = new HashSet<string>(binder.GetMethods().Select(m => m.Name)); | ||
| 107 | + if (names.Count != 1) { | ||
| 108 | + Exceptions.SetError(Exceptions.AttributeError, "a method has no name"); | ||
| 109 | + return default; | ||
| 110 | + } | ||
| 111 | + return NewReference.DangerousFromPointer(Runtime.PyString_FromString(names.First())); | ||
| 112 | + } | ||
| 113 | + | ||
| 104 | 114 | ||
| 105 | 115 | /// <summary> | |
| 106 | 116 | /// This is a little tricky: a class can actually have a static method | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -178,6 +178,7 @@ internal static void Initialize(bool initSigs = false, ShutdownMode mode = Shutd | |||
| 178 | 178 | AssemblyManager.UpdatePath(); | |
| 179 | 179 | ||
| 180 | 180 | clrInterop = GetModuleLazy("clr.interop"); | |
| 181 | + inspect = GetModuleLazy("inspect"); | ||
| 181 | 182 | } | |
| 182 | 183 | ||
| 183 | 184 | private static void InitPyMembers() | |
@@ -573,6 +574,8 @@ private static void MoveClrInstancesOnwershipToPython() | |||
| 573 | 574 | internal static IntPtr PyNone; | |
| 574 | 575 | internal static IntPtr Error; | |
| 575 | 576 | ||
| 577 | + private static Lazy<PyObject> inspect; | ||
| 578 | + internal static PyObject InspectModule => inspect.Value; | ||
| 576 | 579 | private static Lazy<PyObject> clrInterop; | |
| 577 | 580 | internal static PyObject InteropModule => clrInterop.Value; | |
| 578 | 581 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments