Mercurial > piecrust2
comparison piecrust/processing/base.py @ 201:0c9de41689bb
processing: Add ability to specify processors per mount.
The user can now specify which processors to use for each mount (i.e. each
assets directory). This is mostly for disabling anything but `copy` for
websites using Grunt or similar asset pipelines instead of the built-in one.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Wed, 14 Jan 2015 22:42:26 -0800 |
parents | 154b8df04829 |
children | 29165f2f315d |
comparison
equal
deleted
inserted
replaced
200:76e459d48c43 | 201:0c9de41689bb |
---|---|
100 def _doProcess(self, in_path, out_path): | 100 def _doProcess(self, in_path, out_path): |
101 raise NotImplementedError() | 101 raise NotImplementedError() |
102 | 102 |
103 | 103 |
104 class ProcessingContext(object): | 104 class ProcessingContext(object): |
105 def __init__(self, base_dir, job_queue, record=None): | 105 def __init__(self, base_dir, mount_info, job_queue, record=None): |
106 self.base_dir = base_dir | 106 self.base_dir = base_dir |
107 self.mount_info = mount_info | |
107 self.job_queue = job_queue | 108 self.job_queue = job_queue |
108 self.record = record | 109 self.record = record |
109 | 110 |
110 | 111 |
111 class ProcessorPipeline(object): | 112 class ProcessorPipeline(object): |
124 self.skip_patterns = skip_patterns or [] | 125 self.skip_patterns = skip_patterns or [] |
125 self.force_patterns = force_patterns or [] | 126 self.force_patterns = force_patterns or [] |
126 self.processors = app.plugin_loader.getProcessors() | 127 self.processors = app.plugin_loader.getProcessors() |
127 self.num_workers = num_workers | 128 self.num_workers = num_workers |
128 | 129 |
130 self.mounts = make_mount_info(self.mounts) | |
131 | |
129 self.skip_patterns += ['_cache', '_counter', | 132 self.skip_patterns += ['_cache', '_counter', |
130 'theme_info.yml', | 133 'theme_info.yml', |
131 '.DS_Store', 'Thumbs.db', | 134 '.DS_Store', 'Thumbs.db', |
132 '.git*', '.hg*', '.svn'] | 135 '.git*', '.hg*', '.svn'] |
133 | 136 |
136 | 139 |
137 def addSkipPatterns(self, patterns): | 140 def addSkipPatterns(self, patterns): |
138 self.skip_patterns += make_re(patterns) | 141 self.skip_patterns += make_re(patterns) |
139 | 142 |
140 def filterProcessors(self, authorized_names): | 143 def filterProcessors(self, authorized_names): |
141 self.processors = list(filter( | 144 if not authorized_names or authorized_names == '*': |
145 return self.processors | |
146 | |
147 if isinstance(authorized_names, str): | |
148 authorized_names = authorized_names.split(',') | |
149 return list(filter( | |
142 lambda p: p.PROCESSOR_NAME in authorized_names, | 150 lambda p: p.PROCESSOR_NAME in authorized_names, |
143 self.processors)) | 151 self.processors)) |
144 | 152 |
145 def run(self, src_dir_or_file=None, *, | 153 def run(self, src_dir_or_file=None, *, |
146 new_only=False, delete=True, | 154 new_only=False, delete=True, |
182 pool.append(worker) | 190 pool.append(worker) |
183 | 191 |
184 if src_dir_or_file is not None: | 192 if src_dir_or_file is not None: |
185 # Process only the given path. | 193 # Process only the given path. |
186 # Find out what mount point this is in. | 194 # Find out what mount point this is in. |
187 for path in self.mounts: | 195 for path, info in self.mounts.items(): |
188 if src_dir_or_file[:len(path)] == path: | 196 if src_dir_or_file[:len(path)] == path: |
189 base_dir = path | 197 base_dir = path |
198 mount_info = info | |
190 break | 199 break |
191 else: | 200 else: |
192 raise Exception("Input path '%s' is not part of any known " | 201 raise Exception("Input path '%s' is not part of any known " |
193 "mount point: %s" % | 202 "mount point: %s" % |
194 (src_dir_or_file, self.mounts)) | 203 (src_dir_or_file, self.mounts.keys())) |
195 | 204 |
196 ctx = ProcessingContext(base_dir, queue, record) | 205 ctx = ProcessingContext(base_dir, mount_info, queue, record) |
197 logger.debug("Initiating processing pipeline on: %s" % src_dir_or_file) | 206 logger.debug("Initiating processing pipeline on: %s" % src_dir_or_file) |
198 if os.path.isdir(src_dir_or_file): | 207 if os.path.isdir(src_dir_or_file): |
199 self.processDirectory(ctx, src_dir_or_file, new_only) | 208 self.processDirectory(ctx, src_dir_or_file, new_only) |
200 elif os.path.isfile(src_dir_or_file): | 209 elif os.path.isfile(src_dir_or_file): |
201 self.processFile(ctx, src_dir_or_file, new_only) | 210 self.processFile(ctx, src_dir_or_file, new_only) |
202 | 211 |
203 else: | 212 else: |
204 # Process everything. | 213 # Process everything. |
205 for path in self.mounts: | 214 for path, info in self.mounts.items(): |
206 ctx = ProcessingContext(path, queue, record) | 215 ctx = ProcessingContext(path, info, queue, record) |
207 logger.debug("Initiating processing pipeline on: %s" % path) | 216 logger.debug("Initiating processing pipeline on: %s" % path) |
208 self.processDirectory(ctx, path, new_only) | 217 self.processDirectory(ctx, path, new_only) |
209 | 218 |
210 # Wait on all workers. | 219 # Wait on all workers. |
211 for w in pool: | 220 for w in pool: |
249 self.processFile(ctx, os.path.join(dirpath, filename), | 258 self.processFile(ctx, os.path.join(dirpath, filename), |
250 new_only) | 259 new_only) |
251 | 260 |
252 def processFile(self, ctx, path, new_only=False): | 261 def processFile(self, ctx, path, new_only=False): |
253 logger.debug("Queuing: %s" % path) | 262 logger.debug("Queuing: %s" % path) |
254 job = ProcessingWorkerJob(ctx.base_dir, path, new_only) | 263 job = ProcessingWorkerJob(ctx.base_dir, ctx.mount_info, path, new_only) |
255 ctx.job_queue.put_nowait(job) | 264 ctx.job_queue.put_nowait(job) |
256 | 265 |
257 | 266 |
258 class ProcessingWorkerContext(object): | 267 class ProcessingWorkerContext(object): |
259 def __init__(self, pipeline, record, | 268 def __init__(self, pipeline, record, |
264 self.abort_event = abort_event | 273 self.abort_event = abort_event |
265 self.pipeline_lock = pipeline_lock | 274 self.pipeline_lock = pipeline_lock |
266 | 275 |
267 | 276 |
268 class ProcessingWorkerJob(object): | 277 class ProcessingWorkerJob(object): |
269 def __init__(self, base_dir, path, new_only=False): | 278 def __init__(self, base_dir, mount_info, path, new_only=False): |
270 self.base_dir = base_dir | 279 self.base_dir = base_dir |
280 self.mount_info = mount_info | |
271 self.path = path | 281 self.path = path |
272 self.new_only = new_only | 282 self.new_only = new_only |
273 | 283 |
274 | 284 |
275 class ProcessingWorker(threading.Thread): | 285 class ProcessingWorker(threading.Thread): |
316 record_entry.flags |= FLAG_OVERRIDEN | 326 record_entry.flags |= FLAG_OVERRIDEN |
317 logger.info(format_timed(start_time, | 327 logger.info(format_timed(start_time, |
318 '%s [not baked, overridden]' % rel_path)) | 328 '%s [not baked, overridden]' % rel_path)) |
319 return | 329 return |
320 | 330 |
331 processors = pipeline.filterProcessors(job.mount_info['processors']) | |
321 try: | 332 try: |
322 builder = ProcessingTreeBuilder(pipeline.processors) | 333 builder = ProcessingTreeBuilder(processors) |
323 tree_root = builder.build(rel_path) | 334 tree_root = builder.build(rel_path) |
324 except ProcessingTreeError as ex: | 335 except ProcessingTreeError as ex: |
325 record_entry.errors.append(str(ex)) | 336 record_entry.errors.append(str(ex)) |
326 logger.error("Error processing %s: %s" % (rel_path, ex)) | 337 logger.error("Error processing %s: %s" % (rel_path, ex)) |
327 return | 338 return |
349 record_entry.flags |= FLAG_PROCESSED | 360 record_entry.flags |= FLAG_PROCESSED |
350 logger.info(format_timed(start_time, "[%d] %s" % (self.wid, rel_path))) | 361 logger.info(format_timed(start_time, "[%d] %s" % (self.wid, rel_path))) |
351 except ProcessingTreeError as ex: | 362 except ProcessingTreeError as ex: |
352 record_entry.errors.append(str(ex)) | 363 record_entry.errors.append(str(ex)) |
353 logger.error("Error processing %s: %s" % (rel_path, ex)) | 364 logger.error("Error processing %s: %s" % (rel_path, ex)) |
365 | |
366 | |
367 def make_mount_info(mounts): | |
368 if isinstance(mounts, list): | |
369 mounts = {m: {} for m in mounts} | |
370 | |
371 for name, info in mounts.items(): | |
372 if not isinstance(info, dict): | |
373 raise Exception("Asset directory info for '%s' is not a " | |
374 "dictionary." % name) | |
375 info.setdefault('processors', '*') | |
376 | |
377 return mounts | |
354 | 378 |
355 | 379 |
356 def make_re(patterns): | 380 def make_re(patterns): |
357 re_patterns = [] | 381 re_patterns = [] |
358 for pat in patterns: | 382 for pat in patterns: |