view piecrust/data/linker.py @ 182:a54d3c0b5f4a

tests: Patch `os.path.exists` and improve patching for `open`. You can specify additional modules for which to patch `open`. Also, it was incorrectly updating the opened file, even when it was opened for read only. Now it only updates the contents if the file was opened for write, and supports appending to the end. Last, it supports opening text files in binary mode.
author Ludovic Chabant <ludovic@chabant.com>
date Sun, 04 Jan 2015 14:55:41 -0800
parents 4fc1d306046b
children 701591ebfcba
line wrap: on
line source

import logging
import collections
from piecrust.data.base import PaginationData
from piecrust.sources.base import IListableSource, build_pages


logger = logging.getLogger(__name__)


class LinkedPageData(PaginationData):
    debug_render = ['name', 'is_dir', 'is_self']

    def __init__(self, name, page, is_self=False):
        super(LinkedPageData, self).__init__(page)
        self.name = name
        self.is_self = is_self

    @property
    def is_dir(self):
        return False


class Linker(object):
    debug_render_doc = """Provides access to sibling and children pages."""

    def __init__(self, source, *, name=None, dir_path=None, page_path=None):
        self.source = source
        self._name = name
        self._dir_path = dir_path
        self._root_page_path = page_path
        self._cache = None
        self._is_listable = None

    def __iter__(self):
        self._load()
        return iter(self._cache.values())

    def __getattr__(self, name):
        self._load()
        try:
            return self._cache[name]
        except KeyError:
            raise AttributeError()

    @property
    def name(self):
        if not self._name:
            self._load()
        return self._name

    @property
    def is_dir(self):
        return True

    @property
    def is_self(self):
        return False

    def _load(self):
        if self._cache is not None:
            return

        self._is_listable = isinstance(self.source, IListableSource)
        if self._is_listable and self._root_page_path is not None:
            if self._name is None:
                self._name = self.source.getBasename(self._root_page_path)
            if self._dir_path is None:
                self._dir_path = self.source.getDirpath(self._root_page_path)

        self._cache = collections.OrderedDict()
        if not self._is_listable or self._dir_path is None:
            return

        items = self.source.listPath(self._dir_path)
        with self.source.app.env.page_repository.startBatchGet():
            for is_dir, name, data in items:
                if is_dir:
                    self._cache[name] = Linker(self.source,
                                               name=name, dir_path=data)
                else:
                    page = data.buildPage()
                    is_root_page = (self._root_page_path == data.rel_path)
                    self._cache[name] = LinkedPageData(name, page,
                                                       is_root_page)


class RecursiveLinker(Linker):
    def __init__(self, source, page_path):
        super(RecursiveLinker, self).__init__(source, page_path=page_path)

    def __iter__(self):
        self._load()
        if not self._is_listable:
            return
        yield from walk_linkers(self)


def walk_linkers(linker):
    linker._load()
    for item in linker._cache.values():
        if item.is_dir:
            yield from walk_linkers(item)
        else:
            yield item