Skip to content

GH-137630: Convert _interpreters to use Argument Clinic #137631

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 27 commits into
base: main
Choose a base branch
from

Conversation

AA-Turner
Copy link
Member

@AA-Turner AA-Turner commented Aug 11, 2025

cc @ericsnowcurrently @serhiy-storchaka

Changes split up into commits, one per module-level function. If this PR is too large, happy to break it up into smaller pieces. The only unconverted function is _interpreters.new_config(), which AC does't support.

The pydoc diff is below.

$ diff -u --color pydoc-interpreters-HEAD.txt pydoc-interpreters-AC.txt
--- pydoc-interpreters-HEAD.txt 2025-08-11 05:05:56.067834200 +0100
+++ pydoc-interpreters-AC.txt   2025-08-11 05:07:05.140207800 +0100
@@ -185,23 +185,18 @@
      |  args

 FUNCTIONS
-    call(...)
-        call(id, callable, args=None, kwargs=None, *, restrict=False)
-
+    call(id, /, callable, args=(), kwargs={}, *, preserve_exc=False, restrict=False)
         Call the provided object in the identified interpreter.
-        Pass the given args and kwargs, if possible.

-    capture_exception(...)
-        capture_exception(exc=None) -> types.SimpleNamespace
+        Pass the given args and kwargs, if possible.

-        Return a snapshot of an exception.  If "exc" is None
-        then the current exception, if any, is used (but not cleared).
+    capture_exception(exc_arg=None, /)
+        Return a snapshot of an exception.

+        If *exc* is None then the current exception, if any, is used (but not cleared).
         The returned snapshot is the same as what _interpreters.exec() returns.

-    create(...)
-        create([config], *, reqrefs=False) -> ID
-
+    create(config='isolated', /, *, reqrefs=False)
         Create a new interpreter and return a unique generated ID.

         The caller is responsible for destroying the interpreter before exiting,
@@ -209,29 +204,26 @@
         automatically by passing "reqrefs=True" and then using _incref() and
         _decref() appropriately.

-        "config" must be a valid interpreter config or the name of a
-        predefined config ("isolated" or "legacy").  The default
-        is "isolated".
-
-    decref(...)
+        *config* must be a valid interpreter config or the name of a
+        predefined config ('isolated' or 'legacy').  The default
+        is 'isolated'.

-    destroy(...)
-        destroy(id, *, restrict=False)
+    decref(id, /, *, restrict=False)

+    destroy(id, /, *, restrict=False)
         Destroy the identified interpreter.

         Attempting to destroy the current interpreter raises InterpreterError.
         So does an unrecognized ID.

-    exec(...)
-        exec(id, code, shared=None, *, restrict=False)
-
+    exec(id, /, code, shared={}, *, restrict=False)
         Execute the provided code in the identified interpreter.
+
         This is equivalent to running the builtin exec() under the target
         interpreter, using the __dict__ of its __main__ module as both
         globals and locals.

-        "code" may be a string containing the text of a Python script.
+        *code* may be a string containing the text of a Python script.

         Functions (and code objects) are also supported, with some restrictions.
         The code/function must not take any arguments or be a closure
@@ -240,42 +232,27 @@
         If a function is provided, its code object is used and all its state
         is ignored, including its __globals__ dict.

-    get_config(...)
-        get_config(id, *, restrict=False) -> types.SimpleNamespace
-
+    get_config(id, /, *, restrict=False)
         Return a representation of the config used to initialize the interpreter.

     get_current()
-        get_current() -> (ID, whence)
-
         Return the ID of current interpreter.

     get_main()
-        get_main() -> (ID, whence)
-
-        Return the ID of main interpreter.
+        Return the ID of  main interpreter.

-    incref(...)
-
-    is_running(...)
-        is_running(id, *, restrict=False) -> bool
+    incref(id, /, *, implieslink=False, restrict=False)

+    is_running(id, /, *, restrict=False)
         Return whether or not the identified interpreter is running.

-    is_shareable(...)
-        is_shareable(obj) -> bool
-
-        Return True if the object's data may be shared between interpreters and
-        False otherwise.
-
-    list_all(...)
-        list_all() -> [(ID, whence)]
+    is_shareable(obj, /)
+        Return True if the object's data may be shared between interpreters and False otherwise.

+    list_all(*, require_ready=False)
         Return a list containing the ID of every existing interpreter.

-    new_config(...)
-        new_config(name='isolated', /, **overrides) -> type.SimpleNamespace
-
+    new_config(name='isolated', /, **overrides)
         Return a representation of a new PyInterpreterConfig.

         The name determines the initial values of the config.  Supported named
@@ -284,30 +261,23 @@
         Any keyword arguments are set on the corresponding config fields,
         overriding the initial values.

-    run_func(...)
-        run_func(id, func, shared=None, *, restrict=False)
-
+    run_func(id, /, func, shared={}, *, restrict=False)
         Execute the body of the provided function in the identified interpreter.
+
         Code objects are also supported.  In both cases, closures and args
         are not supported.  Methods and other callables are not supported either.

-        (See _interpreters.exec().
-
-    run_string(...)
-        run_string(id, script, shared=None, *, restrict=False)
+        (See _interpreters.exec().)

+    run_string(id, /, script, shared={}, *, restrict=False)
         Execute the provided string in the identified interpreter.

-        (See _interpreters.exec().
-
-    set___main___attrs(...)
-        set___main___attrs(id, ns, *, restrict=False)
+        (See _interpreters.exec().)

+    set___main___attrs(id, ns, /, *, restrict=False)
         Bind the given attributes in the interpreter's __main__ module.

-    whence(...)
-        whence(id) -> int
-
+    whence(id, /)
         Return an identifier for where the interpreter was created.

 DATA

Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Many keyword-or-positional parameters were made positional-only.
  • Incorrect names of some parameters.
  • Mark up does not work here.

@@ -841,17 +848,29 @@ Any keyword arguments are set on the corresponding config fields,\n\
overriding the initial values.");


/*[clinic input]
_interpreters.create
config as configobj: object(py_default="'isolated'") = NULL
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the default value None?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the docstring, "The default is 'isolated'.". I think showing this is more helpful than None, what do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the code the default is equivalent to None. I did not look deeper. @ericsnowcurrently?

Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that in long term, it would be better to rename "restricted" to "restrict" and get rid of "restrict as restricted". That syntax is used to avoid name conflicts (with C keywords and other variable) and too large diffs, but if the parameter is only used once and does not conflict with other names, it is worth to rename it.

I would prefer to backport this to 3.14, but it is too late to break ABI.

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

Successfully merging this pull request may close these issues.

2 participants