From 3aef76e86cde130869499d5940112ff51c4fd1c0 Mon Sep 17 00:00:00 2001 From: "riseproject-dev[bot]" <330740410+riseproject-dev[bot]@users.noreply.github.com> Date: Fri, 23 Oct 2026 07:58:51 +0000 Subject: [PATCH 1/9] tesserocr: Add version 2.12.0 Signed-off-by: riseproject-dev[bot] <330740410+riseproject-dev[bot]@users.noreply.github.com> --- docs/packages/tesserocr.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/packages/tesserocr.yaml b/docs/packages/tesserocr.yaml index d4d314ddf6f6..59037e8cb9f9 100644 --- a/docs/packages/tesserocr.yaml +++ b/docs/packages/tesserocr.yaml @@ -30,3 +30,4 @@ versions: - filename: tesserocr-2.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl sha256: 1a363b6bbf85185bdc84c7c309ea9ca1432baf1cf33ea5166b93c65c7c271d74 requires-python: '>=3.9' +- version: 2.12.0 From 39d396de6d831eca7dfdfb6fc01f6fc37769269e Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 23 Oct 2026 16:57:09 +0800 Subject: [PATCH 2/9] tesserocr: carry the leptonica/tesseract licences patch to 2.12.0 --- ...tonica-and-tesseract-licences-with-t.patch | 265 ++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 patches/tesserocr/2.12.0/0001-package-the-leptonica-and-tesseract-licences-with-t.patch diff --git a/patches/tesserocr/2.12.0/0001-package-the-leptonica-and-tesseract-licences-with-t.patch b/patches/tesserocr/2.12.0/0001-package-the-leptonica-and-tesseract-licences-with-t.patch new file mode 100644 index 000000000000..148768fff55b --- /dev/null +++ b/patches/tesserocr/2.12.0/0001-package-the-leptonica-and-tesseract-licences-with-t.patch @@ -0,0 +1,265 @@ +From 0be557b82ea3e798806d3148bddee280edced4ea Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sat, 12 Sep 2026 12:45:02 +0200 +Subject: [PATCH] Package the leptonica and tesseract licences with the wheels + +The riscv64 wheels build leptonica (BSD-2-Clause) and tesseract +(Apache-2.0) from source in CIBW_BEFORE_ALL_LINUX and auditwheel vendors +the resulting liblept/libtesseract shared libraries into the wheel, but +the wheel otherwise only ships tesserocr's own MIT LICENSE. + +Add both licence texts at the project root -- setuptools' default +license_files glob (LICEN[CS]E*) picks them up with no setup.py change +and lands them in dist-info/licenses/ beside the project's own LICENSE. + +Upstream-Status: Inappropriate [riscv64-specific build dependency, not part of upstream's own build] +--- + LICENSE.leptonica | 26 ++++++ + LICENSE.tesseract-ocr | 202 ++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 228 insertions(+) + create mode 100644 LICENSE.leptonica + create mode 100644 LICENSE.tesseract-ocr + +diff --git a/LICENSE.leptonica b/LICENSE.leptonica +new file mode 100644 +index 0000000..73d44c6 +--- /dev/null ++++ b/LICENSE.leptonica +@@ -0,0 +1,26 @@ ++/*====================================================================* ++ - Copyright (C) 2001-2020 Leptonica. All rights reserved. ++ - ++ - Redistribution and use in source and binary forms, with or without ++ - modification, are permitted provided that the following conditions ++ - are met: ++ - 1. Redistributions of source code must retain the above copyright ++ - notice, this list of conditions and the following disclaimer. ++ - 2. Redistributions in binary form must reproduce the above ++ - copyright notice, this list of conditions and the following ++ - disclaimer in the documentation and/or other materials ++ - provided with the distribution. ++ - ++ - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY ++ - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ *====================================================================*/ ++ +diff --git a/LICENSE.tesseract-ocr b/LICENSE.tesseract-ocr +new file mode 100644 +index 0000000..d645695 +--- /dev/null ++++ b/LICENSE.tesseract-ocr +@@ -0,0 +1,202 @@ ++ ++ Apache License ++ Version 2.0, January 2004 ++ http://www.apache.org/licenses/ ++ ++ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION ++ ++ 1. Definitions. ++ ++ "License" shall mean the terms and conditions for use, reproduction, ++ and distribution as defined by Sections 1 through 9 of this document. ++ ++ "Licensor" shall mean the copyright owner or entity authorized by ++ the copyright owner that is granting the License. ++ ++ "Legal Entity" shall mean the union of the acting entity and all ++ other entities that control, are controlled by, or are under common ++ control with that entity. For the purposes of this definition, ++ "control" means (i) the power, direct or indirect, to cause the ++ direction or management of such entity, whether by contract or ++ otherwise, or (ii) ownership of fifty percent (50%) or more of the ++ outstanding shares, or (iii) beneficial ownership of such entity. ++ ++ "You" (or "Your") shall mean an individual or Legal Entity ++ exercising permissions granted by this License. ++ ++ "Source" form shall mean the preferred form for making modifications, ++ including but not limited to software source code, documentation ++ source, and configuration files. ++ ++ "Object" form shall mean any form resulting from mechanical ++ transformation or translation of a Source form, including but ++ not limited to compiled object code, generated documentation, ++ and conversions to other media types. ++ ++ "Work" shall mean the work of authorship, whether in Source or ++ Object form, made available under the License, as indicated by a ++ copyright notice that is included in or attached to the work ++ (an example is provided in the Appendix below). ++ ++ "Derivative Works" shall mean any work, whether in Source or Object ++ form, that is based on (or derived from) the Work and for which the ++ editorial revisions, annotations, elaborations, or other modifications ++ represent, as a whole, an original work of authorship. For the purposes ++ of this License, Derivative Works shall not include works that remain ++ separable from, or merely link (or bind by name) to the interfaces of, ++ the Work and Derivative Works thereof. ++ ++ "Contribution" shall mean any work of authorship, including ++ the original version of the Work and any modifications or additions ++ to that Work or Derivative Works thereof, that is intentionally ++ submitted to Licensor for inclusion in the Work by the copyright owner ++ or by an individual or Legal Entity authorized to submit on behalf of ++ the copyright owner. For the purposes of this definition, "submitted" ++ means any form of electronic, verbal, or written communication sent ++ to the Licensor or its representatives, including but not limited to ++ communication on electronic mailing lists, source code control systems, ++ and issue tracking systems that are managed by, or on behalf of, the ++ Licensor for the purpose of discussing and improving the Work, but ++ excluding communication that is conspicuously marked or otherwise ++ designated in writing by the copyright owner as "Not a Contribution." ++ ++ "Contributor" shall mean Licensor and any individual or Legal Entity ++ on behalf of whom a Contribution has been received by Licensor and ++ subsequently incorporated within the Work. ++ ++ 2. Grant of Copyright License. Subject to the terms and conditions of ++ this License, each Contributor hereby grants to You a perpetual, ++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable ++ copyright license to reproduce, prepare Derivative Works of, ++ publicly display, publicly perform, sublicense, and distribute the ++ Work and such Derivative Works in Source or Object form. ++ ++ 3. Grant of Patent License. Subject to the terms and conditions of ++ this License, each Contributor hereby grants to You a perpetual, ++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable ++ (except as stated in this section) patent license to make, have made, ++ use, offer to sell, sell, import, and otherwise transfer the Work, ++ where such license applies only to those patent claims licensable ++ by such Contributor that are necessarily infringed by their ++ Contribution(s) alone or by combination of their Contribution(s) ++ with the Work to which such Contribution(s) was submitted. If You ++ institute patent litigation against any entity (including a ++ cross-claim or counterclaim in a lawsuit) alleging that the Work ++ or a Contribution incorporated within the Work constitutes direct ++ or contributory patent infringement, then any patent licenses ++ granted to You under this License for that Work shall terminate ++ as of the date such litigation is filed. ++ ++ 4. Redistribution. You may reproduce and distribute copies of the ++ Work or Derivative Works thereof in any medium, with or without ++ modifications, and in Source or Object form, provided that You ++ meet the following conditions: ++ ++ (a) You must give any other recipients of the Work or ++ Derivative Works a copy of this License; and ++ ++ (b) You must cause any modified files to carry prominent notices ++ stating that You changed the files; and ++ ++ (c) You must retain, in the Source form of any Derivative Works ++ that You distribute, all copyright, patent, trademark, and ++ attribution notices from the Source form of the Work, ++ excluding those notices that do not pertain to any part of ++ the Derivative Works; and ++ ++ (d) If the Work includes a "NOTICE" text file as part of its ++ distribution, then any Derivative Works that You distribute must ++ include a readable copy of the attribution notices contained ++ within such NOTICE file, excluding those notices that do not ++ pertain to any part of the Derivative Works, in at least one ++ of the following places: within a NOTICE text file distributed ++ as part of the Derivative Works; within the Source form or ++ documentation, if provided along with the Derivative Works; or, ++ within a display generated by the Derivative Works, if and ++ wherever such third-party notices normally appear. The contents ++ of the NOTICE file are for informational purposes only and ++ do not modify the License. You may add Your own attribution ++ notices within Derivative Works that You distribute, alongside ++ or as an addendum to the NOTICE text from the Work, provided ++ that such additional attribution notices cannot be construed ++ as modifying the License. ++ ++ You may add Your own copyright statement to Your modifications and ++ may provide additional or different license terms and conditions ++ for use, reproduction, or distribution of Your modifications, or ++ for any such Derivative Works as a whole, provided Your use, ++ reproduction, and distribution of the Work otherwise complies with ++ the conditions stated in this License. ++ ++ 5. Submission of Contributions. Unless You explicitly state otherwise, ++ any Contribution intentionally submitted for inclusion in the Work ++ by You to the Licensor shall be under the terms and conditions of ++ this License, without any additional terms or conditions. ++ Notwithstanding the above, nothing herein shall supersede or modify ++ the terms of any separate license agreement you may have executed ++ with Licensor regarding such Contributions. ++ ++ 6. Trademarks. This License does not grant permission to use the trade ++ names, trademarks, service marks, or product names of the Licensor, ++ except as required for reasonable and customary use in describing the ++ origin of the Work and reproducing the content of the NOTICE file. ++ ++ 7. Disclaimer of Warranty. Unless required by applicable law or ++ agreed to in writing, Licensor provides the Work (and each ++ Contributor provides its Contributions) on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or ++ implied, including, without limitation, any warranties or conditions ++ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A ++ PARTICULAR PURPOSE. You are solely responsible for determining the ++ appropriateness of using or redistributing the Work and assume any ++ risks associated with Your exercise of permissions under this License. ++ ++ 8. Limitation of Liability. In no event and under no legal theory, ++ whether in tort (including negligence), contract, or otherwise, ++ unless required by applicable law (such as deliberate and grossly ++ negligent acts) or agreed to in writing, shall any Contributor be ++ liable to You for damages, including any direct, indirect, special, ++ incidental, or consequential damages of any character arising as a ++ result of this License or out of the use or inability to use the ++ Work (including but not limited to damages for loss of goodwill, ++ work stoppage, computer failure or malfunction, or any and all ++ other commercial damages or losses), even if such Contributor ++ has been advised of the possibility of such damages. ++ ++ 9. Accepting Warranty or Additional Liability. While redistributing ++ the Work or Derivative Works thereof, You may choose to offer, ++ and charge a fee for, acceptance of support, warranty, indemnity, ++ or other liability obligations and/or rights consistent with this ++ License. However, in accepting such obligations, You may act only ++ on Your own behalf and on Your sole responsibility, not on behalf ++ of any other Contributor, and only if You agree to indemnify, ++ defend, and hold each Contributor harmless for any liability ++ incurred by, or claims asserted against, such Contributor by reason ++ of your accepting any such warranty or additional liability. ++ ++ END OF TERMS AND CONDITIONS ++ ++ APPENDIX: How to apply the Apache License to your work. ++ ++ To apply the Apache License to your work, attach the following ++ boilerplate notice, with the fields enclosed by brackets "[]" ++ replaced with your own identifying information. (Don't include ++ the brackets!) The text should be enclosed in the appropriate ++ comment syntax for the file format. We also recommend that a ++ file or class name and description of purpose be included on the ++ same "printed page" as the copyright notice for easier ++ identification within third-party archives. ++ ++ Copyright [yyyy] [name of copyright owner] ++ ++ Licensed under the Apache License, Version 2.0 (the "License"); ++ you may not use this file except in compliance with the License. ++ You may obtain a copy of the License at ++ ++ http://www.apache.org/licenses/LICENSE-2.0 ++ ++ Unless required by applicable law or agreed to in writing, software ++ distributed under the License is distributed on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ See the License for the specific language governing permissions and ++ limitations under the License. +-- +2.50.1 (Apple Git-155) + From 6020808e32d26b2b88aa8ced2bbc392d07da2b58 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 23 Oct 2026 16:57:15 +0800 Subject: [PATCH 3/9] tesserocr: carry the tesseract osdetect patch to 2.12.0 --- ...detect-guard-against-unset-script-id.patch | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 patches/tesserocr/2.12.0/0002-tesseract-osdetect-guard-against-unset-script-id.patch diff --git a/patches/tesserocr/2.12.0/0002-tesseract-osdetect-guard-against-unset-script-id.patch b/patches/tesserocr/2.12.0/0002-tesseract-osdetect-guard-against-unset-script-id.patch new file mode 100644 index 000000000000..98ae0942e5d2 --- /dev/null +++ b/patches/tesserocr/2.12.0/0002-tesseract-osdetect-guard-against-unset-script-id.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sat, 12 Sep 2026 15:40:00 +0200 +Subject: [PATCH] tesseract: osdetect - guard against unset script_id + +This patches vendored tesseract 5.5.1 (built from source in +CIBW_BEFORE_ALL_LINUX, not tesserocr's own Python sources), applied with +`patch -p1` against the extracted tarball since it has no .git directory. + +ScriptDetector::detect_blob() indexes a std::vector with a +BLOB_CHOICE's script_id() without checking it against the vector's bounds +first. BLOB_CHOICE's default constructor sets script_id_ = -1 as its +"unset" sentinel (ccstruct/ratngs.h); when the classifier leaves a choice +at that default, id is a negative int, and passing it to `done.at(id)` +implicitly converts it to a huge size_t, so `at()` throws +std::out_of_range. Nothing up the call stack (through +TessBaseAPI::DetectOS, which promises a plain return value, not an +exception) catches it, so it reaches std::terminate() and aborts the +whole process -- confirmed via a real gdb backtrace on riscv64 +(SIGABRT -> abort -> __cxa_throw -> std::__throw_out_of_range_fmt -> +vector::_M_range_check -> vector::at, called from +ScriptDetector::detect_blob at this exact line). Reproduced against the +tests/eurotext.tif image tesserocr's own test_detect_os exercises via +TessBaseAPI::DetectOS(); not riscv64-specific in root cause (the sentinel +and the unchecked .at() call are architecture-independent), just apparently +not triggered by whatever inputs upstream's own test coverage uses. Still +unfixed on tesseract's `main` branch as of this writing. + +Upstream-Status: To upstream [not yet submitted; this repo's own policy bars opening issues/PRs against any GitHub repo other than riseproject-dev/python-wheels, so filing this against tesseract-ocr/tesseract is left to whoever reviews this port] +--- + src/ccmain/osdetect.cpp | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/ccmain/osdetect.cpp b/src/ccmain/osdetect.cpp +index 64a8bd6..f619d89 100644 +--- a/src/ccmain/osdetect.cpp ++++ b/src/ccmain/osdetect.cpp +@@ -491,8 +491,11 @@ void ScriptDetector::detect_blob(BLOB_CHOICE_LIST *scores) { + continue; // Not found in list. + } + } +- // Script already processed before. +- if (done.at(id)) { ++ // Script already processed before. A BLOB_CHOICE's script_id defaults ++ // to -1 (unset) when its classifier never assigned a real script id; ++ // done.at() on that (wrapping to SIZE_MAX as an unsigned index) throws ++ // std::out_of_range, which nothing here catches, aborting the process. ++ if (id < 0 || done.at(id)) { + continue; + } + done[id] = true; +-- +2.43.0 + From bf90c6987a9983b87ca8f4d9ceaf2dd55479298c Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 23 Oct 2026 16:57:21 +0800 Subject: [PATCH 4/9] tesserocr: carry the test_detect_os patch to 2.12.0 --- ...coincidental-script-index-bound-in-t.patch | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 patches/tesserocr/2.12.0/0003-tests-drop-the-coincidental-script-index-bound-in-t.patch diff --git a/patches/tesserocr/2.12.0/0003-tests-drop-the-coincidental-script-index-bound-in-t.patch b/patches/tesserocr/2.12.0/0003-tests-drop-the-coincidental-script-index-bound-in-t.patch new file mode 100644 index 000000000000..68cb2940e0dd --- /dev/null +++ b/patches/tesserocr/2.12.0/0003-tests-drop-the-coincidental-script-index-bound-in-t.patch @@ -0,0 +1,55 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sat, 12 Sep 2026 15:55:00 +0200 +Subject: [PATCH] tests: drop the coincidental script-index bound in + test_detect_os + +The removed assertLess(orientation["script"], len(get_languages()[1])) +check compares two unrelated things: "script" is an index into +tesseract's internal OSD script table (a fixed, compiled-in table +unrelated to what's installed), while get_languages()[1] just counts +whichever .traineddata files happen to be sitting in TESSDATA_PREFIX. +The assertion only ever held when a test environment's installed +language-pack count happened to exceed the real script table's size by +chance; this port's minimal two-file tessdata set (eng + osd, no LSTM/ +legacy duplicates needed elsewhere) doesn't, and there is no principled +number of extra .traineddata files to add that would make the bound +meaningful rather than coincidental. The test's own next two lines +already call the follow-on script-name lookup "not reliable" for the +same underlying reason. + +Every other assertion in test_detect_os is left intact and does pass, +including the one that matters most for this port: DetectOS() itself +completing without the vector::at(-1) crash tesseract's own +osdetect.cpp has (patched separately, 0002-*.patch in this directory). + +Upstream-Status: To upstream [not yet submitted; this repo's own policy bars opening issues/PRs against any GitHub repo other than riseproject-dev/python-wheels] +--- + tests/test_api.py | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/tests/test_api.py b/tests/test_api.py +index 88b0fa3..3a7ac8c 100644 +--- a/tests/test_api.py ++++ b/tests/test_api.py +@@ -234,9 +234,14 @@ class TestTessBaseApi(unittest.TestCase): + for k in ["sconfidence", "oconfidence", "script", "orientation"] + ) + self.assertEqual(orientation["orientation"], 0) +- # this is sorted alphabetically! +- languages = tesserocr.get_languages()[1] +- self.assertLess(orientation["script"], len(languages)) ++ # The upstream assertLess(orientation["script"], len(get_languages()[1])) ++ # bound removed here is coincidental, not a real invariant: "script" is ++ # an index into tesseract's internal OSD script table, while ++ # get_languages()[1] just counts whichever .traineddata files happen ++ # to be in TESSDATA_PREFIX -- it only held when a test environment's ++ # installed language-pack count happened to exceed the real script ++ # table size. This port's minimal (2-file) tessdata set doesn't, same ++ # as it wouldn't on any platform using the same setup. + # therefore does not work + # script_name = languages[orientation["script"]] + # self.assertEqual(script_name, 'Latin') # cannot test: not reliable +-- +2.43.0 + From d63eacbcd326951234a64fd77572da3b7961d573 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 23 Oct 2026 16:58:42 +0800 Subject: [PATCH 5/9] tesserocr: fix 2.12.0 patch 0002 trailer --- ...0002-tesseract-osdetect-guard-against-unset-script-id.patch | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/patches/tesserocr/2.12.0/0002-tesseract-osdetect-guard-against-unset-script-id.patch b/patches/tesserocr/2.12.0/0002-tesseract-osdetect-guard-against-unset-script-id.patch index 98ae0942e5d2..01e92ef69529 100644 --- a/patches/tesserocr/2.12.0/0002-tesseract-osdetect-guard-against-unset-script-id.patch +++ b/patches/tesserocr/2.12.0/0002-tesseract-osdetect-guard-against-unset-script-id.patch @@ -49,6 +49,5 @@ index 64a8bd6..f619d89 100644 continue; } done[id] = true; --- +-- 2.43.0 - From 91de65584bd76104b07299b47142e7ebd312ef40 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 23 Oct 2026 16:58:48 +0800 Subject: [PATCH 6/9] tesserocr: fix 2.12.0 patch 0003 trailer --- ...3-tests-drop-the-coincidental-script-index-bound-in-t.patch | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/patches/tesserocr/2.12.0/0003-tests-drop-the-coincidental-script-index-bound-in-t.patch b/patches/tesserocr/2.12.0/0003-tests-drop-the-coincidental-script-index-bound-in-t.patch index 68cb2940e0dd..c8ded0158551 100644 --- a/patches/tesserocr/2.12.0/0003-tests-drop-the-coincidental-script-index-bound-in-t.patch +++ b/patches/tesserocr/2.12.0/0003-tests-drop-the-coincidental-script-index-bound-in-t.patch @@ -50,6 +50,5 @@ index 88b0fa3..3a7ac8c 100644 # therefore does not work # script_name = languages[orientation["script"]] # self.assertEqual(script_name, 'Latin') # cannot test: not reliable --- +-- 2.43.0 - From 188484a4d208967aaef44411d7d09a51386ac3dd Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 25 Oct 2026 19:02:16 +0100 Subject: [PATCH 7/9] tesserocr: point the wheel tests at the installed tessdata --- .github/workflows/build-tesserocr.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/build-tesserocr.yml b/.github/workflows/build-tesserocr.yml index e4340e0f9a48..c910d539193b 100644 --- a/.github/workflows/build-tesserocr.yml +++ b/.github/workflows/build-tesserocr.yml @@ -163,6 +163,16 @@ jobs: # exactly what Pillow's setup.py auto-detects and needs for that build. CIBW_TEST_REQUIRES: pytest Pillow CIBW_TEST_SOURCES: tests + # From 2.12.0 upstream's [tool.cibuildwheel] sets test-environment + # TESSDATA_PREFIX=.test-tessdata (a dir of its own test models, not + # staged here via CIBW_TEST_SOURCES), which would override the + # CIBW_ENVIRONMENT value above and leave tesseract with no tessdata + # ("Failed to init API ... invalid tessdata path: ./"). Point it at + # the tessdata CIBW_BEFORE_ALL_LINUX installed, and keep the + # OMP_THREAD_LIMIT=1 upstream sets alongside it. + CIBW_TEST_ENVIRONMENT: >- + TESSDATA_PREFIX=/usr/local/share/tessdata + OMP_THREAD_LIMIT=1 # A relative path, not {project}/tests: tesserocr has no src/ layout, so # {project}/tests would put {project} (the full checkout) on pytest's # rootdir-inserted sys.path and shadow the installed wheel with the From 0d9a6f2b9d7afe72396b90ef10489f8bd7e174d2 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 26 Oct 2026 07:15:39 +0100 Subject: [PATCH 8/9] tesserocr: list the vendored licences in 2.12.0's setup.py license_files 2.12.0's setup.py hardcodes license_files, so the root LICENSE.leptonica and LICENSE.tesseract-ocr added by patch 0001 no longer reached the wheel and the post-build licence check failed. --- ...tonica-and-tesseract-licences-with-t.patch | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/patches/tesserocr/2.12.0/0001-package-the-leptonica-and-tesseract-licences-with-t.patch b/patches/tesserocr/2.12.0/0001-package-the-leptonica-and-tesseract-licences-with-t.patch index 148768fff55b..a4d0b1554b8f 100644 --- a/patches/tesserocr/2.12.0/0001-package-the-leptonica-and-tesseract-licences-with-t.patch +++ b/patches/tesserocr/2.12.0/0001-package-the-leptonica-and-tesseract-licences-with-t.patch @@ -8,15 +8,17 @@ The riscv64 wheels build leptonica (BSD-2-Clause) and tesseract the resulting liblept/libtesseract shared libraries into the wheel, but the wheel otherwise only ships tesserocr's own MIT LICENSE. -Add both licence texts at the project root -- setuptools' default -license_files glob (LICEN[CS]E*) picks them up with no setup.py change -and lands them in dist-info/licenses/ beside the project's own LICENSE. +Add both licence texts at the project root and list them in setup.py's +license_files (2.12.0 hardcodes that list instead of using setuptools' +LICEN[CS]E* default), so they land in dist-info/licenses/ beside the +project's own LICENSE. Upstream-Status: Inappropriate [riscv64-specific build dependency, not part of upstream's own build] --- + setup.py | 7 ++++++- LICENSE.leptonica | 26 ++++++ LICENSE.tesseract-ocr | 202 ++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 228 insertions(+) + 3 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 LICENSE.leptonica create mode 100644 LICENSE.tesseract-ocr @@ -260,6 +262,24 @@ index 0000000..d645695 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +diff --git a/setup.py b/setup.py +index 5cd7f39..993d714 100644 +--- a/setup.py ++++ b/setup.py +@@ -293,7 +293,12 @@ setup( + author="Fayez Zouheiry", + author_email="iamfayez@gmail.com", + license="MIT", +- license_files=["LICENSE", *glob.glob(".wheel-licenses/**/*", recursive=True)], ++ license_files=[ ++ "LICENSE", ++ "LICENSE.leptonica", ++ "LICENSE.tesseract-ocr", ++ *glob.glob(".wheel-licenses/**/*", recursive=True), ++ ], + python_requires=">=3.9", + classifiers=[ + "Development Status :: 5 - Production/Stable", -- 2.50.1 (Apple Git-155) From 706890679c9efc6186aa44520b26159d5cd36d24 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Tue, 27 Oct 2026 17:31:11 +0100 Subject: [PATCH 9/9] tesserocr: give 2.12.0's parallel threading tests headroom on slow runners test_parallel_api_lifecycle waits on its worker threads with a hardcoded result(timeout=45) and takes ~43s when it passes on the riscv64 runners; the cp313-musllinux leg took ~57s and failed with a bare TimeoutError while the same wheel passed on every other leg. Carry a patch raising the wait to 180s and apply it for the versions that ship the test. --- .github/workflows/build-tesserocr.yml | 9 +++- ...arallel-threading-tests-headroom-on-.patch | 43 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 patches/tesserocr/2.12.0/0004-tests-give-the-parallel-threading-tests-headroom-on-.patch diff --git a/.github/workflows/build-tesserocr.yml b/.github/workflows/build-tesserocr.yml index c910d539193b..5a72a6df614a 100644 --- a/.github/workflows/build-tesserocr.yml +++ b/.github/workflows/build-tesserocr.yml @@ -77,13 +77,18 @@ jobs: persist-credentials: false - name: Patch tesserocr source - # 0001-*/0003-*: git apply against this checkout. 0002-* targets the + # 0001-*/0003-*/0004-*: git apply against this checkout. 0002-* targets the # vendored tesseract tarball (no .git dir) fetched later in # CIBW_BEFORE_ALL_LINUX, applied there with `patch -p1` instead -- - # excluded from this glob. + # excluded from this glob. 0004-* only exists from 2.12.0 (tests/test_threads.py + # is new there), hence the existence check. run: | git apply python-wheels/patches/tesserocr/${{ env.TESSEROCR_VERSION }}/0001-*.patch git apply python-wheels/patches/tesserocr/${{ env.TESSEROCR_VERSION }}/0003-*.patch + for p in python-wheels/patches/tesserocr/${{ env.TESSEROCR_VERSION }}/0004-*.patch; do + [ -e "$p" ] || continue + git apply "$p" + done - uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 with: diff --git a/patches/tesserocr/2.12.0/0004-tests-give-the-parallel-threading-tests-headroom-on-.patch b/patches/tesserocr/2.12.0/0004-tests-give-the-parallel-threading-tests-headroom-on-.patch new file mode 100644 index 000000000000..8dca96da3e48 --- /dev/null +++ b/patches/tesserocr/2.12.0/0004-tests-give-the-parallel-threading-tests-headroom-on-.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Tue, 27 Oct 2026 17:00:00 +0200 +Subject: [PATCH] tests: give the parallel threading tests headroom on slow + runners + +test_parallel_api_lifecycle and test_parallel_helpers wait for their +four worker threads with a hardcoded result(timeout=45). On the riscv64 +runners the lifecycle test takes ~43s when it passes, so the margin is +already gone and an unlucky run trips it with a bare TimeoutError (the +cp313-musllinux leg of 2.12.0 took ~57s and failed; the same wheel +passed on every other leg). Nothing hangs or crashes: the barrier waits +inside the workers still succeed and every other test passes. + +Raise the wait to 180s. The assertions inside the workers are unchanged. + +Upstream-Status: Inappropriate [riscv64 CI runners are slower than the fixed 45s margin upstream sized for its own hosts] +--- + tests/test_threads.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tests/test_threads.py b/tests/test_threads.py +index 22ae5d9..26b2686 100644 +--- a/tests/test_threads.py ++++ b/tests/test_threads.py +@@ -98,7 +98,7 @@ assert not sys._is_gil_enabled(), 'tesserocr enabled the GIL' + with ThreadPoolExecutor(max_workers=4) as pool: + results = [pool.submit(recognize) for _ in range(4)] + for result in results: +- result.result(timeout=45) ++ result.result(timeout=180) + """) + + @unittest.skipUnless(importlib.util.find_spec("PIL"), "Pillow not installed") +@@ -123,5 +123,5 @@ assert not sys._is_gil_enabled(), 'tesserocr enabled the GIL' + with ThreadPoolExecutor(max_workers=4) as pool: + results = [pool.submit(recognize) for _ in range(4)] + for result in results: +- result.result(timeout=45) ++ result.result(timeout=180) + """) +-- +2.43.0