def __init__(self, app, migrations_folder=None): self.app = app self.path = os.path.join(app.root_path, migrations_folder or 'migrations') if not os.path.exists(self.path): os.mkdir(self.path) self.cwd = os.path.dirname(__file__) self.file_template = self.app.config.migrations.file_template or \ self._default_file_template self.truncate_slug_length = \ self.app.config.migrations.filename_len or 40 self.revision_map = RevisionsMap(self.app, self._load_revisions) self.templater = Renoir(path=self.cwd, mode='plain')
def templater_html_indent(): return Renoir( escape='all', adjust_indent=True, debug=True, path=os.path.join( os.path.dirname(os.path.abspath(__file__)), 'html' ) )
def templater_noreload(): return Renoir()
def templater_reload(): return Renoir(reload=True)
class ScriptDir(object): _slug_re = re.compile(r'\w+') _default_file_template = "%(rev)s_%(slug)s" def __init__(self, app, migrations_folder=None): self.app = app self.path = os.path.join(app.root_path, migrations_folder or 'migrations') if not os.path.exists(self.path): os.mkdir(self.path) self.cwd = os.path.dirname(__file__) self.file_template = self.app.config.migrations.file_template or \ self._default_file_template self.truncate_slug_length = \ self.app.config.migrations.filename_len or 40 self.revision_map = RevisionsMap(self.app, self._load_revisions) self.templater = Renoir(path=self.cwd, mode='plain') def _load_revisions(self): sys.path.insert(0, self.path) for rev_file in os.listdir(os.path.abspath(self.path)): script = Script._from_filename(self, rev_file) if script is None: continue yield script @contextmanager def _catch_revision_errors(self, ancestor=None, multiple_heads=None, start=None, end=None, resolution=None): try: yield except RangeNotAncestorError as rna: if start is None: start = rna.lower if end is None: end = rna.upper if not ancestor: ancestor = ( "Requested range %(start)s:%(end)s does not refer to " "ancestor/descendant revisions along the same branch") ancestor = ancestor % {"start": start, "end": end} raise Exception(ancestor) except MultipleHeads as mh: if not multiple_heads: multiple_heads = ( "Multiple head revisions are present for given " "argument '%(head_arg)s'; please " "specify a specific target revision, " "'<branchname>@%(head_arg)s' to " "narrow to a specific head, or 'heads' for all heads") multiple_heads = multiple_heads % { "head_arg": end or mh.argument, "heads": str(mh.heads) } raise Exception(multiple_heads) except ResolutionError as re: if resolution is None: resolution = "Can't locate revision identified by '%s'" % ( re.argument) raise Exception(resolution) except RevisionError as err: raise Exception(err.args[0]) def walk_revisions(self, base="base", head="heads"): with self._catch_revision_errors(start=base, end=head): for rev in self.revision_map.iterate_revisions(head, base, inclusive=True): yield rev def get_revision(self, revid): with self._catch_revision_errors(): return self.revision_map.get_revision(revid) def get_revisions(self, revid): with self._catch_revision_errors(): return self.revision_map.get_revisions(revid) def get_upgrade_revs(self, destination, current_rev): with self._catch_revision_errors( ancestor="Destination %(end)s is not a valid upgrade " "target from current head(s)", end=destination): revs = self.revision_map.iterate_revisions(destination, current_rev, implicit_base=True) return reversed(list(revs)) def get_downgrade_revs(self, destination, current_rev): with self._catch_revision_errors( ancestor="Destination %(end)s is not a valid downgrade " "target from current head(s)", end=destination): revs = self.revision_map.iterate_revisions(current_rev, destination) return list(revs) def _rev_filename(self, revid, message, creation_date): slug = "_".join(self._slug_re.findall(message or "")).lower() if len(slug) > self.truncate_slug_length: slug = slug[:self.truncate_slug_length].rsplit('_', 1)[0] + '_' filename = "%s.py" % (self.file_template % { 'rev': revid, 'slug': slug, 'year': creation_date.year, 'month': creation_date.month, 'day': creation_date.day, 'hour': creation_date.hour, 'minute': creation_date.minute, 'second': creation_date.second }) return filename def _generate_template(self, filename, ctx): rendered = self.templater.render('migration.tmpl', ctx) with open(os.path.join(self.path, filename), 'w') as f: f.write(rendered) def generate_revision(self, revid, message, head=None, splice=False, **kw): """Generate a new revision file. This runs the templater, and creates a new file. :param revid: String revision id. :param message: the revision message. :param head: the head revision to generate against. Defaults to the current "head" if no branches are present, else raises an exception. :param splice: if True, allow the "head" version to not be an actual head; otherwise, the selected head must be a head (e.g. endpoint) revision. """ if head is None: head = "head" with self._catch_revision_errors(multiple_heads=( "Multiple heads are present; please specify the head " "revision on which the new revision should be based, " "or perform a merge.")): heads = self.revision_map.get_revisions(head) if len(set(heads)) != len(heads): raise Exception("Duplicate head revisions specified") creation_date = datetime.now() rev_filename = self._rev_filename(revid, message, creation_date) if not splice: for head in heads: if head is not None and not head.is_head: raise Exception( "Revision %s is not a head revision; please specify " "--splice to create a new branch from this revision" % head.revision) down_migration = tuple(h.revision if h is not None else None for h in heads) down_migration_var = tuple_rev_as_scalar(down_migration) if isinstance(down_migration_var, str): down_migration_var = "%r" % down_migration_var else: down_migration_var = str(down_migration_var) template_ctx = dict( asis=asis, up_migration=revid, down_migration=down_migration_var, creation_date=creation_date, down_migration_str=", ".join(r for r in down_migration), message=message if message is not None else ("empty message"), upgrades=kw.get('upgrades', ['pass']), downgrades=kw.get('downgrades', ['pass'])) self._generate_template(rev_filename, template_ctx) script = Script._from_filename(self, rev_filename) self.revision_map.add_revision(script) return script
def templater_html(): return Renoir( debug=True, path=os.path.join( os.path.dirname(os.path.abspath(__file__)), 'html' ) )
def templater_indent(): return Renoir( mode='plain', adjust_indent=True, debug=True, path=os.path.join( os.path.dirname(os.path.abspath(__file__)), 'yaml' ) )
def templater_blocks(): return Renoir( mode='plain', debug=True, path=os.path.join( os.path.dirname(os.path.abspath(__file__)), 'blocks' ) )
@property def current_line(self): try: return self.sourcelines[self.lineno - 1] except IndexError: return u'' @cachedprop def render_locals(self): rv = dict() for k, v in self.locals.items(): try: rv[k] = str(v) except Exception: rv[k] = '<unavailable>' return rv debug_templater = Renoir( path=os.path.join(os.path.dirname(__file__), 'assets', 'debug')) def smart_traceback(app): exc_type, exc_value, tb = sys.exc_info() return Traceback(app, exc_type, exc_value, tb) def debug_handler(tb): return debug_templater.render('view.html', {'tb': tb})
def templater_escape(): return Renoir(escape='all', debug=True)
def templater_indent(): return Renoir(mode='plain', adjust_indent=True, debug=True)
def templater(): return Renoir(mode='plain', debug=True)
def templater(): rv = Renoir() rv.use_extension(FooExtension) rv.use_extension(BarExtension) return rv