move to Pylons 0.9.7 and related packages

This commit is contained in:
Jan Dittberner 2009-03-08 23:52:49 +01:00
parent 787f6050d4
commit 30b74165c6
28 changed files with 199 additions and 173 deletions

3
.gitignore vendored
View file

@ -1,4 +1,5 @@
*~
*.pyc
data
pyalchemybiz.db
development.db
pyalchemybiz/public/js/jquery.js

View file

@ -1,2 +1,3 @@
include pyalchemybiz/config/deployment.ini_tmpl
recursive-include pyalchemybiz/public *
recursive-include pyalchemybiz/templates *

View file

@ -12,12 +12,14 @@ error_email_from = paste@localhost
[server:main]
use = egg:Paste#http
host = 0.0.0.0
host = 127.0.0.1
port = 5000
[app:main]
use = egg:pyalchemybiz
full_stack = true
static_files = true
cache_dir = %(here)s/data
beaker.session.key = pyalchemybiz
beaker.session.secret = somesecret
@ -28,26 +30,24 @@ beaker.session.secret = somesecret
#beaker.cache.data_dir = %(here)s/data/cache
#beaker.session.data_dir = %(here)s/data/sessions
# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
# Debug mode will enable the interactive debugging tool, allowing ANYONE to
# execute malicious code after an exception is raised.
#set debug = false
# Specify the database for SQLAlchemy to use.
# %(here)s may include a ':' character on Windows environments; this can
# invalidate the URI when specifying a SQLite db via path name
sqlalchemy.default.url = sqlite:///%(here)s/pyalchemybiz.db
sqlalchemy.default.echo = true
# SQLAlchemy database URL
sqlalchemy.url = sqlite:///%(here)s/development.db
sqlalchemy.echo = true
sqlalchemy.convert_unicode = true
# settings for sqlalchemy-migrate
migrate.repo.version = 2
migrate.repo.dir = %(here)s/data/dbrepo
# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
# Debug mode will enable the interactive debugging tool, allowing ANYONE to
# execute malicious code after an exception is raised.
#set debug = false
# Logging configuration
[loggers]
keys = root, pyalchemybiz
keys = root, routes, pyalchemybiz, sqlalchemy
[handlers]
keys = console
@ -59,11 +59,25 @@ keys = generic
level = INFO
handlers = console
[logger_routes]
level = INFO
handlers =
qualname = routes.middleware
# "level = DEBUG" logs the route matched and routing variables.
[logger_pyalchemybiz]
level = DEBUG
handlers =
qualname = pyalchemybiz
[logger_sqlalchemy]
level = INFO
handlers =
qualname = sqlalchemy.engine
# "level = INFO" logs SQL queries.
# "level = DEBUG" logs SQL queries and results.
# "level = WARN" logs neither. (Recommended for production systems.)
[handler_console]
class = StreamHandler
args = (sys.stderr,)

View file

@ -8,5 +8,12 @@ You can generate your documentation in HTML format by running this command::
setup.py pudge
For this to work you will need to download and install ``buildutils`` and
``pudge``.
For this to work you will need to download and install `buildutils`_,
`pudge`_, and `pygments`_. The ``pudge`` command is disabled by
default; to ativate it in your project, run::
setup.py addcommand -p buildutils.pudge_command
.. _buildutils: http://pypi.python.org/pypi/buildutils
.. _pudge: http://pudge.lesscode.org/
.. _pygments: http://pygments.org/

View file

@ -1,6 +1,6 @@
Metadata-Version: 1.0
Name: pyalchemybiz
Version: 0.1dev-r6
Version: 0.1dev
Summary: python based small business suite.
Home-page: http://www.dittberner.info/projects/pyalchemybiz
Author: Jan Dittberner

View file

@ -18,11 +18,12 @@ pyalchemybiz.egg-info/PKG-INFO
pyalchemybiz.egg-info/SOURCES.txt
pyalchemybiz.egg-info/dependency_links.txt
pyalchemybiz.egg-info/entry_points.txt
pyalchemybiz.egg-info/paste_deploy_config.ini_tmpl
pyalchemybiz.egg-info/not-zip-safe
pyalchemybiz.egg-info/paster_plugins.txt
pyalchemybiz.egg-info/requires.txt
pyalchemybiz.egg-info/top_level.txt
pyalchemybiz/config/__init__.py
pyalchemybiz/config/deployment.ini_tmpl
pyalchemybiz/config/environment.py
pyalchemybiz/config/middleware.py
pyalchemybiz/config/routing.py
@ -40,9 +41,13 @@ pyalchemybiz/model/customer.py
pyalchemybiz/model/meta.py
pyalchemybiz/model/person.py
pyalchemybiz/model/product.py
pyalchemybiz/templates/base.mako
pyalchemybiz/templates/customer.mako
pyalchemybiz/templates/index.mako
pyalchemybiz/templates/base/base.mako
pyalchemybiz/templates/base/customer.mako
pyalchemybiz/templates/derived/customer/edit.mako
pyalchemybiz/templates/derived/customer/list.mako
pyalchemybiz/templates/derived/customer/new.mako
pyalchemybiz/templates/derived/customer/view.mako
pyalchemybiz/templates/derived/index/index.mako
pyalchemybiz/tests/__init__.py
pyalchemybiz/tests/test_models.py
pyalchemybiz/tests/functional/__init__.py

View file

@ -0,0 +1 @@

View file

@ -1,2 +1,2 @@
PasteScript
Pylons
WebHelpers

View file

@ -1,3 +1,5 @@
Pylons>=0.9.6.2
SQLAlchemy>=0.4.7
sqlalchemy-migrate>=0.4.5
Pylons>=0.9.7
SQLAlchemy>=0.5.2,<0.6
sqlalchemy-migrate>=0.5.2,<0.6
Mako
FormBuild>=2.0,<3

View file

@ -17,6 +17,8 @@ port = 5000
[app:main]
use = egg:pyalchemybiz
full_stack = true
static_files = true
cache_dir = %(here)s/data
beaker.session.key = pyalchemybiz
beaker.session.secret = ${app_instance_secret}
@ -28,22 +30,14 @@ app_instance_uuid = ${app_instance_uuid}
#beaker.cache.data_dir = %(here)s/data/cache
#beaker.session.data_dir = %(here)s/data/sessions
# SQLAlchemy database URL
sqlalchemy.url = sqlite:///production.db
# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
# Debug mode will enable the interactive debugging tool, allowing ANYONE to
# execute malicious code after an exception is raised.
set debug = false
# Specify the database for SQLAlchemy to use.
# %(here)s may include a ':' character on Windows environments; this can
# invalidate the URI when specifying a SQLite db via path name
#sqlalchemy.default.url = sqlite:///%(here)s/pyalchemybiz.db
#sqlalchemy.default.echo = false
sqlalchemy.default.convert_unicode = true
# settings for sqlalchemy-migrate
migrate.repo.version = 2
migrate.repo.dir = %(here)s/data/dbrepo
# Logging configuration
[loggers]

View file

@ -1,9 +1,11 @@
"""Pylons environment configuration"""
import os
from mako.lookup import TemplateLookup
from pylons import config
from pylons.error import handle_mako_error
from sqlalchemy import engine_from_config
from sqlalchemy.exceptions import NoSuchTableError
from sqlalchemy.exc import NoSuchTableError
from migrate.versioning.api import db_version
from pyalchemybiz.model import init_model
@ -12,6 +14,7 @@ import pyalchemybiz.lib.app_globals as app_globals
import pyalchemybiz.lib.helpers
from pyalchemybiz.config.routing import make_map
from pyalchemybiz.model import init_model
def load_environment(global_conf, app_conf):
"""Configure the Pylons environment via the ``pylons.config``
@ -25,31 +28,36 @@ def load_environment(global_conf, app_conf):
templates=[os.path.join(root, 'templates')])
# Initialize config with the basic options
config.init_app(global_conf, app_conf, package='pyalchemybiz',
template_engine='mako', paths=paths)
config.init_app(global_conf, app_conf, package='pyalchemybiz', paths=paths)
config['routes.map'] = make_map()
config['pylons.g'] = app_globals.Globals()
config['pylons.app_globals'] = app_globals.Globals()
config['pylons.h'] = pyalchemybiz.lib.helpers
# Customize templating options via this variable
tmpl_options = config['buffet.template_options']
# Create the Mako TemplateLookup, with the default auto-escaping
config['pylons.app_globals'].mako_lookup = TemplateLookup(
directories=paths['templates'],
error_handler=handle_mako_error,
module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
input_encoding='utf-8', default_filters=['escape'],
imports=['from webhelpers.html import escape'])
# CONFIGURATION OPTIONS HERE (note: all config options will override
# any Pylons config options)
engine = \
engine_from_config(config, 'sqlalchemy.default.')
# Setup the SQLAlchemy database engine
engine = engine_from_config(config, 'sqlalchemy.')
try:
init_model(engine)
except Exception:
except NoSuchTableError:
# special handling for calls in websetup.py
import inspect
frame = inspect.currentframe()
try:
functions = [current[3] for current in \
inspect.getouterframes(frame)]
if functions[1] != 'setup_config':
if functions[1] != 'setup_app':
raise
finally:
del frame
# CONFIGURATION OPTIONS HERE (note: all config options will override
# any Pylons config options)

View file

@ -1,19 +1,17 @@
"""Pylons middleware initialization"""
from beaker.middleware import CacheMiddleware, SessionMiddleware
from paste.cascade import Cascade
from paste.registry import RegistryManager
from paste.urlparser import StaticURLParser
from paste.deploy.converters import asbool
from pylons import config
from pylons.error import error_template
from pylons.middleware import error_mapper, ErrorDocuments, ErrorHandler, \
StaticJavascripts
from pylons.middleware import ErrorHandler, StatusCodeRedirect
from pylons.wsgiapp import PylonsApp
from routes.middleware import RoutesMiddleware
from pyalchemybiz.config.environment import load_environment
def make_app(global_conf, full_stack=True, **app_conf):
def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
"""Create a Pylons WSGI application and return it
``global_conf``
@ -21,15 +19,20 @@ def make_app(global_conf, full_stack=True, **app_conf):
the [DEFAULT] section of the Paste ini file.
``full_stack``
Whether or not this application provides a full WSGI stack (by
default, meaning it handles its own exceptions and errors).
Disable full_stack when this application is "managed" by
another WSGI middleware.
Whether this application provides a full WSGI stack (by default,
meaning it handles its own exceptions and errors). Disable
full_stack when this application is "managed" by another WSGI
middleware.
``static_files``
Whether this application serves its own static files; disable
when another web server is responsible for serving them.
``app_conf``
The application's local configuration. Normally specified in the
[app:<name>] section of the Paste ini file (where <name>
The application's local configuration. Normally specified in
the [app:<name>] section of the Paste ini file (where <name>
defaults to main).
"""
# Configure the Pylons environment
load_environment(global_conf, app_conf)
@ -37,22 +40,30 @@ def make_app(global_conf, full_stack=True, **app_conf):
# The Pylons WSGI app
app = PylonsApp()
# Routing/Session/Cache Middleware
app = RoutesMiddleware(app, config['routes.map'])
app = SessionMiddleware(app, config)
app = CacheMiddleware(app, config)
# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
if asbool(full_stack):
# Handle Python exceptions
app = ErrorHandler(app, global_conf, error_template=error_template,
**config['pylons.errorware'])
app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
# Display error documents for 401, 403, 404 status codes (and
# 500 when debug is disabled)
app = ErrorDocuments(app, global_conf, mapper=error_mapper, **app_conf)
if asbool(config['debug']):
app = StatusCodeRedirect(app)
else:
app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
# Establish the Registry for this application
app = RegistryManager(app)
# Static files
javascripts_app = StaticJavascripts()
static_app = StaticURLParser(config['pylons.paths']['static_files'])
app = Cascade([static_app, javascripts_app, app])
if asbool(static_files):
# Serve static files
static_app = StaticURLParser(config['pylons.paths']['static_files'])
app = Cascade([static_app, app])
return app

View file

@ -7,19 +7,21 @@ refer to the routes manual at http://routes.groovie.org/docs/
from pylons import config
from routes import Mapper
def make_map():
"""Create, configure and return the routes Mapper"""
map = Mapper(directory=config['pylons.paths']['controllers'],
always_scan=config['debug'])
map.minimization = False
# The ErrorController route (handles 404/500 error pages); it should
# likely stay at the top, ensuring it can always be resolved
map.connect('error/:action/:id', controller='error')
map.connect('/error/{action}', controller='error')
map.connect('/error/{action}/{id}', controller='error')
# CUSTOM ROUTES HERE
map.connect('', controller='index', action='index')
map.connect(':controller/:action/:id')
map.connect('*url', controller='template', action='view')
map.connect('/', controller='index', action='index')
map.connect('/{controller}/{action}')
map.connect('/{controller}/{action}/{id}')
return map

View file

@ -1,13 +1,15 @@
import cgi
import os.path
from paste.urlparser import StaticURLParser
from pylons.middleware import error_document_template, media_path
from pyalchemybiz.lib.base import *
from paste.urlparser import PkgResourcesParser
from pylons import request
from pylons.controllers.util import forward
from pylons.middleware import error_document_template
from webhelpers.html.builder import literal
from pyalchemybiz.lib.base import BaseController
class ErrorController(BaseController):
"""Generates error documents as and when they are required.
The ErrorDocuments middleware forwards to ErrorController when error
@ -20,24 +22,25 @@ class ErrorController(BaseController):
def document(self):
"""Render the error document"""
resp = request.environ.get('pylons.original_response')
content = literal(resp.body) or cgi.escape(request.GET.get('message', ''))
page = error_document_template % \
dict(prefix=request.environ.get('SCRIPT_NAME', ''),
code=cgi.escape(request.params.get('code', '')),
message=cgi.escape(request.params.get('message', '')))
code=cgi.escape(request.GET.get('code', str(resp.status_int))),
message=content)
return page
def img(self, id):
"""Serve Pylons' stock images"""
return self._serve_file(os.path.join(media_path, 'img'), id)
return self._serve_file('/'.join(['media/img', id]))
def style(self, id):
"""Serve Pylons' stock stylesheets"""
return self._serve_file(os.path.join(media_path, 'style'), id)
return self._serve_file('/'.join(['media/style', id]))
def _serve_file(self, root, path):
def _serve_file(self, path):
"""Call Paste's FileApp (a WSGI application) to serve the file
at the specified path
"""
static = StaticURLParser(root)
request.environ['PATH_INFO'] = '/%s' % path
return static(request.environ, self.start_response)
return forward(PkgResourcesParser('pylons', 'pylons'))

View file

@ -1,6 +1,8 @@
import logging
from pyalchemybiz.lib.base import *
from pylons.i18n.translation import ugettext, _, set_lang, get_lang
log = logging.getLogger(__name__)

View file

@ -1,15 +1,15 @@
"""The application's Globals object"""
from pylons import config
class Globals(object):
"""Globals acts as a container for objects available throughout the
life of the application
"""
def __init__(self):
"""One instance of Globals is created during application
initialization and is available during requests via the 'g'
variable
initialization and is available during requests via the
'app_globals' variable
"""
pass

View file

@ -1,40 +1,37 @@
"""The base Controller API
Provides the BaseController class for subclassing, and other objects
utilized by Controllers.
Provides the BaseController class for subclassing.
"""
from pylons import c, cache, config, g, request, response, session
from pylons.controllers import WSGIController
from pylons.controllers.util import abort, etag_cache, redirect_to
from pylons.decorators import jsonify, validate
from pylons.i18n import _, ungettext, N_, add_fallback, set_lang
from pylons.templating import render
from pylons.i18n.translation import _, ugettext, add_fallback
from pylons.templating import render_mako as render
import pyalchemybiz.lib.helpers as h
from pyalchemybiz.model import meta
class BaseController(WSGIController):
def __call__(self, environ, start_response):
"""Invoke the Controller"""
# WSGIController.__call__ dispatches to the Controller method
# the request is routed to. This routing information is
# available in environ['pylons.routes_dict']
def __before__(self):
# set language environment
for lang in request.languages:
try:
add_fallback(lang)
except:
pass
def __call__(self, environ, start_response):
"""Invoke the Controller"""
# WSGIController.__call__ dispatches to the Controller method
# the request is routed to. This routing information is
# available in environ['pylons.routes_dict']
# connect to database
try:
return WSGIController.__call__(self, environ, start_response)
finally:
meta.Session.remove()
# Include the '_' function in the public names
__all__ = [__name for __name in locals().keys() if not __name.startswith('_') \
or __name == '_']

View file

@ -1,7 +1,7 @@
"""Helper functions
Consists of functions to typically be used within templates, but also
available to Controllers. This module is available to both as 'h'.
available to Controllers. This module is available to templates as 'h'.
"""
from webhelpers.html.tags import *
from routes import url_for, redirect_to

View file

@ -1,14 +1,15 @@
"""SQLAlchemy Metadata and Session object"""
from sqlalchemy import MetaData
from sqlalchemy.orm import scoped_session, sessionmaker
__all__ = ['engine', 'metadata', 'Session']
__all__ = ['Session', 'engine', 'metadata']
# SQLAlchemy database engine. Updated by model.init_model().
# SQLAlchemy database engine. Updated by model.init_model()
engine = None
# SQLAlchemy session manager. Updated by model.init_model().
Session = None
# SQLAlchemy session manager. Updated by model.init_model()
Session = scoped_session(sessionmaker())
# Global metadata. If you have multiple databases with overlapping table
# names, you'll need a metadata for each database.
# names, you'll need a metadata for each database
metadata = MetaData()

View file

View file

View file

@ -6,7 +6,7 @@
<head>
<title>${self.title()}</title>
${h.stylesheet_link('/pyalchemybiz.css')}
${h.javascript_link('/javascripts/jquery.js', '/javascripts/pyalchemybiz.js')}
${h.javascript_link('/js/jquery.js', '/js/pyalchemybiz.js')}
${self.head()}
</head>
<body>

View file

@ -2,7 +2,7 @@
<ul id="customers">
% for customer in c.customers:
<li>${customer | h} [${h.link_to(_('view customer'), h.url_for(id=customer.id, action='view'))}] [${h.link_to(_('edit customer'), h.url_for(id=customer.id, action="edit"))}] [${h.link_to(_('delete customer'), h.url_for(id=customer.id, action="delete"))}]</li>
<li>${customer} [${h.link_to(_('view customer'), h.url_for(id=customer.id, action='view'))}] [${h.link_to(_('edit customer'), h.url_for(id=customer.id, action="edit"))}] [${h.link_to(_('delete customer'), h.url_for(id=customer.id, action="delete"))}]</li>
% endfor
</ul>

View file

@ -1,41 +1,36 @@
"""Pylons application test package
When the test runner finds and executes tests within this directory,
this file will be loaded to setup the test environment.
This package assumes the Pylons environment is already loaded, such as
when this script is imported from the `nosetests --with-pylons=test.ini`
command.
It registers the root directory of the project in sys.path and
pkg_resources, in case the project hasn't been installed with
setuptools. It also initializes the application via websetup (paster
setup-app) with the project's test.ini configuration file.
This module initializes the application via ``websetup`` (`paster
setup-app`) and provides the base testing objects.
"""
import os
import sys
from unittest import TestCase
import pkg_resources
import paste.fixture
import paste.script.appinstall
from paste.deploy import loadapp
from routes import url_for
from paste.script.appinstall import SetupCommand
from pylons import config, url
from routes.util import URLGenerator
from webtest import TestApp
__all__ = ['url_for', 'TestController']
import pylons.test
here_dir = os.path.dirname(os.path.abspath(__file__))
conf_dir = os.path.dirname(os.path.dirname(here_dir))
__all__ = ['environ', 'url', 'TestController']
sys.path.insert(0, conf_dir)
pkg_resources.working_set.add_entry(conf_dir)
pkg_resources.require('Paste')
pkg_resources.require('PasteScript')
test_file = os.path.join(conf_dir, 'test.ini')
cmd = paste.script.appinstall.SetupCommand('setup-app')
cmd.run([test_file])
# Invoke websetup with the current config file
SetupCommand('setup-app').run([config['__file__']])
environ = {}
class TestController(TestCase):
def __init__(self, *args, **kwargs):
wsgiapp = loadapp('config:test.ini', relative_to=conf_dir)
self.app = paste.fixture.TestApp(wsgiapp)
if pylons.test.pylonsapp:
wsgiapp = pylons.test.pylonsapp
else:
wsgiapp = loadapp('config:%s' % config['__file__'])
self.app = TestApp(wsgiapp)
url._push_object(URLGenerator(config['routes.map'], environ))
TestCase.__init__(self, *args, **kwargs)

View file

@ -13,14 +13,13 @@ from pyalchemybiz.config.environment import load_environment
log = logging.getLogger(__name__)
def setup_config(command, filename, section, vars):
def setup_app(command, conf, vars):
"""Place any commands to setup pyalchemybiz here"""
conf = appconfig('config:' + filename)
load_environment(conf.global_conf, conf.local_conf)
repoversion = int(config.get('migrate.repo.version'))
repodir = config.get('migrate.repo.dir')
dburl = config.get('sqlalchemy.default.url')
dburl = config.get('sqlalchemy.url')
# Populate the DB on 'paster setup-app'

View file

@ -5,36 +5,8 @@ tag_svn_revision = true
[easy_install]
find_links = http://www.pylonshq.com/download/
[pudge]
theme = pythonpaste.org
# Add extra doc files here with spaces between them
docs = docs/index.txt
# Doc Settings
doc_base = docs/
dest = docs/html
# Add extra modules here separated with commas
modules = pyalchemybiz
title = Pyalchemybiz
organization = Pylons
# Highlight code-block sections with Pygments
highlighter = pygments
# Optionally add extra links
#organization_url = http://pylonshq.com/
#trac_url = http://pylonshq.com/project
settings = no_about=true
# Optionally add extra settings
# link1=/community/ Community
# link2=/download/ Download
[publish]
doc-dir=docs/html
make-dirs=1
[nosetests]
with-pylons = test.ini
# Babel configuration
[compile_catalog]

View file

@ -7,21 +7,32 @@ except ImportError:
setup(
name='pyalchemybiz',
version="0.1",
version='0.1',
description='python based small business suite.',
author='Jan Dittberner',
author_email='jan@dittberner.info',
url='http://www.dittberner.info/projects/pyalchemybiz',
install_requires=["Pylons>=0.9.7", "SQLAlchemy>=0.5.2",
"sqlalchemy-migrate>=0.5.2"],
install_requires=[
"Pylons>=0.9.7",
"SQLAlchemy>=0.5.2,<0.6",
"sqlalchemy-migrate>=0.5.2,<0.6",
"Mako",
"FormBuild>=2.0,<3",
],
setup_requires=[
"PasteScript>=1.6.3",
"Babel>=0.9.1",
],
packages=find_packages(exclude=['ez_setup']),
include_package_data=True,
test_suite='nose.collector',
package_data={'pyalchemybiz': ['i18n/*/LC_MESSAGES/*.mo']},
message_extractors = {'pyalchemybiz': [
('**.py', 'python', None),
('templates/**.mako', 'mako', None),
('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
('public/**', 'ignore', None)]},
zip_safe=False,
paster_plugins=['PasteScript', 'Pylons'],
entry_points="""
[paste.app_factory]
main = pyalchemybiz.config.middleware:make_app

View file

@ -12,7 +12,7 @@ error_email_from = paste@localhost
[server:main]
use = egg:Paste#http
host = 0.0.0.0
host = 127.0.0.1
port = 5000
[app:main]