API

Package abilian

Module abilian.app

Base Flask application class, used by tests or to be extended in real applications.

class Application(name: Optional[Any] = None, *args, **kwargs)[source]

Base application class.

Extend it in your own app.

celery_app_cls

alias of abilian.core.celery.FlaskCelery

add_access_controller(name: str, func: Callable, endpoint: bool = False) → None[source]

Add an access controller.

If name is None it is added at application level, else if is considered as a blueprint name. If endpoint is True then it is considered as an endpoint.

add_static_url(url_path: str, directory: str, endpoint: str, roles: Collection[abilian.services.security.models.Role] = ()) → None[source]

Add a new url rule for static files.

Parameters
  • url_path – subpath from application static url path. No heading or trailing slash.

  • directory – directory to serve content from.

  • endpoint – flask endpoint name for this url rule.

Example:

app.add_static_url('myplugin',
                   '/path/to/myplugin/resources',
                   endpoint='myplugin_static')

With default setup it will serve content from directory /path/to/myplugin/resources from url http://…/static/myplugin

add_url_rule_with_role(rule: str, endpoint: str, view_func: Callable, roles: Collection[abilian.services.security.models.Role] = (), **options) → None[source]

See Flask.add_url_rule().

If roles parameter is present, it must be a abilian.service.security.models.Role instance, or a list of Role instances.

check_instance_folder(create=False)[source]

Verify instance folder exists, is a directory, and has necessary permissions.

Param:create

if True, creates directory hierarchy

Raises

OSError with relevant errno if something is wrong.

configure(config: Optional[type]) → None[source]
init_breadcrumbs() → None[source]

Insert the first element in breadcrumbs.

This happens during request_started event, which is triggered before any url_value_preprocessor and before_request handlers.

init_extensions() → None[source]

Initialize flask extensions, helpers and services.

install_id_generator(sender: flask.app.Flask, **kwargs) → None[source]
setup(config: Optional[type]) → None[source]
setup_nav_and_breadcrumbs(app: flask.app.Flask) → None[source]

Listener for request_started event.

If you want to customize first items of breadcrumbs, override init_breadcrumbs()

data_dir[source]
default_config = {'ABILIAN_UPSTREAM_INFO_ENABLED': False, 'ADMIN_PANELS': ('abilian.web.admin.panels.dashboard.DashboardPanel', 'abilian.web.admin.panels.audit.AuditPanel', 'abilian.web.admin.panels.login_sessions.LoginSessionsPanel', 'abilian.web.admin.panels.settings.SettingsPanel', 'abilian.web.admin.panels.users.UsersPanel', 'abilian.web.admin.panels.groups.GroupsPanel', 'abilian.web.admin.panels.sysinfo.SysinfoPanel', 'abilian.web.admin.panels.impersonate.ImpersonatePanel', 'abilian.services.vocabularies.admin.VocabularyPanel', 'abilian.web.tags.admin.TagPanel'), 'APPLICATION_ROOT': '/', 'BABEL_ACCEPT_LANGUAGES': ['en'], 'CELERYD_MAX_TASKS_PER_CHILD': 1000, 'CELERY_ACCEPT_CONTENT': ['pickle', 'json', 'msgpack', 'yaml'], 'CELERY_TIMEZONE': <StaticTzInfo 'Etc/UTC'>, 'DEBUG': None, 'DEBUG_TB_INTERCEPT_REDIRECTS': False, 'DEFAULT_COUNTRY': None, 'ENV': None, 'EXPLAIN_TEMPLATE_LOADING': False, 'JSONIFY_MIMETYPE': 'application/json', 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'LOGO_URL': Endpoint('abilian_static', *(), **{'filename': 'img/logo-abilian-32x32.png'}), 'MAIL_ADDRESS_TAG_CHAR': None, 'MAX_CONTENT_LENGTH': None, 'MAX_COOKIE_SIZE': 4093, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'PLUGINS': (), 'PREFERRED_URL_SCHEME': 'http', 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'PRIVATE_SITE': False, 'PRODUCTION': False, 'PROPAGATE_EXCEPTIONS': None, 'SECRET_KEY': 'CHANGEME', 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(seconds=43200), 'SENTRY_SDK_URL': 'https://browser.sentry-cdn.com/4.5.3/bundle.min.js', 'SERVER_NAME': None, 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_COOKIE_SECURE': False, 'SESSION_REFRESH_EACH_REQUEST': True, 'SQLALCHEMY_POOL_RECYCLE': 1800, 'SQLALCHEMY_TRACK_MODIFICATIONS': False, 'TEMPLATES_AUTO_RELOAD': None, 'TESTING': False, 'TRACKING_CODE': '', 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'USE_X_SENDFILE': False, 'WTF_CSRF_ENABLED': True, '__dict__': <attribute '__dict__' of 'DefaultConfig' objects>, '__doc__': None, '__module__': 'abilian.config', '__weakref__': <attribute '__weakref__' of 'DefaultConfig' objects>}
default_view = None

instance of web.views.registry.Registry.

js_api = None

json serializable dict to land in Javascript under Abilian.api

private_site

If True all views will require by default an authenticated user, unless Anonymous role is authorized. Static assets are always public.

class ServiceManager[source]

Mixin that provides lifecycle (register/start/stop) support for services.

start_services()[source]
stop_services()[source]
create_app(config: Optional[type] = None, app_class: type = <class 'abilian.app.Application'>, **kw) → abilian.app.Application[source]

Module abilian.i18n

I18n.

To mark strings for translation:

from abilian.i18n import _
_(u'message to translate')

Use _ for gettext, _l for lazy_gettext, _n for ngettext.

Babel extension support multiple translation paths. This allows to add more catalogs to search for translations, in LIFO order. This feature can be used to override some translations in a custom application, be providing a catalog with messages to override:

current_app.extensions['babel'].add_translations('abilian.core')

See add_translations.

To extract messages to build the message catalog template (.pot), use the following “-k” parameters:

$ pybabel extract -F babel.cfg -k "_n:1,2" -k "_l" -o "msg.pot" "src"

This can be made easier by placing in setup.cfg:

[extract_messages]
mapping_file = babel.cfg
keywords = _n:1,2 _l
output-file = msg.pot
input-dirs = src

And just type:

$ python setup.py extract_messages
_ = <function gettext>

gettext alias

_l = <function lazy_gettext>

lazy_gettext alias

_n = <function ngettext>

ngettext alias

class Babel(app=None, default_locale='en', default_timezone='UTC', default_domain='messages', date_formats=None, configure_jinja=True)[source]

Bases: flask_babel.Babel

Allow to load translations from other modules.

add_translations(module_name: str, translations_dir: str = 'translations', domain: str = 'messages') → None[source]

Add translations from external module.

For example:

babel.add_translations('abilian.core')

Will add translations files from abilian.core module.

init_app(app: flask.app.Flask) → None[source]

Set up this instance for use with app, if no app was passed to the constructor.

babel = <abilian.i18n.Babel object>

importable instance of Babel

get_default_locale() → babel.core.Locale[source]
gettext(string, **variables)[source]

Translates a string with the current locale and passes in the given keyword arguments as mapping to a string formatting string.

gettext(u'Hello World!')
gettext(u'Hello %(name)s!', name='World')
lazy_country_name(code)[source]
lazy_gettext(string, **variables)[source]

Like gettext() but the string returned is lazy which means it will be translated when it is used as an actual string.

Example:

hello = lazy_gettext(u'Hello World')

@app.route('/')
def index():
    return unicode(hello)
localeselector() → Optional[str][source]

Default locale selector used in abilian applications.

ngettext(singular, plural, num, **variables)[source]

Translates a string with the current locale and passes in the given keyword arguments as mapping to a string formatting string. The num parameter is used to dispatch between singular and various plural forms of the message. It is available in the format string as %(num)d or %(num)s. The source language should be English or a similar language which only has one plural form.

ngettext(u'%(num)d Apple', u'%(num)d Apples', num=len(apples))
render_template_i18n(template_name_or_list: str, **context) → str[source]

Try to build an ordered list of template to satisfy the current locale.

timezoneselector() → datetime.tzinfo[source]

Default timezone selector used in abilian applications.

babel = <abilian.i18n.Babel object>

importable instance of Babel

VALID_LANGUAGES_CODE = frozenset({'af', 'ak', 'am', 'ar', 'as', 'az', 'be', 'bg', 'bm', 'bn', 'bo', 'br', 'bs', 'ca', 'ce', 'cs', 'cu', 'cy', 'da', 'de', 'dz', 'ee', 'el', 'en', 'eo', 'es', 'et', 'eu', 'fa', 'ff', 'fi', 'fo', 'fr', 'fy', 'ga', 'gd', 'gl', 'gu', 'gv', 'ha', 'he', 'hi', 'hr', 'hu', 'hy', 'ia', 'id', 'ig', 'ii', 'is', 'it', 'ja', 'jv', 'ka', 'ki', 'kk', 'kl', 'km', 'kn', 'ko', 'ks', 'ku', 'kw', 'ky', 'lb', 'lg', 'ln', 'lo', 'lt', 'lu', 'lv', 'mg', 'mi', 'mk', 'ml', 'mn', 'mr', 'ms', 'mt', 'my', 'nb', 'nd', 'ne', 'nl', 'nn', 'om', 'or', 'os', 'pa', 'pl', 'ps', 'pt', 'qu', 'rm', 'rn', 'ro', 'ru', 'rw', 'sd', 'se', 'sg', 'si', 'sk', 'sl', 'sn', 'so', 'sq', 'sr', 'sv', 'sw', 'ta', 'te', 'tg', 'th', 'ti', 'tk', 'to', 'tr', 'tt', 'ug', 'uk', 'ur', 'uz', 'vi', 'vo', 'wo', 'xh', 'yi', 'yo', 'zh', 'zu'})

accepted languages codes

Package abilian.core

Module abilian.core.entities

Base class for entities, objects that are managed by the Abilian framwework (unlike SQLAlchemy models which are considered lower-level).

class Entity(*args, **kwargs)[source]

Bases: abilian.core.models.base.Indexable, abilian.core.models.BaseMixin, abilian.core.models.base.Model

Base class for Abilian entities.

From Sqlalchemy POV, Entities use Joined-Table inheritance, thus entities subclasses cannot use inheritance themselves (as of 2013 Sqlalchemy does not support multi-level inheritance)

The metaclass automatically sets up polymorphic inheritance parameters by inserting a mixin class in parent classes. If you need to pass additional parameters to __mapper_args__, do it as follow:

class MyContent(Entity):

    @sqlalchemy.ext.declarative.declared_attr
    def __mapper_args__(cls):
        # super(Mycontent, cls).__mapper_args__ would be prettier, but
        # `MyContent` is not defined at this stage.
        args = Entity.__dict__['__mapper_args__'].fget(cls)
        args['order_by'] = cls.created_at # for example
        return args
query_class

alias of EntityQuery

__init__(*args, **kwargs)
clone()[source]

Copy an entity: copy every field, except the id and sqlalchemy internals, without forgetting about the n-n relationships.

  • return: the newly created entity

Example:

def clone(self):
    old_attrs = self.__dict__.copy()
    del old_attrs['_sa_instance_state']
    if 'id' in old_attrs:
        del old_attrs['id']
    new = AnEntity(**old_attrs)
    # Needs special treatment for n-n relationship
    new.related_projects = self.related_projects
    new.ancestor = self
    return new
display_value(field_name, value=<object object>)

Return display value for fields having ‘choices’ mapping (stored value.

-> human readable value). For other fields it will simply return field value.

display_value should be used instead of directly getting field value.

If value is provided, it is “translated” to a human-readable value. This is useful for obtaining a human readable label from a raw value.

SLUG_SEPARATOR = '-'
__annotations__ = {'__auditable__': typing.FrozenSet, '__editable__': typing.FrozenSet, '__searchable__': typing.FrozenSet}
__auditable__ = frozenset({})
__default_permissions__ = frozenset({})

Permission to roles mapping to set at object creation time.

Default permissions can be declared as a dict on classes, the final datastructure will changed by metaclass to a frozenset of dict.items(). This is made to garantee the immutability of definition on parent classes.

Exemple definition:

__default_permissions__ = {
    READ: {Owner, Authenticated},
    WRITE: {Owner},
}

To alter inherited default permissions:

class Child(Parent):
    __default_permissions__ = dp = dict(ParentClass.__default_permissions__)
    dp[READ] = dp[READ] - {Authenticated} + {Anonymous}
    del dp
__editable__ = frozenset({})
__index_to__ = (('_indexable_roles_and_users', ('allowed_roles_and_users',)), ('_indexable_tag_ids', ('tag_ids',)), ('_indexable_tag_text', ('tag_text', 'text')))
__indexable__ = False
__mapper__ = <Mapper at 0x7f89a435ac18; Entity>
__mapper_args__ = {'polymorphic_on': '_entity_type'}
__module__ = 'abilian.core.entities'
__permissions__
__searchable__ = frozenset({})
__table__ = Table('entity', MetaData(bind=None), Column('id', Integer(), table=<entity>, primary_key=True, nullable=False), Column('created_at', DateTime(), table=<entity>, default=ColumnDefault(<function datetime.utcnow>)), Column('updated_at', DateTime(), table=<entity>, onupdate=ColumnDefault(<function datetime.utcnow>), default=ColumnDefault(<function datetime.utcnow>)), Column('deleted_at', DateTime(), table=<entity>), Column('name', UnicodeText(), table=<entity>), Column('slug', UnicodeText(), table=<entity>), Column('entity_type', String(length=1000), table=<entity>, nullable=False), Column('meta', JSON(), table=<entity>, nullable=False, default=ColumnDefault(<function dict>), server_default=DefaultClause('{}', for_update=False)), Column('creator_id', Integer(), ForeignKey('user.id'), table=<entity>), Column('owner_id', Integer(), ForeignKey('user.id'), table=<entity>), schema=None)
property auto_slug

This property is used to auto-generate a slug from the name attribute.

It can be customized by subclasses.

created_at
creator
creator_id
deleted_at
property entity_class
entity_type = ''
id
meta

A dictionnary of simple values (JSON-serializable) to conveniently annotate the entity.

It is recommanded to keep it lighwight and not store large objects in it.

name

The name is a string that is shown to the user; it could be a title for document, a folder name, etc.

property object_type
owner
owner_id
slug

The slug attribute may be used in URLs to reference the entity, but uniqueness is not enforced, even within same entity type. For example if an entity class represent folders, one could want uniqueness only within same parent folder.

If slug is empty at first creation, its is derived from the name. When name changes the slug is not updated. If name is also empty, the slug will be the friendly entity_type with concatenated with entity’s id.

updated_at
exception ValidationError[source]
class Entity(*args, **kwargs)[source]

Base class for Abilian entities.

From Sqlalchemy POV, Entities use Joined-Table inheritance, thus entities subclasses cannot use inheritance themselves (as of 2013 Sqlalchemy does not support multi-level inheritance)

The metaclass automatically sets up polymorphic inheritance parameters by inserting a mixin class in parent classes. If you need to pass additional parameters to __mapper_args__, do it as follow:

class MyContent(Entity):

    @sqlalchemy.ext.declarative.declared_attr
    def __mapper_args__(cls):
        # super(Mycontent, cls).__mapper_args__ would be prettier, but
        # `MyContent` is not defined at this stage.
        args = Entity.__dict__['__mapper_args__'].fget(cls)
        args['order_by'] = cls.created_at # for example
        return args
query_class

alias of EntityQuery

clone()[source]

Copy an entity: copy every field, except the id and sqlalchemy internals, without forgetting about the n-n relationships.

  • return: the newly created entity

Example:

def clone(self):
    old_attrs = self.__dict__.copy()
    del old_attrs['_sa_instance_state']
    if 'id' in old_attrs:
        del old_attrs['id']
    new = AnEntity(**old_attrs)
    # Needs special treatment for n-n relationship
    new.related_projects = self.related_projects
    new.ancestor = self
    return new
display_value(field_name, value=<object object>)

Return display value for fields having ‘choices’ mapping (stored value.

-> human readable value). For other fields it will simply return field value.

display_value should be used instead of directly getting field value.

If value is provided, it is “translated” to a human-readable value. This is useful for obtaining a human readable label from a raw value.

SLUG_SEPARATOR = '-'
property auto_slug

This property is used to auto-generate a slug from the name attribute.

It can be customized by subclasses.

created_at
creator
creator_id
deleted_at
property entity_class
entity_type = ''
id
meta

A dictionnary of simple values (JSON-serializable) to conveniently annotate the entity.

It is recommanded to keep it lighwight and not store large objects in it.

name

The name is a string that is shown to the user; it could be a title for document, a folder name, etc.

property object_type
owner
owner_id
slug

The slug attribute may be used in URLs to reference the entity, but uniqueness is not enforced, even within same entity type. For example if an entity class represent folders, one could want uniqueness only within same parent folder.

If slug is empty at first creation, its is derived from the name. When name changes the slug is not updated. If name is also empty, the slug will be the friendly entity_type with concatenated with entity’s id.

updated_at
class EntityQuery(entities, session=None)[source]
with_permission(permission: Permission, user: Optional[User] = None) → EntityQuery[source]
class Indexable[source]

Mixin with sensible defaults for indexable objects.

property object_key
property object_type
all_entity_classes[source]

Return the list of all concrete persistent classes that are subclasses of Entity.

Module abilian.core.extensions

Create all standard extensions.

get_extension(name: str)[source]

Get the named extension from the current app, returning None if not found.

Module abilian.core.logging

Special loggers

Changing patch_logger logging level must be done very early, because it may emit logging during imports. Ideally, it’s should be the very first action in your entry point before anything has been imported:

import logging
logging.getLogger('PATCH').setLevel(logging.INFO)
patch_logger = <PatchLoggerAdapter PATCH (WARNING)>

logger for monkey patchs. use like this: patch_logger.info(<func>`patched_func`)

Module abilian.core.signals

All signals used by Abilian Core.

Signals are the main tools used for decoupling applications components by sending notifications. In short, signals allow certain senders to notify subscribers that something happened.

Cf. http://flask.pocoo.org/docs/signals/ for detailed documentation.

The main signal is currently activity.

activity = <blinker.base.NamedSignal object at 0x7f89aa6e22b0; 'activity'>

This signal is used by the activity streams service and its clients.

components_registered = <blinker.base.NamedSignal object at 0x7f89aa6e21d0; 'app:components:registered'>

Triggered at application initialization when all extensions and plugins have been loaded

register_js_api = <blinker.base.NamedSignal object at 0x7f89aa6e2278; 'app:register-js-api'>

Trigger when JS api must be registered. At this time flask.url_for() is usable

user_loaded = <blinker.base.NamedSignal object at 0x7f89aa6e2358; 'user_loaded'>

This signal is sent when user object has been loaded. g.user and current_user are available.

Module abilian.core.sqlalchemy

Additional data types for sqlalchemy.

class JSON(*args, **kwargs)[source]

Stores any structure serializable with json.

Usage JSON() Takes same parameters as sqlalchemy.types.Text

impl

alias of sqlalchemy.sql.sqltypes.Text

process_bind_param(value: Any, dialect: sqlalchemy.engine.interfaces.Dialect) → Optional[str][source]

Receive a bound parameter value to be converted.

Subclasses override this method to return the value that should be passed along to the underlying TypeEngine object, and from there to the DBAPI execute() method.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

This operation should be designed with the reverse operation in mind, which would be the process_result_value method of this class.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

process_result_value(value: Optional[str], dialect: sqlalchemy.engine.interfaces.Dialect) → Union[Dict[str, Any], List[int], None][source]

Receive a result-row column value to be converted.

Subclasses should implement this method to operate on data fetched from the database.

Subclasses override this method to return the value that should be passed back to the application, given a value that is already processed by the underlying TypeEngine object, originally from the DBAPI cursor method fetchone() or similar.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

This operation should be designed to be reversible by the “process_bind_param” method of this class.

class JSONUniqueListType(*args, **kwargs)[source]

Store a list in JSON format, with items made unique and sorted.

process_bind_param(value, dialect)[source]

Receive a bound parameter value to be converted.

Subclasses override this method to return the value that should be passed along to the underlying TypeEngine object, and from there to the DBAPI execute() method.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

This operation should be designed with the reverse operation in mind, which would be the process_result_value method of this class.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

property python_type

Return the Python type object expected to be returned by instances of this type, if known.

Basically, for those types which enforce a return type, or are known across the board to do such for all common DBAPIs (like int for example), will return that type.

If a return type is not defined, raises NotImplementedError.

Note that any type also accommodates NULL in SQL which means you can also get back None from any type in practice.

class Locale(*args, **kwargs)[source]

Store a babel.Locale instance.

impl

alias of sqlalchemy.sql.sqltypes.UnicodeText

process_bind_param(value: Optional[Any], dialect: sqlalchemy.engine.interfaces.Dialect) → Optional[Any][source]

Receive a bound parameter value to be converted.

Subclasses override this method to return the value that should be passed along to the underlying TypeEngine object, and from there to the DBAPI execute() method.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

This operation should be designed with the reverse operation in mind, which would be the process_result_value method of this class.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

process_result_value(value: Optional[Any], dialect: sqlalchemy.engine.interfaces.Dialect) → Optional[Any][source]

Receive a result-row column value to be converted.

Subclasses should implement this method to operate on data fetched from the database.

Subclasses override this method to return the value that should be passed back to the application, given a value that is already processed by the underlying TypeEngine object, originally from the DBAPI cursor method fetchone() or similar.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

This operation should be designed to be reversible by the “process_bind_param” method of this class.

property python_type

Return the Python type object expected to be returned by instances of this type, if known.

Basically, for those types which enforce a return type, or are known across the board to do such for all common DBAPIs (like int for example), will return that type.

If a return type is not defined, raises NotImplementedError.

Note that any type also accommodates NULL in SQL which means you can also get back None from any type in practice.

class MutationDict[source]

Provides a dictionary type with mutability support.

clear() → None. Remove all items from D.[source]
classmethod coerce(key: str, value: Dict) → abilian.core.sqlalchemy.MutationDict[source]

Convert plain dictionaries to MutationDict.

pop(k[, d]) → v, remove specified key and return the corresponding value.[source]

If key is not found, d is returned if given, otherwise KeyError is raised

popitem() → (k, v), remove and return some (key, value) pair as a[source]

2-tuple; but raise KeyError if D is empty.

setdefault(key, failobj=None)[source]

Insert key with a value of default if key is not in the dictionary.

Return the value for key if key is in the dictionary, else default.

update([E, ]**F) → None. Update D from dict/iterable E and F.[source]

If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]

class MutationList[source]

Provides a list type with mutability support.

append(item: Any) → None[source]

Append object to the end of the list.

classmethod coerce(key: str, value: List) → abilian.core.sqlalchemy.MutationList[source]

Convert list to MutationList.

extend(other)[source]

Extend list by appending elements from the iterable.

insert(idx, value)[source]

Insert object before index.

pop(i=-1)[source]

Remove and return item at index (default last).

Raises IndexError if list is empty or index is out of range.

remove(item)[source]

Remove first occurrence of value.

Raises ValueError if the value is not present.

reverse()[source]

Reverse IN PLACE.

sort(*args, **kwargs)[source]

Stable sort IN PLACE.

class SQLAlchemy(app=None, use_native_unicode=True, session_options=None, metadata=None)[source]

Base subclass of flask_sqlalchemy.SQLAlchemy.

Add our custom driver hacks.

apply_driver_hacks(app: flask.app.Flask, info: sqlalchemy.engine.url.URL, options: Dict[str, Any]) → None[source]

This method is called before engine creation and used to inject driver specific hacks into the options. The options parameter is a dictionary of keyword arguments that will then be used to call the sqlalchemy.create_engine() function.

The default implementation provides some saner defaults for things like pool sizes for MySQL and sqlite. Also it injects the setting of SQLALCHEMY_NATIVE_UNICODE.

class Timezone(*args, **kwargs)[source]

Store a pytz.tzfile.DstTzInfo instance.

impl

alias of sqlalchemy.sql.sqltypes.UnicodeText

process_bind_param(value: Optional[Any], dialect: sqlalchemy.engine.interfaces.Dialect) → Optional[Any][source]

Receive a bound parameter value to be converted.

Subclasses override this method to return the value that should be passed along to the underlying TypeEngine object, and from there to the DBAPI execute() method.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

This operation should be designed with the reverse operation in mind, which would be the process_result_value method of this class.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

process_result_value(value: Optional[Any], dialect: sqlalchemy.engine.interfaces.Dialect) → Optional[Any][source]

Receive a result-row column value to be converted.

Subclasses should implement this method to operate on data fetched from the database.

Subclasses override this method to return the value that should be passed back to the application, given a value that is already processed by the underlying TypeEngine object, originally from the DBAPI cursor method fetchone() or similar.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

This operation should be designed to be reversible by the “process_bind_param” method of this class.

property python_type

Return the Python type object expected to be returned by instances of this type, if known.

Basically, for those types which enforce a return type, or are known across the board to do such for all common DBAPIs (like int for example), will return that type.

If a return type is not defined, raises NotImplementedError.

Note that any type also accommodates NULL in SQL which means you can also get back None from any type in practice.

class UUID(*args, **kwargs)[source]

Platform-independent UUID type.

Uses Postgresql’s UUID type, otherwise uses CHAR(32), storing as stringified hex values.

From SQLAlchemy documentation.

impl

alias of sqlalchemy.sql.sqltypes.CHAR

compare_against_backend(dialect, conn_type)[source]

Compare this type against the given backend type.

This function is currently not implemented for SQLAlchemy types, and for all built in types will return None. However, it can be implemented by a user-defined type where it can be consumed by schema comparison tools such as Alembic autogenerate.

A future release of SQLAlchemy will potentially implement this method for builtin types as well.

The function should return True if this type is equivalent to the given type; the type is typically reflected from the database so should be database specific. The dialect in use is also passed. It can also return False to assert that the type is not equivalent.

Parameters
  • dialect – a Dialect that is involved in the comparison.

  • conn_type – the type object reflected from the backend.

New in version 1.0.3.

load_dialect_impl(dialect: sqlalchemy.engine.interfaces.Dialect) → sqlalchemy.sql.sqltypes.CHAR[source]

Return a TypeEngine object corresponding to a dialect.

This is an end-user override hook that can be used to provide differing types depending on the given dialect. It is used by the TypeDecorator implementation of type_engine() to help determine what type should ultimately be returned for a given TypeDecorator.

By default returns self.impl.

process_bind_param(value: Union[None, str, uuid.UUID], dialect: sqlalchemy.engine.interfaces.Dialect) → Optional[str][source]

Receive a bound parameter value to be converted.

Subclasses override this method to return the value that should be passed along to the underlying TypeEngine object, and from there to the DBAPI execute() method.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

This operation should be designed with the reverse operation in mind, which would be the process_result_value method of this class.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

process_result_value(value: Optional[str], dialect: sqlalchemy.engine.interfaces.Dialect) → Optional[uuid.UUID][source]

Receive a result-row column value to be converted.

Subclasses should implement this method to operate on data fetched from the database.

Subclasses override this method to return the value that should be passed back to the application, given a value that is already processed by the underlying TypeEngine object, originally from the DBAPI cursor method fetchone() or similar.

The operation could be anything desired to perform custom behavior, such as transforming or serializing data. This could also be used as a hook for validating logic.

Parameters
  • value – Data to operate upon, of any type expected by this method in the subclass. Can be None.

  • dialect – the Dialect in use.

This operation should be designed to be reversible by the “process_bind_param” method of this class.

JSONDict(*args, **kwargs)[source]

Stores a dict as JSON on database, with mutability support.

JSONList(*args, **kwargs)[source]

Stores a list as JSON on database, with mutability support.

If kwargs has a param unique_sorted (which evaluated to True), list values are made unique and sorted.

filter_cols(model, *filtered_columns)[source]

Return columnsnames for a model except named ones.

Useful for defer() for example to retain only columns of interest

ping_connection(dbapi_connection: sqlite3.Connection, connection_record, connection_proxy) → None[source]

Ensure connections are valid.

From: http://docs.sqlalchemy.org/en/rel_0_8/core/pooling.html

In case db has been restarted pool may return invalid connections.

Module abilian.core.models

class IdMixin[source]
id = Column(None, Integer(), table=None, primary_key=True, nullable=False)
class Indexable[source]

Mixin with sensible defaults for indexable objects.

property object_key
property object_type
class Info(**kw)[source]
copy() → a shallow copy of D[source]
class Model(**kwargs)[source]
class TimestampedMixin[source]
created_at = Column(None, DateTime(), table=None, default=ColumnDefault(<function datetime.utcnow>))

creation date

deleted_at = Column(None, DateTime(), table=None)
updated_at = Column(None, DateTime(), table=None, onupdate=ColumnDefault(<function datetime.utcnow>), default=ColumnDefault(<function datetime.utcnow>))

last modification date

SYSTEM = {'auditable': False, 'editable': False}

SYSTEM properties are properties defined by the system and not supposed to be changed manually.

Subject classes (i.e. people, groups, etc.).

See ICOM-ics-v1.0 “Subject Branch”.

TODO: I’m not a big fan of the “subject” name. Could be replaced by something else, like “people” or “principal” ?

class User(password=None, **kwargs)[source]
query_class

alias of UserQuery

authenticate(password: str) → bool[source]
display_value(field_name, value=<object object>)

Return display value for fields having ‘choices’ mapping (stored value.

-> human readable value). For other fields it will simply return field value.

display_value should be used instead of directly getting field value.

If value is provided, it is “translated” to a human-readable value. This is useful for obtaining a human readable label from a raw value.

follow(followee)[source]
is_admin_of(group)[source]
is_following(other)[source]
is_member_of(group)[source]
join(group)[source]
leave(group)[source]
set_password(password: str) → None[source]

Encrypts and sets password.

unfollow(followee)[source]
can_login
created_at
deleted_at
email
entity_type = 'abilian.core.models.subjects.User'
first_name
followees
followers
groups
id
property is_online
last_active
last_name
locale
property name
password
photo
preferences
property short_name
timezone
updated_at
class Group(**kwargs)[source]
display_value(field_name, value=<object object>)

Return display value for fields having ‘choices’ mapping (stored value.

-> human readable value). For other fields it will simply return field value.

display_value should be used instead of directly getting field value.

If value is provided, it is “translated” to a human-readable value. This is useful for obtaining a human readable label from a raw value.

admins
created_at
deleted_at
description
entity_type = 'abilian.core.models.subjects.Group'
id
members
members_count
name
photo
public
updated_at
class Principal[source]

A principal is either a User or a Group.

has_role(role, context=None)[source]
class ClearPasswordStrategy[source]

Don’t encrypt at all.

This strategy should not ever be used elsewhere than in tests. It’s useful in tests since a hash like bcrypt is designed to be slow.

authenticate(user, password)[source]

Predicate to tell wether password match user’s or not.

process(user, password)[source]

Return a string to be stored as user password.

property name

Strategy name.

gen_random_password(length=15)[source]
create_root_user() → abilian.core.models.subjects.User[source]
class OwnedMixin(*args, **kwargs)[source]
creator = <RelationshipProperty at 0x7f89a294e4c8; no key>
creator_id = Column(None, NullType(), ForeignKey('user.id'), table=None)
property creator_name
owner = <RelationshipProperty at 0x7f89a294ef48; no key>
owner_id = Column(None, NullType(), ForeignKey('user.id'), table=None)
property owner_name

Blob.

References to files stored in a on-disk repository

class Blob(value=None, *args, **kwargs)[source]

Model for storing large file content.

Files are stored on-disk, named after their uuid. Repository is located in instance folder/data/files.

display_value(field_name, value=<object object>)

Return display value for fields having ‘choices’ mapping (stored value.

-> human readable value). For other fields it will simply return field value.

display_value should be used instead of directly getting field value.

If value is provided, it is “translated” to a human-readable value. This is useful for obtaining a human readable label from a raw value.

property file

Return pathlib.Path object used for storing value.

id
property md5

Return md5 from meta, or compute it if absent.

meta
property size

Return size in bytes of value.

uuid
property value

Binary value content.

class SupportTagging[source]
class Tag(**kwargs)[source]

Tags are text labels that can be attached to entities.Entity.

They are namespaced, so that independent group of tags can be defined in the application. The default namespace is “default”.

display_value(field_name, value=<object object>)

Return display value for fields having ‘choices’ mapping (stored value.

-> human readable value). For other fields it will simply return field value.

display_value should be used instead of directly getting field value.

If value is provided, it is “translated” to a human-readable value. This is useful for obtaining a human readable label from a raw value.

entities

entities attached to this tag

id
label

Label visible to the user

ns

namespace

register(cls)[source]

Register an Entity as a taggable class.

Can be used as a class decorator:

@tag.register
class MyContent(Entity):
    ....
supports_tagging(obj)[source]
Parameters

obj – a class or instance

TAGS_ATTR = '__tags__'

backref attribute on tagged elements

class Comment(*args, **kwargs)[source]

A Comment related to an Entity.

SLUG_SEPARATOR = '-'
body

comment’s main content

created_at
creator
creator_id
deleted_at
entity

Commented entity

entity_id
entity_type = 'abilian.core.models.comment.Comment'
property history
id
meta
name
owner
owner_id
slug
updated_at
class Commentable[source]
for_entity(obj, check_commentable=False)[source]

Return comments on an entity.

is_commentable(obj_or_class: Any) → bool[source]
Parameters

obj_or_class – a class or instance

register(cls: type) → type[source]

Register an Entity as a commentable class.

Can be used as a class decorator:

@comment.register
class MyContent(Entity):
    ...
ATTRIBUTE = '__comments__'

name of backref on target Entity object

class Attachment(*args, **kwargs)[source]

An Attachment owned by an Entity.

SLUG_SEPARATOR = '-'
blob

file. Stored in a Blob

blob_id
created_at
creator
creator_id
deleted_at
description
entity

owning entity

entity_id
entity_type = 'abilian.core.models.attachment.Attachment'
id
meta
name
owner
owner_id
slug
updated_at
class SupportAttachment[source]
for_entity(obj, check_support_attachments=False)[source]

Return attachments on an entity.

register(cls)[source]

Register an Entity as a attachmentable class.

Can be used as a class decorator:

@attachment.register
class MyContent(Entity):
    ....
set_attachment_name(mapper, connection, target)[source]
supports_attachments(obj)[source]
Parameters

obj – a class or instance

Returns

True is obj supports attachments.

ATTRIBUTE = '__attachments__'

name of backref on target Entity object

class BaseMixin[source]
to_dict()[source]
to_json()[source]
property column_names

Module abilian.core.util

Various tools that don’t belong some place specific.

class BasePresenter(model)[source]

A presenter wraps a model an adds specific (often, web-centric) accessors.

Subclass to make it useful. Presenters are immutable.

classmethod wrap_collection(models)[source]
class Pagination(page, per_page, total_count)[source]
iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2)[source]
property has_next
property has_prev
property next
property pages
property prev
class memoized(func)[source]

Decorator that caches a function’s return value each time it is called.

If called later with the same arguments, the cached value is returned (not reevaluated).

class timer(f)[source]

Decorator that mesures the time it takes to run a function.

encode_string(string)[source]

Encode a string to bytes, if it isn’t already.

Parameters

string – The string to encode

fqcn(cls: type) → str[source]

Fully Qualified Class Name.

friendly_fqcn(cls_or_cls_name: Union[type, str]) → str[source]

Friendly name of fully qualified class name.

Parameters

cls_or_cls_name – a string or a class

get_params(names)[source]

Return a dictionary with params from request.

TODO: I think we don’t use it anymore and it should be removed before someone gets hurt.

local_dt(dt: datetime.datetime) → datetime.datetime[source]

Return an aware datetime in system timezone, from a naive or aware datetime.

Naive datetime are assumed to be in UTC TZ.

md5(data)[source]

md5 function, as in flask-security.

noproxy(obj: Any)

Unwrap obj from werkzeug.local.LocalProxy if needed.

This is required if one want to test isinstance(obj, SomeClass).

slugify(value, separator='-')[source]

Slugify an Unicode string, to make it URL friendly.

unwrap(obj: Any)[source]

Unwrap obj from werkzeug.local.LocalProxy if needed.

This is required if one want to test isinstance(obj, SomeClass).

utc_dt(dt: datetime.datetime) → datetime.datetime[source]

Set UTC timezone on a datetime object.

A naive datetime is assumed to be in UTC TZ.

utcnow() → datetime.datetime[source]

Return a new aware datetime with current date and time, in UTC TZ.

Package abilian.services

Module abilian.services.base

exception ServiceNotRegistered[source]
class Service(app: Optional[Any] = None)[source]

Base class for services.

AppStateClass

State class to use for this Service

alias of ServiceState

static if_running(meth: Callable) → Callable[source]

Decorator for service methods that must be ran only if service is in running state.

init_app(app: Application) → None[source]
start(ignore_state: bool = False) → None[source]

Starts the service.

stop(ignore_state: bool = False) → None[source]

Stops the service.

property app_state

Current service state in current application.

:raise:RuntimeError if working outside application context.

name = ''

service name in Application.extensions / Application.services

property running
Returns

False if working outside application context, if service is not registered on current application, or if service is halted for current application.

class ServiceState(service: abilian.services.base.Service, running: bool = False)[source]

Service state stored in Application.extensions.

running = False
service = None

reference to Service instance

Module abilian.services.activity

class ActivityEntry(**kwargs)[source]

Main table for all activities.

display_value(field_name, value=<object object>)

Return display value for fields having ‘choices’ mapping (stored value.

-> human readable value). For other fields it will simply return field value.

display_value should be used instead of directly getting field value.

If value is provided, it is “translated” to a human-readable value. This is useful for obtaining a human readable label from a raw value.

actor
actor_id
happened_at
id
object
object_id
object_type
target
target_id
target_type
verb
class ActivityService(app: Optional[Any] = None)[source]
static entries_for_actor(actor: abilian.core.models.subjects.User, limit: int = 50) → List[abilian.services.activity.models.ActivityEntry][source]
log_activity(sender: None, actor: abilian.core.models.subjects.User, verb: str, object: Any, target: Optional[abilian.core.entities.Entity] = None) → None[source]
start(ignore_state: bool = False) → None[source]

Starts the service.

stop(ignore_state: bool = False) → None[source]

Stops the service.

name = 'activity'

Module abilian.services.conversion

Conversion service.

Hardcoded to manage only conversion to PDF, to text and to image series.

Includes result caching (on filesystem).

Assumes poppler-utils and LibreOffice are installed.

TODO: rename Converter into ConversionService ?

exception ConversionError[source]
exception HandlerNotFound[source]
class Converter[source]
clear() → None[source]
get_image(digest, blob, mime_type, index, size=500)[source]

Return an image for the given content, only if it already exists in the image cache.

get_metadata(digest, content, mime_type)[source]

Get a dictionary representing the metadata embedded in the given content.

has_image(digest, mime_type, index, size=500)[source]

Tell if there is a preview image.

init_app(app: flask.app.Flask) → None[source]
init_work_dirs(cache_dir: pathlib.Path, tmp_dir: pathlib.Path) → None[source]
register_handler(handler: abilian.services.conversion.handlers.Handler) → None[source]
to_image(digest: str, blob: bytes, mime_type: str, index: int, size: int = 500) → bytes[source]

Convert a file to a list of images.

Returns image at the given index.

to_pdf(digest: str, blob: bytes, mime_type: str) → bytes[source]
to_text(digest: str, blob: bytes, mime_type: str) → str[source]

Convert a file to plain text.

Useful for full-text indexing. Returns a Unicode string.

Module abilian.services.image

Provides tools (currently: only functions, not a real service) for image processing.

resize(orig: Any, width: int, height: int, mode: str = 'fit') → bytes[source]
get_format(img)[source]
SCALE = 'scale'

resize without retaining original proportions

FIT = 'fit'

resize image and retain original proportions. Image width and height will be at most specified width and height, respectively; At least width or height will be equal to specified width and height, respectively.

CROP = 'crop'

crop image and resize so that it matches specified width and height.

Module abilian.services.indexing

service

Index documents using whoosh.

class WhooshIndexService(*args, **kwargs)[source]

Index documents using whoosh.

AppStateClass

alias of IndexServiceState

after_commit(session: sqlalchemy.orm.session.Session) → None[source]

Any db updates go through here.

We check if any of these models have __searchable__ fields, indicating they need to be indexed. With these we update the whoosh index for the model. If no index exists, it will be created here; this could impose a penalty on the initial commit of a model.

after_flush(session: sqlalchemy.orm.session.Session, flush_context: sqlalchemy.orm.unitofwork.UOWTransaction) → None[source]
clear() → None[source]

Remove all content from indexes, and unregister all classes.

After clear() the service is stopped. It must be started again to create new indexes and register classes.

clear_update_queue(app: Optional[flask.app.Flask] = None) → None[source]
get_document(obj: abilian.core.entities.Entity, adapter: abilian.services.indexing.adapter.SAAdapter = None) → Dict[str, Any][source]
index(name: str = 'default') → whoosh.index.Index[source]
index_objects(objects, index='default')[source]

Bulk index a list of objects.

init_app(app: Application) → None[source]
init_indexes() → None[source]

Create indexes for schemas.

register_class(cls: type, app_state: abilian.services.indexing.service.IndexServiceState = None) → None[source]

Register a model class.

register_classes() → None[source]
register_search_filter(func)[source]

Register a function that returns a query used for filtering search results. This query is And’ed with other filters.

If no filtering should be performed the function must return None.

register_value_provider(func)[source]

Register a function that may alter content of indexable document.

It is used in get_document() and called after adapter has built document.

The function must accept (document, obj) as arguments, and return the new document object.

search(q: str, index: str = 'default', fields: Optional[Dict[str, float]] = None, Models: Tuple[Type[abilian.core.models.base.Model]] = (), object_types: Tuple[str] = (), prefix: bool = True, facet_by_type: None = None, **search_args)[source]

Interface to search indexes.

Parameters
  • q – unparsed search string.

  • index – name of index to use for search.

  • fields – optionnal mapping of field names -> boost factor?

  • Models – list of Model classes to limit search on.

  • object_types – same as Models, but directly the model string.

  • prefix – enable or disable search by prefix

  • facet_by_type – if set, returns a dict of object_type: results with a max of limit matches for each type.

  • search_args – any valid parameter for whoosh.searching.Search.search(). This includes limit, groupedby and sortedby

search_for_class(query, cls, index='default', **search_args)[source]
searchable_object_types() → List[source]

List of (object_types, friendly name) present in the index.

start(ignore_state: bool = False) → None[source]

Starts the service.

property default_search_fields

Return default field names and boosts to be used for searching.

Can be configured with SEARCH_DEFAULT_BOOSTS

name = 'indexing'
indexable_role(principal: abilian.services.security.models.Role) → str[source]

Return a string suitable for query against allowed_roles_and_users field.

Parameters

principal – It can be Anonymous, Authenticated, or an instance of User or Group.

Module abilian.services.security

service

Security service, manages roles and permissions.

class SecurityServiceState(service: abilian.services.base.Service, running: bool = False)[source]
needs_db_flush = False

True if security has changed

use_cache = True
Anonymous

Defines role by name. Roles instances are unique by name.

Parameters

assignable – true if this role is can be assigned through security service. Non-assignable roles are roles automatically given depending on context (ex: Anonymous/Authenticated).

Authenticated

Defines role by name. Roles instances are unique by name.

Parameters

assignable – true if this role is can be assigned through security service. Non-assignable roles are roles automatically given depending on context (ex: Anonymous/Authenticated).

Package abilian.web

Module abilian.web.attachments

register_plugin(app: flask.app.Flask) → None[source]

Module abilian.web.comments

register_plugin(app: flask.app.Flask) → None[source]

Module abilian.web.filters

Add a few specific filters to Jinja2.

abbrev(s: str, max_size: int) → str[source]
age(dt: Optional[datetime.datetime], now: Optional[datetime.datetime] = None, add_direction: bool = True, date_threshold: Optional[Any] = None) → str[source]
Parameters
  • dtdatetime instance to format

  • nowdatetime instance to compare to dt

  • add_direction – if True, will add “in” or “ago” (example for en locale) to time difference dt - now, i.e “in 9 min.” or ” 9min. ago”

  • date_threshold – above threshold, will use a formated date instead of elapsed time indication. Supported values: “day”.

autoescape(filter_func: Callable) → Callable[source]

Decorator to autoescape result from filters.

babel2datepicker(pattern: babel.dates.DateTimePattern) → str[source]

Convert date format from babel (http://babel.pocoo.org/docs/dates/#date- fields)) to a format understood by bootstrap-datepicker.

bool2check(val, true='✓', false='')[source]

Filter value as boolean and show check mark (✓) or nothing.

date_age(dt: Optional[datetime.datetime], now: Optional[datetime.datetime] = None) → str[source]
date_fmt(value, format='EE, d MMMM y')[source]

@deprecated: use flask_babel’s dateformat filter instead.

datetimeparse(s) → Optional[datetime.datetime][source]

Parse a string date time to a datetime object.

Suitable for dates serialized with .isoformat()

Returns

None, or an aware datetime instance, tz=UTC.

filesize(d: Union[int, str]) → markupsafe.Markup[source]
init_filters(env: jinja2.environment.Environment) → None[source]
labelize(s: str) → str[source]
linkify(s: str) → markupsafe.Markup[source]
nl2br(value: str) → markupsafe.Markup[source]

Replace newlines with <br />.

obj_to_url(obj)[source]

Find url for obj using url_for(), return empty string is not found.

url_for() is also provided in jinja context, the filtering version is forgiving when obj has no default view set.

paragraphs(value: str) → str[source]

Blank lines delimitates paragraphs.

roughsize(size: int, above: int = 20, mod: int = 10) → str[source]

6 -> ‘6’ 15 -> ‘15’ 134 -> ‘130+’.

to_timestamp(dt)[source]

Module abilian.web.action

class ActionRegistry[source]

The Action registry.

This is a Flask extension which registers Action sets. Actions are grouped by category and are ordered by registering order.

From your application use the instanciated registry actions.

The registry is available in jinja2 templates as actions.

actions(context: Optional[Any] = None) → Dict[str, Any][source]

Return a mapping of category => actions list.

Actions are filtered according to Action.available().

if context is None, then current action context is used (context).

for_category(category: str, context: Any = None) → List[abilian.web.action.Action][source]

Returns actions list for this category in current application.

Actions are filtered according to Action.available().

if context is None, then current action context is used (context)

init_app(app: flask.app.Flask) → None[source]
installed(app: Optional[flask.app.Flask] = None) → bool[source]

Return True if the registry has been installed in current applications.

register(*actions) → None[source]

Register actions in the current application. All actions must be an instance of Action or one of its subclasses.

If overwrite is True, then it is allowed to overwrite an existing action with same name and category; else ValueError is raised.

property context

Return action context (dict type).

Applications can modify it to suit their needs.

class Action(category: str, name: str, title: Union[flask_babel.speaklater.LazyString, str] = '', description: str = '', icon: Union[str, abilian.web.action.Icon, None] = None, url: Union[str, Callable] = '', endpoint: Optional[abilian.web.action.Endpoint] = None, condition: Optional[Callable] = None, status: Optional[Any] = None, template: Optional[Any] = None, template_string: Optional[Any] = None, button: Optional[Any] = None, css: Optional[Any] = None)[source]

Action interface.

class Endpoint(name: str, *args, **kwargs)
get_kwargs() → Dict[str, str]

Hook for subclasses.

The key and values in the returned dictionnary can be safely changed without side effects on self.kwargs (provided you don’t alter mutable values, like calling list.pop()).

available(context: Dict[str, Any]) → bool[source]

Determine if this actions is available in this context.

Parameters

context – a dict whose content is left to application needs; if condition is a callable it receives context in parameter.

get_render_args(**kwargs) → Dict[str, Any][source]
pre_condition(context: Dict[str, Any]) → bool[source]

Called by available() before checking condition.

Subclasses may override it to ease creating actions with repetitive check (for example: actions that apply on a given content type only).

render(**kwargs) → markupsafe.Markup[source]
url(context: Dict[str, Any] = None) → str[source]
CSS_CLASS = 'action action-{category} action-{category}-{name}'
property description
property enabled
property endpoint
property icon
property status
template_string = '<a class="{{ action.css_class }}" href="{{ url }}">{%- if action.icon %}{{ action.icon }} {% endif %}{{ action.title }}</a>'
property title
class ActionDropDown(category, name, items=(), *args, **kwargs)[source]

Renders as a button dropdown.

template_string = '\n <div class="btn-group">\n <button type="button" class="{{ action.css_class }} dropdown-toggle"\n data-toggle="dropdown" aria-expanded="false">\n {%- if action.icon %}{{ action.icon }} {% endif %}\n {{ action.title }}\n <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" role="menu">\n {%- for entry in action_items %}\n {%- if entry.divider %}<li class="divider"></li>{%- endif %}\n <li>{{ entry.render() }}</a>\n </li>\n {%- endfor %}\n </ul>\n </div>\n '
class ActionGroup(category, name, items=(), *args, **kwargs)[source]

A group of single actions.

get_render_args(**kwargs)[source]
template_string = '<div class="btn-group" role="group" aria-label="{{ action.name}}">{%- for entry in action_items %}{{ entry.render() }}{%- endfor %}</div>'
class ActionGroupItem(category, name, divider=False, *args, **kwargs)[source]
divider = False

if True, add a divider in dropdowns

class ButtonAction(category: str, name: str, submit_name: str = '__action', btn_class: str = 'default', *args, **kwargs)[source]
btn_class = 'default'
template_string = '<button type="submit" class="btn btn-{{ action.btn_class }} {{ action.css_class}}" name="{{ action.submit_name }}" value="{{ action.name }}">{%- if action.icon %}{{ action.icon }} {% endif %}{{ action.title }}</button>'
class FAIcon(name: str = '')[source]

Renders markup for FontAwesome icons.

template = <Template memory:7f89a5c3cef0>
class DynamicIcon(endpoint: Union[str, Callable, None] = None, width: int = 12, height: int = 12, css: str = '', size: Optional[int] = None, url_args: Optional[Callable] = None, **fixed_url_args)[source]
get_url_args() → Dict[str, str][source]
template = <Template memory:7f89a5c48358>
class StaticIcon(filename: str, endpoint: str = 'static', width: int = 12, height: int = 12, css: str = '', size: Optional[int] = None)[source]

Renders markup for icon located in static folder served by endpoint.

Default endpoint is application static folder.

class ModalActionMixin[source]
template_string = '<a class="{{ action.css_class }}" href="{{ url }}" data-toggle="modal">{%- if action.icon %}{{ action.icon}} {% endif %}{{ action.title }}</a>'
class Endpoint(name: str, *args, **kwargs)[source]
get_kwargs() → Dict[str, str][source]

Hook for subclasses.

The key and values in the returned dictionnary can be safely changed without side effects on self.kwargs (provided you don’t alter mutable values, like calling list.pop()).

class Glyphicon(name: str = '')[source]

Renders markup for bootstrap’s glyphicons.

template = <Template memory:7f89a5b67c18>
getset(f: Callable) → property[source]

Shortcut for a custom getter/ standard setter.

Usage:

@getset
def my_property(self, value=None):
    if value is None:
        return getter_value
    set_value(value)

Default value for value should be any marker that helps distinguish between getter or setter mode. If None is not appropriate a good approach is to use a unique object instance:

MARK = object()
# test like this
if value is MARK:
  # getter mode
ENABLED = Status('enabled')

default action status: show in UID, usable, not marked “current”

ACTIVE = Status('active')

action is “active” or “current”. For example the current navigation item.

DISABLED = Status('disabled')

action should be shown in a disabled state

Module abilian.web.nav

Navigation elements.

Abilian define theses categories: section: Used for navigation elements relevant to site section user: User for element that should appear in user menu

class BreadcrumbItem(label: Union[flask_babel.speaklater.LazyString, str] = '', url: Union[str, abilian.web.action.Endpoint] = '#', icon: Optional[str] = None, description: Optional[Any] = None)[source]

A breadcrumb element has at least a label or an icon.

render() → markupsafe.Markup[source]
description = None

Additional text, can be used as tooltip for example

icon = None

Icon to use.

label = None

Label shown to user. May be an i18n string instance

template_string = '{%- if url %}<a href="{{ url }}">{%- endif %}{%- if item.icon %}{{ item.icon }}\xa0{%- endif %}{{ item.label }}{%- if url %}</a>{%- endif %}'
property url
class NavGroup(category: str, name: str, items: Tuple[()] = (), *args, **kwargs)[source]

A navigation group renders a list of items.

append(item: abilian.web.nav.NavItem) → None[source]
get_render_args(**kwargs) → Dict[str, Any][source]
insert(pos: int, item: abilian.web.nav.NavItem) → None[source]
property status
template_string = '\n <ul class="nav navbar-nav {{ action.css_class }}">\n <li class="dropdown">\n <a class="dropdown-toggle" data-toggle="dropdown">\n {%- if action.icon %}{{ action.icon }}{% endif %}\n {{ action.title }} <b class="caret"></b>\n </a>\n <ul class="dropdown-menu">\n {%- for item in action_items %}\n {%- if item.divider %}<li class="divider"></li>{%- endif %}\n <li class="{{ item.status|safe }}">{{ item.render() }}</li>\n {%- endfor %}\n </ul>\n </li>\n </ul>\n '
class NavItem(category: str, name: str, divider: bool = False, *args, **kwargs)[source]

A single navigation item.

divider = False
property path
property status

Module abilian.web.views

class View[source]

Base class to use for all class based views.

The view instance is accessible in g and is set in actions context.

classmethod as_view(name: str, *class_args, **class_kwargs) → Callable[source]

Converts the class into an actual view function that can be used with the routing system. Internally this generates a function on the fly which will instantiate the View on each request and call the dispatch_request() method on it.

The arguments passed to as_view() are forwarded to the constructor of the class.

dispatch_request(*args, **kwargs) → str[source]

Subclasses have to override this method to implement the actual view function code. This method is called with all the arguments from the URL rule.

prepare_args(args, kwargs)[source]

If view arguments need to be prepared it can be done here.

A typical use case is to take an identifier, convert it to an object instance and maybe store it on view instance and/or replace identifier by object in arguments.

redirect(url)[source]

Shortcut all call stack and return response.

usage: self.response(url_for(…))

class JSONView[source]

Base view for JSON GET.

Renders as JSON when requested by Ajax, renders as HTML when requested from browser.

data(*args, **kwargs) → Dict[source]

This method should return data to be serialized using JSON.

get(*args, **kwargs) → str[source]
prepare_args(args: Tuple, kwargs: Dict[Any, Any]) → Tuple[Tuple, Dict[Any, Any]][source]

If view arguments need to be prepared it can be done here.

A typical use case is to take an identifier, convert it to an object instance and maybe store it on view instance and/or replace identifier by object in arguments.

methods = {'GET'}
class Registry(*args, **kwargs)[source]

Registry for default (canonical) views for entities.

There is one registry per application instance.

register(entity: Union[abilian.core.entities.Entity, Type[abilian.core.entities.Entity]], url_func: Callable) → None[source]

Associate a url_func with entity’s type.

Param:entity

an abilian.core.extensions.db.Model class or instance.

Param:url_func

any callable that accepts an entity instance and return an url for it.

url_for(entity: Union[flask_sqlalchemy.Model, whoosh.searching.Hit, Dict, None] = None, object_type: Optional[str] = None, object_id: Optional[int] = None, **kwargs) → str[source]

Return canonical view URL for given entity instance.

If no view has been registered the registry will try to find an endpoint named with entity’s class lowercased followed by ‘.view’ and that accepts object_id=entity.id to generates an url.

Parameters
  • entity – a instance of a subclass of abilian.core.extensions.db.Model, whoosh.searching.Hit or dict

  • object_id – if entity is not an instance, this parameter must be set to target id. This is usefull when you know the type and id of an object but don’t want to retrieve it from DB.

Raises

KeyError – if no view can be found for the given entity.

class default_view(app_or_blueprint: Union[Application, flask.blueprints.Blueprint], entity: abilian.core.entities.Entity, id_attr: str = 'object_id', endpoint: Optional[Any] = None, kw_func: Optional[Any] = None)[source]

Decorator to register a view as default view for given entity class.

Parameters
  • id_attr – url parameter name for object id.

  • endpoint – endpoint to use, defaults to view function’s name.

  • kw_func – function to process keywords to be passed to url_for. Useful for additional keywords. This function receives: kw, obj, obj_type, obj_id, **kwargs. It must return kw.

class BaseObjectView(Model=None, pk=None, base_template=None, *args, **kwargs)[source]

Base class common to all database objects views.

breadcrumb()[source]

Return nav.BreadcrumbItem instance for this object.

This method may return a list of BreadcrumbItem instances. Return None if nothing.

get(*args, **kwargs)[source]
init_object(args, kwargs)[source]

This method is reponsible for setting obj.

It is called during prepare_args().

prepare_args(args, kwargs)[source]

If view arguments need to be prepared it can be done here.

A typical use case is to take an identifier, convert it to an object instance and maybe store it on view instance and/or replace identifier by object in arguments.

Model = None

Model class

base_template = 'base.html'

default templates inherit from “base_template”. This allows to use generic templates with a custom base

methods = {'GET'}
obj = None

object instance for this view

object_id = None

object id

pk = 'object_id'

primary key name to look for in url arguments

template = None

template to render

property template_kwargs

Get template render arguments.

You may override base_template for instance. Only view cannot be overriden.

title = None

form title

class ObjectView(Model=None, pk=None, Form=None, template=None, *args, **kwargs)[source]

View objects.

get_form_kwargs()[source]
index_url()[source]
prepare_args(args, kwargs)[source]

form is initialized here. See also View.prepare_args().

redirect_to_index()[source]
Form = None

View form class. Form object used to show objects fields

form = None

form instance for this view

methods = {'GET'}
permission = Permission('read')

required permission. Must be an instance of abilian.services.security.Permission

template = 'default/object_view.html'

html template

property template_kwargs

Provides form to templates.

class ObjectEdit(Model=None, pk=None, Form=None, template=None, view_endpoint=None, message_success=None, *args, **kwargs)[source]

Edit object.

after_populate_obj()[source]

Called after self.obj values have been updated, and self.obj attached to an ORM session.

before_populate_obj()[source]

This method is called after form has been validated and before calling form.populate_obj().

Sometimes one may want to remove a field from the form because it’s non-sense to store it on edited object, and use it in a specific manner, for example:

image = form.image
del form.image
store_image(image)
cancel()[source]
commit_success()[source]

Called after object has been successfully saved to database.

edit(redirect_to=None)[source]
form_csrf_invalid()[source]

Called when a form doesn’t validate only because of csrf token expiration.

This works only if form is an instance of flask_wtf.form.SecureForm. Else default CSRF protection (before request) will take place.

It must return a valid Flask.Response instance. By default it returns to edit form screen with an informative message.

form_invalid()[source]

When a form doesn’t validate this method is called.

It may return a Flask.Response instance, to handle specific errors in custom screens.

Else the edit form screen is returned with error(s) highlighted.

This method is useful for detecting edition conflict using hidden fields and show a specific screen to help resolve the conflict.

form_valid(redirect_to=None)[source]

Save object.

Called when form is validated.

Parameters

redirect_to – real url (created with url_for) to redirect to, instead of the view by default.

get_form_buttons(*args, **kwargs)[source]
handle_action(action)[source]
handle_commit_exception(exc)[source]

Hook point to handle exception that may happen during commit.

It is the responsability of this method to perform a rollback if it is required for handling exc. If the method does not handle exc if should do nothing and return None.

Returns

  • a valid Response if exception is handled.

  • None if exception is not handled. Default handling happens.

message_success()[source]
post(*args, **kwargs)[source]
prepare_args(args, kwargs)[source]

form is initialized here. See also View.prepare_args().

put()[source]
redirect_to_view()[source]
send_activity()[source]
validate()[source]
view_url()[source]
action = None

action name from form data

property activity_target

Return target to use when creating activity.

activity_verb = 'update'

verb used to describe activity

button = None

button clicked, corresponding to action.

property buttons
data = None

submitted form data

decorators = (<function support_graceful_failure>,)
methods = {'GET', 'POST', 'PUT'}
permission = Permission('write')
template = 'default/object_edit.html'
view_endpoint = None
class ObjectCreate(chain_create_allowed=None, *args, **kwargs)[source]

Create a new object.

breadcrumb()[source]

Return nav.BreadcrumbItem instance for this object.

This method may return a list of BreadcrumbItem instances. Return None if nothing.

cancel()[source]
chain_create()
create()[source]
get_form_buttons(*args, **kwargs)[source]
get_form_kwargs()[source]
init_object(args, kwargs)[source]

This method is reponsible for setting obj.

It is called during prepare_args().

prepare_args(args, kwargs)[source]

form is initialized here. See also View.prepare_args().

activity_verb = 'post'
chain_create_allowed = False

set to True to show ‘Save and add new’ button

methods = {'GET', 'POST', 'PUT'}
permission = Permission('create')
class ObjectDelete(Model=None, pk=None, Form=None, template=None, view_endpoint=None, message_success=None, *args, **kwargs)[source]

Delete object.

Supports the DELETE verb.

delete()[source]
get_form_buttons(*args, **kwargs)[source]
init_object(args, kwargs)

This method is reponsible for setting obj.

It is called during prepare_args().

activity_verb = 'delete'
methods = ['POST']
permission = Permission('delete')
class JSONBaseSearch(*args, **kwargs)[source]
data(q, *args, **kwargs) → Dict[source]

This method should return data to be serialized using JSON.

get_item(obj)[source]

Return a result item.

Parameters

obj – Instance object

Returns

a dictionnary with at least id and text values

get_results(q, *args, **kwargs)[source]
prepare_args(args, kwargs)[source]

If view arguments need to be prepared it can be done here.

A typical use case is to take an identifier, convert it to an object instance and maybe store it on view instance and/or replace identifier by object in arguments.

Model = None
methods = {'GET'}
minimum_input_length = 2
class JSONModelSearch(*args, **kwargs)[source]

Base class for json sqlalchemy model search.

As used by select2 widgets for example.

filter(query, q, **kwargs)[source]
get_item(obj)[source]

Return a result item.

Parameters

obj – Instance object

Returns

a dictionnary with at least id and text values

get_label(obj)[source]
get_results(q, *args, **kwargs)[source]
options(query)[source]
order_by(query)[source]
methods = {'GET', 'OPTIONS'}
class JSONWhooshSearch(*args, **kwargs)[source]

Base class for JSON Whoosh search, as used by select2 widgets for example.

get_item(hit)[source]

Return a result item.

Parameters

hit – Hit object from Whoosh

Returns

a dictionnary with at least id and text values

get_results(q, *args, **kwargs)[source]
methods = {'GET'}

Module abilian.web.frontend

Front-end for a CRM app.

This should eventually allow implementing very custom CRM-style application.

class BaseEntityView(module: abilian.web.frontend.Module, *args, **kwargs)[source]
breadcrumb()[source]
check_access()[source]
init_object(args, kwargs)[source]
prepare_args(args, kwargs)[source]
redirect_to_index()[source]
property can_create
property can_delete
property can_edit
pk = 'entity_id'
property single_view
class CRUDApp(app: abilian.app.Application, modules: None = None, name: None = None)[source]
add_module(module: abilian.web.frontend.Module) → None[source]
create_blueprint(module: abilian.web.frontend.Module) → flask.blueprints.Blueprint[source]
get_module(module_id)[source]
class DefaultRelatedView(label, attr, column_names, options=None, show_empty=False)[source]

Default view used by Module for items directly related to entity.

render(entity)[source]

Return a dict with keys ‘label’, ‘attr_name’, ‘rendered’, ‘size’, ‘show_empty’, ‘default_collapsed’.

class EntityCreate(module: abilian.web.frontend.Module, *args, **kwargs)[source]
breadcrumb()

Return nav.BreadcrumbItem instance for this object.

This method may return a list of BreadcrumbItem instances. Return None if nothing.

check_access()[source]
prepare_args(args, kwargs)

form is initialized here. See also View.prepare_args().

methods = {'GET', 'POST', 'PUT'}
mode = 'create'
template = 'default/single_view.html'
property template_kwargs

Provides form to templates.

class EntityDelete(module: abilian.web.frontend.Module, *args, **kwargs)[source]
methods = {'DELETE', 'GET', 'POST', 'PUT'}
class EntityEdit(module: abilian.web.frontend.Module, *args, **kwargs)[source]
methods = {'GET', 'POST', 'PUT'}
mode = 'edit'
template = 'default/single_view.html'
property template_kwargs

Provides form to templates.

class EntityView(module: abilian.web.frontend.Module, *args, **kwargs)[source]
methods = {'GET'}
mode = 'view'
property object_actions
template = 'default/single_view.html'
property template_kwargs

Provides form to templates.

class ListJson(module: abilian.web.frontend.Module, *args, **kwargs)[source]

JSON endpoint, for AJAX-backed table views.

data(*args, **kwargs) → Dict[source]

This method should return data to be serialized using JSON.

methods = {'GET'}
class Module[source]
create_cls

alias of EntityCreate

delete_cls

alias of EntityDelete

edit_cls

alias of EntityEdit

json_search_cls

alias of abilian.web.views.object.JSONWhooshSearch

view_cls

alias of EntityView

create_blueprint(crud_app: abilian.web.frontend.CRUDApp) → flask.blueprints.Blueprint[source]

Create a Flask blueprint for this module.

get_component(name)[source]
get_grouped_actions() → collections.OrderedDict[source]
is_current()[source]
list_json2()[source]

Other JSON endpoint, this time used for filling select boxes dynamically.

You can write your own search method in list_json2_query_all, that returns a list of results (not json).

list_json2_query_all(q)[source]

Implements the search query for the list_json2 endpoint.

May be re-defined by a Module subclass in order to customize the search results.

  • Return: a list of results (not json) with an ‘id’ and a ‘text’ (that will be displayed in the select2).

list_query(request: flask.wrappers.Request) → abilian.core.entities.EntityQuery[source]

Return a filtered query based on request args, for listings.

Like query, but subclasses can modify it to remove costly joined loads for example.

list_view() → str[source]
ordered_query(request: flask.wrappers.Request, query: Optional[abilian.core.entities.EntityQuery] = None) → abilian.core.entities.EntityQuery[source]

Order query according to request args.

If query is None, the query is generated according to request args with self.query(request)

query(request: flask.wrappers.Request)[source]

Return filtered query based on request args.

register_actions() → None[source]
JSON2_SEARCH_LENGTH = 50
property action_category
property base_query

Return a query instance for managed_class.

base_template = 'base.html'
blueprint = None
components = ()
edit_form_class = None
endpoint = None
id = None
label = None
list_view_columns = []
property listing_query

Like read_query, but can be made lightweight with only columns and joins of interest.

read_query can be used with exports for example, with lot more columns (generallly it means more joins).

managed_class = None
name = None
property read_query

Return a query instance for managed_class filtering on READ permission.

related_views = []
search_criterions = (<TextSearchCriterion name=name>,)
single_view = None
static_folder = None
tableview_options = {}
url = None
view_form_class = None
view_new_save_and_add = False
view_options = None
view_template = None
class ModuleAction(module: abilian.web.frontend.Module, group: str, name: str, *args, **kwargs)[source]

Base action class for Module actions.

Basic condition is simple: category must match the string ‘module:{module.endpoint}’

pre_condition(context: Dict[str, Module]) → bool[source]

Called by available() before checking condition.

Subclasses may override it to ease creating actions with repetitive check (for example: actions that apply on a given content type only).

class ModuleActionDropDown(module: abilian.web.frontend.Module, group: str, name: str, *args, **kwargs)[source]
template_string = '\n <div class="btn-group">\n <button type="button" class="{{ action.css_class }} dropdown-toggle"\n data-toggle="dropdown" aria-expanded="false">\n {%- if action.icon %}{{ action.icon }} {% endif %}\n {{ action.title }}\n <span class="caret"></span>\n </button>\n <ul class="dropdown-menu" role="menu">\n {%- for entry in action_items %}\n {%- if entry.divider %}<li class="divider"></li>{%- endif %}\n <li>{{ entry.render() }}</a>\n </li>\n {%- endfor %}\n </ul>\n </div>\n '
class ModuleActionGroup(module: abilian.web.frontend.Module, group: str, name: str, *args, **kwargs)[source]
template_string = '<div class="btn-group" role="group" aria-label="{{ action.name}}">{%- for entry in action_items %}{{ entry.render() }}{%- endfor %}</div>'
class ModuleActionGroupItem(module: abilian.web.frontend.Module, group: str, name: str, *args, **kwargs)[source]
class ModuleComponent(name=None)[source]

A component that provide new functions for a Module

get_actions()[source]
init(*args, **kwargs)[source]

Implements this in components.

init_module(module)[source]
name = None
class ModuleMeta(classname: str, bases: Tuple, fields: Dict[str, Any])[source]

Module metaclass.

Does some precalculations (like getting list of view methods from the class) to avoid calculating them for each view class instance.

class ModuleView(module: abilian.web.frontend.Module, *args, **kwargs)[source]

Mixin for module base views.

Provide module.

module = None

Module instance

class RelatedView[source]

A base class for related views.

render(entity)[source]

Return a dict with keys ‘label’, ‘attr_name’, ‘rendered’, ‘size’, ‘show_empty’, ‘default_collapsed’.

add_to_recent_items(entity, type='ignored')[source]
expose(url: str = '/', methods: Tuple[str] = ('GET', )) → Callable[source]

Use this decorator to expose views in your view classes.

url Relative URL for the view methods Allowed HTTP methods. By default only GET is allowed.

labelize(s: str) → str[source]
make_single_view(form: wtforms.form.Form, **options) → abilian.web.forms.widgets.SingleView[source]

Module abilian.web.tags

class TagsExtension(app: flask.app.Flask)[source]

API for tags, installed as an application extension.

It is also available in templates as tags.

add(entity: abilian.core.entities.Entity, tag: abilian.core.models.tag.Tag = None, ns: Any = None, label: Any = None) → abilian.core.models.tag.Tag[source]
entity_default_ns(entity)[source]
entity_tags(entity)[source]
entity_tags_form(entity, ns=None)[source]

Construct a form class with a field for tags in namespace ns.

get(ns, label=None)[source]

Return tags instances for the namespace ns, ordered by label.

If label is not None the only one instance may be returned, or None if no tags exists for this label.

get_form_context(obj, ns=None)[source]

Return a dict: form instance, action button, submit url…

Used by macro m_tags_form(entity)

remove(entity, tag=None, ns=None, label=None)[source]
supports_tagging(entity)[source]
tags_from_hit(tag_ids)[source]
Parameters

tag_ids – indexed ids of tags in hit result. Do not pass hit instances.

Returns

an iterable of Tag instances.

class TagCriterion(*args, **kwargs)[source]

Filter entities with selected tag(s).

filter(query, module, request, searched_text, *args, **kwargs)[source]
get_request_values(request)[source]
form_default_value = ''
property form_filter_args
property form_filter_type
property form_unset_value
property model
property valid_tags

Module abilian.web.util

A few utility functions.

See https://docs.djangoproject.com/en/dev/topics/http/shortcuts/ for more ideas of stuff to implement.

get_object_or_404(cls, *args)[source]

Shorthand similar to Django’s get_object_or_404.

send_file_from_directory(filename, directory, app=None)[source]

Helper to add static rules, like in abilian.app.app.

Example use:

app.add_url_rule(
   app.static_url_path + '/abilian/<path:filename>',
   endpoint='abilian_static',
   view_func=partial(send_file_from_directory,
                     directory='/path/to/static/files/dir'))
url_for(obj: Any, **kw) → str[source]

Polymorphic variant of Flask’s url_for function.

Behaves like the original function when the first argument is a string. When it’s an object, it

Package abilian.testing