comparison piecrust/sources/fs.py @ 852:4850f8c21b6e

core: Start of the big refactor for PieCrust 3.0. * Everything is a `ContentSource`, including assets directories. * Most content sources are subclasses of the base file-system source. * A source is processed by a "pipeline", and there are 2 built-in pipelines, one for assets and one for pages. The asset pipeline is vaguely functional, but the page pipeline is completely broken right now. * Rewrite the baking process as just running appropriate pipelines on each content item. This should allow for better parallelization.
author Ludovic Chabant <ludovic@chabant.com>
date Wed, 17 May 2017 00:11:48 -0700
parents
children f070a4fc033c
comparison
equal deleted inserted replaced
851:2c7e57d80bba 852:4850f8c21b6e
1 import os.path
2 import logging
3 from piecrust import osutil
4 from piecrust.routing import RouteParameter
5 from piecrust.sources.base import ContentItem, ContentGroup, ContentSource
6
7
8 logger = logging.getLogger(__name__)
9
10
11 class InvalidFileSystemEndpointError(Exception):
12 def __init__(self, source_name, fs_endpoint):
13 super(InvalidFileSystemEndpointError, self).__init__(
14 "Invalid file-system endpoint for source '%s': %s" %
15 (source_name, fs_endpoint))
16
17
18 def _filter_crap_files(f):
19 return (f[-1] != '~' and # Vim temp files and what-not
20 f not in ['.DS_Store', 'Thumbs.db']) # OSX and Windows bullshit
21
22
23 class FSContentSourceBase(ContentSource):
24 """ Implements some basic stuff for a `ContentSource` that stores its
25 items as files on disk.
26 """
27 def __init__(self, app, name, config):
28 super().__init__(app, name, config)
29 self.fs_endpoint = config.get('fs_endpoint', name)
30 self.fs_endpoint_path = os.path.join(self.root_dir, self.fs_endpoint)
31 self._fs_filter = None
32
33 def _checkFSEndpoint(self):
34 if not os.path.isdir(self.fs_endpoint_path):
35 if self.config.get('ignore_missing_dir'):
36 return False
37 raise InvalidFileSystemEndpointError(self.name,
38 self.fs_endpoint_path)
39 return True
40
41 def openItem(self, item, mode='r'):
42 for m in 'wxa+':
43 if m in mode:
44 # If opening the file for writing, let's make sure the
45 # directory exists.
46 dirname = os.path.dirname(item.spec)
47 if not os.path.exists(dirname):
48 os.makedirs(dirname, 0o755)
49 break
50 return open(item.spec, mode)
51
52 def getItemMtime(self, item):
53 return os.path.getmtime(item.spec)
54
55
56 class FSContentSource(FSContentSourceBase):
57 """ Implements a `ContentSource` that simply returns files on disk
58 under a given root directory.
59 """
60 SOURCE_NAME = 'fs'
61
62 def getContents(self, group):
63 logger.debug("Scanning for content in: %s" % self.fs_endpoint_path)
64 if not self._checkFSEndpoint():
65 return None
66
67 parent_path = self.fs_endpoint_path
68 if group is not None:
69 parent_path = group.spec
70
71 names = filter(_filter_crap_files, osutil.listdir(parent_path))
72 if self._fs_filter is not None:
73 names = filter(self._fs_filter, names)
74
75 items = []
76 groups = []
77 for name in names:
78 path = os.path.join(parent_path, name)
79 if os.path.isdir(path):
80 metadata = self._createGroupMetadata(path)
81 groups.append(ContentGroup(path, metadata))
82 else:
83 metadata = self._createItemMetadata(path)
84 items.append(ContentItem(path, metadata))
85 self._finalizeContent(group, items, groups)
86 return items + groups
87
88 def _createGroupMetadata(self, path):
89 return {}
90
91 def _createItemMetadata(self, path):
92 return {}
93
94 def _finalizeContent(self, parent_group, items, groups):
95 pass
96
97 def getRelatedContents(self, item, relationship):
98 return None
99
100 def findContent(self, route_params):
101 rel_path = route_params['path']
102 path = os.path.join(self.fs_endpoint_path, rel_path)
103 metadata = self._createItemMetadata(path)
104 return ContentItem(path, metadata)
105
106 def getSupportedRouteParameters(self):
107 return [
108 RouteParameter('path', RouteParameter.TYPE_PATH)]
109
110 def describe(self):
111 return {'endpoint_path': self.fs_endpoint_path}