From f477ff1eb0ad65f6ead7f029983807ffb6396932 Mon Sep 17 00:00:00 2001
From: Juern Brodersen <brodersen4univention@unknown.invalid>
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 <ben+python@benfinney.id.au>

---

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 <ben+python@benfinney.id.au>
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 <ben+python@benfinney.id.au>
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 <ben+python@benfinney.id.au>
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

