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
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)
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')
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')
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)
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
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)
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
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)
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)
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
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)
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())
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)
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, )
[], 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'])
def render(self, site=None): template = Registry.get('jinja_environment').get_template(self.source_path) return template.render(self.config, my=self, site=site)
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,)
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):
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)
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)
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', '*.*')
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)
# 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)
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)
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)
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'])
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
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)
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'])
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'])
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'])
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'])
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)