From b6fb5c91d5cb7da96d47f05a28255bb6fcbe98ca Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Tue, 10 Jun 2025 12:05:24 +0100 Subject: [PATCH 1/6] Update versions for next release and removed release date information. Including release dates in git files complicates the release process and is not particularly useful. --- .github/ISSUE_TEMPLATE/bug_report.yml | 9 ++------- changes.txt | 5 ++++- docs/vars.rst | 8 +++----- docs/version.rst | 2 +- src/__init__.py | 9 ++++----- 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 66304973e..cf7b71470 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -46,14 +46,9 @@ body: attributes: label: PyMuPDF version options: + - 1.26.1 - 1.26.0 - - 1.25.5 - - 1.25.4 - - 1.25.3 - - 1.25.2 - - 1.25.1 - - 1.25.0 - - 1.24.x or earlier + - 1.25.x or earlier - Built from source description: | * For example from `pymupdf.VersionBind`. diff --git a/changes.txt b/changes.txt index fbf36ad62..fc2920c8f 100644 --- a/changes.txt +++ b/changes.txt @@ -2,7 +2,7 @@ Change Log ========== -**Changes in version 1.26.1 ()** +**Changes in version 1.26.1** * Use MuPDF-1.26.2. @@ -18,6 +18,9 @@ Change Log * New method `Document.rewrite_images()`, useful for reducing file size, changing image formats, or converting color spaces. * `Page.get_text()`: restrict positional args to match docs. * Removed bogus definition of class `Shape`. + * Removed release date from module, docs and changelog. + * `pymupdf.pymupdf_date` and `pymupdf.VersionDate` are now both None. + * They will be removed in a future release. **Changes in version 1.26.0 (2025-05-22)** diff --git a/docs/vars.rst b/docs/vars.rst index dc86c9233..cc0e8a5fc 100644 --- a/docs/vars.rst +++ b/docs/vars.rst @@ -77,10 +77,8 @@ Constants .. py:data:: pymupdf_date - ISO timestamp *YYYY-MM-DD HH:MM:SS* when these bindings were built. - - :type: string - + Disabled (set to None) in 1.26.1. + .. py:data:: version (pymupdf_version, mupdf_version, timestamp) -- combined version information where `timestamp` is the generation point in time formatted as "YYYYMMDDhhmmss". @@ -97,7 +95,7 @@ Constants .. py:data:: VersionDate - Legacy equivalent to `mupdf_version`. + Disabled (set to None) in 1.26.1. .. _PermissionCodes: diff --git a/docs/version.rst b/docs/version.rst index 8208892e3..0e6a48a25 100644 --- a/docs/version.rst +++ b/docs/version.rst @@ -1,6 +1,6 @@ ---- -This documentation covers **PyMuPDF v1.26.0** features as of **2025-05-22 00:00:01**. +This documentation covers **PyMuPDF v1.26.1**. The major and minor versions of |PyMuPDF| and |MuPDF| will always be the same. Only the third qualifier (patch level) may deviate from that of |MuPDF|. diff --git a/src/__init__.py b/src/__init__.py index db0e69a13..ae7863dbb 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -375,9 +375,9 @@ def _int_rc(text): # Basic version information. # -pymupdf_version = "1.26.0" +pymupdf_version = "1.26.1" mupdf_version = mupdf.FZ_VERSION -pymupdf_date = "2025-05-22 00:00:01" +pymupdf_date = None # Versions as tuples; useful when comparing versions. # @@ -389,11 +389,10 @@ def _int_rc(text): # Legacy version information. # -pymupdf_date2 = pymupdf_date.replace('-', '').replace(' ', '').replace(':', '') -version = (pymupdf_version, mupdf_version, pymupdf_date2) +version = (pymupdf_version, mupdf_version, None) VersionFitz = mupdf_version VersionBind = pymupdf_version -VersionDate = pymupdf_date +VersionDate = None # String formatting. From d76da6b604ded6fcc52e48ca2d5456ffade4adce Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Tue, 10 Jun 2025 15:00:01 +0100 Subject: [PATCH 2/6] tests/run_compound.py: show more system/platform/python information before running pytest. --- tests/run_compound.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/run_compound.py b/tests/run_compound.py index 7598237cd..2d204072a 100755 --- a/tests/run_compound.py +++ b/tests/run_compound.py @@ -82,6 +82,20 @@ def main(): for i in implementations: log(f'run_compound.py: {i=}') + + cpu_bits = int.bit_length(sys.maxsize+1) + log(f'{os.getcwd()=}') + log(f'{platform.machine()=}') + log(f'{platform.platform()=}') + log(f'{platform.python_version()=}') + log(f'{platform.system()=}') + log(f'{platform.uname()=}') + log(f'{sys.executable=}') + log(f'{sys.version=}') + log(f'{sys.version_info=}') + log(f'{list(sys.version_info)=}') + log(f'{cpu_bits=}') + timeout = None if endtime: timeout = max(0, endtime - time.time()) From 8583396bfeefad96245d4e63c9dc2cb9937f9a92 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Wed, 11 Jun 2025 16:25:14 +0100 Subject: [PATCH 3/6] setup.py: build dummy wheel/sdist if PYMUPDF_SETUP_DUMMY=1. --- setup.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/setup.py b/setup.py index 3d6a01f2e..86cc15f76 100755 --- a/setup.py +++ b/setup.py @@ -43,6 +43,9 @@ Location of devenv.com on Windows. If unset we search for it - see wdev.py. if that fails we use just 'devenv.com'. + PYMUPDF_SETUP_DUMMY + If 1, we build dummy sdist and wheel with no files. + PYMUPDF_SETUP_FLAVOUR Control building of separate wheels for PyMuPDF. @@ -248,6 +251,10 @@ def run(command, check=1): PYMUPDF_SETUP_URL_WHEEL = os.environ.get('PYMUPDF_SETUP_URL_WHEEL') log(f'{PYMUPDF_SETUP_URL_WHEEL=}') +PYMUPDF_SETUP_DUMMY = os.environ.get('PYMUPDF_SETUP_DUMMY') +log(f'{PYMUPDF_SETUP_DUMMY=}') + + def _fs_remove(path): ''' Removes file or directory, without raising exception if it doesn't exist. @@ -588,6 +595,10 @@ def build(): ''' pipcl.py `build_fn()` callback. ''' + if PYMUPDF_SETUP_DUMMY == '1': + log(f'{PYMUPDF_SETUP_DUMMY=} Building dummy wheel with no files.') + return list() + # Download MuPDF. # mupdf_local, mupdf_location = get_mupdf() @@ -1175,6 +1186,8 @@ def _extension_flags( mupdf_local, mupdf_build_dir, build_type): def sdist(): ret = list() + if PYMUPDF_SETUP_DUMMY == '1': + return ret if PYMUPDF_SETUP_FLAVOUR == 'b': # Create a minimal sdist that will build/install a dummy PyMuPDFb. From af99a8e9cea6897bda0e8506fe02c3d300d3d357 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Wed, 11 Jun 2025 16:29:10 +0100 Subject: [PATCH 4/6] .github/workflows/test.yml: also copy sdist into artifact. --- .github/workflows/test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9815f9326..3a02c1fee 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -58,5 +58,7 @@ jobs: # - uses: actions/upload-artifact@v4 with: - path: ./wheelhouse/pymupdf*.whl + path: | + wheelhouse/pymupdf*.whl + wheelhouse/pymupdf*.tar.gz name: artifact-${{ matrix.os }} From f8f979d5f4c9de5911e304cdabdc432d2cdbdeb8 Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Tue, 10 Jun 2025 14:58:30 +0100 Subject: [PATCH 5/6] scripts/test.py: Added support for building releases. * Show all output if using valgrind. * cibw - do not build linux-aarch64 by default. * Added support for building releases - new args --cibw-release-1 and --cibw-release-2. --- scripts/test.py | 83 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/scripts/test.py b/scripts/test.py index 03428023a..a0529e62b 100755 --- a/scripts/test.py +++ b/scripts/test.py @@ -64,6 +64,10 @@ its own new venv to build PyMuPDF. Otherwise we force pip to use the current venv. + --cibw-archs-linux + Set CIBW_ARCHS_LINUX, e.g. to `auto64 aarch64`. Default is `auto64` so + this allows control over whether to build linux-aarch64 wheels. + --cibw-name Name to use when installing cibuildwheel, e.g.: --cibw-name cibuildwheel==3.0.0b1 @@ -75,9 +79,20 @@ ...` fails with: emcc: error: binary: No such file or directory ("binary" was expected to be an input file, based on the commandline arguments provided) + --cibw-release-1 + Set up so that `cibw` builds all wheels except linux-aarch64, and sdist + if on Linux. + + --cibw-release-2 + Set up so that `cibw` builds only linux-aarch64 wheel. + -d Equivalent to `-b debug`. + --dummy + Sets PYMUPDF_SETUP_DUMMY=1 which makes setup.py build a dummy wheel + with no content. For internal testing only. + -e = Add to environment used in build and test commands. Can be specified multiple times. @@ -201,11 +216,6 @@ If CIBW_ARCHS is unset we set $CIBW_ARCHS_WINDOWS, $CIBW_ARCHS_MACOS and $CIBW_ARCHS_LINUX to auto64 if they are unset. - - Additionally, if running on Github ($GITHUB_ACTIONS=true) and - $CIBW_ARCHS_LINUX is unset, we set $CIBW_ARCHS_LINUX to 'auto64 - aarch64' so that we build for aarch64 using emulation. This is required - as of 2025-05-23 because there is no native aarch64 host available. install Install with `pip install --force-reinstall `. @@ -269,10 +279,6 @@ def main(argv): if github_workflow_unimportant(): return - if len(argv) == 1: - show_help() - return - build_isolation = None cibw_name = 'cibuildwheel' cibw_pyodide = None @@ -286,6 +292,7 @@ def main(argv): pyodide_build_version = None pytest_options = '' pytest_prefix = None + cibw_sdist = None show_args = False show_help = False sync_paths = False @@ -330,6 +337,20 @@ def main(argv): elif arg == '--build-isolation': build_isolation = int(next(args)) + elif arg == '--cibw-release-1': + cibw_sdist = True + env_extra['CIBW_ARCHS_LINUX'] = 'auto64' + env_extra['CIBW_ARCHS_MACOS'] = 'auto64' + env_extra['CIBW_ARCHS_WINDOWS'] = 'auto' # win32 and win64. + env_extra['CIBW_SKIP'] = 'pp* *i686 cp36* cp37* *musllinux*aarch64*' + + elif arg == '--cibw-release-2': + env_extra['CIBW_ARCHS_LINUX'] = 'aarch64' + os_names = ['linux'] + + elif arg == '--cibw-archs-linux': + env_extra['CIBW_ARCHS_LINUX'] = next(args) + elif arg == '--cibw-name': cibw_name = next(args) @@ -339,6 +360,10 @@ def main(argv): elif arg == '-d': env_extra['PYMUPDF_SETUP_MUPDF_BUILD_TYPE'] = 'debug' + elif arg == '--dummy': + env_extra['PYMUPDF_SETUP_DUMMY'] = '1' + env_extra['CIBW_TEST_COMMAND'] = '' + elif arg == '-e': _nv = next(args) assert '=' in _nv, f'-e = does not contain "=": {_nv!r}' @@ -493,7 +518,7 @@ def main(argv): elif command == 'cibw': # Build wheel(s) with cibuildwheel. - cibuildwheel(env_extra, cibw_name, cibw_pyodide) + cibuildwheel(env_extra, cibw_name, cibw_pyodide, cibw_sdist) elif command.startswith('install.'): name = command[len('install.'):] @@ -629,27 +654,33 @@ def build( run(f'pip install{build_isolation_text} -v --force-reinstall {pymupdf_dir_abs}', env_extra=env_extra) -def cibuildwheel(env_extra, cibw_name, cibw_pyodide): +def cibuildwheel(env_extra, cibw_name, cibw_pyodide, cibw_sdist): + + if cibw_sdist and platform.system() == 'Linux': + log(f'Building sdist.') + run(f'cd {pymupdf_dir_abs} && {sys.executable} setup.py -d wheelhouse sdist', env_extra=env_extra) + sdists = glob.glob(f'{pymupdf_dir_abs}/wheelhouse/pymupdf-*.tar.gz') + log(f'{sdists=}') + assert sdists + run(f'pip install --upgrade {cibw_name}') # Some general flags. - env_extra['CIBW_BUILD_VERBOSITY'] = '1' - env_extra['CIBW_SKIP'] = 'pp* *i686 cp36* cp37* *musllinux* *-win32 *-aarch64' + if 'CIBW_BUILD_VERBOSITY' not in env_extra: + env_extra['CIBW_BUILD_VERBOSITY'] = '1' + if 'CIBW_SKIP' not in env_extra: + env_extra['CIBW_SKIP'] = 'pp* *i686 cp36* cp37* *musllinux* *-win32 *-aarch64' # Set what wheels to build, if not already specified. - if os.environ.get('CIBW_ARCHS') is None: - if os.environ.get('CIBW_ARCHS_WINDOWS') is None: + if 'CIBW_ARCHS' not in env_extra: + if 'CIBW_ARCHS_WINDOWS' not in env_extra: env_extra['CIBW_ARCHS_WINDOWS'] = 'auto64' - if os.environ.get('CIBW_ARCHS_MACOS') is None: + if 'CIBW_ARCHS_MACOS' not in env_extra: env_extra['CIBW_ARCHS_MACOS'] = 'auto64' - if os.environ.get('CIBW_ARCHS_LINUX') is None: + if 'CIBW_ARCHS_LINUX' not in env_extra: env_extra['CIBW_ARCHS_LINUX'] = 'auto64' - if os.environ.get('GITHUB_ACTIONS') == 'true': - # Special case to use emulation/cross-compilation of - # aarch64 on Linux. - env_extra['CIBW_ARCHS_LINUX'] += ' aarch64' # Tell cibuildwheel not to use `auditwheel` on Linux and MacOS, # because it cannot cope with us deliberately having required @@ -664,7 +695,8 @@ def cibuildwheel(env_extra, cibw_name, cibw_pyodide): env_extra['CIBW_REPAIR_WHEEL_COMMAND_MACOS'] = '' # Tell cibuildwheel how to test PyMuPDF. - env_extra['CIBW_TEST_COMMAND'] = f'python {{project}}/scripts/test.py test' + if 'CIBW_TEST_COMMAND' not in env_extra: + env_extra['CIBW_TEST_COMMAND'] = f'python {{project}}/scripts/test.py test' # Specify python versions. CIBW_BUILD = env_extra.get('CIBW_BUILD') @@ -910,11 +942,8 @@ def getmtime(path): return pymupdf_dir_rel = gh_release.relpath(pymupdf_dir) - if pytest_options is None: - if valgrind: - pytest_options = '-s -vv' - else: - pytest_options = '' + if not pytest_options and pytest_prefix == 'valgrind': + pytest_options = '-sv' if pytest_k: pytest_options += f' -k {shlex.quote(pytest_k)}' pytest_arg = '' From 70acd3109a6b17d20e714e68d9b98e2625fc712b Mon Sep 17 00:00:00 2001 From: Julian Smith Date: Wed, 11 Jun 2025 18:43:25 +0100 Subject: [PATCH 6/6] tests/: update expectations for latest mupdf master. --- tests/resources/test_2608_expected | 12 ++++++++---- tests/resources/test_2608_expected_1.26 | 10 ++++++++++ tests/test_font.py | 7 ++++++- tests/test_textextract.py | 5 ++++- 4 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 tests/resources/test_2608_expected_1.26 diff --git a/tests/resources/test_2608_expected b/tests/resources/test_2608_expected index d550f1237..10cfa6127 100644 --- a/tests/resources/test_2608_expected +++ b/tests/resources/test_2608_expected @@ -1,10 +1,14 @@ No significant gamma-ray excess above the expected background is detected from the direction of FRB 20171019A, with 52 gamma candidate events from the source region and 524 background event. -A second analysis using an independent event calibration and reconstruction (Parsons & Hinton 2014) confirms this result. A search for -variable emission on timescales ranging from milliseconds to several minutes with tools provided in (Brun et al. 2020) does not reveal -any variability above 2.2 𝜎. For the total data set of 1.8 h, 95% confidence level (C. L.) upper limits on the photon flux are derived using +A second analysis using an independent event calibration and recon- +struction (Parsons & Hinton 2014) confirms this result. A search for +variable emission on timescales ranging from milliseconds to sev- +eral minutes with tools provided in (Brun et al. 2020) does not reveal +any variability above 2.2 𝜎. For the total data set of 1.8 h, 95% confi- +dence level (C. L.) upper limits on the photon flux are derived using the method described by Rolke et al. (2005). The energy threshold -of the data is highly dependent on the zenith angle of the observations. For these observations, the zenith angles range from 15 to 25 +of the data is highly dependent on the zenith angle of the observa- +tions. For these observations, the zenith angles range from 15 to 25 deg, which leads to an energy threshold for the stacked data set of 𝐸th = 120 GeV. The upper limit on the Very High Energy (VHE) diff --git a/tests/resources/test_2608_expected_1.26 b/tests/resources/test_2608_expected_1.26 new file mode 100644 index 000000000..d550f1237 --- /dev/null +++ b/tests/resources/test_2608_expected_1.26 @@ -0,0 +1,10 @@ +No significant gamma-ray excess above the expected background +is detected from the direction of FRB 20171019A, with 52 gamma +candidate events from the source region and 524 background event. +A second analysis using an independent event calibration and reconstruction (Parsons & Hinton 2014) confirms this result. A search for +variable emission on timescales ranging from milliseconds to several minutes with tools provided in (Brun et al. 2020) does not reveal +any variability above 2.2 𝜎. For the total data set of 1.8 h, 95% confidence level (C. L.) upper limits on the photon flux are derived using +the method described by Rolke et al. (2005). The energy threshold +of the data is highly dependent on the zenith angle of the observations. For these observations, the zenith angles range from 15 to 25 +deg, which leads to an energy threshold for the stacked data set of +𝐸th = 120 GeV. The upper limit on the Very High Energy (VHE) diff --git a/tests/test_font.py b/tests/test_font.py index 4cc857751..58396984c 100644 --- a/tests/test_font.py +++ b/tests/test_font.py @@ -68,7 +68,12 @@ def test_2608(): with open(os.path.abspath(f'{__file__}/../../tests/test_2608_out'), 'wb') as f: f.write(text.encode('utf8')) path_expected = os.path.normpath(f'{__file__}/../../tests/resources/test_2608_expected') - with open(path_expected, 'rb') as f: + path_expected_1_26 = os.path.normpath(f'{__file__}/../../tests/resources/test_2608_expected_1.26') + if pymupdf.mupdf_version_tuple >= (1, 27): + path_expected2 = path_expected + else: + path_expected2 = path_expected_1_26 + with open(path_expected2, 'rb') as f: expected = f.read().decode('utf8') # Github windows x32 seems to insert \r characters; maybe something to # do with the Python installation's line endings settings. diff --git a/tests/test_textextract.py b/tests/test_textextract.py index 64491667d..c62f9a72b 100644 --- a/tests/test_textextract.py +++ b/tests/test_textextract.py @@ -887,7 +887,10 @@ def test_4503(): strikeout = span_0['char_flags'] & pymupdf.mupdf.FZ_STEXT_STRIKEOUT print(f'{strikeout=}') - if pymupdf.mupdf_version_tuple >= (1, 26, 2): + if pymupdf.mupdf_version_tuple >= (1, 27): + assert strikeout, f'Expected bit 0 (FZ_STEXT_STRIKEOUT) to be set in {span_0["char_flags"]=:#x}.' + assert text_0 == 'the right to request the state to review and, if appropriate,' + elif pymupdf.mupdf_version_tuple >= (1, 26, 2): # 2025-06-09: This is still incorrect - the span should include the # following text 'and, if appropriate,'. It looks like following spans # are: