Pautas adicionales de Python

Aquí están algunas de las directrices adicionales relacionadas con Python, trasladadas aquí con el fin de conservar la página principal manejable.

Compilación manual de byte

Esta sección solamente se aplica para las directrices de 201x-era. En las directrices nuevas, consulte la sección Manual de byte de compilación.

Al compilar un archivo .py, Python incrusta un número mágico en los archivos compilados correspondientes al entorno de ejecución. Los archivos en %{python?_sitelib} y %{python?_sitearch} DEBEN corresponder al entorno de ejecución para el que fueron compilados. Por ejemplo, un módulo de Python puro compilado para el entorno de ejecución 3.4 DEBE estar en %{_usr}/lib/python3.4/site-packages

El script brp-python-bytecompile intenta resolver esto automáticamente. El script determina qué intérprete usar al compilar el módulo mediante bytes, comprobando el directorio en el que está instalado el archivo. Si es /usr/lib{,64}/pythonX.Y, se usa pythonX.Y para compilar el módulo mediante bytes. Si pythonX.Y no está instalado, se devuelve un error y el proceso de compilación rpm finalizará con un error, así que recuerde BuildRequire para el paquete de Python correcto.

If you have *.py files outside of the /usr/lib(64)?/pythonX.Y/ directories and you require those files to be byte compiled (e.g. it’s an importable Python module) you MUST compile them explicitly using the %py_byte_compile macro. Note that not all Python files are importable Python modules; when in doubt, grep the sources for the appropriate import statement.

An example for a package that has both Python versions:

# Buildrequire both python2 and python3
BuildRequires: python2-devel python3-devel

%install
# Installs a python2 private module into %{buildroot}%{_datadir}/mypackage/foo
# and installs a python3 private module into %{buildroot}%{_datadir}/mypackage/bar
make install DESTDIR=%{buildroot}

# Manually invoke the python byte compile macro for each path that needs byte
# compilation.
%py_byte_compile %{python2} %{buildroot}%{_datadir}/mypackage/foo
%py_byte_compile %{python3} %{buildroot}%{_datadir}/mypackage/bar

The %py_byte_compile macro takes two arguments. The first is the python interpreter to use for byte compiling. The second is a file or directory to byte compile. If the second argument is a directory, the macro will recursively byte compile any *.py file in the directory.

Manual byte compilation for EPEL 6 and 7

The script interpreter defined in %{__python} is used to compile the modules outside of /usr/lib(64)?/pythonX.Y/ directories. This defaults to /usr/bin/python (that’s Python 2.6 or on EPEL 6 and 2.7 on EPEL 7). If you need to compile the modules for python3, set it to /usr/bin/python3 instead:

%global __python %{python3}

Doing this is useful when you have a python3 application that’s installing a private module into its own directory. For instance, if the foobar application installs a module for use only by the command line application in %{_datadir}/foobar. Since these files are not in one of the python3 library paths (i.e., /usr/lib/python3.6) you have to override %{__python} to tell brp-python-bytecompile to use the python3 interpreter for byte compiling.

These settings are enough to properly byte compile any package that builds Python modules in %{python?_sitelib} or %{python?_sitearch} or builds for only a single Python interpreter. However, if the application you’re packaging needs to build with both python2 and python3 and install into a private module directory (perhaps because it provides one utility written in python2 and a second utility written in python3) then you need to do this manually. Here’s a sample spec file snippet that shows what to do:

# Turn off the brp-python-bytecompile script
%global __os_install_post %(echo '%{__os_install_post}' | sed -e 's!/usr/lib[^[:space:]]*/brp-python-bytecompile[[:space:]].*$!!g')
# Buildrequire both python2 and python3
BuildRequires: python2-devel python3-devel
[...]

%install
# Installs a python2 private module into %{buildroot}%{_datadir}/mypackage/foo
# and installs a python3 private module into %{buildroot}%{_datadir}/mypackage/bar
make install DESTDIR=%{buildroot}

# Manually invoke the python byte compile macro for each path that needs byte
# compilation.
%py_byte_compile %{python2} %{buildroot}%{_datadir}/mypackage/foo
%py_byte_compile %{python3} %{buildroot}%{_datadir}/mypackage/bar

Note that this does disable the compilation of files in /usr/lib(64)?/pythonX.Y/.

Byte compilation reproducibility

This subsection only applies to Fedora ⇐ 40, ELN, and EPEL. In later Fedora releases, this is implemented automatically.

For two Python files with the exact same content and metadata, byte compilation might produce different results. The resulting .pyc files are functionally identical but are not bit-by-bit identical. In most cases, internal Python reference counter is here to blame because it might have a different internal state during each byte compilation. If you want a deeper explanation, take a look at this Bugzilla comment.

This inconvenience might cause a problem in Koji where noarch packages built as a part of an arch build might be rejected because they have different content.

To work around this issue, BuildRequire marshalparser BuildRequires: /usr/bin/marshalparser (a tool that makes .pyc files more reproducible) and instruct it to process the .pyc files in certain paths by setting the %py_reproducible_pyc_path macro:

%global py_reproducible_pyc_path %{buildroot}%{_datadir}/llamafarm/plugins

With that setting, marshalparser recursively finds all byte-compiled Python files in %{buildroot}%{_datadir}/llamafarm/plugins and attempts to fix them. This happens at the very end of the build process when all previous byte compilation steps are finished. If marshalparser cannot parse some of the cache files, the build fails.