diff --git a/Doc/library/lzma.rst b/Doc/library/lzma.rst index 69f7cb8d48d7ae..deec54fad9ed32 100644 --- a/Doc/library/lzma.rst +++ b/Doc/library/lzma.rst @@ -336,6 +336,39 @@ Miscellaneous feature set. +Information about the version of the lzma library in use is available through +the following constants: + + +.. data:: LZMA_VERSION +.. data:: LZMA_VERSION_STRING + + The version of the lzma C library actually loaded at runtime, in both + integer and string forms. + + .. versionadded:: 3.15 + +.. data:: LZMA_HEADER_VERSION +.. data:: LZMA_HEADER_VERSION_STRING + + The version of the lzma library that was used for building the module, in + both integer and string forms. This may be different from the lzma library + actually used at runtime. + + .. versionadded:: 3.15 + +The version number and string formats are as defined in by C library. The +integer is represented in decimal digits as ``jmmmppps`` where ``j`` is the +major version, ``mmm`` is the minor version, ``ppp`` is the patch level, and +``s`` is the "stability indicator" (2 means stable):: + + >>> import lzma + >>> lzma.LZMA_VERSION + 50020052 + >>> lzma.LZMA_VERSION_STRING + '5.2.5' + + .. _filter-chain-specs: Specifying custom filter chains @@ -357,12 +390,26 @@ options. Valid filter IDs are as follows: * Branch-Call-Jump (BCJ) filters: - * :const:`FILTER_X86` - * :const:`FILTER_IA64` - * :const:`FILTER_ARM` - * :const:`FILTER_ARMTHUMB` - * :const:`FILTER_POWERPC` - * :const:`FILTER_SPARC` + * :const:`!FILTER_X86` + * :const:`!FILTER_IA64` + * :const:`!FILTER_ARM` + * :const:`!FILTER_ARMTHUMB` + * :const:`!FILTER_POWERPC` + * :const:`!FILTER_SPARC` + + The above work on all lzma runtime library versions. + + * :const:`!FILTER_ARM64` + + Only works if the lzma version is 5.4.0 or later. + + .. versionadded:: 3.15 + + * :const:`!FILTER_RISCV` + + Only works if the lzma version is 5.6.0 or later. + + .. versionadded:: 3.15 A filter chain can consist of up to 4 filters, and cannot be empty. The last filter in the chain must be a compression filter, and any other filters must be diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 010abb7d9b9278..7533f62e54ab1d 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -206,6 +206,21 @@ difflib (Contributed by Jiahao Li in :gh:`134580`.) +lzma +---- + +* Add :const:`lzma.LZMA_VERSION`, :const:`lzma.LZMA_HEADER_VERSION`, and + related constants. They report the version of the underlying ``lzma`` library + found at runtime and the header versions used at build time. + (Contributed by Chien Wong in :gh:`115988`.) + +* Add support of new BCJ filters ARM64 and RISC-V via + :const:`!lzma.FILTER_ARM64` and :const:`!lzma.FILTER_RISCV`. Note that the + new filters will work only if runtime library supports them. ARM64 filter + requires ``lzma`` 5.4.0 or newer while RISC-V requires 5.6.0 or newer. + (Contributed by Chien Wong in :gh:`115988`.) + + math ---- diff --git a/Lib/lzma.py b/Lib/lzma.py index 316066d024ea02..5f44a43224baa3 100644 --- a/Lib/lzma.py +++ b/Lib/lzma.py @@ -13,7 +13,10 @@ "CHECK_ID_MAX", "CHECK_UNKNOWN", "FILTER_LZMA1", "FILTER_LZMA2", "FILTER_DELTA", "FILTER_X86", "FILTER_IA64", "FILTER_ARM", "FILTER_ARMTHUMB", "FILTER_POWERPC", "FILTER_SPARC", + "FILTER_ARM64", "FILTER_RISCV", "FORMAT_AUTO", "FORMAT_XZ", "FORMAT_ALONE", "FORMAT_RAW", + "LZMA_HEADER_VERSION", "LZMA_HEADER_VERSION_STRING", + "LZMA_VERSION", "LZMA_VERSION_STRING", "MF_HC3", "MF_HC4", "MF_BT2", "MF_BT3", "MF_BT4", "MODE_FAST", "MODE_NORMAL", "PRESET_DEFAULT", "PRESET_EXTREME", diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index e93c3c37354e27..fca852dcd4faa9 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -383,6 +383,20 @@ def test_uninitialized_LZMADecompressor_crash(self): self.assertEqual(LZMADecompressor.__new__(LZMADecompressor). decompress(bytes()), b'') + def test_riscv_filter_constant_exists(self): + self.assertTrue(lzma.FILTER_RISCV) + + def test_arm64_filter_constant_exists(self): + self.assertTrue(lzma.FILTER_ARM64) + + def test_lzma_header_versions(self): + self.assertIsInstance(lzma.LZMA_HEADER_VERSION_STRING, str) + self.assertGreater(lzma.LZMA_HEADER_VERSION, 0) + + def test_lzma_versions(self): + self.assertIsInstance(lzma.LZMA_VERSION_STRING, str) + self.assertGreater(lzma.LZMA_VERSION, 0) + class CompressDecompressFunctionTestCase(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2024-03-17-22-16-53.gh-issue-115988.EshSDM.rst b/Misc/NEWS.d/next/Library/2024-03-17-22-16-53.gh-issue-115988.EshSDM.rst new file mode 100644 index 00000000000000..e0d18a88d49d8b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-03-17-22-16-53.gh-issue-115988.EshSDM.rst @@ -0,0 +1,2 @@ +:mod:`lzma` gains C library version info constants and adds constants to +support the newer BCJ filters for ARM64 and RISC-V. diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c index 462c2181fa6036..a5ffb3996c3477 100644 --- a/Modules/_lzmamodule.c +++ b/Modules/_lzmamodule.c @@ -25,6 +25,19 @@ #error "The maximum block size accepted by liblzma is SIZE_MAX." #endif + +/* + * If the lzma.h we're building against is so old as not to define these, this + * provides their equivalent values so that the names remain defined in Python + * regardless of the header versions used at build time. + */ +#ifndef LZMA_FILTER_ARM64 +#define LZMA_FILTER_ARM64 LZMA_VLI_C(0x0A) +#endif +#ifndef LZMA_FILTER_RISCV +#define LZMA_FILTER_RISCV LZMA_VLI_C(0x0B) +#endif + /* On success, return value >= 0 On failure, return -1 */ static inline Py_ssize_t @@ -379,6 +392,8 @@ lzma_filter_converter(_lzma_state *state, PyObject *spec, void *ptr) case LZMA_FILTER_ARM: case LZMA_FILTER_ARMTHUMB: case LZMA_FILTER_SPARC: + case LZMA_FILTER_ARM64: + case LZMA_FILTER_RISCV: f->options = parse_filter_spec_bcj(state, spec); return f->options != NULL; default: @@ -497,7 +512,9 @@ build_filter_spec(const lzma_filter *f) case LZMA_FILTER_IA64: case LZMA_FILTER_ARM: case LZMA_FILTER_ARMTHUMB: - case LZMA_FILTER_SPARC: { + case LZMA_FILTER_SPARC: + case LZMA_FILTER_ARM64: + case LZMA_FILTER_RISCV: { lzma_options_bcj *options = f->options; if (options) { ADD_FIELD(options, start_offset); @@ -1559,6 +1576,8 @@ lzma_exec(PyObject *module) ADD_INT_PREFIX_MACRO(module, FILTER_ARMTHUMB); ADD_INT_PREFIX_MACRO(module, FILTER_SPARC); ADD_INT_PREFIX_MACRO(module, FILTER_POWERPC); + ADD_INT_PREFIX_MACRO(module, FILTER_ARM64); + ADD_INT_PREFIX_MACRO(module, FILTER_RISCV); ADD_INT_PREFIX_MACRO(module, MF_HC3); ADD_INT_PREFIX_MACRO(module, MF_HC4); ADD_INT_PREFIX_MACRO(module, MF_BT2); @@ -1599,6 +1618,27 @@ lzma_exec(PyObject *module) return -1; } + if (PyModule_AddStringConstant( + module, "LZMA_HEADER_VERSION_STRING", + LZMA_VERSION_STRING) < 0) { + return -1; + } + if (PyModule_AddStringConstant( + module, "LZMA_VERSION_STRING", + lzma_version_string()) < 0) { + return -1; + } + PyObject *uint32_obj = PyLong_FromUnsignedLong(LZMA_VERSION); + if (PyModule_AddObject(module, "LZMA_HEADER_VERSION", uint32_obj) < 0) { + Py_XDECREF(uint32_obj); + return -1; + } + uint32_obj = PyLong_FromUnsignedLong(lzma_version_number()); + if (PyModule_AddObject(module, "LZMA_VERSION", uint32_obj) < 0) { + Py_XDECREF(uint32_obj); + return -1; + } + return 0; }