diff --git a/.gitignore b/.gitignore index ce34cfe..92bf38d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ *.pyc data -pyalchemybiz.db +development.db +pyalchemybiz/public/js/jquery.js diff --git a/MANIFEST.in b/MANIFEST.in index 5b63bda..cf1a520 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ +include pyalchemybiz/config/deployment.ini_tmpl recursive-include pyalchemybiz/public * recursive-include pyalchemybiz/templates * diff --git a/development.ini b/development.ini index 08bcb9b..119a9e4 100644 --- a/development.ini +++ b/development.ini @@ -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,) diff --git a/docs/index.txt b/docs/index.txt index b1e5533..cbdbb7f 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -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/ diff --git a/pyalchemybiz.egg-info/PKG-INFO b/pyalchemybiz.egg-info/PKG-INFO index 6b3b986..1dada9a 100644 --- a/pyalchemybiz.egg-info/PKG-INFO +++ b/pyalchemybiz.egg-info/PKG-INFO @@ -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 diff --git a/pyalchemybiz.egg-info/SOURCES.txt b/pyalchemybiz.egg-info/SOURCES.txt index 52dbaaa..8c0d40d 100644 --- a/pyalchemybiz.egg-info/SOURCES.txt +++ b/pyalchemybiz.egg-info/SOURCES.txt @@ -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 diff --git a/pyalchemybiz.egg-info/not-zip-safe b/pyalchemybiz.egg-info/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pyalchemybiz.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/pyalchemybiz.egg-info/paster_plugins.txt b/pyalchemybiz.egg-info/paster_plugins.txt index 435fb65..c24c7fe 100644 --- a/pyalchemybiz.egg-info/paster_plugins.txt +++ b/pyalchemybiz.egg-info/paster_plugins.txt @@ -1,2 +1,2 @@ +PasteScript Pylons -WebHelpers diff --git a/pyalchemybiz.egg-info/requires.txt b/pyalchemybiz.egg-info/requires.txt index 2994bc9..406493c 100644 --- a/pyalchemybiz.egg-info/requires.txt +++ b/pyalchemybiz.egg-info/requires.txt @@ -1,3 +1,5 @@ -Pylons>=0.9.6.2 -SQLAlchemy>=0.4.7 -sqlalchemy-migrate>=0.4.5 \ No newline at end of file +Pylons>=0.9.7 +SQLAlchemy>=0.5.2,<0.6 +sqlalchemy-migrate>=0.5.2,<0.6 +Mako +FormBuild>=2.0,<3 \ No newline at end of file diff --git a/pyalchemybiz.egg-info/paste_deploy_config.ini_tmpl b/pyalchemybiz/config/deployment.ini_tmpl similarity index 75% rename from pyalchemybiz.egg-info/paste_deploy_config.ini_tmpl rename to pyalchemybiz/config/deployment.ini_tmpl index 9772b1f..af8b169 100644 --- a/pyalchemybiz.egg-info/paste_deploy_config.ini_tmpl +++ b/pyalchemybiz/config/deployment.ini_tmpl @@ -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] diff --git a/pyalchemybiz/config/environment.py b/pyalchemybiz/config/environment.py index 5963ee6..3d122f0 100644 --- a/pyalchemybiz/config/environment.py +++ b/pyalchemybiz/config/environment.py @@ -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) diff --git a/pyalchemybiz/config/middleware.py b/pyalchemybiz/config/middleware.py index d0eda92..7fac26d 100644 --- a/pyalchemybiz/config/middleware.py +++ b/pyalchemybiz/config/middleware.py @@ -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:] section of the Paste ini file (where + The application's local configuration. Normally specified in + the [app:] section of the Paste ini file (where 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 diff --git a/pyalchemybiz/config/routing.py b/pyalchemybiz/config/routing.py index e685eb8..53281bf 100644 --- a/pyalchemybiz/config/routing.py +++ b/pyalchemybiz/config/routing.py @@ -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 diff --git a/pyalchemybiz/controllers/error.py b/pyalchemybiz/controllers/error.py index 1ef899b..57f8b53 100644 --- a/pyalchemybiz/controllers/error.py +++ b/pyalchemybiz/controllers/error.py @@ -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')) diff --git a/pyalchemybiz/controllers/index.py b/pyalchemybiz/controllers/index.py index 503566a..a3cb4ec 100644 --- a/pyalchemybiz/controllers/index.py +++ b/pyalchemybiz/controllers/index.py @@ -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__) diff --git a/pyalchemybiz/lib/app_globals.py b/pyalchemybiz/lib/app_globals.py index d650178..8e62241 100644 --- a/pyalchemybiz/lib/app_globals.py +++ b/pyalchemybiz/lib/app_globals.py @@ -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 diff --git a/pyalchemybiz/lib/base.py b/pyalchemybiz/lib/base.py index 1e7074a..ae3b511 100644 --- a/pyalchemybiz/lib/base.py +++ b/pyalchemybiz/lib/base.py @@ -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 == '_'] diff --git a/pyalchemybiz/lib/helpers.py b/pyalchemybiz/lib/helpers.py index 8b482db..eb51f7c 100644 --- a/pyalchemybiz/lib/helpers.py +++ b/pyalchemybiz/lib/helpers.py @@ -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 diff --git a/pyalchemybiz/model/meta.py b/pyalchemybiz/model/meta.py index 6eeb4e6..1a20aa7 100644 --- a/pyalchemybiz/model/meta.py +++ b/pyalchemybiz/model/meta.py @@ -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() diff --git a/pyalchemybiz/public/js/pyalchemybiz.js b/pyalchemybiz/public/js/pyalchemybiz.js new file mode 100644 index 0000000..e69de29 diff --git a/pyalchemybiz/public/pyalchemybiz.css b/pyalchemybiz/public/pyalchemybiz.css new file mode 100644 index 0000000..e69de29 diff --git a/pyalchemybiz/templates/base/base.mako b/pyalchemybiz/templates/base/base.mako index 2ea16ff..349216e 100644 --- a/pyalchemybiz/templates/base/base.mako +++ b/pyalchemybiz/templates/base/base.mako @@ -6,7 +6,7 @@ ${self.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()} diff --git a/pyalchemybiz/templates/derived/customer/list.mako b/pyalchemybiz/templates/derived/customer/list.mako index dcc2ef1..f3844fb 100644 --- a/pyalchemybiz/templates/derived/customer/list.mako +++ b/pyalchemybiz/templates/derived/customer/list.mako @@ -2,7 +2,7 @@
    % for customer in c.customers: -
  • ${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"))}]
  • +
  • ${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"))}]
  • % endfor
diff --git a/pyalchemybiz/tests/__init__.py b/pyalchemybiz/tests/__init__.py index 1caa487..d51f6f2 100644 --- a/pyalchemybiz/tests/__init__.py +++ b/pyalchemybiz/tests/__init__.py @@ -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) diff --git a/pyalchemybiz/websetup.py b/pyalchemybiz/websetup.py index 651259d..58f682b 100644 --- a/pyalchemybiz/websetup.py +++ b/pyalchemybiz/websetup.py @@ -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' diff --git a/setup.cfg b/setup.cfg index 60ab224..c070d00 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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] diff --git a/setup.py b/setup.py index 233d5b0..855340b 100755 --- a/setup.py +++ b/setup.py @@ -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 diff --git a/test.ini b/test.ini index 7bb9ce4..8af3165 100644 --- a/test.ini +++ b/test.ini @@ -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]