diff --git a/abilian/app.py b/abilian/app.py index 4cb67ea3..513105ef 100644 --- a/abilian/app.py +++ b/abilian/app.py @@ -250,19 +250,21 @@ def __init__(self, name=None, config=None, *args, **kwargs): self.config['BABEL_ACCEPT_LANGUAGES'] = languages self._jinja_loaders = list() - self.register_jinja_loaders(jinja2.PackageLoader('abilian.web', - 'templates')) + self.register_jinja_loaders( + jinja2.PackageLoader('abilian.web', 'templates')) js_filters = (('closure_js',) if self.config.get('PRODUCTION', False) else None) self._assets_bundles = { - 'css': {'options': dict(filters=('less', 'cssmin'), - output='style-%(version)s.min.css',)}, - 'js-top': {'options': dict(output='top-%(version)s.min.js', - filters=js_filters,)}, - 'js': {'options': dict(output='app-%(version)s.min.js', - filters=js_filters)}, + 'css': {'options': dict( + filters=('less', 'cssmin'), + output='style-%(version)s.min.css',)}, + 'js-top': {'options': dict( + output='top-%(version)s.min.js', + filters=js_filters,)}, + 'js': {'options': dict( + output='app-%(version)s.min.js', filters=js_filters)}, } # bundles for JS translations @@ -270,8 +272,8 @@ def __init__(self, name=None, config=None, *args, **kwargs): code = 'js-i18n-' + lang filename = 'lang-' + lang + '-%(version)s.min.js' self._assets_bundles[code] = { - 'options': dict(output=filename, - filters=js_filters), + 'options': dict( + output=filename, filters=js_filters), } for http_error_code in (403, 404, 500): @@ -280,17 +282,17 @@ def __init__(self, name=None, config=None, *args, **kwargs): with self.app_context(): self.init_extensions() self.register_plugins() - self.add_access_controller('static', - allow_access_for_roles(Anonymous), - endpoint=True) + self.add_access_controller( + 'static', allow_access_for_roles(Anonymous), endpoint=True) # debugtoolbar: this is needed to have it when not authenticated on a # private site. We cannot do this in init_debug_toolbar, since auth # service is not yet installed self.add_access_controller('debugtoolbar', allow_access_for_roles(Anonymous),) - self.add_access_controller('_debug_toolbar.static', - allow_access_for_roles(Anonymous), - endpoint=True) + self.add_access_controller( + '_debug_toolbar.static', + allow_access_for_roles(Anonymous), + endpoint=True) self.maybe_register_setup_wizard() self._finalize_assets_setup() @@ -368,8 +370,9 @@ def init_breadcrumbs(self): This happens during `request_started` event, which is triggered before any url_value_preprocessor and `before_request` handlers. """ - g.breadcrumb.append(BreadcrumbItem(icon='home', - url='/' + request.script_root)) + g.breadcrumb.append( + BreadcrumbItem( + icon='home', url='/' + request.script_root)) def check_instance_folder(self, create=False): """Verify instance folder exists, is a directory, and has necessary permissions. @@ -443,15 +446,15 @@ def setup_logging(self): logging_file = self.config.get('LOGGING_CONFIG_FILE') if logging_file: - logging_file = os.path.abspath(os.path.join(self.instance_path, - logging_file)) + logging_file = os.path.abspath( + os.path.join(self.instance_path, logging_file)) else: logging_file = resource_filename(__name__, 'default_logging.yml') if logging_file.endswith('.conf'): # old standard 'ini' file config - logging.config.fileConfig(logging_file, - disable_existing_loggers=False) + logging.config.fileConfig( + logging_file, disable_existing_loggers=False) elif logging_file.endswith('.yml'): # yml config file logging_cfg = yaml.load(open(logging_file, 'r')) @@ -512,9 +515,8 @@ def init_extensions(self): babel.timezone_selector_func = None babel.init_app(self) - babel.add_translations('wtforms', - translations_dir='locale', - domain='wtforms') + babel.add_translations( + 'wtforms', translations_dir='locale', domain='wtforms') babel.add_translations('abilian') babel.localeselector(abilian.i18n.localeselector) babel.timezoneselector(abilian.i18n.timezoneselector) @@ -605,9 +607,8 @@ def add_url_rule(self, rule, endpoint=None, view_func=None, **options): **options) if roles: - self.add_access_controller(endpoint, - allow_access_for_roles(roles), - endpoint=True) + self.add_access_controller( + endpoint, allow_access_for_roles(roles), endpoint=True) def add_access_controller(self, name, func, endpoint=False): """Add an access controller. @@ -644,29 +645,30 @@ def add_static_url(self, url_path, directory, endpoint=None, roles=None): `/path/to/myplugin/resources` from url `http://.../static/myplugin` """ url_path = self.static_url_path + '/' + url_path + '/' - self.add_url_rule(url_path, - endpoint=endpoint, - view_func=partial(send_file_from_directory, - directory=directory), - roles=roles) - self.add_access_controller(endpoint, - allow_access_for_roles(Anonymous), - endpoint=True) + self.add_url_rule( + url_path, + endpoint=endpoint, + view_func=partial( + send_file_from_directory, directory=directory), + roles=roles) + self.add_access_controller( + endpoint, allow_access_for_roles(Anonymous), endpoint=True) # # Templating and context injection setup # def create_jinja_environment(self): env = Flask.create_jinja_environment(self) - env.globals.update(app=current_app, - csrf=csrf, - get_locale=babel_get_locale, - local_dt=abilian.core.util.local_dt, - _n=abilian.i18n._n, - url_for=url_for, - user_photo_url=user_photo_url, - NO_VALUE=NO_VALUE, - NEVER_SET=NEVER_SET,) + env.globals.update( + app=current_app, + csrf=csrf, + get_locale=babel_get_locale, + local_dt=abilian.core.util.local_dt, + _n=abilian.i18n._n, + url_for=url_for, + user_photo_url=user_photo_url, + NO_VALUE=NO_VALUE, + NEVER_SET=NEVER_SET,) init_filters(env) return env @@ -815,10 +817,11 @@ def create_db(self): db.create_all() if User.query.get(0) is None: - root = User(id=0, - last_name='SYSTEM', - email='system@example.com', - can_login=False) + root = User( + id=0, + last_name='SYSTEM', + email='system@example.com', + can_login=False) db.session.add(root) db.session.commit() @@ -858,10 +861,11 @@ def _setup_asset_extension(self): # static minified are here assets.url = self.static_url_path + '/min' assets.append_path(str(assets_dir), assets.url) - self.add_static_url('min', - str(assets_dir), - endpoint='webassets_static', - roles=Anonymous,) + self.add_static_url( + 'min', + str(assets_dir), + endpoint='webassets_static', + roles=Anonymous,) def _finalize_assets_setup(self): assets = self.extensions['webassets'] diff --git a/abilian/core/commands/base.py b/abilian/core/commands/base.py index aedfd0b1..be7c46cc 100644 --- a/abilian/core/commands/base.py +++ b/abilian/core/commands/base.py @@ -7,12 +7,11 @@ import runpy from pprint import pformat -from six.moves import input -from six.moves import urllib import sqlalchemy as sa from flask import current_app from flask_script import Manager, prompt_pass from six import text_type +from six.moves import input, urllib from abilian.core.extensions import db from abilian.core.logging import patch_logger @@ -94,23 +93,22 @@ def log_config(config): logger.setLevel(original_level) -@manager.option('-p', - '--port', - dest='port', - help='listening port', - default=5000) -@manager.option('--show-config', - dest='show_config', - action='store_const', - const=True, - default=False, - help='show application configuration on startup') -@manager.option('--ssl', - dest='ssl', - action='store_const', - default=False, - const=True, - help='Enable werkzeug SSL') +@manager.option( + '-p', '--port', dest='port', help='listening port', default=5000) +@manager.option( + '--show-config', + dest='show_config', + action='store_const', + const=True, + default=False, + help='show application configuration on startup') +@manager.option( + '--ssl', + dest='ssl', + action='store_const', + default=False, + const=True, + help='Enable werkzeug SSL') def run(port, show_config, ssl): """Like runserver. May also print application configuration if used with --show-config. @@ -166,25 +164,25 @@ def routes(): # user commands email_opt = manager.option('email', help='user\'s email') -password_opt = manager.option('-p', - '--password', - dest='password', - default=None, - help='If absent, a prompt will ask for password',) -role_opt = manager.option('-r', - '--role', - dest='role', - choices=[r.name for r in Role.assignable_roles()],) -name_opt = manager.option('-n', - '--name', - dest='name', - default=None, - help='Last name (e.g "Smith")') -firstname_opt = manager.option('-f', - '--firstname', - dest='first_name', - default=None, - help='Fist name (e.g. "John")') +password_opt = manager.option( + '-p', + '--password', + dest='password', + default=None, + help='If absent, a prompt will ask for password',) +role_opt = manager.option( + '-r', + '--role', + dest='role', + choices=[r.name for r in Role.assignable_roles()],) +name_opt = manager.option( + '-n', '--name', dest='name', default=None, help='Last name (e.g "Smith")') +firstname_opt = manager.option( + '-f', + '--firstname', + dest='first_name', + default=None, + help='Fist name (e.g. "John")') @email_opt @@ -203,11 +201,12 @@ def createuser(email, password, role=None, name=None, first_name=None): if password is None: password = prompt_pass(u'Password') - user = User(email=email, - password=password, - last_name=name, - first_name=first_name, - can_login=True) + user = User( + email=email, + password=password, + last_name=name, + first_name=first_name, + can_login=True) db.session.add(user) if role in ('admin',): diff --git a/abilian/core/commands/config.py b/abilian/core/commands/config.py index cb9811a4..5cc66e8a 100644 --- a/abilian/core/commands/config.py +++ b/abilian/core/commands/config.py @@ -14,8 +14,9 @@ from .base import log_config, logger #: sub-manager for config commands -manager = Manager(description='Show config / create default config', - help='Show config / create default config') +manager = Manager( + description='Show config / create default config', + help='Show config / create default config') @manager.command diff --git a/abilian/core/commands/indexing.py b/abilian/core/commands/indexing.py index 6a44b670..7233db67 100644 --- a/abilian/core/commands/indexing.py +++ b/abilian/core/commands/indexing.py @@ -36,17 +36,16 @@ def reindex(clear=False, progressive=False, batch_size=None): svc = current_app.services['indexing'] adapted = svc.adapted index = svc.app_state.indexes['default'] - session = Session(bind=current_app.db.session.get_bind(None, None), - autocommit=True) + session = Session( + bind=current_app.db.session.get_bind(None, None), autocommit=True) setattr(session, '_model_changes', {}) # please flask-sqlalchemy <= 1.0 indexed = set() cleared = set() if batch_size is not None: batch_size = int(batch_size) - strategy_kw = dict(clear=clear, - progressive=progressive, - batch_size=batch_size) + strategy_kw = dict( + clear=clear, progressive=progressive, batch_size=batch_size) strategy = progressive_mode if progressive else single_transaction strategy = strategy(index, **strategy_kw) next(strategy) # starts generator diff --git a/abilian/core/entities.py b/abilian/core/entities.py index 2d79e400..e3d5d86c 100644 --- a/abilian/core/entities.py +++ b/abilian/core/entities.py @@ -127,12 +127,12 @@ class _EntityInherit(object): @declared_attr def id(cls): - return Column(Integer, - ForeignKey('entity.id', - use_alter=True, - name='fk_inherited_entity_id'), - primary_key=True, - info=SYSTEM | SEARCHABLE) + return Column( + Integer, + ForeignKey( + 'entity.id', use_alter=True, name='fk_inherited_entity_id'), + primary_key=True, + info=SYSTEM | SEARCHABLE) @declared_attr def __mapper_args__(cls): @@ -148,9 +148,8 @@ class EntityQuery(db.Model.query_class): def with_permission(self, permission, user=None): security = current_app.services['security'] model = self._entity_zero().entity_zero.entity - expr = security.query_entity_with_permission(permission, - user, - Model=model) + expr = security.query_entity_with_permission( + permission, user, Model=model) return self.filter(expr) @@ -197,8 +196,8 @@ def __new__(mcs, classname, bases, d): for permission, roles in default_permissions) d['__default_permissions__'] = default_permissions - d['SLUG_SEPARATOR'] = text_type(d.get('SLUG_SEPARATOR', - Entity.SLUG_SEPARATOR)) + d['SLUG_SEPARATOR'] = text_type( + d.get('SLUG_SEPARATOR', Entity.SLUG_SEPARATOR)) cls = BaseMeta.__new__(mcs, classname, bases, d) diff --git a/abilian/core/jinjaext.py b/abilian/core/jinjaext.py index 7bb17d34..8005d875 100644 --- a/abilian/core/jinjaext.py +++ b/abilian/core/jinjaext.py @@ -57,12 +57,11 @@ def parse(self, parser): # now we parse body of the block body = parser.parse_statements( - ['name:enddeferJS', 'name:enddeferredJS'], - drop_needle=True) + ['name:enddeferJS', 'name:enddeferredJS'], drop_needle=True) method = 'defer_nodes' if tag == 'deferJS' else 'collect_deferred' - return nodes.CallBlock( - self.call_method(method, []), [], [], body).set_lineno(lineno) + return nodes.CallBlock(self.call_method(method, []), [], [], + body).set_lineno(lineno) def defer_nodes(self, caller): body = '
{}
'.format(caller().strip()) diff --git a/abilian/core/models/attachment.py b/abilian/core/models/attachment.py index 3d74839c..2c97373c 100644 --- a/abilian/core/models/attachment.py +++ b/abilian/core/models/attachment.py @@ -85,22 +85,25 @@ def __mapper_args__(cls): entity_id = Column(Integer, ForeignKey(Entity.id), nullable=False) #: owning entity - entity = relationship(Entity, - lazy='immediate', - foreign_keys=[entity_id], - backref=backref(ATTRIBUTE, - lazy='select', - order_by='Attachment.created_at', - cascade="all, delete-orphan",)) + entity = relationship( + Entity, + lazy='immediate', + foreign_keys=[entity_id], + backref=backref( + ATTRIBUTE, + lazy='select', + order_by='Attachment.created_at', + cascade="all, delete-orphan",)) blob_id = Column(Integer, sa.ForeignKey(Blob.id), nullable=False) #: file. Stored in a :class:`Blob` blob = relationship(Blob, cascade='all, delete', foreign_keys=[blob_id]) - description = Column(UnicodeText(), - nullable=False, - default='', - server_default='',) + description = Column( + UnicodeText(), + nullable=False, + default='', + server_default='',) def __repr__(self): class_ = self.__class__ diff --git a/abilian/core/models/base.py b/abilian/core/models/base.py index ea92589a..80683b00 100644 --- a/abilian/core/models/base.py +++ b/abilian/core/models/base.py @@ -81,12 +81,12 @@ def object_key(self): class TimestampedMixin(object): #: creation date - created_at = Column(DateTime, - default=datetime.utcnow, - info=SYSTEM | SEARCHABLE) + created_at = Column( + DateTime, default=datetime.utcnow, info=SYSTEM | SEARCHABLE) #: last modification date - updated_at = Column(DateTime, - default=datetime.utcnow, - onupdate=datetime.utcnow, - info=SYSTEM | SEARCHABLE) + updated_at = Column( + DateTime, + default=datetime.utcnow, + onupdate=datetime.utcnow, + info=SYSTEM | SEARCHABLE) deleted_at = Column(DateTime, default=None, info=SYSTEM) diff --git a/abilian/core/models/comment.py b/abilian/core/models/comment.py index a02b942e..047c00c9 100644 --- a/abilian/core/models/comment.py +++ b/abilian/core/models/comment.py @@ -88,18 +88,19 @@ def __mapper_args__(cls): entity_id = Column(Integer, ForeignKey(Entity.id), nullable=False) #: Commented entity - entity = relationship(Entity, - lazy='immediate', - foreign_keys=[entity_id], - backref=backref(ATTRIBUTE, - lazy='select', - order_by='Comment.created_at', - cascade="all, delete-orphan",)) + entity = relationship( + Entity, + lazy='immediate', + foreign_keys=[entity_id], + backref=backref( + ATTRIBUTE, + lazy='select', + order_by='Comment.created_at', + cascade="all, delete-orphan",)) #: comment's main content - body = Column(UnicodeText(), - sa.CheckConstraint("trim(body) != ''"), - nullable=False) + body = Column( + UnicodeText(), sa.CheckConstraint("trim(body) != ''"), nullable=False) @property def history(self): diff --git a/abilian/core/models/owned.py b/abilian/core/models/owned.py index 4f13980e..19dc988b 100644 --- a/abilian/core/models/owned.py +++ b/abilian/core/models/owned.py @@ -37,11 +37,12 @@ def creator_id(cls): @declared_attr def creator(cls): pj = "User.id == %s.creator_id" % cls.__name__ - return relationship(User, - primaryjoin=pj, - lazy='joined', - uselist=False, - info=SYSTEM | SEARCHABLE) + return relationship( + User, + primaryjoin=pj, + lazy='joined', + uselist=False, + info=SYSTEM | SEARCHABLE) @property def creator_name(self): @@ -54,11 +55,12 @@ def owner_id(cls): @declared_attr def owner(cls): pj = "User.id == %s.owner_id" % cls.__name__ - return relationship(User, - primaryjoin=pj, - lazy='joined', - uselist=False, - info=EDITABLE | AUDITABLE | SEARCHABLE) + return relationship( + User, + primaryjoin=pj, + lazy='joined', + uselist=False, + info=EDITABLE | AUDITABLE | SEARCHABLE) @property def owner_name(self): diff --git a/abilian/core/models/subjects.py b/abilian/core/models/subjects.py index e49589cb..5442054a 100644 --- a/abilian/core/models/subjects.py +++ b/abilian/core/models/subjects.py @@ -38,34 +38,36 @@ Column('follower_id', Integer, ForeignKey('user.id')), Column('followee_id', Integer, ForeignKey('user.id')), UniqueConstraint('follower_id', 'followee_id'),) -membership = Table('membership', - db.Model.metadata, - Column('user_id', - Integer, - ForeignKey('user.id', - onupdate='CASCADE', - ondelete='CASCADE')), - Column('group_id', - Integer, - ForeignKey('group.id', - onupdate='CASCADE', - ondelete='CASCADE')), - UniqueConstraint('user_id', 'group_id'),) +membership = Table( + 'membership', + db.Model.metadata, + Column( + 'user_id', + Integer, + ForeignKey( + 'user.id', onupdate='CASCADE', ondelete='CASCADE')), + Column( + 'group_id', + Integer, + ForeignKey( + 'group.id', onupdate='CASCADE', ondelete='CASCADE')), + UniqueConstraint('user_id', 'group_id'),) # Should not be needed (?) -administratorship = Table('administratorship', - db.Model.metadata, - Column('user_id', - Integer, - ForeignKey('user.id', - onupdate='CASCADE', - ondelete='CASCADE')), - Column('group_id', - Integer, - ForeignKey('group.id', - onupdate='CASCADE', - ondelete='CASCADE')), - UniqueConstraint('user_id', 'group_id'),) +administratorship = Table( + 'administratorship', + db.Model.metadata, + Column( + 'user_id', + Integer, + ForeignKey( + 'user.id', onupdate='CASCADE', ondelete='CASCADE')), + Column( + 'group_id', + Integer, + ForeignKey( + 'group.id', onupdate='CASCADE', ondelete='CASCADE')), + UniqueConstraint('user_id', 'group_id'),) _RANDOM_PASSWORD_CHARS = ( string.ascii_letters + string.digits + string.punctuation) @@ -179,9 +181,8 @@ class User(Principal, UserMixin, db.Model): # System information email = Column(UnicodeText, nullable=False) can_login = Column(Boolean, nullable=False, default=True) - password = Column(UnicodeText, - default="*", - info={'audit_hide_content': True}) + password = Column( + UnicodeText, default="*", info={'audit_hide_content': True}) photo = deferred(Column(LargeBinary)) @@ -258,8 +259,7 @@ def is_online(self): @property def name(self): name = u'{first_name} {last_name}'.format( - first_name=self.first_name or u'', - last_name=self.last_name or u'') + first_name=self.first_name or u'', last_name=self.last_name or u'') return name.strip() or u'Unknown' @property @@ -288,9 +288,10 @@ def _add_user_indexes(mapper, class_): # it in __table_args__. # # see: https://groups.google.com/d/msg/sqlalchemy/CgSJUlelhGs/_Nj3f201hs4J - idx = sa.schema.Index('user_unique_lowercase_email', - sa.sql.func.lower(class_.email), - unique=True) + idx = sa.schema.Index( + 'user_unique_lowercase_email', + sa.sql.func.lower(class_.email), + unique=True) idx.info['engines'] = ('postgresql',) @@ -305,15 +306,14 @@ class Group(Principal, db.Model): name = Column(UnicodeText, nullable=False, info=SEARCHABLE) description = Column(UnicodeText, info=SEARCHABLE) - members = relationship("User", - collection_class=set, - secondary=membership, - backref=backref('groups', - lazy='select', - collection_class=set)) - admins = relationship("User", - collection_class=set, - secondary=administratorship) + members = relationship( + "User", + collection_class=set, + secondary=membership, + backref=backref( + 'groups', lazy='select', collection_class=set)) + admins = relationship( + "User", collection_class=set, secondary=administratorship) photo = deferred(Column(LargeBinary)) diff --git a/abilian/core/models/tag.py b/abilian/core/models/tag.py index 3faf2797..f0c10d8f 100644 --- a/abilian/core/models/tag.py +++ b/abilian/core/models/tag.py @@ -57,17 +57,16 @@ def is_support_tagging(obj): return True -entity_tag_tbl = sa.Table('entity_tags', - Model.metadata, - sa.Column('tag_id', - sa.Integer, - sa.ForeignKey('tag.id', - ondelete='CASCADE')), - sa.Column('entity_id', - sa.Integer, - sa.ForeignKey(Entity.id, - ondelete='CASCADE')), - sa.UniqueConstraint('tag_id', 'entity_id'),) +entity_tag_tbl = sa.Table( + 'entity_tags', + Model.metadata, + sa.Column( + 'tag_id', sa.Integer, sa.ForeignKey( + 'tag.id', ondelete='CASCADE')), + sa.Column( + 'entity_id', sa.Integer, sa.ForeignKey( + Entity.id, ondelete='CASCADE')), + sa.UniqueConstraint('tag_id', 'entity_id'),) @total_ordering @@ -80,10 +79,11 @@ class Tag(IdMixin, Model): __tablename__ = 'tag' #: namespace - ns = sa.Column(sa.UnicodeText(), - nullable=False, - default='default', - server_default='default') + ns = sa.Column( + sa.UnicodeText(), + nullable=False, + default='default', + server_default='default') #: Label visible to the user label = sa.Column(sa.UnicodeText(), nullable=False) @@ -93,18 +93,18 @@ class Tag(IdMixin, Model): Entity, collection_class=set, secondary=entity_tag_tbl, - backref=sa.orm.backref(TAGS_ATTR, collection_class=set),) + backref=sa.orm.backref( + TAGS_ATTR, collection_class=set),) __mapper_args__ = {'order_by': label,} __table_args__ = ( sa.UniqueConstraint(ns, label), # namespace is not empty and is not surrounded by space characters - sa.CheckConstraint(sa.sql.and_( - sa.sql.func.trim(ns) == ns, ns != u''),), + sa.CheckConstraint(sa.sql.and_(sa.sql.func.trim(ns) == ns, ns != u''),), # label is not empty and is not surrounded by space characters - sa.CheckConstraint(sa.sql.and_( - sa.sql.func.trim(label) == label, label != u''),),) + sa.CheckConstraint( + sa.sql.and_(sa.sql.func.trim(label) == label, label != u''),),) def __unicode__(self): return self.label diff --git a/abilian/core/sentry.py b/abilian/core/sentry.py index ac981086..5ced58ea 100644 --- a/abilian/core/sentry.py +++ b/abilian/core/sentry.py @@ -23,8 +23,9 @@ def _on_user_logged_in(self, app, user, *args, **kwargs): def raven_js_url(self): url = u'//cdn.ravenjs.com/{version}/{plugins}/raven.min.js' cfg = current_app.config - return url.format(version=text_type(cfg['SENTRY_JS_VERSION']), - plugins=','.join(cfg['SENTRY_JS_PLUGINS'])) + return url.format( + version=text_type(cfg['SENTRY_JS_VERSION']), + plugins=','.join(cfg['SENTRY_JS_PLUGINS'])) __all__ = ['Sentry'] diff --git a/abilian/core/sqlalchemy.py b/abilian/core/sqlalchemy.py index a3cdba10..d1562281 100644 --- a/abilian/core/sqlalchemy.py +++ b/abilian/core/sqlalchemy.py @@ -112,8 +112,9 @@ def filter_cols(model, *filtered_columns): Useful for defer() for example to retain only columns of interest """ m = sa.orm.class_mapper(model) - return list(set(p.key for p in m.iterate_properties - if hasattr(p, 'columns')).difference(filtered_columns)) + return list( + set(p.key for p in m.iterate_properties + if hasattr(p, 'columns')).difference(filtered_columns)) class MutationDict(Mutable, dict): diff --git a/abilian/i18n.py b/abilian/i18n.py index dff163fd..f5c1c3eb 100644 --- a/abilian/i18n.py +++ b/abilian/i18n.py @@ -261,9 +261,8 @@ def _get_translations_multi_paths(): # reverse order: thus the application catalog is loaded last, so that # translations from libraries can be overriden for (dirname, domain) in reversed(babel_ext._translations_paths): - trs = Translations.load(dirname, - locales=[flask_babel.get_locale()], - domain=domain) + trs = Translations.load( + dirname, locales=[flask_babel.get_locale()], domain=domain) # babel.support.Translations is a subclass of # babel.support.NullTranslations, so we test if object has a 'merge' diff --git a/abilian/services/antivirus/__init__.py b/abilian/services/antivirus/__init__.py index 9ba13926..a7ea9412 100644 --- a/abilian/services/antivirus/__init__.py +++ b/abilian/services/antivirus/__init__.py @@ -29,8 +29,8 @@ conf_path = pathlib.Path('/etc', 'clamav', 'clamd.conf') if conf_path.exists(): conf_lines = [l.strip() for l in conf_path.open('rt').readlines()] - CLAMD_CONF = dict(l.split(u' ', 1) for l in conf_lines - if not l.startswith('#')) + CLAMD_CONF = dict( + l.split(u' ', 1) for l in conf_lines if not l.startswith('#')) def _size_to_int(size_str): multiplier = 0 diff --git a/abilian/services/audit/service.py b/abilian/services/audit/service.py index db32b375..e01a5907 100644 --- a/abilian/services/audit/service.py +++ b/abilian/services/audit/service.py @@ -115,24 +115,18 @@ def register_class(self, entity_class, app_state=None): if info.get('auditable', True): entity_class.__auditable__.audited_attrs.add(attr) - event.listen(attr, - "set", - self.set_attribute, - active_history=True) + event.listen( + attr, "set", self.set_attribute, active_history=True) for relation in mapper.relationships: if relation.direction is not sa.orm.interfaces.MANYTOMANY: continue attr = getattr(entity_class, relation.key) entity_class.__auditable__.collection_attrs.add(attr) - event.listen(attr, - "append", - self.collection_append, - active_history=True) - event.listen(attr, - "remove", - self.collection_remove, - active_history=True) + event.listen( + attr, "append", self.collection_append, active_history=True) + event.listen( + attr, "remove", self.collection_remove, active_history=True) def setup_auditable_entity(self, entity_class): meta = AuditableMeta(entity_class.__name__, 'id') @@ -171,8 +165,8 @@ def setup_auditable_entity(self, entity_class): raise ValueError( 'Audit setup class<{cls}: Could not guess backref name' ' of relationship "{related_attr}", please use tuple annotation ' - 'on __auditable_entity__'.format(cls=entity_class.__name__, - related_attr=related_attr)) + 'on __auditable_entity__'.format( + cls=entity_class.__name__, related_attr=related_attr)) meta.related = related_path meta.backref_attr = backref_attr @@ -237,8 +231,8 @@ def create_audit_entries(self, session, flush_context): if current_app.config.get( 'DEBUG') or current_app.config.get('TESTING'): raise - log.error('Exception during entry creation', - exc_info=True) + log.error( + 'Exception during entry creation', exc_info=True) session.add_all(entries) finally: diff --git a/abilian/services/audit/tests.py b/abilian/services/audit/tests.py index aafecac7..97cef2e9 100644 --- a/abilian/services/audit/tests.py +++ b/abilian/services/audit/tests.py @@ -56,10 +56,10 @@ class AccountRelated(db.Model): id = Column(Integer, primary_key=True) account_id = Column(Integer, ForeignKey(DummyAccount.id), nullable=False) - account = relationship(DummyAccount, - backref=backref('data', - order_by='AccountRelated.id', - cascade='all, delete-orphan')) + account = relationship( + DummyAccount, + backref=backref( + 'data', order_by='AccountRelated.id', cascade='all, delete-orphan')) text = Column(UnicodeText, default="") @@ -71,10 +71,12 @@ class CommentRelated(db.Model): id = Column(Integer, primary_key=True) related_id = Column(Integer, ForeignKey(AccountRelated.id), nullable=False) - related = relationship(AccountRelated, - backref=backref('comments', - order_by='CommentRelated.id', - cascade='all, delete-orphan')) + related = relationship( + AccountRelated, + backref=backref( + 'comments', + order_by='CommentRelated.id', + cascade='all, delete-orphan')) text = Column(UnicodeText, default="") diff --git a/abilian/services/auth/models.py b/abilian/services/auth/models.py index ed26f646..b602bec1 100644 --- a/abilian/services/auth/models.py +++ b/abilian/services/auth/models.py @@ -89,7 +89,6 @@ def new(): ip_address = request.remote_addr else: ip_address = forwared_for[0] - session = LoginSession(user=current_user, - user_agent=user_agent, - ip_address=ip_address) + session = LoginSession( + user=current_user, user_agent=user_agent, ip_address=ip_address) return session diff --git a/abilian/services/auth/service.py b/abilian/services/auth/service.py index 9873cfed..d2897b5d 100644 --- a/abilian/services/auth/service.py +++ b/abilian/services/auth/service.py @@ -46,27 +46,31 @@ def _user_photo_icon_args(icon, url_args): return images.user_url_args(current_user, max(icon.width, icon.height))[1] -user_menu = NavGroup('user', - 'authenticated', - title=lambda c: current_user.name, - icon=DynamicIcon(endpoint=_user_photo_endpoint, - css='avatar', - size=20, - url_args=_user_photo_icon_args), - condition=is_authenticated, - items=(NavItem('user', - 'logout', - title=_l(u'Logout'), - icon='log-out', - url=lambda context: url_for('login.logout'), - divider=True),)) - -_ACTIONS = (NavItem('user', - 'login', - title=_l(u'Login'), - icon='log-in', - url=lambda context: url_for('login.login_form'), - condition=is_anonymous), +user_menu = NavGroup( + 'user', + 'authenticated', + title=lambda c: current_user.name, + icon=DynamicIcon( + endpoint=_user_photo_endpoint, + css='avatar', + size=20, + url_args=_user_photo_icon_args), + condition=is_authenticated, + items=(NavItem( + 'user', + 'logout', + title=_l(u'Logout'), + icon='log-out', + url=lambda context: url_for('login.logout'), + divider=True),)) + +_ACTIONS = (NavItem( + 'user', + 'login', + title=_l(u'Login'), + icon='log-in', + url=lambda context: url_for('login.login_form'), + condition=is_anonymous), user_menu,) diff --git a/abilian/services/auth/tests.py b/abilian/services/auth/tests.py index ee7cf087..ff50a98c 100644 --- a/abilian/services/auth/tests.py +++ b/abilian/services/auth/tests.py @@ -40,21 +40,21 @@ def test_get_redirect_target(self): # test "next" from referer referrer = url_root + u'/some/path' - with self.app.test_request_context(form_url(), - headers=[('Referer', referrer)]): + with self.app.test_request_context( + form_url(), headers=[('Referer', referrer)]): assert get_redirect_target() == referrer # don't cycle if coming from 'login.*' page, like this kind of cycle: # forgot password form -> login page -> success # -> redirect(next = forgot password form) -> ... referrer = url_root + url_for('login.forgotten_pw') - with self.app.test_request_context(form_url(), - headers=[('Referer', referrer)]): + with self.app.test_request_context( + form_url(), headers=[('Referer', referrer)]): assert get_redirect_target() is None # test open redirect is forbidden - with self.app.test_request_context(form_url( - next='http://google.com/test')): + with self.app.test_request_context( + form_url(next='http://google.com/test')): assert get_redirect_target() is None # open redirect through malicious construct and browser not checking Location @@ -62,9 +62,8 @@ def test_get_redirect_target(self): assert get_redirect_target() == url_root + u'///google.com' def test_login_post(self): - kwargs = dict(email='User@domain.tld', - password='azerty', - can_login=True) + kwargs = dict( + email='User@domain.tld', password='azerty', can_login=True) u = User(**kwargs) self.session.add(u) self.session.commit() @@ -85,31 +84,32 @@ def test_login_post(self): self.assertEqual(rv.status_code, 401, "expected 401, got:" + rv.status) def test_api_post(self): - kwargs = dict(email='User@domain.tld', - password='azerty', - can_login=True) + kwargs = dict( + email='User@domain.tld', password='azerty', can_login=True) u = User(**kwargs) self.session.add(u) self.session.commit() - rv = self.client.post('/user/api/login', - data=json.dumps(kwargs), - content_type='application/json') + rv = self.client.post( + '/user/api/login', + data=json.dumps(kwargs), + content_type='application/json') self.assertEqual(rv.status_code, 200, "expected 200, got:" + rv.status) - self.assertEqual(rv.json, - dict(email='User@domain.tld', - username='user@domain.tld', - fullname='Unknown', - next_url='')) + self.assertEqual( + rv.json, + dict( + email='User@domain.tld', + username='user@domain.tld', + fullname='Unknown', + next_url='')) rv = self.client.post('/user/api/logout') self.assertEqual(rv.status_code, 200, "expected 200, got:" + rv.status) def test_forgotten_pw(self): mail = self.app.extensions['mail'] - kwargs = dict(email='User@domain.tld', - password='azerty', - can_login=True) + kwargs = dict( + email='User@domain.tld', password='azerty', can_login=True) u = User(**kwargs) self.session.add(u) self.session.commit() diff --git a/abilian/services/auth/views.py b/abilian/services/auth/views.py index 8195b9a7..be8252cf 100644 --- a/abilian/services/auth/views.py +++ b/abilian/services/auth/views.py @@ -35,11 +35,12 @@ __all__ = [] -login = Blueprint("login", - __name__, - url_prefix="/user", - allowed_roles=Anonymous, - template_folder='templates') +login = Blueprint( + "login", + __name__, + url_prefix="/user", + allowed_roles=Anonymous, + template_folder='templates') route = login.route @@ -267,11 +268,8 @@ def send_reset_password_instructions(user): subject = _(u"Password reset instruction for {site_name}" ).format(site_name=current_app.config.get('SITE_NAME')) mail_template = 'password_reset_instructions' - send_mail(subject, - user.email, - mail_template, - user=user, - reset_link=reset_link) + send_mail( + subject, user.email, mail_template, user=user, reset_link=reset_link) def generate_reset_password_token(user): diff --git a/abilian/services/base.py b/abilian/services/base.py index 0ef26ae0..281f3f58 100644 --- a/abilian/services/base.py +++ b/abilian/services/base.py @@ -43,8 +43,8 @@ class Service(object): def __init__(self, app=None): if self.name is None: - raise ValueError('Service must have a name ({})'.format(fqcn( - self.__class__))) + raise ValueError('Service must have a name ({})'.format( + fqcn(self.__class__))) self.logger = logging.getLogger(fqcn(self.__class__)) if app: diff --git a/abilian/services/conversion.py b/abilian/services/conversion.py index ed0f10b9..e143db24 100644 --- a/abilian/services/conversion.py +++ b/abilian/services/conversion.py @@ -111,8 +111,9 @@ def __init__(self): self.cache = Cache() def init_app(self, app): - self.init_work_dirs(cache_dir=Path(app.instance_path, CACHE_DIR), - tmp_dir=Path(app.instance_path, TMP_DIR),) + self.init_work_dirs( + cache_dir=Path(app.instance_path, CACHE_DIR), + tmp_dir=Path(app.instance_path, TMP_DIR),) app.extensions['conversion'] = self @@ -271,8 +272,8 @@ def get_metadata(self, digest, content, mime_type): for line in output.split(b"\n"): if b":" in line: key, value = line.strip().split(b":", 1) - ret["PDF:" + key] = text_type(value.strip(), - errors="replace") + ret["PDF:" + key] = text_type( + value.strip(), errors="replace") return ret @@ -445,8 +446,7 @@ def convert(self, blob, size=500): converted_images = [] for fn in l: converted = resize( - open(fn, 'rb').read(), - size, size, mode=FIT) + open(fn, 'rb').read(), size, size, mode=FIT) converted_images.append(converted) return converted_images @@ -532,9 +532,8 @@ def convert(self, blob, **kw): def run_uno(): try: - self._process = subprocess.Popen(cmd, - close_fds=True, - cwd=bytes(self.TMP_DIR)) + self._process = subprocess.Popen( + cmd, close_fds=True, cwd=bytes(self.TMP_DIR)) self._process.communicate() except Exception as e: logger.error('run_uno error: %s', bytes(e), exc_info=True) @@ -630,9 +629,8 @@ def convert(self, blob, **kw): if encoding in ("binary", None): encoding = "ascii" try: - converted_unicode = text_type(converted, - encoding, - errors="ignore") + converted_unicode = text_type( + converted, encoding, errors="ignore") except: traceback.print_exc() converted_unicode = text_type(converted, errors="ignore") diff --git a/abilian/services/indexing/schema.py b/abilian/services/indexing/schema.py index a7012619..d91a54f5 100644 --- a/abilian/services/indexing/schema.py +++ b/abilian/services/indexing/schema.py @@ -45,10 +45,8 @@ class _DefaultSearchSchema(SchemaClass): tag_text = TEXT(stored=False, analyzer=accent_folder) # hierarchical index of ids path ('/' is the separator) - parent_ids = FieldType(format=Existence(), - analyzer=PathTokenizer(), - stored=True, - unique=False) + parent_ids = FieldType( + format=Existence(), analyzer=PathTokenizer(), stored=True, unique=False) name = TEXT(stored=True, analyzer=accent_folder) slug = ID(stored=True) @@ -58,7 +56,8 @@ class _DefaultSearchSchema(SchemaClass): _default_dyn_fields = { '*_prefix': EdgeNgramField(), - '*_at': DATETIME(stored=True, sortable=True), + '*_at': DATETIME( + stored=True, sortable=True), } diff --git a/abilian/services/indexing/service.py b/abilian/services/indexing/service.py index d216ef09..5ea02a09 100644 --- a/abilian/services/indexing/service.py +++ b/abilian/services/indexing/service.py @@ -290,8 +290,9 @@ def search(self, if not fields: fields = self.default_search_fields - valid_fields = set(f for f in index.schema.names(check_names=fields) - if prefix or not f.endswith('_prefix')) + valid_fields = set( + f for f in index.schema.names(check_names=fields) + if prefix or not f.endswith('_prefix')) for invalid in set(fields) - valid_fields: del fields[invalid] @@ -310,8 +311,8 @@ def search(self, if not user.is_anonymous: roles.add(indexable_role(Anonymous)) roles.add(indexable_role(Authenticated)) - roles |= set(indexable_role(r) - for r in security.get_roles(user)) + roles |= set( + indexable_role(r) for r in security.get_roles(user)) filter_q = wq.Or([wq.Term('allowed_roles_and_users', role) for role in roles]) @@ -376,10 +377,8 @@ def search(self, return results def search_for_class(self, query, cls, index='default', **search_args): - return self.search(query, - Models=(fqcn(cls),), - index=index, - **search_args) + return self.search( + query, Models=(fqcn(cls),), index=index, **search_args) def register_classes(self): state = self.app_state @@ -513,9 +512,8 @@ def index_objects(self, objects, index='default'): except ValueError: # logger is here to give us more infos in order to catch a weird bug # that happens regularly on CI but is not reliably reproductible. - logger.error('writer.add_document(%r)', - document, - exc_info=True) + logger.error( + 'writer.add_document(%r)', document, exc_info=True) raise indexed.add(object_key) @@ -571,9 +569,8 @@ def index_update(index, items): except ValueError: # logger is here to give us more infos in order to catch a weird bug # that happens regularly on CI but is not reliably reproductible. - logger.error('writer.add_document(%r)', - document, - exc_info=True) + logger.error( + 'writer.add_document(%r)', document, exc_info=True) raise updated.add(object_key) except: diff --git a/abilian/services/indexing/tests/test_adapter.py b/abilian/services/indexing/tests/test_adapter.py index f0edad7c..6132b534 100644 --- a/abilian/services/indexing/tests/test_adapter.py +++ b/abilian/services/indexing/tests/test_adapter.py @@ -29,12 +29,13 @@ class SANotIndexable(IdMixin, db.Model): class Indexable(IdMixin, CoreIndexable, db.Model): __tablename__ = 'sa_indexable' - __indexation_args__ = dict(index_to=(('related.name', ('name', 'text')), - ('related.description', 'text'),),) + __indexation_args__ = dict( + index_to=(('related.name', ('name', 'text')), + ('related.description', 'text'),),) - num = sa.Column(sa.Integer, - info=SEARCHABLE | dict(index_to=( - ('num', NUMERIC(numtype=int)),)),) + num = sa.Column( + sa.Integer, + info=SEARCHABLE | dict(index_to=(('num', NUMERIC(numtype=int)),)),) class SubclassEntityIndexable(Entity): @@ -100,11 +101,9 @@ def test_build_attrs(self): 'tag_text', } - schema = Schema(id=NUMERIC(numtype=int, - bits=64, - signed=False, - stored=True, - unique=True),) + schema = Schema( + id=NUMERIC( + numtype=int, bits=64, signed=False, stored=True, unique=True),) adapter = SAAdapter(Indexable, schema) assert adapter.indexable assert set(adapter.doc_attrs) == {'id', 'text', 'num', 'name'} @@ -121,10 +120,11 @@ class DocumentTestCase(AppTestCase): def test_get_document(self): schema = Schema() adapter = SAAdapter(SubclassEntityIndexable, schema) - expected = dict(id=2, - name='entity name', - created_at=datetime(2013, 11, 28, 16, 17, 0), - updated_at=datetime(2013, 11, 29, 12, 17, 58)) + expected = dict( + id=2, + name='entity name', + created_at=datetime(2013, 11, 28, 16, 17, 0), + updated_at=datetime(2013, 11, 29, 12, 17, 58)) obj = SubclassEntityIndexable(**expected) obj.slug = u'entity-name' expected['object_type'] = u'test_adapter.SubclassEntityIndexable' @@ -136,11 +136,9 @@ def test_get_document(self): assert adapter.get_document(obj) == expected # test retrieve related attributes - schema = Schema(id=NUMERIC(numtype=int, - bits=64, - signed=False, - stored=True, - unique=True),) + schema = Schema( + id=NUMERIC( + numtype=int, bits=64, signed=False, stored=True, unique=True),) adapter = SAAdapter(Indexable, schema) expected = dict(id=1, num=42) obj = Indexable(**expected) diff --git a/abilian/services/preferences/models.py b/abilian/services/preferences/models.py index 51383a28..ef12a1c8 100644 --- a/abilian/services/preferences/models.py +++ b/abilian/services/preferences/models.py @@ -22,9 +22,10 @@ class UserPreference(db.Model): id = Column(Integer, primary_key=True, autoincrement=True) #: The user who set this preference. - user = relation(User, - backref=backref('preferences', - cascade="all, delete, delete-orphan")) + user = relation( + User, + backref=backref( + 'preferences', cascade="all, delete, delete-orphan")) user_id = Column(ForeignKey(User.id)) #: The key diff --git a/abilian/services/preferences/service.py b/abilian/services/preferences/service.py index 5b057625..4b564877 100644 --- a/abilian/services/preferences/service.py +++ b/abilian/services/preferences/service.py @@ -115,10 +115,8 @@ def register_panel(self, panel, app=None): state.blueprint.add_url_rule(rule, endpoint, panel.get) if hasattr(panel, 'post'): endpoint += "_post" - state.blueprint.add_url_rule(rule, - endpoint, - panel.post, - methods=['POST']) + state.blueprint.add_url_rule( + rule, endpoint, panel.post, methods=['POST']) state.breadcrumb_items[abs_endpoint] = BreadcrumbItem( label=panel.label, @@ -126,10 +124,11 @@ def register_panel(self, panel, app=None): url=Endpoint(abs_endpoint),) def setup_blueprint(self, app): - bp = self.app_state.blueprint = Blueprint("preferences", - __name__, - template_folder='templates', - url_prefix="/preferences") + bp = self.app_state.blueprint = Blueprint( + "preferences", + __name__, + template_folder='templates', + url_prefix="/preferences") # we need to delay blueprint registration to allow adding more panels during # initialization diff --git a/abilian/services/security/debug_toolbar.py b/abilian/services/security/debug_toolbar.py index db1c92d5..d919b304 100644 --- a/abilian/services/security/debug_toolbar.py +++ b/abilian/services/security/debug_toolbar.py @@ -73,8 +73,8 @@ def content(self): for r in roles: info = roles[r] info['groups'] = [u'{g} (id={g.id})'.format(g=g) - for g in sorted(info['groups'], - key=lambda g: g.name)] + for g in sorted( + info['groups'], key=lambda g: g.name)] users = sorted( info['users'], key=lambda u: (u.last_name.lower(), u.first_name.lower())) diff --git a/abilian/services/security/models.py b/abilian/services/security/models.py index 8b86a4a0..255eac9f 100644 --- a/abilian/services/security/models.py +++ b/abilian/services/security/models.py @@ -103,9 +103,8 @@ class RoleType(UniqueNameType): Anonymous = Role('anonymous', _l(u'role_anonymous'), assignable=False) #: marker for role assigned to 'Authenticated' -Authenticated = Role('authenticated', - _l('role_authenticated'), - assignable=False) +Authenticated = Role( + 'authenticated', _l('role_authenticated'), assignable=False) #: marker for `admin` role Admin = Role('admin', _l('role_administrator')) @@ -130,42 +129,45 @@ class RoleAssignment(db.Model): __tablename__ = "roleassignment" __table_args__ = ( # - CheckConstraint("(CAST(anonymous AS INTEGER) = 1)" - " OR " - "((CAST(anonymous AS INTEGER) = 0)" - " AND " - " ((user_id IS NOT NULL AND group_id IS NULL)" - " OR " - " (user_id IS NULL AND group_id IS NOT NULL)))", - name="roleassignment_ck_user_xor_group"), + CheckConstraint( + "(CAST(anonymous AS INTEGER) = 1)" + " OR " + "((CAST(anonymous AS INTEGER) = 0)" + " AND " + " ((user_id IS NOT NULL AND group_id IS NULL)" + " OR " + " (user_id IS NULL AND group_id IS NOT NULL)))", + name="roleassignment_ck_user_xor_group"), # - UniqueConstraint('anonymous', - 'user_id', - 'group_id', - 'role', - 'object_id', - name='assignment_mapped_role_unique')) + UniqueConstraint( + 'anonymous', + 'user_id', + 'group_id', + 'role', + 'object_id', + name='assignment_mapped_role_unique')) id = Column(Integer, primary_key=True, autoincrement=True, nullable=False) role = Column(RoleType, index=True, nullable=False) - anonymous = Column('anonymous', - Boolean, - index=True, - nullable=True, - default=False, - server_default=sql.false()) - user_id = Column(Integer, - ForeignKey('user.id', ondelete='CASCADE'), - index=True) + anonymous = Column( + 'anonymous', + Boolean, + index=True, + nullable=True, + default=False, + server_default=sql.false()) + user_id = Column( + Integer, ForeignKey( + 'user.id', ondelete='CASCADE'), index=True) user = relationship(User, lazy='joined') - group_id = Column(Integer, - ForeignKey('group.id', ondelete='CASCADE'), - index=True) + group_id = Column( + Integer, ForeignKey( + 'group.id', ondelete='CASCADE'), index=True) group = relationship(Group, lazy='joined') - object_id = Column(Integer, - ForeignKey(Entity.id, ondelete='CASCADE'), - index=True) + object_id = Column( + Integer, ForeignKey( + Entity.id, ondelete='CASCADE'), index=True) object = relationship(Entity, lazy='select') @@ -249,25 +251,27 @@ def _postgres_indexes(): class PermissionAssignment(db.Model): __tablename__ = 'permission_assignment' - __table_args__ = (UniqueConstraint('permission', - 'role', - 'object_id', - name='assignments_unique'),) + __table_args__ = (UniqueConstraint( + 'permission', 'role', 'object_id', name='assignments_unique'),) id = Column(Integer, primary_key=True, autoincrement=True, nullable=False) permission = Column(PermissionType, index=True, nullable=False) role = Column(RoleType, index=True, nullable=False) - object_id = Column(Integer, - ForeignKey(Entity.id, ondelete='CASCADE'), - index=True, - nullable=True) - object = relationship(Entity, - lazy='select', - backref=backref(PERMISSIONS_ATTR, - lazy='select', - collection_class=set, - cascade='all, delete-orphan', - passive_deletes=True)) + object_id = Column( + Integer, + ForeignKey( + Entity.id, ondelete='CASCADE'), + index=True, + nullable=True) + object = relationship( + Entity, + lazy='select', + backref=backref( + PERMISSIONS_ATTR, + lazy='select', + collection_class=set, + cascade='all, delete-orphan', + passive_deletes=True)) def __hash__(self): return hash((self.permission, self.role, self.object)) @@ -288,8 +292,8 @@ def __repr__(self): return ('<{cls} instance at 0x{id:x} ' 'permission={self.permission.name!r} ' 'role={self.role.name!r} object={self.object!r}>' - ''.format(cls=classname, id=id(self), - self=self)) + ''.format( + cls=classname, id=id(self), self=self)) def _postgres_indexes(): @@ -330,33 +334,36 @@ class SecurityAudit(db.Model): __table_args__ = ( # constraint: either a inherit/no_inherit op on an object AND no user no group # either a grant/revoke on a user XOR a group. - CheckConstraint("(op IN ('{grant}', '{revoke}') " - " AND object_id IS NOT NULL" - " AND user_id IS NULL " - " AND group_id IS NULL " - " AND (CAST(anonymous AS INTEGER) = 0)" - ")" - " OR " - "(op NOT IN ('{grant}', '{revoke}')" - " AND " - " (((CAST(anonymous AS INTEGER) = 1) " - " AND user_id IS NULL AND group_id IS NULL)" - " OR " - " ((CAST(anonymous AS INTEGER) = 0) " - " AND ((user_id IS NOT NULL AND group_id IS NULL)" - " OR " - " (user_id IS NULL AND group_id IS NOT NULL)))" - "))".format(grant=SET_INHERIT, - revoke=UNSET_INHERIT), - name="securityaudit_ck_user_xor_group"),) + CheckConstraint( + "(op IN ('{grant}', '{revoke}') " + " AND object_id IS NOT NULL" + " AND user_id IS NULL " + " AND group_id IS NULL " + " AND (CAST(anonymous AS INTEGER) = 0)" + ")" + " OR " + "(op NOT IN ('{grant}', '{revoke}')" + " AND " + " (((CAST(anonymous AS INTEGER) = 1) " + " AND user_id IS NULL AND group_id IS NULL)" + " OR " + " ((CAST(anonymous AS INTEGER) = 0) " + " AND ((user_id IS NOT NULL AND group_id IS NULL)" + " OR " + " (user_id IS NULL AND group_id IS NOT NULL)))" + "))".format( + grant=SET_INHERIT, revoke=UNSET_INHERIT), + name="securityaudit_ck_user_xor_group"),) id = Column(Integer, primary_key=True) happened_at = Column(DateTime, default=datetime.utcnow, index=True) - op = Column(Enum(GRANT, - REVOKE, - SET_INHERIT, - UNSET_INHERIT, - name='securityaudit_enum_op')) + op = Column( + Enum( + GRANT, + REVOKE, + SET_INHERIT, + UNSET_INHERIT, + name='securityaudit_enum_op')) role = Column(RoleType) manager_id = Column(Integer, ForeignKey(User.id)) @@ -379,7 +386,5 @@ class InheritSecurity(object): """ Mixin for objects with a parent relation and security inheritance. """ - inherit_security = Column(Boolean, - default=True, - nullable=False, - info={'auditable': False}) + inherit_security = Column( + Boolean, default=True, nullable=False, info={'auditable': False}) diff --git a/abilian/services/security/service.py b/abilian/services/security/service.py index 9e9ed015..7a0e6836 100644 --- a/abilian/services/security/service.py +++ b/abilian/services/security/service.py @@ -7,6 +7,7 @@ from functools import wraps from itertools import chain +from typing import Dict, Set import sqlalchemy as sa from flask import current_app, g @@ -14,7 +15,6 @@ from six import string_types, text_type from sqlalchemy import sql from sqlalchemy.orm import object_session, subqueryload -from typing import Dict, Set from abilian.core.entities import Entity from abilian.core.extensions import db @@ -180,12 +180,13 @@ def set_inherit_security(self, obj, inherit_security): manager = self._current_user_manager(session=session) op = (SecurityAudit.SET_INHERIT if inherit_security else SecurityAudit.UNSET_INHERIT) - audit = SecurityAudit(manager=manager, - op=op, - object=obj, - object_id=obj.id, - object_type=obj.entity_type, - object_name=obj.name) + audit = SecurityAudit( + manager=manager, + op=op, + object=obj, + object_id=obj.id, + object_type=obj.entity_type, + object_name=obj.name) session.add(audit) self._needs_flush() @@ -349,11 +350,11 @@ def _fill_role_cache_batch(self, principals, overwrite=False): filter_cond = [] if users: - filter_cond.append(RoleAssignment.user_id.in_((u.id - for u in users))) + filter_cond.append( + RoleAssignment.user_id.in_((u.id for u in users))) if groups: - filter_cond.append(RoleAssignment.group_id.in_((g.id - for g in groups))) + filter_cond.append( + RoleAssignment.group_id.in_((g.id for g in groups))) query = query.filter(sql.or_(*filter_cond)) ra_users = {} @@ -461,11 +462,8 @@ def grant_role(self, principal, role, obj=None): principal = noproxy(principal) session = object_session(obj) if obj is not None else db.session manager = self._current_user_manager(session=session) - args = dict(role=role, - object=obj, - anonymous=False, - user=None, - group=None) + args = dict( + role=role, object=obj, anonymous=False, user=None, group=None) if (principal is AnonymousRole or (hasattr(principal, 'is_anonymous') and principal.is_anonymous)): @@ -516,11 +514,8 @@ def ungrant_role(self, principal, role, object=None): session = object_session(object) if object is not None else db.session manager = self._current_user_manager(session=session) - args = dict(role=role, - object=object, - anonymous=False, - user=None, - group=None) + args = dict( + role=role, object=object, anonymous=False, user=None, group=None) query = session.query(RoleAssignment) query = query.filter(RoleAssignment.role == role, RoleAssignment.object == object) @@ -770,9 +765,8 @@ def add_permission(self, permission, role, obj=None): pa = query_pa_no_flush(session, permission, role, obj) if not pa: - pa = PermissionAssignment(permission=permission, - role=role, - object=obj) + pa = PermissionAssignment( + permission=permission, role=role, object=obj) # do it in any case: it could have been found in session.deleted session.add(pa) diff --git a/abilian/services/security/tests.py b/abilian/services/security/tests.py index b61f424e..6fca3e05 100644 --- a/abilian/services/security/tests.py +++ b/abilian/services/security/tests.py @@ -20,10 +20,11 @@ def init_user(): - user = User(first_name="Joe", - last_name="User", - email=TEST_EMAIL, - password=TEST_PASSWORD) + user = User( + first_name="Joe", + last_name="User", + email=TEST_EMAIL, + password=TEST_PASSWORD) db.session.add(user) db.session.flush() @@ -338,9 +339,8 @@ def test_has_permission_on_objects(self): assert not has_permission(user, READ, obj=obj) assert not has_permission(user, WRITE, obj=obj) - pa = PermissionAssignment(role=Authenticated, - permission=READ, - object=obj) + pa = PermissionAssignment( + role=Authenticated, permission=READ, object=obj) self.session.add(pa) self.session.flush() assert has_permission(user, READ, obj=obj) @@ -408,13 +408,11 @@ def test_query_entity_with_permission(self): obj_none = DummyModel(name='none') self.session.add_all([obj_reader, obj_writer, obj_none]) - assigments = [PermissionAssignment(role=Reader, - permission=READ, - object=obj_reader), + assigments = [PermissionAssignment( + role=Reader, permission=READ, object=obj_reader), # - PermissionAssignment(role=Writer, - permission=WRITE, - object=obj_writer)] + PermissionAssignment( + role=Writer, permission=WRITE, object=obj_writer)] self.session.add_all(assigments) self.session.flush() @@ -436,9 +434,8 @@ def test_query_entity_with_permission(self): == [obj_writer] # Permission granted to anonymous: objects returned - pa = PermissionAssignment(role=Anonymous, - permission=WRITE, - object=obj_reader) + pa = PermissionAssignment( + role=Anonymous, permission=WRITE, object=obj_reader) self.session.add(pa) assert base_query.filter(get_filter(READ, user=user)).all() \ @@ -456,12 +453,10 @@ def test_query_entity_with_permission(self): security.grant_role(user, Writer) self.session.flush() - assert base_query.filter(get_filter(READ, user=user)).all() == [ - obj_reader - ] - assert base_query.filter(get_filter(WRITE, user=user)).all() == [ - obj_writer - ] + assert base_query.filter(get_filter( + READ, user=user)).all() == [obj_reader] + assert base_query.filter(get_filter( + WRITE, user=user)).all() == [obj_writer] # admin role has all permissions # 1: local role @@ -491,13 +486,11 @@ def test_query_entity_with_permission(self): obj_reader.creator = user obj_writer.owner = user - assigments = [PermissionAssignment(role=Creator, - permission=READ, - object=obj_reader), + assigments = [PermissionAssignment( + role=Creator, permission=READ, object=obj_reader), # - PermissionAssignment(role=Owner, - permission=WRITE, - object=obj_writer)] + PermissionAssignment( + role=Owner, permission=WRITE, object=obj_writer)] self.session.add_all(assigments) self.session.flush() diff --git a/abilian/services/settings/models.py b/abilian/services/settings/models.py index 2f3ec56c..9f4f2363 100644 --- a/abilian/services/settings/models.py +++ b/abilian/services/settings/models.py @@ -101,6 +101,7 @@ def value(self, value): def from_int(i): return "{}".format(i).encode() + register('int', from_int, int) diff --git a/abilian/services/vocabularies/admin.py b/abilian/services/vocabularies/admin.py index d211bb11..9efd5f07 100644 --- a/abilian/services/vocabularies/admin.py +++ b/abilian/services/vocabularies/admin.py @@ -42,9 +42,10 @@ def prepare_args(self, args, kwargs): return views.ObjectEdit.prepare_args(self, args, kwargs) def view_url(self): - return url_for('.vocabularies_model', - group=self.Model.Meta.group or '_', - Model=self.Model.Meta.name,) + return url_for( + '.vocabularies_model', + group=self.Model.Meta.group or '_', + Model=self.Model.Meta.name,) class Create(views.ObjectCreate, Edit): @@ -72,18 +73,20 @@ def svc(self): return current_app.services['vocabularies'] def voc_edit_url(self, item): - return url_for('.' + self.id + '_edit', - group=item.Meta.group or u'_', - Model=item.Meta.name, - object_id=item.id) + return url_for( + '.' + self.id + '_edit', + group=item.Meta.group or u'_', + Model=item.Meta.name, + object_id=item.id) def get(self): svc = self.svc - return render_template('admin/vocabularies.html', - service=svc, - url_for_voc_edit=self.voc_edit_url, - icon_checked=Glyphicon('check'), - vocabularies=svc.grouped_vocabularies,) + return render_template( + 'admin/vocabularies.html', + service=svc, + url_for_voc_edit=self.voc_edit_url, + icon_checked=Glyphicon('check'), + vocabularies=svc.grouped_vocabularies,) def post(self): data = request.form @@ -158,20 +161,22 @@ def group_view(self, group): groups = self.svc.grouped_vocabularies vocabularies = groups.get(group) - return render_template('admin/vocabularies.html', - service=self.svc, - url_for_voc_edit=self.voc_edit_url, - icon_checked=Glyphicon('check'), - vocabularies={group: vocabularies}, - edit_return_to='group') + return render_template( + 'admin/vocabularies.html', + service=self.svc, + url_for_voc_edit=self.voc_edit_url, + icon_checked=Glyphicon('check'), + vocabularies={group: vocabularies}, + edit_return_to='group') def model_view(self, Model, group=None): - return render_template('admin/vocabularies.html', - service=self.svc, - url_for_voc_edit=self.voc_edit_url, - icon_checked=Glyphicon('check'), - vocabularies={Model.Meta.group: [Model]}, - edit_return_to='model') + return render_template( + 'admin/vocabularies.html', + service=self.svc, + url_for_voc_edit=self.voc_edit_url, + icon_checked=Glyphicon('check'), + vocabularies={Model.Meta.group: [Model]}, + edit_return_to='model') def install_additional_rules(self, add_url_rule): panel_endpoint = '.' + self.id @@ -183,9 +188,10 @@ def install_additional_rules(self, add_url_rule): edit_view = Edit.as_view(b'edit', view_endpoint=panel_endpoint) add_url_rule(base + '', view_func=edit_view) - add_url_rule(base + 'new', - view_func=Create.as_view(b'new', - view_endpoint=panel_endpoint)) + add_url_rule( + base + 'new', + view_func=Create.as_view( + b'new', view_endpoint=panel_endpoint)) def url_value_preprocess(self, endpoint, view_args): Model = view_args.pop('Model', None) @@ -205,9 +211,11 @@ def url_value_preprocess(self, endpoint, view_args): label=Model.Meta.group if group else _('Global'), url=url_for('.vocabularies_group', group=group or u'_'), )) - g.breadcrumb.append(BreadcrumbItem(label=Model.Meta.label, - url=url_for( - '.vocabularies_model', - group=group or u'_', - Model=Model.Meta.name),)) + g.breadcrumb.append( + BreadcrumbItem( + label=Model.Meta.label, + url=url_for( + '.vocabularies_model', + group=group or u'_', + Model=Model.Meta.name),)) view_args['Model'] = Model diff --git a/abilian/services/vocabularies/forms.py b/abilian/services/vocabularies/forms.py index 0d4de087..8a905763 100644 --- a/abilian/services/vocabularies/forms.py +++ b/abilian/services/vocabularies/forms.py @@ -20,18 +20,18 @@ class EditForm(ModelForm): label = StringField( _l(u'Label'), - description=_l(u'allowed tags: %(tags)s', - tags=', '.join(ALLOWED_TAGS)), + description=_l(u'allowed tags: %(tags)s', tags=', '.join(ALLOWED_TAGS)), filters=(strip,), validators=[required()]) default = BooleanField(_l(u'Default'), default=False) active = BooleanField(_l(u'Active'), default=True) def validate_label(self, field): - field.data = bleach.clean(field.data, - tags=ALLOWED_TAGS, - attributes=ALLOWED_ATTRIBUTES, - strip=True) + field.data = bleach.clean( + field.data, + tags=ALLOWED_TAGS, + attributes=ALLOWED_ATTRIBUTES, + strip=True) class ListEditForm(EditForm): diff --git a/abilian/services/vocabularies/models.py b/abilian/services/vocabularies/models.py index c0db4f00..c2249b9b 100644 --- a/abilian/services/vocabularies/models.py +++ b/abilian/services/vocabularies/models.py @@ -73,14 +73,16 @@ class BaseVocabulary(db.Model): id = Column(sa.Integer(), primary_key=True, autoincrement=True) label = Column(sa.UnicodeText(), nullable=False, unique=True) - active = Column(sa.Boolean(), - nullable=False, - server_default=sa.sql.true(), - default=True) - default = Column(sa.Boolean(), - nullable=False, - server_default=sa.sql.false(), - default=False) + active = Column( + sa.Boolean(), + nullable=False, + server_default=sa.sql.true(), + default=True) + default = Column( + sa.Boolean(), + nullable=False, + server_default=sa.sql.false(), + default=False) position = Column(sa.Integer, nullable=False, unique=True) __table_args__ = ( @@ -101,14 +103,15 @@ def __repr__(self): fmt = ('<{module}.{cls} id={id} label={label} position={position} ' 'active={active} default={default} at 0x{addr:x}') cls = self.__class__ - return fmt.format(module=cls.__module__, - cls=cls.__name__, - id=self.id, - label=repr(self.label), - position=repr(self.position), - active=repr(self.active), - default=repr(self.default), - addr=id(self),) + return fmt.format( + module=cls.__module__, + cls=cls.__name__, + id=self.id, + label=repr(self.label), + position=repr(self.position), + active=repr(self.active), + default=repr(self.default), + addr=id(self),) @sa.event.listens_for(BaseVocabulary, "before_insert", propagate=True) @@ -145,9 +148,9 @@ def _before_insert(mapper, connection, target): def Vocabulary(name, label=None, group=None): cls_name = b'Vocabulary' + name.capitalize() - Meta = type(b'Meta', (object,), - dict(name=name.lower(), - label=label, group=group)) + Meta = type( + b'Meta', (object,), dict( + name=name.lower(), label=label, group=group)) cls = type(cls_name, (BaseVocabulary,), dict(Meta=Meta)) _generated_vocabularies.append(cls) return cls diff --git a/abilian/services/vocabularies/tests.py b/abilian/services/vocabularies/tests.py index f64b68c3..b45bf2ab 100644 --- a/abilian/services/vocabularies/tests.py +++ b/abilian/services/vocabularies/tests.py @@ -27,9 +27,8 @@ def test_vocabulary_creator(self): assert issubclass(PriorityVoc, BaseVocabulary) StateVoc = self.DefaultVoc - DocCatVoc = Vocabulary(b'categories', - group='documents', - label='Categories') + DocCatVoc = Vocabulary( + b'categories', group='documents', label='Categories') # test registered vocabularies assert vocabularies.vocabularies == {PriorityVoc, StateVoc, DocCatVoc} diff --git a/abilian/testing/__init__.py b/abilian/testing/__init__.py index 07c02288..866c2971 100644 --- a/abilian/testing/__init__.py +++ b/abilian/testing/__init__.py @@ -141,8 +141,10 @@ def setUpClass(cls): else: cls.SERVICES = tuple(cls.SERVICES) - tmp_dir = Path(tempfile.mkdtemp(prefix='tmp-py-unittest-', - suffix='-' + cls.__name__,)) + tmp_dir = Path( + tempfile.mkdtemp( + prefix='tmp-py-unittest-', + suffix='-' + cls.__name__,)) cls.TEST_INSTANCE_PATH = str(tmp_dir) for p in (tmp_dir / 'tmp', tmp_dir / 'cache', tmp_dir / 'data'): p.mkdir() @@ -208,8 +210,8 @@ def setUp(self): self.__pg_schema)) conn.execute( 'ALTER ROLE {username} SET search_path TO {schema}' - ''.format(username=username, - schema=self.__pg_schema)) + ''.format( + username=username, schema=self.__pg_schema)) conn.execute('COMMIT') self.app.create_db() @@ -401,9 +403,10 @@ def assert_valid(self, response): self.validate(None, response.data, content_type, validator_url) def validate(self, url, content, content_type, validator_url): - response = requests.post(validator_url + '?out=json', - content, - headers={'Content-Type': content_type}) + response = requests.post( + validator_url + '?out=json', + content, + headers={'Content-Type': content_type}) body = response.json() diff --git a/abilian/web/action.py b/abilian/web/action.py index a750620e..0722a592 100644 --- a/abilian/web/action.py +++ b/abilian/web/action.py @@ -122,9 +122,8 @@ def __init__(self, name, second, stack=''): self.stack = stack def __html__(self): - return self.template.render(name=self.name, - second=self.second, - stack_class=self.stack) + return self.template.render( + name=self.name, second=self.second, stack_class=self.stack) class DynamicIcon(Icon): @@ -166,10 +165,11 @@ def __html__(self): if self.url_args_callback is not None: url_args = self.url_args_callback(self, url_args) - return self.template.render(url=url_for(endpoint, **url_args), - width=self.width, - height=self.height, - css=self.css) + return self.template.render( + url=url_for(endpoint, **url_args), + width=self.width, + height=self.height, + css=self.css) class StaticIcon(DynamicIcon): @@ -186,13 +186,8 @@ def __init__(self, height=12, css='', size=None): - DynamicIcon.__init__(self, - endpoint, - width, - height, - css, - size, - filename=filename) + DynamicIcon.__init__( + self, endpoint, width, height, css, size, filename=filename) class Endpoint(object): @@ -334,9 +329,8 @@ def title(self, title): self._title = title def _build_css_class(self): - css_cat = self.CSS_CLASS.format(action=self, - category=self.category, - name=self.name) + css_cat = self.CSS_CLASS.format( + action=self, category=self.category, name=self.name) css_cat = re.sub(r'[^ _a-zA-Z0-9-]', '-', css_cat) self.css_class = css_cat diff --git a/abilian/web/admin/extension.py b/abilian/web/admin/extension.py index 0a9a74d5..fede0f7d 100644 --- a/abilian/web/admin/extension.py +++ b/abilian/web/admin/extension.py @@ -81,8 +81,9 @@ def no_panels_view(): else: self.nav_root.endpoint = self.nav_root.items[0].endpoint - self.root_breadcrumb_item = BreadcrumbItem(label=self.nav_root.title, - url=self.nav_root.endpoint,) + self.root_breadcrumb_item = BreadcrumbItem( + label=self.nav_root.title, + url=self.nav_root.endpoint,) app.register_blueprint(self.blueprint) @@ -109,27 +110,24 @@ def register_panel(self, panel): self._panels_endpoints[abs_endpoint] = panel if hasattr(panel, 'post'): post_endpoint = endpoint + "_post" - self.blueprint.add_url_rule(rule, - post_endpoint, - panel.post, - methods=['POST']) + self.blueprint.add_url_rule( + rule, post_endpoint, panel.post, methods=['POST']) self._panels_endpoints['admin.' + post_endpoint] = panel - panel.install_additional_rules(self.get_panel_url_rule_adder( - panel, rule, endpoint)) + panel.install_additional_rules( + self.get_panel_url_rule_adder(panel, rule, endpoint)) - nav = NavItem('admin:panel', - nav_id, - title=panel.label, - icon=panel.icon, - divider=False, - endpoint=abs_endpoint) + nav = NavItem( + 'admin:panel', + nav_id, + title=panel.label, + icon=panel.icon, + divider=False, + endpoint=abs_endpoint) self.nav_root.append(nav) self.nav_paths[abs_endpoint] = nav.path self.breadcrumb_items[panel] = BreadcrumbItem( - label=panel.label, - icon=panel.icon, - url=Endpoint(abs_endpoint)) + label=panel.label, icon=panel.icon, url=Endpoint(abs_endpoint)) def get_panel_url_rule_adder(self, panel, base_url, base_endpoint): extension = self @@ -137,8 +135,8 @@ def get_panel_url_rule_adder(self, panel, base_url, base_endpoint): def add_url_rule(rule, endpoint=None, view_func=None, *args, **kwargs): if not rule: # '' is already used for panel get/post - raise ValueError('Invalid additional url rule: {}'.format(repr( - rule))) + raise ValueError('Invalid additional url rule: {}'.format( + repr(rule))) if endpoint is None: endpoint = _endpoint_from_view_func(view_func) @@ -147,19 +145,21 @@ def add_url_rule(rule, endpoint=None, view_func=None, *args, **kwargs): endpoint = base_endpoint + '_' + endpoint extension._panels_endpoints['admin.' + endpoint] = panel - return self.blueprint.add_url_rule(base_url + rule, - endpoint=endpoint, - view_func=view_func, - *args, - **kwargs) + return self.blueprint.add_url_rule( + base_url + rule, + endpoint=endpoint, + view_func=view_func, + *args, + **kwargs) return add_url_rule def setup_blueprint(self): - self.blueprint = Blueprint("admin", - __name__, - template_folder='templates', - url_prefix='/' + _BP_PREFIX) + self.blueprint = Blueprint( + "admin", + __name__, + template_folder='templates', + url_prefix='/' + _BP_PREFIX) self.blueprint.url_value_preprocessor(self.build_breadcrumbs) self.blueprint.url_value_preprocessor(self.panel_preprocess_value) diff --git a/abilian/web/admin/panels/audit.py b/abilian/web/admin/panels/audit.py index d11c2509..521fccfb 100644 --- a/abilian/web/admin/panels/audit.py +++ b/abilian/web/admin/panels/audit.py @@ -53,9 +53,10 @@ def data(self, q, *args, **kwargs): lower = sa.sql.func.lower filters = [] for part in q.split(u' '): - filters.append(sa.sql.or_( - lower(User.first_name).like(part + "%"), lower( - User.last_name).like(part + "%"))) + filters.append( + sa.sql.or_( + lower(User.first_name).like(part + "%"), lower( + User.last_name).like(part + "%"))) filters = sa.sql.and_(*filters) if len(filters) > 1 else filters[0] @@ -91,8 +92,8 @@ class AuditPanel(AdminPanel): icon = 'list-alt' def install_additional_rules(self, add_url_rule): - add_url_rule('/search_users', - view_func=JSONUserSearch.as_view(b'search_users')) + add_url_rule( + '/search_users', view_func=JSONUserSearch.as_view(b'search_users')) # noinspection PyComparisonWithNone def get(self): @@ -165,11 +166,12 @@ def before_query(q, model, date): .limit(LIMIT) \ .all() # audit_entries = [] - all_entries = list(chain( - # - (AuditEntryPresenter(e) for e in audit_entries), - # - (SecurityEntryPresenter(e) for e in security_entries))) + all_entries = list( + chain( + # + (AuditEntryPresenter(e) for e in audit_entries), + # + (SecurityEntryPresenter(e) for e in security_entries))) all_entries.sort() if after: @@ -236,16 +238,16 @@ def before_query(q, model, date): if filter_types: url_params['types'] = list(filter_types)[0] - return render_template("admin/audit.html", - entries=entries, - filter_user=filter_user, - all_classes=[(c.__name__, c.entity_type) - for c in all_classes], - filter_types=filter_types, - url_params=url_params, - current_date=current_date, - top_date=top_date, - lowest_date=lowest_date) + return render_template( + "admin/audit.html", + entries=entries, + filter_user=filter_user, + all_classes=[(c.__name__, c.entity_type) for c in all_classes], + filter_types=filter_types, + url_params=url_params, + current_date=current_date, + top_date=top_date, + lowest_date=lowest_date) # @@ -297,10 +299,11 @@ def render(self): except (BuildError, ValueError): pass else: - entity_html = Markup(render( - u'{{ entity.path or entity.name }}', - url=entity_url, - entity=e.entity)) + entity_html = Markup( + render( + u'{{ entity.path or entity.name }}', + url=entity_url, + entity=e.entity)) if e.type == 0: msg = _(u'{user} created {entity_type} {entity_id} "{entity}"') @@ -311,11 +314,12 @@ def render(self): else: raise Exception("Bad entry type: {}".format(e.type)) - self.msg = Markup(msg.format(user=user, - entity=entity_html, - entity_type=e.entity_type.rsplit('.', 1)[ - -1], - entity_id=e.entity_id,)) + self.msg = Markup( + msg.format( + user=user, + entity=entity_html, + entity_type=e.entity_type.rsplit('.', 1)[-1], + entity_id=e.entity_id,)) tmpl = get_template_attribute('admin/_macros.html', 'm_audit_entry') return tmpl(self) @@ -355,10 +359,11 @@ def render(self): entity_name = getattr(e.object, 'path', e.object.name) entity_url = url_for(e.object) - entity = render(u'{%- if url %}{%- endif %}' - u'{{ name }}{%- if url %}{%- endif %}', - url=entity_url, - name=entity_name) + entity = render( + u'{%- if url %}{%- endif %}' + u'{{ name }}{%- if url %}{%- endif %}', + url=entity_url, + name=entity_name) if e.op == e.SET_INHERIT: msg = _(u'{manager} has activated inheritance on {entity}') @@ -380,9 +385,11 @@ def render(self): else: raise Exception("Invalid entity op: {}".format(e.op)) - self.msg = Markup(msg.format(manager=manager, - principal=principal, - role=e.role, - entity=entity)) + self.msg = Markup( + msg.format( + manager=manager, + principal=principal, + role=e.role, + entity=entity)) tmpl = get_template_attribute('admin/_macros.html', 'm_security_entry') return tmpl(self) diff --git a/abilian/web/admin/panels/dashboard.py b/abilian/web/admin/panels/dashboard.py index ac5b77d7..d3adfb9e 100644 --- a/abilian/web/admin/panels/dashboard.py +++ b/abilian/web/admin/panels/dashboard.py @@ -50,10 +50,11 @@ def get(self): {'key': _('Total'), 'color': '#2ca02c', 'values': total_users} ] - return render_template("admin/dashboard.html", - stats=stats, - connections=connections, - new_logins=new_logins) + return render_template( + "admin/dashboard.html", + stats=stats, + connections=connections, + new_logins=new_logins) def stats_since(dt): diff --git a/abilian/web/admin/panels/groups/__init__.py b/abilian/web/admin/panels/groups/__init__.py index f92d2563..0d45bb9e 100644 --- a/abilian/web/admin/panels/groups/__init__.py +++ b/abilian/web/admin/panels/groups/__init__.py @@ -21,15 +21,15 @@ class GroupsPanel(AdminPanel): icon = 'grain' def install_additional_rules(self, add_url_rule): - add_url_rule('/groups', - view_func=views.JsonGroupsList.as_view(b'json_list')) + add_url_rule( + '/groups', view_func=views.JsonGroupsList.as_view(b'json_list')) add_url_rule('/new', view_func=views.GroupCreate.as_view(b'new')) - add_url_rule('//', - view_func=views.GroupView.as_view(b'group')) - add_url_rule('//edit', - view_func=views.GroupEdit.as_view( - b'group_edit', - view_endpoint='.groups_group')) + add_url_rule( + '//', view_func=views.GroupView.as_view(b'group')) + add_url_rule( + '//edit', + view_func=views.GroupEdit.as_view( + b'group_edit', view_endpoint='.groups_group')) def get(self): # FIXME: use widgets.AjaxMainTableView instead @@ -62,6 +62,5 @@ def get(self): 'sAjaxSource': url_for('.groups_json_list'), } - return render_template('admin/groups.html', - next=next, - datatable_options=datatable_options) + return render_template( + 'admin/groups.html', next=next, datatable_options=datatable_options) diff --git a/abilian/web/admin/panels/groups/forms.py b/abilian/web/admin/panels/groups/forms.py index 85db32f7..1344950a 100644 --- a/abilian/web/admin/panels/groups/forms.py +++ b/abilian/web/admin/panels/groups/forms.py @@ -18,8 +18,7 @@ class GroupAdminForm(Form): description = StringField(_l(u'Description'), filters=(strip,)) public = BooleanField( - _l(u'Public'), - widget=widgets.BooleanWidget(on_off_mode=True)) + _l(u'Public'), widget=widgets.BooleanWidget(on_off_mode=True)) roles = fields.Select2MultipleField( _l(u'Roles'), diff --git a/abilian/web/admin/panels/groups/views.py b/abilian/web/admin/panels/groups/views.py index 6a007266..e1b77ff6 100644 --- a/abilian/web/admin/panels/groups/views.py +++ b/abilian/web/admin/panels/groups/views.py @@ -42,8 +42,8 @@ def data(self, *args, **kw): if search: # TODO: gérer les accents - query = query.filter(func.lower(Group.name).like("%" + search + - "%")) + query = query.filter( + func.lower(Group.name).like("%" + search + "%")) count = query.count() columns = [func.lower(Group.name)] @@ -66,13 +66,15 @@ def data(self, *args, **kw): name = escape(getattr(group, "name") or "") roles = [r for r in security.get_roles(group) if r.assignable] columns = [] - columns.append(u'{name}'.format(url=group_url, - name=name)) + columns.append(u'{name}'.format( + url=group_url, name=name)) columns.append(text_type(members_count or 0)) - columns.append(render_template_string(u'''{%- for role in roles %} + columns.append( + render_template_string( + u'''{%- for role in roles %} {{ role }} {%- endfor %}''', - roles=roles)) + roles=roles)) columns.append(u'\u2713' if group.public else u'') data.append(columns) @@ -99,18 +101,20 @@ def view_url(self): # those buttons are made to have valid edit actions, but will not be shown in # edit forms: they must be availabe only during POST -ADD_USER_BUTTON = ButtonAction('form', - 'add_user', - condition=lambda v: request.method == 'POST', - title=_l(u'Add'), - btn_class='primary') - -REMOVE_USER_BUTTON = ButtonAction('form', - 'remove_user', - condition=lambda v: request.method == 'POST', - btn_class='danger', - icon=FAIcon('times'), - title="",) +ADD_USER_BUTTON = ButtonAction( + 'form', + 'add_user', + condition=lambda v: request.method == 'POST', + title=_l(u'Add'), + btn_class='primary') + +REMOVE_USER_BUTTON = ButtonAction( + 'form', + 'remove_user', + condition=lambda v: request.method == 'POST', + btn_class='danger', + icon=FAIcon('times'), + title="",) class GroupView(GroupBase, views.ObjectView): @@ -127,10 +131,10 @@ def template_kwargs(self): members = list(self.obj.members) members.sort(key=lambda u: (u.last_name, u.first_name)) kw['members'] = members - kw['roles'] = sorted( - [r - for r in security.get_roles(self.obj, no_group_roles=True) - if r.assignable]) + kw['roles'] = sorted([r + for r in security.get_roles( + self.obj, no_group_roles=True) + if r.assignable]) kw['ADD_USER_BUTTON'] = ADD_USER_BUTTON kw['REMOVE_USER_BUTTON'] = REMOVE_USER_BUTTON return kw @@ -152,9 +156,8 @@ def get_form_kwargs(self): kw = super(GroupEdit, self).get_form_kwargs() security = current_app.services['security'] roles = [ - r - for r in security.get_roles(self.obj, no_group_roles=True) - if r.assignable + r for r in security.get_roles( + self.obj, no_group_roles=True) if r.assignable ] kw['roles'] = [r.name for r in roles] return kw diff --git a/abilian/web/admin/panels/settings.py b/abilian/web/admin/panels/settings.py index 522b3728..4bc98660 100644 --- a/abilian/web/admin/panels/settings.py +++ b/abilian/web/admin/panels/settings.py @@ -29,9 +29,8 @@ def __init__(self, id, type_, label=None, description=None): self.description = description def __html__(self): - return render_template(self.template, - key=self, - config=current_app.config) + return render_template( + self.template, key=self, config=current_app.config) def value_from_request(self): return request.form.get(self.id).strip() @@ -42,13 +41,13 @@ class SessionLifeTimeKey(Key): template = 'admin/settings_session_lifetime.html' def __init__(self): - Key.__init__(self, - 'PERMANENT_SESSION_LIFETIME', - 'timedelta', - label=_l(u'Session lifetime'), - description=_l( - u'Session expiration time after last visit. ' - u'When session is expired user must login again.')) + Key.__init__( + self, + 'PERMANENT_SESSION_LIFETIME', + 'timedelta', + label=_l(u'Session lifetime'), + description=_l(u'Session expiration time after last visit. ' + u'When session is expired user must login again.')) def value_from_request(self): form = request.form @@ -108,8 +107,7 @@ def settings(self): return current_app.services.get('settings').namespace('config') def get(self): - return render_template('admin/settings.html', - keys=self._keys,) + return render_template('admin/settings.html', keys=self._keys) @csrf.protect def post(self): diff --git a/abilian/web/admin/panels/sysinfo.py b/abilian/web/admin/panels/sysinfo.py index 7d059d4d..50e8bd54 100644 --- a/abilian/web/admin/panels/sysinfo.py +++ b/abilian/web/admin/panels/sysinfo.py @@ -29,11 +29,12 @@ def get(self): packages = [] for dist in pkg_resources.working_set: - package = dict(name=dist.project_name, - key=dist.key, - version=dist.version - if dist.has_version() else u'Unknown version', - vcs=None,) + package = dict( + name=dist.project_name, + key=dist.key, + version=dist.version + if dist.has_version() else u'Unknown version', + vcs=None,) location = text_type(Path(dist.location).absolute()) vcs_name = vcs.get_backend_name(location) @@ -56,8 +57,9 @@ def get(self): config_values = [(k, repr(v)) for k, v in sorted(current_app.config.items())] - return render_template("admin/sysinfo.html", - python_version=python_version, - packages=packages, - uname=uname, - config_values=config_values) + return render_template( + "admin/sysinfo.html", + python_version=python_version, + packages=packages, + uname=uname, + config_values=config_values) diff --git a/abilian/web/admin/panels/users/__init__.py b/abilian/web/admin/panels/users/__init__.py index 896ca0c6..5263e538 100644 --- a/abilian/web/admin/panels/users/__init__.py +++ b/abilian/web/admin/panels/users/__init__.py @@ -21,11 +21,11 @@ class UsersPanel(AdminPanel): icon = 'user' def install_additional_rules(self, add_url_rule): - add_url_rule('/users', - view_func=views.JsonUsersList.as_view(b'json_list')) + add_url_rule( + '/users', view_func=views.JsonUsersList.as_view(b'json_list')) add_url_rule('/new', view_func=views.UserCreate.as_view(b'new')) - add_url_rule('/', - view_func=views.UserEdit.as_view(b'user')) + add_url_rule( + '/', view_func=views.UserEdit.as_view(b'user')) def get(self): # FIXME: use widgets.AjaxMainTableView instead @@ -62,6 +62,5 @@ def get(self): 'sAjaxSource': url_for('.users_json_list'), } - return render_template('admin/users.html', - next=next, - datatable_options=datatable_options) + return render_template( + 'admin/users.html', next=next, datatable_options=datatable_options) diff --git a/abilian/web/admin/panels/users/views.py b/abilian/web/admin/panels/users/views.py index aa171341..33391346 100644 --- a/abilian/web/admin/panels/users/views.py +++ b/abilian/web/admin/panels/users/views.py @@ -83,28 +83,30 @@ def data(self, *args, **kw): mugshot = user_photo_url(user, size=MUGSHOT_SIZE) name = escape(getattr(user, "name") or "") email = escape(getattr(user, "email") or "") - roles = [r - for r in security.get_roles(user, no_group_roles=True) - if r.assignable] + roles = [r for r in security.get_roles( + user, no_group_roles=True) if r.assignable] columns = [] columns.append( u'' - u''.format(url=user_url, - src=mugshot, - size=MUGSHOT_SIZE)) - columns.append(u'{name}'.format(url=user_url, - name=name)) + u''.format( + url=user_url, src=mugshot, size=MUGSHOT_SIZE)) + columns.append(u'{name}'.format( + url=user_url, name=name)) columns.append(u'{email}'.format( url=user_url, email=email)) columns.append(u'\u2713' if user.can_login else u'') - columns.append(render_template_string(u'''{%- for g in groups %} + columns.append( + render_template_string( + u'''{%- for g in groups %} {{ g.name }} {%- endfor %}''', - groups=sorted(user.groups))) - columns.append(render_template_string(u'''{%- for role in roles %} + groups=sorted(user.groups))) + columns.append( + render_template_string( + u'''{%- for role in roles %} {{ role }} {%- endfor %}''', - roles=roles)) + roles=roles)) if user.last_active: last_active = format_datetime(user.last_active) diff --git a/abilian/web/assets/__init__.py b/abilian/web/assets/__init__.py index 1a952f42..f41da03f 100644 --- a/abilian/web/assets/__init__.py +++ b/abilian/web/assets/__init__.py @@ -17,10 +17,8 @@ def init_app(app): assets = app.extensions['webassets'] assets.append_path(RESOURCES_DIR, '/static/abilian') - app.add_static_url('abilian', - RESOURCES_DIR, - endpoint='abilian_static', - roles=Anonymous) + app.add_static_url( + 'abilian', RESOURCES_DIR, endpoint='abilian_static', roles=Anonymous) app.before_first_request(requirejs_config) @@ -32,8 +30,8 @@ def requirejs_config(): # setup ckeditor ckeditor_lib = 'ckeditor/ckeditor' config['shim']['ckeditor'] = {'exports': 'CKEDITOR'} - config['paths']['ckeditor'] = url_for('abilian_static', - filename=ckeditor_lib) + config['paths']['ckeditor'] = url_for( + 'abilian_static', filename=ckeditor_lib) d3_lib = 'nvd3/d3.min' config['shim']['d3'] = {'exports': 'd3'} diff --git a/abilian/web/assets/filters.py b/abilian/web/assets/filters.py index 5eba1d68..28d1a9af 100644 --- a/abilian/web/assets/filters.py +++ b/abilian/web/assets/filters.py @@ -65,20 +65,22 @@ def input(self, _in, out, **kwargs): url_rewriter = get_filter('cssrewrite') url_rewriter.set_context(self.ctx) url_rewriter.setup() - url_rewriter.input(included, - buf, - source=rel_filename, - source_path=abs_filename, - output=source, - output_path=filepath) + url_rewriter.input( + included, + buf, + source=rel_filename, + source_path=abs_filename, + output=source, + output_path=filepath) buf.seek(0) # now process '@includes' directives in included file - self.input(buf, - out, - source=rel_filename, - source_path=abs_filename, - output=source, - output_path=filepath) + self.input( + buf, + out, + source=rel_filename, + source_path=abs_filename, + output=source, + output_path=filepath) if end < len(line): out.write(line[end:]) @@ -274,20 +276,21 @@ def _apply_less(self, in_, out, output_path, output, **kw): url_rewriter = get_filter('cssrewrite', replace=replace_url) url_rewriter.set_context(self.ctx) url_rewriter.setup() - url_rewriter.input(buf, - out, - source=output, - source_path=output_path, - output=output, - output_path=output_path) + url_rewriter.input( + buf, + out, + source=output, + source_path=output_path, + output=output, + output_path=output_path) def fix_url(self, cur_path, url): if url.startswith(u'data:'): # base64 embeded return url - src_path = os.path.normpath(os.path.abspath(os.path.join(cur_path, - url))) + src_path = os.path.normpath( + os.path.abspath(os.path.join(cur_path, url))) possible_paths = [p for p in self.ctx.url_mapping.keys() if src_path.startswith(p)] if not possible_paths: diff --git a/abilian/web/attachments/forms.py b/abilian/web/attachments/forms.py index 5e94b99e..0321708b 100644 --- a/abilian/web/attachments/forms.py +++ b/abilian/web/attachments/forms.py @@ -17,10 +17,7 @@ class AttachmentForm(Form): blob = FileField( - _l(u'file'), - validators=[required()], - filters=[strip], - multiple=False) + _l(u'file'), validators=[required()], filters=[strip], multiple=False) description = StringField(_l(u'description (optional)'), filters=[strip]) diff --git a/abilian/web/attachments/views.py b/abilian/web/attachments/views.py index e1a77d68..28843cdd 100644 --- a/abilian/web/attachments/views.py +++ b/abilian/web/attachments/views.py @@ -35,10 +35,8 @@ def register_default_view(state): state.app.default_view.register(Attachment, _default_attachment_view) -UPLOAD_BUTTON = ButtonAction('form', - 'edit', - btn_class='primary', - title=_l(u'Send')) +UPLOAD_BUTTON = ButtonAction( + 'form', 'edit', btn_class='primary', title=_l(u'Send')) class BaseAttachmentView(object): @@ -92,13 +90,14 @@ def get(self): content_type = metadata.get('mimetype') stream = blob.file.open('rb') - return send_file(stream, - as_attachment=True, - attachment_filename=filename, - mimetype=content_type, - cache_timeout=0, - add_etags=False, - conditional=False) + return send_file( + stream, + as_attachment=True, + attachment_filename=filename, + mimetype=content_type, + cache_timeout=0, + add_etags=False, + conditional=False) download_view = AttachmentDownload.as_view(b'download') diff --git a/abilian/web/comments/forms.py b/abilian/web/comments/forms.py index 5d27e681..d088ee88 100644 --- a/abilian/web/comments/forms.py +++ b/abilian/web/comments/forms.py @@ -16,10 +16,12 @@ class CommentForm(Form): - body = TextAreaField(label=_l(u'Comment'), - validators=[required()], - filters=(strip,), - widget=TextArea(rows=5, resizeable='vertical'),) + body = TextAreaField( + label=_l(u'Comment'), + validators=[required()], + filters=(strip,), + widget=TextArea( + rows=5, resizeable='vertical'),) class Meta: model = Comment diff --git a/abilian/web/comments/views.py b/abilian/web/comments/views.py index 4068ba57..fd79f5ef 100644 --- a/abilian/web/comments/views.py +++ b/abilian/web/comments/views.py @@ -36,10 +36,8 @@ def register_default_view(state): state.app.default_view.register(Comment, _default_comment_view) -COMMENT_BUTTON = ButtonAction('form', - 'edit', - btn_class='primary', - title=_l(u'Post')) +COMMENT_BUTTON = ButtonAction( + 'form', 'edit', btn_class='primary', title=_l(u'Post')) class BaseCommentView(object): @@ -92,9 +90,11 @@ def get_form_buttons(self, *args, **kwargs): def after_populate_obj(self): obj_meta = self.obj.meta.setdefault('abilian.core.models.comment', {}) history = obj_meta.setdefault('history', []) - history.append(dict(user_id=current_user.id, - user=text_type(current_user), - date=utc_dt(datetime.utcnow()).isoformat(),)) + history.append( + dict( + user_id=current_user.id, + user=text_type(current_user), + date=utc_dt(datetime.utcnow()).isoformat(),)) self.obj.meta.changed() diff --git a/abilian/web/coreviews/users.py b/abilian/web/coreviews/users.py index 859363dd..acc2a352 100644 --- a/abilian/web/coreviews/users.py +++ b/abilian/web/coreviews/users.py @@ -72,9 +72,10 @@ class UserJsonListing(JSONModelSearch): def filter(self, query, q, **kwargs): if q: - query = query.filter(or_( - func.lower(User.first_name).like(q + "%"), func.lower( - User.last_name).like(q + "%"))) + query = query.filter( + or_( + func.lower(User.first_name).like(q + "%"), func.lower( + User.last_name).like(q + "%"))) return query def order_by(self, query): diff --git a/abilian/web/filters.py b/abilian/web/filters.py index b0a4979b..a28c6af2 100644 --- a/abilian/web/filters.py +++ b/abilian/web/filters.py @@ -165,11 +165,12 @@ def age(dt, now=None, add_direction=True, date_threshold=None): # don't use (flask.ext.)babel.format_timedelta: as of Flask-Babel 0.9 it # doesn't support "threshold" arg. - return format_timedelta(delta, - locale=locale, - granularity='minute', - threshold=0.9, - add_direction=add_direction) + return format_timedelta( + delta, + locale=locale, + granularity='minute', + threshold=0.9, + add_direction=add_direction) def date_age(dt, now=None): diff --git a/abilian/web/forms/__init__.py b/abilian/web/forms/__init__.py index c83b43e7..989949ae 100644 --- a/abilian/web/forms/__init__.py +++ b/abilian/web/forms/__init__.py @@ -247,10 +247,11 @@ def __init__(self, *args, **kwargs): for label, fields in self.__class__._groups.items(): self._groups[label] = list(fields) - has_permission = partial(self._permissions.has_permission, - ctx.permission, - obj=ctx.obj, - user=ctx.user) + has_permission = partial( + self._permissions.has_permission, + ctx.permission, + obj=ctx.obj, + user=ctx.user) empty_form = not has_permission() for field_name in list(self._fields): diff --git a/abilian/web/forms/fields.py b/abilian/web/forms/fields.py index 54f9086f..5249ffb2 100644 --- a/abilian/web/forms/fields.py +++ b/abilian/web/forms/fields.py @@ -113,9 +113,8 @@ def __init__(self, *args, **kwargs): # instanciation so as to have permission filtering field_names = [] labels = [] - fieldsubform = self.unbound_field.bind(form=None, - name='dummy', - _meta=self.meta) + fieldsubform = self.unbound_field.bind( + form=None, name='dummy', _meta=self.meta) subform = fieldsubform.form_class(csrf_enabled=False) for f in subform: if f.is_hidden: @@ -126,8 +125,8 @@ def __init__(self, *args, **kwargs): self._field_names = field_names self._field_labels = labels - self._field_nameTolabel = dict(zip(self._field_names, - self._field_labels)) + self._field_nameTolabel = dict( + zip(self._field_names, self._field_labels)) def __call__(self, **kwargs): """ @@ -170,8 +169,9 @@ def __init__(self, *args, **kwargs): self._has_uploads = False if allow_delete is not None: - if any(isinstance(v, DataRequired if allow_delete else Optional) - for v in validators): + if any( + isinstance(v, DataRequired if allow_delete else Optional) + for v in validators): raise ValueError( "Field validators are conflicting with `allow_delete`," "validators={!r}, allow_delete={!r}".format(validators, diff --git a/abilian/web/forms/tests.py b/abilian/web/forms/tests.py index 63a53eb4..169e576e 100644 --- a/abilian/web/forms/tests.py +++ b/abilian/web/forms/tests.py @@ -95,9 +95,8 @@ def test_form_permissions_controller(): # field roles has_role.reset_mock() - fp = FormPermissions(default=MarkRole, - read=Anonymous, - fields_read={'test': Owner}) + fp = FormPermissions( + default=MarkRole, read=Anonymous, fields_read={'test': Owner}) fp.has_permission(READ) assert has_role.call_args[-1]['role'] == [Anonymous] diff --git a/abilian/web/forms/validators.py b/abilian/web/forms/validators.py index 110f8f1c..ef2aee0a 100644 --- a/abilian/web/forms/validators.py +++ b/abilian/web/forms/validators.py @@ -108,9 +108,8 @@ def __call__(self, form, field): message = _( u'Field must be between %(min)d and %(max)d characters long.', min=self.min, max=self.max) - raise ValidationError(message % dict(min=self.min, - max=self.max, - length=l)) + raise ValidationError(message % dict( + min=self.min, max=self.max, length=l)) class NumberRange(NumberRange, Rule): diff --git a/abilian/web/forms/widgets.py b/abilian/web/forms/widgets.py index 886e33fe..b161be95 100644 --- a/abilian/web/forms/widgets.py +++ b/abilian/web/forms/widgets.py @@ -167,15 +167,16 @@ def render(self, entities, **kwargs): 'bLengthChange': False, 'iDisplayLength': self.options.get('paginate_length', 50) } - js = render_template_string(''' + js = render_template_string( + ''' require( ['jquery', 'jquery.dataTables'], function($) { $('#{{ table_id }}').dataTable({{ options|tojson|safe }}); }); ''', - table_id=self.name, - options=datatable_options,) + table_id=self.name, + options=datatable_options,) table = [] for entity in entities: @@ -183,10 +184,9 @@ def render(self, entities, **kwargs): template = filter(bool, (self.options.get('template'), 'widgets/render_table.html')) - return Markup(render_template( - template, table=table, - js=Markup(js), - view=self, **kwargs)) + return Markup( + render_template( + template, table=table, js=Markup(js), view=self, **kwargs)) def render_line(self, entity): line = [] @@ -338,11 +338,12 @@ def render(self): for c in self.search_criterions: if not c.has_form_filter: continue - d = dict(name=c.name, - label=text_type(c.label), - type=c.form_filter_type, - args=c.form_filter_args, - unset=c.form_unset_value,) + d = dict( + name=c.name, + label=text_type(c.label), + type=c.form_filter_type, + args=c.form_filter_args, + unset=c.form_unset_value,) if c.has_form_default_value: d['defaultValue'] = c.form_default_value @@ -352,9 +353,11 @@ def render(self): datatable_options[ 'aoAdvancedSearchFilters'] = advanced_search_filters - return Markup(render_template('widgets/render_ajax_table.html', - datatable_options=datatable_options, - view=self)) + return Markup( + render_template( + 'widgets/render_ajax_table.html', + datatable_options=datatable_options, + view=self)) def render_line(self, entity): line = [] @@ -451,13 +454,15 @@ def render(self, item, form, related_views=()): template = filter(bool, (self.options.get('view_template'), 'widgets/render_single.html')) - return Markup(render_template(template, - view=self, - related_views=related_views, - csrf_token=csrf.field(), - entity=item, - panels=panels, - form=form)) + return Markup( + render_template( + template, + view=self, + related_views=related_views, + csrf_token=csrf.field(), + entity=item, + panels=panels, + form=form)) def render_form(self, form, for_new=False, has_save_and_add_new=False): # Client-side rules for jQuery.validate @@ -479,12 +484,14 @@ def render_form(self, form, for_new=False, has_save_and_add_new=False): template = filter(bool, (self.options.get('edit_template'), 'widgets/render_for_edit.html')) - return Markup(render_template(template, - view=self, - form=form, - for_new=for_new, - has_save_and_add_new=has_save_and_add_new, - rules=rules)) + return Markup( + render_template( + template, + view=self, + form=form, + for_new=for_new, + has_save_and_add_new=has_save_and_add_new, + rules=rules)) def label_for(self, field, mapper, name): label = field.label @@ -624,8 +631,9 @@ def __call__(self, field, *args, **kwargs): if 'value' not in kwargs: kwargs['value'] = field._value() - return Markup(render_template_string( - u''' + return Markup( + render_template_string( + u'''
{%- if widget.pre_icon %}
{{ widget.pre_icon }}
@@ -636,8 +644,9 @@ def __call__(self, field, *args, **kwargs): {%- endif %}
''', - widget=self, - params=self.html_params(name=field.name, **kwargs))) + widget=self, + params=self.html_params( + name=field.name, **kwargs))) @property def typename(self): @@ -709,13 +718,14 @@ def __call__(self, field, **kwargs): # due to debugtoolbar capturing template parameters del data['file'] - ctx = dict(id=field.id, - field=field, - widget=self, - input=input_elem, - button_label=button_label, - existing=existing, - uploaded=uploads) + ctx = dict( + id=field.id, + field=field, + widget=self, + input=input_elem, + button_label=button_label, + existing=existing, + uploaded=uploads) return Markup(render_template(self.template, **ctx)) def build_exisiting_files_list(self, field): @@ -788,8 +798,8 @@ def build_exisiting_files_list(self, field): if hasattr(value, 'url'): image_url = value.url else: - image_url = self.get_b64_thumb_url(self.get_thumb( - value, self.width, self.height)) + image_url = self.get_b64_thumb_url( + self.get_thumb(value, self.width, self.height)) data['image_url'] = image_url @@ -804,8 +814,8 @@ def build_uploads_list(self, field): image_url = value.url else: with value.open('rb') as in_: - image_url = self.get_b64_thumb_url(self.get_thumb( - in_, self.width, self.height)) + image_url = self.get_b64_thumb_url( + self.get_thumb(in_, self.width, self.height)) data['image_url'] = image_url @@ -842,10 +852,8 @@ def render_view(self, field, **kwargs): width, height = image.get_size(thumb) tmpl = u'' - return render_template_string(tmpl, - url=self.get_b64_thumb_url(thumb), - width=width, - height=height) + return render_template_string( + tmpl, url=self.get_b64_thumb_url(thumb), width=width, height=height) class Chosen(Select): @@ -855,8 +863,8 @@ class Chosen(Select): def __call__(self, field, **kwargs): kwargs.setdefault('id', field.id) - html = [u'' % html_params( + name=field.name, **kwargs)] for val, label, selected in field.iter_choices(): html.append(self.render_option(val, label, selected)) html.append(u'') @@ -883,8 +891,8 @@ def __call__(self, field, **kwargs): if 'value' not in kwargs: kwargs['value'] = field._value() - return HTMLString(u'' % self.html_params(name=field.name, - **kwargs)) + return HTMLString(u'' % self.html_params( + name=field.name, **kwargs)) class DateInput(Input): @@ -928,10 +936,8 @@ def __call__(self, field, **kwargs): s = u'
\n'.format(html_params(**attributes)) s += u' \n'.format( - html_params(name=field_name, - id=field_id, - value=value, - **kwargs)) + html_params( + name=field_name, id=field_id, value=value, **kwargs)) s += u' \n' s += u'
\n' return Markup(s) @@ -998,11 +1004,12 @@ def __call__(self, field, **kwargs): input_params = {k: Markup(json.dumps(v)) for k, v in input_params.items()} - ctx = dict(id=field_id, - value=value, - field=field, - required=False, - timepicker_attributes=input_params) + ctx = dict( + id=field_id, + value=value, + field=field, + required=False, + timepicker_attributes=input_params) return Markup(render_template(self.template, **ctx)) @@ -1212,8 +1219,8 @@ def render_view(self, field, **kwargs): # units, which we don't want # # \u00A0: non-breakable whitespace - return u'{value}\u00A0{unit}'.format(value=format_number(val), - unit=unit) + return u'{value}\u00A0{unit}'.format( + value=format_number(val), unit=unit) class EmailWidget(TextInput): @@ -1277,10 +1284,8 @@ def __init__(self, allowed_tags=None, template=None): def __call__(self, field, **kwargs): value = kwargs.pop('value') if 'value' in kwargs else field._value() kwargs.setdefault('allowed_tags', self.allowed_tags) - return render_template(self.template, - field=field, - value=value, - kw=kwargs) + return render_template( + self.template, field=field, value=value, kw=kwargs) class ListWidget(wtforms.widgets.ListWidget): @@ -1370,9 +1375,9 @@ def __call__(self, field, **kwargs): Data = namedtuple(data_type, field_names) labels = Data(*[f.label for f in field[0] if not f.is_hidden]) - return Markup(render_template(self.template, - labels=labels, - field=field)) + return Markup( + render_template( + self.template, labels=labels, field=field)) class ModelListWidget(object): @@ -1384,11 +1389,8 @@ def render_view(self, field, **kwargs): assert isinstance(field, ModelFieldList) value = field.object_data if not value: - return render_template(self.template, - field=field, - labels=(), - rows=(), - **kwargs) + return render_template( + self.template, field=field, labels=(), rows=(), **kwargs) field_names = field._field_names labels = field._field_labels @@ -1404,11 +1406,8 @@ def render_view(self, field, **kwargs): rows.append(Data(*row)) - rendered = render_template(self.template, - field=field, - labels=labels, - rows=rows, - **kwargs) + rendered = render_template( + self.template, field=field, labels=labels, rows=rows, **kwargs) return rendered @@ -1548,12 +1547,13 @@ def __call__(self, field, **kwargs): extra_args = Markup(html_params(**kwargs)) - ctx = dict(field=field, - name=field.name, - id=field.id, - input_value=input_value, - json_data=json_data, - required=not field.allow_blank, - data_node_id=data_node_id, - extra_args=extra_args) + ctx = dict( + field=field, + name=field.name, + id=field.id, + input_value=input_value, + json_data=json_data, + required=not field.allow_blank, + data_node_id=data_node_id, + extra_args=extra_args) return Markup(render_template(self.template, **ctx)) diff --git a/abilian/web/frontend.py b/abilian/web/frontend.py index 0ecbd0ed..4e8a88b9 100644 --- a/abilian/web/frontend.py +++ b/abilian/web/frontend.py @@ -157,7 +157,8 @@ def init_object(self, args, kwargs): def breadcrumb(self): return BreadcrumbItem( label=self.obj.name or self.obj.id, - url=Endpoint('.entity_view', entity_id=self.obj.id)) + url=Endpoint( + '.entity_view', entity_id=self.obj.id)) def prepare_args(self, args, kwargs): args, kwargs = super(BaseEntityView, self).prepare_args(args, kwargs) @@ -170,11 +171,12 @@ def redirect_to_index(self): @property def single_view(self): - return make_single_view(self.form, - view_template=self.module.view_template, - view=self, - module=self.module, - **self.module.view_options) + return make_single_view( + self.form, + view_template=self.module.view_template, + view=self, + module=self.module, + **self.module.view_options) def check_access(self): return self._check_view_permission(self) @@ -202,10 +204,11 @@ def can_create(self): if self.permission in cls_permissions: security = current_app.services['security'] - return security.has_permission(current_user, - create_cls.permission, - obj=self.obj, - roles=cls_permissions[permission]) + return security.has_permission( + current_user, + create_cls.permission, + obj=self.obj, + roles=cls_permissions[permission]) return False @@ -242,17 +245,17 @@ def object_actions(self): def template_kwargs(self): module = self.module related_views = [v.render(self.obj) for v in module.related_views] - rendered_entity = self.single_view.render(self.obj, - self.form, - related_views=related_views) + rendered_entity = self.single_view.render( + self.obj, self.form, related_views=related_views) audit_entries = audit_service.entries_for(self.obj) - return dict(rendered_entity=rendered_entity, - related_views=related_views, - audit_entries=audit_entries, - show_new_comment_form=True, - show_new_attachment_form=True, - module=self.module) + return dict( + rendered_entity=rendered_entity, + related_views=related_views, + audit_entries=audit_entries, + show_new_comment_form=True, + show_new_attachment_form=True, + module=self.module) class EntityEdit(BaseEntityView, ObjectEdit): @@ -262,10 +265,11 @@ class EntityEdit(BaseEntityView, ObjectEdit): @property def template_kwargs(self): rendered_entity = self.single_view.render_form(self.form) - return dict(rendered_entity=rendered_entity, - show_new_comment_form=False, - show_new_attachment_form=False, - module=self.module) + return dict( + rendered_entity=rendered_entity, + show_new_comment_form=False, + show_new_attachment_form=False, + module=self.module) class EntityCreate(BaseEntityView, ObjectCreate): @@ -281,9 +285,8 @@ def check_access(self): @property def template_kwargs(self): rendered_entity = self.single_view.render_form(self.form) - return dict(rendered_entity=rendered_entity, - for_new=True, - module=self.module) + return dict( + rendered_entity=rendered_entity, for_new=True, module=self.module) class EntityDelete(BaseEntityView, ObjectDelete): @@ -443,45 +446,51 @@ def __init__(self): self.view_form_class = self.edit_form_class # init class based views - kw = dict(Model=self.managed_class, - pk='entity_id', - module=self, - base_template=self.base_template) - self._setup_view("/", - b'entity_view', - self.view_cls, - Form=self.view_form_class, - **kw) + kw = dict( + Model=self.managed_class, + pk='entity_id', + module=self, + base_template=self.base_template) + self._setup_view( + "/", + b'entity_view', + self.view_cls, + Form=self.view_form_class, + **kw) view_endpoint = self.endpoint + '.entity_view' - self._setup_view("//edit", - b'entity_edit', - self.edit_cls, - Form=self.edit_form_class, - view_endpoint=view_endpoint, - **kw) - - self._setup_view("/new", - b'entity_new', - self.create_cls, - Form=self.edit_form_class, - chain_create_allowed=self.view_new_save_and_add, - view_endpoint=view_endpoint, - **kw) - - self._setup_view("//delete", - b'entity_delete', - self.delete_cls, - Form=self.edit_form_class, - view_endpoint=view_endpoint, - **kw) + self._setup_view( + "//edit", + b'entity_edit', + self.edit_cls, + Form=self.edit_form_class, + view_endpoint=view_endpoint, + **kw) + + self._setup_view( + "/new", + b'entity_new', + self.create_cls, + Form=self.edit_form_class, + chain_create_allowed=self.view_new_save_and_add, + view_endpoint=view_endpoint, + **kw) + + self._setup_view( + "//delete", + b'entity_delete', + self.delete_cls, + Form=self.edit_form_class, + view_endpoint=view_endpoint, + **kw) self._setup_view("/json", b'list_json', ListJson, module=self) - self._setup_view('/json_search', - b'json_search', - self.json_search_cls, - Model=self.managed_class) + self._setup_view( + '/json_search', + b'json_search', + self.json_search_cls, + Model=self.managed_class) self.init_related_views() @@ -529,13 +538,14 @@ def get_grouped_actions(self): def register_actions(self): ACTIONS = [ - ModuleAction(self, - u'entity', - u'create', - title=_l(u'Create New'), - icon=FAIcon('plus'), - endpoint=Endpoint(self.endpoint + '.entity_new'), - button='default',), + ModuleAction( + self, + u'entity', + u'create', + title=_l(u'Create New'), + icon=FAIcon('plus'), + endpoint=Endpoint(self.endpoint + '.entity_new'), + button='default',), ] for component in self.components: ACTIONS.extend(component.get_actions()) @@ -561,15 +571,13 @@ def create_blueprint(self, crud_app): self.blueprint = Blueprint(self.endpoint, __name__, url_prefix=self.url) for url, name, methods in self._urls: - self.blueprint.add_url_rule(url, - name, - getattr(self, name), - methods=methods) + self.blueprint.add_url_rule( + url, name, getattr(self, name), methods=methods) # run default_view decorator - default_view(self.blueprint, - self.managed_class, - id_attr='entity_id')(self.entity_view) + default_view( + self.blueprint, self.managed_class, + id_attr='entity_id')(self.entity_view) # delay registration of our breadcrumbs to when registered on app; thus # 'parents' blueprint can register theirs befores ours @@ -581,8 +589,9 @@ def _setup_breadcrumb_preprocessors(self, state): self.blueprint.url_value_preprocessor(self._add_breadcrumb) def _add_breadcrumb(self, endpoint, values): - g.breadcrumb.append(BreadcrumbItem(label=self.label, - url=Endpoint('.list_view'))) + g.breadcrumb.append( + BreadcrumbItem( + label=self.label, url=Endpoint('.list_view'))) @property def base_query(self): @@ -703,16 +712,18 @@ def ordered_query(self, request, query=None): @expose("/") def list_view(self): actions.context['module'] = self - table_view = AjaxMainTableView(name=self.managed_class.__name__.lower(), - columns=self.list_view_columns, - ajax_source=url_for('.list_json'), - search_criterions=self.search_criterions, - options=self.tableview_options) + table_view = AjaxMainTableView( + name=self.managed_class.__name__.lower(), + columns=self.list_view_columns, + ajax_source=url_for('.list_json'), + search_criterions=self.search_criterions, + options=self.tableview_options) rendered_table = table_view.render() - ctx = dict(rendered_table=rendered_table, - module=self, - base_template=self.base_template) + ctx = dict( + rendered_table=rendered_table, + module=self, + base_template=self.base_template) return render_template("default/list_view.html", **ctx) @expose("/json2") @@ -791,12 +802,13 @@ def __init__(self, def render(self, entity): view = RelatedTableView(self.column_names, self.options) related_entities = getattr(entity, self.attr) - return dict(label=self.label, - attr_name=self.attr, - rendered=view.render(related_entities, - related_to=entity), - show_empty=self.show_empty, - size=len(related_entities)) + return dict( + label=self.label, + attr_name=self.attr, + rendered=view.render( + related_entities, related_to=entity), + show_empty=self.show_empty, + size=len(related_entities)) # TODO: rename to CRMApp ? @@ -807,8 +819,8 @@ class CRUDApp(object): def __init__(self, app, modules=None, name=None): if name is None: name = self.__class__.__module__ - modules_signature = ','.join(str(module.id) - for module in self.modules) + modules_signature = ','.join( + str(module.id) for module in self.modules) name = name + '-' + modules_signature self.name = name diff --git a/abilian/web/preferences/tests/test_user_preferences.py b/abilian/web/preferences/tests/test_user_preferences.py index 64b81653..a8a91ba6 100644 --- a/abilian/web/preferences/tests/test_user_preferences.py +++ b/abilian/web/preferences/tests/test_user_preferences.py @@ -21,10 +21,11 @@ class TestUserPreferences(BaseTestCase): def setUp(self): BaseTestCase.setUp(self) - self.user = User(email='john@example.com', - first_name='John', - last_name='Doe', - can_login=True) + self.user = User( + email='john@example.com', + first_name='John', + last_name='Doe', + can_login=True) self.session.add(self.user) self.session.commit() @@ -32,10 +33,8 @@ def test_form_photo(self): url = url_for('preferences.user') uploads = self.app.extensions['uploads'] with AVATAR_COLORMAP.open('rb') as f: - handle = uploads.add_file(self.user, - f, - filename='avatar.png', - mimetype='image/png') + handle = uploads.add_file( + self.user, f, filename='avatar.png', mimetype='image/png') kwargs = dict(method='POST', data={'photo': handle}) diff --git a/abilian/web/preferences/user.py b/abilian/web/preferences/user.py index eda0b185..e61a9f93 100644 --- a/abilian/web/preferences/user.py +++ b/abilian/web/preferences/user.py @@ -24,23 +24,24 @@ class UserPreferencesForm(Form): password = StringField( - _l(u'New Password'), - widget=widgets.PasswordInput(autocomplete='off')) + _l(u'New Password'), widget=widgets.PasswordInput(autocomplete='off')) confirm_password = StringField( _l(u'Confirm new password'), widget=widgets.PasswordInput(autocomplete='off')) photo = fields.FileField( - label=_l('Photo'), - widget=widgets.ImageInput(width=55, height=55)) + label=_l('Photo'), widget=widgets.ImageInput( + width=55, height=55)) - locale = fields.LocaleSelectField(label=_l(u'Preferred Language'), - validators=(validators.required(),), - default=lambda: get_default_locale(),) + locale = fields.LocaleSelectField( + label=_l(u'Preferred Language'), + validators=(validators.required(),), + default=lambda: get_default_locale(),) - timezone = fields.TimezoneField(label=_l(u'Time zone'), - validators=(validators.required(),), - default=babel.dates.LOCALTZ,) + timezone = fields.TimezoneField( + label=_l(u'Time zone'), + validators=(validators.required(),), + default=babel.dates.LOCALTZ,) def validate_password(self, field): pwd = field.data @@ -102,19 +103,17 @@ def get(self): # subclass str/bytes to set additional 'url' attribute photo = type( b'Photo', (bytes,), - dict(object=photo, - url=url_for('users.photo', user_id=g.user.id))) + dict( + object=photo, url=url_for( + 'users.photo', user_id=g.user.id))) data['photo'] = photo - form = UserPreferencesForm(obj=g.user, - formdata=None, - prefix=self.id, - **data) + form = UserPreferencesForm( + obj=g.user, formdata=None, prefix=self.id, **data) if form['locale'].data is None: form['locale'].data = get_default_locale() - return render_template('preferences/user.html', - form=form, - title=self.label) + return render_template( + 'preferences/user.html', form=form, title=self.label) @csrf.support_graceful_failure def post(self): diff --git a/abilian/web/search/criterion.py b/abilian/web/search/criterion.py index 0004f2f9..a9b526be 100644 --- a/abilian/web/search/criterion.py +++ b/abilian/web/search/criterion.py @@ -83,8 +83,8 @@ class TextSearchCriterion(BaseCriterion): def __init__(self, name, label='', attributes=None, search_fmt='%{q}%'): super(TextSearchCriterion, self).__init__(name, label) - self.attributes = dict.fromkeys(attributes - if attributes is not None else (name,)) + self.attributes = dict.fromkeys( + attributes if attributes is not None else (name,)) self._attributes_prepared = False if isinstance(search_fmt, string_types): @@ -113,10 +113,12 @@ def _prepare_attributes(self): self.model.__class__.__name__, attr_name) to_del.append(attr_name) else: - val.update(dict(attr=attr, - name=name, - model=model, - rel_attr_name=rel_attr_name,)) + val.update( + dict( + attr=attr, + name=name, + model=model, + rel_attr_name=rel_attr_name,)) for k in to_del: del self.attributes[k] diff --git a/abilian/web/search/views.py b/abilian/web/search/views.py index 6604cbb9..88cf7133 100644 --- a/abilian/web/search/views.py +++ b/abilian/web/search/views.py @@ -20,10 +20,8 @@ logger = logging.getLogger(__name__) -BOOTSTRAP_MARKUP_HIGHLIGHTER = whoosh.highlight.HtmlFormatter(tagname='mark', - classname='', - termclass='term-', - between='[…]') +BOOTSTRAP_MARKUP_HIGHLIGHTER = whoosh.highlight.HtmlFormatter( + tagname='mark', classname='', termclass='term-', between='[…]') RESULTS_FRAGMENTER = whoosh.highlight.SentenceFragmenter() @@ -33,10 +31,8 @@ PAGE_SIZE = 20 MAX_LIVE_RESULTS_PER_CLASS = 5 -search = Blueprint('search', - __name__, - url_prefix="/search", - template_folder='templates') +search = Blueprint( + 'search', __name__, url_prefix="/search", template_folder='templates') route = search.route @@ -52,25 +48,29 @@ def init_search(endpoint, values): except: page = 1 - g.breadcrumb.append(BreadcrumbItem(label='"{}"'.format(q), - icon="search", - url=Endpoint('search.search_main', - q=q))) + g.breadcrumb.append( + BreadcrumbItem( + label='"{}"'.format(q), + icon="search", + url=Endpoint( + 'search.search_main', q=q))) page_kw = OrderedDict(q=q) object_types = request.args.getlist('object_type') if object_types: page_kw['object_type'] = object_types - g.breadcrumb.append(BreadcrumbItem( - label=' | '.join(friendly_fqcn(name) for name in object_types), - url=Endpoint('search.search_main', **page_kw))) + g.breadcrumb.append( + BreadcrumbItem( + label=' | '.join(friendly_fqcn(name) for name in object_types), + url=Endpoint('search.search_main', **page_kw))) if page > 1: - g.breadcrumb.append(BreadcrumbItem(label=text_type(page), - url=Endpoint('search.search_main', - page=page, - **page_kw))) + g.breadcrumb.append( + BreadcrumbItem( + label=text_type(page), + url=Endpoint( + 'search.search_main', page=page, **page_kw))) values['q'] = q values['page'] = page @@ -151,19 +151,20 @@ def search_main(q='', page=1): next_pages_numbered = [(index, page_url(page=index)) for index in range(page_min, page_max + 1)] - return render_template('search/search.html', - q=q, - results=results, - results_count=results_count, - pagecount=pagecount, - filtered_by_type=filtered_by_type, - by_object_type=by_object_type, - prev_page=prev_page, - next_page=next_page, - first_page=first_page, - last_page=last_page, - next_pages_numbered=next_pages_numbered, - friendly_fqcn=friendly_fqcn,) + return render_template( + 'search/search.html', + q=q, + results=results, + results_count=results_count, + pagecount=pagecount, + filtered_by_type=filtered_by_type, + by_object_type=by_object_type, + prev_page=prev_page, + next_page=next_page, + first_page=first_page, + last_page=last_page, + next_pages_numbered=next_pages_numbered, + friendly_fqcn=friendly_fqcn,) class Live(views.JSONView): diff --git a/abilian/web/setupwizard/__init__.py b/abilian/web/setupwizard/__init__.py index 6ae2c67f..7b207eaa 100644 --- a/abilian/web/setupwizard/__init__.py +++ b/abilian/web/setupwizard/__init__.py @@ -25,10 +25,8 @@ from abilian.web.blueprints import Blueprint logger = logging.getLogger(__name__) -setup = Blueprint('setup', - __name__, - allowed_roles=Anonymous, - template_folder='templates') +setup = Blueprint( + 'setup', __name__, allowed_roles=Anonymous, template_folder='templates') # list supported dialects and detect unavailable ones due to missing dbapi # module ('psycopg2' missing for example) @@ -50,9 +48,9 @@ _setup_steps = ( Step('db', 'step_db', 'Setup Database', 'Setup basic database connection'), - Step('redis', 'step_redis', 'Setup Redis', 'Redis connection'), - Step('site_info', 'step_site_info', 'Basic site informations', - 'Site name, admin email...'), + Step('redis', 'step_redis', 'Setup Redis', 'Redis connection'), Step( + 'site_info', 'step_site_info', 'Basic site informations', + 'Site name, admin email...'), Step('admin_account', 'step_admin_account', u'Admin account', None), Step('finalize', 'finalize', 'Finalize', None)) @@ -129,8 +127,8 @@ def step_db(): def step_db_form(): - return render_template('setupwizard/step_db.html', - data=session_get('db', {})) + return render_template( + 'setupwizard/step_db.html', data=session_get('db', {})) def step_db_validate(): @@ -163,19 +161,20 @@ def step_db_validate(): db_uri += u'/' if dialect == u'sqlite' and (not database or database == u':memory:'): - database = text_type(Path(current_app.instance_path) / u'data' / - u'sqlite.db') + database = text_type( + Path(current_app.instance_path) / u'data' / u'sqlite.db') db_uri += database # store in session - db_params = dict(uri=db_uri, - dialect=dialect, - username=username, - password=password, - host=host, - port=port, - database=database,) + db_params = dict( + uri=db_uri, + dialect=dialect, + username=username, + password=password, + host=host, + port=port, + database=database,) session_set('db', db_params) # test connection @@ -215,15 +214,16 @@ def step_redis(): def step_redis_form(): - return render_template('setupwizard/step_redis.html', - data=session_get('redis', {})) + return render_template( + 'setupwizard/step_redis.html', data=session_get('redis', {})) def step_redis_validate(): form = request.form - data = dict(host=form.get(u'host', u'localhost').strip(), - port=form.get(u'port', u'').strip() or u'6379', - db=form.get(u'db', u'').strip() or u'1',) + data = dict( + host=form.get(u'host', u'localhost').strip(), + port=form.get(u'port', u'').strip() or u'6379', + db=form.get(u'db', u'').strip() or u'1',) for k in ('port', 'db'): try: @@ -236,9 +236,8 @@ def step_redis_validate(): error = None try: - r = redis.StrictRedis(host=data['host'], - port=data['port'], - db=data['db']) + r = redis.StrictRedis( + host=data['host'], port=data['port'], db=data['db']) except Exception as e: error = u'Connection error, check parameters' raise e @@ -297,16 +296,18 @@ def step_site_info_form(): 'sitename': cfg.get('SITE_NAME', u'') or u'', 'mailsender': cfg.get('MAIL_SENDER', u'') or u'', } - return render_template('setupwizard/step_site_info.html', - data=session_get('site_info', default_data), - suggested_hosts=get_possible_hostnames()) + return render_template( + 'setupwizard/step_site_info.html', + data=session_get('site_info', default_data), + suggested_hosts=get_possible_hostnames()) def step_site_info_validate(): form = request.form - data = dict(sitename=form.get('sitename', u'').strip(), - mailsender=form.get('mailsender', u'').strip(), - server_mode=form.get('server_mode', u'').strip()) + data = dict( + sitename=form.get('sitename', u'').strip(), + mailsender=form.get('mailsender', u'').strip(), + server_mode=form.get('server_mode', u'').strip()) session_set('site_info', data) step_validated('site_info') @@ -326,19 +327,21 @@ def step_admin_account(): def step_admin_account_form(): - return render_template('setupwizard/step_admin_account.html', - data=session_get('admin_account', {})) + return render_template( + 'setupwizard/step_admin_account.html', + data=session_get('admin_account', {})) def step_admin_account_validate(): form = request.form password = form.get('password', u'').strip() confirm_password = form.get('confirm_password', u'').strip() - admin_user = dict(email=form.get('email', u'').strip(), - name=form.get('name', u'').strip(), - firstname=form.get('firstname', u'').strip(), - password=password, - confirm_password=confirm_password) + admin_user = dict( + email=form.get('email', u'').strip(), + name=form.get('name', u'').strip(), + firstname=form.get('firstname', u'').strip(), + password=password, + confirm_password=confirm_password) session_set('admin_account', admin_user) if password != confirm_password: @@ -366,8 +369,8 @@ def finalize(): def finalize_form(): file_location = os.path.join(current_app.instance_path, 'config.py') - return render_template('setupwizard/finalize.html', - file_location=file_location) + return render_template( + 'setupwizard/finalize.html', file_location=file_location) def finalize_validate(): @@ -400,19 +403,21 @@ def finalize_validate(): # create a new app that will be configured with new config, to create database # and admin_user setup_app = current_app._get_current_object() - app = setup_app.__class__(setup_app.import_name, - static_url_path=setup_app.static_url_path, - static_folder=setup_app.static_folder, - template_folder=setup_app.template_folder, - instance_path=setup_app.instance_path,) + app = setup_app.__class__( + setup_app.import_name, + static_url_path=setup_app.static_url_path, + static_folder=setup_app.static_folder, + template_folder=setup_app.template_folder, + instance_path=setup_app.instance_path,) with app.test_request_context('/setup/finalize'): app.create_db() db_session = app.db.session() - admin = User(email=admin_account['email'], - password=admin_account['password'], - last_name=admin_account['name'], - first_name=admin_account['firstname'], - can_login=True) + admin = User( + email=admin_account['email'], + password=admin_account['password'], + last_name=admin_account['name'], + first_name=admin_account['firstname'], + can_login=True) db_session.add(admin) security = get_service('security') security.grant_role(admin, Admin) @@ -421,8 +426,9 @@ def finalize_validate(): session_clear() response = make_response( - render_template('setupwizard/done.html', - config_file=config_file, - logging_file=logging_file), + render_template( + 'setupwizard/done.html', + config_file=config_file, + logging_file=logging_file), 200) return response diff --git a/abilian/web/tags/admin.py b/abilian/web/tags/admin.py index 40ddc225..0a05c4e1 100644 --- a/abilian/web/tags/admin.py +++ b/abilian/web/tags/admin.py @@ -61,8 +61,8 @@ def schedule_entities_reindex(entities): :param entities: as returned by :func:`get_entities_for_reindex` """ entities = [(e[0], e[1], e[2], dict(e[3])) for e in entities] - return index_update.apply_async( - kwargs=dict(index='default', items=entities)) + return index_update.apply_async(kwargs=dict( + index='default', items=entities)) class NSView(View): @@ -95,9 +95,7 @@ def get(self, ns): ns=ns, tags=tags, errors=self.form_errors, - merge_to=request.form.get('merge_to', - default='__None__', - type=int), + merge_to=request.form.get('merge_to', default='__None__', type=int), selected_tags=set(t.id for t in self._get_selected_tags()),) def redirect_to_view(self): @@ -277,15 +275,18 @@ def install_additional_rules(self, add_url_rule): add_url_rule( ns_base, endpoint='ns', - view_func=NSView.as_view(b'ns', view_endpoint=panel_endpoint)) + view_func=NSView.as_view( + b'ns', view_endpoint=panel_endpoint)) tag_base = ns_base + '/' - add_url_rule(tag_base, - endpoint='tag_edit', - view_func=TagEdit.as_view(b'tag_edit', - view_endpoint=panel_endpoint)) - - add_url_rule(tag_base + 'delete', - endpoint='tag_delete', - view_func=TagEdit.as_view(b'tag_delete', - view_endpoint=panel_endpoint)) + add_url_rule( + tag_base, + endpoint='tag_edit', + view_func=TagEdit.as_view( + b'tag_edit', view_endpoint=panel_endpoint)) + + add_url_rule( + tag_base + 'delete', + endpoint='tag_delete', + view_func=TagEdit.as_view( + b'tag_delete', view_endpoint=panel_endpoint)) diff --git a/abilian/web/tags/criterion.py b/abilian/web/tags/criterion.py index 995768c9..6641e7f4 100644 --- a/abilian/web/tags/criterion.py +++ b/abilian/web/tags/criterion.py @@ -38,8 +38,8 @@ def valid_tags(self): join_clause = entity_tag_tbl.join( self.model, self.model.id == entity_tag_tbl.c.entity_id,) - model_tags = sa.sql.select([entity_tag_tbl.c.tag_id], - from_obj=join_clause) + model_tags = sa.sql.select( + [entity_tag_tbl.c.tag_id], from_obj=join_clause) return Tag.query.filter(Tag.ns == self.ns, Tag.id.in_(model_tags))\ .all() @@ -68,9 +68,10 @@ def filter(self, query, module, request, searched_text, *args, **kwargs): if not tags: return query - cond = sa.sql.exists(sa.sql.select([1], sa.sql.and_( - entity_tag_tbl.c.entity_id == self.model.id, - entity_tag_tbl.c.tag_id.in_(t.id for t in tags)))) + cond = sa.sql.exists( + sa.sql.select([1], sa.sql.and_( + entity_tag_tbl.c.entity_id == self.model.id, + entity_tag_tbl.c.tag_id.in_(t.id for t in tags)))) return query.filter(cond) @property diff --git a/abilian/web/tags/extension.py b/abilian/web/tags/extension.py index 98865476..d7a2186b 100644 --- a/abilian/web/tags/extension.py +++ b/abilian/web/tags/extension.py @@ -44,11 +44,8 @@ def process(self, formdata=None, obj=None, data=None, **kwargs): if obj is not None: tags = getattr(obj, TAGS_ATTR, set()) - super(_TagsForm, self).process(formdata=formdata, - obj=None, - data=data, - tags=tags, - **kwargs) + super(_TagsForm, self).process( + formdata=formdata, obj=None, data=data, tags=tags, **kwargs) class TagsExtension(object): diff --git a/abilian/web/tags/views.py b/abilian/web/tags/views.py index 47994ede..363937da 100644 --- a/abilian/web/tags/views.py +++ b/abilian/web/tags/views.py @@ -17,10 +17,8 @@ from .forms import TagForm -bp = Blueprint('tags', - __name__, - url_prefix='/tags', - template_folder='templates') +bp = Blueprint( + 'tags', __name__, url_prefix='/tags', template_folder='templates') class BaseTagView(object): @@ -117,8 +115,8 @@ def form_valid(self): op(self.entity, ns=ns, label=label) -entity_bp.route('//add')(EntityTagManage.as_view(b'add', - mode='add')) +entity_bp.route('//add')(EntityTagManage.as_view( + b'add', mode='add')) entity_bp.route('//remove')(EntityTagManage.as_view( b'remove', mode='remove')) diff --git a/abilian/web/tests/test_actions.py b/abilian/web/tests/test_actions.py index 8354fc10..0edd8826 100644 --- a/abilian/web/tests/test_actions.py +++ b/abilian/web/tests/test_actions.py @@ -9,25 +9,25 @@ from abilian.testing import BaseTestCase from abilian.web.action import Action, Glyphicon, StaticIcon, actions -BASIC = Action('cat_1', - 'basic', - 'Basic Action', - url='http://some.where', - icon='ok') -CONDITIONAL = Action('cat_1', - 'conditional', - 'Conditional Action', - url='http://condition.al', - condition=lambda ctx: ctx['show_all'], - icon=Glyphicon('hand-right'), - button='warning') - -OTHER_CAT = Action('cat_2:sub', - 'other', - 'Other Action', - url=lambda ctx: 'http://count?%d' % len(ctx), - icon=StaticIcon('icons/other.png', size=14), - css='custom-class') +BASIC = Action( + 'cat_1', 'basic', 'Basic Action', url='http://some.where', icon='ok') +CONDITIONAL = Action( + 'cat_1', + 'conditional', + 'Conditional Action', + url='http://condition.al', + condition=lambda ctx: ctx['show_all'], + icon=Glyphicon('hand-right'), + button='warning') + +OTHER_CAT = Action( + 'cat_2:sub', + 'other', + 'Other Action', + url=lambda ctx: 'http://count?%d' % len(ctx), + icon=StaticIcon( + 'icons/other.png', size=14), + css='custom-class') ALL_ACTIONS = (BASIC, CONDITIONAL, OTHER_CAT) diff --git a/abilian/web/tests/test_filters.py b/abilian/web/tests/test_filters.py index 0c168328..68a4b547 100644 --- a/abilian/web/tests/test_filters.py +++ b/abilian/web/tests/test_filters.py @@ -77,9 +77,8 @@ def test_date_age(self): self.assertEqual("2012-06-10 16:30 (2 hours ago)", date_age(dt, now)) # for coverage: test when using default parameter now=None - dt_patcher = mock.patch.object(filters.datetime, - 'datetime', - mock.Mock(wraps=datetime.datetime)) + dt_patcher = mock.patch.object( + filters.datetime, 'datetime', mock.Mock(wraps=datetime.datetime)) with dt_patcher as mocked: mocked.utcnow.return_value = now self.assertEqual("2012-06-10 16:30 (2 hours ago)", date_age(dt)) @@ -109,9 +108,8 @@ def test_age(self): u'September 4, 2011, 8:12 PM') # using default parameter now=None - dt_patcher = mock.patch.object(filters.datetime, - 'datetime', - mock.Mock(wraps=datetime.datetime)) + dt_patcher = mock.patch.object( + filters.datetime, 'datetime', mock.Mock(wraps=datetime.datetime)) with dt_patcher as mocked: mocked.utcnow.return_value = now assert age(d1m) == u'1 minute ago' diff --git a/abilian/web/tests/test_widgets.py b/abilian/web/tests/test_widgets.py index 404694ce..b833a2ca 100644 --- a/abilian/web/tests/test_widgets.py +++ b/abilian/web/tests/test_widgets.py @@ -79,12 +79,10 @@ def test_single_view(self): with self.app.test_request_context(): panels = [Panel('main', Row('name'), Row('price'), Row('email'))] view = SingleView( - DummyForm, - *panels, - view=dict(can_edit=False, can_delete=False)) - model = WidgetTestModel(name="Renault Megane", - price=10000, - email="joe@example.com") + DummyForm, *panels, view=dict( + can_edit=False, can_delete=False)) + model = WidgetTestModel( + name="Renault Megane", price=10000, email="joe@example.com") form = DummyForm(obj=model) res = view.render(model, form) diff --git a/abilian/web/uploads/views.py b/abilian/web/uploads/views.py index 08092450..2ef4f8e3 100644 --- a/abilian/web/uploads/views.py +++ b/abilian/web/uploads/views.py @@ -45,7 +45,8 @@ class NewUploadView(BaseUploadsView, JSONView): def data(self, *args, **kwargs): return { 'handle': self.handle, - 'url': url_for('.handle', handle=self.handle), + 'url': url_for( + '.handle', handle=self.handle), } def post(self, *args, **kwargs): @@ -57,10 +58,8 @@ def post(self, *args, **kwargs): uploaded = form['file'].data filename = secure_filename(uploaded.filename) mimetype = uploaded.mimetype - self.handle = self.uploads.add_file(self.user, - uploaded, - filename=filename, - mimetype=mimetype) + self.handle = self.uploads.add_file( + self.user, uploaded, filename=filename, mimetype=mimetype) return self.get(*args, **kwargs) def put(self, *args, **kwargs): @@ -88,13 +87,14 @@ def get(self, handle, *args, **kwargs): content_type = metadata.get('mimetype') stream = file_obj.open('rb') - return send_file(stream, - as_attachment=True, - attachment_filename=filename, - mimetype=content_type, - cache_timeout=0, - add_etags=False, - conditional=False) + return send_file( + stream, + as_attachment=True, + attachment_filename=filename, + mimetype=content_type, + cache_timeout=0, + add_etags=False, + conditional=False) def delete(self, handle, *args, **kwargs): if self.uploads.get_file(self.user, handle) is None: diff --git a/abilian/web/views/files.py b/abilian/web/views/files.py index d7d7a87a..775480bd 100644 --- a/abilian/web/views/files.py +++ b/abilian/web/views/files.py @@ -127,9 +127,10 @@ def make_response(self, *args, **kwargs): blob = self.blob stream = blob.file.open('rb') - return send_file(stream, - as_attachment=False, - mimetype=self.content_type, - cache_timeout=0, - add_etags=False, - conditional=False) + return send_file( + stream, + as_attachment=False, + mimetype=self.content_type, + cache_timeout=0, + add_etags=False, + conditional=False) diff --git a/abilian/web/views/images.py b/abilian/web/views/images.py index ec524fc1..c215463c 100644 --- a/abilian/web/views/images.py +++ b/abilian/web/views/images.py @@ -25,8 +25,9 @@ blueprint = Blueprint('images', __name__, url_prefix='/images') route = blueprint.route -DEFAULT_AVATAR = Path(pkg_resources.resource_filename( - 'abilian.web', 'resources/img/avatar-default.png')) +DEFAULT_AVATAR = Path( + pkg_resources.resource_filename('abilian.web', + 'resources/img/avatar-default.png')) DEFAULT_AVATAR_MD5 = hashlib.md5(DEFAULT_AVATAR.open('rb').read()).hexdigest() @@ -201,10 +202,8 @@ def make_response(self, user, image, size, *args, **kwargs): color = colorsys.hsv_to_rgb(hue, 0.65, 1.0) color = [int(x * 255) for x in color] color = u'rgb({0[0]}, {0[1]}, {0[2]})'.format(color) - svg = render_template('default/avatar.svg', - color=color, - letter=letter, - size=size) + svg = render_template( + 'default/avatar.svg', color=color, letter=letter, size=size) response = make_response(svg) self.content_type = u'image/svg+xml' self.filename = u'avatar-{}.svg'.format(id_hash) @@ -213,9 +212,8 @@ def make_response(self, user, image, size, *args, **kwargs): user_photo = UserMugshot.as_view(b'user_photo', set_expire=True, max_size=500) route("/users/")(user_photo) -route('/users/default')(StaticImageView.as_view(b'user_default', - set_expire=True, - image=DEFAULT_AVATAR)) +route('/users/default')(StaticImageView.as_view( + b'user_default', set_expire=True, image=DEFAULT_AVATAR)) def user_url_args(user, size): diff --git a/abilian/web/views/object.py b/abilian/web/views/object.py index f8c2895e..b524d55a 100644 --- a/abilian/web/views/object.py +++ b/abilian/web/views/object.py @@ -51,11 +51,7 @@ class BaseObjectView(View): #: templates with a custom base base_template = "base.html" - def __init__(self, - Model=None, - pk=None, - base_template=None, - *args, + def __init__(self, Model=None, pk=None, base_template=None, *args, **kwargs): View.__init__(self, *args, **kwargs) cls = self.__class__ @@ -180,10 +176,8 @@ def template_kwargs(self): btn_class='default cancel' # .cancel: if jquery.validate is used it will ) # properly skip validation -EDIT_BUTTON = ButtonAction('form', - 'edit', - btn_class='primary', - title=_l(u'Save')) +EDIT_BUTTON = ButtonAction( + 'form', 'edit', btn_class='primary', title=_l(u'Save')) class ObjectEdit(ObjectView): @@ -222,13 +216,8 @@ def __init__(self, message_success=None, *args, **kwargs): - ObjectView.__init__(self, - Model, - pk, - Form, - template=template, - *args, - **kwargs) + ObjectView.__init__( + self, Model, pk, Form, template=template, *args, **kwargs) if view_endpoint is not None: self.view_endpoint = view_endpoint @@ -286,8 +275,8 @@ def handle_action(self, action): ''.format(action.encode('utf-8'))) break else: - raise ValueError('Unknown action: "{}"'.format(action.encode( - 'utf-8'))) + raise ValueError('Unknown action: "{}"'.format( + action.encode('utf-8'))) self.action = action self.button = button @@ -424,11 +413,12 @@ def form_csrf_invalid(self): return self.get() def send_activity(self): - activity.send(self, - actor=g.user, - verb=self.activity_verb, - object=self.obj, - target=self.activity_target) + activity.send( + self, + actor=g.user, + verb=self.activity_verb, + object=self.obj, + target=self.activity_target) @property def activity_target(self): @@ -438,10 +428,8 @@ def activity_target(self): return None -CREATE_BUTTON = ButtonAction('form', - 'create', - btn_class='primary', - title=_l(u'Create')) +CREATE_BUTTON = ButtonAction( + 'form', 'create', btn_class='primary', title=_l(u'Create')) CHAIN_CREATE_BUTTON = ButtonAction( 'form', 'chain_create', @@ -532,11 +520,12 @@ def get_form_buttons(self, *args, **kwargs): def delete(self): session = current_app.db.session() session.delete(self.obj) - activity.send(self, - actor=g.user, - verb="delete", - object=self.obj, - target=self.activity_target) + activity.send( + self, + actor=g.user, + verb="delete", + object=self.obj, + target=self.activity_target) try: session.commit() except sa.exc.IntegrityError as e: diff --git a/abilian/web/views/tests/test_registry.py b/abilian/web/views/tests/test_registry.py index c25b8ddd..bee09304 100644 --- a/abilian/web/views/tests/test_registry.py +++ b/abilian/web/views/tests/test_registry.py @@ -66,8 +66,8 @@ def url_from_type_and_id(obj, obj_type, obj_id): def test_default_url_func(self): obj = RegEntity(id=1) - @self.app.route('/regentities_path//view', - endpoint='regentity.view') + @self.app.route( + '/regentities_path//view', endpoint='regentity.view') def dummy_default_view(object_id): pass @@ -75,7 +75,8 @@ def dummy_default_view(object_id): self.app.default_view.url_for(obj), '/regentities_path/1/view') self.assertEqual( - self.app.default_view.url_for(obj, _external=True), + self.app.default_view.url_for( + obj, _external=True), 'http://localhost/regentities_path/1/view') def test_default_view_decorator(self): @@ -94,5 +95,6 @@ def view(object_id): self.app.register_blueprint(bp) self.assertEqual(self.app.default_view.url_for(obj), '/blueprint/1') self.assertEqual( - self.app.default_view.url_for(obj, _external=True), + self.app.default_view.url_for( + obj, _external=True), 'http://localhost/blueprint/1') diff --git a/setup.py b/setup.py index 4a297ab4..93a5448a 100644 --- a/setup.py +++ b/setup.py @@ -11,11 +11,11 @@ from setuptools.command.sdist import sdist as _sdist session = session = pip.download.PipSession() -_install_requires = pip.req.parse_requirements('requirements.in', - session=session) +_install_requires = pip.req.parse_requirements( + 'requirements.in', session=session) install_requires = [str(ir.req) for ir in _install_requires] -_dev_requires = pip.req.parse_requirements('etc/dev-requirements.txt', - session=session) +_dev_requires = pip.req.parse_requirements( + 'etc/dev-requirements.txt', session=session) dev_requires = [str(ir.req) for ir in _dev_requires] LONG_DESCRIPTION = open('README.rst', 'r').read() diff --git a/tests/integration/test_frontend.py b/tests/integration/test_frontend.py index 763fa604..d67f4bfd 100644 --- a/tests/integration/test_frontend.py +++ b/tests/integration/test_frontend.py @@ -31,10 +31,14 @@ class Contacts(Module): managed_class = Contact list_view_columns = [ - dict(name='_name', width=35), - dict(name='first_name', width=25), - dict(name='last_name', width=14), - dict(name='email', width=20), + dict( + name='_name', width=35), + dict( + name='first_name', width=25), + dict( + name='last_name', width=14), + dict( + name='email', width=20), ] edit_form_class = ContactEditForm diff --git a/tests/integration/test_search.py b/tests/integration/test_search.py index e4941a23..60f0e09f 100644 --- a/tests/integration/test_search.py +++ b/tests/integration/test_search.py @@ -20,11 +20,12 @@ def gen_name(ctx): class DummyContact1(Entity): name = column_property( - Column('name', - UnicodeText(), - info=SEARCHABLE, - default=gen_name, - onupdate=gen_name), + Column( + 'name', + UnicodeText(), + info=SEARCHABLE, + default=gen_name, + onupdate=gen_name), Entity.name) salutation = Column(UnicodeText, default="") @@ -43,9 +44,8 @@ def tearDown(self): def test_contacts_are_indexed(self): self.login_system() - contact = DummyContact1(first_name="John", - last_name="Test User", - email="test@example.com") + contact = DummyContact1( + first_name="John", last_name="Test User", email="test@example.com") self.session.add(contact) self.session.commit()