Beginning with conda version 4.3 and conda-build 2.1, two new types of noarch packages are supported. Noarch Python packages cut down on the overhead of building multiple different pure Python packages on different architectures and Python versions by sorting out platform and Python version-specific differences at install time. Noarch generic packages allow users to distribute docs, datasets, and source code in conda packages.
It’s true that conda-build has had a noarch_python
option for a while, but the user experience has been suboptimal. The deprecated noarch_python
flag adds a pre-link script to the package to handle the several install-time platform-dependent differences. Our noarch Python implementation teaches conda itself about noarch packages of the Python type. The noarch Python-dependent install logic is moved out of the package and into conda, where any extra capabilities and bugs can be directly addressed without the need to rebuild the package.
How to Build Noarch Packages
To build a noarch Python package, specify noarch in your meta.yaml:
build: noarch: python
Similarly, to build a noarch generic package, specify noarch in your meta.yaml:
build: noarch: generic
While there are currently only two flavors of supported noarch package—generic
and python
—we’ll likely extend the concept in future releases.
The Anatomy of a Python Noarch Package
Similar to a regular conda package, a noarch Python package will contain a site-packages
and info
directory that define the package. In addition to the standard files in the info
directory there is package_metadata.json
that defines the type of noarch package and any entry points or scripts. Entry points, defined in the setup.py
style entry_points['console_scripts']
will be created by conda when the package is installed. These packages also do not contain any .pyc
files since these differ among python versions. Instead, generation of .pyc
files is handled by conda at install time. All other scripts associated with the package, for example those found in bin
or Scripts
, will be included in the directory python-scripts
.
The package structure will look something like:
package - info/ - files - about.json - index.json - package_metadata.json - recipe/ - ... - site-packages/ - ... - python-scripts/ - ...
Where, package_metadata.json
will have a noarch section that will look something like:
{ "noarch": { "type": "python", "entry_points": [ "pkg = pkg.foo:main" ] } }
The Noarch Python Package Build Process
By defining noarch: python
in meta.yaml, conda-build will create a noarch Python package as defined above without any .pyc
files or __pycache__
directories. It will also create a info/package_metadata.json
file with information about the type of noarch package and the entry point information.
For example, consider the flask package on the anaconda-recipes repo. This is a pure Python package that can easily be turned into a noarch package by slight modification to the meta.yaml file:
build: noarch: python entry_points: flask = flask.cli:main
Then, build the package as any normal conda package: conda build .
The resulting package is a noarch flask package and installable on any architecture and Python version (that the package itself supports).
The Noarch Python Package Install Process
To install these packages, conda will map site-packages
and python-scripts
directories to the corresponding correct locations within the install prefix. It will then generate the entry points for the package if applicable. On Windows systems, it will include the shim script required for entry points to work. Finally, conda will compile .pyc
files. From the user’s perspective, installing a noarch package is the same as any other, and is as simple as conda install <package>
.
Uninstalling noarch packages works the same way that uninstalling regular conda packages works. That is, conda uninstall <package>
will remove it from the environment.
Looking to the Future
This new way of treating noarch packages aims to provide users with a flexible way of creating conda packages that are platform and python version agnostic. Further, it should provide the flexibility to create noarch packages for other interpreted languages, like R, lua, and ruby.