Accessing the API from Python modules
Your models or controller may import Python modules. These are usually Python files you store in the modules directory of your app. They may need to use some of the web2py API. The way to do it is by importing them:
from gluon import ...
In fact, any Python module, even if not imported by a web2py application, can import the web2py API as long as web2py is in the sys.path
.
Sharing the global scope with modules using the current object
There is one caveat, though. Web2py defines some global objects (request, response, session, cache, T) that can only exist when an HTTP request is present (or is faked). Therefore, modules can access them only if they are called from an application. For this reasons they are placed into a container called current
, which is a thread local object. Here is an example.
Create a module “/myapp/modules/mytest.py” that contains:
from gluon import current
def ip(): return current.request.client
Now from a controller in “myapp” you can do
import mytest
def index():
return "Your ip is " + mytest.ip()
Notice a few things:
import mytest
looks for the module first in the current app’s modules folder, then in the folders listed insys.path
. Therefore, app-level modules always take precedence over Python modules. This allows different apps to ship with different versions of their modules, without conflicts.- Different users can call the same action
index
concurrently, which calls the function in the module, and yet there is no conflict becausecurrent.request
is a different object in different threads. Just be careful not to accesscurrent.request
outside of functions or classes (i.e., at the top level) in the module. import mytest
is a shortcut forfrom applications.appname.modules import mytest
. Using the longer syntax, it is possible to import modules from other applications.
For uniformity with normal Python behavior, by default web2py does not reload modules when changes are made. Yet this can be changed. To turn on the auto-reload feature for modules, use the track_changes
function as follows (typically in a model file, before any imports):
from gluon.custom_import import track_changes; track_changes(True)
From now on, every time a module is imported, the importer will check if the Python source file (.py) has changed. If it has changed, the module will be reloaded.
Do not call track_changes in the modules themselves.
Track changes only tracks changes for modules that are stored in the application. Modules that import current
can access:
current.request
current.response
current.session
current.cache
current.T
and any other variable your application chooses to store in current. For example a model could do
auth = Auth(db)
from gluon import current
current.auth = auth
current.db = db #not needed in this case but useful
and now all modules imported can access current.auth
.
current
and import
create a powerful mechanism to build extensible and reusable modules for your applications.
Warning! Do not use the current object in global scope in a module
Beware! Given from gluon import current
, it is correct to use current.request
and any of the other thread local objects but one should never assign them to global variables in the module, such as in
request = current.request # WRONG! DANGER!
nor should one use current to assign class attributes:
class MyClass:
request = current.request # WRONG! DANGER!
This is because the thread local object must be extracted at runtime. Global variables instead are defined only once when the model is imported for the first time.
Instead, assign inside a function.
from gluon import current
...
def a_module_function():
db = current.db # assuming you assigned current.db = db in the model db.py
...
Another caveat has to do with cache. You cannot use the cache
object to decorate functions in modules, that is because it would not behave as expected. In order to cache a function f
in a module you must use lazy_cache
:
from gluon.cache import lazy_cache
@lazy_cache('key', time_expire=60, cache_model='ram')
def f(a, b, c): ....
Mind that the key is user defined but must be uniquely associated to the function. If omitted web2py will automatically determine a key.