inspect: Implement a very basic signature function.

This implements a very basic `inspect.signature()` function.

At the moment it returns only a simple `Signature` instance with a
`parameters` attribute that holds an `OrderedDict` whose length matches the
arity of the input function (the number of arguments it takes).

So, the following code works and is compatible with CPython:

    def f(a, b, *, c):
        pass

    print(len(inspect.signature(f).parameters))

That should print 3.

Signed-off-by: Damien George <damien@micropython.org>
pull/1043/head
Damien George 2025-09-19 13:38:26 +10:00
rodzic bdc4706cc7
commit b4565b41ea
3 zmienionych plików z 71 dodań i 1 usunięć

Wyświetl plik

@ -80,3 +80,60 @@ def currentframe():
def getframeinfo(frame, context=1):
return ("<unknown>", -1, "<unknown>", [""], 0)
class Signature:
pass
# This `signature()` function is very limited. It's main purpose is to work out
# the arity of the given function, ie the number of arguments it takes.
#
# The return value is an instance of `Signature` with a `parameters` member which
# is an OrderedDict whose length is the number of arguments of `f`.
def signature(f):
import collections
import uctypes
s = Signature()
s.parameters = collections.OrderedDict()
t = type(f)
if t is type(globals):
# A zero-parameter built-in.
num_args = 0
elif t is type(abs):
# A one-parameter built-in.
num_args = 1
elif t is type(hasattr):
# A two-parameter built-in.
num_args = 2
elif t is type(setattr):
# A three-parameter built-in.
num_args = 3
elif t is type(signature):
# A bytecode function, work out the number of arguments by inspecting the bytecode data.
fun_obj = uctypes.struct(id(f), (uctypes.ARRAY | 0, uctypes.LONG | 4))
bytecode = uctypes.bytearray_at(fun_obj[3], 8)
# See py/bc.h:MP_BC_PRELUDE_SIG_DECODE_INTO macro.
i = 0
z = bytecode[i]
i += 1
A = z & 0x3
K = 0
n = 0
while z & 0x80:
z = bytecode[i]
i += 1
A |= (z & 0x4) << n
K |= ((z & 0x08) >> 3) << n
num_args = A + K
else:
raise NotImplementedError("unsupported function type")
# Add dummy arguments to the OrderedDict.
for i in range(num_args):
a = "x{}".format(i)
s.parameters[a] = a
return s

Wyświetl plik

@ -1,3 +1,3 @@
metadata(version="0.1.3")
metadata(version="0.2.0")
module("inspect.py")

Wyświetl plik

@ -1,3 +1,4 @@
import collections
import inspect
import unittest
@ -58,3 +59,15 @@ class TestInspect(unittest.TestCase):
def test_ismodule(self):
self._test_is_helper(inspect.ismodule, entities[6])
def test_signature(self):
self.assertEqual(inspect.signature(globals).parameters, collections.OrderedDict())
self.assertEqual(len(inspect.signature(abs).parameters), 1)
self.assertEqual(len(inspect.signature(hasattr).parameters), 2)
self.assertEqual(len(inspect.signature(setattr).parameters), 3)
self.assertEqual(len(inspect.signature(lambda: 0).parameters), 0)
self.assertEqual(len(inspect.signature(lambda x: 0).parameters), 1)
self.assertEqual(len(inspect.signature(lambda *, x: 0).parameters), 1)
self.assertEqual(len(inspect.signature(lambda x, y: 0).parameters), 2)
self.assertEqual(len(inspect.signature(lambda x, y, z: 0).parameters), 3)
self.assertEqual(len(inspect.signature(lambda x, y, *, z: 0).parameters), 3)