comparison piecrust/serving/procloop.py @ 570:7dabfdd056a1

serve: Fix corner cases where the pipeline doesn't run correctly. * When `chef serve` is run before the `assets` folder is created, monitor that folder suddenly appearing and rebuild the pipeline. * Do the same when the `config.yml` file has changed.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 31 Oct 2015 00:03:32 -0700
parents ff714d7f074d
children 3ceeca7bb71c
comparison
equal deleted inserted replaced
569:34e57d4b97e2 570:7dabfdd056a1
75 75
76 class ProcessingLoop(threading.Thread): 76 class ProcessingLoop(threading.Thread):
77 def __init__(self, root_dir, out_dir, sub_cache_dir=None, debug=False): 77 def __init__(self, root_dir, out_dir, sub_cache_dir=None, debug=False):
78 super(ProcessingLoop, self).__init__( 78 super(ProcessingLoop, self).__init__(
79 name='pipeline-reloader', daemon=True) 79 name='pipeline-reloader', daemon=True)
80 # TODO: re-create the app when `config.yml` is changed. 80 self.root_dir = root_dir
81 self.app = PieCrust(root_dir=root_dir, debug=debug) 81 self.out_dir = out_dir
82 if sub_cache_dir: 82 self.sub_cache_dir = sub_cache_dir
83 self.app._useSubCacheDir(sub_cache_dir) 83 self.debug = debug
84 self.pipeline = ProcessorPipeline(self.app, out_dir)
85 self.last_status_id = 0 84 self.last_status_id = 0
86 self.interval = 1 85 self.interval = 1
86 self.app = None
87 self._roots = []
88 self._monitor_assets_root = False
87 self._paths = set() 89 self._paths = set()
90 self._config_path = os.path.join(root_dir, 'config.yml')
88 self._record = None 91 self._record = None
89 self._last_bake = 0 92 self._last_bake = 0
93 self._last_config_mtime = 0
90 self._obs = [] 94 self._obs = []
91 self._obs_lock = threading.Lock() 95 self._obs_lock = threading.Lock()
92 96
93 def addObserver(self, obs): 97 def addObserver(self, obs):
94 with self._obs_lock: 98 with self._obs_lock:
97 def removeObserver(self, obs): 101 def removeObserver(self, obs):
98 with self._obs_lock: 102 with self._obs_lock:
99 self._obs.remove(obs) 103 self._obs.remove(obs)
100 104
101 def run(self): 105 def run(self):
102 # Build the first list of known files and run the pipeline once. 106 self._initPipeline()
103 roots = [os.path.join(self.app.root_dir, r) 107
104 for r in self.pipeline.mounts.keys()]
105 for root in roots:
106 for dirpath, dirnames, filenames in os.walk(root):
107 self._paths |= set([os.path.join(dirpath, f)
108 for f in filenames])
109 self._last_bake = time.time() 108 self._last_bake = time.time()
109 self._last_config_mtime = os.path.getmtime(self._config_path)
110 self._record = self.pipeline.run() 110 self._record = self.pipeline.run()
111 111
112 while True: 112 while True:
113 for root in roots: 113 cur_config_time = os.path.getmtime(self._config_path)
114 if self._last_config_mtime < cur_config_time:
115 logger.info("Site configuration changed, reloading pipeline.")
116 self._last_config_mtime = cur_config_time
117 self._initPipeline()
118 for root in self._roots:
119 self._runPipeline(root)
120 continue
121
122 if self._monitor_assets_root:
123 assets_dir = os.path.join(self.app.root_dir, 'assets')
124 if os.path.isdir(assets_dir):
125 logger.info("Assets directory was created, reloading "
126 "pipeline.")
127 self._initPipeline()
128 self._runPipeline(assets_dir)
129 continue
130
131 for root in self._roots:
114 # For each mount root we try to find the first new or 132 # For each mount root we try to find the first new or
115 # modified file. If any, we just run the pipeline on 133 # modified file. If any, we just run the pipeline on
116 # that mount. 134 # that mount.
117 found_new_or_modified = False 135 found_new_or_modified = False
118 for dirpath, dirnames, filenames in os.walk(root): 136 for dirpath, dirnames, filenames in os.walk(root):
134 if found_new_or_modified: 152 if found_new_or_modified:
135 self._runPipeline(root) 153 self._runPipeline(root)
136 154
137 time.sleep(self.interval) 155 time.sleep(self.interval)
138 156
157 def _initPipeline(self):
158 # Create the app and pipeline.
159 self.app = PieCrust(root_dir=self.root_dir, debug=self.debug)
160 if self.sub_cache_dir:
161 self.app._useSubCacheDir(self.sub_cache_dir)
162 self.pipeline = ProcessorPipeline(self.app, self.out_dir)
163
164 # Get the list of assets directories.
165 self._roots = list(self.pipeline.mounts.keys())
166
167 # The 'assets' folder may not be in the mounts list if it doesn't
168 # exist yet, but we want to monitor for when the user creates it.
169 default_root = os.path.join(self.app.root_dir, 'assets')
170 self._monitor_assets_root = (default_root not in self._roots)
171
172 # Build the list of initial asset files.
173 self._paths = set()
174 for root in self._roots:
175 for dirpath, dirnames, filenames in os.walk(root):
176 self._paths |= set([os.path.join(dirpath, f)
177 for f in filenames])
178
139 def _runPipeline(self, root): 179 def _runPipeline(self, root):
140 self._last_bake = time.time() 180 self._last_bake = time.time()
141 try: 181 try:
142 self._record = self.pipeline.run( 182 self._record = self.pipeline.run(
143 root, 183 root,