class Maker(object): """ Main class of chimney. Executes compilers and stuff. """ STOP_WATCHING = 0 RELOAD = 1 def __init__(self, *tasks, **kw): """ ``tasks`` - A list of Compiler instances to run ``directory`` - Must be the top level of the project. All files will be relative to this path. Defaults to the current directory. """ self.tasks = tasks self.directory = kw.pop('directory', None) or os.path.abspath(os.path.curdir) self.jobs = int(kw.pop('jobs', multiprocessing.cpu_count() * 1.5)) # observed changes self.changes = [] if kw: raise TypeError('Unknown keyword arguments: {0}'.format(', '.join(kw.keys()))) for task in self.tasks: task.maker = self self.executor = DelayedThreadPoolExecutor(self.jobs) super(Maker, self).__init__() def execute(self): runners = Scheduler().load(self.tasks).run() # schedule all of the runners for runner in six.itervalues(runners): runner.schedule(self.executor) self.executor.wait() def watch(self, reload_patterns=None): scheduler = Scheduler().load(self.tasks) runners = scheduler.run() # schedule all of the runners self.by_source = {} for runner in six.itervalues(runners): runner.schedule(self.executor) for dep in runner.task.dependent: self.by_source[os.path.abspath(dep)] = runner.task self.executor.wait() def change_handler(obs): self.changes.append(obs) self.watcher = Watcher(change_handler) log.info('Watching for changes. Control-C to cancel') try: while self.sleep(): ret = self.process_changes(reload_patterns) if ret == Maker.RELOAD: return Maker.RELOAD except KeyboardInterrupt: self.close() return Maker.STOP_WATCHING def process_changes(self, reload_patterns=None): batch = self.changes self.changes = [] for obs in set(batch): if obs.type in ('created', 'deleted',): if reload_patterns is None: log.info(u'File %s: %s, reloading', obs.type, obs.path) return Maker.RELOAD for p in reload_patterns: if fnmatch.fnmatch(obs.path, p): log.info(u'File created: %s, reloading', obs.path) return Maker.RELOAD if obs.type != 'modified': continue task = self.by_source.get(os.path.abspath(obs.path)) if task: log.info('Detected %s: %s', obs.type, obs.path) runner = Runner(task) runner.schedule(self.executor) return None def sleep(self): time.sleep(.1) return True def close(self): self.executor.shutdown()