374 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			374 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| Metadata-Version: 2.1
 | |
| Name: pyinstaller-hooks-contrib
 | |
| Version: 2024.5
 | |
| Summary: Community maintained hooks for PyInstaller
 | |
| Home-page: https://github.com/pyinstaller/pyinstaller-hooks-contrib
 | |
| Download-URL: https://pypi.org/project/pyinstaller-hooks-contrib
 | |
| Maintainer: Legorooj
 | |
| Maintainer-email: legorooj@protonmail.com
 | |
| Keywords: pyinstaller development hooks
 | |
| Classifier: Intended Audience :: Developers
 | |
| Classifier: Topic :: Software Development :: Build Tools
 | |
| Classifier: License :: OSI Approved :: Apache Software License
 | |
| Classifier: License :: OSI Approved :: GNU General Public License v2 (GPLv2)
 | |
| Classifier: Natural Language :: English
 | |
| Classifier: Operating System :: OS Independent
 | |
| Classifier: Programming Language :: Python
 | |
| Classifier: Programming Language :: Python :: 3
 | |
| Requires-Python: >=3.7
 | |
| Description-Content-Type: text/markdown
 | |
| License-File: LICENSE
 | |
| License-File: LICENSE.APL.txt
 | |
| License-File: LICENSE.GPL.txt
 | |
| Requires-Dist: setuptools >=42.0.0
 | |
| Requires-Dist: packaging >=22.0
 | |
| Requires-Dist: importlib-metadata >=4.6 ; python_version < "3.10"
 | |
| 
 | |
| # `pyinstaller-hooks-contrib`: The PyInstaller community hooks repository
 | |
| 
 | |
| What happens when (your?) package doesn't work with PyInstaller? Say you have data files that you need at runtime?
 | |
| PyInstaller doesn't bundle those. Your package requires others which PyInstaller can't see? How do you fix that?
 | |
| 
 | |
| In summary, a "hook" file extends PyInstaller to adapt it to the special needs and methods used by a Python package.
 | |
| The word "hook" is used for two kinds of files. A runtime hook helps the bootloader to launch an app, setting up the
 | |
| environment. A package hook (there are several types of those) tells PyInstaller what to include in the final app -
 | |
| such as the data files and (hidden) imports mentioned above.
 | |
| 
 | |
| This repository is a collection of hooks for many packages, and allows PyInstaller to work with these packages
 | |
| seamlessly.
 | |
| 
 | |
| 
 | |
| ## Installation
 | |
| 
 | |
| `pyinstaller-hooks-contrib` is automatically installed when you install PyInstaller, or can be installed with pip:
 | |
| 
 | |
| ```commandline
 | |
| pip install -U pyinstaller-hooks-contrib
 | |
| ```
 | |
| 
 | |
| 
 | |
| ## I can't see a hook for `a-package`
 | |
| 
 | |
| Either `a-package` works fine without a hook, or no-one has contributed hooks.
 | |
| If you'd like to add a hook, or view information about hooks,
 | |
| please see below.
 | |
| 
 | |
| 
 | |
| ## Hook configuration (options)
 | |
| 
 | |
| Hooks that support configuration (options) and their options are documented in
 | |
| [Supported hooks and options](hooks-config.rst).
 | |
| 
 | |
| 
 | |
| ## I want to help!
 | |
| 
 | |
| If you've got a hook you want to share then great!
 | |
| The rest of this page will walk you through the process of contributing a hook.
 | |
| If you've been here before then you may want to skip to the [summary checklist](#summary)
 | |
| 
 | |
| **Unless you are very comfortable with `git rebase -i`, please provide one hook per pull request!**
 | |
| **If you have more than one then submit them in separate pull requests.**
 | |
| 
 | |
| 
 | |
| ### Setup
 | |
| 
 | |
| [Fork this repo](https://github.com/pyinstaller/pyinstaller-hooks-contrib/fork) if you haven't already done so.
 | |
| (If you have a fork already but its old, click the **Fetch upstream** button on your fork's homepage.)
 | |
| Clone and `cd` inside your fork by running the following (replacing `bob-the-barnacle` with your github username):
 | |
| 
 | |
| ```
 | |
| git clone https://github.com/bob-the-barnacle/pyinstaller-hoooks-contrib.git
 | |
| cd pyinstaller-hooks-contrib
 | |
| ```
 | |
| 
 | |
| Create a new branch for you changes (replacing `foo` with the name of the package):
 | |
| You can name this branch whatever you like.
 | |
| 
 | |
| ```
 | |
| git checkout -b hook-for-foo
 | |
| ```
 | |
| 
 | |
| If you wish to create a virtual environment then do it now before proceeding to the next step.
 | |
| 
 | |
| Install this repo in editable mode.
 | |
| This will overwrite your current installation.
 | |
| (Note that you can reverse this with `pip install --force-reinstall pyinstaller-hooks-contrib`).
 | |
| 
 | |
| ```
 | |
| pip install -e .
 | |
| pip install -r requirements-test.txt
 | |
| pip install flake8 pyinstaller
 | |
| ```
 | |
| 
 | |
| Note that on macOS and Linux, `pip` may by called `pip3`.
 | |
| If you normally use `pip3` and `python3` then use `pip3` here too.
 | |
| You may skip the 2<sup>nd</sup> line if you have no intention of providing tests (but please do provide tests!).
 | |
| 
 | |
| 
 | |
| ### Add the hook
 | |
| 
 | |
| Standard hooks live in the [src/_pyinstaller_hooks_contrib/hooks/stdhooks/](../master/src/_pyinstaller_hooks_contrib/hooks/stdhooks/) directory.
 | |
| Runtime hooks live in the [src/_pyinstaller_hooks_contrib/hooks/rthooks/](../master/src/_pyinstaller_hooks_contrib/hooks/rthooks/) directory.
 | |
| Simply copy your hook into there.
 | |
| If you're unsure if your hook is a runtime hook then it almost certainly is a standard hook.
 | |
| 
 | |
| Please annotate (with comments) anything unusual in the hook.
 | |
| *Unusual* here is defined as any of the following:
 | |
| 
 | |
| *   Long lists of `hiddenimport` submodules.
 | |
|     If you need lots of hidden imports then use [`collect_submodules('foo')`](https://pyinstaller.readthedocs.io/en/latest/hooks.html#PyInstaller.utils.hooks.collect_submodules).
 | |
|     For bonus points, track down why so many submodules are hidden. Typical causes are:
 | |
|     *   Lazily loaded submodules (`importlib.importmodule()` inside a module `__getattr__()`).
 | |
|     *   Dynamically loaded *backends*.
 | |
|     *   Usage of `Cython` or Python extension modules containing `import` statements.
 | |
| *   Use of [`collect_all()`](https://pyinstaller.readthedocs.io/en/latest/hooks.html#PyInstaller.utils.hooks.collect_all).
 | |
|     This function's performance is abismal and [it is broken by
 | |
|     design](https://github.com/pyinstaller/pyinstaller/issues/6458#issuecomment-1000481631) because it confuses
 | |
|     packages with distributions.
 | |
|     Check that you really do need to collect all of submodules, data files, binaries, metadata and dependencies.
 | |
|     If you do then add a comment to say so (and if you know it - why).
 | |
|     Do not simply use `collect_all()` just to *future proof* the hook.
 | |
| *   Any complicated `os.path` arithmetic (by which I simply mean overly complex filename manipulations).
 | |
| 
 | |
| 
 | |
| #### Add the copyright header
 | |
| 
 | |
| All source files must contain the copyright header to be covered by our terms and conditions.
 | |
| 
 | |
| If you are **adding** a new hook (or any new python file), copy/paste the appropriate copyright header (below) at the top
 | |
| replacing 2021 with the current year.
 | |
| 
 | |
| <details><summary>GPL 2 header for standard hooks or other Python files.</summary>
 | |
| 
 | |
| ```python
 | |
| # ------------------------------------------------------------------
 | |
| # Copyright (c) 2021 PyInstaller Development Team.
 | |
| #
 | |
| # This file is distributed under the terms of the GNU General Public
 | |
| # License (version 2.0 or later).
 | |
| #
 | |
| # The full license is available in LICENSE.GPL.txt, distributed with
 | |
| # this software.
 | |
| #
 | |
| # SPDX-License-Identifier: GPL-2.0-or-later
 | |
| # ------------------------------------------------------------------
 | |
| ```
 | |
| 
 | |
| </details>
 | |
| 
 | |
| <details><summary>APL header for runtime hooks only.
 | |
| Again, if you're unsure if your hook is a runtime hook then it'll be a standard hook.</summary>
 | |
| 
 | |
| ```python
 | |
| # ------------------------------------------------------------------
 | |
| # Copyright (c) 2021 PyInstaller Development Team.
 | |
| #
 | |
| # This file is distributed under the terms of the Apache License 2.0
 | |
| #
 | |
| # The full license is available in LICENSE.APL.txt, distributed with
 | |
| # this software.
 | |
| #
 | |
| # SPDX-License-Identifier: Apache-2.0
 | |
| # ------------------------------------------------------------------
 | |
| ```
 | |
| 
 | |
| </details>
 | |
| 
 | |
| 
 | |
| If you are **updating** a hook, skip this step.
 | |
| Do not update the year of the copyright header - even if it's out of date.
 | |
| 
 | |
| 
 | |
| ### Test
 | |
| 
 | |
| Having tests is key to our continuous integration.
 | |
| With them we can automatically verify that your hook works on all platforms, all Python versions and new versions of
 | |
| libraries as and when they are released.
 | |
| Without them, we have no idea if the hook is broken until someone finds out the hard way.
 | |
| Please write tests!!!
 | |
| 
 | |
| Some user interface libraries may be impossible to test without user interaction
 | |
| or a wrapper library for some web API may require credentials (and possibly a paid subscription) to test.
 | |
| In such cases, don't provide a test.
 | |
| Instead explain either in the commit message or when you open your pull request why an automatic test is impractical
 | |
| then skip on to [the next step](#run-linter).
 | |
| 
 | |
| 
 | |
| #### Write tests(s)
 | |
| 
 | |
| A test should be the least amount of code required to cause a breakage
 | |
| if you do not have the hook which you are contributing.
 | |
| For example if you are writing a hook for a library called `foo`
 | |
| which crashes immediately under PyInstaller on `import foo` then `import foo` is your test.
 | |
| If `import foo` works even without the hook then you will have to get a bit more creative.
 | |
| Good sources of such minimal tests are introductory examples
 | |
| from the documentation of whichever library you're writing a hook for.
 | |
| Package's internal data files and hidden dependencies are prone to moving around so
 | |
| tests should not explicitly check for presence of data files or hidden modules directly -
 | |
| rather they should use parts of the library which are expected to use said data files or hidden modules.
 | |
| 
 | |
| Tests currently all live in [src/_pyinstaller_hooks_contrib/tests/test_libraries.py](../master/src/_pyinstaller_hooks_contrib/tests/test_libraries.py).
 | |
| Navigate there and add something like the following, replacing all occurrences of `foo` with the real name of the library.
 | |
| (Note where you put it in that file doesn't matter.)
 | |
| 
 | |
| ```python
 | |
| @importorskip('foo')
 | |
| def test_foo(pyi_builder):
 | |
|     pyi_builder.test_source("""
 | |
| 
 | |
|         # Your test here!
 | |
|         import foo
 | |
| 
 | |
|         foo.something_fooey()
 | |
| 
 | |
|     """)
 | |
| ```
 | |
| 
 | |
| If the library has changed significantly over past versions then you may need to add version constraints to the test.
 | |
| To do that, replace the `@importorskip("foo")` with a call to `PyInstaller.utils.tests.requires()` (e.g.
 | |
| `@requires("foo >= 1.4")`) to only run the test if the given version constraint is satisfied.
 | |
| Note that `@importorskip` uses module names (something you'd `import`) whereas `@requires` uses distribution names
 | |
| (something you'd `pip install`) so you'd use `@importorskip("PIL")` but `@requires("pillow")`.
 | |
| For most packages, the distribution and packages names are the same.
 | |
| 
 | |
| 
 | |
| #### Run the test locally
 | |
| 
 | |
| Running our full test suite is not recommended as it will spend a very long time testing code which you have not touched.
 | |
| Instead, run tests individually using either the `-k` option to search for test names:
 | |
| 
 | |
| ```
 | |
| pytest -k test_foo
 | |
| ```
 | |
| 
 | |
| Or using full paths:
 | |
| ```
 | |
| pytest src/_pyinstaller_hooks_contrib/tests/test_libraries.py::test_foo
 | |
| ```
 | |
| 
 | |
| 
 | |
| #### Pin the test requirement
 | |
| 
 | |
| Get the version of the package you are working with (`pip show foo`)
 | |
| and add it to the [requirements-test-libraries.txt](../master/requirements-test-libraries.txt) file.
 | |
| The requirements already in there should guide you on the syntax.
 | |
| 
 | |
| 
 | |
| #### Run the test on CI/CD
 | |
| 
 | |
| <details><summary>CI/CD now triggers itself when you open a pull request.
 | |
| These instructions for triggering jobs manually are obsolete except in rare cases.</summary>
 | |
| 
 | |
| To test hooks on all platforms we use Github's continuous integration (CI/CD).
 | |
| Our CI/CD is a bit unusual in that it's triggered manually and takes arguments
 | |
| which limit which tests are run.
 | |
| This is for the same reason we filter tests when running locally -
 | |
| the full test suite takes ages.
 | |
| 
 | |
| First push the changes you've made so far.
 | |
| 
 | |
| ```commandline
 | |
| git push --set-upstream origin hook-for-foo
 | |
| ```
 | |
| 
 | |
| Replace *billy-the-buffalo* with your Github username in the following url then open it.
 | |
| It should take you to the `oneshot-test` actions workflow on your fork.
 | |
| You may be asked if you want to enable actions on your fork - say yes.
 | |
| ```
 | |
| https://github.com/billy-the-buffalo/pyinstaller-hooks-contrib/actions/workflows/oneshot-test.yml
 | |
| ```
 | |
| 
 | |
| Find the **Run workflow** button and click on it.
 | |
| If you can't see the button,
 | |
| select the **Oneshot test** tab from the list of workflows on the left of the page
 | |
| and it should appear.
 | |
| A dialog should appear containing one drop-down menu and 5 line-edit fields.
 | |
| This dialog is where you specify what to test and which platforms and Python versions to test on.
 | |
| Its fields are as follows:
 | |
| 
 | |
| 1.  A branch to run from. Set this to the branch which you are using (e.g. ``hook-for-foo``),
 | |
| 2.  Which package(s) to install and their version(s).
 | |
|     Which packages to test are inferred from which packages are installed.
 | |
|     You can generally just copy your own changes to the `requirements-test-libraries.txt` file into this box.
 | |
|     * Set to `foo` to test the latest version of `foo`,
 | |
|     * Set to `foo==1.2, foo==2.3` (note the comma) to test two different versions of `foo` in separate jobs,
 | |
|     * Set to `foo bar` (note the lack of a comma) to test `foo` and `bar` in the same job,
 | |
| 3.  Which OS or OSs to run on
 | |
|     * Set to `ubuntu` to test only `ubuntu`,
 | |
|     * Set to `ubuntu, macos, windows` (order is unimportant) to test all three OSs.
 | |
| 4.  Which Python version(s) to run on
 | |
|     * Set to `3.9` to test only Python 3.9,
 | |
|     * Set to `3.8, 3.9, 3.10, 3.11` to test all currently supported version of Python.
 | |
| 5.  The final two options can generally be left alone.
 | |
| 
 | |
| Hit the green **Run workflow** button at the bottom of the dialog, wait a few seconds then refresh the page.
 | |
| Your workflow run should appear.
 | |
| 
 | |
| We'll eventually want to see a build (or collection of builds) which pass on
 | |
| all OSs and all Python versions.
 | |
| Once you have one, hang onto its URL - you'll need it when you submit the pull request.
 | |
| If you can't get it to work - that's fine.
 | |
| Open a pull request as a draft, show us what you've got and we'll try and help.
 | |
| 
 | |
| 
 | |
| #### Triggering CI/CD from a terminal
 | |
| 
 | |
| If you find repeatedly entering the configuration into Github's **Run workflow** dialog arduous
 | |
| then we also have a CLI script to launch it.
 | |
| Run ``python scripts/cloud-test.py --help`` which should walk you through it.
 | |
| You will have to enter all the details again but, thanks to the wonders of terminal history,
 | |
| rerunning a configuration is just a case of pressing up then enter.
 | |
| 
 | |
| </details>
 | |
| 
 | |
| 
 | |
| ### Run Linter
 | |
| 
 | |
| We use `flake8` to enforce code-style.
 | |
| `pip install flake8` if you haven't already then run it with the following.
 | |
| 
 | |
| ```
 | |
| flake8
 | |
| ```
 | |
| 
 | |
| No news is good news.
 | |
| If it complains about your changes then do what it asks then run it again.
 | |
| If you don't understand the errors it come up with them lookup the error code
 | |
| in each line (a capital letter followed by a number e.g. `W391`).
 | |
| 
 | |
| **Please do not fix flake8 issues found in parts of the repository other than the bit that you are working on.** Not only is it very boring for you, but it is harder for maintainers to
 | |
| review your changes because so many of them are irrelevant to the hook you are adding or changing.
 | |
| 
 | |
| 
 | |
| ### Add a news entry
 | |
| 
 | |
| Please read [news/README.txt](https://github.com/pyinstaller/pyinstaller-hooks-contrib/blob/master/news/README.txt) before submitting you pull request.
 | |
| This will require you to know the pull request number before you make the pull request.
 | |
| You can usually guess it by adding 1 to the number of [the latest issue or pull request](https://github.com/pyinstaller/pyinstaller-hooks-contrib/issues?q=sort%3Acreated-desc).
 | |
| Alternatively, [submit the pull request](#submit-the-pull-request) as a draft,
 | |
| then add, commit and push the news item after you know your pull request number.
 | |
| 
 | |
| 
 | |
| ### Summary
 | |
| 
 | |
| A brief checklist for before submitting your pull request:
 | |
| 
 | |
| * [ ] All new Python files have [the appropriate copyright header](#add-the-copyright-header).
 | |
| * [ ] You have written a [news entry](#add-a-news-entry).
 | |
| * [ ] Your changes [satisfy the linter](#run-linter) (run `flake8`).
 | |
| * [ ] You have written tests (if possible) and [pinned the test requirement](#pin-the-test-requirement).
 | |
| 
 | |
| 
 | |
| ### Submit the pull request
 | |
| 
 | |
| Once you've done all the above, run `git push --set-upstream origin hook-for-foo` then go ahead and create a pull request.
 | |
| If you're stuck doing any of the above steps, create a draft pull request and explain what's wrong - we'll sort you out...
 | |
| Feel free to copy/paste commit messages into the Github pull request title and description.
 | |
| If you've never done a pull request before, note that you can edit it simply by running `git push` again.
 | |
| No need to close the old one and start a new one.
 | |
| 
 | |
| ---
 | |
| 
 | |
| If you plan to contribute frequently or are interested in becoming a developer,
 | |
| send an email to `legorooj@protonmail.com` to let us know.
 |