Question

I have a Pyramid web application managed with zc.buildout. In it, I need to read a file on disk, which is located in a sub-directory of buildout directory.

The problem is with determining the path to the file - I do not want to hard-code the absolute path and just providing a relative path does not work when serving the app in production (supposedly because the working directory is different).

So the promising "hooks" I am thinking about are:

  • the "root" buildout directory, which I can address in buildout.cfg as ${buildout:directory} - however, I can't figure out how can I "export" it so it can be accessed by the Python code

  • the location of the Paster's .ini file which starts the app

Was it helpful?

Solution 2

If the path to the file relative to the buildout root or location of paster.ini is always the same, which it seems it is from your question, you could set it in paster.ini:

[app:main]
...
config_file = %(here)s/path/to/file.txt

Then access it from the registry as in Reinout's answer:

def your_view(request):
    config_file = request.registry.settings['config_file']

OTHER TIPS

Like @MartijnPieters suggests in a comment on your own answer, I'd use collective.recipe.template to generate an entry in the .ini. I wondered myself how I could then access that data in my project, so I worked it out :-)

Let's work our way backwards to what you need. First in your view code where you want the buildout directory:

def your_view(request):
    buildout_dir = request.registry.settings['buildout_dir']
    ....

request.registry.settings (see documentation) is a "dictonary-like deployment settings object". See deployment settings, that's the **settings that gets passed into your main method like def main(global_config, **settings)

Those settings are what's in the [app:main] part of your deployment.ini or production.ini file. So add the buildout directory there:

[app:main]
use = egg:your_app

buildout_dir = /home/you/wherever/it/is

pyramid.reload_templates = true
pyramid.debug_authorization = false
...

But, and this is the last step, you don't want to have that hardcoded path in there. So generate the .ini with a template. The template development.ini.in uses a ${partname:variable} expansion language. in your case you need${buildout:directory}:

[app:main]
use = egg:your_app

buildout_dir = ${buildout:dir}
#              ^^^^^^^^^^^^^^^

pyramid.reload_templates = true
pyramid.debug_authorization = false
...

Add a buildout part in buildout.cfg to generate development.ini from development.ini.in:

[buildout]
...
parts =
    ...
    inifile
    ...

[inifile]
recipe = collective.recipe.template
input = ${buildout:directory}/development.ini.in
output = ${buildout:directory}/development.ini

Note that you can do all sorts of cool stuff with collective.recipe.template. ${serverconfig:portnumber} to generate a matching port number in your production.ini and in your your_site_name.nginx.conf, for instance. Have fun!

Here's a rather clumsy solution I've devised:

In buildout.cfg I used extra-paths option of zc.recipe.egg to add the buildout directory to sys.path:

....
[webserver]
recipe = zc.recipe.egg:scripts
eggs = ${buildout:eggs}
extra-paths = ${buildout:directory}

then I put a file called app_config.py into the buildout directory:

# This remembers the root of the installation (similar to {buildout:directory}
# so we can import it and use where we need access to the filesystem.
# Note: we could use os.getcwd() for that but it feels kinda wonky
# This is not directly related to Celery, we may want to move it somewhere
import os.path
INSTALLATION_ROOT = os.path.dirname(__file__)

Now we can import it in our Python code:

from app_config import INSTALLATION_ROOT
filename = os.path.join(INSTALLATION_ROOT, "somefile.ext")
do_stuff_with_file(filename)

If anyone knows a nicer solution you're welcome :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top