Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various documentation updates [skip ci] #8214

Merged
merged 3 commits into from Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
443 changes: 443 additions & 0 deletions doc/common-issues-and-pitfalls.rst

Large diffs are not rendered by default.

66 changes: 39 additions & 27 deletions doc/hooks.rst
Expand Up @@ -33,39 +33,50 @@ These helpers are documented below.

The name of a hook file is :file:`hook-{full.import.name}.py`,
where *full.import.name* is
the fully-qualified name of an imported script or module.
the fully-qualified name of an imported module.
For example, ``hook-PyQt5.QtCore.py`` is a hook file corresponding to
the module ``PyQt5.QtCore``. When your script (or one of its dependencies)
contains ``import PyQt5.QtCore`` (or ``from PyQt5 import QtCore``),
Analysis notes that ``hook-PyQt5.QtCore.py`` exists, and will call it.

You can browse through the existing hooks in the
``hooks`` folder of the PyInstaller distribution folder
and see the names of the packages for which hooks have been written.
For example ``hook-PyQt5.QtCore.py`` is a hook file telling
about hidden imports needed by the module ``PyQt5.QtCore``.
When your script contains ``import PyQt5.QtCore``
(or ``from PyQt5 import QtCore``),
Analysis notes that ``hook-PyQt5.QtCore.py`` exists, and will call it.
Additional hooks are provided by the ``pyinstaller-hooks-contrib``
package, which is typically installed as part of PyInstaller dependencies.
See `here <https://github.com/pyinstaller/pyinstaller/tree/develop/PyInstaller/hooks>`__
to browse PyInstaller-provided hooks in the online repository,
and `here <https://github.com/pyinstaller/pyinstaller-hooks-contrib/tree/master/src/_pyinstaller_hooks_contrib/hooks/stdhooks>`__
for hooks provided by the ``pyinstaller-hooks-contrib``.

Many hooks consist of only one statement, an assignment to ``hiddenimports``.
For example, the hook for the `dnspython`_ package, called
``hook-dns.rdata.py``, has only this statement::

hiddenimports = [
"dns.rdtypes.*",
"dns.rdtypes.ANY.*"
]

When Analysis sees ``import dns.rdata`` or ``from dns import rdata``
it calls ``hook-dns.rdata.py`` and examines its value
of ``hiddenimports``.
As a result, it is as if your source script also contained::

import dns.rdtypes.*
import dsn.rdtypes.ANY.*

A hook can also cause the addition of data files,
and it can cause certain files to *not* be imported.
For example, the ``xml.dom`` module from Python standard library imports
a module called ``xml.dom.domreg``, which in turn indirectly imports
``xml.dom.minidom`` as one of registered XML DOM implementations.
Therefore, to ensure that this implementation module
is collected, PyInstaller provides a hook called
``hook-xml.dom.domreg.py``, which contains only the following statement::

hiddenimports = ["xml.dom.minidom"]

When Analysis sees an ``import xml.dom`` statement in the user code (or
one of its dependencies), and subsequently sees that ``xml.dom`` module
imports the ``xml.dom.domreg`` module (via the
``from .domreg import getDOMImplementation, registerDOMImplementation``
statement), it calls ``hook-xml.dom.domreg.py``, and examines the value
of ``hiddenimports`` hook global variable set by the hook.
As a result, the ``xml.dom.minidom`` module is collected into the frozen
application, as if the ``xml.dom.domreg`` module (or your source script)
contained a direct ``import xml.dom.minidom`` statement.

A hook can also cause the collection of data files or binaries (shared
libraries) from a package, collection of metadata for a package, and it
can also prevent collection of packages/modules that are imported only
from the hooked module or a package.
Examples of these actions are shown below.

When the module that needs these hidden imports is useful only to your project,
store the hook file(s) somewhere near your source file.
When the module that needs a hook is useful only to your project,
you can store the hook file(s) somewhere near your source file.
Then specify their location to the ``pyinstaller`` or ``pyi-makespec``
command with the :option:`--additional-hooks-dir` option.
If the hook file(s) are at the same level as the script,
Expand All @@ -76,7 +87,8 @@ the command could be simply::
If you write a hook for a module used by others,
please ask the package developer to
:ref:`include the hook with her/his package <provide hooks with package>`
or send us the hook file so we can make it available.
or send us the hook file so we can include it in `the contributed
hooks repository <https://github.com/pyinstaller/pyinstaller-hooks-contrib>`__.


How a Hook Is Loaded
Expand Down
1 change: 1 addition & 0 deletions doc/index.rst
Expand Up @@ -55,6 +55,7 @@ _________
installation
operating-mode
usage
common-issues-and-pitfalls
runtime-information
spec-files
feature-notes
Expand Down
3 changes: 3 additions & 0 deletions doc/runtime-information.rst
Expand Up @@ -187,6 +187,7 @@ symbolic link::
print( 'os.getcwd is', os.getcwd() )


.. _library path considerations:

LD_LIBRARY_PATH / LIBPATH considerations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -220,6 +221,8 @@ with the system program.
env.pop(lp_key, None)
p = Popen(system_cmd, ..., env=env) # create the process

See also: :ref:`launching external programs`


.. include:: _common_definitions.txt

Expand Down
40 changes: 31 additions & 9 deletions doc/usage.rst
Expand Up @@ -437,6 +437,8 @@ Or you can apply the ``unicode()`` function to the object
to reproduce the version text file.


.. _macOS app bundles:

Building macOS App Bundles
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand All @@ -450,17 +452,37 @@ Either executable can be started from a Terminal command line.
Standard input and output work as normal through that Terminal window.

If you specify :option:`--windowed` with either option, the ``dist`` folder
also contains a macOS application named :file:`myscript.app`.
also contains a macOS app bundle named :file:`myscript.app`.

As you probably know, an application is a special type of folder.
The one built by PyInstaller contains a folder always named
:file:`Contents` which contains:
.. note::
Generating app bundles with onefile executables (i.e., using the
combination of :option:`--onefile` and :option:`--windowed` options),
while possible, is not recommended. Such app bundles are inefficient,
because they require unpacking on each run (and the unpacked content
might be scanned by the OS each time). Furthermore, onefile executables
will not work when signed/notarized with sandbox enabled (which
is a requirement for distribution of apps through Mac App Store).

As you are likely aware, an app bundle is a special type of folder.
The one built by PyInstaller always contains a folder named
:file:`Contents`, which contains:

+ A file named :file:`Info.plist` that describes the app.
+ A folder named :file:`MacOS` that contains the program executable.
+ A folder named :file:`Frameworks` that contains the collected binaries
(shared libraries, python extensions) and nested .framework bundles.
It also contains symbolic links to data files and directories from
the :file:`Resources` directory.
+ A folder named :file:`Resources` that contains the icon file and all
collected data files. It also contains symbolic links to binaries
and directories from the :file:`Resources` directory.

+ A folder :file:`Frameworks` which is empty.
+ A folder :file:`Resources` that contains an icon file.
+ A file :file:`Info.plist` that describes the app.
+ A folder :file:`MacOS` that contains the the executable and
supporting files, just as in the :option:`--onedir` folder.
.. note::
The contents of the :file:`Frameworks` and :file:`Resources` directories
are cross-linked between the two directories in an effort to
maintain an illusion of a single content directory (which is required
by some packages), while also trying to satisfy the Apple's file
placement requirements for codesigning.

Use the :option:`--icon` argument to specify a custom icon for the application.
It will be copied into the :file:`Resources` folder.
Expand Down
3 changes: 3 additions & 0 deletions news/8214.doc.1.rst
@@ -0,0 +1,3 @@
Update the :ref:`macOS app bundles` section to reflect the layout of
macOS app bundles as produced by PyInstaller 6.0 and later. Add a note
to discourage use of onefile .app bundles.
8 changes: 8 additions & 0 deletions news/8214.doc.2.rst
@@ -0,0 +1,8 @@
Add a new documentation chapter, called :ref:`common issues`, to cover
topics such as launching external programs from frozen applications,
multi-processing via :mod:`multiprocessing` (specifically, the requirement
to call :func:`multiprocessing.freeze_support`), use of symbolic links in
POSIX builds in PyInstaller >= 6.0 and its implications for distribution
(e.g., when copying frozen application, or creating ``zip`` archives),
``sys.stdout`` and ``sys.stderr`` being ``None`` in Windows no-console
builds.
2 changes: 2 additions & 0 deletions news/8214.doc.rst
@@ -0,0 +1,2 @@
Update the introduction part of the :ref:`understanding pyinstaller hooks`
section.