passing **settings info to unittest from nose - python

I'm running my unit tests using nose.
I have .ini files such as production.ini, development.ini, local.ini. Finally, I have a test.ini file which looks like:
[app:main]
use = config:local.ini
# Add additional test specific configuration options as necessary.
sqlalchemy.url = sqlite:///%(here)s/tests.db
In my test class I want to setup the database as I would in my app server code. Something like:
engine = engine_from_config(settings)
initialize_sql(engine)
dbfixture = SQLAlchemyFixture(
env=model,
engine=engine,
style=NamedDataStyle()
)
How does nose pass 'settings' to my test code?
I've been reading the following link for some guidance, but I haven't been able to connect all the dots. http://farmdev.com/projects/fixture/using-fixture-with-pylons.html
Thanks much!

You will need to parse the settings from the INI file yourself. Pylons used to do this automatically for you by just hard-coding a load for "test.ini". The two options you have are 1) just load the INI settings via settings = paste.deploy.appconfig('test.ini') or 2) loading the actual WSGI app yourself, like if you wanted to use it via WebTest app = pyramid.paster.get_app('test.ini') which would parse the INI file and return an actual WSGI app. Unfortunately that route doesn't give you access to the INI file directly, it automatically just passes the settings to your app's startup function main(global_conf, **settings).
You may also find the Pyramid docs on functional tests useful.

Related

How to use different .env files with python-decouple

I am working on a django project that I need to run it with Docker. In this project I have multiples .env files: .env.dev, .env.prod, .env.staging. Is there a right way to manage all this file with the package python-decouple? I've search for a workaround to deal with this challenge and do not find any kind of answer, not even on the official documentation.
Can I use something like:
# dont works that way, it's just a dummie example
python manage.py runserver --env-file=.env.prod
or maybe any way to setting or override the file I need to use?
Instead of importing decouple.config and doing the usual config('SOME_ENV_VAR'), create a new decouple.Config object using RepositoryEnv('/path/to/.env.prod').
from decouple import Config, RepositoryEnv
DOTENV_FILE = '/home/user/my-project/.env.prod'
env_config = Config(RepositoryEnv(DOTENV_FILE))
# use the Config().get() method as you normally would since
# decouple.config uses that internally.
# i.e. config('SECRET_KEY') = env_config.get('SECRET_KEY')
SECRET_KEY = env_config.get('SECRET_KEY')

In the Pyramid web framework, how do I source sensitive settings into development.ini / production.ini from an external file?

I'd like to keep development.ini and production.ini under version control, but for security reason would not want the sqlalchemy.url connection string to be stored, as this would contain the username and password used for the database connection.
What's the canonical way, in Pyramid, of sourcing this setting from an additional external file?
Edit
In addition to solution using the environment variable, I came up with this solution after asking around on #pyramid:
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
# Read db password from config file outside of version control
secret_cfg = ConfigParser()
secret_cfg.read(settings['secrets'])
dbpass = secret_cfg.get("secrets", "dbpass")
settings['sqlalchemy.url'] = settings['connstr'] % (dbpass,)
I looked into this a lot and played with a lot of different approaches. However, Pyramid is so flexible, and the .ini config parser is so minimal in what it does for you, that there doesn't seem to be a de facto answer.
In my scenario, I tried having a production.example.ini in version control at first that got copied on the production server with the details filled in, but this got hairy, as updates to the example didn't get translated to the copy, and so the copy had to be re-created any time a change was made. Also, I started using Heroku, so files not in version control never made it into the deployment.
Then, there's the encrypted config approach. Which, I don't like the paradigm. Imagine a sysadmin being responsible for maintaining the production environment, but he or she is unable to change the location of a database or environment-specific setting without running it back through version control. It's really nice to have the separation between environment and code as much as possible so those changes can be made on the fly without version control revisions.
My ultimate solution was to have some values that looked like this:
[app:main]
sqlalchemy.url = ${SQLALCHEMY_URL}
Then, on the production server, I would set the environment variable SQLALCHEMY_URL to point to the database. This even allowed me to use the same configuration file for staging and production, which is nice.
In my Pyramid init, I just expanded the environment variable value using os.path.expandvars:
sqlalchemy_url = os.path.expandvars(settings.get('sqlalchemy.url'))
engine = create_engine(sqlalchemy_url)
And, if you want to get fancy with it and automatically replace all the environment variables in your settings dictionary, I made this little helper method for my projects:
def expandvars_dict(settings):
"""Expands all environment variables in a settings dictionary."""
return dict((key, os.path.expandvars(value)) for
key, value in settings.iteritems())
Use it like this in your main app entry point:
settings = expandvars_dict(settings)
The whole point of the separate ini files in Pyramid is that you do not have to version control all of them and that they can contain different settings for different scenarios (development/production/testing). Your production.ini almost always should not be in the same VCS as your source code.
I found this way for loading secrets from a extra configuration and from the env.
from pyramid.config import Configurator
from paste.deploy import appconfig
from os import path
__all__ = [ "main" ]
def _load_secrets(global_config, settings):
""" Helper to load secrets from a secrets config and
from env (in that order).
"""
if "drawstack.secrets" in settings:
secrets_config = appconfig('config:' + settings["drawstack.secrets"],
relative_to=path.dirname(global_config['__file__']))
for k, v in secrets_config.items():
if k == "here" or k == "__file__":
continue
settings[k] = v
if "ENV_DB_URL" in global_config:
settings["sqlalchemy.url"] = global_config["ENV_DB_URL"]
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
_load_secrets(global_config, settings)
config = Configurator(settings=settings)
config.include('pyramid_jinja2')
config.include('.models')
config.include('.routes')
config.scan()
return config.make_wsgi_app()
The code above, will load any variables from the value of the config key drawstack.secrets and after that it tries to load DB_URL from the enviornment.
drawstack.secrets can be relative to the original config file OR absolute.

Pyramid: sending miscellaneous config to the application factory

I have setup the Pyramid configuration file like this:
[app:main]
...
[server:main]
...
[memcache]
# memcache config
[zeromq]
# zeromq xonfig
Now inside my app_factory, I want to get all the config -- not just the app:main section but also the memcache, zeromq and other possible sections.
How should I do this.
The reason I am separating the config into different sections is because my app uses lots of disparate services and I don't want to cobble all the config together into the app:main section.
One way to do this is to manually pull the config into the application by reading the config file. Then you have to somehow know what mode (development or production) the app is running. Also, there's the overhead of parsing the config multiple times (because the paste-deploy will do it first).
Is there a better and more elegant solution to this?
Unfortunately you'll have to parse the config file again if you want to take this approach. You can grab the file via config_file = global_config['__file__'] in your main and parse it yourself using the stdlib ConfigParser.
If you like this INI format with separate sections, the Mozilla Services has a nice little module you can use to handle all of this for you. It does value conversion (attempts to cast values to integers or booleans). It supports extending your config with other sections, such as [foo:bar] key = value, which when parsed will return you a simple dictionary containing settings['foo.bar.key'] == value. It will also conveniently parse the thing for you so your main can just look like:
def main(global_config, **settings):
config = get_configurator(global_config, **settings)
settings = config.registry.settings
# ... do your app configuration
return config.make_wsgi_app()
https://wiki.mozilla.org/index.php?title=Services/Sync/Server/GlobalConfFile
https://github.com/mozilla-services/mozservices/blob/master/mozsvc/config.py
You can pass application specific config into the ini files:
[app:xyz]
something = True
Then in your main function (application factory), you can access it as
settings['something']
More info:
http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html#adding-a-custom-setting

Running scripts within Pyramid framework (ie without a server)

I have a fair bit of experience with PHP frameworks and Python for scripting so am now taking the step to Pyramid.
I'd like to know what is the 'correct' way to run a script in Pyramid. That is, how should I set it up so that it is part of the application and has access to config and thus database but does not run through paster (or whatever WSGI).
As an example, say I have a web application which while a user is offline grabs Facebook updates through a web service. I want to write a script to poll that service and store in the database ready for next login.
How should I do this in terms of:
Adding variables in the ini file
Starting the script correctly
I understand the basics of Python modules and packages; however I don't fully understand Configurator/Paster/package setup, wherein I suspect the answer lies.
Thanks
Update:
Thanks, this seems along the lines of what I am looking for. I note that you have to follow a certain structure (eg have summary and parser attributes set) and that the function called command() will always be run. My test code now looks something like this:
class AwesomeCommand(Command):
max_args = 2
min_args = 2
usage = "NAME"
# These are required
summary = "Say hello!"
group_name = "My Package Name"
# Required:
parser = Command.standard_parser(verbose=True)
def command(self):
# Load the config file/section
config_file, section_name = self.args
# What next?
I'm now stuck as to how to get the settings themselves. For example, in init.py you can do this:
engine = engine_from_config(settings, 'sqlalchemy.')
What do I need to do to transform the config file into the settings?
EDIT: The (simpler) way to do this in Pylons is here:
Run Pylons controller as separate app?
As of Pyramid 1.1, this is handled by the framework:
http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/commandline.html#writing-a-script
paster starts an application given an ini file that describes that application. the "serve" command is a built in command for starting a wsgi application and serving it. BUT, you can write other commands.
from paste.script.command import Command
class AwesomeCommand(Command):
def command(self):
print "the awesome thing it does"
and then register them as entry points in your setup.py.
setup(...
entry_points="""
[paste.app_factory]
.....
[paste.global_paster_command]
myawesome-command = mypackage.path.to.command:AwesomeCommand """)
pyramid adds it's own commands this way like the pshell command.
After going to the pylons discuss list, I came up with an answer. Hope this helps somebody:
#Bring in pyramid application--------------------------------------
import pyramid
from paste.deploy import appconfig
config_file = '/path_to_config_file/configname.ini'
name = 'app_name'
config_name = 'config:%s' % config_file
here_dir = os.getcwd()
conf = appconfig(config_name, name, relative_to=here_dir)
from main_package import main
app = main(conf.global_conf, **conf.local_conf)
#--------------------------------------------------------------------------
you need to make view for that action and then run it using:
paster request development.ini /url_to_your_view

CherryPy combine file and dictionary based configuration

I'm setting up a CherryPy application and would like to have the majority of my configuration settings in a .conf file like this:
[global]
server.socketPort = 8080
server.threadPool = 10
server.environment = "production"
However I would also like to setup a few with a dictionary in code like this:
conf = {'/': {'tools.staticdir.on': True,
'tools.staticdir.dir': os.path.join(current_dir, 'templates')}}
cherrypy.quickstart(HelloWorld(), config=conf)
Is it possible to combine both configs into one and then pass it into the config quickstart option?
quickstart is for quick sites. If you're doing anything as complex as having multiple configs, it's time to graduate. Look at the source code for the quickstart function (it's not scary!): you're going to unpack that into your startup script. So instead of quickstart, write this:
cherrypy.config.update(conffile)
cherrypy.config.update(confdict)
app = cherrypy.tree.mount(HelloWorld(), '/', conffile)
app.merge(confdict)
if hasattr(cherrypy.engine, "signal_handler"):
cherrypy.engine.signal_handler.subscribe()
if hasattr(cherrypy.engine, "console_control_handler"):
cherrypy.engine.console_control_handler.subscribe()
cherrypy.engine.start()
cherrypy.engine.block()
We've essentially added two lines to the quickstart code. First, we have an extra call to config.update; that merges the config dict into the global config. Second, app.merge(confdict); that's for merging multiple configs into each app.
It's perfectly OK to do these in the opposite order if you want the file config to override the dict. It's also OK to stick the dict-based config in HelloWorld._cp_config as described in the docs.
Those are two different configurations. Cherrypy has two configurations: One is the global config and the other is application config. You can use both normally:
cherrypy.config.update('my_file.ini')
cherrypy.quickstart(HelloWorld(), config=conf)
Please note that your example config file is wrong -- instead of server.socketPort it should be server.socket_port and instead of server.threadPool it should be server.threadpool. Check config docs for more information.

Categories