gh-108765: Python.h no longer includes <unistd.h>
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index e7b60ddbdbbfda..c4fb328db9cfa0 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -921,6 +921,11 @@ Porting to Python 3.13
   also the ``HAVE_IEEEFP_H`` macro.
   (Contributed by Victor Stinner in :gh:`108765`.)
 
+* ``Python.h`` no longer includes the ``<unistd.h>`` standard header file. If
+  needed, it should now be included explicitly. For example, it provides the
+  functions: ``close()``, ``getpagesize()``, ``getpid()`` and ``sysconf()``.
+  (Contributed by Victor Stinner in :gh:`108765`.)
+
 Deprecated
 ----------
 
diff --git a/Include/Python.h b/Include/Python.h
index 002a79dbdc9362..4cc72bb23ce7a3 100644
--- a/Include/Python.h
+++ b/Include/Python.h
@@ -26,14 +26,13 @@
 #ifdef HAVE_STDDEF_H
 #  include <stddef.h>             // size_t
 #endif
-#ifndef MS_WINDOWS
-#  include <unistd.h>             // sysconf()
+#ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>          // ssize_t
 #endif
 
-// errno.h, stdio.h, stdlib.h and string.h headers are no longer used by Python
-// headers, but kept for backward compatibility (no introduce new compiler
-// warnings). They are not included by the limited C API version 3.11 and
-// above.
+// errno.h, stdio.h, stdlib.h and string.h headers are no longer used by
+// Python, but kept for backward compatibility (avoid compiler warnings).
+// They are no longer included by limited C API version 3.11 and newer.
 #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
 #  include <errno.h>              // errno
 #  include <stdio.h>              // FILE*
diff --git a/Misc/NEWS.d/next/C API/2023-09-01-21-10-29.gh-issue-108765.eeXtYF.rst b/Misc/NEWS.d/next/C API/2023-09-01-21-10-29.gh-issue-108765.eeXtYF.rst
new file mode 100644
index 00000000000000..ff8f79998fa968
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2023-09-01-21-10-29.gh-issue-108765.eeXtYF.rst	
@@ -0,0 +1,4 @@
+``Python.h`` no longer includes the ``<unistd.h>`` standard header file. If
+needed, it should now be included explicitly. For example, it provides the
+functions: ``close()``, ``getpagesize()``, ``getpid()`` and ``sysconf()``.
+Patch by Victor Stinner.
diff --git a/Modules/_ctypes/malloc_closure.c b/Modules/_ctypes/malloc_closure.c
index 3a859322772ba7..bb4f8f21bd3f77 100644
--- a/Modules/_ctypes/malloc_closure.c
+++ b/Modules/_ctypes/malloc_closure.c
@@ -1,16 +1,17 @@
 #ifndef Py_BUILD_CORE_BUILTIN
 #  define Py_BUILD_CORE_MODULE 1
 #endif
+
 #include <Python.h>
 #include <ffi.h>
 #ifdef MS_WIN32
-#include <windows.h>
+#  include <windows.h>
 #else
-#include <sys/mman.h>
-#include <unistd.h>
-# if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
-#  define MAP_ANONYMOUS MAP_ANON
-# endif
+#  include <sys/mman.h>
+#  include <unistd.h>             // sysconf()
+#  if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+#    define MAP_ANONYMOUS MAP_ANON
+#  endif
 #endif
 #include "ctypes.h"
 
diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c
index ac2b0d4f55468c..ef76d26282e1b3 100644
--- a/Modules/_posixsubprocess.c
+++ b/Modules/_posixsubprocess.c
@@ -8,28 +8,28 @@
 #include "pycore_pystate.h"
 #include "pycore_signal.h"        // _Py_RestoreSignals()
 #if defined(HAVE_PIPE2) && !defined(_GNU_SOURCE)
-# define _GNU_SOURCE
+#  define _GNU_SOURCE
 #endif
-#include <unistd.h>
-#include <fcntl.h>
+#include <unistd.h>               // close()
+#include <fcntl.h>                // fcntl()
 #ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
+#  include <sys/types.h>
 #endif
 #if defined(HAVE_SYS_STAT_H)
-#include <sys/stat.h>
+#  include <sys/stat.h>           // stat()
 #endif
 #ifdef HAVE_SYS_SYSCALL_H
-#include <sys/syscall.h>
+#  include <sys/syscall.h>
 #endif
 #if defined(HAVE_SYS_RESOURCE_H)
-#include <sys/resource.h>
+#  include <sys/resource.h>
 #endif
 #ifdef HAVE_DIRENT_H
-#include <dirent.h>
+#  include <dirent.h>             // opendir()
+#endif
+#if defined(HAVE_SETGROUPS)
+#  include <grp.h>                // setgroups()
 #endif
-#ifdef HAVE_GRP_H
-#include <grp.h>
-#endif /* HAVE_GRP_H */
 
 #include "posixmodule.h"
 
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 4fc354ae79bfed..ab33702cdfd872 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -24,9 +24,6 @@
 #include <float.h>                // FLT_MAX
 #include <signal.h>
 #include <stddef.h>               // offsetof()
-#ifndef MS_WINDOWS
-#  include <unistd.h>
-#endif
 
 #ifdef HAVE_SYS_WAIT_H
 #  include <sys/wait.h>           // W_STOPCODE
diff --git a/Modules/grpmodule.c b/Modules/grpmodule.c
index f5709296334a8f..20e83de84e8340 100644
--- a/Modules/grpmodule.c
+++ b/Modules/grpmodule.c
@@ -4,7 +4,8 @@
 #include "Python.h"
 #include "posixmodule.h"
 
-#include <grp.h>
+#include <grp.h>                  // getgrgid_r()
+#include <unistd.h>               // sysconf()
 
 #include "clinic/grpmodule.c.h"
 /*[clinic input]
diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c
index c8cd7e59dbab50..d11200a4042551 100644
--- a/Modules/mmapmodule.c
+++ b/Modules/mmapmodule.c
@@ -28,6 +28,9 @@
 #include "pycore_fileutils.h"     // _Py_stat_struct
 
 #include <stddef.h>               // offsetof()
+#ifndef MS_WINDOWS
+#  include <unistd.h>             // close()
+#endif
 
 // to support MS_WINDOWS_SYSTEM OpenFileMappingA / CreateFileMappingA
 // need to be replaced with OpenFileMappingW / CreateFileMappingW
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 761542866d8f96..6e829b200fa46d 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -286,7 +286,7 @@ corresponding Unix manual entries for more information on calls.");
 #endif
 
 #ifdef HAVE_COPY_FILE_RANGE
-#  include <unistd.h>
+#  include <unistd.h>             // copy_file_range()
 #endif
 
 #if !defined(CPU_ALLOC) && defined(HAVE_SCHED_SETAFFINITY)
diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c
index cc2e2a43893971..b7034369c4731e 100644
--- a/Modules/pwdmodule.c
+++ b/Modules/pwdmodule.c
@@ -4,7 +4,8 @@
 #include "Python.h"
 #include "posixmodule.h"
 
-#include <pwd.h>
+#include <pwd.h>                  // getpwuid()
+#include <unistd.h>               // sysconf()
 
 #include "clinic/pwdmodule.c.h"
 /*[clinic input]
diff --git a/Modules/resource.c b/Modules/resource.c
index 4614f5e98cc888..f5d9972d9a8ff7 100644
--- a/Modules/resource.c
+++ b/Modules/resource.c
@@ -1,13 +1,12 @@
-
 #include "Python.h"
-#include <sys/resource.h>
+#include <errno.h>                // errno
+#include <string.h>
+#include <sys/resource.h>         // getrusage()
 #ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
+#  include <sys/time.h>
 #endif
 #include <time.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
+#include <unistd.h>               // getpagesize()
 
 /* On some systems, these aren't in any header file.
    On others they are, with inconsistent prototypes.
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
index 4987cf0f2065c2..c56e682b21e2a1 100644
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -17,6 +17,9 @@
 #include "pycore_time.h"          // _PyTime_t
 
 #include <stddef.h>               // offsetof()
+#ifndef MS_WINDOWS
+#  include <unistd.h>             // close()
+#endif
 
 #ifdef HAVE_SYS_DEVPOLL_H
 #include <sys/resource.h>
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index 2f12c9cedbd8a6..74b1c1c661604f 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -269,7 +269,7 @@ shutdown(how) -- shut down traffic in one or both directions\n\
 #ifdef HAVE_NETDB_H
 #  include <netdb.h>
 #endif
-# include <unistd.h>
+#include <unistd.h>               // close()
 
 /* Headers needed for inet_ntoa() and inet_addr() */
 #   include <arpa/inet.h>
diff --git a/Programs/_freeze_module.c b/Programs/_freeze_module.c
index e55f1d56745c4d..f6c46fa629efba 100644
--- a/Programs/_freeze_module.c
+++ b/Programs/_freeze_module.c
@@ -19,7 +19,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #ifndef MS_WINDOWS
-#include <unistd.h>
+#  include <unistd.h>
 #endif
 
 uint32_t _Py_next_func_version = 1;
diff --git a/Python/dup2.c b/Python/dup2.c
index a1df0492099163..936211f27ec737 100644
--- a/Python/dup2.c
+++ b/Python/dup2.c
@@ -11,9 +11,9 @@
  * Return fd2 if all went well; return BADEXIT otherwise.
  */
 
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
+#include <errno.h>                // errno
+#include <fcntl.h>                // fcntl()
+#include <unistd.h>               // close()
 
 #define BADEXIT -1
 
diff --git a/Python/perf_trampoline.c b/Python/perf_trampoline.c
index b8885a303977d0..10675bf9f8292a 100644
--- a/Python/perf_trampoline.c
+++ b/Python/perf_trampoline.c
@@ -140,9 +140,9 @@ any DWARF information available for them).
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/mman.h>
+#include <sys/mman.h>             // mmap()
 #include <sys/types.h>
-#include <unistd.h>
+#include <unistd.h>               // sysconf()
 
 #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
 #define PY_HAVE_INVALIDATE_ICACHE

<!--
Thanks for your contribution!
Please read this comment in its entirety. It's quite important.

# Pull Request title

It should be in the following format:

```
gh-NNNNN: Summary of the changes made
```

Where: gh-NNNNN refers to the GitHub issue number.

Most PRs will require an issue number. Trivial changes, like fixing a typo, do not need an issue.

# Backport Pull Request title

If this is a backport PR (PR made against branches other than `main`),
please ensure that the PR title is in the following format:

```
[X.Y] <title from the original PR> (GH-NNNN)
```

Where: [X.Y] is the branch name, e.g. [3.6].

GH-NNNN refers to the PR number from `main`.

-->


<!-- gh-issue-number: gh-108765 -->
* Issue: gh-108765
<!-- /gh-issue-number -->


<!-- readthedocs-preview cpython-previews start -->
----
:books: Documentation preview :books:: https://cpython-previews--108783.org.readthedocs.build/

<!-- readthedocs-preview cpython-previews end -->