def make_dir(inpath, tmpldict, outpath=None, pathsubs=None): pathsubs = pathsubs or [] inpath = op.abspath(inpath) bpath = op.basename(inpath) if not outpath: outpath = os.getcwd() dname = render_str(bpath, tmpldict) if not dname: return False mpath = op.abspath(op.join(outpath, dname)) if not mpath: return False for sub in pathsubs: mpath = mpath.replace(*sub) if inpath == mpath: qprompt.fatal("Output cannot overwrite input template!") mpath = render_str(mpath, tmpldict) qprompt.status("Making dir `%s`..." % (mpath), fsys.makedirs, [mpath]) # Iterate over files and directories IN PARENT ONLY. for r, ds, fs in os.walk(inpath): for f in fs: ipath = op.join(r, f) fname = render_str(f, tmpldict) opath = op.join(mpath, fname) if not make_file(ipath, tmpldict, opath): return False for d in ds: ipath = op.join(r, d) if not make_dir(ipath, tmpldict, mpath, pathsubs=pathsubs): return False break # Prevents from walking beyond parent. return True
def make_file(inpath, tmpldict, outpath=None): inpath = op.abspath(inpath) if outpath: outpath = render_str(outpath, tmpldict) if op.isdir(outpath): outpath = op.join(outpath, op.basename(inpath)) outpath = render_str(outpath, tmpldict) if is_binary(inpath): qprompt.status("Copying `%s`..." % (outpath), fsys.copy, [inpath, outpath]) return text = render_file(inpath, tmpldict) if text == None: return False # Handle rendered output. if outpath: outpath = op.abspath(outpath) if inpath == outpath: qprompt.fatal("Output cannot overwrite input template!") fsys.makedirs(op.dirname(outpath)) with io.open(outpath, "w", encoding="utf-8") as f: qprompt.status("Writing `%s`..." % (outpath), f.write, [text]) else: qprompt.echo(text) return True
def render_file(tmplpath, tmpldict, bail_miss=False): """Renders the template file and the given path using the given template variable dictionary. Returns the rendered text as a string.""" tmplpath = op.abspath(tmplpath) env = Environment(undefined=SkipUndefined, extensions=['jinja2_time.TimeExtension']) env.trim_blocks = True env.lstrip_blocks = True env.keep_trailing_newline = True for encoding in ["utf-8", "mbcs"]: try: env.loader = FileSystemLoader(op.dirname(tmplpath), encoding=encoding) tmpl = env.get_template(op.basename(tmplpath)) break except UnicodeDecodeError: qprompt.warn("Issue while decoding template with `%s`!" % encoding) else: qprompt.fatal("Unknown issue while loading template!") with io.open(tmplpath) as fo: tmplstr = fo.read() miss = check_template(tmplstr, tmpldict) if miss: qprompt.warn("Template vars `%s` in `%s` were not supplied values!" % ("/".join(miss), op.basename(tmplpath))) return tmpl.render(**tmpldict)
def scan(scandir, picdirname, overwrite, shrink): """Scan scandir and all subdirectories for pics. Note text will be extracted and a notes file will be created under scandir.""" dirpath = auxly.filesys.Path(scandir) if not dirpath.isdir(): qprompt.fatal("Given path must be existing directory!") if picdirname != dirpath.name: if not qprompt.ask_yesno("Directory not named `pics`, continue?"): sys.exit() create_picnotes(dirpath, confirm=not overwrite, shrink=shrink)
def get_tmpldict(args): # Setup custom YAML tags. yaml.add_constructor(u'!file', ctor_file) yaml.add_constructor(u'!cmd', ctor_cmd) yaml.add_constructor(u'!yaml', ctor_yaml) yaml.add_constructor(u'!opt', ctor_opt) yaml.add_constructor(u'!ask', ctor_ask) yaml.add_constructor(u'!py', ctor_py) # Prepare template dictionary. tmpldict = {} dfltfile = args['--defaults'] if dfltfile: global _DFLTFILE _DFLTFILE = op.abspath(dfltfile) data = fsys.File(dfltfile).read() tmpldict = yaml.load(data) tmpldict = update(tmpldict, {k: v for k, v in zip(args['--string'], args['VAL'])}) tmpldict = update( tmpldict, { k: fsys.File(v).read().strip() for k, v in zip(args['--file'], args['PATH']) }) # Handle nested dictionaries. topop = [] tmplnest = {} global KEYSEP KEYSEP = args['--keysep'] if type(tmpldict) != dict: qprompt.fatal("Template dictionary is not correct type (%s)!" % (type(tmpldict))) for k, v in tmpldict.items(): if k.find(KEYSEP) > -1: level = tmplnest ks = k.split(KEYSEP) for ln, sk in enumerate(ks): level[sk] = level.get(sk, {}) if len(ks) - 1 == ln: level[sk] = v else: level = level[sk] topop.append(k) for k in topop: tmpldict.pop(k) tmpldict = update(tmpldict, tmplnest) return tmpldict
def parse(args): """Parses the given arguments into a template dictionary.""" if args.get('INPATH'): args['--defaults'] = args['INPATH'] if not op.isfile(args['--defaults']): qprompt.fatal("Given path could not be found: `%s`" % (args['--defaults'])) tmpldict = get_tmpldict(args) utildict = get_defopts(tmpldict) utildict.update(get_cliopts(args)) if args.get('--defaults') and not utildict.get('command'): utildict['command'] = "run" for key in ["inpath", "outpath"]: if key not in utildict.keys(): utildict[key] = [] elif type(utildict[key]) != list: utildict[key] = _tolist_no_none(utildict[key]) while len(utildict['outpath']) < len(utildict['inpath']): utildict['outpath'].append(None) return utildict, tmpldict
def get_tmpldict(args): # Setup custom YAML tags. yaml.add_constructor(u'!file', ctor_file) yaml.add_constructor(u'!cmd', ctor_cmd) yaml.add_constructor(u'!yaml', ctor_yaml) yaml.add_constructor(u'!opt', ctor_opt) yaml.add_constructor(u'!ask', ctor_ask) yaml.add_constructor(u'!py', ctor_py) # Prepare template dictionary. tmpldict = {} dfltfile = args['--defaults'] if dfltfile: global _DFLTFILE _DFLTFILE = op.abspath(dfltfile) data = fsys.File(dfltfile).read() tmpldict = yaml.load(data) tmpldict = update(tmpldict, {k:v for k,v in zip(args['--string'], args['VAL'])}) tmpldict = update(tmpldict, {k:fsys.File(v).read().strip() for k,v in zip(args['--file'], args['PATH'])}) # Handle nested dictionaries. topop = [] tmplnest = {} global KEYSEP KEYSEP = args['--keysep'] if type(tmpldict) != dict: qprompt.fatal("Template dictionary is not correct type (%s)!" % (type(tmpldict))) for k,v in tmpldict.items(): if k.find(KEYSEP) > -1: level = tmplnest ks = k.split(KEYSEP) for ln,sk in enumerate(ks): level[sk] = level.get(sk, {}) if len(ks)-1 == ln: level[sk] = v else: level = level[sk] topop.append(k) for k in topop: tmpldict.pop(k) tmpldict = update(tmpldict, tmplnest) return tmpldict
def walk(startdir, picdirname): """Walk all directories under startdir and scan when directory name matches picdirname. Existing notes are overwritten.""" if not op.isdir(startdir): qprompt.fatal("Given path must be existing directory!") total_count = {'reused': 0, 'scanned': 0} for d in auxly.filesys.walkdirs(startdir, "pics"): if op.basename(d) != picdirname: continue qprompt.hrule() qprompt.alert(f"Walking through `{d}`...") dirpath = auxly.filesys.Path(d) new_count = create_picnotes(dirpath, False) if new_count: total_count['reused'] += new_count['reused'] total_count['scanned'] += new_count['scanned'] qprompt.alert( f"Walk complete, scanned={total_count['scanned']} reused={total_count['reused']}" ) sys.exit()
def main(): """This function implements the main logic.""" if len(sys.argv) > 1 and op.isfile(sys.argv[1]): args = {} args['--defaults'] = sys.argv[1] args['--file'] = [] args['--keysep'] = "::" args['--string'] = [] args['PATH'] = [] args['VAL'] = [] args['runargs'] = sys.argv[2:] or "" else: args = docopt(__doc__, version="poppage-%s" % (__version__)) utildict, tmpldict = utilconf.parse(args) # Check required conditions. if not utildict.get('inpath'): qprompt.fatal("Must supply INPATH!") # Handle command. if utildict['command'] == "check": check(utildict['inpath'][0], echo=True) elif utildict['command'] == "make": for inpath, outpath in zip(utildict['inpath'], utildict['outpath']): make(inpath, tmpldict, outpath=outpath) elif utildict['command'] == "run": run(utildict['inpath'][0], tmpldict, outpath=utildict['outpath'][0], execute=utildict.get('execute'), runargs=utildict.get('runargs')) elif utildict['command'] == "debug": qprompt.echo("Utility Dictionary:") pprint(utildict) qprompt.echo("Template Dictionary:") pprint(tmpldict)