Beispiel #1
0
def strange_case(config):
    # pull out important values.
    config['site_path'] = site_path = os.path.abspath(config['site_path'])
    config['deploy_path'] = deploy_path = os.path.abspath(
        config['deploy_path'])

    # check for site/ folder (required)
    if not os.path.isdir(site_path):
        raise IOError('Could not find site_path folder "%s"' % site_path)

    # create the public/ folder
    if not os.path.isdir(config['deploy_path']):
        os.mkdir(config['deploy_path'])

    from strange_case.support.jinja import StrangeCaseEnvironment
    try:
        from plywood import PlywoodEnv, PlywoodFunction
    except ImportError:

        class PlywoodEnv(object):
            pass

        def PlywoodFunction(_):
            pass

    ##|
    ##|  EXTENSIONS
    ##|  these are Jinja2 extensions that get loaded into the Environment object
    ##|
    extensions = []
    if 'extensions' in config:
        for extension in config['extensions']:
            if isinstance(extension, str):
                try:
                    extension = fancy_import(extension)
                except ImportError:
                    sys.stderr.write(
                        'Error in processors: Could not find "%s"\n' %
                        extension)
                    raise
            extensions.append(extension)
        del config['extensions']

    if not Registry.get('jinja_environment'):
        jinja_environment = StrangeCaseEnvironment(
            extensions=extensions, project_path=config['project_path'])
        Registry.set('jinja_environment', jinja_environment)
    else:
        jinja_environment = Registry.get('jinja_environment')

    if not Registry.get('plywood_environment'):
        plywood_environment = PlywoodEnv()
        Registry.set('plywood_environment', plywood_environment)
    else:
        plywood_environment = Registry.get('plywood_environment')

    ##|
    ##|  FILTERS
    ##|  Jinja2 filter functions (`{{ var|filter }}`).  These are inserted into
    ##|  the Environment object's `filter` property.
    ##|
    if 'filters' in config:
        for filter_name, method in config['filters'].items():
            if isinstance(method, str):
                try:
                    method = fancy_import(method)
                except ImportError:
                    sys.stderr.write(
                        'Error in filters: Could not find "%s"\n' % method)
                    raise
            jinja_environment.filters[filter_name] = method
            if filter_name not in plywood_environment.scope:
                plywood_environment.scope[filter_name] = PlywoodFunction(
                    method)
        del config['filters']

    ##|
    ##|  PROCESSORS
    ##|  A processors function registers itself using `Registry.register`, so
    ##|  all that is needed here is to load the module.
    ##|
    if 'processors' in config:
        for processor in config['processors']:
            try:
                fancy_import(processor)
            except ImportError:
                sys.stderr.write('Error in processors: Could not find "%s"\n' %
                                 processor)
                raise
        del config['processors']
    configurators = get_configurators(config)

    # register configurators - I broke this out into a separate function (below)
    Registry.reset_configurators()
    for configurator in configurators:
        Registry.add_configurator(configurator)

    # configurators can respond to the 'on_start' hook
    # skip_if_not_modified configurator uses this to read in the .timestamps
    # file, and strip_extensions makes sure that set_url is run before itself.
    for configurator in configurators:
        # configurators might be removed (?)
        if configurator in Registry.configurators:
            try:
                configurator.on_start(config)
            except AttributeError:
                pass

    # generic Registry hooks can listen for 'on_start'
    # category plugin uses this to reset when --watch is used
    Registry.trigger('on_start', config)

    # each node class should add files to these properties, so that watchdog and
    # stale-file-removal work.
    Node.files_written = []
    Node.files_tracked = []

    # create the list of existing files.  files that aren't generated will be
    # removed (unless dont_remove config is True)
    remove_stale_files = config['remove_stale_files']
    dont_remove = config['dont_remove']
    existing_files = []
    if os.path.isdir(deploy_path):
        existing_files = find_files(deploy_path)
    else:
        os.makedirs(deploy_path)

    # this is the one folder that *doesn't* get processed by
    # processors.build_page_tree - it needs special handling here.
    root_node = build_node(config, site_path, deploy_path, '')[0]
    Registry.set('root', root_node)
    root_node.generate()

    # configurators can respond to the 'on_finish' hook
    for configurator in Registry.configurators:
        try:
            configurators.on_finish(config)
        except AttributeError:
            pass

    if remove_stale_files and existing_files:
        paths = []
        for f in existing_files:
            if f not in Node.files_written:
                f = os.path.abspath(f)
                f_rel = os.path.relpath(f)
                if any(pattern for pattern in dont_remove
                       if fnmatch(f, pattern)):
                    sys.stderr.write("\033[32mignoring\033[0m \033[1m" +
                                     f_rel + "\033[0m\n")
                    continue

                if os.path.isdir(f):
                    paths.insert(0, f)
                else:
                    sys.stderr.write("\033[31mrm\033[0m \033[1m" + f_rel +
                                     "\033[0m\n")
                    os.remove(f)
        # filter out directories that are not empty
        paths = [p for p in paths if not os.listdir(p)]
        for p in paths:
            p_rel = os.path.relpath(p)
            sys.stderr.write("\033[31mrmdir\033[0m \033[1m" + p_rel +
                             "\033[0m\n")
            os.removedirs(p)
Beispiel #2
0
def run():
    import logging
    logging.basicConfig()

    # so that strange_case.py can be executed from any project folder, add CWD to the import paths
    sys.path.insert(0, os.getcwd())

    import argparse
    parser = argparse.ArgumentParser(description='Process some integers.')
    parser.add_argument('-w', '--watch', dest='watch', action='store_const',
                       const=True, default=False,
                       help='watch the site_path for changes (default: find the max)')
    conf_overrides = [
        'project_path',
        'site_path',
        'deploy_path',
        'remove_stale_files',
        'config_file',
    ]
    parser.add_argument('-x', '--exclude', nargs='*', dest='exclude_paths', default=None)
    parser.add_argument('-p', '--project', dest='project_path')
    parser.add_argument('-s', '--site', dest='site_path')
    parser.add_argument('-d', '--deploy', dest='deploy_path')
    parser.add_argument('-r', '--remove', dest='remove_stale_files', action='store_true', default=None)
    parser.add_argument('-n', '--no-remove', dest='remove_stale_files', action='store_false', default=None)
    parser.add_argument('-c', '--config', dest='config_file')
    parser.add_argument('configs', nargs='*')

    # config section catches assertion errors and prints them as error messages
    try:
        if os.path.isfile(os.path.join(os.getcwd(), 'config.py')):
            from config import CONFIG
            if not isinstance(CONFIG, ConfigDict):
                CONFIG = ConfigDict(CONFIG)
        else:
            from strange_case.strange_case_config import CONFIG

        # normalize paths
        for conf in ['project_path', 'site_path', 'deploy_path']:
            if CONFIG[conf][0] == '~':
                CONFIG[conf] = os.path.expanduser(CONFIG[conf])
            elif CONFIG[conf][0] == '.':
                CONFIG[conf] = os.path.abspath(CONFIG[conf])

        # now we can look for the app config
        config_path = os.path.join(CONFIG['project_path'], CONFIG['config_file'])

        if os.path.isfile(config_path):
            with open(config_path, 'r') as config_file:
                yaml_config = yaml.load(config_file)
            if yaml_config:
                CONFIG.update(yaml_config)

        args = parser.parse_args()
        for conf in conf_overrides:
            if getattr(args, conf) is not None:
                CONFIG[conf] = getattr(args, conf)

        assign = None
        for confs in args.configs:
            if assign:
                CONFIG[assign] = confs
                assign = None
            elif ':' in confs:
                key, val = confs.split(':', 1)
                CONFIG[key] = val
            else:
                assign = confs

        if CONFIG['config_hook']:
            CONFIG['config_hook'](CONFIG)
            del CONFIG['config_hook']

        assert CONFIG['project_path'], "project_path is required"
        assert CONFIG['site_path'], "site_path is required"
        assert CONFIG['deploy_path'], "deploy_path is required"
    except AssertionError as e:
        sys.stderr.write("\033[1;31mError:\033[0m \033[1m" + e.message + "\033[0m\n")
        return

    if not os.path.isdir(CONFIG['deploy_path']):
        os.mkdir(CONFIG['deploy_path'])

    from strange_case.support.jinja import StrangeCaseEnvironment

    extensions = []
    if 'extensions' in CONFIG:
        for extension in CONFIG['extensions']:
            if isinstance(extension, basestring):
                try:
                    extension = fancy_import(extension)
                except ImportError:
                    sys.error.write('Error in processors: Could not find "%s"\n' % extension)
                    raise
            extensions.append(extension)
        del CONFIG['extensions']

    if not Registry.get('jinja_environment', None):
        jinja_environment = StrangeCaseEnvironment(extensions=extensions, project_path=CONFIG['project_path'])
        Registry.set('jinja_environment', jinja_environment)
    else:
        jinja_environment = Registry.get('jinja_environment')

    if 'filters' in CONFIG:
        for filter_name, method in CONFIG['filters'].iteritems():
            if isinstance(method, basestring):
                try:
                    method = fancy_import(method)
                except ImportError:
                    sys.error.write('Error in filters: Could not find "%s"\n' % method)
                    raise
            jinja_environment.filters[filter_name] = method
        del CONFIG['filters']

    if 'processors' in CONFIG:
        for processor in CONFIG['processors']:
            try:
                fancy_import(processor)
            except ImportError:
                sys.error.write('Error in processors: Could not find "%s"\n' % processor)
                raise
        del CONFIG['processors']

    configurators = []
    if 'configurators' in CONFIG:
        for configurator in CONFIG['configurators']:
            if isinstance(configurator, basestring):
                configurator = fancy_import(configurator)
            configurators.append(configurator)
            Registry.add_configurator(configurator)
        del CONFIG['configurators']

    # additional configurators, in addition to the all-important defaults
    if 'configurators +' in CONFIG:
        for configurator in CONFIG['configurators +']:
            if isinstance(configurator, basestring):
                configurator = fancy_import(configurator)
            configurators.append(configurator)
            Registry.add_configurator(configurator)
        del CONFIG['configurators +']

    # additional file_types
    for entry in Registry.file_types:
        CONFIG['file_types'].append(entry)

    if 'file_types +' in CONFIG:
        CONFIG['file_types'].extend(CONFIG['file_types +'])

    # read timestamps file
    timestamps_file = os.path.join(CONFIG['project_path'], '.timestamps')
    if os.path.exists(timestamps_file):
        CONFIG['file_mtimes'] = pickle.load(open(timestamps_file))

    timestamps = {}
    for file_tracked in Node.files_tracked:
        f = os.path.abspath(file_tracked)
        timestamps[f] = os.stat(file_tracked).st_mtime

    if args.watch:
        import time
        from watchdog.observers import Observer
        from watchdog.events import FileSystemEventHandler

        class Regenerate(FileSystemEventHandler):
            last_run = None

            def on_any_event(self, event, alert=True):
                if self.last_run and time.time() - self.last_run < .1:
                    return

                if alert:
                    sys.stderr.write("Change detected.  Running StrangeCase\n")
                strange_case(CONFIG)
                sys.stderr.write("StrangeCase generated at %i\n" % int(time.time()))
                self.last_run = time.time()

        exclude_paths = [
            os.path.abspath('.git'),
            os.path.abspath(CONFIG['deploy_path']),
        ]
        if args.exclude_paths:
            exclude_paths.extend([os.path.abspath(path) for path in args.exclude_paths])

        observer = Observer()
        handler = Regenerate()
        for path in os.listdir(os.getcwd()):
            path = os.path.abspath(path)
            if os.path.isdir(path) and path not in exclude_paths:
                sys.stderr.write('Watching "%s" for changes\n' % path)
                observer.schedule(handler, path=path, recursive=True)
        observer.start()
        try:
            handler.on_any_event(None, False)  # run the first time, no alert
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            sys.stderr.write("Stopping\n")
            observer.stop()
        observer.join()
    else:
        strange_case(CONFIG)
Beispiel #3
0
def run():
    # so that strange_case.py can be executed from any project folder, add CWD to the import paths
    import sys

    sys.path.insert(0, os.getcwd())

    CONFIG = None
    if os.path.isfile(os.path.join(os.getcwd(), "config.py")):
        from config import CONFIG
    else:
        from strange_case.strange_case_config import CONFIG

    from strange_case.support.jinja import StrangeCaseEnvironment

    extensions = []
    if "extensions" in CONFIG:
        for extension in CONFIG["extensions"]:
            if isinstance(extension, basestring):
                extension = fancy_import(extension)
            extensions.append(extension)
        del CONFIG["extensions"]

    if not Registry.get("jinja_environment", None):
        jinja_environment = StrangeCaseEnvironment(extensions=extensions)
        Registry.set("jinja_environment", jinja_environment)
    else:
        jinja_environment = Registry.get("jinja_environment")

    if "filters" in CONFIG:
        for filter_name, method in CONFIG["filters"].iteritems():
            if isinstance(method, basestring):
                method = fancy_import(method)
            jinja_environment.filters[filter_name] = method
        del CONFIG["filters"]

    if "processors" in CONFIG:
        for processor in CONFIG["processors"]:
            __import__(processor)
        del CONFIG["processors"]

    configurators = []
    if "configurators" in CONFIG:
        for configurator in CONFIG["configurators"]:
            if isinstance(configurator, basestring):
                configurator = fancy_import(configurator)
            configurators.append(configurator)
            Registry.add_configurator(configurator)
        del CONFIG["configurators"]

    # additional configurators, in addition to the all-important defaults
    if "configurators +" in CONFIG:
        for configurator in CONFIG["configurators +"]:
            if isinstance(configurator, basestring):
                configurator = fancy_import(configurator)
            configurators.append(configurator)
            Registry.add_configurator(configurator)
        del CONFIG["configurators"]

    import argparse

    parser = argparse.ArgumentParser(description="Process some integers.")
    parser.add_argument(
        "--watch",
        dest="watch",
        action="store_const",
        const=True,
        default=False,
        help="watch the site_path for changes (default: find the max)",
    )
    args = parser.parse_args()

    if args.watch:
        import time
        from watchdog.observers import Observer
        from watchdog.events import FileSystemEventHandler

        exclude_paths = [os.path.abspath(".git"), os.path.abspath(CONFIG["deploy_path"])]

        class Regenerate(FileSystemEventHandler):
            last_run = None

            def on_any_event(self, event, alert=True):
                if self.last_run and time.time() - self.last_run < 0.1:
                    return

                if alert:
                    print "Change detected.  Running StrangeCase"
                strange_case(CONFIG)
                print "StrangeCase generated at %i" % int(time.time())
                self.last_run = time.time()

        observer = Observer()
        handler = Regenerate()
        for path in os.listdir(os.getcwd()):
            path = os.path.abspath(path)
            if os.path.isdir(path) and path not in exclude_paths:
                print 'Watching "%s" for changes' % path
                observer.schedule(handler, path=path, recursive=True)
        observer.start()
        try:
            handler.on_any_event(None, False)  # run the first time, no alert
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            print "Stopping"
            observer.stop()
        observer.join()
    else:
        strange_case(CONFIG)
Beispiel #4
0
def strange_case(config):
    # pull out important values.
    config['site_path'] = site_path = os.path.abspath(config['site_path'])
    config['deploy_path'] = deploy_path = os.path.abspath(config['deploy_path'])

    # check for site/ folder (required)
    if not os.path.isdir(site_path):
        raise IOError('Could not find site_path folder "%s"' % site_path)

    # create the public/ folder
    if not os.path.isdir(config['deploy_path']):
        os.mkdir(config['deploy_path'])

    from strange_case.support.jinja import StrangeCaseEnvironment
    from plywood import PlywoodEnv, PlywoodFunction

    ##|
    ##|  EXTENSIONS
    ##|  these are Jinja2 extensions that get loaded into the Environment object
    ##|
    extensions = []
    if 'extensions' in config:
        for extension in config['extensions']:
            if isinstance(extension, basestring):
                try:
                    extension = fancy_import(extension)
                except ImportError:
                    sys.stderr.write('Error in processors: Could not find "%s"\n' % extension)
                    raise
            extensions.append(extension)
        del config['extensions']

    if not Registry.get('jinja_environment'):
        jinja_environment = StrangeCaseEnvironment(extensions=extensions, project_path=config['project_path'])
        Registry.set('jinja_environment', jinja_environment)
    else:
        jinja_environment = Registry.get('jinja_environment')

    if not Registry.get('plywood_environment'):
        plywood_environment = PlywoodEnv()
        Registry.set('plywood_environment', plywood_environment)
    else:
        plywood_environment = Registry.get('plywood_environment')

    ##|
    ##|  FILTERS
    ##|  Jinja2 filter functions (`{{ var|filter }}`).  These are inserted into
    ##|  the Environment object's `filter` property.
    ##|
    if 'filters' in config:
        for filter_name, method in config['filters'].iteritems():
            if isinstance(method, basestring):
                try:
                    method = fancy_import(method)
                except ImportError:
                    sys.stderr.write('Error in filters: Could not find "%s"\n' % method)
                    raise
            jinja_environment.filters[filter_name] = method
            if filter_name not in plywood_environment.scope:
                plywood_environment.scope[filter_name] = PlywoodFunction(method)
        del config['filters']

    ##|
    ##|  PROCESSORS
    ##|  A processors function registers itself using `Registry.register`, so
    ##|  all that is needed here is to load the module.
    ##|
    if 'processors' in config:
        for processor in config['processors']:
            try:
                fancy_import(processor)
            except ImportError:
                sys.stderr.write('Error in processors: Could not find "%s"\n' % processor)
                raise
        del config['processors']
    configurators = get_configurators(config)

    # register configurators - I broke this out into a separate function (below)
    Registry.reset_configurators()
    for configurator in configurators:
        Registry.add_configurator(configurator)

    # configurators can respond to the 'on_start' hook
    # skip_if_not_modified configurator uses this to read in the .timestamps
    # file, and strip_extensions makes sure that set_url is run before itself.
    for configurator in configurators:
        # configurators might be removed (?)
        if configurator in Registry.configurators:
            try:
                configurator.on_start(config)
            except AttributeError:
                pass

    # generic Registry hooks can listen for 'on_start'
    # category plugin uses this to reset when --watch is used
    Registry.trigger('on_start', config)

    # each node class should add files to these properties, so that watchdog and
    # stale-file-removal work.
    Node.files_written = []
    Node.files_tracked = []

    # create the list of existing files.  files that aren't generated will be
    # removed (unless dont_remove config is True)
    remove_stale_files = config['remove_stale_files']
    dont_remove = config['dont_remove']
    existing_files = []
    if os.path.isdir(deploy_path):
        existing_files = find_files(deploy_path)
    else:
        os.makedirs(deploy_path)

    # this is the one folder that *doesn't* get processed by
    # processors.build_page_tree - it needs special handling here.
    root_node = build_node(config, site_path, deploy_path, '')[0]
    Registry.set('root', root_node)
    root_node.generate()

    # configurators can respond to the 'on_finish' hook
    for configurator in Registry.configurators:
        try:
            configurators.on_finish(config)
        except AttributeError:
            pass

    if remove_stale_files and existing_files:
        paths = []
        for f in existing_files:
            if f not in Node.files_written:
                f = os.path.abspath(f)
                f_rel = os.path.relpath(f)
                if any(pattern for pattern in dont_remove if fnmatch(f, pattern)):
                    sys.stderr.write("\033[32mignoring\033[0m \033[1m" + f_rel + "\033[0m\n")
                    continue

                if os.path.isdir(f):
                    paths.insert(0, f)
                else:
                    sys.stderr.write("\033[31mrm\033[0m \033[1m" + f_rel + "\033[0m\n")
                    os.remove(f)
        # filter out directories that are not empty
        paths = [p for p in paths if not os.listdir(p)]
        for p in paths:
            p_rel = os.path.relpath(p)
            sys.stderr.write("\033[31mrmdir\033[0m \033[1m" + p_rel + "\033[0m\n")
            os.removedirs(p)