Module routing
source code
The meat of Lamson, doing all the work that actually takes an email
and makes sure that your code gets it.
The three most important parts for a programmer are the Router
variable, the StateStorage base class, and the @route, @route_like, and
@stateless decorators.
The lamson.routing.Router variable (it's not a class, just named like
one) is how the whole system gets to the Router. It is an instance of
RoutingBase and there's usually only one.
The lamson.routing.StateStorage is what you need to implement if you
want Lamson to store the state in a different way. By default the
lamson.routing.Router object just uses a default MemoryStorage to do its
job. If you want to use a custom storage, then in your config/boot.py
(or config/testing.py) you would set lamson.routing.Router.STATE_STORE to
what you want to use.
Finally, when you write a state handler, it has functions that act as
state functions for dealing with each state. To tell the Router what
function should handle what email you use a @route decorator. To tell
the Route that one function routes the same as another use @route_like.
In the case where a state function should run on every matching email,
just use the @stateless decorator after a @route or @route_like.
If at any time you need to debug your routing setup just use the
lamson routes command.
Routing Control
To control routing there are a set of decorators that you apply to
your functions.
* @route -- The main routing function that determines what addresses
you are interested in. * @route_like -- Says that this function routes
like another one. * @stateless -- Indicates this function always runs
on each route encountered, and no state is maintained. * @nolocking --
Use this if you want this handler to run parallel without any locking
around Lamson internals. SUPER DANGEROUS, add @stateless as well. *
@state_key_generator -- Used on a function that knows how to make your
state keys for the module, for example if module_name +
message.route_to is needed to maintain state.
It's best to put @route or @route_like as the first decorator, then
the others after that.
The @state_key_generator is different since it's not intended to go
on a handler but instead on a simple function, so it shouldn't be
combined with the others.
|
|
StateStorage
The base storage class you need to implement for a custom storage
system.
|
|
|
MemoryStorage
The default simplified storage for the Router to hold the states.
|
|
|
ShelveStorage
Uses Python's shelve to store the state of the Routers to disk
rather than in memory like with MemoryStorage.
|
|
|
RoutingBase
The self is a globally accessible class that is actually more like a
glorified module.
|
|
|
route
The @route decorator is attached to state handlers to configure
them in the Router so they handle messages for them.
|
|
|
route_like
Many times you want your state handler to just accept mail like
another handler.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stateless(func)
This simple decorator is attached to a handler to indicate to the
Router.deliver() method that it does NOT maintain state or care about
it. |
source code
|
|
|
|
nolocking(func)
Normally lamson.routing.Router has a lock around each call to all
handlers to prevent them from stepping on eachother. |
source code
|
|
|
|
state_key_generator(func)
Used to indicate that a function in your handlers should be used to
determine what they key is for state storage. |
source code
|
|
|
Simple way to get the lamson setting off the function, or None.
|
|
Used to make sure that the func has been setup by a routing
decorator.
|
|
Use this to setup the _lamson_settings if they aren't already
there.
|
|
This simple decorator is attached to a handler to indicate to the
Router.deliver() method that it does NOT maintain state or care about it.
This is how you create a handler that processes all messages matching the
given format+captures in a @route.
Another way to think about a @stateless handler is that it is a
passthrough handler that does its processing and then passes the results
on to others.
Stateless handlers are NOT guaranteed to run before the handler with
state.
|
|
Normally lamson.routing.Router has a lock around each call to all
handlers to prevent them from stepping on eachother. It's assumed that
95% of the time this is what you want, so it's the default. You probably
want everything to go in order and not step on other things going off
from other threads in the system.
However, sometimes you know better what you are doing and this is
where @nolocking comes in. Put this decorator on your state functions
that you don't care about threading issues or that you have found a need
to manually tune, and it will run it without any locks.
|
|
Used to indicate that a function in your handlers should be used to
determine what they key is for state storage. It should be a function
that takes the module_name and message being worked on and returns a
string.
|
LOG
- Value:
logging.getLogger("routing")
|
|