def hint(): if os.path.exists(to_file): fname = f'{name}-{datetime.datetime.utcnow().isoformat()}.txt' t = path.logs(fname, makedirs=True) reflink.auto(to_file, t) home = os.path.expanduser('~') t = t if not t.startswith(home) else t.replace(home, '~') m = (f'Check {t} for more details or set FINGERTIP_DEBUG=1' if not DEBUG else f'Logfile: {t}') sys.stderr.write(m + '\n')
def hint(self): if not os.path.exists(self.path): self.warning(f'{self.path} missing!') fname = f'{datetime.datetime.utcnow().isoformat()}.txt' t = path.logs(fname, makedirs=True) reflink.auto(self.path, t) home = os.path.expanduser('~') t = t if not t.startswith(home) else t.replace(home, '~') m = (f'For an intermediate log, check {t} or set FINGERTIP_DEBUG=1.' if not DEBUG else f'Logfile: {t}') sys.stderr.write(m + '\n')
def build(first_step, *args, fingertip_last_step=False, **kwargs): func, tag = step_loader.func_and_autotag(first_step, *args, **kwargs) # Could there already be a cached result? mpath = path.machines(tag) lock_path = path.machines('.' + tag + '-lock') log.info(f'acquiring lock for {tag}...') transient_hint = func.transient if hasattr(func, 'transient') else None if callable(transient_hint): transient_hint = supply_last_step_if_requested(transient_hint, fingertip_last_step) transient_hint = transient_hint(*args, **kwargs) transient = (transient_hint in ('always', True) or transient_hint == 'last' and fingertip_last_step) with lock.Lock(lock_path) if not transient else lock.NoLock(): if not os.path.exists(mpath) or needs_a_rebuild(mpath): log.info(f'building {tag}...') func = supply_last_step_if_requested(func, fingertip_last_step) first = func(*args, **kwargs) if first is None: assert transient, 'first step returned None' return if transient: log.info(f'succesfully built and discarded {tag}') first._finalize() # discard (not fast-dropped though) if transient_hint == 'last' and fingertip_last_step: fname = f'{datetime.datetime.utcnow().isoformat()}.txt' t = path.logs(fname, makedirs=True) with open(t, 'w') as f: f.write(first.log_contents) return t else: log.info(f'succesfully built and saved {tag}') first._finalize(link_as=mpath, name_hint=tag) if fingertip_last_step: return os.path.join(mpath, 'log.txt') m = clone_and_load(mpath) m.log = log.Sublogger('fingertip.<just built>', os.path.join(m.path, 'log.txt')) return m
def _cache_aware_apply(self, step, tag, func, args, kwargs, last_step): assert self._state == 'loaded' transient_hint = func.transient if hasattr(func, 'transient') else None if callable(transient_hint): transient_hint = supply_last_step_if_requested( transient_hint, last_step) transient_hint = transient_hint(self, *args, **kwargs) return_as_transient = self._transient exec_as_transient = (transient_hint in ('always', True) or transient_hint == 'last' and last_step) log.debug(f'transient: {transient_hint}') log.debug(f'exec_as_transient: {exec_as_transient}') log.debug(f'return_as_transient: {return_as_transient}') self._transient = exec_as_transient # Could there already be a cached result? log.debug(f'PATH {self.path} {tag}') new_mpath = os.path.join(self._parent_path, tag) lock_path = os.path.join(self._parent_path, '.' + tag + '-lock') do_lock = not self._transient if do_lock: log.info(f'acquiring lock for {tag}...') prev_log_name = self.log.name self.log.finalize() with lock.Lock(lock_path) if do_lock else lock.NoLock(): if (os.path.exists(new_mpath) and not needs_a_rebuild(new_mpath) and not exec_as_transient): # sweet, scratch this instance, fast-forward to cached result log.info(f'reusing {step} @ {new_mpath}') self._finalize() clone_from_path = new_mpath else: # loaded, not spun up, step not cached: perform step, cache log.info(f'applying (and, possibly, caching) {tag}') self.log = log.Sublogger('plugins.' + tag.split(':', 1)[0], os.path.join(self.path, 'log.txt')) func = supply_last_step_if_requested(func, last_step) m = func(self, *args, **kwargs) if m: if m._transient and transient_hint == 'last' and last_step: assert m._state == 'dropped' # transient-when-last step returned m # just in case it's not the last, but it was. # m is dropped already, only log contents is preserved. fname = f'{datetime.datetime.utcnow().isoformat()}.txt' t = path.logs(fname, makedirs=True) with open(t, 'w') as f: f.write(m.log_contents) return t assert not m._transient, 'transient step returned a value' m._finalize(link_as=new_mpath, name_hint=tag) clone_from_path = new_mpath log.info(f'successfully applied and saved {tag}') else: # transient step, either had hints or just returned None clone_from_path = self._parent_path log.info(f'successfully applied and dropped {tag}') if last_step: return os.path.join(clone_from_path, 'log.txt') m = clone_and_load(clone_from_path) m.log = log.Sublogger(prev_log_name, os.path.join(m.path, 'log.txt')) m._transient = return_as_transient return m