I am writing a Python wrapper for a C library using the cffi
.
The C library has to be initialized and shut down. Also, the cffi
needs some place to save the state returned from ffi.dlopen()
.
I can see two paths here:
Either I wrap this whole stateful business in a class like this
class wrapper(object):
def __init__(self):
self.c = ffi.dlopen("mylibrary")
self.c.initialize()
def __del__(self):
self.c.terminate()
Or I provide two global functions that hide the state in a global variable
def initialize():
global __library
__library = ffi.dlopen("mylibrary")
__library.initialize()
def terminate():
__library.terminate()
del __library
The first path is somewhat cumbersome in that it requires the user to always create an object that really serves no other purpose other than managing the library state. On the other hand, it makes sure that terminate()
is actually called every time.
The second path seems to result in a somewhat easier API. However, it exposes some hidden global state, which might be a bad thing. Also, if the user forgets to call terminate()
, the C library is not unloaded correctly (which is not a big problem on the C side).
Which one of these paths would be more pythonic?
What Is the Global Variable In Python? In the programming world, a global variable in Python means having a scope throughout the program, i.e., a global variable value is accessible throughout the program unless shadowed. A global variable in Python is often declared as the top of the program.
A global variable is a variable which is accessible in multiple scopes. In Python, it is better to use a single module to hold all the global variables you want to use and whenever you want to use them, just import this module, and then you can modify that and it will be visible in other modules that do the same.
In Python, global keyword allows you to modify the variable outside of the current scope. It is used to create a global variable and make changes to the variable in a local context.
Each Module/file has its own global variable When you create a variable (not within a function) in a Python file, you mount this variable to the namespace of the current module. Every command in this Python file can access, read, and modify the value of the variable, that is, it becomes a global variable.
Exposing a wrapper object only makes sense in python if the library actually supports something like multiple instances in one application. If it doesn't support that or it's not really relevant go for kindall's suggestion and just initialize the library when imported and add an atexit handler for cleanup.
Adding wrappers around a stateless api or even an api without support for keeping different sets of state is not really pythonic and would raise expectations that different instances have some kind of isolation.
Example code:
import atexit
# Normal library initialization
__library = ffi.dlopen("mylibrary")
__library.initialize()
# Private library cleanup function
def __terminate():
__library.terminate()
# register function to be called on clean interpreter termination
atexit.register(__terminate)
For more details about atexit this question has some more details, as has the python documentation of course.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With