Extending my application - Pyramid/Pylons/Python - python

Simple question about extending my application
Lets say I have a "Main Application", and in this application I have the following in the _init_.py file:
config.add_route('image_upload', '/admin/image_upload/',
view='mainapp.views.uploader',
view_renderer='/site/upload.mako')
and in the views.py I have:
def uploader(request):
# some code goes here
return {'xyz':xyz}
Now when I create a new application, and I want to extend it, to use the above view and route:
In the new application _init_.py file I would manually copy over the config.add_route code:
config.add_route( 'image_upload', '/admin/image_upload/',
view='mainapp.views.uploader',
view_renderer='mainapp:templates/site/upload.mako'
)
And is that all I would need to do? From this would my application be able to use the view and template from the main application, or is am I missing something else?
Thanks for reading!

You don't have to copy your code to do this. Use the Configurator.include method to include your "Main Application" configuration in your new application. The documentation explains this pretty well both here and here, but the essentially, if you declare your main apps configuration inside a callable:
def main_app_config(config):
config.add_route('image_upload', '/admin/image_upload/',
view='mainapp.views.uploader',
view_renderer='/site/upload.mako')
Then you can include your main app in your new app's configuration like this:
from my.main.app import main_app_config
# do your new application Configurator setup, etc.
# then "include" it.
config.include(main_app_config)
# continue on with your new app configuration

Related

How does _init_ file in Flask project work when starting the server?

I'm reading the Flask tutorial and learned that in large application structure the code is moved from single app.py file to 'application factory function' create_app in _init_.py file, then the server can be started like this:
export FLASK_APP=flaskr
export FLASK_ENV=development
flask run
I wonder how does the last command work behind the scene, because there's no explicit call to this factory function anywhere in the code hence no app returned?
Another Flask doc tries to explain why such project structure is used but in that case code in _init_.py creates app explicitly:
from flask import Flask
app = Flask(__name__)
import yourapplication.views
python doc says:
The init.py files are required to make Python treat the
directories as containing packages; this is done to prevent
directories with a common name, such as string, from unintentionally
hiding valid modules that occur later (deeper) on the module search
path. In the simplest case, init.py can just be an empty file, but
it can also execute initialization code for the package or set the
all variable, described later.
The last sentence mentions about 'initialization code' which can be applied in the second case(and simple project structure with app.py file) but I failed to apply it to the first case.
In the flask source code, they check if a function called create_app exists.
# Search for app factory functions.
for attr_name in ('create_app', 'make_app'):
app_factory = getattr(module, attr_name, None)
if inspect.isfunction(app_factory):
try:
app = call_factory(script_info, app_factory)
if isinstance(app, Flask):
return
It evens says so in the documentation:
Flask will automatically detect the factory (create_app or make_app) in myapp.

How and when to initialise configuration in Python?

I'm getting pretty confused as to how and where to initialise application configuration in Python 3.
I have configuration that consists of application specific config (db connection strings, url endpoints etc.) and logging configuration.
Before my application performs its intended function I want to initialise the application and logging config.
After a few different attempts, I eventually ended up with something like the code below in my main entry module. It has the nice effect of all imports being grouped at the top of the file (https://www.python.org/dev/peps/pep-0008/#imports), but it doesn't feel right since the config modules are being imported for side effects alone which is pretty non-intuitive.
import config.app_config # sets up the app config
import config.logging_config # sets up the logging config
...
if __name__ == "__main__":
...
config.app_config looks something like follows:
_config = {
'DB_URL': None
}
_config['DB_URL'] = _get_db_url()
def db_url():
return _config['DB_URL']
def _get_db_url():
#somehow get the db url
and config.logging_config looks like:
if not os.path.isdir('.\logs'):
os.makedirs('.\logs')
if os.path.exists('logging_config.json'):
with open(path, 'rt') as f:
config = json.load(f)
logging.config.dictConfig(config)
else:
logging.basicConfig(level=log_level)
What is the common way to set up application configuration in Python? Bearing in mind that I will have multiple applications each using the config.app_config and config.logging_config module, but with different connection string possibly read from a file
I ended up with a cut down version of the Django approach: https://github.com/django/django/blob/master/django/conf/init.py
It seems pretty elegant and has the nice benefit of working regardless of which module imports settings first.

Django: where do I call settings.configure?

The Django docs say that I can call settings.configure instead of having a DJANGO_SETTINGS_MODULE. I would like my website's project to do this. In what file should I put the call to settings.configure so that my settings will get configured at the right time?
Edit in response to Daniel Roseman's comment:
The reason I want to do this is that settings.configure lets you pass in the settings variables as a kwargs dict, e.g. {'INSTALLED_APPS': ..., 'TEMPLATE_DIRS': ..., ...}. This would allow my app's users to specify their settings in a dict, then pass that dict to a function in my app that augments it with certain settings necessary to make my app work, e.g. adding entries to INSTALLED_APPS.
What I envision looks like this. Let's call my app "rexe_app". In wsgi.py, my app's users would do:
import rexe_app
my_settings = {'INSTALLED_APPS': ('a','b'), ...}
updated_settings = rexe_app.augment_settings(my_settings)
# now updated_settings is {'INSTALLED_APPS': ('a','b','c'), 'SESSION_SAVE_EVERY_REQUEST': True, ...}
settings.configure(**updated_settings)
Its not quite equivalent, since settings DJANGO_SETTINGS_MODULE updates defaults, found in django.conf.settings.global_settings, while parameter for configure completely ignores them, so you have to add some additional processing.
Beware, first, that you can't modify INSTALLED_APPS on the fly, as they are examined once on settings processing. For example, to apply modifications to INSTALLED_APPS in Apache-deployed application, you need to restart Apache.
Second, as settings are imported, therefore this point is prone to injections of some sorts, and is highly vulnerable to expose them to users.
If this is a meta app, there are two possibilities:
If you want to provide default settings for `django`-wise setting, add following to `default_settings.py` in your app:
INSTALLED_APPS = ('default_app', )
Just make sure you don't import `default_settings.py` in your app, and make your users add to their settings.py
from rexe_app.default_settings import *
INSTALLED_APPS += ('users_app', )
that effectively will set `INSTALLED_APPS` to `('default_app', 'users_app', )` for your end users.
In case you need `rexe_app`-wise settings, you can default them in your app's `__init__.py`:
from django.conf import settings
REXE_APP_CONFIG_PARAM = getattr(settings, 'REXE_APP_CONFIG_PARAM',
'default_param_value')
so when user needs to change default `REXE_APP_CONFIG_PARAM` he needs to just add
INSTALLED_APPS = ('rexe_app')
REXE_APP_CONFIG_PARAM = 'user_param_value'
to his `settings.py`.
It would be in your wsgi script. please have look at the docs.
You could run mysite.settings.configure() instead of os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
If you are using wsgi then call that in your wsgi script before running the app.
If you want to do this for dev server, pass it as command line option (mentioned on the same page).
django-admin.py runserver --settings=mysite.settings

Create pyramid request for testing, so that events are triggered

I would like to test a pyramid view like the following one:
def index(request):
data = request.some_custom_property.do_something()
return {'some':data}
some_custom_property is added to the request via such an event handler:
#subscriber(NewRequest)
def prepare_event(event):
event.request.set_property(
create_some_custom_property,
'some_custom_property',reify=True
)
My problem is: If I create a test request manually, the event is not setup correctly, because no events are triggered. Because the real event handler is more complicated and depends on configuration settings, I don't want to reproduce that code in my test code. I would like to use the pyramid infracstructure as much as possible. I learned from an earlier question how to set up a real pyramid app from an ini file:
from webtest import TestApp
from pyramid.paster import get_app
app = get_app('testing.ini#main')
test_app = TestApp(app)
The test_app works fine, but I can only get back the html output (which is the idea of TestApp). What I want to do is, to execute index in the context of app or test_app, but to get back the result of index before it's send to a renderer.
Any hint how to do that?
First of all, I believe this is a really bad idea to write doctests like this. Since it requires a lot of initialization work, which is going to be included in documentation (remember doctests) and will not "document" anything. And, to me, these tests seems to be the job for unit/integration test. But if you really want, here's a way to do it:
import myapp
from pyramid.paster import get_appsettings
from webtest import TestApp
app, conf = myapp.init(get_appsettings('settings.ini#appsection'))
rend = conf.testing_add_renderer('template.pt')
test_app = TestApp(app)
resp = test_app.get('/my/view/url')
rend.assert_(key='val')
where myapp.init is a function that does the same work as your application initialization function, which is called by pserve (like main function here. Except myapp.init takes 1 argument, which is settings dictionary (instead of main(global_config, **settings)). And returns app (i.e. conf.make_wsgi_app()) and conf (i.e pyramid.config.Configurator instance). rend is a pyramid.testing.DummyTemplateRenderer instance.
P.S. Sorry for my English, I hope you'll be able to understand my answer.
UPD. Forgot to mention that rend has _received property, which is the value passed to renderer, though I would not recommend to use it, since it is not in public interface.

Getting config variables outside of the application context

I've extracted several of my sqlalchemy models to a separate and installable package (../lib/site-packages), to use across several applications. So I only need to:
from models_package import MyModel
from any application needing access to these models.
Everything is ok so far, except I cannot find a satisfactory way of getting several application dependent config variables used by some of the models, which may vary from application to application. So some model need to be aware of some variables, where previously I've used the application they were in.
Neither
current_app.config['XYZ']
or
config = LocalProxy(lambda: current_app.config['XYZ'])
have worked (outside of application context errors) so I'm stuck right now. Maybe this is poor programming and/or design on my behalf, so how do clear this up? There must be some way, but I haven't reasoned myself toward it yet.
SOLUTION:
Avoiding setting items that would occur on module load (like a constant containing an api key), both of the above should work, and they do. Anything not using those in the context of model-in-the-application use will of course error, methods returning the values you need should be good.
If you are using a configuration pattern utilising classes and inheritance as described here, you could simply import your config classes with their respective properties and access them anywhere you want:
class Config(object):
IMPORT = 'ME'
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite:///:memory:'
class ProductionConfig(Config):
DATABASE_URI = 'mysql://user#localhost/foo'
class DevelopmentConfig(Config):
DEBUG = True
class TestingConfig(Config):
TESTING = True
Now, in your foo.py:
from config import Config
print(Config.IMPORT) # prints 'ME'
well, since current_app can be a proxy of your flask program when the blueprint is registered, and that is done at run-time, you can't use it in your models_package modules.
(app tries to import models_package, and models_package requires app's configs to initalize things- thus import fails)
one option would be doing circular imports :
assuming everything is in 'App' module:
__init__.py
import flask
application = flask.Flask(__name__)
application.config = #load configs
import models_package
models_package.py
from App import application
config = application.config
or create your own config object, but that somehow doubles complexity:
models_package.py
import flask
config = flask.config.Config(defaults=flask.Flask.default_config)
#pick one of those and apply the same config initialization as you do in
#your __init__.py
config.from_pyfile(..) #or
config.from_object(..) #or
config.from_envvar(..)

Categories

Resources