Help with testing Pyramid app composition done with config.include()

3 views
Skip to first unread message

Karl O. Pinc

unread,
Dec 3, 2020, 3:21:22 PM12/3/20
to pylons...@googlegroups.com
Hi,

I've a Pyramid app that's composed of multiple python distributions
(packages). I'm writing integration tests for the code that calls
Configurator() and then uses the resulting config to do
config.include() on the various components.

The application uses URL dispatch. Mostly, the config.included()ed
components setup routes to their views. But routes can also be
overridden by settings, sometimes settings acted upon by the included
components and sometimes settings acted upon by my main Pyramid app.
Route prefixes are added and removed at a couple of points in the
code path.

I want a functional test to check that the configuration produced by
my code establishes the expected routes -- that the route names exist
and that when request.route_url() (etc.) are called the expected URLs
result. I can't find a direct way to do this.

I _could_ use WebTest to call my wsgi app, passing it various paths
and poking about inside the result body to try to figure out that the
right view was called. But this seems clunky and does not directly
tell me that I've got the right route names in existence and that they
produce the right paths.

I can't seem to use pyramid.testing.SetUp() to generate a request so
that I can call request.route_path(). SetUp() returns it's own
configuration, but I want to test the configuration produced by my
code.

The code I want to test is, roughly:

rp = settings.get('route_prefix')
with Configurator(settings=settings, route_prefix=rp) as config:
config.include('this_compoent_always_exists')
for component in components_to_config():
config.include(component)
my_code_that_does_more_route_frobbing(config, settings)
do_more_initializing(config, settings)
return config # to main(), which returns config.make_wsgi() to pyramid


Any help would be appreciated. Thanks.


There is a related issue. I don't use route patterns that contain
replacement markers. The route paths are "fixed". I make a many of
the route paths available to my templates, for navbar generation ,etc.

Presently I'm calling request.route_path(), at runtime when generating
a response, with route names determined based on whatever
config.include()ed components happen to be installed. The results go
into a data structure made available to my templates. But this data
structure is the same for every request. It would be nice to produce
the data during configuration and re-use it when requests arrive.

I seem to have the same problem discovering route paths for my templates
during configuration that I have when trying to write an integration test
that discovers the route paths configured go with my route names. There's
no way to give a configuration a route name and get a route path.
(Presumably, such a thing would have to be done after calling
config.commit().)

I can file an issue at github if that would help resolve any of these
questions and keep them from getting lost.

Regards,

Karl <k...@karlpinc.com>
Free Software: "You don't pay back, you pay forward."
-- Robert A. Heinlein

Michael Merickel

unread,
Dec 3, 2020, 11:16:19 PM12/3/20
to pylons...@googlegroups.com
Have you looked at querying data out of the introspector after config.commit()?

For example, the following will return the route descriptors registered for this name [1]:

config.introspector.get('routes', route_name)

You can also make a dummy request and use it to generate routes. For example:

request = DummyRequest()
# set request properties like host etc to adjust how the url is generated
request.registry = config.registry
path = request.route_path(route_name)

[1] https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/introspector.html#using-the-introspector

- Michael
> --
> You received this message because you are subscribed to the Google Groups "pylons-devel" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to pylons-devel...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/pylons-devel/20201203142116.3bde490d%40slate.karlpinc.com.

Karl O. Pinc

unread,
Dec 4, 2020, 12:30:39 AM12/4/20
to Michael Merickel, pylons...@googlegroups.com
Hi Micheal,

Thanks very much for the prompt reply. My reply inline below.

On Thu, 3 Dec 2020 22:16:15 -0600
Michael Merickel <mmer...@gmail.com> wrote:

> Have you looked at querying data out of the introspector after
> config.commit()?
>
> For example, the following will return the route descriptors
> registered for this name [1]:
>
> config.introspector.get('routes', route_name)

I did, but didn't get very far. I probably didn't read the
narrative docs you link to below and instead got lost in the API
reference. *doh*

Introspection seems the right way to go. Thanks!

There can't be much of a down-side to having introspection
turned on for production, should I decide to work on my
second problem below, or the default wouldn't be True.

> You can also make a dummy request and use it to generate routes. For
> example:
>
> request = DummyRequest()
> # set request properties like host etc to adjust how the url is generated
> request.registry = config.registry
> path = request.route_path(route_name)

I thought about this. But doing "request.registry = config.registry"
seemed unsupported voodoo, however appealing and easy to do in
testing code. If you'd like to comment
on this I'd be interested but don't feel you have to reply.

Michael Merickel

unread,
Dec 4, 2020, 12:41:09 AM12/4/20
to Karl O. Pinc, pylons...@googlegroups.com
The request.registry = ... is fine and supported. The alternative is that you can just do everything with the threadlocal pushed and then DummyRequest will pick up the registry automatically.

with config:
    request = DummyRequest()
    path = request.route_path(route_name)

Or manually with config.begin(); ...; config.end().

- Michael

Karl O. Pinc

unread,
Dec 4, 2020, 12:50:26 AM12/4/20
to Michael Merickel, pylons...@googlegroups.com
On Thu, 3 Dec 2020 23:41:04 -0600
Michael Merickel <mmer...@gmail.com> wrote:

> The request.registry = ... is fine and supported. The alternative is
> that you can just do everything with the threadlocal pushed and then
> DummyRequest will pick up the registry automatically.
>
> with config:
> request = DummyRequest()
> path = request.route_path(route_name)
>
> Or manually with config.begin(); ...; config.end().

I see. Understood. Thanks.

I was so proud of my pytest fixture:

@fixture
def pyramid_request_config():
request = DummyRequest()
yield setUp(request=request)
tearDown()

But it's no good in this case given that the code I'm testing
needs to produce it's own config. :-/
Reply all
Reply to author
Forward
0 new messages