Merge lp:~stevenk/convoy/exportable-app into lp:convoy

Proposed by Steve Kowalik
Status: Rejected
Rejected by: Steve Kowalik
Proposed branch: lp:~stevenk/convoy/exportable-app
Merge into: lp:convoy
Diff against target: 156 lines (+69/-28)
3 files modified
convoy/combo.py (+25/-20)
convoy/tests/test_combo.py (+41/-4)
setup.py (+3/-4)
To merge this branch: bzr merge lp:~stevenk/convoy/exportable-app
Reviewer Review Type Date Requested Status
Sidnei da Silva (community) Needs Fixing
Review via email: mp+91601@code.launchpad.net

Description of the change

This branch moves the inner WSGI application handler from inside combo_app() to be a fully fledged method that can be called outside of combo_app.

The reason for this is we'd like to override the root directory using SetEnv in the Apache config, which gets passed into the WSGI call in the environ dict. Since the application handler used by combo_app() isn't exported, I can't use combo_app, since the start_response variable can't be passed around.

I've tested use of combo_app() and _application() before submitting this MP, and both look to work great.

To post a comment you must log in.
Revision history for this message
Sidnei da Silva (sidnei) wrote :

I'm really confused as to why this is needed. In any case, if the intention is to call _application externally, making this some sort of public API it should definitely not start with an underscore, and a test for it should be added.

review: Needs Fixing
lp:~stevenk/convoy/exportable-app updated
21. By Steve Kowalik

Rename _application to application, write a test, and fix up setup.py not to
install tests.

Revision history for this message
Steve Kowalik (stevenk) wrote :

I've added a test, and I've chopped off the underscore.

Hopefully, the WSGI file I'm going to use with this will help explain:

from convoy.combo import application

def application(environ, start_response):
    root = environ.get('CONVOY_ROOT', '/srv/launchpad.dev/convoy')
    return application(environ, start_response, root=root)

As you can see, we want to get the root from the environment passed to the WSGI app. I can't use combo_app() for this, since WSGI is expecting the same environ and start_response to be used, and before this branch, they were hidden inside combo_app() and couldn't be set by callers.

The above snippet allows me to use the same WSGI for local development, our staging environments and production by use of Apache's SetEnv directive.

Revision history for this message
Steve Kowalik (stevenk) wrote :

One thing I meant to add is I'm happy to split out the setup.py changes into a separate smaller branch if you wish.

Revision history for this message
Sidnei da Silva (sidnei) wrote :

Surely there's some misunderstanding going on here. Take a look at the following script that emulates your usage, and see how combo_app works just fine as-is, you just need to call it to get an application and then call the application.

https://pastebin.canonical.com/59582/

Revision history for this message
Steve Kowalik (stevenk) wrote :

Right, that was the piece I was missing. I was trying for hours yesterday to get this working, and the diff on this MP was the only way I could get it working. Thanks to your pastebin, I don't need this change, and I'll set this MP to rejected.

Unmerged revisions

21. By Steve Kowalik

Rename _application to application, write a test, and fix up setup.py not to
install tests.

20. By Steve Kowalik

Split out the actual WSGI application function to one that can be exported.
Bump version.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'convoy/combo.py'
2--- convoy/combo.py 2012-01-27 03:37:59 +0000
3+++ convoy/combo.py 2012-02-07 05:22:20 +0000
4@@ -16,6 +16,7 @@
5
6
7 import cgi
8+from functools import partial
9 import os
10 import re
11 import urlparse
12@@ -122,23 +123,27 @@
13 C{Content-Type} header.
14 """
15 root = os.path.abspath(root)
16- def app(environ, start_response, root=root):
17- path_hint = parse_path_hint(environ['PATH_INFO'])
18- fnames = parse_qs(environ["QUERY_STRING"])
19- content_type = "text/plain"
20- if fnames:
21- if fnames[0].endswith(".js"):
22- content_type = "text/javascript"
23- elif fnames[0].endswith(".css"):
24- content_type = "text/css"
25- else:
26- start_response("404 Not Found", [("Content-Type", content_type)])
27- return ("Not Found",)
28- start_response("200 OK", [("Content-Type", content_type)])
29-
30- # take any prefix in the url route into consideration for the root to
31- # find files at
32- updated_root = os.path.join(root, path_hint)
33- return combine_files(fnames, updated_root, resource_prefix,
34- rewrite_urls=rewrite_urls)
35- return app
36+ return partial(application, root=root, resource_prefix=resource_prefix,
37+ rewrite_urls=rewrite_urls)
38+
39+
40+def application(environ, start_response, root="", resource_prefix="",
41+ rewrite_urls=True):
42+ path_hint = parse_path_hint(environ['PATH_INFO'])
43+ fnames = parse_qs(environ["QUERY_STRING"])
44+ content_type = "text/plain"
45+ if fnames:
46+ if fnames[0].endswith(".js"):
47+ content_type = "text/javascript"
48+ elif fnames[0].endswith(".css"):
49+ content_type = "text/css"
50+ else:
51+ start_response("404 Not Found", [("Content-Type", content_type)])
52+ return ("Not Found",)
53+ start_response("200 OK", [("Content-Type", content_type)])
54+
55+ # take any prefix in the url route into consideration for the root to
56+ # find files at
57+ updated_root = os.path.join(root, path_hint)
58+ return combine_files(fnames, updated_root, resource_prefix,
59+ rewrite_urls=rewrite_urls)
60
61=== modified file 'convoy/tests/test_combo.py'
62--- convoy/tests/test_combo.py 2012-01-27 03:37:59 +0000
63+++ convoy/tests/test_combo.py 2012-02-07 05:22:20 +0000
64@@ -23,10 +23,8 @@
65 import mocker
66 from paste.fixture import TestApp
67
68-from convoy.combo import combo_app
69-from convoy.combo import combine_files
70-from convoy.combo import parse_path_hint
71-from convoy.combo import parse_url
72+from convoy.combo import (
73+ application, combo_app, combine_files, parse_path_hint, parse_url)
74
75 class ComboTestBase(object):
76
77@@ -483,5 +481,44 @@
78 ["yui/yui-min.js"])), status=200)
79 self.assertEquals(res.body.strip(), expected2)
80
81+ def test_application_can_inject_root(self):
82+ """If root is passed into application, it can be done per-request."""
83+ first_root = self.makeDir()
84+ second_root = self.makeDir()
85+
86+ self.makeSampleFile(
87+ first_root,
88+ os.path.join("yui", "yui-min.js"),
89+ "/* yui-min */"),
90+
91+ self.makeSampleFile(
92+ second_root,
93+ os.path.join("yui", "yui-min.js"),
94+ "/* yui-min-2 */"),
95+
96+ expected = "\n".join(("/* yui/yui-min.js */",
97+ "/* yui-min */"))
98+ expected2 = "\n".join(("/* yui/yui-min.js */",
99+ "/* yui-min-2 */"))
100+
101+ def app(environ, start_response):
102+ root = environ.get("CONVOY_ROOT")
103+ return application(environ, start_response, root=root)
104+
105+ environ = {'CONVOY_ROOT': first_root}
106+ app = TestApp(app)
107+
108+ res = app.get(
109+ "/+combo/?yui/yui-min.js", status=200, extra_environ=environ)
110+ self.assertEquals(res.body.strip(), expected)
111+
112+ environ['CONVOY_ROOT'] = second_root
113+ res = app.get(
114+ "/+combo/?yui/yui-min.js", status=200, extra_environ=environ)
115+ self.assertEquals(res.body.strip(), expected2)
116+
117+
118+
119+
120 def test_suite():
121 return defaultTestLoader.loadTestsFromName(__name__)
122
123=== modified file 'setup.py'
124--- setup.py 2012-01-27 03:37:59 +0000
125+++ setup.py 2012-02-07 05:22:20 +0000
126@@ -22,14 +22,13 @@
127 # declare our dependency on epsilon.
128 extra_setup_args = {}
129 try:
130- import setuptools
131 from setuptools import find_packages
132 extra_setup_args = {
133 'install_requires': ['Paste'],
134 'tests_require': ['nose', 'mocker']
135 }
136 except ImportError:
137- def find_packages():
138+ def find_packages(excludes=None):
139 """
140 Compatibility wrapper.
141
142@@ -43,12 +42,12 @@
143
144 setup(
145 name="convoy",
146- version="0.2.1",
147+ version="0.2.2",
148 description="A combo WSGI application for use with YUI",
149 author="Canonical Javascripters",
150 url="https://launchpad.net/convoy",
151 license="AGPL",
152- packages=find_packages(),
153+ packages=find_packages(exclude=('convoy.tests',)),
154 include_package_data=True,
155 zip_safe=False,
156 test_suite="nose.collector",

Subscribers

People subscribed via source and target branches