コード例 #1
0
ファイル: container.py プロジェクト: runyaga/ptah
    def __delitem__(self, item, flush=True):
        """Delete a value from the container using the key."""

        if isinstance(item, string_types):
            item = self[item]

        if item.__parent_uri__ == self.__uri__:
            if isinstance(item, BaseContainer):
                for key in item.keys():
                    item.__delitem__(key, False)

            get_current_registry().notify(
                ptah.events.ContentDeletingEvent(item))

            name = item.__name__
            if self._v_keys:
                self._v_keys.remove(name)
            if self._v_items and name in self._v_items:
                del self._v_items[name]

            if item in Session:
                try:
                    Session.delete(item)
                    if flush:
                        Session.flush()
                except:
                    pass

            return

        raise KeyError(item)
コード例 #2
0
ファイル: container.py プロジェクト: runyaga/ptah
    def __delitem__(self, item, flush=True):
        """Delete a value from the container using the key."""

        if isinstance(item, string_types):
            item = self[item]

        if item.__parent_uri__ == self.__uri__:
            if isinstance(item, BaseContainer):
                for key in item.keys():
                    item.__delitem__(key, False)

            get_current_registry().notify(
                ptah.events.ContentDeletingEvent(item))

            name = item.__name__
            if self._v_keys:
                self._v_keys.remove(name)
            if self._v_items and name in self._v_items:
                del self._v_items[name]

            if item in Session:
                try:
                    Session.delete(item)
                    if flush:
                        Session.flush()
                except:
                    pass

            return

        raise KeyError(item)
コード例 #3
0
ファイル: container.py プロジェクト: runyaga/ptah
    def __setitem__(self, key, item):
        """Set a new item in the container."""

        if not isinstance(item, BaseContent):
            raise ValueError("Content object is required")

        if item.__uri__ == self.__uri__:
            raise ValueError("Can't set to it self")

        parents = [p.__uri__ for p in load_parents(self)]
        if item.__uri__ in parents:
            raise TypeError("Can't itself to chidlren")

        if key in self.keys():
            raise KeyError(key)

        if item.__parent_uri__ is None:
            event = ptah.events.ContentAddedEvent(item)
        else:
            event = ptah.events.ContentMovedEvent(item)

        item.__name__ = key
        item.__parent__ = self
        item.__parent_uri__ = self.__uri__
        item.__path__ = '%s%s/'%(self.__path__, key)

        if item not in Session:
            Session.add(item)

        # temporary keys
        if not self._v_items:
            self._v_items = {key: item}
        else:
            self._v_items[key] = item

        if key not in self._v_keys:
            self._v_keys.append(key)

        # recursevly update children paths
        def update_path(container):
            path = container.__path__
            for item in container.values():
                item.__path__ = '%s%s/'%(path, item.__name__)

                if isinstance(item, BaseContainer):
                    update_path(item)

        if isinstance(item, BaseContainer):
            update_path(item)

        get_current_registry().notify(event)
コード例 #4
0
ファイル: container.py プロジェクト: runyaga/ptah
    def __setitem__(self, key, item):
        """Set a new item in the container."""

        if not isinstance(item, BaseContent):
            raise ValueError("Content object is required")

        if item.__uri__ == self.__uri__:
            raise ValueError("Can't set to it self")

        parents = [p.__uri__ for p in load_parents(self)]
        if item.__uri__ in parents:
            raise TypeError("Can't itself to chidlren")

        if key in self.keys():
            raise KeyError(key)

        if item.__parent_uri__ is None:
            event = ptah.events.ContentAddedEvent(item)
        else:
            event = ptah.events.ContentMovedEvent(item)

        item.__name__ = key
        item.__parent__ = self
        item.__parent_uri__ = self.__uri__
        item.__path__ = '%s%s/' % (self.__path__, key)

        if item not in Session:
            Session.add(item)

        # temporary keys
        if not self._v_items:
            self._v_items = {key: item}
        else:
            self._v_items[key] = item

        if key not in self._v_keys:
            self._v_keys.append(key)

        # recursevly update children paths
        def update_path(container):
            path = container.__path__
            for item in container.values():
                item.__path__ = '%s%s/' % (path, item.__name__)

                if isinstance(item, BaseContainer):
                    update_path(item)

        if isinstance(item, BaseContainer):
            update_path(item)

        get_current_registry().notify(event)
コード例 #5
0
def register_type_impl(
    config, cls, tinfo, name, fieldset,
    permission = ptah.NOT_ALLOWED, fieldNames=None, **kw):

    # generate schema
    if fieldset is None:
        fieldset = ptah.generate_fieldset(
            cls, fieldNames=fieldNames, namesFilter=names_filter)
        log.info("Generating fieldset for %s content type.", cls)

    if 'global_allow' not in kw and not issubclass(cls, Content):
        kw['global_allow'] = False

    tinfo.__dict__.update(kw)

    if fieldset is not None:
        tinfo.fieldset = fieldset

    tinfo.cls = cls
    tinfo.permission = permission

    config.get_cfg_storage(TYPES_DIR_ID)[tinfo.__uri__] = tinfo

    # sql query for content resolver
    cls.__uri_sql_get__ = ptah.QueryFreezer(
        lambda: Session.query(cls) \
            .filter(cls.__uri__ == sqla.sql.bindparam('uri')))

    # build cms actions
    build_class_actions(cls)
コード例 #6
0
ファイル: blob.py プロジェクト: runyaga/ptah
class BlobStorage(object):
    """ simple blob storage """

    _sql_get = ptah.QueryFreezer(lambda: Session.query(Blob).filter(
        Blob.__uri__ == sqla.sql.bindparam('uri')))

    _sql_get_by_parent = ptah.QueryFreezer(lambda: Session.query(Blob).filter(
        Blob.__parent_uri__ == sqla.sql.bindparam('parent')))

    def create(self, parent=None):
        blob = Blob(__parent__=parent)
        Session.add(blob)
        Session.flush()

        return blob

    def add(self, data, parent=None, **metadata):
        blob = self.create(parent)

        data.seek(0)

        blob.data = data.read()
        blob.updateMetadata(**metadata)

        data.seek(0, os.SEEK_END)
        blob.size = data.tell()

        return blob

    def get(self, uri):
        """SQL Blob resolver"""
        return self._sql_get.first(uri=uri)

    def getByParent(self, parent):
        return self._sql_get_by_parent.first(parent=parent)

    def replace(self, uri, data, **metadata):  # pragma: no cover
        pass

    def remove(self, uri):  # pragma: no cover
        pass
コード例 #7
0
    def __call__(self, request=None):
        root = self._sql_get_root.first(name=self.name, type=self.type.__uri__)
        if root is None:
            root = self.type.create(title=self.title)
            root.__name_id__ = self.name
            root.__path__ = '/%s/' % root.__uri__
            Session.add(root)
            Session.flush()

        root.__root_path__ = self.path
        root.__parent__ = policy = self.policy(request)
        root.__default_root__ = self.default_root

        if request is not None:
            set_policy(policy)
            request.root = root

        if self.parent_factory:
            policy.__parent__ = self.parent_factory()

        return root
コード例 #8
0
ファイル: root.py プロジェクト: runyaga/ptah
    def __call__(self, request=None):
        root = self._sql_get_root.first(name=self.name, type=self.type.__uri__)
        if root is None:
            root = self.type.create(title=self.title)
            root.__name_id__ = self.name
            root.__path__ = '/%s/'%root.__uri__
            Session.add(root)
            Session.flush()

        root.__root_path__ = self.path
        root.__parent__ = policy = self.policy(request)
        root.__default_root__ = self.default_root

        if request is not None:
            set_policy(policy)
            request.root = root

        if self.parent_factory:
            policy.__parent__ = self.parent_factory()

        return root
コード例 #9
0
    def __init__(self,
                 cls,
                 path='',
                 name='',
                 title='',
                 policy=ApplicationPolicy,
                 default_root=None,
                 parent_factory=None,
                 config=None):
        self.id = '-'.join(part for part in path.split('/') if part)
        self.path = path if path.endswith('/') else '%s/' % path
        self.name = name
        self.title = title

        self.default_root = default_root
        if (self.path == '/') and default_root is None:
            self.default_root = True

        self.cls = cls
        self.type = cls.__type__
        self.policy = policy
        self.parent_factory = parent_factory

        discr = (APPFACTORY_ID, path)
        intr = ptah.config.Introspectable(APPFACTORY_ID, discr, name,
                                          APPFACTORY_ID)
        intr['id'] = self.id
        intr['factory'] = self

        if config is not None:
            ptah.config.get_cfg_storage(
                APPFACTORY_ID, registry=config.registry)[self.id] = self

        info = ptah.config.DirectiveInfo()
        info.attach(
            ptah.config.Action(
                lambda cfg: cfg.get_cfg_storage(APPFACTORY_ID)\
                    .update({self.id: self}),
                discriminator=discr, introspectables=(intr,))
            )

        self._sql_get_root = ptah.QueryFreezer(
            lambda: Session.query(cls)\
                .filter(sqla.sql.and_(
                    cls.__name_id__ == sqla.sql.bindparam('name'),
                    cls.__type_id__ == sqla.sql.bindparam('type'))))
コード例 #10
0
ファイル: root.py プロジェクト: runyaga/ptah
    def __init__(self, cls, path='', name='', title='',
                 policy = ApplicationPolicy, default_root = None,
                 parent_factory = None, config=None):
        self.id = '-'.join(part for part in path.split('/') if part)
        self.path = path if path.endswith('/') else '%s/'%path
        self.name = name
        self.title = title

        self.default_root = default_root
        if (self.path == '/') and default_root is None:
            self.default_root = True

        self.cls = cls
        self.type = cls.__type__
        self.policy = policy
        self.parent_factory = parent_factory

        discr = (APPFACTORY_ID, path)
        intr = ptah.config.Introspectable(
            APPFACTORY_ID, discr, name, APPFACTORY_ID)
        intr['id'] = self.id
        intr['factory'] = self

        if config is not None:
            ptah.config.get_cfg_storage(
                APPFACTORY_ID, registry=config.registry)[self.id] = self

        info = ptah.config.DirectiveInfo()
        info.attach(
            ptah.config.Action(
                lambda cfg: cfg.get_cfg_storage(APPFACTORY_ID)\
                    .update({self.id: self}),
                discriminator=discr, introspectables=(intr,))
            )

        self._sql_get_root = ptah.QueryFreezer(
            lambda: Session.query(cls)\
                .filter(sqla.sql.and_(
                    cls.__name_id__ == sqla.sql.bindparam('name'),
                    cls.__type_id__ == sqla.sql.bindparam('type'))))
コード例 #11
0
ファイル: blob.py プロジェクト: runyaga/ptah
    def create(self, parent=None):
        blob = Blob(__parent__=parent)
        Session.add(blob)
        Session.flush()

        return blob
コード例 #12
0
ファイル: blob.py プロジェクト: runyaga/ptah
    def create(self, parent=None):
        blob = Blob(__parent__=parent)
        Session.add(blob)
        Session.flush()

        return blob
コード例 #13
0
ファイル: traverser.py プロジェクト: runyaga/ptah
    def __call__(self, request, queries=_path_queries):
        environ = request.environ
        context = root = self.root

        if root.__default_root__ and 'bfg.routes.route' in environ:
            return ResourceTreeTraverser(root)(request)

        path = '/%s/'%'/'.join(traversal_path(environ.get('PATH_INFO','/')))

        vroot_tuple = ()

        l_path = len(root.__root_path__)

        # build paths for all parents in content hierarchy
        idx = 0
        paths = {}
        current = root.__path__
        for sec in path[l_path:].split('/'):
            if sec:
                current = '%s%s/'%(current, sec)
                paths[str(idx)] = current
                idx += 1

        if idx:
            if idx not in queries:
                bindparams = [sql.bindparam(str(p)) for p in range(idx)]

                queries[idx] = ptah.QueryFreezer(
                    lambda: Session.query(BaseContent)\
                        .filter(BaseContent.__path__.in_(bindparams))
                        .order_by(sql.desc(BaseContent.__path__)))

            parents = queries[idx].all(**paths)
        else:
            parents = []

        if parents:
            # set __parent__
            parents[-1].__parent__ = root
            for idx in range(len(parents)-1):
                parents[idx].__parent__ = parents[idx+1]

            context = parents[0]
            node = context.__path__[len(root.__path__):]
            leaf = path[l_path+len(node):].split('/')
            leaf, subpath = leaf[0], leaf[1:]

            return {'context': context,
                    'view_name': leaf,
                    'subpath': subpath,
                    'traversed': traversal_path(node),
                    'virtual_root': root,
                    'virtual_root_path': vroot_tuple,
                    'root': root}
        else:
            vpath_tuple = ()

            leaf = path[l_path:].split('/')
            leaf, subpath = leaf[0], leaf[1:]

            return {'context': context,
                    'view_name': leaf,
                    'subpath': subpath,
                    'traversed': vpath_tuple,
                    'virtual_root': root,
                    'virtual_root_path': (),
                    'root': root}
コード例 #14
0
class BaseContent(Node):
    """ Base class for content objects. A content class should inherit from
    `Content` to participate in content hierarchy traversal.

    .. attribute:: __path__

       A string used by the :py:class:`ptah.cms.ContentTraverser` which is
       used for efficient resolution of URL structure to content models.
       This is internal implementation and manually editing it can break
       your hierarchy.

    .. attribute:: __name__

       This is the identifier in a container if you are using containment and
       hierarchies.

    .. attribute:: title

       Content title which is editable by end user.

    .. attribute:: description

       Content description which is editable by end user.

    .. attribute:: view

       A URI which can be resolved with :py:func:`ptah.resolve` function
       which represents the 'default' view for content. Akin to index.html
       or default.php in Apache.

    .. attribute:: created

       Content creation time which is set by
       :py:func:`ptah.cms.content.createdHandler` during object creation.

       :type: :py:class:`datetime.datetime`

    .. attribute:: modified

       Content modification time which is set by
       :py:func:`ptah.cms.content.modifiedHandler` during object modification.

       :type: :py:class:`datetime.datetime`

    .. attribute:: effective

       :type: :py:class:`datetime.datetime` or None

    .. attribute:: expires

       :type: :py:class:`datetime.datetime` or None

    .. attribute:: creators

       a :py:class:`ptah.JsonListType` which contains sequence of users.  Using
       principal URIs is a good idea.

    .. attribute:: subjects

       a :py:class:`ptah.JsonListType` which contains sequence of subjects.
       Holding a sequence of URIs could resolve to subject objects. Or you can
       use strings.

    .. attribute: publisher

       a Unicode string which should identify the publisher.

    .. attribute: contributors

       a :py:class:`ptah.JsonListType` which contains sequence of contributors.
       You could keep a sequence of principal URIs.
    """

    __tablename__ = 'ptah_content'

    __id__ = sqla.Column('id',
                         sqla.Integer,
                         sqla.ForeignKey('ptah_nodes.id'),
                         primary_key=True)
    __path__ = sqla.Column('path', sqla.Unicode, default=text_type(''))
    __name_id__ = sqla.Column('name', sqla.Unicode(255))

    title = sqla.Column(sqla.Unicode, default=text_type(''))
    description = sqla.Column(sqla.Unicode,
                              default=text_type(''),
                              info={
                                  'missing': '',
                                  'field_type': 'textarea'
                              })
    view = sqla.Column(sqla.Unicode, default=text_type(''))

    created = sqla.Column(sqla.DateTime)
    modified = sqla.Column(sqla.DateTime)
    effective = sqla.Column(sqla.DateTime)
    expires = sqla.Column(sqla.DateTime)

    creators = sqla.Column(ptah.JsonListType(), default=[])
    subjects = sqla.Column(ptah.JsonListType(), default=[])
    publisher = sqla.Column(sqla.Unicode, default=text_type(''))
    contributors = sqla.Column(ptah.JsonListType(), default=[])

    # sql queries
    _sql_get = ptah.QueryFreezer(lambda: Session.query(BaseContent).filter(
        BaseContent.__uri__ == sqla.sql.bindparam('uri')))

    _sql_get_in_parent = ptah.QueryFreezer(
        lambda: Session.query(BaseContent).filter(
            BaseContent.__name_id__ == sqla.sql.bindparam('key')).filter(
                BaseContent.__parent_uri__ == sqla.sql.bindparam('parent')))

    _sql_parent = ptah.QueryFreezer(lambda: Session.query(BaseContent).filter(
        BaseContent.__uri__ == sqla.sql.bindparam('parent')))

    def __init__(self, **kw):
        super(BaseContent, self).__init__(**kw)

        if self.__name__ and self.__parent__ is not None:
            self.__path__ = '%s%s/' % (self.__parent__.__path__, self.__name__)

    @hybrid_property
    def __name__(self):
        return self.__name_id__

    @__name__.setter
    def __name__(self, value):
        self.__name_id__ = value

    def __resource_url__(self, request, info):
        return '%s%s' % (request.root.__root_path__,
                         self.__path__[len(request.root.__path__):])

    @action(permission=DeleteContent)
    def delete(self):
        parent = self.__parent__
        if parent is None:
            parent = self.__parent_ref__

        if parent is None:
            raise Error("Can't find parent")

        del parent[self]

    @action(permission=ModifyContent)
    def update(self, **data):
        if self.__type__:
            tinfo = self.__type__

            for field in tinfo.fieldset.fields():
                val = data.get(field.name, field.default)
                if val is not ptah.form.null:
                    setattr(self, field.name, val)

            get_current_registry().notify(
                ptah.events.ContentModifiedEvent(self))

    def _extra_info(self, info):
        if self.__type__:
            fieldset = self.__type__.fieldset.bind()
            for field in fieldset.fields():
                val = getattr(self, field.name, field.default)
                info[field.name] = field.serialize(val)

        info['view'] = self.view
        info['created'] = self.created
        info['modified'] = self.modified
        info['effective'] = self.effective
        info['expires'] = self.expires

    def info(self):
        info = super(BaseContent, self).info()
        info['__name__'] = self.__name__
        info['__content__'] = True
        info['__container__'] = False
        self._extra_info(info)
        return info
コード例 #15
0
ファイル: container.py プロジェクト: runyaga/ptah
class BaseContainer(BaseContent):
    """ Content container implementation. """

    _v_keys = None
    _v_keys_loaded = False
    _v_items = None

    _sql_keys = ptah.QueryFreezer(
        lambda: Session.query(BaseContent.__name_id__).filter(
            BaseContent.__parent_uri__ == sqla.sql.bindparam('uri')))

    _sql_values = ptah.QueryFreezer(lambda: Session.query(BaseContent).filter(
        BaseContent.__parent_uri__ == sqla.sql.bindparam('uri')))

    def keys(self):
        """Return an list of the keys in the container."""
        if self._v_keys_loaded:
            return self._v_keys
        else:
            if self._v_keys is None:
                self._v_keys = [
                    n for n, in self._sql_keys.all(uri=self.__uri__)
                ]

            self._v_keys_loaded = True
            return self._v_keys

    def get(self, key, default=None):
        """Get a value for a key

        The default is returned if there is no value for the key.
        """
        if self._v_items and key in self._v_items:
            return self._v_items[key]

        item = self._sql_get_in_parent.first(key=key, parent=self.__uri__)
        if item is not None:
            item.__parent__ = self
            if not self._v_items:
                self._v_items = {key: item}
            else:
                self._v_items[key] = item
        return item

    def values(self):
        """Return an list of the values in the container."""

        if self._v_keys_loaded and self._v_items:
            if len(self._v_items) == len(self._v_keys):
                return self._v_items.values()

        values = []
        old_items = self._v_items

        self._v_keys = keys = []
        self._v_items = items = {}

        for item in self._sql_values.all(uri=self.__uri__):
            item.__parent__ = self
            items[item.__name__] = item
            keys.append(item.__name__)
            values.append(item)

        if old_items:
            for name, item in old_items.items():
                if name not in items:
                    keys.append(name)
                    items[name] = item
                    values.append(item)

        return values

    def items(self):
        """Return the items of the container."""
        for item in self.values():
            yield item.__name__, item

    def __contains__(self, key):
        """Tell if a key exists in the mapping."""
        return key in self.keys()

    def __getitem__(self, key):
        """Get a value for a key

        A KeyError is raised if there is no value for the key.
        """
        if self._v_items and key in self._v_items:
            return self._v_items[key]

        try:
            item = self._sql_get_in_parent.one(key=key, parent=self.__uri__)
            item.__parent__ = self
            if not self._v_items:
                self._v_items = {key: item}
            else:
                self._v_items[key] = item

            return item
        except sqla.orm.exc.NoResultFound:
            raise KeyError(key)

    def __setitem__(self, key, item):
        """Set a new item in the container."""

        if not isinstance(item, BaseContent):
            raise ValueError("Content object is required")

        if item.__uri__ == self.__uri__:
            raise ValueError("Can't set to it self")

        parents = [p.__uri__ for p in load_parents(self)]
        if item.__uri__ in parents:
            raise TypeError("Can't itself to chidlren")

        if key in self.keys():
            raise KeyError(key)

        if item.__parent_uri__ is None:
            event = ptah.events.ContentAddedEvent(item)
        else:
            event = ptah.events.ContentMovedEvent(item)

        item.__name__ = key
        item.__parent__ = self
        item.__parent_uri__ = self.__uri__
        item.__path__ = '%s%s/' % (self.__path__, key)

        if item not in Session:
            Session.add(item)

        # temporary keys
        if not self._v_items:
            self._v_items = {key: item}
        else:
            self._v_items[key] = item

        if key not in self._v_keys:
            self._v_keys.append(key)

        # recursevly update children paths
        def update_path(container):
            path = container.__path__
            for item in container.values():
                item.__path__ = '%s%s/' % (path, item.__name__)

                if isinstance(item, BaseContainer):
                    update_path(item)

        if isinstance(item, BaseContainer):
            update_path(item)

        get_current_registry().notify(event)

    def __delitem__(self, item, flush=True):
        """Delete a value from the container using the key."""

        if isinstance(item, string_types):
            item = self[item]

        if item.__parent_uri__ == self.__uri__:
            if isinstance(item, BaseContainer):
                for key in item.keys():
                    item.__delitem__(key, False)

            get_current_registry().notify(
                ptah.events.ContentDeletingEvent(item))

            name = item.__name__
            if self._v_keys:
                self._v_keys.remove(name)
            if self._v_items and name in self._v_items:
                del self._v_items[name]

            if item in Session:
                try:
                    Session.delete(item)
                    if flush:
                        Session.flush()
                except:
                    pass

            return

        raise KeyError(item)

    @action
    def create(self, tname, name, **params):
        tinfo = ptah.resolve(tname)
        if tinfo is None:
            raise NotFound('Type information is not found')

        tinfo.check_context(self)

        if '/' in name:
            raise Error("Names cannot contain '/'")

        if name.startswith(' '):
            raise Error("Names cannot starts with ' '")

        if name in self:
            raise Error("Name already in use.")

        self[name] = tinfo.create()
        content = self[name]
        content.update(**params)

        return content

    @action(permission=DeleteContent)
    def batchdelete(self, uris):
        """Batch delete"""
        raise NotImplements()  # pragma: no cover

    def info(self):
        info = super(BaseContainer, self).info()
        info['__container__'] = True
        return info
コード例 #16
0
    def __call__(self, request, queries=_path_queries):
        environ = request.environ
        context = root = self.root

        if root.__default_root__ and 'bfg.routes.route' in environ:
            return ResourceTreeTraverser(root)(request)

        path = '/%s/' % '/'.join(traversal_path(environ.get('PATH_INFO', '/')))

        vroot_tuple = ()

        l_path = len(root.__root_path__)

        # build paths for all parents in content hierarchy
        idx = 0
        paths = {}
        current = root.__path__
        for sec in path[l_path:].split('/'):
            if sec:
                current = '%s%s/' % (current, sec)
                paths[str(idx)] = current
                idx += 1

        if idx:
            if idx not in queries:
                bindparams = [sql.bindparam(str(p)) for p in range(idx)]

                queries[idx] = ptah.QueryFreezer(
                    lambda: Session.query(BaseContent)\
                        .filter(BaseContent.__path__.in_(bindparams))
                        .order_by(sql.desc(BaseContent.__path__)))

            parents = queries[idx].all(**paths)
        else:
            parents = []

        if parents:
            # set __parent__
            parents[-1].__parent__ = root
            for idx in range(len(parents) - 1):
                parents[idx].__parent__ = parents[idx + 1]

            context = parents[0]
            node = context.__path__[len(root.__path__):]
            leaf = path[l_path + len(node):].split('/')
            leaf, subpath = leaf[0], leaf[1:]

            return {
                'context': context,
                'view_name': leaf,
                'subpath': subpath,
                'traversed': traversal_path(node),
                'virtual_root': root,
                'virtual_root_path': vroot_tuple,
                'root': root
            }
        else:
            vpath_tuple = ()

            leaf = path[l_path:].split('/')
            leaf, subpath = leaf[0], leaf[1:]

            return {
                'context': context,
                'view_name': leaf,
                'subpath': subpath,
                'traversed': vpath_tuple,
                'virtual_root': root,
                'virtual_root_path': (),
                'root': root
            }