Reply to comment
Python: Setting up a TurboGears Application Under Shared Namespace
drks — Wed, 2009-06-17 17:27
Summary
I came across an issue today whilst trying to get a TurboGears application setup, and that is... how do you setup a TurboGears applications as part of a bigger application. Meaning, the overall project has multiple parts: The web frontend (TurboGears), the command line client, etc. I found that using paster to get a TurboGears application created in the way isn't the most straight forward thing in the world.
That said, with a bit of fenagling, it is possible to get a TG application created as part of a shared namespace (See SetupTools Namespace Packages).
Please note that this article is not intended to give solid info on setting up and using TurboGears, but rather to simply address the issue of namespace packages and TurboGears. Please review the Reference at the bottom of this article for more information.
Please also note that I am not a TurboGears or Python expert. If there is a better way to do this, please drop me a line or leave a comment.
Getting Started - How Things Work Out of The Box
For those that might not be entirely familiar with TurboGears, I want to show how things work first before throwing in namespacing. The following steps create, setup, and start a TurboGears project:
Create the project:
[tmp] $ paster quickstart Helloworld Enter package name [helloworld]: Do you need authentication and authorization in this project? [yes] Selected and implied templates: tg.devtools#turbogears2 TurboGears 2.0 Standard Quickstart Template Variables: auth: sqlalchemy egg: Helloworld geo: None package: helloworld project: Helloworld sqlalchemy: True sqlobject: False tgversion: 2.0 Creating template turbogears2 Creating directory ./Helloworld <output snipped>
As you can see, the 'paster' utility easily created us a brand new TurboGears 2 project. To get this going, we just need to setup the app, and then start it up. Using paster once again, we issue the 'setup-app' command to do any initial setup like creating databases, and what-not.
[Helloworld] $ paster setup-app development.ini Running setup_config() from helloworld.websetup <output snipped>
I snipped out all the output, but essentially anything in ./helloworld/websetup.py gets run to setup databases and such. Remember that line there that says "Running setup_config() from helloworld.websetup" as that will be important later in the article. Finally, we run our development web server and test out our application:
[Helloworld] $ paster serve --reload development.ini Starting subprocess with file monitor Starting server in PID 18816. serving on http://127.0.0.1:8080 ...
And obviously, you can hit your web app by directing your web browser to 'http://127.0.0.1:8080'.
So What The Problem Is
Now that we've seen how easy it is to setup a basic TG2 application, lets now look at another example that is a little bit more complicated.
Lets say that our 'Helloworld' application is actually just a small part of a larger application we will call 'SampleApplications'. And as such, we want to have a shared namespace so that I can do things like:
>>> from sample_apps.web.helloworld import HelloWorld >>> from sample_apps.web.goodbyeworld import GoodbyeWorld >>> ....
You can see what we mean now by a shared namespace. All applications are installed as part of 'sample_apps' and are called via that library path 'sample_apps.namespace.subapp_name'.
Lets see what happens when we try to create a TurboGears project as a 'sub application' of another larger application.
Create The Parent Application
[tmp] $ paster create sample_apps [tmp] $ cd sample_apps
Here we just need to setup a namespace, which for our Helloworld app we will call 'web'. So I'm going to add the following to setup.py:
entry_points=""" # -*- Entry points: -*- """, namespace_packages=['sample_apps', 'sample_apps.web'], )
Then we just create the namespace dir and add a '__init__.py' file in it:
[sample_apps] $ mkdir sample_apps/web [sample_apps] $ touch sample_apps/web/__init__.py
Note that every namespace package must have the following in their '__init__.py' files:
__import__('pkg_resources').declare_namespace(__name__)
So for this example we need to add the above line to the files:
./sample_apps/__init__.py ./sample_apps/web/__init__.py
And now that we have our namespace setup, we need to install our application. Since we are only developing, I'll just use the 'develop' install method.
[sample_apps] $ python setup.py develop
The TurboGears SubProject Within Namespace
Now that we have our global project 'sample_apps' setup, and the 'sample_apps.web' namespace, we want to create a TurboGears application under that namespace.
Note that we create the project outside of the sample_apps directory.
[tmp] $ paster quickstart sample_apps.web.helloworld [tmp] $ cd sample_apps.web.helloworld
We've created the project as 'sample_apps.helloworld' as a convenience, however we need to clean this up a little bit to make it look the way we want (as a sub project/namespace package of sample_apps).
[sample_apps.web.helloworld] $ mkdir -p sample_apps/web [sample_apps.web.helloworld] $ mv sample_apps.web.helloworld sample_apps/web/helloworld
Again, you need to add the following to all the __init__.py file in the namespace packages:
__import__('pkg_resources').declare_namespace(__name__)
For this example, that would be the following files:
sample_apps/__init__.py sample_apps/web/__init__.py sample_apps/web/helloworld/__init__.py
There are a few references to 'sample_apps.web.helloworld' in setup.py that need to be changed to 'sample_apps/web/helloworld'. Note that you only want to change the references to the directory, not the library path.
In setup.py change the following:
package_data={'sample_apps.web.helloworld' => package_data={'sample_apps/web/helloworld':
message_extractors={'sample_apps.web.helloworld': => message_extractors={'sample_apps/web/helloworld':Remove the .egg-info directory, and regenerate it:
[sample_apps.web.helloworld] $ rm -rf sample_apps.web.helloworld.egg-info/ [sample_apps.web.helloworld] $ python setup.py egg_info
Now, the final piece is a bit of a hack, however 'paster setup-app' will not work unless you modify the file 'top_level.txt' in your .egg-info directory. This will need to be changed anytime you need to run 'paster setup-app' after the .egg-info has be regenerated (python setup.py [install, develop, etc]). I simple change it from 'sample_apps' to 'sample_apps.web.helloworld':
[sample_apps.web.helloworld] $ echo "sample_apps.web.helloworld" > sample_apps.web.helloworld.egg-info/top_level.txt
You should now be able to run the following successfully:
[sample_apps.web.helloworld] $ paster setup-app development.ini [sample_apps.web.helloworld] $ paster serve --reload development.ini
Lets just see how that shared namespace works now in a python shell:
>>> from sample_apps.web.helloworld import controllers
And there you have it, a TurboGears projects installed as part of a shared namespace.
Reference
* SetupTools
* Turbo Gears
* Paste
RSS Feed