annotate 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
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
172
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
1 import logging
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
2 import collections
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
3 from piecrust.data.base import PaginationData
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
4 from piecrust.sources.base import IListableSource, build_pages
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
5
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
6
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
7 logger = logging.getLogger(__name__)
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
8
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
9
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
10 class LinkedPageData(PaginationData):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
11 debug_render = ['name', 'is_dir', 'is_self']
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
12
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
13 def __init__(self, name, page, is_self=False):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
14 super(LinkedPageData, self).__init__(page)
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
15 self.name = name
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
16 self.is_self = is_self
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
17
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
18 @property
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
19 def is_dir(self):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
20 return False
3
f485ba500df3 Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
21
f485ba500df3 Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
22
f485ba500df3 Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
23 class Linker(object):
172
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
24 debug_render_doc = """Provides access to sibling and children pages."""
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
25
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
26 def __init__(self, source, *, name=None, dir_path=None, page_path=None):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
27 self.source = source
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
28 self._name = name
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
29 self._dir_path = dir_path
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
30 self._root_page_path = page_path
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
31 self._cache = None
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
32 self._is_listable = None
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
33
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
34 def __iter__(self):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
35 self._load()
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
36 return iter(self._cache.values())
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
37
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
38 def __getattr__(self, name):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
39 self._load()
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
40 try:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
41 return self._cache[name]
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
42 except KeyError:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
43 raise AttributeError()
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
44
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
45 @property
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
46 def name(self):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
47 if not self._name:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
48 self._load()
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
49 return self._name
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
50
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
51 @property
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
52 def is_dir(self):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
53 return True
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
54
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
55 @property
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
56 def is_self(self):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
57 return False
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
58
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
59 def _load(self):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
60 if self._cache is not None:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
61 return
3
f485ba500df3 Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
diff changeset
62
172
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
63 self._is_listable = isinstance(self.source, IListableSource)
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
64 if self._is_listable and self._root_page_path is not None:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
65 if self._name is None:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
66 self._name = self.source.getBasename(self._root_page_path)
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
67 if self._dir_path is None:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
68 self._dir_path = self.source.getDirpath(self._root_page_path)
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
69
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
70 self._cache = collections.OrderedDict()
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
71 if not self._is_listable or self._dir_path is None:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
72 return
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
73
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
74 items = self.source.listPath(self._dir_path)
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
75 with self.source.app.env.page_repository.startBatchGet():
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
76 for is_dir, name, data in items:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
77 if is_dir:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
78 self._cache[name] = Linker(self.source,
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
79 name=name, dir_path=data)
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
80 else:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
81 page = data.buildPage()
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
82 is_root_page = (self._root_page_path == data.rel_path)
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
83 self._cache[name] = LinkedPageData(name, page,
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
84 is_root_page)
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
85
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
86
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
87 class RecursiveLinker(Linker):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
88 def __init__(self, source, page_path):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
89 super(RecursiveLinker, self).__init__(source, page_path=page_path)
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
90
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
91 def __iter__(self):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
92 self._load()
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
93 if not self._is_listable:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
94 return
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
95 yield from walk_linkers(self)
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
96
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
97
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
98 def walk_linkers(linker):
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
99 linker._load()
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
100 for item in linker._cache.values():
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
101 if item.is_dir:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
102 yield from walk_linkers(item)
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
103 else:
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
104 yield item
4fc1d306046b linker: Actually implement the `Linker` class, and use it in the page data.
Ludovic Chabant <ludovic@chabant.com>
parents: 3
diff changeset
105