You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a preparatory stage toward implementing PEP 489 (multi-phase module initialization + heap-allocated types).
Analysis
Uses of cPersistenceCAPI
By default, the persistent/cPersistent.h header declares a module-static (global across the C module being compiled) pointer named cPersistenceCAPI, which is a pointer to a cPersistenceCAPIstruct holding the C-defined Persistent base class and a set of helper functions (not module methods).
It also defines a big pile of macros (see below) which rely on that static being initalized: the current BTrees modules do so during module initialization: they import the PyCapsule object exported by persistent, and assign it (with a cast) to the static.
Aside from the macros, BTrees code pretty much uses the static only to find the type object for the Persistent base class:
`` c
#define PER_ALLOW_DEACTIVATION(O) ((O)->state==cPersistent_STICKY_STATE && ((O)->state=cPersistent_UPTODATE_STATE))
If the object is sticky, make it non-sticky, so that it can be ghostified. No error can be returned.
```bash
$ git grep PER_ALLOW_DEACTIVATION src/
src/BTrees/BTreeModuleTemplate.c:#define PER_ALLOW_DEACTIVATION(self)
src/BTrees/BTreeModuleTemplate.c: PER_ALLOW_DEACTIVATION(OBJ); \
src/BTrees/BTreeTemplate.c: PER_ALLOW_DEACTIVATION(child);
src/BTrees/BTreeTemplate.c: PER_ALLOW_DEACTIVATION(child2);
src/BTrees/BTreeTemplate.c: PER_ALLOW_DEACTIVATION(child);
src/BTrees/BTreeTemplate.c: PER_ALLOW_DEACTIVATION(self);
src/BTrees/BTreeTemplate.c: PER_ALLOW_DEACTIVATION(activated_child);
src/BTrees/BTreeTemplate.c: PER_ALLOW_DEACTIVATION(v);
src/BTrees/BTreeTemplate.c: PER_ALLOW_DEACTIVATION(self);
src/BTrees/BucketTemplate.c: PER_ALLOW_DEACTIVATION(BUCKET(i->set));
src/BTrees/SetTemplate.c: PER_ALLOW_DEACTIVATION(BUCKET(i->set));
Making it sticky.
If you call this and don't call PER_ALLOW_DEACTIVATION, your object will not be ghostified.
Returns a 1 on success and 0 failure, where failure means error.
Ditch use of the cPersistenceCAPI static object, by replacing it with a module state variable.
Add a static inline helper function, _get_per_capi, which initially just returns the global static cPersistenceCAPI pointer.
Re-wire all functions which use any of the macros above which depend on the static cPersistenceCAPI object to first call the new _get_per_capi helper, assigning the result to a locally-declared cPersistenceCAPI.
Add a new module_state struct definition, and allocate space for it using the .m_size slot on the PyModule_Def declaration.
Move the post-PyModule_Create bits to a new module_exec function.
In that function, add an import of the CAPI capsule, and populate the module state struct using that pointer.
Add module_traverse and module_clear slot implementations, to ensure that our capsule import plays nice with GC.
Update _get_per_capi, such that it navigates to the module, initially using PyState_FindModule), fetches its state using PyModule_GetState, and returns the CAPI pointer.
Update all call-sites for the macros to ensure that they find their functions capi_struct pointer using _get_capi_struct.
Copy in the macro definitions from cPersistent.h, and update them to use the local capi_struct pointer.
Set the #define which suppresses declaring the global static CAPI pointer when including persistent/persistent/cPersistence.h.
Likewise other global statics:
BTreeType_setattro_allowed_names (declared / used in BTreeTemplate.c but initialized in BTreeModuleTemplate.c.
object_ (declared / used in objectkeymacros.h, but initialized in BTreeModuleTemplate.c. Remove it altogether: it is a weird hack around not using &PyBaseObject_Type (perhaps for BBB with Python2?).
ConflictError (imported from the Python BTrees.intefaces module, used in MergeTemplate.c (which falls back to aliasing ValueError if the static is never initialized).
Closes #207.
This is a preparatory stage toward implementing PEP 489 (multi-phase module initialization + heap-allocated types).
Analysis
Uses of
cPersistenceCAPIBy default, the
persistent/cPersistent.hheader declares a module-static (global across the C module being compiled) pointer namedcPersistenceCAPI, which is a pointer to acPersistenceCAPIstructholding the C-definedPersistentbase class and a set of helper functions (not module methods).It also defines a big pile of macros (see below) which rely on that static being initalized: the current
BTreesmodules do so during module initialization: they import thePyCapsuleobject exported bypersistent, and assign it (with a cast) to the static.Aside from the macros, BTrees code pretty much uses the static only to find the type object for the
Persistentbase class:Uses of
PER_*macrosPER_TypeCheckUnused in
BTrees codePER_USE_OR_RETURNUnghostifies a ghost, returning failure code; ensures object is in "sticky" state on success.
PER_CHANGEDInvokes the CAPI
changedfunction, which returns -1 for errors.PER_READCURRENT$ git grep PER_READCURRENT src/ src/BTrees/BTreeTemplate.c: PER_READCURRENT(self, goto Error);PER_GHOSTIFYInvokes the CAPI
ghostifyfunction, which cannot return an error (ugh).PER_ALLOW_DEACTIVATION`` c
#define PER_ALLOW_DEACTIVATION(O) ((O)->state==cPersistent_STICKY_STATE && ((O)->state=cPersistent_UPTODATE_STATE))
PER_PREVENT_DEACTIVATIONIf the object is up-to-date, make it sticky, so that it cannot be ghostified. No error can be returned.
PER_USEMake a persistent object usable from C by:
If you call this and don't call PER_ALLOW_DEACTIVATION, your object will not be ghostified.
Returns a 1 on success and 0 failure, where failure means error.
PER_ACCESSEDInvokes the CAPI
changedfunction, which can return an error, but that error is always ignored (ugh).PER_UNUSELocally defined (not in
persistent):Since it uses
PER_ACCESSED, we need to ensure thatcapi_structis in scope everywhere it is used.Implementation Plan
Cleanups:
cPersistenceCAPIstatic object, by replacing it with a module state variable._get_per_capi, which initially just returns the global staticcPersistenceCAPIpointer.cPersistenceCAPIobject to first call the new_get_per_capihelper, assigning the result to a locally-declaredcPersistenceCAPI.module_statestruct definition, and allocate space for it using the.m_sizeslot on thePyModule_Defdeclaration.PyModule_Createbits to a newmodule_execfunction.module_traverseandmodule_clearslot implementations, to ensure that our capsule import plays nice with GC._get_per_capi, such that it navigates to the module, initially usingPyState_FindModule), fetches its state usingPyModule_GetState, and returns the CAPI pointer.capi_structpointer using_get_capi_struct.cPersistent.h, and update them to use the localcapi_structpointer.#definewhich suppresses declaring the global static CAPI pointer when includingpersistent/persistent/cPersistence.h.Likewise other global statics:
BTreeType_setattro_allowed_names(declared / used inBTreeTemplate.cbut initialized inBTreeModuleTemplate.c.object_(declared / used inobjectkeymacros.h, but initialized inBTreeModuleTemplate.c. Remove it altogether: it is a weird hack around not using&PyBaseObject_Type(perhaps for BBB with Python2?).ConflictError(imported from the PythonBTrees.intefacesmodule, used inMergeTemplate.c(which falls back to aliasingValueErrorif the static is never initialized).