diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index ac0ae8cf0133e6..54298014655ab6 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -1285,6 +1285,12 @@ Build changes
* GNU Autoconf 2.72 is now required to generate :file:`configure`.
(Contributed by Erlend Aasland in :gh:`115765`.)
+* zlib is now officially required to build CPython.
+ It is still technically possible to build CPython without it for special needs, like bootstrapping.
+ Such builds are not supported, but we can accept pull requests to keep them working.
+ As an exception, zlib is not required on WASI.
+ (Contributed by Stan Ulbrych & Gregory P. Smith in :gh:`130234`.)
+
.. _whatsnew314-pep761:
PEP 761: Discontinuation of PGP signatures
diff --git a/Lib/encodings/zlib_codec.py b/Lib/encodings/zlib_codec.py
index 95908a4b4a13a1..25acfba1c01483 100644
--- a/Lib/encodings/zlib_codec.py
+++ b/Lib/encodings/zlib_codec.py
@@ -6,7 +6,7 @@
"""
import codecs
-import zlib # this codec needs the optional zlib module !
+import zlib
### Codec APIs
diff --git a/Lib/shutil.py b/Lib/shutil.py
index 510ae8c6f22d59..941b9c5d492f58 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -10,13 +10,7 @@
import fnmatch
import collections
import errno
-
-try:
- import zlib
- del zlib
- _ZLIB_SUPPORTED = True
-except ImportError:
- _ZLIB_SUPPORTED = False
+import zlib
try:
import bz2
@@ -1000,7 +994,7 @@ def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
"""
if compress is None:
tar_compression = ''
- elif _ZLIB_SUPPORTED and compress == 'gzip':
+ elif compress == 'gzip':
tar_compression = 'gz'
elif _BZ2_SUPPORTED and compress == 'bzip2':
tar_compression = 'bz2'
@@ -1121,10 +1115,9 @@ def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0,
"uncompressed tar file"),
}
-if _ZLIB_SUPPORTED:
- _ARCHIVE_FORMATS['gztar'] = (_make_tarball, [('compress', 'gzip')],
- "gzip'ed tar-file")
- _ARCHIVE_FORMATS['zip'] = (_make_zipfile, [], "ZIP file")
+_ARCHIVE_FORMATS['gztar'] = (_make_tarball, [('compress', 'gzip')],
+ "gzip'ed tar-file")
+_ARCHIVE_FORMATS['zip'] = (_make_zipfile, [], "ZIP file")
if _BZ2_SUPPORTED:
_ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')],
@@ -1345,12 +1338,9 @@ def _unpack_tarfile(filename, extract_dir, *, filter=None):
_UNPACK_FORMATS = {
'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"),
'zip': (['.zip'], _unpack_zipfile, [], "ZIP file"),
+ 'gztar': (['.tar.gz', '.tgz'], _unpack_tarfile, [], "gzip'ed tar-file"),
}
-if _ZLIB_SUPPORTED:
- _UNPACK_FORMATS['gztar'] = (['.tar.gz', '.tgz'], _unpack_tarfile, [],
- "gzip'ed tar-file")
-
if _BZ2_SUPPORTED:
_UNPACK_FORMATS['bztar'] = (['.tar.bz2', '.tbz2'], _unpack_tarfile, [],
"bzip2'ed tar-file")
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index a0fab46b24e249..5523ab3598e3bc 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -364,10 +364,7 @@ def __init__(self, name, mode, comptype, fileobj, bufsize,
try:
if comptype == "gz":
- try:
- import zlib
- except ImportError:
- raise CompressionError("zlib module is not available") from None
+ import zlib
self.zlib = zlib
self.crc = zlib.crc32(b"")
if mode == "r":
@@ -2697,13 +2694,10 @@ def next(self):
except SubsequentHeaderError as e:
raise ReadError(str(e)) from None
except Exception as e:
- try:
- import zlib
- if isinstance(e, zlib.error):
- raise ReadError(f'zlib error: {e}') from None
- else:
- raise e
- except ImportError:
+ import zlib
+ if isinstance(e, zlib.error):
+ raise ReadError(f'zlib error: {e}') from None
+ else:
raise e
break
diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py
index 4d97fe56f3a094..f75fbd1a71ac6b 100644
--- a/Lib/test/test_zlib.py
+++ b/Lib/test/test_zlib.py
@@ -8,8 +8,16 @@
import sys
from test.support import bigmemtest, _1G, _4G, is_s390x
+zlib = import_helper.import_module('zlib', required_on=('linux', 'android', 'ios', 'darwin', 'win32', 'cygwin'))
+
+# Building CPython without zlib is not supported except WASI.
+#
+# Anyone who wants build CPython this way should be prepared to patch it,
+# but the core team may help getting those patches to the main branch
+# (as that’s the place where multiple third parties can cooperate).
+#
+# For tests to pass without zlib, this file needs to be removed.
-zlib = import_helper.import_module('zlib')
requires_Compress_copy = unittest.skipUnless(
hasattr(zlib.compressobj(), "copy"),
diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py
index b8b496ad9471f4..a1f747180c593e 100644
--- a/Lib/zipfile/__init__.py
+++ b/Lib/zipfile/__init__.py
@@ -13,13 +13,10 @@
import sys
import threading
import time
-
-try:
- import zlib # We may need its compression method
- crc32 = zlib.crc32
-except ImportError:
- zlib = None
- crc32 = binascii.crc32
+import contextlib
+import pathlib
+import zlib
+crc32 = zlib.crc32
try:
import bz2 # We may need its compression method
@@ -771,12 +768,8 @@ def decompress(self, data):
}
def _check_compression(compression):
- if compression == ZIP_STORED:
+ if compression in (ZIP_STORED, ZIP_DEFLATED):
pass
- elif compression == ZIP_DEFLATED:
- if not zlib:
- raise RuntimeError(
- "Compression requires the (missing) zlib module")
elif compression == ZIP_BZIP2:
if not bz2:
raise RuntimeError(
diff --git a/Lib/zipimport.py b/Lib/zipimport.py
index 444c9dd11d8672..70e0988ba3b258 100644
--- a/Lib/zipimport.py
+++ b/Lib/zipimport.py
@@ -604,9 +604,7 @@ def _read_directory(archive):
_importing_zlib = False
-# Return the zlib.decompress function object, or NULL if zlib couldn't
-# be imported. The function is cached when found, so subsequent calls
-# don't import zlib again.
+# Return the zlib.decompress function object or raise an import error.
def _get_decompress_func():
global _importing_zlib
if _importing_zlib:
@@ -618,9 +616,6 @@ def _get_decompress_func():
_importing_zlib = True
try:
from zlib import decompress
- except Exception:
- _bootstrap._verbose_message('zipimport: zlib UNAVAILABLE')
- raise ZipImportError("can't decompress data; zlib not available")
finally:
_importing_zlib = False
diff --git a/Misc/NEWS.d/next/Build/2025-02-17-16-43-00.gh-issue-91246.3z8l7i3b.rst b/Misc/NEWS.d/next/Build/2025-02-17-16-43-00.gh-issue-91246.3z8l7i3b.rst
new file mode 100644
index 00000000000000..488d4a024c1c55
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2025-02-17-16-43-00.gh-issue-91246.3z8l7i3b.rst
@@ -0,0 +1,2 @@
+Make zlib required to build CPython with the exception of WASI.
+(Contributed by Stan Ulbrych & Gregory P. Smith in :gh:`130234`.)
diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in
index 4dcc0f55176d0e..95ea16dcf5c806 100644
--- a/Modules/Setup.bootstrap.in
+++ b/Modules/Setup.bootstrap.in
@@ -25,6 +25,8 @@ _thread _threadmodule.c
time timemodule.c
_typing _typingmodule.c
_weakref _weakref.c
+# used by shutil
+#zlib zlibmodule.c
# commonly used core modules
_abc _abc.c
diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in
index 6bb05a06a3465d..a331e18f0617f8 100644
--- a/Modules/Setup.stdlib.in
+++ b/Modules/Setup.stdlib.in
@@ -61,7 +61,7 @@
# with ./configure --with-system-libmpdec
@MODULE__DECIMAL_TRUE@_decimal _decimal/_decimal.c
-# compression libs and binascii (optional CRC32 from zlib)
+# compression libs and binascii (CRC32 from zlib)
# bindings need -lbz2, -lz, or -llzma, respectively
@MODULE_BINASCII_TRUE@binascii binascii.c
@MODULE__BZ2_TRUE@_bz2 _bz2module.c
diff --git a/Modules/binascii.c b/Modules/binascii.c
index 6bb01d148b6faa..b00e286072acd1 100644
--- a/Modules/binascii.c
+++ b/Modules/binascii.c
@@ -60,7 +60,7 @@
#include "Python.h"
#include "pycore_long.h" // _PyLong_DigitValue
#include "pycore_strhex.h" // _Py_strhex_bytes_with_sep()
-#ifdef USE_ZLIB_CRC32
+#ifndef NO_ZLIB_CRC32
# include "zlib.h"
#endif
@@ -616,7 +616,7 @@ binascii_crc_hqx_impl(PyObject *module, Py_buffer *data, unsigned int crc)
return PyLong_FromUnsignedLong(crc);
}
-#ifndef USE_ZLIB_CRC32
+#ifdef NO_ZLIB_CRC32
/* Crc - 32 BIT ANSI X3.66 CRC checksum files
Also known as: ISO 3307
**********************************************************************|
@@ -749,7 +749,7 @@ internal_crc32(const unsigned char *bin_data, Py_ssize_t len, unsigned int crc)
result = (crc ^ 0xFFFFFFFF);
return result & 0xffffffff;
}
-#endif /* USE_ZLIB_CRC32 */
+#endif /* NO_ZLIB_CRC32 */
/*[clinic input]
binascii.crc32 -> unsigned_int
@@ -765,9 +765,11 @@ static unsigned int
binascii_crc32_impl(PyObject *module, Py_buffer *data, unsigned int crc)
/*[clinic end generated code: output=52cf59056a78593b input=bbe340bc99d25aa8]*/
-#ifdef USE_ZLIB_CRC32
+#ifndef NO_ZLIB_CRC32
/* This is the same as zlibmodule.c zlib_crc32_impl. It exists in two
- * modules for historical reasons. */
+ modules for historical reasons. They should be consolidated however
+ this will not be possible for WASI does not have zlib.
+ */
{
/* Releasing the GIL for very small buffers is inefficient
and may lower performance */
@@ -798,7 +800,7 @@ binascii_crc32_impl(PyObject *module, Py_buffer *data, unsigned int crc)
}
return crc & 0xffffffff;
}
-#else /* USE_ZLIB_CRC32 */
+#else /* NO_ZLIB_CRC32 */
{
const unsigned char *bin_data = data->buf;
Py_ssize_t len = data->len;
@@ -815,7 +817,7 @@ binascii_crc32_impl(PyObject *module, Py_buffer *data, unsigned int crc)
return internal_crc32(bin_data, len, crc);
}
}
-#endif /* USE_ZLIB_CRC32 */
+#endif /* NO_ZLIB_CRC32 */
/*[clinic input]
binascii.b2a_hex
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index 9ebf58ae8a9bc4..89edcba8f5694f 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -457,9 +457,7 @@
-
- USE_ZLIB_CRC32;%(PreprocessorDefinitions)
-
+
diff --git a/configure b/configure
index 453b0123ded0a4..484877356369ac 100755
--- a/configure
+++ b/configure
@@ -21820,18 +21820,30 @@ else
printf "%s\n" "yes" >&6; }
have_zlib=yes
- printf "%s\n" "#define HAVE_ZLIB_COPY 1" >>confdefs.h
+ printf "%s\n" "#define HAVE_ZLIB_COPY 1" >>confdefs.h
fi
+case $ac_sys_system in #(
+ WASI) :
-if test "x$have_zlib" = xyes
-then :
+ BINASCII_CFLAGS="-DNO_ZLIB_CRC32"
+ BINASCII_LIBS=""
+ have_zlib=no
+ ;; #(
+ *) :
+
+ if test "$have_zlib" != "yes"; then
+ as_fn_error $? "zlib.h and libz are required. Install your OS's zlib-devel or zlib1g-dev equivalent library or get it from https://zlib.net/." "$LINENO" 5
+ fi
+ BINASCII_CFLAGS="$ZLIB_CFLAGS"
+ BINASCII_LIBS="$ZLIB_LIBS"
- BINASCII_CFLAGS="-DUSE_ZLIB_CRC32 $ZLIB_CFLAGS"
- BINASCII_LIBS="$ZLIB_LIBS"
+ ;; #(
+ *) :
+ ;;
+esac
-fi
@@ -35140,4 +35152,3 @@ if test "$ac_cv_header_stdatomic_h" != "yes"; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: Your compiler or platform does have a working C11 stdatomic.h. A future version of Python may require stdatomic.h." >&5
printf "%s\n" "$as_me: Your compiler or platform does have a working C11 stdatomic.h. A future version of Python may require stdatomic.h." >&6;}
fi
-
diff --git a/configure.ac b/configure.ac
index 234ae90616af62..2b0ef9f26cdee2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5323,14 +5323,13 @@ if test "$ac_cv_have_lchflags" = yes ; then
fi
dnl Check for compression libraries
-AH_TEMPLATE([HAVE_ZLIB_COPY], [Define if the zlib library has inflateCopy])
+AH_TEMPLATE([HAVE_ZLIB_COPY], [Define if the zlib library has inflateCopy; zlib 1.2.0 (2003) added inflateCopy.])
dnl detect zlib from Emscripten emport
PY_CHECK_EMSCRIPTEN_PORT([ZLIB], [-sUSE_ZLIB])
PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.0], [
have_zlib=yes
- dnl zlib 1.2.0 (2003) added inflateCopy
AC_DEFINE([HAVE_ZLIB_COPY], [1])
], [
WITH_SAVE_ENV([
@@ -5346,12 +5345,24 @@ PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.0], [
])
])
])
+AS_CASE([$ac_sys_system],
+ [WASI], [
+ BINASCII_CFLAGS="-DNO_ZLIB_CRC32"
+ BINASCII_LIBS=""
+ have_zlib=no
+ ],
+ [*], [
+ if test "$have_zlib" != "yes"; then
+ AC_MSG_ERROR([m4_normalize([
+ zlib.h and libz are required. Install your OS's zlib-devel or
+ zlib1g-dev equivalent library or get it from https://zlib.net/.
+ ])])
+ fi
+ BINASCII_CFLAGS="$ZLIB_CFLAGS"
+ BINASCII_LIBS="$ZLIB_LIBS"
+ ]
+)
-dnl binascii can use zlib for optimized crc32.
-AS_VAR_IF([have_zlib], [yes], [
- BINASCII_CFLAGS="-DUSE_ZLIB_CRC32 $ZLIB_CFLAGS"
- BINASCII_LIBS="$ZLIB_LIBS"
-])
dnl detect bzip2 from Emscripten emport
PY_CHECK_EMSCRIPTEN_PORT([BZIP2], [-sUSE_BZIP2])
@@ -8030,4 +8041,4 @@ if test "$ac_cv_header_stdatomic_h" != "yes"; then
Your compiler or platform does have a working C11 stdatomic.h. A future
version of Python may require stdatomic.h.
]))
-fi
+fi
\ No newline at end of file
diff --git a/pyconfig.h.in b/pyconfig.h.in
index 4295b4f5ea5fbd..ee4cac2ce8472a 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -1609,7 +1609,8 @@
/* Define to 1 if you have the 'writev' function. */
#undef HAVE_WRITEV
-/* Define if the zlib library has inflateCopy */
+/* Define if the zlib library has inflateCopy; zlib 1.2.0 (2003) added
+ inflateCopy. */
#undef HAVE_ZLIB_COPY
/* Define to 1 if you have the header file. */