comparison piecrust/processing/less.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 35d4c172e5a6
children 63be34ce6e65
comparison
equal deleted inserted replaced
851:2c7e57d80bba 852:4850f8c21b6e
5 import hashlib 5 import hashlib
6 import logging 6 import logging
7 import platform 7 import platform
8 import subprocess 8 import subprocess
9 from piecrust.processing.base import ( 9 from piecrust.processing.base import (
10 SimpleFileProcessor, ExternalProcessException) 10 SimpleFileProcessor, ExternalProcessException, FORCE_BUILD)
11 from piecrust.processing.tree import FORCE_BUILD
12 11
13 12
14 logger = logging.getLogger(__name__) 13 logger = logging.getLogger(__name__)
15 14
16 15
20 def __init__(self): 19 def __init__(self):
21 super(LessProcessor, self).__init__({'less': 'css'}) 20 super(LessProcessor, self).__init__({'less': 'css'})
22 self._conf = None 21 self._conf = None
23 self._map_dir = None 22 self._map_dir = None
24 23
25 def onPipelineStart(self, pipeline): 24 def onPipelineStart(self, ctx):
26 self._map_dir = os.path.join(pipeline.tmp_dir, 'less') 25 self._map_dir = os.path.join(ctx.tmp_dir, 'less')
27 if (pipeline.is_first_worker and 26 if (ctx.is_main_process and
28 not os.path.isdir(self._map_dir)): 27 not os.path.isdir(self._map_dir)):
29 os.makedirs(self._map_dir) 28 os.makedirs(self._map_dir)
30 29
31 def getDependencies(self, path): 30 def getDependencies(self, path):
32 map_path = self._getMapPath(path) 31 map_path = self._getMapPath(path)
57 def _doProcess(self, in_path, out_path): 56 def _doProcess(self, in_path, out_path):
58 self._ensureInitialized() 57 self._ensureInitialized()
59 58
60 map_path = self._getMapPath(in_path) 59 map_path = self._getMapPath(in_path)
61 map_url = '/' + os.path.relpath( 60 map_url = '/' + os.path.relpath(
62 map_path, self.app.root_dir).replace('\\', '/') 61 map_path, self.app.root_dir).replace('\\', '/')
63 62
64 # On Windows, it looks like LESSC is confused with paths when the 63 # On Windows, it looks like LESSC is confused with paths when the
65 # map file is not to be created in the same directory as the input 64 # map file is not to be created in the same directory as the input
66 # file (it ends up writing invalid dependencies in the map file, with 65 # file (it ends up writing invalid dependencies in the map file, with
67 # a mix of relative and absolute paths stuck together). 66 # a mix of relative and absolute paths stuck together).
68 # So create it there and move it afterwards... :( 67 # So create it there and move it afterwards... :(
69 temp_map_path = os.path.join( 68 temp_map_path = os.path.join(
70 os.path.dirname(in_path), 69 os.path.dirname(in_path),
71 os.path.basename(map_path)) 70 os.path.basename(map_path))
72 71
73 args = [self._conf['bin'], 72 args = [self._conf['bin'],
74 '--source-map=%s' % temp_map_path, 73 '--source-map=%s' % temp_map_path,
75 '--source-map-url=%s' % map_url] 74 '--source-map-url=%s' % map_url]
76 args += self._conf['options'] 75 args += self._conf['options']
81 # On Windows, we need to run the process in a shell environment 80 # On Windows, we need to run the process in a shell environment
82 # otherwise it looks like `PATH` isn't taken into account. 81 # otherwise it looks like `PATH` isn't taken into account.
83 shell = (platform.system() == 'Windows') 82 shell = (platform.system() == 'Windows')
84 try: 83 try:
85 proc = subprocess.Popen( 84 proc = subprocess.Popen(
86 args, shell=shell, 85 args, shell=shell,
87 stderr=subprocess.PIPE) 86 stderr=subprocess.PIPE)
88 stdout_data, stderr_data = proc.communicate() 87 stdout_data, stderr_data = proc.communicate()
89 except FileNotFoundError as ex: 88 except FileNotFoundError as ex:
90 logger.error("Tried running LESS processor with command: %s" % 89 logger.error("Tried running LESS processor with command: %s" %
91 args) 90 args)
92 raise Exception("Error running LESS processor. " 91 raise Exception("Error running LESS processor. "
93 "Did you install it?") from ex 92 "Did you install it?") from ex
94 if proc.returncode != 0: 93 if proc.returncode != 0:
95 raise ExternalProcessException( 94 raise ExternalProcessException(
96 stderr_data.decode(sys.stderr.encoding)) 95 stderr_data.decode(sys.stderr.encoding))
97 96
98 logger.debug("Moving map file: %s -> %s" % (temp_map_path, map_path)) 97 logger.debug("Moving map file: %s -> %s" % (temp_map_path, map_path))
99 if os.path.exists(map_path): 98 if os.path.exists(map_path):
100 os.remove(map_path) 99 os.remove(map_path)
101 os.rename(temp_map_path, map_path) 100 os.rename(temp_map_path, map_path)
113 raise Exception("The `less/options` configuration setting " 112 raise Exception("The `less/options` configuration setting "
114 "must be an array of arguments.") 113 "must be an array of arguments.")
115 114
116 def _getMapPath(self, path): 115 def _getMapPath(self, path):
117 map_name = "%s_%s.map" % ( 116 map_name = "%s_%s.map" % (
118 os.path.basename(path), 117 os.path.basename(path),
119 hashlib.md5(path.encode('utf8')).hexdigest()) 118 hashlib.md5(path.encode('utf8')).hexdigest())
120 map_path = os.path.join(self._map_dir, map_name) 119 map_path = os.path.join(self._map_dir, map_name)
121 return map_path 120 return map_path
122 121