def initialize(ext_dir, conf, env): global __views_list __views_list = [] # view -> path urlmap = [(conf['views'][k]['view'], k) for k in conf['views']] ext_dir.extend([os.path.dirname(__file__)]) ext_list = [] # handle ext_dir for mem in ext_dir[:]: if os.path.isdir(mem): sys.path.insert(0, mem) else: ext_dir.remove(mem) log.error("View directory %r does not exist. -- skipping" % mem) for mem in ext_dir: files = glob.glob(os.path.join(mem, "*.py")) files += [p.rstrip('/__init__.py') for p in \ glob.glob(os.path.join(mem, '*/__init__.py'))] ext_list += files for mem in [os.path.basename(x).replace('.py', '') for x in ext_list]: if mem.startswith('_'): continue try: _module = __import__(mem) #sys.modules[__package__].__dict__[mem] = _module index_views(_module, urlmap, conf, env) except (ImportError, Exception) as e: log.error('%r ImportError %r', mem, e) traceback.print_exc(file=sys.stdout)
def generate(self, conf, env, data): pathes, entrylist = set(), data[self.type] unmodified = not env.modified and not conf.modified for i, entry in enumerate(entrylist): if entry.hasproperty('permalink'): path = joinurl(conf['output_dir'], entry.permalink) else: path = joinurl(conf['output_dir'], expand(self.path, entry)) if isfile(path) and path in pathes: try: os.remove(path) finally: other = [e.filename for e in entrylist if e is not entry and e.permalink == entry.permalink][0] log.error("title collision %s caused by %s and %s", entry.permalink, entry.filename, other) raise SystemExit pathes.add(path) next, prev = self.next(entrylist, i), self.prev(entrylist, i) # per-entry template tt = env.engine.fromfile(env, entry.props.get('layout', self.template)) if all([isfile(path), unmodified, not tt.modified, not entry.modified, not modified(*references(entry))]): event.skip(self.name, path) else: html = tt.render(conf=conf, entry=entry, env=union(env, entrylist=[entry], type=self.__class__.__name__.lower(), prev=prev, next=next, route=expand(self.path, entry))) yield html, path # check if any resources need to be moved if entry.hasproperty('copy'): for res_src in entry.resources: res_dest = join(dirname(path), basename(res_src)) # Note, presence of res_src check in FileReader.getresources if isfile(res_dest) and getmtime(res_dest) > getmtime(res_src): event.skip(self.name, res_dest) continue try: fp = io.open(res_src, 'rb') # use mkfile rather than yield so different ns can be specified (and filtered by sitemap) mkfile(fp, res_dest, ns='resource', force=env.options.force, dryrun=env.options.dryrun) except IOError as e: log.warn("Failed to copy resource '%s' whilst processing '%s' (%s)" % (res_src, entry.filename, e.strerror))
def Acryl(): """The main function that dispatches the CLI. We use :class:`AcrylFormatter` as custom help formatter that ommits the useless list of available subcommands and their aliases. All flags from acrylamid --help are also available in subcommands altough not explicitely printed in their help.""" parser = argparse.ArgumentParser( parents=[], formatter_class=AcrylFormatter ) parser.add_argument("-v", "--verbose", action="store_const", dest="verbosity", help="more verbose", const=log.SKIP, default=log.INFO) parser.add_argument("-q", "--quiet", action="store_const", dest="verbosity", help="less verbose", const=log.WARN) parser.add_argument("-C", "--no-color", action="store_false", dest="colors", help="disable color", default=True) parser.add_argument("--conf", dest="conf", help="alternate conf.py", default="conf.py", metavar="/path/to/conf") parser.add_argument("--version", action="version", version=colors.blue('Acrylamid ') + dist.version) subparsers = parser.add_subparsers(dest="parser") # a repeat yourself of default arguments but not visible on subcommand --help default = argparse.ArgumentParser(add_help=False) default.add_argument("-v", "--verbose", action="store_const", dest="verbosity", help=argparse.SUPPRESS, const=log.SKIP, default=log.INFO) default.add_argument("-q", "--quiet", action="store_const", dest="verbosity", help=argparse.SUPPRESS, const=log.WARN) default.add_argument("-C", "--no-color", action="store_false", dest="colors", help=argparse.SUPPRESS, default=True) # --- gen params --- # generate = subparsers.add_parser('compile', help='compile blog', parents=[default]) generate.add_argument("-f", "--force", action="store_true", dest="force", help="clear cache before compilation", default=False) generate.add_argument("-n", "--dry-run", dest="dryrun", action='store_true', help="show what would have been compiled", default=False) generate.add_argument("--ignore", dest="ignore", action="store_true", help="ignore critical errors", default=False) generate.add_argument("--search", dest="search", action="store_true", help="build search index", default=False) # --- webserver params --- # view = subparsers.add_parser('view', help="fire up built-in webserver", parents=[default]) view.add_argument("-p", "--port", dest="port", type=int, default=8000, help="webserver port") # --- aco params --- # autocompile = subparsers.add_parser('autocompile', help="automatic compilation and serving", parents=[default]) autocompile.add_argument("-f", "--force", action="store_true", dest="force", help="clear cache before compilation", default=False) autocompile.add_argument("-n", "--dry-run", dest="dryrun", action='store_true', help="show what would have been compiled", default=False) autocompile.add_argument("--ignore", dest="ignore", action="store_true", help="ignore critical errors", default=False) autocompile.add_argument("--search", dest="search", action="store_true", help="build search index", default=False) autocompile.add_argument("-p", "--port", dest="port", type=int, default=8000, help="webserver port") for alias in ('co', 'gen', 'generate'): subparsers._name_parser_map[alias] = generate for alias in ('serve', 'srv'): subparsers._name_parser_map[alias] = view subparsers._name_parser_map['aco'] = autocompile # temporary log to catch issues during task initialization log.init('temporary', level=log.WARN, colors=False) # initialize other tasks tasks.initialize(subparsers, default) # parse args options = parser.parse_args() # initialize colored logger log.init('acrylamid', level=options.verbosity, colors=options.colors) env = core.Environment({'author': __author__, 'url': __url__, 'options': options, 'globals': Struct()}) try: conf = core.load(options.conf) except IOError: log.critical('no conf.py found. Are you inside your blog?') sys.exit(1) except Exception as e: log.critical("%s in `conf.py`" % e.__class__.__name__) traceback.print_exc(file=sys.stdout) sys.exit(1) # -- run -- # if options.parser in ('gen', 'generate', 'co', 'compile'): log.setLevel(options.verbosity) try: commands.compile(conf, env) except AcrylamidException as e: log.exception(e.args[0]) sys.exit(1) elif options.parser in ('srv', 'serve', 'view'): from acrylamid.lib.httpd import Webserver ws = partial(Webserver, options.port, conf['output_dir']) ws = ws(log.info) if options.verbosity < 20 else ws(); ws.start() log.info(' * Running on http://127.0.0.1:%i/' % options.port) try: while True: time.sleep(1) except (SystemExit, KeyboardInterrupt) as e: ws.kill_received = True sys.exit(0) elif options.parser in ('aco', 'autocompile'): from acrylamid.lib.httpd import Webserver # XXX compile on request _or_ use inotify/fsevent ws = Webserver(options.port, conf['output_dir']); ws.start() log.info(' * Running on http://127.0.0.1:%i/' % options.port) try: commands.autocompile(ws, conf, env) except (SystemExit, KeyboardInterrupt) as e: ws.kill_received = True log.error(e.args[0]) traceback.print_exc(file=sys.stdout) sys.exit(0) elif options.parser in tasks.collected: try: tasks.collected[options.parser](conf, env, options) except AcrylamidException as e: log.exception('uncaught exception') sys.exit(1) else: log.critical('No such command!') sys.exit(2) sys.exit(0)
def initialize(ext_dir, conf, env, include=[], exclude=[]): """Imports and initializes extensions from the directories in the list specified by 'ext_dir'. If no such list exists, the we don't load any plugins. 'include' and 'exclude' may contain a list of shell patterns used for fnmatch. If empty, this filter is not applied. :param ext_dir: list of directories :param conf: user config :param env: environment :param include: a list of filename patterns to include :param exclude: a list of filename patterns to exclude """ def get_name(filename): """Takes a filename and returns the module name from the filename.""" return os.path.splitext(os.path.split(filename)[1])[0] def get_extension_list(ext_dir, include, exclude): """Load all plugins that matches include/exclude pattern in given lust of directories. Arguments as in initializes(**kw).""" def pattern(name, incl, excl): for i in incl: if fnmatch.fnmatch(name, i): return True for e in excl: if fnmatch.fnmatch(name, e): return False if not incl: return True else: return False ext_list = [] for mem in ext_dir: files = glob.glob(os.path.join(mem, "*.py")) files = [get_name(f) for f in files if pattern(get_name(f), include, exclude)] ext_list += files return sorted(ext_list) global plugins exclude.extend(['mdx_*', 'rstx_*']) ext_dir.extend([os.path.dirname(__file__)]) for mem in ext_dir[:]: if os.path.isdir(mem): sys.path.insert(0, mem) else: ext_dir.remove(mem) log.error("Filter directory '%s' does not exist. -- skipping" % mem) ext_list = get_extension_list(ext_dir, include, exclude) for mem in ext_list: try: if PY3: from importlib import import_module _module = import_module('.' + mem, package='acrylamid.filters') else: _module = __import__(mem) except (ImportError, Exception) as e: log.warn('%r %s: %s', mem, e.__class__.__name__, e) continue index_filters(_module, conf, env)
def Acryl(): """The main function that dispatches the CLI. We use :class:`AcrylFormatter` as custom help formatter that ommits the useless list of available subcommands and their aliases. All flags from acrylamid --help are also available in subcommands altough not explicitely printed in their help.""" parser = argparse.ArgumentParser( parents=[], formatter_class=AcrylFormatter ) parser.add_argument("-v", "--verbose", action="store_const", dest="verbosity", help="more verbose", const=log.SKIP, default=log.INFO) parser.add_argument("-q", "--quiet", action="store_const", dest="verbosity", help="less verbose", const=log.WARN) parser.add_argument("-C", "--no-color", action="store_false", dest="colors", help="disable color", default=True) parser.add_argument("--version", action="version", version=colors.blue('acrylamid ') + __version__) subparsers = parser.add_subparsers(dest="parser") # a repeat yourself of default arguments but not visible on subcommand --help default = argparse.ArgumentParser(add_help=False) default.add_argument("-v", "--verbose", action="store_const", dest="verbosity", help=argparse.SUPPRESS, const=log.SKIP, default=log.INFO) default.add_argument("-q", "--quiet", action="store_const", dest="verbosity", help=argparse.SUPPRESS, const=log.WARN) default.add_argument("-C", "--no-color", action="store_false", dest="colors", help=argparse.SUPPRESS, default=True) # --- gen params --- # generate = subparsers.add_parser('compile', help='compile blog', parents=[default]) generate.add_argument("-f", "--force", action="store_true", dest="force", help="clear cache before compilation", default=False) generate.add_argument("-n", "--dry-run", dest="dryrun", action='store_true', help="show what would have been compiled", default=False) generate.add_argument("-i", "--ignore", dest="ignore", action="store_true", help="ignore critical errors", default=False) # --- webserver params --- # view = subparsers.add_parser('view', help="fire up built-in webserver", parents=[default]) view.add_argument("-p", "--port", dest="port", type=int, default=8000, help="webserver port") # --- aco params --- # autocompile = subparsers.add_parser('autocompile', help="automatic compilation and serving", parents=[default]) autocompile.add_argument("-f", "--force", action="store_true", dest="force", help="clear cache before compilation", default=False) autocompile.add_argument("-n", "--dry-run", dest="dryrun", action='store_true', help="show what would have been compiled", default=False) autocompile.add_argument("-i", "--ignore", dest="ignore", action="store_true", help="ignore critical errors", default=False) autocompile.add_argument("-p", "--port", dest="port", type=int, default=8000, help="webserver port") for alias in ('co', 'gen', 'generate'): subparsers._name_parser_map[alias] = generate for alias in ('serve', 'srv'): subparsers._name_parser_map[alias] = view subparsers._name_parser_map['aco'] = autocompile new = subparsers.add_parser('new', help="create a new entry", parents=[default], epilog=("Takes all leading [args] as title or prompt if none given. creates " "a new entry based on your PERMALINK_FORMAT and opens it with your " "favourite $EDITOR.")) new.add_argument("title", nargs="*", default='') # initialize other tasks tasks.initialize(subparsers, default) # parse args options = parser.parse_args() # initialize colored logger log.init('acrylamid', level=options.verbosity, colors=options.colors) env = Struct({'version': __version__, 'author': __author__, 'url': __url__}) env['options'] = options env['globals'] = Struct() # -- init -- # # TODO: acrylamid init --layout_dir=somedir to overwrite defaults if options.parser in ('init', ): tasks.collected[options.parser](env, options) sys.exit(0) # -- teh real thing -- # conf = Struct(defaults.conf) try: ns = dict([(k.upper(), v) for k, v in defaults.conf.iteritems()]) os.chdir(os.path.dirname(find('conf.py', os.getcwd()))) execfile('conf.py', ns) conf.update(dict([(k.lower(), ns[k]) for k in ns if k.upper() == k])) except IOError: log.critical('no conf.py found. Try "acrylamid init".') sys.exit(1) except Exception as e: log.critical("%s in `conf.py`" % e.__class__.__name__) traceback.print_exc(file=sys.stdout) sys.exit(1) conf['output_dir'] = conf.get('output_dir', 'output/') conf['content_dir'] = conf.get('content_dir', 'content/') conf['layout_dir'] = conf.get('layout_dir', 'layouts/') # -- run -- # if options.parser in ('gen', 'generate', 'co', 'compile'): log.setLevel(options.verbosity) try: commands.compile(conf, env, **options.__dict__) except AcrylamidException as e: log.fatal(e.args[0]) sys.exit(1) elif options.parser in ('new', 'create'): try: commands.new(conf, env, title=' '.join(options.title), prompt=log.level()<log.WARN) except AcrylamidException as e: log.fatal(e.args[0]) sys.exit(1) elif options.parser in ('srv', 'serve', 'view'): from acrylamid.lib.httpd import Webserver ws = Webserver(options.port, conf['output_dir']); ws.start() log.info(' * Running on http://127.0.0.1:%i/' % options.port) try: while True: time.sleep(1) except (SystemExit, KeyboardInterrupt, Exception) as e: ws.kill_received = True sys.exit(0) elif options.parser in ('aco', 'autocompile'): from acrylamid.lib.httpd import Webserver # XXX compile on request _or_ use inotify/fsevent ws = Webserver(options.port, conf['output_dir']); ws.start() log.info(' * Running on http://127.0.0.1:%i/' % options.port) try: commands.autocompile(ws, conf, env, **options.__dict__) except (SystemExit, KeyboardInterrupt, Exception) as e: ws.kill_received = True log.error(e.args[0]) traceback.print_exc(file=sys.stdout) sys.exit(0) elif options.parser in tasks.collected: try: tasks.collected[options.parser](conf, env, options) except AcrylamidException as e: log.critical(e.args[0]) sys.exit(1) else: log.critical('No such command!') sys.exit(2) sys.exit(0)
time.sleep(1) except (SystemExit, KeyboardInterrupt, Exception) as e: ws.kill_received = True sys.exit(0) elif options.parser in ('aco', 'autocompile'): from acrylamid.lib.httpd import Webserver # XXX compile on request _or_ use inotify/fsevent ws = Webserver(options.port, conf['output_dir']); ws.start() log.info(' * Running on http://127.0.0.1:%i/' % options.port) try: commands.autocompile(ws, conf, env, **options.__dict__) except (SystemExit, KeyboardInterrupt, Exception) as e: ws.kill_received = True log.error(e.args[0]) traceback.print_exc(file=sys.stdout) sys.exit(0) elif options.parser in ('import', ): try: commands.imprt(conf, env, options) except AcrylamidException as e: log.critical(e.args[0]) sys.exit(1) elif options.parser in tasks.collected: try: tasks.collected[options.parser](conf, env, options) except AcrylamidException as e: log.critical(e.args[0])
def Acryl(): """The main function that dispatches the CLI. We use :class:`AcrylFormatter` as custom help formatter that ommits the useless list of available subcommands and their aliases. All flags from acrylamid --help are also available in subcommands altough not explicitely printed in their help.""" parser = argparse.ArgumentParser(parents=[], formatter_class=AcrylFormatter) parser.add_argument( "-v", "--verbose", action="store_const", dest="verbosity", help="more verbose", const=log.SKIP, default=log.INFO ) parser.add_argument("-q", "--quiet", action="store_const", dest="verbosity", help="less verbose", const=log.WARN) parser.add_argument("-C", "--no-color", action="store_false", dest="colors", help="disable color", default=True) parser.add_argument("--version", action="version", version=colors.blue("acrylamid ") + __version__) subparsers = parser.add_subparsers(dest="parser") # a repeat yourself of default arguments but not visible on subcommand --help default = argparse.ArgumentParser(add_help=False) default.add_argument( "-v", "--verbose", action="store_const", dest="verbosity", help=argparse.SUPPRESS, const=log.SKIP, default=log.INFO, ) default.add_argument( "-q", "--quiet", action="store_const", dest="verbosity", help=argparse.SUPPRESS, const=log.WARN ) default.add_argument("-C", "--no-color", action="store_false", dest="colors", help=argparse.SUPPRESS, default=True) # --- gen params --- # generate = subparsers.add_parser("compile", help="compile blog", parents=[default]) generate.add_argument( "-f", "--force", action="store_true", dest="force", help="clear cache before compilation", default=False ) generate.add_argument( "-n", "--dry-run", dest="dryrun", action="store_true", help="show what would have been compiled", default=False ) generate.add_argument( "-i", "--ignore", dest="ignore", action="store_true", help="ignore critical errors", default=False ) # --- webserver params --- # view = subparsers.add_parser("view", help="fire up built-in webserver", parents=[default]) view.add_argument("-p", "--port", dest="port", type=int, default=8000, help="webserver port") # --- aco params --- # autocompile = subparsers.add_parser("autocompile", help="automatic compilation and serving", parents=[default]) autocompile.add_argument( "-f", "--force", action="store_true", dest="force", help="clear cache before compilation", default=False ) autocompile.add_argument( "-n", "--dry-run", dest="dryrun", action="store_true", help="show what would have been compiled", default=False ) autocompile.add_argument( "-i", "--ignore", dest="ignore", action="store_true", help="ignore critical errors", default=False ) autocompile.add_argument("-p", "--port", dest="port", type=int, default=8000, help="webserver port") for alias in ("co", "gen", "generate"): subparsers._name_parser_map[alias] = generate for alias in ("serve", "srv"): subparsers._name_parser_map[alias] = view subparsers._name_parser_map["aco"] = autocompile # initialize other tasks tasks.initialize(subparsers, default) # parse args options = parser.parse_args() # initialize colored logger log.init("acrylamid", level=options.verbosity, colors=options.colors) env = Struct({"version": __version__, "author": __author__, "url": __url__}) env["options"] = options env["globals"] = Struct() # -- init -- # try: if options.parser in ("init",): tasks.collected[options.parser](env, options) sys.exit(0) except AcrylamidException as e: log.fatal(e.args[0]) sys.exit(1) # -- teh real thing -- # conf = Struct(defaults.conf) try: ns = dict([(k.upper(), v) for k, v in defaults.conf.iteritems()]) os.chdir(os.path.dirname(find("conf.py", os.getcwd()))) execfile("conf.py", ns) conf.update(dict([(k.lower(), ns[k]) for k in ns if k.upper() == k])) except IOError: log.critical('no conf.py found. Try "acrylamid init".') sys.exit(1) except Exception as e: log.critical("%s in `conf.py`" % e.__class__.__name__) traceback.print_exc(file=sys.stdout) sys.exit(1) # -- run -- # if options.parser in ("gen", "generate", "co", "compile"): log.setLevel(options.verbosity) try: commands.compile(conf, env, **options.__dict__) except AcrylamidException as e: log.fatal(e.args[0]) sys.exit(1) elif options.parser in ("srv", "serve", "view"): from acrylamid.lib.httpd import Webserver ws = partial(Webserver, options.port, conf["output_dir"]) ws = ws(log.info) if options.verbosity < 20 else ws() ws.start() log.info(" * Running on http://127.0.0.1:%i/" % options.port) try: while True: time.sleep(1) except (SystemExit, KeyboardInterrupt, Exception) as e: ws.kill_received = True sys.exit(0) elif options.parser in ("aco", "autocompile"): from acrylamid.lib.httpd import Webserver # XXX compile on request _or_ use inotify/fsevent ws = Webserver(options.port, conf["output_dir"]) ws.start() log.info(" * Running on http://127.0.0.1:%i/" % options.port) try: commands.autocompile(ws, conf, env, **options.__dict__) except (SystemExit, KeyboardInterrupt, Exception) as e: ws.kill_received = True log.error(e.args[0]) traceback.print_exc(file=sys.stdout) sys.exit(0) elif options.parser in tasks.collected: try: tasks.collected[options.parser](conf, env, options) except AcrylamidException as e: log.critical(e.args[0]) sys.exit(1) else: log.critical("No such command!") sys.exit(2) sys.exit(0)
def generate(self, conf, env, data): pathes, entrylist = set(), data[self.type] unmodified = not env.modified and not conf.modified for i, entry in enumerate(entrylist): if entry.hasproperty('permalink'): path = joinurl(conf['output_dir'], entry.permalink) else: path = joinurl(conf['output_dir'], expand(self.path, entry)) if isfile(path) and path in pathes: try: os.remove(path) finally: other = [ e.filename for e in entrylist if e is not entry and e.permalink == entry.permalink ][0] log.error("title collision %s caused by %s and %s", entry.permalink, entry.filename, other) raise SystemExit pathes.add(path) next, prev = self.next(entrylist, i), self.prev(entrylist, i) # per-entry template tt = env.engine.fromfile(env, entry.props.get('layout', self.template)) if all([ isfile(path), unmodified, not tt.modified, not entry.modified, not modified(*references(entry)) ]): event.skip(self.name, path) else: html = tt.render(conf=conf, entry=entry, env=union( env, entrylist=[entry], type=self.__class__.__name__.lower(), prev=prev, next=next, route=expand(self.path, entry))) yield html, path # check if any resources need to be moved if entry.hasproperty('copy'): for res_src in entry.resources: res_dest = join(dirname(path), basename(res_src)) # Note, presence of res_src check in FileReader.getresources if isfile(res_dest ) and getmtime(res_dest) > getmtime(res_src): event.skip(self.name, res_dest) continue try: fp = io.open(res_src, 'rb') # use mkfile rather than yield so different ns can be specified (and filtered by sitemap) mkfile(fp, res_dest, ns='resource', force=env.options.force, dryrun=env.options.dryrun) except IOError as e: log.warn( "Failed to copy resource '%s' whilst processing '%s' (%s)" % (res_src, entry.filename, e.strerror))