Real World template project¶
Presents a template of a real Python project and shows features of the summer framework.
Requirements:
- SQLite – uses in memory db available with no special config.
- LDAP – uses simple ldap db holding user accounts.
SQL database is simple and uses just two entities with m:n relationship between Category <–> Item.
LDAP support can be turned off, if not, you will need to set up your own
ldap server, see ldap/sample.local.sh
for details.
As well as with Basic Application Context, there are some obvious configuration files:
- standard Python logging config
logging.cfg
- summer framework config
summer.cfg
- custom application config
custom.cfg
Project is now organized with one single package, so please look at:
tmplprojecttest.py
tmplproject/main.py
tmplproject/appcontext.py
summer.cfg¶
# Time-stamp: < summer.cfg (2016-01-24 23:39) >
# Copyright (C) 2009-2016 Martin Slouf <martin.slouf@sourceforge.net>
#
# This file is a part of Summer.
#
# Summer is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 3 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
[DEFAULT]
project_name = tmplproject
project_top_dir = .
[SQLALCHEMY]
#uri sqlite:///:memory: (or, sqlite://)
#uri sqlite:///relative/path/to/file.db
#uri sqlite:////absolute/path/to/file.db
#uri = sqlite:///%(project_top_dir)s/file.sqlite
uri = sqlite:///:memory:
echo = no
autoflush = yes
autocommit = no
[L10N]
domain = %(project_name)s
l10n_dir = %(project_top_dir)s/l10n
languages = cs,en
[LDAP]
hostname = localhost
port = 389
base = dc=sample,dc=local
login = cn=admin,dc=sample,dc=local
passwd = secret
tmplprojecttest.py¶
# -*- coding: utf-8 -*-
# Time-stamp: < tmplprojecttest.py (2016-02-08 19:48) >
# Copyright (C) 2009-2016 Martin Slouf <martin.slouf@sourceforge.net>
#
# This file is a part of Summer.
#
# Summer is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 3 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""Relatively advanced standalone example.
Check the sample config files in this directory. Look at the single
:py:module:`tmplproject` module.
"""
import logging
import logging.config
import os
import os.path
import sys
from unittest import TestCase
# configure logging as soon as possible
LOGGING_CFG = os.path.join(os.path.dirname(__file__), "logging.cfg")
logging.config.fileConfig(LOGGING_CFG)
logger = logging.getLogger(__name__)
logger.info("logging config = %s", LOGGING_CFG)
from .tmplproject import main
class TmplProjectTest(TestCase):
"""Demonstrates various summer features in *real-world* project."""
def setUp(self):
pass
def tearDown(self):
pass
def test_tmplproject(self):
logger.info("#### TEST STARTS HERE ####")
main.main(sys.argv[1:])
logger.info("#### TEST ENDS HERE ####")
tmplproject/main.py¶
# -*- coding: utf-8 -*-
# Time-stamp: < main.py (2016-02-08 19:49) >
# Copyright (C) 2009-2016 Martin Slouf <martin.slouf@sourceforge.net>
#
# This file is a part of Summer.
#
# Summer is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 3 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""Main entry point to the program."""
import logging
# configure logging as soon as possible
logger = logging.getLogger(__name__)
from summer import Filter
from summer.ipythonutils import run_ipshell
from .appcontext import ApplicationContext
from .manager import Category
from .cmdline import SHELL
def main(args):
"""Entry point to our program."""
ctx = ApplicationContext(__file__, "tmplproject.cfg")
logger.info("sample started")
(options, args) = ctx.my_option_parser.parse_args(args)
logger.info("args: %s", args)
logger.info("options: %s", options)
# create the sample database
ctx.session_factory.create_schema()
# do the specified action
process_args(args, options, ctx)
logger.info("sample finished")
def process_args(args, options, ctx):
"""Process the command line and takes the proper action."""
if len(args) > 0:
command = args[0]
logger.info("running command '%s'", command)
if command == SHELL:
run_ipshell("Embedded IPython Shell Banner", {"ctx": ctx})
else:
ctx.my_option_parser.print_help()
else: # take the default action
do_demonstration(ctx, options.use_ldap)
def do_demonstration(ctx, use_ldap):
mark = "PROGRAM OUTPUT>"
# l10n
logger.info("%s test localization -- %s", mark, _("localized message"))
# db
# we need a proxied version cause we want database transactions
logger.info("let's create some objects and persist them")
category_dao = ctx.category_dao_proxy
for i in range(1, 16):
cat = Category()
cat.order = i
cat.code = "code_%02d" % (cat.order,)
category_dao.save(cat)
# we go through result set using paging
logger.info(
"let's iterate using db paging through what we have just persisted")
cat_filter = Filter(1, 5)
for page in range(1, 4):
cat_filter.page = page
logger.info("%s page %d", mark, cat_filter.page)
for cat in category_dao.find(cat_filter):
logger.info("%s %s", mark, cat)
# ldap
if use_ldap:
logger.info("let's use LDAP demo query for arbitrary objects" +
" (not all of them, just those with ou=users)")
user_manager = ctx.user_manager_proxy
for user in user_manager.find():
logger.info("%s %s", mark, user)
tmplproject/appcontext.py¶
# -*- coding: utf-8 -*-
# Time-stamp: < appcontext.py (2016-02-12 08:13) >
# Copyright (C) 2009-2016 Martin Slouf <martin.slouf@sourceforge.net>
#
# This file is a part of Summer.
#
# Summer is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 3 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
Application context definition.
"""
import logging
# configure logging as soon as possible
logger = logging.getLogger(__name__)
import summer
# do all the necessary imports, all that is imported bellow is deployed
# into the context, just as an example -- you can deploy anything you want
# of course
from .cmdline import MyOptionParser
from .orm.tables import TableDefinitions
from .orm.mappings import ClassMappings
from .manager import (
CategoryDao, CategoryManager,
ItemDao, ItemManager,
UserDao, UserManager,
)
class ApplicationContext(summer.Context):
def __init__(self, path_to_module, customcfg):
summer.Context.__init__(self, path_to_module, customcfg)
def orm_init(self):
# you can do whatever setup you want, here you can see so-called
# "classical" mapping, see SQLAlchemy for details
# first let's complete database initialization with custom table
# definitions and mappings
self.session_factory.set_table_definitions(TableDefinitions())
# class mappings must be defined _after_ table definitions
self.session_factory.set_class_mappings(ClassMappings())
def context_init(self):
# create command line parser, pass it reference of our custom
# config to guess the correct defaults
self.my_option_parser = MyOptionParser(self.config)
# let's deploy SQL DAO's with corresponding proxies -- use proxy
# object whenever you want a separate transaction, use non-proxied
# version as a 'building block' when you need to take part in
# on-going transaction
self.category_dao = CategoryDao(self.session_factory)
self.category_dao_proxy = summer.TransactionProxy(self.category_dao)
self.item_dao = ItemDao(self.session_factory)
self.item_dao_proxy = summer.TransactionProxy(self.item_dao)
# let's deploy LDAP DAO's; treat them as analogy to SQL DAO's,
# though the LDAP has no sense of SQL transaction
self.user_dao = UserDao(self.ldap_session_factory)
self.user_dao_proxy = summer.LdapProxy(self.user_dao)
# let's define some higher level business level objects (managers)
self.category_manager = CategoryManager(self.category_dao)
self.category_manager_proxy = \
summer.TransactionProxy(self.category_manager,
self.session_factory)
self.item_manager = ItemManager(self.item_dao)
self.item_manager_proxy = summer.TransactionProxy(self.item_manager,
self.session_factory)
self.user_manager = UserManager(self.user_dao)
self.user_manager_proxy = summer.LdapProxy(self.user_manager,
self.ldap_session_factory)