Mercurial > hgrepos > Python2 > PyMuPDF
comparison scripts/test.py @ 41:71bcc18e306f
MERGE: New upstream PyMuPDF v1.26.5 including MuPDF v1.26.10
BUGS: Needs some additional changes yet.
Not yet tested.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Sat, 11 Oct 2025 15:24:40 +0200 |
| parents | a6bc019ac0b2 |
| children |
comparison
equal
deleted
inserted
replaced
| 38:8934ac156ef5 | 41:71bcc18e306f |
|---|---|
| 2 | 2 |
| 3 '''Developer build/test script for PyMuPDF. | 3 '''Developer build/test script for PyMuPDF. |
| 4 | 4 |
| 5 Examples: | 5 Examples: |
| 6 | 6 |
| 7 ./PyMuPDF/scripts/test.py --m mupdf build test | 7 ./PyMuPDF/scripts/test.py -m mupdf build test |
| 8 Build and test with pre-existing local mupdf/ checkout. | 8 Build and test with pre-existing local mupdf/ checkout. |
| 9 | 9 |
| 10 ./PyMuPDF/scripts/test.py build test | 10 ./PyMuPDF/scripts/test.py build test |
| 11 Build and test with default internal download of mupdf. | 11 Build and test with default internal download of mupdf. |
| 12 | 12 |
| 13 ./PyMuPDF/scripts/test.py -m 'git:https://git.ghostscript.com/mupdf.git' build test | 13 ./PyMuPDF/scripts/test.py -m 'git:https://git.ghostscript.com/mupdf.git' build test |
| 14 Build and test with internal checkout of MuPDF master. | 14 Build and test with internal checkout of MuPDF master. |
| 15 | 15 |
| 16 ./PyMuPDF/scripts/test.py -m 'git:--branch 1.26.x https://github.com/ArtifexSoftware/mupdf.git' build test | 16 ./PyMuPDF/scripts/test.py -m ':1.26.x' build test |
| 17 Build and test using internal checkout of mupdf 1.26.x branch from | 17 Build and test using internal checkout of mupdf 1.26.x branch from |
| 18 Github. | 18 Github. |
| 19 | |
| 20 ./PyMuPDF/scripts/test.py install test -i 1.26.3 -k test_2596 | |
| 21 Install pymupdf-1.26.3 from pupi.org and test only test_2596. | |
| 19 | 22 |
| 20 Usage: | 23 Usage: |
| 21 | 24 |
| 22 * Command line arguments are called parameters if they start with `-`, | 25 * Command line arguments are called parameters if they start with `-`, |
| 23 otherwise they are called commands. | 26 otherwise they are called commands. |
| 29 them on the command line. | 32 them on the command line. |
| 30 | 33 |
| 31 Other: | 34 Other: |
| 32 | 35 |
| 33 * If we are not already running inside a Python venv, we automatically create a | 36 * If we are not already running inside a Python venv, we automatically create a |
| 34 venv and re-run ourselves inside it. | 37 venv and re-run ourselves inside it (also see the -v option). |
| 35 * Build/wheel/install commands always install into the venv. | 38 * Build/wheel/install commands always install into the venv. |
| 36 * Tests use whatever PyMuPDF/MuPDF is currently installed in the venv. | 39 * Tests use whatever PyMuPDF/MuPDF is currently installed in the venv. |
| 37 * We run tests with pytest. | 40 * We run tests with pytest. |
| 38 | 41 |
| 39 * One can generate call traces by setting environment variables in debug | 42 * One can generate call traces by setting environment variables in debug |
| 53 'release', 'debug', 'memento'. [This makes `build` set environment | 56 'release', 'debug', 'memento'. [This makes `build` set environment |
| 54 variable `PYMUPDF_SETUP_MUPDF_BUILD_TYPE`, which is used by PyMuPDF's | 57 variable `PYMUPDF_SETUP_MUPDF_BUILD_TYPE`, which is used by PyMuPDF's |
| 55 `setup.py`.] | 58 `setup.py`.] |
| 56 | 59 |
| 57 --build-flavour <build_flavour> | 60 --build-flavour <build_flavour> |
| 61 [Obsolete.] | |
| 58 Combination of 'p', 'b', 'd'. See ../setup.py's description of | 62 Combination of 'p', 'b', 'd'. See ../setup.py's description of |
| 59 PYMUPDF_SETUP_FLAVOUR. Default is 'pbd', i.e. self-contained PyMuPDF | 63 PYMUPDF_SETUP_FLAVOUR. Default is 'pbd', i.e. self-contained PyMuPDF |
| 60 wheels including MuPDF build-time files. | 64 wheels including MuPDF build-time files. |
| 61 | 65 |
| 62 --build-isolation 0|1 | 66 --build-isolation 0|1 |
| 69 this allows control over whether to build linux-aarch64 wheels. | 73 this allows control over whether to build linux-aarch64 wheels. |
| 70 | 74 |
| 71 --cibw-name <cibw_name> | 75 --cibw-name <cibw_name> |
| 72 Name to use when installing cibuildwheel, e.g.: | 76 Name to use when installing cibuildwheel, e.g.: |
| 73 --cibw-name cibuildwheel==3.0.0b1 | 77 --cibw-name cibuildwheel==3.0.0b1 |
| 78 --cibw-name git+https://github.com/pypa/cibuildwheel | |
| 74 Default is `cibuildwheel`, i.e. the current release. | 79 Default is `cibuildwheel`, i.e. the current release. |
| 75 | 80 |
| 76 --cibw-pyodide 0|1 | 81 --cibw-pyodide 0|1 |
| 77 Experimental, make `cibuild` command build a pyodide wheel. | 82 Experimental, make `cibw` command build a pyodide wheel. |
| 78 2025-05-27: this fails when building mupdf C API - `ld -r -b binary | 83 2025-05-27: this fails when building mupdf C API - `ld -r -b binary |
| 79 ...` fails with: | 84 ...` fails with: |
| 80 emcc: error: binary: No such file or directory ("binary" was expected to be an input file, based on the commandline arguments provided) | 85 emcc: error: binary: No such file or directory ("binary" was expected to be an input file, based on the commandline arguments provided) |
| 81 | 86 |
| 82 --cibw-pyodide-version <cibw_pyodide_version> | 87 --cibw-pyodide-version <cibw_pyodide_version> |
| 88 if on Linux. | 93 if on Linux. |
| 89 | 94 |
| 90 --cibw-release-2 | 95 --cibw-release-2 |
| 91 Set up so that `cibw` builds only linux-aarch64 wheel. | 96 Set up so that `cibw` builds only linux-aarch64 wheel. |
| 92 | 97 |
| 98 --cibw-skip-add-defaults 0|1 | |
| 99 If 1 (the default) we add defaults to CIBW_SKIP such as `pp*` (to | |
| 100 exclude pypy) and `cp3??t-*` (to exclude free-threading). | |
| 101 | |
| 102 --cibw-test-project 0|1 | |
| 103 If 1, command `cibw` will use a minimal test project instead of the | |
| 104 PyMuPDF directory itself. | |
| 105 | |
| 106 The test project uses setjmp/longjmp and C++ throw/catch. | |
| 107 | |
| 108 The test checks for current behaviour, so with `--cibw-pyodide 1` it | |
| 109 succeeds if the cibw command fails with the expected error message. | |
| 110 | |
| 111 2025-08-22: | |
| 112 Builds ok on Linux. | |
| 113 | |
| 114 Fails at runtime with --cibw-pyodide 1: | |
| 115 | |
| 116 With compile/link flags ``: | |
| 117 (+45.0s): remote.py:233:main: jules-devuan: Pyodide has suffered a fatal error. Please report this to the Pyodide maintainers. | |
| 118 (+45.1s): remote.py:233:main: jules-devuan: Stack (most recent call first): | |
| 119 (+45.1s): remote.py:233:main: jules-devuan: File "/tmp/cibw-run-h_pfo0wf/cp312-pyodide_wasm32/venv-test/lib/python3.12/site-packages/foo/__init__.py", line 63 in bar | |
| 120 (+45.1s): remote.py:233:main: jules-devuan: File "<string>The cause of the fatal error was: | |
| 121 (+45.1s): remote.py:233:main: jules-devuan: CppException std::runtime_error: deliberate exception | |
| 122 (+45.1s): remote.py:233:main: jules-devuan: at convertCppException (/home/jules/.cache/cibuildwheel/pyodide-build-0.30.7/0.27.7/xbuildenv/pyodide-root/dist/pyodide.asm.js:10:48959) | |
| 123 (+45.1s): remote.py:233:main: jules-devuan: at API.fatal_error (/home/jules/.cache/cibuildwheel/pyodide-build-0.30.7/0.27.7/xbuildenv/pyodide-root/dist/pyodide.asm.js:10:49253) | |
| 124 (+45.1s): remote.py:233:main: jules-devuan: at main (file:///home/jules/.cache/cibuildwheel/pyodide-build-0.30.7/0.27.7/xbuildenv/pyodide-root/dist/python_cli_entry.mjs:149:13) { | |
| 125 (+45.1s): remote.py:233:main: jules-devuan: ty: 'std::runtime_error', | |
| 126 (+45.1s): remote.py:233:main: jules-devuan: pyodide_fatal_error: true | |
| 127 (+45.1s): remote.py:233:main: jules-devuan: } | |
| 128 (+45.1s): remote.py:233:main: jules-devuan: ", line 1 in <module> | |
| 129 (+45.1s): remote.py:233:main: jules-devuan: CppException std::runtime_error: deliberate exception | |
| 130 (+45.1s): remote.py:233:main: jules-devuan: at convertCppException (/home/jules/.cache/cibuildwheel/pyodide-build-0.30.7/0.27.7/xbuildenv/pyodide-root/dist/pyodide.asm.js:10:48959) | |
| 131 (+45.1s): remote.py:233:main: jules-devuan: at API.fatal_error (/home/jules/.cache/cibuildwheel/pyodide-build-0.30.7/0.27.7/xbuildenv/pyodide-root/dist/pyodide.asm.js:10:49253) | |
| 132 (+45.1s): remote.py:233:main: jules-devuan: at main (file:///home/jules/.cache/cibuildwheel/pyodide-build-0.30.7/0.27.7/xbuildenv/pyodide-root/dist/python_cli_entry.mjs:149:13) { | |
| 133 (+45.1s): remote.py:233:main: jules-devuan: ty: 'std::runtime_error', | |
| 134 (+45.1s): remote.py:233:main: jules-devuan: pyodide_fatal_error: true | |
| 135 (+45.1s): remote.py:233:main: jules-devuan: } | |
| 136 | |
| 137 With compile/link flags `-fwasm-exceptions`: | |
| 138 [LinkError: WebAssembly.instantiate(): Import #60 module="env" function="__c_longjmp": tag import requires a WebAssembly.Tag] | |
| 139 | |
| 140 With compile/link flags `-fwasm-exceptions -sSUPPORT_LONGJMP=wasm`: | |
| 141 [LinkError: WebAssembly.instantiate(): Import #60 module="env" function="__c_longjmp": tag import requires a WebAssembly.Tag] | |
| 142 | |
| 143 --cibw-test-project-setjmp 0|1 | |
| 144 If 1, --cibw-test-project builds a project that uses | |
| 145 setjmp/longjmp. Default is 0 (Windows builds fail when attempting to | |
| 146 compile the output from swig). | |
| 147 | |
| 93 -d | 148 -d |
| 94 Equivalent to `-b debug`. | 149 Equivalent to `-b debug`. |
| 95 | 150 |
| 96 --dummy | 151 --dummy |
| 97 Sets PYMUPDF_SETUP_DUMMY=1 which makes setup.py build a dummy wheel | 152 Sets PYMUPDF_SETUP_DUMMY=1 which makes setup.py build a dummy wheel |
| 101 Add to environment used in build and test commands. Can be specified | 156 Add to environment used in build and test commands. Can be specified |
| 102 multiple times. | 157 multiple times. |
| 103 | 158 |
| 104 -f 0|1 | 159 -f 0|1 |
| 105 If 1 we also test alias `fitz` as well as `pymupdf`. Default is '0'. | 160 If 1 we also test alias `fitz` as well as `pymupdf`. Default is '0'. |
| 106 | |
| 107 --gdb 0|1 | |
| 108 Run tests under gdb. Requires user interaction. | |
| 109 | 161 |
| 110 --graal | 162 --graal |
| 111 Use graal - run inside a Graal VM instead of a Python venv. | 163 Use graal - run inside a Graal VM instead of a Python venv. |
| 112 | 164 |
| 113 As of 2025-08-04 we: | 165 As of 2025-08-04 we: |
| 128 'r' - rebased. | 180 'r' - rebased. |
| 129 'R' - rebased without optimisations. | 181 'R' - rebased without optimisations. |
| 130 Default is 'r'. Also see `PyMuPDF:tests/run_compound.py`. | 182 Default is 'r'. Also see `PyMuPDF:tests/run_compound.py`. |
| 131 | 183 |
| 132 -i <install_version> | 184 -i <install_version> |
| 133 Set version installed by the 'install' command. | 185 Controls behaviour of `install` command: |
| 186 | |
| 187 * If <install_version> ends with `.whl` we use `pip install | |
| 188 <install_version>`. | |
| 189 * If <install_version> starts with == or >= or >, we use `pip install | |
| 190 pymupdf<install_version>`. | |
| 191 * Otherwise we use `pip install pymupdf==<install_version>`. | |
| 134 | 192 |
| 135 -k <expression> | 193 -k <expression> |
| 136 Specify which test(s) to run; passed straight through to pytest's `-k`. | 194 Specify which test(s) to run; passed straight through to pytest's `-k`. |
| 137 For example `-k test_3354`. | 195 For example `-k test_3354`. |
| 138 | 196 |
| 139 -m <location> | --mupdf <location> | 197 -m <location> | --mupdf <location> |
| 140 Location of local mupdf/ directory or 'git:...' to be used | 198 Location of mupdf as local directory or remote git, to be used when |
| 141 when building PyMuPDF. | 199 building PyMuPDF. |
| 142 | 200 |
| 143 This sets environment variable PYMUPDF_SETUP_MUPDF_BUILD, which is used | 201 This sets environment variable PYMUPDF_SETUP_MUPDF_BUILD, which is used |
| 144 by PyMuPDF/setup.py. If not specified PyMuPDF will download its default | 202 by PyMuPDF/setup.py. If not specified PyMuPDF will download its default |
| 145 mupdf .tgz. | 203 mupdf .tgz. |
| 146 | 204 |
| 174 -p <pytest-options> | 232 -p <pytest-options> |
| 175 Set pytest options; default is ''. | 233 Set pytest options; default is ''. |
| 176 | 234 |
| 177 -P 0|1 | 235 -P 0|1 |
| 178 If 1, automatically install required system packages such as | 236 If 1, automatically install required system packages such as |
| 179 Valgrind. Default is 0. | 237 Valgrind. Default is 1 if running as Github action, otherwise 0. |
| 180 | 238 |
| 181 --pybind 0|1 | 239 --pybind 0|1 |
| 182 Experimental, for investigating | 240 Experimental, for investigating |
| 183 https://github.com/pymupdf/PyMuPDF/issues/3869. Runs run basic code | 241 https://github.com/pymupdf/PyMuPDF/issues/3869. Runs run basic code |
| 184 inside C++ pybind. Requires `sudo apt install pybind11-dev` or similar. | 242 inside C++ pybind. Requires `sudo apt install pybind11-dev` or similar. |
| 195 PyMuPDF/setup.py.] | 253 PyMuPDF/setup.py.] |
| 196 | 254 |
| 197 --show-args: | 255 --show-args: |
| 198 Show sys.argv and exit. For debugging. | 256 Show sys.argv and exit. For debugging. |
| 199 | 257 |
| 200 --sync-paths | 258 --sync-paths <path> |
| 201 Do not run anything, instead write required files/directories/checkouts | 259 Do not run anything, instead write required files/directories/checkouts |
| 202 to stdout, one per line. This is to help with automated running on | 260 to <path>, one per line. This is to help with automated running on |
| 203 remote machines. | 261 remote machines. |
| 204 | 262 |
| 205 --system-site-packages 0|1 | 263 --system-site-packages 0|1 |
| 206 If 1, use `--system-site-packages` when creating venv. Defaults is 0. | 264 If 1, use `--system-site-packages` when creating venv. Defaults is 0. |
| 207 | 265 |
| 239 | 297 |
| 240 -T <prefix> | 298 -T <prefix> |
| 241 Use specified prefix when running pytest, must be one of: | 299 Use specified prefix when running pytest, must be one of: |
| 242 gdb | 300 gdb |
| 243 helgrind | 301 helgrind |
| 244 vagrind | 302 valgrind |
| 245 | 303 |
| 246 -v <venv> | 304 -v <venv> |
| 247 venv is: | 305 venv is: |
| 248 0 - do not use a venv. | 306 0 - do not use a venv. |
| 249 1 - Use venv. If it already exists, we assume the existing directory | 307 1 - Use venv. If it already exists, we assume the existing directory |
| 330 | 388 |
| 331 log = pipcl.log0 | 389 log = pipcl.log0 |
| 332 run = pipcl.run | 390 run = pipcl.run |
| 333 | 391 |
| 334 | 392 |
| 393 # We build and test Python 3.x for x in this range. | |
| 394 python_versions_minor = range(9, 14+1) | |
| 395 | |
| 396 def cibw_cp(*version_minors): | |
| 397 ''' | |
| 398 Returns <version_tuples> in 'cp39*' format, e.g. suitable for CIBW_BUILD. | |
| 399 ''' | |
| 400 ret = list() | |
| 401 for version_minor in version_minors: | |
| 402 ret.append(f'cp3{version_minor}*') | |
| 403 return ' '.join(ret) | |
| 404 | |
| 405 | |
| 335 def main(argv): | 406 def main(argv): |
| 336 | 407 |
| 337 if github_workflow_unimportant(): | 408 if github_workflow_unimportant(): |
| 338 return | 409 return |
| 339 | 410 |
| 340 build_isolation = None | 411 build_isolation = None |
| 341 cibw_name = None | 412 cibw_name = None |
| 342 cibw_pyodide = None | 413 cibw_pyodide = None |
| 343 cibw_pyodide_version = None | 414 cibw_pyodide_version = None |
| 415 cibw_skip_add_defaults = True | |
| 416 cibw_test_project = None | |
| 417 cibw_test_project_setjmp = False | |
| 344 commands = list() | 418 commands = list() |
| 345 env_extra = dict() | 419 env_extra = dict() |
| 346 graal = False | 420 graal = False |
| 347 implementations = 'r' | 421 implementations = 'r' |
| 348 install_version = None | 422 install_version = None |
| 349 mupdf_sync = None | 423 mupdf_sync = None |
| 350 os_names = list() | 424 os_names = list() |
| 351 system_packages = False | 425 system_packages = True if os.environ.get('GITHUB_ACTIONS') == 'true' else False |
| 352 pybind = False | 426 pybind = False |
| 353 pyodide_build_version = None | 427 pyodide_build_version = None |
| 354 pytest_options = '' | 428 pytest_options = '' |
| 355 pytest_prefix = None | 429 pytest_prefix = None |
| 356 cibw_sdist = None | 430 cibw_sdist = None |
| 406 elif arg == '--cibw-release-1': | 480 elif arg == '--cibw-release-1': |
| 407 cibw_sdist = True | 481 cibw_sdist = True |
| 408 env_extra['CIBW_ARCHS_LINUX'] = 'auto64' | 482 env_extra['CIBW_ARCHS_LINUX'] = 'auto64' |
| 409 env_extra['CIBW_ARCHS_MACOS'] = 'auto64' | 483 env_extra['CIBW_ARCHS_MACOS'] = 'auto64' |
| 410 env_extra['CIBW_ARCHS_WINDOWS'] = 'auto' # win32 and win64. | 484 env_extra['CIBW_ARCHS_WINDOWS'] = 'auto' # win32 and win64. |
| 411 env_extra['CIBW_SKIP'] = 'pp* *i686 cp36* cp37* *musllinux*aarch64*' | 485 env_extra['CIBW_SKIP'] = '*i686 *musllinux*aarch64* cp3??t-*' |
| 486 cibw_skip_add_defaults = 0 | |
| 412 | 487 |
| 413 elif arg == '--cibw-release-2': | 488 elif arg == '--cibw-release-2': |
| 414 env_extra['CIBW_ARCHS_LINUX'] = 'aarch64' | |
| 415 # Testing only first and last python versions because otherwise | 489 # Testing only first and last python versions because otherwise |
| 416 # Github times out after 6h. | 490 # Github times out after 6h. |
| 417 env_extra['CIBW_BUILD'] = 'cp39* cp313*' | 491 env_extra['CIBW_BUILD'] = cibw_cp(python_versions_minor[0], python_versions_minor[-1]) |
| 492 env_extra['CIBW_ARCHS_LINUX'] = 'aarch64' | |
| 493 env_extra['CIBW_SKIP'] = '*i686 *musllinux*aarch64* cp3??t-*' | |
| 494 cibw_skip_add_defaults = 0 | |
| 418 os_names = ['linux'] | 495 os_names = ['linux'] |
| 419 | 496 |
| 420 elif arg == '--cibw-archs-linux': | 497 elif arg == '--cibw-archs-linux': |
| 421 env_extra['CIBW_ARCHS_LINUX'] = next(args) | 498 env_extra['CIBW_ARCHS_LINUX'] = next(args) |
| 422 | 499 |
| 423 elif arg == '--cibw-name': | 500 elif arg == '--cibw-name': |
| 424 cibw_name = next(args) | 501 cibw_name = next(args) |
| 425 | 502 |
| 426 elif arg == '--cibw-pyodide': | 503 elif arg == '--cibw-pyodide': |
| 427 cibw_pyodide = next(args) | 504 cibw_pyodide = int(next(args)) |
| 505 | |
| 506 elif arg == '--cibw-skip-add-defaults': | |
| 507 cibw_skip_add_defaults = int(next(args)) | |
| 508 | |
| 509 elif arg == '--cibw-test-project': | |
| 510 cibw_test_project = int(next(args)) | |
| 511 | |
| 512 elif arg == '--cibw-test-project-setjmp': | |
| 513 cibw_test_project_setjmp = int(next(args)) | |
| 428 | 514 |
| 429 elif arg == '-d': | 515 elif arg == '-d': |
| 430 env_extra['PYMUPDF_SETUP_MUPDF_BUILD_TYPE'] = 'debug' | 516 env_extra['PYMUPDF_SETUP_MUPDF_BUILD_TYPE'] = 'debug' |
| 431 | 517 |
| 432 elif arg == '--dummy': | 518 elif arg == '--dummy': |
| 461 _mupdf = next(args) | 547 _mupdf = next(args) |
| 462 if _mupdf == '-': | 548 if _mupdf == '-': |
| 463 _mupdf = None | 549 _mupdf = None |
| 464 elif _mupdf.startswith(':'): | 550 elif _mupdf.startswith(':'): |
| 465 _branch = _mupdf[1:] | 551 _branch = _mupdf[1:] |
| 466 _mupdf = 'git:--branch {_branch} https://github.com/ArtifexSoftware/mupdf.git' | 552 _mupdf = f'git:--branch {_branch} https://github.com/ArtifexSoftware/mupdf.git' |
| 467 os.environ['PYMUPDF_SETUP_MUPDF_BUILD'] = _mupdf | 553 env_extra['PYMUPDF_SETUP_MUPDF_BUILD'] = _mupdf |
| 468 elif _mupdf.startswith('git:') or '://' in _mupdf: | 554 elif _mupdf.startswith('git:') or '://' in _mupdf: |
| 469 os.environ['PYMUPDF_SETUP_MUPDF_BUILD'] = _mupdf | 555 env_extra['PYMUPDF_SETUP_MUPDF_BUILD'] = _mupdf |
| 470 else: | 556 else: |
| 471 assert os.path.isdir(_mupdf), f'Not a directory: {_mupdf=}' | 557 assert os.path.isdir(_mupdf), f'Not a directory: {_mupdf=}' |
| 472 os.environ['PYMUPDF_SETUP_MUPDF_BUILD'] = os.path.abspath(_mupdf) | 558 env_extra['PYMUPDF_SETUP_MUPDF_BUILD'] = os.path.abspath(_mupdf) |
| 473 mupdf_sync = _mupdf | 559 mupdf_sync = _mupdf |
| 474 | 560 |
| 475 elif arg == '--mupdf-clean': | 561 elif arg == '--mupdf-clean': |
| 476 env_extra['PYMUPDF_SETUP_MUPDF_CLEAN']=next(args) | 562 env_extra['PYMUPDF_SETUP_MUPDF_CLEAN']=next(args) |
| 477 | 563 |
| 499 env_extra['PYMUPDF_SETUP_PY_LIMITED_API'] = _value | 585 env_extra['PYMUPDF_SETUP_PY_LIMITED_API'] = _value |
| 500 | 586 |
| 501 elif arg == '--show-args': | 587 elif arg == '--show-args': |
| 502 show_args = 1 | 588 show_args = 1 |
| 503 elif arg == '--sync-paths': | 589 elif arg == '--sync-paths': |
| 504 sync_paths = True | 590 sync_paths = next(args) |
| 505 | 591 |
| 506 elif arg == '--system-site-packages': | 592 elif arg == '--system-site-packages': |
| 507 system_site_packages = int(next(args)) | 593 system_site_packages = int(next(args)) |
| 508 | 594 |
| 509 elif arg == '--swig': | 595 elif arg == '--swig': |
| 537 assert 0, f'Unrecognised option/command: {arg=}.' | 623 assert 0, f'Unrecognised option/command: {arg=}.' |
| 538 | 624 |
| 539 # Handle special args --sync-paths, -h, -v, -o first. | 625 # Handle special args --sync-paths, -h, -v, -o first. |
| 540 # | 626 # |
| 541 if sync_paths: | 627 if sync_paths: |
| 542 # Just print required files, directories and checkouts. | 628 # Print required files, directories and checkouts. |
| 543 print(pymupdf_dir) | 629 with open(sync_paths, 'w') as f: |
| 544 if mupdf_sync: | 630 print(pymupdf_dir, file=f) |
| 545 print(mupdf_sync) | 631 if mupdf_sync: |
| 632 print(mupdf_sync, file=f) | |
| 546 return | 633 return |
| 547 | 634 |
| 548 if show_help: | 635 if show_help: |
| 549 print(__doc__) | 636 print(__doc__) |
| 550 return | 637 return |
| 576 if venv >= 3: | 663 if venv >= 3: |
| 577 shutil.rmtree(venv_name, ignore_errors=1) | 664 shutil.rmtree(venv_name, ignore_errors=1) |
| 578 if venv == 1 and os.path.exists(pyenv_dir) and os.path.exists(venv_name): | 665 if venv == 1 and os.path.exists(pyenv_dir) and os.path.exists(venv_name): |
| 579 log(f'{venv=} and {venv_name=} already exists so not building pyenv or creating venv.') | 666 log(f'{venv=} and {venv_name=} already exists so not building pyenv or creating venv.') |
| 580 else: | 667 else: |
| 581 pipcl.git_get('https://github.com/pyenv/pyenv.git', pyenv_dir, branch='master') | 668 pipcl.git_get(pyenv_dir, remote='https://github.com/pyenv/pyenv.git', branch='master') |
| 582 run(f'cd {pyenv_dir} && src/configure && make -C src') | 669 run(f'cd {pyenv_dir} && src/configure && make -C src') |
| 583 run(f'which pyenv') | 670 run(f'which pyenv') |
| 584 run(f'pyenv install -v -s {graalpy}') | 671 run(f'pyenv install -v -s {graalpy}') |
| 585 run(f'{pyenv_dir}/versions/{graalpy}/bin/graalpy -m venv {venv_name}') | 672 run(f'{pyenv_dir}/versions/{graalpy}/bin/graalpy -m venv {venv_name}') |
| 586 e = run(f'. {venv_name}/bin/activate && python {shlex.join(sys.argv)}', | 673 e = run(f'. {venv_name}/bin/activate && python {shlex.join(sys.argv)}', |
| 620 ) | 707 ) |
| 621 have_installed = True | 708 have_installed = True |
| 622 | 709 |
| 623 elif command == 'cibw': | 710 elif command == 'cibw': |
| 624 # Build wheel(s) with cibuildwheel. | 711 # Build wheel(s) with cibuildwheel. |
| 625 if cibw_pyodide and env_extra.get('CIBW_BUILD') is None: | 712 |
| 626 assert 0, f'Need a Python version for Pyodide.' | 713 if platform.system() == 'Linux': |
| 627 CIBW_BUILD = 'cp312*' | 714 PYMUPDF_SETUP_MUPDF_BUILD = env_extra.get('PYMUPDF_SETUP_MUPDF_BUILD') |
| 628 env_extra['CIBW_BUILD'] = CIBW_BUILD | 715 if PYMUPDF_SETUP_MUPDF_BUILD and not PYMUPDF_SETUP_MUPDF_BUILD.startswith('git:'): |
| 629 log(f'Defaulting to {CIBW_BUILD=} for Pyodide.') | 716 assert PYMUPDF_SETUP_MUPDF_BUILD.startswith('/') |
| 630 #if cibw_pyodide_version == None: | 717 env_extra['PYMUPDF_SETUP_MUPDF_BUILD'] = f'/host/{PYMUPDF_SETUP_MUPDF_BUILD}' |
| 631 # cibw_pyodide_version = '0.28.0' | 718 |
| 632 cibuildwheel( | 719 cibuildwheel( |
| 633 env_extra, | 720 env_extra, |
| 634 cibw_name or 'cibuildwheel', | 721 cibw_name or 'cibuildwheel', |
| 635 cibw_pyodide, | 722 cibw_pyodide, |
| 636 cibw_pyodide_version, | 723 cibw_pyodide_version, |
| 637 cibw_sdist, | 724 cibw_sdist, |
| 725 cibw_test_project, | |
| 726 cibw_test_project_setjmp, | |
| 727 cibw_skip_add_defaults, | |
| 638 ) | 728 ) |
| 639 | 729 |
| 640 elif command == 'install': | 730 elif command == 'install': |
| 641 p = 'pymupdf' | 731 p = 'pymupdf' |
| 642 if install_version: | 732 if install_version: |
| 643 if not install_version.startswith(('==', '>=', '>')): | 733 if install_version.endswith('.whl'): |
| 644 p = f'{p}==' | 734 p = install_version |
| 645 p = f'{p}{install_version}' | 735 elif install_version.startswith(('==', '>=', '>')): |
| 736 p = f'{p}{install_version}' | |
| 737 else: | |
| 738 p = f'{p}=={install_version}' | |
| 646 run(f'pip install --force-reinstall {p}') | 739 run(f'pip install --force-reinstall {p}') |
| 647 have_installed = True | 740 have_installed = True |
| 648 | 741 |
| 649 elif command == 'test': | 742 elif command == 'test': |
| 650 if not have_installed: | 743 if not have_installed: |
| 737 *, | 830 *, |
| 738 build_isolation, | 831 build_isolation, |
| 739 venv, | 832 venv, |
| 740 wheel, | 833 wheel, |
| 741 ): | 834 ): |
| 742 print(f'{build_isolation=}') | 835 log(f'{build_isolation=}') |
| 743 | 836 |
| 744 if build_isolation is None: | 837 if build_isolation is None: |
| 745 # On OpenBSD libclang is not available on pypi.org, so we need to force | 838 # On OpenBSD libclang is not available on pypi.org, so we need to force |
| 746 # use of system package py3-llvm with --no-build-isolation, manually | 839 # use of system package py3-llvm with --no-build-isolation, manually |
| 747 # installing other required packages. | 840 # installing other required packages. |
| 773 run(f'pip install --force-reinstall {wheel}') | 866 run(f'pip install --force-reinstall {wheel}') |
| 774 else: | 867 else: |
| 775 run(f'pip install{build_isolation_text} -v --force-reinstall {pymupdf_dir_abs}', env_extra=env_extra) | 868 run(f'pip install{build_isolation_text} -v --force-reinstall {pymupdf_dir_abs}', env_extra=env_extra) |
| 776 | 869 |
| 777 | 870 |
| 778 def cibuildwheel(env_extra, cibw_name, cibw_pyodide, cibw_pyodide_version, cibw_sdist): | 871 def cibuildwheel( |
| 872 env_extra, | |
| 873 cibw_name, | |
| 874 cibw_pyodide, | |
| 875 cibw_pyodide_version, | |
| 876 cibw_sdist, | |
| 877 cibw_test_project, | |
| 878 cibw_test_project_setjmp, | |
| 879 cibw_skip_add_defaults, | |
| 880 ): | |
| 779 | 881 |
| 780 if cibw_sdist and platform.system() == 'Linux': | 882 if cibw_sdist and platform.system() == 'Linux': |
| 781 log(f'Building sdist.') | 883 log(f'Building sdist.') |
| 782 run(f'cd {pymupdf_dir_abs} && {sys.executable} setup.py -d wheelhouse sdist', env_extra=env_extra) | 884 run(f'cd {pymupdf_dir_abs} && {sys.executable} setup.py -d wheelhouse sdist', env_extra=env_extra) |
| 783 sdists = glob.glob(f'{pymupdf_dir_abs}/wheelhouse/pymupdf-*.tar.gz') | 885 sdists = glob.glob(f'{pymupdf_dir_abs}/wheelhouse/pymupdf-*.tar.gz') |
| 787 run(f'pip install --upgrade --force-reinstall {cibw_name}') | 889 run(f'pip install --upgrade --force-reinstall {cibw_name}') |
| 788 | 890 |
| 789 # Some general flags. | 891 # Some general flags. |
| 790 if 'CIBW_BUILD_VERBOSITY' not in env_extra: | 892 if 'CIBW_BUILD_VERBOSITY' not in env_extra: |
| 791 env_extra['CIBW_BUILD_VERBOSITY'] = '1' | 893 env_extra['CIBW_BUILD_VERBOSITY'] = '1' |
| 792 if 'CIBW_SKIP' not in env_extra: | 894 |
| 793 env_extra['CIBW_SKIP'] = 'pp* *i686 cp36* cp37* *musllinux* *-win32 *-aarch64' | 895 # Add default flags to CIBW_SKIP. |
| 794 | 896 # 2025-10-07: `cp3??t-*` excludes free-threading, which currently breaks |
| 897 # some tests. | |
| 898 | |
| 899 if cibw_skip_add_defaults: | |
| 900 CIBW_SKIP = env_extra.get('CIBW_SKIP', '') | |
| 901 CIBW_SKIP += ' *i686 *musllinux* *-win32 *-aarch64 cp3??t-*' | |
| 902 CIBW_SKIP = CIBW_SKIP.split() | |
| 903 CIBW_SKIP = sorted(list(set(CIBW_SKIP))) | |
| 904 CIBW_SKIP = ' '.join(CIBW_SKIP) | |
| 905 env_extra['CIBW_SKIP'] = CIBW_SKIP | |
| 906 | |
| 795 # Set what wheels to build, if not already specified. | 907 # Set what wheels to build, if not already specified. |
| 796 if 'CIBW_ARCHS' not in env_extra: | 908 if 'CIBW_ARCHS' not in env_extra: |
| 797 if 'CIBW_ARCHS_WINDOWS' not in env_extra: | 909 if 'CIBW_ARCHS_WINDOWS' not in env_extra: |
| 798 env_extra['CIBW_ARCHS_WINDOWS'] = 'auto64' | 910 env_extra['CIBW_ARCHS_WINDOWS'] = 'auto64' |
| 799 | 911 |
| 821 | 933 |
| 822 # Specify python versions. | 934 # Specify python versions. |
| 823 CIBW_BUILD = env_extra.get('CIBW_BUILD') | 935 CIBW_BUILD = env_extra.get('CIBW_BUILD') |
| 824 log(f'{CIBW_BUILD=}') | 936 log(f'{CIBW_BUILD=}') |
| 825 if CIBW_BUILD is None: | 937 if CIBW_BUILD is None: |
| 826 if os.environ.get('GITHUB_ACTIONS') == 'true': | 938 if cibw_pyodide: |
| 939 # Using python-3.13 fixes problems with MuPDF's setjmp/longjmp. | |
| 940 CIBW_BUILD = 'cp313*' | |
| 941 elif os.environ.get('GITHUB_ACTIONS') == 'true': | |
| 827 # Build/test all supported Python versions. | 942 # Build/test all supported Python versions. |
| 828 CIBW_BUILD = 'cp39* cp310* cp311* cp312* cp313*' | 943 CIBW_BUILD = cibw_cp(*python_versions_minor) |
| 829 else: | 944 else: |
| 830 # Build/test current Python only. | 945 # Build/test current Python only. |
| 831 v = platform.python_version_tuple()[:2] | 946 v = platform.python_version_tuple()[:2] |
| 832 log(f'{v=}') | 947 log(f'{v=}') |
| 833 CIBW_BUILD = f'cp{"".join(v)}*' | 948 CIBW_BUILD = f'cp{"".join(v)}*' |
| 949 log(f'Defaulting to {CIBW_BUILD=}.') | |
| 834 | 950 |
| 835 cibw_pyodide_args = '' | 951 cibw_pyodide_args = '' |
| 836 if cibw_pyodide: | 952 if cibw_pyodide: |
| 837 cibw_pyodide_args = ' --platform pyodide' | 953 cibw_pyodide_args = ' --platform pyodide' |
| 838 env_extra['HAVE_LIBCRYPTO'] = 'no' | 954 env_extra['HAVE_LIBCRYPTO'] = 'no' |
| 841 # 2025-07-21: there is no --pyodide-version option so we set | 957 # 2025-07-21: there is no --pyodide-version option so we set |
| 842 # CIBW_PYODIDE_VERSION. | 958 # CIBW_PYODIDE_VERSION. |
| 843 env_extra['CIBW_PYODIDE_VERSION'] = cibw_pyodide_version | 959 env_extra['CIBW_PYODIDE_VERSION'] = cibw_pyodide_version |
| 844 env_extra['CIBW_ENABLE'] = 'pyodide-prerelease' | 960 env_extra['CIBW_ENABLE'] = 'pyodide-prerelease' |
| 845 | 961 |
| 846 # Pass all the environment variables we have set, to Linux | 962 # Pass all the environment variables we have set, to Linux docker. Note |
| 847 # docker. Note that this will miss any settings in the original | 963 # that this will miss any settings in the original environment. We have to |
| 848 # environment. | 964 # add CIBW_BUILD explicitly because we haven't set it yet. |
| 849 env_extra['CIBW_ENVIRONMENT_PASS_LINUX'] = ' '.join(sorted(env_extra.keys())) | 965 CIBW_ENVIRONMENT_PASS_LINUX = set(env_extra.keys()) |
| 850 | 966 CIBW_ENVIRONMENT_PASS_LINUX.add('CIBW_BUILD') |
| 967 CIBW_ENVIRONMENT_PASS_LINUX = sorted(list(CIBW_ENVIRONMENT_PASS_LINUX)) | |
| 968 CIBW_ENVIRONMENT_PASS_LINUX = ' '.join(CIBW_ENVIRONMENT_PASS_LINUX) | |
| 969 env_extra['CIBW_ENVIRONMENT_PASS_LINUX'] = CIBW_ENVIRONMENT_PASS_LINUX | |
| 970 | |
| 971 if cibw_test_project: | |
| 972 cibw_do_test_project( | |
| 973 env_extra, | |
| 974 CIBW_BUILD, | |
| 975 cibw_pyodide, | |
| 976 cibw_pyodide_args, | |
| 977 cibw_test_project_setjmp, | |
| 978 ) | |
| 979 return | |
| 980 | |
| 851 # Build for lowest (assumed first) Python version. | 981 # Build for lowest (assumed first) Python version. |
| 852 # | 982 # |
| 853 CIBW_BUILD_0 = CIBW_BUILD.split()[0] | 983 CIBW_BUILD_0 = CIBW_BUILD.split()[0] |
| 854 log(f'Building for first Python version {CIBW_BUILD_0}.') | 984 log(f'Building for first Python version {CIBW_BUILD_0}.') |
| 855 env_extra['CIBW_BUILD'] = CIBW_BUILD_0 | 985 env_extra['CIBW_BUILD'] = CIBW_BUILD_0 |
| 857 | 987 |
| 858 # Tell cibuildwheel to build and test all specified Python versions; it | 988 # Tell cibuildwheel to build and test all specified Python versions; it |
| 859 # will notice that the wheel we built above supports all versions of | 989 # will notice that the wheel we built above supports all versions of |
| 860 # Python, so will not actually do any builds here. | 990 # Python, so will not actually do any builds here. |
| 861 # | 991 # |
| 992 # We only do this if there are more than one Python versions. This still | |
| 993 # duplicates the testing of the first python version. | |
| 994 if len(CIBW_BUILD.split()) > 1: | |
| 995 env_extra['CIBW_BUILD'] = CIBW_BUILD | |
| 996 run(f'cd {pymupdf_dir} && cibuildwheel{cibw_pyodide_args}', env_extra=env_extra) | |
| 997 run(f'ls -ld {pymupdf_dir}/wheelhouse/*') | |
| 998 | |
| 999 | |
| 1000 def cibw_do_test_project( | |
| 1001 env_extra, | |
| 1002 CIBW_BUILD, | |
| 1003 cibw_pyodide, | |
| 1004 cibw_pyodide_args, | |
| 1005 cibw_test_project_setjmp, | |
| 1006 ): | |
| 1007 testdir = f'{pymupdf_dir_abs}/cibw_test' | |
| 1008 shutil.rmtree(testdir, ignore_errors=1) | |
| 1009 os.mkdir(testdir) | |
| 1010 with open(f'{testdir}/setup.py', 'w') as f: | |
| 1011 f.write(textwrap.dedent(f''' | |
| 1012 import shutil | |
| 1013 import sys | |
| 1014 import os | |
| 1015 import pipcl | |
| 1016 | |
| 1017 def build(): | |
| 1018 so_leaf = pipcl.build_extension( | |
| 1019 name = 'foo', | |
| 1020 path_i = 'foo.i', | |
| 1021 outdir = 'build', | |
| 1022 source_extra = 'qwerty.cpp', | |
| 1023 py_limited_api = True, | |
| 1024 ) | |
| 1025 | |
| 1026 return [ | |
| 1027 ('build/foo.py', 'foo/__init__.py'), | |
| 1028 (f'build/{{so_leaf}}', f'foo/'), | |
| 1029 ] | |
| 1030 | |
| 1031 p = pipcl.Package( | |
| 1032 name = 'pymupdf-test', | |
| 1033 version = '1.2.3', | |
| 1034 fn_build = build, | |
| 1035 py_limited_api=True, | |
| 1036 ) | |
| 1037 | |
| 1038 def get_requires_for_build_wheel(config_settings=None): | |
| 1039 return ['swig'] | |
| 1040 | |
| 1041 build_wheel = p.build_wheel | |
| 1042 build_sdist = p.build_sdist | |
| 1043 | |
| 1044 # Handle old-style setup.py command-line usage: | |
| 1045 if __name__ == '__main__': | |
| 1046 p.handle_argv(sys.argv) | |
| 1047 ''')) | |
| 1048 with open(f'{testdir}/foo.i', 'w') as f: | |
| 1049 if cibw_test_project_setjmp: | |
| 1050 f.write(textwrap.dedent(''' | |
| 1051 %{ | |
| 1052 #include <stdexcept> | |
| 1053 | |
| 1054 #include <assert.h> | |
| 1055 #include <setjmp.h> | |
| 1056 #include <stdio.h> | |
| 1057 #include <string.h> | |
| 1058 | |
| 1059 int qwerty(void); | |
| 1060 | |
| 1061 static sigjmp_buf jmpbuf; | |
| 1062 static int bar0(const char* text) | |
| 1063 { | |
| 1064 printf("bar0(): text: %s\\n", text); | |
| 1065 | |
| 1066 int q = qwerty(); | |
| 1067 printf("bar0(): q=%i\\n", q); | |
| 1068 | |
| 1069 int len = (int) strlen(text); | |
| 1070 printf("bar0(): len=%i\\n", len); | |
| 1071 printf("bar0(): calling longjmp().\\n"); | |
| 1072 fflush(stdout); | |
| 1073 longjmp(jmpbuf, 1); | |
| 1074 assert(0); | |
| 1075 } | |
| 1076 int bar1(const char* text) | |
| 1077 { | |
| 1078 int ret = 0; | |
| 1079 if (setjmp(jmpbuf) == 0) | |
| 1080 { | |
| 1081 ret = bar0(text); | |
| 1082 } | |
| 1083 else | |
| 1084 { | |
| 1085 printf("bar1(): setjmp() returned non-zero.\\n"); | |
| 1086 throw std::runtime_error("deliberate exception"); | |
| 1087 } | |
| 1088 assert(0); | |
| 1089 } | |
| 1090 int bar(const char* text) | |
| 1091 { | |
| 1092 int ret = 0; | |
| 1093 try | |
| 1094 { | |
| 1095 ret = bar1(text); | |
| 1096 } | |
| 1097 catch(std::exception& e) | |
| 1098 { | |
| 1099 printf("bar1(): received exception: %s\\n", e.what()); | |
| 1100 } | |
| 1101 return ret; | |
| 1102 } | |
| 1103 %} | |
| 1104 int bar(const char* text); | |
| 1105 ''')) | |
| 1106 else: | |
| 1107 f.write(textwrap.dedent(''' | |
| 1108 %{ | |
| 1109 #include <stdexcept> | |
| 1110 | |
| 1111 #include <assert.h> | |
| 1112 #include <stdio.h> | |
| 1113 #include <string.h> | |
| 1114 | |
| 1115 int qwerty(void); | |
| 1116 | |
| 1117 int bar(const char* text) | |
| 1118 { | |
| 1119 qwerty(); | |
| 1120 return strlen(text); | |
| 1121 } | |
| 1122 %} | |
| 1123 int bar(const char* text); | |
| 1124 ''')) | |
| 1125 | |
| 1126 with open(f'{testdir}/qwerty.cpp', 'w') as f: | |
| 1127 f.write(textwrap.dedent(''' | |
| 1128 #include <stdio.h> | |
| 1129 int qwerty(void) | |
| 1130 { | |
| 1131 printf("qwerty()\\n"); | |
| 1132 return 3; | |
| 1133 } | |
| 1134 ''')) | |
| 1135 | |
| 1136 with open(f'{testdir}/pyproject.toml', 'w') as f: | |
| 1137 f.write(textwrap.dedent(''' | |
| 1138 [build-system] | |
| 1139 # We define required packages in setup.py:get_requires_for_build_wheel(). | |
| 1140 requires = [] | |
| 1141 | |
| 1142 # See pep-517. | |
| 1143 # | |
| 1144 build-backend = "setup" | |
| 1145 backend-path = ["."] | |
| 1146 ''')) | |
| 1147 | |
| 1148 shutil.copy2(f'{pymupdf_dir_abs}/pipcl.py', f'{testdir}/pipcl.py') | |
| 1149 shutil.copy2(f'{pymupdf_dir_abs}/wdev.py', f'{testdir}/wdev.py') | |
| 1150 | |
| 862 env_extra['CIBW_BUILD'] = CIBW_BUILD | 1151 env_extra['CIBW_BUILD'] = CIBW_BUILD |
| 863 run(f'cd {pymupdf_dir} && cibuildwheel{cibw_pyodide_args}', env_extra=env_extra) | 1152 CIBW_TEST_COMMAND = '' |
| 864 run(f'ls -ld {pymupdf_dir}/wheelhouse/*') | 1153 if cibw_pyodide: |
| 1154 CIBW_TEST_COMMAND += 'pyodide xbuildenv search --all; ' | |
| 1155 CIBW_TEST_COMMAND += 'python -c "import foo; foo.bar(\\"some text\\")"' | |
| 1156 env_extra['CIBW_TEST_COMMAND'] = CIBW_TEST_COMMAND | |
| 1157 #env_extra['CIBW_TEST_COMMAND'] = '' | |
| 1158 | |
| 1159 run(f'cd {testdir} && cibuildwheel --output-dir ../wheelhouse{cibw_pyodide_args}', env_extra=env_extra) | |
| 1160 run(f'ls -ldt {pymupdf_dir_abs}/wheelhouse/*') | |
| 865 | 1161 |
| 866 | 1162 |
| 867 def build_pyodide_wheel(pyodide_build_version=None): | 1163 def build_pyodide_wheel(pyodide_build_version=None): |
| 868 ''' | 1164 ''' |
| 869 Build Pyodide wheel. | 1165 Build Pyodide wheel. |
| 1086 python = gh_release.relpath(sys.executable) | 1382 python = gh_release.relpath(sys.executable) |
| 1087 log('Running tests with tests/run_compound.py and pytest.') | 1383 log('Running tests with tests/run_compound.py and pytest.') |
| 1088 | 1384 |
| 1089 PYODIDE_ROOT = os.environ.get('PYODIDE_ROOT') | 1385 PYODIDE_ROOT = os.environ.get('PYODIDE_ROOT') |
| 1090 if PYODIDE_ROOT is not None: | 1386 if PYODIDE_ROOT is not None: |
| 1387 # We can't install packages with `pip install`; setup.py will have | |
| 1388 # specified pytest in the wheels's <requires_dist>, so it will be | |
| 1389 # already installed. | |
| 1390 # | |
| 1091 log(f'Not installing test packages because {PYODIDE_ROOT=}.') | 1391 log(f'Not installing test packages because {PYODIDE_ROOT=}.') |
| 1092 command = f'{pytest_options} {pytest_arg} -s' | 1392 command = f'{pytest_options} {pytest_arg}' |
| 1093 args = shlex.split(command) | 1393 args = shlex.split(command) |
| 1094 print(f'{PYODIDE_ROOT=} so calling pytest.main(args).') | 1394 log(f'{PYODIDE_ROOT=} so calling pytest.main(args).') |
| 1095 print(f'{command=}') | 1395 log(f'{command=}') |
| 1096 print(f'args are ({len(args)}):') | 1396 log(f'args are ({len(args)}):') |
| 1097 for arg in args: | 1397 for arg in args: |
| 1098 print(f' {arg!r}') | 1398 log(f' {arg!r}') |
| 1099 import pytest | 1399 import pytest |
| 1100 pytest.main(args) | 1400 e = pytest.main(args) |
| 1401 assert e == 0, f'pytest.main() failed: {e=}' | |
| 1101 return | 1402 return |
| 1102 | 1403 |
| 1103 if venv >= 2: | 1404 if venv >= 2: |
| 1104 run(f'pip install --upgrade {gh_release.test_packages}') | 1405 run(f'pip install --upgrade {gh_release.test_packages}') |
| 1105 else: | 1406 else: |
| 1161 | 1462 |
| 1162 command += f' {pytest_options} {pytest_arg}' | 1463 command += f' {pytest_options} {pytest_arg}' |
| 1163 | 1464 |
| 1164 # Always start by removing any test_*_fitz.py files. | 1465 # Always start by removing any test_*_fitz.py files. |
| 1165 for p in glob.glob(f'{pymupdf_dir_rel}/tests/test_*_fitz.py'): | 1466 for p in glob.glob(f'{pymupdf_dir_rel}/tests/test_*_fitz.py'): |
| 1166 print(f'Removing {p=}') | 1467 log(f'Removing {p=}') |
| 1167 os.remove(p) | 1468 os.remove(p) |
| 1168 if test_fitz: | 1469 if test_fitz: |
| 1169 # Create copies of each test file, modified to use `pymupdf` | 1470 # Create copies of each test file, modified to use `pymupdf` |
| 1170 # instead of `fitz`. | 1471 # instead of `fitz`. |
| 1171 for p in glob.glob(f'{pymupdf_dir_rel}/tests/test_*.py'): | 1472 for p in glob.glob(f'{pymupdf_dir_rel}/tests/test_*.py'): |
| 1173 # Don't recursively generate test_fitz_fitz_foo.py, | 1474 # Don't recursively generate test_fitz_fitz_foo.py, |
| 1174 # test_fitz_fitz_fitz_foo.py, ... etc. | 1475 # test_fitz_fitz_fitz_foo.py, ... etc. |
| 1175 continue | 1476 continue |
| 1176 branch, leaf = os.path.split(p) | 1477 branch, leaf = os.path.split(p) |
| 1177 p2 = f'{branch}/{leaf[:5]}fitz_{leaf[5:]}' | 1478 p2 = f'{branch}/{leaf[:5]}fitz_{leaf[5:]}' |
| 1178 print(f'Converting {p=} to {p2=}.') | 1479 log(f'Converting {p=} to {p2=}.') |
| 1179 with open(p, encoding='utf8') as f: | 1480 with open(p, encoding='utf8') as f: |
| 1180 text = f.read() | 1481 text = f.read() |
| 1181 text2 = re.sub("([^\'])\\bpymupdf\\b", '\\1fitz', text) | 1482 text2 = re.sub("([^\'])\\bpymupdf\\b", '\\1fitz', text) |
| 1182 if p.replace(os.sep, '/') == f'{pymupdf_dir_rel}/tests/test_docs_samples.py'.replace(os.sep, '/'): | 1483 if p.replace(os.sep, '/') == f'{pymupdf_dir_rel}/tests/test_docs_samples.py'.replace(os.sep, '/'): |
| 1183 assert text2 == text | 1484 assert text2 == text |
