GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Start the deprecation cycle for subprocess preexec_fn · Issue #82616 · python/cpython

Start the deprecation cycle for subprocess preexec_fn #82616

New issue
Copy link
New issue
Copy link
Open
Open
Start the deprecation cycle for subprocess preexec_fn#82616
Copy link
Assignees
gpshead
Labels
stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-subprocessSubprocess issues.Subprocess issues.type-featureA feature request or enhancementA feature request or enhancement

Description

@gpshead
gpshead
opened on Oct 10, 2019
Issue body actions
BPO 38435
Nosy @gpshead, @vstinner, @diekhans, @markmentovai, @serhiy-storchaka, @izbyshev
PRs
  • gh-82616: Add process_group support to subprocess.Popen #23930
  • bpo-38435: Detect preexec_fn=os.setsid as start_new_session=True #23936
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/gpshead'
    closed_at = None
    created_at = <Date 2019-10-10.18:18:17.382>
    labels = ['type-feature', 'library', '3.11']
    title = 'Start the deprecation cycle for subprocess preexec_fn'
    updated_at = <Date 2022-03-16.16:54:37.730>
    user = 'https://github.com/gpshead'

    bugs.python.org fields:

    activity = <Date 2022-03-16.16:54:37.730>
    actor = 'markmentovai'
    assignee = 'gregory.p.smith'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2019-10-10.18:18:17.382>
    creator = 'gregory.p.smith'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 38435
    keywords = ['patch']
    message_count = 18.0
    messages = ['354397', '354404', '354407', '354439', '383708', '383720', '383724', '383725', '383726', '383727', '383728', '383736', '383740', '383863', '383988', '400498', '403026', '415352']
    nosy_count = 6.0
    nosy_names = ['gregory.p.smith', 'vstinner', 'diekhans', 'markmentovai', 'serhiy.storchaka', 'izbyshev']
    pr_nums = ['23930', '23936']
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue38435'
    versions = ['Python 3.11']

    Activity

    Next
    1. gpshead commented on Oct 10, 2019

      @gpshead
      gpshead
      on Oct 10, 2019
      MemberAuthor
      More actions

      subprocess's preexec_fn feature needs to enter PendingDeprecationWarning state in 3.9, to become a DeprecationWarning in 3.10, with a goal of removing it in 3.11.

      Rationale: We now live in a world full of threads, it is entirely unsafe to call back into the python interpreter within the forked child before exec per POSIX specification.

      We've also already made preexec_fn no longer supported from CPython subinterpreters in 3.8.

      If there are not already issues open for desired features of subprocess that do not yet have replacements or workarounds for *specific* actions that preexec_fn is being used for in your application, please open feature requests for those. (ex: calling umask is https://bugs.python.org/issue38417, and group, uid, gid setting has already landed in 3.9)

    2. gpshead
      added
      3.9 (EOL)end of lifeend of life
      on Oct 10, 2019
    3. gpshead
      self-assigned this
      on Oct 10, 2019
    4. gpshead
      added
      type-featureA feature request or enhancementA feature request or enhancement
      on Oct 10, 2019
    5. vstinner commented on Oct 10, 2019

      @vstinner
      vstinner
      on Oct 10, 2019
      Member
      More actions

      What is the recommanded way to replace preexec_fn?

    6. gpshead commented on Oct 10, 2019

      @gpshead
      gpshead
      on Oct 10, 2019
      MemberAuthor
      More actions

      With task specific arguments. cwd, start_new_session, group, extra_groups,
      user, etc..

      We cannot provide a general do everything replacement and should not try.
      It not possible.

    7. vstinner commented on Oct 11, 2019

      @vstinner
      vstinner
      on Oct 11, 2019
      Member
      More actions

      We cannot provide a general do everything replacement and should not try. It not possible.

      Well, I proposed a solution at:
      https://bugs.python.org/issue38417#msg354242

      I know that this solution has multiple flaws, but a bad solution may be better than no solution: breaking applications when upgrading to Python 3.11.

      Reacted by Ping He
    8. diekhans commented on Dec 24, 2020

      @diekhans
      diekhansmannequin
      on Dec 24, 2020
      Mannequin
      More actions

      calling setpgid() is a common post-fork task that would need to be an explicit parameter to Popen when preexec_fn goes away

    9. gpshead
      added
      3.10 (EOL)end of lifeend of life
      and removed
      3.9 (EOL)end of lifeend of life
      on Dec 25, 2020
    10. gpshead commented on Dec 25, 2020

      @gpshead
      gpshead
      on Dec 25, 2020
      MemberAuthor
      More actions

      PR up to add setpgid support. From what I've come across, some setpgid() users can use setsid() already available via start_new_session= instead. But rather than getting into the differences between those, making both available makes sense to allow for anyone's case where setsid() isn't desired.

    11. gpshead commented on Dec 25, 2020

      @gpshead
      gpshead
      on Dec 25, 2020
      MemberAuthor
      More actions

      https://bugs.python.org/issue42736 filed to track adding Linux prctl() support.

    12. gpshead commented on Dec 25, 2020

      @gpshead
      gpshead
      on Dec 25, 2020
      MemberAuthor
      More actions

      Another preexec_fn use to consider:

       resource.setrlimit(resource.RLIMIT_CORE, (XXX, XXX))

      Using an intermediate shell script wrapper that changes the rlimit and exec's the actual process is also an alternative.

    13. gpshead commented on Dec 25, 2020

      @gpshead
      gpshead
      on Dec 25, 2020
      MemberAuthor
      More actions

      I'm also seeing a lot of os.setpgrp() calls, though those are more likely able to use start_new_session to do setsid() as a dropin replacement.

    14. gpshead commented on Dec 25, 2020

      @gpshead
      gpshead
      on Dec 25, 2020
      MemberAuthor
      More actions

      signal.signal use case:

      Calls to signal.signal(x, y) to sometimes to set a non SIG_DFL behavior on a signal. SIGINT -> SIG_IGN for example.

      I see a lot of legacy looking code calling signal.signal in prexec_fn that appears to set SIG_DFL for the signals that Python otherwise modifies. Which restore_signals=True should already be doing.

    15. 19 remaining items

    16. gpshead commented on Feb 14, 2024

      @gpshead
      gpshead
      on Feb 14, 2024
      MemberAuthor
      More actions

      How would you refactor the following code without using preexec_fn:

      Creating your own intermediate executable that does the same thing as your preexec code before calling exec to replace itself with the ultimate desired process rather than expecting it to be done in the restricted environment of process between fork+exec should work well for things like that.

      Reacted by Sam Grayson
    17. gpshead
      removed
      3.13only security fixesonly security fixes
      on Feb 15, 2024
    18. hauntsaninja commented on Feb 15, 2024

      @hauntsaninja
      hauntsaninja
      on Feb 15, 2024
      Contributor
      More actions

      My work codebase has a fair amount of preexec_fn use, more than half of it is prctl PR_SET_PDEATHSIG

      Reacted by Gregory P. Smith and falekseev-pinely
    19. vstinner commented on Feb 15, 2024

      @vstinner
      vstinner
      on Feb 15, 2024
      Member
      More actions

      Would it make sense to have a portable and generic way to:

      • spawn a first Python "preexec" process which runs arbitrary code.
      • then this process exec() the target process.

      Rather than preexec_fn=<function>, it would be something like preexec_code: str.

      We should design carefully "preexec" about signals, threads, file descriptor inheritance, but also report properly exec() errors to the parent process!

      The main difference is that preexec_fn is called between fork() and exec(), rather than preexec_code would be called after fork()+exec("python").

    20. astrand commented on Mar 26, 2024

      @astrand
      astrand
      on Mar 26, 2024
      Contributor
      More actions

      Hi all. As the original subprocess module author, I'm a bit concerned over the expansion of the API. The number of arguments for the subprocess.Popen constructor is, to be honest, crazy. I am grateful for all work and bugfixes that has been done over the years, but the API is now quite far from the my original vision of a "small and clean API", which was the reason to "start from scratch", thus create the subprocess module.

      Removing preexec_fn and then adding additional arguments for cases where preexec_fn was used will further contribute to the "bloat" of the API, if I may use that word.

      I do understand the technical difficulties, but as always there are multiple alternatives. Better documentation as well as #82616 (comment) are very interesting, as I see it.

      Reacted by Gregory P. Smith and Nikolas Dahn
    21. gpshead commented on Mar 26, 2024

      @gpshead
      gpshead
      on Mar 26, 2024
      MemberAuthor
      More actions

      Major kudos for its creation @astrand! Your work largely succeeded! If it had not, it would've needed replacing again. =)

      The original overall subprocess public API design was sound with one major exception: preexec_fn= was a conflicted with the existence of threads, thus why I label it as a fundamentally flawed feature. Yet its existence also allowed for a lot of use cases that were otherwise unimplemented by subprocess and would've resulted in people coming up with even worse custom (read: buggy x1000) workarounds. So despite the problem, please don't consider this a criticism. It made the most sense at the time.

      The remaining conundrum:

      A library using the subprocess API with preexec_fn= is often not be owned by the same people owning code elsewhere in a broader application that uses threads. Both code owners believe they are right to do what they did. Afterall, they were just using documented public APIs with an expectation that they just work thanks to Python. Python programmers are often operating at a high level and not expected to need to know low level system programming details for what appear to them to be normal mundane tasks like spawning a process or using threads.

      If told they cannot expect do either they will have no good options that satisfy their needs. Their mere existence of both within the same process is the source of conflict. Use of threading is far more important to the world than realistic preexec_fn= use cases, thus nobody is advising "nobody should use threads, a minority of people won't be able to do these esoteric things with child processes easily!". 😄

      In absence of ways to replace the functionality people use it for, preexec_fn= continues to exist, just with the giant warning about the random deadlocks that will happen to end users of such unfortunate applications when the entire application stack is not tightly controlled.

      Thus our dirty plethora of options added to replace many common things people were using preexec_fn= for. Those also greatly increase subprocess launching performance (done in C, vfork can be used, etc). We've provided for some common cases. More exist. There is no way for us to predict and provide everything.

      This is the largest detractor to "small and clean" that I hear users complain about today. It is true! But most people do not need these options in daily life. So that is perhaps best considered a documentation organization issue.

    22. gpshead commented on Mar 26, 2024

      @gpshead
      gpshead
      on Mar 26, 2024
      MemberAuthor
      More actions

      Stepping back from a "What do users actually need?" point of view, nothing has provided that:

      • Users need to be able to make subsets of a pretty long set of possible system calls with options before the ultimate child process is exec'd.
      • Users want doing that to perform well.
      • It is not possible to use arbitrary Python code and meet both of the above two constraints.

      Idea from above: Launching an intermediate Python interpreter process to run the preexec_fn code.

      This is a good idea in terms of code compatibility. It would add a large Python interpreter startup time to the child process launch. But functionality wise it would work and the caveat can be documented. It also requires that a sys.executable interpreter binary to launch exists (excluding users of embedded Python, and possibly users running under an emulator? A deliberate choice we could make).

      Brainstorming further Idea: A smaller binary to execute as the intermediate is one possibility. A microscopic tiny Python-ish interpreter with a restricted set of APIs (no import capability, presume os and sys exist, etc) sufficient to just allow the kinds of system calls people need to make? I don't like the complexity, but it'd be entertaining to see how feasible this is. People can already manually use things like /bin/sh for some fraction of pieces of this concept today (very system dependent).

    23. astrand commented on Mar 31, 2024

      @astrand
      astrand
      on Mar 31, 2024
      Contributor
      More actions

      @gpshead thanks for your comments. preexec_fn is just a way to execute code between fork and exec, so as far as I can tell, the problem is exactly the same if you manually code fork() and exec() and some code in between. I assume that neither fork() nor exec() will be removed from the Python API, so why should preexec_fn be removed? As I understand it, Python 3.12 already has a mechanism in place to raise a DeprecationWarning if threads are detected. Why not use the same approach for preexec_fn?

    24. gpshead commented on Mar 31, 2024

      @gpshead
      gpshead
      on Mar 31, 2024
      MemberAuthor
      More actions

      The same warning can and should be added there (I'll get to it).

    25. eli-schwartz commented on May 21, 2024

      @eli-schwartz
      eli-schwartz
      on May 21, 2024
      Contributor
      More actions

      I've been trying to figure out if I can change from a preexec_fn that does os.setuid "to irrevocably drop permissions", to the user= kwarg. It makes for fairly obtuse reading, since the subprocess module documentation somewhat vaguely says "the setreuid() system call will be made" but not what arguments the low-level description of syscalls will be called with, and my underlying goal was actually to just "irrevocably switch to that user" (a high-level goal).

      I think it is safe "depending on your version of POSIX conformance" assuming that cpython passes the user kwarg twice to setreuid, (rather than 3 times to setresuid ;)). Do I understand correctly?

    26. vstinner
      added
      topic-subprocessSubprocess issues.Subprocess issues.
      on Jul 8, 2024
    27. ofek commented on Feb 7, 2025

      @ofek
      ofek
      on Feb 7, 2025
      SponsorContributor
      More actions

      To create a daemon process on Linux that continues running after the current process exits, does not block from I/O and is immune to signals from the current process group, is the following sufficient? Or does one need to manually do that double fork technique? subprocess.Popen(command, start_new_session=True, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, close_fds=True)

      I asked on the forum instead.

    28. dmitmel commented on Dec 30, 2025

      @dmitmel
      dmitmel
      on Dec 30, 2025
      More actions

      I was trying to use preexec_fn to reset the dispositions of SIGINT and SIGQUIT to SIG_DFL in the subprocess because my script which sets those signals to SIG_IGN in the parent, so that the parent does not die when CTRL+C is issued, but the child receives that signal (basically to emulate the way interactive shells spawn foreground jobs). I wish the subprocess.Popen API exposed something akin to POSIX_SPAWN_SETSIGMASK and POSIX_SPAWN_SETSIGDEF of posix_spawn, both of which are accessible with os.posix_spawn from Python, by the way.

    29. JackKelly commented on Jan 14, 2026

      @JackKelly
      JackKelly
      on Jan 14, 2026
      More actions

      Thanks for everyone's work on this. Just to mention that, like some other folks in the comments above, I'm using preexec_fn to set the PDEATHSIG of the child process. (In my case, I'm setting it to SIGTERM so the child dies if the parent is killed).

      Instead of having to write code to kill the child process if the parent is killed, I'd much prefer to be able to pass an argument to Popen to say kill_child_if_parent_is_killed=True (although I'm sure you can think of a better name for that argument!).

    30. ndahn commented on Aug 10, 2026

      @ndahn
      ndahn
      on Aug 10, 2026
      More actions

      Instead of adding more and more function args, how about a ProcessSettings dataclass-like object that's handled by the internals? This would be very easy to extend without making the API more messy.

      Background: I'm the author of better_launch, a process launcher and manager for ROS2. Right now I'm modifying the process group and niceness, but additional customization may be needed in the future.

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

    Metadata

    Metadata

    Assignees

    • @gpshead
      gpshead

    Labels

    stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-subprocessSubprocess issues.Subprocess issues.type-featureA feature request or enhancementA feature request or enhancement

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.