├── .github ├── actions │ ├── build-conda-packages │ │ └── action.yaml │ └── build-java-runtime │ │ └── action.yaml └── workflows │ ├── deployment.yaml │ └── test.yaml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── build_java_runtime.py ├── conda.recipe ├── bld.bat ├── build.sh ├── conda_build_config.yaml └── meta.yaml ├── hatch_build.py ├── pyproject.toml ├── src └── jdk4py │ ├── __init__.py │ ├── _added_modules.py │ ├── _included_locales.py │ ├── py.typed │ └── version.json ├── tests ├── __init__.py ├── resources │ ├── HelloWorld.jar │ ├── HelloWorld.java │ ├── PrintAvailableLocales.jar │ ├── PrintAvailableLocales.java │ └── README.md └── test_jdk4py.py └── uv.lock /.github/actions/build-conda-packages/action.yaml: -------------------------------------------------------------------------------- 1 | name: Build Conda packages 2 | description: Build the Conda packages 3 | runs: 4 | using: composite 5 | steps: 6 | - uses: conda-incubator/setup-miniconda@v3 7 | with: 8 | conda-build-version: "24.7.1" 9 | 10 | # See https://github.com/conda/conda/issues/12563#issuecomment-1494264704. 11 | - if: ${{ runner.os == 'Windows' }} 12 | run: echo "LIBARCHIVE=C:\\Miniconda\\Library\\bin\\archive.dll" >> "$GITHUB_ENV" 13 | shell: bash -l {0} 14 | 15 | - run: conda build --no-anaconda-upload --output-folder dist . 16 | # See https://github.com/conda-incubator/setup-miniconda/blame/059455a698430d8b68fa317268fa2e3da3492a98/README.md#L609-L610. 17 | shell: bash -l {0} 18 | -------------------------------------------------------------------------------- /.github/actions/build-java-runtime/action.yaml: -------------------------------------------------------------------------------- 1 | name: Build Java runtime 2 | description: Build the Java runtime after installing the required dependencies. 3 | inputs: 4 | python-version: 5 | description: The version of Python to set up. 6 | required: true 7 | runs: 8 | using: composite 9 | steps: 10 | - uses: astral-sh/setup-uv@v5 11 | with: 12 | enable-cache: true 13 | python-version: ${{ inputs.python-version }} 14 | version: "0.5.16" # Keep synced with same field in `deployment.yaml`. 15 | 16 | - id: get-java-version 17 | run: uv run python -c 'from jdk4py import JAVA_VERSION; print(f"""version={".".join(str(number) for number in JAVA_VERSION)}""")' >> "$GITHUB_OUTPUT" 18 | shell: bash 19 | 20 | - uses: actions/setup-java@v4 21 | with: 22 | distribution: temurin 23 | java-version: ${{ steps.get-java-version.outputs.version }} 24 | 25 | - run: uv run python build_java_runtime.py 26 | shell: bash 27 | -------------------------------------------------------------------------------- /.github/workflows/deployment.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy Conda packages and Python wheels 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | build: 9 | name: Build on ${{ matrix.runner }} 10 | runs-on: ${{ matrix.runner }} 11 | strategy: 12 | matrix: 13 | runner: 14 | - macos-13 # x64 15 | - macos-14 # arm64 16 | - ubuntu-24.04 17 | - ubuntu-24.04-arm64 # GitHub-hosted larger runner in the ActiveViam organization. 18 | - windows-2022 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - uses: ./.github/actions/build-java-runtime 23 | with: 24 | python-version: "3.10" 25 | 26 | - run: uv build --wheel 27 | 28 | - uses: actions/upload-artifact@v4 29 | with: 30 | if-no-files-found: error 31 | name: jdk4py-${{ matrix.runner }}-python-wheel 32 | path: dist/jdk4py-*.whl 33 | 34 | - uses: ./.github/actions/build-conda-packages 35 | 36 | - uses: actions/upload-artifact@v4 37 | with: 38 | if-no-files-found: error 39 | name: jdk4py-${{ matrix.runner }}-conda-packages 40 | path: dist/*/jdk4py-*.tar.bz2 41 | 42 | publish: 43 | environment: deployment 44 | name: Publish Conda packages and Python wheels 45 | needs: build 46 | runs-on: ubuntu-24.04 47 | permissions: 48 | # Required for trusted publishing. 49 | id-token: write 50 | steps: 51 | - uses: astral-sh/setup-uv@v5 52 | with: 53 | version: "0.5.16" # Keep synced with same field in `build-java-runtime/action.yaml`. 54 | 55 | - uses: actions/download-artifact@v4 56 | with: 57 | merge-multiple: true 58 | path: dist/ 59 | 60 | - if: ${{ runner.debug == '1' }} 61 | name: List Conda packages and Python wheels 62 | run: ls -R dist/ 63 | 64 | - name: Publish Conda packages 65 | env: 66 | JDK4PY_CONDA_CHANNEL_TOKEN: ${{ secrets.CONDA_CHANNEL_TOKEN }} 67 | JDK4PY_CONDA_CHANNEL_URL: ${{ vars.CONDA_CHANNEL_URL }} 68 | JDK4PY_CONDA_CHANNEL_USERNAME: ${{ vars.CONDA_CHANNEL_USERNAME }} 69 | run: | 70 | cd dist/ 71 | ls */jdk4py-*.tar.bz2 | \ 72 | while read filepath; do 73 | echo Uploading ${filepath} 74 | curl --fail --user "$JDK4PY_CONDA_CHANNEL_USERNAME":"$JDK4PY_CONDA_CHANNEL_TOKEN" --upload-file ${filepath} "$JDK4PY_CONDA_CHANNEL_URL/$(dirname $filepath)/" 75 | done 76 | shell: bash 77 | 78 | - name: Publish Python wheels 79 | run: uv publish 80 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | pull_request: 4 | types: [opened, synchronize] 5 | 6 | jobs: 7 | test: 8 | name: Test on ${{ matrix.runner }} with Python ${{ matrix.python }} 9 | runs-on: ${{ matrix.runner }} 10 | strategy: 11 | matrix: 12 | runner: 13 | - macos-13 # x64 14 | - macos-14 # arm64 15 | - ubuntu-24.04 16 | - ubuntu-24.04-arm64 # GitHub-hosted larger runner in the ActiveViam organization. 17 | - windows-2022 18 | python: 19 | - "3.10" 20 | include: 21 | - runner: ubuntu-24.04 22 | python: "3.11" 23 | - runner: ubuntu-24.04 24 | python: "3.12" 25 | - runner: ubuntu-24.04 26 | python: "3.13" 27 | fail-fast: false 28 | steps: 29 | - uses: actions/checkout@v4 30 | 31 | - uses: ./.github/actions/build-java-runtime 32 | with: 33 | python-version: ${{ matrix.python}} 34 | 35 | - run: uv build --wheel 36 | 37 | # No need to test each platform and Python version. 38 | - if: ${{ matrix.runner == 'ubuntu-24.04' && matrix.python == '3.10' }} 39 | run: uv run ruff format --check 40 | 41 | # No need to test each platform and Python version. 42 | - if: ${{ matrix.runner == 'ubuntu-24.04' && matrix.python == '3.10' }} 43 | run: uv run ruff check 44 | 45 | # No need to test each platform and Python version. 46 | - if: ${{ matrix.runner == 'ubuntu-24.04' && matrix.python == '3.10' }} 47 | run: uv run mypy 48 | 49 | - run: uv run pytest 50 | 51 | # The Python wheels are not tied to a specific Python version. 52 | - if: ${{ matrix.python == '3.10' }} 53 | uses: actions/upload-artifact@v4 54 | with: 55 | if-no-files-found: error 56 | name: jdk4py-${{ matrix.runner }}-python-wheel 57 | path: dist/jdk4py-*.whl 58 | 59 | conda-package: 60 | name: Test Conda packaging on ${{ matrix.runner }} 61 | runs-on: ${{ matrix.runner }} 62 | strategy: 63 | matrix: 64 | runner: 65 | - macos-13 # x64 66 | - macos-14 # arm64 67 | - ubuntu-24.04 68 | - ubuntu-24.04-arm64 # GitHub-hosted larger runner in the ActiveViam organization. 69 | - windows-2022 70 | steps: 71 | - uses: actions/checkout@v4 72 | 73 | - uses: ./.github/actions/build-java-runtime 74 | with: 75 | # No need to test each Python version. 76 | python-version: "3.10" 77 | 78 | - uses: ./.github/actions/build-conda-packages 79 | 80 | - uses: actions/upload-artifact@v4 81 | with: 82 | if-no-files-found: error 83 | name: jdk4py-${{ matrix.runner }}-conda-packages 84 | path: dist/*/jdk4py-*.tar.bz2 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv/ 2 | __pycache__/ 3 | build/ 4 | dist/ 5 | java-runtime/ 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "charliermarsh.ruff", 4 | "esbenp.prettier-vscode", 5 | "ms-python.python", 6 | "tamasfe.even-better-toml" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[json]": { 3 | "editor.defaultFormatter": "esbenp.prettier-vscode" 4 | }, 5 | "[python]": { 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll": "explicit", 8 | "source.organizeImports": "explicit" 9 | }, 10 | "editor.defaultFormatter": "charliermarsh.ruff", 11 | "editor.formatOnType": true 12 | }, 13 | "[toml]": { 14 | "editor.defaultFormatter": "tamasfe.even-better-toml" 15 | }, 16 | "files.insertFinalNewline": true, 17 | "files.trimFinalNewlines": true, 18 | "files.trimTrailingWhitespace": true, 19 | "python.analysis.autoImportCompletions": true, 20 | "python.languageServer": "Pylance", 21 | "python.testing.pytestEnabled": true, 22 | } 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The GNU General Public License (GPL) 2 | 3 | Version 2, June 1991 4 | 5 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 6 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this license 9 | document, but changing it is not allowed. 10 | 11 | Preamble 12 | 13 | The licenses for most software are designed to take away your freedom to share 14 | and change it. By contrast, the GNU General Public License is intended to 15 | guarantee your freedom to share and change free software--to make sure the 16 | software is free for all its users. This General Public License applies to 17 | most of the Free Software Foundation's software and to any other program whose 18 | authors commit to using it. (Some other Free Software Foundation software is 19 | covered by the GNU Library General Public License instead.) You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not price. Our 23 | General Public Licenses are designed to make sure that you have the freedom to 24 | distribute copies of free software (and charge for this service if you wish), 25 | that you receive source code or can get it if you want it, that you can change 26 | the software or use pieces of it in new free programs; and that you know you 27 | can do these things. 28 | 29 | To protect your rights, we need to make restrictions that forbid anyone to deny 30 | you these rights or to ask you to surrender the rights. These restrictions 31 | translate to certain responsibilities for you if you distribute copies of the 32 | software, or if you modify it. 33 | 34 | For example, if you distribute copies of such a program, whether gratis or for 35 | a fee, you must give the recipients all the rights that you have. You must 36 | make sure that they, too, receive or can get the source code. And you must 37 | show them these terms so they know their rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and (2) 40 | offer you this license which gives you legal permission to copy, distribute 41 | and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain that 44 | everyone understands that there is no warranty for this free software. If the 45 | software is modified by someone else and passed on, we want its recipients to 46 | know that what they have is not the original, so that any problems introduced 47 | by others will not reflect on the original authors' reputations. 48 | 49 | Finally, any free program is threatened constantly by software patents. We 50 | wish to avoid the danger that redistributors of a free program will 51 | individually obtain patent licenses, in effect making the program proprietary. 52 | To prevent this, we have made it clear that any patent must be licensed for 53 | everyone's free use or not licensed at all. 54 | 55 | The precise terms and conditions for copying, distribution and modification 56 | follow. 57 | 58 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 59 | 60 | 0. This License applies to any program or other work which contains a notice 61 | placed by the copyright holder saying it may be distributed under the terms of 62 | this General Public License. The "Program", below, refers to any such program 63 | or work, and a "work based on the Program" means either the Program or any 64 | derivative work under copyright law: that is to say, a work containing the 65 | Program or a portion of it, either verbatim or with modifications and/or 66 | translated into another language. (Hereinafter, translation is included 67 | without limitation in the term "modification".) Each licensee is addressed as 68 | "you". 69 | 70 | Activities other than copying, distribution and modification are not covered by 71 | this License; they are outside its scope. The act of running the Program is 72 | not restricted, and the output from the Program is covered only if its contents 73 | constitute a work based on the Program (independent of having been made by 74 | running the Program). Whether that is true depends on what the Program does. 75 | 76 | 1. You may copy and distribute verbatim copies of the Program's source code as 77 | you receive it, in any medium, provided that you conspicuously and 78 | appropriately publish on each copy an appropriate copyright notice and 79 | disclaimer of warranty; keep intact all the notices that refer to this License 80 | and to the absence of any warranty; and give any other recipients of the 81 | Program a copy of this License along with the Program. 82 | 83 | You may charge a fee for the physical act of transferring a copy, and you may 84 | at your option offer warranty protection in exchange for a fee. 85 | 86 | 2. You may modify your copy or copies of the Program or any portion of it, thus 87 | forming a work based on the Program, and copy and distribute such modifications 88 | or work under the terms of Section 1 above, provided that you also meet all of 89 | these conditions: 90 | 91 | a) You must cause the modified files to carry prominent notices stating 92 | that you changed the files and the date of any change. 93 | 94 | b) You must cause any work that you distribute or publish, that in whole or 95 | in part contains or is derived from the Program or any part thereof, to be 96 | licensed as a whole at no charge to all third parties under the terms of 97 | this License. 98 | 99 | c) If the modified program normally reads commands interactively when run, 100 | you must cause it, when started running for such interactive use in the 101 | most ordinary way, to print or display an announcement including an 102 | appropriate copyright notice and a notice that there is no warranty (or 103 | else, saying that you provide a warranty) and that users may redistribute 104 | the program under these conditions, and telling the user how to view a copy 105 | of this License. (Exception: if the Program itself is interactive but does 106 | not normally print such an announcement, your work based on the Program is 107 | not required to print an announcement.) 108 | 109 | These requirements apply to the modified work as a whole. If identifiable 110 | sections of that work are not derived from the Program, and can be reasonably 111 | considered independent and separate works in themselves, then this License, and 112 | its terms, do not apply to those sections when you distribute them as separate 113 | works. But when you distribute the same sections as part of a whole which is a 114 | work based on the Program, the distribution of the whole must be on the terms 115 | of this License, whose permissions for other licensees extend to the entire 116 | whole, and thus to each and every part regardless of who wrote it. 117 | 118 | Thus, it is not the intent of this section to claim rights or contest your 119 | rights to work written entirely by you; rather, the intent is to exercise the 120 | right to control the distribution of derivative or collective works based on 121 | the Program. 122 | 123 | In addition, mere aggregation of another work not based on the Program with the 124 | Program (or with a work based on the Program) on a volume of a storage or 125 | distribution medium does not bring the other work under the scope of this 126 | License. 127 | 128 | 3. You may copy and distribute the Program (or a work based on it, under 129 | Section 2) in object code or executable form under the terms of Sections 1 and 130 | 2 above provided that you also do one of the following: 131 | 132 | a) Accompany it with the complete corresponding machine-readable source 133 | code, which must be distributed under the terms of Sections 1 and 2 above 134 | on a medium customarily used for software interchange; or, 135 | 136 | b) Accompany it with a written offer, valid for at least three years, to 137 | give any third party, for a charge no more than your cost of physically 138 | performing source distribution, a complete machine-readable copy of the 139 | corresponding source code, to be distributed under the terms of Sections 1 140 | and 2 above on a medium customarily used for software interchange; or, 141 | 142 | c) Accompany it with the information you received as to the offer to 143 | distribute corresponding source code. (This alternative is allowed only 144 | for noncommercial distribution and only if you received the program in 145 | object code or executable form with such an offer, in accord with 146 | Subsection b above.) 147 | 148 | The source code for a work means the preferred form of the work for making 149 | modifications to it. For an executable work, complete source code means all 150 | the source code for all modules it contains, plus any associated interface 151 | definition files, plus the scripts used to control compilation and installation 152 | of the executable. However, as a special exception, the source code 153 | distributed need not include anything that is normally distributed (in either 154 | source or binary form) with the major components (compiler, kernel, and so on) 155 | of the operating system on which the executable runs, unless that component 156 | itself accompanies the executable. 157 | 158 | If distribution of executable or object code is made by offering access to copy 159 | from a designated place, then offering equivalent access to copy the source 160 | code from the same place counts as distribution of the source code, even though 161 | third parties are not compelled to copy the source along with the object code. 162 | 163 | 4. You may not copy, modify, sublicense, or distribute the Program except as 164 | expressly provided under this License. Any attempt otherwise to copy, modify, 165 | sublicense or distribute the Program is void, and will automatically terminate 166 | your rights under this License. However, parties who have received copies, or 167 | rights, from you under this License will not have their licenses terminated so 168 | long as such parties remain in full compliance. 169 | 170 | 5. You are not required to accept this License, since you have not signed it. 171 | However, nothing else grants you permission to modify or distribute the Program 172 | or its derivative works. These actions are prohibited by law if you do not 173 | accept this License. Therefore, by modifying or distributing the Program (or 174 | any work based on the Program), you indicate your acceptance of this License to 175 | do so, and all its terms and conditions for copying, distributing or modifying 176 | the Program or works based on it. 177 | 178 | 6. Each time you redistribute the Program (or any work based on the Program), 179 | the recipient automatically receives a license from the original licensor to 180 | copy, distribute or modify the Program subject to these terms and conditions. 181 | You may not impose any further restrictions on the recipients' exercise of the 182 | rights granted herein. You are not responsible for enforcing compliance by 183 | third parties to this License. 184 | 185 | 7. If, as a consequence of a court judgment or allegation of patent 186 | infringement or for any other reason (not limited to patent issues), conditions 187 | are imposed on you (whether by court order, agreement or otherwise) that 188 | contradict the conditions of this License, they do not excuse you from the 189 | conditions of this License. If you cannot distribute so as to satisfy 190 | simultaneously your obligations under this License and any other pertinent 191 | obligations, then as a consequence you may not distribute the Program at all. 192 | For example, if a patent license would not permit royalty-free redistribution 193 | of the Program by all those who receive copies directly or indirectly through 194 | you, then the only way you could satisfy both it and this License would be to 195 | refrain entirely from distribution of the Program. 196 | 197 | If any portion of this section is held invalid or unenforceable under any 198 | particular circumstance, the balance of the section is intended to apply and 199 | the section as a whole is intended to apply in other circumstances. 200 | 201 | It is not the purpose of this section to induce you to infringe any patents or 202 | other property right claims or to contest validity of any such claims; this 203 | section has the sole purpose of protecting the integrity of the free software 204 | distribution system, which is implemented by public license practices. Many 205 | people have made generous contributions to the wide range of software 206 | distributed through that system in reliance on consistent application of that 207 | system; it is up to the author/donor to decide if he or she is willing to 208 | distribute software through any other system and a licensee cannot impose that 209 | choice. 210 | 211 | This section is intended to make thoroughly clear what is believed to be a 212 | consequence of the rest of this License. 213 | 214 | 8. If the distribution and/or use of the Program is restricted in certain 215 | countries either by patents or by copyrighted interfaces, the original 216 | copyright holder who places the Program under this License may add an explicit 217 | geographical distribution limitation excluding those countries, so that 218 | distribution is permitted only in or among countries not thus excluded. In 219 | such case, this License incorporates the limitation as if written in the body 220 | of this License. 221 | 222 | 9. The Free Software Foundation may publish revised and/or new versions of the 223 | General Public License from time to time. Such new versions will be similar in 224 | spirit to the present version, but may differ in detail to address new problems 225 | or concerns. 226 | 227 | Each version is given a distinguishing version number. If the Program 228 | specifies a version number of this License which applies to it and "any later 229 | version", you have the option of following the terms and conditions either of 230 | that version or of any later version published by the Free Software Foundation. 231 | If the Program does not specify a version number of this License, you may 232 | choose any version ever published by the Free Software Foundation. 233 | 234 | 10. If you wish to incorporate parts of the Program into other free programs 235 | whose distribution conditions are different, write to the author to ask for 236 | permission. For software which is copyrighted by the Free Software Foundation, 237 | write to the Free Software Foundation; we sometimes make exceptions for this. 238 | Our decision will be guided by the two goals of preserving the free status of 239 | all derivatives of our free software and of promoting the sharing and reuse of 240 | software generally. 241 | 242 | NO WARRANTY 243 | 244 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR 245 | THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE 246 | STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE 247 | PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 248 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 249 | FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND 250 | PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, 251 | YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 252 | 253 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL 254 | ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE 255 | PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 256 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR 257 | INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA 258 | BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 259 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER 260 | OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 261 | 262 | END OF TERMS AND CONDITIONS 263 | 264 | How to Apply These Terms to Your New Programs 265 | 266 | If you develop a new program, and you want it to be of the greatest possible 267 | use to the public, the best way to achieve this is to make it free software 268 | which everyone can redistribute and change under these terms. 269 | 270 | To do so, attach the following notices to the program. It is safest to attach 271 | them to the start of each source file to most effectively convey the exclusion 272 | of warranty; and each file should have at least the "copyright" line and a 273 | pointer to where the full notice is found. 274 | 275 | One line to give the program's name and a brief idea of what it does. 276 | 277 | Copyright (C) 278 | 279 | This program is free software; you can redistribute it and/or modify it 280 | under the terms of the GNU General Public License as published by the Free 281 | Software Foundation; either version 2 of the License, or (at your option) 282 | any later version. 283 | 284 | This program is distributed in the hope that it will be useful, but WITHOUT 285 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 286 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 287 | more details. 288 | 289 | You should have received a copy of the GNU General Public License along 290 | with this program; if not, write to the Free Software Foundation, Inc., 291 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 292 | 293 | Also add information on how to contact you by electronic and paper mail. 294 | 295 | If the program is interactive, make it output a short notice like this when it 296 | starts in an interactive mode: 297 | 298 | Gnomovision version 69, Copyright (C) year name of author Gnomovision comes 299 | with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free 300 | software, and you are welcome to redistribute it under certain conditions; 301 | type 'show c' for details. 302 | 303 | The hypothetical commands 'show w' and 'show c' should show the appropriate 304 | parts of the General Public License. Of course, the commands you use may be 305 | called something other than 'show w' and 'show c'; they could even be 306 | mouse-clicks or menu items--whatever suits your program. 307 | 308 | You should also get your employer (if you work as a programmer) or your school, 309 | if any, to sign a "copyright disclaimer" for the program, if necessary. Here 310 | is a sample; alter the names: 311 | 312 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 313 | 'Gnomovision' (which makes passes at compilers) written by James Hacker. 314 | 315 | signature of Ty Coon, 1 April 1989 316 | 317 | Ty Coon, President of Vice 318 | 319 | This General Public License does not permit incorporating your program into 320 | proprietary programs. If your program is a subroutine library, you may 321 | consider it more useful to permit linking proprietary applications with the 322 | library. If this is what you want to do, use the GNU Library General Public 323 | License instead of this License. 324 | 325 | 326 | "CLASSPATH" EXCEPTION TO THE GPL 327 | 328 | Certain source files distributed by Oracle America and/or its affiliates are 329 | subject to the following clarification and special exception to the GPL, but 330 | only where Oracle has expressly included in the particular source file's header 331 | the words "Oracle designates this particular file as subject to the "Classpath" 332 | exception as provided by Oracle in the LICENSE file that accompanied this code." 333 | 334 | Linking this library statically or dynamically with other modules is making 335 | a combined work based on this library. Thus, the terms and conditions of 336 | the GNU General Public License cover the whole combination. 337 | 338 | As a special exception, the copyright holders of this library give you 339 | permission to link this library with independent modules to produce an 340 | executable, regardless of the license terms of these independent modules, 341 | and to copy and distribute the resulting executable under terms of your 342 | choice, provided that you also meet, for each linked independent module, 343 | the terms and conditions of the license of that module. An independent 344 | module is a module which is not derived from or based on this library. If 345 | you modify this library, you may extend this exception to your version of 346 | the library, but you are not obligated to do so. If you do not wish to do 347 | so, delete this exception statement from your version. 348 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jdk4py 2 | 3 | A JDK shipped in a Python package. 4 | 5 | [![PyPI version](https://badge.fury.io/py/jdk4py.svg)](https://badge.fury.io/py/jdk4py) 6 | 7 | ## Install 8 | 9 | ```bash 10 | pip install jdk4py 11 | ``` 12 | 13 | or as a Conda package: 14 | 15 | ```bash 16 | conda config --add channels https://conda.atoti.io 17 | conda install jdk4py 18 | ``` 19 | 20 | ## Usage 21 | 22 | ```python 23 | >>> from jdk4py import JAVA, JAVA_HOME, JAVA_VERSION 24 | >>> JAVA_HOME 25 | PosixPath('/Users/johndoe/dev/jdk4py/jdk4py/java-runtime') 26 | >>> JAVA 27 | PosixPath('/Users/johndoe/dev/jdk4py/jdk4py/java-runtime/bin/java') 28 | >>> JAVA_VERSION 29 | (21, 0, 4) 30 | >>> from subprocess import run 31 | >>> some_java_options = ["-Xmx16G", "-Xms2G"] 32 | >>> run( 33 | ... [JAVA, "-jar", "HelloWorld.jar", *some_java_options], 34 | ... capture_output=True, 35 | ... check=True, 36 | ... text=True, 37 | ... ).stdout.strip() 38 | "Hello, World!" 39 | ``` 40 | 41 | ## Versioning 42 | 43 | `jdk4py`'s version contains 4 numbers: 44 | 45 | - The first 3 numbers correspond to the JDK version. 46 | - The fourth number is the library API version. 47 | -------------------------------------------------------------------------------- /build_java_runtime.py: -------------------------------------------------------------------------------- 1 | from shutil import rmtree 2 | from subprocess import check_call 3 | 4 | from jdk4py import JAVA_HOME 5 | from jdk4py._added_modules import ADDED_MODULES 6 | from jdk4py._included_locales import INCLUDED_LOCALES 7 | 8 | 9 | def build_java_runtime() -> None: 10 | rmtree(JAVA_HOME, ignore_errors=True) 11 | 12 | check_call( # noqa: S603 13 | [ # noqa: S607 14 | "jlink", 15 | "--no-man-pages", 16 | "--strip-debug", 17 | "--add-modules", 18 | ",".join(ADDED_MODULES), 19 | f"--include-locales={','.join(INCLUDED_LOCALES)}", 20 | "--output", 21 | JAVA_HOME, 22 | ], 23 | ) 24 | 25 | 26 | if __name__ == "__main__": 27 | build_java_runtime() 28 | -------------------------------------------------------------------------------- /conda.recipe/bld.bat: -------------------------------------------------------------------------------- 1 | python -m pip install --verbose . 2 | if errorlevel 1 exit 1 3 | -------------------------------------------------------------------------------- /conda.recipe/build.sh: -------------------------------------------------------------------------------- 1 | python -m pip install --verbose . 2 | -------------------------------------------------------------------------------- /conda.recipe/conda_build_config.yaml: -------------------------------------------------------------------------------- 1 | python: 2 | - "3.10" 3 | - "3.11" 4 | - "3.12" 5 | - "3.13" 6 | -------------------------------------------------------------------------------- /conda.recipe/meta.yaml: -------------------------------------------------------------------------------- 1 | {% set pyproject = load_file_data("pyproject.toml") %} 2 | {% set dev_dependencies = pyproject["tool"]["uv"]["dev-dependencies"] %} 3 | {% set version = load_file_data("src/jdk4py/version.json") %} 4 | 5 | package: 6 | name: {{ pyproject["project"]["name"] }} 7 | version: {{ version }} 8 | 9 | source: 10 | path: ../ 11 | 12 | build: 13 | # Binaries are broken on macOS without it. 14 | binary_relocation: False 15 | include_recipe: False 16 | 17 | requirements: 18 | build: 19 | - python {{ python }} 20 | - pip ==24.2 21 | {% for dependency in dev_dependencies if dependency.startswith("hatchling ") %} 22 | - {{ dependency }} 23 | {% endfor %} 24 | run: 25 | - python {{ python }} 26 | 27 | test: 28 | source_files: 29 | - tests/ 30 | requires: 31 | {% for dependency in dev_dependencies if dependency.startswith("pytest ") %} 32 | - {{ dependency }} 33 | {% endfor %} 34 | commands: 35 | - pytest 36 | 37 | about: 38 | dev_url: {{ pyproject["project"]["urls"]["Repository"] }} 39 | license: GNU General Public License v2.0 40 | license_file: LICENSE 41 | summary: {{ pyproject["project"]["description"] }} 42 | 43 | extra: 44 | recipe-maintainers: 45 | {% for author in pyproject["project"]["authors"] %} 46 | - {{ author["name"] }} 47 | {% endfor %} 48 | -------------------------------------------------------------------------------- /hatch_build.py: -------------------------------------------------------------------------------- 1 | import json 2 | import platform 3 | from pathlib import Path 4 | from typing import Literal 5 | 6 | from hatchling.builders.config import BuilderConfigBound 7 | from hatchling.builders.hooks.plugin.interface import BuildHookInterface 8 | from hatchling.metadata.plugin.interface import MetadataHookInterface 9 | 10 | _PROJECT_DIRECTORY = Path(__file__).parent 11 | 12 | 13 | _Architecture = Literal["arm64", "x64"] 14 | 15 | 16 | def _get_architecture(machine: str) -> _Architecture: 17 | match machine.lower(): 18 | case "amd64" | "x64" | "x86_64": 19 | return "x64" 20 | case "aarch64" | "arm64": 21 | return "arm64" 22 | case _: 23 | raise ValueError(f"Unsupported machine: `{machine}`.") 24 | 25 | 26 | def _get_platform_tag(system: str, architecture: _Architecture) -> str: 27 | # Tag values taken from https://pypi.org/project/numpy/2.1.0/#files. 28 | match system.lower(): 29 | case "darwin": 30 | match architecture: 31 | case "arm64": 32 | return "macosx_11_0_arm64" 33 | case "x64": 34 | return "macosx_10_9_x86_64" 35 | case "linux": 36 | match architecture: 37 | case "arm64": 38 | return "manylinux_2_17_aarch64" 39 | case "x64": 40 | return "manylinux_2_17_x86_64" 41 | case "windows": 42 | match architecture: 43 | case "x64": 44 | return "win_amd64" 45 | case _: 46 | raise ValueError( 47 | f"Unsupported {system} architecture: `{architecture}`.", 48 | ) 49 | case _: 50 | raise ValueError(f"Unsupported system: `{system}`.") 51 | 52 | 53 | class BuildHook(BuildHookInterface[BuilderConfigBound]): 54 | def initialize( 55 | self, 56 | version: str, # noqa: ARG002 57 | build_data: dict[str, object], 58 | ) -> None: 59 | python_tag = "py3" 60 | abi_tag = "none" 61 | 62 | system = platform.system() 63 | architecture = _get_architecture(platform.machine()) 64 | platform_tag = _get_platform_tag(system, architecture) 65 | 66 | build_data["tag"] = f"{python_tag}-{abi_tag}-{platform_tag}" 67 | 68 | 69 | class MetadataHook(MetadataHookInterface): 70 | def update(self, metadata: dict[str, object]) -> None: 71 | version = json.loads( 72 | (_PROJECT_DIRECTORY / "src" / "jdk4py" / "version.json").read_bytes(), 73 | ) 74 | assert isinstance(version, str) 75 | metadata["version"] = version 76 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | build-backend = "hatchling.build" 3 | requires = [ 4 | # Keep pinned to ensure same version is used by `conda build` (version must be available on anaconda). 5 | # Keep in sync with version in `tool.uv.dev-dependencies`. 6 | "hatchling ==1.27.0", 7 | ] 8 | 9 | [project] 10 | authors = [{ email = "dev@atoti.io", name = "ActiveViam" }] 11 | classifiers = [ 12 | "Development Status :: 5 - Production/Stable", 13 | "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", 14 | "Operating System :: MacOS", 15 | "Operating System :: Microsoft :: Windows", 16 | "Operating System :: POSIX :: Linux", 17 | "Programming Language :: Python :: 3", 18 | ] 19 | dependencies = [] 20 | description = "A JDK shipped in a Python package" 21 | dynamic = ["version"] 22 | keywords = ["jdk", "java", "jvm", "jre"] 23 | name = "jdk4py" 24 | readme = "README.md" 25 | requires-python = ">=3.10" 26 | 27 | [project.urls] 28 | Repository = "https://github.com/activeviam/jdk4py" 29 | 30 | [tool.hatch.build.hooks.custom] 31 | 32 | [tool.hatch.metadata.hooks.custom] 33 | 34 | [tool.hatch.build.targets.wheel] 35 | artifacts = ["java-runtime"] 36 | 37 | [tool.mypy] 38 | # Remove once https://github.com/python/mypy/issues/10428 is fixed. 39 | files = "build_java_runtime.py,hatch_build.py,src,tests" 40 | strict = true 41 | warn_redundant_casts = true 42 | warn_unused_configs = true 43 | warn_unused_ignores = true 44 | 45 | [tool.pytest.ini_options] 46 | filterwarnings = ["error"] 47 | 48 | [tool.ruff.lint] 49 | ignore = [ 50 | "COM812", 51 | "D100", 52 | "D101", 53 | "D102", 54 | "D103", 55 | "D104", 56 | "D203", 57 | "D211", 58 | "D212", 59 | "EM102", 60 | "ISC001", 61 | "S101", 62 | "TRY003", 63 | ] 64 | select = ["ALL"] 65 | 66 | [tool.uv] 67 | dev-dependencies = [ 68 | # Keep in sync with version in `build-system.requires`. 69 | "hatchling ==1.27.0", 70 | "mypy", 71 | # Keep pinned to ensure same version is used by `conda build` (version must be available on anaconda). 72 | "pytest ==7.4.4", 73 | "ruff", 74 | ] 75 | required-version = ">=0.5.16,<0.6" 76 | trusted-publishing = "always" 77 | -------------------------------------------------------------------------------- /src/jdk4py/__init__.py: -------------------------------------------------------------------------------- 1 | """JDK packaged for Python.""" 2 | 3 | import json 4 | from pathlib import Path 5 | 6 | _PACKAGE_DIRECTORY = Path(__file__).parent 7 | 8 | JAVA_HOME = _PACKAGE_DIRECTORY / "java-runtime" 9 | JAVA = JAVA_HOME / "bin" / "java" 10 | 11 | _VERSION = json.loads((_PACKAGE_DIRECTORY / "version.json").read_bytes()) 12 | assert isinstance(_VERSION, str) 13 | _MAJOR, _MINOR, _PATCH = tuple(int(number) for number in _VERSION.split(".")[:3]) 14 | JAVA_VERSION = _MAJOR, _MINOR, _PATCH 15 | -------------------------------------------------------------------------------- /src/jdk4py/_added_modules.py: -------------------------------------------------------------------------------- 1 | ADDED_MODULES = frozenset( 2 | { 3 | "java.se", 4 | "jdk.crypto.ec", 5 | "jdk.httpserver", 6 | "jdk.jcmd", 7 | "jdk.jfr", 8 | "jdk.localedata", 9 | "jdk.management.agent", 10 | "jdk.management.jfr", 11 | "jdk.security.auth", 12 | "jdk.unsupported", 13 | "jdk.zipfs", 14 | }, 15 | ) 16 | -------------------------------------------------------------------------------- /src/jdk4py/_included_locales.py: -------------------------------------------------------------------------------- 1 | INCLUDED_LOCALES = frozenset( 2 | [ 3 | "bn-IN", 4 | "da-DK", 5 | "de-DE", 6 | "en-US", 7 | "en-GB", 8 | "es-ES", 9 | "es-MX", 10 | "fr-FR", 11 | "it-IT", 12 | "ja-JP", 13 | "pt-BR", 14 | "ru-RU", 15 | "zh-CN", 16 | ], 17 | ) 18 | -------------------------------------------------------------------------------- /src/jdk4py/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/activeviam/jdk4py/5a73545b36343c6a7622da7838eb55e656d760b2/src/jdk4py/py.typed -------------------------------------------------------------------------------- /src/jdk4py/version.json: -------------------------------------------------------------------------------- 1 | "21.0.4.2" 2 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/activeviam/jdk4py/5a73545b36343c6a7622da7838eb55e656d760b2/tests/__init__.py -------------------------------------------------------------------------------- /tests/resources/HelloWorld.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/activeviam/jdk4py/5a73545b36343c6a7622da7838eb55e656d760b2/tests/resources/HelloWorld.jar -------------------------------------------------------------------------------- /tests/resources/HelloWorld.java: -------------------------------------------------------------------------------- 1 | package resources; 2 | 3 | public class HelloWorld { 4 | 5 | public static void main(String[] args) { 6 | System.out.println("Hello, World"); 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /tests/resources/PrintAvailableLocales.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/activeviam/jdk4py/5a73545b36343c6a7622da7838eb55e656d760b2/tests/resources/PrintAvailableLocales.jar -------------------------------------------------------------------------------- /tests/resources/PrintAvailableLocales.java: -------------------------------------------------------------------------------- 1 | package resources; 2 | 3 | import java.util.Locale; 4 | 5 | public class PrintAvailableLocales { 6 | 7 | public static void main(String[] args) { 8 | for (final Locale locale: java.text.DecimalFormatSymbols.getAvailableLocales()) { 9 | System.out.println(locale); 10 | } 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /tests/resources/README.md: -------------------------------------------------------------------------------- 1 | JARs in this folder can be generated like this: 2 | 3 | ```bash 4 | cd .. 5 | javac resources/$CLASS_NAME.java 6 | jar cfe resources/$CLASS_NAME.jar resources.$CLASS_NAME resources/$CLASS_NAME.class 7 | rm resources/$CLASS_NAME.class 8 | jar tf resources/$CLASS_NAME.jar 9 | ``` 10 | -------------------------------------------------------------------------------- /tests/test_jdk4py.py: -------------------------------------------------------------------------------- 1 | import re 2 | from pathlib import Path 3 | from subprocess import run 4 | 5 | from jdk4py import JAVA, JAVA_VERSION 6 | from jdk4py._included_locales import INCLUDED_LOCALES 7 | 8 | _TEST_RESOURCES_DIRECTORY = Path(__file__).parent / "resources" 9 | 10 | 11 | def test_java_version() -> None: 12 | completed_process = run( # noqa: S603 13 | [JAVA, "-version"], 14 | capture_output=True, 15 | check=True, 16 | text=True, 17 | ) 18 | match = re.match(r'^openjdk version "(?P[^"]+)"', completed_process.stderr) 19 | assert match, f"Unexpected output:\n{completed_process.stdout}" 20 | version = match.group("version") 21 | assert isinstance(version, str) 22 | assert tuple([int(number) for number in version.split(".")][:3]) == JAVA_VERSION 23 | 24 | 25 | def test_jar_execution() -> None: 26 | completed_process = run( # noqa: S603 27 | [JAVA, "-jar", _TEST_RESOURCES_DIRECTORY / "HelloWorld.jar"], 28 | capture_output=True, 29 | check=True, 30 | text=True, 31 | ) 32 | assert completed_process.stdout.strip() == "Hello, World" 33 | 34 | 35 | def test_included_locales() -> None: 36 | completed_process = run( # noqa: S603 37 | [JAVA, "-jar", _TEST_RESOURCES_DIRECTORY / "PrintAvailableLocales.jar"], 38 | capture_output=True, 39 | check=True, 40 | text=True, 41 | ) 42 | locales = { 43 | locale.replace("_", "-") 44 | for locale in completed_process.stdout.strip().splitlines() 45 | } 46 | assert locales.issuperset(INCLUDED_LOCALES) 47 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.10" 3 | 4 | [[package]] 5 | name = "colorama" 6 | version = "0.4.6" 7 | source = { registry = "https://pypi.org/simple" } 8 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 9 | wheels = [ 10 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 11 | ] 12 | 13 | [[package]] 14 | name = "exceptiongroup" 15 | version = "1.2.2" 16 | source = { registry = "https://pypi.org/simple" } 17 | sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } 18 | wheels = [ 19 | { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, 20 | ] 21 | 22 | [[package]] 23 | name = "hatchling" 24 | version = "1.27.0" 25 | source = { registry = "https://pypi.org/simple" } 26 | dependencies = [ 27 | { name = "packaging" }, 28 | { name = "pathspec" }, 29 | { name = "pluggy" }, 30 | { name = "tomli", marker = "python_full_version < '3.11'" }, 31 | { name = "trove-classifiers" }, 32 | ] 33 | sdist = { url = "https://files.pythonhosted.org/packages/8f/8a/cc1debe3514da292094f1c3a700e4ca25442489731ef7c0814358816bb03/hatchling-1.27.0.tar.gz", hash = "sha256:971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6", size = 54983 } 34 | wheels = [ 35 | { url = "https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", hash = "sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b", size = 75794 }, 36 | ] 37 | 38 | [[package]] 39 | name = "iniconfig" 40 | version = "2.0.0" 41 | source = { registry = "https://pypi.org/simple" } 42 | sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } 43 | wheels = [ 44 | { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, 45 | ] 46 | 47 | [[package]] 48 | name = "jdk4py" 49 | version = "21.0.4.2" 50 | source = { editable = "." } 51 | 52 | [package.dev-dependencies] 53 | dev = [ 54 | { name = "hatchling" }, 55 | { name = "mypy" }, 56 | { name = "pytest" }, 57 | { name = "ruff" }, 58 | ] 59 | 60 | [package.metadata] 61 | 62 | [package.metadata.requires-dev] 63 | dev = [ 64 | { name = "hatchling", specifier = "==1.27.0" }, 65 | { name = "mypy" }, 66 | { name = "pytest", specifier = "==7.4.4" }, 67 | { name = "ruff" }, 68 | ] 69 | 70 | [[package]] 71 | name = "mypy" 72 | version = "1.14.1" 73 | source = { registry = "https://pypi.org/simple" } 74 | dependencies = [ 75 | { name = "mypy-extensions" }, 76 | { name = "tomli", marker = "python_full_version < '3.11'" }, 77 | { name = "typing-extensions" }, 78 | ] 79 | sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051 } 80 | wheels = [ 81 | { url = "https://files.pythonhosted.org/packages/9b/7a/87ae2adb31d68402da6da1e5f30c07ea6063e9f09b5e7cfc9dfa44075e74/mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb", size = 11211002 }, 82 | { url = "https://files.pythonhosted.org/packages/e1/23/eada4c38608b444618a132be0d199b280049ded278b24cbb9d3fc59658e4/mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0", size = 10358400 }, 83 | { url = "https://files.pythonhosted.org/packages/43/c9/d6785c6f66241c62fd2992b05057f404237deaad1566545e9f144ced07f5/mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d", size = 12095172 }, 84 | { url = "https://files.pythonhosted.org/packages/c3/62/daa7e787770c83c52ce2aaf1a111eae5893de9e004743f51bfcad9e487ec/mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b", size = 12828732 }, 85 | { url = "https://files.pythonhosted.org/packages/1b/a2/5fb18318a3637f29f16f4e41340b795da14f4751ef4f51c99ff39ab62e52/mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427", size = 13012197 }, 86 | { url = "https://files.pythonhosted.org/packages/28/99/e153ce39105d164b5f02c06c35c7ba958aaff50a2babba7d080988b03fe7/mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f", size = 9780836 }, 87 | { url = "https://files.pythonhosted.org/packages/da/11/a9422850fd506edbcdc7f6090682ecceaf1f87b9dd847f9df79942da8506/mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", size = 11120432 }, 88 | { url = "https://files.pythonhosted.org/packages/b6/9e/47e450fd39078d9c02d620545b2cb37993a8a8bdf7db3652ace2f80521ca/mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", size = 10279515 }, 89 | { url = "https://files.pythonhosted.org/packages/01/b5/6c8d33bd0f851a7692a8bfe4ee75eb82b6983a3cf39e5e32a5d2a723f0c1/mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", size = 12025791 }, 90 | { url = "https://files.pythonhosted.org/packages/f0/4c/e10e2c46ea37cab5c471d0ddaaa9a434dc1d28650078ac1b56c2d7b9b2e4/mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", size = 12749203 }, 91 | { url = "https://files.pythonhosted.org/packages/88/55/beacb0c69beab2153a0f57671ec07861d27d735a0faff135a494cd4f5020/mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", size = 12885900 }, 92 | { url = "https://files.pythonhosted.org/packages/a2/75/8c93ff7f315c4d086a2dfcde02f713004357d70a163eddb6c56a6a5eff40/mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", size = 9777869 }, 93 | { url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668 }, 94 | { url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060 }, 95 | { url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167 }, 96 | { url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341 }, 97 | { url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991 }, 98 | { url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016 }, 99 | { url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097 }, 100 | { url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728 }, 101 | { url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965 }, 102 | { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660 }, 103 | { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198 }, 104 | { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276 }, 105 | { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905 }, 106 | ] 107 | 108 | [[package]] 109 | name = "mypy-extensions" 110 | version = "1.0.0" 111 | source = { registry = "https://pypi.org/simple" } 112 | sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } 113 | wheels = [ 114 | { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, 115 | ] 116 | 117 | [[package]] 118 | name = "packaging" 119 | version = "24.2" 120 | source = { registry = "https://pypi.org/simple" } 121 | sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } 122 | wheels = [ 123 | { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, 124 | ] 125 | 126 | [[package]] 127 | name = "pathspec" 128 | version = "0.12.1" 129 | source = { registry = "https://pypi.org/simple" } 130 | sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } 131 | wheels = [ 132 | { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, 133 | ] 134 | 135 | [[package]] 136 | name = "pluggy" 137 | version = "1.5.0" 138 | source = { registry = "https://pypi.org/simple" } 139 | sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } 140 | wheels = [ 141 | { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, 142 | ] 143 | 144 | [[package]] 145 | name = "pytest" 146 | version = "7.4.4" 147 | source = { registry = "https://pypi.org/simple" } 148 | dependencies = [ 149 | { name = "colorama", marker = "sys_platform == 'win32'" }, 150 | { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, 151 | { name = "iniconfig" }, 152 | { name = "packaging" }, 153 | { name = "pluggy" }, 154 | { name = "tomli", marker = "python_full_version < '3.11'" }, 155 | ] 156 | sdist = { url = "https://files.pythonhosted.org/packages/80/1f/9d8e98e4133ffb16c90f3b405c43e38d3abb715bb5d7a63a5a684f7e46a3/pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", size = 1357116 } 157 | wheels = [ 158 | { url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287 }, 159 | ] 160 | 161 | [[package]] 162 | name = "ruff" 163 | version = "0.9.0" 164 | source = { registry = "https://pypi.org/simple" } 165 | sdist = { url = "https://files.pythonhosted.org/packages/75/48/385f276f41e89623a5ea8e4eb9c619a44fdfc2a64849916b3584eca6cb9f/ruff-0.9.0.tar.gz", hash = "sha256:143f68fa5560ecf10fc49878b73cee3eab98b777fcf43b0e62d43d42f5ef9d8b", size = 3489167 } 166 | wheels = [ 167 | { url = "https://files.pythonhosted.org/packages/e9/01/e0885e5519212efc7ab9d868bc39cb9781931c4c6f9b17becafa81193ec4/ruff-0.9.0-py3-none-linux_armv6l.whl", hash = "sha256:949b3513f931741e006cf267bf89611edff04e1f012013424022add3ce78f319", size = 10647069 }, 168 | { url = "https://files.pythonhosted.org/packages/dd/69/510a9a5781dcf84c2ad513c2003936fefc802f39c745d5f2355d77fa45fd/ruff-0.9.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:99fbcb8c7fe94ae1e462ab2a1ef17cb20b25fb6438b9f198b1bcf5207a0a7916", size = 10401936 }, 169 | { url = "https://files.pythonhosted.org/packages/07/9f/37fb86bfdf28c4cbfe94cbcc01fb9ab0cb8128548f243f34d5298b212562/ruff-0.9.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0b022afd8eb0fcfce1e0adec84322abf4d6ce3cd285b3b99c4f17aae7decf749", size = 10010347 }, 170 | { url = "https://files.pythonhosted.org/packages/30/0d/b95121f53c7f7bfb7ba427a35d25f983ed3b476620c5cd69f45caa5b294e/ruff-0.9.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:336567ce92c9ca8ec62780d07b5fa11fbc881dc7bb40958f93a7d621e7ab4589", size = 10882152 }, 171 | { url = "https://files.pythonhosted.org/packages/d4/0b/a955cb6b19eb900c4c594707ab72132ce2d5cd8b5565137fb8fed21b8f08/ruff-0.9.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d338336c44bda602dc8e8766836ac0441e5b0dfeac3af1bd311a97ebaf087a75", size = 10405502 }, 172 | { url = "https://files.pythonhosted.org/packages/1e/fa/9a6c70af74f20edd2519b89eb3322f4bfa399315cf306383443700f2d6b6/ruff-0.9.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9b3ececf523d733e90b540e7afcc0494189e8999847f8855747acd5a9a8c45f", size = 11465069 }, 173 | { url = "https://files.pythonhosted.org/packages/ee/8b/7effac8915470da496be009fe861060baff2692f92801976b2c01cdc8c54/ruff-0.9.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a11c0872a31232e473e2e0e2107f3d294dbadd2f83fb281c3eb1c22a24866924", size = 12176850 }, 174 | { url = "https://files.pythonhosted.org/packages/bd/ed/626179786889eca47b1e821c1582622ac0c1c8f01d60ac974f8b96867a57/ruff-0.9.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5fd06220c17a9cc0dc7fc6552f2ac4db74e8e8bff9c401d160ac59d00566f54", size = 11700963 }, 175 | { url = "https://files.pythonhosted.org/packages/75/79/094c34ddec47fd3c61a0bc5e83ca164344c592949cff91f05961fd40922e/ruff-0.9.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0457e775c74bf3976243f910805242b7dcd389e1d440deccbd1194ca17a5728c", size = 13096560 }, 176 | { url = "https://files.pythonhosted.org/packages/e7/23/ec85dca0dcb329835197401734501bfa1d39e72343df64628c67b72bcbf5/ruff-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05415599bbcb318f730ea1b46a39e4fbf71f6a63fdbfa1dda92efb55f19d7ecf", size = 11278658 }, 177 | { url = "https://files.pythonhosted.org/packages/6c/17/1b3ea5f06578ea1daa08ac35f9de099d1827eea6e116a8cabbf11235c925/ruff-0.9.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fbf9864b009e43cfc1c8bed1a6a4c529156913105780af4141ca4342148517f5", size = 10879847 }, 178 | { url = "https://files.pythonhosted.org/packages/a6/e5/00bc97d6f419da03c0d898e95cca77311494e7274dc7cc17d94976e32e52/ruff-0.9.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:37b3da222b12e2bb2ce628e02586ab4846b1ed7f31f42a5a0683b213453b2d49", size = 10494220 }, 179 | { url = "https://files.pythonhosted.org/packages/cc/70/d0a23d94f3e40b7ffac0e5506f33bb504672569173781a6c7cab0db6a4ba/ruff-0.9.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:733c0fcf2eb0c90055100b4ed1af9c9d87305b901a8feb6a0451fa53ed88199d", size = 11004182 }, 180 | { url = "https://files.pythonhosted.org/packages/20/8e/367cf8e401890f823d0e4eb33635d0113719d5660b6522b7295376dd95fd/ruff-0.9.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8221a454bfe5ccdf8017512fd6bb60e6ec30f9ea252b8a80e5b73619f6c3cefd", size = 11345761 }, 181 | { url = "https://files.pythonhosted.org/packages/fe/08/4b54e02da73060ebc29368ab15868613f7d2496bde3b01d284d5423646bc/ruff-0.9.0-py3-none-win32.whl", hash = "sha256:d345f2178afd192c7991ddee59155c58145e12ad81310b509bd2e25c5b0247b3", size = 8807005 }, 182 | { url = "https://files.pythonhosted.org/packages/a1/a7/0b422971e897c51bf805f998d75bcfe5d4d858f5002203832875fc91b733/ruff-0.9.0-py3-none-win_amd64.whl", hash = "sha256:0cbc0905d94d21305872f7f8224e30f4bbcd532bc21b2225b2446d8fc7220d19", size = 9689974 }, 183 | { url = "https://files.pythonhosted.org/packages/73/0e/c00f66731e514be3299801b1d9d54efae0abfe8f00a5c14155f2ab9e2920/ruff-0.9.0-py3-none-win_arm64.whl", hash = "sha256:7b1148771c6ca88f820d761350a053a5794bc58e0867739ea93eb5e41ad978cd", size = 9147729 }, 184 | ] 185 | 186 | [[package]] 187 | name = "tomli" 188 | version = "2.2.1" 189 | source = { registry = "https://pypi.org/simple" } 190 | sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } 191 | wheels = [ 192 | { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, 193 | { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, 194 | { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, 195 | { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, 196 | { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, 197 | { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, 198 | { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, 199 | { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, 200 | { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, 201 | { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, 202 | { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, 203 | { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, 204 | { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, 205 | { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, 206 | { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, 207 | { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, 208 | { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, 209 | { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, 210 | { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, 211 | { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, 212 | { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, 213 | { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, 214 | { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, 215 | { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, 216 | { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, 217 | { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, 218 | { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, 219 | { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, 220 | { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, 221 | { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, 222 | { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, 223 | ] 224 | 225 | [[package]] 226 | name = "trove-classifiers" 227 | version = "2025.1.7.14" 228 | source = { registry = "https://pypi.org/simple" } 229 | sdist = { url = "https://files.pythonhosted.org/packages/fb/14/53bf5a0b2ae13a7d3c9405797d00e286aa3d08e5acb853b262a0097b0c17/trove_classifiers-2025.1.7.14.tar.gz", hash = "sha256:0fd08ab2b517ee22f2a539dcdab772ccee4e744eff61ba819846a5fac913d285", size = 16130 } 230 | wheels = [ 231 | { url = "https://files.pythonhosted.org/packages/3b/e4/5bd91cb201c80bbccd8514f6e63f51b15f46b9f692723519aab155b32836/trove_classifiers-2025.1.7.14-py3-none-any.whl", hash = "sha256:969b4ea1ef4e5e91b0398b60ae3a5e94027a50a65d5410badc920b2fc3de7ebb", size = 13536 }, 232 | ] 233 | 234 | [[package]] 235 | name = "typing-extensions" 236 | version = "4.12.2" 237 | source = { registry = "https://pypi.org/simple" } 238 | sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } 239 | wheels = [ 240 | { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, 241 | ] 242 | --------------------------------------------------------------------------------