From Fedora Project Wiki

m (Added the new spec)
No edit summary
Line 1: Line 1:
== Multiple Python Runtimes ==
== Multiple Python Runtimes ==


In Fedora we have multiple python runtimes, one for each supported major release.  At this point that's one for python2.x and one for python3.x
In Fedora we have multiple python runtimes, one for each supported major release.  At this point that's one for Python 2.7 and one for Python 3.4


Each runtime corresponds to a binary of the form <code>/usr/bin/python$MAJOR.$MINOR</code>
Each runtime corresponds to a binary of the form <code>/usr/bin/python$MAJOR.$MINOR</code>
Line 13: Line 13:
python modules using these runtimes should have a corresponding "Requires" line on the python runtime that they are used with.  This is done automatically for files below <code>/usr/lib[^/]*/python${PYVER}</code>
python modules using these runtimes should have a corresponding "Requires" line on the python runtime that they are used with.  This is done automatically for files below <code>/usr/lib[^/]*/python${PYVER}</code>


{{admon/warning|Test your work| Remember to test the built RPMs and verify that they actually work!  For instance, when you're packaging a python module that builds for both python2 and python3, don't test the python3 module but ship the python2 module without testing that it does what it's supposed to. If you are requesting that an application '''switch''' from Python 2 to Python 3 for its Python implementation, please provide supporting material (e.g. a list of tests performed, and their outcome).  Simply getting a package to build against Python 3 is no guarantee that the package's functionality still works.}}
{{admon/warning|Test your work| Remember to test the built RPMs and verify that they actually work!  For instance, when you're packaging a python module that builds for both Python 2 and Python 3, don't test the Python 3 module but ship the Python 2 module without testing that it does what it's supposed to.


{{admon/note|For packagers of the python interpreter|Unlike the Requires lines, the "Provides" for each runtime are manually entered into the specfile for each runtime.  In theory <code>/usr/lib/rpm/pythondeps.sh</code> would also automatically generate "Provides" lines for the runtime, but in practice rpmbuild only invokes it for files in the rpm payload identified as "python" by the <code>file</code> utility, and the runtime is an ELF binary, not a python script, hence it isn't passed.  It's simplest to manually supply the Provides line, rather than change these innards of rpmbuild.  See [[rhbug:532118|Red Hat Bug 532118]].}}
{{admon/note|For packagers of the python interpreter|Unlike the Requires lines, the "Provides" for each runtime are manually entered into the specfile for each runtime.  In theory <code>/usr/lib/rpm/pythondeps.sh</code> would also automatically generate "Provides" lines for the runtime, but in practice rpmbuild only invokes it for files in the rpm payload identified as "python" by the <code>file</code> utility, and the runtime is an ELF binary, not a python script, hence it isn't passed.  It's simplest to manually supply the Provides line, rather than change these innards of rpmbuild.  See [[rhbug:532118|Red Hat Bug 532118]].}}


== BuildRequires ==
== BuildRequires ==
To build a package containing python2 files, you need to have
To build a package containing Python 2 files, you need to have
<pre>
<pre>
BuildRequires: python2-devel
BuildRequires: python2-devel
</pre>
</pre>


Similarly, when building a package which ships python3 files, you need
Similarly, when building a package which ships Python 3 files, you need
<pre>
<pre>
BuildRequires: python3-devel
BuildRequires: python3-devel
</pre>
</pre>


A package that has both python2 and python3 files will need to BuildRequire both.
A package that has both Python 2 and Python 3 files will need to BuildRequire both.


== Macros ==
== Macros ==
In RHEL 6 and older, python2 packages that install python modules need to define <code>__python2</code>, <code>python2_sitelib</code>, and <code>python2_sitearch</code> macros.  This is not needed in current Fedora or with python3 modules as the macros are defined by <code>rpm</code> and the <code>python3-devel</code> package.  To define those conditionally you can use this:
<pre>
%if 0%{?rhel} && 0%{?rhel} <= 6
%{!?__python2: %global __python2 /usr/bin/python2}
%{!?python2_sitelib: %global python2_sitelib %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")}
%{!?python2_sitearch: %global python2_sitearch %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")}
%endif
</pre>


Note that the use of <code>%{!? [...]}</code> does allow this to work without the check for rhel versions but putting the conditional in documents when we can remove the entire stanza from the spec file.
Note that the use of <code>%{!? [...]}</code> does allow this to work without the check for rhel versions but putting the conditional in documents when we can remove the entire stanza from the spec file.
Line 151: Line 142:
<li>what directory is the module installed in?  If it's <code>/usr/lib{,64}/pythonX.Y</code>, then <code>pythonX.Y</code> is used to byte compile the module.  If <code>pythonX.Y</code> is not installed, then an error is returned and the rpm build process will exit on an error so remember to <code>BuildRequire</code> the proper python package.</li>
<li>what directory is the module installed in?  If it's <code>/usr/lib{,64}/pythonX.Y</code>, then <code>pythonX.Y</code> is used to byte compile the module.  If <code>pythonX.Y</code> is not installed, then an error is returned and the rpm build process will exit on an error so remember to <code>BuildRequire</code> the proper python package.</li>


<li>the script interpreter defined in <code>%{__python}</code> is used to compile the modules.  This defaults to the latest python3 version on Fedora.  If you need to compile this module for python2, set it to <code>/usr/bin/python2</code> instead:
<li>the script interpreter defined in <code>%{__python}</code> is used to compile the modules.  This defaults to the latest Python 3 version on Fedora.  If you need to compile this module for Python 2, set it to <code>/usr/bin/python2</code> instead:


<pre>
<pre>
Line 157: Line 148:
</pre>
</pre>


Doing this is useful when you have a python2 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 <code>%{_datadir}/foobar</code>.  Since these files are not in one of the python2 library paths (ie. <code>/usr/lib/python2.7</code>) you have to override <code>%{__python}</code> to tell <code>brp-python-bytecompile</code> to use the python2 interpreter for byte compiling.
Doing this is useful when you have a Python 2 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 <code>%{_datadir}/foobar</code>.  Since these files are not in one of the Python 2 library paths (ie. <code>/usr/lib/python2.7</code>) you have to override <code>%{__python}</code> to tell <code>brp-python-bytecompile</code> to use the Python 2 interpreter for byte compiling.
</li>
</li>
</ol>
</ol>


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


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


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


Line 181: Line 172:
</pre>
</pre>


The <code>%py_byte_compile</code> 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.
The <code>%py_byte_compile</code> 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.


{{admon/warning|No %{} for py_byte_compile|RPM macros can only take arguments when they do not have curly braces around them.  Therefore, py_byte_compile won't work correctly if you write: <code>%{py_byte_compile} %{__python3}</code>}}
{{admon/warning|No %{} for py_byte_compile|RPM macros can only take arguments when they do not have curly braces around them.  Therefore, py_byte_compile won't work correctly if you write: <code>%{py_byte_compile} %{__python3}</code>}}
Line 189: Line 180:
== Common SRPM vs split SRPMs ==
== Common SRPM vs split SRPMs ==


Many times when you package a python module you will want to create a module for python2 and a module for python3.  There are two ways of doing this: either from a single SRPM or from multiple.  The rule to choose which method is simple: if the python2 and python3 modules are distributed as a single tarball (many times as a single directory of source where the <code>/usr/bin/2to3</code> program is used to transform the code at buildtime) then you must package them as subpackages built from a single SRPM.  If they come in multiple tarballs then package them from multiple SRPMs.
Many times when you package a Python module you will want to create a module for Python 2 and a module for Python 3.  There are two ways of doing this: either from a single SRPM or from multiple.  The rule to choose which method is simple: if the Python 2 and Python 3 modules are distributed as a single tarball (many times as a single directory of source where the <code>/usr/bin/2to3</code> program is used to transform the code at buildtime) then you must package them as subpackages built from a single SRPM.  If they come in multiple tarballs then package them from multiple SRPMs.


{{admon/note|Python Bindings|python bindings are sometimes built as part of the C library's build.  The ideal for these is to patch the code so it will build against both python2 and python3.  Then take a copy of the sources during the <code>%prep</code> phase, and configure one subdirectory to build against python 2, another to build against python 3.  These changes should be upstreamed.  Example: the build of <code>rpm</code> itself emits an <code>rpm-python</code> subpackage (see [[rhbug:531543|Red Hat Bug 531543]].)}}
{{admon/note|Python Bindings|python bindings are sometimes built as part of the C library's build.  The ideal for these is to patch the code so it will build against both Python 2 and Python 3.  Then take a copy of the sources during the <code>%prep</code> phase, and configure one subdirectory to build against Python 2, another to build against Python 3.  These changes should be upstreamed.  Example: the build of <code>rpm</code> itself emits an <code>rpm-python</code> subpackage (see [[rhbug:531543|Red Hat Bug 531543]].)}}


=== Multiple SRPMS ===
=== Multiple SRPMS ===


When upstream ships multiple tarballs with one tarball containing python2 code and a different tarball containing python3 code, we should ship those as multiple SRPMs.  The two SRPMs could have different maintainers within Fedora and the two packages need not upgrade at the same time.  Building from multiple SRPMs has some advantages and disadvantages:
When upstream ships multiple tarballs with one tarball containing Python 2 code and a different tarball containing Python 3 code, we should ship those as multiple SRPMs.  The two SRPMs could have different maintainers within Fedora and the two packages need not upgrade at the same time.  Building from multiple SRPMs has some advantages and disadvantages:


'''Advantages''':
'''Advantages''':
* There can be separate maintainers for python2 and python3 so each maintainer can concentrate on one stack.
* There can be separate maintainers for Python 2 and Python 3 so each maintainer can concentrate on one stack.
* The two packages can evolve separately; if 2 and 3 need to have different versions, they can.
* The two packages can evolve separately; if 2 and 3 need to have different versions, they can.


Line 207: Line 198:
The following practices are designed to help mitigate the disadvantages listed above:
The following practices are designed to help mitigate the disadvantages listed above:


* When packaging a module for python3 contact the maintainers for the python2 module (or vice versa) and try to coordinate with them.
* When packaging a module for Python 3 contact the maintainers for the Python 2 module (or vice versa) and try to coordinate with them.
* Request at least watchbugzilla and watchcommit acls on each other's packages so you're aware of outstanding bugs.
* Request at least watchbugzilla and watchcommit acls on each other's packages so you're aware of outstanding bugs.
* Complete any python 2 Merge Review when doing the python 3 version.  Doing this gets issues that apply to both packages addressed at the same time.
* Complete any Python 2 Merge Review when doing the Python 3 version.  Doing this gets issues that apply to both packages addressed at the same time.
* Add a link to the python 2 Merge Review/Package Review to the python 3 Package Review
* Add a link to the Python 2 Merge Review/Package Review to the Python 3 Package Review


=== Subpackages ===
=== Subpackages ===


{{admon/warning|Do not build python3 modules without upstream support|If upstream is shipping a module for python2 and does not support making that module run on python3, do not package a python3 version of it in Fedora.  If running <code>2to3</code> or adding a patch enables the code to work, you can certainly tell upstream that it works to encourage them to support python3.  However, doing this on our own in Fedora is essentially creating a fork.  That has a large burden for maintaining the code, fixing bugs, porting when a new version of upstream's code appears, managing a release schedule, and other tasks normally handled by upstream.  It's much better if we can cooperate with upstream to share this work than doing it all on our own.}}
{{admon/warning|Do not build Python 3 modules without upstream support|If upstream is shipping a module for Python 2 and does not support making that module run on Python 3, do not package a Python 3 version of it in Fedora (or vice versa).  If running <code>2to3</code> or adding a patch enables the code to work, you can certainly tell upstream that it works to encourage them to support Python 3.  However, doing this on our own in Fedora is essentially creating a fork.  That has a large burden for maintaining the code, fixing bugs, porting when a new version of upstream's code appears, managing a release schedule, and other tasks normally handled by upstream.  It's much better if we can cooperate with upstream to share this work than doing it all on our own.}}


Sometimes upstream will ship one tarball that builds both a python2 and a python3 module.  There's several ways that upstream can structure this.  When upstream writes their build scripts to build both python2 and python3 modules in a single build this is just like building subpackages for any other package.  You expand the tarball and patch the source in <code>%prep</code>, run upstream's build scripts to build the package in <code>%build</code>, and then run upstream's build scripts to install it in <code>%install</code>.
Sometimes upstream will ship one tarball that builds both a Python 2 and a Python 3 module.  There's several ways that upstream can structure this.  When upstream writes their build scripts to build both Python 2 and Python 3 modules in a single build this is just like building subpackages for any other package.  You expand the tarball and patch the source in <code>%prep</code>, run upstream's build scripts to build the package in <code>%build</code>, and then run upstream's build scripts to install it in <code>%install</code>.


'''Advantages''':
'''Advantages''':
* Single src.rpm to review and build
* Single source rpm to review and build
* Avoids having to update multiple packages when things change.
* Avoids having to update multiple packages when things change.


'''Disadvantages''':
'''Disadvantages''':
* The Fedora maintainer needs to care about both python 2 and python 3 modules which makes more work to maintain that package.
* The Fedora maintainer needs to care about both Python 2 and Python 3 modules which makes more work to maintain that package.
* The 2 and 3 versions are in lockstep.  Bugfixes need to apply to python2 while not breaking the translation into python3.
* The 2 and 3 versions are in lockstep.  Bugfixes need to apply to Python 2 while not breaking the translation into Python 3 and vice versa.
* Bugzilla components are set up according to source RPM, so they will have a single shared bugzilla component.  This could be confusing to end-users, as it would be more difficult to figure out e.g. that a bug with python3-foo needs to be filed against python-foo.  There's a similar problem with checking out package sources from CVS, though this is less serious as it is less visible to end users.
* Bugzilla components are set up according to source RPM, so they will have a single shared bugzilla component.  This could be confusing to end-users, as it would be more difficult to figure out e.g. that a bug with python3-foo needs to be filed against python-foo.  There's a similar problem with checking out package sources from CVS, though this is less serious as it is less visible to end users.


Two other ways exist for the upstream to support building python3 modules from a single source:
Two other ways exist for the upstream to support building Python 3 modules from a single source:


==== Building more than once ====
==== Building more than once ====


One way that's currently very common is for the build scripts to create either a python2 or python3 module based on which interpreter is used to run the setup.py script.  (The [http://pkgs.fedoraproject.org/cgit/python-setuptools.git/tree/python-setuptools.spec python-setuptools package] is currently built this way).
One way that's currently very common is for the build scripts to create either a Python 2 or Python 3 module based on which interpreter is used to run the setup.py script.  (The [http://pkgs.fedoraproject.org/cgit/python-setuptools.git/tree/python-setuptools.spec python-setuptools package] is currently built this way).


= Example Specfile =
= Example Specfile =
Line 240: Line 231:
<pre>
<pre>
%global with_python2 1
%global with_python2 1
%global with_python3 1


# this macro is defined here only for testing purposes, it would
# this macro is defined here only for testing purposes, it would
Line 264: Line 254:
%endif
%endif


%if 0%{?with_python3}
BuildRequires:  python3-devel
BuildRequires:  python3-devel
# For use by selftests:
# For use by selftests:
BuildRequires:  python3-pytest
BuildRequires:  python3-pytest
BuildRequires:  python3-tkinter
BuildRequires:  python3-tkinter
%endif
</pre>
</pre>


Line 290: Line 278:
%endif
%endif


%if 0%{?with_python3}
%package -n python3-six
%package -n python3-six
Summary:        Python 2 and 3 compatibility utilities
Summary:        Python 2 and 3 compatibility utilities
Line 300: Line 287:


This is the Python 3 build of the module.
This is the Python 3 build of the module.
%endif
</pre>
</pre>


Line 315: Line 301:
%endif
%endif


%if 0%{?with_python3}
rm -rf %{py3dir}
rm -rf %{py3dir}
cp -a . %{py3dir}
cp -a . %{py3dir}
%endif




Line 328: Line 312:
%endif
%endif


%if 0%{?with_python3}
pushd %{py3dir}
pushd %{py3dir}
%{__python3} setup.py build
%{__python3} setup.py build
popd
popd
%endif




%install
%install
%if 0%{?with_python3}
pushd %{py3dir}
pushd %{py3dir}
%{__python3} setup.py install -O1 --skip-build --root $RPM_BUILD_ROOT
%{__python3} setup.py install -O1 --skip-build --root $RPM_BUILD_ROOT
popd
popd
%endif


%if 0%{?with_python2}
%if 0%{?with_python2}
Line 357: Line 337:
%endif
%endif


%if 0%{?with_python3}
%files -n python3-six
%files -n python3-six
%doc LICENSE README documentation/index.rst
%doc LICENSE README documentation/index.rst
%{python3_sitelib}/*
%{python3_sitelib}/*
%endif
</pre>
</pre>


Line 547: Line 525:
* If <code>2to3</code> runs into a problem, please [https://bugzilla.redhat.com/enter_bug.cgi?component=python&product=Fedora file a Fedora bug].  Please try to isolate a minimal test case that reproduces the problem when doing so.
* If <code>2to3</code> runs into a problem, please [https://bugzilla.redhat.com/enter_bug.cgi?component=python&product=Fedora file a Fedora bug].  Please try to isolate a minimal test case that reproduces the problem when doing so.


== Avoiding collisions between the python 2 and python 3 stacks ==
== Avoiding collisions between the Python 2 and Python 3 stacks ==
The python 2 and python 3 stacks are intended to be fully-installable in parallel. When generalizing the package for both python 2 and python 3, it is important to ensure that two different built packages do not attempt to place different payloads into the same path.
The Python 2 and Python 3 stacks are intended to be fully-installable in parallel. When generalizing the package for both Python 2 and Python 3, it is important to ensure that two different built packages do not attempt to place different payloads into the same path.


=== Executables in <code>/usr/bin</code> ===
=== Executables in <code>/usr/bin</code> ===
==== The problem ====
==== The problem ====
Many existing python packages install executables into <code>/usr/bin</code>.  
Many existing Python packages install executables into <code>/usr/bin</code>.  


For example if we have a <code>console_scripts</code> in a <code>setup.py</code> shared between
For example if we have a <code>console_scripts</code> in a <code>setup.py</code> shared between
python 2 and python 3 builds: these will spit out files in <code>/usr/bin/</code>,
Python 2 and Python 3 builds: these will spit out files in <code>/usr/bin/</code>,
and these will collide.
and these will collide.


Line 567: Line 545:
</pre>
</pre>


which thus generates a <code>/usr/bin/coverage</code> executable (this is a python
which thus generates a <code>/usr/bin/coverage</code> executable (this is a Python
script that runs another python script whilst generating code-coverage
script that runs another Python script whilst generating code-coverage
information on the latter).
information on the latter).


Line 576: Line 554:
     scripts = ['pygmentize'],
     scripts = ['pygmentize'],
</pre>
</pre>
which generates a <code>/usr/bin/pygmentize</code> (this is a python script that leverages the pygments syntax-highlighting module, giving a simple command-line interface for generating syntax-highlighted files)
which generates a <code>/usr/bin/pygmentize</code> (this is a Python script that leverages the pygments syntax-highlighting module, giving a simple command-line interface for generating syntax-highlighted files)


==== Guidelines ====
==== Guidelines ====
Line 598: Line 576:
For other executables, the general rule is:
For other executables, the general rule is:
* if only one executable is to be shipped, then it owns its own slot
* if only one executable is to be shipped, then it owns its own slot
* if executables are to be shipped for both python 2 and python 3, then the python 2 version of the executable gains a <code>python2-</code> prefix.  For example, the python 3 version of "coverage" remains <code>/usr/bin/coverage</code> and the python 2 version is <code>/usr/bin/python2-coverage</code>.
* if executables are to be shipped for both Python 2 and Python 3, then the Python 2 version of the executable gains a <code>Python2.7-</code> prefix.  For example, the python 3 version of "coverage" remains <code>/usr/bin/coverage</code> and the Python 2 version is <code>/usr/bin/python2.7-coverage</code>.


== Packaging eggs and setuptools concerns ==
== Packaging eggs and setuptools concerns ==

Revision as of 08:22, 17 June 2014

Multiple Python Runtimes

In Fedora we have multiple python runtimes, one for each supported major release. At this point that's one for Python 2.7 and one for Python 3.4

Each runtime corresponds to a binary of the form /usr/bin/python$MAJOR.$MINOR

One of these python runtimes is the "system runtime" which is what we run when invoking /usr/bin/python. On Fedora 22 this is /usr/bin/python3.4

All python runtimes have a virtual provide for python(abi) = $MAJOR-$MINOR. For example, the python-3.4 runtime rpm has:

 $ rpm -q --provides python3 |grep -i abi
 python(abi) = 3.4

python modules using these runtimes should have a corresponding "Requires" line on the python runtime that they are used with. This is done automatically for files below /usr/lib[^/]*/python${PYVER}

Warning.png
Test your work
Remember to test the built RPMs and verify that they actually work! For instance, when you're packaging a python module that builds for both Python 2 and Python 3, don't test the Python 3 module but ship the Python 2 module without testing that it does what it's supposed to.
Note.png
For packagers of the python interpreter
Unlike the Requires lines, the "Provides" for each runtime are manually entered into the specfile for each runtime. In theory /usr/lib/rpm/pythondeps.sh would also automatically generate "Provides" lines for the runtime, but in practice rpmbuild only invokes it for files in the rpm payload identified as "python" by the file utility, and the runtime is an ELF binary, not a python script, hence it isn't passed. It's simplest to manually supply the Provides line, rather than change these innards of rpmbuild. See Red Hat Bug 532118.

BuildRequires

To build a package containing Python 2 files, you need to have

BuildRequires: python2-devel

Similarly, when building a package which ships Python 3 files, you need

BuildRequires: python3-devel

A package that has both Python 2 and Python 3 files will need to BuildRequire both.

Macros

Note that the use of %{!? [...]} does allow this to work without the check for rhel versions but putting the conditional in documents when we can remove the entire stanza from the spec file.

In Fedora 13 and greater, the following macros are defined for you:

{

rpmbuild resets the directory at the end of each phase, so you don't need to restore the directory at the end of %prep.

%build
CFLAGS="$RPM_OPT_FLAGS" %{__python2} setup.py build

%if 0%{?with_python3}
pushd %{py3dir}
CFLAGS="$RPM_OPT_FLAGS" %{__python3} setup.py build
popd
%endif # with_python3

%install
rm -rf %{buildroot}

# Must do the python3 install first because the scripts in /usr/bin are
# overwritten with every setup.py install (and we want the python2 version
# to be the default for now).
%if 0%{?with_python3}
pushd %{py3dir}
%{__python3} setup.py install --skip-build --root $RPM_BUILD_ROOT

rm -rf %{buildroot}%{python3_sitelib}/setuptools/tests

find %{buildroot}%{python3_sitelib} -name '*.exe' | xargs rm -f
chmod +x %{buildroot}%{python3_sitelib}/setuptools/command/easy_install.py
popd
%endif # with_python3

%{__python2} setup.py install --skip-build --root $RPM_BUILD_ROOT

rm -rf ${buildroot}%{python2_sitelib}/setuptools/tests

find %{buildroot}%{python2_sitelib} -name '*.exe' | xargs rm -f
chmod +x %{buildroot}%{python2_sitelib}/setuptools/command/easy_install.py

%check
%{__python2} setup.py test

%if 0%{?with_python3}
pushd %{py3dir}
%{__python3} setup.py test
popd
%endif # with_python3

You'll notice that the %build, %install, and %check sections follow a common pattern. They do the normal steps for building the python2 module but then they switch to %{py3dir} and run the same steps for python3. Creating the new sections is generally pretty easy. First copy the existing code. Then wrap it with a pushd/popd to %{py3dir}. The usage of pushd/popd commands will ensure that the directories are logged. Finally, convert all macro references:

  • %{__python2} becomes %{__python3}
  • %{python2_sitelib} becomes %{python3_sitelib}
  • %{python2_sitearch} becomes %{python3_sitearch}
Warning.png
Order can be important
As you can see in the %install section, the order in which you do the python2 versus python3 install can sometimes matter. You need to be aware of when the install is writing to the same file in both packages (in this example, a script in %{_bindir} and make sure that you're getting the version you expect.
%clean
rm -rf $RPM_BUILD_ROOT


%files
%doc psfl.txt zpl.txt docs
%{python2_sitelib}/*
%{_bindir}/easy_install
%{_bindir}/easy_install-2.6

%if 0%{?with_python3}
%files -n python3-setuptools
%doc psfl.txt zpl.txt docs
%{python3_sitelib}/*
%{_bindir}/easy_install-3.4
%endif # with_python3

%changelog

In this final section, you can see that we once again switch macros from %{python2_sitelib} to %{python3_sitelib}. Since we chose to install the python2 version of %{_bindir}/easy_install earlier we need to include that file in the python2 package rather than the python3 subpackage.

Running 2to3 from the spec file

Sometimes, upstream hasn't integrated running 2to3 on the code into their build scripts but they support making a python3 module from it if you manually run 2to3 on the source. This is the case when it's documented on the upstream's website, in a file in the tarball, or even when email with the module's author has instructions for building a python3 module from the python2 source and the authors are willing to support the result. In these cases it's usually just a matter of the upstream not having written the build script that can turn the python2 source into python3. When this happens you can run 2to3 from the spec file. Once you have it working, you can also help upstream integrate it into their build scripts which will benefit everyone in the long term.

You should usually follow upstream's directions on how to run 2to3 and build the python3 module in these cases but there's a few things you should check to make sure upstream is doing it correctly.

  • Since the code is being built from a unified source, you need to copy the code to a new directory before invoking 2to3 just like the building more than once method.
  • If the 2to3 program is invoked instead of using the lib2to3 library functions, make sure it's invoked with --write --nobackups. --write is needed to make 2to3 actually change the files. --nobackups avoids leaving foo.py.bak files in the module directories that then make it into the final package payload.
  • Be sure to run 2to3 on the correct directory. When you run 2to3 you need to run it on the whole tree. A common mistake here for distutils packages has been to run it on the directory below setup.py, missing the setup.py file itself. This leads to errors when python3 tries to execute setup.py
  • If you need to run 2to3 to fix code, use 2to3 or /usr/bin/2to3. At the moment, this program is coming from the python-tools rpm. Using 2to3 means that you'll be using a name that is supported upstream and across distros rather than /usr/bin/python3-2to3 which we have renamed in Fedora to avoid filesystem conflicts. This also makes it easier for us to test and eventually change from using the python2 2to3 to the python3 2to3. We just need to change the python3 package to provide the /usr/bin/2to3 program instead of python and all of our python packages will start using that version instead.
  • If 2to3 runs into a problem, please file a Fedora bug. Please try to isolate a minimal test case that reproduces the problem when doing so.

Avoiding collisions between the Python 2 and Python 3 stacks

The Python 2 and Python 3 stacks are intended to be fully-installable in parallel. When generalizing the package for both Python 2 and Python 3, it is important to ensure that two different built packages do not attempt to place different payloads into the same path.

Executables in /usr/bin

The problem

Many existing Python packages install executables into /usr/bin.

For example if we have a console_scripts in a setup.py shared between Python 2 and Python 3 builds: these will spit out files in /usr/bin/, and these will collide.

For example python-coverage has a setup.py that contains:

    entry_points = {
        'console_scripts': [
            'coverage = coverage:main',
            ]
        },

which thus generates a /usr/bin/coverage executable (this is a Python script that runs another Python script whilst generating code-coverage information on the latter).

Similarly for the 'scripts' clause; see e.g. python-pygments: Pygments-1.1.1/setup.py has:

    scripts = ['pygmentize'],

which generates a /usr/bin/pygmentize (this is a Python script that leverages the pygments syntax-highlighting module, giving a simple command-line interface for generating syntax-highlighted files)

Guidelines

If the executables provide the same functionality independent of whether they are run on top of Python 2 or Python 3, then only the Python 3 version of the executable should be packaged.

Examples of this:

  • /usr/bin/pygmentize ought to generate the same output regardless of whether it's implemented via Python 2 or Python 3, so only one version needs to be shipped.

If the executables provide different functionality for Python 2 and Python 3, then both versions should be packaged.

Examples of this:

  • /usr/bin/coverage runs a python script, augmenting the interpreter with code-coverage information. Given that the interpreter itself is the thing being worked with, it's reasonable to package both versions of the executable.
  • /usr/bin/bpython augments the interpreter with a "curses" interface. Again, it's reasonable to package both versions of this.
  • /usr/bin/easy_install installs a module into one of the Python runtimes: we need a version for each runtime.

As an exception, for the rpms that are part of a python runtime itself, we plan to package both versions of the executables, so that e.g. both the python 2 and python 3 versions of 2to3 are packaged.

Naming

Many executables already contain a "-MAJOR.MINOR" suffix, for example /usr/bin/easy_install-3.4. These obviously can be used as-is, as they won't conflict.

For other executables, the general rule is:

  • if only one executable is to be shipped, then it owns its own slot
  • if executables are to be shipped for both Python 2 and Python 3, then the Python 2 version of the executable gains a Python2.7- prefix. For example, the python 3 version of "coverage" remains /usr/bin/coverage and the Python 2 version is /usr/bin/python2.7-coverage.

Packaging eggs and setuptools concerns

Eggs can mean several different things because they can be placed on disk in several formats:

  • A module and a file with a .egg-info extension that contains the metadata. Created by distutils in Fedora 9 and above.
  • As a module and a directory with a .egg-info extension that contains the metadata. Created using setuptools and also the invocation of setup.py in our examples below.
  • As a directory with a .egg extension that contains the module and egg metadata. Created when we use easy_install -m to allow installing multiple versions of a module.
  • As a single zip file with a .egg extension that contains the module and the egg metadata.

In Fedora packages, these will be installed to %{python2_sitelib} or %{python2_sitearch} directories. We do not install the single zip file version of eggs in Fedora but the three other formats are used.

How to package

The following are a summary of the guidelines for reviewers to go over when a python module is packaged. The complete policy includes examples and rationale for the way we do things.

  • Must: Python eggs must be built from source. They cannot simply drop an egg from upstream into the proper directory. (See prebuilt binaries Guidelines for details)
  • Must: Python eggs must not download any dependencies during the build process.
  • Must: When building a compat package, it must install using easy_install -m so it won't conflict with the main package.
  • Must: When building multiple versions (for a compat package) one of the packages must contain a default version that is usable via "import MODULE" with no prior setup.
  • Should: A package which is used by another package via an egg interface should provide egg info.

Filtering Requires: and Provides:

RPM's dependency generator can often throw in additional dependencies and will often think packages provide functionality contrary to reality. To fix this, the dependency generator needs to be overriden so that the additional dependencies can be filtered out. See Packaging:AutoProvidesAndRequiresFiltering for details.

PyGTK2 and Numpy

Note.png
This workaround is necessary until [Gnome bug #591745] is fixed.

If your package uses pygtk2, and calls the gtk.gdk.get_pixels_array() function, that package needs to explicitly Require: numpy. In the past, pygtk2 had a Requires on numpy, but since it is only used for that one function (and that function is not commonly used), the Requires has been removed to minimize the install footprint of pygtk2.