Skip to content

[Bug]: incompatible (wrt CPython 3.12) signatures for some integer math functions and int's methods #998

Open

Description

@skirpichev

Describe the bug

Python 3.12.13 (tags/v3.12.13:3bb231a6a5d, May 14 2026, 11:06:30) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import math, inspect
>>> for fn in ["comb", "factorial", "gcd", "isqrt", "lcm", "perm"]:
...     f = getattr(math, fn)
...     print(f, inspect.signature(f))
...
<built-in function comb> (n, k, /)
<built-in function factorial> (n, /)
<built-in function gcd> (*integers)
<built-in function isqrt> (n, /)
<built-in function lcm> (*integers)
<built-in function perm> (n, k=None, /)
>>> for meth in dir(int):
...     m = getattr(int, meth)
...     if meth.startswith("_") or not callable(m):
...         continue
...     try:
...         print(meth, inspect.signature(m))
...     except ValueError:
...         pass
...
as_integer_ratio (self, /)
bit_count (self, /)
bit_length (self, /)
from_bytes (bytes, byteorder='big', *, signed=False)
is_integer (self, /)
to_bytes (self, /, length=1, byteorder='big', *, signed=False)

In the GraalPy instead:

Python 3.12.8 (Tue Jun 23 15:17:16 UTC 2026)
[Graal, GraalVM CE, Java 25.0.3 (amd64)] on 'linux'
Type "help", "copyright", "credits" or "license" for more information.
>>> import math, inspect
>>> for fn in ["comb", "factorial", "gcd", "isqrt", "lcm", "perm"]:
...     f = getattr(math, fn)
...     print(f, inspect.signature(f))
...
<built-in function comb> (a, b, /)
<built-in function factorial> (a, /)
<built-in function gcd> (*args, **kwargs)
<built-in function isqrt> (a, /)
<built-in function lcm> (*args, **kwargs)
<built-in function perm> (n, k, /)
>>> for meth in dir(int):
...      m = getattr(int, meth)
...      if meth.startswith("_") or not callable(m):
...          continue
...      try:
...          print(meth, inspect.signature(m))
...      except ValueError:
...          pass
... 
as_integer_ratio (self, /)
bit_count (self, /)
bit_length (self, /)
conjugate (self, /)
from_bytes (bytes, byteorder, /, *, signed)
is_integer (self, /)
to_bytes (self, length, byteorder, /, *, signed)

As you can see, all present function signatures from the math module are different. Of course, most functions have positional-only arguments and naming differences aren't significant. But differences for gcd, lcm and perm - are. Same true for to_bytes/from_bytes methods: they lack default values and incorrectly specify positional-only arguments.

Though, I would appreciate if signatures will be fully compatible, including naming of positional-only parameters. The reason is that such naming is used in the Python prose docs, e.g. https://docs.python.org/3.12/library/math.html#math.comb. The GraalPy has no documentation like this for the stdlib.

Operating system

Linux

CPU architecture

x86_64

GraalPy version

25.1.3

JDK version

No response

Context configuration

No response

Steps to reproduce

See bug description.

Expected behavior

Match CPython.

Stack trace

Additional context

No response

Activity

  1. skirpichev commented on Nov 4, 2026

    @skirpichev
    Author

    It seems that some cases could be easily solved by attached patch.

    Details
    diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathModuleBuiltins.java
    index 029f6ac3cf..1b53c2ceb9 100644
    --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathModuleBuiltins.java
    +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathModuleBuiltins.java
    @@ -363,7 +363,7 @@ public final class MathModuleBuiltins extends PythonBuiltins {
             }
         }
     
    -    @Builtin(name = "factorial", minNumOfPositionalArgs = 1)
    +    @Builtin(name = "factorial", minNumOfPositionalArgs = 1, parameterNames = {"n"})
         @GenerateNodeFactory
         public abstract static class FactorialNode extends PythonUnaryBuiltinNode {
             public abstract Object execute(VirtualFrame frame, long value);
    @@ -443,7 +443,7 @@ public final class MathModuleBuiltins extends PythonBuiltins {
     
         }
     
    -    @Builtin(name = "comb", minNumOfPositionalArgs = 2)
    +    @Builtin(name = "comb", minNumOfPositionalArgs = 2, parameterNames = {"n", "k"})
         @TypeSystemReference(PythonIntegerAndFloatTypes.class)
         @GenerateNodeFactory
         @ImportStatic(MathGuards.class)
    @@ -948,7 +948,7 @@ public final class MathModuleBuiltins extends PythonBuiltins {
             }
         }
     
    -    @Builtin(name = "gcd", minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true, declaresExplicitSelf = true)
    +    @Builtin(name = "gcd", minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = false, declaresExplicitSelf = true)
         @GenerateNodeFactory
         public abstract static class GcdNode extends PythonVarargsBuiltinNode {
     
    @@ -1092,7 +1092,7 @@ public final class MathModuleBuiltins extends PythonBuiltins {
             }
         }
     
    -    @Builtin(name = "lcm", minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true, declaresExplicitSelf = true)
    +    @Builtin(name = "lcm", minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = false, declaresExplicitSelf = true)
         @GenerateNodeFactory
         public abstract static class LcmNode extends PythonVarargsBuiltinNode {
     
    @@ -2719,7 +2719,7 @@ public final class MathModuleBuiltins extends PythonBuiltins {
     
         }
     
    -    @Builtin(name = "isqrt", minNumOfPositionalArgs = 1)
    +    @Builtin(name = "isqrt", minNumOfPositionalArgs = 1, parameterNames = {"n"})
         @TypeSystemReference(PythonIntegerAndFloatTypes.class)
         @GenerateNodeFactory
         @ImportStatic(MathGuards.class)

    The rest requires better support for signatures of builtins:

    1. default parameter values
    2. renaming of *args (and **kwargs)
  2. msimacek commented on Nov 4, 2026

    @msimacek
    Contributor

    Thanks. Renaming the args and kwargs seems like a straighforward addition to the current system, but the default parameter values will be tricky, because we can't have python objects in the annotations that we use to declare those. We would need some way to map string values to python objects which will be awkward for things like module constants. And we would need to integrate that with the clinic system we have for argument conversions. I'll need to think about it more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions