when dealing with file names, removing custom code (with the
associated benefits).
+Important API changes/bug fixes:
+
+- Initialisation protocol has been changed, to disallow uninitialised
+ objects; this means that `__new__` will always create valid objects,
+ to prevent the need for checking initialisation status in all code
+ paths; this also (implicitly) fixes memory leaks on re-initialisation
+ (calling `__init__(…)` on an existing object) and segfaults (!) on
+ non-initialised object attribute access.
+
Additionally, test suite has changed to `pytest`.
Version 0.5.4
static PyObject* Entry_new(PyTypeObject* type, PyObject* args,
PyObject *keywds) {
PyObject* newentry;
+ Entry_Object* entry;
+ ACL_Object* parent = NULL;
+
+ if (!PyArg_ParseTuple(args, "O!", &ACL_Type, &parent))
+ return NULL;
newentry = PyType_GenericNew(type, args, keywds);
- if(newentry != NULL) {
- ((Entry_Object*)newentry)->entry = NULL;
- ((Entry_Object*)newentry)->parent_acl = NULL;
+ if(newentry == NULL) {
+ return NULL;
}
+ entry = (Entry_Object*)newentry;
+
+ if(acl_create_entry(&parent->acl, &entry->entry) == -1) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ Py_DECREF(newentry);
+ return NULL;
+ }
+ Py_INCREF(parent);
+ entry->parent_acl = (PyObject*)parent;
return newentry;
}
if (!PyArg_ParseTuple(args, "O!", &ACL_Type, &parent))
return -1;
- if(acl_create_entry(&parent->acl, &self->entry) == -1) {
- PyErr_SetFromErrno(PyExc_IOError);
+ if ((PyObject*)parent != self->parent_acl) {
+ PyErr_SetString(PyExc_ValueError,
+ "Can't reinitialize with a different parent");
return -1;
}
-
- self->parent_acl = (PyObject*)parent;
- Py_INCREF(parent);
-
return 0;
}
Entry_Object *self = (Entry_Object*) obj;
acl_tag_t value;
- if (self->entry == NULL) {
- PyErr_SetString(PyExc_AttributeError, "entry attribute");
- return NULL;
- }
if(acl_get_tag_type(self->entry, &value) == -1) {
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
with pytest.raises(TypeError):
posix1e.Entry(object())
+ def test_entry_reinitialisations(self):
+ a = posix1e.ACL()
+ b = posix1e.ACL()
+ e = posix1e.Entry(a)
+ e.__init__(a)
+ with pytest.raises(ValueError, match="different parent"):
+ e.__init__(b)
+
+ @NOT_PYPY
+ def test_entry_reinit_leaks_refcount(self):
+ acl = posix1e.ACL()
+ e = acl.append()
+ ref = sys.getrefcount(acl)
+ e.__init__(acl)
+ assert ref == sys.getrefcount(acl), "Uh-oh, ref leaks..."
+
def test_delete(self):
"""Test delete Entry from the ACL"""
acl = posix1e.ACL()