Example #1
0
def test_getitem_and_getattr():
    from strange_case.registry import Registry
    config = {
        'key': 'value',
        'friend ->': 'site.bob.joe'
    }
    n = Node(config, 'node')
    Registry.set('root', n)
    config = {
        'name': 'bob',
    }
    bob = Node(config, 'bob')
    n.append(bob)

    config = {
        'name': 'joe',
    }
    joe = Node(config, 'joe')
    bob.append(joe)

    assert n['key'] == 'value'
    assert n.key == 'value'
    assert n['bob'] == bob
    assert n.bob == bob
    assert n.friend == joe
Example #2
0
def build_node(config, source_path, target_path, file_name):
    source_file = os.path.join(source_path, file_name)

    config = Registry.configurate(source_file, config)
    if not config:
        return

    # create node(s). if you specify a 'type' it will override the default.
    # built-in types are 'page', 'folder', and 'asset'

    processor = config['type']
    return Registry.nodes(processor, config, source_file, target_path)
Example #3
0
 def __init__(self, name='test', addl_config={}):
     config = {
         'target_name': name,
         'name': name,
         'iterable': True,
         'root_url': '/',
     }
     config.update(addl_config)
     Registry.set('root', self)
     super(TestRootNode, self).__init__(config=config,
                               source_path=get_test_file('a_folder/'),
                               target_folder='target')
Example #4
0
 def __init__(self, name='test', addl_config={}):
     config = {
         'target_name': name,
         'name': name,
         'iterable': True,
         'root_url': '/',
     }
     config.update(addl_config)
     Registry.set('root', self)
     super(MockingRootNode,
           self).__init__(config=config,
                          source_path=get_test_file('a_folder/'),
                          target_folder='target')
Example #5
0
    def __getattr__(self, key):
        if key == 'config':
            return {}

        if key in self.config:
            return self.config.get(key)

        child_by_name = [child for child in self.children if child.name == key]
        if len(child_by_name) > 1:
            raise KeyError(
                'There are multiple results for the node named "{0}" '
                'in folder "{1}"'.format(key, self.name))
        elif child_by_name:
            return child_by_name[0]

        # look for a "pointer" attribute
        if not key.endswith(' ->'):
            try:
                ret = self[key + ' ->']
                path = ret.split('.')
                assert path.pop(0) == 'site'
                search_in = Registry.get('root')
                while path:
                    part = path.pop(0)
                    search_in = search_in[part]
                return search_in
            except AttributeError:
                pass

        raise AttributeError(key)
Example #6
0
    def render(self, site=None):
        try:
            env = Registry.get("plywood_environment")
            template_path = fix_path(self.source_path)
            with open(template_path) as f:
                contents = f.read()
                front_matter_match = re.match(r"\A([-]{3,}|[`]{3,})(\r\n|\r|\n)", contents)
                number_yaml_lines = 0
                if front_matter_match:
                    newline = front_matter_match.group(2)  # group(2) contains the newline/CRLF
                    number_yaml_lines += 1
                    offset = len(front_matter_match.group(0))
                    delim = re.compile("^" + front_matter_match.group(1) + "$")
                    lines = contents.splitlines()
                    for line in lines[1:]:  # skip the first line, the opening front matter
                        offset += len(line) + len(newline)
                        number_yaml_lines += 1
                        if delim.match(line):
                            break
                    contents = (newline * number_yaml_lines) + contents[offset:]

        except UnicodeDecodeError as e:
            e.args += "Could not process '%s' because of unicode error." % self.source_path
            raise
        env.scope.push()
        env.scope["site"] = site
        env.scope["my"] = self
        retval = Plywood(contents).run(self.config, env)
        env.scope.pop()
        return retval
Example #7
0
 def render(self, site=None):
     try:
         template = Registry.get('jinja_environment').get_template(fix_path(self.source_path))
     except UnicodeDecodeError as e:
         e.args += "Could not process '%s' because of unicode error." % self.source_path
         raise
     return template.render(self.config, my=self, site=site)
Example #8
0
    def render(self, site=None):
        try:
            env = Registry.get('plywood_environment')
            template_path = fix_path(self.source_path)
            with open(template_path) as f:
                contents = f.read()
                front_matter_match = re.match(
                    r"\A([-]{3,}|[`]{3,})(\r\n|\r|\n)", contents)
                number_yaml_lines = 0
                if front_matter_match:
                    newline = front_matter_match.group(
                        2)  # group(2) contains the newline/CRLF
                    number_yaml_lines += 1
                    offset = len(front_matter_match.group(0))
                    delim = re.compile("^" + front_matter_match.group(1) + "$")
                    lines = contents.splitlines()
                    for line in lines[
                            1:]:  # skip the first line, the opening front matter
                        offset += len(line) + len(newline)
                        number_yaml_lines += 1
                        if delim.match(line):
                            break
                    contents = (newline *
                                number_yaml_lines) + contents[offset:]

        except UnicodeDecodeError as e:
            e.args += "Could not process '%s' because of unicode error." % self.source_path
            raise
        env.scope.push()
        env.scope['site'] = site
        env.scope['my'] = self
        retval = Plywood(contents).run(self.config, env)
        env.scope.pop()
        return retval
Example #9
0
    def __getattr__(self, key):
        if key == 'config':
            return {}

        if key in self.config:
            return self.config.get(key)

        child_by_name = [child for child in self.children if child.name == key]
        if len(child_by_name) > 1:
            raise KeyError('There are multiple results for the node named "{}" '
                'in folder "{}"'.format(key, self.name))
        elif child_by_name:
            return child_by_name[0]

        # look for a "pointer" attribute
        if not key.endswith(' ->'):
            try:
                ret = self[key + ' ->']
                path = ret.split('.')
                assert path.pop(0) == 'site'
                search_in = Registry.get('root')
                while path:
                    part = path.pop(0)
                    search_in = search_in[part]
                return search_in
            except AttributeError:
                pass

        raise AttributeError(key)
Example #10
0
 def render(self, site=None):
     # print "self.source_path is '{}', fixed to '{}'".format(self.source_path, fix_path(self.source_path))
     try:
         template = Registry.get("jinja_environment").get_template(fix_path(os.path.relpath(self.source_path)))
     except UnicodeDecodeError as e:
         e.args += "Could not process '%s' because of unicode error." % self.source_path
         raise
     return template.render(self.config, my=self, site=site)
Example #11
0
    def populate(self, site):
        ret = []
        nodes = [node for node in self.parent if node.is_page]
        if page_order.upper() == 'DESC':
            nodes.reverse()
        pages = paginate(nodes, page_limit)

        first_page = None
        last_page = None

        for page in pages:
            if not len(ret):  # is this the first page?
                page_config = self.config_copy(True)  # copy *all* config, even name and title.
            else:
                name = "%s%i" % (page_name, 1 + len(ret))
                target_name = name + config['html_extension']
                page_config = self.config_copy(
                    name=name,
                    target_name=target_name,
                    )
            # page configuration can be further customized by creating a
            # pages: {} dictionary.  The key names should be the page index
            page_index = 1 + len(ret)
            page_config.setdefault('title', "%s %i" % (page_title, page_index))
            page_config.setdefault('page', page)
            page_config.setdefault('iterable', False)
            Registry.configurate(source_path, page_config)
            more_page_config = self.config.get('pages', {}).get(page_index, None)
            if more_page_config:
                page_config.update(more_page_config)
            node = JinjaNode(page_config, source_path, target_path)

            # now that we have node objects we can assign prev and next properties onto
            # the page object.
            page.prev = last_page
            page.next = None
            if last_page:
                last_page.page.next = node
            ret.append(node)
            last_page = node

        for node in ret:
            node.page.first = first_page
            node.page.last = last_page
        return ret
Example #12
0
def processor(config, source_path, target_path):
    """
    If 'thumbnails' or 'thumbnail' has been set, thumbnails will be generated.

    Examples:
        thumbnail: 75x150  # one thumbnail, specifying width and height as a string
        thumbnails:  # multiple thumbnails, by name
            medium: [200, 200]  # array of [width, height]
            large: 1000         # max dimension for both width and height
        thumbnails:  # multiple thumbnails by index
            - 200  # this will be image_node.thumbnail
            - 1000  # this will be image_node.thumbnail_2
    """
    image_node = ImageNode(config, source_path, target_path)

    if 'thumbnail' in config and 'thumbnails' not in config:
        config['thumbnails'] = {'thumbnail': config['thumbnail']}

    if 'thumbnails' not in config:
        return (image_node,)

    thumbs = []
    thumb_index = 0
    for thumbnail in config['thumbnails']:
        if isinstance(config['thumbnails'], list):
            thumb_index += 1
            size = thumbnail
            thumbnail = thumb_index = 1 and 'thumbnail' or 'thumbnail_' + thumb_index
        else:
            size = config['thumbnails'][thumbnail]
        target_name, ext = os.path.splitext(image_node.target_name)
        target_name += '_' + thumbnail
        target_name += ext
        thumb_config = image_node.config_copy(name=thumbnail, target_name=target_name)
        thumb_config['size'] = size
        thumb_config['iterable'] = False
        thumb_config['is_thumbnail'] = True
        thumb_config['skip'] = config['skip']

        Registry.configurate(os.path.join(source_path, target_name), thumb_config)
        thumbnail_node = ImageNode(thumb_config, source_path, target_path)
        image_node.config[thumbnail] = thumbnail_node
        thumbs.append(thumbnail_node)
    return (image_node, ) + tuple(thumbs)
Example #13
0
    def populate(self, site):
        pages = site.pages(recursive=True)
        categories = {}
        for page in pages:
            if not page['category']:
                continue

            if page.category not in categories:
                config = self.config_copy()
                target_name = re.sub(r'[\W -]+', '_', page.category, re.UNICODE)
                config['name'] = target_name
                config['target_name'] = target_name + config['html_extension']
                Registry.configurate(CategoryDetail.source_path, config)
                categories[page.category] = CategoryDetail(config, self.target_folder, page.category)

            categories[page.category].count += 1
            categories[page.category].pages.append(page)

        # assign categories list to the category index page
        if CategoryDetail.index_node:
            CategoryDetail.index_node.config.setdefault('categories', categories.values())
        self.replace_with(categories.values())
Example #14
0
def build_node(config, source_path, target_path, file_name):
    source_file = os.path.join(source_path, file_name)

    configurators = Registry.configurators
    # Run the config through each configurator.
    # If a configurator returns a falsey
    # value, the node will be ignored.
    for configurator in configurators:
        config = configurator(source_file, config)
        if not config:
            return

    # create node(s). if you specify a 'type' it will override the default.
    # built-in types are 'page', 'folder', and 'asset'

    processor = config['type']
    return Registry.nodes(processor, config, source_file, target_path)
Example #15
0
def processor(config, source_path, target_path):
    Registry.configurate(source_path, config)
    categories_name = config.get('name', 'categories')

    folder_config = config.copy()
    folder_config['target_name'] = categories_name
    folder_config['name'] = categories_name
    Registry.configurate(source_path, folder_config)
    folder = FolderNode(folder_config, None, target_path)
    categories_folder_target_path = os.path.join(target_path, categories_name)

    config['dont_inherit'] = [key for key in config['dont_inherit'] if key != 'title']
    index_config = config.copy()
    index_config['target_name'] = config['index.html']
    index_config['name'] = 'index'
    Registry.configurate(os.path.join(source_path, index_config['target_name']), index_config)
    index_node = JinjaNode(index_config, source_path, categories_folder_target_path)
    folder.append(index_node)
    CategoryDetail.index_node = index_node

    # attach Processor node - this is the class that will scan the pages for
    # the `category` property:
    folder.append(CategoryFolderProcesser(config, categories_folder_target_path))
    return (folder, )
Example #16
0
                                      [], body).set_lineno(lineno)

    def _scss_support(self, caller):
        return scss_compiler(caller()).strip()


class ScssNode(AssetNode):
    """
    Converts a .scss file into css
    """
    def generate_file(self, site, source_path, target_path):
        if not self['skip']:
            scss_content = open(source_path, 'r').read()
            css_content = scss_compiler(scss_content)
            with open(target_path, 'w') as f:
                f.write(css_content)
        self.files_tracked.append(source_path)
        self.files_written.append(target_path)


def processor(config, source_path, target_path):
    if config['target_name'].endswith('.scss'):
        config['target_name'] = config['target_name'][:-4] + 'css'

    scss_node = ScssNode(config, source_path, target_path)
    return (scss_node, )


Registry.register('scss', processor)
Registry.associate('scss', ['*.scss'])
Example #17
0
 def render(self, site=None):
     template = Registry.get('jinja_environment').get_template(self.source_path)
     return template.render(self.config, my=self, site=site)
Example #18
0
def page_processor(config, source_path, target_path):
    page_type = config["page_type"]
    node_class = Registry.get_engine(page_type)
    node = node_class(config, source_path, target_path)
    return (node,)
Example #19
0
    files = os.listdir(source_path)
    files.sort()
    for file_name in files:
        nodes = build_node(parent_node.config_copy(), source_path, target_path, file_name)
        if nodes:
            parent_node.extend(nodes)


def root_processor(config, deploy_path, target_path):
    node = RootFolderNode(config, deploy_path, target_path)

    build_node_tree(node, deploy_path, target_path)
    return (node,)


Registry.register("root", root_processor)


def folder_processor(config, source_path, target_path):
    node = FolderNode(config, source_path, target_path)

    target_path = os.path.join(target_path, node.target_name)
    if source_path and os.path.isdir(source_path):
        build_node_tree(node, source_path, target_path)
    return (node,)


Registry.register("folder", folder_processor)


def asset_processor(config, source_path, target_path):
Example #20
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)
Example #21
0
def root_processor(config, deploy_path, target_path):
    node = RootFolderNode(config, deploy_path, target_path)

    build_node_tree(node, deploy_path, target_path)
    return (node, )


def folder_processor(config, source_path, target_path):
    node = FolderNode(config, source_path, target_path)

    target_path = os.path.join(target_path, node.target_name)
    if source_path and os.path.isdir(source_path):
        build_node_tree(node, source_path, target_path)
    return (node, )


def asset_processor(config, source_path, target_path):
    node = AssetNode(config, source_path, target_path)
    return (node, )


def page_processor(config, source_path, target_path):
    node = JinjaNode(config, source_path, target_path)
    return (node, )


Registry.register('root', root_processor)
Registry.register('folder', folder_processor)
Registry.register('asset', asset_processor)
Registry.register('page', page_processor)
Example #22
0
from strange_case.nodes import CategoryFolderProcesser, FolderNode, JinjaNode, CategoryDetail
from copy import deepcopy


def category_index_processor(config, source_path, target_path):
    categories_name = 'categories'

    folder_config = deepcopy(config)
    folder_config['target_name'] = categories_name
    folder_config['name'] = categories_name
    folder = FolderNode(folder_config, None, target_path)

    index_config = deepcopy(config)
    index_config['target_name'] = config['index']
    index_config['name'] = 'index'
    categories_target_path = os.path.join(target_path, categories_name)
    index = JinjaNode(index_config, source_path, categories_target_path)

    folder.append(index)
    folder.append(CategoryFolderProcesser(config, categories_target_path))
    return (folder, )


def category_detail_processer(config, source_path, target_path):
    CategoryDetail.source_path = source_path
    return ()


Registry.register('category_index', category_index_processor)
Registry.register('category_detail', category_detail_processer)
import os
from strange_case.strange_case_config import CONFIG
from strange_case.registry import Registry
from strange_case.extensions.configurator_title_from_name import titlecase


def project_title(CONFIG):
    if CONFIG.get('project', None) is None:
        import sys
        sys.stderr.write("Usage:\n    scase project:hyphenated-name [desc:description]\n\n'project' is required\n")
        sys.exit(1)

    CONFIG['project'] = CONFIG['project'].replace('-', '_')
    CONFIG['Project'] = titlecase(CONFIG['project'])
    CONFIG['ProJect'] = CONFIG['Project'].replace(' ', '')
    CONFIG['deploy_path'] = os.path.abspath(os.path.dirname(__file__) + '/../' + CONFIG['ProJect'])

CONFIG['config_hook'] = project_title
CONFIG['len'] = len
CONFIG['deploy_path'] = 'ignore'

Registry.associate('page', '*.*')
Example #24
0
def register_category_page(category, source_path):
    if category in CategoryDetail.source_paths:
        raise TypeError('Duplicate CategoryDetail page registered for "%s"' %
                        (category or '(default category page)'))
    CategoryDetail.source_paths[category] = source_path


def detail_processor(config, source_path, target_path):
    # just store the source path - when the detail pages get created, they
    # will use this path.
    config = configurate(source_path, config)

    if 'category' in config:
        register_category_page(config['category'], source_path)
    elif 'categories' in config:
        for category in config['categories']:
            register_category_page(category, source_path)
    else:
        register_category_page(None, source_path)
    return ()


def reset_category_detail(config):
    CategoryDetail.source_paths = {}


Registry.listen('on_start', reset_category_detail)
Registry.register('category_index', index_processor)
Registry.register('category_detail', detail_processor)
Example #25
0
            # page configuration can be further customized by creating a
            # pages: {} dictionary.  The key names should be the page index
            page_index = 1 + len(ret)
            page_config.setdefault('title', "%s %i" % (page_title, page_index))
            page_config.setdefault('page', page)
            page_config.setdefault('iterable', False)
            configurate(source_path, page_config)
            more_page_config = self.config.get('pages', {}).get(page_index)
            if more_page_config:
                page_config.update(more_page_config)
            node = JinjaNode(page_config, source_path, target_path)

            # now that we have node objects we can assign prev and next properties onto
            # the page object.
            page.prev = last_page
            page.next = None
            if last_page:
                last_page.page.next = node
            ret.append(node)
            last_page = node

        for node in ret:
            node.page.first = first_page
            node.page.last = last_page
        return ret

    return (paginated_processor, )


Registry.register('paginated', paginated_processor)
Example #26
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)
Example #27
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)
Example #28
0
            # page configuration can be further customized by creating a
            # pages: {} dictionary.  The key names should be the page index
            page_index = 1 + len(ret)
            page_config.setdefault('title', "%s %i" % (page_title, page_index))
            page_config.setdefault('page', page)
            page_config.setdefault('iterable', False)
            configurate(source_path, page_config)
            more_page_config = self.config.get('pages', {}).get(page_index)
            if more_page_config:
                page_config.update(more_page_config)
            node = JinjaNode(page_config, source_path, target_path)

            # now that we have node objects we can assign prev and next properties onto
            # the page object.
            page.prev = last_page
            page.next = None
            if last_page:
                last_page.page.next = node
            ret.append(node)
            last_page = node

        for node in ret:
            node.page.first = first_page
            node.page.last = last_page
        return ret

    return (paginated_processor, )


Registry.register('paginated', paginated_processor)
Example #29
0

class CleverCssNode(AssetNode):
    """
    Converts a .ccss file into css
    """
    def generate_file(self, site, source_path, target_path):
        if self['skip']:
            print 'Skipping %s' % target_path
        if not self['skip']:
            ccss_content = open(source_path, 'r').read()
            css_content = clevercss_compiler(ccss_content)
            with open(target_path, 'w') as f:
                f.write(css_content)
        self.files_tracked.append(source_path)
        self.files_written.append(target_path)


def processor(config, source_path, target_path):
    if config['target_name'].endswith('ccss'):
        config['target_name'] = config['target_name'][:-4] + 'css'
    if config['target_name'].endswith('clevercss'):
        config['target_name'] = config['target_name'][:-9] + 'css'

    ccss_node = CleverCssNode(config, source_path, target_path)
    return (ccss_node,)


Registry.register('clevercss', processor)
Registry.associate('clevercss', ['*.ccss', '*.clevercss'])
Example #30
0
def strange_case(config):
    # pull out important values.
    site_path = config['site_path']
    deploy_path = config['deploy_path']

    # look for files in content/
    if not os.path.isdir(site_path):
        raise IOError('Could not find site_path folder "%s"' % site_path)

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

    remove_stale_files = config['remove_stale_files']
    dont_remove = config['dont_remove']
    existing_files = []
    if os.path.isdir(deploy_path):
        existing_files = __scan(deploy_path)
    else:
        os.makedirs(deploy_path, 0755)
    root_node.generate()

    # create timestamps file
    timestamps = {}
    for file_tracked in Node.files_tracked:
        f = os.path.abspath(file_tracked)
        timestamps[f] = os.stat(file_tracked).st_mtime
    timestamps_file = os.path.join(config['project_path'], '.timestamps')
    pickle.dump(timestamps, open(timestamps_file, 'w'))

    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)

    if notifier:
        try:
            growl = notifier.GrowlNotifier(
                applicationName="StrangeCase",
                notifications=["New Messages"],
                defaultNotifications=["New Messages"],
            )
            growl.register()

            # Send one message
            growl.notify(
                noteType="New Messages",
                title="StrangeCase site generated",
                description="site is available at:\n"
                    "{config[deploy_path]}"\
                    .format(config=config),
            )
        except socket.error:
            pass
Example #31
0
    def dec(function):
        my_name = name or function.__name__

        setattr(object, my_name, types.MethodType(function, object))
    return dec


def toc_processor(config, source_path, target_path):
    toc_processor = Processor(config)
    options = {
        'entries': int(config.get('toc', {}).get('entries', [])),
        'maxdepth': int(config.get('toc', {}).get('maxdepth', 3)),
        'numbered': int(config.get('toc', {}).get('numbered', False)),
        'titlesonly': int(config.get('toc', {}).get('titlesonly', False)),
        'glob': int(config.get('toc', {}).get('glob', False)),
        'hidden': int(config.get('toc', {}).get('hidden', [])),
    }

    @bind(toc_processor)
    def populate(self, site):
        ret = []
        page_config = self.config_copy(True)  # copy *all* config, even name and title.
        node = JinjaNode(page_config, source_path, target_path)
        ret.append(node)
        return ret

    return (toc_processor, )


Registry.register('toc', toc_processor)
Example #32
0
            body
        ).set_lineno(lineno)

    def _scss_support(self, caller):
        return scss_compiler(caller()).strip()


class ScssNode(AssetNode):
    """
    Converts a .scss file into css
    """
    def generate_file(self, site, source_path, target_path):
        if not self['skip']:
            scss_content = open(source_path, 'r').read()
            css_content = scss_compiler(scss_content)
            with open(target_path, 'w') as f:
                f.write(css_content)
        self.files_tracked.append(source_path)
        self.files_written.append(target_path)


def processor(config, source_path, target_path):
    if config['target_name'].endswith('.scss'):
        config['target_name'] = config['target_name'][:-4] + 'css'

    scss_node = ScssNode(config, source_path, target_path)
    return (scss_node,)

Registry.register('scss', processor)
Registry.associate('scss', ['*.scss'])
Example #33
0
    if 'thumbnails' not in config:
        return (image_node,)

    thumbs = []
    thumb_index = 0
    for thumbnail in config['thumbnails']:
        if isinstance(config['thumbnails'], list):
            thumb_index += 1
            size = thumbnail
            thumbnail = thumb_index = 1 and 'thumbnail' or 'thumbnail_' + thumb_index
        else:
            size = config['thumbnails'][thumbnail]
        target_name, ext = os.path.splitext(image_node.target_name)
        target_name += '_' + thumbnail
        target_name += ext
        thumb_config = image_node.config_copy(name=thumbnail, target_name=target_name)
        thumb_config['size'] = size
        thumb_config['iterable'] = False
        thumb_config['is_thumbnail'] = True
        thumb_config['skip'] = config['skip']

        configurate(os.path.join(source_path, target_name), thumb_config)
        thumbnail_node = ImageNode(thumb_config, source_path, target_path)
        image_node.config[thumbnail] = thumbnail_node
        thumbs.append(thumbnail_node)
    return (image_node, ) + tuple(thumbs)

Registry.register('image', processor)
Registry.associate('image', ['*.png', '*.jpg', '*.jpeg', '*.gif'])
Example #34
0

def register_category_page(category, source_path):
    if category in CategoryDetail.source_paths:
        raise TypeError('Duplicate CategoryDetail page registered for "%s"' % (category or '(default category page)'))
    CategoryDetail.source_paths[category] = source_path


def detail_processor(config, source_path, target_path):
    # just store the source path - when the detail pages get created, they
    # will use this path.
    config = configurate(source_path, config)

    if 'category' in config:
        register_category_page(config['category'], source_path)
    elif 'categories' in config:
        for category in config['categories']:
            register_category_page(category, source_path)
    else:
        register_category_page(None, source_path)
    return ()


def reset_category_detail(config):
    CategoryDetail.source_paths = {}


Registry.listen('on_start', reset_category_detail)
Registry.register('category_index', index_processor)
Registry.register('category_detail', detail_processor)
Example #35
0
    if 'thumbnails' not in config:
        return (image_node,)

    thumbs = []
    thumb_index = 0
    for thumbnail in config['thumbnails']:
        if isinstance(config['thumbnails'], list):
            thumb_index += 1
            size = thumbnail
            thumbnail = thumb_index = 1 and 'thumbnail' or 'thumbnail_' + thumb_index
        else:
            size = config['thumbnails'][thumbnail]
        target_name, ext = os.path.splitext(image_node.target_name)
        target_name += '_' + thumbnail
        target_name += ext
        thumb_config = image_node.config_copy(name=thumbnail, target_name=target_name)
        thumb_config['size'] = size
        thumb_config['iterable'] = False
        thumb_config['is_thumbnail'] = True
        thumb_config['skip'] = config['skip']

        configurate(os.path.join(source_path, target_name), thumb_config)
        thumbnail_node = ImageNode(thumb_config, source_path, target_path)
        image_node.config[thumbnail] = thumbnail_node
        thumbs.append(thumbnail_node)
    return (image_node, ) + tuple(thumbs)

Registry.register('image', processor)
Registry.associate('image', ['*.png', '*.jpg', '*.jpeg', '*.gif',
                             '*.PNG', '*.JPG', '*.JPEG', '*.GIF'])
Example #36
0
    if 'thumbnails' not in config:
        return (image_node,)

    thumbs = []
    thumb_index = 0
    for thumbnail in config['thumbnails']:
        if isinstance(config['thumbnails'], list):
            thumb_index += 1
            size = thumbnail
            thumbnail = thumb_index = 1 and 'thumbnail' or 'thumbnail_' + thumb_index
        else:
            size = config['thumbnails'][thumbnail]
        target_name, ext = os.path.splitext(image_node.target_name)
        target_name += '_' + thumbnail
        target_name += ext
        thumb_config = image_node.config_copy(name=thumbnail, target_name=target_name)
        thumb_config['size'] = size
        thumb_config['iterable'] = False
        thumb_config['is_thumbnail'] = True
        thumb_config['skip'] = config['skip']

        configurate(os.path.join(source_path, target_name), thumb_config)
        thumbnail_node = ImageNode(thumb_config, source_path, target_path)
        image_node.config[thumbnail] = thumbnail_node
        thumbs.append(thumbnail_node)
    return (image_node, ) + tuple(thumbs)

Registry.register('image', processor)
Registry.associate('image', ['*.png', '*.jpg', '*.jpeg', '*.gif',
                             '*.PNG', '*.JPG', '*.JPEG', '*.GIF'])
Example #37
0

class CleverCssNode(AssetNode):
    """
    Converts a .ccss file into css
    """
    def generate_file(self, site, source_path, target_path):
        if not self['skip']:
            ccss_content = open(source_path, 'r').read()
            css_content = clevercss_compiler(ccss_content)
            with open(target_path, 'w') as f:
                f.write(css_content)
        elif self['__verbose']:
            sys.stderr.write("Skipping %s\n" % target_path)
        self.files_tracked.append(source_path)
        self.files_written.append(target_path)


def processor(config, source_path, target_path):
    if config['target_name'].endswith('ccss'):
        config['target_name'] = config['target_name'][:-4] + 'css'
    if config['target_name'].endswith('clevercss'):
        config['target_name'] = config['target_name'][:-9] + 'css'

    ccss_node = CleverCssNode(config, source_path, target_path)
    return (ccss_node,)


Registry.register('clevercss', processor)
Registry.associate('clevercss', ['*.ccss', '*.clevercss'])
Example #38
0
import os
from strange_case.nodes import ImageNode
from strange_case.registry import Registry


def image_processor(config, source_path, target_path):
    image_node = ImageNode(config, source_path, target_path)
    if 'thumbnails' not in config:
        return (image_node,)

    thumbs = []
    for thumbnail in config['thumbnails']:
        target_name, ext = os.path.splitext(image_node.target_name)
        target_name += '_' + thumbnail
        target_name += ext
        thumb_config = image_node.config_copy(name=thumbnail, target_name=target_name)
        thumb_config['size'] = config['thumbnails'][thumbnail]
        thumb_config['iterable'] = False
        thumb_config['is_thumbnail'] = True

        thumbnail_node = ImageNode(thumb_config, source_path, target_path)
        image_node.config[thumbnail] = thumbnail_node
        thumbs.append(thumbnail_node)
    return (image_node, ) + tuple(thumbs)


Registry.register('image', image_processor)