Exemple #1
0
def execute(cmd, ns, src, dest=None):
    """Execute `cmd` such as `yui-compressor %1 -o %2` in-place.
    If `dest` is none, you don't have to supply %2."""

    assert '%1' in cmd
    cmd = cmd.replace('%1', src)

    if dest:
        assert '%2' in cmd
        cmd = cmd.replace('%2', dest)

        if not isdir(dirname(dest)):
            os.makedirs(dirname(dest))

    try:
        rv = system(cmd, shell=True)
    except (AcrylamidException, OSError):
        log.exception("uncaught exception during execution")
        return

    if dest is None:
        fd, path = mkstemp()
        with io.open(fd, 'w', encoding='utf-8') as fp:
            fp.write(rv)
        shutil.move(path, src)
        log.info('update  %s', src)
    else:
        log.info('create  %s', dest)
Exemple #2
0
def run(conf, env, options):
    """Subcommand: new -- create a new blog entry the easy way.  Either run
    ``acrylamid new My fresh new Entry`` or interactively via ``acrylamid new``
    and the file will be created using the preferred permalink format."""

    # we need the actual defaults values
    commands.initialize(conf, env)

    ext = conf.get('content_extension', '.txt')
    fd, tmp = tempfile.mkstemp(suffix=ext, dir='.cache/')

    editor = os.getenv('VISUAL') if os.getenv('VISUAL') else os.getenv('EDITOR')
    tt = formats.get((conf.get('metastyle') == 'native', ext), yaml)

    if not options.title:
        options.title = raw_input("Entry's title: ")

    title = (' '.join(options.title))
    if not PY3:
        title = title.decode('utf-8')

    with io.open(fd, 'w') as f:
        f.write(tt(title, datetime.now().strftime(conf['date_format'])))

    entry = readers.Entry(tmp, conf)
    p = join(conf['content_dir'], dirname(entry.permalink)[1:])

    try:
        os.makedirs(p.rsplit('/', 1)[0])
    except OSError:
        pass

    filepath = p + ext
    if isfile(filepath):
        raise AcrylamidException('Entry already exists %r' % filepath)
    shutil.move(tmp, filepath)
    event.create(filepath)

    if datetime.now().hour == 23 and datetime.now().minute > 45:
        log.info("notice  consider editing entry.date-day after you passed mignight!")

    if log.level() >= log.WARN:
        return

    try:
        if editor:
            retcode = subprocess.call([editor, filepath])
        elif sys.platform == 'darwin':
            retcode = subprocess.call(['open', filepath])
        else:
            retcode = subprocess.call(['xdg-open', filepath])
    except OSError:
        raise AcrylamidException('Could not launch an editor')

    # XXX process detaches... m(
    if retcode < 0:
        raise AcrylamidException('Child was terminated by signal %i' % -retcode)

    if os.stat(filepath)[6] == 0:
        raise AcrylamidException('File is empty!')
Exemple #3
0
def autocompile(ws, conf, env, **options):
    """Subcommand: autocompile -- automatically re-compiles when something in
    content-dir has changed and parallel serving files."""

    CONF_PY = './conf.py'

    mtime = -1
    cmtime = getmtime(CONF_PY)

    while True:
        ntime = max(
            max(
                getmtime(e) for e in readers.filelist(conf['content_dir'])
                if utils.istext(e)),
            max(getmtime(p) for p in readers.filelist(conf['layout_dir'])))
        if mtime != ntime:
            try:
                compile(conf, env, **options)
            except AcrylamidException as e:
                log.fatal(e.args[0])
                pass
            event.reset()
            mtime = ntime

        if cmtime != getmtime(CONF_PY):
            log.info(' * Restarting due to change in %s' % (CONF_PY))
            # Kill the webserver
            ws.shutdown()
            # Force compilation since no template was changed
            argv = sys.argv if options['force'] else sys.argv[:] + ["--force"]
            # Restart acrylamid
            os.execvp(sys.argv[0], argv)

        time.sleep(1)
Exemple #4
0
def autocompile(ws, conf, env):
    """Subcommand: autocompile -- automatically re-compiles when something in
    content-dir has changed and parallel serving files."""

    mtime = -1
    cmtime = getmtime('conf.py')

    while True:

        ws.wait = True
        ntime = max(
            max(getmtime(e) for e in readers.filelist(
                conf['content_dir'], conf.get('content_ignore', [])) if istext(e)),
            max(getmtime(p) for p in readers.filelist(
                conf['theme'], conf.get('theme_ignore', []))))
        if mtime != ntime:
            try:
                compile(conf, env)
            except (SystemExit, KeyboardInterrupt) as e:
                raise e
            except Exception as e:
                log.fatal(e.args[0])
            event.reset()
            mtime = ntime
        ws.wait = False

        if cmtime != getmtime('conf.py'):
            log.info(' * Restarting due to change in conf.py')
            # Kill the webserver
            ws.shutdown()
            # Restart acrylamid
            os.execvp(sys.argv[0], sys.argv)

        time.sleep(1)
Exemple #5
0
def normalize(conf):

    # metastyle has been removed
    if 'metastyle' in conf:
        log.info('notice  METASTYLE is no longer needed to determine the metadata format ' + \
                 'and can be removed.')

    # deprecated since 0.8
    if isinstance(conf['static'], list):
        conf['static'] = conf['static'][0]
        log.warn("multiple static directories has been deprecated, " + \
                 "Acrylamid continues with '%s'.", conf['static'])

    # deprecated since 0.8
    for fx in 'Jinja2', 'Mako':
        try:
            conf['static_filter'].remove(fx)
        except ValueError:
            pass
        else:
            log.warn(
                "%s asset filter has been renamed to `Template` and is "
                "included by default.", fx)

    for key in 'content_dir', 'theme', 'static', 'output_dir':
        if conf[key] is not None and not conf[key].endswith('/'):
            conf[key] += '/'

    for key in 'views_dir', 'filters_dir':
        if isinstance(conf[key], compat.string_types):
            conf[key] = [
                conf[key],
            ]

    return conf
Exemple #6
0
def run(conf, env, options):
    """Subcommand: deploy -- run the shell command specified in
    DEPLOYMENT[task] using Popen. Each string value from :doc:`conf.py` is
    added to the execution environment. Every argument after ``acrylamid
    deploy task ARG1 ARG2`` is appended to cmd."""

    if options.list:
        for task in conf.get('deployment', {}).keys():
            print >>sys.stdout, task
        sys.exit(0)

    task, args = options.task or 'default', options.args
    cmd = conf.get('deployment', {}).get(task, None)

    if not cmd:
        raise AcrylamidException('no tasks named %r in conf.py' % task)

    # apply ARG1 ARG2 ... and -v --long-args to the command, e.g.:
    # $> acrylamid deploy task arg1 -b --foo
    cmd += ' ' + ' '.join(args)

    enc = sys.getfilesystemencoding()
    env = os.environ
    env.update(dict([(k.upper(), v.encode(enc, 'replace')) for k, v in conf.items() if isinstance(v, basestring)]))

    log.info('execute  %s', cmd)
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    while True:
        output = p.stdout.read(1)
        if output == '' and p.poll() != None:
            break
        if output != '':
            sys.stdout.write(output)
            sys.stdout.flush()
Exemple #7
0
def run(cmd, ns, src, dest=None):
    """Execute `cmd` such as `yui-compressor %1 -o %2` in-place.
    If `dest` is none, you don't have to supply %2."""

    assert '%1' in cmd
    cmd = cmd.replace('%1', src)

    if dest:
        assert '%2' in cmd
        cmd = cmd.replace('%2', dest)

        if not isdir(dirname(dest)):
            os.makedirs(dirname(dest))

    try:
        rv = system(cmd, shell=True)
    except (AcrylamidException, OSError):
        log.exception("uncaught exception during execution")
        return

    if dest is None:
        fd, path = mkstemp()
        with io.open(fd, 'w', encoding='utf-8') as fp:
            fp.write(rv)
        shutil.move(path, src)
        log.info('update  %s', src)
    else:
        log.info('create  %s', dest)
Exemple #8
0
def autocompile(ws, conf, env, **options):
    """Subcommand: autocompile -- automatically re-compiles when something in
    content-dir has changed and parallel serving files."""

    CONF_PY = './conf.py'

    mtime = -1
    cmtime = getmtime(CONF_PY)

    while True:
        ntime = max(
            max(getmtime(e) for e in readers.filelist(conf['content_dir']) if utils.istext(e)),
            max(getmtime(p) for p in readers.filelist(conf['layout_dir'])))
        if mtime != ntime:
            try:
                compile(conf, env, **options)
            except AcrylamidException as e:
                log.fatal(e.args[0])
                pass
            event.reset()
            mtime = ntime

        if cmtime != getmtime(CONF_PY):
            log.info(' * Restarting due to change in %s' % (CONF_PY))
            # Kill the webserver
            ws.shutdown()
            # Force compilation since no template was changed
            argv = sys.argv if options['force'] else sys.argv[:] + ["--force"]
            # Restart acrylamid
            os.execvp(sys.argv[0], argv)

        time.sleep(1)
Exemple #9
0
def deploy(conf, env, task, *args):
    """Subcommand: deploy -- run the shell command specified in DEPLOYMENT[task] using
    Popen. Use ``%s`` inside your command to let acrylamid substitute ``%s`` with the
    output path, if no ``%s`` is set, the path is appended  as first argument. Every
    argument after ``acrylamid deploy task ARG1 ARG2`` is appended to cmd."""

    cmd = conf.get('deployment', {}).get(task, None)
    if not cmd:
        raise AcrylamidException('no tasks named %r in conf.py' % task)

    if '%s' in cmd:
        cmd = cmd.replace('%s', conf['output_dir'])
    else:
        # append output-string
        cmd += ' ' + conf['output_dir']

    # apply ARG1 ARG2 ... and -v --long-args to the command, e.g.:
    # $> acrylamid deploy task arg1 -- -b --foo
    cmd += ' ' + ' '.join(args)

    log.info('execute %s', cmd)
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    while True:
        output = p.stdout.read(1)
        if output == '' and p.poll() != None:
            break
        if output != '':
            sys.stdout.write(output)
            sys.stdout.flush()
Exemple #10
0
def normalize(conf):

    # metastyle has been removed
    if 'metastyle' in conf:
        log.info('notice  METASTYLE is no longer needed to determine the metadata format ' + \
                 'and can be removed.')

    # deprecated since 0.8
    if isinstance(conf['static'], list):
        conf['static'] = conf['static'][0]
        log.warn("multiple static directories has been deprecated, " + \
                 "Acrylamid continues with '%s'.", conf['static'])

    # deprecated since 0.8
    for fx in 'Jinja2', 'Mako':
        try:
            conf['static_filter'].remove(fx)
        except ValueError:
            pass
        else:
            log.warn("%s asset filter has been renamed to `Template` and is "
                     "included by default.", fx)


    for key in 'content_dir', 'theme', 'static', 'output_dir':
        if conf[key] is not None and not conf[key].endswith('/'):
            conf[key] += '/'

    for key in 'views_dir', 'filters_dir':
        if isinstance(conf[key], compat.string_types):
            conf[key] = [conf[key], ]

    return conf
Exemple #11
0
def new(conf, env, title, prompt=True):
    """Subcommand: new -- create a new blog entry the easy way.  Either run
    ``acrylamid new My fresh new Entry`` or interactively via ``acrylamid new``
    and the file will be created using the preferred permalink format."""

    # we need the actual defaults values
    initialize(conf, env)

    fd, tmp = tempfile.mkstemp(suffix='.txt', dir='.cache/')
    editor = os.getenv('VISUAL') if os.getenv('VISUAL') else os.getenv('EDITOR')

    if not title:
        title = raw_input("Entry's title: ")
    title = escape(title)

    with io.open(fd, 'w') as f:
        f.write(u'---\n')
        f.write(u'title: %s\n' % title)
        f.write(u'date: %s\n' % datetime.now().strftime(conf['date_format']))
        f.write(u'---\n\n')

    entry = readers.Entry(tmp, conf)
    p = join(conf['content_dir'], dirname(entry.permalink)[1:])

    try:
        os.makedirs(p.rsplit('/', 1)[0])
    except OSError:
        pass

    filepath = p + '.txt'
    if isfile(filepath):
        raise AcrylamidException('Entry already exists %r' % filepath)
    shutil.move(tmp, filepath)
    event.create(filepath)

    if datetime.now().hour == 23 and datetime.now().minute > 45:
        log.info("notice  consider editing entry.date-day after you passed mignight!")

    if not prompt:
        return

    try:
        if editor:
            retcode = subprocess.call([editor, filepath])
        elif sys.platform == 'darwin':
            retcode = subprocess.call(['open', filepath])
        else:
            retcode = subprocess.call(['xdg-open', filepath])
    except OSError:
        raise AcrylamidException('Could not launch an editor')

    # XXX process detaches... m(
    if retcode < 0:
        raise AcrylamidException('Child was terminated by signal %i' % -retcode)

    if os.stat(filepath)[6] == 0:
        raise AcrylamidException('File is empty!')
Exemple #12
0
def init(env, options):
    """Subcommand: init -- create the base structure of an Acrylamid blog
    or restore individual files and folders.

    If the destination directory is empty, it will create a new blog. If the
    destination  directory is not empty it won't overwrite anything unless
    you supply -f, --force to re-initialize the whole theme.

    If you need to restore a single file, run

        $ cd blog && acrylamid init theme/main.html
    """

    if not options.engine:
        try:
            import jinja2

            options.engine = "jinja2"
        except ImportError:
            options.engine = "mako"

    theme, files = rollout(options.theme, options.engine)

    # if destination is part of theme, restore it!
    for src, dest in resolve(options, theme, files):
        if dest.endswith(options.dest):
            if (
                not isfile(options.dest)
                or options.overwrite
                or raw_input("re-initialize %s ? [yn]: " % options.dest) == "y"
            ):
                write(src, options.dest)
                log.info("re-initialized  " + options.dest)
                return

    if isfile(options.dest):
        raise AcrylamidException("%s already exists!" % options.dest)

    if isdir(options.dest) and len(os.listdir(options.dest)) > 0 and not options.overwrite:
        if raw_input("Destination directory not empty! Continue? [yn]: ") != "y":
            sys.exit(1)

    for src, dest in resolve(options, theme, files):

        if not isdir(dirname(dest)):
            os.makedirs(dirname(dest))

        if options.overwrite or not exists(dest):
            write(src, dest)
            event.create(dest)
        else:
            event.skip(dest)

    log.info("Created your fresh new blog at %r. Enjoy!", options.dest)
Exemple #13
0
    def create(directory, path):
        """A shortcut for check if exists and shutil.copy to."""

        dest = join(root, directory, basename(path))
        if not isfile(dest) or options.overwrite == True:
            try:
                shutil.copy(path, dest)
                log.info('create  %s', dest)
            except IOError as e:
                log.fatal(unicode(e))
        else:
            log.info('skip  %s already exists', dest)
Exemple #14
0
    def create(directory, path):
        """A shortcut for check if exists and shutil.copy to."""

        dest = join(root, directory, basename(path))
        if not isfile(dest) or options.overwrite == True:
            try:
                shutil.copy(path, dest)
                log.info('create  %s', dest)
            except IOError as e:
                log.fatal(unicode(e))
        else:
            log.info('skip  %s already exists', dest)
Exemple #15
0
def parse(content):

    failed = []
    for method in (wp, rss20, atom):
        try:
            res = method(content)
            return next(res), res
        except ImportError:
            log.info('notice  BeautifulSoup is required for WordPress import')
        except InvalidSource as e:
            failed.append(e.args[0])
    else:
        raise AcrylamidException('unable to parse source')
Exemple #16
0
def parse(content):

    failed = []
    for method in (wp, rss20, atom):
        try:
            res = method(content)
            return next(res), res
        except ImportError:
            log.info('notice  BeautifulSoup is required for WordPress import')
        except InvalidSource as e:
            failed.append(e.args[0])
    else:
        raise AcrylamidException('unable to parse source')
Exemple #17
0
def autocompile(ws, conf, env):
    """Subcommand: autocompile -- automatically re-compiles when something in
    content-dir has changed and parallel serving files."""

    mtime = -1
    cmtime = getmtime('conf.py')

    # config content_extension originally defined as string, not a list
    exts = conf.get('content_extension', ['.txt', '.rst', '.md'])
    if isinstance(exts, string_types):
        whitelist = (exts, )
    else:
        whitelist = tuple(exts)

    while True:

        ws.wait = True
        ntime = max(
            max(
                getmtime(e) for e in readers.filelist(conf['content_dir'],
                                                      conf['content_ignore'])
                if e.endswith(whitelist)),
            max(
                getmtime(p) for p in chain([
                    f for theme in conf['theme']
                    for f in readers.filelist(theme, conf['theme_ignore'])
                ], readers.filelist(conf['static'], conf['static_ignore']))))

        if mtime != ntime:
            try:
                compile(conf, env)
            except (SystemExit, KeyboardInterrupt):
                raise
            except Exception:
                log.exception("uncaught exception during auto-compilation")
            else:
                conf = load(env.options.conf)
                env = Environment.new(env)
            event.reset()
            mtime = ntime
        ws.wait = False

        if cmtime != getmtime('conf.py'):
            log.info(' * Restarting due to change in conf.py')
            # Kill the webserver
            ws.shutdown()
            # Restart acrylamid
            os.execvp(sys.argv[0], sys.argv)

        time.sleep(1)
Exemple #18
0
def autocompile(ws, conf, env):
    """Subcommand: autocompile -- automatically re-compiles when something in
    content-dir has changed and parallel serving files."""

    mtime = -1
    cmtime = getmtime('conf.py')

    # config content_extension originally defined as string, not a list
    exts = conf.get('content_extension',['.txt', '.rst', '.md'])
    if isinstance(exts, string_types):
        whitelist = (exts,)
    else:
        whitelist = tuple(exts)

    while True:

        ws.wait = True
        ntime = max(
            max(getmtime(e) for e in readers.filelist(
                conf['content_dir'], conf['content_ignore']) if e.endswith(whitelist)),
            max(getmtime(p) for p in chain(
                readers.filelist(conf['theme'], conf['theme_ignore']),
                readers.filelist(conf['static'], conf['static_ignore']))))

        if mtime != ntime:
            try:
                compile(conf, env)
            except (SystemExit, KeyboardInterrupt):
                raise
            except Exception:
                log.exception("uncaught exception during auto-compilation")
            else:
                conf = load(env.options.conf)
                env = Environment.new(env)
            event.reset()
            mtime = ntime
        ws.wait = False

        if cmtime != getmtime('conf.py'):
            log.info(' * Restarting due to change in conf.py')
            # Kill the webserver
            ws.shutdown()
            # Restart acrylamid
            os.execvp(sys.argv[0], sys.argv)

        time.sleep(1)
Exemple #19
0
def autocompile(ws, conf, env):
    """Subcommand: autocompile -- automatically re-compiles when something in
    content-dir has changed and parallel serving files."""

    mtime = -1
    cmtime = getmtime("conf.py")

    while True:

        ws.wait = True
        ntime = max(
            max(getmtime(e) for e in readers.filelist(conf["content_dir"], conf["content_ignore"]) if istext(e)),
            max(
                getmtime(p)
                for p in chain(
                    readers.filelist(conf["theme"], conf["theme_ignore"]),
                    readers.filelist(conf["static"], conf["static_ignore"]),
                )
            ),
        )

        if mtime != ntime:
            try:
                compile(conf, env)
            except (SystemExit, KeyboardInterrupt):
                raise
            except Exception:
                log.exception("uncaught exception during auto-compilation")
            else:
                conf = load(env.options.conf)
                env = Environment.new(env)
            event.reset()
            mtime = ntime
        ws.wait = False

        if cmtime != getmtime("conf.py"):
            log.info(" * Restarting due to change in conf.py")
            # Kill the webserver
            ws.shutdown()
            # Restart acrylamid
            os.execvp(sys.argv[0], sys.argv)

        time.sleep(1)
Exemple #20
0
def run(conf, env, options):
    """Subcommand: deploy -- run the shell command specified in
    DEPLOYMENT[task] using Popen. Each string value from :doc:`conf.py` is
    added to the execution environment. Every argument after ``acrylamid
    deploy task ARG1 ARG2`` is appended to cmd."""

    if options.task is None:
        for task in conf.get('deployment', {}).keys():
            print >> sys.stdout, task
        sys.exit(0)

    task, args = options.task, options.args
    cmd = conf.get('deployment', {}).get(task, None)

    if not cmd:
        raise AcrylamidException('no tasks named %r in conf.py' % task)

    # apply ARG1 ARG2 ... and -v --long-args to the command, e.g.:
    # $> acrylamid deploy task arg1 -b --foo
    cmd += ' ' + ' '.join(args)

    if '%s' in cmd:
        log.warn("'%s' syntax is deprecated, use $OUTPUT_DIR variable.")
        cmd = cmd.replace('%s', '$OUTPUT_DIR')

    env = os.environ
    env.update(
        dict([(k.upper(), v) for k, v in conf.items()
              if isinstance(v, basestring)]))

    log.info('execute  %s', cmd)
    p = subprocess.Popen(cmd,
                         shell=True,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)

    while True:
        output = p.stdout.read(1)
        if output == '' and p.poll() != None:
            break
        if output != '':
            sys.stdout.write(output)
            sys.stdout.flush()
Exemple #21
0
    def init(self, cache_dir=None, mode=0600):
        """Initialize cache object by creating the cache_dir if non-existent,
        read all available cache objects and restore memoized key/values.

        :param cache_dir: the directory where cache files are stored.
        :param mode: the file mode wanted for the cache files, default 0600
        """
        if cache_dir:
            self.cache_dir = cache_dir
        if mode:
            self.mode = mode
        if not exists(self.cache_dir):
            try:
                os.mkdir(self.cache_dir, 0700)
            except OSError:
                raise AcrylamidException("could not create directory '%s'" %
                                         self.cache_dir)

        # get all cache objects
        for path in self._list_dir():
            try:
                with io.open(path, 'rb') as fp:
                    self.objects[path] = set(pickle.load(fp).keys())
            except pickle.PickleError:
                os.remove(path)
            except (AttributeError, EOFError):
                # this may happen after a refactor
                log.info('notice  invalid cache objects')
                for obj in self._list_dir():
                    cache.remove(obj)
                break
            except IOError:
                continue

        # load memorized items
        try:
            with io.open(join(cache.cache_dir, 'info'), 'rb') as fp:
                cache.memoize.update(pickle.load(fp))
        except (IOError, pickle.PickleError):
            pass
Exemple #22
0
def run(conf, env, options):
    """Subcommand: deploy -- run the shell command specified in
    DEPLOYMENT[task] using Popen. Each string value from :doc:`conf.py` is
    added to the execution environment. Every argument after ``acrylamid
    deploy task ARG1 ARG2`` is appended to cmd."""

    if options.list:
        for task in iterkeys(conf.get('deployment', {})):
            print(task)
        sys.exit(0)

    task, args = options.task or 'default', options.args
    cmd = conf.get('deployment', {}).get(task, None)

    if not cmd:
        raise AcrylamidException('no tasks named %r in conf.py' % task)

    # apply ARG1 ARG2 ... and -v --long-args to the command, e.g.:
    # $> acrylamid deploy task arg1 -b --foo
    cmd += ' ' + ' '.join(args)

    enc = sys.getfilesystemencoding()
    env = os.environ
    env.update(
        dict([(k.upper(), v.encode(enc, 'replace') if PY2K else v)
              for k, v in iteritems(conf) if isinstance(v, string_types)]))

    log.info('execute  %s', cmd)
    p = subprocess.Popen(cmd,
                         shell=True,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)

    while True:
        output = p.stdout.read(1)
        if output == b'' and p.poll() != None:
            break
        if output != b'':
            sys.stdout.write(output.decode(enc))
            sys.stdout.flush()
Exemple #23
0
    def init(self, cache_dir=None, mode=0600):
        """Initialize cache object by creating the cache_dir if non-existent,
        read all available cache objects and restore memoized key/values.

        :param cache_dir: the directory where cache files are stored.
        :param mode: the file mode wanted for the cache files, default 0600
        """
        if cache_dir:
            self.cache_dir = cache_dir
        if mode:
            self.mode = mode
        if not exists(self.cache_dir):
            try:
                os.mkdir(self.cache_dir, 0700)
            except OSError:
                raise AcrylamidException("could not create directory '%s'" %
                                         self.cache_dir)

        # get all cache objects
        for path in self._list_dir():
            try:
                with io.open(path, 'rb') as fp:
                    self.objects[path] = set(pickle.load(fp).keys())
            except pickle.PickleError:
                os.remove(path)
            except (AttributeError, EOFError):
                # this may happen after a refactor
                log.info('notice  stale cache objects')
                for obj in self._list_dir():
                    cache.remove(obj)
                break
            except IOError:
                continue

        # load memorized items
        try:
            with io.open(join(cache.cache_dir, 'info'), 'rb') as fp:
                cache.memoize.update(pickle.load(fp))
        except (IOError, pickle.PickleError):
            pass
Exemple #24
0
def normalize(conf):

    # metastyle has been removed
    if "metastyle" in conf:
        log.info("notice  METASTYLE is no longer needed to determine the metadata format " + "and can be removed.")

    # deprecated since 0.8
    if isinstance(conf["static"], list):
        conf["static"] = conf["static"][0]
        log.warn("multiple static directories has been deprecated, " + "Acrylamid continues with '%s'.", conf["static"])

    # deprecated since 0.8
    for fx in "Jinja2", "Mako":
        try:
            conf["static_filter"].remove(fx)
        except ValueError:
            pass
        else:
            log.warn("%s asset filter has been renamed to `Template` and is " "included by default.", fx)

    if not isinstance(conf["theme"], list):
        conf["theme"] = [conf["theme"]]

    for i, path in enumerate(conf["theme"]):
        if not path.endswith("/"):
            conf["theme"][i] = path + "/"

    for key in "content_dir", "static", "output_dir":
        if conf[key] is not None and not conf[key].endswith("/"):
            conf[key] += "/"

    for key in "views_dir", "filters_dir":
        if isinstance(conf[key], compat.string_types):
            conf[key] = [conf[key]]

    return conf
Exemple #25
0
 def update(self, path, ctime=None):
     if ctime:
         log.info("update  [%.2fs] %s", ctime, path)
     else:
         log.info("update  %s", path)
Exemple #26
0
def initialize(conf, env):
    """Initializes Jinja2 environment, prepares locale and configure
    some minor things. Filter and View are inited with conf and env,
    a data dict is returned.
    """
    # initialize cache, optional to cache_dir
    cache.init(conf.get("cache_dir"))

    env["version"] = type("Version", (str,), dict(zip(["major", "minor"], LooseVersion(dist.version).version[:2])))(
        dist.version
    )

    # crawl through CHANGES.md and stop on breaking changes
    if history.breaks(env, cache.emptyrun):
        cache.shutdown()
        print "Detected version upgrade that might break your configuration. Run"
        print "Acrylamid a second time to get rid of this message and premature exit."
        raise SystemExit

    # register hooks
    if env.options.parser.startswith(("co", "gen", "auto", "aco")):
        hooks.initialize(conf, env)

    # set up templating environment
    env.engine = import_object(conf["engine"])()

    env.engine.init(conf["theme"], cache.cache_dir)
    env.engine.register("safeslug", helpers.safeslug)
    env.engine.register("tagify", lambda x: x)

    # try language set in LANG, if set correctly use it
    try:
        locale.setlocale(locale.LC_ALL, str(conf.get("lang", "")))
    except (locale.Error, TypeError):
        # try if LANG is an alias
        try:
            locale.setlocale(locale.LC_ALL, locale.locale_alias[str(conf.get("lang", "")).lower()])
        except (locale.Error, KeyError):
            # LANG is not an alias, so we use system's default
            try:
                locale.setlocale(locale.LC_ALL, "")
            except locale.Error:
                pass  # hope this makes Travis happy
            log.info("notice  your OS does not support %s, fallback to %s", conf.get("lang", ""), locale.getlocale()[0])
    if locale.getlocale()[0] is not None:
        conf["lang"] = locale.getlocale()[0][:2]
    else:
        # getlocale() is (None, None) aka 'C'
        conf["lang"] = "en"

    if "www_root" not in conf:
        log.warn("no `www_root` specified, using localhost:8000")
        conf["www_root"] = "http://localhost:8000/"

    # figure out timezone and set offset, more verbose for 2.6 compatibility
    td = datetime.now() - datetime.utcnow()
    offset = round(total_seconds(td) / 3600.0)
    conf["tzinfo"] = readers.Timezone(offset)

    # determine http(s), host and path
    env["protocol"], env["netloc"], env["path"], x, y = urlsplit(conf["www_root"])

    # take off the trailing slash for www_root and path
    conf["www_root"] = conf["www_root"].rstrip("/")
    env["path"] = env["path"].rstrip("/")

    if env["path"]:
        conf["output_dir"] = conf["output_dir"] + env["path"]

    lazy.enable()
    filters.initialize(conf["filters_dir"][:], conf, env)
    lazy.disable()  # this has weird side effects with jinja2, so disabled after filters

    views.initialize(conf["views_dir"][:], conf, env)
    env.views = views.Views(view for view in views.get_views())

    entryfmt, pagefmt = "/:year/:slug/", "/:slug/"
    for view in views.get_views():
        if view.name == "entry":
            entryfmt = view.path
        if view.name == "page":
            pagefmt = view.path

    conf.setdefault("entry_permalink", entryfmt)
    conf.setdefault("page_permalink", pagefmt)

    return {"conf": conf, "env": env}
Exemple #27
0
def compile(conf, env):
    """The compilation process."""

    if env.options.force:
        cache.clear(conf.get('cache_dir'))

    # time measurement
    ctime = time.time()

    # populate env and corrects some conf things
    data = initialize(conf, env)

    # load pages/entries and store them in env
    rv = dict(zip(['entrylist', 'pages', 'translations', 'drafts'],
        map(HashableList, readers.load(conf))))

    entrylist, pages = rv['entrylist'], rv['pages']
    translations, drafts = rv['translations'], rv['drafts']

    # load references
    refs.load(entrylist, pages, translations, drafts)

    data.update(rv)
    env.globals.update(rv)

    # here we store all found filter and their aliases
    ns = defaultdict(set)

    # get available filter list, something like with obj.get-function
    # list = [<class head_offset.Headoffset at 0x1014882c0>, <class html.HTML at 0x101488328>,...]
    aflist = filters.get_filters()

    # ... and get all configured views
    _views = views.get_views()

    # filters found in all entries, views and conf.py (skip translations, has no items)
    found = sum((x.filters for x in chain(entrylist, pages, drafts, _views, [conf])), [])

    for val in found:
        # first we for `no` and get the function name and arguments
        f = val[2:] if val.startswith('no') else val
        fname, fargs = f.split('+')[:1][0], f.split('+')[1:]

        try:
            # initialize the filter with its function name and arguments
            fx = aflist[fname](conf, env, val, *fargs)
            if val.startswith('no'):
                fx = filters.disable(fx)
        except ValueError:
            try:
                fx = aflist[val.split('+')[:1][0]](conf, env, val, *fargs)
            except ValueError:
                raise AcrylamidException('no such filter: %s' % val)

        ns[fx].add(val)

    for entry in chain(entrylist, pages, drafts):
        for v in _views:

            # a list that sorts out conflicting and duplicated filters
            flst = filters.FilterList()

            # filters found in this specific entry plus views and conf.py
            found = entry.filters + v.filters + data['conf']['filters']

            for fn in found:
                fx, _ = next((k for k in ns.iteritems() if fn in k[1]))
                if fx not in flst:
                    flst.append(fx)

            # sort them ascending because we will pop within filters.add
            entry.filters.add(sorted(flst, key=lambda k: (-k.priority, k.name)),
                              context=v)

    # lets offer a last break to populate tags and such
    for v in _views:
        env = v.context(conf, env, data)

    # now teh real thing!
    for v in _views:

        for entry in chain(entrylist, pages, translations, drafts):
            entry.context = v

        for var in 'entrylist', 'pages', 'translations', 'drafts':
            data[var] = HashableList(filter(v.condition, locals()[var])) \
                if v.condition else locals()[var]

        tt = time.time()
        for buf, path in v.generate(conf, env, data):
            try:
                helpers.mkfile(buf, path, time.time()-tt, **env.options.__dict__)
            finally:
                buf.close()
            tt = time.time()

    # copy modified/missing assets to output
    assets.compile(conf, env)

    # save conf/environment hash and new/changed/unchanged references
    helpers.memoize('Configuration', hash(conf))
    helpers.memoize('Environment', hash(env))
    refs.save()

    # remove abandoned cache files
    cache.shutdown()

    # print a short summary
    log.info('%i new, %i updated, %i skipped [%.2fs]', event.count('create'),
             event.count('update'), event.count('identical') + event.count('skip'),
             time.time() - ctime)
Exemple #28
0
def compile(conf, env):
    """The compilation process."""

    hooks.initialize(conf, env)
    hooks.run(conf, env, 'pre')

    if env.options.force:
        cache.clear(conf.get('cache_dir'))

    # time measurement
    ctime = time.time()

    # populate env and corrects some conf things
    data = initialize(conf, env)

    # load pages/entries and store them in env
    rv = dict(
        zip(['entrylist', 'pages', 'translations', 'drafts'],
            map(HashableList, readers.load(conf))))

    entrylist, pages = rv['entrylist'], rv['pages']
    translations, drafts = rv['translations'], rv['drafts']

    # load references
    refs.load(entrylist, pages, translations, drafts)

    data.update(rv)
    env.globals.update(rv)

    # here we store all found filter and their aliases
    ns = defaultdict(set)

    # [<class head_offset.Headoffset at 0x1014882c0>, <class html.HTML at 0x101488328>,...]
    aflist = filters.get_filters()

    # ... and get all configured views
    _views = views.get_views()

    # filters found in all entries, views and conf.py (skip translations, has no items)
    found = sum((x.filters
                 for x in chain(entrylist, pages, drafts, _views, [conf])), [])

    for val in found:
        # first we for `no` and get the function name and arguments
        f = val[2:] if val.startswith('no') else val
        fname, fargs = f.split('+')[:1][0], f.split('+')[1:]

        try:
            # initialize the filter with its function name and arguments
            fx = aflist[fname](conf, env, val, *fargs)
            if val.startswith('no'):
                fx = filters.disable(fx)
        except ValueError:
            try:
                fx = aflist[val.split('+')[:1][0]](conf, env, val, *fargs)
            except ValueError:
                raise AcrylamidException('no such filter: %s' % val)

        ns[fx].add(val)

    # include actual used filters to trigger modified state
    env.filters = HashableList(iterkeys(ns))

    for entry in chain(entrylist, pages, drafts):
        for v in _views:

            # a list that sorts out conflicting and duplicated filters
            flst = filters.FilterList()

            # filters found in this specific entry plus views and conf.py
            found = entry.filters + v.filters + data['conf']['filters']

            for fn in found:
                fx, _ = next((k for k in iteritems(ns) if fn in k[1]))
                if fx not in flst:
                    flst.append(fx)

            # sort them ascending because we will pop within filters.add
            entry.filters.add(sorted(flst, key=lambda k:
                                     (-k.priority, k.name)),
                              context=v)

    # lets offer a last break to populate tags and such
    for v in _views:
        env = v.context(conf, env, data)

    # now teh real thing!
    for v in _views:

        for entry in chain(entrylist, pages, translations, drafts):
            entry.context = v

        for var in 'entrylist', 'pages', 'translations', 'drafts':
            data[var] = HashableList(filter(v.condition, locals()[var])) \
                if v.condition else locals()[var]

        tt = time.time()
        for buf, path in v.generate(conf, env, data):
            try:
                helpers.mkfile(buf,
                               path,
                               time.time() - tt,
                               ns=v.name,
                               force=env.options.force,
                               dryrun=env.options.dryrun)
            except UnicodeError:
                log.exception(path)
            finally:
                buf.close()
            tt = time.time()

    # copy modified/missing assets to output
    assets.compile(conf, env)

    # wait for unfinished hooks
    hooks.shutdown()

    # run post hooks (blocks)
    hooks.run(conf, env, 'post')

    # save conf/environment hash and new/changed/unchanged references
    helpers.memoize('Configuration', hash(conf))
    helpers.memoize('Environment', hash(env))
    refs.save()

    # remove abandoned cache files
    cache.shutdown()

    # print a short summary
    log.info('%i new, %i updated, %i skipped [%.2fs]', event.count('create'),
             event.count('update'),
             event.count('identical') + event.count('skip'),
             time.time() - ctime)
Exemple #29
0
def new(conf, env, title, prompt=True):
    """Subcommand: new -- create a new blog entry the easy way.  Either run
    ``acrylamid new My fresh new Entry`` or interactively via ``acrylamid new``
    and the file will be created using the preferred permalink format."""

    # we need the actual defaults values
    initialize(conf, env)

    fd, tmp = tempfile.mkstemp(suffix='.txt', dir='.cache/')
    editor = os.getenv('VISUAL') if os.getenv('VISUAL') else os.getenv(
        'EDITOR')

    if not title:
        title = raw_input("Entry's title: ")
    title = safe(title)

    with io.open(fd, 'w') as f:
        f.write(u'---\n')
        f.write(u'title: %s\n' % title)
        f.write(u'date: %s\n' % datetime.now().strftime(conf['date_format']))
        f.write(u'---\n\n')

    entry = readers.Entry(tmp, conf)
    p = join(conf['content_dir'], dirname(entry.permalink)[1:])

    try:
        os.makedirs(p.rsplit('/', 1)[0])
    except OSError:
        pass

    filepath = p + '.txt'
    if isfile(filepath):
        raise AcrylamidException('Entry already exists %r' % filepath)
    shutil.move(tmp, filepath)
    event.create(filepath)

    if datetime.now().hour == 23 and datetime.now().minute > 45:
        log.info(
            "notice  consider editing entry.date-day after you passed mignight!"
        )

    if not prompt:
        return

    try:
        if editor:
            retcode = subprocess.call([editor, filepath])
        elif sys.platform == 'darwin':
            retcode = subprocess.call(['open', filepath])
        else:
            retcode = subprocess.call(['xdg-open', filepath])
    except OSError:
        raise AcrylamidException('Could not launch an editor')

    # XXX process detaches... m(
    if retcode < 0:
        raise AcrylamidException('Child was terminated by signal %i' %
                                 -retcode)

    if os.stat(filepath)[6] == 0:
        raise AcrylamidException('File is empty!')
Exemple #30
0
def run(conf, env, options):
    """Subcommand: new -- create a new blog entry the easy way.  Either run
    ``acrylamid new My fresh new Entry`` or interactively via ``acrylamid new``
    and the file will be created using the preferred permalink format."""

    # we need the actual default values
    commands.initialize(conf, env)

    # config content_extension originally defined as string, not a list
    extlist = conf.get('content_extension', ['.txt'])
    if isinstance(extlist, string_types):
        ext = extlist
    else:
        ext = extlist[0]

    fd, tmp = tempfile.mkstemp(suffix=ext, dir='.cache/')

    editor = os.getenv('VISUAL') if os.getenv('VISUAL') else os.getenv(
        'EDITOR')
    tt = formats.get(ext, yaml)

    if options.title:
        title = u(' '.join(options.title))
    else:
        title = u(input("Entry's title: "))

    with io.open(fd, 'w', encoding='utf-8') as f:
        f.write(tt(title, datetime.now().strftime(conf['date_format'])))

    entry = readers.Entry(tmp, conf)
    p = join(conf['content_dir'], splitext(entry.permalink.strip('/'))[0])

    try:
        os.makedirs(p.rsplit('/', 1)[0])
    except OSError:
        pass

    filepath = p + ext
    if isfile(filepath):
        raise AcrylamidException('Entry already exists %r' % filepath)
    shutil.move(tmp, filepath)
    event.create('new', filepath)

    if datetime.now().hour == 23 and datetime.now().minute > 45:
        log.info(
            "notice  don't forget to update entry.date-day after mignight!")

    if log.level() >= log.WARN:
        return

    try:
        if editor:
            retcode = subprocess.call(shlex.split(editor) + [filepath])
        elif sys.platform == 'darwin':
            retcode = subprocess.call(['open', filepath])
        else:
            retcode = subprocess.call(['xdg-open', filepath])
    except OSError:
        raise AcrylamidException('Could not launch an editor')

    # XXX process detaches... m(
    if retcode < 0:
        raise AcrylamidException('Child was terminated by signal %i' %
                                 -retcode)

    if os.stat(filepath)[6] == 0:
        raise AcrylamidException('File is empty!')
Exemple #31
0
def compile(conf, env, force=False, **options):
    """The compilation process.

    Current API:

        #. when we require context
        #. when we called an event

    New API:

        #. before we start with view Initialization
        #. after we initialized views
        #. before we require context
        #. after we required context
        #. before we template
        #. before we write a file
        #. when we called an event
        #. when we finish
    """

    # time measurement
    ctime = time.time()

    # populate env and corrects some conf things
    request = initialize(conf, env)

    # load pages/entries and store them in env
    entrylist, pages = readers.load(conf)
    env.globals['entrylist'] = entrylist
    env.globals['pages'] = pages

    # XXX translations should be moved out of core
    env.globals['translations'] = translations = []

    if force:
        # acrylamid compile -f
        cache.clear()

    # here we store all found filter and their aliases
    ns = defaultdict(set)

    # get available filter list, something like with obj.get-function
    # list = [<class head_offset.Headoffset at 0x1014882c0>, <class html.HTML at 0x101488328>,...]
    aflist = filters.get_filters()

    # ... and get all configured views
    _views = views.get_views()

    # filters found in all entries, views and conf.py
    found = sum((x.filters for x in entrylist+pages+_views), []) + request['conf']['filters']

    for val in found:
        # first we for `no` and get the function name and arguments
        f = val[2:] if val.startswith('no') else val
        fname, fargs = f.split('+')[:1][0], f.split('+')[1:]

        try:
            # initialize the filter with its function name and arguments
            fx = aflist[fname](conf, env, val, *fargs)
            if val.startswith('no'):
                fx = filters.disable(fx)
        except ValueError:
            try:
                fx = aflist[val.split('+')[:1][0]](conf, env, val, *fargs)
            except ValueError:
                raise AcrylamidException('no such filter: %s' % val)

        ns[fx].add(val)

    for entry in entrylist + pages:
        for v in _views:

            # a list that sorts out conflicting and duplicated filters
            flst = filters.FilterList()

            # filters found in this specific entry plus views and conf.py
            found = entry.filters + v.filters + request['conf']['filters']

            for fn in found:
                fx, _ = next((k for k in ns.iteritems() if fn in k[1]))
                if fx not in flst:
                    flst.append(fx)

            # sort them ascending because we will pop within filters.add
            entry.filters.add(sorted(flst, key=lambda k: (-k.priority, k.name)),
                              context=v.__class__.__name__)

    # lets offer a last break to populate tags or so
    # XXX this API component needs a review
    for v in _views:
        env = v.context(env, {'entrylist': entrylist, 'pages': pages,
                              'translations': translations})

    # now teh real thing!
    for v in _views:

        # XXX the entry should automatically determine its caller (using
        # some sys magic to recursively check wether the calling class is
        # derieved from `View`.)
        for entry in entrylist + pages + translations:
            entry.context = v.__class__.__name__

        request['pages'], request['translations'] = pages, translations
        request['entrylist'] = filter(v.condition, entrylist)
        tt = time.time()

        for html, path in v.generate(request):
            helpers.mkfile(html, path, time.time()-tt, **options)
            tt = time.time()

    # remove abandoned cache files
    cache.shutdown()

    # print a short summary
    log.info('%i new, %i updated, %i skipped [%.2fs]', event.count('create'),
             event.count('update'), event.count('identical') + event.count('skip'),
             time.time() - ctime)
Exemple #32
0
 def update(self, path, ctime=None):
     """:param path: path\n:param ctime: computing time"""
     if ctime:
         log.info("update  [%.2fs] %s", ctime, path)
     else:
         log.info("update  %s", path)
Exemple #33
0
 def remove(self, path):
     """:param path: path"""
     log.info("remove  %s", path)
Exemple #34
0
 def remove(self, path):
     """:param path: path"""
     log.info("remove  %s", path)
Exemple #35
0
def init(env, options):
    """Subcommand: init -- creates the base structure of an Acrylamid blog
    or restores individual files."""

    root = options.dest

    if not options.engine:
        try:
            import jinja2
            options.engine = 'jinja2'
        except ImportError:
            options.engine = 'mako'

    def create(directory, path):
        """A shortcut for check if exists and shutil.copy to."""

        dest = join(root, directory, basename(path))
        if not isfile(dest) or options.overwrite == True:
            try:
                shutil.copy(path, dest)
                log.info('create  %s', dest)
            except IOError as e:
                log.fatal(unicode(e))
        else:
            log.info('skip  %s already exists', dest)

    default = defaults.conf

    default['output_dir'] = default['output_dir'].rstrip('/')
    default['content_dir'] = default['content_dir'].rstrip('/')
    default['layout_dir'] = default['layout_dir'].rstrip('/')

    dirs = [
        '%(content_dir)s/', '%(layout_dir)s/', '%(output_dir)s/', '.cache/'
    ]

    files = [
        p % {
            'engine': options.engine,
            'theme': options.theme
        } for p in [
            '%(engine)s/%(theme)s/base.html', '%(engine)s/%(theme)s/main.html',
            '%(engine)s/%(theme)s/entry.html',
            '%(engine)s/%(theme)s/articles.html', '%(engine)s/rss.xml',
            '%(engine)s/atom.xml', 'misc/%(theme)s/style.css',
            'misc/sample-entry.txt'
        ]
    ]
    files = [join(dirname(defaults.__file__), path) for path in files]

    # restore a given file from defaults
    # XXX restore folders, too
    if filter(lambda p: basename(p) == basename(root), files):

        for path in files:
            if basename(path) == basename(root):
                break
        if isfile(root) and (options.overwrite or raw_input(
                're-initialize %r? [yn]: ' % root) == 'y'):
            shutil.copy(path, root)
            log.info('re-initialized %s' % root)
        else:
            shutil.copy(path, root)
            log.info('create %s' % root)
        sys.exit(0)

    # re-initialize conf.py
    if root == 'conf.py':
        if options.overwrite or raw_input(
                're-initialize %r? [yn]: ' % root) == 'y':
            with io.open('conf.py', 'w') as fp:
                fp.write(confstring % {'engine': options.engine})
            log.info('re-initialized %s' % root)
        sys.exit(0)

    # YO DAWG I HERD U LIEK BLOGS SO WE PUT A BLOG IN UR BLOG -- ask user before
    if isfile('conf.py') and not options.overwrite:
        q = raw_input("Create blog inside a blog? [yn]: ")
        if q != 'y':
            sys.exit(1)

    if exists(root) and len(os.listdir(root)) > 0 and not options.overwrite:
        if raw_input(
                "Destination directory not empty! Continue? [yn]: ") != 'y':
            sys.exit(1)

    if root != '.' and not exists(root):
        os.mkdir(root)

    for directory in dirs:
        directory = join(root, directory % default)
        if exists(directory) and not isdir(directory):
            log.critical('Unable to create %r. Please remove this file',
                         directory)
            sys.exit(1)
        elif not exists(directory):
            os.mkdir(directory)

    with io.open(join(root, 'conf.py'), 'w') as fp:
        fp.write(confstring % {'engine': options.engine})
        log.info('create  %s', join(root, 'conf.py'))

    for path in files:
        if path.endswith(('.html', '.xml')):
            create(default['layout_dir'], path)
        elif path.endswith('.txt'):
            create(default['content_dir'], path)
        else:
            create(default['output_dir'], path)

    log.info('Created your fresh new blog at %r. Enjoy!', root)
Exemple #36
0
 def update(self, path, ctime=None):
     """:param path: path\n:param ctime: computing time"""
     if ctime:
         log.info("update  [%.2fs] %s", ctime, path)
     else:
         log.info("update  %s", path)
Exemple #37
0
def compile(conf, env, force=False, **options):
    """The compilation process.

    Current API:

        #. when we require context
        #. when we called an event

    New API:

        #. before we start with view Initialization
        #. after we initialized views
        #. before we require context
        #. after we required context
        #. before we template
        #. before we write a file
        #. when we called an event
        #. when we finish
    """

    # time measurement
    ctime = time.time()

    # populate env and corrects some conf things
    request = initialize(conf, env)

    # load pages/entries and store them in env
    entrylist, pages = readers.load(conf)
    env.globals['entrylist'] = entrylist
    env.globals['pages'] = pages

    # XXX translations should be moved out of core
    env.globals['translations'] = translations = []

    if force:
        # acrylamid compile -f
        cache.clear()

    # here we store all found filter and their aliases
    ns = defaultdict(set)

    # get available filter list, something like with obj.get-function
    # list = [<class head_offset.Headoffset at 0x1014882c0>, <class html.HTML at 0x101488328>,...]
    aflist = filters.get_filters()

    # ... and get all configured views
    _views = views.get_views()

    # filters found in all entries, views and conf.py
    found = sum(
        (x.filters
         for x in entrylist + pages + _views), []) + request['conf']['filters']

    for val in found:
        # first we for `no` and get the function name and arguments
        f = val[2:] if val.startswith('no') else val
        fname, fargs = f.split('+')[:1][0], f.split('+')[1:]

        try:
            # initialize the filter with its function name and arguments
            fx = aflist[fname](conf, env, val, *fargs)
            if val.startswith('no'):
                fx = filters.disable(fx)
        except ValueError:
            try:
                fx = aflist[val.split('+')[:1][0]](conf, env, val, *fargs)
            except ValueError:
                raise AcrylamidException('no such filter: %s' % val)

        ns[fx].add(val)

    for entry in entrylist + pages:
        for v in _views:

            # a list that sorts out conflicting and duplicated filters
            flst = filters.FilterList()

            # filters found in this specific entry plus views and conf.py
            found = entry.filters + v.filters + request['conf']['filters']

            for fn in found:
                fx, _ = next((k for k in ns.iteritems() if fn in k[1]))
                if fx not in flst:
                    flst.append(fx)

            # sort them ascending because we will pop within filters.add
            entry.filters.add(sorted(flst, key=lambda k:
                                     (-k.priority, k.name)),
                              context=v)

    # lets offer a last break to populate tags or so
    # XXX this API component needs a review
    for v in _views:
        env = v.context(env, {
            'entrylist': entrylist,
            'pages': pages,
            'translations': translations
        })

    # now teh real thing!
    for v in _views:

        # XXX the entry should automatically determine its caller (using
        # some sys magic to recursively check wether the calling class is
        # derieved from `View`.)
        for entry in entrylist + pages + translations:
            entry.context = v

        request['pages'], request['translations'] = pages, translations
        request['entrylist'] = filter(v.condition, entrylist)
        tt = time.time()

        for html, path in v.generate(request):
            helpers.mkfile(html, path, time.time() - tt, **options)
            tt = time.time()

    # remove abandoned cache files
    cache.shutdown()

    # print a short summary
    log.info('%i new, %i updated, %i skipped [%.2fs]', event.count('create'),
             event.count('update'),
             event.count('identical') + event.count('skip'),
             time.time() - ctime)
Exemple #38
0
def initialize(conf, env):
    """Initializes Jinja2 environment, prepares locale and configure
    some minor things. Filter and View are inited with conf and env,
    a request dict is returned.
    """
    # initialize cache, optional to cache_dir
    cache.init(conf.get('cache_dir', None))

    # set up templating environment
    env.engine = utils.import_object(conf['engine'])()

    env.engine.init(conf['layout_dir'], cache.cache_dir)
    env.engine.register('safeslug', helpers.safeslug)
    env.engine.register('tagify', lambda x: x)

    # try language set in LANG, if set correctly use it
    try:
        locale.setlocale(locale.LC_ALL, str(conf.get('lang', '')))
    except (locale.Error, TypeError):
        # try if LANG is an alias
        try:
            locale.setlocale(
                locale.LC_ALL, locale.locale_alias[str(conf.get('lang',
                                                                '')).lower()])
        except (locale.Error, KeyError):
            # LANG is not an alias, so we use system's default
            try:
                locale.setlocale(locale.LC_ALL, '')
            except locale.Error:
                pass  # hope this makes Travis happy
            log.info('notice  your OS does not support %s, fallback to %s',
                     conf.get('lang', ''),
                     locale.getlocale()[0])
    if locale.getlocale()[0] is not None:
        conf['lang'] = locale.getlocale()[0][:2]
    else:
        # getlocale() is (None, None) aka 'C'
        conf['lang'] = 'en'

    if 'www_root' not in conf:
        log.warn('no `www_root` specified, using localhost:8000')
        conf['www_root'] = 'http://localhost:8000/'

    # figure out timezone and set offset, more verbose for 2.6 compatibility
    td = (datetime.now() - datetime.utcnow())
    total_seconds = (td.microseconds +
                     (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6
    offset = round(total_seconds / 3600.0)
    conf['tzinfo'] = readers.Timezone(offset)

    # determine http(s), host and path
    env['protocol'], env['netloc'], env['path'], x, y = urlsplit(
        conf['www_root'])

    # take off the trailing slash for www_root and path
    conf['www_root'] = conf['www_root'].rstrip('/')
    env['path'] = env['path'].rstrip('/')

    # check if encoding is available
    try:
        codecs.lookup(conf['encoding'])
    except LookupError:
        raise AcrylamidException('no such encoding available: %r' %
                                 conf['encoding'])

    # prepare, import and initialize filters and views
    if isinstance(conf['filters_dir'], basestring):
        conf['filters_dir'] = [
            conf['filters_dir'],
        ]

    if isinstance(conf['views_dir'], basestring):
        conf['views_dir'] = [
            conf['views_dir'],
        ]

    lazy.enable()
    filters.initialize(conf["filters_dir"],
                       conf,
                       env,
                       exclude=conf["filters_ignore"],
                       include=conf["filters_include"])
    lazy.disable(
    )  # this has weird side effects with jinja2, so disabled after filters

    views.initialize(conf["views_dir"], conf, env)
    env['views'] = dict([(v.view, v) for v in views.get_views()])

    entryfmt, pagefmt = '/:year/:slug/', '/:slug/'
    for view in views.get_views():
        if view.view == 'entry':
            entryfmt = view.path
        if view.view == 'page':
            pagefmt = view.path

    conf.setdefault('entry_permalink', entryfmt)
    conf.setdefault('page_permalink', pagefmt)

    return {'conf': conf, 'env': env}
Exemple #39
0
 def remove(self, path):
     log.info("remove  %s", path)
Exemple #40
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

    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)
Exemple #41
0
def compile(conf, env, force=False, **options):

    # time measurement
    ctime = time.time()

    # populate env and corrects some conf things
    request = initialize(conf, env)

    if force:
        # acrylamid compile -f
        cache.clear()

    # list of FileEntry-objects reverse sorted by date.
    entrylist = sorted([FileEntry(e, conf) for e in utils.filelist(conf['content_dir'],
                                                             conf.get('entries_ignore', []))],
                       key=lambda k: k.date, reverse=True)

    # here we store all possible filter configurations
    ns = set()

    # get available filter list, something like with obj.get-function
    # list = [<class head_offset.Headoffset at 0x1014882c0>, <class html.HTML at 0x101488328>,...]
    aflist = filters.get_filters()

    # ... and get all configured views
    _views = views.get_views()

    # filters found in all entries, views and conf.py
    found = sum((x.filters for x in entrylist+_views), []) + request['conf']['filters']

    for val in found:
        # first we for `no` and get the function name and arguments
        f = val[2:] if val.startswith('no') else val
        fname, fargs = f.split('+')[:1][0], f.split('+')[1:]

        try:
            # initialize the filter with its function name and arguments
            fx = aflist[fname](val, *fargs)
            if val.startswith('no'):
                fx.transform = lambda x, y, *z: x
                fx.__hash__ = lambda : 0
        except ValueError:
            try:
                fx = aflist[val.split('+')[:1][0]](val, *fargs)
            except ValueError:
                raise AcrylamidException('no such filter: %s' % val)

        ns.add(fx)

    for entry in entrylist:
        for v in _views:

            # a list that sorts out conflicting and duplicated filters
            flst = filters.FilterList()

            # filters found in this specific entry plus views and conf.py
            found = entry.filters + v.filters + request['conf']['filters']

            for fn in found:
                fx = filter(lambda k: fn == k.name, ns)[0]
                if fx not in flst:
                    flst.append(fx)

            # sort them ascending because we will pop within filters.add
            entry.filters.add(sorted(flst, key=lambda k: (-k.priority, k.name)),
                              context=v.__class__.__name__)

    # lets offer a last break to populate tags or so
    # XXX this API component needs a review
    for v in _views:
        env = v.context(env, {'entrylist': filter(v.condition, entrylist)})

    # now teh real thing!
    for v in _views:

        # XXX the entry should automatically determine its caller (using
        # some sys magic to recursively check wether the calling class is
        # derieved from `View`.)
        for entry in entrylist:
            entry.context = v.__class__.__name__

        request['entrylist'] = filter(v.condition, entrylist)
        tt = time.time()

        for html, path in v.generate(request):
            helpers.mkfile(html, path, time.time()-tt, **options)
            tt = time.time()

    # remove abandoned cache files
    cache.shutdown()

    log.info('Blog compiled in %.2fs' % (time.time() - ctime))
Exemple #42
0
def initialize(conf, env):
    """Initializes Jinja2 environment, prepares locale and configure
    some minor things. Filter and View are inited with conf and env,
    a data dict is returned.
    """
    # initialize cache, optional to cache_dir
    cache.init(conf.get('cache_dir'))

    env['version'] = type('Version', (str, ), dict(zip(
        ['major', 'minor'], LooseVersion(dist.version).version[:2])))(dist.version)

    # crawl through CHANGES.md and stop on breaking changes
    if history.breaks(env, cache.emptyrun):
        cache.shutdown()
        print("Detected version upgrade that might break your configuration. Run")
        print("Acrylamid a second time to get rid of this message and premature exit.")
        raise SystemExit

    # set up templating environment
    env.engine = import_object(conf['engine'])()

    env.engine.init(conf['theme'], cache.cache_dir)
    env.engine.register('safeslug', helpers.safeslug)
    env.engine.register('tagify', lambda x: x)

    # try language set in LANG, if set correctly use it
    try:
        locale.setlocale(locale.LC_ALL, str(conf.get('lang', '')))
    except (locale.Error, TypeError):
        # try if LANG is an alias
        try:
            locale.setlocale(locale.LC_ALL, locale.locale_alias[str(conf.get('lang', '')).lower()])
        except (locale.Error, KeyError):
            # LANG is not an alias, so we use system's default
            try:
                locale.setlocale(locale.LC_ALL, '')
            except locale.Error:
                pass  # hope this makes Travis happy
            log.info('notice  your OS does not support %s, fallback to %s', conf.get('lang', ''),
                     locale.getlocale()[0])
    if locale.getlocale()[0] is not None:
        conf['lang'] = locale.getlocale()[0][:2]
    else:
        # getlocale() is (None, None) aka 'C'
        conf['lang'] = 'en'

    if 'www_root' not in conf:
        log.warn('no `www_root` specified, using localhost:8000')
        conf['www_root'] = 'http://localhost:8000/'

    # figure out timezone and set offset, more verbose for 2.6 compatibility
    td = (datetime.now() - datetime.utcnow())
    offset = round(total_seconds(td) / 3600.0)
    conf['tzinfo'] = readers.Timezone(offset)

    # determine http(s), host and path
    env['protocol'], env['netloc'], env['path'], x, y = urlsplit(conf['www_root'])

    # take off the trailing slash for www_root and path
    conf['www_root'] = conf['www_root'].rstrip('/')
    env['path'] = env['path'].rstrip('/')

    if env['path']:
        conf['output_dir'] = conf['output_dir'] + env['path']

    lazy.enable()
    filters.initialize(conf["filters_dir"][:], conf, env)
    lazy.disable()  # this has weird side effects with jinja2, so disabled after filters

    views.initialize(conf["views_dir"][:], conf, env)
    env.views = views.Views(view for view in views.get_views())

    entryfmt, pagefmt = '/:year/:slug/', '/:slug/'
    for view in views.get_views():
        if view.name == 'entry':
            entryfmt = view.path
        if view.name == 'page':
            pagefmt = view.path

    conf.setdefault('entry_permalink', entryfmt)
    conf.setdefault('page_permalink', pagefmt)

    # register webassets to theme engine, make webassets available as env.webassets
    assets.initialize(conf, env)

    return {'conf': conf, 'env': env}
Exemple #43
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("--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)
Exemple #44
0
def compile(conf, env):
    """The compilation process."""

    if env.options.force:
        cache.clear(conf.get("cache_dir"))

    # time measurement
    ctime = time.time()

    # populate env and corrects some conf things
    data = initialize(conf, env)

    # load pages/entries and store them in env
    rv = dict(zip(["entrylist", "pages", "translations", "drafts"], map(HashableList, readers.load(conf))))

    entrylist, pages = rv["entrylist"], rv["pages"]
    translations, drafts = rv["translations"], rv["drafts"]

    # load references
    refs.load(entrylist, pages, translations, drafts)

    data.update(rv)
    env.globals.update(rv)

    # here we store all found filter and their aliases
    ns = defaultdict(set)

    # [<class head_offset.Headoffset at 0x1014882c0>, <class html.HTML at 0x101488328>,...]
    aflist = filters.get_filters()

    # ... and get all configured views
    _views = views.get_views()

    # filters found in all entries, views and conf.py (skip translations, has no items)
    found = sum((x.filters for x in chain(entrylist, pages, drafts, _views, [conf])), [])

    for val in found:
        # first we for `no` and get the function name and arguments
        f = val[2:] if val.startswith("no") else val
        fname, fargs = f.split("+")[:1][0], f.split("+")[1:]

        try:
            # initialize the filter with its function name and arguments
            fx = aflist[fname](conf, env, val, *fargs)
            if val.startswith("no"):
                fx = filters.disable(fx)
        except ValueError:
            try:
                fx = aflist[val.split("+")[:1][0]](conf, env, val, *fargs)
            except ValueError:
                raise AcrylamidException("no such filter: %s" % val)

        ns[fx].add(val)

    # include actual used filters to trigger modified state
    env.filters = HashableList(ns.keys())

    for entry in chain(entrylist, pages, drafts):
        for v in _views:

            # a list that sorts out conflicting and duplicated filters
            flst = filters.FilterList()

            # filters found in this specific entry plus views and conf.py
            found = entry.filters + v.filters + data["conf"]["filters"]

            for fn in found:
                fx, _ = next((k for k in ns.iteritems() if fn in k[1]))
                if fx not in flst:
                    flst.append(fx)

            # sort them ascending because we will pop within filters.add
            entry.filters.add(sorted(flst, key=lambda k: (-k.priority, k.name)), context=v)

    # lets offer a last break to populate tags and such
    for v in _views:
        env = v.context(conf, env, data)

    # now teh real thing!
    for v in _views:

        for entry in chain(entrylist, pages, translations, drafts):
            entry.context = v

        for var in "entrylist", "pages", "translations", "drafts":
            data[var] = HashableList(filter(v.condition, locals()[var])) if v.condition else locals()[var]

        tt = time.time()
        for buf, path in v.generate(conf, env, data):
            try:
                helpers.mkfile(
                    buf, path, time.time() - tt, ns=v.name, force=env.options.force, dryrun=env.options.dryrun
                )
            except UnicodeError:
                log.exception(path)
            finally:
                buf.close()
            tt = time.time()

    # copy modified/missing assets to output
    assets.compile(conf, env)

    # wait for unfinished hooks
    hooks.shutdown()

    # save conf/environment hash and new/changed/unchanged references
    helpers.memoize("Configuration", hash(conf))
    helpers.memoize("Environment", hash(env))
    refs.save()

    # remove abandoned cache files
    cache.shutdown()

    # print a short summary
    log.info(
        "%i new, %i updated, %i skipped [%.2fs]",
        event.count("create"),
        event.count("update"),
        event.count("identical") + event.count("skip"),
        time.time() - ctime,
    )
Exemple #45
0
def initialize(conf, env):
    """Initializes Jinja2 environment, prepares locale and configure
    some minor things. Filter and View are inited with conf and env,
    a data dict is returned.
    """
    # initialize cache, optional to cache_dir
    cache.init(conf.get('cache_dir'))

    env['version'] = type(
        'Version', (str, ),
        dict(zip(['major', 'minor'],
                 LooseVersion(dist.version).version[:2])))(dist.version)

    # crawl through CHANGES.md and stop on breaking changes
    if history.breaks(env, cache.emptyrun):
        cache.shutdown()
        print(
            "Detected version upgrade that might break your configuration. Run"
        )
        print(
            "Acrylamid a second time to get rid of this message and premature exit."
        )
        raise SystemExit

    # set up templating environment
    env.engine = import_object(conf['engine'])(conf['theme'], cache.cache_dir)
    env.engine.register('safeslug', helpers.safeslug)
    env.engine.register('tagify', lambda x: x)

    # try language set in LANG, if set correctly use it
    try:
        locale.setlocale(locale.LC_ALL, str(conf.get('lang', '')))
    except (locale.Error, TypeError):
        # try if LANG is an alias
        try:
            locale.setlocale(
                locale.LC_ALL, locale.locale_alias[str(conf.get('lang',
                                                                '')).lower()])
        except (locale.Error, KeyError):
            # LANG is not an alias, so we use system's default
            try:
                locale.setlocale(locale.LC_ALL, '')
            except locale.Error:
                pass  # hope this makes Travis happy
            log.info('notice  your OS does not support %s, fallback to %s',
                     conf.get('lang', ''),
                     locale.getlocale()[0])
    if locale.getlocale()[0] is not None:
        conf['lang'] = locale.getlocale()[0][:2]
    else:
        # getlocale() is (None, None) aka 'C'
        conf['lang'] = 'en'

    if 'www_root' not in conf:
        log.warn('no `www_root` specified, using localhost:8000')
        conf['www_root'] = 'http://localhost:8000/'

    # figure out timezone and set offset, more verbose for 2.6 compatibility
    td = (datetime.now() - datetime.utcnow())
    offset = round(total_seconds(td) / 3600.0)
    conf['tzinfo'] = readers.Timezone(offset)

    # determine http(s), host and path
    env['protocol'], env['netloc'], env['path'], x, y = urlsplit(
        conf['www_root'])

    # take off the trailing slash for www_root and path
    conf['www_root'] = conf['www_root'].rstrip('/')
    env['path'] = env['path'].rstrip('/')

    if env['path']:
        conf['output_dir'] = conf['output_dir'] + env['path']

    lazy.enable()
    filters.initialize(conf["filters_dir"][:], conf, env)
    lazy.disable(
    )  # this has weird side effects with jinja2, so disabled after filters

    views.initialize(conf["views_dir"][:], conf, env)
    env.views = views.Views(view for view in views.get_views())

    entryfmt, pagefmt = '/:year/:slug/', '/:slug/'
    for view in views.get_views():
        if view.name == 'entry':
            entryfmt = view.path
        if view.name == 'page':
            pagefmt = view.path

    conf.setdefault('entry_permalink', entryfmt)
    conf.setdefault('page_permalink', pagefmt)

    # register webassets to theme engine, make webassets available as env.webassets
    assets.initialize(conf, env)

    return {'conf': conf, 'env': env}
Exemple #46
0
def initialize(conf, env):
    """Initializes Jinja2 environment, prepares locale and configure
    some minor things. Filter and View are inited with conf and env,
    a request dict is returned.
    """
    # initialize cache, optional to cache_dir
    cache.init(conf.get('cache_dir', None))

    # rewrite static directory
    assets.initialize(conf, env)

    # set up templating environment
    env.engine = utils.import_object(conf['engine'])()

    env.engine.init(conf['theme'], cache.cache_dir)
    env.engine.register('safeslug', helpers.safeslug)
    env.engine.register('tagify', lambda x: x)

    # try language set in LANG, if set correctly use it
    try:
        locale.setlocale(locale.LC_ALL, str(conf.get('lang', '')))
    except (locale.Error, TypeError):
        # try if LANG is an alias
        try:
            locale.setlocale(locale.LC_ALL, locale.locale_alias[str(conf.get('lang', '')).lower()])
        except (locale.Error, KeyError):
            # LANG is not an alias, so we use system's default
            try:
                locale.setlocale(locale.LC_ALL, '')
            except locale.Error:
                pass  # hope this makes Travis happy
            log.info('notice  your OS does not support %s, fallback to %s', conf.get('lang', ''),
                     locale.getlocale()[0])
    if locale.getlocale()[0] is not None:
        conf['lang'] = locale.getlocale()[0][:2]
    else:
        # getlocale() is (None, None) aka 'C'
        conf['lang'] = 'en'

    if 'www_root' not in conf:
        log.warn('no `www_root` specified, using localhost:8000')
        conf['www_root'] = 'http://localhost:8000/'

    # figure out timezone and set offset, more verbose for 2.6 compatibility
    td = (datetime.now() - datetime.utcnow())
    total_seconds = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6
    offset = round(total_seconds / 3600.0)
    conf['tzinfo'] = readers.Timezone(offset)

    # determine http(s), host and path
    env['protocol'], env['netloc'], env['path'], x, y = urlsplit(conf['www_root'])

    # take off the trailing slash for www_root and path
    conf['www_root'] = conf['www_root'].rstrip('/')
    env['path'] = env['path'].rstrip('/')

    # check if encoding is available
    try:
        codecs.lookup(conf['encoding'])
    except LookupError:
        raise AcrylamidException('no such encoding available: %r' % conf['encoding'])

    # prepare, import and initialize filters and views
    if isinstance(conf['filters_dir'], basestring):
        conf['filters_dir'] = [conf['filters_dir'], ]

    if isinstance(conf['views_dir'], basestring):
        conf['views_dir'] = [conf['views_dir'], ]

    lazy.enable()
    filters.initialize(conf["filters_dir"], conf, env)
    lazy.disable()  # this has weird side effects with jinja2, so disabled after filters

    views.initialize(conf["views_dir"], conf, env)
    env.views = views.Views(view for view in views.get_views())

    entryfmt, pagefmt = '/:year/:slug/', '/:slug/'
    for view in views.get_views():
        if view.name == 'entry':
            entryfmt = view.path
        if view.name == 'page':
            pagefmt = view.path

    conf.setdefault('entry_permalink', entryfmt)
    conf.setdefault('page_permalink', pagefmt)

    return {'conf': conf, 'env': env}
Exemple #47
0
def initialize(conf, env):
    """Initializes Jinja2 environment, prepares locale and configure
    some minor things. Filter and View are inited with conf and env,
    a request dict is returned.
    """
    # initialize cache, optional to cache_dir
    cache.init(conf.get('cache_dir', None))

    # set up templating environment
    env.engine = utils.import_object(conf['engine'])()

    env.engine.init(conf['layout_dir'], cache.cache_dir)
    env.engine.register('safeslug', helpers.safeslug)
    env.engine.register('tagify', lambda x: x)

    # try language set in LANG, if set correctly use it
    try:
        locale.setlocale(locale.LC_ALL, conf.get('lang', ''))
    except (locale.Error, TypeError):
        # try if LANG is an alias
        try:
            locale.setlocale(locale.LC_ALL, locale.locale_alias[conf['lang'].lower()])
        except (locale.Error, KeyError):
            # LANG is not an alias, so we use system's default
            locale.setlocale(locale.LC_ALL, '')
            log.info('notice  your OS does not support %s, fallback to %s', conf['lang'],
                     locale.getlocale()[0])
    if locale.getlocale()[0] is not None:
        conf['lang'] = locale.getlocale()[0][:2]
    else:
        # getlocale() is (None, None) aka 'C'
        conf['lang'] = 'en'

    if 'www_root' not in conf:
        log.warn('no `www_root` specified, using localhost:8000')
        conf['www_root'] = 'http://localhost:8000/'

    env['protocol'], env['netloc'], env['path'], x, y = urlsplit(conf['www_root'])

    # take off the trailing slash for www_root and path
    conf['www_root'] = conf['www_root'].rstrip('/')
    env['path'] = env['path'].rstrip('/')

    # check if encoding is available
    try:
        codecs.lookup(conf['encoding'])
    except LookupError:
        raise AcrylamidException('no such encoding available: %r' % conf['encoding'])

    # prepare, import and initialize filters and views
    if isinstance(conf['filters_dir'], basestring):
        conf['filters_dir'] = [conf['filters_dir'], ]

    if isinstance(conf['views_dir'], basestring):
        conf['views_dir'] = [conf['views_dir'], ]

    lazy.enable()
    filters.initialize(conf["filters_dir"], conf, env, exclude=conf["filters_ignore"],
                                                       include=conf["filters_include"])
    lazy.disable()  # this has weird side effects with jinja2, so disabled after filters

    views.initialize(conf["views_dir"], conf, env)
    env['views'] = dict([(v.view, v) for v in views.get_views()])

    entryfmt, pagefmt = '/:year/:slug/', '/:slug/'
    for view in views.get_views():
        if view.view == 'entry':
            entryfmt = view.path
        if view.view == 'page':
            pagefmt = view.path

    conf['entry_permalink'] = conf['entry_permalink'] or entryfmt
    conf['page_permalink'] = conf['page_permalink'] or pagefmt

    return {'conf': conf, 'env': env}
Exemple #48
0
def run(conf, env, options):
    """Subcommand: new -- create a new blog entry the easy way.  Either run
    ``acrylamid new My fresh new Entry`` or interactively via ``acrylamid new``
    and the file will be created using the preferred permalink format."""

    # we need the actual default values
    commands.initialize(conf, env)

    # config content_extension originally defined as string, not a list
    extlist = conf.get('content_extension',['.txt'])
    if isinstance(extlist, string_types):
        ext = extlist
    else:
        ext = extlist[0]

    fd, tmp = tempfile.mkstemp(suffix=ext, dir='.cache/')

    editor = os.getenv('VISUAL') if os.getenv('VISUAL') else os.getenv('EDITOR')
    tt = formats.get(ext, yaml)

    if options.title:
        title = u(' '.join(options.title))
    else:
        title = u(input("Entry's title: "))

    with io.open(fd, 'w', encoding='utf-8') as f:
        f.write(tt(title, datetime.now().strftime(conf['date_format'])))

    entry = readers.Entry(tmp, conf)
    p = join(conf['content_dir'], splitext(entry.permalink.strip('/'))[0])

    try:
        os.makedirs(p.rsplit('/', 1)[0])
    except OSError:
        pass

    filepath = p + ext
    if isfile(filepath):
        raise AcrylamidException('Entry already exists %r' % filepath)
    shutil.move(tmp, filepath)
    event.create('new', filepath)

    if datetime.now().hour == 23 and datetime.now().minute > 45:
        log.info("notice  don't forget to update entry.date-day after mignight!")

    if log.level() >= log.WARN:
        return

    try:
        if editor:
            retcode = subprocess.call(shlex.split(editor) + [filepath])
        elif sys.platform == 'darwin':
            retcode = subprocess.call(['open', filepath])
        else:
            retcode = subprocess.call(['xdg-open', filepath])
    except OSError:
        raise AcrylamidException('Could not launch an editor')

    # XXX process detaches... m(
    if retcode < 0:
        raise AcrylamidException('Child was terminated by signal %i' % -retcode)

    if os.stat(filepath)[6] == 0:
        raise AcrylamidException('File is empty!')