diff --git a/CHANGES.txt b/CHANGES.txt index f703f67..8bfbfbc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,29 @@ +0.1.2 +----- + +* Allow config file to prevent logging configuration from happening. + + +0.1.1 +----- + +* Some random things required in production at MaMa Jean's... + + Specifically this is known to replace occurrences of e.g. ``edbob.User`` with + a more standard (properly imported) reference to ``User``. + + +0.1a29 +------ + +* Removed ``setup.cfg`` file. + +* Changed some logging instances from ``INFO`` to ``DEBUG``. + +* Updated ``repr()`` output for model classes. + + 0.1a28 ------ diff --git a/edbob/_version.py b/edbob/_version.py index f99350a..10939f0 100644 --- a/edbob/_version.py +++ b/edbob/_version.py @@ -1 +1 @@ -__version__ = '0.1a28' +__version__ = '0.1.2' diff --git a/edbob/db/extensions/auth/model.py b/edbob/db/extensions/auth/model.py index a3f36f0..f167739 100644 --- a/edbob/db/extensions/auth/model.py +++ b/edbob/db/extensions/auth/model.py @@ -51,7 +51,8 @@ class Permission(Base): permission = Column(String(50), primary_key=True) def __repr__(self): - return "" % (self.role, self.permission) + return "Permission(role_uuid={0}, permission={1})".format( + repr(self.role_uuid), repr(self.permission)) def __unicode__(self): return unicode(self.permission or '') @@ -69,7 +70,7 @@ class UserRole(Base): role_uuid = Column(String(32), ForeignKey('roles.uuid')) def __repr__(self): - return "" % (self.user, self.role) + return "UserRole(uuid={0})".format(repr(self.uuid)) class Role(Base): @@ -97,7 +98,7 @@ class Role(Base): getset_factory=getset_factory) def __repr__(self): - return "" % self.name + return "Role(uuid={0})".format(repr(self.uuid)) def __unicode__(self): return unicode(self.name or '') @@ -124,7 +125,7 @@ class User(Base): getset_factory=getset_factory) def __repr__(self): - return "" % self.username + return "User(uuid={0})".format(repr(self.uuid)) def __unicode__(self): return unicode(self.username or '') diff --git a/edbob/db/extensions/contact/model.py b/edbob/db/extensions/contact/model.py index 8cd05de..6576b5c 100644 --- a/edbob/db/extensions/contact/model.py +++ b/edbob/db/extensions/contact/model.py @@ -72,7 +72,8 @@ class PhoneNumber(Base): __mapper_args__ = {'polymorphic_on': parent_type} def __repr__(self): - return "<%s: %s>" % (self.__class__.__name__, self.number) + return "{0}(uuid={1})".format( + self.__class__.__name__, repr(self.uuid)) def __unicode__(self): return unicode(self.number) @@ -103,7 +104,8 @@ class EmailAddress(Base): __mapper_args__ = {'polymorphic_on': parent_type} def __repr__(self): - return "<%s: %s>" % (self.__class__.__name__, self.address) + return "{0}(uuid={1})".format( + self.__class__.__name__, repr(self.uuid)) def __unicode__(self): return unicode(self.address) @@ -131,7 +133,7 @@ class Person(Base): display_name = Column(String(100), default=get_person_display_name) def __repr__(self): - return "" % self.display_name + return "Person(uuid={0})".format(repr(self.uuid)) def __unicode__(self): return unicode(self.display_name or '') diff --git a/edbob/db/model.py b/edbob/db/model.py index a077ba0..b5222c3 100644 --- a/edbob/db/model.py +++ b/edbob/db/model.py @@ -54,7 +54,7 @@ class ActiveExtension(Base): name = Column(String(50), primary_key=True) def __repr__(self): - return "" % self.name + return "ActiveExtension(name={0})".format(repr(self.name)) def __str__(self): return str(self.name or '') @@ -71,4 +71,4 @@ class Setting(Base): value = Column(Text) def __repr__(self): - return "" % self.name + return "Setting(name={0})".format(repr(self.name)) diff --git a/edbob/filemon/linux.py b/edbob/filemon/linux.py index 6d5ab83..ae1c4a0 100644 --- a/edbob/filemon/linux.py +++ b/edbob/filemon/linux.py @@ -28,11 +28,19 @@ import sys import os.path -import pyinotify import threading import Queue import logging +try: + import pyinotify +except ImportError: + # Mock out for testing on Windows. + class Dummy(object): + pass + pyinotify = Dummy() + pyinotify.ProcessEvent = Dummy + import edbob from edbob import filemon from edbob.daemon import Daemon diff --git a/edbob/initialization.py b/edbob/initialization.py index 410b572..af2aeff 100644 --- a/edbob/initialization.py +++ b/edbob/initialization.py @@ -89,7 +89,8 @@ def init(appname='edbob', *args, **kwargs): shell = kwargs.get('shell', False) for paths in config_paths: config.read(paths, recurse=not shell) - config.configure_logging() + if config.getboolean('edbob', 'configure_logging', default=True): + config.configure_logging() default_modules = 'edbob.time' modules = config.get('edbob', 'init', default=default_modules) diff --git a/edbob/pyramid/views/people.py b/edbob/pyramid/views/people.py index afdf657..1f50470 100644 --- a/edbob/pyramid/views/people.py +++ b/edbob/pyramid/views/people.py @@ -33,37 +33,38 @@ from sqlalchemy import and_ # from formalchemy import Field -import edbob # from edbob.pyramid import filters # from edbob.pyramid import forms # from edbob.pyramid import grids # from edbob.pyramid import Session from edbob.pyramid.views import SearchableAlchemyGridView, CrudView +from edbob.db.extensions.contact.model import ( + Person, PersonEmailAddress, PersonPhoneNumber) class PeopleGrid(SearchableAlchemyGridView): - mapped_class = edbob.Person + mapped_class = Person config_prefix = 'people' sort = 'first_name' def join_map(self): return { 'email': - lambda q: q.outerjoin(edbob.PersonEmailAddress, and_( - edbob.PersonEmailAddress.parent_uuid == edbob.Person.uuid, - edbob.PersonEmailAddress.preference == 1)), + lambda q: q.outerjoin(PersonEmailAddress, and_( + PersonEmailAddress.parent_uuid == Person.uuid, + PersonEmailAddress.preference == 1)), 'phone': - lambda q: q.outerjoin(edbob.PersonPhoneNumber, and_( - edbob.PersonPhoneNumber.parent_uuid == edbob.Person.uuid, - edbob.PersonPhoneNumber.preference == 1)), + lambda q: q.outerjoin(PersonPhoneNumber, and_( + PersonPhoneNumber.parent_uuid == Person.uuid, + PersonPhoneNumber.preference == 1)), } def filter_map(self): return self.make_filter_map( ilike=['first_name', 'last_name'], - email=self.filter_ilike(edbob.PersonEmailAddress.address), - phone=self.filter_ilike(edbob.PersonPhoneNumber.number)) + email=self.filter_ilike(PersonEmailAddress.address), + phone=self.filter_ilike(PersonPhoneNumber.number)) def filter_config(self): return self.make_filter_config( @@ -77,8 +78,8 @@ class PeopleGrid(SearchableAlchemyGridView): def sort_map(self): return self.make_sort_map( 'first_name', 'last_name', - email=self.sorter(edbob.PersonEmailAddress.address), - phone=self.sorter(edbob.PersonPhoneNumber.number)) + email=self.sorter(PersonEmailAddress.address), + phone=self.sorter(PersonPhoneNumber.number)) def grid(self): g = self.make_grid() @@ -97,7 +98,7 @@ class PeopleGrid(SearchableAlchemyGridView): class PersonCrud(CrudView): - mapped_class = edbob.Person + mapped_class = Person home_route = 'people' def fieldset(self, model): diff --git a/edbob/pyramid/views/roles.py b/edbob/pyramid/views/roles.py index 2d0aa57..531c95a 100644 --- a/edbob/pyramid/views/roles.py +++ b/edbob/pyramid/views/roles.py @@ -32,10 +32,10 @@ import formalchemy from webhelpers.html import tags from webhelpers.html.builder import HTML -import edbob from edbob.db import auth from edbob.pyramid import Session from edbob.pyramid.views import SearchableAlchemyGridView, CrudView +from edbob.db.extensions.auth.model import Role default_permissions = [ @@ -68,7 +68,7 @@ default_permissions = [ class RolesGrid(SearchableAlchemyGridView): - mapped_class = edbob.Role + mapped_class = Role config_prefix = 'roles' sort = 'name' @@ -161,7 +161,7 @@ def PermissionsFieldRenderer(permissions, *args, **kwargs): class RoleCrud(CrudView): - mapped_class = edbob.Role + mapped_class = Role home_route = 'roles' permissions = default_permissions diff --git a/edbob/pyramid/views/users.py b/edbob/pyramid/views/users.py index a2e201e..42ba461 100644 --- a/edbob/pyramid/views/users.py +++ b/edbob/pyramid/views/users.py @@ -32,28 +32,29 @@ from webhelpers.html.builder import HTML import formalchemy from formalchemy.fields import SelectFieldRenderer -import edbob from edbob.db import auth from edbob.pyramid import Session from edbob.pyramid.views import SearchableAlchemyGridView, CrudView +from edbob.db.extensions.auth.model import User, Role +from edbob.db.extensions.contact.model import Person class UsersGrid(SearchableAlchemyGridView): - mapped_class = edbob.User + mapped_class = User config_prefix = 'users' sort = 'username' def join_map(self): return { 'person': - lambda q: q.outerjoin(edbob.Person), + lambda q: q.outerjoin(Person), } def filter_map(self): return self.make_filter_map( ilike=['username'], - person=self.filter_ilike(edbob.Person.display_name)) + person=self.filter_ilike(Person.display_name)) def filter_config(self): return self.make_filter_config( @@ -65,7 +66,7 @@ class UsersGrid(SearchableAlchemyGridView): def sort_map(self): return self.make_sort_map( 'username', - person=self.sorter(edbob.Person.display_name)) + person=self.sorter(Person.display_name)) def grid(self): g = self.make_grid() @@ -92,7 +93,7 @@ def RolesFieldRenderer(request): class RolesFieldRenderer(SelectFieldRenderer): def render_readonly(self, **kwargs): - roles = Session.query(edbob.Role) + roles = Session.query(Role) html = '' for uuid in self.value: role = roles.get(uuid) @@ -117,15 +118,15 @@ class RolesField(formalchemy.Field): return [x.uuid for x in user.roles] def get_options(self): - q = Session.query(edbob.Role.name, edbob.Role.uuid) - q = q.filter(edbob.Role.uuid != auth.guest_role(Session()).uuid) - q = q.order_by(edbob.Role.name) + q = Session.query(Role.name, Role.uuid) + q = q.filter(Role.uuid != auth.guest_role(Session()).uuid) + q = q.order_by(Role.name) return q.all() def sync(self): if not self.is_readonly(): user = self.model - roles = Session.query(edbob.Role) + roles = Session.query(Role) data = self.renderer.deserialize() user.roles = [roles.get(x) for x in data] @@ -140,7 +141,7 @@ class _ProtectedPersonRenderer(formalchemy.FieldRenderer): def ProtectedPersonRenderer(uuid): - person = Session.query(edbob.Person).get(uuid) + person = Session.query(Person).get(uuid) assert person return type('ProtectedPersonRenderer', (_ProtectedPersonRenderer,), {'person': person}) @@ -187,7 +188,7 @@ class PasswordField(formalchemy.Field): class UserCrud(CrudView): - mapped_class = edbob.User + mapped_class = User home_route = 'users' def fieldset(self, user): @@ -213,7 +214,7 @@ class UserCrud(CrudView): del fs.confirm_password # if fs.edit and user.person: - if isinstance(user, edbob.User) and user.person: + if isinstance(user, User) and user.person: fs.person.set(readonly=True, renderer=LinkedPersonRenderer(self.request)) diff --git a/edbob/tests/__init__.py b/edbob/tests/__init__.py new file mode 100644 index 0000000..e5c6980 --- /dev/null +++ b/edbob/tests/__init__.py @@ -0,0 +1,19 @@ + +import unittest + +from pyramid import testing + + +class TestCase(unittest.TestCase): + """ + Base class for all test suites. + """ + + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def test_something(self): + self.assertTrue(1) diff --git a/edbob/win32.py b/edbob/win32.py index 96a76bd..314ddaa 100644 --- a/edbob/win32.py +++ b/edbob/win32.py @@ -38,9 +38,17 @@ if sys.platform == 'win32': # docs should build for everyone import win32file import win32print import win32service - import win32serviceutil import winerror +try: + import win32serviceutil +except ImportError: + # Mock out for testing on Linux. + class Object(object): + pass + win32serviceutil = Object() + win32serviceutil.ServiceFramework = Object + import edbob diff --git a/fabfile.py b/fabfile.py index 8e01404..1eb4728 100644 --- a/fabfile.py +++ b/fabfile.py @@ -22,25 +22,15 @@ # ################################################################################ -import os.path +import shutil from fabric.api import * -execfile(os.path.join(os.path.dirname(__file__), 'edbob', '_version.py')) - - @task def release(): """ Release a new version of 'edbob'. """ - - local("python setup.py egg_info --tag-build='' sdist --formats=gztar") - - filename = 'edbob-{0}.tar.gz'.format(__version__) - - put(os.path.join('dist', filename), '/srv/pypi/{0}'.format(filename)) - with cd('/srv/pypi'): - run('rm --recursive --force simple') - run('compoze index') + shutil.rmtree('edbob.egg-info') + local('python setup.py sdist --formats=gztar register upload') diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..28110c5 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,7 @@ +[nosetests] +nocapture = 1 +cover-package = edbob +cover-erase = 1 +cover-inclusive = 1 +cover-html = 1 +cover-html-dir = htmlcov diff --git a/setup.py b/setup.py index f81a1fb..6c9bf9a 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,10 @@ requires = [ 'decorator', # 3.3.2 'lockfile', # 0.9.1 'progressbar', # 2.3 - 'pytz', # 2012b + + # Hardcode ``pytz`` minimum since apparently it isn't (any longer?) enough + # to simply require the library. + 'pytz>=2013b', # 2013b ] if sys.version_info < (2, 7): @@ -209,6 +212,8 @@ setup( install_requires = requires, extras_require = extras, + tests_require = requires + ['nose'], + test_suite = 'nose.collector', packages = find_packages(), include_package_data = True,