Some of rpm's types can't be instantiated by calling the class.
The flag for this was only added in Python 3.10.
Replace it by a tp_new slot with the same effect: raising an exception.
The previous commit removed all *_Type variables.
This adds them back, but as *pointers* to Python type objects
rather than type objects themselves.
All usages now need an extra pointer dereference (or removing an
address-of).
(The compiler will complain of type safety if we miss a spot,
so I'm OK using the original names.)
Also, add header declarations for the Spec structs.
In the stable ABI Python's type objects aren't defined in structs
that reference other structs, but by an array of "slots" from which
type objects are created.
(This commit was generated semi-automatically. The automation
is too messy and bespoke to share widely, but if you're interested
in undocumented work in progress, look in
https://github.com/encukou/api-limiter/tree/rpm-mess )
The Python type struct changes between versions, and thus direct access to
its fields is not allowed in Stable ABI.
The members can, however, be retrieved using `PyType_GetSlot`.
Commit generated with this semantic patch (see https://coccinelle.lip6.fr ):
```
@@
identifier subtype;
type t;
expression size;
identifier var;
@@
- t var = (t)subtype->tp_alloc(subtype, size);
+ allocfunc subtype_alloc = (allocfunc)PyType_GetSlot(subtype, Py_tp_alloc);
+ t var = (t)subtype_alloc(subtype, size);
@@
identifier subtype;
type t;
expression size;
identifier var;
@@
- var = (t)subtype->tp_alloc(subtype, size);
+ allocfunc subtype_alloc = (allocfunc)PyType_GetSlot(subtype, Py_tp_alloc);
+ var = (t)subtype_alloc(subtype, size);
```
applied using:
spatch --sp-file patch.cocci --dir python/ --in-place
The Python type struct changes between versions, and thus direct access to
its fields is not allowed in Stable ABI.
The members can, however, be retrieved using `PyType_GetSlot`.
Commit generated with this semantic patch (see https://coccinelle.lip6.fr ):
```
@@
identifier s;
type t;
@@
- Py_TYPE(s)->tp_free((t*) s);
+ Py_TYPE(s)->tp_free(s);
@@
@@
- Py_TYPE(s)->tp_free(s);
+ freefunc free = PyType_GetSlot(Py_TYPE(s), Py_tp_free);
+ free(s);
```
applied using:
spatch --sp-file patch.cocci --dir python/ --in-place
In the almost ten years of rpm sort of supporting Python 3 bindings, quite
obviously nobody has actually tried to use them. There's a major mismatch
between what the header API outputs (bytes) and what all the other APIs
accept (strings), resulting in hysterical TypeErrors all over the place,
including but not limited to labelCompare() (RhBug:1631292). Also a huge
number of other places have been returning strings and silently assuming
utf-8 through use of Py_BuildValue("s", ...), which will just irrevocably
fail when non-utf8 data is encountered.
The politically Python 3-correct solution would be declaring all our data
as bytes with unspecified encoding - that's exactly what it historically is.
However doing so would by definition break every single rpm script people
have developed on Python 2. And when 99% of the rpm content in the world
actually is utf-8 encoded even if it doesn't say so (and in recent times
packages even advertise themselves as utf-8 encoded), the bytes-only route
seems a wee bit too draconian, even to this grumpy old fella.
Instead, route all our string returns through a single helper macro
which on Python 2 just does what we always did, but in Python 3 converts
the data to surrogate-escaped utf-8 strings. This makes stuff "just work"
out of the box pretty much everywhere even with Python 3 (including
our own test-suite!), while still allowing to handle the non-utf8 case.
Handling the non-utf8 case is a bit more uglier but still possible,
which is exactly how you want corner-cases to be. There might be some
uses for retrieving raw byte data from the header, but worrying about
such an API is a case for some other rainy day, for now we mostly only
care that stuff works again.
Also add test-cases for mixed data source labelCompare() and
non-utf8 insert to + retrieve from header.
- Various functions in the Python bindings construct lists of objects, but
assume that all calls succeed. Each of these could segfault under
low-memory conditions: if the PyList_New() call fails,
PyList_Append(NULL, item ) will segfault. Similarly, although
Py_List_Append(list, NULL) is safe, Py_DECREF(NULL) will segfault.
Signed-off-by: Ales Kozumplik <akozumpl@redhat.com>
- rpm.ps object only supports iteration and subscript (with wonderfully
wacko semantics), returning a regular list serves us better
- rip the now useless rpm.ps object type
- The code to insert new problems has been using invalid conversion code
causing crashes since 2004 and nobody noticed, safe to say this is an
unused interface. Additionally the method argument flags were wrong, it
was declared as METH_VARARGS but actually expected METH_O semantics. RIP.
The layout of PyVarObject changed between python 2 and python 3, and this leads
to the existing code for all of the various PyTypeObject initializers failing to
compile with python 3
Change the way we initialize these structs to use PyVarObject_HEAD_INIT directly,
rather than merely PyObject_HEAD_INIT, so that it compiles cleanly with both major
versions of Python
Python 2's various object structs use macros to implement common fields at the top of each
struct.
Python 3's objects instead embed a PyObject struct as the first member within the more refined
object structs.
Use the Py_TYPE() macro when accessing ob_type in order to encapsulate this difference.
- these violate the intended use of tp_print, python docs state
"A type should never implement tp_print in a way that produces
different output than tp_repr or tp_str would."
- tp_init can be called several times, allocating from there leaks memory
- tp_init gets called automatically on object creation, dont call manually
- nothing to initialize for rpmps...
- put some consistency into include ordering
- everything (apart from bits missed ;) is now ordered like this
1. "system.h"
2. other system includes
3. rpm public headers
4. rpm private headers
5. "debug.h"