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 __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 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)
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 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 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 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)