A bunch of cleanup, and the removal of a few features:

* Consolidating a considerable amount of duplicate (for loading a Pecan app and its environment) into `pecan.core.load_app`.
* Removing support for module-based configurations.
* Wrote a simple utility for loading a test environment, `pecan.testing.load_test_app`.
This commit is contained in:
Ryan Petrello
2012-03-06 23:21:28 -05:00
parent ca8f767de1
commit 054e1c4828
9 changed files with 69 additions and 139 deletions

View File

@@ -9,7 +9,7 @@ from paste.urlparser import StaticURLParser
from weberror.errormiddleware import ErrorMiddleware
from weberror.evalexception import EvalException
from core import abort, error_for, override_template, Pecan, redirect, render, request, response, ValidationException
from core import abort, error_for, override_template, Pecan, load_app, redirect, render, request, response, ValidationException
from decorators import expose
from hooks import RequestViewerHook
from templating import error_formatters
@@ -18,7 +18,7 @@ from configuration import set_config
from configuration import _runtime_conf as conf
__all__ = [
'make_app', 'Pecan', 'request', 'response', 'override_template', 'expose', 'conf', 'set_config'
'make_app', 'load_app', 'Pecan', 'request', 'response', 'override_template', 'expose', 'conf', 'set_config'
]
def make_app(root, static_root=None, debug=False, errorcfg={}, wrap_app=None, logging=False, **kw):

View File

@@ -1,6 +1,7 @@
"""
PasteScript base command for Pecan.
"""
from pecan import load_app
from pecan.configuration import _runtime_conf, set_config
from paste.script import command as paste_command
@@ -31,37 +32,18 @@ class Command(paste_command.Command):
except paste_command.BadCommand, ex:
ex.args[0] = self.parser.error(ex.args[0])
raise
def get_package_names(self, config):
if not hasattr(config.app, 'modules'):
return []
return config.app.modules
def import_module(self, package, name):
parent = __import__(package, fromlist=[name])
return getattr(parent, name, None)
def load_configuration(self, name):
set_config(name)
return _runtime_conf
def load_app(self, config):
for package_name in self.get_package_names(config):
module = self.import_module(package_name, 'app')
if hasattr(module, 'setup_app'):
return module.setup_app(config)
raise paste_command.BadCommand('No app.setup_app found in any app modules')
def load_model(self, config):
for package_name in self.get_package_names(config):
module = self.import_module(package_name, 'model')
if module:
return module
return None
def load_app(self):
return load_app(self.validate_file(self.args))
def logging_file_config(self, config_file):
if os.path.splitext(config_file)[1].lower() == '.ini':
paste_command.Command.logging_file_config(self, config_file)
def validate_file(self, argv):
if not argv or not os.path.isfile(argv[0]):
raise paste_command.BadCommand('This command needs a valid config file.')
return argv[0]
def command(self):
pass

View File

@@ -45,25 +45,11 @@ class ServeCommand(_ServeCommand, Command):
setattr(self.options, 'server', None)
setattr(self.options, 'server_name', None)
config_file = self.validate_file(self.args)
# for file-watching to work, we need a filename, not a module
if self.requires_config_file and self.args:
self.config = self.load_configuration(config_file)
self.args[0] = self.config.__file__
if self.options.reload is None:
self.options.reload = getattr(self.config.app, 'reload', False)
# run the base command
_ServeCommand.command(self)
def loadserver(self, server_spec, name, relative_to, **kw):
return (lambda app: httpserver.serve(app, self.config.server.host, self.config.server.port))
return (lambda app: httpserver.serve(app, app.config.server.host, app.config.server.port))
def loadapp(self, app_spec, name, relative_to, **kw):
return self.load_app(self.config)
def validate_file(self, argv):
if not argv or not os.path.isfile(argv[0]):
raise paste_command.BadCommand('This command needs a valid config file.')
return argv[0]
return self.load_app()

View File

@@ -24,9 +24,7 @@ class ShellCommand(Command):
def command(self):
# load the application
config = self.load_configuration(self.args[0])
setattr(config.app, 'reload', False)
app = self.load_app(config)
app = self.load_app()
# prepare the locals
locs = dict(__name__='pecan-admin')
@@ -34,7 +32,7 @@ class ShellCommand(Command):
locs['app'] = TestApp(app)
# find the model for the app
model = self.load_model(config)
model = getattr(app, 'model', None)
if model:
locs['model'] = model

View File

@@ -5,6 +5,27 @@ import os
IDENTIFIER = re.compile(r'[a-z_](\w)*$', re.IGNORECASE)
DEFAULT = {
# Server Specific Configurations
'server' : {
'port' : '8080',
'host' : '0.0.0.0'
},
# Pecan Application Configurations
'app' : {
'root' : None,
'modules' : [],
'static_root' : 'public',
'template_path' : '',
'debug' : False,
'force_canonical' : True,
'errors' : {
'__force_dict__' : True
}
}
}
class ConfigDict(dict):
pass
@@ -80,15 +101,6 @@ class Config(object):
conf_obj = dict(self)
return self.__dictify__(conf_obj, prefix)
def update_with_module(self, module):
'''
Updates this configuration with a module.
:param module: The module to update this configuration with. Either a string or the module itself.
'''
self.update(conf_from_module(module))
def __getattr__(self, name):
try:
return self.__values__[name]
@@ -123,20 +135,6 @@ class Config(object):
def __repr__(self):
return 'Config(%s)' % str(self.__values__)
def conf_from_module(module):
'''
Creates a configuration dictionary from a module.
:param module: The module, either as a string or the module itself.
'''
if isinstance(module, str):
module = import_module(module)
module_dict = dict(inspect.getmembers(module))
return conf_from_dict(module_dict)
def conf_from_file(filepath):
'''
@@ -173,48 +171,23 @@ def conf_from_dict(conf_dict):
return conf
def import_module(conf):
'''
Imports the a configuration as a module.
:param conf: The string to the configuration. Automatically strips off ".py" file extensions.
'''
if '.' in conf:
parts = conf.split('.')
name = '.'.join(parts[:-1])
fromlist = parts[-1:]
try:
module = __import__(name, fromlist=fromlist)
conf_mod = getattr(module, parts[-1])
except AttributeError, e:
raise ImportError('No module named %s' % conf)
else:
conf_mod = __import__(conf)
return conf_mod
def initconf():
'''
Initializes the default configuration and exposes it at ``pecan.configuration.conf``,
which is also exposed at ``pecan.conf``.
'''
import default_config
conf = conf_from_module(default_config)
return conf
return conf_from_dict(DEFAULT)
def set_config(name):
def set_config(name, overwrite=False):
'''
Updates the global configuration a filename.
:param name: filename, as a string.
'''
_runtime_conf.update(conf_from_file(name))
conf = conf_from_file(name)
_runtime_conf.update(conf)
_runtime_conf = initconf()

View File

@@ -1,3 +1,4 @@
from configuration import _runtime_conf, set_config
from templating import RendererFactory
from routing import lookup_controller, NonCanonicalPath
from util import _cfg, splitext, encode_if_needed
@@ -171,6 +172,26 @@ class ValidationException(ForwardRequestException):
ForwardRequestException.__init__(self, location)
def load_app(config):
'''
Used to load a ``Pecan`` application and its environment based on passed
configuration.
:param config: Can be a dictionary containing configuration, or a string which
represents a (relative) configuration filename.
:returns a pecan.Pecan object
'''
set_config(config, overwrite=True)
for package_name in getattr(_runtime_conf.app, 'modules', []):
module = __import__(package_name, fromlist=['app'])
if hasattr(module, 'app') and hasattr(module.app, 'setup_app'):
app = module.app.setup_app(_runtime_conf)
app.config = _runtime_conf
return app
raise RuntimeError('No app.setup_app found in any of the configured app.modules')
class Pecan(object):
'''
Base Pecan application object. Generally created using ``pecan.make_app``,

View File

@@ -1,25 +0,0 @@
# Server Specific Configurations
server = {
'port' : '8080',
'host' : '0.0.0.0'
}
# Pecan Application Configurations
app = {
'root' : None,
'modules' : [],
'static_root' : 'public',
'template_path' : '',
'debug' : False,
'force_canonical' : True,
'errors' : {
'__force_dict__' : True
}
}
# Custom Configurations must be in Python dictionary format in user config
#
# foo = {'bar':'baz'}
#
# All configurations are accessible at::
# pecan.conf

View File

@@ -1,14 +1,4 @@
from configuration import set_config, import_module, _runtime_conf as conf
def deploy(config_module_or_path):
set_config(config_module_or_path)
for module in getattr(conf.app, 'modules'):
try:
module_app = import_module('%s.app' % module)
if hasattr(module_app, 'setup_app'):
return module_app.setup_app(conf)
except ImportError:
continue
raise Exception, 'No app.setup_app found in any of the configured app.modules'
from core import load_app
def deploy(config):
return load_app(config)

5
pecan/testing.py Normal file
View File

@@ -0,0 +1,5 @@
from pecan import load_app
from webtest import TestApp
def load_test_app(config):
return TestApp(load_app(config))