METADATA 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. Metadata-Version: 2.1
  2. Name: pyinstaller-hooks-contrib
  3. Version: 2022.0
  4. Summary: Community maintained hooks for PyInstaller
  5. Home-page: https://github.com/pyinstaller/pyinstaller-hooks-contrib
  6. Maintainer: Legorooj
  7. Maintainer-email: legorooj@protonmail.com
  8. License: UNKNOWN
  9. Download-URL: https://pypi.org/project/pyinstaller-hooks-contrib
  10. Keywords: pyinstaller development hooks
  11. Platform: UNKNOWN
  12. Classifier: Intended Audience :: Developers
  13. Classifier: Topic :: Software Development :: Build Tools
  14. Classifier: Natural Language :: English
  15. Classifier: Operating System :: OS Independent
  16. Classifier: Programming Language :: Python
  17. Classifier: Programming Language :: Python :: 3
  18. Description-Content-Type: text/markdown
  19. License-File: LICENSE
  20. License-File: LICENSE.APL.txt
  21. License-File: LICENSE.GPL.txt
  22. # `pyinstaller-hooks-contrib`: The PyInstaller community hooks repository
  23. What happens when (your?) package doesn't work with PyInstaller? Say you have data files that you need at runtime?
  24. PyInstaller doesn't bundle those. Your package requires others which PyInstaller can't see? How do you fix that?
  25. In summary, a "hook" file extends PyInstaller to adapt it to the special needs and methods used by a Python package.
  26. The word "hook" is used for two kinds of files. A runtime hook helps the bootloader to launch an app, setting up the
  27. environment. A package hook (there are several types of those) tells PyInstaller what to include in the final app -
  28. such as the data files and (hidden) imports mentioned above.
  29. This repository is a collection of hooks for many packages, and allows PyInstaller to work with these packages
  30. seamlessly.
  31. ## Installation
  32. `pyinstaller-hooks-contrib` is automatically installed when you install PyInstaller, or can be installed with pip:
  33. ```commandline
  34. pip install -U pyinstaller-hooks-contrib
  35. ```
  36. ## I can't see a hook for `a-package`
  37. Either `a-package` works fine without a hook, or no-one has contributed hooks.
  38. If you'd like to add a hook, or view information about hooks,
  39. please see below.
  40. ## I want to help!
  41. If you've got a hook you want to share then great!
  42. The rest of this page will walk you through the process of contributing a hook.
  43. If you've been here before then you may want to skip to the [summary checklist](#summary)
  44. **Unless you are very comfortable with `git rebase -i`, please provide one hook per pull request!**
  45. **If you have more than one then submit them in separate pull requests.**
  46. ### Setup
  47. [Fork this repo](https://github.com/pyinstaller/pyinstaller-hooks-contrib/fork) if you haven't already done so.
  48. (If you have a fork already but its old, click the **Fetch upstream** button on your fork's homepage.)
  49. Clone and `cd` inside your fork by running the following (replacing `bob-the-barnacle` with your github username):
  50. ```
  51. git clone https://github.com/bob-the-barnacle/pyinstaller-hoooks-contrib.git
  52. cd pyinstaller-hooks-contrib
  53. ```
  54. Create a new branch for you changes (replacing `foo` with the name of the package):
  55. You can name this branch whatever you like.
  56. ```
  57. git checkout -b hook-for-foo
  58. ```
  59. If you wish to create a virtual environment then do it now before proceeding to the next step.
  60. Install this repo in editable mode.
  61. This will overwrite your current installation.
  62. (Note that you can reverse this with `pip install --force-reinstall pyinstaller-hooks-contrib`).
  63. ```
  64. pip install -e .
  65. pip install -r requirements-test.txt
  66. pip install flake8
  67. ```
  68. Note that on macOS and Linux, `pip` may by called `pip3`.
  69. If you normally use `pip3` and `python3` then use `pip3` here too.
  70. You may skip the 2<sup>nd</sup> line if you have no intention of providing tests (but please do provide tests!).
  71. ### Add the hook
  72. Standard hooks live in the [src/_pyinstaller_hooks_contrib/hooks/stdhooks/](../master/src/_pyinstaller_hooks_contrib/hooks/stdhooks/) directory.
  73. Runtime hooks live in the [src/_pyinstaller_hooks_contrib/hooks/rthooks/](../master/src/_pyinstaller_hooks_contrib/hooks/rthooks/) directory.
  74. Simply copy your hook into there.
  75. If you're unsure if your hook is a runtime hook then it almost certainly is a standard hook.
  76. Please annotate (with comments) anything unusual in the hook.
  77. *Unusual* here is defined as any of the following:
  78. * Long lists of `hiddenimport` submodules.
  79. 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).
  80. For bonus points, track down why so many submodules are hidden. Typical causes are:
  81. * Lazily loaded submodules (`importlib.importmodule()` inside a module `__getattr__()`).
  82. * Dynamically loaded *backends*.
  83. * Usage of `Cython` or Python extension modules containing `import` statements.
  84. * Use of [`collect_all()`](https://pyinstaller.readthedocs.io/en/latest/hooks.html#PyInstaller.utils.hooks.collect_all).
  85. This function's performance is abismal and [it is broken by
  86. design](https://github.com/pyinstaller/pyinstaller/issues/6458#issuecomment-1000481631) because it confuses
  87. packages with distributions.
  88. Check that you really do need to collect all of submodules, data files, binaries, metadata and dependencies.
  89. If you do then add a comment to say so (and if you know it - why).
  90. Do not simply use `collect_all()` just to *future proof* the hook.
  91. * Any complicated `os.path` arithmetic (by which I simply mean overly complex filename manipulations).
  92. #### Add the copyright header
  93. All source files must contain the copyright header to be covered by our terms and conditions.
  94. If you are **adding** a new hook (or any new python file), copy/paste the appropriate copyright header (below) at the top
  95. replacing 2021 with the current year.
  96. <details><summary>GPL 2 header for standard hooks or other Python files.</summary>
  97. ```python
  98. # ------------------------------------------------------------------
  99. # Copyright (c) 2021 PyInstaller Development Team.
  100. #
  101. # This file is distributed under the terms of the GNU General Public
  102. # License (version 2.0 or later).
  103. #
  104. # The full license is available in LICENSE.GPL.txt, distributed with
  105. # this software.
  106. #
  107. # SPDX-License-Identifier: GPL-2.0-or-later
  108. # ------------------------------------------------------------------
  109. ```
  110. </details>
  111. <details><summary>APL header for runtime hooks only.
  112. Again, if you're unsure if your hook is a runtime hook then it'll be a standard hook.</summary>
  113. ```python
  114. # ------------------------------------------------------------------
  115. # Copyright (c) 2021 PyInstaller Development Team.
  116. #
  117. # This file is distributed under the terms of the Apache License 2.0
  118. #
  119. # The full license is available in LICENSE.APL.txt, distributed with
  120. # this software.
  121. #
  122. # SPDX-License-Identifier: Apache-2.0
  123. # ------------------------------------------------------------------
  124. ```
  125. </details>
  126. If you are **updating** a hook, skip this step.
  127. Do not update the year of the copyright header - even if it's out of date.
  128. ### Test
  129. Having tests is key to our continuous integration.
  130. With them we can automatically verify that your hook works on all platforms, all Python versions and new versions of
  131. libraries as and when they are released.
  132. Without them, we have no idea if the hook is broken until someone finds out the hard way.
  133. Please write tests!!!
  134. Some user interface libraries may be impossible to test without user interaction
  135. or a wrapper library for some web API may require credentials (and possibly a paid subscription) to test.
  136. In such cases, don't provide a test.
  137. Instead explain either in the commit message or when you open your pull request why an automatic test is impractical
  138. then skip on to [the next step](#run-linter).
  139. #### Write tests(s)
  140. A test should be the least amount of code required to cause a breakage
  141. if you do not have the hook which you are contributing.
  142. For example if you are writing a hook for a library called `foo`
  143. which crashes immediately under PyInstaller on `import foo` then `import foo` is your test.
  144. If `import foo` works even without the hook then you will have to get a bit more creative.
  145. Good sources of such minimal tests are introductory examples
  146. from the documentation of whichever library you're writing a hook for.
  147. Package's internal data files and hidden dependencies are prone to moving around so
  148. tests should not explicitly check for presence of data files or hidden modules directly -
  149. rather they should use parts of the library which are expected to use said data files or hidden modules.
  150. Tests currently all live in [src/_pyinstaller_hooks_contrib/tests/test_libraries.py](../master/src/_pyinstaller_hooks_contrib/tests/test_libraries.py).
  151. Navigate there and add something like the following, replacing all occurrences of `foo` with the real name of the library.
  152. (Note where you put it in that file doesn't matter.)
  153. ```python
  154. @importorskip('foo')
  155. def test_foo(pyi_builder):
  156. pyi_builder.test_source("""
  157. # Your test here!
  158. import foo
  159. foo.something_fooey()
  160. """)
  161. ```
  162. If the library has changed significantly over past versions then you may need to add version constraints to the test.
  163. To do that, replace the `@importorskip("foo")` with a call to `PyInstaller.utils.tests.requires()` (e.g.
  164. `@requires("foo >= 1.4")`) to only run the test if the given version constraint is satisfied.
  165. Note that `@importorskip` uses module names (something you'd `import`) whereas `@requires` uses distribution names
  166. (something you'd `pip install`) so you'd use `@importorskip("PIL")` but `@requires("pillow")`.
  167. For most packages, the distribution and packages names are the same.
  168. #### Run the test locally
  169. Running our full test suite is not recommended as it will spend a very long time testing code which you have not touched.
  170. Instead, run tests individually using either the `-k` option to search for test names:
  171. ```
  172. pytest -k test_foo
  173. ```
  174. Or using full paths:
  175. ```
  176. pytest src/_pyinstaller_hooks_contrib/tests/test_libraries.py::test_foo
  177. ```
  178. #### Pin the test requirement
  179. Get the version of the package you are working with (`pip show foo`)
  180. and add it to the [requirements-test-libraries.txt](../master/requirements-test-libraries.txt) file.
  181. The requirements already in there should guide you on the syntax.
  182. #### Run the test on CI/CD
  183. To test hooks on all platforms we use Github's continuous integration (CI/CD).
  184. Our CI/CD is a bit unusual in that it's triggered manually and takes arguments
  185. which limit which tests are run.
  186. This is for the same reason we filter tests when running locally -
  187. the full test suite takes ages.
  188. First push the changes you've made so far.
  189. ```commandline
  190. git push --set-upstream origin hook-for-foo
  191. ```
  192. Replace *billy-the-buffalo* with your Github username in the following url then open it.
  193. It should take you to the `oneshot-test` actions workflow on your fork.
  194. You may be asked if you want to enable actions on your fork - say yes.
  195. ```
  196. https://github.com/billy-the-buffalo/pyinstaller-hooks-contrib/actions/workflows/oneshot-test.yml
  197. ```
  198. Find the **Run workflow** button and click on it.
  199. If you can't see the button,
  200. select the **Oneshot test** tab from the list of workflows on the left of the page
  201. and it should appear.
  202. A dialog should appear containing one drop-down menu and 5 line-edit fields.
  203. This dialog is where you specify what to test and which platforms and Python versions to test on.
  204. Its fields are as follows:
  205. 1. A branch to run from. Set this to the branch which you are using (e.g. ``hook-for-foo``),
  206. 2. Which package(s) to install and their version(s).
  207. Which packages to test are inferred from which packages are installed.
  208. You can generally just copy your own changes to the `requirements-test-libraries.txt` file into this box.
  209. * Set to `foo` to test the latest version of `foo`,
  210. * Set to `foo==1.2, foo==2.3` (note the comma) to test two different versions of `foo` in separate jobs,
  211. * Set to `foo bar` (note the lack of a comma) to test `foo` and `bar` in the same job,
  212. 3. Which OS or OSs to run on
  213. * Set to `ubuntu` to test only `ubuntu`,
  214. * Set to `ubuntu, macos, windows` (order is unimportant) to test all three OSs.
  215. 4. Which Python version(s) to run on
  216. * Set to `3.9` to test only Python 3.9,
  217. * Set to `3.6, 3.7, 3.8, 3.9` to test all currently supported version of Python.
  218. 5. The final two options can generally be left alone.
  219. Hit the green **Run workflow** button at the bottom of the dialog, wait a few seconds then refresh the page.
  220. Your workflow run should appear.
  221. We'll eventually want to see a build (or collection of builds) which pass on
  222. all OSs and all Python versions.
  223. Once you have one, hang onto its URL - you'll need it when you submit the pull request.
  224. If you can't get it to work - that's fine.
  225. Open a pull request as a draft, show us what you've got and we'll try and help.
  226. #### Triggering CI/CD from a terminal
  227. If you find repeatedly entering the configuration into Github's **Run workflow** dialog arduous
  228. then we also have a CLI script to launch it.
  229. Run ``python scripts/cloud-test.py --help`` which should walk you through it.
  230. You will have to enter all the details again but, thanks to the wonders of terminal history,
  231. rerunning a configuration is just a case of pressing up then enter.
  232. ### Run Linter
  233. We use `flake8` to enforce code-style.
  234. `pip install flake8` if you haven't already then run it with the following.
  235. Note that this assumes that you did create a new branch in the [setup step](#setup).
  236. ```
  237. git diff -U0 master | flake8 --diff -
  238. ```
  239. No news is good news.
  240. If it complains about your changes then do what it asks then run it again.
  241. If you don't understand the errors it come up with them lookup the error code
  242. in each line (a capital letter followed by a number e.g. `W391`).
  243. If it complains about code which you haven't written,
  244. or if you didn't create a new branch at the start then, using `git log`,
  245. find the commit ID of the newest commit which you didn't write, copy it
  246. and replace `master` in the above command with that ID.
  247. ```
  248. git diff -U0 a5d3841c282fa23fd68c3d6a85519e73c08acb4a | flake8 --diff -
  249. ```
  250. **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
  251. review your changes because so many of them are irrelevant to the hook you are adding or changing.
  252. ### Add a news entry
  253. Please read [news/README.txt](https://github.com/pyinstaller/pyinstaller-hooks-contrib/blob/master/news/README.txt) before submitting you pull request.
  254. This will require you to know the pull request number before you make the pull request.
  255. 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).
  256. Alternatively, [submit the pull request](#submit-the-pull-request) as a draft,
  257. then add, commit and push the news item after you know your pull request number.
  258. ### Summary
  259. A brief checklist for before submitting your pull request:
  260. * [ ] All new Python files have [the appropriate copyright header](#add-the-copyright-header).
  261. * [ ] You have written a [news entry](#add-a-news-entry).
  262. * [ ] Your changes [satisfy the linter](#run-linter) (run `git diff -U0 master | flake8 --diff -`).
  263. * [ ] You have written tests (if possible), [pinned the test requirement](#pin-the-test-requirement) and linked to a successful CI build.
  264. ### Submit the pull request
  265. Once you've done all the above, go ahead and create a pull request.
  266. 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...
  267. Feel free to copy/paste commit messages into the Github pull request title and description.
  268. If you have run CI/CD, please include a link to it in your description so that we can see that it works.
  269. If you've never done a pull request before, note that you can edit it simply by running `git push` again.
  270. No need to close the old one and start a new one.
  271. ---
  272. If you plan to contribute frequently or are interested in becoming a developer,
  273. send an email to `legorooj@protonmail.com` to let us know.