This commit is contained in:
Lance Edgar 2012-07-09 03:46:23 -05:00
commit 3b208904f8
15 changed files with 471 additions and 370 deletions

View file

@ -1,5 +1,34 @@
0.1a6
-----
- Fixed MANIFEST.in file.
0.1a5
-----
- Added :mod:`edbob.csv` module.
- Tweaked logging configuration and initialization semantics.
0.1a4
-----
- Fixed call to sleep() in filemon service.
0.1a3
-----
- Various tweaks to Pyramid code.
0.1a2
-----
- Add ``win32.send_data_to_printer()`` function.
- Various tweaks to Pyramid code.
0.1a1 0.1a1
----- -----
- Initial version - Initial version

View file

@ -1,2 +1,18 @@
include *.txt include COPYING.txt README.txt CHANGES.txt
include ez_setup.py include ez_setup.py
include edbob/pyramid/static/favicon.ico
include edbob/pyramid/static/robots.txt
include edbob/pyramid/static/css/*.css
include edbob/pyramid/static/js/*.js
include edbob/scaffolds/edbob/CHANGES.txt
include edbob/scaffolds/edbob/+package+/pyramid/static/favicon.ico
include edbob/scaffolds/edbob/+package+/pyramid/static/robots.txt
recursive-include edbob/pyramid/static/img *.jpg *.png
recursive-include edbob/pyramid/templates *.mako
recursive-include edbob/scaffolds/edbob *.py
recursive-include edbob/scaffolds/edbob *_tmpl
recursive-include edbob/scaffolds/edbob/+package+/pyramid/templates *.mako

View file

@ -1 +1 @@
__version__ = '0.1a3' __version__ = '0.1a7'

View file

@ -70,15 +70,17 @@ class AppConfigParser(ConfigParser.SafeConfigParser):
file, and passes that to ``logging.config.fileConfig()``. file, and passes that to ``logging.config.fileConfig()``.
""" """
if self.getboolean(self.appname, 'basic_logging', default=False): if self.getboolean('edbob', 'basic_logging', default=False):
edbob.basic_logging(self.appname) edbob.basic_logging()
path = edbob.temp_path(suffix='.conf') if self.getboolean('edbob', 'configure_logging', default=False):
self.save(path) path = edbob.temp_path(suffix='.conf')
try: self.save(path)
logging.config.fileConfig(path) try:
except ConfigParser.NoSectionError: logging.config.fileConfig(path, disable_existing_loggers=False)
pass except ConfigParser.NoSectionError:
os.remove(path) pass
os.remove(path)
log.debug("Configured logging")
def get(self, section, option, raw=False, vars=None, default=None): def get(self, section, option, raw=False, vars=None, default=None):
""" """
@ -243,7 +245,6 @@ class AppConfigParser(ConfigParser.SafeConfigParser):
config.has_option('edbob', 'include_config')): config.has_option('edbob', 'include_config')):
include = config.get('edbob', 'include_config') include = config.get('edbob', 'include_config')
if include: if include:
log.debug("Including config: %s" % include)
for p in eval(include): for p in eval(include):
self.read_path(os.path.abspath(p)) self.read_path(os.path.abspath(p))
ConfigParser.SafeConfigParser.read(self, path) ConfigParser.SafeConfigParser.read(self, path)

View file

@ -71,7 +71,9 @@ def basic_logging():
handler = logging.StreamHandler() handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter( handler.setFormatter(logging.Formatter(
'%(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s')) '%(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s'))
logging.getLogger().addHandler(handler) root = logging.getLogger()
root.addHandler(handler)
root.setLevel(logging.INFO)
def get_uuid(): def get_uuid():

71
edbob/csv.py Normal file
View file

@ -0,0 +1,71 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
################################################################################
#
# edbob -- Pythonic Software Framework
# Copyright © 2010-2012 Lance Edgar
#
# This file is part of edbob.
#
# edbob is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# edbob 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 Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with edbob. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
``edbob.csv`` -- CSV File Utilities
"""
from __future__ import absolute_import
import codecs
import csv
class UTF8Recoder(object):
"""
Iterator that reads an encoded stream and reencodes the input to UTF-8.
.. note::
This class was stolen from the Python 2.7 documentation.
"""
def __init__(self, fileobj, encoding):
self.reader = codecs.getreader(encoding)(fileobj)
def __iter__(self):
return self
def next(self):
return self.reader.next().encode('utf_8')
class UnicodeReader(object):
"""
A CSV reader which will iterate over lines in a CSV file, which is encoded
in the given encoding.
.. note::
This class was stolen from the Python 2.7 documentation.
"""
def __init__(self, fileobj, dialect=csv.excel, encoding='utf_8', **kwargs):
fileobj = UTF8Recoder(fileobj, encoding)
self.reader = csv.reader(fileobj, dialect=dialect, **kwargs)
def __iter__(self):
return self
def next(self):
row = self.reader.next()
return [unicode(x, 'utf_8') for x in row]

View file

@ -1,184 +1,184 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################ ################################################################################
# #
# edbob -- Pythonic Software Framework # edbob -- Pythonic Software Framework
# Copyright © 2010-2012 Lance Edgar # Copyright © 2010-2012 Lance Edgar
# #
# This file is part of edbob. # This file is part of edbob.
# #
# edbob is free software: you can redistribute it and/or modify it under the # edbob is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free # terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) # Software Foundation, either version 3 of the License, or (at your option)
# any later version. # any later version.
# #
# edbob is distributed in the hope that it will be useful, but WITHOUT ANY # edbob is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details. # more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with edbob. If not, see <http://www.gnu.org/licenses/>. # along with edbob. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
``edbob.db`` -- Database Framework ``edbob.db`` -- Database Framework
""" """
from __future__ import absolute_import from __future__ import absolute_import
from sqlalchemy import engine_from_config, MetaData from sqlalchemy import engine_from_config, MetaData
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
import edbob import edbob
# __all__ = ['engines', 'engine', 'Session', 'metadata', # __all__ = ['engines', 'engine', 'Session', 'metadata',
# 'get_setting', 'save_setting'] # 'get_setting', 'save_setting']
__all__ = ['engines', 'engine', 'Session', 'get_setting', 'save_setting'] __all__ = ['engines', 'engine', 'Session', 'get_setting', 'save_setting']
inited = False inited = False
engines = None engines = None
engine = None engine = None
Session = sessionmaker() Session = sessionmaker()
Base = declarative_base() Base = declarative_base()
# metadata = None # metadata = None
def init(config): def init(config):
""" """
Initializes the database connection(s); called by :func:`edbob.init()` if Initializes the database connection(s); called by :func:`edbob.init()` if
config includes something like:: config includes something like::
.. highlight:: ini .. highlight:: ini
[edbob] [edbob]
init = ['edbob.db'] init = ['edbob.db']
[edbob.db] [edbob.db]
sqlalchemy.urls = { sqlalchemy.urls = {
'default': 'postgresql://user:pass@localhost/edbob, 'default': 'postgresql://user:pass@localhost/edbob,
} }
This function reads connection info from ``config`` and builds a dictionary This function reads connection info from ``config`` and builds a dictionary
or :class:`sqlalchemy.Engine` instances accordingly. It also extends the or :class:`sqlalchemy.Engine` instances accordingly. It also extends the
root ``edbob`` namespace with the ORM classes (:class:`edbob.Person`, root ``edbob`` namespace with the ORM classes (:class:`edbob.Person`,
:class:`edbob.User`, etc.), as well as a few other things :class:`edbob.User`, etc.), as well as a few other things
(e.g. :attr:`edbob.engine`, :attr:`edbob.Session`, :attr:`edbob.metadata`). (e.g. :attr:`edbob.engine`, :attr:`edbob.Session`, :attr:`edbob.metadata`).
""" """
import edbob.db import edbob.db
# from edbob.db import classes # from edbob.db import classes
from edbob.db import model from edbob.db import model
from edbob.db import enum from edbob.db import enum
# from edbob.db.model import get_metadata # from edbob.db.model import get_metadata
# from edbob.db.mappers import make_mappers # from edbob.db.mappers import make_mappers
from edbob.db.extensions import extend_framework from edbob.db.extensions import extend_framework
# global inited, engines, engine, metadata # global inited, engines, engine, metadata
global inited, engines, engine global inited, engines, engine
keys = config.get('edbob.db', 'sqlalchemy.keys') keys = config.get('edbob.db', 'sqlalchemy.keys')
if keys: if keys:
keys = keys.split() keys = keys.split()
else: else:
keys = ['default'] keys = ['default']
engines = {} engines = {}
cfg = config.get_dict('edbob.db') cfg = config.get_dict('edbob.db')
for key in keys: for key in keys:
try: try:
engines[key] = engine_from_config(cfg, 'sqlalchemy.%s.' % key) engines[key] = engine_from_config(cfg, 'sqlalchemy.%s.' % key)
except KeyError: except KeyError:
if key == 'default': if key == 'default':
try: try:
engines[key] = engine_from_config(cfg) engines[key] = engine_from_config(cfg)
except KeyError: except KeyError:
pass pass
engine = engines.get('default') engine = engines.get('default')
if engine: if engine:
Base.metadata.bind = engine Base.metadata.bind = engine
# metadata = get_metadata(bind=engine) # metadata = get_metadata(bind=engine)
# make_mappers(metadata) # make_mappers(metadata)
extend_framework() extend_framework()
edbob.graft(edbob, edbob.db) edbob.graft(edbob, edbob.db)
# edbob.graft(edbob, classes) # edbob.graft(edbob, classes)
edbob.graft(edbob, model) edbob.graft(edbob, model)
edbob.graft(edbob, enum) edbob.graft(edbob, enum)
inited = True inited = True
def get_setting(name, session=None): def get_setting(name, session=None):
""" """
Returns a setting from the database. Returns a setting from the database.
""" """
_session = session _session = session
if not session: if not session:
session = Session() session = Session()
setting = session.query(edbob.Setting).get(name) setting = session.query(edbob.Setting).get(name)
if setting: if setting:
setting = setting.value setting = setting.value
if not _session: if not _session:
session.close() session.close()
return setting return setting
def save_setting(name, value, session=None): def save_setting(name, value, session=None):
""" """
Saves a setting to the database. Saves a setting to the database.
""" """
_session = session _session = session
if not session: if not session:
session = Session() session = Session()
setting = session.query(edbob.Setting).get(name) setting = session.query(edbob.Setting).get(name)
if not setting: if not setting:
setting = edbob.Setting(name=name) setting = edbob.Setting(name=name)
session.add(setting) session.add(setting)
setting.value = value setting.value = value
if not _session: if not _session:
session.commit() session.commit()
session.close() session.close()
def get_core_metadata(): def get_core_metadata():
""" """
Returns a :class:`sqlalchemy.MetaData` instance containing only those Returns a :class:`sqlalchemy.MetaData` instance containing only those
:class:`sqlalchemy.Table`s which are part of the core ``edbob`` schema. :class:`sqlalchemy.Table`s which are part of the core ``edbob`` schema.
""" """
from edbob.db import model from edbob.db import model
meta = MetaData() meta = MetaData()
for name in model.__all__: for name in model.__all__:
if name != 'Base': if name != 'Base':
obj = getattr(model, name) obj = getattr(model, name)
if isinstance(obj, type) and issubclass(obj, model.Base): if isinstance(obj, type) and issubclass(obj, model.Base):
obj.__table__.tometadata(meta) obj.__table__.tometadata(meta)
return meta return meta
def needs_session(func): def needs_session(func):
""" """
Decorator which adds helpful session handling. Decorator which adds helpful session handling.
""" """
def wrapped(*args, **kwargs): def wrapped(*args, **kwargs):
session = kwargs.pop('session', None) session = kwargs.pop('session', None)
_orig_session = session _orig_session = session
if not session: if not session:
session = Session() session = Session()
res = func(session, *args, **kwargs) res = func(session, *args, **kwargs)
if not _orig_session: if not _orig_session:
session.commit() session.commit()
session.close() session.close()
return res return res
return wrapped return wrapped

View file

@ -1,46 +1,46 @@
# A generic, single database configuration. # A generic, single database configuration.
[alembic] [alembic]
# path to migration scripts # path to migration scripts
script_location = schema script_location = schema
# template used to generate migration files # template used to generate migration files
# file_template = %%(rev)s_%%(slug)s # file_template = %%(rev)s_%%(slug)s
sqlalchemy.url = driver://user:pass@localhost/dbname sqlalchemy.url = driver://user:pass@localhost/dbname
# Logging configuration # Logging configuration
[loggers] [loggers]
keys = root,sqlalchemy,alembic keys = root,sqlalchemy,alembic
[handlers] [handlers]
keys = console keys = console
[formatters] [formatters]
keys = generic keys = generic
[logger_root] [logger_root]
level = WARN level = WARN
handlers = console handlers = console
qualname = qualname =
[logger_sqlalchemy] [logger_sqlalchemy]
level = WARN level = WARN
handlers = handlers =
qualname = sqlalchemy.engine qualname = sqlalchemy.engine
[logger_alembic] [logger_alembic]
level = INFO level = INFO
handlers = handlers =
qualname = alembic qualname = alembic
[handler_console] [handler_console]
class = StreamHandler class = StreamHandler
args = (sys.stderr,) args = (sys.stderr,)
level = NOTSET level = NOTSET
formatter = generic formatter = generic
[formatter_generic] [formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S datefmt = %H:%M:%S

View file

@ -109,7 +109,7 @@ class FileMonitorService(win32serviceutil.ServiceFramework):
while not file_is_free(path): while not file_is_free(path):
# TODO: Add configurable timeout so long-open files can't hijack # TODO: Add configurable timeout so long-open files can't hijack
# our prcessing. # our prcessing.
time.sleep(0.25) win32api.SleepEx(250, True)
for action in self.monitored[key].actions: for action in self.monitored[key].actions:
if isinstance(action, tuple): if isinstance(action, tuple):
func = action[0] func = action[0]

View file

@ -1,98 +1,98 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################ ################################################################################
# #
# edbob -- Pythonic Software Framework # edbob -- Pythonic Software Framework
# Copyright © 2010-2012 Lance Edgar # Copyright © 2010-2012 Lance Edgar
# #
# This file is part of edbob. # This file is part of edbob.
# #
# edbob is free software: you can redistribute it and/or modify it under the # edbob is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free # terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) # Software Foundation, either version 3 of the License, or (at your option)
# any later version. # any later version.
# #
# edbob is distributed in the hope that it will be useful, but WITHOUT ANY # edbob is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details. # more details.
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with edbob. If not, see <http://www.gnu.org/licenses/>. # along with edbob. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
``edbob.initialization`` -- Initialization Framework ``edbob.initialization`` -- Initialization Framework
""" """
import os import os
import logging import logging
import edbob import edbob
from edbob.configuration import ( from edbob.configuration import (
AppConfigParser, AppConfigParser,
default_system_paths, default_system_paths,
default_user_paths, default_user_paths,
) )
__all__ = ['init'] __all__ = ['init']
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def init(appname='edbob', *args, **kwargs): def init(appname='edbob', *args, **kwargs):
""" """
Initializes the edbob framework, typically by first reading some config Initializes the edbob framework, typically by first reading some config
file(s) to determine which interfaces to engage. This must normally be file(s) to determine which interfaces to engage. This must normally be
called prior to doing anything really useful, as it is responsible for called prior to doing anything really useful, as it is responsible for
extending the live API in-place. extending the live API in-place.
The meaning of ``args`` is as follows: The meaning of ``args`` is as follows:
If ``args`` is empty, the ``EDBOB_CONFIG`` environment variable is first If ``args`` is empty, the ``EDBOB_CONFIG`` environment variable is first
consulted. If it is nonempty, then its value is split according to consulted. If it is nonempty, then its value is split according to
``os.pathsep`` and the resulting sequence is passed to ``os.pathsep`` and the resulting sequence is passed to
``edbob.config.read()``. ``edbob.config.read()``.
If both ``args`` and ``EDBOB_CONFIG`` are empty, the "standard" locations If both ``args`` and ``EDBOB_CONFIG`` are empty, the "standard" locations
are assumed, and the results of calling both are assumed, and the results of calling both
:func:`edbob.configuration.default_system_paths()` and :func:`edbob.configuration.default_system_paths()` and
:func:`edbob.configuration.default_user_paths()` are passed on to :func:`edbob.configuration.default_user_paths()` are passed on to
``edbob.config.read()``. ``edbob.config.read()``.
Any other values in ``args`` will be passed directly to Any other values in ``args`` will be passed directly to
``edbob.config.read()`` and so will be interpreted there. Basically they ``edbob.config.read()`` and so will be interpreted there. Basically they
are assumed to be either strings, or sequences of strings, which represent are assumed to be either strings, or sequences of strings, which represent
paths to various config files, each being read in the order in which it paths to various config files, each being read in the order in which it
appears within ``args``. (Configuration is thereby cascaded such that the appears within ``args``. (Configuration is thereby cascaded such that the
file read last will override those before it.) file read last will override those before it.)
""" """
config = AppConfigParser(appname) config = AppConfigParser(appname)
if args: if args:
config_paths = list(args) config_paths = list(args)
elif os.environ.get('EDBOB_CONFIG'): elif os.environ.get('EDBOB_CONFIG'):
config_paths = os.environ['EDBOB_CONFIG'].split(os.pathsep) config_paths = os.environ['EDBOB_CONFIG'].split(os.pathsep)
else: else:
config_paths = default_system_paths(appname) + default_user_paths(appname) config_paths = default_system_paths(appname) + default_user_paths(appname)
shell = bool(kwargs.get('shell')) shell = bool(kwargs.get('shell'))
for paths in config_paths: for paths in config_paths:
config.read(paths, recurse=not shell) config.read(paths, recurse=not shell)
config.configure_logging() config.configure_logging()
default_modules = 'edbob.time' default_modules = 'edbob.time'
modules = config.get('edbob', 'init', default=default_modules) modules = config.get('edbob', 'init', default=default_modules)
if modules: if modules:
for name in modules.split(','): for name in modules.split(','):
name = name.strip() name = name.strip()
module = __import__(name, globals(), locals(), fromlist=['init']) module = __import__(name, globals(), locals(), fromlist=['init'])
getattr(module, 'init')(config) getattr(module, 'init')(config)
# config.inited.append(name) # config.inited.append(name)
# config.inited.append('edbob') # config.inited.append('edbob')
edbob.graft(edbob, locals(), 'config') edbob.graft(edbob, locals(), 'config')
edbob.inited = True edbob.inited = True

View file

@ -51,7 +51,6 @@ def main(global_config, **settings):
# Configure edbob. Note that this is done last, primarily to allow logging # Configure edbob. Note that this is done last, primarily to allow logging
# to leverage edbob's config inheritance. # to leverage edbob's config inheritance.
edbob.basic_logging()
edbob.init('{{package}}', os.path.abspath(settings['edbob.config'])) edbob.init('{{package}}', os.path.abspath(settings['edbob.config']))
return config.make_wsgi_app() return config.make_wsgi_app()

View file

@ -27,10 +27,7 @@ whatever = you like
use = egg:{{package}} use = egg:{{package}}
pyramid.reload_templates = true pyramid.reload_templates = true
pyramid.debug_authorization = false pyramid.debug_all = true
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en pyramid.default_locale_name = en
pyramid.includes = pyramid.includes =
pyramid_debugtoolbar pyramid_debugtoolbar
@ -51,9 +48,6 @@ port = 6543
[edbob] [edbob]
include_config = ['%(here)s/production.ini'] include_config = ['%(here)s/production.ini']
[edbob.db]
sqlalchemy.url = sqlite:///{{package}}.sqlite
#################### ####################
# logging # logging
@ -62,8 +56,5 @@ sqlalchemy.url = sqlite:///{{package}}.sqlite
[logger_root] [logger_root]
level = INFO level = INFO
[logger_edbob]
level = INFO
[logger_{{package_logger}}] [logger_{{package_logger}}]
level = DEBUG level = DEBUG

View file

@ -21,10 +21,7 @@ whatever = you like
use = egg:{{package}} use = egg:{{package}}
pyramid.reload_templates = false pyramid.reload_templates = false
pyramid.debug_authorization = false pyramid.debug_all = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en pyramid.default_locale_name = en
# Hack so edbob can find this file from within WSGI app. # Hack so edbob can find this file from within WSGI app.
@ -56,6 +53,8 @@ sqlalchemy.url = postgresql://user:pass@localhost/{{package}}
[edbob] [edbob]
init = edbob.time, edbob.db init = edbob.time, edbob.db
basic_logging = True
configure_logging = True
# shell.python = ipython # shell.python = ipython
[edbob.db] [edbob.db]
@ -82,7 +81,7 @@ timezone = US/Central
#################### ####################
[loggers] [loggers]
keys = root, edbob, {{package_logger}} keys = root, {{package_logger}}
[handlers] [handlers]
keys = file, console, email keys = file, console, email
@ -95,15 +94,10 @@ keys = generic, console
handlers = file, console handlers = file, console
level = WARNING level = WARNING
[logger_edbob]
qualname = edbob
handlers =
# level = INFO
[logger_{{package_logger}}] [logger_{{package_logger}}]
qualname = {{package}} qualname = {{package}}
handlers = handlers =
level = WARNING # level = NOTSET
[handler_file] [handler_file]
class = FileHandler class = FileHandler
@ -114,12 +108,10 @@ formatter = generic
class = StreamHandler class = StreamHandler
args = (sys.stderr,) args = (sys.stderr,)
formatter = console formatter = console
# level = NOTSET
[handler_email] [handler_email]
class = handlers.SMTPHandler class = handlers.SMTPHandler
args = ('mail.example.com', '{{package}}@example.com', ['support@example.com'], '[{{project}} error]', args = ('mail.example.com', '{{package}}@example.com', ['support@example.com'], '[{{project}} error]', ('user', 'pass'))
('user', 'pass'))
level = ERROR level = ERROR
formatter = generic formatter = generic

View file

@ -47,7 +47,7 @@ def init(config):
tz = config.get('edbob.time', 'timezone') tz = config.get('edbob.time', 'timezone')
if tz: if tz:
set_timezone(tz) set_timezone(tz)
log.debug("Timezone set to '%s'" % tz) log.info("Timezone set to '%s'" % tz)
else: else:
log.warning("No timezone configured; falling back to US/Central") log.warning("No timezone configured; falling back to US/Central")
set_timezone('US/Central') set_timezone('US/Central')

View file

@ -121,7 +121,7 @@ setup(
# #
# package # low high # package # low high
'alembic', # 0.2.1 'alembic', # 0.3.4
'py-bcrypt', # 0.2 'py-bcrypt', # 0.2
'SQLAlchemy', # 0.7.6 'SQLAlchemy', # 0.7.6
'Tempita', # 0.5.1 'Tempita', # 0.5.1