ipython / ipython/ipykernel

Different child termination behavior during Kernel shutdown on Posix and Windows systems

Open
#1,232 1 comment 1 reaction 0 assignees View on GitHub
Dominant language
Python
Stars
734
Forks
412
Avg merge
1d 5h
Merged PRs (30d)
8

Description

Hello,

I've developed a custom Kernel inheriting from the IPython Kernel. This Kernel initiates a child process, and I aim to manage this process' lifecycle independently of the Kernel's shutdown workflow. To achieve this independence, I start the child process as a new process group. This approach functions as expected in POSIX systems, as the IPython Kernel verifies if the kernel process group is distinct from the child process group. However, a divergence in behavior arises when attempting the same workflow on Windows. This discrepancy stems from the IPython Kernel's behavior of returning all children of the Kernel process and subsequently sending SIGTERM signals to them. Please refer:
https://github.com/ipython/ipykernel/blob/main/ipykernel/kernelbase.py#L1218:L1227 and https://github.com/ipython/ipykernel/blob/main/ipykernel/kernelbase.py#L1216:L1217

I understand that it is not straightforward to determine the process group equivalence in Windows OS without using lower-level win32 APIs but this difference in behavior is causing issues in my custom kernel workflows.

Can I achieve my requirements without having to make any changes to the IPython Kernel? My proposed solution involves introducing an instance-level variable in the base Kernel class, which, if set to `False`, ensures that the Kernel refrains from terminating child processes by default during Kernel shutdown.

Here's a potential implementation:

self.terminate_all_children: bool = True # Set to true by default to maintain backward-compatibility

This variable is then checked in the _process_children() method to determine whether to process the children or not.

kernel_process = psutil.Process()
all_children = kernel_process.children(recursive=True)
process_group_children = []
if not self.terminate_all_children:
return process_group_children
else:
if os.name == "nt":
return all_children
kernel_pgid = os.getpgrp()

for child in all_children:
try:
child_pgid = os.getpgid(child.pid)
except OSError:
pass
else:
if child_pgid == kernel_pgid:
process_group_children.append(child)
return process_group_children

By setting `terminate_all_children` to `False` in the subclass, I ensure that the IPython Kernel doesn't terminate the child process by default when Kernel shutdown is initiated.

I'd appreciate your thoughts on this proposed solution. If it aligns with your expectations, I'm prepared to submit a PR. Thank you in advance!

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.