From f477ff1eb0ad65f6ead7f029983807ffb6396932 Mon Sep 17 00:00:00 2001 From: Juern Brodersen Date: Oct 22 2023 01:35:56 +0000 Subject: [PATCH 1/4] Add a test case to exercise setting “inheritable” flag. Signed-off-by: Ben Finney --- diff --git a/test/test_daemon.py b/test/test_daemon.py index 512f8fb..c7741d2 100644 --- a/test/test_daemon.py +++ b/test/test_daemon.py @@ -19,6 +19,7 @@ import socket import sys import tempfile from types import ModuleType +import unittest import unittest.mock import warnings @@ -375,6 +376,7 @@ class DaemonContext_open_TestCase(DaemonContext_BaseTestCase): self.mock_module_daemon.attach_mock( unittest.mock.Mock(), 'DaemonContext') + make_fake_streams(self) self.test_files_preserve_fds = object() self.test_signal_handler_map = object() daemoncontext_method_return_values = { @@ -553,6 +555,28 @@ class DaemonContext_open_TestCase(DaemonContext_BaseTestCase): self.mock_module_daemon.redirect_stream.assert_has_calls( expected_calls, any_order=True) + @unittest.skipIf( + sys.version_info < (3, 4), + "File handle inheritable attribute only in Python 3.4 or later") + @unittest.skipIf( + sys.platform != 'windows', + "File handles implemented only on MS Windows") + @unittest.mock.patch.object(os, "set_inheritable") + def test_duplicate_is_inheritable( + self, mock_func_os_set_inheritable): + """ The system streams should be inheritable. + + dup2 sets the new file descriptors to be inhertiable. But if the + old and new file descriptors are equal, the inheritable attribute + is not changed. + If the system streams aren't inheritable they would not be copied + to any children of the daemon. + """ + test_stream = self.fake_streams['stdout'] + stream_fileno = test_stream.fileno() + daemon.daemon.redirect_stream(test_stream, test_stream) + mock_func_os_set_inheritable.assert_called_with(stream_fileno, True) + def test_enters_pidfile_context(self): """ Should enter the PID file context manager. """ instance = self.test_instance From e69c961ecc2b92ced4879fc7015590b9283d1bf8 Mon Sep 17 00:00:00 2001 From: Ben Finney Date: Oct 22 2023 01:35:56 +0000 Subject: [PATCH 2/4] Set the “inheritable” flag on the stream handle. --- diff --git a/ChangeLog b/ChangeLog index ab0b340..44cd520 100644 --- a/ChangeLog +++ b/ChangeLog @@ -84,6 +84,12 @@ Bugs Fixed: Closes: Pagure #72. Thanks to Anton Anton for the report. +* Set file handles (MS Windows only) of standard streams to be + inheritable. + + Closes: Pagure #57. Thanks to Juern Brodersen for the report and + test case. + Added: * Document a `ServiceRunner` class as an example of using `DaemonContext`. diff --git a/daemon/daemon.py b/daemon/daemon.py index bf142c8..3feb3e2 100644 --- a/daemon/daemon.py +++ b/daemon/daemon.py @@ -978,6 +978,27 @@ def redirect_stream(system_stream, target_stream): else: target_fd = target_stream.fileno() os.dup2(target_fd, system_stream.fileno()) + _set_file_handle_inheritable(target_fd) + + +def _set_file_handle_inheritable(handle): + """ Set the “inheritable” flag of file handle `handle`. + + :param handle: The file handle value. + :return: ``None``. + + See the Python standard library `os` module. Only the + `windows` platform uses file handles; the “inheritable” flag + only exists in Python 3.4 or later. + """ + try: + set_handle_inheritable = os.set_handle_inheritable + except AttributeError: + # Not available (and not needed) if Python < 3.4, or if the + # platform is not MS Windows. + pass + else: + set_handle_inheritable(handle) def make_default_signal_map(): From cdbbca288c54c145a867315785fb489a66a355a9 Mon Sep 17 00:00:00 2001 From: Ben Finney Date: Oct 22 2023 01:35:56 +0000 Subject: [PATCH 3/4] Require Python version 3.4 or later. This is needed to have the “inheritable file descriptor” behaviour introduced with PEP 446. --- diff --git a/setup.py b/setup.py index 03849c6..188ec3c 100644 --- a/setup.py +++ b/setup.py @@ -65,7 +65,7 @@ setup_kwargs = dict( "packaging", "lockfile >=0.10", ], - python_requires=">=3", + python_requires=">=3.4", extras_require={ 'test': test_requirements, 'devel': devel_requirements, diff --git a/test/test_daemon.py b/test/test_daemon.py index c7741d2..8712f50 100644 --- a/test/test_daemon.py +++ b/test/test_daemon.py @@ -556,9 +556,6 @@ class DaemonContext_open_TestCase(DaemonContext_BaseTestCase): expected_calls, any_order=True) @unittest.skipIf( - sys.version_info < (3, 4), - "File handle inheritable attribute only in Python 3.4 or later") - @unittest.skipIf( sys.platform != 'windows', "File handles implemented only on MS Windows") @unittest.mock.patch.object(os, "set_inheritable") From 3a05ea90b804b26e4808148f438f96a2ab3c6b59 Mon Sep 17 00:00:00 2001 From: Ben Finney Date: Oct 22 2023 01:35:56 +0000 Subject: [PATCH 4/4] Import a module required for a unit test. --- diff --git a/test/test_pidfile.py b/test/test_pidfile.py index 782f6df..9eb6f15 100644 --- a/test/test_pidfile.py +++ b/test/test_pidfile.py @@ -10,6 +10,7 @@ import builtins import contextlib import errno +import contextlib import io import itertools import os