No more automagic Python bytecompilation
Summary
The current way of automatic Python byte-compiling of files outside Python-specific directories is too magical and error-prone. It is built on heuristics that are increasingly wrong. We will provide a way to opt-out of it and adjust the guidelines to prefer explicit bytecompilation of such files. Later, the old behavior will be opt-in only or will cease to exist.
Note that bytecompilation in Python-specific directories (e.g. /usr/lib/python3.6/
) is not affected.
Owner
- Name: Miro Hrončok
- Name: Petr Viktorin
- Email: mhroncok@redhat.com, pviktori@redhat.com
- Release notes owner:
Current status
- Targeted release: Fedora 29
- Last updated: 2018-05-28
- Tracker bug: #1551326
- Release Notes tracking: #125
Detailed Description
Background
When a Python modules is imported, the source file (*.py
) is automatically compiled to bytecode.
The bytecode is automatically cached in “pyc files” next to the source (e.g. moldulename.pyc
and moldulename.pyo
in Python 2.7; __pycache__/moldulename.cpython-36*.pyc
in Python 3.6).
For RPM packages installed system-wide, creating cache files would generally require root priviliges. So they are included in RPMs rather than generated on import.
Status quo
For packagers' convenience, rpmbuild's brp-python-bytecompile
script generates .pyc
/.pyo
cache files automatically:
- In interpreter-specific directories, such as
/usr/lib/python3.6/
, these are compiled using the appropriate interpreter. - Some directories, such as
/bin
,/sbin
and/usr/share/doc
, are excluded. - Outside these directories, modules are compiled using
%{__python}
, which is/usr/bin/python
, which is currently Python 2. This is only done if the%{__python}
binary is available.
The first two points are good, straightforward, and the automatism is limited to Python-specific directories. The last point, however, relies on several assumptions which are not always right:
- All files named
*.py
are Python modules that need to be bytecompiled. (Quite an accurate heuristic, but has very bad behavior on false positives: it can affect packages that don't have anything to do with Python.) - Any package that has
.py
files BuildRequires/usr/bin/python
. (If it does not, the package will build differently depending on whether/usr/bin/python
happens to be available during rpmbuild.) - When a Python module is not in
/usr/lib(64)?/pythonX.Y/
it is intended for the%{__python}
interpreter – currently Python 2. (But the module could also be, for example, for Python 3, or PyPy, or several of those.)
Bytecompilation outside Python-specific directories may be changed by redefining %__python
to:
- python3: This is documented in the guidelines as a way to enable the automatic bytecompilation for python3. Around 70 packages use it.
- python2: This *should* be done to ensure consistency when
%{__python}
is switched. Only about 2 packages does it, because the magic "just works" for the time being.
Automatic bytecompilation oudside Python-specific directories cannot be disabled without disabling all bytecompilation. Also, it can not be done for more than one interpreter.
See Packaging:Python Appendix for more information (this links to a specific revision so the link makes sense once this change is implemented and the guidelines are changed).
The current behavior is surprising. Mistakes are made. Things are done or not done based on the presence of /usr/bin/python
.
Here is a simple example of a package that builds fine without /usr/bin/python
in the buildroot but fails when it's there.
The same issue was reported on fedora-devel for a "real" package.
Name: reproducer Version: 0.1 Release: 1%{?dist} Summary: Reproducer for a bytecompile script issue License: MIT BuildArch: noarch %description This package will build fine if /usr/bin/python is *not* in the buildroot. %prep echo "Poland" > country-name.pl echo "Paraguay" > country-name.py echo "Saint Helena" > country-name.sh echo "Serbia" > country-name.rs %build %install mkdir -p %{buildroot}%{_datadir}/%{name} cp country-name.* %{buildroot}%{_datadir}/%{name} %files %dir %{_datadir}/%{name}/ %{_datadir}/%{name}/country-name.??
How we are changing it
For the time being, we keep the old behavior working, so the hundreds of packages that implicitly rely on it do not break all at once. However, we will not (automatically) apply it to Python 3, so it will be phased out as packages switch to Python 3.
A opt-out mechanism for automatic compilation of files outside of /usr/lib(64)?/pythonX.Y/
will be provided (setting %_python_bytecompile_extra
to 0 or undefining it).
Speaking code, this will disable the final part of the brp-python-bytecompile script.
Guidelines will be adjusted to say the following:
- if you have
*.py
files outside of the/usr/lib(64)?/pythonX.Y/
, you MUST disable their automatic compilation, and compile them explicitly by the%py_byte_compile
macro if and only if the bytecompilation is desired (i.e. it's an importable Python module).
Example for package that has both Python versions:
# Turn off the brp-python-bytecompile automagic %global _python_bytecompile_extra 0 # 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 unlike the current example in the guidelines linked above, this does not disable the compilation of files in /usr/lib(64)?/pythonX.Y/
.
Example for Python 3 only:
# Turn off the brp-python-bytecompile automagic %global _python_bytecompile_extra 0 BuildRequires: python3-devel %install # 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 %{__python3} %{buildroot}%{_datadir}/mypackage/bar
The Python 2 only example is analogical.
The notion of redefining %{__python}
will be removed from the guidelines.
Currently, %py_byte_compile
lives in python3-devel
. We'll move it to some generic package that all Python devel packages require (such as python-rpm-macros
) (this has been done already).
Analogically, packagers can %global _python_bytecompile_extra 1
to explicitly say they rely on the current behavior.
Later (i.e. not in Fedora 29, but approximately when %{__python}
stops being python2), we'll make the old behavior opt-in (or disable it entirely if no package uses %_python_bytecompile_extra
).
All brp-python-bytecompile
changes will be shared and discussed in upstream RPM.
We will provide pull requests for the ~50 packages that redefine __python
to %{__python3}
(the currently recommended way to enable automatic byte-compilation on Python 3).
Benefit to Fedora
More explicit specfiles when it comes to Python byte compilation. This will ease the change once we decide /usr/bin/python
is no longer python2.
The new guidelines will be less error prone.
Note that we'd prefer to switch to the new behavior right now, but we keep it opt-in to not break the ~500 packages that use it.
Scope
- Proposal owners: Make it work technically, propose the new guidelines, file pull requests for python3 modules that follow the current guidelines.
- Other developers: Maintainers of python3 packages that redefine
%__python
should merge provided pull requests. Others may opt-in for the new behavior or explicitly stick with the old one (not a System Wide Change, they don't have to do anything).
- Release engineering: #7315 (a check of an impact with Release Engineering is needed)
- Policies and guidelines: will be changed as described in description
- Trademark approval: not needed
Upgrade/compatibility impact
None expected.
How To Test
More specific instructions based on the examples in description will be here once ready. In the meantime, feel free to test the examples in the description as you see fit.
User Experience
The users of this change are packagers. The new behavior should make byte-compilation more obvious, explicit, and discoverable. Users of Fedora should not feel this (except if this change uncovers a packaging bug).
Contingency Plan
- Contingency mechanism: we'll finish the change later (not a System Wide Change)
- Contingency deadline: none (not a System Wide Change)
- Blocks release? no (not a System Wide Change)
- Blocks product? no
Documentation
The guidelines will be the documentation.
Release Notes
This change does not deserve Release Notes, it is not user facing.