flake8 in integrationmain
| @@ -22,5 +22,7 @@ before_script: | |||||
| - export PYTHONPATH=$PYTHONPATH:/usr/share/asciidoc/ | - export PYTHONPATH=$PYTHONPATH:/usr/share/asciidoc/ | ||||
| script: | script: | ||||
| # Source code sanity check | |||||
| - flake8 hyde | |||||
| # Run Python tests and generate coverage statistics | # Run Python tests and generate coverage statistics | ||||
| - nosetests | - nosetests | ||||
| @@ -5,3 +5,4 @@ mock==1.0.1 | |||||
| nose==1.3.6 | nose==1.3.6 | ||||
| Pillow==2.7.0 | Pillow==2.7.0 | ||||
| pyScss==1.3.4 | pyScss==1.3.4 | ||||
| flake8==2.4.1 | |||||
| @@ -32,10 +32,11 @@ class Engine(Application): | |||||
| ) | ) | ||||
| @command(description='hyde - a python static website generator', | @command(description='hyde - a python static website generator', | ||||
| epilog='Use %(prog)s {command} -h to get help on individual commands') | |||||
| epilog='Use %(prog)s {command} -h to get help' | |||||
| 'on individual commands') | |||||
| @true('-v', '--verbose', help="Show detailed information in console") | @true('-v', '--verbose', help="Show detailed information in console") | ||||
| @true('-x', '--raise-exceptions', default=None, | @true('-x', '--raise-exceptions', default=None, | ||||
| help="Don't handle exceptions.") | |||||
| help="Don't handle exceptions.") | |||||
| @version('--version', version='%(prog)s ' + __version__) | @version('--version', version='%(prog)s ' + __version__) | ||||
| @store('-s', '--sitepath', default='.', help="Location of the hyde site") | @store('-s', '--sitepath', default='.', help="Location of the hyde site") | ||||
| def main(self, args): | def main(self, args): | ||||
| @@ -52,7 +53,7 @@ class Engine(Application): | |||||
| @subcommand('create', help='Create a new hyde site.') | @subcommand('create', help='Create a new hyde site.') | ||||
| @store('-l', '--layout', default='basic', help='Layout for the new site') | @store('-l', '--layout', default='basic', help='Layout for the new site') | ||||
| @true('-f', '--force', default=False, dest='overwrite', | @true('-f', '--force', default=False, dest='overwrite', | ||||
| help='Overwrite the current site if it exists') | |||||
| help='Overwrite the current site if it exists') | |||||
| def create(self, args): | def create(self, args): | ||||
| """ | """ | ||||
| The create command. Creates a new site from the template at the given | The create command. Creates a new site from the template at the given | ||||
| @@ -64,27 +65,27 @@ class Engine(Application): | |||||
| if exists and not args.overwrite: | if exists and not args.overwrite: | ||||
| raise HydeException( | raise HydeException( | ||||
| "The given site path [%s] already contains a hyde site." | |||||
| " Use -f to overwrite." % sitepath) | |||||
| "The given site path [%s] already contains a hyde site." | |||||
| " Use -f to overwrite." % sitepath) | |||||
| layout = Layout.find_layout(args.layout) | layout = Layout.find_layout(args.layout) | ||||
| self.logger.info( | self.logger.info( | ||||
| "Creating site at [%s] with layout [%s]" % (sitepath, layout)) | "Creating site at [%s] with layout [%s]" % (sitepath, layout)) | ||||
| if not layout or not layout.exists: | if not layout or not layout.exists: | ||||
| raise HydeException( | raise HydeException( | ||||
| "The given layout is invalid. Please check if you have the" | |||||
| " `layout` in the right place and the environment variable(%s)" | |||||
| " has been setup properly if you are using custom path for" | |||||
| " layouts" % HYDE_DATA) | |||||
| "The given layout is invalid. Please check if you have the" | |||||
| " `layout` in the right place and the environment variable(%s)" | |||||
| " has been setup properly if you are using custom path for" | |||||
| " layouts" % HYDE_DATA) | |||||
| layout.copy_contents_to(args.sitepath) | layout.copy_contents_to(args.sitepath) | ||||
| self.logger.info("Site creation complete") | self.logger.info("Site creation complete") | ||||
| @subcommand('gen', help='Generate the site') | @subcommand('gen', help='Generate the site') | ||||
| @store('-c', '--config-path', default='site.yaml', dest='config', | @store('-c', '--config-path', default='site.yaml', dest='config', | ||||
| help='The configuration used to generate the site') | |||||
| help='The configuration used to generate the site') | |||||
| @store('-d', '--deploy-path', dest='deploy', default=None, | @store('-d', '--deploy-path', dest='deploy', default=None, | ||||
| help='Where should the site be generated?') | |||||
| help='Where should the site be generated?') | |||||
| @true('-r', '--regen', dest='regen', default=False, | @true('-r', '--regen', dest='regen', default=False, | ||||
| help='Regenerate the whole site, including unchanged files') | |||||
| help='Regenerate the whole site, including unchanged files') | |||||
| def gen(self, args): | def gen(self, args): | ||||
| """ | """ | ||||
| The generate command. Generates the site at the given | The generate command. Generates the site at the given | ||||
| @@ -103,13 +104,13 @@ class Engine(Application): | |||||
| @subcommand('serve', help='Serve the website') | @subcommand('serve', help='Serve the website') | ||||
| @store('-a', '--address', default='localhost', dest='address', | @store('-a', '--address', default='localhost', dest='address', | ||||
| help='The address where the website must be served from.') | |||||
| help='The address where the website must be served from.') | |||||
| @store('-p', '--port', type=int, default=8080, dest='port', | @store('-p', '--port', type=int, default=8080, dest='port', | ||||
| help='The port where the website must be served from.') | |||||
| help='The port where the website must be served from.') | |||||
| @store('-c', '--config-path', default='site.yaml', dest='config', | @store('-c', '--config-path', default='site.yaml', dest='config', | ||||
| help='The configuration used to generate the site') | |||||
| help='The configuration used to generate the site') | |||||
| @store('-d', '--deploy-path', dest='deploy', default=None, | @store('-d', '--deploy-path', dest='deploy', default=None, | ||||
| help='Where should the site be generated?') | |||||
| help='Where should the site be generated?') | |||||
| def serve(self, args): | def serve(self, args): | ||||
| """ | """ | ||||
| The serve command. Serves the site at the given | The serve command. Serves the site at the given | ||||
| @@ -120,7 +121,8 @@ class Engine(Application): | |||||
| site = self.make_site(sitepath, args.config, args.deploy) | site = self.make_site(sitepath, args.config, args.deploy) | ||||
| from hyde.server import HydeWebServer | from hyde.server import HydeWebServer | ||||
| server = HydeWebServer(site, args.address, args.port) | server = HydeWebServer(site, args.address, args.port) | ||||
| self.logger.info("Starting webserver at [%s]:[%d]", args.address, args.port) | |||||
| self.logger.info( | |||||
| "Starting webserver at [%s]:[%d]", args.address, args.port) | |||||
| try: | try: | ||||
| server.serve_forever() | server.serve_forever() | ||||
| except (KeyboardInterrupt, SystemExit): | except (KeyboardInterrupt, SystemExit): | ||||
| @@ -131,11 +133,11 @@ class Engine(Application): | |||||
| @subcommand('publish', help='Publish the website') | @subcommand('publish', help='Publish the website') | ||||
| @store('-c', '--config-path', default='site.yaml', dest='config', | @store('-c', '--config-path', default='site.yaml', dest='config', | ||||
| help='The configuration used to generate the site') | |||||
| help='The configuration used to generate the site') | |||||
| @store('-p', '--publisher', dest='publisher', default='default', | @store('-p', '--publisher', dest='publisher', default='default', | ||||
| help='Points to the publisher configuration.') | |||||
| help='Points to the publisher configuration.') | |||||
| @store('-m', '--message', dest='message', | @store('-m', '--message', dest='message', | ||||
| help='Optional message.') | |||||
| help='Optional message.') | |||||
| def publish(self, args): | def publish(self, args): | ||||
| """ | """ | ||||
| Publishes the site based on the configuration from the `target` | Publishes the site based on the configuration from the `target` | ||||
| @@ -145,11 +147,10 @@ class Engine(Application): | |||||
| site = self.make_site(sitepath, args.config) | site = self.make_site(sitepath, args.config) | ||||
| from hyde.publisher import Publisher | from hyde.publisher import Publisher | ||||
| publisher = Publisher.load_publisher(site, | publisher = Publisher.load_publisher(site, | ||||
| args.publisher, | |||||
| args.message) | |||||
| args.publisher, | |||||
| args.message) | |||||
| publisher.publish() | publisher.publish() | ||||
| def make_site(self, sitepath, config, deploy=None): | def make_site(self, sitepath, config, deploy=None): | ||||
| """ | """ | ||||
| Creates a site object from the given sitepath and the config file. | Creates a site object from the given sitepath and the config file. | ||||
| @@ -157,4 +158,4 @@ class Engine(Application): | |||||
| config = Config(sitepath, config_file=config) | config = Config(sitepath, config_file=config) | ||||
| if deploy: | if deploy: | ||||
| config.deploy_root = deploy | config.deploy_root = deploy | ||||
| return Site(sitepath, config) | |||||
| return Site(sitepath, config) | |||||
| @@ -1,4 +1,5 @@ | |||||
| class HydeException(Exception): | class HydeException(Exception): | ||||
| """ | """ | ||||
| Base class for exceptions from hyde | Base class for exceptions from hyde | ||||
| """ | """ | ||||
| @@ -7,5 +8,3 @@ class HydeException(Exception): | |||||
| def reraise(message, exc_info): | def reraise(message, exc_info): | ||||
| _, _, tb = exc_info | _, _, tb = exc_info | ||||
| raise HydeException(message), None, tb | raise HydeException(message), None, tb | ||||
| @@ -10,13 +10,12 @@ from hyde.plugin import Plugin | |||||
| class DraftsPlugin(Plugin): | class DraftsPlugin(Plugin): | ||||
| def begin_site(self): | def begin_site(self): | ||||
| in_production = self.site.config.mode.startswith('prod') | in_production = self.site.config.mode.startswith('prod') | ||||
| if not in_production: | if not in_production: | ||||
| self.logger.info( | |||||
| 'Generating draft posts as the site is not in production mode.') | |||||
| self.logger.info('Generating draft posts as the site is' | |||||
| 'not in production mode.') | |||||
| return | return | ||||
| for resource in self.site.content.walk_resources(): | for resource in self.site.content.walk_resources(): | ||||
| @@ -33,4 +32,4 @@ class DraftsPlugin(Plugin): | |||||
| self.logger.info( | self.logger.info( | ||||
| '%s is%s draft' % (resource, | '%s is%s draft' % (resource, | ||||
| '' if is_draft else ' not')) | |||||
| '' if is_draft else ' not')) | |||||
| @@ -18,7 +18,9 @@ from fswrap import File | |||||
| # Less CSS | # Less CSS | ||||
| # | # | ||||
| class LessCSSPlugin(CLTransformer): | class LessCSSPlugin(CLTransformer): | ||||
| """ | """ | ||||
| The plugin class for less css | The plugin class for less css | ||||
| """ | """ | ||||
| @@ -29,7 +31,6 @@ class LessCSSPlugin(CLTransformer): | |||||
| re.compile('^\\s*@import\s+(?:\'|\")([^\'\"]*)(?:\'|\")\s*\;\s*$', | re.compile('^\\s*@import\s+(?:\'|\")([^\'\"]*)(?:\'|\")\s*\;\s*$', | ||||
| re.MULTILINE) | re.MULTILINE) | ||||
| @property | @property | ||||
| def executable_name(self): | def executable_name(self): | ||||
| return "lessc" | return "lessc" | ||||
| @@ -39,7 +40,7 @@ class LessCSSPlugin(CLTransformer): | |||||
| Check user defined | Check user defined | ||||
| """ | """ | ||||
| return resource.source_file.kind == 'less' and \ | return resource.source_file.kind == 'less' and \ | ||||
| getattr(resource, 'meta', {}).get('parse', True) | |||||
| getattr(resource, 'meta', {}).get('parse', True) | |||||
| def _should_replace_imports(self, resource): | def _should_replace_imports(self, resource): | ||||
| return getattr(resource, 'meta', {}).get('uses_template', True) | return getattr(resource, 'meta', {}).get('uses_template', True) | ||||
| @@ -72,13 +73,13 @@ class LessCSSPlugin(CLTransformer): | |||||
| afile = File(afile.path + '.less') | afile = File(afile.path + '.less') | ||||
| ref = self.site.content.resource_from_path(afile.path) | ref = self.site.content.resource_from_path(afile.path) | ||||
| if not ref: | if not ref: | ||||
| raise HydeException("Cannot import from path [%s]" % afile.path) | |||||
| raise HydeException( | |||||
| "Cannot import from path [%s]" % afile.path) | |||||
| ref.is_processable = False | ref.is_processable = False | ||||
| return self.template.get_include_statement(ref.relative_path) | return self.template.get_include_statement(ref.relative_path) | ||||
| text = self.import_finder.sub(import_to_include, text) | text = self.import_finder.sub(import_to_include, text) | ||||
| return text | return text | ||||
| @property | @property | ||||
| def plugin_name(self): | def plugin_name(self): | ||||
| """ | """ | ||||
| @@ -114,10 +115,10 @@ class LessCSSPlugin(CLTransformer): | |||||
| try: | try: | ||||
| self.call_app(args) | self.call_app(args) | ||||
| except subprocess.CalledProcessError: | except subprocess.CalledProcessError: | ||||
| HydeException.reraise( | |||||
| "Cannot process %s. Error occurred when " | |||||
| "processing [%s]" % (self.app.name, resource.source_file), | |||||
| sys.exc_info()) | |||||
| HydeException.reraise( | |||||
| "Cannot process %s. Error occurred when " | |||||
| "processing [%s]" % (self.app.name, resource.source_file), | |||||
| sys.exc_info()) | |||||
| return target.read_all() | return target.read_all() | ||||
| @@ -127,6 +128,7 @@ class LessCSSPlugin(CLTransformer): | |||||
| # | # | ||||
| class StylusPlugin(CLTransformer): | class StylusPlugin(CLTransformer): | ||||
| """ | """ | ||||
| The plugin class for stylus css | The plugin class for stylus css | ||||
| """ | """ | ||||
| @@ -162,7 +164,8 @@ class StylusPlugin(CLTransformer): | |||||
| if not match.lastindex: | if not match.lastindex: | ||||
| return '' | return '' | ||||
| path = match.groups(1)[0] | path = match.groups(1)[0] | ||||
| afile = File(File(resource.source_file.parent.child(path)).fully_expanded_path) | |||||
| first_child = resource.source_file.parent.child(path) | |||||
| afile = File(File(first_child).fully_expanded_path) | |||||
| if len(afile.kind.strip()) == 0: | if len(afile.kind.strip()) == 0: | ||||
| afile = File(afile.path + '.styl') | afile = File(afile.path + '.styl') | ||||
| @@ -179,8 +182,8 @@ class StylusPlugin(CLTransformer): | |||||
| else: | else: | ||||
| ref.is_processable = False | ref.is_processable = False | ||||
| return "\n" + \ | return "\n" + \ | ||||
| self.template.get_include_statement(ref.relative_path) + \ | |||||
| "\n" | |||||
| self.template.get_include_statement(ref.relative_path) + \ | |||||
| "\n" | |||||
| return '@import "' + path + '"\n' | return '@import "' + path + '"\n' | ||||
| text = self.import_finder.sub(import_to_include, text) | text = self.import_finder.sub(import_to_include, text) | ||||
| @@ -196,7 +199,7 @@ class StylusPlugin(CLTransformer): | |||||
| except AttributeError: | except AttributeError: | ||||
| mode = "production" | mode = "production" | ||||
| defaults = {"compress":""} | |||||
| defaults = {"compress": ""} | |||||
| if mode.startswith('dev'): | if mode.startswith('dev'): | ||||
| defaults = {} | defaults = {} | ||||
| return defaults | return defaults | ||||
| @@ -227,9 +230,9 @@ class StylusPlugin(CLTransformer): | |||||
| self.call_app(args) | self.call_app(args) | ||||
| except subprocess.CalledProcessError: | except subprocess.CalledProcessError: | ||||
| HydeException.reraise( | HydeException.reraise( | ||||
| "Cannot process %s. Error occurred when " | |||||
| "processing [%s]" % (stylus.name, resource.source_file), | |||||
| sys.exc_info()) | |||||
| "Cannot process %s. Error occurred when " | |||||
| "processing [%s]" % (stylus.name, resource.source_file), | |||||
| sys.exc_info()) | |||||
| target = File(source.path + '.css') | target = File(source.path + '.css') | ||||
| return target.read_all() | return target.read_all() | ||||
| @@ -239,6 +242,7 @@ class StylusPlugin(CLTransformer): | |||||
| # | # | ||||
| class CleverCSSPlugin(Plugin): | class CleverCSSPlugin(Plugin): | ||||
| """ | """ | ||||
| The plugin class for CleverCSS | The plugin class for CleverCSS | ||||
| """ | """ | ||||
| @@ -257,7 +261,7 @@ class CleverCSSPlugin(Plugin): | |||||
| Check user defined | Check user defined | ||||
| """ | """ | ||||
| return resource.source_file.kind == 'ccss' and \ | return resource.source_file.kind == 'ccss' and \ | ||||
| getattr(resource, 'meta', {}).get('parse', True) | |||||
| getattr(resource, 'meta', {}).get('parse', True) | |||||
| def _should_replace_imports(self, resource): | def _should_replace_imports(self, resource): | ||||
| return getattr(resource, 'meta', {}).get('uses_template', True) | return getattr(resource, 'meta', {}).get('uses_template', True) | ||||
| @@ -282,8 +286,8 @@ class CleverCSSPlugin(Plugin): | |||||
| return text | return text | ||||
| import_finder = re.compile( | import_finder = re.compile( | ||||
| '^\\s*@import\s+(?:\'|\")([^\'\"]*)(?:\'|\")\s*\;\s*$', | |||||
| re.MULTILINE) | |||||
| '^\\s*@import\s+(?:\'|\")([^\'\"]*)(?:\'|\")\s*\;\s*$', | |||||
| re.MULTILINE) | |||||
| def import_to_include(match): | def import_to_include(match): | ||||
| if not match.lastindex: | if not match.lastindex: | ||||
| @@ -294,7 +298,8 @@ class CleverCSSPlugin(Plugin): | |||||
| afile = File(afile.path + '.ccss') | afile = File(afile.path + '.ccss') | ||||
| ref = self.site.content.resource_from_path(afile.path) | ref = self.site.content.resource_from_path(afile.path) | ||||
| if not ref: | if not ref: | ||||
| raise HydeException("Cannot import from path [%s]" % afile.path) | |||||
| raise HydeException( | |||||
| "Cannot import from path [%s]" % afile.path) | |||||
| ref.is_processable = False | ref.is_processable = False | ||||
| return self.template.get_include_statement(ref.relative_path) | return self.template.get_include_statement(ref.relative_path) | ||||
| text = import_finder.sub(import_to_include, text) | text = import_finder.sub(import_to_include, text) | ||||
| @@ -313,7 +318,9 @@ class CleverCSSPlugin(Plugin): | |||||
| # Sassy CSS | # Sassy CSS | ||||
| # | # | ||||
| class SassyCSSPlugin(Plugin): | class SassyCSSPlugin(Plugin): | ||||
| """ | """ | ||||
| The plugin class for SassyCSS | The plugin class for SassyCSS | ||||
| """ | """ | ||||
| @@ -332,7 +339,7 @@ class SassyCSSPlugin(Plugin): | |||||
| Check user defined | Check user defined | ||||
| """ | """ | ||||
| return resource.source_file.kind == 'scss' and \ | return resource.source_file.kind == 'scss' and \ | ||||
| getattr(resource, 'meta', {}).get('parse', True) | |||||
| getattr(resource, 'meta', {}).get('parse', True) | |||||
| @property | @property | ||||
| def options(self): | def options(self): | ||||
| @@ -364,7 +371,6 @@ class SassyCSSPlugin(Plugin): | |||||
| """ | """ | ||||
| return self.settings.get('includes', []) | return self.settings.get('includes', []) | ||||
| def begin_site(self): | def begin_site(self): | ||||
| """ | """ | ||||
| Find all the sassycss files and set their relative deploy path. | Find all the sassycss files and set their relative deploy path. | ||||
| @@ -373,7 +379,7 @@ class SassyCSSPlugin(Plugin): | |||||
| self.scss.STATIC_ROOT = self.site.config.content_root_path.path | self.scss.STATIC_ROOT = self.site.config.content_root_path.path | ||||
| self.scss.ASSETS_URL = self.site.media_url('/') | self.scss.ASSETS_URL = self.site.media_url('/') | ||||
| self.scss.ASSETS_ROOT = self.site.config.deploy_root_path.child( | self.scss.ASSETS_ROOT = self.site.config.deploy_root_path.child( | ||||
| self.site.config.media_root) | |||||
| self.site.config.media_root) | |||||
| for resource in self.site.content.walk_resources(): | for resource in self.site.content.walk_resources(): | ||||
| if self._should_parse_resource(resource): | if self._should_parse_resource(resource): | ||||
| @@ -391,8 +397,8 @@ class SassyCSSPlugin(Plugin): | |||||
| includes = [resource.node.path] + self.includes | includes = [resource.node.path] + self.includes | ||||
| includes = [path.rstrip(os.sep) + os.sep for path in includes] | includes = [path.rstrip(os.sep) + os.sep for path in includes] | ||||
| options = self.options | options = self.options | ||||
| if not 'load_paths' in options: | |||||
| if 'load_paths' not in options: | |||||
| options['load_paths'] = [] | options['load_paths'] = [] | ||||
| options['load_paths'].extend(includes) | options['load_paths'].extend(includes) | ||||
| scss = self.scss.Scss(scss_opts=options, scss_vars=self.vars ) | |||||
| scss = self.scss.Scss(scss_opts=options, scss_vars=self.vars) | |||||
| return scss.compile(text) | return scss.compile(text) | ||||
| @@ -7,7 +7,9 @@ Depends plugin | |||||
| from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
| class DependsPlugin(Plugin): | class DependsPlugin(Plugin): | ||||
| """ | """ | ||||
| The plugin class setting explicit dependencies. | The plugin class setting explicit dependencies. | ||||
| """ | """ | ||||
| @@ -16,15 +18,14 @@ class DependsPlugin(Plugin): | |||||
| super(DependsPlugin, self).__init__(site) | super(DependsPlugin, self).__init__(site) | ||||
| def begin_site(self): | def begin_site(self): | ||||
| """ | |||||
| Initialize dependencies. | |||||
| Go through all the nodes and resources to initialize | |||||
| dependencies at each level. | |||||
| """ | |||||
| for resource in self.site.content.walk_resources(): | |||||
| self._update_resource(resource) | |||||
| """ | |||||
| Initialize dependencies. | |||||
| Go through all the nodes and resources to initialize | |||||
| dependencies at each level. | |||||
| """ | |||||
| for resource in self.site.content.walk_resources(): | |||||
| self._update_resource(resource) | |||||
| def _update_resource(self, resource): | def _update_resource(self, resource): | ||||
| """ | """ | ||||
| @@ -53,7 +54,7 @@ class DependsPlugin(Plugin): | |||||
| for dep in depends: | for dep in depends: | ||||
| resource.depends.append(dep.format(node=resource.node, | resource.depends.append(dep.format(node=resource.node, | ||||
| resource=resource, | |||||
| site=self.site, | |||||
| context=self.site.context)) | |||||
| resource=resource, | |||||
| site=self.site, | |||||
| context=self.site.context)) | |||||
| resource.depends = list(set(resource.depends)) | resource.depends = list(set(resource.depends)) | ||||
| @@ -39,6 +39,7 @@ class PILPlugin(Plugin): | |||||
| class ImageSizerPlugin(PILPlugin): | class ImageSizerPlugin(PILPlugin): | ||||
| """ | """ | ||||
| Each HTML page is modified to add width and height for images if | Each HTML page is modified to add width and height for images if | ||||
| they are not already specified. | they are not already specified. | ||||
| @@ -50,26 +51,28 @@ class ImageSizerPlugin(PILPlugin): | |||||
| super(ImageSizerPlugin, self).__init__(site) | super(ImageSizerPlugin, self).__init__(site) | ||||
| self.cache = {} | self.cache = {} | ||||
| def _handle_img(self, resource, src, width, height): | def _handle_img(self, resource, src, width, height): | ||||
| """Determine what should be added to an img tag""" | """Determine what should be added to an img tag""" | ||||
| if height is not None and width is not None: | if height is not None and width is not None: | ||||
| return "" # Nothing | return "" # Nothing | ||||
| if src is None: | if src is None: | ||||
| self.logger.warn("[%s] has an img tag without src attribute" % resource) | |||||
| self.logger.warn( | |||||
| "[%s] has an img tag without src attribute" % resource) | |||||
| return "" # Nothing | return "" # Nothing | ||||
| if src not in self.cache: | if src not in self.cache: | ||||
| if src.startswith(self.site.config.media_url): | if src.startswith(self.site.config.media_url): | ||||
| path = src[len(self.site.config.media_url):].lstrip("/") | path = src[len(self.site.config.media_url):].lstrip("/") | ||||
| path = self.site.config.media_root_path.child(path) | path = self.site.config.media_root_path.child(path) | ||||
| image = self.site.content.resource_from_relative_deploy_path(path) | |||||
| image = self.site.content.resource_from_relative_deploy_path( | |||||
| path) | |||||
| elif re.match(r'([a-z]+://|//).*', src): | elif re.match(r'([a-z]+://|//).*', src): | ||||
| # Not a local link | # Not a local link | ||||
| return "" # Nothing | return "" # Nothing | ||||
| elif src.startswith("/"): | elif src.startswith("/"): | ||||
| # Absolute resource | # Absolute resource | ||||
| path = src.lstrip("/") | path = src.lstrip("/") | ||||
| image = self.site.content.resource_from_relative_deploy_path(path) | |||||
| image = self.site.content.resource_from_relative_deploy_path( | |||||
| path) | |||||
| else: | else: | ||||
| # Relative resource | # Relative resource | ||||
| path = resource.node.source_folder.child(src) | path = resource.node.source_folder.child(src) | ||||
| @@ -80,7 +83,7 @@ class ImageSizerPlugin(PILPlugin): | |||||
| return "" # Nothing | return "" # Nothing | ||||
| if image.source_file.kind not in ['png', 'jpg', 'jpeg', 'gif']: | if image.source_file.kind not in ['png', 'jpg', 'jpeg', 'gif']: | ||||
| self.logger.warn( | self.logger.warn( | ||||
| "[%s] has an img tag not linking to an image" % resource) | |||||
| "[%s] has an img tag not linking to an image" % resource) | |||||
| return "" # Nothing | return "" # Nothing | ||||
| # Now, get the size of the image | # Now, get the size of the image | ||||
| try: | try: | ||||
| @@ -96,9 +99,9 @@ class ImageSizerPlugin(PILPlugin): | |||||
| if new_width is None or new_height is None: | if new_width is None or new_height is None: | ||||
| return "" # Nothing | return "" # Nothing | ||||
| if width is not None: | if width is not None: | ||||
| return 'height="%d" ' % (int(width)*new_height/new_width) | |||||
| return 'height="%d" ' % (int(width) * new_height / new_width) | |||||
| elif height is not None: | elif height is not None: | ||||
| return 'width="%d" ' % (int(height)*new_width/new_height) | |||||
| return 'width="%d" ' % (int(height) * new_width / new_height) | |||||
| return 'height="%d" width="%d" ' % (new_height, new_width) | return 'height="%d" width="%d" ' % (new_height, new_width) | ||||
| def text_resource_complete(self, resource, text): | def text_resource_complete(self, resource, text): | ||||
| @@ -151,7 +154,7 @@ class ImageSizerPlugin(PILPlugin): | |||||
| continue | continue | ||||
| attr = None | attr = None | ||||
| for tag in tags: | for tag in tags: | ||||
| if text[pos:(pos+len(tag)+1)] == ("%s=" % tag): | |||||
| if text[pos:(pos + len(tag) + 1)] == ("%s=" % tag): | |||||
| attr = tag | attr = tag | ||||
| pos = pos + len(tag) + 1 | pos = pos + len(tag) + 1 | ||||
| break | break | ||||
| @@ -177,12 +180,13 @@ class ImageSizerPlugin(PILPlugin): | |||||
| return text | return text | ||||
| def scale_aspect(a, b1, b2): | def scale_aspect(a, b1, b2): | ||||
| from math import ceil | |||||
| """ | |||||
| from math import ceil | |||||
| """ | |||||
| Scales a by b2/b1 rounding up to nearest integer | Scales a by b2/b1 rounding up to nearest integer | ||||
| """ | """ | ||||
| return int(ceil(a * b2 / float(b1))) | |||||
| return int(ceil(a * b2 / float(b1))) | |||||
| def thumb_scale_size(orig_width, orig_height, width, height): | def thumb_scale_size(orig_width, orig_height, width, height): | ||||
| @@ -197,7 +201,7 @@ def thumb_scale_size(orig_width, orig_height, width, height): | |||||
| width = scale_aspect(orig_width, orig_height, height) | width = scale_aspect(orig_width, orig_height, height) | ||||
| elif height is None: | elif height is None: | ||||
| height = scale_aspect(orig_height, orig_width, width) | height = scale_aspect(orig_height, orig_width, width) | ||||
| elif orig_width*height >= orig_height*width: | |||||
| elif orig_width * height >= orig_height * width: | |||||
| width = scale_aspect(orig_width, orig_height, height) | width = scale_aspect(orig_width, orig_height, height) | ||||
| else: | else: | ||||
| height = scale_aspect(orig_height, orig_width, width) | height = scale_aspect(orig_height, orig_width, width) | ||||
| @@ -208,7 +212,9 @@ def thumb_scale_size(orig_width, orig_height, width, height): | |||||
| # Image Thumbnails | # Image Thumbnails | ||||
| # | # | ||||
| class ImageThumbnailsPlugin(PILPlugin): | class ImageThumbnailsPlugin(PILPlugin): | ||||
| """ | """ | ||||
| Provide a function to get thumbnail for any image resource. | Provide a function to get thumbnail for any image resource. | ||||
| @@ -239,11 +245,11 @@ class ImageThumbnailsPlugin(PILPlugin): | |||||
| prefix: thumbs4_ | prefix: thumbs4_ | ||||
| include: | include: | ||||
| - '*.jpg' | - '*.jpg' | ||||
| which means - make four thumbnails from every picture with different prefixes | |||||
| and sizes | |||||
| which means - make four thumbnails from every picture with different | |||||
| prefixes and sizes | |||||
| It is only valid to specify either width/height or larger/smaller, but not to | |||||
| mix the two types. | |||||
| It is only valid to specify either width/height or larger/smaller, but | |||||
| not to mix the two types. | |||||
| If larger/smaller are specified, then the orientation (i.e., landscape or | If larger/smaller are specified, then the orientation (i.e., landscape or | ||||
| portrait) is preserved while thumbnailing. | portrait) is preserved while thumbnailing. | ||||
| @@ -256,7 +262,8 @@ class ImageThumbnailsPlugin(PILPlugin): | |||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(ImageThumbnailsPlugin, self).__init__(site) | super(ImageThumbnailsPlugin, self).__init__(site) | ||||
| def thumb(self, resource, width, height, prefix, crop_type, preserve_orientation=False): | |||||
| def thumb(self, resource, width, height, prefix, crop_type, | |||||
| preserve_orientation=False): | |||||
| """ | """ | ||||
| Generate a thumbnail for the given image | Generate a thumbnail for the given image | ||||
| """ | """ | ||||
| @@ -268,14 +275,17 @@ class ImageThumbnailsPlugin(PILPlugin): | |||||
| # for simple maintenance but keep original deploy path to preserve | # for simple maintenance but keep original deploy path to preserve | ||||
| # naming logic in generated site | # naming logic in generated site | ||||
| path = os.path.join(".thumbnails", | path = os.path.join(".thumbnails", | ||||
| os.path.dirname(resource.get_relative_deploy_path()), | |||||
| os.path.dirname( | |||||
| resource.get_relative_deploy_path()), | |||||
| "%s%s" % (prefix, name)) | "%s%s" % (prefix, name)) | ||||
| target = resource.site.config.content_root_path.child_file(path) | target = resource.site.config.content_root_path.child_file(path) | ||||
| res = self.site.content.add_resource(target) | res = self.site.content.add_resource(target) | ||||
| res.set_relative_deploy_path(res.get_relative_deploy_path().replace('.thumbnails/', '', 1)) | |||||
| res.set_relative_deploy_path( | |||||
| res.get_relative_deploy_path().replace('.thumbnails/', '', 1)) | |||||
| target.parent.make() | target.parent.make() | ||||
| if os.path.exists(target.path) and os.path.getmtime(resource.path) <= os.path.getmtime(target.path): | |||||
| if (os.path.exists(target.path) and os.path.getmtime(resource.path) <= | |||||
| os.path.getmtime(target.path)): | |||||
| return | return | ||||
| self.logger.debug("Making thumbnail for [%s]" % resource) | self.logger.debug("Making thumbnail for [%s]" % resource) | ||||
| @@ -285,17 +295,18 @@ class ImageThumbnailsPlugin(PILPlugin): | |||||
| format = im.format | format = im.format | ||||
| if preserve_orientation and im.size[1] > im.size[0]: | if preserve_orientation and im.size[1] > im.size[0]: | ||||
| width, height = height, width | |||||
| width, height = height, width | |||||
| resize_width, resize_height = thumb_scale_size(im.size[0], im.size[1], width, height) | |||||
| resize_width, resize_height = thumb_scale_size( | |||||
| im.size[0], im.size[1], width, height) | |||||
| self.logger.debug("Resize to: %d,%d" % (resize_width, resize_height)) | self.logger.debug("Resize to: %d,%d" % (resize_width, resize_height)) | ||||
| im = im.resize((resize_width, resize_height), self.Image.ANTIALIAS) | im = im.resize((resize_width, resize_height), self.Image.ANTIALIAS) | ||||
| if width is not None and height is not None: | if width is not None and height is not None: | ||||
| shiftx = shifty = 0 | shiftx = shifty = 0 | ||||
| if crop_type == "center": | if crop_type == "center": | ||||
| shiftx = (im.size[0] - width)/2 | |||||
| shifty = (im.size[1] - height)/2 | |||||
| shiftx = (im.size[0] - width) / 2 | |||||
| shifty = (im.size[1] - height) / 2 | |||||
| elif crop_type == "bottomright": | elif crop_type == "bottomright": | ||||
| shiftx = (im.size[0] - width) | shiftx = (im.size[0] - width) | ||||
| shifty = (im.size[1] - height) | shifty = (im.size[1] - height) | ||||
| @@ -304,22 +315,23 @@ class ImageThumbnailsPlugin(PILPlugin): | |||||
| options = dict(optimize=True) | options = dict(optimize=True) | ||||
| if format == "JPEG": | if format == "JPEG": | ||||
| options['quality'] = 75 | |||||
| options['quality'] = 75 | |||||
| im.save(target.path, **options) | im.save(target.path, **options) | ||||
| def begin_site(self): | def begin_site(self): | ||||
| """ | """ | ||||
| Find any image resource that should be thumbnailed and call thumb on it. | |||||
| Find any image resource that should be thumbnailed and call thumb | |||||
| on it. | |||||
| """ | """ | ||||
| # Grab default values from config | # Grab default values from config | ||||
| config = self.site.config | config = self.site.config | ||||
| defaults = { "width": None, | |||||
| "height": None, | |||||
| "larger": None, | |||||
| "smaller": None, | |||||
| "crop_type": "topleft", | |||||
| "prefix": 'thumb_'} | |||||
| defaults = {"width": None, | |||||
| "height": None, | |||||
| "larger": None, | |||||
| "smaller": None, | |||||
| "crop_type": "topleft", | |||||
| "prefix": 'thumb_'} | |||||
| if hasattr(config, 'thumbnails'): | if hasattr(config, 'thumbnails'): | ||||
| defaults.update(config.thumbnails) | defaults.update(config.thumbnails) | ||||
| @@ -327,45 +339,64 @@ class ImageThumbnailsPlugin(PILPlugin): | |||||
| if hasattr(node, 'meta') and hasattr(node.meta, 'thumbnails'): | if hasattr(node, 'meta') and hasattr(node.meta, 'thumbnails'): | ||||
| for th in node.meta.thumbnails: | for th in node.meta.thumbnails: | ||||
| if not hasattr(th, 'include'): | if not hasattr(th, 'include'): | ||||
| self.logger.error("Include is not set for node [%s]" % node) | |||||
| self.logger.error( | |||||
| "Include is not set for node [%s]" % node) | |||||
| continue | continue | ||||
| include = th.include | include = th.include | ||||
| prefix = th.prefix if hasattr(th, 'prefix') else defaults['prefix'] | |||||
| height = th.height if hasattr(th, 'height') else defaults['height'] | |||||
| width = th.width if hasattr(th, 'width') else defaults['width'] | |||||
| larger = th.larger if hasattr(th, 'larger') else defaults['larger'] | |||||
| smaller = th.smaller if hasattr(th, 'smaller') else defaults['smaller'] | |||||
| crop_type = th.crop_type if hasattr(th, 'crop_type') else defaults['crop_type'] | |||||
| prefix = th.prefix if hasattr( | |||||
| th, 'prefix') else defaults['prefix'] | |||||
| height = th.height if hasattr( | |||||
| th, 'height') else defaults['height'] | |||||
| width = th.width if hasattr( | |||||
| th, 'width') else defaults['width'] | |||||
| larger = th.larger if hasattr( | |||||
| th, 'larger') else defaults['larger'] | |||||
| smaller = th.smaller if hasattr( | |||||
| th, 'smaller') else defaults['smaller'] | |||||
| crop_type = th.crop_type if hasattr( | |||||
| th, 'crop_type') else defaults['crop_type'] | |||||
| if crop_type not in ["topleft", "center", "bottomright"]: | if crop_type not in ["topleft", "center", "bottomright"]: | ||||
| self.logger.error("Unknown crop_type defined for node [%s]" % node) | |||||
| self.logger.error( | |||||
| "Unknown crop_type defined for node [%s]" % node) | |||||
| continue | continue | ||||
| if width is None and height is None and larger is None and smaller is None: | |||||
| self.logger.error("At least one of width, height, larger, or smaller must be set for node [%s]" % node) | |||||
| if (width is None and height is None and larger is None and | |||||
| smaller is None): | |||||
| self.logger.error( | |||||
| "At least one of width, height, larger, or smaller" | |||||
| "must be set for node [%s]" % node) | |||||
| continue | continue | ||||
| if ((larger is not None or smaller is not None) and | if ((larger is not None or smaller is not None) and | ||||
| (width is not None or height is not None)): | |||||
| self.logger.error("It is not valid to specify both one of width/height and one of larger/smaller for node [%s]" % node) | |||||
| (width is not None or height is not None)): | |||||
| self.logger.error( | |||||
| "It is not valid to specify both one of" | |||||
| "width/height and one of larger/smaller" | |||||
| "for node [%s]" % node) | |||||
| continue | continue | ||||
| if larger is None and smaller is None: | if larger is None and smaller is None: | ||||
| preserve_orientation = False | |||||
| dim1, dim2 = width, height | |||||
| preserve_orientation = False | |||||
| dim1, dim2 = width, height | |||||
| else: | else: | ||||
| preserve_orientation = True | |||||
| dim1, dim2 = larger, smaller | |||||
| preserve_orientation = True | |||||
| dim1, dim2 = larger, smaller | |||||
| match_includes = lambda s: any([glob.fnmatch.fnmatch(s, inc) for inc in include]) | |||||
| match_includes = lambda s: any( | |||||
| [glob.fnmatch.fnmatch(s, inc) for inc in include]) | |||||
| for resource in node.resources: | for resource in node.resources: | ||||
| if match_includes(resource.path): | if match_includes(resource.path): | ||||
| self.thumb(resource, dim1, dim2, prefix, crop_type, preserve_orientation) | |||||
| self.thumb( | |||||
| resource, dim1, dim2, prefix, crop_type, | |||||
| preserve_orientation) | |||||
| # | # | ||||
| # JPEG Optimization | # JPEG Optimization | ||||
| # | # | ||||
| class JPEGOptimPlugin(CLTransformer): | class JPEGOptimPlugin(CLTransformer): | ||||
| """ | """ | ||||
| The plugin class for JPEGOptim | The plugin class for JPEGOptim | ||||
| """ | """ | ||||
| @@ -408,7 +439,7 @@ class JPEGOptimPlugin(CLTransformer): | |||||
| "strip-icc", | "strip-icc", | ||||
| ] | ] | ||||
| target = File(self.site.config.deploy_root_path.child( | target = File(self.site.config.deploy_root_path.child( | ||||
| resource.relative_deploy_path)) | |||||
| resource.relative_deploy_path)) | |||||
| jpegoptim = self.app | jpegoptim = self.app | ||||
| args = [unicode(jpegoptim)] | args = [unicode(jpegoptim)] | ||||
| args.extend(self.process_args(supported)) | args.extend(self.process_args(supported)) | ||||
| @@ -417,6 +448,7 @@ class JPEGOptimPlugin(CLTransformer): | |||||
| class JPEGTranPlugin(CLTransformer): | class JPEGTranPlugin(CLTransformer): | ||||
| """ | """ | ||||
| Almost like jpegoptim except it uses jpegtran. jpegtran allows to make | Almost like jpegoptim except it uses jpegtran. jpegtran allows to make | ||||
| progressive JPEG. Unfortunately, it only does lossless compression. If | progressive JPEG. Unfortunately, it only does lossless compression. If | ||||
| @@ -463,7 +495,7 @@ class JPEGTranPlugin(CLTransformer): | |||||
| "copy", | "copy", | ||||
| ] | ] | ||||
| source = File(self.site.config.deploy_root_path.child( | source = File(self.site.config.deploy_root_path.child( | ||||
| resource.relative_deploy_path)) | |||||
| resource.relative_deploy_path)) | |||||
| target = File.make_temp('') | target = File.make_temp('') | ||||
| jpegtran = self.app | jpegtran = self.app | ||||
| args = [unicode(jpegtran)] | args = [unicode(jpegtran)] | ||||
| @@ -474,12 +506,12 @@ class JPEGTranPlugin(CLTransformer): | |||||
| target.delete() | target.delete() | ||||
| # | # | ||||
| # PNG Optimization | # PNG Optimization | ||||
| # | # | ||||
| class OptiPNGPlugin(CLTransformer): | class OptiPNGPlugin(CLTransformer): | ||||
| """ | """ | ||||
| The plugin class for OptiPNG | The plugin class for OptiPNG | ||||
| """ | """ | ||||
| @@ -535,7 +567,7 @@ class OptiPNGPlugin(CLTransformer): | |||||
| "nz" | "nz" | ||||
| ] | ] | ||||
| target = File(self.site.config.deploy_root_path.child( | target = File(self.site.config.deploy_root_path.child( | ||||
| resource.relative_deploy_path)) | |||||
| resource.relative_deploy_path)) | |||||
| optipng = self.app | optipng = self.app | ||||
| args = [unicode(optipng)] | args = [unicode(optipng)] | ||||
| args.extend(self.process_args(supported)) | args.extend(self.process_args(supported)) | ||||
| @@ -16,6 +16,7 @@ from fswrap import File | |||||
| # | # | ||||
| class UglifyPlugin(CLTransformer): | class UglifyPlugin(CLTransformer): | ||||
| """ | """ | ||||
| The plugin class for Uglify JS | The plugin class for Uglify JS | ||||
| """ | """ | ||||
| @@ -85,7 +86,9 @@ class UglifyPlugin(CLTransformer): | |||||
| out = target.read_all() | out = target.read_all() | ||||
| return out | return out | ||||
| class RequireJSPlugin(CLTransformer): | class RequireJSPlugin(CLTransformer): | ||||
| """ | """ | ||||
| requirejs plugin | requirejs plugin | ||||
| @@ -103,6 +106,7 @@ class RequireJSPlugin(CLTransformer): | |||||
| Please see the homepage of requirejs for usage details. | Please see the homepage of requirejs for usage details. | ||||
| """ | """ | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(RequireJSPlugin, self).__init__(site) | super(RequireJSPlugin, self).__init__(site) | ||||
| @@ -124,20 +128,22 @@ class RequireJSPlugin(CLTransformer): | |||||
| rjs = self.app | rjs = self.app | ||||
| target = File.make_temp('') | target = File.make_temp('') | ||||
| args = [unicode(rjs)] | args = [unicode(rjs)] | ||||
| args.extend(['-o', unicode(resource), ("out=" + target.fully_expanded_path)]) | |||||
| args.extend( | |||||
| ['-o', unicode(resource), ("out=" + target.fully_expanded_path)]) | |||||
| try: | try: | ||||
| self.call_app(args) | self.call_app(args) | ||||
| except subprocess.CalledProcessError: | except subprocess.CalledProcessError: | ||||
| HydeException.reraise( | |||||
| "Cannot process %s. Error occurred when " | |||||
| "processing [%s]" % (self.app.name, resource.source_file), | |||||
| sys.exc_info()) | |||||
| HydeException.reraise( | |||||
| "Cannot process %s. Error occurred when " | |||||
| "processing [%s]" % (self.app.name, resource.source_file), | |||||
| sys.exc_info()) | |||||
| return target.read_all() | return target.read_all() | ||||
| class CoffeePlugin(CLTransformer): | class CoffeePlugin(CLTransformer): | ||||
| """ | """ | ||||
| The plugin class for Coffeescript | The plugin class for Coffeescript | ||||
| """ | """ | ||||
| @@ -5,7 +5,9 @@ Contains classes to help manage multi-language pages. | |||||
| from hyde.plugin import Plugin | from hyde.plugin import Plugin | ||||
| class LanguagePlugin(Plugin): | class LanguagePlugin(Plugin): | ||||
| """ | """ | ||||
| Each page should be tagged with a language using `language` meta | Each page should be tagged with a language using `language` meta | ||||
| data. Each page should also have an UUID stored in `uuid` meta | data. Each page should also have an UUID stored in `uuid` meta | ||||
| @@ -36,7 +38,8 @@ class LanguagePlugin(Plugin): | |||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(LanguagePlugin, self).__init__(site) | super(LanguagePlugin, self).__init__(site) | ||||
| self.languages = {} # Associate a UUID to the list of resources available | |||||
| # Associate a UUID to the list of resources available | |||||
| self.languages = {} | |||||
| def begin_site(self): | def begin_site(self): | ||||
| """ | """ | ||||
| @@ -60,8 +63,9 @@ class LanguagePlugin(Plugin): | |||||
| resource.translations = \ | resource.translations = \ | ||||
| [r for r in resources | [r for r in resources | ||||
| if r.meta.language != language] | if r.meta.language != language] | ||||
| translations = ",".join([t.meta.language for t in resource.translations]) | |||||
| self.logger.debug( | |||||
| "Adding translations for resource [%s] from %s to %s" % (resource, | |||||
| language, | |||||
| translations)) | |||||
| translations = ",".join( | |||||
| [t.meta.language for t in resource.translations]) | |||||
| self.logger.debug("Adding translations for resource" | |||||
| "[%s] from %s to %s" % (resource, | |||||
| language, | |||||
| translations)) | |||||
| @@ -26,6 +26,7 @@ import yaml | |||||
| # | # | ||||
| class Metadata(Expando): | class Metadata(Expando): | ||||
| """ | """ | ||||
| Container class for yaml meta data. | Container class for yaml meta data. | ||||
| """ | """ | ||||
| @@ -49,6 +50,7 @@ class Metadata(Expando): | |||||
| class MetaPlugin(Plugin): | class MetaPlugin(Plugin): | ||||
| """ | """ | ||||
| Metadata plugin for hyde. Loads meta data in the following order: | Metadata plugin for hyde. Loads meta data in the following order: | ||||
| @@ -66,8 +68,8 @@ class MetaPlugin(Plugin): | |||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(MetaPlugin, self).__init__(site) | super(MetaPlugin, self).__init__(site) | ||||
| self.yaml_finder = re.compile( | self.yaml_finder = re.compile( | ||||
| r"^\s*(?:---|===)\s*\n((?:.|\n)+?)\n\s*(?:---|===)\s*\n*", | |||||
| re.MULTILINE) | |||||
| r"^\s*(?:---|===)\s*\n((?:.|\n)+?)\n\s*(?:---|===)\s*\n*", | |||||
| re.MULTILINE) | |||||
| def begin_site(self): | def begin_site(self): | ||||
| """ | """ | ||||
| @@ -88,7 +90,8 @@ class MetaPlugin(Plugin): | |||||
| if not hasattr(resource, 'meta'): | if not hasattr(resource, 'meta'): | ||||
| resource.meta = Metadata({}, node.meta) | resource.meta = Metadata({}, node.meta) | ||||
| if resource.source_file.is_text and not resource.simple_copy: | if resource.source_file.is_text and not resource.simple_copy: | ||||
| self.__read_resource__(resource, resource.source_file.read_all()) | |||||
| self.__read_resource__( | |||||
| resource, resource.source_file.read_all()) | |||||
| def __read_resource__(self, resource, text): | def __read_resource__(self, resource, text): | ||||
| """ | """ | ||||
| @@ -96,7 +99,8 @@ class MetaPlugin(Plugin): | |||||
| the resource. Load meta data by looking for the marker. | the resource. Load meta data by looking for the marker. | ||||
| Once loaded, remove the meta area from the text. | Once loaded, remove the meta area from the text. | ||||
| """ | """ | ||||
| self.logger.debug("Trying to load metadata from resource [%s]" % resource) | |||||
| self.logger.debug( | |||||
| "Trying to load metadata from resource [%s]" % resource) | |||||
| match = re.match(self.yaml_finder, text) | match = re.match(self.yaml_finder, text) | ||||
| if not match: | if not match: | ||||
| self.logger.debug("No metadata found in resource [%s]" % resource) | self.logger.debug("No metadata found in resource [%s]" % resource) | ||||
| @@ -113,7 +117,7 @@ class MetaPlugin(Plugin): | |||||
| resource.meta.update(data) | resource.meta.update(data) | ||||
| self.__update_standard_attributes__(resource) | self.__update_standard_attributes__(resource) | ||||
| self.logger.debug("Successfully loaded metadata from resource [%s]" | self.logger.debug("Successfully loaded metadata from resource [%s]" | ||||
| % resource) | |||||
| % resource) | |||||
| return text or ' ' | return text or ' ' | ||||
| def __update_standard_attributes__(self, obj): | def __update_standard_attributes__(self, obj): | ||||
| @@ -165,6 +169,7 @@ class MetaPlugin(Plugin): | |||||
| # | # | ||||
| class AutoExtendPlugin(Plugin): | class AutoExtendPlugin(Plugin): | ||||
| """ | """ | ||||
| The plugin class for extending templates using metadata. | The plugin class for extending templates using metadata. | ||||
| """ | """ | ||||
| @@ -204,9 +209,9 @@ class AutoExtendPlugin(Plugin): | |||||
| extended_text += '\n' | extended_text += '\n' | ||||
| if block: | if block: | ||||
| extended_text += ('%s\n%s\n%s' % | extended_text += ('%s\n%s\n%s' % | ||||
| (self.t_block_open_tag(block), | |||||
| text, | |||||
| self.t_block_close_tag(block))) | |||||
| (self.t_block_open_tag(block), | |||||
| text, | |||||
| self.t_block_close_tag(block))) | |||||
| else: | else: | ||||
| extended_text += text | extended_text += text | ||||
| return extended_text | return extended_text | ||||
| @@ -218,6 +223,7 @@ class AutoExtendPlugin(Plugin): | |||||
| # | # | ||||
| class Tag(Expando): | class Tag(Expando): | ||||
| """ | """ | ||||
| A simple object that represents a tag. | A simple object that represents a tag. | ||||
| """ | """ | ||||
| @@ -255,6 +261,7 @@ def get_tagger_sort_method(site): | |||||
| sys.exc_info()) | sys.exc_info()) | ||||
| return walker | return walker | ||||
| def walk_resources_tagged_with(node, tag): | def walk_resources_tagged_with(node, tag): | ||||
| tags = set(unicode(tag).split('+')) | tags = set(unicode(tag).split('+')) | ||||
| walker = get_tagger_sort_method(node.site) | walker = get_tagger_sort_method(node.site) | ||||
| @@ -266,7 +273,9 @@ def walk_resources_tagged_with(node, tag): | |||||
| if tags <= taglist: | if tags <= taglist: | ||||
| yield resource | yield resource | ||||
| class TaggerPlugin(Plugin): | class TaggerPlugin(Plugin): | ||||
| """ | """ | ||||
| Tagger plugin for hyde. Adds the ability to do tag resources and search | Tagger plugin for hyde. Adds the ability to do tag resources and search | ||||
| based on the tags. | based on the tags. | ||||
| @@ -286,6 +295,7 @@ class TaggerPlugin(Plugin): | |||||
| target: blog/tags | target: blog/tags | ||||
| archive_extension: html | archive_extension: html | ||||
| """ | """ | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(TaggerPlugin, self).__init__(site) | super(TaggerPlugin, self).__init__(site) | ||||
| @@ -295,11 +305,13 @@ class TaggerPlugin(Plugin): | |||||
| and methods for walking tagged resources. | and methods for walking tagged resources. | ||||
| """ | """ | ||||
| self.logger.debug("Adding tags from metadata") | self.logger.debug("Adding tags from metadata") | ||||
| config = self.site.config | |||||
| content = self.site.content | |||||
| # *F841 local variable 'config' is assigned to but never used | |||||
| # config = self.site.config | |||||
| # *F841 local variable 'content' is assigned to but never used | |||||
| # content = self.site.content | |||||
| tags = {} | tags = {} | ||||
| add_method(Node, | add_method(Node, | ||||
| 'walk_resources_tagged_with', walk_resources_tagged_with) | |||||
| 'walk_resources_tagged_with', walk_resources_tagged_with) | |||||
| walker = get_tagger_sort_method(self.site) | walker = get_tagger_sort_method(self.site) | ||||
| for resource in walker(): | for resource in walker(): | ||||
| self._process_tags_in_resource(resource, tags) | self._process_tags_in_resource(resource, tags) | ||||
| @@ -337,14 +349,14 @@ class TaggerPlugin(Plugin): | |||||
| return | return | ||||
| for tagname in taglist: | for tagname in taglist: | ||||
| if not tagname in tags: | |||||
| if tagname not in tags: | |||||
| tag = Tag(tagname) | tag = Tag(tagname) | ||||
| tags[tagname] = tag | tags[tagname] = tag | ||||
| tag.resources.append(resource) | tag.resources.append(resource) | ||||
| add_method(Node, | add_method(Node, | ||||
| 'walk_resources_tagged_with_%s' % tagname, | |||||
| walk_resources_tagged_with, | |||||
| tag=tag) | |||||
| 'walk_resources_tagged_with_%s' % tagname, | |||||
| walk_resources_tagged_with, | |||||
| tag=tag) | |||||
| else: | else: | ||||
| tags[tagname].resources.append(resource) | tags[tagname].resources.append(resource) | ||||
| if not hasattr(resource, 'tags'): | if not hasattr(resource, 'tags'): | ||||
| @@ -367,13 +379,13 @@ class TaggerPlugin(Plugin): | |||||
| for name, config in archive_config.to_dict().iteritems(): | for name, config in archive_config.to_dict().iteritems(): | ||||
| self._create_tag_archive(config) | self._create_tag_archive(config) | ||||
| def _create_tag_archive(self, config): | def _create_tag_archive(self, config): | ||||
| """ | """ | ||||
| Generates archives for each tag based on the given configuration. | Generates archives for each tag based on the given configuration. | ||||
| """ | """ | ||||
| if not 'template' in config: | |||||
| raise HydeException("No Template specified in tagger configuration.") | |||||
| if 'template' not in config: | |||||
| raise HydeException( | |||||
| "No Template specified in tagger configuration.") | |||||
| content = self.site.content.source_folder | content = self.site.content.source_folder | ||||
| source = Folder(config.get('source', '')) | source = Folder(config.get('source', '')) | ||||
| target = content.child_folder(config.get('target', 'tags')) | target = content.child_folder(config.get('target', 'tags')) | ||||
| @@ -441,15 +453,17 @@ def filter_method(item, settings=None): | |||||
| break | break | ||||
| return all_match | return all_match | ||||
| def attributes_checker(item, attributes=None): | def attributes_checker(item, attributes=None): | ||||
| """ | """ | ||||
| Checks if the given list of attributes exist. | Checks if the given list of attributes exist. | ||||
| """ | """ | ||||
| try: | try: | ||||
| attrgetter(*attributes)(item) | |||||
| return True | |||||
| attrgetter(*attributes)(item) | |||||
| return True | |||||
| except AttributeError: | except AttributeError: | ||||
| return False | |||||
| return False | |||||
| def sort_method(node, settings=None): | def sort_method(node, settings=None): | ||||
| """ | """ | ||||
| @@ -471,11 +485,12 @@ def sort_method(node, settings=None): | |||||
| resources = ifilter(lambda x: excluder_(x) and filter_(x), | resources = ifilter(lambda x: excluder_(x) and filter_(x), | ||||
| node.walk_resources()) | node.walk_resources()) | ||||
| return sorted(resources, | return sorted(resources, | ||||
| key=attrgetter(*attr), | |||||
| reverse=reverse) | |||||
| key=attrgetter(*attr), | |||||
| reverse=reverse) | |||||
| class SorterPlugin(Plugin): | class SorterPlugin(Plugin): | ||||
| """ | """ | ||||
| Sorter plugin for hyde. Adds the ability to do | Sorter plugin for hyde. Adds the ability to do | ||||
| sophisticated sorting by expanding the site objects | sophisticated sorting by expanding the site objects | ||||
| @@ -529,8 +544,8 @@ class SorterPlugin(Plugin): | |||||
| setattr(Resource, next_att, None) | setattr(Resource, next_att, None) | ||||
| walker = getattr(self.site.content, | walker = getattr(self.site.content, | ||||
| sort_method_name, | |||||
| self.site.content.walk_resources) | |||||
| sort_method_name, | |||||
| self.site.content.walk_resources) | |||||
| first, last = None, None | first, last = None, None | ||||
| for prev, next in pairwalk(walker()): | for prev, next in pairwalk(walker()): | ||||
| if not first: | if not first: | ||||
| @@ -555,7 +570,9 @@ class SorterPlugin(Plugin): | |||||
| Grouper = namedtuple('Grouper', 'group resources') | Grouper = namedtuple('Grouper', 'group resources') | ||||
| class Group(Expando): | class Group(Expando): | ||||
| """ | """ | ||||
| A wrapper class for groups. Adds methods for | A wrapper class for groups. Adds methods for | ||||
| grouping resources. | grouping resources. | ||||
| @@ -573,21 +590,21 @@ class Group(Expando): | |||||
| super(Group, self).__init__(grouping) | super(Group, self).__init__(grouping) | ||||
| add_method(Node, | add_method(Node, | ||||
| 'walk_%s_groups' % self.name, | |||||
| Group.walk_groups_in_node, | |||||
| group=self) | |||||
| 'walk_%s_groups' % self.name, | |||||
| Group.walk_groups_in_node, | |||||
| group=self) | |||||
| add_method(Node, | add_method(Node, | ||||
| 'walk_resources_grouped_by_%s' % self.name, | |||||
| Group.walk_resources, | |||||
| group=self) | |||||
| 'walk_resources_grouped_by_%s' % self.name, | |||||
| Group.walk_resources, | |||||
| group=self) | |||||
| add_property(Resource, | add_property(Resource, | ||||
| '%s_group' % self.name, | |||||
| Group.get_resource_group, | |||||
| group=self) | |||||
| '%s_group' % self.name, | |||||
| Group.get_resource_group, | |||||
| group=self) | |||||
| add_method(Resource, | add_method(Resource, | ||||
| 'walk_%s_groups' % self.name, | |||||
| Group.walk_resource_groups, | |||||
| group=self) | |||||
| 'walk_%s_groups' % self.name, | |||||
| Group.walk_resource_groups, | |||||
| group=self) | |||||
| def set_expando(self, key, value): | def set_expando(self, key, value): | ||||
| """ | """ | ||||
| @@ -612,9 +629,9 @@ class Group(Expando): | |||||
| group_name = None | group_name = None | ||||
| return next((g for g in group.walk_groups() | return next((g for g in group.walk_groups() | ||||
| if g.name == group_name), None) \ | |||||
| if group_name \ | |||||
| else None | |||||
| if g.name == group_name), None) \ | |||||
| if group_name \ | |||||
| else None | |||||
| @staticmethod | @staticmethod | ||||
| def walk_resource_groups(resource, group): | def walk_resource_groups(resource, group): | ||||
| @@ -693,7 +710,9 @@ class Group(Expando): | |||||
| if group_value == self.name: | if group_value == self.name: | ||||
| yield resource | yield resource | ||||
| class GrouperPlugin(Plugin): | class GrouperPlugin(Plugin): | ||||
| """ | """ | ||||
| Grouper plugin for hyde. Adds the ability to do | Grouper plugin for hyde. Adds the ability to do | ||||
| group resources and nodes in an arbitrary | group resources and nodes in an arbitrary | ||||
| @@ -726,6 +745,7 @@ class GrouperPlugin(Plugin): | |||||
| Helpful snippets and tweaks to | Helpful snippets and tweaks to | ||||
| make hyde more awesome. | make hyde more awesome. | ||||
| """ | """ | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(GrouperPlugin, self).__init__(site) | super(GrouperPlugin, self).__init__(site) | ||||
| @@ -748,9 +768,8 @@ class GrouperPlugin(Plugin): | |||||
| setattr(Resource, next_att, None) | setattr(Resource, next_att, None) | ||||
| self.site.grouper[name] = Group(grouping) | self.site.grouper[name] = Group(grouping) | ||||
| walker = Group.walk_resources( | walker = Group.walk_resources( | ||||
| self.site.content, self.site.grouper[name]) | |||||
| self.site.content, self.site.grouper[name]) | |||||
| for prev, next in pairwalk(walker): | for prev, next in pairwalk(walker): | ||||
| setattr(next, prev_att, prev) | setattr(next, prev_att, prev) | ||||
| setattr(prev, next_att, next) | setattr(prev, next_att, next) | ||||
| @@ -63,6 +63,7 @@ except ImportError: | |||||
| class SphinxPlugin(Plugin): | class SphinxPlugin(Plugin): | ||||
| """The plugin class for rendering sphinx-generated documentation.""" | """The plugin class for rendering sphinx-generated documentation.""" | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| @@ -93,7 +94,7 @@ class SphinxPlugin(Plugin): | |||||
| else: | else: | ||||
| for name in dir(user_settings): | for name in dir(user_settings): | ||||
| if not name.startswith("_"): | if not name.startswith("_"): | ||||
| setattr(settings,name,getattr(user_settings,name)) | |||||
| setattr(settings, name, getattr(user_settings, name)) | |||||
| return settings | return settings | ||||
| @property | @property | ||||
| @@ -109,11 +110,11 @@ class SphinxPlugin(Plugin): | |||||
| conf_path = self.site.sitepath.child_folder(conf_path) | conf_path = self.site.sitepath.child_folder(conf_path) | ||||
| # Sphinx always execs the config file in its parent dir. | # Sphinx always execs the config file in its parent dir. | ||||
| conf_file = conf_path.child("conf.py") | conf_file = conf_path.child("conf.py") | ||||
| self._sphinx_config = {"__file__":conf_file} | |||||
| self._sphinx_config = {"__file__": conf_file} | |||||
| curdir = os.getcwd() | curdir = os.getcwd() | ||||
| os.chdir(conf_path.path) | os.chdir(conf_path.path) | ||||
| try: | try: | ||||
| execfile(conf_file,self._sphinx_config) | |||||
| execfile(conf_file, self._sphinx_config) | |||||
| finally: | finally: | ||||
| os.chdir(curdir) | os.chdir(curdir) | ||||
| return self._sphinx_config | return self._sphinx_config | ||||
| @@ -132,16 +133,17 @@ class SphinxPlugin(Plugin): | |||||
| # We need to: | # We need to: | ||||
| # * change the deploy name from .rst to .html | # * change the deploy name from .rst to .html | ||||
| # * if a block_map is given, switch off default_block | # * if a block_map is given, switch off default_block | ||||
| suffix = self.sphinx_config.get("source_suffix",".rst") | |||||
| suffix = self.sphinx_config.get("source_suffix", ".rst") | |||||
| for resource in self.site.content.walk_resources(): | for resource in self.site.content.walk_resources(): | ||||
| if resource.source_file.path.endswith(suffix): | if resource.source_file.path.endswith(suffix): | ||||
| new_name = resource.source_file.name_without_extension + ".html" | |||||
| new_name = resource.source_file.name_without_extension + \ | |||||
| ".html" | |||||
| target_folder = File(resource.relative_deploy_path).parent | target_folder = File(resource.relative_deploy_path).parent | ||||
| resource.relative_deploy_path = target_folder.child(new_name) | resource.relative_deploy_path = target_folder.child(new_name) | ||||
| if settings.block_map: | if settings.block_map: | ||||
| resource.meta.default_block = None | resource.meta.default_block = None | ||||
| def begin_text_resource(self,resource,text): | |||||
| def begin_text_resource(self, resource, text): | |||||
| """Event hook for processing an individual resource. | """Event hook for processing an individual resource. | ||||
| If the input resource is a sphinx input file, this method will replace | If the input resource is a sphinx input file, this method will replace | ||||
| @@ -151,7 +153,7 @@ class SphinxPlugin(Plugin): | |||||
| This means that if no sphinx-related resources need updating, then | This means that if no sphinx-related resources need updating, then | ||||
| we entirely avoid running sphinx. | we entirely avoid running sphinx. | ||||
| """ | """ | ||||
| suffix = self.sphinx_config.get("source_suffix",".rst") | |||||
| suffix = self.sphinx_config.get("source_suffix", ".rst") | |||||
| if not resource.source_file.path.endswith(suffix): | if not resource.source_file.path.endswith(suffix): | ||||
| return text | return text | ||||
| if self.sphinx_build_dir is None: | if self.sphinx_build_dir is None: | ||||
| @@ -164,9 +166,9 @@ class SphinxPlugin(Plugin): | |||||
| if not settings.block_map: | if not settings.block_map: | ||||
| output.append(sphinx_output["body"]) | output.append(sphinx_output["body"]) | ||||
| else: | else: | ||||
| for (nm,content) in sphinx_output.iteritems(): | |||||
| for (nm, content) in sphinx_output.iteritems(): | |||||
| try: | try: | ||||
| block = getattr(settings.block_map,nm) | |||||
| block = getattr(settings.block_map, nm) | |||||
| except AttributeError: | except AttributeError: | ||||
| pass | pass | ||||
| else: | else: | ||||
| @@ -198,41 +200,45 @@ class SphinxPlugin(Plugin): | |||||
| conf_path = self.settings.conf_path | conf_path = self.settings.conf_path | ||||
| conf_path = self.site.sitepath.child_folder(conf_path) | conf_path = self.site.sitepath.child_folder(conf_path) | ||||
| conf_file = conf_path.child("conf.py") | conf_file = conf_path.child("conf.py") | ||||
| logger.error("Please ensure %s is a valid sphinx config",conf_file) | |||||
| logger.error( | |||||
| "Please ensure %s is a valid sphinx config", conf_file) | |||||
| logger.error("or set sphinx.conf_path to the directory") | logger.error("or set sphinx.conf_path to the directory") | ||||
| logger.error("containing your sphinx conf.py") | logger.error("containing your sphinx conf.py") | ||||
| raise | raise | ||||
| # Check that the hyde_json extension is loaded | # Check that the hyde_json extension is loaded | ||||
| extensions = sphinx_config.get("extensions",[]) | |||||
| extensions = sphinx_config.get("extensions", []) | |||||
| if "hyde.ext.plugins.sphinx" not in extensions: | if "hyde.ext.plugins.sphinx" not in extensions: | ||||
| logger.error("The hyde_json sphinx extension is not configured.") | logger.error("The hyde_json sphinx extension is not configured.") | ||||
| logger.error("Please add 'hyde.ext.plugins.sphinx' to the list") | logger.error("Please add 'hyde.ext.plugins.sphinx' to the list") | ||||
| logger.error("of extensions in your sphinx conf.py file.") | logger.error("of extensions in your sphinx conf.py file.") | ||||
| logger.info("(set sphinx.sanity_check=false to disable this check)") | |||||
| logger.info( | |||||
| "(set sphinx.sanity_check=false to disable this check)") | |||||
| raise RuntimeError("sphinx is not configured correctly") | raise RuntimeError("sphinx is not configured correctly") | ||||
| # Check that the master doc exists in the source tree. | # Check that the master doc exists in the source tree. | ||||
| master_doc = sphinx_config.get("master_doc","index") | |||||
| master_doc += sphinx_config.get("source_suffix",".rst") | |||||
| master_doc = os.path.join(self.site.content.path,master_doc) | |||||
| master_doc = sphinx_config.get("master_doc", "index") | |||||
| master_doc += sphinx_config.get("source_suffix", ".rst") | |||||
| master_doc = os.path.join(self.site.content.path, master_doc) | |||||
| if not os.path.exists(master_doc): | if not os.path.exists(master_doc): | ||||
| logger.error("The sphinx master document doesn't exist.") | logger.error("The sphinx master document doesn't exist.") | ||||
| logger.error("Please create the file %s",master_doc) | |||||
| logger.error("Please create the file %s", master_doc) | |||||
| logger.error("or change the 'master_doc' setting in your") | logger.error("or change the 'master_doc' setting in your") | ||||
| logger.error("sphinx conf.py file.") | logger.error("sphinx conf.py file.") | ||||
| logger.info("(set sphinx.sanity_check=false to disable this check)") | |||||
| logger.info( | |||||
| "(set sphinx.sanity_check=false to disable this check)") | |||||
| raise RuntimeError("sphinx is not configured correctly") | raise RuntimeError("sphinx is not configured correctly") | ||||
| # Check that I am *before* the other plugins, | # Check that I am *before* the other plugins, | ||||
| # with the possible exception of MetaPlugin | # with the possible exception of MetaPlugin | ||||
| for plugin in self.site.plugins: | for plugin in self.site.plugins: | ||||
| if plugin is self: | if plugin is self: | ||||
| break | break | ||||
| if not isinstance(plugin,_MetaPlugin): | |||||
| if not isinstance(plugin, _MetaPlugin): | |||||
| logger.error("The sphinx plugin is installed after the") | logger.error("The sphinx plugin is installed after the") | ||||
| logger.error("plugin %r.",plugin.__class__.__name__) | |||||
| logger.error("plugin %r.", plugin.__class__.__name__) | |||||
| logger.error("It's quite likely that this will break things.") | logger.error("It's quite likely that this will break things.") | ||||
| logger.error("Please move the sphinx plugin to the top") | logger.error("Please move the sphinx plugin to the top") | ||||
| logger.error("of the plugins list.") | logger.error("of the plugins list.") | ||||
| logger.info("(sphinx.sanity_check=false to disable this check)") | |||||
| logger.info( | |||||
| "(sphinx.sanity_check=false to disable this check)") | |||||
| raise RuntimeError("sphinx is not configured correctly") | raise RuntimeError("sphinx is not configured correctly") | ||||
| def _run_sphinx(self): | def _run_sphinx(self): | ||||
| @@ -254,7 +260,7 @@ class SphinxPlugin(Plugin): | |||||
| if sphinx.main(sphinx_args) != 0: | if sphinx.main(sphinx_args) != 0: | ||||
| raise RuntimeError("sphinx build failed") | raise RuntimeError("sphinx build failed") | ||||
| def _get_sphinx_output(self,resource): | |||||
| def _get_sphinx_output(self, resource): | |||||
| """Get the sphinx output for a given resource. | """Get the sphinx output for a given resource. | ||||
| This returns a dict mapping block names to HTML text fragments. | This returns a dict mapping block names to HTML text fragments. | ||||
| @@ -263,13 +269,14 @@ class SphinxPlugin(Plugin): | |||||
| related pages and so-on. | related pages and so-on. | ||||
| """ | """ | ||||
| relpath = File(resource.relative_path) | relpath = File(resource.relative_path) | ||||
| relpath = relpath.parent.child(relpath.name_without_extension+".fjson") | |||||
| with open(self.sphinx_build_dir.child(relpath),"rb") as f: | |||||
| relpath = relpath.parent.child( | |||||
| relpath.name_without_extension + ".fjson") | |||||
| with open(self.sphinx_build_dir.child(relpath), "rb") as f: | |||||
| return json.load(f) | return json.load(f) | ||||
| class HydeJSONHTMLBuilder(JSONHTMLBuilder): | class HydeJSONHTMLBuilder(JSONHTMLBuilder): | ||||
| """A slightly-customised JSONHTMLBuilder, for use by Hyde. | """A slightly-customised JSONHTMLBuilder, for use by Hyde. | ||||
| This is a Sphinx builder that serilises the generated HTML fragments into | This is a Sphinx builder that serilises the generated HTML fragments into | ||||
| @@ -280,6 +287,7 @@ class HydeJSONHTMLBuilder(JSONHTMLBuilder): | |||||
| work correctly once things have been processed by Hyde. | work correctly once things have been processed by Hyde. | ||||
| """ | """ | ||||
| name = "hyde_json" | name = "hyde_json" | ||||
| def get_target_uri(self, docname, typ=None): | def get_target_uri(self, docname, typ=None): | ||||
| return docname + ".html" | return docname + ".html" | ||||
| @@ -291,5 +299,3 @@ def setup(app): | |||||
| Hyde plugin. It simply registers the HydeJSONHTMLBuilder class. | Hyde plugin. It simply registers the HydeJSONHTMLBuilder class. | ||||
| """ | """ | ||||
| app.add_builder(HydeJSONHTMLBuilder) | app.add_builder(HydeJSONHTMLBuilder) | ||||
| @@ -20,9 +20,11 @@ import operator | |||||
| # | # | ||||
| class FlattenerPlugin(Plugin): | class FlattenerPlugin(Plugin): | ||||
| """ | """ | ||||
| The plugin class for flattening nested folders. | The plugin class for flattening nested folders. | ||||
| """ | """ | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(FlattenerPlugin, self).__init__(site) | super(FlattenerPlugin, self).__init__(site) | ||||
| @@ -50,7 +52,7 @@ class FlattenerPlugin(Plugin): | |||||
| target_path = target.child(resource.name) | target_path = target.child(resource.name) | ||||
| self.logger.debug( | self.logger.debug( | ||||
| 'Flattening resource path [%s] to [%s]' % | 'Flattening resource path [%s] to [%s]' % | ||||
| (resource, target_path)) | |||||
| (resource, target_path)) | |||||
| resource.relative_deploy_path = target_path | resource.relative_deploy_path = target_path | ||||
| for child in node.walk(): | for child in node.walk(): | ||||
| child.relative_deploy_path = target.path | child.relative_deploy_path = target.path | ||||
| @@ -61,12 +63,14 @@ class FlattenerPlugin(Plugin): | |||||
| # | # | ||||
| class CombinePlugin(Plugin): | class CombinePlugin(Plugin): | ||||
| """ | """ | ||||
| To use this combine, the following configuration should be added | To use this combine, the following configuration should be added | ||||
| to meta data:: | to meta data:: | ||||
| combine: | combine: | ||||
| sort: false #Optional. Defaults to true. | sort: false #Optional. Defaults to true. | ||||
| root: content/media #Optional. Path must be relative to content folder - default current folder | |||||
| root: content/media #Optional. Path must be relative to content | |||||
| folder - default current folder | |||||
| recurse: true #Optional. Default false. | recurse: true #Optional. Default false. | ||||
| files: | files: | ||||
| - ns1.*.js | - ns1.*.js | ||||
| @@ -97,13 +101,14 @@ class CombinePlugin(Plugin): | |||||
| except AttributeError: | except AttributeError: | ||||
| raise AttributeError("No resources to combine for [%s]" % resource) | raise AttributeError("No resources to combine for [%s]" % resource) | ||||
| if type(files) is str: | if type(files) is str: | ||||
| files = [ files ] | |||||
| files = [files] | |||||
| # Grab resources to combine | # Grab resources to combine | ||||
| # select site root | # select site root | ||||
| try: | try: | ||||
| root = self.site.content.node_from_relative_path(resource.meta.combine.root) | |||||
| root = self.site.content.node_from_relative_path( | |||||
| resource.meta.combine.root) | |||||
| except AttributeError: | except AttributeError: | ||||
| root = resource.node | root = resource.node | ||||
| @@ -122,10 +127,12 @@ class CombinePlugin(Plugin): | |||||
| sort = True | sort = True | ||||
| if sort: | if sort: | ||||
| resources = sorted([r for r in walker if any(fnmatch(r.name, f) for f in files)], | |||||
| key=operator.attrgetter('name')) | |||||
| resources = sorted([r for r in walker | |||||
| if any(fnmatch(r.name, f) for f in files)], | |||||
| key=operator.attrgetter('name')) | |||||
| else: | else: | ||||
| resources = [(f, r) for r in walker for f in files if fnmatch(r.name, f)] | |||||
| resources = [(f, r) | |||||
| for r in walker for f in files if fnmatch(r.name, f)] | |||||
| resources = [r[1] for f in files for r in resources if f in r] | resources = [r[1] for f in files for r in resources if f in r] | ||||
| if not resources: | if not resources: | ||||
| @@ -173,7 +180,7 @@ class CombinePlugin(Plugin): | |||||
| except AttributeError: | except AttributeError: | ||||
| pass | pass | ||||
| if where not in [ "top", "bottom" ]: | |||||
| if where not in ["top", "bottom"]: | |||||
| raise ValueError("%r should be either `top` or `bottom`" % where) | raise ValueError("%r should be either `top` or `bottom`" % where) | ||||
| self.logger.debug( | self.logger.debug( | ||||
| @@ -190,11 +197,14 @@ class CombinePlugin(Plugin): | |||||
| # | # | ||||
| class Page: | class Page: | ||||
| def __init__(self, posts, number): | def __init__(self, posts, number): | ||||
| self.posts = posts | self.posts = posts | ||||
| self.number = number | self.number = number | ||||
| class Paginator: | class Paginator: | ||||
| """ | """ | ||||
| Iterates resources which have pages associated with them. | Iterates resources which have pages associated with them. | ||||
| """ | """ | ||||
| @@ -204,7 +214,8 @@ class Paginator: | |||||
| def __init__(self, settings): | def __init__(self, settings): | ||||
| self.sorter = getattr(settings, 'sorter', None) | self.sorter = getattr(settings, 'sorter', None) | ||||
| self.size = getattr(settings, 'size', 10) | self.size = getattr(settings, 'size', 10) | ||||
| self.file_pattern = getattr(settings, 'file_pattern', self.file_pattern) | |||||
| self.file_pattern = getattr( | |||||
| settings, 'file_pattern', self.file_pattern) | |||||
| def _relative_url(self, source_path, number, basename, ext): | def _relative_url(self, source_path, number, basename, ext): | ||||
| """ | """ | ||||
| @@ -214,8 +225,8 @@ class Paginator: | |||||
| path = File(source_path) | path = File(source_path) | ||||
| if number != 1: | if number != 1: | ||||
| filename = self.file_pattern.replace('$PAGE', str(number)) \ | filename = self.file_pattern.replace('$PAGE', str(number)) \ | ||||
| .replace('$FILE', basename) \ | |||||
| .replace('$EXT', ext) | |||||
| .replace('$FILE', basename) \ | |||||
| .replace('$EXT', ext) | |||||
| path = path.parent.child(os.path.normpath(filename)) | path = path.parent.child(os.path.normpath(filename)) | ||||
| return path | return path | ||||
| @@ -227,10 +238,11 @@ class Paginator: | |||||
| res = Resource(base_resource.source_file, node) | res = Resource(base_resource.source_file, node) | ||||
| res.node.meta = Metadata(node.meta) | res.node.meta = Metadata(node.meta) | ||||
| res.meta = Metadata(base_resource.meta, res.node.meta) | res.meta = Metadata(base_resource.meta, res.node.meta) | ||||
| brs = base_resource.source_file | |||||
| path = self._relative_url(base_resource.relative_path, | path = self._relative_url(base_resource.relative_path, | ||||
| page_number, | |||||
| base_resource.source_file.name_without_extension, | |||||
| base_resource.source_file.extension) | |||||
| page_number, | |||||
| brs.name_without_extension, | |||||
| brs.extension) | |||||
| res.set_relative_deploy_path(path) | res.set_relative_deploy_path(path) | ||||
| return res | return res | ||||
| @@ -250,7 +262,7 @@ class Paginator: | |||||
| if not hasattr(resource, 'depends'): | if not hasattr(resource, 'depends'): | ||||
| resource.depends = [] | resource.depends = [] | ||||
| resource.depends.extend([dep.relative_path for dep in dependencies | resource.depends.extend([dep.relative_path for dep in dependencies | ||||
| if dep.relative_path not in resource.depends]) | |||||
| if dep.relative_path not in resource.depends]) | |||||
| def _walk_pages_in_node(self, node): | def _walk_pages_in_node(self, node): | ||||
| """ | """ | ||||
| @@ -294,6 +306,7 @@ class Paginator: | |||||
| class PaginatorPlugin(Plugin): | class PaginatorPlugin(Plugin): | ||||
| """ | """ | ||||
| Paginator plugin. | Paginator plugin. | ||||
| @@ -315,6 +328,7 @@ class PaginatorPlugin(Plugin): | |||||
| {{ resource.page.next }} | {{ resource.page.next }} | ||||
| """ | """ | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(PaginatorPlugin, self).__init__(site) | super(PaginatorPlugin, self).__init__(site) | ||||
| @@ -322,10 +336,10 @@ class PaginatorPlugin(Plugin): | |||||
| for node in self.site.content.walk(): | for node in self.site.content.walk(): | ||||
| added_resources = [] | added_resources = [] | ||||
| paged_resources = (res for res in node.resources | paged_resources = (res for res in node.resources | ||||
| if hasattr(res.meta, 'paginator')) | |||||
| if hasattr(res.meta, 'paginator')) | |||||
| for resource in paged_resources: | for resource in paged_resources: | ||||
| paginator = Paginator(resource.meta.paginator) | paginator = Paginator(resource.meta.paginator) | ||||
| added_resources += paginator.walk_paged_resources(node, resource) | |||||
| added_resources += paginator.walk_paged_resources( | |||||
| node, resource) | |||||
| node.resources += added_resources | node.resources += added_resources | ||||
| @@ -3,7 +3,7 @@ | |||||
| Text processing plugins | Text processing plugins | ||||
| """ | """ | ||||
| from hyde.plugin import Plugin,TextyPlugin | |||||
| from hyde.plugin import Plugin, TextyPlugin | |||||
| # | # | ||||
| @@ -11,9 +11,11 @@ from hyde.plugin import Plugin,TextyPlugin | |||||
| # | # | ||||
| class BlockdownPlugin(TextyPlugin): | class BlockdownPlugin(TextyPlugin): | ||||
| """ | """ | ||||
| The plugin class for block text replacement. | The plugin class for block text replacement. | ||||
| """ | """ | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(BlockdownPlugin, self).__init__(site) | super(BlockdownPlugin, self).__init__(site) | ||||
| @@ -55,9 +57,11 @@ class BlockdownPlugin(TextyPlugin): | |||||
| # | # | ||||
| class MarkingsPlugin(TextyPlugin): | class MarkingsPlugin(TextyPlugin): | ||||
| """ | """ | ||||
| The plugin class for mark text replacement. | The plugin class for mark text replacement. | ||||
| """ | """ | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(MarkingsPlugin, self).__init__(site) | super(MarkingsPlugin, self).__init__(site) | ||||
| @@ -99,9 +103,11 @@ class MarkingsPlugin(TextyPlugin): | |||||
| # | # | ||||
| class ReferencePlugin(TextyPlugin): | class ReferencePlugin(TextyPlugin): | ||||
| """ | """ | ||||
| The plugin class for reference text replacement. | The plugin class for reference text replacement. | ||||
| """ | """ | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(ReferencePlugin, self).__init__(site) | super(ReferencePlugin, self).__init__(site) | ||||
| @@ -143,9 +149,11 @@ class ReferencePlugin(TextyPlugin): | |||||
| # | # | ||||
| class SyntextPlugin(TextyPlugin): | class SyntextPlugin(TextyPlugin): | ||||
| """ | """ | ||||
| The plugin class for syntax text replacement. | The plugin class for syntax text replacement. | ||||
| """ | """ | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(SyntextPlugin, self).__init__(site) | super(SyntextPlugin, self).__init__(site) | ||||
| @@ -170,7 +178,6 @@ class SyntextPlugin(TextyPlugin): | |||||
| """ | """ | ||||
| return '^\s*~~~+\s*$' | return '^\s*~~~+\s*$' | ||||
| def get_params(self, match, start=True): | def get_params(self, match, start=True): | ||||
| """ | """ | ||||
| ~~~css~~~ will return css | ~~~css~~~ will return css | ||||
| @@ -199,16 +206,18 @@ class SyntextPlugin(TextyPlugin): | |||||
| # | # | ||||
| class TextlinksPlugin(Plugin): | class TextlinksPlugin(Plugin): | ||||
| """ | """ | ||||
| The plugin class for text link replacement. | The plugin class for text link replacement. | ||||
| """ | """ | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(TextlinksPlugin, self).__init__(site) | super(TextlinksPlugin, self).__init__(site) | ||||
| import re | import re | ||||
| self.content_link = re.compile('\[\[([^\]^!][^\]]*)\]\]', | self.content_link = re.compile('\[\[([^\]^!][^\]]*)\]\]', | ||||
| re.UNICODE|re.MULTILINE) | |||||
| re.UNICODE | re.MULTILINE) | |||||
| self.media_link = re.compile('\[\[\!\!([^\]]*)\]\]', | self.media_link = re.compile('\[\[\!\!([^\]]*)\]\]', | ||||
| re.UNICODE|re.MULTILINE) | |||||
| re.UNICODE | re.MULTILINE) | |||||
| def begin_text_resource(self, resource, text): | def begin_text_resource(self, resource, text): | ||||
| """ | """ | ||||
| @@ -221,11 +230,12 @@ class TextlinksPlugin(Plugin): | |||||
| """ | """ | ||||
| if not resource.uses_template: | if not resource.uses_template: | ||||
| return text | return text | ||||
| def replace_content(match): | def replace_content(match): | ||||
| return self.template.get_content_url_statement(match.groups(1)[0]) | return self.template.get_content_url_statement(match.groups(1)[0]) | ||||
| def replace_media(match): | def replace_media(match): | ||||
| return self.template.get_media_url_statement(match.groups(1)[0]) | return self.template.get_media_url_statement(match.groups(1)[0]) | ||||
| text = self.content_link.sub(replace_content, text) | text = self.content_link.sub(replace_content, text) | ||||
| text = self.media_link.sub(replace_media, text) | text = self.media_link.sub(replace_media, text) | ||||
| return text | return text | ||||
| @@ -8,7 +8,9 @@ from hyde.site import Site | |||||
| from functools import wraps | from functools import wraps | ||||
| from fswrap import File | from fswrap import File | ||||
| class UrlCleanerPlugin(Plugin): | class UrlCleanerPlugin(Plugin): | ||||
| """ | """ | ||||
| Url Cleaner plugin for hyde. Adds to hyde the ability to generate clean | Url Cleaner plugin for hyde. Adds to hyde the ability to generate clean | ||||
| urls. | urls. | ||||
| @@ -53,13 +55,13 @@ class UrlCleanerPlugin(Plugin): | |||||
| def wrapper(site, path, safe=None): | def wrapper(site, path, safe=None): | ||||
| url = urlgetter(site, path, safe) | url = urlgetter(site, path, safe) | ||||
| index_file_names = getattr(settings, | index_file_names = getattr(settings, | ||||
| 'index_file_names', | |||||
| ['index.html']) | |||||
| 'index_file_names', | |||||
| ['index.html']) | |||||
| rep = File(url) | rep = File(url) | ||||
| if rep.name in index_file_names: | if rep.name in index_file_names: | ||||
| url = rep.parent.path.rstrip('/') | url = rep.parent.path.rstrip('/') | ||||
| if hasattr(settings, 'append_slash') and \ | if hasattr(settings, 'append_slash') and \ | ||||
| settings.append_slash: | |||||
| settings.append_slash: | |||||
| url += '/' | url += '/' | ||||
| elif hasattr(settings, 'strip_extensions'): | elif hasattr(settings, 'strip_extensions'): | ||||
| if rep.kind in settings.strip_extensions: | if rep.kind in settings.strip_extensions: | ||||
| @@ -12,9 +12,11 @@ import subprocess | |||||
| class VCSDatesPlugin(Plugin): | class VCSDatesPlugin(Plugin): | ||||
| """ | """ | ||||
| Base class for getting resource timestamps from VCS. | Base class for getting resource timestamps from VCS. | ||||
| """ | """ | ||||
| def __init__(self, site, vcs_name='vcs'): | def __init__(self, site, vcs_name='vcs'): | ||||
| super(VCSDatesPlugin, self).__init__(site) | super(VCSDatesPlugin, self).__init__(site) | ||||
| self.vcs_name = vcs_name | self.vcs_name = vcs_name | ||||
| @@ -38,8 +40,8 @@ class VCSDatesPlugin(Plugin): | |||||
| if created == "git": | if created == "git": | ||||
| created = date_created or \ | created = date_created or \ | ||||
| datetime.utcfromtimestamp( | |||||
| os.path.getctime(resource.path)) | |||||
| datetime.utcfromtimestamp( | |||||
| os.path.getctime(resource.path)) | |||||
| created = created.replace(tzinfo=None) | created = created.replace(tzinfo=None) | ||||
| resource.meta.created = created | resource.meta.created = created | ||||
| @@ -48,7 +50,6 @@ class VCSDatesPlugin(Plugin): | |||||
| modified = modified.replace(tzinfo=None) | modified = modified.replace(tzinfo=None) | ||||
| resource.meta.modified = modified | resource.meta.modified = modified | ||||
| def get_dates(self): | def get_dates(self): | ||||
| """ | """ | ||||
| Extract creation and last modification date from the vcs and include | Extract creation and last modification date from the vcs and include | ||||
| @@ -60,7 +61,10 @@ class VCSDatesPlugin(Plugin): | |||||
| # | # | ||||
| # Git Dates | # Git Dates | ||||
| # | # | ||||
| class GitDatesPlugin(VCSDatesPlugin): | class GitDatesPlugin(VCSDatesPlugin): | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| super(GitDatesPlugin, self).__init__(site, 'git') | super(GitDatesPlugin, self).__init__(site, 'git') | ||||
| @@ -78,7 +82,8 @@ class GitDatesPlugin(VCSDatesPlugin): | |||||
| ]).split("\n") | ]).split("\n") | ||||
| commits = commits[:-1] | commits = commits[:-1] | ||||
| except subprocess.CalledProcessError: | except subprocess.CalledProcessError: | ||||
| self.logger.warning("Unable to get git history for [%s]" % resource) | |||||
| self.logger.warning( | |||||
| "Unable to get git history for [%s]" % resource) | |||||
| commits = None | commits = None | ||||
| if commits: | if commits: | ||||
| @@ -93,6 +98,8 @@ class GitDatesPlugin(VCSDatesPlugin): | |||||
| # | # | ||||
| # Mercurial Dates | # Mercurial Dates | ||||
| # | # | ||||
| class MercurialDatesPlugin(VCSDatesPlugin): | class MercurialDatesPlugin(VCSDatesPlugin): | ||||
| def __init__(self, site): | def __init__(self, site): | ||||
| @@ -105,12 +112,12 @@ class MercurialDatesPlugin(VCSDatesPlugin): | |||||
| # Run hg log --template={date|isodatesec} | # Run hg log --template={date|isodatesec} | ||||
| try: | try: | ||||
| commits = subprocess.check_output([ | commits = subprocess.check_output([ | ||||
| "hg", "log", "--template={date|isodatesec}\n", | |||||
| resource.path]).split('\n') | |||||
| "hg", "log", "--template={date|isodatesec}\n", | |||||
| resource.path]).split('\n') | |||||
| commits = commits[:-1] | commits = commits[:-1] | ||||
| except subprocess.CalledProcessError: | except subprocess.CalledProcessError: | ||||
| self.logger.warning("Unable to get mercurial history for [%s]" | self.logger.warning("Unable to get mercurial history for [%s]" | ||||
| % resource) | |||||
| % resource) | |||||
| commits = None | commits = None | ||||
| if not commits: | if not commits: | ||||
| @@ -8,6 +8,7 @@ from hyde.publisher import Publisher | |||||
| import abc | import abc | ||||
| from subprocess import Popen, PIPE | from subprocess import Popen, PIPE | ||||
| class DVCS(Publisher): | class DVCS(Publisher): | ||||
| __metaclass__ = abc.ABCMeta | __metaclass__ = abc.ABCMeta | ||||
| @@ -19,23 +20,28 @@ class DVCS(Publisher): | |||||
| self.switch(self.branch) | self.switch(self.branch) | ||||
| @abc.abstractmethod | @abc.abstractmethod | ||||
| def pull(self): pass | |||||
| def pull(self): | |||||
| pass | |||||
| @abc.abstractmethod | @abc.abstractmethod | ||||
| def push(self): pass | |||||
| def push(self): | |||||
| pass | |||||
| @abc.abstractmethod | @abc.abstractmethod | ||||
| def commit(self, message): pass | |||||
| def commit(self, message): | |||||
| pass | |||||
| @abc.abstractmethod | @abc.abstractmethod | ||||
| def switch(self, branch): pass | |||||
| def switch(self, branch): | |||||
| pass | |||||
| @abc.abstractmethod | @abc.abstractmethod | ||||
| def add(self, path="."): pass | |||||
| def add(self, path="."): | |||||
| pass | |||||
| @abc.abstractmethod | @abc.abstractmethod | ||||
| def merge(self, branch): pass | |||||
| def merge(self, branch): | |||||
| pass | |||||
| def publish(self): | def publish(self): | ||||
| super(DVCS, self).publish() | super(DVCS, self).publish() | ||||
| @@ -47,8 +53,8 @@ class DVCS(Publisher): | |||||
| self.push() | self.push() | ||||
| class Git(DVCS): | class Git(DVCS): | ||||
| """ | """ | ||||
| Acts as a publisher to a git repository. Can be used to publish to | Acts as a publisher to a git repository. Can be used to publish to | ||||
| github pages. | github pages. | ||||
| @@ -56,7 +62,7 @@ class Git(DVCS): | |||||
| def add(self, path="."): | def add(self, path="."): | ||||
| cmd = Popen('git add "%s"' % path, | cmd = Popen('git add "%s"' % path, | ||||
| cwd=unicode(self.path), stdout=PIPE, shell=True) | |||||
| cwd=unicode(self.path), stdout=PIPE, shell=True) | |||||
| cmdresult = cmd.communicate()[0] | cmdresult = cmd.communicate()[0] | ||||
| if cmd.returncode: | if cmd.returncode: | ||||
| raise Exception(cmdresult) | raise Exception(cmdresult) | ||||
| @@ -79,7 +85,6 @@ class Git(DVCS): | |||||
| if cmd.returncode: | if cmd.returncode: | ||||
| raise Exception(cmdresult) | raise Exception(cmdresult) | ||||
| def commit(self, message): | def commit(self, message): | ||||
| cmd = Popen('git commit -a -m"%s"' % message, | cmd = Popen('git commit -a -m"%s"' % message, | ||||
| cwd=unicode(self.path), stdout=PIPE, shell=True) | cwd=unicode(self.path), stdout=PIPE, shell=True) | ||||
| @@ -100,4 +105,4 @@ class Git(DVCS): | |||||
| cwd=unicode(self.path), stdout=PIPE, shell=True) | cwd=unicode(self.path), stdout=PIPE, shell=True) | ||||
| cmdresult = cmd.communicate()[0] | cmdresult = cmd.communicate()[0] | ||||
| if cmd.returncode: | if cmd.returncode: | ||||
| raise Exception(cmdresult) | |||||
| raise Exception(cmdresult) | |||||
| @@ -32,15 +32,14 @@ except ImportError: | |||||
| raise | raise | ||||
| class PyFS(Publisher): | class PyFS(Publisher): | ||||
| def initialize(self, settings): | def initialize(self, settings): | ||||
| self.settings = settings | self.settings = settings | ||||
| self.url = settings.url | self.url = settings.url | ||||
| self.check_mtime = getattr(settings,"check_mtime",False) | |||||
| self.check_etag = getattr(settings,"check_etag",False) | |||||
| if self.check_etag and not isinstance(self.check_etag,basestring): | |||||
| self.check_mtime = getattr(settings, "check_mtime", False) | |||||
| self.check_etag = getattr(settings, "check_etag", False) | |||||
| if self.check_etag and not isinstance(self.check_etag, basestring): | |||||
| raise ValueError("check_etag must name the etag algorithm") | raise ValueError("check_etag must name the etag algorithm") | ||||
| self.prompt_for_credentials() | self.prompt_for_credentials() | ||||
| self.fs = fsopendir(self.url) | self.fs = fsopendir(self.url) | ||||
| @@ -58,48 +57,47 @@ class PyFS(Publisher): | |||||
| def publish(self): | def publish(self): | ||||
| super(PyFS, self).publish() | super(PyFS, self).publish() | ||||
| deploy_fs = OSFS(self.site.config.deploy_root_path.path) | deploy_fs = OSFS(self.site.config.deploy_root_path.path) | ||||
| for (dirnm,local_filenms) in deploy_fs.walk(): | |||||
| logger.info("Making directory: %s",dirnm) | |||||
| self.fs.makedir(dirnm,allow_recreate=True) | |||||
| remote_fileinfos = self.fs.listdirinfo(dirnm,files_only=True) | |||||
| for (dirnm, local_filenms) in deploy_fs.walk(): | |||||
| logger.info("Making directory: %s", dirnm) | |||||
| self.fs.makedir(dirnm, allow_recreate=True) | |||||
| remote_fileinfos = self.fs.listdirinfo(dirnm, files_only=True) | |||||
| # Process each local file, to see if it needs updating. | # Process each local file, to see if it needs updating. | ||||
| for filenm in local_filenms: | for filenm in local_filenms: | ||||
| filepath = pathjoin(dirnm,filenm) | |||||
| filepath = pathjoin(dirnm, filenm) | |||||
| # Try to find an existing remote file, to compare metadata. | # Try to find an existing remote file, to compare metadata. | ||||
| for (nm,info) in remote_fileinfos: | |||||
| for (nm, info) in remote_fileinfos: | |||||
| if nm == filenm: | if nm == filenm: | ||||
| break | break | ||||
| else: | else: | ||||
| info = {} | info = {} | ||||
| # Skip it if the etags match | # Skip it if the etags match | ||||
| if self.check_etag and "etag" in info: | if self.check_etag and "etag" in info: | ||||
| with deploy_fs.open(filepath,"rb") as f: | |||||
| with deploy_fs.open(filepath, "rb") as f: | |||||
| local_etag = self._calculate_etag(f) | local_etag = self._calculate_etag(f) | ||||
| if info["etag"] == local_etag: | if info["etag"] == local_etag: | ||||
| logger.info("Skipping file [etag]: %s",filepath) | |||||
| logger.info("Skipping file [etag]: %s", filepath) | |||||
| continue | continue | ||||
| # Skip it if the mtime is more recent remotely. | # Skip it if the mtime is more recent remotely. | ||||
| if self.check_mtime and "modified_time" in info: | if self.check_mtime and "modified_time" in info: | ||||
| local_mtime = deploy_fs.getinfo(filepath)["modified_time"] | local_mtime = deploy_fs.getinfo(filepath)["modified_time"] | ||||
| if info["modified_time"] > local_mtime: | if info["modified_time"] > local_mtime: | ||||
| logger.info("Skipping file [mtime]: %s",filepath) | |||||
| logger.info("Skipping file [mtime]: %s", filepath) | |||||
| continue | continue | ||||
| # Upload it to the remote filesystem. | # Upload it to the remote filesystem. | ||||
| logger.info("Uploading file: %s",filepath) | |||||
| with deploy_fs.open(filepath,"rb") as f: | |||||
| self.fs.setcontents(filepath,f) | |||||
| logger.info("Uploading file: %s", filepath) | |||||
| with deploy_fs.open(filepath, "rb") as f: | |||||
| self.fs.setcontents(filepath, f) | |||||
| # Process each remote file, to see if it needs deleting. | # Process each remote file, to see if it needs deleting. | ||||
| for (filenm,info) in remote_fileinfos: | |||||
| filepath = pathjoin(dirnm,filenm) | |||||
| for (filenm, info) in remote_fileinfos: | |||||
| filepath = pathjoin(dirnm, filenm) | |||||
| if filenm not in local_filenms: | if filenm not in local_filenms: | ||||
| logger.info("Removing file: %s",filepath) | |||||
| logger.info("Removing file: %s", filepath) | |||||
| self.fs.remove(filepath) | self.fs.remove(filepath) | ||||
| def _calculate_etag(self,f): | |||||
| hasher = getattr(hashlib,self.check_etag.lower())() | |||||
| data = f.read(1024*64) | |||||
| def _calculate_etag(self, f): | |||||
| hasher = getattr(hashlib, self.check_etag.lower())() | |||||
| data = f.read(1024 * 64) | |||||
| while data: | while data: | ||||
| hasher.update(data) | hasher.update(data) | ||||
| data = f.read(1024*64) | |||||
| data = f.read(1024 * 64) | |||||
| return hasher.hexdigest() | return hasher.hexdigest() | ||||
| @@ -19,16 +19,14 @@ from commando.util import getLoggerWithNullHandler | |||||
| logger = getLoggerWithNullHandler('hyde.ext.publishers.pypi') | logger = getLoggerWithNullHandler('hyde.ext.publishers.pypi') | ||||
| class PyPI(Publisher): | class PyPI(Publisher): | ||||
| def initialize(self, settings): | def initialize(self, settings): | ||||
| self.settings = settings | self.settings = settings | ||||
| self.project = settings.project | self.project = settings.project | ||||
| self.url = getattr(settings,"url","https://pypi.python.org/pypi/") | |||||
| self.username = getattr(settings,"username",None) | |||||
| self.password = getattr(settings,"password",None) | |||||
| self.url = getattr(settings, "url", "https://pypi.python.org/pypi/") | |||||
| self.username = getattr(settings, "username", None) | |||||
| self.password = getattr(settings, "password", None) | |||||
| self.prompt_for_credentials() | self.prompt_for_credentials() | ||||
| def prompt_for_credentials(self): | def prompt_for_credentials(self): | ||||
| @@ -38,12 +36,13 @@ class PyPI(Publisher): | |||||
| else: | else: | ||||
| pypirc = ConfigParser.RawConfigParser() | pypirc = ConfigParser.RawConfigParser() | ||||
| pypirc.read([pypirc_file]) | pypirc.read([pypirc_file]) | ||||
| missing_errs = (ConfigParser.NoSectionError,ConfigParser.NoOptionError) | |||||
| missing_errs = ( | |||||
| ConfigParser.NoSectionError, ConfigParser.NoOptionError) | |||||
| # Try to find username in .pypirc | # Try to find username in .pypirc | ||||
| if self.username is None: | if self.username is None: | ||||
| if pypirc is not None: | if pypirc is not None: | ||||
| try: | try: | ||||
| self.username = pypirc.get("server-login","username") | |||||
| self.username = pypirc.get("server-login", "username") | |||||
| except missing_errs: | except missing_errs: | ||||
| pass | pass | ||||
| # Prompt for username on command-line | # Prompt for username on command-line | ||||
| @@ -54,7 +53,7 @@ class PyPI(Publisher): | |||||
| if self.password is None: | if self.password is None: | ||||
| if pypirc is not None: | if pypirc is not None: | ||||
| try: | try: | ||||
| self.password = pypirc.get("server-login","password") | |||||
| self.password = pypirc.get("server-login", "password") | |||||
| except missing_errs: | except missing_errs: | ||||
| pass | pass | ||||
| # Prompt for username on command-line | # Prompt for username on command-line | ||||
| @@ -73,11 +72,11 @@ class PyPI(Publisher): | |||||
| # Bundle it up into a zipfile | # Bundle it up into a zipfile | ||||
| logger.info("building the zipfile") | logger.info("building the zipfile") | ||||
| root = self.site.config.deploy_root_path | root = self.site.config.deploy_root_path | ||||
| zf = zipfile.ZipFile(tf,"w",zipfile.ZIP_DEFLATED) | |||||
| zf = zipfile.ZipFile(tf, "w", zipfile.ZIP_DEFLATED) | |||||
| try: | try: | ||||
| for item in root.walker.walk_files(): | for item in root.walker.walk_files(): | ||||
| logger.info(" adding file: %s",item.path) | |||||
| zf.write(item.path,item.get_relative_path(root)) | |||||
| logger.info(" adding file: %s", item.path) | |||||
| zf.write(item.path, item.get_relative_path(root)) | |||||
| finally: | finally: | ||||
| zf.close() | zf.close() | ||||
| # Formulate the necessary bits for the HTTP POST. | # Formulate the necessary bits for the HTTP POST. | ||||
| @@ -85,12 +84,14 @@ class PyPI(Publisher): | |||||
| authz = self.username + ":" + self.password | authz = self.username + ":" + self.password | ||||
| authz = "Basic " + standard_b64encode(authz) | authz = "Basic " + standard_b64encode(authz) | ||||
| boundary = "-----------" + os.urandom(20).encode("hex") | boundary = "-----------" + os.urandom(20).encode("hex") | ||||
| sep_boundary = "\r\n--" + boundary | |||||
| end_boundary = "\r\n--" + boundary + "--\r\n" | |||||
| # *F841 local variable 'sep_boundary' is assigned to but never used | |||||
| # sep_boundary = "\r\n--" + boundary | |||||
| # *F841 local variable 'end_boundary' is assigned to but never used | |||||
| # end_boundary = "\r\n--" + boundary + "--\r\n" | |||||
| content_type = "multipart/form-data; boundary=%s" % (boundary,) | content_type = "multipart/form-data; boundary=%s" % (boundary,) | ||||
| items = ((":action","doc_upload"),("name",self.project)) | |||||
| items = ((":action", "doc_upload"), ("name", self.project)) | |||||
| body_prefix = "" | body_prefix = "" | ||||
| for (name,value) in items: | |||||
| for (name, value) in items: | |||||
| body_prefix += "--" + boundary + "\r\n" | body_prefix += "--" + boundary + "\r\n" | ||||
| body_prefix += "Content-Disposition: form-data; name=\"" | body_prefix += "Content-Disposition: form-data; name=\"" | ||||
| body_prefix += name + "\"\r\n\r\n" | body_prefix += name + "\"\r\n\r\n" | ||||
| @@ -110,24 +111,24 @@ class PyPI(Publisher): | |||||
| con.connect() | con.connect() | ||||
| try: | try: | ||||
| con.putrequest("POST", self.url) | con.putrequest("POST", self.url) | ||||
| con.putheader("Content-Type",content_type) | |||||
| con.putheader("Content-Length",str(content_length)) | |||||
| con.putheader("Authorization",authz) | |||||
| con.putheader("Content-Type", content_type) | |||||
| con.putheader("Content-Length", str(content_length)) | |||||
| con.putheader("Authorization", authz) | |||||
| con.endheaders() | con.endheaders() | ||||
| con.send(body_prefix) | con.send(body_prefix) | ||||
| tf.seek(0) | tf.seek(0) | ||||
| data = tf.read(1024*32) | |||||
| data = tf.read(1024 * 32) | |||||
| while data: | while data: | ||||
| con.send(data) | con.send(data) | ||||
| data = tf.read(1024*32) | |||||
| data = tf.read(1024 * 32) | |||||
| con.send(body_suffix) | con.send(body_suffix) | ||||
| r = con.getresponse() | r = con.getresponse() | ||||
| try: | try: | ||||
| # PyPI tries to redirect to the page on success. | # PyPI tries to redirect to the page on success. | ||||
| if r.status in (200,301,): | |||||
| if r.status in (200, 301,): | |||||
| logger.info("success!") | logger.info("success!") | ||||
| else: | else: | ||||
| msg = "Upload failed: %s %s" % (r.status,r.reason,) | |||||
| msg = "Upload failed: %s %s" % (r.status, r.reason,) | |||||
| raise Exception(msg) | raise Exception(msg) | ||||
| finally: | finally: | ||||
| r.close() | r.close() | ||||
| @@ -135,5 +136,3 @@ class PyPI(Publisher): | |||||
| con.close() | con.close() | ||||
| finally: | finally: | ||||
| tf.close() | tf.close() | ||||
| @@ -34,7 +34,9 @@ from hyde.publisher import Publisher | |||||
| from subprocess import Popen, PIPE | from subprocess import Popen, PIPE | ||||
| class SSH(Publisher): | class SSH(Publisher): | ||||
| def initialize(self, settings): | def initialize(self, settings): | ||||
| self.settings = settings | self.settings = settings | ||||
| self.username = settings.username | self.username = settings.username | ||||
| @@ -47,7 +49,7 @@ class SSH(Publisher): | |||||
| command = "{command} {opts} ./ {username}{server}:{target}".format( | command = "{command} {opts} ./ {username}{server}:{target}".format( | ||||
| command=self.command, | command=self.command, | ||||
| opts=self.opts, | opts=self.opts, | ||||
| username=self.username+'@' if self.username else '', | |||||
| username=self.username + '@' if self.username else '', | |||||
| server=self.server, | server=self.server, | ||||
| target=self.target) | target=self.target) | ||||
| deploy_path = self.site.config.deploy_root_path.path | deploy_path = self.site.config.deploy_root_path.path | ||||
| @@ -29,10 +29,13 @@ from commando.util import getLoggerWithNullHandler | |||||
| logger = getLoggerWithNullHandler('hyde.engine.Jinja2') | logger = getLoggerWithNullHandler('hyde.engine.Jinja2') | ||||
| class SilentUndefined(Undefined): | class SilentUndefined(Undefined): | ||||
| """ | """ | ||||
| A redefinition of undefined that eats errors. | A redefinition of undefined that eats errors. | ||||
| """ | """ | ||||
| def __getattr__(self, name): | def __getattr__(self, name): | ||||
| return self | return self | ||||
| @@ -41,6 +44,7 @@ class SilentUndefined(Undefined): | |||||
| def __call__(self, *args, **kwargs): | def __call__(self, *args, **kwargs): | ||||
| return self | return self | ||||
| @contextfunction | @contextfunction | ||||
| def media_url(context, path, safe=None): | def media_url(context, path, safe=None): | ||||
| """ | """ | ||||
| @@ -48,6 +52,7 @@ def media_url(context, path, safe=None): | |||||
| """ | """ | ||||
| return context['site'].media_url(path, safe) | return context['site'].media_url(path, safe) | ||||
| @contextfunction | @contextfunction | ||||
| def content_url(context, path, safe=None): | def content_url(context, path, safe=None): | ||||
| """ | """ | ||||
| @@ -55,6 +60,7 @@ def content_url(context, path, safe=None): | |||||
| """ | """ | ||||
| return context['site'].content_url(path, safe) | return context['site'].content_url(path, safe) | ||||
| @contextfunction | @contextfunction | ||||
| def full_url(context, path, safe=None): | def full_url(context, path, safe=None): | ||||
| """ | """ | ||||
| @@ -62,6 +68,7 @@ def full_url(context, path, safe=None): | |||||
| """ | """ | ||||
| return context['site'].full_url(path, safe) | return context['site'].full_url(path, safe) | ||||
| @contextfilter | @contextfilter | ||||
| def urlencode(ctx, url, safe=None): | def urlencode(ctx, url, safe=None): | ||||
| if safe is not None: | if safe is not None: | ||||
| @@ -69,16 +76,18 @@ def urlencode(ctx, url, safe=None): | |||||
| else: | else: | ||||
| return quote(url.encode('utf8')) | return quote(url.encode('utf8')) | ||||
| @contextfilter | @contextfilter | ||||
| def urldecode(ctx, url): | def urldecode(ctx, url): | ||||
| return unquote(url).decode('utf8') | return unquote(url).decode('utf8') | ||||
| @contextfilter | @contextfilter | ||||
| def date_format(ctx, dt, fmt=None): | def date_format(ctx, dt, fmt=None): | ||||
| if not dt: | if not dt: | ||||
| dt = datetime.now() | dt = datetime.now() | ||||
| if not isinstance(dt, datetime) or \ | if not isinstance(dt, datetime) or \ | ||||
| not isinstance(dt, date): | |||||
| not isinstance(dt, date): | |||||
| logger.error("Date format called on a non date object") | logger.error("Date format called on a non date object") | ||||
| return dt | return dt | ||||
| @@ -93,9 +102,11 @@ def date_format(ctx, dt, fmt=None): | |||||
| def islice(iterable, start=0, stop=3, step=1): | def islice(iterable, start=0, stop=3, step=1): | ||||
| return itertools.islice(iterable, start, stop, step) | return itertools.islice(iterable, start, stop, step) | ||||
| def top(iterable, count=3): | def top(iterable, count=3): | ||||
| return islice(iterable, stop=count) | return islice(iterable, stop=count) | ||||
| def xmldatetime(dt): | def xmldatetime(dt): | ||||
| if not dt: | if not dt: | ||||
| dt = datetime.now() | dt = datetime.now() | ||||
| @@ -105,6 +116,7 @@ def xmldatetime(dt): | |||||
| zprefix = tz[:3] + ":" + tz[3:] | zprefix = tz[:3] + ":" + tz[3:] | ||||
| return dt.strftime("%Y-%m-%dT%H:%M:%S") + zprefix | return dt.strftime("%Y-%m-%dT%H:%M:%S") + zprefix | ||||
| @environmentfilter | @environmentfilter | ||||
| def asciidoc(env, value): | def asciidoc(env, value): | ||||
| """ | """ | ||||
| @@ -122,9 +134,11 @@ def asciidoc(env, value): | |||||
| asciidoc = AsciiDocAPI() | asciidoc = AsciiDocAPI() | ||||
| asciidoc.options('--no-header-footer') | asciidoc.options('--no-header-footer') | ||||
| result = StringIO.StringIO() | result = StringIO.StringIO() | ||||
| asciidoc.execute(StringIO.StringIO(output.encode('utf-8')), result, backend='html4') | |||||
| asciidoc.execute( | |||||
| StringIO.StringIO(output.encode('utf-8')), result, backend='html4') | |||||
| return unicode(result.getvalue(), "utf-8") | return unicode(result.getvalue(), "utf-8") | ||||
| @environmentfilter | @environmentfilter | ||||
| def markdown(env, value): | def markdown(env, value): | ||||
| """ | """ | ||||
| @@ -140,14 +154,15 @@ def markdown(env, value): | |||||
| if hasattr(env.config, 'markdown'): | if hasattr(env.config, 'markdown'): | ||||
| d['extensions'] = getattr(env.config.markdown, 'extensions', []) | d['extensions'] = getattr(env.config.markdown, 'extensions', []) | ||||
| d['extension_configs'] = getattr(env.config.markdown, | d['extension_configs'] = getattr(env.config.markdown, | ||||
| 'extension_configs', | |||||
| Expando({})).to_dict() | |||||
| 'extension_configs', | |||||
| Expando({})).to_dict() | |||||
| if hasattr(env.config.markdown, 'output_format'): | if hasattr(env.config.markdown, 'output_format'): | ||||
| d['output_format'] = env.config.markdown.output_format | d['output_format'] = env.config.markdown.output_format | ||||
| marked = md.Markdown(**d) | marked = md.Markdown(**d) | ||||
| return marked.convert(output) | return marked.convert(output) | ||||
| @environmentfilter | @environmentfilter | ||||
| def restructuredtext(env, value): | def restructuredtext(env, value): | ||||
| """ | """ | ||||
| @@ -161,18 +176,20 @@ def restructuredtext(env, value): | |||||
| highlight_source = False | highlight_source = False | ||||
| if hasattr(env.config, 'restructuredtext'): | if hasattr(env.config, 'restructuredtext'): | ||||
| highlight_source = getattr(env.config.restructuredtext, 'highlight_source', False) | |||||
| highlight_source = getattr( | |||||
| env.config.restructuredtext, 'highlight_source', False) | |||||
| extensions = getattr(env.config.restructuredtext, 'extensions', []) | extensions = getattr(env.config.restructuredtext, 'extensions', []) | ||||
| import imp | import imp | ||||
| for extension in extensions: | for extension in extensions: | ||||
| imp.load_module(extension, *imp.find_module(extension)) | imp.load_module(extension, *imp.find_module(extension)) | ||||
| if highlight_source: | if highlight_source: | ||||
| import hyde.lib.pygments.rst_directive | |||||
| import hyde.lib.pygments.rst_directive # noqa | |||||
| parts = publish_parts(source=value, writer_name="html") | parts = publish_parts(source=value, writer_name="html") | ||||
| return parts['html_body'] | return parts['html_body'] | ||||
| @environmentfilter | @environmentfilter | ||||
| def syntax(env, value, lexer=None, filename=None): | def syntax(env, value, lexer=None, filename=None): | ||||
| """ | """ | ||||
| @@ -184,17 +201,17 @@ def syntax(env, value, lexer=None, filename=None): | |||||
| from pygments import formatters | from pygments import formatters | ||||
| except ImportError: | except ImportError: | ||||
| logger.error(u"pygments library is required to" | logger.error(u"pygments library is required to" | ||||
| " use syntax highlighting tags.") | |||||
| " use syntax highlighting tags.") | |||||
| raise TemplateError("Cannot load pygments") | raise TemplateError("Cannot load pygments") | ||||
| pyg = (lexers.get_lexer_by_name(lexer) | pyg = (lexers.get_lexer_by_name(lexer) | ||||
| if lexer else | |||||
| lexers.guess_lexer(value)) | |||||
| if lexer else | |||||
| lexers.guess_lexer(value)) | |||||
| settings = {} | settings = {} | ||||
| if hasattr(env.config, 'syntax'): | if hasattr(env.config, 'syntax'): | ||||
| settings = getattr(env.config.syntax, | settings = getattr(env.config.syntax, | ||||
| 'options', | |||||
| Expando({})).to_dict() | |||||
| 'options', | |||||
| Expando({})).to_dict() | |||||
| formatter = formatters.HtmlFormatter(**settings) | formatter = formatters.HtmlFormatter(**settings) | ||||
| code = pygments.highlight(value, pyg, formatter) | code = pygments.highlight(value, pyg, formatter) | ||||
| @@ -204,10 +221,13 @@ def syntax(env, value, lexer=None, filename=None): | |||||
| if not getattr(env.config.syntax, 'use_figure', True): | if not getattr(env.config.syntax, 'use_figure', True): | ||||
| return Markup(code) | return Markup(code) | ||||
| return Markup( | return Markup( | ||||
| '<div class="codebox"><figure class="code">%s<figcaption>%s</figcaption></figure></div>\n\n' | |||||
| % (code, caption)) | |||||
| '<div class="codebox"><figure class="code">%s<figcaption>' | |||||
| '%s</figcaption></figure></div>\n\n' | |||||
| % (code, caption)) | |||||
| class Spaceless(Extension): | class Spaceless(Extension): | ||||
| """ | """ | ||||
| Emulates the django spaceless template tag. | Emulates the django spaceless template tag. | ||||
| """ | """ | ||||
| @@ -220,10 +240,10 @@ class Spaceless(Extension): | |||||
| """ | """ | ||||
| lineno = parser.stream.next().lineno | lineno = parser.stream.next().lineno | ||||
| body = parser.parse_statements(['name:endspaceless'], | body = parser.parse_statements(['name:endspaceless'], | ||||
| drop_needle=True) | |||||
| drop_needle=True) | |||||
| return nodes.CallBlock( | return nodes.CallBlock( | ||||
| self.call_method('_render_spaceless'), | |||||
| [], [], body).set_lineno(lineno) | |||||
| self.call_method('_render_spaceless'), | |||||
| [], [], body).set_lineno(lineno) | |||||
| def _render_spaceless(self, caller=None): | def _render_spaceless(self, caller=None): | ||||
| """ | """ | ||||
| @@ -235,7 +255,9 @@ class Spaceless(Extension): | |||||
| return '' | return '' | ||||
| return re.sub(r'>\s+<', '><', unicode(caller().strip())) | return re.sub(r'>\s+<', '><', unicode(caller().strip())) | ||||
| class Asciidoc(Extension): | class Asciidoc(Extension): | ||||
| """ | """ | ||||
| A wrapper around the asciidoc filter for syntactic sugar. | A wrapper around the asciidoc filter for syntactic sugar. | ||||
| """ | """ | ||||
| @@ -243,14 +265,15 @@ class Asciidoc(Extension): | |||||
| def parse(self, parser): | def parse(self, parser): | ||||
| """ | """ | ||||
| Parses the statements and defers to the callback for asciidoc processing. | |||||
| Parses the statements and defers to the callback | |||||
| for asciidoc processing. | |||||
| """ | """ | ||||
| lineno = parser.stream.next().lineno | lineno = parser.stream.next().lineno | ||||
| body = parser.parse_statements(['name:endasciidoc'], drop_needle=True) | body = parser.parse_statements(['name:endasciidoc'], drop_needle=True) | ||||
| return nodes.CallBlock( | return nodes.CallBlock( | ||||
| self.call_method('_render_asciidoc'), | |||||
| [], [], body).set_lineno(lineno) | |||||
| self.call_method('_render_asciidoc'), | |||||
| [], [], body).set_lineno(lineno) | |||||
| def _render_asciidoc(self, caller=None): | def _render_asciidoc(self, caller=None): | ||||
| """ | """ | ||||
| @@ -261,7 +284,9 @@ class Asciidoc(Extension): | |||||
| output = caller().strip() | output = caller().strip() | ||||
| return asciidoc(self.environment, output) | return asciidoc(self.environment, output) | ||||
| class Markdown(Extension): | class Markdown(Extension): | ||||
| """ | """ | ||||
| A wrapper around the markdown filter for syntactic sugar. | A wrapper around the markdown filter for syntactic sugar. | ||||
| """ | """ | ||||
| @@ -269,14 +294,15 @@ class Markdown(Extension): | |||||
| def parse(self, parser): | def parse(self, parser): | ||||
| """ | """ | ||||
| Parses the statements and defers to the callback for markdown processing. | |||||
| Parses the statements and defers to the callback | |||||
| for markdown processing. | |||||
| """ | """ | ||||
| lineno = parser.stream.next().lineno | lineno = parser.stream.next().lineno | ||||
| body = parser.parse_statements(['name:endmarkdown'], drop_needle=True) | body = parser.parse_statements(['name:endmarkdown'], drop_needle=True) | ||||
| return nodes.CallBlock( | return nodes.CallBlock( | ||||
| self.call_method('_render_markdown'), | |||||
| [], [], body).set_lineno(lineno) | |||||
| self.call_method('_render_markdown'), | |||||
| [], [], body).set_lineno(lineno) | |||||
| def _render_markdown(self, caller=None): | def _render_markdown(self, caller=None): | ||||
| """ | """ | ||||
| @@ -287,7 +313,9 @@ class Markdown(Extension): | |||||
| output = caller().strip() | output = caller().strip() | ||||
| return markdown(self.environment, output) | return markdown(self.environment, output) | ||||
| class restructuredText(Extension): | class restructuredText(Extension): | ||||
| """ | """ | ||||
| A wrapper around the restructuredtext filter for syntactic sugar | A wrapper around the restructuredtext filter for syntactic sugar | ||||
| """ | """ | ||||
| @@ -298,10 +326,11 @@ class restructuredText(Extension): | |||||
| Simply extract our content | Simply extract our content | ||||
| """ | """ | ||||
| lineno = parser.stream.next().lineno | lineno = parser.stream.next().lineno | ||||
| body = parser.parse_statements(['name:endrestructuredtext'], drop_needle=True) | |||||
| body = parser.parse_statements( | |||||
| ['name:endrestructuredtext'], drop_needle=True) | |||||
| return nodes.CallBlock(self.call_method('_render_rst'), [], [], body | return nodes.CallBlock(self.call_method('_render_rst'), [], [], body | ||||
| ).set_lineno(lineno) | |||||
| ).set_lineno(lineno) | |||||
| def _render_rst(self, caller=None): | def _render_rst(self, caller=None): | ||||
| """ | """ | ||||
| @@ -312,7 +341,9 @@ class restructuredText(Extension): | |||||
| output = caller().strip() | output = caller().strip() | ||||
| return restructuredtext(self.environment, output) | return restructuredtext(self.environment, output) | ||||
| class YamlVar(Extension): | class YamlVar(Extension): | ||||
| """ | """ | ||||
| An extension that converts the content between the tags | An extension that converts the content between the tags | ||||
| into an yaml object and sets the value in the given | into an yaml object and sets the value in the given | ||||
| @@ -330,16 +361,15 @@ class YamlVar(Extension): | |||||
| var = parser.stream.expect('name').value | var = parser.stream.expect('name').value | ||||
| body = parser.parse_statements(['name:endyaml'], drop_needle=True) | body = parser.parse_statements(['name:endyaml'], drop_needle=True) | ||||
| return [ | return [ | ||||
| nodes.Assign( | |||||
| nodes.Name(var, 'store'), | |||||
| nodes.Const({}) | |||||
| ).set_lineno(lineno), | |||||
| nodes.CallBlock( | |||||
| self.call_method('_set_yaml', | |||||
| args=[nodes.Name(var, 'load')]), | |||||
| [], [], body).set_lineno(lineno) | |||||
| ] | |||||
| nodes.Assign( | |||||
| nodes.Name(var, 'store'), | |||||
| nodes.Const({}) | |||||
| ).set_lineno(lineno), | |||||
| nodes.CallBlock( | |||||
| self.call_method('_set_yaml', | |||||
| args=[nodes.Name(var, 'load')]), | |||||
| [], [], body).set_lineno(lineno) | |||||
| ] | |||||
| def _set_yaml(self, var, caller=None): | def _set_yaml(self, var, caller=None): | ||||
| """ | """ | ||||
| @@ -356,6 +386,7 @@ class YamlVar(Extension): | |||||
| var.update(yaml.load(out)) | var.update(yaml.load(out)) | ||||
| return '' | return '' | ||||
| def parse_kwargs(parser): | def parse_kwargs(parser): | ||||
| """ | """ | ||||
| Parses keyword arguments in tags. | Parses keyword arguments in tags. | ||||
| @@ -368,17 +399,19 @@ def parse_kwargs(parser): | |||||
| value = nodes.Const(parser.stream.next().value) | value = nodes.Const(parser.stream.next().value) | ||||
| return (name, value) | return (name, value) | ||||
| class Syntax(Extension): | class Syntax(Extension): | ||||
| """ | """ | ||||
| A wrapper around the syntax filter for syntactic sugar. | A wrapper around the syntax filter for syntactic sugar. | ||||
| """ | """ | ||||
| tags = set(['syntax']) | tags = set(['syntax']) | ||||
| def parse(self, parser): | def parse(self, parser): | ||||
| """ | """ | ||||
| Parses the statements and defers to the callback for pygments processing. | |||||
| Parses the statements and defers to the callback for | |||||
| pygments processing. | |||||
| """ | """ | ||||
| lineno = parser.stream.next().lineno | lineno = parser.stream.next().lineno | ||||
| lex = nodes.Const(None) | lex = nodes.Const(None) | ||||
| @@ -392,8 +425,8 @@ class Syntax(Extension): | |||||
| (_, value1) = parse_kwargs(parser) | (_, value1) = parse_kwargs(parser) | ||||
| (lex, filename) = (value, value1) \ | (lex, filename) = (value, value1) \ | ||||
| if name == 'lex' \ | |||||
| else (value1, value) | |||||
| if name == 'lex' \ | |||||
| else (value1, value) | |||||
| else: | else: | ||||
| lex = nodes.Const(parser.stream.next().value) | lex = nodes.Const(parser.stream.next().value) | ||||
| if parser.stream.skip_if('comma'): | if parser.stream.skip_if('comma'): | ||||
| @@ -401,10 +434,9 @@ class Syntax(Extension): | |||||
| body = parser.parse_statements(['name:endsyntax'], drop_needle=True) | body = parser.parse_statements(['name:endsyntax'], drop_needle=True) | ||||
| return nodes.CallBlock( | return nodes.CallBlock( | ||||
| self.call_method('_render_syntax', | |||||
| args=[lex, filename]), | |||||
| [], [], body).set_lineno(lineno) | |||||
| self.call_method('_render_syntax', | |||||
| args=[lex, filename]), | |||||
| [], [], body).set_lineno(lineno) | |||||
| def _render_syntax(self, lex, filename, caller=None): | def _render_syntax(self, lex, filename, caller=None): | ||||
| """ | """ | ||||
| @@ -415,7 +447,9 @@ class Syntax(Extension): | |||||
| output = caller().strip() | output = caller().strip() | ||||
| return syntax(self.environment, output, lex, filename) | return syntax(self.environment, output, lex, filename) | ||||
| class IncludeText(Extension): | class IncludeText(Extension): | ||||
| """ | """ | ||||
| Automatically runs `markdown` and `typogrify` on included | Automatically runs `markdown` and `typogrify` on included | ||||
| files. | files. | ||||
| @@ -429,8 +463,8 @@ class IncludeText(Extension): | |||||
| """ | """ | ||||
| node = parser.parse_include() | node = parser.parse_include() | ||||
| return nodes.CallBlock( | return nodes.CallBlock( | ||||
| self.call_method('_render_include_text'), | |||||
| [], [], [node]).set_lineno(node.lineno) | |||||
| self.call_method('_render_include_text'), | |||||
| [], [], [node]).set_lineno(node.lineno) | |||||
| def _render_include_text(self, caller=None): | def _render_include_text(self, caller=None): | ||||
| """ | """ | ||||
| @@ -448,7 +482,9 @@ class IncludeText(Extension): | |||||
| MARKINGS = '_markings_' | MARKINGS = '_markings_' | ||||
| class Reference(Extension): | class Reference(Extension): | ||||
| """ | """ | ||||
| Marks a block in a template such that its available for use | Marks a block in a template such that its available for use | ||||
| when referenced using a `refer` tag. | when referenced using a `refer` tag. | ||||
| @@ -465,11 +501,13 @@ class Reference(Extension): | |||||
| tag = token.value | tag = token.value | ||||
| name = parser.stream.next().value | name = parser.stream.next().value | ||||
| body = parser.parse_statements(['name:end%s' % tag], drop_needle=True) | body = parser.parse_statements(['name:end%s' % tag], drop_needle=True) | ||||
| return nodes.CallBlock( | |||||
| self.call_method('_render_output', | |||||
| args=[nodes.Name(MARKINGS, 'load'), nodes.Const(name)]), | |||||
| [], [], body).set_lineno(lineno) | |||||
| return nodes.CallBlock(self.call_method('_render_output', | |||||
| args=[ | |||||
| nodes.Name(MARKINGS, | |||||
| 'load'), | |||||
| nodes.Const(name) | |||||
| ]), [], [], | |||||
| body).set_lineno(lineno) | |||||
| def _render_output(self, markings, name, caller=None): | def _render_output(self, markings, name, caller=None): | ||||
| """ | """ | ||||
| @@ -482,7 +520,9 @@ class Reference(Extension): | |||||
| markings[name] = out | markings[name] = out | ||||
| return out | return out | ||||
| class Refer(Extension): | class Refer(Extension): | ||||
| """ | """ | ||||
| Imports content blocks specified in the referred template as | Imports content blocks specified in the referred template as | ||||
| variables in a given namespace. | variables in a given namespace. | ||||
| @@ -507,43 +547,43 @@ class Refer(Extension): | |||||
| temp = parser.free_identifier(lineno) | temp = parser.free_identifier(lineno) | ||||
| return [ | return [ | ||||
| nodes.Assign( | |||||
| nodes.Name(temp.name, 'store'), | |||||
| nodes.Name(MARKINGS, 'load') | |||||
| ).set_lineno(lineno), | |||||
| nodes.Assign( | |||||
| nodes.Name(MARKINGS, 'store'), | |||||
| nodes.Const({})).set_lineno(lineno), | |||||
| nodes.Assign( | |||||
| nodes.Name(namespace, 'store'), | |||||
| nodes.Const({})).set_lineno(lineno), | |||||
| nodes.CallBlock( | |||||
| self.call_method('_push_resource', | |||||
| args=[ | |||||
| nodes.Name(namespace, 'load'), | |||||
| nodes.Name('site', 'load'), | |||||
| nodes.Name('resource', 'load'), | |||||
| template]), | |||||
| [], [], []).set_lineno(lineno), | |||||
| nodes.Assign( | |||||
| nodes.Name('resource', 'store'), | |||||
| nodes.Getitem(nodes.Name(namespace, 'load'), | |||||
| nodes.Const('resource'), 'load') | |||||
| ).set_lineno(lineno), | |||||
| nodes.CallBlock( | |||||
| self.call_method('_assign_reference', | |||||
| args=[ | |||||
| nodes.Name(MARKINGS, 'load'), | |||||
| nodes.Name(namespace, 'load')]), | |||||
| [], [], [includeNode]).set_lineno(lineno), | |||||
| nodes.Assign(nodes.Name('resource', 'store'), | |||||
| nodes.Getitem(nodes.Name(namespace, 'load'), | |||||
| nodes.Const('parent_resource'), 'load') | |||||
| ).set_lineno(lineno), | |||||
| nodes.Assign( | |||||
| nodes.Name(MARKINGS, 'store'), | |||||
| nodes.Name(temp.name, 'load') | |||||
| ).set_lineno(lineno), | |||||
| nodes.Assign( | |||||
| nodes.Name(temp.name, 'store'), | |||||
| nodes.Name(MARKINGS, 'load') | |||||
| ).set_lineno(lineno), | |||||
| nodes.Assign( | |||||
| nodes.Name(MARKINGS, 'store'), | |||||
| nodes.Const({})).set_lineno(lineno), | |||||
| nodes.Assign( | |||||
| nodes.Name(namespace, 'store'), | |||||
| nodes.Const({})).set_lineno(lineno), | |||||
| nodes.CallBlock( | |||||
| self.call_method('_push_resource', | |||||
| args=[ | |||||
| nodes.Name(namespace, 'load'), | |||||
| nodes.Name('site', 'load'), | |||||
| nodes.Name('resource', 'load'), | |||||
| template]), | |||||
| [], [], []).set_lineno(lineno), | |||||
| nodes.Assign( | |||||
| nodes.Name('resource', 'store'), | |||||
| nodes.Getitem(nodes.Name(namespace, 'load'), | |||||
| nodes.Const('resource'), 'load') | |||||
| ).set_lineno(lineno), | |||||
| nodes.CallBlock( | |||||
| self.call_method('_assign_reference', | |||||
| args=[ | |||||
| nodes.Name(MARKINGS, 'load'), | |||||
| nodes.Name(namespace, 'load')]), | |||||
| [], [], [includeNode]).set_lineno(lineno), | |||||
| nodes.Assign(nodes.Name('resource', 'store'), | |||||
| nodes.Getitem(nodes.Name(namespace, 'load'), | |||||
| nodes.Const('parent_resource'), 'load') | |||||
| ).set_lineno(lineno), | |||||
| nodes.Assign( | |||||
| nodes.Name(MARKINGS, 'store'), | |||||
| nodes.Name(temp.name, 'load') | |||||
| ).set_lineno(lineno), | |||||
| ] | ] | ||||
| def _push_resource(self, namespace, site, resource, template, caller): | def _push_resource(self, namespace, site, resource, template, caller): | ||||
| @@ -553,10 +593,10 @@ class Refer(Extension): | |||||
| namespace['parent_resource'] = resource | namespace['parent_resource'] = resource | ||||
| if not hasattr(resource, 'depends'): | if not hasattr(resource, 'depends'): | ||||
| resource.depends = [] | resource.depends = [] | ||||
| if not template in resource.depends: | |||||
| if template not in resource.depends: | |||||
| resource.depends.append(template) | resource.depends.append(template) | ||||
| namespace['resource'] = site.content.resource_from_relative_path( | namespace['resource'] = site.content.resource_from_relative_path( | ||||
| template) | |||||
| template) | |||||
| return '' | return '' | ||||
| def _assign_reference(self, markings, namespace, caller): | def _assign_reference(self, markings, namespace, caller): | ||||
| @@ -573,6 +613,7 @@ class Refer(Extension): | |||||
| class HydeLoader(FileSystemLoader): | class HydeLoader(FileSystemLoader): | ||||
| """ | """ | ||||
| A wrapper around the file system loader that performs | A wrapper around the file system loader that performs | ||||
| hyde specific tweaks. | hyde specific tweaks. | ||||
| @@ -582,9 +623,9 @@ class HydeLoader(FileSystemLoader): | |||||
| config = site.config if hasattr(site, 'config') else None | config = site.config if hasattr(site, 'config') else None | ||||
| if config: | if config: | ||||
| super(HydeLoader, self).__init__([ | super(HydeLoader, self).__init__([ | ||||
| unicode(config.content_root_path), | |||||
| unicode(config.layout_root_path), | |||||
| ]) | |||||
| unicode(config.content_root_path), | |||||
| unicode(config.layout_root_path), | |||||
| ]) | |||||
| else: | else: | ||||
| super(HydeLoader, self).__init__(unicode(sitepath)) | super(HydeLoader, self).__init__(unicode(sitepath)) | ||||
| @@ -604,8 +645,8 @@ class HydeLoader(FileSystemLoader): | |||||
| try: | try: | ||||
| (contents, | (contents, | ||||
| filename, | filename, | ||||
| date) = super(HydeLoader, self).get_source( | |||||
| environment, template) | |||||
| date) = super(HydeLoader, self).get_source( | |||||
| environment, template) | |||||
| except UnicodeDecodeError: | except UnicodeDecodeError: | ||||
| HydeException.reraise( | HydeException.reraise( | ||||
| "Unicode error when processing %s" % template, sys.exc_info()) | "Unicode error when processing %s" % template, sys.exc_info()) | ||||
| @@ -624,6 +665,7 @@ class HydeLoader(FileSystemLoader): | |||||
| # pylint: disable-msg=W0104,E0602,W0613,R0201 | # pylint: disable-msg=W0104,E0602,W0613,R0201 | ||||
| class Jinja2Template(Template): | class Jinja2Template(Template): | ||||
| """ | """ | ||||
| The Jinja2 Template implementation | The Jinja2 Template implementation | ||||
| """ | """ | ||||
| @@ -638,23 +680,23 @@ class Jinja2Template(Template): | |||||
| self.site = site | self.site = site | ||||
| self.engine = engine | self.engine = engine | ||||
| self.preprocessor = (engine.preprocessor | self.preprocessor = (engine.preprocessor | ||||
| if hasattr(engine, 'preprocessor') else None) | |||||
| if hasattr(engine, 'preprocessor') else None) | |||||
| self.loader = HydeLoader(self.sitepath, site, self.preprocessor) | self.loader = HydeLoader(self.sitepath, site, self.preprocessor) | ||||
| default_extensions = [ | default_extensions = [ | ||||
| IncludeText, | |||||
| Spaceless, | |||||
| Asciidoc, | |||||
| Markdown, | |||||
| restructuredText, | |||||
| Syntax, | |||||
| Reference, | |||||
| Refer, | |||||
| YamlVar, | |||||
| 'jinja2.ext.do', | |||||
| 'jinja2.ext.loopcontrols', | |||||
| 'jinja2.ext.with_' | |||||
| IncludeText, | |||||
| Spaceless, | |||||
| Asciidoc, | |||||
| Markdown, | |||||
| restructuredText, | |||||
| Syntax, | |||||
| Reference, | |||||
| Refer, | |||||
| YamlVar, | |||||
| 'jinja2.ext.do', | |||||
| 'jinja2.ext.loopcontrols', | |||||
| 'jinja2.ext.with_' | |||||
| ] | ] | ||||
| defaults = { | defaults = { | ||||
| @@ -694,12 +736,12 @@ class Jinja2Template(Template): | |||||
| settings['filters'][name] = getattr(module, function_name) | settings['filters'][name] = getattr(module, function_name) | ||||
| self.env = Environment( | self.env = Environment( | ||||
| loader=self.loader, | |||||
| undefined=SilentUndefined, | |||||
| line_statement_prefix=settings['line_statement_prefix'], | |||||
| trim_blocks=True, | |||||
| bytecode_cache=FileSystemBytecodeCache(), | |||||
| extensions=settings['extensions']) | |||||
| loader=self.loader, | |||||
| undefined=SilentUndefined, | |||||
| line_statement_prefix=settings['line_statement_prefix'], | |||||
| trim_blocks=True, | |||||
| bytecode_cache=FileSystemBytecodeCache(), | |||||
| extensions=settings['extensions']) | |||||
| self.env.globals['media_url'] = media_url | self.env.globals['media_url'] = media_url | ||||
| self.env.globals['content_url'] = content_url | self.env.globals['content_url'] = content_url | ||||
| self.env.globals['full_url'] = full_url | self.env.globals['full_url'] = full_url | ||||
| @@ -738,7 +780,6 @@ class Jinja2Template(Template): | |||||
| if self.env.bytecode_cache: | if self.env.bytecode_cache: | ||||
| self.env.bytecode_cache.clear() | self.env.bytecode_cache.clear() | ||||
| def get_dependencies(self, path): | def get_dependencies(self, path): | ||||
| """ | """ | ||||
| Finds dependencies hierarchically based on the included | Finds dependencies hierarchically based on the included | ||||
| @@ -774,10 +815,12 @@ class Jinja2Template(Template): | |||||
| The pattern for matching selected template statements. | The pattern for matching selected template statements. | ||||
| """ | """ | ||||
| return { | return { | ||||
| "block_open": '\s*\{\%\s*block\s*([^\s]+)\s*\%\}', | |||||
| "block_close": '\s*\{\%\s*endblock\s*([^\s]*)\s*\%\}', | |||||
| "include": '\s*\{\%\s*include\s*(?:\'|\")(.+?\.[^.]*)(?:\'|\")\s*\%\}', | |||||
| "extends": '\s*\{\%\s*extends\s*(?:\'|\")(.+?\.[^.]*)(?:\'|\")\s*\%\}' | |||||
| "block_open": '\s*\{\%\s*block\s*([^\s]+)\s*\%\}', | |||||
| "block_close": '\s*\{\%\s*endblock\s*([^\s]*)\s*\%\}', | |||||
| "include": | |||||
| '\s*\{\%\s*include\s*(?:\'|\")(.+?\.[^.]*)(?:\'|\")\s*\%\}', | |||||
| "extends": | |||||
| '\s*\{\%\s*extends\s*(?:\'|\")(.+?\.[^.]*)(?:\'|\")\s*\%\}' | |||||
| } | } | ||||
| def get_include_statement(self, path_to_include): | def get_include_statement(self, path_to_include): | ||||
| @@ -43,7 +43,6 @@ class Generator(object): | |||||
| site.context = Context.load(site.sitepath, site.config.context) | site.context = Context.load(site.sitepath, site.config.context) | ||||
| self.__context__.update(site.context) | self.__context__.update(site.context) | ||||
| @contextmanager | @contextmanager | ||||
| def context_for_resource(self, resource): | def context_for_resource(self, resource): | ||||
| """ | """ | ||||
| @@ -77,7 +76,8 @@ class Generator(object): | |||||
| providing restricted access to the methods. | providing restricted access to the methods. | ||||
| """ | """ | ||||
| def __init__(self, preprocessor=None, postprocessor=None, context_for_path=None): | |||||
| def __init__(self, preprocessor=None, postprocessor=None, | |||||
| context_for_path=None): | |||||
| self.preprocessor = preprocessor | self.preprocessor = preprocessor | ||||
| self.postprocessor = postprocessor | self.postprocessor = postprocessor | ||||
| self.context_for_path = context_for_path | self.context_for_path = context_for_path | ||||
| @@ -86,14 +86,16 @@ class Generator(object): | |||||
| logger.info("Generating site at [%s]" % self.site.sitepath) | logger.info("Generating site at [%s]" % self.site.sitepath) | ||||
| self.template = Template.find_template(self.site) | self.template = Template.find_template(self.site) | ||||
| logger.debug("Using [%s] as the template", | logger.debug("Using [%s] as the template", | ||||
| self.template.__class__.__name__) | |||||
| self.template.__class__.__name__) | |||||
| logger.info("Configuring the template environment") | logger.info("Configuring the template environment") | ||||
| preprocessor = self.events.begin_text_resource | |||||
| postprocessor = self.events.text_resource_complete | |||||
| proxy = GeneratorProxy(context_for_path=self.context_for_path, | |||||
| preprocessor=preprocessor, | |||||
| postprocessor=postprocessor) | |||||
| self.template.configure(self.site, | self.template.configure(self.site, | ||||
| engine=GeneratorProxy( | |||||
| context_for_path=self.context_for_path, | |||||
| preprocessor=self.events.begin_text_resource, | |||||
| postprocessor=self.events.text_resource_complete)) | |||||
| engine=proxy) | |||||
| self.events.template_loaded(self.template) | self.events.template_loaded(self.template) | ||||
| def initialize(self): | def initialize(self): | ||||
| @@ -124,7 +126,7 @@ class Generator(object): | |||||
| rel_path = resource.relative_path | rel_path = resource.relative_path | ||||
| deps = self.deps[rel_path] if rel_path in self.deps \ | deps = self.deps[rel_path] if rel_path in self.deps \ | ||||
| else self.update_deps(resource) | |||||
| else self.update_deps(resource) | |||||
| return deps | return deps | ||||
| def update_deps(self, resource): | def update_deps(self, resource): | ||||
| @@ -143,7 +145,8 @@ class Generator(object): | |||||
| dep_res = self.site.content.resource_from_relative_path(dep) | dep_res = self.site.content.resource_from_relative_path(dep) | ||||
| if dep_res: | if dep_res: | ||||
| if dep_res.relative_path in self.waiting_deps.keys(): | if dep_res.relative_path in self.waiting_deps.keys(): | ||||
| self.waiting_deps[dep_res.relative_path].append(rel_path) | |||||
| self.waiting_deps[ | |||||
| dep_res.relative_path].append(rel_path) | |||||
| else: | else: | ||||
| deps.extend(self.get_dependencies(dep_res)) | deps.extend(self.get_dependencies(dep_res)) | ||||
| if resource.uses_template: | if resource.uses_template: | ||||
| @@ -166,7 +169,7 @@ class Generator(object): | |||||
| self.load_site_if_needed() | self.load_site_if_needed() | ||||
| target = File(self.site.config.deploy_root_path.child( | target = File(self.site.config.deploy_root_path.child( | ||||
| resource.relative_deploy_path)) | |||||
| resource.relative_deploy_path)) | |||||
| if not target.exists or target.older_than(resource.source_file): | if not target.exists or target.older_than(resource.source_file): | ||||
| logger.debug("Found changes in %s" % resource) | logger.debug("Found changes in %s" % resource) | ||||
| return True | return True | ||||
| @@ -209,7 +212,7 @@ class Generator(object): | |||||
| self.load_site_if_needed() | self.load_site_if_needed() | ||||
| self.events.begin_site() | self.events.begin_site() | ||||
| logger.info("Generating site to [%s]" % | logger.info("Generating site to [%s]" % | ||||
| self.site.config.deploy_root_path) | |||||
| self.site.config.deploy_root_path) | |||||
| self.__generate_node__(self.site.content, incremental) | self.__generate_node__(self.site.content, incremental) | ||||
| self.events.site_complete() | self.events.site_complete() | ||||
| self.finalize() | self.finalize() | ||||
| @@ -261,9 +264,8 @@ class Generator(object): | |||||
| except HydeException: | except HydeException: | ||||
| self.generate_all() | self.generate_all() | ||||
| def generate_resource_at_path(self, | |||||
| resource_path=None, | |||||
| incremental=False): | |||||
| def generate_resource_at_path(self, resource_path=None, | |||||
| incremental=False): | |||||
| """ | """ | ||||
| Generates a single resource. If resource_path is non-existent or empty, | Generates a single resource. If resource_path is non-existent or empty, | ||||
| generates the entire website. | generates the entire website. | ||||
| @@ -311,7 +313,6 @@ class Generator(object): | |||||
| self.__generate_resource__(resource, incremental) | self.__generate_resource__(resource, incremental) | ||||
| self.events.node_complete(node) | self.events.node_complete(node) | ||||
| def __generate_resource__(self, resource, incremental=False): | def __generate_resource__(self, resource, incremental=False): | ||||
| self.refresh_config() | self.refresh_config() | ||||
| if not resource.is_processable: | if not resource.is_processable: | ||||
| @@ -323,7 +324,7 @@ class Generator(object): | |||||
| logger.debug("Processing [%s]", resource) | logger.debug("Processing [%s]", resource) | ||||
| with self.context_for_resource(resource) as context: | with self.context_for_resource(resource) as context: | ||||
| target = File(self.site.config.deploy_root_path.child( | target = File(self.site.config.deploy_root_path.child( | ||||
| resource.relative_deploy_path)) | |||||
| resource.relative_deploy_path)) | |||||
| target.parent.make() | target.parent.make() | ||||
| if resource.simple_copy: | if resource.simple_copy: | ||||
| logger.debug("Simply Copying [%s]", resource) | logger.debug("Simply Copying [%s]", resource) | ||||
| @@ -334,19 +335,19 @@ class Generator(object): | |||||
| logger.debug("Rendering [%s]", resource) | logger.debug("Rendering [%s]", resource) | ||||
| try: | try: | ||||
| text = self.template.render_resource(resource, | text = self.template.render_resource(resource, | ||||
| context) | |||||
| context) | |||||
| except Exception, e: | except Exception, e: | ||||
| HydeException.reraise("Error occurred when" | |||||
| " processing template: [%s]: %s" % | |||||
| (resource, repr(e)), | |||||
| sys.exc_info() | |||||
| ) | |||||
| HydeException.reraise("Error occurred when processing" | |||||
| "template: [%s]: %s" % | |||||
| (resource, repr(e)), | |||||
| sys.exc_info()) | |||||
| else: | else: | ||||
| text = resource.source_file.read_all() | text = resource.source_file.read_all() | ||||
| text = self.events.begin_text_resource(resource, text) or text | |||||
| text = self.events.begin_text_resource( | |||||
| resource, text) or text | |||||
| text = self.events.text_resource_complete( | text = self.events.text_resource_complete( | ||||
| resource, text) or text | |||||
| resource, text) or text | |||||
| target.write(text) | target.write(text) | ||||
| copymode(resource.source_file.path, target.path) | copymode(resource.source_file.path, target.path) | ||||
| else: | else: | ||||
| @@ -11,6 +11,7 @@ LAYOUTS = "layouts" | |||||
| class Layout(object): | class Layout(object): | ||||
| """ | """ | ||||
| Represents a layout package | Represents a layout package | ||||
| """ | """ | ||||
| @@ -26,10 +27,10 @@ class Layout(object): | |||||
| layout_folder = None | layout_folder = None | ||||
| if HYDE_DATA in os.environ: | if HYDE_DATA in os.environ: | ||||
| layout_folder = Layout._get_layout_folder( | layout_folder = Layout._get_layout_folder( | ||||
| os.environ[HYDE_DATA], layout_name) | |||||
| os.environ[HYDE_DATA], layout_name) | |||||
| if not layout_folder: | if not layout_folder: | ||||
| layout_folder = Layout._get_layout_folder( | layout_folder = Layout._get_layout_folder( | ||||
| File(__file__).parent, layout_name) | |||||
| File(__file__).parent, layout_name) | |||||
| return layout_folder | return layout_folder | ||||
| @staticmethod | @staticmethod | ||||
| @@ -58,7 +58,9 @@ from docutils.parsers.rst import directives, Directive | |||||
| from pygments import highlight | from pygments import highlight | ||||
| from pygments.lexers import get_lexer_by_name, TextLexer | from pygments.lexers import get_lexer_by_name, TextLexer | ||||
| class Pygments(Directive): | class Pygments(Directive): | ||||
| """ Source code syntax hightlighting. | """ Source code syntax hightlighting. | ||||
| """ | """ | ||||
| required_arguments = 1 | required_arguments = 1 | ||||
| @@ -75,7 +77,8 @@ class Pygments(Directive): | |||||
| # no lexer found - use the text one instead of an exception | # no lexer found - use the text one instead of an exception | ||||
| lexer = TextLexer() | lexer = TextLexer() | ||||
| # take an arbitrary option if more than one is given | # take an arbitrary option if more than one is given | ||||
| formatter = self.options and VARIANTS[self.options.keys()[0]] or DEFAULT | |||||
| formatter = self.options and VARIANTS[ | |||||
| self.options.keys()[0]] or DEFAULT | |||||
| parsed = highlight(u'\n'.join(self.content), lexer, formatter) | parsed = highlight(u'\n'.join(self.content), lexer, formatter) | ||||
| return [nodes.raw('', parsed, format='html')] | return [nodes.raw('', parsed, format='html')] | ||||
| @@ -5,9 +5,10 @@ The hyde executable | |||||
| """ | """ | ||||
| from hyde.engine import Engine | from hyde.engine import Engine | ||||
| def main(): | def main(): | ||||
| """Main""" | """Main""" | ||||
| Engine().run() | Engine().run() | ||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||
| main() | |||||
| main() | |||||
| @@ -14,6 +14,7 @@ logger = getLoggerWithNullHandler('hyde.engine') | |||||
| SEQS = (tuple, list, set, frozenset) | SEQS = (tuple, list, set, frozenset) | ||||
| def make_expando(primitive): | def make_expando(primitive): | ||||
| """ | """ | ||||
| Creates an expando object, a sequence of expando objects or just | Creates an expando object, a sequence of expando objects or just | ||||
| @@ -29,6 +30,7 @@ def make_expando(primitive): | |||||
| class Expando(object): | class Expando(object): | ||||
| """ | """ | ||||
| A generic expando class that creates attributes from | A generic expando class that creates attributes from | ||||
| the passed in dictionary. | the passed in dictionary. | ||||
| @@ -63,7 +65,6 @@ class Expando(object): | |||||
| """ | """ | ||||
| setattr(self, unicode(key).encode('utf-8'), make_expando(value)) | setattr(self, unicode(key).encode('utf-8'), make_expando(value)) | ||||
| def __repr__(self): | def __repr__(self): | ||||
| return unicode(self.to_dict()) | return unicode(self.to_dict()) | ||||
| @@ -79,9 +80,9 @@ class Expando(object): | |||||
| elif isinstance(v, SEQS): | elif isinstance(v, SEQS): | ||||
| seq = type(v) | seq = type(v) | ||||
| result[k] = seq(item.to_dict() | result[k] = seq(item.to_dict() | ||||
| if isinstance(item, Expando) | |||||
| else item for item in v | |||||
| ) | |||||
| if isinstance(item, Expando) | |||||
| else item for item in v | |||||
| ) | |||||
| else: | else: | ||||
| result[k] = v | result[k] = v | ||||
| return result | return result | ||||
| @@ -94,6 +95,7 @@ class Expando(object): | |||||
| class Context(object): | class Context(object): | ||||
| """ | """ | ||||
| Wraps the context related functions and utilities. | Wraps the context related functions and utilities. | ||||
| """ | """ | ||||
| @@ -125,7 +127,9 @@ class Context(object): | |||||
| return context | return context | ||||
| class Dependents(IterableUserDict): | class Dependents(IterableUserDict): | ||||
| """ | """ | ||||
| Represents the dependency graph for hyde. | Represents the dependency graph for hyde. | ||||
| """ | """ | ||||
| @@ -146,11 +150,14 @@ class Dependents(IterableUserDict): | |||||
| if self.deps_file.parent.exists: | if self.deps_file.parent.exists: | ||||
| self.deps_file.write(yaml.dump(self.data)) | self.deps_file.write(yaml.dump(self.data)) | ||||
| def _expand_path(sitepath, path): | def _expand_path(sitepath, path): | ||||
| child = sitepath.child_folder(path) | child = sitepath.child_folder(path) | ||||
| return Folder(child.fully_expanded_path) | return Folder(child.fully_expanded_path) | ||||
| class Config(Expando): | class Config(Expando): | ||||
| """ | """ | ||||
| Represents the hyde configuration file | Represents the hyde configuration file | ||||
| """ | """ | ||||
| @@ -158,7 +165,7 @@ class Config(Expando): | |||||
| def __init__(self, sitepath, config_file=None, config_dict=None): | def __init__(self, sitepath, config_file=None, config_dict=None): | ||||
| self.default_config = dict( | self.default_config = dict( | ||||
| mode='production', | mode='production', | ||||
| simple_copy = [], | |||||
| simple_copy=[], | |||||
| content_root='content', | content_root='content', | ||||
| deploy_root='deploy', | deploy_root='deploy', | ||||
| media_root='media', | media_root='media', | ||||
| @@ -167,9 +174,9 @@ class Config(Expando): | |||||
| base_url="/", | base_url="/", | ||||
| encode_safe=None, | encode_safe=None, | ||||
| not_found='404.html', | not_found='404.html', | ||||
| plugins = [], | |||||
| ignore = [ "*~", "*.bak", ".hg", ".git", ".svn"], | |||||
| meta = { | |||||
| plugins=[], | |||||
| ignore=["*~", "*.bak", ".hg", ".git", ".svn"], | |||||
| meta={ | |||||
| "nodemeta": 'meta.yaml' | "nodemeta": 'meta.yaml' | ||||
| } | } | ||||
| ) | ) | ||||
| @@ -188,7 +195,7 @@ class Config(Expando): | |||||
| if not self.config_files: | if not self.config_files: | ||||
| return True | return True | ||||
| return any((conf.has_changed_since(self.load_time) | return any((conf.has_changed_since(self.load_time) | ||||
| for conf in self.config_files)) | |||||
| for conf in self.config_files)) | |||||
| def load(self): | def load(self): | ||||
| conf = dict(**self.default_config) | conf = dict(**self.default_config) | ||||
| @@ -202,15 +209,14 @@ class Config(Expando): | |||||
| return | return | ||||
| self.update(self.load()) | self.update(self.load()) | ||||
| def read_config(self, config_file): | def read_config(self, config_file): | ||||
| """ | """ | ||||
| Reads the configuration file and updates this | Reads the configuration file and updates this | ||||
| object while allowing for inherited configurations. | object while allowing for inherited configurations. | ||||
| """ | """ | ||||
| conf_file = self.sitepath.child( | conf_file = self.sitepath.child( | ||||
| config_file if | |||||
| config_file else 'site.yaml') | |||||
| config_file if | |||||
| config_file else 'site.yaml') | |||||
| conf = {} | conf = {} | ||||
| if File(conf_file).exists: | if File(conf_file).exists: | ||||
| self.config_files.append(File(conf_file)) | self.config_files.append(File(conf_file)) | ||||
| @@ -224,7 +230,6 @@ class Config(Expando): | |||||
| self.load_time = datetime.now() | self.load_time = datetime.now() | ||||
| return conf | return conf | ||||
| @property | @property | ||||
| def deploy_root_path(self): | def deploy_root_path(self): | ||||
| """ | """ | ||||
| @@ -13,7 +13,6 @@ import os | |||||
| import re | import re | ||||
| import subprocess | import subprocess | ||||
| import sys | import sys | ||||
| import traceback | |||||
| from commando.util import getLoggerWithNullHandler, load_python_object | from commando.util import getLoggerWithNullHandler, load_python_object | ||||
| from fswrap import File | from fswrap import File | ||||
| @@ -22,30 +21,53 @@ logger = getLoggerWithNullHandler('hyde.engine') | |||||
| # Plugins have been reorganized. Map old plugin paths to new. | # Plugins have been reorganized. Map old plugin paths to new. | ||||
| PLUGINS_OLD_AND_NEW = { | PLUGINS_OLD_AND_NEW = { | ||||
| "hyde.ext.plugins.less.LessCSSPlugin" : "hyde.ext.plugins.css.LessCSSPlugin", | |||||
| "hyde.ext.plugins.stylus.StylusPlugin" : "hyde.ext.plugins.css.StylusPlugin", | |||||
| "hyde.ext.plugins.jpegoptim.JPEGOptimPlugin" : "hyde.ext.plugins.images.JPEGOptimPlugin", | |||||
| "hyde.ext.plugins.optipng.OptiPNGPlugin" : "hyde.ext.plugins.images.OptiPNGPlugin", | |||||
| "hyde.ext.plugins.jpegtran.JPEGTranPlugin" : "hyde.ext.plugins.images.JPEGTranPlugin", | |||||
| "hyde.ext.plugins.uglify.UglifyPlugin": "hyde.ext.plugins.js.UglifyPlugin", | |||||
| "hyde.ext.plugins.requirejs.RequireJSPlugin": "hyde.ext.plugins.js.RequireJSPlugin", | |||||
| "hyde.ext.plugins.coffee.CoffeePlugin": "hyde.ext.plugins.js.CoffeePlugin", | |||||
| "hyde.ext.plugins.sorter.SorterPlugin": "hyde.ext.plugins.meta.SorterPlugin", | |||||
| "hyde.ext.plugins.grouper.GrouperPlugin": "hyde.ext.plugins.meta.GrouperPlugin", | |||||
| "hyde.ext.plugins.tagger.TaggerPlugin": "hyde.ext.plugins.meta.TaggerPlugin", | |||||
| "hyde.ext.plugins.auto_extend.AutoExtendPlugin": "hyde.ext.plugins.meta.AutoExtendPlugin", | |||||
| "hyde.ext.plugins.folders.FlattenerPlugin": "hyde.ext.plugins.structure.FlattenerPlugin", | |||||
| "hyde.ext.plugins.combine.CombinePlugin": "hyde.ext.plugins.structure.CombinePlugin", | |||||
| "hyde.ext.plugins.paginator.PaginatorPlugin": "hyde.ext.plugins.structure.PaginatorPlugin", | |||||
| "hyde.ext.plugins.blockdown.BlockdownPlugin": "hyde.ext.plugins.text.BlockdownPlugin", | |||||
| "hyde.ext.plugins.markings.MarkingsPlugin": "hyde.ext.plugins.text.MarkingsPlugin", | |||||
| "hyde.ext.plugins.markings.ReferencePlugin": "hyde.ext.plugins.text.ReferencePlugin", | |||||
| "hyde.ext.plugins.syntext.SyntextPlugin": "hyde.ext.plugins.text.SyntextPlugin", | |||||
| "hyde.ext.plugins.textlinks.TextlinksPlugin": "hyde.ext.plugins.text.TextlinksPlugin", | |||||
| "hyde.ext.plugins.git.GitDatesPlugin": "hyde.ext.plugins.vcs.GitDatesPlugin" | |||||
| "hyde.ext.plugins.less.LessCSSPlugin": | |||||
| "hyde.ext.plugins.css.LessCSSPlugin", | |||||
| "hyde.ext.plugins.stylus.StylusPlugin": | |||||
| "hyde.ext.plugins.css.StylusPlugin", | |||||
| "hyde.ext.plugins.jpegoptim.JPEGOptimPlugin": | |||||
| "hyde.ext.plugins.images.JPEGOptimPlugin", | |||||
| "hyde.ext.plugins.optipng.OptiPNGPlugin": | |||||
| "hyde.ext.plugins.images.OptiPNGPlugin", | |||||
| "hyde.ext.plugins.jpegtran.JPEGTranPlugin": | |||||
| "hyde.ext.plugins.images.JPEGTranPlugin", | |||||
| "hyde.ext.plugins.uglify.UglifyPlugin": | |||||
| "hyde.ext.plugins.js.UglifyPlugin", | |||||
| "hyde.ext.plugins.requirejs.RequireJSPlugin": | |||||
| "hyde.ext.plugins.js.RequireJSPlugin", | |||||
| "hyde.ext.plugins.coffee.CoffeePlugin": | |||||
| "hyde.ext.plugins.js.CoffeePlugin", | |||||
| "hyde.ext.plugins.sorter.SorterPlugin": | |||||
| "hyde.ext.plugins.meta.SorterPlugin", | |||||
| "hyde.ext.plugins.grouper.GrouperPlugin": | |||||
| "hyde.ext.plugins.meta.GrouperPlugin", | |||||
| "hyde.ext.plugins.tagger.TaggerPlugin": | |||||
| "hyde.ext.plugins.meta.TaggerPlugin", | |||||
| "hyde.ext.plugins.auto_extend.AutoExtendPlugin": | |||||
| "hyde.ext.plugins.meta.AutoExtendPlugin", | |||||
| "hyde.ext.plugins.folders.FlattenerPlugin": | |||||
| "hyde.ext.plugins.structure.FlattenerPlugin", | |||||
| "hyde.ext.plugins.combine.CombinePlugin": | |||||
| "hyde.ext.plugins.structure.CombinePlugin", | |||||
| "hyde.ext.plugins.paginator.PaginatorPlugin": | |||||
| "hyde.ext.plugins.structure.PaginatorPlugin", | |||||
| "hyde.ext.plugins.blockdown.BlockdownPlugin": | |||||
| "hyde.ext.plugins.text.BlockdownPlugin", | |||||
| "hyde.ext.plugins.markings.MarkingsPlugin": | |||||
| "hyde.ext.plugins.text.MarkingsPlugin", | |||||
| "hyde.ext.plugins.markings.ReferencePlugin": | |||||
| "hyde.ext.plugins.text.ReferencePlugin", | |||||
| "hyde.ext.plugins.syntext.SyntextPlugin": | |||||
| "hyde.ext.plugins.text.SyntextPlugin", | |||||
| "hyde.ext.plugins.textlinks.TextlinksPlugin": | |||||
| "hyde.ext.plugins.text.TextlinksPlugin", | |||||
| "hyde.ext.plugins.git.GitDatesPlugin": | |||||
| "hyde.ext.plugins.vcs.GitDatesPlugin" | |||||
| } | } | ||||
| class PluginProxy(object): | class PluginProxy(object): | ||||
| """ | """ | ||||
| A proxy class to raise events in registered plugins | A proxy class to raise events in registered plugins | ||||
| """ | """ | ||||
| @@ -61,7 +83,8 @@ class PluginProxy(object): | |||||
| if self.site.plugins: | if self.site.plugins: | ||||
| for plugin in self.site.plugins: | for plugin in self.site.plugins: | ||||
| if hasattr(plugin, method_name): | if hasattr(plugin, method_name): | ||||
| checker = getattr(plugin, 'should_call__' + method_name) | |||||
| checker = getattr( | |||||
| plugin, 'should_call__' + method_name) | |||||
| if checker(*args): | if checker(*args): | ||||
| function = getattr(plugin, method_name) | function = getattr(plugin, method_name) | ||||
| try: | try: | ||||
| @@ -80,9 +103,11 @@ class PluginProxy(object): | |||||
| return __call_plugins__ | return __call_plugins__ | ||||
| raise HydeException( | raise HydeException( | ||||
| "Unknown plugin method [%s] called." % method_name) | |||||
| "Unknown plugin method [%s] called." % method_name) | |||||
| class Plugin(object): | class Plugin(object): | ||||
| """ | """ | ||||
| The plugin protocol | The plugin protocol | ||||
| """ | """ | ||||
| @@ -92,10 +117,9 @@ class Plugin(object): | |||||
| super(Plugin, self).__init__() | super(Plugin, self).__init__() | ||||
| self.site = site | self.site = site | ||||
| self.logger = getLoggerWithNullHandler( | self.logger = getLoggerWithNullHandler( | ||||
| 'hyde.engine.%s' % self.__class__.__name__) | |||||
| 'hyde.engine.%s' % self.__class__.__name__) | |||||
| self.template = None | self.template = None | ||||
| def template_loaded(self, template): | def template_loaded(self, template): | ||||
| """ | """ | ||||
| Called when the template for the site has been identified. | Called when the template for the site has been identified. | ||||
| @@ -123,7 +147,8 @@ class Plugin(object): | |||||
| elif name.startswith('should_call__'): | elif name.startswith('should_call__'): | ||||
| (_, _, method) = name.rpartition('__') | (_, _, method) = name.rpartition('__') | ||||
| if (method in ('begin_text_resource', 'text_resource_complete', | if (method in ('begin_text_resource', 'text_resource_complete', | ||||
| 'begin_binary_resource', 'binary_resource_complete')): | |||||
| 'begin_binary_resource', | |||||
| 'binary_resource_complete')): | |||||
| result = self._file_filter | result = self._file_filter | ||||
| elif (method in ('begin_node', 'node_complete')): | elif (method in ('begin_node', 'node_complete')): | ||||
| result = self._dir_filter | result = self._dir_filter | ||||
| @@ -132,7 +157,7 @@ class Plugin(object): | |||||
| return True | return True | ||||
| result = always_true | result = always_true | ||||
| return result if result else super(Plugin, self).__getattribute__(name) | |||||
| return result if result else super(Plugin, self).__getattribute__(name) | |||||
| @property | @property | ||||
| def settings(self): | def settings(self): | ||||
| @@ -147,7 +172,6 @@ class Plugin(object): | |||||
| pass | pass | ||||
| return opts | return opts | ||||
| @property | @property | ||||
| def plugin_name(self): | def plugin_name(self): | ||||
| """ | """ | ||||
| @@ -195,26 +219,26 @@ class Plugin(object): | |||||
| except AttributeError: | except AttributeError: | ||||
| filters = None | filters = None | ||||
| result = any(fnmatch.fnmatch(resource.path, f) | result = any(fnmatch.fnmatch(resource.path, f) | ||||
| for f in filters) if filters else True | |||||
| for f in filters) if filters else True | |||||
| return result | return result | ||||
| def _dir_filter(self, node, *args, **kwargs): | def _dir_filter(self, node, *args, **kwargs): | ||||
| """ | """ | ||||
| Returns True if the node path is a descendant of the include_paths property in | |||||
| plugin settings. | |||||
| Returns True if the node path is a descendant of the | |||||
| include_paths property in plugin settings. | |||||
| """ | """ | ||||
| try: | try: | ||||
| node_filters = self.settings.include_paths | node_filters = self.settings.include_paths | ||||
| if not isinstance(node_filters, list): | if not isinstance(node_filters, list): | ||||
| node_filters = [node_filters] | node_filters = [node_filters] | ||||
| node_filters = [self.site.content.node_from_relative_path(f) | node_filters = [self.site.content.node_from_relative_path(f) | ||||
| for f in node_filters] | |||||
| for f in node_filters] | |||||
| except AttributeError: | except AttributeError: | ||||
| node_filters = None | node_filters = None | ||||
| result = any(node.source == f.source or | result = any(node.source == f.source or | ||||
| node.source.is_descendant_of(f.source) | |||||
| for f in node_filters if f) \ | |||||
| if node_filters else True | |||||
| node.source.is_descendant_of(f.source) | |||||
| for f in node_filters if f) \ | |||||
| if node_filters else True | |||||
| return result | return result | ||||
| def begin_text_resource(self, resource, text): | def begin_text_resource(self, resource, text): | ||||
| @@ -293,7 +317,7 @@ class Plugin(object): | |||||
| return load_python_object(plugin_name)(site) | return load_python_object(plugin_name)(site) | ||||
| site.plugins = [load_plugin(name) | site.plugins = [load_plugin(name) | ||||
| for name in site.config.plugins] | |||||
| for name in site.config.plugins] | |||||
| @staticmethod | @staticmethod | ||||
| def get_proxy(site): | def get_proxy(site): | ||||
| @@ -302,7 +326,9 @@ class Plugin(object): | |||||
| """ | """ | ||||
| return PluginProxy(site) | return PluginProxy(site) | ||||
| class CLTransformer(Plugin): | class CLTransformer(Plugin): | ||||
| """ | """ | ||||
| Handy class for plugins that simply call a command line app to | Handy class for plugins that simply call a command line app to | ||||
| transform resources. | transform resources. | ||||
| @@ -333,11 +359,11 @@ class CLTransformer(Plugin): | |||||
| """ | """ | ||||
| return ("%(name)s executable path not configured properly. " | return ("%(name)s executable path not configured properly. " | ||||
| "This plugin expects `%(name)s.app` to point " | |||||
| "to the full path of the `%(exec)s` executable." % | |||||
| { | |||||
| "name":self.plugin_name, "exec": self.executable_name | |||||
| }) | |||||
| "This plugin expects `%(name)s.app` to point " | |||||
| "to the full path of the `%(exec)s` executable." % | |||||
| { | |||||
| "name": self.plugin_name, "exec": self.executable_name | |||||
| }) | |||||
| @property | @property | ||||
| def app(self): | def app(self): | ||||
| @@ -398,7 +424,7 @@ class CLTransformer(Plugin): | |||||
| if match: | if match: | ||||
| val = args[match] | val = args[match] | ||||
| param = "%s%s" % (self.option_prefix(descriptive), | param = "%s%s" % (self.option_prefix(descriptive), | ||||
| descriptive) | |||||
| descriptive) | |||||
| if descriptive.endswith("="): | if descriptive.endswith("="): | ||||
| param += val | param += val | ||||
| val = None | val = None | ||||
| @@ -414,13 +440,15 @@ class CLTransformer(Plugin): | |||||
| try: | try: | ||||
| self.logger.debug( | self.logger.debug( | ||||
| "Calling executable [%s] with arguments %s" % | "Calling executable [%s] with arguments %s" % | ||||
| (args[0], unicode(args[1:]))) | |||||
| (args[0], unicode(args[1:]))) | |||||
| return subprocess.check_output(args) | return subprocess.check_output(args) | ||||
| except subprocess.CalledProcessError, error: | except subprocess.CalledProcessError, error: | ||||
| self.logger.error(error.output) | self.logger.error(error.output) | ||||
| raise | raise | ||||
| class TextyPlugin(Plugin): | class TextyPlugin(Plugin): | ||||
| """ | """ | ||||
| Base class for text preprocessing plugins. | Base class for text preprocessing plugins. | ||||
| @@ -493,10 +521,11 @@ class TextyPlugin(Plugin): | |||||
| """ | """ | ||||
| Replace a text base pattern with a template statement. | Replace a text base pattern with a template statement. | ||||
| """ | """ | ||||
| text_open = re.compile(self.open_pattern, re.UNICODE|re.MULTILINE) | |||||
| text_open = re.compile(self.open_pattern, re.UNICODE | re.MULTILINE) | |||||
| text = text_open.sub(self.text_to_tag, text) | text = text_open.sub(self.text_to_tag, text) | ||||
| if self.close_pattern: | if self.close_pattern: | ||||
| text_close = re.compile(self.close_pattern, re.UNICODE|re.MULTILINE) | |||||
| text_close = re.compile( | |||||
| self.close_pattern, re.UNICODE | re.MULTILINE) | |||||
| text = text_close.sub( | text = text_close.sub( | ||||
| partial(self.text_to_tag, start=False), text) | |||||
| partial(self.text_to_tag, start=False), text) | |||||
| return text | return text | ||||
| @@ -8,7 +8,9 @@ Contains abstract classes and utilities that help publishing a website to a | |||||
| server. | server. | ||||
| """ | """ | ||||
| class Publisher(object): | class Publisher(object): | ||||
| """ | """ | ||||
| The abstract base class for publishers. | The abstract base class for publishers. | ||||
| """ | """ | ||||
| @@ -18,13 +20,14 @@ class Publisher(object): | |||||
| def __init__(self, site, settings, message): | def __init__(self, site, settings, message): | ||||
| super(Publisher, self).__init__() | super(Publisher, self).__init__() | ||||
| self.logger = getLoggerWithNullHandler( | self.logger = getLoggerWithNullHandler( | ||||
| 'hyde.engine.%s' % self.__class__.__name__) | |||||
| 'hyde.engine.%s' % self.__class__.__name__) | |||||
| self.site = site | self.site = site | ||||
| self.message = message | self.message = message | ||||
| self.initialize(settings) | self.initialize(settings) | ||||
| @abc.abstractmethod | @abc.abstractmethod | ||||
| def initialize(self, settings): pass | |||||
| def initialize(self, settings): | |||||
| pass | |||||
| @abc.abstractmethod | @abc.abstractmethod | ||||
| def publish(self): | def publish(self): | ||||
| @@ -43,7 +46,8 @@ class Publisher(object): | |||||
| # Find the first configured publisher | # Find the first configured publisher | ||||
| try: | try: | ||||
| publisher = site.config.publisher.__dict__.iterkeys().next() | publisher = site.config.publisher.__dict__.iterkeys().next() | ||||
| logger.warning("No default publisher configured. Using: %s" % publisher) | |||||
| logger.warning( | |||||
| "No default publisher configured. Using: %s" % publisher) | |||||
| settings = attrgetter("publisher.%s" % publisher)(site.config) | settings = attrgetter("publisher.%s" % publisher)(site.config) | ||||
| except (AttributeError, StopIteration): | except (AttributeError, StopIteration): | ||||
| logger.error( | logger.error( | ||||
| @@ -56,4 +60,4 @@ class Publisher(object): | |||||
| raise Exception("Please specify the publisher type in config.") | raise Exception("Please specify the publisher type in config.") | ||||
| pub_class = load_python_object(settings.type) | pub_class = load_python_object(settings.type) | ||||
| return pub_class(site, settings, message) | |||||
| return pub_class(site, settings, message) | |||||
| @@ -18,7 +18,9 @@ from fswrap import File, Folder | |||||
| from commando.util import getLoggerWithNullHandler | from commando.util import getLoggerWithNullHandler | ||||
| logger = getLoggerWithNullHandler('hyde.server') | logger = getLoggerWithNullHandler('hyde.server') | ||||
| class HydeRequestHandler(SimpleHTTPRequestHandler): | class HydeRequestHandler(SimpleHTTPRequestHandler): | ||||
| """ | """ | ||||
| Serves files by regenerating the resource (or) | Serves files by regenerating the resource (or) | ||||
| everything when a request is issued. | everything when a request is issued. | ||||
| @@ -35,7 +37,7 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): | |||||
| logger.debug("Processing request: [%s]" % self.path) | logger.debug("Processing request: [%s]" % self.path) | ||||
| result = urlparse.urlparse(self.path) | result = urlparse.urlparse(self.path) | ||||
| query = urlparse.parse_qs(result.query) | query = urlparse.parse_qs(result.query) | ||||
| if 'refresh' in query or result.query=='refresh': | |||||
| if 'refresh' in query or result.query == 'refresh': | |||||
| self.server.regenerate() | self.server.regenerate() | ||||
| if 'refresh' in query: | if 'refresh' in query: | ||||
| del query['refresh'] | del query['refresh'] | ||||
| @@ -48,7 +50,6 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): | |||||
| else: | else: | ||||
| SimpleHTTPRequestHandler.do_GET(self) | SimpleHTTPRequestHandler.do_GET(self) | ||||
| def translate_path(self, path): | def translate_path(self, path): | ||||
| """ | """ | ||||
| Finds the absolute path of the requested file by | Finds the absolute path of the requested file by | ||||
| @@ -56,7 +57,8 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): | |||||
| """ | """ | ||||
| site = self.server.site | site = self.server.site | ||||
| result = urlparse.urlparse(urllib.unquote(self.path).decode('utf-8')) | result = urlparse.urlparse(urllib.unquote(self.path).decode('utf-8')) | ||||
| logger.debug("Trying to load file based on request: [%s]" % result.path) | |||||
| logger.debug( | |||||
| "Trying to load file based on request: [%s]" % result.path) | |||||
| path = result.path.lstrip('/') | path = result.path.lstrip('/') | ||||
| res = None | res = None | ||||
| if path.strip() == "" or File(path).kind.strip() == "": | if path.strip() == "" or File(path).kind.strip() == "": | ||||
| @@ -65,13 +67,16 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): | |||||
| if isinstance(deployed, Folder): | if isinstance(deployed, Folder): | ||||
| node = site.content.node_from_relative_path(path) | node = site.content.node_from_relative_path(path) | ||||
| res = node.get_resource('index.html') | res = node.get_resource('index.html') | ||||
| elif hasattr(site.config, 'urlcleaner') and hasattr(site.config.urlcleaner, 'strip_extensions'): | |||||
| elif hasattr(site.config, 'urlcleaner') and hasattr( | |||||
| site.config.urlcleaner, 'strip_extensions'): | |||||
| for ext in site.config.urlcleaner.strip_extensions: | for ext in site.config.urlcleaner.strip_extensions: | ||||
| res = site.content.resource_from_relative_deploy_path(path + '.' + ext) | |||||
| res = site.content.resource_from_relative_deploy_path( | |||||
| path + '.' + ext) | |||||
| if res: | if res: | ||||
| break | break | ||||
| for ext in site.config.urlcleaner.strip_extensions: | for ext in site.config.urlcleaner.strip_extensions: | ||||
| new_path = site.config.deploy_root_path.child(path + '.' + ext) | |||||
| new_path = site.config.deploy_root_path.child( | |||||
| path + '.' + ext) | |||||
| if File(new_path).exists: | if File(new_path).exists: | ||||
| return new_path | return new_path | ||||
| else: | else: | ||||
| @@ -83,7 +88,7 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): | |||||
| else: | else: | ||||
| self.server.generate_resource(res) | self.server.generate_resource(res) | ||||
| new_path = site.config.deploy_root_path.child( | new_path = site.config.deploy_root_path.child( | ||||
| res.relative_deploy_path) | |||||
| res.relative_deploy_path) | |||||
| return new_path | return new_path | ||||
| def do_404(self): | def do_404(self): | ||||
| @@ -95,13 +100,13 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): | |||||
| self.redirect(site.config.not_found) | self.redirect(site.config.not_found) | ||||
| else: | else: | ||||
| res = site.content.resource_from_relative_deploy_path( | res = site.content.resource_from_relative_deploy_path( | ||||
| site.config.not_found) | |||||
| site.config.not_found) | |||||
| message = "Requested resource not found" | message = "Requested resource not found" | ||||
| if not res: | if not res: | ||||
| logger.error( | logger.error( | ||||
| "Cannot find the 404 template [%s]." | "Cannot find the 404 template [%s]." | ||||
| % site.config.not_found) | |||||
| % site.config.not_found) | |||||
| else: | else: | ||||
| f404 = File(self.translate_path(site.config.not_found)) | f404 = File(self.translate_path(site.config.not_found)) | ||||
| if f404.exists: | if f404.exists: | ||||
| @@ -118,6 +123,7 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): | |||||
| class HydeWebServer(HTTPServer): | class HydeWebServer(HTTPServer): | ||||
| """ | """ | ||||
| The hyde web server that regenerates the resource, node or site when | The hyde web server that regenerates the resource, node or site when | ||||
| a request is issued. | a request is issued. | ||||
| @@ -133,7 +139,7 @@ class HydeWebServer(HTTPServer): | |||||
| self.__shutdown_request = False | self.__shutdown_request = False | ||||
| self.map_extensions() | self.map_extensions() | ||||
| HTTPServer.__init__(self, (address, port), | HTTPServer.__init__(self, (address, port), | ||||
| HydeRequestHandler) | |||||
| HydeRequestHandler) | |||||
| def map_extensions(self): | def map_extensions(self): | ||||
| """ | """ | ||||
| @@ -161,7 +167,7 @@ class HydeWebServer(HTTPServer): | |||||
| self.generator.generate_all(incremental=False) | self.generator.generate_all(incremental=False) | ||||
| except Exception, exception: | except Exception, exception: | ||||
| logger.error('Error occured when regenerating the site [%s]' | logger.error('Error occured when regenerating the site [%s]' | ||||
| % exception.message) | |||||
| % exception.message) | |||||
| logger.debug(traceback.format_exc()) | logger.debug(traceback.format_exc()) | ||||
| def generate_node(self, node): | def generate_node(self, node): | ||||
| @@ -179,7 +185,7 @@ class HydeWebServer(HTTPServer): | |||||
| except Exception, exception: | except Exception, exception: | ||||
| logger.error( | logger.error( | ||||
| 'Error [%s] occured when generating the node [%s]' | 'Error [%s] occured when generating the node [%s]' | ||||
| % (repr(exception), node)) | |||||
| % (repr(exception), node)) | |||||
| logger.debug(traceback.format_exc()) | logger.debug(traceback.format_exc()) | ||||
| def generate_resource(self, resource): | def generate_resource(self, resource): | ||||
| @@ -198,5 +204,5 @@ class HydeWebServer(HTTPServer): | |||||
| except Exception, exception: | except Exception, exception: | ||||
| logger.error( | logger.error( | ||||
| 'Error [%s] occured when serving the resource [%s]' | 'Error [%s] occured when serving the resource [%s]' | ||||
| % (repr(exception), resource)) | |||||
| % (repr(exception), resource)) | |||||
| logger.debug(traceback.format_exc()) | logger.debug(traceback.format_exc()) | ||||
| @@ -15,6 +15,7 @@ from hyde.model import Config | |||||
| from commando.util import getLoggerWithNullHandler | from commando.util import getLoggerWithNullHandler | ||||
| from fswrap import FS, File, Folder | from fswrap import FS, File, Folder | ||||
| def path_normalized(f): | def path_normalized(f): | ||||
| @wraps(f) | @wraps(f) | ||||
| def wrapper(self, path): | def wrapper(self, path): | ||||
| @@ -23,6 +24,7 @@ def path_normalized(f): | |||||
| logger = getLoggerWithNullHandler('hyde.engine') | logger = getLoggerWithNullHandler('hyde.engine') | ||||
| class Processable(object): | class Processable(object): | ||||
| """ | """ | ||||
| A node or resource. | A node or resource. | ||||
| @@ -64,8 +66,8 @@ class Processable(object): | |||||
| after its been processed. | after its been processed. | ||||
| """ | """ | ||||
| return self._relative_deploy_path \ | return self._relative_deploy_path \ | ||||
| if self._relative_deploy_path is not None \ | |||||
| else self.relative_path | |||||
| if self._relative_deploy_path is not None \ | |||||
| else self.relative_path | |||||
| def set_relative_deploy_path(self, path): | def set_relative_deploy_path(self, path): | ||||
| """ | """ | ||||
| @@ -75,7 +77,8 @@ class Processable(object): | |||||
| self._relative_deploy_path = path | self._relative_deploy_path = path | ||||
| self.site.content.deploy_path_changed(self) | self.site.content.deploy_path_changed(self) | ||||
| relative_deploy_path = property(get_relative_deploy_path, set_relative_deploy_path) | |||||
| relative_deploy_path = property(get_relative_deploy_path, | |||||
| set_relative_deploy_path) | |||||
| @property | @property | ||||
| def url(self): | def url(self): | ||||
| @@ -118,9 +121,10 @@ class Resource(Processable): | |||||
| @property | @property | ||||
| def slug(self): | def slug(self): | ||||
| #TODO: Add a more sophisticated slugify method | |||||
| # TODO: Add a more sophisticated slugify method | |||||
| return self.source.name_without_extension | return self.source.name_without_extension | ||||
| class Node(Processable): | class Node(Processable): | ||||
| """ | """ | ||||
| Represents any folder that is processed by hyde | Represents any folder that is processed by hyde | ||||
| @@ -159,7 +163,7 @@ class Node(Processable): | |||||
| if self.contains_resource(resource_name): | if self.contains_resource(resource_name): | ||||
| return self.root.resource_from_path( | return self.root.resource_from_path( | ||||
| self.source_folder.child(resource_name)) | |||||
| self.source_folder.child(resource_name)) | |||||
| return None | return None | ||||
| def add_child_node(self, folder): | def add_child_node(self, folder): | ||||
| @@ -223,6 +227,7 @@ class Node(Processable): | |||||
| """ | """ | ||||
| return self.source_folder.get_relative_path(self.root.source_folder) | return self.source_folder.get_relative_path(self.root.source_folder) | ||||
| class RootNode(Node): | class RootNode(Node): | ||||
| """ | """ | ||||
| Represents one of the roots of site: Content, Media or Layout | Represents one of the roots of site: Content, Media or Layout | ||||
| @@ -253,7 +258,7 @@ class RootNode(Node): | |||||
| If no match is found it returns None. | If no match is found it returns None. | ||||
| """ | """ | ||||
| return self.node_from_path( | return self.node_from_path( | ||||
| self.source_folder.child(unicode(relative_path))) | |||||
| self.source_folder.child(unicode(relative_path))) | |||||
| @path_normalized | @path_normalized | ||||
| def resource_from_path(self, path): | def resource_from_path(self, path): | ||||
| @@ -270,7 +275,7 @@ class RootNode(Node): | |||||
| If no match is found it returns None. | If no match is found it returns None. | ||||
| """ | """ | ||||
| return self.resource_from_path( | return self.resource_from_path( | ||||
| self.source_folder.child(relative_path)) | |||||
| self.source_folder.child(relative_path)) | |||||
| def deploy_path_changed(self, item): | def deploy_path_changed(self, item): | ||||
| """ | """ | ||||
| @@ -320,7 +325,7 @@ class RootNode(Node): | |||||
| node = node.add_child_node(h_folder) | node = node.add_child_node(h_folder) | ||||
| self.node_map[unicode(h_folder)] = node | self.node_map[unicode(h_folder)] = node | ||||
| logger.debug("Added node [%s] to [%s]" % ( | logger.debug("Added node [%s] to [%s]" % ( | ||||
| node.relative_path, self.source_folder)) | |||||
| node.relative_path, self.source_folder)) | |||||
| return node | return node | ||||
| @@ -350,11 +355,10 @@ class RootNode(Node): | |||||
| self.resource_map[unicode(afile)] = resource | self.resource_map[unicode(afile)] = resource | ||||
| relative_path = resource.relative_path | relative_path = resource.relative_path | ||||
| resource.simple_copy = any(fnmatch.fnmatch(relative_path, pattern) | resource.simple_copy = any(fnmatch.fnmatch(relative_path, pattern) | ||||
| for pattern | |||||
| in self.site.config.simple_copy) | |||||
| for pattern in self.site.config.simple_copy) | |||||
| logger.debug("Added resource [%s] to [%s]" % | logger.debug("Added resource [%s] to [%s]" % | ||||
| (resource.relative_path, self.source_folder)) | |||||
| (resource.relative_path, self.source_folder)) | |||||
| return resource | return resource | ||||
| def load(self): | def load(self): | ||||
| @@ -396,6 +400,7 @@ def _encode_path(base, path, safe): | |||||
| path = quote(path, safe) if safe is not None else quote(path) | path = quote(path, safe) if safe is not None else quote(path) | ||||
| return base.rstrip('/') + '/' + path.lstrip('/') | return base.rstrip('/') + '/' + path.lstrip('/') | ||||
| class Site(object): | class Site(object): | ||||
| """ | """ | ||||
| Represents the site to be generated. | Represents the site to be generated. | ||||
| @@ -420,9 +425,8 @@ class Site(object): | |||||
| """ | """ | ||||
| if self.config.needs_refresh(): | if self.config.needs_refresh(): | ||||
| logger.debug("Refreshing config data") | logger.debug("Refreshing config data") | ||||
| self.config = Config(self.sitepath, | |||||
| self.config.config_file, | |||||
| self.config.config_dict) | |||||
| self.config = Config(self.sitepath, self.config.config_file, | |||||
| self.config.config_dict) | |||||
| def reload_if_needed(self): | def reload_if_needed(self): | ||||
| """ | """ | ||||
| @@ -453,13 +457,13 @@ class Site(object): | |||||
| """ | """ | ||||
| return _encode_path(self.config.base_url, path, self._safe_chars(safe)) | return _encode_path(self.config.base_url, path, self._safe_chars(safe)) | ||||
| def media_url(self, path, safe=None): | def media_url(self, path, safe=None): | ||||
| """ | """ | ||||
| Returns the media url by appending the media base url from the config | Returns the media url by appending the media base url from the config | ||||
| with the given path. The return value is url encoded. | with the given path. The return value is url encoded. | ||||
| """ | """ | ||||
| return _encode_path(self.config.media_url, path, self._safe_chars(safe)) | |||||
| return _encode_path(self.config.media_url, path, | |||||
| self._safe_chars(safe)) | |||||
| def full_url(self, path, safe=None): | def full_url(self, path, safe=None): | ||||
| """ | """ | ||||
| @@ -467,12 +471,12 @@ class Site(object): | |||||
| configuration and returns the appropriate url. The return value | configuration and returns the appropriate url. The return value | ||||
| is url encoded. | is url encoded. | ||||
| """ | """ | ||||
| if urlparse.urlparse(path)[:2] != ("",""): | |||||
| if urlparse.urlparse(path)[:2] != ("", ""): | |||||
| return path | return path | ||||
| if self.is_media(path): | if self.is_media(path): | ||||
| relative_path = File(path).get_relative_path( | relative_path = File(path).get_relative_path( | ||||
| Folder(self.config.media_root)) | |||||
| Folder(self.config.media_root)) | |||||
| return self.media_url(relative_path, safe) | return self.media_url(relative_path, safe) | ||||
| else: | else: | ||||
| return self.content_url(path, safe) | return self.content_url(path, safe) | ||||
| @@ -482,4 +486,4 @@ class Site(object): | |||||
| Given the relative path, determines if it is content or media. | Given the relative path, determines if it is content or media. | ||||
| """ | """ | ||||
| folder = self.content.source.child_folder(path) | folder = self.content.source.child_folder(path) | ||||
| return folder.is_descendant_of(self.config.media_root_path) | |||||
| return folder.is_descendant_of(self.config.media_root_path) | |||||
| @@ -11,6 +11,7 @@ from commando.util import getLoggerWithNullHandler | |||||
| class HtmlWrap(object): | class HtmlWrap(object): | ||||
| """ | """ | ||||
| A wrapper class for raw html. | A wrapper class for raw html. | ||||
| @@ -36,7 +37,9 @@ class HtmlWrap(object): | |||||
| return self.raw | return self.raw | ||||
| return self.q(selector).html() | return self.q(selector).html() | ||||
| class Template(object): | class Template(object): | ||||
| """ | """ | ||||
| Interface for hyde template engines. To use a different template engine, | Interface for hyde template engines. To use a different template engine, | ||||
| the following interface must be implemented. | the following interface must be implemented. | ||||
| @@ -50,7 +53,6 @@ class Template(object): | |||||
| @abc.abstractmethod | @abc.abstractmethod | ||||
| def configure(self, site, engine): | def configure(self, site, engine): | ||||
| """ | """ | ||||
| The site object should contain a config attribute. The config object | The site object should contain a config attribute. The config object | ||||
| is a simple YAML object with required settings. The template | is a simple YAML object with required settings. The template | ||||
| @@ -20,7 +20,7 @@ class TestAutoExtend(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -33,7 +33,8 @@ class TestAutoExtend(object): | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_resource_at_path(bd.path) | gen.generate_resource_at_path(bd.path) | ||||
| res = s.content.resource_from_path(bd.path) | res = s.content.resource_from_path(bd.path) | ||||
| target = File(s.config.deploy_root_path.child(res.relative_deploy_path)) | |||||
| target = File( | |||||
| s.config.deploy_root_path.child(res.relative_deploy_path)) | |||||
| assert target.exists | assert target.exists | ||||
| text = target.read_all() | text = target.read_all() | ||||
| q = PyQuery(text) | q = PyQuery(text) | ||||
| @@ -44,7 +45,8 @@ class TestAutoExtend(object): | |||||
| s.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin', | s.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin', | ||||
| 'hyde.ext.plugins.meta.AutoExtendPlugin', | 'hyde.ext.plugins.meta.AutoExtendPlugin', | ||||
| 'hyde.ext.plugins.text.BlockdownPlugin'] | 'hyde.ext.plugins.text.BlockdownPlugin'] | ||||
| txt ="This template tests to make sure blocks can be replaced with markdownish syntax." | |||||
| txt = ("This template tests to make sure blocks can be replaced with" | |||||
| "markdownish syntax.") | |||||
| templ = """ | templ = """ | ||||
| --- | --- | ||||
| extends: base.html | extends: base.html | ||||
| @@ -54,14 +56,13 @@ extends: base.html | |||||
| ====/title========""" | ====/title========""" | ||||
| self.assert_extended(s, txt, templ) | self.assert_extended(s, txt, templ) | ||||
| def test_can_auto_extend_with_default_blocks(self): | def test_can_auto_extend_with_default_blocks(self): | ||||
| s = Site(TEST_SITE) | s = Site(TEST_SITE) | ||||
| s.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin', | s.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin', | ||||
| 'hyde.ext.plugins.meta.AutoExtendPlugin', | 'hyde.ext.plugins.meta.AutoExtendPlugin', | ||||
| 'hyde.ext.plugins.text.BlockdownPlugin'] | 'hyde.ext.plugins.text.BlockdownPlugin'] | ||||
| txt ="This template tests to make sure blocks can be replaced with markdownish syntax." | |||||
| txt = ("This template tests to make sure blocks can be replaced with" | |||||
| "markdownish syntax.") | |||||
| templ = """ | templ = """ | ||||
| --- | --- | ||||
| extends: base.html | extends: base.html | ||||
| @@ -18,7 +18,7 @@ class TestBlockdown(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -26,7 +26,8 @@ class TestBlockdown(object): | |||||
| def test_can_parse_blockdown(self): | def test_can_parse_blockdown(self): | ||||
| s = Site(TEST_SITE) | s = Site(TEST_SITE) | ||||
| s.config.plugins = ['hyde.ext.plugins.text.BlockdownPlugin'] | s.config.plugins = ['hyde.ext.plugins.text.BlockdownPlugin'] | ||||
| txt ="This template tests to make sure blocks can be replaced with markdownish syntax." | |||||
| txt = ("This template tests to make sure blocks can be replaced" | |||||
| "with markdownish syntax.") | |||||
| templ = """ | templ = """ | ||||
| {%% extends "base.html" %%} | {%% extends "base.html" %%} | ||||
| =====title======== | =====title======== | ||||
| @@ -39,7 +40,8 @@ class TestBlockdown(object): | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_resource_at_path(bd.path) | gen.generate_resource_at_path(bd.path) | ||||
| res = s.content.resource_from_path(bd.path) | res = s.content.resource_from_path(bd.path) | ||||
| target = File(s.config.deploy_root_path.child(res.relative_deploy_path)) | |||||
| target = File( | |||||
| s.config.deploy_root_path.child(res.relative_deploy_path)) | |||||
| assert target.exists | assert target.exists | ||||
| text = target.read_all() | text = target.read_all() | ||||
| q = PyQuery(text) | q = PyQuery(text) | ||||
| @@ -12,14 +12,17 @@ from fswrap import File, Folder | |||||
| COMBINE_SOURCE = File(__file__).parent.child_folder('combine') | COMBINE_SOURCE = File(__file__).parent.child_folder('combine') | ||||
| TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
| class CombineTester(object): | class CombineTester(object): | ||||
| def _test_combine(self, content): | def _test_combine(self, content): | ||||
| s = Site(TEST_SITE) | s = Site(TEST_SITE) | ||||
| s.config.plugins = [ | s.config.plugins = [ | ||||
| 'hyde.ext.plugins.meta.MetaPlugin', | 'hyde.ext.plugins.meta.MetaPlugin', | ||||
| 'hyde.ext.plugins.structure.CombinePlugin'] | 'hyde.ext.plugins.structure.CombinePlugin'] | ||||
| source = TEST_SITE.child('content/media/js/script.js') | source = TEST_SITE.child('content/media/js/script.js') | ||||
| target = File(Folder(s.config.deploy_root_path).child('media/js/script.js')) | |||||
| target = File( | |||||
| Folder(s.config.deploy_root_path).child('media/js/script.js')) | |||||
| File(source).write(content) | File(source).write(content) | ||||
| gen = Generator(s) | gen = Generator(s) | ||||
| @@ -29,12 +32,13 @@ class CombineTester(object): | |||||
| text = target.read_all() | text = target.read_all() | ||||
| return text, s | return text, s | ||||
| class TestCombine(CombineTester): | class TestCombine(CombineTester): | ||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| TEST_SITE.child_folder('content/media/js').make() | TEST_SITE.child_folder('content/media/js').make() | ||||
| COMBINE_SOURCE.copy_contents_to(TEST_SITE.child('content/media/js')) | COMBINE_SOURCE.copy_contents_to(TEST_SITE.child('content/media/js')) | ||||
| @@ -107,7 +111,7 @@ combine: | |||||
| --- | --- | ||||
| First line""") | First line""") | ||||
| for i in range(1,4): | |||||
| for i in range(1, 4): | |||||
| assert not File(Folder(s.config.deploy_root_path). | assert not File(Folder(s.config.deploy_root_path). | ||||
| child('media/js/script.%d.js' % i)).exists | child('media/js/script.%d.js' % i)).exists | ||||
| @@ -117,7 +121,7 @@ class TestCombinePaths(CombineTester): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| TEST_SITE.child_folder('content/media/js').make() | TEST_SITE.child_folder('content/media/js').make() | ||||
| JS = TEST_SITE.child_folder('content/scripts').make() | JS = TEST_SITE.child_folder('content/scripts').make() | ||||
| S1 = JS.child_folder('s1').make() | S1 = JS.child_folder('s1').make() | ||||
| @@ -17,10 +17,10 @@ class TestDepends(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'templates/jinja2').copy_contents_to( | |||||
| TEST_SITE.child_folder('content')) | |||||
| 'templates/jinja2').copy_contents_to( | |||||
| TEST_SITE.child_folder('content')) | |||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -40,6 +40,7 @@ depends: index.html | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.load_site_if_needed() | gen.load_site_if_needed() | ||||
| gen.load_template_if_needed() | gen.load_template_if_needed() | ||||
| def dateformat(x): | def dateformat(x): | ||||
| return x.strftime('%Y-%m-%d') | return x.strftime('%Y-%m-%d') | ||||
| gen.template.env.filters['dateformat'] = dateformat | gen.template.env.filters['dateformat'] = dateformat | ||||
| @@ -52,4 +53,4 @@ depends: index.html | |||||
| assert 'helpers.html' in deps | assert 'helpers.html' in deps | ||||
| assert 'layout.html' in deps | assert 'layout.html' in deps | ||||
| assert 'index.html' in deps | |||||
| assert 'index.html' in deps | |||||
| @@ -23,12 +23,13 @@ A draft post. | |||||
| """ | """ | ||||
| class TestDrafts(object): | class TestDrafts(object): | ||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| draft = TEST_SITE.child_file('content/blog/2013/may/draft-post.html') | draft = TEST_SITE.child_file('content/blog/2013/may/draft-post.html') | ||||
| draft.parent.make() | draft.parent.make() | ||||
| draft.write(DRAFT_POST) | draft.write(DRAFT_POST) | ||||
| @@ -50,7 +51,7 @@ class TestDrafts(object): | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| assert not s.config.deploy_root_path.child_file( | assert not s.config.deploy_root_path.child_file( | ||||
| 'blog/2013/may/draft-post.html').exists | |||||
| 'blog/2013/may/draft-post.html').exists | |||||
| def test_drafts_are_published_in_development(self): | def test_drafts_are_published_in_development(self): | ||||
| s = Site(TEST_SITE) | s = Site(TEST_SITE) | ||||
| @@ -66,6 +67,4 @@ class TestDrafts(object): | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| assert s.config.deploy_root_path.child_file( | assert s.config.deploy_root_path.child_file( | ||||
| 'blog/2013/may/draft-post.html').exists | |||||
| 'blog/2013/may/draft-post.html').exists | |||||
| @@ -19,7 +19,7 @@ class TestFlattner(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -42,7 +42,8 @@ class TestFlattner(object): | |||||
| gen.generate_all() | gen.generate_all() | ||||
| assert not s.config.deploy_root_path.child_folder('blog').exists | assert not s.config.deploy_root_path.child_folder('blog').exists | ||||
| assert File(s.config.deploy_root_path.child('merry-christmas.html')).exists | |||||
| assert File( | |||||
| s.config.deploy_root_path.child('merry-christmas.html')).exists | |||||
| def test_flattener_fixes_nodes(self): | def test_flattener_fixes_nodes(self): | ||||
| s = Site(TEST_SITE) | s = Site(TEST_SITE) | ||||
| @@ -64,5 +65,3 @@ class TestFlattner(object): | |||||
| assert blog_node | assert blog_node | ||||
| assert blog_node.url == '/' | assert blog_node.url == '/' | ||||
| @@ -21,7 +21,7 @@ class TestGrouperSingleLevel(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_grouper').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_grouper').copy_contents_to(TEST_SITE) | |||||
| self.s = Site(TEST_SITE) | self.s = Site(TEST_SITE) | ||||
| cfg = """ | cfg = """ | ||||
| @@ -55,7 +55,8 @@ class TestGrouperSingleLevel(object): | |||||
| SorterPlugin(self.s).begin_site() | SorterPlugin(self.s).begin_site() | ||||
| GrouperPlugin(self.s).begin_site() | GrouperPlugin(self.s).begin_site() | ||||
| self.all = ['installation.html', 'overview.html', 'templating.html', 'plugins.html', 'tags.html'] | |||||
| self.all = ['installation.html', 'overview.html', | |||||
| 'templating.html', 'plugins.html', 'tags.html'] | |||||
| self.start = ['installation.html', 'overview.html', 'templating.html'] | self.start = ['installation.html', 'overview.html', 'templating.html'] | ||||
| self.plugins = ['plugins.html', 'tags.html'] | self.plugins = ['plugins.html', 'tags.html'] | ||||
| self.section = self.all | self.section = self.all | ||||
| @@ -72,7 +73,8 @@ class TestGrouperSingleLevel(object): | |||||
| def test_site_grouper_walk_groups(self): | def test_site_grouper_walk_groups(self): | ||||
| groups = dict([(g.name, g) for g in self.s.grouper['section'].walk_groups()]) | |||||
| groups = dict([(g.name, g) | |||||
| for g in self.s.grouper['section'].walk_groups()]) | |||||
| assert len(groups) == 3 | assert len(groups) == 3 | ||||
| assert 'section' in groups | assert 'section' in groups | ||||
| assert 'start' in groups | assert 'start' in groups | ||||
| @@ -81,7 +83,8 @@ class TestGrouperSingleLevel(object): | |||||
| def test_walk_section_groups(self): | def test_walk_section_groups(self): | ||||
| assert hasattr(self.s.content, 'walk_section_groups') | assert hasattr(self.s.content, 'walk_section_groups') | ||||
| groups = dict([(grouper.group.name, grouper) for grouper in self.s.content.walk_section_groups()]) | |||||
| groups = dict([(grouper.group.name, grouper) | |||||
| for grouper in self.s.content.walk_section_groups()]) | |||||
| assert len(groups) == 3 | assert len(groups) == 3 | ||||
| assert 'section' in groups | assert 'section' in groups | ||||
| assert 'start' in groups | assert 'start' in groups | ||||
| @@ -93,15 +96,16 @@ class TestGrouperSingleLevel(object): | |||||
| def test_walk_start_groups(self): | def test_walk_start_groups(self): | ||||
| assert hasattr(self.s.content, 'walk_start_groups') | assert hasattr(self.s.content, 'walk_start_groups') | ||||
| groups = dict([(g.name, g) for g, resources in self.s.content.walk_start_groups()]) | |||||
| groups = dict([(g.name, g) | |||||
| for g, resources in self.s.content.walk_start_groups()]) | |||||
| assert len(groups) == 1 | assert len(groups) == 1 | ||||
| assert 'start' in groups | assert 'start' in groups | ||||
| def test_walk_plugins_groups(self): | def test_walk_plugins_groups(self): | ||||
| assert hasattr(self.s.content, 'walk_plugins_groups') | assert hasattr(self.s.content, 'walk_plugins_groups') | ||||
| groups = dict([(g.name, g) for g, resources in self.s.content.walk_plugins_groups()]) | |||||
| groups = dict([(g.name, g) for g, resources in | |||||
| self.s.content.walk_plugins_groups()]) | |||||
| assert len(groups) == 1 | assert len(groups) == 1 | ||||
| assert 'plugins' in groups | assert 'plugins' in groups | ||||
| @@ -109,22 +113,24 @@ class TestGrouperSingleLevel(object): | |||||
| assert hasattr(self.s.content, 'walk_resources_grouped_by_section') | assert hasattr(self.s.content, 'walk_resources_grouped_by_section') | ||||
| resources = [resource.name for resource in self.s.content.walk_resources_grouped_by_section()] | |||||
| resources = [resource.name for resource in | |||||
| self.s.content.walk_resources_grouped_by_section()] | |||||
| assert resources == self.all | assert resources == self.all | ||||
| def test_walk_start_resources(self): | def test_walk_start_resources(self): | ||||
| assert hasattr(self.s.content, 'walk_resources_grouped_by_start') | assert hasattr(self.s.content, 'walk_resources_grouped_by_start') | ||||
| start_resources = [resource.name for resource in self.s.content.walk_resources_grouped_by_start()] | |||||
| start_resources = [resource.name for resource in | |||||
| self.s.content.walk_resources_grouped_by_start()] | |||||
| assert start_resources == self.start | assert start_resources == self.start | ||||
| def test_walk_plugins_resources(self): | def test_walk_plugins_resources(self): | ||||
| assert hasattr(self.s.content, 'walk_resources_grouped_by_plugins') | assert hasattr(self.s.content, 'walk_resources_grouped_by_plugins') | ||||
| plugin_resources = [resource.name for resource in self.s.content.walk_resources_grouped_by_plugins()] | |||||
| plugin_resources = [resource.name for resource in | |||||
| self.s.content.walk_resources_grouped_by_plugins()] | |||||
| assert plugin_resources == self.plugins | assert plugin_resources == self.plugins | ||||
| def test_resource_group(self): | def test_resource_group(self): | ||||
| @@ -134,7 +140,8 @@ class TestGrouperSingleLevel(object): | |||||
| for name, group in groups.items(): | for name, group in groups.items(): | ||||
| pages = getattr(self, name) | pages = getattr(self, name) | ||||
| for page in pages: | for page in pages: | ||||
| res = self.s.content.resource_from_relative_path('blog/' + page) | |||||
| res = self.s.content.resource_from_relative_path( | |||||
| 'blog/' + page) | |||||
| assert hasattr(res, 'section_group') | assert hasattr(res, 'section_group') | ||||
| res_group = getattr(res, 'section_group') | res_group = getattr(res, 'section_group') | ||||
| assert res_group == group | assert res_group == group | ||||
| @@ -146,7 +153,8 @@ class TestGrouperSingleLevel(object): | |||||
| for name, group in groups.items(): | for name, group in groups.items(): | ||||
| pages = getattr(self, name) | pages = getattr(self, name) | ||||
| for page in pages: | for page in pages: | ||||
| res = self.s.content.resource_from_relative_path('blog/' + page) | |||||
| res = self.s.content.resource_from_relative_path( | |||||
| 'blog/' + page) | |||||
| res_groups = getattr(res, 'walk_%s_groups' % name)() | res_groups = getattr(res, 'walk_%s_groups' % name)() | ||||
| assert group in res_groups | assert group in res_groups | ||||
| @@ -154,7 +162,8 @@ class TestGrouperSingleLevel(object): | |||||
| resources = [] | resources = [] | ||||
| for page in self.all: | for page in self.all: | ||||
| resources.append(self.s.content.resource_from_relative_path('blog/' + page)) | |||||
| resources.append( | |||||
| self.s.content.resource_from_relative_path('blog/' + page)) | |||||
| index = 0 | index = 0 | ||||
| for res in resources: | for res in resources: | ||||
| @@ -174,7 +183,7 @@ class TestGrouperSingleLevel(object): | |||||
| def test_nav_with_grouper(self): | def test_nav_with_grouper(self): | ||||
| text =""" | |||||
| text = """ | |||||
| {% for group, resources in site.content.walk_section_groups() %} | {% for group, resources in site.content.walk_section_groups() %} | ||||
| <ul> | <ul> | ||||
| <li> | <li> | ||||
| @@ -225,7 +234,7 @@ class TestGrouperSingleLevel(object): | |||||
| gen = Generator(self.s) | gen = Generator(self.s) | ||||
| gen.load_site_if_needed() | gen.load_site_if_needed() | ||||
| gen.load_template_if_needed() | gen.load_template_if_needed() | ||||
| out = gen.template.render(text, {'site':self.s}) | |||||
| out = gen.template.render(text, {'site': self.s}) | |||||
| assert_html_equals(out, expected) | assert_html_equals(out, expected) | ||||
| def test_nav_with_grouper_sorted(self): | def test_nav_with_grouper_sorted(self): | ||||
| @@ -264,7 +273,7 @@ class TestGrouperSingleLevel(object): | |||||
| SorterPlugin(self.s).begin_site() | SorterPlugin(self.s).begin_site() | ||||
| GrouperPlugin(self.s).begin_site() | GrouperPlugin(self.s).begin_site() | ||||
| text =""" | |||||
| text = """ | |||||
| {% set sorted = site.grouper['section'].groups|sort(attribute='name') %} | {% set sorted = site.grouper['section'].groups|sort(attribute='name') %} | ||||
| {% for group in sorted %} | {% for group in sorted %} | ||||
| <ul> | <ul> | ||||
| @@ -314,9 +323,10 @@ class TestGrouperSingleLevel(object): | |||||
| """ | """ | ||||
| self.s.config.grouper.section.groups.append(Expando({"name": "awesome", "description": "Aweesoome"})); | |||||
| self.s.config.grouper.section.groups.append( | |||||
| Expando({"name": "awesome", "description": "Aweesoome"})) | |||||
| gen = Generator(self.s) | gen = Generator(self.s) | ||||
| gen.load_site_if_needed() | gen.load_site_if_needed() | ||||
| gen.load_template_if_needed() | gen.load_template_if_needed() | ||||
| out = gen.template.render(text, {'site':self.s}) | |||||
| out = gen.template.render(text, {'site': self.s}) | |||||
| assert_html_equals(out, expected) | assert_html_equals(out, expected) | ||||
| @@ -1,286 +0,0 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | |||||
| Use nose | |||||
| `$ pip install nose` | |||||
| `$ nosetests` | |||||
| Requires PIL | |||||
| """ | |||||
| from hyde.generator import Generator | |||||
| from hyde.site import Site | |||||
| from hyde.ext.plugins.images import thumb_scale_size | |||||
| from fswrap import File | |||||
| import yaml | |||||
| TEST_SITE = File(__file__).parent.parent.child_folder('_test') | |||||
| IMAGE_SOURCE = File(__file__).parent.child_folder('images') | |||||
| PORTRAIT_IMAGE = "portrait.jpg" | |||||
| PORTRAIT_SIZE = (90, 120) | |||||
| LANDSCAPE_IMAGE = "landscape.jpg" | |||||
| LANDSCAPE_SIZE = (120, 90) | |||||
| IMAGES = [PORTRAIT_IMAGE, LANDSCAPE_IMAGE] | |||||
| SIZES = [PORTRAIT_SIZE, LANDSCAPE_SIZE] | |||||
| # PIL requirement | |||||
| try: | |||||
| from PIL import Image | |||||
| except ImportError: | |||||
| # No pillow | |||||
| import Image | |||||
| class TestImageSizer(object): | |||||
| def setUp(self): | |||||
| TEST_SITE.make() | |||||
| TEST_SITE.parent.child_folder( | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| IMAGES = TEST_SITE.child_folder('content/media/img') | |||||
| IMAGES.make() | |||||
| IMAGE_SOURCE.copy_contents_to(IMAGES) | |||||
| self.site = Site(TEST_SITE) | |||||
| def tearDown(self): | |||||
| TEST_SITE.delete() | |||||
| def _generic_test_image(self, text): | |||||
| self.site.config.mode = "production" | |||||
| self.site.config.plugins = ['hyde.ext.plugins.images.ImageSizerPlugin'] | |||||
| tlink = File(self.site.content.source_folder.child('timg.html')) | |||||
| tlink.write(text) | |||||
| gen = Generator(self.site) | |||||
| gen.generate_all() | |||||
| f = File(self.site.config.deploy_root_path.child(tlink.name)) | |||||
| assert f.exists | |||||
| html = f.read_all() | |||||
| assert html | |||||
| return html | |||||
| def test_size_image(self): | |||||
| text = u""" | |||||
| <img src="/media/img/%s"> | |||||
| """ % PORTRAIT_IMAGE | |||||
| html = self._generic_test_image(text) | |||||
| assert ' width="%d"' % PORTRAIT_SIZE[0] in html | |||||
| assert ' height="%d"' % PORTRAIT_SIZE[1] in html | |||||
| def test_size_image_relative(self): | |||||
| text = u""" | |||||
| <img src="media/img/%s"> | |||||
| """ % PORTRAIT_IMAGE | |||||
| html = self._generic_test_image(text) | |||||
| assert ' width="%d"' % PORTRAIT_SIZE[0] in html | |||||
| assert ' height="%d"' % PORTRAIT_SIZE[1] in html | |||||
| def test_size_image_no_resize(self): | |||||
| text = u""" | |||||
| <img src="/media/img/%s" width="2000" height="150"> | |||||
| """ % PORTRAIT_IMAGE | |||||
| html = self._generic_test_image(text) | |||||
| assert ' width="%d"' % PORTRAIT_SIZE[0] not in html | |||||
| assert ' height="%d"' % PORTRAIT_SIZE[1] not in html | |||||
| def test_size_image_size_proportional(self): | |||||
| text = u""" | |||||
| <img src="/media/img/%s" width="%d"> | |||||
| """ % (PORTRAIT_IMAGE, PORTRAIT_SIZE[0]*2) | |||||
| html = self._generic_test_image(text) | |||||
| assert ' width="%d"' % (PORTRAIT_SIZE[0]*2) in html | |||||
| assert ' height="%d"' % (PORTRAIT_SIZE[1]*2) in html | |||||
| def test_size_image_not_exists(self): | |||||
| text = u""" | |||||
| <img src="/media/img/hyde-logo-no.png"> | |||||
| """ | |||||
| self._generic_test_image(text) | |||||
| def test_size_image_multiline(self): | |||||
| text = u""" | |||||
| <img src="/media/img/%s"> | |||||
| """ % PORTRAIT_IMAGE | |||||
| html = self._generic_test_image(text) | |||||
| assert ' width="%d"' % PORTRAIT_SIZE[0] in html | |||||
| assert ' height="%d"' % PORTRAIT_SIZE[1] in html | |||||
| def test_size_multiple_images(self): | |||||
| text = u""" | |||||
| <img src="/media/img/%s"> | |||||
| <img src="/media/img/%s">Hello <img src="/media/img/%s"> | |||||
| <img src="/media/img/%s">Bye | |||||
| """ % ((PORTRAIT_IMAGE,)*4) | |||||
| html = self._generic_test_image(text) | |||||
| assert ' width="%d"' % PORTRAIT_SIZE[0] in html | |||||
| assert ' height="%d"' % PORTRAIT_SIZE[1] in html | |||||
| assert 'Hello ' in html | |||||
| assert 'Bye' in html | |||||
| assert len([f for f in html.split("<img") | |||||
| if ' width=' in f]) == 4 | |||||
| assert len([f for f in html.split("<img") | |||||
| if ' height=' in f]) == 4 | |||||
| def test_size_malformed1(self): | |||||
| text = u""" | |||||
| <img src="/media/img/%s> | |||||
| """ % PORTRAIT_IMAGE | |||||
| html = self._generic_test_image(text) | |||||
| assert ' width="%d"' % PORTRAIT_SIZE[0] in html | |||||
| assert ' height="%d"' % PORTRAIT_SIZE[1] in html | |||||
| def test_size_malformed2(self): | |||||
| text = u""" | |||||
| <img src="/media/img/%s alt="hello"> | |||||
| """ % PORTRAIT_IMAGE | |||||
| html = self._generic_test_image(text) | |||||
| assert ' width="%d"' % PORTRAIT_SIZE[0] in html | |||||
| assert ' height="%d"' % PORTRAIT_SIZE[1] in html | |||||
| def test_outside_media_url(self): | |||||
| self.site.config.media_url = "http://media.example.com/" | |||||
| text = u""" | |||||
| <img src="http://media.example.com/img/%s" alt="hello"> | |||||
| """ % PORTRAIT_IMAGE | |||||
| html = self._generic_test_image(text) | |||||
| assert ' width="%d"' % PORTRAIT_SIZE[0] in html | |||||
| assert ' height="%d"' % PORTRAIT_SIZE[1] in html | |||||
| class TestImageThumbSize(object): | |||||
| def test_width_only(self): | |||||
| ow, oh = 100, 200 | |||||
| nw, nh = thumb_scale_size(ow, oh, 50, None) | |||||
| assert nw == 50 | |||||
| assert nh == 100 | |||||
| def test_width_only_nonintegral(self): | |||||
| ow, oh = 100, 205 | |||||
| nw, nh = thumb_scale_size(ow, oh, 50, None) | |||||
| assert nw == 50 | |||||
| assert nh == 103 | |||||
| def test_height_only(self): | |||||
| ow, oh = 100, 200 | |||||
| nw, nh = thumb_scale_size(ow, oh, None, 100) | |||||
| assert nw == 50 | |||||
| assert nh == 100 | |||||
| def test_height_only_nonintegral(self): | |||||
| ow, oh = 105, 200 | |||||
| nw, nh = thumb_scale_size(ow, oh, None, 100) | |||||
| assert nw == 53 | |||||
| assert nh == 100 | |||||
| def test_height_and_width_portrait(self): | |||||
| ow, oh = 100, 200 | |||||
| nw, nh = thumb_scale_size(ow, oh, 50, 50) | |||||
| assert nw == 50 | |||||
| assert nh == 100 | |||||
| def test_height_and_width_landscape(self): | |||||
| ow, oh = 200, 100 | |||||
| nw, nh = thumb_scale_size(ow, oh, 50, 50) | |||||
| assert nw == 100 | |||||
| assert nh == 50 | |||||
| class TestImageThumbnails(object): | |||||
| # TODO: add tests for cropping? (not easy currently) | |||||
| def setUp(self): | |||||
| TEST_SITE.make() | |||||
| TEST_SITE.parent.child_folder( | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| IMAGES = TEST_SITE.child_folder('content/media/img') | |||||
| IMAGES.make() | |||||
| IMAGE_SOURCE.copy_contents_to(IMAGES) | |||||
| self.image_folder = IMAGES | |||||
| self.site = Site(TEST_SITE) | |||||
| def tearDown(self): | |||||
| TEST_SITE.delete() | |||||
| def _generate_site_with_meta(self, meta): | |||||
| self.site.config.mode = "production" | |||||
| self.site.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin', 'hyde.ext.plugins.images.ImageThumbnailsPlugin'] | |||||
| mlink = File(self.image_folder.child('meta.yaml')) | |||||
| meta_text = yaml.dump(meta, default_flow_style=False) | |||||
| mlink.write(meta_text) | |||||
| gen = Generator(self.site) | |||||
| gen.generate_all() | |||||
| def _test_generic_thumbnails(self, meta): | |||||
| self._generate_site_with_meta(meta) | |||||
| thumb_meta = meta.get('thumbnails', []) | |||||
| for th in thumb_meta: | |||||
| prefix = th.get('prefix') | |||||
| if prefix is None: | |||||
| continue | |||||
| for fn in [PORTRAIT_IMAGE, LANDSCAPE_IMAGE]: | |||||
| f = File(self._deployed_image(prefix, fn)) | |||||
| assert f.exists | |||||
| def _deployed_image(self, prefix, filename): | |||||
| return self.site.config.deploy_root_path.child('media/img/%s%s'%(prefix,filename)) | |||||
| def test_width(self): | |||||
| prefix='thumb_' | |||||
| meta = dict(thumbnails=[dict(width=50, prefix=prefix, include=['*.jpg'])]) | |||||
| self._test_generic_thumbnails(meta) | |||||
| for fn in IMAGES: | |||||
| im = Image.open(self._deployed_image(prefix, fn)) | |||||
| assert im.size[0] == 50 | |||||
| def test_height(self): | |||||
| prefix='thumb_' | |||||
| meta = dict(thumbnails=[dict(height=50, prefix=prefix, include=['*.jpg'])]) | |||||
| self._test_generic_thumbnails(meta) | |||||
| for fn in IMAGES: | |||||
| im = Image.open(self._deployed_image(prefix, fn)) | |||||
| assert im.size[1] == 50 | |||||
| def test_width_and_height(self): | |||||
| prefix='thumb_' | |||||
| meta = dict(thumbnails=[dict(width=50, height=50, prefix=prefix, include=['*.jpg'])]) | |||||
| self._test_generic_thumbnails(meta) | |||||
| for fn in IMAGES: | |||||
| im = Image.open(self._deployed_image(prefix, fn)) | |||||
| assert im.size[0] == 50 | |||||
| assert im.size[1] == 50 | |||||
| def test_larger(self): | |||||
| prefix='thumb_' | |||||
| meta = dict(thumbnails=[dict(larger=50, prefix=prefix, include=['*.jpg'])]) | |||||
| self._test_generic_thumbnails(meta) | |||||
| im = Image.open(self._deployed_image(prefix, PORTRAIT_IMAGE)) | |||||
| assert im.size[1] == 50 | |||||
| im = Image.open(self._deployed_image(prefix, LANDSCAPE_IMAGE)) | |||||
| assert im.size[0] == 50 | |||||
| def test_smaller(self): | |||||
| prefix='thumb_' | |||||
| meta = dict(thumbnails=[dict(smaller=50, prefix=prefix, include=['*.jpg'])]) | |||||
| self._test_generic_thumbnails(meta) | |||||
| im = Image.open(self._deployed_image(prefix, PORTRAIT_IMAGE)) | |||||
| assert im.size[0] == 50 | |||||
| im = Image.open(self._deployed_image(prefix, LANDSCAPE_IMAGE)) | |||||
| assert im.size[1] == 50 | |||||
| def test_larger_and_smaller(self): | |||||
| prefix='thumb_' | |||||
| meta = dict(thumbnails=[dict(larger=100, smaller=50, prefix=prefix, include=['*.jpg'])]) | |||||
| self._test_generic_thumbnails(meta) | |||||
| im = Image.open(self._deployed_image(prefix, PORTRAIT_IMAGE)) | |||||
| assert im.size[0] == 50 | |||||
| assert im.size[1] == 100 | |||||
| im = Image.open(self._deployed_image(prefix, LANDSCAPE_IMAGE)) | |||||
| assert im.size[0] == 100 | |||||
| assert im.size[1] == 50 | |||||
| @@ -18,11 +18,10 @@ class TestLess(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| LESS_SOURCE.copy_contents_to(TEST_SITE.child('content/media/css')) | LESS_SOURCE.copy_contents_to(TEST_SITE.child('content/media/css')) | ||||
| File(TEST_SITE.child('content/media/css/site.css')).delete() | File(TEST_SITE.child('content/media/css/site.css')).delete() | ||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -30,7 +29,8 @@ class TestLess(object): | |||||
| s = Site(TEST_SITE) | s = Site(TEST_SITE) | ||||
| s.config.plugins = ['hyde.ext.plugins.css.LessCSSPlugin'] | s.config.plugins = ['hyde.ext.plugins.css.LessCSSPlugin'] | ||||
| source = TEST_SITE.child('content/media/css/site.less') | source = TEST_SITE.child('content/media/css/site.less') | ||||
| target = File(Folder(s.config.deploy_root_path).child('media/css/site.css')) | |||||
| target = File( | |||||
| Folder(s.config.deploy_root_path).child('media/css/site.css')) | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_resource_at_path(source) | gen.generate_resource_at_path(source) | ||||
| @@ -12,6 +12,7 @@ from pyquery import PyQuery | |||||
| TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
| def assert_valid_conversion(html): | def assert_valid_conversion(html): | ||||
| assert html | assert html | ||||
| q = PyQuery(html) | q = PyQuery(html) | ||||
| @@ -25,18 +26,17 @@ def assert_valid_conversion(html): | |||||
| assert '.' not in q.text() | assert '.' not in q.text() | ||||
| assert '/' not in q.text() | assert '/' not in q.text() | ||||
| class TestMarkings(object): | class TestMarkings(object): | ||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| def test_mark(self): | def test_mark(self): | ||||
| text = u""" | text = u""" | ||||
| === | === | ||||
| @@ -20,7 +20,7 @@ class TestMeta(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -76,8 +76,8 @@ Heading 2 | |||||
| def test_can_load_front_matter(self): | def test_can_load_front_matter(self): | ||||
| d = {'title': 'A nice title', | d = {'title': 'A nice title', | ||||
| 'author': 'Lakshmi Vyas', | |||||
| 'twitter': 'lakshmivyas'} | |||||
| 'author': 'Lakshmi Vyas', | |||||
| 'twitter': 'lakshmivyas'} | |||||
| text = """ | text = """ | ||||
| --- | --- | ||||
| title: %(title)s | title: %(title)s | ||||
| @@ -118,8 +118,8 @@ twitter: %(twitter)s | |||||
| def test_can_load_from_node_meta(self): | def test_can_load_from_node_meta(self): | ||||
| d = {'title': 'A nice title', | d = {'title': 'A nice title', | ||||
| 'author': 'Lakshmi Vyas', | |||||
| 'twitter': 'lakshmivyas'} | |||||
| 'author': 'Lakshmi Vyas', | |||||
| 'twitter': 'lakshmivyas'} | |||||
| text = """ | text = """ | ||||
| === | === | ||||
| title: Even nicer title | title: Even nicer title | ||||
| @@ -161,7 +161,7 @@ title: Even nicer title | |||||
| def test_can_load_from_site_meta(self): | def test_can_load_from_site_meta(self): | ||||
| d = {'title': 'A nice title', | d = {'title': 'A nice title', | ||||
| 'author': 'Lakshmi Vyas'} | |||||
| 'author': 'Lakshmi Vyas'} | |||||
| text = """ | text = """ | ||||
| --- | --- | ||||
| title: Even nicer title | title: Even nicer title | ||||
| @@ -205,15 +205,14 @@ title: Even nicer title | |||||
| assert v in q("span." + k).text() | assert v in q("span." + k).text() | ||||
| assert q("span.title").text() == "Even nicer title" | assert q("span.title").text() == "Even nicer title" | ||||
| def test_multiple_levels(self): | def test_multiple_levels(self): | ||||
| page_d = {'title': 'An even nicer title'} | page_d = {'title': 'An even nicer title'} | ||||
| blog_d = {'author': 'Laks'} | blog_d = {'author': 'Laks'} | ||||
| content_d = {'title': 'A nice title', | |||||
| 'author': 'Lakshmi Vyas'} | |||||
| content_d = {'title': 'A nice title', | |||||
| 'author': 'Lakshmi Vyas'} | |||||
| site_d = {'author': 'Lakshmi', | site_d = {'author': 'Lakshmi', | ||||
| 'twitter': 'lakshmivyas', | 'twitter': 'lakshmivyas', | ||||
| @@ -256,7 +255,8 @@ title: %(title)s | |||||
| for k, v in expected.items(): | for k, v in expected.items(): | ||||
| assert hasattr(res.meta, k) | assert hasattr(res.meta, k) | ||||
| assert getattr(res.meta, k) == v | assert getattr(res.meta, k) == v | ||||
| target = File(Folder(s.config.deploy_root_path).child('blog/about2.html')) | |||||
| target = File( | |||||
| Folder(s.config.deploy_root_path).child('blog/about2.html')) | |||||
| text = target.read_all() | text = target.read_all() | ||||
| q = PyQuery(text) | q = PyQuery(text) | ||||
| for k, v in expected.items(): | for k, v in expected.items(): | ||||
| @@ -19,12 +19,11 @@ class TestOptipng(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| IMAGES = TEST_SITE.child_folder('content/media/images') | IMAGES = TEST_SITE.child_folder('content/media/images') | ||||
| IMAGES.make() | IMAGES.make() | ||||
| OPTIPNG_SOURCE.copy_contents_to(IMAGES) | OPTIPNG_SOURCE.copy_contents_to(IMAGES) | ||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -33,8 +32,10 @@ class TestOptipng(object): | |||||
| s.config.mode = "production" | s.config.mode = "production" | ||||
| s.config.plugins = ['hyde.ext.plugins.images.OptiPNGPlugin'] | s.config.plugins = ['hyde.ext.plugins.images.OptiPNGPlugin'] | ||||
| s.config.optipng = Expando(dict(args=dict(quiet=""))) | s.config.optipng = Expando(dict(args=dict(quiet=""))) | ||||
| source =File(TEST_SITE.child('content/media/images/hyde-lt-b.png')) | |||||
| target = File(Folder(s.config.deploy_root_path).child('media/images/hyde-lt-b.png')) | |||||
| source = File(TEST_SITE.child('content/media/images/hyde-lt-b.png')) | |||||
| target = File( | |||||
| Folder(s.config.deploy_root_path).child( | |||||
| 'media/images/hyde-lt-b.png')) | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_resource_at_path(source) | gen.generate_resource_at_path(source) | ||||
| assert target.exists | assert target.exists | ||||
| @@ -13,12 +13,13 @@ from fswrap import File | |||||
| TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
| class TestPaginator(object): | class TestPaginator(object): | ||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_paginator').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_paginator').copy_contents_to(TEST_SITE) | |||||
| self.s = Site(TEST_SITE) | self.s = Site(TEST_SITE) | ||||
| self.deploy = TEST_SITE.child_folder('deploy') | self.deploy = TEST_SITE.child_folder('deploy') | ||||
| @@ -27,14 +28,12 @@ class TestPaginator(object): | |||||
| self.gen.load_template_if_needed() | self.gen.load_template_if_needed() | ||||
| self.gen.generate_all() | self.gen.generate_all() | ||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| def test_pages_of_one(self): | def test_pages_of_one(self): | ||||
| pages = ['pages_of_one.txt', 'page2/pages_of_one.txt', | pages = ['pages_of_one.txt', 'page2/pages_of_one.txt', | ||||
| 'page3/pages_of_one.txt', 'page4/pages_of_one.txt'] | |||||
| 'page3/pages_of_one.txt', 'page4/pages_of_one.txt'] | |||||
| files = [File(self.deploy.child(p)) for p in pages] | files = [File(self.deploy.child(p)) for p in pages] | ||||
| for f in files: | for f in files: | ||||
| assert f.exists | assert f.exists | ||||
| @@ -42,7 +41,6 @@ class TestPaginator(object): | |||||
| page5 = File(self.deploy.child('page5/pages_of_one.txt')) | page5 = File(self.deploy.child('page5/pages_of_one.txt')) | ||||
| assert not page5.exists | assert not page5.exists | ||||
| def test_pages_of_one_content(self): | def test_pages_of_one_content(self): | ||||
| expected_page1_content = dedent('''\ | expected_page1_content = dedent('''\ | ||||
| Another Sad Post | Another Sad Post | ||||
| @@ -77,7 +75,6 @@ class TestPaginator(object): | |||||
| content = File(page4).read_all() | content = File(page4).read_all() | ||||
| assert expected_page4_content == content | assert expected_page4_content == content | ||||
| def test_pages_of_ten(self): | def test_pages_of_ten(self): | ||||
| page1 = self.deploy.child('pages_of_ten.txt') | page1 = self.deploy.child('pages_of_ten.txt') | ||||
| page2 = self.deploy.child('page2/pages_of_ten.txt') | page2 = self.deploy.child('page2/pages_of_ten.txt') | ||||
| @@ -85,7 +82,6 @@ class TestPaginator(object): | |||||
| assert File(page1).exists | assert File(page1).exists | ||||
| assert not File(page2).exists | assert not File(page2).exists | ||||
| def test_pages_of_ten_depends(self): | def test_pages_of_ten_depends(self): | ||||
| depends = self.gen.deps['pages_of_ten.txt'] | depends = self.gen.deps['pages_of_ten.txt'] | ||||
| @@ -96,7 +92,6 @@ class TestPaginator(object): | |||||
| assert 'blog/angry-post.html' in depends | assert 'blog/angry-post.html' in depends | ||||
| assert 'blog/happy-post.html' in depends | assert 'blog/happy-post.html' in depends | ||||
| def test_pages_of_ten_content(self): | def test_pages_of_ten_content(self): | ||||
| expected_content = dedent('''\ | expected_content = dedent('''\ | ||||
| Another Sad Post | Another Sad Post | ||||
| @@ -109,7 +104,6 @@ class TestPaginator(object): | |||||
| content = File(page).read_all() | content = File(page).read_all() | ||||
| assert expected_content == content | assert expected_content == content | ||||
| def test_pages_of_one_depends(self): | def test_pages_of_one_depends(self): | ||||
| depends = self.gen.deps['pages_of_one.txt'] | depends = self.gen.deps['pages_of_one.txt'] | ||||
| @@ -120,7 +114,6 @@ class TestPaginator(object): | |||||
| assert 'blog/angry-post.html' in depends | assert 'blog/angry-post.html' in depends | ||||
| assert 'blog/happy-post.html' in depends | assert 'blog/happy-post.html' in depends | ||||
| def test_custom_file_pattern(self): | def test_custom_file_pattern(self): | ||||
| page1 = self.deploy.child('custom_file_pattern.txt') | page1 = self.deploy.child('custom_file_pattern.txt') | ||||
| page2 = self.deploy.child('custom_file_pattern-2.txt') | page2 = self.deploy.child('custom_file_pattern-2.txt') | ||||
| @@ -12,10 +12,13 @@ from fswrap import File, Folder | |||||
| RJS_SOURCE = File(__file__).parent.child_folder('requirejs') | RJS_SOURCE = File(__file__).parent.child_folder('requirejs') | ||||
| TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
| class TestRequireJS(object): | class TestRequireJS(object): | ||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder('sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| TEST_SITE.parent.child_folder( | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| RJS_SOURCE.copy_contents_to(TEST_SITE.child('content/media/js')) | RJS_SOURCE.copy_contents_to(TEST_SITE.child('content/media/js')) | ||||
| File(TEST_SITE.child('content/media/js/app.js')).delete() | File(TEST_SITE.child('content/media/js/app.js')).delete() | ||||
| @@ -26,7 +29,8 @@ class TestRequireJS(object): | |||||
| s = Site(TEST_SITE) | s = Site(TEST_SITE) | ||||
| s.config.plugins = ['hyde.ext.plugins.js.RequireJSPlugin'] | s.config.plugins = ['hyde.ext.plugins.js.RequireJSPlugin'] | ||||
| source = TEST_SITE.child('content/media/js/rjs.conf') | source = TEST_SITE.child('content/media/js/rjs.conf') | ||||
| target = File(Folder(s.config.deploy_root_path).child('media/js/app.js')) | |||||
| target = File( | |||||
| Folder(s.config.deploy_root_path).child('media/js/app.js')) | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_resource_at_path(source) | gen.generate_resource_at_path(source) | ||||
| @@ -19,21 +19,20 @@ class TestSassyCSS(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| SCSS_SOURCE.copy_contents_to(TEST_SITE.child('content/media/css')) | SCSS_SOURCE.copy_contents_to(TEST_SITE.child('content/media/css')) | ||||
| File(TEST_SITE.child('content/media/css/site.css')).delete() | File(TEST_SITE.child('content/media/css/site.css')).delete() | ||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| def test_scss(self): | def test_scss(self): | ||||
| s = Site(TEST_SITE) | s = Site(TEST_SITE) | ||||
| s.config.mode = 'prod' | s.config.mode = 'prod' | ||||
| s.config.plugins = ['hyde.ext.plugins.css.SassyCSSPlugin'] | s.config.plugins = ['hyde.ext.plugins.css.SassyCSSPlugin'] | ||||
| source = TEST_SITE.child('content/media/css/site.scss') | source = TEST_SITE.child('content/media/css/site.scss') | ||||
| target = File(Folder(s.config.deploy_root_path).child('media/css/site.css')) | |||||
| target = File( | |||||
| Folder(s.config.deploy_root_path).child('media/css/site.css')) | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_resource_at_path(source) | gen.generate_resource_at_path(source) | ||||
| @@ -41,4 +40,3 @@ class TestSassyCSS(object): | |||||
| text = target.read_all() | text = target.read_all() | ||||
| expected_text = File(SCSS_SOURCE.child('expected-site.css')).read_all() | expected_text = File(SCSS_SOURCE.child('expected-site.css')).read_all() | ||||
| assert_no_diff(expected_text, text) | assert_no_diff(expected_text, text) | ||||
| @@ -1,366 +0,0 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | |||||
| Use nose | |||||
| `$ pip install nose` | |||||
| `$ nosetests` | |||||
| """ | |||||
| from hyde.ext.plugins.meta import MetaPlugin, SorterPlugin | |||||
| from hyde.generator import Generator | |||||
| from hyde.site import Site | |||||
| from hyde.model import Config, Expando | |||||
| from fswrap import File, Folder | |||||
| import yaml | |||||
| TEST_SITE = File(__file__).parent.parent.child_folder('_test') | |||||
| class TestSorter(object): | |||||
| def setUp(self): | |||||
| TEST_SITE.make() | |||||
| TEST_SITE.parent.child_folder( | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | |||||
| TEST_SITE.delete() | |||||
| def test_walk_resources_sorted(self): | |||||
| s = Site(TEST_SITE) | |||||
| s.load() | |||||
| s.config.plugins = ['hyde.ext.meta.SorterPlugin'] | |||||
| s.config.sorter = Expando(dict(kind=dict(attr=['source_file.kind', 'name']))) | |||||
| SorterPlugin(s).begin_site() | |||||
| assert hasattr(s.content, 'walk_resources_sorted_by_kind') | |||||
| expected = ["404.html", | |||||
| "about.html", | |||||
| "apple-touch-icon.png", | |||||
| "merry-christmas.html", | |||||
| "crossdomain.xml", | |||||
| "favicon.ico", | |||||
| "robots.txt", | |||||
| "site.css" | |||||
| ] | |||||
| pages = [page.name for page in | |||||
| s.content.walk_resources_sorted_by_kind()] | |||||
| assert pages == sorted(expected, key=lambda f: (File(f).kind, f)) | |||||
| def test_walk_resources_sorted_reverse(self): | |||||
| s = Site(TEST_SITE) | |||||
| s.load() | |||||
| s.config.plugins = ['hyde.ext.meta.SorterPlugin'] | |||||
| s.config.sorter = Expando(dict(kind=dict(attr=['source_file.kind', 'name'], reverse=True))) | |||||
| SorterPlugin(s).begin_site() | |||||
| assert hasattr(s.content, 'walk_resources_sorted_by_kind') | |||||
| expected = ["404.html", | |||||
| "about.html", | |||||
| "apple-touch-icon.png", | |||||
| "merry-christmas.html", | |||||
| "crossdomain.xml", | |||||
| "favicon.ico", | |||||
| "robots.txt", | |||||
| "site.css" | |||||
| ] | |||||
| pages = [page.name for page in | |||||
| s.content.walk_resources_sorted_by_kind()] | |||||
| assert pages == sorted(expected, key=lambda f: (File(f).kind, f), reverse=True) | |||||
| def test_walk_resources_sorted_with_filters(self): | |||||
| s = Site(TEST_SITE) | |||||
| cfg = """ | |||||
| plugins: | |||||
| - hyde.ext.meta.SorterPlugin | |||||
| sorter: | |||||
| kind2: | |||||
| filters: | |||||
| source_file.kind: html | |||||
| """ | |||||
| s.config = Config(TEST_SITE, config_dict=yaml.load(cfg)) | |||||
| s.load() | |||||
| SorterPlugin(s).begin_site() | |||||
| assert hasattr(s.content, 'walk_resources_sorted_by_kind2') | |||||
| expected = ["404.html", | |||||
| "about.html", | |||||
| "merry-christmas.html" | |||||
| ] | |||||
| pages = [page.name for page in s.content.walk_resources_sorted_by_kind2()] | |||||
| assert pages == sorted(expected) | |||||
| def test_walk_resources_sorted_with_multiple_attributes(self): | |||||
| s = Site(TEST_SITE) | |||||
| cfg = """ | |||||
| plugins: | |||||
| - hyde.ext.meta.SorterPlugin | |||||
| sorter: | |||||
| multi: | |||||
| attr: | |||||
| - source_file.kind | |||||
| - node.name | |||||
| - name | |||||
| """ | |||||
| s.config = Config(TEST_SITE, config_dict=yaml.load(cfg)) | |||||
| s.load() | |||||
| SorterPlugin(s).begin_site() | |||||
| assert hasattr(s.content, 'walk_resources_sorted_by_multi') | |||||
| expected = ["content/404.html", | |||||
| "content/about.html", | |||||
| "content/apple-touch-icon.png", | |||||
| "content/blog/2010/december/merry-christmas.html", | |||||
| "content/crossdomain.xml", | |||||
| "content/favicon.ico", | |||||
| "content/robots.txt", | |||||
| "content/site.css" | |||||
| ] | |||||
| pages = [page.name for page in s.content.walk_resources_sorted_by_multi()] | |||||
| expected_sorted = [File(page).name | |||||
| for page in | |||||
| sorted(expected, | |||||
| key=lambda p: tuple( | |||||
| [File(p).kind, | |||||
| File(p).parent.name, p]))] | |||||
| assert pages == expected_sorted | |||||
| def test_walk_resources_sorted_no_default_is_processable(self): | |||||
| s = Site(TEST_SITE) | |||||
| cfg = """ | |||||
| plugins: | |||||
| - hyde.ext.meta.SorterPlugin | |||||
| sorter: | |||||
| kind2: | |||||
| filters: | |||||
| source_file.kind: html | |||||
| attr: | |||||
| - name | |||||
| """ | |||||
| s.config = Config(TEST_SITE, config_dict=yaml.load(cfg)) | |||||
| s.load() | |||||
| p_404 = s.content.resource_from_relative_path('404.html') | |||||
| p_404.is_processable = False | |||||
| SorterPlugin(s).begin_site() | |||||
| assert hasattr(s.content, 'walk_resources_sorted_by_kind2') | |||||
| expected = ["404.html", "about.html", "merry-christmas.html"] | |||||
| pages = [page.name for page in s.content.walk_resources_sorted_by_kind2()] | |||||
| assert pages == sorted(expected) | |||||
| def test_prev_next(self): | |||||
| s = Site(TEST_SITE) | |||||
| cfg = """ | |||||
| plugins: | |||||
| - hyde.ext.meta.SorterPlugin | |||||
| sorter: | |||||
| kind2: | |||||
| filters: | |||||
| source_file.kind: html | |||||
| attr: | |||||
| - name | |||||
| """ | |||||
| s.config = Config(TEST_SITE, config_dict=yaml.load(cfg)) | |||||
| s.load() | |||||
| SorterPlugin(s).begin_site() | |||||
| p_404 = s.content.resource_from_relative_path('404.html') | |||||
| p_about = s.content.resource_from_relative_path('about.html') | |||||
| p_mc = s.content.resource_from_relative_path( | |||||
| 'blog/2010/december/merry-christmas.html') | |||||
| assert hasattr(p_404, 'prev_by_kind2') | |||||
| assert not p_404.prev_by_kind2 | |||||
| assert hasattr(p_404, 'next_by_kind2') | |||||
| assert p_404.next_by_kind2 == p_about | |||||
| assert hasattr(p_about, 'prev_by_kind2') | |||||
| assert p_about.prev_by_kind2 == p_404 | |||||
| assert hasattr(p_about, 'next_by_kind2') | |||||
| assert p_about.next_by_kind2 == p_mc | |||||
| assert hasattr(p_mc, 'prev_by_kind2') | |||||
| assert p_mc.prev_by_kind2 == p_about | |||||
| assert hasattr(p_mc, 'next_by_kind2') | |||||
| assert not p_mc.next_by_kind2 | |||||
| def test_prev_next_looped(self): | |||||
| s = Site(TEST_SITE) | |||||
| cfg = """ | |||||
| plugins: | |||||
| - hyde.ext.meta.SorterPlugin | |||||
| sorter: | |||||
| kind2: | |||||
| circular: true | |||||
| filters: | |||||
| source_file.kind: html | |||||
| attr: | |||||
| - name | |||||
| """ | |||||
| s.config = Config(TEST_SITE, config_dict=yaml.load(cfg)) | |||||
| s.load() | |||||
| SorterPlugin(s).begin_site() | |||||
| p_404 = s.content.resource_from_relative_path('404.html') | |||||
| p_about = s.content.resource_from_relative_path('about.html') | |||||
| p_mc = s.content.resource_from_relative_path( | |||||
| 'blog/2010/december/merry-christmas.html') | |||||
| assert hasattr(p_404, 'prev_by_kind2') | |||||
| assert p_404.prev_by_kind2 == p_mc | |||||
| assert hasattr(p_404, 'next_by_kind2') | |||||
| assert p_404.next_by_kind2 == p_about | |||||
| assert hasattr(p_about, 'prev_by_kind2') | |||||
| assert p_about.prev_by_kind2 == p_404 | |||||
| assert hasattr(p_about, 'next_by_kind2') | |||||
| assert p_about.next_by_kind2 == p_mc | |||||
| assert hasattr(p_mc, 'prev_by_kind2') | |||||
| assert p_mc.prev_by_kind2 == p_about | |||||
| assert hasattr(p_mc, 'next_by_kind2') | |||||
| assert p_mc.next_by_kind2 == p_404 | |||||
| def test_prev_next_reversed(self): | |||||
| s = Site(TEST_SITE) | |||||
| cfg = """ | |||||
| plugins: | |||||
| - hyde.ext.meta.SorterPlugin | |||||
| sorter: | |||||
| folder_name: | |||||
| attr: | |||||
| - node.name | |||||
| reverse: True | |||||
| filters: | |||||
| source_file.kind: html | |||||
| """ | |||||
| s.config = Config(TEST_SITE, config_dict=yaml.load(cfg)) | |||||
| s.load() | |||||
| SorterPlugin(s).begin_site() | |||||
| p_404 = s.content.resource_from_relative_path('404.html') | |||||
| p_about = s.content.resource_from_relative_path('about.html') | |||||
| p_mc = s.content.resource_from_relative_path( | |||||
| 'blog/2010/december/merry-christmas.html') | |||||
| assert hasattr(p_mc, 'prev_by_folder_name') | |||||
| assert not p_mc.prev_by_folder_name | |||||
| assert hasattr(p_mc, 'next_by_folder_name') | |||||
| assert p_mc.next_by_folder_name == p_404 | |||||
| assert hasattr(p_404, 'prev_by_folder_name') | |||||
| assert p_404.prev_by_folder_name == p_mc | |||||
| assert hasattr(p_404, 'next_by_folder_name') | |||||
| assert p_404.next_by_folder_name == p_about | |||||
| assert hasattr(p_about, 'prev_by_folder_name') | |||||
| assert p_about.prev_by_folder_name == p_404 | |||||
| assert hasattr(p_about, 'next_by_folder_name') | |||||
| assert not p_about.next_by_folder_name | |||||
| def test_walk_resources_sorted_using_generator(self): | |||||
| s = Site(TEST_SITE) | |||||
| cfg = """ | |||||
| meta: | |||||
| time: !!timestamp 2010-10-23 | |||||
| title: NahNahNah | |||||
| plugins: | |||||
| - hyde.ext.plugins.meta.MetaPlugin | |||||
| - hyde.ext.plugins.meta.SorterPlugin | |||||
| sorter: | |||||
| time: | |||||
| attr: meta.time | |||||
| filters: | |||||
| source_file.kind: html | |||||
| """ | |||||
| s.config = Config(TEST_SITE, config_dict=yaml.load(cfg)) | |||||
| text = """ | |||||
| --- | |||||
| time: !!timestamp 2010-12-31 | |||||
| title: YayYayYay | |||||
| --- | |||||
| {% extends "base.html" %} | |||||
| {% block main %} | |||||
| {% set latest = site.content.walk_resources_sorted_by_time()|reverse|first %} | |||||
| <span class="latest">{{ latest.meta.title }}</span> | |||||
| {% endblock %} | |||||
| """ | |||||
| about2 = File(TEST_SITE.child('content/about2.html')) | |||||
| about2.write(text) | |||||
| gen = Generator(s) | |||||
| gen.generate_all() | |||||
| from pyquery import PyQuery | |||||
| target = File(Folder(s.config.deploy_root_path).child('about2.html')) | |||||
| text = target.read_all() | |||||
| q = PyQuery(text) | |||||
| assert q('span.latest').text() == 'YayYayYay' | |||||
| class TestSorterMeta(object): | |||||
| def setUp(self): | |||||
| TEST_SITE.make() | |||||
| TEST_SITE.parent.child_folder( | |||||
| 'sites/test_sorter').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | |||||
| TEST_SITE.delete() | |||||
| def test_attribute_checker_no_meta(self): | |||||
| s = Site(TEST_SITE) | |||||
| s.load() | |||||
| from hyde.ext.plugins.meta import attributes_checker | |||||
| for r in s.content.walk_resources(): | |||||
| assert not attributes_checker(r, ['meta.index']) | |||||
| def test_attribute_checker_with_meta(self): | |||||
| s = Site(TEST_SITE) | |||||
| s.load() | |||||
| MetaPlugin(s).begin_site() | |||||
| from hyde.ext.plugins.meta import attributes_checker | |||||
| have_index = ["angry-post.html", | |||||
| "another-sad-post.html", | |||||
| "happy-post.html"] | |||||
| for r in s.content.walk_resources(): | |||||
| expected = r.name in have_index | |||||
| assert attributes_checker(r, ['meta.index']) == expected | |||||
| def test_walk_resources_sorted_by_index(self): | |||||
| s = Site(TEST_SITE) | |||||
| s.load() | |||||
| config = { | |||||
| "index": { | |||||
| "attr": ['meta.index', 'name'] | |||||
| } | |||||
| } | |||||
| s.config.sorter = Expando(config) | |||||
| MetaPlugin(s).begin_site() | |||||
| SorterPlugin(s).begin_site() | |||||
| assert hasattr(s.content, 'walk_resources_sorted_by_index') | |||||
| expected = ["angry-post.html", | |||||
| "another-sad-post.html", | |||||
| "happy-post.html"] | |||||
| pages = [page.name for page in | |||||
| s.content.walk_resources_sorted_by_index()] | |||||
| assert pages == sorted(expected, key=lambda f: (File(f).kind, f)) | |||||
| @@ -13,16 +13,16 @@ from fswrap import File, Folder | |||||
| STYLUS_SOURCE = File(__file__).parent.child_folder('stylus') | STYLUS_SOURCE = File(__file__).parent.child_folder('stylus') | ||||
| TEST_SITE = File(__file__).parent.parent.child_folder('_test') | TEST_SITE = File(__file__).parent.parent.child_folder('_test') | ||||
| class TestStylus(object): | class TestStylus(object): | ||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| STYLUS_SOURCE.copy_contents_to(TEST_SITE.child('content/media/css')) | STYLUS_SOURCE.copy_contents_to(TEST_SITE.child('content/media/css')) | ||||
| File(TEST_SITE.child('content/media/css/site.css')).delete() | File(TEST_SITE.child('content/media/css/site.css')).delete() | ||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -34,13 +34,15 @@ class TestStylus(object): | |||||
| if File(path).exists: | if File(path).exists: | ||||
| s.config.stylus = Expando(dict(app=path)) | s.config.stylus = Expando(dict(app=path)) | ||||
| source = TEST_SITE.child('content/media/css/site.styl') | source = TEST_SITE.child('content/media/css/site.styl') | ||||
| target = File(Folder(s.config.deploy_root_path).child('media/css/site.css')) | |||||
| target = File( | |||||
| Folder(s.config.deploy_root_path).child('media/css/site.css')) | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_resource_at_path(source) | gen.generate_resource_at_path(source) | ||||
| assert target.exists | assert target.exists | ||||
| text = target.read_all() | text = target.read_all() | ||||
| expected_text = File(STYLUS_SOURCE.child('expected-site.css')).read_all() | |||||
| expected_text = File( | |||||
| STYLUS_SOURCE.child('expected-site.css')).read_all() | |||||
| assert text.strip() == expected_text.strip() | assert text.strip() == expected_text.strip() | ||||
| def test_can_compress_with_stylus(self): | def test_can_compress_with_stylus(self): | ||||
| @@ -52,11 +54,13 @@ class TestStylus(object): | |||||
| if File(path).exists: | if File(path).exists: | ||||
| s.config.stylus = Expando(dict(app=path)) | s.config.stylus = Expando(dict(app=path)) | ||||
| source = TEST_SITE.child('content/media/css/site.styl') | source = TEST_SITE.child('content/media/css/site.styl') | ||||
| target = File(Folder(s.config.deploy_root_path).child('media/css/site.css')) | |||||
| target = File( | |||||
| Folder(s.config.deploy_root_path).child('media/css/site.css')) | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_resource_at_path(source) | gen.generate_resource_at_path(source) | ||||
| assert target.exists | assert target.exists | ||||
| text = target.read_all() | text = target.read_all() | ||||
| expected_text = File(STYLUS_SOURCE.child('expected-site-compressed.css')).read_all() | |||||
| expected_text = File( | |||||
| STYLUS_SOURCE.child('expected-site-compressed.css')).read_all() | |||||
| assert text.strip() == expected_text.strip() | assert text.strip() == expected_text.strip() | ||||
| @@ -18,13 +18,11 @@ class TestSyntext(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| def test_syntext(self): | def test_syntext(self): | ||||
| text = u""" | text = u""" | ||||
| ~~~~~~~~css~~~~~~~ | ~~~~~~~~css~~~~~~~ | ||||
| @@ -1,226 +0,0 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | |||||
| Use nose | |||||
| `$ pip install nose` | |||||
| `$ nosetests` | |||||
| """ | |||||
| from hyde.generator import Generator | |||||
| from hyde.site import Site | |||||
| from fswrap import File | |||||
| TEST_SITE = File(__file__).parent.parent.child_folder('_test') | |||||
| class TestTagger(object): | |||||
| def setUp(self): | |||||
| TEST_SITE.make() | |||||
| TEST_SITE.parent.child_folder( | |||||
| 'sites/test_tagger').copy_contents_to(TEST_SITE) | |||||
| self.s = Site(TEST_SITE) | |||||
| self.deploy = TEST_SITE.child_folder('deploy') | |||||
| def tearDown(self): | |||||
| TEST_SITE.delete() | |||||
| def test_tagger_walker(self): | |||||
| gen = Generator(self.s) | |||||
| gen.load_site_if_needed() | |||||
| gen.generate_all() | |||||
| assert hasattr(self.s, 'tagger') | |||||
| assert hasattr(self.s.tagger, 'tags') | |||||
| assert self.s.tagger.tags | |||||
| tags = self.s.tagger.tags.to_dict() | |||||
| assert len(tags) == 6 | |||||
| for tag in ['sad', 'happy', 'angry', 'thoughts', 'events']: | |||||
| assert tag in tags | |||||
| sad_posts = [post.name for post in tags['sad']['resources']] | |||||
| assert len(sad_posts) == 2 | |||||
| assert "sad-post.html" in sad_posts | |||||
| assert "another-sad-post.html" in sad_posts | |||||
| sad_posts == [post.name for post in | |||||
| self.s.content.walk_resources_tagged_with('sad')] | |||||
| happy_posts = [post.name for post in | |||||
| self.s.content.walk_resources_tagged_with('happy')] | |||||
| assert len(happy_posts) == 1 | |||||
| assert "happy-post.html" in happy_posts | |||||
| angry_posts = [post.name for post in | |||||
| self.s.content.walk_resources_tagged_with('angry')] | |||||
| assert len(angry_posts) == 1 | |||||
| assert "angry-post.html" in angry_posts | |||||
| sad_thought_posts = [post.name for post in | |||||
| self.s.content.walk_resources_tagged_with('sad+thoughts')] | |||||
| assert len(sad_thought_posts) == 1 | |||||
| assert "sad-post.html" in sad_thought_posts | |||||
| def test_tagger_archives_generated(self): | |||||
| gen = Generator(self.s) | |||||
| gen.load_site_if_needed() | |||||
| gen.load_template_if_needed() | |||||
| gen.generate_all() | |||||
| tags_folder = self.deploy.child_folder('blog/tags') | |||||
| assert tags_folder.exists | |||||
| tags = ['sad', 'happy', 'angry', 'thoughts', 'events'] | |||||
| archives = (File(tags_folder.child("%s.html" % tag)) for tag in tags) | |||||
| for archive in archives: | |||||
| assert archive.exists | |||||
| from pyquery import PyQuery | |||||
| q = PyQuery(File(tags_folder.child('sad.html')).read_all()) | |||||
| assert q | |||||
| assert q('li').length == 2 | |||||
| assert q('li:nth-child(1) a').attr('href') == '/blog/another-sad-post.html' | |||||
| assert q('li:nth-child(2) a').attr('href') == '/blog/sad-post.html' | |||||
| q = PyQuery(File(tags_folder.child('happy.html')).read_all()) | |||||
| assert q | |||||
| assert q('li').length == 1 | |||||
| assert q('li a:first-child').attr('href') == '/blog/happy-post.html' | |||||
| q = PyQuery(File(tags_folder.child('angry.html')).read_all()) | |||||
| assert q | |||||
| assert q('li').length == 1 | |||||
| assert q('li a:first-child').attr('href') == '/blog/angry-post.html' | |||||
| q = PyQuery(File(tags_folder.child('thoughts.html')).read_all()) | |||||
| assert q | |||||
| assert q('li').length == 3 | |||||
| assert q('li:nth-child(1) a').attr('href') == '/blog/happy-post.html' | |||||
| assert q('li:nth-child(2) a').attr('href') == '/blog/angry-post.html' | |||||
| assert q('li:nth-child(3) a').attr('href') == '/blog/sad-post.html' | |||||
| q = PyQuery(File(tags_folder.child('events.html')).read_all()) | |||||
| assert q | |||||
| assert q('li').length == 1 | |||||
| assert q('li a:first-child').attr('href') == '/blog/another-sad-post.html' | |||||
| def test_tagger_metadata(self): | |||||
| conf = { | |||||
| "tagger":{ | |||||
| "tags": { | |||||
| "sad" : { | |||||
| "emotions": ["Dissappointed", "Lost"] | |||||
| }, | |||||
| "angry": { | |||||
| "emotions": ["Irritated", "Annoyed", "Disgusted"] | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| s = Site(TEST_SITE) | |||||
| s.config.update(conf) | |||||
| gen = Generator(s) | |||||
| gen.load_site_if_needed() | |||||
| gen.generate_all() | |||||
| assert hasattr(s, 'tagger') | |||||
| assert hasattr(s.tagger, 'tags') | |||||
| assert s.tagger.tags | |||||
| tags = s.tagger.tags | |||||
| sad_tag = tags.sad | |||||
| assert hasattr(sad_tag, "emotions") | |||||
| assert sad_tag.emotions == s.config.tagger.tags.sad.emotions | |||||
| assert hasattr(tags, "angry") | |||||
| angry_tag = tags.angry | |||||
| assert angry_tag | |||||
| assert hasattr(angry_tag, "emotions") | |||||
| assert angry_tag.emotions == s.config.tagger.tags.angry.emotions | |||||
| for tagname in ['happy', 'thoughts', 'events']: | |||||
| tag = getattr(tags, tagname) | |||||
| assert tag | |||||
| assert not hasattr(tag, "emotions") | |||||
| def test_tagger_sorted(self): | |||||
| conf = { | |||||
| "tagger":{ | |||||
| "sorter": "time", | |||||
| "archives": { | |||||
| "blog": { | |||||
| "template": "emotions.j2", | |||||
| "source": "blog", | |||||
| "target": "blog/tags", | |||||
| "extension": "html", | |||||
| "meta": { | |||||
| "author": "Tagger Plugin" | |||||
| } | |||||
| } | |||||
| }, | |||||
| "tags": { | |||||
| "sad" : { | |||||
| "emotions": ["Dissappointed", "Lost"] | |||||
| }, | |||||
| "angry": { | |||||
| "emotions": ["Irritated", "Annoyed", "Disgusted"] | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| text = """ | |||||
| <div id="author">{{ resource.meta.author }}</div> | |||||
| <h1>Posts tagged: {{ tag }} in {{ node.name|title }}</h1> | |||||
| Emotions: | |||||
| <ul> | |||||
| {% for emotion in tag.emotions %} | |||||
| <li class="emotion"> | |||||
| {{ emotion }} | |||||
| </li> | |||||
| {% endfor %} | |||||
| <ul> | |||||
| {% for resource in walker() -%} | |||||
| <li> | |||||
| <a href="{{ content_url(resource.url) }}">{{ resource.meta.title }}</a> | |||||
| </li> | |||||
| {%- endfor %} | |||||
| </ul> | |||||
| """ | |||||
| template = File(TEST_SITE.child('layout/emotions.j2')) | |||||
| template.write(text) | |||||
| s = Site(TEST_SITE) | |||||
| s.config.update(conf) | |||||
| gen = Generator(s) | |||||
| gen.load_site_if_needed() | |||||
| gen.generate_all() | |||||
| tags_folder = self.deploy.child_folder('blog/tags') | |||||
| assert tags_folder.exists | |||||
| tags = ['sad', 'happy', 'angry', 'thoughts', 'events'] | |||||
| archives = dict((tag, File(tags_folder.child("%s.html" % tag))) for tag in tags) | |||||
| for tag, archive in archives.items(): | |||||
| assert archive.exists | |||||
| from pyquery import PyQuery | |||||
| q = PyQuery(archives['sad'].read_all()) | |||||
| assert len(q("li.emotion")) == 2 | |||||
| assert q("#author").text() == "Tagger Plugin" | |||||
| q = PyQuery(archives['angry'].read_all()) | |||||
| assert len(q("li.emotion")) == 3 | |||||
| for tag, archive in archives.items(): | |||||
| if tag not in ["sad", "angry"]: | |||||
| q = PyQuery(archives[tag].read_all()) | |||||
| assert not len(q("li.emotion")) | |||||
| @@ -17,13 +17,11 @@ class TestTextlinks(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| def test_textlinks(self): | def test_textlinks(self): | ||||
| d = { | d = { | ||||
| 'objects': 'template/variables', | 'objects': 'template/variables', | ||||
| @@ -34,8 +32,8 @@ class TestTextlinks(object): | |||||
| {%% markdown %%} | {%% markdown %%} | ||||
| [[!!img/hyde-logo.png]] | [[!!img/hyde-logo.png]] | ||||
| * [Rich object model][hyde objects] and | * [Rich object model][hyde objects] and | ||||
| [overridable hierarchical metadata]([[ %(plugins)s ]]) thats available for use in | |||||
| templates. | |||||
| [overridable hierarchical metadata]([[ %(plugins)s ]]) thats available | |||||
| for use in templates. | |||||
| * Configurable [sorting][], filtering and grouping support. | * Configurable [sorting][], filtering and grouping support. | ||||
| [hyde objects]: [[ %(objects)s ]] | [hyde objects]: [[ %(objects)s ]] | ||||
| @@ -57,5 +55,5 @@ class TestTextlinks(object): | |||||
| assert html | assert html | ||||
| for name, path in d.items(): | for name, path in d.items(): | ||||
| assert site.config.base_url + quote(path) in html | |||||
| assert site.config.base_url + quote(path) in html | |||||
| assert '/media/img/hyde-logo.png' in html | assert '/media/img/hyde-logo.png' in html | ||||
| @@ -20,12 +20,11 @@ class TestUglify(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| JS = TEST_SITE.child_folder('content/media/js') | JS = TEST_SITE.child_folder('content/media/js') | ||||
| JS.make() | JS.make() | ||||
| UGLIFY_SOURCE.copy_contents_to(JS) | UGLIFY_SOURCE.copy_contents_to(JS) | ||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -34,7 +33,8 @@ class TestUglify(object): | |||||
| s.config.plugins = ['hyde.ext.plugins.js.UglifyPlugin'] | s.config.plugins = ['hyde.ext.plugins.js.UglifyPlugin'] | ||||
| s.config.mode = "production" | s.config.mode = "production" | ||||
| source = TEST_SITE.child('content/media/js/jquery.js') | source = TEST_SITE.child('content/media/js/jquery.js') | ||||
| target = File(Folder(s.config.deploy_root_path).child('media/js/jquery.js')) | |||||
| target = File( | |||||
| Folder(s.config.deploy_root_path).child('media/js/jquery.js')) | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_resource_at_path(source) | gen.generate_resource_at_path(source) | ||||
| @@ -48,9 +48,11 @@ class TestUglify(object): | |||||
| s = Site(TEST_SITE) | s = Site(TEST_SITE) | ||||
| s.config.plugins = ['hyde.ext.plugins.js.UglifyPlugin'] | s.config.plugins = ['hyde.ext.plugins.js.UglifyPlugin'] | ||||
| s.config.mode = "production" | s.config.mode = "production" | ||||
| s.config.uglify = Expando(dict(args={"comments":"/http\:\/\/jquery.org\/license/"})) | |||||
| s.config.uglify = Expando( | |||||
| dict(args={"comments": "/http\:\/\/jquery.org\/license/"})) | |||||
| source = TEST_SITE.child('content/media/js/jquery.js') | source = TEST_SITE.child('content/media/js/jquery.js') | ||||
| target = File(Folder(s.config.deploy_root_path).child('media/js/jquery.js')) | |||||
| target = File( | |||||
| Folder(s.config.deploy_root_path).child('media/js/jquery.js')) | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_resource_at_path(source) | gen.generate_resource_at_path(source) | ||||
| @@ -65,7 +67,8 @@ class TestUglify(object): | |||||
| s.config.plugins = ['hyde.ext.plugins.js.UglifyPlugin'] | s.config.plugins = ['hyde.ext.plugins.js.UglifyPlugin'] | ||||
| s.config.mode = "dev" | s.config.mode = "dev" | ||||
| source = TEST_SITE.child('content/media/js/jquery.js') | source = TEST_SITE.child('content/media/js/jquery.js') | ||||
| target = File(Folder(s.config.deploy_root_path).child('media/js/jquery.js')) | |||||
| target = File( | |||||
| Folder(s.config.deploy_root_path).child('media/js/jquery.js')) | |||||
| gen = Generator(s) | gen = Generator(s) | ||||
| gen.generate_resource_at_path(source) | gen.generate_resource_at_path(source) | ||||
| @@ -74,5 +77,3 @@ class TestUglify(object): | |||||
| # TODO: Very fragile. Better comparison needed. | # TODO: Very fragile. Better comparison needed. | ||||
| text = target.read_all() | text = target.read_all() | ||||
| assert_no_diff(expected, text) | assert_no_diff(expected, text) | ||||
| @@ -19,14 +19,14 @@ class TestUrlCleaner(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder( | TEST_SITE.parent.child_folder( | ||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| def test_url_cleaner(self): | def test_url_cleaner(self): | ||||
| s = Site(TEST_SITE) | |||||
| cfg = """ | |||||
| s = Site(TEST_SITE) | |||||
| cfg = """ | |||||
| plugins: | plugins: | ||||
| - hyde.ext.plugins.urls.UrlCleanerPlugin | - hyde.ext.plugins.urls.UrlCleanerPlugin | ||||
| urlcleaner: | urlcleaner: | ||||
| @@ -36,24 +36,26 @@ class TestUrlCleaner(object): | |||||
| - html | - html | ||||
| append_slash: true | append_slash: true | ||||
| """ | """ | ||||
| s.config = Config(TEST_SITE, config_dict=yaml.load(cfg)) | |||||
| text = """ | |||||
| s.config = Config(TEST_SITE, config_dict=yaml.load(cfg)) | |||||
| text = """ | |||||
| {% extends "base.html" %} | {% extends "base.html" %} | ||||
| {% block main %} | {% block main %} | ||||
| <a id="index" href="{{ content_url('about.html') }}"></a> | <a id="index" href="{{ content_url('about.html') }}"></a> | ||||
| <a id="blog" href="{{ content_url('blog/2010/december/merry-christmas.html') }}"></a> | |||||
| <a id="blog" href= | |||||
| "{{ content_url('blog/2010/december/merry-christmas.html') }}"></a> | |||||
| {% endblock %} | {% endblock %} | ||||
| """ | """ | ||||
| about2 = File(TEST_SITE.child('content/test.html')) | |||||
| about2.write(text) | |||||
| gen = Generator(s) | |||||
| gen.generate_all() | |||||
| from pyquery import PyQuery | |||||
| target = File(Folder(s.config.deploy_root_path).child('test.html')) | |||||
| text = target.read_all() | |||||
| q = PyQuery(text) | |||||
| assert q('a#index').attr("href") == '/' | |||||
| assert q('a#blog').attr("href") == '/blog/2010/december/merry-christmas' | |||||
| about2 = File(TEST_SITE.child('content/test.html')) | |||||
| about2.write(text) | |||||
| gen = Generator(s) | |||||
| gen.generate_all() | |||||
| from pyquery import PyQuery | |||||
| target = File(Folder(s.config.deploy_root_path).child('test.html')) | |||||
| text = target.read_all() | |||||
| q = PyQuery(text) | |||||
| assert q('a#index').attr("href") == '/' | |||||
| assert q('a#blog').attr( | |||||
| "href") == '/blog/2010/december/merry-christmas' | |||||
| @@ -2,17 +2,18 @@ from hyde.plugin import Plugin | |||||
| class BannerPlugin(Plugin): | class BannerPlugin(Plugin): | ||||
| """ | |||||
| Adds a comment banner to all generated html files | |||||
| """ | |||||
| def text_resource_complete(self, resource, text): | |||||
| banner = """ | |||||
| """ | |||||
| Adds a comment banner to all generated html files | |||||
| """ | |||||
| def text_resource_complete(self, resource, text): | |||||
| banner = """ | |||||
| <!-- | <!-- | ||||
| This file was produced with infinite love, care & sweat. | This file was produced with infinite love, care & sweat. | ||||
| Please dont copy. If you have to, please drop me a note. | Please dont copy. If you have to, please drop me a note. | ||||
| --> | --> | ||||
| """ | """ | ||||
| if resource.source.kind == "html": | |||||
| text = banner + text | |||||
| return text | |||||
| if resource.source.kind == "html": | |||||
| text = banner + text | |||||
| return text | |||||
| @@ -1,4 +1,4 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # -*- coding: utf-8 -*- | |||||
| """ | """ | ||||
| Use nose | Use nose | ||||
| `$ pip install nose` | `$ pip install nose` | ||||
| @@ -15,11 +15,13 @@ from fswrap import File, Folder | |||||
| TEST_SITE = File(__file__).parent.child_folder('_test') | TEST_SITE = File(__file__).parent.child_folder('_test') | ||||
| class TestGenerator(object): | class TestGenerator(object): | ||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder('sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| TEST_SITE.parent.child_folder( | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -38,7 +40,8 @@ class TestGenerator(object): | |||||
| def test_generate_resource_from_path_with_is_processable_false(self): | def test_generate_resource_from_path_with_is_processable_false(self): | ||||
| site = Site(TEST_SITE) | site = Site(TEST_SITE) | ||||
| site.load() | site.load() | ||||
| resource = site.content.resource_from_path(TEST_SITE.child('content/about.html')) | |||||
| resource = site.content.resource_from_path( | |||||
| TEST_SITE.child('content/about.html')) | |||||
| resource.is_processable = False | resource.is_processable = False | ||||
| gen = Generator(site) | gen = Generator(site) | ||||
| gen.generate_resource_at_path(TEST_SITE.child('content/about.html')) | gen.generate_resource_at_path(TEST_SITE.child('content/about.html')) | ||||
| @@ -48,7 +51,8 @@ class TestGenerator(object): | |||||
| def test_generate_resource_from_path_with_uses_template_false(self): | def test_generate_resource_from_path_with_uses_template_false(self): | ||||
| site = Site(TEST_SITE) | site = Site(TEST_SITE) | ||||
| site.load() | site.load() | ||||
| resource = site.content.resource_from_path(TEST_SITE.child('content/about.html')) | |||||
| resource = site.content.resource_from_path( | |||||
| TEST_SITE.child('content/about.html')) | |||||
| resource.uses_template = False | resource.uses_template = False | ||||
| gen = Generator(site) | gen = Generator(site) | ||||
| gen.generate_resource_at_path(TEST_SITE.child('content/about.html')) | gen.generate_resource_at_path(TEST_SITE.child('content/about.html')) | ||||
| @@ -61,11 +65,13 @@ class TestGenerator(object): | |||||
| def test_generate_resource_from_path_with_deploy_override(self): | def test_generate_resource_from_path_with_deploy_override(self): | ||||
| site = Site(TEST_SITE) | site = Site(TEST_SITE) | ||||
| site.load() | site.load() | ||||
| resource = site.content.resource_from_path(TEST_SITE.child('content/about.html')) | |||||
| resource = site.content.resource_from_path( | |||||
| TEST_SITE.child('content/about.html')) | |||||
| resource.relative_deploy_path = 'about/index.html' | resource.relative_deploy_path = 'about/index.html' | ||||
| gen = Generator(site) | gen = Generator(site) | ||||
| gen.generate_resource_at_path(TEST_SITE.child('content/about.html')) | gen.generate_resource_at_path(TEST_SITE.child('content/about.html')) | ||||
| about = File(Folder(site.config.deploy_root_path).child('about/index.html')) | |||||
| about = File( | |||||
| Folder(site.config.deploy_root_path).child('about/index.html')) | |||||
| assert about.exists | assert about.exists | ||||
| text = about.read_all() | text = about.read_all() | ||||
| q = PyQuery(text) | q = PyQuery(text) | ||||
| @@ -74,7 +80,8 @@ class TestGenerator(object): | |||||
| def test_has_resource_changed(self): | def test_has_resource_changed(self): | ||||
| site = Site(TEST_SITE) | site = Site(TEST_SITE) | ||||
| site.load() | site.load() | ||||
| resource = site.content.resource_from_path(TEST_SITE.child('content/about.html')) | |||||
| resource = site.content.resource_from_path( | |||||
| TEST_SITE.child('content/about.html')) | |||||
| gen = Generator(site) | gen = Generator(site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| import time | import time | ||||
| @@ -110,7 +117,8 @@ class TestGenerator(object): | |||||
| {% endblock %} | {% endblock %} | ||||
| """ | """ | ||||
| site.load() | site.load() | ||||
| resource = site.content.resource_from_path(TEST_SITE.child('content/about.html')) | |||||
| resource = site.content.resource_from_path( | |||||
| TEST_SITE.child('content/about.html')) | |||||
| gen = Generator(site) | gen = Generator(site) | ||||
| resource.source_file.write(text) | resource.source_file.write(text) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| @@ -150,7 +158,8 @@ class TestGenerator(object): | |||||
| """ | """ | ||||
| File(TEST_SITE.child('nav.yaml')).write(nav) | File(TEST_SITE.child('nav.yaml')).write(nav) | ||||
| site.load() | site.load() | ||||
| resource = site.content.resource_from_path(TEST_SITE.child('content/about.html')) | |||||
| resource = site.content.resource_from_path( | |||||
| TEST_SITE.child('content/about.html')) | |||||
| gen = Generator(site) | gen = Generator(site) | ||||
| resource.source_file.write(text) | resource.source_file.write(text) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| @@ -192,7 +201,8 @@ main: | |||||
| """ | """ | ||||
| File(TEST_SITE.child('nav.yaml')).write(nav) | File(TEST_SITE.child('nav.yaml')).write(nav) | ||||
| site.load() | site.load() | ||||
| resource = site.content.resource_from_path(TEST_SITE.child('content/about.html')) | |||||
| resource = site.content.resource_from_path( | |||||
| TEST_SITE.child('content/about.html')) | |||||
| gen = Generator(site) | gen = Generator(site) | ||||
| resource.source_file.write(text) | resource.source_file.write(text) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| @@ -257,4 +267,3 @@ main: | |||||
| left = File(site.config.deploy_root_path.child(f1.name)).read_all() | left = File(site.config.deploy_root_path.child(f1.name)).read_all() | ||||
| right = File(site.config.deploy_root_path.child(f2.name)).read_all() | right = File(site.config.deploy_root_path.child(f2.name)).read_all() | ||||
| assert left == right | assert left == right | ||||
| @@ -16,22 +16,27 @@ from nose.tools import raises, with_setup, nottest | |||||
| TEST_SITE = File(__file__).parent.child_folder('_test') | TEST_SITE = File(__file__).parent.child_folder('_test') | ||||
| TEST_SITE_AT_USER = Folder('~/_test') | TEST_SITE_AT_USER = Folder('~/_test') | ||||
| @nottest | @nottest | ||||
| def create_test_site(): | def create_test_site(): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| @nottest | @nottest | ||||
| def delete_test_site(): | def delete_test_site(): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @nottest | @nottest | ||||
| def create_test_site_at_user(): | def create_test_site_at_user(): | ||||
| TEST_SITE_AT_USER.make() | TEST_SITE_AT_USER.make() | ||||
| @nottest | @nottest | ||||
| def delete_test_site_at_user(): | def delete_test_site_at_user(): | ||||
| TEST_SITE_AT_USER.delete() | TEST_SITE_AT_USER.delete() | ||||
| @raises(HydeException) | @raises(HydeException) | ||||
| @with_setup(create_test_site, delete_test_site) | @with_setup(create_test_site, delete_test_site) | ||||
| def test_ensure_exception_when_site_yaml_exists(): | def test_ensure_exception_when_site_yaml_exists(): | ||||
| @@ -39,6 +44,7 @@ def test_ensure_exception_when_site_yaml_exists(): | |||||
| File(TEST_SITE.child('site.yaml')).write("Hey") | File(TEST_SITE.child('site.yaml')).write("Hey") | ||||
| e.run(e.parse(['-s', unicode(TEST_SITE), 'create'])) | e.run(e.parse(['-s', unicode(TEST_SITE), 'create'])) | ||||
| @raises(HydeException) | @raises(HydeException) | ||||
| @with_setup(create_test_site, delete_test_site) | @with_setup(create_test_site, delete_test_site) | ||||
| def test_ensure_exception_when_content_folder_exists(): | def test_ensure_exception_when_content_folder_exists(): | ||||
| @@ -46,6 +52,7 @@ def test_ensure_exception_when_content_folder_exists(): | |||||
| TEST_SITE.child_folder('content').make() | TEST_SITE.child_folder('content').make() | ||||
| e.run(e.parse(['-s', unicode(TEST_SITE), 'create'])) | e.run(e.parse(['-s', unicode(TEST_SITE), 'create'])) | ||||
| @raises(HydeException) | @raises(HydeException) | ||||
| @with_setup(create_test_site, delete_test_site) | @with_setup(create_test_site, delete_test_site) | ||||
| def test_ensure_exception_when_layout_folder_exists(): | def test_ensure_exception_when_layout_folder_exists(): | ||||
| @@ -53,12 +60,14 @@ def test_ensure_exception_when_layout_folder_exists(): | |||||
| TEST_SITE.child_folder('layout').make() | TEST_SITE.child_folder('layout').make() | ||||
| e.run(e.parse(['-s', unicode(TEST_SITE), 'create'])) | e.run(e.parse(['-s', unicode(TEST_SITE), 'create'])) | ||||
| @with_setup(create_test_site, delete_test_site) | @with_setup(create_test_site, delete_test_site) | ||||
| def test_ensure_no_exception_when_empty_site_exists(): | def test_ensure_no_exception_when_empty_site_exists(): | ||||
| e = Engine(raise_exceptions=True) | e = Engine(raise_exceptions=True) | ||||
| e.run(e.parse(['-s', unicode(TEST_SITE), 'create'])) | e.run(e.parse(['-s', unicode(TEST_SITE), 'create'])) | ||||
| verify_site_contents(TEST_SITE, Layout.find_layout()) | verify_site_contents(TEST_SITE, Layout.find_layout()) | ||||
| @with_setup(create_test_site, delete_test_site) | @with_setup(create_test_site, delete_test_site) | ||||
| def test_ensure_no_exception_when_forced(): | def test_ensure_no_exception_when_forced(): | ||||
| e = Engine(raise_exceptions=True) | e = Engine(raise_exceptions=True) | ||||
| @@ -75,6 +84,7 @@ def test_ensure_no_exception_when_forced(): | |||||
| e.run(e.parse(['-s', unicode(TEST_SITE), 'create', '-f'])) | e.run(e.parse(['-s', unicode(TEST_SITE), 'create', '-f'])) | ||||
| verify_site_contents(TEST_SITE, Layout.find_layout()) | verify_site_contents(TEST_SITE, Layout.find_layout()) | ||||
| @with_setup(create_test_site, delete_test_site) | @with_setup(create_test_site, delete_test_site) | ||||
| def test_ensure_no_exception_when_sitepath_does_not_exist(): | def test_ensure_no_exception_when_sitepath_does_not_exist(): | ||||
| e = Engine(raise_exceptions=True) | e = Engine(raise_exceptions=True) | ||||
| @@ -82,6 +92,7 @@ def test_ensure_no_exception_when_sitepath_does_not_exist(): | |||||
| e.run(e.parse(['-s', unicode(TEST_SITE), 'create', '-f'])) | e.run(e.parse(['-s', unicode(TEST_SITE), 'create', '-f'])) | ||||
| verify_site_contents(TEST_SITE, Layout.find_layout()) | verify_site_contents(TEST_SITE, Layout.find_layout()) | ||||
| @with_setup(create_test_site_at_user, delete_test_site_at_user) | @with_setup(create_test_site_at_user, delete_test_site_at_user) | ||||
| def test_ensure_can_create_site_at_user(): | def test_ensure_can_create_site_at_user(): | ||||
| e = Engine(raise_exceptions=True) | e = Engine(raise_exceptions=True) | ||||
| @@ -89,13 +100,15 @@ def test_ensure_can_create_site_at_user(): | |||||
| e.run(e.parse(['-s', unicode(TEST_SITE_AT_USER), 'create', '-f'])) | e.run(e.parse(['-s', unicode(TEST_SITE_AT_USER), 'create', '-f'])) | ||||
| verify_site_contents(TEST_SITE_AT_USER, Layout.find_layout()) | verify_site_contents(TEST_SITE_AT_USER, Layout.find_layout()) | ||||
| @nottest | @nottest | ||||
| def verify_site_contents(site, layout): | def verify_site_contents(site, layout): | ||||
| assert site.exists | assert site.exists | ||||
| assert site.child_folder('layout').exists | assert site.child_folder('layout').exists | ||||
| assert File(site.child('info.yaml')).exists | assert File(site.child('info.yaml')).exists | ||||
| expected = map(lambda f: f.get_relative_path(layout), layout.walker.walk_all()) | |||||
| expected = map( | |||||
| lambda f: f.get_relative_path(layout), layout.walker.walk_all()) | |||||
| actual = map(lambda f: f.get_relative_path(site), site.walker.walk_all()) | actual = map(lambda f: f.get_relative_path(site), site.walker.walk_all()) | ||||
| assert actual | assert actual | ||||
| assert expected | assert expected | ||||
| @@ -104,9 +117,9 @@ def verify_site_contents(site, layout): | |||||
| actual.sort() | actual.sort() | ||||
| assert actual == expected | assert actual == expected | ||||
| @raises(HydeException) | @raises(HydeException) | ||||
| @with_setup(create_test_site, delete_test_site) | @with_setup(create_test_site, delete_test_site) | ||||
| def test_ensure_exception_when_layout_is_invalid(): | def test_ensure_exception_when_layout_is_invalid(): | ||||
| e = Engine(raise_exceptions=True) | e = Engine(raise_exceptions=True) | ||||
| e.run(e.parse(['-s', unicode(TEST_SITE), 'create', '-l', 'junk'])) | e.run(e.parse(['-s', unicode(TEST_SITE), 'create', '-l', 'junk'])) | ||||
| @@ -24,6 +24,7 @@ import yaml | |||||
| ROOT = File(__file__).parent | ROOT = File(__file__).parent | ||||
| JINJA2 = ROOT.child_folder('templates/jinja2') | JINJA2 = ROOT.child_folder('templates/jinja2') | ||||
| class Article(object): | class Article(object): | ||||
| def __init__(self, id): | def __init__(self, id): | ||||
| @@ -33,12 +34,14 @@ class Article(object): | |||||
| self.user = choice(users) | self.user = choice(users) | ||||
| self.body = generate_lorem_ipsum() | self.body = generate_lorem_ipsum() | ||||
| self.pub_date = datetime.utcfromtimestamp( | self.pub_date = datetime.utcfromtimestamp( | ||||
| randrange(10 ** 9, 2 * 10 ** 9)) | |||||
| randrange(10 ** 9, 2 * 10 ** 9)) | |||||
| self.published = True | self.published = True | ||||
| def dateformat(x): | def dateformat(x): | ||||
| return x.strftime('%Y-%m-%d') | return x.strftime('%Y-%m-%d') | ||||
| class User(object): | class User(object): | ||||
| def __init__(self, username): | def __init__(self, username): | ||||
| @@ -59,6 +62,7 @@ navigation = [ | |||||
| context = dict(users=users, articles=articles, page_navigation=navigation) | context = dict(users=users, articles=articles, page_navigation=navigation) | ||||
| def test_render(): | def test_render(): | ||||
| """ | """ | ||||
| Uses pyquery to test the html structure for validity | Uses pyquery to test the html structure for validity | ||||
| @@ -77,6 +81,7 @@ def test_render(): | |||||
| assert actual("div.article p.meta").length == 20 | assert actual("div.article p.meta").length == 20 | ||||
| assert actual("div.article div.text").length == 20 | assert actual("div.article div.text").length == 20 | ||||
| def test_typogrify(): | def test_typogrify(): | ||||
| source = """ | source = """ | ||||
| {%filter typogrify%} | {%filter typogrify%} | ||||
| @@ -89,6 +94,7 @@ def test_typogrify(): | |||||
| html = t.render(source, {}).strip() | html = t.render(source, {}).strip() | ||||
| assert html == u'One <span class="amp">&</span> two' | assert html == u'One <span class="amp">&</span> two' | ||||
| def test_spaceless(): | def test_spaceless(): | ||||
| source = """ | source = """ | ||||
| {%spaceless%} | {%spaceless%} | ||||
| @@ -124,6 +130,7 @@ def test_spaceless(): | |||||
| """ | """ | ||||
| assert html.strip() == expected.strip() | assert html.strip() == expected.strip() | ||||
| def test_asciidoc(): | def test_asciidoc(): | ||||
| source = """ | source = """ | ||||
| {%asciidoc%} | {%asciidoc%} | ||||
| @@ -146,6 +153,7 @@ def test_asciidoc(): | |||||
| assert q("li:nth-child(2)").text().strip() == "test2" | assert q("li:nth-child(2)").text().strip() == "test2" | ||||
| assert q("li:nth-child(3)").text().strip() == "test3" | assert q("li:nth-child(3)").text().strip() == "test3" | ||||
| def test_markdown(): | def test_markdown(): | ||||
| source = """ | source = """ | ||||
| {%markdown%} | {%markdown%} | ||||
| @@ -157,6 +165,7 @@ def test_markdown(): | |||||
| html = t.render(source, {}).strip() | html = t.render(source, {}).strip() | ||||
| assert html == u'<h3>Heading 3</h3>' | assert html == u'<h3>Heading 3</h3>' | ||||
| def test_restructuredtext(): | def test_restructuredtext(): | ||||
| source = """ | source = """ | ||||
| {% restructuredtext %} | {% restructuredtext %} | ||||
| @@ -171,6 +180,7 @@ Hello | |||||
| <h1 class="title">Hello</h1> | <h1 class="title">Hello</h1> | ||||
| </div>""", html | </div>""", html | ||||
| def test_restructuredtext_with_sourcecode(): | def test_restructuredtext_with_sourcecode(): | ||||
| source = """ | source = """ | ||||
| {% restructuredtext %} | {% restructuredtext %} | ||||
| @@ -188,24 +198,30 @@ See `Example`_ | |||||
| {% endrestructuredtext %} | {% endrestructuredtext %} | ||||
| """ | """ | ||||
| expected = """ | |||||
| expected = (""" | |||||
| <div class="document" id="code"> | <div class="document" id="code"> | ||||
| <h1 class="title">Code</h1> | <h1 class="title">Code</h1> | ||||
| <div class="highlight"><pre><span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span> | |||||
| <span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span> | |||||
| <div class="highlight"><pre><span class="k">def</span> """ | |||||
| """<span class="nf">add</span><span class="p">(</span>""" | |||||
| """<span class="n">a""" | |||||
| """</span><span class="p">,</span> <span class="n">b</span>""" | |||||
| """<span class="p">):</span> | |||||
| <span class="k">return</span> <span class="n">a</span> """ | |||||
| """<span class="o">+</span> <span class="n">b</span> | |||||
| </pre></div> | </pre></div> | ||||
| <p>See <a class="reference external" href="example.html">Example</a></p> | <p>See <a class="reference external" href="example.html">Example</a></p> | ||||
| </div> | </div> | ||||
| """ | |||||
| """) | |||||
| t = Jinja2Template(JINJA2.path) | t = Jinja2Template(JINJA2.path) | ||||
| s = Site(JINJA2.path) | s = Site(JINJA2.path) | ||||
| c = Config(JINJA2.path, config_dict=dict( | c = Config(JINJA2.path, config_dict=dict( | ||||
| restructuredtext=dict(highlight_source=True))) | |||||
| restructuredtext=dict(highlight_source=True))) | |||||
| s.config = c | s.config = c | ||||
| t.configure(s) | t.configure(s) | ||||
| html = t.render(source, {}).strip() | html = t.render(source, {}).strip() | ||||
| assert html.strip() == expected.strip() | assert html.strip() == expected.strip() | ||||
| def test_markdown_with_extensions(): | def test_markdown_with_extensions(): | ||||
| source = """ | source = """ | ||||
| {%markdown%} | {%markdown%} | ||||
| @@ -215,13 +231,15 @@ def test_markdown_with_extensions(): | |||||
| """ | """ | ||||
| t = Jinja2Template(JINJA2.path) | t = Jinja2Template(JINJA2.path) | ||||
| s = Site(JINJA2.path) | s = Site(JINJA2.path) | ||||
| c = Config(JINJA2.path, config_dict=dict(markdown=dict(extensions=['headerid']))) | |||||
| c = Config(JINJA2.path, config_dict=dict( | |||||
| markdown=dict(extensions=['headerid']))) | |||||
| s.config = c | s.config = c | ||||
| t.configure(s) | t.configure(s) | ||||
| t.env.filters['dateformat'] = dateformat | t.env.filters['dateformat'] = dateformat | ||||
| html = t.render(source, {}).strip() | html = t.render(source, {}).strip() | ||||
| assert html == u'<h3 id="heading-3">Heading 3</h3>' | assert html == u'<h3 id="heading-3">Heading 3</h3>' | ||||
| def test_markdown_with_sourcecode(): | def test_markdown_with_sourcecode(): | ||||
| source = """ | source = """ | ||||
| {%markdown%} | {%markdown%} | ||||
| @@ -237,19 +255,23 @@ See [Example][] | |||||
| {%endmarkdown%} | {%endmarkdown%} | ||||
| """ | """ | ||||
| expected = """ | |||||
| expected = (""" | |||||
| <h1>Code</h1> | <h1>Code</h1> | ||||
| <div class="codehilite"><pre><span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span> | |||||
| <span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span> | |||||
| <div class="codehilite"><pre><span class="k">def</span> """ | |||||
| """<span class="nf">add</span><span class="p">(</span>""" | |||||
| """<span class="n">a</span><span class="p">,</span> """ | |||||
| """<span class="n">b</span><span class="p">):</span> | |||||
| <span class="k">return</span> <span class="n">a</span> <span class="o">+""" | |||||
| """</span> <span class="n">b</span> | |||||
| </pre></div> | </pre></div> | ||||
| <p>See <a href="example.html">Example</a></p> | <p>See <a href="example.html">Example</a></p> | ||||
| """ | |||||
| """) | |||||
| t = Jinja2Template(JINJA2.path) | t = Jinja2Template(JINJA2.path) | ||||
| s = Site(JINJA2.path) | s = Site(JINJA2.path) | ||||
| c = Config(JINJA2.path, config_dict=dict( | c = Config(JINJA2.path, config_dict=dict( | ||||
| markdown=dict(extensions=['codehilite']))) | |||||
| markdown=dict(extensions=['codehilite']))) | |||||
| s.config = c | s.config = c | ||||
| t.configure(s) | t.configure(s) | ||||
| html = t.render(source, {}).strip() | html = t.render(source, {}).strip() | ||||
| @@ -265,13 +287,15 @@ def test_line_statements(): | |||||
| """ | """ | ||||
| t = Jinja2Template(JINJA2.path) | t = Jinja2Template(JINJA2.path) | ||||
| s = Site(JINJA2.path) | s = Site(JINJA2.path) | ||||
| c = Config(JINJA2.path, config_dict=dict(markdown=dict(extensions=['headerid']))) | |||||
| c = Config(JINJA2.path, config_dict=dict( | |||||
| markdown=dict(extensions=['headerid']))) | |||||
| s.config = c | s.config = c | ||||
| t.configure(s) | t.configure(s) | ||||
| t.env.filters['dateformat'] = dateformat | t.env.filters['dateformat'] = dateformat | ||||
| html = t.render(source, {}).strip() | html = t.render(source, {}).strip() | ||||
| assert html == u'<h3 id="heading-3">Heading 3</h3>' | assert html == u'<h3 id="heading-3">Heading 3</h3>' | ||||
| def test_line_statements_with_config(): | def test_line_statements_with_config(): | ||||
| source = """ | source = """ | ||||
| %% markdown | %% markdown | ||||
| @@ -298,6 +322,7 @@ def test_line_statements_with_config(): | |||||
| TEST_SITE = File(__file__).parent.child_folder('_test') | TEST_SITE = File(__file__).parent.child_folder('_test') | ||||
| @nottest | @nottest | ||||
| def assert_markdown_typogrify_processed_well(include_text, includer_text): | def assert_markdown_typogrify_processed_well(include_text, includer_text): | ||||
| site = Site(TEST_SITE) | site = Site(TEST_SITE) | ||||
| @@ -317,11 +342,13 @@ def assert_markdown_typogrify_processed_well(include_text, includer_text): | |||||
| assert q(".amp").length == 1 | assert q(".amp").length == 1 | ||||
| return html | return html | ||||
| class TestJinjaTemplate(object): | class TestJinjaTemplate(object): | ||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder('sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| TEST_SITE.parent.child_folder( | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -375,7 +402,6 @@ class TestJinjaTemplate(object): | |||||
| assert article.length == 1 | assert article.length == 1 | ||||
| assert article.text() == "Heya" | assert article.text() == "Heya" | ||||
| def test_depends_with_reference_tag(self): | def test_depends_with_reference_tag(self): | ||||
| site = Site(TEST_SITE) | site = Site(TEST_SITE) | ||||
| JINJA2.copy_contents_to(site.content.source) | JINJA2.copy_contents_to(site.content.source) | ||||
| @@ -408,7 +434,6 @@ class TestJinjaTemplate(object): | |||||
| assert not deps[0] | assert not deps[0] | ||||
| def test_can_include_templates_with_processing(self): | def test_can_include_templates_with_processing(self): | ||||
| text = """ | text = """ | ||||
| === | === | ||||
| @@ -424,11 +449,9 @@ Hyde & Jinja. | |||||
| {% endmarkdown %}{% endfilter %} | {% endmarkdown %}{% endfilter %} | ||||
| """ | """ | ||||
| text2 = """{% include "inc.md" %}""" | text2 = """{% include "inc.md" %}""" | ||||
| assert_markdown_typogrify_processed_well(text, text2) | assert_markdown_typogrify_processed_well(text, text2) | ||||
| def test_includetext(self): | def test_includetext(self): | ||||
| text = """ | text = """ | ||||
| === | === | ||||
| @@ -595,7 +618,6 @@ Hyde & Jinja. | |||||
| assert "mark" not in html | assert "mark" not in html | ||||
| assert "reference" not in html | assert "reference" not in html | ||||
| def test_refer_with_var(self): | def test_refer_with_var(self): | ||||
| text = """ | text = """ | ||||
| === | === | ||||
| @@ -623,7 +645,6 @@ Hyde & Jinja. | |||||
| assert "mark" not in html | assert "mark" not in html | ||||
| assert "reference" not in html | assert "reference" not in html | ||||
| def test_yaml_tag(self): | def test_yaml_tag(self): | ||||
| text = """ | text = """ | ||||
| @@ -728,20 +749,22 @@ item_list: | |||||
| </ul> | </ul> | ||||
| </aside> | </aside> | ||||
| """ | """ | ||||
| text = "{%% filter markdown|typogrify %%}{%% raw %%}%s{%% endraw %%}{%% endfilter %%}" % expected | |||||
| text = """{%% filter markdown|typogrify %%}{%% raw | |||||
| %%}%s{%% endraw %%}{%% endfilter %%}""" % expected | |||||
| t = Jinja2Template(JINJA2.path) | t = Jinja2Template(JINJA2.path) | ||||
| t.configure(None) | t.configure(None) | ||||
| html = t.render(text, {}).strip() | html = t.render(text, {}).strip() | ||||
| assert html.strip() == expected.strip() | assert html.strip() == expected.strip() | ||||
| def test_urlencode_filter(self): | def test_urlencode_filter(self): | ||||
| text= u""" | |||||
| <a href="{{ 'фотография.jpg'|urlencode }}">фотография</a> | |||||
| <a href="{{ 'http://localhost:8080/"abc.jpg'|urlencode }}">quoted</a> | |||||
| text = u""" | |||||
| <a href="{{ 'фотография.jpg'|urlencode }}" | |||||
| >фотография</a><a href="{{ 'http://localhost:8080/"abc.jpg'|urlencode | |||||
| }}">quoted</a> | |||||
| """ | """ | ||||
| expected = u""" | expected = u""" | ||||
| <a href="%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%8F.jpg">фотография</a> | |||||
| <a href="http%3A//localhost%3A8080/%22abc.jpg">quoted</a> | |||||
| <a href="%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%8F.jpg" | |||||
| >фотография</a><a href="http%3A//localhost%3A8080/%22abc.jpg">quoted</a> | |||||
| """ | """ | ||||
| t = Jinja2Template(JINJA2.path) | t = Jinja2Template(JINJA2.path) | ||||
| t.configure(None) | t.configure(None) | ||||
| @@ -749,13 +772,14 @@ item_list: | |||||
| assert html.strip() == expected.strip() | assert html.strip() == expected.strip() | ||||
| def test_urldecode_filter(self): | def test_urldecode_filter(self): | ||||
| text= u""" | |||||
| <a href="{{ 'фотография.jpg'|urlencode }}">{{ "%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%8F.jpg"|urldecode }}</a> | |||||
| """ | |||||
| expected = u""" | |||||
| <a href="%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%8F.jpg">фотография.jpg</a> | |||||
| text = u""" | |||||
| <a href="{{ 'фотография.jpg'|urlencode }}">{{ | |||||
| "%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%8F.jpg"|urldecode | |||||
| }}</a> | |||||
| """ | """ | ||||
| expected = (u'<a href="%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1' | |||||
| u'%80%D0%B0%D1%84%D0%B8%D1%8F.jpg">фотография.jpg</a>') | |||||
| t = Jinja2Template(JINJA2.path) | t = Jinja2Template(JINJA2.path) | ||||
| t.configure(None) | t.configure(None) | ||||
| html = t.render(text, {}).strip() | html = t.render(text, {}).strip() | ||||
| assert html.strip() == expected.strip() | |||||
| assert html.strip() == expected.strip() | |||||
| @@ -14,19 +14,23 @@ from nose.tools import nottest, with_setup | |||||
| DATA_ROOT = File(__file__).parent.child_folder('data') | DATA_ROOT = File(__file__).parent.child_folder('data') | ||||
| LAYOUT_ROOT = DATA_ROOT.child_folder(LAYOUTS) | LAYOUT_ROOT = DATA_ROOT.child_folder(LAYOUTS) | ||||
| @nottest | @nottest | ||||
| def setup_data(): | def setup_data(): | ||||
| DATA_ROOT.make() | DATA_ROOT.make() | ||||
| @nottest | @nottest | ||||
| def cleanup_data(): | def cleanup_data(): | ||||
| DATA_ROOT.delete() | DATA_ROOT.delete() | ||||
| def test_find_layout_from_package_dir(): | def test_find_layout_from_package_dir(): | ||||
| f = Layout.find_layout() | f = Layout.find_layout() | ||||
| assert f.name == 'basic' | assert f.name == 'basic' | ||||
| assert f.child_folder('layout').exists | assert f.child_folder('layout').exists | ||||
| @with_setup(setup_data, cleanup_data) | @with_setup(setup_data, cleanup_data) | ||||
| def test_find_layout_from_env_var(): | def test_find_layout_from_env_var(): | ||||
| f = Layout.find_layout() | f = Layout.find_layout() | ||||
| @@ -8,18 +8,21 @@ from hyde.model import Config, Expando | |||||
| from fswrap import File, Folder | from fswrap import File, Folder | ||||
| def test_expando_one_level(): | def test_expando_one_level(): | ||||
| d = {"a": 123, "b": "abc"} | d = {"a": 123, "b": "abc"} | ||||
| x = Expando(d) | x = Expando(d) | ||||
| assert x.a == d['a'] | assert x.a == d['a'] | ||||
| assert x.b == d['b'] | assert x.b == d['b'] | ||||
| def test_expando_two_levels(): | def test_expando_two_levels(): | ||||
| d = {"a": 123, "b": {"c": 456}} | d = {"a": 123, "b": {"c": 456}} | ||||
| x = Expando(d) | x = Expando(d) | ||||
| assert x.a == d['a'] | assert x.a == d['a'] | ||||
| assert x.b.c == d['b']['c'] | assert x.b.c == d['b']['c'] | ||||
| def test_expando_three_levels(): | def test_expando_three_levels(): | ||||
| d = {"a": 123, "b": {"c": 456, "d": {"e": "abc"}}} | d = {"a": 123, "b": {"c": 456, "d": {"e": "abc"}}} | ||||
| x = Expando(d) | x = Expando(d) | ||||
| @@ -27,6 +30,7 @@ def test_expando_three_levels(): | |||||
| assert x.b.c == d['b']['c'] | assert x.b.c == d['b']['c'] | ||||
| assert x.b.d.e == d['b']['d']['e'] | assert x.b.d.e == d['b']['d']['e'] | ||||
| def test_expando_update(): | def test_expando_update(): | ||||
| d1 = {"a": 123, "b": "abc"} | d1 = {"a": 123, "b": "abc"} | ||||
| x = Expando(d1) | x = Expando(d1) | ||||
| @@ -34,7 +38,7 @@ def test_expando_update(): | |||||
| assert x.b == d1['b'] | assert x.b == d1['b'] | ||||
| d = {"b": {"c": 456, "d": {"e": "abc"}}, "f": "lmn"} | d = {"b": {"c": 456, "d": {"e": "abc"}}, "f": "lmn"} | ||||
| x.update(d) | x.update(d) | ||||
| assert x.a == d1['a'] | |||||
| assert x.a == d1['a'] | |||||
| assert x.b.c == d['b']['c'] | assert x.b.c == d['b']['c'] | ||||
| assert x.b.d.e == d['b']['d']['e'] | assert x.b.d.e == d['b']['d']['e'] | ||||
| assert x.f == d["f"] | assert x.f == d["f"] | ||||
| @@ -44,11 +48,13 @@ def test_expando_update(): | |||||
| assert x.a == 789 | assert x.a == 789 | ||||
| assert x.f == "opq" | assert x.f == "opq" | ||||
| def test_expando_to_dict(): | def test_expando_to_dict(): | ||||
| d = {"a": 123, "b": {"c": 456, "d": {"e": "abc"}}} | d = {"a": 123, "b": {"c": 456, "d": {"e": "abc"}}} | ||||
| x = Expando(d) | x = Expando(d) | ||||
| assert d == x.to_dict() | assert d == x.to_dict() | ||||
| def test_expando_to_dict_with_update(): | def test_expando_to_dict_with_update(): | ||||
| d1 = {"a": 123, "b": "abc"} | d1 = {"a": 123, "b": "abc"} | ||||
| x = Expando(d1) | x = Expando(d1) | ||||
| @@ -67,6 +73,8 @@ def test_expando_to_dict_with_update(): | |||||
| TEST_SITE = File(__file__).parent.child_folder('_test') | TEST_SITE = File(__file__).parent.child_folder('_test') | ||||
| import yaml | import yaml | ||||
| class TestConfig(object): | class TestConfig(object): | ||||
| @classmethod | @classmethod | ||||
| @@ -94,7 +102,8 @@ class TestConfig(object): | |||||
| def setUp(self): | def setUp(self): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder('sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| TEST_SITE.parent.child_folder( | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| def tearDown(self): | def tearDown(self): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| @@ -16,15 +16,18 @@ from fswrap import File, Folder | |||||
| TEST_SITE = File(__file__).parent.child_folder('_test') | TEST_SITE = File(__file__).parent.child_folder('_test') | ||||
| class PluginLoaderStub(Plugin): | class PluginLoaderStub(Plugin): | ||||
| pass | pass | ||||
| class NoReturnPlugin(Plugin): | class NoReturnPlugin(Plugin): | ||||
| def begin_text_resource(self, resource, text): | def begin_text_resource(self, resource, text): | ||||
| print "NoReturnPlugin" | print "NoReturnPlugin" | ||||
| return None | return None | ||||
| class ConstantReturnPlugin(Plugin): | class ConstantReturnPlugin(Plugin): | ||||
| def begin_text_resource(self, resource, text): | def begin_text_resource(self, resource, text): | ||||
| @@ -37,7 +40,8 @@ class TestPlugins(object): | |||||
| @classmethod | @classmethod | ||||
| def setup_class(cls): | def setup_class(cls): | ||||
| TEST_SITE.make() | TEST_SITE.make() | ||||
| TEST_SITE.parent.child_folder('sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| TEST_SITE.parent.child_folder( | |||||
| 'sites/test_jinja').copy_contents_to(TEST_SITE) | |||||
| folders = [] | folders = [] | ||||
| text_files = [] | text_files = [] | ||||
| binary_files = [] | binary_files = [] | ||||
| @@ -58,14 +62,13 @@ class TestPlugins(object): | |||||
| cls.content_text_resources = sorted(text_files) | cls.content_text_resources = sorted(text_files) | ||||
| cls.content_binary_resources = sorted(binary_files) | cls.content_binary_resources = sorted(binary_files) | ||||
| @classmethod | @classmethod | ||||
| def teardown_class(cls): | def teardown_class(cls): | ||||
| TEST_SITE.delete() | TEST_SITE.delete() | ||||
| def setUp(self): | def setUp(self): | ||||
| self.site = Site(TEST_SITE) | |||||
| self.site.config.plugins = ['hyde.tests.test_plugin.PluginLoaderStub'] | |||||
| self.site = Site(TEST_SITE) | |||||
| self.site.config.plugins = ['hyde.tests.test_plugin.PluginLoaderStub'] | |||||
| def test_can_load_plugin_modules(self): | def test_can_load_plugin_modules(self): | ||||
| assert not len(self.site.plugins) | assert not len(self.site.plugins) | ||||
| @@ -74,25 +77,27 @@ class TestPlugins(object): | |||||
| assert len(self.site.plugins) == 1 | assert len(self.site.plugins) == 1 | ||||
| assert self.site.plugins[0].__class__.__name__ == 'PluginLoaderStub' | assert self.site.plugins[0].__class__.__name__ == 'PluginLoaderStub' | ||||
| def test_generator_loads_plugins(self): | def test_generator_loads_plugins(self): | ||||
| gen = Generator(self.site) | |||||
| Generator(self.site) | |||||
| assert len(self.site.plugins) == 1 | assert len(self.site.plugins) == 1 | ||||
| def test_generator_template_registered_called(self): | def test_generator_template_registered_called(self): | ||||
| with patch.object(PluginLoaderStub, 'template_loaded') as template_loaded_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'template_loaded') as template_loaded_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| assert template_loaded_stub.call_count == 1 | assert template_loaded_stub.call_count == 1 | ||||
| def test_generator_template_begin_generation_called(self): | def test_generator_template_begin_generation_called(self): | ||||
| with patch.object(PluginLoaderStub, 'begin_generation') as begin_generation_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'begin_generation') as begin_generation_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| assert begin_generation_stub.call_count == 1 | assert begin_generation_stub.call_count == 1 | ||||
| def test_generator_template_begin_generation_called_for_single_resource(self): | |||||
| with patch.object(PluginLoaderStub, 'begin_generation') as begin_generation_stub: | |||||
| def test_generator_template_begin_generation_called_for_single_res(self): | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'begin_generation') as begin_generation_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| path = self.site.content.source_folder.child('about.html') | path = self.site.content.source_folder.child('about.html') | ||||
| gen.generate_resource_at_path(path) | gen.generate_resource_at_path(path) | ||||
| @@ -100,29 +105,32 @@ class TestPlugins(object): | |||||
| assert begin_generation_stub.call_count == 1 | assert begin_generation_stub.call_count == 1 | ||||
| def test_generator_template_begin_generation_called_for_single_node(self): | def test_generator_template_begin_generation_called_for_single_node(self): | ||||
| with patch.object(PluginLoaderStub, 'begin_generation') as begin_generation_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'begin_generation') as begin_generation_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| path = self.site.content.source_folder | path = self.site.content.source_folder | ||||
| gen.generate_node_at_path(path) | gen.generate_node_at_path(path) | ||||
| assert begin_generation_stub.call_count == 1 | assert begin_generation_stub.call_count == 1 | ||||
| def test_generator_template_generation_complete_called(self): | def test_generator_template_generation_complete_called(self): | ||||
| with patch.object(PluginLoaderStub, 'generation_complete') as generation_complete_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'generation_complete') as generation_complete_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| assert generation_complete_stub.call_count == 1 | assert generation_complete_stub.call_count == 1 | ||||
| def test_generator_template_generation_complete_called_for_single_resource(self): | |||||
| with patch.object(PluginLoaderStub, 'generation_complete') as generation_complete_stub: | |||||
| def test_generator_template_generation_complete_called_for_single_rs(self): | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'generation_complete') as generation_complete_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| path = self.site.content.source_folder.child('about.html') | path = self.site.content.source_folder.child('about.html') | ||||
| gen.generate_resource_at_path(path) | gen.generate_resource_at_path(path) | ||||
| assert generation_complete_stub.call_count == 1 | assert generation_complete_stub.call_count == 1 | ||||
| def test_generator_template_generation_complete_called_for_single_node(self): | |||||
| with patch.object(PluginLoaderStub, 'generation_complete') as generation_complete_stub: | |||||
| def test_generator_template_generation_complete_called_for_single_nd(self): | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'generation_complete') as generation_complete_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| path = self.site.content.source_folder | path = self.site.content.source_folder | ||||
| gen.generate_node_at_path(path) | gen.generate_node_at_path(path) | ||||
| @@ -141,7 +149,7 @@ class TestPlugins(object): | |||||
| gen.generate_resource_at_path(path) | gen.generate_resource_at_path(path) | ||||
| assert begin_site_stub.call_count == 1 | assert begin_site_stub.call_count == 1 | ||||
| def test_generator_template_begin_site_not_called_for_single_resource_second_time(self): | |||||
| def test_generator_template_begin_site_not_called_sngle_res_scnd_tm(self): | |||||
| with patch.object(PluginLoaderStub, 'begin_site') as begin_site_stub: | with patch.object(PluginLoaderStub, 'begin_site') as begin_site_stub: | ||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| @@ -158,7 +166,7 @@ class TestPlugins(object): | |||||
| assert begin_site_stub.call_count == 1 | assert begin_site_stub.call_count == 1 | ||||
| def test_generator_template_begin_site_not_called_for_single_node_second_time(self): | |||||
| def test_generator_template_begin_site_not_call_for_sngl_nd_scnd_tm(self): | |||||
| with patch.object(PluginLoaderStub, 'begin_site') as begin_site_stub: | with patch.object(PluginLoaderStub, 'begin_site') as begin_site_stub: | ||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| @@ -169,24 +177,26 @@ class TestPlugins(object): | |||||
| assert begin_site_stub.call_count == 1 | assert begin_site_stub.call_count == 1 | ||||
| def test_generator_template_site_complete_called(self): | def test_generator_template_site_complete_called(self): | ||||
| with patch.object(PluginLoaderStub, 'site_complete') as site_complete_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'site_complete') as site_complete_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| assert site_complete_stub.call_count == 1 | assert site_complete_stub.call_count == 1 | ||||
| def test_generator_template_site_complete_called_for_single_resource(self): | def test_generator_template_site_complete_called_for_single_resource(self): | ||||
| with patch.object(PluginLoaderStub, 'site_complete') as site_complete_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'site_complete') as site_complete_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| path = self.site.content.source_folder.child('about.html') | path = self.site.content.source_folder.child('about.html') | ||||
| gen.generate_resource_at_path(path) | gen.generate_resource_at_path(path) | ||||
| assert site_complete_stub.call_count == 1 | assert site_complete_stub.call_count == 1 | ||||
| def test_generator_template_site_complete_not_called_for_single_resource_second_time(self): | |||||
| def test_generator_template_site_complt_not_call_4_sngl_res_scnd_tm(self): | |||||
| with patch.object(PluginLoaderStub, 'site_complete') as site_complete_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'site_complete') as site_complete_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| assert site_complete_stub.call_count == 1 | assert site_complete_stub.call_count == 1 | ||||
| @@ -197,16 +207,18 @@ class TestPlugins(object): | |||||
| def test_generator_template_site_complete_called_for_single_node(self): | def test_generator_template_site_complete_called_for_single_node(self): | ||||
| with patch.object(PluginLoaderStub, 'site_complete') as site_complete_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'site_complete') as site_complete_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| path = self.site.content.source_folder | path = self.site.content.source_folder | ||||
| gen.generate_node_at_path(path) | gen.generate_node_at_path(path) | ||||
| assert site_complete_stub.call_count == 1 | assert site_complete_stub.call_count == 1 | ||||
| def test_generator_template_site_complete_not_called_for_single_node_second_time(self): | |||||
| def test_generator_template_site_complete_not_call_4_sngl_nd_scnd_tm(self): | |||||
| with patch.object(PluginLoaderStub, 'site_complete') as site_complete_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'site_complete') as site_complete_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| path = self.site.content.source_folder | path = self.site.content.source_folder | ||||
| @@ -221,67 +233,81 @@ class TestPlugins(object): | |||||
| gen.generate_all() | gen.generate_all() | ||||
| assert begin_node_stub.call_count == len(self.content_nodes) | assert begin_node_stub.call_count == len(self.content_nodes) | ||||
| called_with_nodes = sorted([arg[0][0].path for arg in begin_node_stub.call_args_list]) | |||||
| called_with_nodes = sorted( | |||||
| [arg[0][0].path for arg in begin_node_stub.call_args_list]) | |||||
| assert called_with_nodes == self.content_nodes | assert called_with_nodes == self.content_nodes | ||||
| def test_generator_template_begin_node_called_for_single_resource(self): | def test_generator_template_begin_node_called_for_single_resource(self): | ||||
| with patch.object(PluginLoaderStub, 'begin_node') as begin_node_stub: | with patch.object(PluginLoaderStub, 'begin_node') as begin_node_stub: | ||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_resource_at_path(self.site.content.source_folder.child('about.html')) | |||||
| gen.generate_resource_at_path( | |||||
| self.site.content.source_folder.child('about.html')) | |||||
| assert begin_node_stub.call_count == len(self.content_nodes) | assert begin_node_stub.call_count == len(self.content_nodes) | ||||
| def test_generator_template_begin_node_not_called_for_single_resource_second_time(self): | |||||
| def test_generator_template_begin_node_not_called_4_sngl_res_scnd_tm(self): | |||||
| with patch.object(PluginLoaderStub, 'begin_node') as begin_node_stub: | with patch.object(PluginLoaderStub, 'begin_node') as begin_node_stub: | ||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| assert begin_node_stub.call_count == len(self.content_nodes) | assert begin_node_stub.call_count == len(self.content_nodes) | ||||
| gen.generate_resource_at_path(self.site.content.source_folder.child('about.html')) | |||||
| assert begin_node_stub.call_count == len(self.content_nodes) # No extra calls | |||||
| gen.generate_resource_at_path( | |||||
| self.site.content.source_folder.child('about.html')) | |||||
| assert begin_node_stub.call_count == len( | |||||
| self.content_nodes) # No extra calls | |||||
| def test_generator_template_node_complete_called(self): | def test_generator_template_node_complete_called(self): | ||||
| with patch.object(PluginLoaderStub, 'node_complete') as node_complete_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'node_complete') as node_complete_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| assert node_complete_stub.call_count == len(self.content_nodes) | assert node_complete_stub.call_count == len(self.content_nodes) | ||||
| called_with_nodes = sorted([arg[0][0].path for arg in node_complete_stub.call_args_list]) | |||||
| called_with_nodes = sorted( | |||||
| [arg[0][0].path for arg in node_complete_stub.call_args_list]) | |||||
| assert called_with_nodes == self.content_nodes | assert called_with_nodes == self.content_nodes | ||||
| def test_generator_template_node_complete_called_for_single_resource(self): | def test_generator_template_node_complete_called_for_single_resource(self): | ||||
| with patch.object(PluginLoaderStub, 'node_complete') as node_complete_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'node_complete') as node_complete_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_resource_at_path(self.site.content.source_folder.child('about.html')) | |||||
| gen.generate_resource_at_path( | |||||
| self.site.content.source_folder.child('about.html')) | |||||
| assert node_complete_stub.call_count == len(self.content_nodes) | assert node_complete_stub.call_count == len(self.content_nodes) | ||||
| def test_generator_template_node_complete_not_called_for_single_resource_second_time(self): | |||||
| def test_generator_template_node_complete_not_cal_4_sngl_res_scnd_tm(self): | |||||
| with patch.object(PluginLoaderStub, 'node_complete') as node_complete_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'node_complete') as node_complete_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| assert node_complete_stub.call_count == len(self.content_nodes) | assert node_complete_stub.call_count == len(self.content_nodes) | ||||
| gen.generate_resource_at_path(self.site.content.source_folder.child('about.html')) | |||||
| assert node_complete_stub.call_count == len(self.content_nodes) # No extra calls | |||||
| gen.generate_resource_at_path( | |||||
| self.site.content.source_folder.child('about.html')) | |||||
| assert node_complete_stub.call_count == len( | |||||
| self.content_nodes) # No extra calls | |||||
| def test_generator_template_begin_text_resource_called(self): | def test_generator_template_begin_text_resource_called(self): | ||||
| with patch.object(PluginLoaderStub, 'begin_text_resource') as begin_text_resource_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'begin_text_resource') as begin_text_resource_stub: | |||||
| begin_text_resource_stub.reset_mock() | begin_text_resource_stub.reset_mock() | ||||
| begin_text_resource_stub.return_value = '' | begin_text_resource_stub.return_value = '' | ||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| called_with_resources = sorted([arg[0][0].path for arg in begin_text_resource_stub.call_args_list]) | |||||
| assert set(called_with_resources) == set(self.content_text_resources) | |||||
| called_with_resources = sorted( | |||||
| [arg[0][0].path for arg in | |||||
| begin_text_resource_stub.call_args_list]) | |||||
| assert set(called_with_resources) == set( | |||||
| self.content_text_resources) | |||||
| def test_generator_template_begin_text_resource_called_for_single_resource(self): | |||||
| def test_generator_template_begin_text_resource_called_for_sngl_res(self): | |||||
| with patch.object(PluginLoaderStub, 'begin_text_resource') as begin_text_resource_stub: | |||||
| with patch.object(PluginLoaderStub, | |||||
| 'begin_text_resource') as begin_text_resource_stub: | |||||
| begin_text_resource_stub.return_value = '' | begin_text_resource_stub.return_value = '' | ||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| @@ -290,81 +316,105 @@ class TestPlugins(object): | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_resource_at_path(path, incremental=True) | gen.generate_resource_at_path(path, incremental=True) | ||||
| called_with_resources = sorted([arg[0][0].path for arg in begin_text_resource_stub.call_args_list]) | |||||
| called_with_resources = sorted( | |||||
| [arg[0][0].path for arg in | |||||
| begin_text_resource_stub.call_args_list]) | |||||
| assert begin_text_resource_stub.call_count == 1 | assert begin_text_resource_stub.call_count == 1 | ||||
| assert called_with_resources[0] == path | assert called_with_resources[0] == path | ||||
| def test_generator_template_begin_binary_resource_called(self): | def test_generator_template_begin_binary_resource_called(self): | ||||
| with patch.object(PluginLoaderStub, 'begin_binary_resource') as begin_binary_resource_stub: | |||||
| with patch.object(PluginLoaderStub, 'begin_binary_resource') as \ | |||||
| begin_binary_resource_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| called_with_resources = sorted([arg[0][0].path for arg in begin_binary_resource_stub.call_args_list]) | |||||
| assert begin_binary_resource_stub.call_count == len(self.content_binary_resources) | |||||
| called_with_resources = sorted( | |||||
| [arg[0][0].path for arg in | |||||
| begin_binary_resource_stub.call_args_list]) | |||||
| assert begin_binary_resource_stub.call_count == len( | |||||
| self.content_binary_resources) | |||||
| assert called_with_resources == self.content_binary_resources | assert called_with_resources == self.content_binary_resources | ||||
| def test_generator_template_begin_binary_resource_called_for_single_resource(self): | |||||
| def test_generator_template_begin_binary_resource_called_4_sngl_res(self): | |||||
| with patch.object(PluginLoaderStub, 'begin_binary_resource') as begin_binary_resource_stub: | |||||
| with patch.object(PluginLoaderStub, 'begin_binary_resource') as \ | |||||
| begin_binary_resource_stub: | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| begin_binary_resource_stub.reset_mock() | begin_binary_resource_stub.reset_mock() | ||||
| path = self.site.content.source_folder.child('favicon.ico') | path = self.site.content.source_folder.child('favicon.ico') | ||||
| gen.generate_resource_at_path(path) | gen.generate_resource_at_path(path) | ||||
| called_with_resources = sorted([arg[0][0].path for arg in begin_binary_resource_stub.call_args_list]) | |||||
| called_with_resources = sorted( | |||||
| [arg[0][0].path for arg in | |||||
| begin_binary_resource_stub.call_args_list]) | |||||
| assert begin_binary_resource_stub.call_count == 1 | assert begin_binary_resource_stub.call_count == 1 | ||||
| assert called_with_resources[0] == path | assert called_with_resources[0] == path | ||||
| def test_plugin_chaining(self): | def test_plugin_chaining(self): | ||||
| self.site.config.plugins = [ | |||||
| self.site.config.plugins = [ | |||||
| 'hyde.tests.test_plugin.ConstantReturnPlugin', | 'hyde.tests.test_plugin.ConstantReturnPlugin', | ||||
| 'hyde.tests.test_plugin.NoReturnPlugin' | 'hyde.tests.test_plugin.NoReturnPlugin' | ||||
| ] | |||||
| path = self.site.content.source_folder.child('about.html') | |||||
| gen = Generator(self.site) | |||||
| gen.generate_resource_at_path(path) | |||||
| about = File(Folder( | |||||
| self.site.config.deploy_root_path).child('about.html')) | |||||
| assert about.read_all() == "Jam" | |||||
| ] | |||||
| path = self.site.content.source_folder.child('about.html') | |||||
| gen = Generator(self.site) | |||||
| gen.generate_resource_at_path(path) | |||||
| about = File(Folder( | |||||
| self.site.config.deploy_root_path).child('about.html')) | |||||
| assert about.read_all() == "Jam" | |||||
| def test_plugin_filters_begin_text_resource(self): | def test_plugin_filters_begin_text_resource(self): | ||||
| def empty_return(self, resource, text=''): | def empty_return(self, resource, text=''): | ||||
| return text | return text | ||||
| with patch.object(ConstantReturnPlugin, 'begin_text_resource', new=Mock(wraps=empty_return)) as mock1: | |||||
| with patch.object(NoReturnPlugin, 'begin_text_resource', new=Mock(wraps=empty_return)) as mock2: | |||||
| with patch.object(ConstantReturnPlugin, 'begin_text_resource', | |||||
| new=Mock(wraps=empty_return)) as mock1: | |||||
| with patch.object(NoReturnPlugin, 'begin_text_resource', | |||||
| new=Mock(wraps=empty_return)) as mock2: | |||||
| self.site.config.plugins = [ | self.site.config.plugins = [ | ||||
| 'hyde.tests.test_plugin.ConstantReturnPlugin', | 'hyde.tests.test_plugin.ConstantReturnPlugin', | ||||
| 'hyde.tests.test_plugin.NoReturnPlugin' | 'hyde.tests.test_plugin.NoReturnPlugin' | ||||
| ] | |||||
| self.site.config.constantreturn = Expando(dict(include_file_pattern="*.css")) | |||||
| self.site.config.noreturn = Expando(dict(include_file_pattern=["*.html", "*.txt"])) | |||||
| ] | |||||
| self.site.config.constantreturn = Expando( | |||||
| dict(include_file_pattern="*.css")) | |||||
| self.site.config.noreturn = Expando( | |||||
| dict(include_file_pattern=["*.html", "*.txt"])) | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| mock1_args = sorted(set([arg[0][0].name for arg in mock1.call_args_list])) | |||||
| mock2_args = sorted(set([arg[0][0].name for arg in mock2.call_args_list])) | |||||
| mock1_args = sorted( | |||||
| set([arg[0][0].name for arg in mock1.call_args_list])) | |||||
| mock2_args = sorted( | |||||
| set([arg[0][0].name for arg in mock2.call_args_list])) | |||||
| assert len(mock1_args) == 1 | assert len(mock1_args) == 1 | ||||
| assert len(mock2_args) == 4 | assert len(mock2_args) == 4 | ||||
| assert mock1_args == ["site.css"] | assert mock1_args == ["site.css"] | ||||
| assert mock2_args == ["404.html", "about.html", "merry-christmas.html", "robots.txt"] | |||||
| assert mock2_args == [ | |||||
| "404.html", "about.html", | |||||
| "merry-christmas.html", "robots.txt"] | |||||
| def test_plugin_node_filters_begin_text_resource(self): | def test_plugin_node_filters_begin_text_resource(self): | ||||
| def empty_return(*args, **kwargs): | def empty_return(*args, **kwargs): | ||||
| return None | return None | ||||
| with patch.object(ConstantReturnPlugin, 'begin_text_resource', new=Mock(wraps=empty_return)) as mock1: | |||||
| with patch.object(NoReturnPlugin, 'begin_text_resource', new=Mock(wraps=empty_return)) as mock2: | |||||
| with patch.object(ConstantReturnPlugin, 'begin_text_resource', | |||||
| new=Mock(wraps=empty_return)) as mock1: | |||||
| with patch.object(NoReturnPlugin, 'begin_text_resource', | |||||
| new=Mock(wraps=empty_return)) as mock2: | |||||
| self.site.config.plugins = [ | self.site.config.plugins = [ | ||||
| 'hyde.tests.test_plugin.ConstantReturnPlugin', | 'hyde.tests.test_plugin.ConstantReturnPlugin', | ||||
| 'hyde.tests.test_plugin.NoReturnPlugin' | 'hyde.tests.test_plugin.NoReturnPlugin' | ||||
| ] | |||||
| self.site.config.constantreturn = Expando(dict(include_paths="media")) | |||||
| self.site.config.noreturn = Expando(dict(include_file_pattern="*.html", include_paths=["blog"])) | |||||
| ] | |||||
| self.site.config.constantreturn = Expando( | |||||
| dict(include_paths="media")) | |||||
| self.site.config.noreturn = Expando( | |||||
| dict(include_file_pattern="*.html", | |||||
| include_paths=["blog"])) | |||||
| gen = Generator(self.site) | gen = Generator(self.site) | ||||
| gen.generate_all() | gen.generate_all() | ||||
| mock1_args = sorted(set([arg[0][0].name for arg in mock1.call_args_list])) | |||||
| mock2_args = sorted(set([arg[0][0].name for arg in mock2.call_args_list])) | |||||
| mock1_args = sorted( | |||||
| set([arg[0][0].name for arg in mock1.call_args_list])) | |||||
| mock2_args = sorted( | |||||
| set([arg[0][0].name for arg in mock2.call_args_list])) | |||||
| assert len(mock1_args) == 1 | assert len(mock1_args) == 1 | ||||
| assert len(mock2_args) == 1 | assert len(mock2_args) == 1 | ||||
| assert mock1_args == ["site.css"] | assert mock1_args == ["site.css"] | ||||
| assert mock2_args == ["merry-christmas.html"] | |||||
| assert mock2_args == ["merry-christmas.html"] | |||||
| @@ -29,10 +29,13 @@ from nose.tools import nottest | |||||
| TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') | TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') | ||||
| class TestSimpleCopy(object): | class TestSimpleCopy(object): | ||||
| @classmethod | @classmethod | ||||
| def setup_class(cls): | def setup_class(cls): | ||||
| cls.SITE_PATH = File(__file__).parent.child_folder('sites/test_jinja_with_config') | |||||
| cls.SITE_PATH = File(__file__).parent.child_folder( | |||||
| 'sites/test_jinja_with_config') | |||||
| cls.SITE_PATH.make() | cls.SITE_PATH.make() | ||||
| TEST_SITE_ROOT.copy_contents_to(cls.SITE_PATH) | TEST_SITE_ROOT.copy_contents_to(cls.SITE_PATH) | ||||
| @@ -67,7 +70,8 @@ class TestSimpleCopy(object): | |||||
| res = s.content.resource_from_relative_path('about.html') | res = s.content.resource_from_relative_path('about.html') | ||||
| assert res | assert res | ||||
| assert not res.simple_copy | assert not res.simple_copy | ||||
| res = s.content.resource_from_relative_path('blog/2010/december/merry-christmas.html') | |||||
| res = s.content.resource_from_relative_path( | |||||
| 'blog/2010/december/merry-christmas.html') | |||||
| assert res | assert res | ||||
| assert res.simple_copy | assert res.simple_copy | ||||
| @@ -81,7 +85,8 @@ class TestSimpleCopy(object): | |||||
| res = s.content.resource_from_relative_path('about.html') | res = s.content.resource_from_relative_path('about.html') | ||||
| assert res | assert res | ||||
| assert not res.simple_copy | assert not res.simple_copy | ||||
| res = s.content.resource_from_relative_path('blog/2010/december/merry-christmas.html') | |||||
| res = s.content.resource_from_relative_path( | |||||
| 'blog/2010/december/merry-christmas.html') | |||||
| assert res | assert res | ||||
| assert res.simple_copy | assert res.simple_copy | ||||
| res = s.content.resource_from_relative_path('media/css/site.css') | res = s.content.resource_from_relative_path('media/css/site.css') | ||||
| @@ -96,8 +101,10 @@ class TestSimpleCopy(object): | |||||
| s = Site(self.SITE_PATH, self.config) | s = Site(self.SITE_PATH, self.config) | ||||
| g = Generator(s) | g = Generator(s) | ||||
| g.generate_all() | g.generate_all() | ||||
| source = s.content.resource_from_relative_path('blog/2010/december/merry-christmas.html') | |||||
| target = File(s.config.deploy_root_path.child(source.relative_deploy_path)) | |||||
| source = s.content.resource_from_relative_path( | |||||
| 'blog/2010/december/merry-christmas.html') | |||||
| target = File( | |||||
| s.config.deploy_root_path.child(source.relative_deploy_path)) | |||||
| left = source.source_file.read_all() | left = source.source_file.read_all() | ||||
| right = target.read_all() | right = target.read_all() | ||||
| assert left == right | assert left == right | ||||
| @@ -129,11 +136,13 @@ twitter: @me | |||||
| ]) | ]) | ||||
| conf = {'plugins': ['hyde.ext.plugins.meta.MetaPlugin']} | conf = {'plugins': ['hyde.ext.plugins.meta.MetaPlugin']} | ||||
| conf.update(self.config.to_dict()) | conf.update(self.config.to_dict()) | ||||
| s = Site(self.SITE_PATH, Config(sitepath=self.SITE_PATH, config_dict=conf)) | |||||
| s = Site(self.SITE_PATH, Config( | |||||
| sitepath=self.SITE_PATH, config_dict=conf)) | |||||
| g = Generator(s) | g = Generator(s) | ||||
| g.generate_all() | g.generate_all() | ||||
| source = s.content.resource_from_relative_path('blog/index.html') | source = s.content.resource_from_relative_path('blog/index.html') | ||||
| target = File(s.config.deploy_root_path.child(source.relative_deploy_path)) | |||||
| target = File( | |||||
| s.config.deploy_root_path.child(source.relative_deploy_path)) | |||||
| left = source.source_file.read_all() | left = source.source_file.read_all() | ||||
| right = target.read_all() | right = target.read_all() | ||||
| assert left == right | |||||
| assert left == right | |||||
| @@ -14,6 +14,7 @@ from fswrap import File, Folder | |||||
| TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') | TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') | ||||
| def test_node_site(): | def test_node_site(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) | r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) | ||||
| @@ -21,6 +22,7 @@ def test_node_site(): | |||||
| n = Node(r.source_folder.child_folder('blog'), r) | n = Node(r.source_folder.child_folder('blog'), r) | ||||
| assert n.site == s | assert n.site == s | ||||
| def test_node_root(): | def test_node_root(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) | r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) | ||||
| @@ -28,12 +30,14 @@ def test_node_root(): | |||||
| n = Node(r.source_folder.child_folder('blog'), r) | n = Node(r.source_folder.child_folder('blog'), r) | ||||
| assert n.root == r | assert n.root == r | ||||
| def test_node_parent(): | def test_node_parent(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) | r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) | ||||
| c = r.add_node(TEST_SITE_ROOT.child_folder('content/blog/2010/december')) | c = r.add_node(TEST_SITE_ROOT.child_folder('content/blog/2010/december')) | ||||
| assert c.parent == r.node_from_relative_path('blog/2010') | assert c.parent == r.node_from_relative_path('blog/2010') | ||||
| def test_node_module(): | def test_node_module(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) | r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) | ||||
| @@ -43,6 +47,7 @@ def test_node_module(): | |||||
| c = r.add_node(TEST_SITE_ROOT.child_folder('content/blog/2010/december')) | c = r.add_node(TEST_SITE_ROOT.child_folder('content/blog/2010/december')) | ||||
| assert c.module == n | assert c.module == n | ||||
| def test_node_url(): | def test_node_url(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) | r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) | ||||
| @@ -54,6 +59,7 @@ def test_node_url(): | |||||
| assert c.url == '/' + c.relative_path | assert c.url == '/' + c.relative_path | ||||
| assert c.url == '/blog/2010/december' | assert c.url == '/blog/2010/december' | ||||
| def test_node_full_url(): | def test_node_full_url(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| s.config.base_url = 'http://localhost' | s.config.base_url = 'http://localhost' | ||||
| @@ -64,6 +70,7 @@ def test_node_full_url(): | |||||
| c = r.add_node(TEST_SITE_ROOT.child_folder('content/blog/2010/december')) | c = r.add_node(TEST_SITE_ROOT.child_folder('content/blog/2010/december')) | ||||
| assert c.full_url == 'http://localhost/blog/2010/december' | assert c.full_url == 'http://localhost/blog/2010/december' | ||||
| def test_node_full_url_quoted(): | def test_node_full_url_quoted(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| s.config.base_url = 'http://localhost' | s.config.base_url = 'http://localhost' | ||||
| @@ -74,6 +81,7 @@ def test_node_full_url_quoted(): | |||||
| c = r.add_node(TEST_SITE_ROOT.child_folder('content/blo~g/2010/december')) | c = r.add_node(TEST_SITE_ROOT.child_folder('content/blo~g/2010/december')) | ||||
| assert c.full_url == 'http://localhost/' + quote('blo~g/2010/december') | assert c.full_url == 'http://localhost/' + quote('blo~g/2010/december') | ||||
| def test_node_relative_path(): | def test_node_relative_path(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) | r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) | ||||
| @@ -83,6 +91,7 @@ def test_node_relative_path(): | |||||
| c = r.add_node(TEST_SITE_ROOT.child_folder('content/blog/2010/december')) | c = r.add_node(TEST_SITE_ROOT.child_folder('content/blog/2010/december')) | ||||
| assert c.relative_path == 'blog/2010/december' | assert c.relative_path == 'blog/2010/december' | ||||
| def test_load(): | def test_load(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| s.load() | s.load() | ||||
| @@ -96,6 +105,7 @@ def test_load(): | |||||
| assert resource.relative_path == path | assert resource.relative_path == path | ||||
| assert not s.content.resource_from_relative_path('/happy-festivus.html') | assert not s.content.resource_from_relative_path('/happy-festivus.html') | ||||
| def test_walk_resources(): | def test_walk_resources(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| s.load() | s.load() | ||||
| @@ -113,6 +123,7 @@ def test_walk_resources(): | |||||
| expected.sort() | expected.sort() | ||||
| assert pages == expected | assert pages == expected | ||||
| def test_contains_resource(): | def test_contains_resource(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| s.load() | s.load() | ||||
| @@ -120,13 +131,16 @@ def test_contains_resource(): | |||||
| node = s.content.node_from_relative_path(path) | node = s.content.node_from_relative_path(path) | ||||
| assert node.contains_resource('merry-christmas.html') | assert node.contains_resource('merry-christmas.html') | ||||
| def test_get_resource(): | def test_get_resource(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| s.load() | s.load() | ||||
| path = 'blog/2010/december' | path = 'blog/2010/december' | ||||
| node = s.content.node_from_relative_path(path) | node = s.content.node_from_relative_path(path) | ||||
| resource = node.get_resource('merry-christmas.html') | resource = node.get_resource('merry-christmas.html') | ||||
| assert resource == s.content.resource_from_relative_path(Folder(path).child('merry-christmas.html')) | |||||
| assert resource == s.content.resource_from_relative_path( | |||||
| Folder(path).child('merry-christmas.html')) | |||||
| def test_resource_slug(): | def test_resource_slug(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| @@ -143,9 +157,12 @@ def test_get_resource_from_relative_deploy_path(): | |||||
| path = 'blog/2010/december' | path = 'blog/2010/december' | ||||
| node = s.content.node_from_relative_path(path) | node = s.content.node_from_relative_path(path) | ||||
| resource = node.get_resource('merry-christmas.html') | resource = node.get_resource('merry-christmas.html') | ||||
| assert resource == s.content.resource_from_relative_deploy_path(Folder(path).child('merry-christmas.html')) | |||||
| assert resource == s.content.resource_from_relative_deploy_path( | |||||
| Folder(path).child('merry-christmas.html')) | |||||
| resource.relative_deploy_path = Folder(path).child('merry-christmas.php') | resource.relative_deploy_path = Folder(path).child('merry-christmas.php') | ||||
| assert resource == s.content.resource_from_relative_deploy_path(Folder(path).child('merry-christmas.php')) | |||||
| assert resource == s.content.resource_from_relative_deploy_path( | |||||
| Folder(path).child('merry-christmas.php')) | |||||
| def test_is_processable_default_true(): | def test_is_processable_default_true(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| @@ -153,6 +170,7 @@ def test_is_processable_default_true(): | |||||
| for page in s.content.walk_resources(): | for page in s.content.walk_resources(): | ||||
| assert page.is_processable | assert page.is_processable | ||||
| def test_relative_deploy_path(): | def test_relative_deploy_path(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| s.load() | s.load() | ||||
| @@ -160,28 +178,35 @@ def test_relative_deploy_path(): | |||||
| assert page.relative_deploy_path == Folder(page.relative_path) | assert page.relative_deploy_path == Folder(page.relative_path) | ||||
| assert page.url == '/' + page.relative_deploy_path | assert page.url == '/' + page.relative_deploy_path | ||||
| def test_relative_deploy_path_override(): | def test_relative_deploy_path_override(): | ||||
| s = Site(TEST_SITE_ROOT) | s = Site(TEST_SITE_ROOT) | ||||
| s.load() | s.load() | ||||
| res = s.content.resource_from_relative_path('blog/2010/december/merry-christmas.html') | |||||
| res = s.content.resource_from_relative_path( | |||||
| 'blog/2010/december/merry-christmas.html') | |||||
| res.relative_deploy_path = 'blog/2010/december/happy-holidays.html' | res.relative_deploy_path = 'blog/2010/december/happy-holidays.html' | ||||
| for page in s.content.walk_resources(): | for page in s.content.walk_resources(): | ||||
| if res.source_file == page.source_file: | if res.source_file == page.source_file: | ||||
| assert page.relative_deploy_path == 'blog/2010/december/happy-holidays.html' | |||||
| assert (page.relative_deploy_path == | |||||
| 'blog/2010/december/happy-holidays.html') | |||||
| else: | else: | ||||
| assert page.relative_deploy_path == Folder(page.relative_path) | assert page.relative_deploy_path == Folder(page.relative_path) | ||||
| class TestSiteWithConfig(object): | class TestSiteWithConfig(object): | ||||
| @classmethod | @classmethod | ||||
| def setup_class(cls): | def setup_class(cls): | ||||
| cls.SITE_PATH = File(__file__).parent.child_folder('sites/test_jinja_with_config') | |||||
| cls.SITE_PATH = File(__file__).parent.child_folder( | |||||
| 'sites/test_jinja_with_config') | |||||
| cls.SITE_PATH.make() | cls.SITE_PATH.make() | ||||
| TEST_SITE_ROOT.copy_contents_to(cls.SITE_PATH) | TEST_SITE_ROOT.copy_contents_to(cls.SITE_PATH) | ||||
| cls.config_file = File(cls.SITE_PATH.child('alternate.yaml')) | cls.config_file = File(cls.SITE_PATH.child('alternate.yaml')) | ||||
| with open(cls.config_file.path) as config: | with open(cls.config_file.path) as config: | ||||
| cls.config = Config(sitepath=cls.SITE_PATH, config_dict=yaml.load(config)) | |||||
| cls.SITE_PATH.child_folder('content').rename_to(cls.config.content_root) | |||||
| cls.config = Config( | |||||
| sitepath=cls.SITE_PATH, config_dict=yaml.load(config)) | |||||
| cls.SITE_PATH.child_folder('content').rename_to( | |||||
| cls.config.content_root) | |||||
| @classmethod | @classmethod | ||||
| def teardown_class(cls): | def teardown_class(cls): | ||||
| @@ -198,7 +223,8 @@ class TestSiteWithConfig(object): | |||||
| resource = s.content.resource_from_relative_path(path) | resource = s.content.resource_from_relative_path(path) | ||||
| assert resource | assert resource | ||||
| assert resource.relative_path == path | assert resource.relative_path == path | ||||
| assert not s.content.resource_from_relative_path('/happy-festivus.html') | |||||
| assert not s.content.resource_from_relative_path( | |||||
| '/happy-festivus.html') | |||||
| def test_content_url(self): | def test_content_url(self): | ||||
| s = Site(self.SITE_PATH, config=self.config) | s = Site(self.SITE_PATH, config=self.config) | ||||
| @@ -217,7 +243,7 @@ class TestSiteWithConfig(object): | |||||
| s.load() | s.load() | ||||
| path = '".jpg/abc' | path = '".jpg/abc' | ||||
| print s.content_url(path, "") | print s.content_url(path, "") | ||||
| print "/" + quote(path, "") | |||||
| print "/" + quote(path, "") | |||||
| assert s.content_url(path, "") == "/" + quote(path, "") | assert s.content_url(path, "") == "/" + quote(path, "") | ||||
| def test_media_url(self): | def test_media_url(self): | ||||
| @@ -254,7 +280,7 @@ class TestSiteWithConfig(object): | |||||
| s.load() | s.load() | ||||
| path = 'css/site.css' | path = 'css/site.css' | ||||
| resource = s.content.resource_from_relative_path( | resource = s.content.resource_from_relative_path( | ||||
| Folder("media").child(path)) | |||||
| Folder("media").child(path)) | |||||
| assert resource | assert resource | ||||
| assert resource.full_url == "/media/" + path | assert resource.full_url == "/media/" + path | ||||
| @@ -265,7 +291,7 @@ class TestSiteWithConfig(object): | |||||
| path = 'apple-touch-icon.png' | path = 'apple-touch-icon.png' | ||||
| resource = s.content.resource_from_relative_path(path) | resource = s.content.resource_from_relative_path(path) | ||||
| assert resource | assert resource | ||||
| assert resource.full_url == "/" + path | |||||
| assert resource.full_url == "/" + path | |||||
| s = Site(self.SITE_PATH, config=c) | s = Site(self.SITE_PATH, config=c) | ||||
| s.config.ignore.append('*.png') | s.config.ignore.append('*.png') | ||||
| resource = s.content.resource_from_relative_path(path) | resource = s.content.resource_from_relative_path(path) | ||||
| @@ -281,10 +307,10 @@ class TestSiteWithConfig(object): | |||||
| assert not git_node | assert not git_node | ||||
| blog_node = s.content.node_from_relative_path('blog') | blog_node = s.content.node_from_relative_path('blog') | ||||
| assert blog_node | assert blog_node | ||||
| assert blog_node.full_url == "/blog" | |||||
| assert blog_node.full_url == "/blog" | |||||
| s = Site(self.SITE_PATH, config=c) | s = Site(self.SITE_PATH, config=c) | ||||
| s.config.ignore.append('blog') | s.config.ignore.append('blog') | ||||
| blog_node = s.content.node_from_relative_path('blog') | blog_node = s.content.node_from_relative_path('blog') | ||||
| assert not blog_node | assert not blog_node | ||||
| git_node = s.content.node_from_relative_path('.git') | git_node = s.content.node_from_relative_path('.git') | ||||
| assert not git_node | |||||
| assert not git_node | |||||
| @@ -1,6 +1,7 @@ | |||||
| import re | import re | ||||
| import difflib | import difflib | ||||
| def strip_spaces_between_tags(value): | def strip_spaces_between_tags(value): | ||||
| """ | """ | ||||
| Stolen from `django.util.html` | Stolen from `django.util.html` | ||||
| @@ -8,10 +9,11 @@ def strip_spaces_between_tags(value): | |||||
| """ | """ | ||||
| return re.sub(r'>\s+<', '><', unicode(value)) | return re.sub(r'>\s+<', '><', unicode(value)) | ||||
| def assert_no_diff(expected, out): | def assert_no_diff(expected, out): | ||||
| diff = [l for l in difflib.unified_diff(expected.splitlines(True), | diff = [l for l in difflib.unified_diff(expected.splitlines(True), | ||||
| out.splitlines(True), | |||||
| n=3)] | |||||
| out.splitlines(True), | |||||
| n=3)] | |||||
| assert not diff, ''.join(diff) | assert not diff, ''.join(diff) | ||||
| @@ -23,6 +25,7 @@ def assert_html_equals(expected, actual, sanitize=None): | |||||
| actual = sanitize(actual) | actual = sanitize(actual) | ||||
| assert expected == actual | assert expected == actual | ||||
| def trap_exit_fail(f): | def trap_exit_fail(f): | ||||
| def test_wrapper(*args): | def test_wrapper(*args): | ||||
| try: | try: | ||||
| @@ -32,6 +35,7 @@ def trap_exit_fail(f): | |||||
| test_wrapper.__name__ = f.__name__ | test_wrapper.__name__ = f.__name__ | ||||
| return test_wrapper | return test_wrapper | ||||
| def trap_exit_pass(f): | def trap_exit_pass(f): | ||||
| def test_wrapper(*args): | def test_wrapper(*args): | ||||
| try: | try: | ||||
| @@ -39,4 +43,4 @@ def trap_exit_pass(f): | |||||
| except SystemExit: | except SystemExit: | ||||
| pass | pass | ||||
| test_wrapper.__name__ = f.__name__ | test_wrapper.__name__ = f.__name__ | ||||
| return test_wrapper | |||||
| return test_wrapper | |||||
| @@ -54,4 +54,4 @@ def discover_executable(name, sitepath): | |||||
| full_name = os.path.join(path, name) | full_name = os.path.join(path, name) | ||||
| if os.path.exists(full_name): | if os.path.exists(full_name): | ||||
| return full_name | return full_name | ||||
| return None | |||||
| return None | |||||
| @@ -132,7 +132,8 @@ setup(name=PROJECT, | |||||
| 'pyquery==1.2.9', | 'pyquery==1.2.9', | ||||
| 'docutils==0.12', | 'docutils==0.12', | ||||
| 'Pillow==2.7.0', | 'Pillow==2.7.0', | ||||
| 'pyScss==1.3.4' | |||||
| 'pyScss==1.3.4', | |||||
| 'flake8==2.4.1' | |||||
| ), | ), | ||||
| test_suite='nose.collector', | test_suite='nose.collector', | ||||
| include_package_data = True, | include_package_data = True, | ||||