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

PyTypeObject.ob_type is not available in cpython #2905

Closed
samuelcolvin opened this issue Jan 24, 2023 · 4 comments
Closed

PyTypeObject.ob_type is not available in cpython #2905

samuelcolvin opened this issue Jan 24, 2023 · 4 comments
Labels

Comments

@samuelcolvin
Copy link
Contributor

Bug Description

I'm trying to access PyTypeObject.ob_type, but it doesn't seem to be available on cpython:

#[cfg(all(PyPy, not(Py_3_9)))]
pub ob_type: *mut PyTypeObject,

I believe it should be available, especially since value.get_type().get_type_ptr() works fine.

According to the cpython api docs I think it should be available.

Steps to Reproduce

I would expect the following to pass, but it fails to compile

let value: &PyAny = ...
let type_ptr = value.get_type_ptr();
let meta_type = unsafe { (*type_ptr).ob_type };
assert_eq!(meta_type, value.get_type().get_type_ptr());

! Note, I've tried this with pypy 3.8 and it passes.

Backtrace

No response

Your operating system and version

Ubuntu

Your Python version (python --version)

Python 3.10.4

Your Rust version (rustc --version)

cargo 1.68.0-nightly (2381cbdb4 2022-12-23)

Your PyO3 version

v0.18.0

How did you install python? Did you use a virtualenv?

pyenv

Additional Info

No response

@davidhewitt
Copy link
Member

davidhewitt commented Jan 26, 2023

So the CPython doc you link is actually for PyObject, not PyTypeObject. Since PEP 3123 PyTypeObject contains PyObject as the first field (well, actually PyVarObject, which itself contains PyObject as the first field), So you could write this as PyTypeObject.ob_base.ob_base.ob_type. Or the easier option, and the point of PEP 3123, is that you can just cast to PyObject * and then read ob_type directly.

PyPy before 3.9 was not PEP 3123 compliant (and still isn't on 3.9 windows, I'm not sure what's going on there), so the source line you link to is just to support old pypy versions.

So in summary, this is working as intended

@davidhewitt davidhewitt closed this as not planned Won't fix, can't repro, duplicate, stale Jan 26, 2023
@davidhewitt
Copy link
Member

davidhewitt commented Jan 26, 2023

I think the following can work to get the metaclass safely (on my phone, so haven't tested):

let value: &PyAny = ...
let type_ = value.get_type();
let meta_type = type_.get_type();

samuelcolvin added a commit to pydantic/pydantic-core that referenced this issue Jan 26, 2023
@samuelcolvin
Copy link
Contributor Author

Thanks, (*type_ptr).ob_base.ob_base.ob_type is what I needed, see pydantic/pydantic-core#373

samuelcolvin added a commit to pydantic/pydantic-core that referenced this issue Jan 26, 2023
* fix is_enum as per PyO3/pyo3#2905

* only check is_enum on first loop

* logic for pypy 3.7 & 3.8
@mejrs
Copy link
Member

mejrs commented Jan 26, 2023

For illustration, an instance of a custom Python class' layout looks something like this:

PyObject @ 0x14e93a9ed40 {
	ob_refcnt: 1,
	ob_type: PyTypeObject @ 0x14e9334b2b0 {
		ob_base: PyVarObject @ 0x14e9334b2b0 {
			ob_base: PyObject @ 0x14e9334b2b0 {
				ob_refcnt: 5,
				// 'object'
				ob_type: PyType_Type @ 0x7ffe3789b920 {
                                      // all the fluff inside `object`
                                }
			},
			ob_size: 0,
		},
		tp_name: "MyClass" @ 0x14e93a73660,
		tp_basicsize: 32,
		tp_itemsize: 0,
// etc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants