I'm working on an application in Python 3, and what I'm doing is unconventional.
cx_Oracle is a difficult module to setup, and for my application is an optional dependency. What I would like to do is wrap the import of the module in a decorator, to place only above the functions that use it. This will keep from having to import at the top of my module and allow it to not be setup.
class Loader():
def load_cx_oracle(func):
def inner(*args, **kwargs):
# Additional code before import.
import cx_Oracle
return func(*args, **kwargs)
return inner
@load_cx_oracle
def function_using_cx_oracle(self):
conn = cx_Oracle.connect()
However, when I try the above, I get NameError: name 'cx_Oracle' is not defined
To use a decorator ,you attach it to a function like you see in the code below. We use a decorator by placing the name of the decorator directly above the function we want to use it on. You prefix the decorator function with an @ symbol. Here is a simple example. This decorator logs the date and time a function is executed:
The goal of the decorator module is to make it easy to define signature-preserving function decorators and decorator factories. It also includes an implementation of multiple dispatch and other niceties (please check the docs).
It is safe even to copy the module decorator.py over an existing one, since we kept backward-compatibility for a long time. The project is hosted on GitHub.
In Decorators, functions are taken as the argument into another function and then called inside the wrapper function. In the above code, gfg_decorator is a callable function, will add some code on the top of some another callable function, hello_decorator function and return the wrapper function. print("This is inside the function !!")
There a couple of problems with the accepted answer. The most important of which is that it runs the import logic each and every time the function is called. The second is that the decorator must be defined in the same module it is used in otherwise the decorator and the decorated will have different globals. You can directly access a function's globals through the __globals__
attribute of the function. The code example first checks if module exists in the function's globals before executing the import logic. The example also uses the functools.wraps
decorator to preserve doc strings, function name and also argument names when using things like help(func)
.
from functools import wraps
def load_operator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if "operator" not in func.__globals__:
# setup logic -- only executed once
import operator
func.__globals__["operator"] = operator
return func(*args, **kwargs)
return wrapper
class A:
@load_operator
def add(self, x, y):
return operator.add(x, y)
def subtract(self, x, y):
return operator.subtract(x, y)
a = A()
try:
a.subtract(1, 2)
except NameError:
print("operator not yet loaded")
print(a.add(1, 2))
print(A.add)
import
only binds the module name in the namespace where you use it. if you do import cx_Oracle
inside a function, the name cx_Oracle
will only be available inside that function.
However, you can use global
to make the assignment global. Change your decorator to use:
global cx_Oracle
import cx_Oracle
Whether this approach is really the right one is debatable. Depending on how your code is being used, it may be cleaner to just have a function that users call if they want to make cx_Oracle
available, to use a try-wrapped import as Daniel's answer suggests, or to have the loading be defined by some external setting (e.g., a config file).
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