Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
pyramid.pdf
Скачиваний:
10
Добавлен:
24.03.2015
Размер:
3.82 Mб
Скачать

32.3. TWO-PHASE CONFIGURATION

1 config.include(add_routes)

Using include rather than calling the function directly will allow Automatic Conflict Resolution to work.

include() can also accept a module as an argument:

1

2

3

import myapp

config.include(myapp)

For this to work properly, the myapp module must contain a callable with the special name includeme, which should perform configuration (like the add_routes callable we showed above as an example).

include() can also accept a dotted Python name to a function or a module.

32.3 Two-Phase Configuration

When a non-autocommitting Configurator is used to do configuration (the default), configuration execution happens in two phases. In the first phase, “eager” configuration actions (actions that must happen before all others, such as registering a renderer) are executed, and discriminators are computed for each of the actions that depend on the result of the eager actions. In the second phase, the discriminators of all actions are compared to do conflict detection.

Due to this, for configuration methods that have no internal ordering constraints, execution order of configuration method calls is not important. For example, the relative ordering of add_view() and add_renderer() is unimportant when a non-autocommitting configurator is used. This code snippet:

1 config.add_view(’some.view’, renderer=’path_to_custom/renderer.rn’) 2 config.add_renderer(’.rn’, SomeCustomRendererFactory)

Has the same result as:

1

config.add_renderer(’.rn’, SomeCustomRendererFactory)

2

config.add_view(’some.view’, renderer=’path_to_custom/renderer.rn’)

 

 

367

32. ADVANCED CONFIGURATION

Even though the view statement depends on the registration of a custom renderer, due to two-phase configuration, the order in which the configuration statements are issued is not important. add_view will be able to find the .rn renderer even if add_renderer is called after add_view.

The same is untrue when you use an autocommitting configurator (see Using An Autocommitting Configurator). When an autocommitting configurator is used, two-phase configuration is disabled, and configuration statements must be ordered in dependency order.

Some configuration methods, such as add_route() have internal ordering constraints: the routes they imply require relative ordering. Such ordering constraints are not absolved by two-phase configuration. Routes are still added in configuration execution order.

368

CHAPTER

THIRTYTHREE

EXTENDING PYRAMID

CONFIGURATION

Pyramid allows you to extend its Configurator with custom directives. Custom directives can use other directives, they can add a custom action, they can participate in conflict resolution, and they can provide some number of introspectable objects.

33.1 Adding Methods to the Configurator via add_directive

Framework extension writers can add arbitrary methods to a Configurator by using the pyramid.config.Configurator.add_directive() method of the configurator. Using add_directive() makes it possible to extend a Pyramid configurator in arbitrary ways, and allows it to perform application-specific tasks more succinctly.

The add_directive() method accepts two positional arguments: a method name and a callable object. The callable object is usually a function that takes the configurator instance as its first argument and accepts other arbitrary positional and keyword arguments. For example:

1

from

pyramid.events

import

NewRequest

2

from

pyramid.config

import

Configurator

3

4 def add_newrequest_subscriber(config, subscriber):

5config.add_subscriber(subscriber, NewRequest)

6

7 if __name__ == ’__main__’:

8config = Configurator()

9config.add_directive(’add_newrequest_subscriber’,

10

add_newrequest_subscriber)

369

33. EXTENDING PYRAMID CONFIGURATION

Once add_directive() is called, a user can then call the added directive by its given name as if it were a built-in method of the Configurator:

1

2

3

4

def mysubscriber(event): print event.request

config.add_newrequest_subscriber(mysubscriber)

A call to add_directive() is often “hidden” within an includeme function within a “frameworky” package meant to be included as per Including Configuration from External Sources via include(). For example, if you put this code in a package named pyramid_subscriberhelpers:

1

2

3

def includeme(config): config.add_directive(’add_newrequest_subscriber’,

add_newrequest_subscriber)

The user of the add-on package pyramid_subscriberhelpers would then be able to install it and subsequently do:

1

2

3

4

5

6

7

def mysubscriber(event): print event.request

from pyramid.config import Configurator config = Configurator() config.include(’pyramid_subscriberhelpers’)

config.add_newrequest_subscriber(mysubscriber)

33.2 Using config.action in a Directive

If a custom directive can’t do its work exclusively in terms of existing configurator methods (such as pyramid.config.Configurator.add_subscriber(), as above), the directive may need to make use of the pyramid.config.Configurator.action() method. This method adds an entry to the list of “actions” that Pyramid will attempt to process when pyramid.config.Configurator.commit() is called. An action is simply a dictionary that includes a discriminator, possibly a callback function, and possibly other metadata used by Pyramid’s action system.

Here’s an example directive which uses the “action” method:

370

33.2. USING CONFIG.ACTION IN A DIRECTIVE

1 def add_jammyjam(config, jammyjam):

2def register():

3 config.registry.jammyjam = jammyjam

4config.action(’jammyjam’, register)

5

6 if __name__ == ’__main__’:

7config = Configurator()

8config.add_directive(’add_jammyjam’, add_jammyjam)

Fancy, but what does it do? The action method accepts a number of arguments. In the above directive named add_jammyjam, we call action() with two arguments: the string jammyjam is passed as the first argument named discriminator, and the closure function named register is passed as the second argument named callable.

When the action() method is called, it appends an action to the list of pending configuration actions. All pending actions with the same discriminator value are potentially in conflict with one another (see Conflict Detection). When the commit() method of the Configurator is called (either explicitly or as the result of calling make_wsgi_app()), conflicting actions are potentially automatically resolved as per Automatic Conflict Resolution. If a conflict cannot be automatically resolved, a ConfigurationConflictError is raised and application startup is prevented.

In our above example, therefore, if a consumer of our add_jammyjam directive did this:

config.add_jammyjam(’first’) config.add_jammyjam(’second’)

When the action list was committed resulting from the set of calls above, our user’s application would not start, because the discriminators of the actions generated by the two calls are in direct conflict. Automatic conflict resolution cannot resolve the conflict (because no config.include is involved), and the user provided no intermediate pyramid.config.Configurator.commit() call between the calls to add_jammyjam to ensure that the successive calls did not conflict with each other.

This demonstrates the purpose of the discriminator argument to the action method: it’s used to indicate a uniqueness constraint for an action. Two actions with the same discriminator will conflict unless the conflict is automatically or manually resolved. A discriminator can be any hashable object, but it is generally a string or a tuple. You use a discriminator to declaratively ensure that the user doesn’t provide ambiguous configuration statements.

But let’s imagine that a consumer of add_jammyjam used it in such a way that no configuration conflicts are generated.

371

33. EXTENDING PYRAMID CONFIGURATION

config.add_jammyjam(’first’)

What happens now? When the add_jammyjam method is called, an action is appended to the pending actions list. When the pending configuration actions are processed during commit(), and no conflicts occur, the callable provided as the second argument to the action() method within add_jammyjam is called with no arguments. The callable in add_jammyjam is the register closure function. It simply sets the value config.registry.jammyjam to whatever the user passed in as the jammyjam argument to the add_jammyjam function. Therefore, the result of the user’s call to our directive will set the jammyjam attribute of the registry to the string first. A callable is used by a directive to defer the result of a user’s call to the directive until conflict detection has had a chance to do its job.

Other arguments exist to the action() method, including args, kw, order, and introspectables.

args and kw exist as values, which, if passed, will be used as arguments to the callable function when it is called back. For example our directive might use them like so:

1 def add_jammyjam(config, jammyjam):

2def register(*arg, **kw):

3 config.registry.jammyjam_args = arg

4config.registry.jammyjam_kw = kw

5config.registry.jammyjam = jammyjam

6config.action(’jammyjam’, register, args=(’one’,), kw={’two’:’two’})

In the above example, when this directive is used to generate an action, and that action is committed, config.registry.jammyjam_args will be set to (’one’,) and config.registry.jammyjam_kw will be set to {’two’:’two’}. args and kw are honestly not very useful when your callable is a closure function, because you already usually have access to every local in the directive without needing them to be passed back. They can be useful, however, if you don’t use a closure as a callable.

order is a crude order control mechanism.

order defaults to

the

integer

0; it

can

be

set

to any

other integer.

All

actions

that share

an order will be called

before

other

actions

that

share a

higher order.

This

makes

it possible

to write a directive

with

callable logic that

relies

on the execution of the callable of another directive being done first. For example, Pyramid’s pyramid.config.Configurator.add_view() directive registers an action with a higher order than the pyramid.config.Configurator.add_route() method. Due to this, the add_view method’s callable can assume that, if a route_name was passed to it, that a route by this name was already registered by add_route, and if such a route has not already been registered, it’s a configuration error (a view that names a nonexistent route via its route_name parameter will never be called).

introspectables is a sequence of introspectable objects. You can pass a sequence of introspectables to the action() method, which allows you to augment Pyramid’s configuration introspection system.

372

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]