Skip to content

Complete abi3 support #1125

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

Closed
alex opened this issue Aug 30, 2020 · 23 comments
Closed

Complete abi3 support #1125

alex opened this issue Aug 30, 2020 · 23 comments
Assignees
Milestone

Comments

@alex
Copy link
Contributor

alex commented Aug 30, 2020

abi3, also known as pylimited abi, is a feature of built python projects where they can be loaded from multiple versions of python3. pyo3 has some limited support for this, but it's commented out in Cargo.toml because it's currently incomplete and does not compile.

I'm filing this as a tracking bug so folks interested in abi3 support have a place to follow along. I intend to attempt to contribute to resolving this issue.

I imagine this is a three part process:

  1. switching things from internals -> abi3 APIs when there's a 1:1 equivalent
  2. disabling pyo3 APIs that map to things that don't exist in abi3 mode
  3. figuring out how to re-enable the things from (2)
@kngwyu
Copy link
Member

kngwyu commented Aug 31, 2020

You need #[pyclass] with abi3?
I think most of the code works with PY_LIMITED_API or can be modified to do so, but not sure about #[pyclass].

@davidhewitt
Copy link
Member

I think it would be desirable if #[pyclass] can work with the limited API, but we may need to talk a lot to upstream cpython to achieve it.

@alex
Copy link
Contributor Author

alex commented Aug 31, 2020

Why do you imagine it'd require upstream support? I imagine most #[pyclass] usage can be achieved with PyType_FromSpec?

alex added a commit to alex/pyo3 that referenced this issue Aug 31, 2020
Refs PyO3#1125

Reduces the number of compilation failures with `--features abi3` from 56 to 53.
@davidhewitt
Copy link
Member

Quite possibly - might need a lot of refactoring!

alex added a commit to alex/pyo3 that referenced this issue Aug 31, 2020
Refs PyO3#1125

Reduces the number of compilation failures with `--features abi3` from 56 to 53.
alex added a commit to alex/pyo3 that referenced this issue Aug 31, 2020
Refs PyO3#1125

Reduces the number of compilation failures with `--features abi3` from 56 to 53.
@alex
Copy link
Contributor Author

alex commented Aug 31, 2020

I started investigating #[pyclass] support, since it's the biggest piece here. Before I get too deep into implementing, I wanted to lay out my thoughts and make sure this all sounded ok.

First, I'll start by describing the current approach:

  • Currently pyo3-dervice-backend emits code that creates a register, which contains a struct for each of the tables of function pointers that a PyTypeObject* contains.
  • pyo3 then takes this table, create a PyTypeObject* and fills in all the fields by hand basically, and then calls PyType_Ready.

My proposal for how to make this abi3 compatible:

  • pyo3-dervice-backend is changed to generate a list of PEP384 slots
  • pyo3 takes those and calls PyType_FromSpec

The one challenge is that some of these APIs are technically public (though they often have #[doc(hidden)]) because they're called from macros that expand in users's crates. Is it ok to change these APIs - perhaps with a major version bump?

One of my goals here is that there's only one version of this code, and not 2 different versions.

@alex
Copy link
Contributor Author

alex commented Sep 1, 2020

#1132 has a PoC of what the first steps of this could look like

sebpuetz pushed a commit to sebpuetz/pyo3 that referenced this issue Sep 1, 2020
Refs PyO3#1125

Reduces the number of compilation failures with `--features abi3` from 56 to 53.
@davidhewitt davidhewitt added this to the 0.13 milestone Sep 1, 2020
@davidhewitt
Copy link
Member

I'm (perhaps optimistically) marking this for the 0.13 release. Let's see how we get on!

@alex
Copy link
Contributor Author

alex commented Sep 7, 2020

I've also done a POC of what the setuptools-rust side of this can look like: PyO3/setuptools-rust#82

@alex
Copy link
Contributor Author

alex commented Sep 7, 2020

Status check, looking at the abi3 branch with #1162 incorporated:

Here's the remaining compilation errors with cargo check --no-default-features --features macros. In total there are 15 compilation errors, but I think it's fewer root causes:

  • FreeFunc - needs to be #[cfg()]'d out
  • A family of errors around tp_new/tp_alloc/tp_free. I haven't looked at these yet
  • Need to find a replacement for ffi::PyRun_StringFlags
  • Need to find a replacement for ffi::PyUnicode_AsUTF8AndSize - this one is a bit messy. The only equivilant, AFAICT, is to encode the object to a PyBytesObject and get the pointer out of this. Since ffi::PyUnicode_AsUTF8AndSize is used in to_str(&self) -> PyResult<&str> this is a mess, since creating a temporary PyBytesObject would have a different lifetime. The best I can come up with is changing to_str() to return something that impl Deref<Target=&str>. Thoughts?
  • tp_dict - setting up class attributes on fresh classes. Can this just be setattr()?
  • tp_name - For PyType::name. I can't find an equivilant API, so I think I'll just use getattr("__name__")
  • No iterator for sets with limited API - switch to just using a generic iterator

@alex
Copy link
Contributor Author

alex commented Sep 9, 2020

Down to 3 underlying issues! PyRun_StringFlags is the next one I plan to look at it. I could use feedback on the other two though.

@alex
Copy link
Contributor Author

alex commented Sep 9, 2020

Ok, the last two issues I could use some feedback on. I'd like to to do the PyUnicode_AsUTF8AndSize one next. I don't see how to fix it without changing the API of to_str() (which probably also impacts extract()). Does anyone have thoughts?

@kngwyu
Copy link
Member

kngwyu commented Sep 10, 2020

I'm sorry but I cannot investigate that until this weekend.

@alex
Copy link
Contributor Author

alex commented Sep 10, 2020

No problem. I'll keep poking myself.

@kngwyu
Copy link
Member

kngwyu commented Sep 12, 2020

I looked over all APIs and am a bit surprised to see there's no UTF-8 methods with LIMITED_API...
@alex
What alternative do you have in your mind?

@alex
Copy link
Contributor Author

alex commented Sep 12, 2020 via email

@alex
Copy link
Contributor Author

alex commented Sep 13, 2020

I'm now looking at the other issue around alloc/dealloc. I found that if I delete these lines:

pyo3/src/pyclass.rs

Lines 48 to 52 in 448f0bb

if Self::is_exact_instance(obj) && ffi::PyObject_CallFinalizerFromDealloc(obj.as_ptr()) < 0
{
// tp_finalize resurrected.
return;
}
no test fails. Does deleting this lines entirely seem right? Is it just a case that isn't tested?

@alex
Copy link
Contributor Author

alex commented Sep 13, 2020

Ok, I poked further and I think indeed this is effectively dead code. is_exact_instance makes sure the ob_type in obj is exactly the same as the one for the class that is Self. Classes that we create never have tp_finalize filled in! And all PyObject_CallFinalizerFromDealloc does is call tp_finalize, which is always null, and therefore as far as I can tell it's a big no-op. I'll send a PR :-)

@programmerjake
Copy link
Contributor

friendly reminder to put a comment in the code so we don't forget to handle this if PyO3 starts to use tp_finalize some time in the future

@alex
Copy link
Contributor Author

alex commented Sep 14, 2020

I've filed a bug with CPython about making PyUnicode_AsUTF8AndSize a limited API: https://bugs.python.org/issue41784

@alex
Copy link
Contributor Author

alex commented Sep 15, 2020

With #1187 and #1183, we'll now be able to cargo check --no-default-features --features macros!

@alex
Copy link
Contributor Author

alex commented Sep 15, 2020

Status update: cargo check --no-default-features --features macros is now error free! I have a PR up (#1188) to get it warning clean.

Then the next step will be to get all the tests passing. After that, we add it to CI and merge I hope:-)

@alex
Copy link
Contributor Author

alex commented Sep 15, 2020

#1189 now has all the tests passing, except the doctests.

alex added a commit to alex/setuptools-rust that referenced this issue Sep 20, 2020
alex added a commit to alex/setuptools-rust that referenced this issue Sep 23, 2020
alex added a commit to alex/setuptools-rust that referenced this issue Sep 26, 2020
alex added a commit to alex/setuptools-rust that referenced this issue Sep 26, 2020
alex added a commit to alex/setuptools-rust that referenced this issue Sep 26, 2020
alex added a commit to alex/setuptools-rust that referenced this issue Sep 26, 2020
alex added a commit to alex/setuptools-rust that referenced this issue Sep 28, 2020
alex added a commit to alex/setuptools-rust that referenced this issue Oct 10, 2020
alex added a commit to alex/setuptools-rust that referenced this issue Oct 10, 2020
alex added a commit to alex/setuptools-rust that referenced this issue Oct 10, 2020
@alex
Copy link
Contributor Author

alex commented Oct 27, 2020

I think this is complete now, with the merge of #1152!

@alex alex closed this as completed Oct 27, 2020
alex added a commit to alex/setuptools-rust that referenced this issue Oct 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants