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. */