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

CEP: support for ABI3 packages #86

Merged
merged 19 commits into from
Dec 19, 2024
235 changes: 235 additions & 0 deletions cep-abi3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
# Support for abi3 python packages

<table>
<tr><td> Title </td><td> Support for abi3 python packages </td>
<tr><td> Status </td><td> Draft </td></tr>
<tr><td> Author(s) </td><td> Isuru Fernando &lt;[email protected]&gt;</td></tr>
<tr><td> Created </td><td> July 01, 2023</td></tr>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<tr><td> Created </td><td> July 01, 2023</td></tr>
<tr><td> Created </td><td> July 01, 2024</td></tr>
<tr><td> Updated </td><td> Nov 26, 2024</td></tr>
<tr><td> Discussion </td><td> https://github.com/conda/ceps/pull/86 </td></tr>
<tr><td> Implementation </td><td> https://github.com/conda/conda-build/pull/5456 </td></tr>

</table>

## Abstract

This CEP specifies how ABI3 python packages are supported in conda install tools
(conda/mamba/micromamba/pixi) and how they are built in conda build tools
(conda-build/rattler-build).

## Motivation

When building extensions for python, they might use python minor version
specific symbols. This results in the extension being usable only on that minor
version. These extensions are identified by the extension suffix.
For eg:

foo.cpython-310-x86_64-linux-gnu.so

is an extension that support only CPython 3.10 on x86_64-linux-gnu platform.

However some symbols are available in all python major.minor versions with some
lower bound on the python version. These symbols are part of the
[limited C API]([C_API_Stability]). It is guaranteed that the symbols Stable ABI
jaimergp marked this conversation as resolved.
Show resolved Hide resolved
introduced in Python 3.X are available in Python 3.Y for any `Y >= X`.
Extensions using only these symbols are identified by the extension suffix
`abi3.so`. For eg:

foo.abi3.so

These extensions only support the platform it was built for (for eg:
`x86_64-linux-gnu`), but is not specified in the extension suffix.

Note that the stable ABI is only specific to CPython and is not compatible with
PyPy or other Python implementations. For a Python implementation independent
ABI, see the [HPy project](HPy).
jaimergp marked this conversation as resolved.
Show resolved Hide resolved

The motivation for building abi3 packages is that we only need to build the
extension for one python version and the extension will work for any python
later version. This reduces build matrix from 4-5 python minor versions to one
python minor version and reduces the maintenance burden of package builders.

## noarch: python packages

abi3 packages are python version independent and we will first look at
`noarch: python` packages that are also python version independent and in addition
are arch independent.

`noarch: python` packages have several attributes to them:

&nbsp;&nbsp;<strong>A1</strong>:
They have `subdir: noarch` in `info/index.json`.

&nbsp;&nbsp;<strong>A2</strong>:
They have `noarch: python` in `info/index.json`.

&nbsp;&nbsp;<strong>A3</strong>:
Python files are in `<PREFIX>/site-packages`.

&nbsp;&nbsp;<strong>A4</strong>:
Entry points are recorded in `info/link.json`.

A conda install tool does four things to support them:

&nbsp;&nbsp;<strong>B1</strong>:
Files in `<PREFIX>/site-packages` are moved to the correct location. Eg:
`<PREFIX>/lib/python3.10/site-packages`.

&nbsp;&nbsp;<strong>B2</strong>:
python files (files ending with `*.py`) are compiled to `.pyc` files. Eg:
`<PREFIX>/lib/python3.10/site-packages/foo.py` is compiled to
`<PREFIX>/lib/python3.10/site-packages/__pycache__/foo.cpython-310.pyc`.

&nbsp;&nbsp;<strong>B3</strong>:
`.pyc` files created are recorded in `<PREFIX>/conda-meta/<pkg>.json`
so that they are removed properly when the package is uninstalled.

&nbsp;&nbsp;<strong>B4</strong>:
Entry points in `info/link.json` are materialised.

### info/link.json file
An example `info/link.json` for `noarch: python` looks like

```json
{
"noarch": {
"entry_points": [
"isympy = isympy:main"
],
"type": "python"
},
"package_metadata_version": 1,
"preferred_env": "foo"
}
```

An example for `info/link.json` for `noarch: generic` looks like
```
jaimergp marked this conversation as resolved.
Show resolved Hide resolved
{
"noarch": {
"type": "generic"
},
"package_metadata_version": 1
}
```


Here `preferred_env` is ignored by conda since 2017 and is not supported by
other conda install tools. Therefore `info/link.json` is used exclusively
for `noarch` packages and out of the two types, `noarch: generic` packages
does not require any special action.

### info/index.json file

An example for a `noarch: python` recipe.

```json
{
"arch": null,
"build": "pyh2585a3b_103",
"build_number": 103,
"depends": [
"mpmath >=0.19",
"python >=3.8"
],
"license": "BSD-3-Clause",
"license_family": "BSD",
"name": "sympy",
"noarch": "python",
"platform": null,
"subdir": "noarch",
"timestamp": 1718625708903,
"version": "1.12.1"
}
```

### Current behaviour in solver tools

Conda package upload tools like `anaconda-client` use `A1` to upload
the package to the `noarch` subdir.

Conda install tools have slightly different behaviour.

Conda:
1. Actions `B1, B2, B3` are applied for packages with `A3`.
2. Action `B4` is applied for packages with `A4`.

Micromamba:
1. Actions `B1, B2, B3` are applied for packages with both `A2, A3`.
2. Action `B4` is applied for packages with both `A2, A4`.
Comment on lines +152 to +154
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference, rattler follows the same behavior as micromamba.

Copy link
Member

@jezdez jezdez Oct 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm interested what your suggestion is for this mismatch in behavior, should conda be adapted to follow mamba and rattler in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion is to keep the existing mismatched behaviour in package_metadata_version = 1 and make this uniform in package_metadata_version = 2



## Implementation for abi3 packages

### Support in install tools

We require the following attributes in abi3 packages:

&nbsp;&nbsp;<strong>C1</strong>:
They have `subdir: <platform>` where `<platform>` is the subdir
that the package was built for.

&nbsp;&nbsp;<strong>C2</strong>:
They have `noarch: python`.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a suggestion for a new way to specify abi3 packages in this proposal? I understand how noarch: python almost works already and so can be used to get abi3 packages working with existing tools, but it is a misleading label since these packages are still architecture dependent.

Also, I had tried to test with post-link.sh and pre-unlink.sh scripts previously here but I didn't try noarch: python and got stuck on the run exports of Python. Maybe I just didn't know the right syntax, but it seemed like when you don't use noarch: python conda-build pins the Python version in a way that is hard to override by setting the Python version in the recipe requirements. That might be worth mentioning as another important behavior of noarch: python (if I was not missing something).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a suggestion for a new way to specify abi3 packages in this proposal? I understand how noarch: python almost works already and so can be used to get abi3 packages working with existing tools, but it is a misleading label since these packages are still architecture dependent.

This is package_metadata_version = 1 which is for existing tool compatibility. Please see the next section for new ways.

Also, I had tried to test with post-link.sh and pre-unlink.sh scripts previously conda-forge/qiskit-terra-feedstock#41 but I didn't try noarch: python and got stuck on the run exports of Python. Maybe I just didn't know the right syntax, but it seemed like when you don't use noarch: python conda-build pins the Python version in a way that is hard to override by setting the Python version in the recipe requirements. That might be worth mentioning as another important behavior of noarch: python (if I was not missing something).

That should be fixable by using ignore_run_exports. This is only specific to conda-forge which has run_exports on python package and defaults does not AFAIK. I wanted to keep the CEP more generic.


&nbsp;&nbsp;<strong>C3</strong>:
`A2, A3, A4` are applied.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: A2 is already covered by C2 if I'm not mistaken. Do I get this right?


This is compatible with `conda/mamba/micromamba` install tools
currently.

### Support in build tools

This requires support from build tools to set `subdir: <platform>`.

In particular recipe authors would set
```
build:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for V1 recipes we would use:

build:
  python:
    version_independent: true/false

python_version_independent: true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is version_independent better than abi3? It seems possibly misleading to me because a package can still set a minimum Python version and so be Python version-dependent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

abi3 packages with package_metadata_version = 2 should not be that different from any other package. It just has more things in info/link.json.

Copy link

@mariusvniekerk mariusvniekerk Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think something like

python:
   abi: abi3

leaves open enough space to support future expansion of other abis that may require special behavior

for example

python:
   abi: hpy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mariusvniekerk, both those options need the exact same thing. (B1-B4)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's what a conda-build PR would look like conda/conda-build#5456

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that it's fairly likely that abi4 will become a thing in 2025 or 2026, since it seems necessary for free-threading to be able to support the Limited C API. python-abi3 in host requirements seems to cover it though.


requirements:
host:
- python
- python-abi3
```
which would make the build tool

&nbsp;&nbsp;<strong>D1</strong>:
Set `noarch: python` in `info/index.json`.

Note that `python-abi3` would set the runtime requirements.
This is explicitly required from recipe authors so that we do not
restrict this CEP to ABI3 packages and allow the possibility for ABI4 etc.

An example `python-abi3=3.8` package would set itself in its

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: perhaps this should use the same {{ python_min }} syntax as was recently introduced for noarch packages?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike python which needs multiple values for arch builds and a single value for noarch builds, we only need a single value for abi3 builds. So we can set

python_abi3:
  - 3.8

in our global pinning file in conda-forge and anaconda.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, that makes sense. Some of the "automatically bump" rationale of python_min probably still applies, but this dependency is indeed easier to handle than python.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only need a single value for abi3 builds.

That's not guaranteed AFAIU. Despite the ABI being stable, new features get added in newer python versions, and if anything depends on those features, you might need both the oldest and a newer abi3 build.

For example, cryptography builds for both the py37 & py39 limited API. In the future, it's quite likely that other features (e.g. nogil) will similarly require a newer baseline for some features.

Of course, individual feedstocks will be able to override python_abi3 where necessary (and we may get away to only have a single value in the global pinning), but conceptually, I don't think we can assume it'll be one value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but what I want to emphasize is that we don't need different requirements based on arch vs noarch.

`run_exports` entry and will have the following requirements:
```yaml
requirements:
run:
- cpython >=3.8
- python-gil
```

## Alternatives considered

### Apply all actions in a `post-link.sh` script.

A draft work provided at [python-feedstock](python-pr-669)
jaimergp marked this conversation as resolved.
Show resolved Hide resolved
This was suggested by `@mbargull`, but some community members (@baszalmstra,
@wolfv) does not prefer post-link scripts as they can be used for arbitrary
code execution. However in the author's opinion, this attack vector is not a
new one since the install tool uses the python executable in the host
environment to compile the python files.

### `noarch: python` packages with `__linux, __osx, __win` constrains.

This suggestion by `@wolfv` is not ideal as this clutters `noarch` subdir
`repodata.json` file with packages that are useless for the platform in question.

### A new `package_metadata_version`

Since we can work within the constraints of the current install tools we
do not need to require extra support from install tools.

<!--links-->
[C_API_Stability]: https://docs.python.org/3/c-api/stable.html

[HPy]: https://hpyproject.org

[python-pr-669]: https://github.com/conda-forge/python-feedstock/pull/669