diff --git a/.travis.yml b/.travis.yml index b938db9..f0366db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,5 +22,7 @@ before_script: - export PYTHONPATH=$PYTHONPATH:/usr/share/asciidoc/ script: + # Source code sanity check + - flake8 hyde # Run Python tests and generate coverage statistics - nosetests diff --git a/dev-req.txt b/dev-req.txt index a4b431f..c1df4d1 100644 --- a/dev-req.txt +++ b/dev-req.txt @@ -5,3 +5,4 @@ mock==1.0.1 nose==1.3.6 Pillow==2.7.0 pyScss==1.3.4 +flake8==2.4.1 \ No newline at end of file diff --git a/hyde/engine.py b/hyde/engine.py index 2dc0cdc..47e3df4 100644 --- a/hyde/engine.py +++ b/hyde/engine.py @@ -32,10 +32,11 @@ class Engine(Application): ) @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('-x', '--raise-exceptions', default=None, - help="Don't handle exceptions.") + help="Don't handle exceptions.") @version('--version', version='%(prog)s ' + __version__) @store('-s', '--sitepath', default='.', help="Location of the hyde site") def main(self, args): @@ -52,7 +53,7 @@ class Engine(Application): @subcommand('create', help='Create a new hyde site.') @store('-l', '--layout', default='basic', help='Layout for the new site') @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): """ 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: 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) self.logger.info( "Creating site at [%s] with layout [%s]" % (sitepath, layout)) if not layout or not layout.exists: 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) self.logger.info("Site creation complete") @subcommand('gen', help='Generate the site') @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, - help='Where should the site be generated?') + help='Where should the site be generated?') @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): """ The generate command. Generates the site at the given @@ -103,13 +104,13 @@ class Engine(Application): @subcommand('serve', help='Serve the website') @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', - 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', - help='The configuration used to generate the site') + help='The configuration used to generate the site') @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): """ 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) from hyde.server import HydeWebServer 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: server.serve_forever() except (KeyboardInterrupt, SystemExit): @@ -131,11 +133,11 @@ class Engine(Application): @subcommand('publish', help='Publish the website') @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', - help='Points to the publisher configuration.') + help='Points to the publisher configuration.') @store('-m', '--message', dest='message', - help='Optional message.') + help='Optional message.') def publish(self, args): """ Publishes the site based on the configuration from the `target` @@ -145,11 +147,10 @@ class Engine(Application): site = self.make_site(sitepath, args.config) from hyde.publisher import Publisher publisher = Publisher.load_publisher(site, - args.publisher, - args.message) + args.publisher, + args.message) publisher.publish() - def make_site(self, sitepath, config, deploy=None): """ 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) if deploy: config.deploy_root = deploy - return Site(sitepath, config) \ No newline at end of file + return Site(sitepath, config) diff --git a/hyde/exceptions.py b/hyde/exceptions.py index 7fd8dfe..70a991e 100644 --- a/hyde/exceptions.py +++ b/hyde/exceptions.py @@ -1,4 +1,5 @@ class HydeException(Exception): + """ Base class for exceptions from hyde """ @@ -7,5 +8,3 @@ class HydeException(Exception): def reraise(message, exc_info): _, _, tb = exc_info raise HydeException(message), None, tb - - diff --git a/hyde/ext/plugins/blog.py b/hyde/ext/plugins/blog.py index a0fcc91..27e9631 100644 --- a/hyde/ext/plugins/blog.py +++ b/hyde/ext/plugins/blog.py @@ -10,13 +10,12 @@ from hyde.plugin import Plugin class DraftsPlugin(Plugin): - def begin_site(self): in_production = self.site.config.mode.startswith('prod') 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 for resource in self.site.content.walk_resources(): @@ -33,4 +32,4 @@ class DraftsPlugin(Plugin): self.logger.info( '%s is%s draft' % (resource, - '' if is_draft else ' not')) \ No newline at end of file + '' if is_draft else ' not')) diff --git a/hyde/ext/plugins/css.py b/hyde/ext/plugins/css.py index ca442dc..a5a6585 100644 --- a/hyde/ext/plugins/css.py +++ b/hyde/ext/plugins/css.py @@ -18,7 +18,9 @@ from fswrap import File # Less CSS # + class LessCSSPlugin(CLTransformer): + """ The plugin class for less css """ @@ -29,7 +31,6 @@ class LessCSSPlugin(CLTransformer): re.compile('^\\s*@import\s+(?:\'|\")([^\'\"]*)(?:\'|\")\s*\;\s*$', re.MULTILINE) - @property def executable_name(self): return "lessc" @@ -39,7 +40,7 @@ class LessCSSPlugin(CLTransformer): Check user defined """ 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): return getattr(resource, 'meta', {}).get('uses_template', True) @@ -72,13 +73,13 @@ class LessCSSPlugin(CLTransformer): afile = File(afile.path + '.less') ref = self.site.content.resource_from_path(afile.path) 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 return self.template.get_include_statement(ref.relative_path) text = self.import_finder.sub(import_to_include, text) return text - @property def plugin_name(self): """ @@ -114,10 +115,10 @@ class LessCSSPlugin(CLTransformer): try: self.call_app(args) 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() @@ -127,6 +128,7 @@ class LessCSSPlugin(CLTransformer): # class StylusPlugin(CLTransformer): + """ The plugin class for stylus css """ @@ -162,7 +164,8 @@ class StylusPlugin(CLTransformer): if not match.lastindex: return '' 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: afile = File(afile.path + '.styl') @@ -179,8 +182,8 @@ class StylusPlugin(CLTransformer): else: ref.is_processable = False return "\n" + \ - self.template.get_include_statement(ref.relative_path) + \ - "\n" + self.template.get_include_statement(ref.relative_path) + \ + "\n" return '@import "' + path + '"\n' text = self.import_finder.sub(import_to_include, text) @@ -196,7 +199,7 @@ class StylusPlugin(CLTransformer): except AttributeError: mode = "production" - defaults = {"compress":""} + defaults = {"compress": ""} if mode.startswith('dev'): defaults = {} return defaults @@ -227,9 +230,9 @@ class StylusPlugin(CLTransformer): self.call_app(args) except subprocess.CalledProcessError: 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') return target.read_all() @@ -239,6 +242,7 @@ class StylusPlugin(CLTransformer): # class CleverCSSPlugin(Plugin): + """ The plugin class for CleverCSS """ @@ -257,7 +261,7 @@ class CleverCSSPlugin(Plugin): Check user defined """ 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): return getattr(resource, 'meta', {}).get('uses_template', True) @@ -282,8 +286,8 @@ class CleverCSSPlugin(Plugin): return text import_finder = re.compile( - '^\\s*@import\s+(?:\'|\")([^\'\"]*)(?:\'|\")\s*\;\s*$', - re.MULTILINE) + '^\\s*@import\s+(?:\'|\")([^\'\"]*)(?:\'|\")\s*\;\s*$', + re.MULTILINE) def import_to_include(match): if not match.lastindex: @@ -294,7 +298,8 @@ class CleverCSSPlugin(Plugin): afile = File(afile.path + '.ccss') ref = self.site.content.resource_from_path(afile.path) 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 return self.template.get_include_statement(ref.relative_path) text = import_finder.sub(import_to_include, text) @@ -313,7 +318,9 @@ class CleverCSSPlugin(Plugin): # Sassy CSS # + class SassyCSSPlugin(Plugin): + """ The plugin class for SassyCSS """ @@ -332,7 +339,7 @@ class SassyCSSPlugin(Plugin): Check user defined """ return resource.source_file.kind == 'scss' and \ - getattr(resource, 'meta', {}).get('parse', True) + getattr(resource, 'meta', {}).get('parse', True) @property def options(self): @@ -364,7 +371,6 @@ class SassyCSSPlugin(Plugin): """ return self.settings.get('includes', []) - def begin_site(self): """ 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.ASSETS_URL = self.site.media_url('/') 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(): if self._should_parse_resource(resource): @@ -391,8 +397,8 @@ class SassyCSSPlugin(Plugin): includes = [resource.node.path] + self.includes includes = [path.rstrip(os.sep) + os.sep for path in includes] options = self.options - if not 'load_paths' in options: + if 'load_paths' not in options: options['load_paths'] = [] 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) diff --git a/hyde/ext/plugins/depends.py b/hyde/ext/plugins/depends.py index 3234a3e..304d403 100644 --- a/hyde/ext/plugins/depends.py +++ b/hyde/ext/plugins/depends.py @@ -7,7 +7,9 @@ Depends plugin from hyde.plugin import Plugin + class DependsPlugin(Plugin): + """ The plugin class setting explicit dependencies. """ @@ -16,15 +18,14 @@ class DependsPlugin(Plugin): super(DependsPlugin, self).__init__(site) 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): """ @@ -53,7 +54,7 @@ class DependsPlugin(Plugin): for dep in depends: 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)) diff --git a/hyde/ext/plugins/images.py b/hyde/ext/plugins/images.py index f09d80e..dee25cd 100644 --- a/hyde/ext/plugins/images.py +++ b/hyde/ext/plugins/images.py @@ -39,6 +39,7 @@ class PILPlugin(Plugin): class ImageSizerPlugin(PILPlugin): + """ Each HTML page is modified to add width and height for images if they are not already specified. @@ -50,26 +51,28 @@ class ImageSizerPlugin(PILPlugin): super(ImageSizerPlugin, self).__init__(site) self.cache = {} - def _handle_img(self, resource, src, width, height): """Determine what should be added to an img tag""" if height is not None and width is not None: return "" # Nothing 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 if src not in self.cache: if src.startswith(self.site.config.media_url): path = src[len(self.site.config.media_url):].lstrip("/") 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): # Not a local link return "" # Nothing elif src.startswith("/"): # Absolute resource path = src.lstrip("/") - image = self.site.content.resource_from_relative_deploy_path(path) + image = self.site.content.resource_from_relative_deploy_path( + path) else: # Relative resource path = resource.node.source_folder.child(src) @@ -80,7 +83,7 @@ class ImageSizerPlugin(PILPlugin): return "" # Nothing if image.source_file.kind not in ['png', 'jpg', 'jpeg', 'gif']: 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 # Now, get the size of the image try: @@ -96,9 +99,9 @@ class ImageSizerPlugin(PILPlugin): if new_width is None or new_height is None: return "" # Nothing 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: - 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) def text_resource_complete(self, resource, text): @@ -151,7 +154,7 @@ class ImageSizerPlugin(PILPlugin): continue attr = None for tag in tags: - if text[pos:(pos+len(tag)+1)] == ("%s=" % tag): + if text[pos:(pos + len(tag) + 1)] == ("%s=" % tag): attr = tag pos = pos + len(tag) + 1 break @@ -177,12 +180,13 @@ class ImageSizerPlugin(PILPlugin): return text + def scale_aspect(a, b1, b2): - from math import ceil - """ + from math import ceil + """ 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): @@ -197,7 +201,7 @@ def thumb_scale_size(orig_width, orig_height, width, height): width = scale_aspect(orig_width, orig_height, height) elif height is None: 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) else: height = scale_aspect(orig_height, orig_width, width) @@ -208,7 +212,9 @@ def thumb_scale_size(orig_width, orig_height, width, height): # Image Thumbnails # + class ImageThumbnailsPlugin(PILPlugin): + """ Provide a function to get thumbnail for any image resource. @@ -239,11 +245,11 @@ class ImageThumbnailsPlugin(PILPlugin): prefix: thumbs4_ include: - '*.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 portrait) is preserved while thumbnailing. @@ -256,7 +262,8 @@ class ImageThumbnailsPlugin(PILPlugin): def __init__(self, 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 """ @@ -268,14 +275,17 @@ class ImageThumbnailsPlugin(PILPlugin): # for simple maintenance but keep original deploy path to preserve # naming logic in generated site 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)) target = resource.site.config.content_root_path.child_file(path) 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() - 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 self.logger.debug("Making thumbnail for [%s]" % resource) @@ -285,17 +295,18 @@ class ImageThumbnailsPlugin(PILPlugin): format = im.format 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)) im = im.resize((resize_width, resize_height), self.Image.ANTIALIAS) if width is not None and height is not None: shiftx = shifty = 0 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": shiftx = (im.size[0] - width) shifty = (im.size[1] - height) @@ -304,22 +315,23 @@ class ImageThumbnailsPlugin(PILPlugin): options = dict(optimize=True) if format == "JPEG": - options['quality'] = 75 + options['quality'] = 75 im.save(target.path, **options) 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 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'): defaults.update(config.thumbnails) @@ -327,45 +339,64 @@ class ImageThumbnailsPlugin(PILPlugin): if hasattr(node, 'meta') and hasattr(node.meta, 'thumbnails'): for th in node.meta.thumbnails: 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 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"]: - self.logger.error("Unknown crop_type defined for node [%s]" % node) + self.logger.error( + "Unknown crop_type defined for node [%s]" % node) 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 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 if larger is None and smaller is None: - preserve_orientation = False - dim1, dim2 = width, height + preserve_orientation = False + dim1, dim2 = width, height 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: 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 # + class JPEGOptimPlugin(CLTransformer): + """ The plugin class for JPEGOptim """ @@ -408,7 +439,7 @@ class JPEGOptimPlugin(CLTransformer): "strip-icc", ] target = File(self.site.config.deploy_root_path.child( - resource.relative_deploy_path)) + resource.relative_deploy_path)) jpegoptim = self.app args = [unicode(jpegoptim)] args.extend(self.process_args(supported)) @@ -417,6 +448,7 @@ class JPEGOptimPlugin(CLTransformer): class JPEGTranPlugin(CLTransformer): + """ Almost like jpegoptim except it uses jpegtran. jpegtran allows to make progressive JPEG. Unfortunately, it only does lossless compression. If @@ -463,7 +495,7 @@ class JPEGTranPlugin(CLTransformer): "copy", ] source = File(self.site.config.deploy_root_path.child( - resource.relative_deploy_path)) + resource.relative_deploy_path)) target = File.make_temp('') jpegtran = self.app args = [unicode(jpegtran)] @@ -474,12 +506,12 @@ class JPEGTranPlugin(CLTransformer): target.delete() - # # PNG Optimization # class OptiPNGPlugin(CLTransformer): + """ The plugin class for OptiPNG """ @@ -535,7 +567,7 @@ class OptiPNGPlugin(CLTransformer): "nz" ] target = File(self.site.config.deploy_root_path.child( - resource.relative_deploy_path)) + resource.relative_deploy_path)) optipng = self.app args = [unicode(optipng)] args.extend(self.process_args(supported)) diff --git a/hyde/ext/plugins/js.py b/hyde/ext/plugins/js.py index 135b2a1..5c849ab 100644 --- a/hyde/ext/plugins/js.py +++ b/hyde/ext/plugins/js.py @@ -16,6 +16,7 @@ from fswrap import File # class UglifyPlugin(CLTransformer): + """ The plugin class for Uglify JS """ @@ -85,7 +86,9 @@ class UglifyPlugin(CLTransformer): out = target.read_all() return out + class RequireJSPlugin(CLTransformer): + """ requirejs plugin @@ -103,6 +106,7 @@ class RequireJSPlugin(CLTransformer): Please see the homepage of requirejs for usage details. """ + def __init__(self, site): super(RequireJSPlugin, self).__init__(site) @@ -124,20 +128,22 @@ class RequireJSPlugin(CLTransformer): rjs = self.app target = File.make_temp('') args = [unicode(rjs)] - args.extend(['-o', unicode(resource), ("out=" + target.fully_expanded_path)]) + args.extend( + ['-o', unicode(resource), ("out=" + target.fully_expanded_path)]) try: self.call_app(args) 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() class CoffeePlugin(CLTransformer): + """ The plugin class for Coffeescript """ diff --git a/hyde/ext/plugins/languages.py b/hyde/ext/plugins/languages.py index 93dd08e..3b2aeed 100644 --- a/hyde/ext/plugins/languages.py +++ b/hyde/ext/plugins/languages.py @@ -5,7 +5,9 @@ Contains classes to help manage multi-language pages. from hyde.plugin import Plugin + class LanguagePlugin(Plugin): + """ Each page should be tagged with a language using `language` meta data. Each page should also have an UUID stored in `uuid` meta @@ -36,7 +38,8 @@ class LanguagePlugin(Plugin): def __init__(self, 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): """ @@ -60,8 +63,9 @@ class LanguagePlugin(Plugin): resource.translations = \ [r for r in resources 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)) diff --git a/hyde/ext/plugins/meta.py b/hyde/ext/plugins/meta.py index 9c4de06..50874a7 100644 --- a/hyde/ext/plugins/meta.py +++ b/hyde/ext/plugins/meta.py @@ -26,6 +26,7 @@ import yaml # class Metadata(Expando): + """ Container class for yaml meta data. """ @@ -49,6 +50,7 @@ class Metadata(Expando): class MetaPlugin(Plugin): + """ Metadata plugin for hyde. Loads meta data in the following order: @@ -66,8 +68,8 @@ class MetaPlugin(Plugin): def __init__(self, site): super(MetaPlugin, self).__init__(site) 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): """ @@ -88,7 +90,8 @@ class MetaPlugin(Plugin): if not hasattr(resource, 'meta'): resource.meta = Metadata({}, node.meta) 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): """ @@ -96,7 +99,8 @@ class MetaPlugin(Plugin): the resource. Load meta data by looking for the marker. 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) if not match: self.logger.debug("No metadata found in resource [%s]" % resource) @@ -113,7 +117,7 @@ class MetaPlugin(Plugin): resource.meta.update(data) self.__update_standard_attributes__(resource) self.logger.debug("Successfully loaded metadata from resource [%s]" - % resource) + % resource) return text or ' ' def __update_standard_attributes__(self, obj): @@ -165,6 +169,7 @@ class MetaPlugin(Plugin): # class AutoExtendPlugin(Plugin): + """ The plugin class for extending templates using metadata. """ @@ -204,9 +209,9 @@ class AutoExtendPlugin(Plugin): extended_text += '\n' if block: 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: extended_text += text return extended_text @@ -218,6 +223,7 @@ class AutoExtendPlugin(Plugin): # class Tag(Expando): + """ A simple object that represents a tag. """ @@ -255,6 +261,7 @@ def get_tagger_sort_method(site): sys.exc_info()) return walker + def walk_resources_tagged_with(node, tag): tags = set(unicode(tag).split('+')) walker = get_tagger_sort_method(node.site) @@ -266,7 +273,9 @@ def walk_resources_tagged_with(node, tag): if tags <= taglist: yield resource + class TaggerPlugin(Plugin): + """ Tagger plugin for hyde. Adds the ability to do tag resources and search based on the tags. @@ -286,6 +295,7 @@ class TaggerPlugin(Plugin): target: blog/tags archive_extension: html """ + def __init__(self, site): super(TaggerPlugin, self).__init__(site) @@ -295,11 +305,13 @@ class TaggerPlugin(Plugin): and methods for walking tagged resources. """ 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 = {} 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) for resource in walker(): self._process_tags_in_resource(resource, tags) @@ -337,14 +349,14 @@ class TaggerPlugin(Plugin): return for tagname in taglist: - if not tagname in tags: + if tagname not in tags: tag = Tag(tagname) tags[tagname] = tag tag.resources.append(resource) 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: tags[tagname].resources.append(resource) if not hasattr(resource, 'tags'): @@ -367,13 +379,13 @@ class TaggerPlugin(Plugin): for name, config in archive_config.to_dict().iteritems(): self._create_tag_archive(config) - def _create_tag_archive(self, config): """ 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 source = Folder(config.get('source', '')) target = content.child_folder(config.get('target', 'tags')) @@ -441,15 +453,17 @@ def filter_method(item, settings=None): break return all_match + def attributes_checker(item, attributes=None): """ Checks if the given list of attributes exist. """ try: - attrgetter(*attributes)(item) - return True + attrgetter(*attributes)(item) + return True except AttributeError: - return False + return False + 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), node.walk_resources()) return sorted(resources, - key=attrgetter(*attr), - reverse=reverse) + key=attrgetter(*attr), + reverse=reverse) class SorterPlugin(Plugin): + """ Sorter plugin for hyde. Adds the ability to do sophisticated sorting by expanding the site objects @@ -529,8 +544,8 @@ class SorterPlugin(Plugin): setattr(Resource, next_att, None) 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 for prev, next in pairwalk(walker()): if not first: @@ -555,7 +570,9 @@ class SorterPlugin(Plugin): Grouper = namedtuple('Grouper', 'group resources') + class Group(Expando): + """ A wrapper class for groups. Adds methods for grouping resources. @@ -573,21 +590,21 @@ class Group(Expando): super(Group, self).__init__(grouping) 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, - '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, - '%s_group' % self.name, - Group.get_resource_group, - group=self) + '%s_group' % self.name, + Group.get_resource_group, + group=self) 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): """ @@ -612,9 +629,9 @@ class Group(Expando): group_name = None 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 def walk_resource_groups(resource, group): @@ -693,7 +710,9 @@ class Group(Expando): if group_value == self.name: yield resource + class GrouperPlugin(Plugin): + """ Grouper plugin for hyde. Adds the ability to do group resources and nodes in an arbitrary @@ -726,6 +745,7 @@ class GrouperPlugin(Plugin): Helpful snippets and tweaks to make hyde more awesome. """ + def __init__(self, site): super(GrouperPlugin, self).__init__(site) @@ -748,9 +768,8 @@ class GrouperPlugin(Plugin): setattr(Resource, next_att, None) self.site.grouper[name] = Group(grouping) walker = Group.walk_resources( - self.site.content, self.site.grouper[name]) + self.site.content, self.site.grouper[name]) for prev, next in pairwalk(walker): setattr(next, prev_att, prev) setattr(prev, next_att, next) - diff --git a/hyde/ext/plugins/sphinx.py b/hyde/ext/plugins/sphinx.py index 1584a9b..cf2ebef 100644 --- a/hyde/ext/plugins/sphinx.py +++ b/hyde/ext/plugins/sphinx.py @@ -63,6 +63,7 @@ except ImportError: class SphinxPlugin(Plugin): + """The plugin class for rendering sphinx-generated documentation.""" def __init__(self, site): @@ -93,7 +94,7 @@ class SphinxPlugin(Plugin): else: for name in dir(user_settings): if not name.startswith("_"): - setattr(settings,name,getattr(user_settings,name)) + setattr(settings, name, getattr(user_settings, name)) return settings @property @@ -109,11 +110,11 @@ class SphinxPlugin(Plugin): conf_path = self.site.sitepath.child_folder(conf_path) # Sphinx always execs the config file in its parent dir. conf_file = conf_path.child("conf.py") - self._sphinx_config = {"__file__":conf_file} + self._sphinx_config = {"__file__": conf_file} curdir = os.getcwd() os.chdir(conf_path.path) try: - execfile(conf_file,self._sphinx_config) + execfile(conf_file, self._sphinx_config) finally: os.chdir(curdir) return self._sphinx_config @@ -132,16 +133,17 @@ class SphinxPlugin(Plugin): # We need to: # * change the deploy name from .rst to .html # * 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(): 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 resource.relative_deploy_path = target_folder.child(new_name) if settings.block_map: 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. 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 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): return text if self.sphinx_build_dir is None: @@ -164,9 +166,9 @@ class SphinxPlugin(Plugin): if not settings.block_map: output.append(sphinx_output["body"]) else: - for (nm,content) in sphinx_output.iteritems(): + for (nm, content) in sphinx_output.iteritems(): try: - block = getattr(settings.block_map,nm) + block = getattr(settings.block_map, nm) except AttributeError: pass else: @@ -198,41 +200,45 @@ class SphinxPlugin(Plugin): conf_path = self.settings.conf_path conf_path = self.site.sitepath.child_folder(conf_path) 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("containing your sphinx conf.py") raise # 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: logger.error("The hyde_json sphinx extension is not configured.") logger.error("Please add 'hyde.ext.plugins.sphinx' to the list") 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") # 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): 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("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") # Check that I am *before* the other plugins, # with the possible exception of MetaPlugin for plugin in self.site.plugins: if plugin is self: break - if not isinstance(plugin,_MetaPlugin): + if not isinstance(plugin, _MetaPlugin): 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("Please move the sphinx plugin to the top") 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") def _run_sphinx(self): @@ -254,7 +260,7 @@ class SphinxPlugin(Plugin): if sphinx.main(sphinx_args) != 0: 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. This returns a dict mapping block names to HTML text fragments. @@ -263,13 +269,14 @@ class SphinxPlugin(Plugin): related pages and so-on. """ 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) - class HydeJSONHTMLBuilder(JSONHTMLBuilder): + """A slightly-customised JSONHTMLBuilder, for use by Hyde. 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. """ name = "hyde_json" + def get_target_uri(self, docname, typ=None): return docname + ".html" @@ -291,5 +299,3 @@ def setup(app): Hyde plugin. It simply registers the HydeJSONHTMLBuilder class. """ app.add_builder(HydeJSONHTMLBuilder) - - diff --git a/hyde/ext/plugins/structure.py b/hyde/ext/plugins/structure.py index abe3f62..82b766d 100644 --- a/hyde/ext/plugins/structure.py +++ b/hyde/ext/plugins/structure.py @@ -20,9 +20,11 @@ import operator # class FlattenerPlugin(Plugin): + """ The plugin class for flattening nested folders. """ + def __init__(self, site): super(FlattenerPlugin, self).__init__(site) @@ -50,7 +52,7 @@ class FlattenerPlugin(Plugin): target_path = target.child(resource.name) self.logger.debug( 'Flattening resource path [%s] to [%s]' % - (resource, target_path)) + (resource, target_path)) resource.relative_deploy_path = target_path for child in node.walk(): child.relative_deploy_path = target.path @@ -61,12 +63,14 @@ class FlattenerPlugin(Plugin): # class CombinePlugin(Plugin): + """ To use this combine, the following configuration should be added to meta data:: combine: 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. files: - ns1.*.js @@ -97,13 +101,14 @@ class CombinePlugin(Plugin): except AttributeError: raise AttributeError("No resources to combine for [%s]" % resource) if type(files) is str: - files = [ files ] + files = [files] # Grab resources to combine # select site root 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: root = resource.node @@ -122,10 +127,12 @@ class CombinePlugin(Plugin): sort = True 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: - 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] if not resources: @@ -173,7 +180,7 @@ class CombinePlugin(Plugin): except AttributeError: pass - if where not in [ "top", "bottom" ]: + if where not in ["top", "bottom"]: raise ValueError("%r should be either `top` or `bottom`" % where) self.logger.debug( @@ -190,11 +197,14 @@ class CombinePlugin(Plugin): # class Page: + def __init__(self, posts, number): self.posts = posts self.number = number + class Paginator: + """ Iterates resources which have pages associated with them. """ @@ -204,7 +214,8 @@ class Paginator: def __init__(self, settings): self.sorter = getattr(settings, 'sorter', None) 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): """ @@ -214,8 +225,8 @@ class Paginator: path = File(source_path) if number != 1: 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)) return path @@ -227,10 +238,11 @@ class Paginator: res = Resource(base_resource.source_file, node) res.node.meta = Metadata(node.meta) res.meta = Metadata(base_resource.meta, res.node.meta) + brs = base_resource.source_file 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) return res @@ -250,7 +262,7 @@ class Paginator: if not hasattr(resource, 'depends'): resource.depends = [] 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): """ @@ -294,6 +306,7 @@ class Paginator: class PaginatorPlugin(Plugin): + """ Paginator plugin. @@ -315,6 +328,7 @@ class PaginatorPlugin(Plugin): {{ resource.page.next }} """ + def __init__(self, site): super(PaginatorPlugin, self).__init__(site) @@ -322,10 +336,10 @@ class PaginatorPlugin(Plugin): for node in self.site.content.walk(): added_resources = [] paged_resources = (res for res in node.resources - if hasattr(res.meta, 'paginator')) + if hasattr(res.meta, 'paginator')) for resource in paged_resources: 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 - diff --git a/hyde/ext/plugins/text.py b/hyde/ext/plugins/text.py index 8754de4..3a1992e 100644 --- a/hyde/ext/plugins/text.py +++ b/hyde/ext/plugins/text.py @@ -3,7 +3,7 @@ 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): + """ The plugin class for block text replacement. """ + def __init__(self, site): super(BlockdownPlugin, self).__init__(site) @@ -55,9 +57,11 @@ class BlockdownPlugin(TextyPlugin): # class MarkingsPlugin(TextyPlugin): + """ The plugin class for mark text replacement. """ + def __init__(self, site): super(MarkingsPlugin, self).__init__(site) @@ -99,9 +103,11 @@ class MarkingsPlugin(TextyPlugin): # class ReferencePlugin(TextyPlugin): + """ The plugin class for reference text replacement. """ + def __init__(self, site): super(ReferencePlugin, self).__init__(site) @@ -143,9 +149,11 @@ class ReferencePlugin(TextyPlugin): # class SyntextPlugin(TextyPlugin): + """ The plugin class for syntax text replacement. """ + def __init__(self, site): super(SyntextPlugin, self).__init__(site) @@ -170,7 +178,6 @@ class SyntextPlugin(TextyPlugin): """ return '^\s*~~~+\s*$' - def get_params(self, match, start=True): """ ~~~css~~~ will return css @@ -199,16 +206,18 @@ class SyntextPlugin(TextyPlugin): # class TextlinksPlugin(Plugin): + """ The plugin class for text link replacement. """ + def __init__(self, site): super(TextlinksPlugin, self).__init__(site) import re self.content_link = re.compile('\[\[([^\]^!][^\]]*)\]\]', - re.UNICODE|re.MULTILINE) + re.UNICODE | re.MULTILINE) self.media_link = re.compile('\[\[\!\!([^\]]*)\]\]', - re.UNICODE|re.MULTILINE) + re.UNICODE | re.MULTILINE) def begin_text_resource(self, resource, text): """ @@ -221,11 +230,12 @@ class TextlinksPlugin(Plugin): """ if not resource.uses_template: return text + def replace_content(match): return self.template.get_content_url_statement(match.groups(1)[0]) + def replace_media(match): return self.template.get_media_url_statement(match.groups(1)[0]) text = self.content_link.sub(replace_content, text) text = self.media_link.sub(replace_media, text) return text - diff --git a/hyde/ext/plugins/urls.py b/hyde/ext/plugins/urls.py index 09fb44c..58fdb74 100644 --- a/hyde/ext/plugins/urls.py +++ b/hyde/ext/plugins/urls.py @@ -8,7 +8,9 @@ from hyde.site import Site from functools import wraps from fswrap import File + class UrlCleanerPlugin(Plugin): + """ Url Cleaner plugin for hyde. Adds to hyde the ability to generate clean urls. @@ -53,13 +55,13 @@ class UrlCleanerPlugin(Plugin): def wrapper(site, path, safe=None): url = urlgetter(site, path, safe) index_file_names = getattr(settings, - 'index_file_names', - ['index.html']) + 'index_file_names', + ['index.html']) rep = File(url) if rep.name in index_file_names: url = rep.parent.path.rstrip('/') if hasattr(settings, 'append_slash') and \ - settings.append_slash: + settings.append_slash: url += '/' elif hasattr(settings, 'strip_extensions'): if rep.kind in settings.strip_extensions: diff --git a/hyde/ext/plugins/vcs.py b/hyde/ext/plugins/vcs.py index a53a7f5..8cc04e8 100644 --- a/hyde/ext/plugins/vcs.py +++ b/hyde/ext/plugins/vcs.py @@ -12,9 +12,11 @@ import subprocess class VCSDatesPlugin(Plugin): + """ Base class for getting resource timestamps from VCS. """ + def __init__(self, site, vcs_name='vcs'): super(VCSDatesPlugin, self).__init__(site) self.vcs_name = vcs_name @@ -38,8 +40,8 @@ class VCSDatesPlugin(Plugin): if created == "git": created = date_created or \ - datetime.utcfromtimestamp( - os.path.getctime(resource.path)) + datetime.utcfromtimestamp( + os.path.getctime(resource.path)) created = created.replace(tzinfo=None) resource.meta.created = created @@ -48,7 +50,6 @@ class VCSDatesPlugin(Plugin): modified = modified.replace(tzinfo=None) resource.meta.modified = modified - def get_dates(self): """ Extract creation and last modification date from the vcs and include @@ -60,7 +61,10 @@ class VCSDatesPlugin(Plugin): # # Git Dates # + + class GitDatesPlugin(VCSDatesPlugin): + def __init__(self, site): super(GitDatesPlugin, self).__init__(site, 'git') @@ -78,7 +82,8 @@ class GitDatesPlugin(VCSDatesPlugin): ]).split("\n") commits = commits[:-1] 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 if commits: @@ -93,6 +98,8 @@ class GitDatesPlugin(VCSDatesPlugin): # # Mercurial Dates # + + class MercurialDatesPlugin(VCSDatesPlugin): def __init__(self, site): @@ -105,12 +112,12 @@ class MercurialDatesPlugin(VCSDatesPlugin): # Run hg log --template={date|isodatesec} try: 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] except subprocess.CalledProcessError: self.logger.warning("Unable to get mercurial history for [%s]" - % resource) + % resource) commits = None if not commits: diff --git a/hyde/ext/publishers/dvcs.py b/hyde/ext/publishers/dvcs.py index ae880f7..cb10e92 100644 --- a/hyde/ext/publishers/dvcs.py +++ b/hyde/ext/publishers/dvcs.py @@ -8,6 +8,7 @@ from hyde.publisher import Publisher import abc from subprocess import Popen, PIPE + class DVCS(Publisher): __metaclass__ = abc.ABCMeta @@ -19,23 +20,28 @@ class DVCS(Publisher): self.switch(self.branch) @abc.abstractmethod - def pull(self): pass + def pull(self): + pass @abc.abstractmethod - def push(self): pass + def push(self): + pass @abc.abstractmethod - def commit(self, message): pass + def commit(self, message): + pass @abc.abstractmethod - def switch(self, branch): pass + def switch(self, branch): + pass @abc.abstractmethod - def add(self, path="."): pass + def add(self, path="."): + pass @abc.abstractmethod - def merge(self, branch): pass - + def merge(self, branch): + pass def publish(self): super(DVCS, self).publish() @@ -47,8 +53,8 @@ class DVCS(Publisher): self.push() - class Git(DVCS): + """ Acts as a publisher to a git repository. Can be used to publish to github pages. @@ -56,7 +62,7 @@ class Git(DVCS): def add(self, 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] if cmd.returncode: raise Exception(cmdresult) @@ -79,7 +85,6 @@ class Git(DVCS): if cmd.returncode: raise Exception(cmdresult) - def commit(self, message): cmd = Popen('git commit -a -m"%s"' % message, cwd=unicode(self.path), stdout=PIPE, shell=True) @@ -100,4 +105,4 @@ class Git(DVCS): cwd=unicode(self.path), stdout=PIPE, shell=True) cmdresult = cmd.communicate()[0] if cmd.returncode: - raise Exception(cmdresult) \ No newline at end of file + raise Exception(cmdresult) diff --git a/hyde/ext/publishers/pyfs.py b/hyde/ext/publishers/pyfs.py index d8219e6..669afac 100644 --- a/hyde/ext/publishers/pyfs.py +++ b/hyde/ext/publishers/pyfs.py @@ -32,15 +32,14 @@ except ImportError: raise - class PyFS(Publisher): def initialize(self, settings): self.settings = settings 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") self.prompt_for_credentials() self.fs = fsopendir(self.url) @@ -58,48 +57,47 @@ class PyFS(Publisher): def publish(self): super(PyFS, self).publish() 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. for filenm in local_filenms: - filepath = pathjoin(dirnm,filenm) + filepath = pathjoin(dirnm, filenm) # 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: break else: info = {} # Skip it if the etags match 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) if info["etag"] == local_etag: - logger.info("Skipping file [etag]: %s",filepath) + logger.info("Skipping file [etag]: %s", filepath) continue # Skip it if the mtime is more recent remotely. if self.check_mtime and "modified_time" in info: local_mtime = deploy_fs.getinfo(filepath)["modified_time"] if info["modified_time"] > local_mtime: - logger.info("Skipping file [mtime]: %s",filepath) + logger.info("Skipping file [mtime]: %s", filepath) continue # 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. - 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: - logger.info("Removing file: %s",filepath) + logger.info("Removing file: %s", 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: hasher.update(data) - data = f.read(1024*64) + data = f.read(1024 * 64) return hasher.hexdigest() - diff --git a/hyde/ext/publishers/pypi.py b/hyde/ext/publishers/pypi.py index 5980f59..22b1153 100644 --- a/hyde/ext/publishers/pypi.py +++ b/hyde/ext/publishers/pypi.py @@ -19,16 +19,14 @@ from commando.util import getLoggerWithNullHandler logger = getLoggerWithNullHandler('hyde.ext.publishers.pypi') - - class PyPI(Publisher): def initialize(self, settings): self.settings = settings 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() def prompt_for_credentials(self): @@ -38,12 +36,13 @@ class PyPI(Publisher): else: pypirc = ConfigParser.RawConfigParser() pypirc.read([pypirc_file]) - missing_errs = (ConfigParser.NoSectionError,ConfigParser.NoOptionError) + missing_errs = ( + ConfigParser.NoSectionError, ConfigParser.NoOptionError) # Try to find username in .pypirc if self.username is None: if pypirc is not None: try: - self.username = pypirc.get("server-login","username") + self.username = pypirc.get("server-login", "username") except missing_errs: pass # Prompt for username on command-line @@ -54,7 +53,7 @@ class PyPI(Publisher): if self.password is None: if pypirc is not None: try: - self.password = pypirc.get("server-login","password") + self.password = pypirc.get("server-login", "password") except missing_errs: pass # Prompt for username on command-line @@ -73,11 +72,11 @@ class PyPI(Publisher): # Bundle it up into a zipfile logger.info("building the zipfile") root = self.site.config.deploy_root_path - zf = zipfile.ZipFile(tf,"w",zipfile.ZIP_DEFLATED) + zf = zipfile.ZipFile(tf, "w", zipfile.ZIP_DEFLATED) try: 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: zf.close() # Formulate the necessary bits for the HTTP POST. @@ -85,12 +84,14 @@ class PyPI(Publisher): authz = self.username + ":" + self.password authz = "Basic " + standard_b64encode(authz) 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,) - items = ((":action","doc_upload"),("name",self.project)) + items = ((":action", "doc_upload"), ("name", self.project)) body_prefix = "" - for (name,value) in items: + for (name, value) in items: body_prefix += "--" + boundary + "\r\n" body_prefix += "Content-Disposition: form-data; name=\"" body_prefix += name + "\"\r\n\r\n" @@ -110,24 +111,24 @@ class PyPI(Publisher): con.connect() try: 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.send(body_prefix) tf.seek(0) - data = tf.read(1024*32) + data = tf.read(1024 * 32) while data: con.send(data) - data = tf.read(1024*32) + data = tf.read(1024 * 32) con.send(body_suffix) r = con.getresponse() try: # PyPI tries to redirect to the page on success. - if r.status in (200,301,): + if r.status in (200, 301,): logger.info("success!") else: - msg = "Upload failed: %s %s" % (r.status,r.reason,) + msg = "Upload failed: %s %s" % (r.status, r.reason,) raise Exception(msg) finally: r.close() @@ -135,5 +136,3 @@ class PyPI(Publisher): con.close() finally: tf.close() - - diff --git a/hyde/ext/publishers/ssh.py b/hyde/ext/publishers/ssh.py index 160a624..b41a167 100644 --- a/hyde/ext/publishers/ssh.py +++ b/hyde/ext/publishers/ssh.py @@ -34,7 +34,9 @@ from hyde.publisher import Publisher from subprocess import Popen, PIPE + class SSH(Publisher): + def initialize(self, settings): self.settings = settings self.username = settings.username @@ -47,7 +49,7 @@ class SSH(Publisher): command = "{command} {opts} ./ {username}{server}:{target}".format( command=self.command, opts=self.opts, - username=self.username+'@' if self.username else '', + username=self.username + '@' if self.username else '', server=self.server, target=self.target) deploy_path = self.site.config.deploy_root_path.path diff --git a/hyde/ext/templates/jinja.py b/hyde/ext/templates/jinja.py index 9ec2b58..9dcee9f 100644 --- a/hyde/ext/templates/jinja.py +++ b/hyde/ext/templates/jinja.py @@ -29,10 +29,13 @@ from commando.util import getLoggerWithNullHandler logger = getLoggerWithNullHandler('hyde.engine.Jinja2') + class SilentUndefined(Undefined): + """ A redefinition of undefined that eats errors. """ + def __getattr__(self, name): return self @@ -41,6 +44,7 @@ class SilentUndefined(Undefined): def __call__(self, *args, **kwargs): return self + @contextfunction 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) + @contextfunction 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) + @contextfunction 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) + @contextfilter def urlencode(ctx, url, safe=None): if safe is not None: @@ -69,16 +76,18 @@ def urlencode(ctx, url, safe=None): else: return quote(url.encode('utf8')) + @contextfilter def urldecode(ctx, url): return unquote(url).decode('utf8') + @contextfilter def date_format(ctx, dt, fmt=None): if not dt: dt = datetime.now() if not isinstance(dt, datetime) or \ - not isinstance(dt, date): + not isinstance(dt, date): logger.error("Date format called on a non date object") return dt @@ -93,9 +102,11 @@ def date_format(ctx, dt, fmt=None): def islice(iterable, start=0, stop=3, step=1): return itertools.islice(iterable, start, stop, step) + def top(iterable, count=3): return islice(iterable, stop=count) + def xmldatetime(dt): if not dt: dt = datetime.now() @@ -105,6 +116,7 @@ def xmldatetime(dt): zprefix = tz[:3] + ":" + tz[3:] return dt.strftime("%Y-%m-%dT%H:%M:%S") + zprefix + @environmentfilter def asciidoc(env, value): """ @@ -122,9 +134,11 @@ def asciidoc(env, value): asciidoc = AsciiDocAPI() asciidoc.options('--no-header-footer') 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") + @environmentfilter def markdown(env, value): """ @@ -140,14 +154,15 @@ def markdown(env, value): if hasattr(env.config, 'markdown'): d['extensions'] = getattr(env.config.markdown, 'extensions', []) d['extension_configs'] = getattr(env.config.markdown, - 'extension_configs', - Expando({})).to_dict() + 'extension_configs', + Expando({})).to_dict() if hasattr(env.config.markdown, 'output_format'): d['output_format'] = env.config.markdown.output_format marked = md.Markdown(**d) return marked.convert(output) + @environmentfilter def restructuredtext(env, value): """ @@ -161,18 +176,20 @@ def restructuredtext(env, value): highlight_source = False 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', []) import imp for extension in extensions: imp.load_module(extension, *imp.find_module(extension)) if highlight_source: - import hyde.lib.pygments.rst_directive + import hyde.lib.pygments.rst_directive # noqa parts = publish_parts(source=value, writer_name="html") return parts['html_body'] + @environmentfilter def syntax(env, value, lexer=None, filename=None): """ @@ -184,17 +201,17 @@ def syntax(env, value, lexer=None, filename=None): from pygments import formatters except ImportError: logger.error(u"pygments library is required to" - " use syntax highlighting tags.") + " use syntax highlighting tags.") raise TemplateError("Cannot load pygments") pyg = (lexers.get_lexer_by_name(lexer) - if lexer else - lexers.guess_lexer(value)) + if lexer else + lexers.guess_lexer(value)) settings = {} if hasattr(env.config, 'syntax'): settings = getattr(env.config.syntax, - 'options', - Expando({})).to_dict() + 'options', + Expando({})).to_dict() formatter = formatters.HtmlFormatter(**settings) 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): return Markup(code) return Markup( - '
def add(a, b):
- return a + b
+def """
+ """add("""
+ """a"""
+ """, b"""
+ """):
+ return a """
+ """+ b
See Example
def add(a, b): - return a + b +def """ + """add(""" + """a, """ + """b): + return a +""" + """ bSee Example
- """ + """) t = Jinja2Template(JINJA2.path) s = Site(JINJA2.path) c = Config(JINJA2.path, config_dict=dict( - markdown=dict(extensions=['codehilite']))) + markdown=dict(extensions=['codehilite']))) s.config = c t.configure(s) html = t.render(source, {}).strip() @@ -265,13 +287,15 @@ def test_line_statements(): """ t = Jinja2Template(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 t.configure(s) t.env.filters['dateformat'] = dateformat html = t.render(source, {}).strip() assert html == u'Heading 3
' + def test_line_statements_with_config(): source = """ %% markdown @@ -298,6 +322,7 @@ def test_line_statements_with_config(): TEST_SITE = File(__file__).parent.child_folder('_test') + @nottest def assert_markdown_typogrify_processed_well(include_text, includer_text): site = Site(TEST_SITE) @@ -317,11 +342,13 @@ def assert_markdown_typogrify_processed_well(include_text, includer_text): assert q(".amp").length == 1 return html + class TestJinjaTemplate(object): def setUp(self): 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): TEST_SITE.delete() @@ -375,7 +402,6 @@ class TestJinjaTemplate(object): assert article.length == 1 assert article.text() == "Heya" - def test_depends_with_reference_tag(self): site = Site(TEST_SITE) JINJA2.copy_contents_to(site.content.source) @@ -408,7 +434,6 @@ class TestJinjaTemplate(object): assert not deps[0] - def test_can_include_templates_with_processing(self): text = """ === @@ -424,11 +449,9 @@ Hyde & Jinja. {% endmarkdown %}{% endfilter %} """ - text2 = """{% include "inc.md" %}""" assert_markdown_typogrify_processed_well(text, text2) - def test_includetext(self): text = """ === @@ -595,7 +618,6 @@ Hyde & Jinja. assert "mark" not in html assert "reference" not in html - def test_refer_with_var(self): text = """ === @@ -623,7 +645,6 @@ Hyde & Jinja. assert "mark" not in html assert "reference" not in html - def test_yaml_tag(self): text = """ @@ -728,20 +749,22 @@ item_list: """ - text = "{%% filter markdown|typogrify %%}{%% raw %%}%s{%% endraw %%}{%% endfilter %%}" % expected + text = """{%% filter markdown|typogrify %%}{%% raw + %%}%s{%% endraw %%}{%% endfilter %%}""" % expected t = Jinja2Template(JINJA2.path) t.configure(None) html = t.render(text, {}).strip() assert html.strip() == expected.strip() def test_urlencode_filter(self): - text= u""" -фотография -quoted + text = u""" +фотографияquoted """ expected = u""" -фотография -quoted +фотографияquoted """ t = Jinja2Template(JINJA2.path) t.configure(None) @@ -749,13 +772,14 @@ item_list: assert html.strip() == expected.strip() def test_urldecode_filter(self): - text= u""" -{{ "%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%8F.jpg"|urldecode }} -""" - expected = u""" -фотография.jpg + text = u""" +{{ +"%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%8F.jpg"|urldecode +}} """ + expected = (u'фотография.jpg') t = Jinja2Template(JINJA2.path) t.configure(None) html = t.render(text, {}).strip() - assert html.strip() == expected.strip() \ No newline at end of file + assert html.strip() == expected.strip() diff --git a/hyde/tests/test_layout.py b/hyde/tests/test_layout.py index 1899ab9..ad92fa2 100644 --- a/hyde/tests/test_layout.py +++ b/hyde/tests/test_layout.py @@ -14,19 +14,23 @@ from nose.tools import nottest, with_setup DATA_ROOT = File(__file__).parent.child_folder('data') LAYOUT_ROOT = DATA_ROOT.child_folder(LAYOUTS) + @nottest def setup_data(): DATA_ROOT.make() + @nottest def cleanup_data(): DATA_ROOT.delete() + def test_find_layout_from_package_dir(): f = Layout.find_layout() assert f.name == 'basic' assert f.child_folder('layout').exists + @with_setup(setup_data, cleanup_data) def test_find_layout_from_env_var(): f = Layout.find_layout() diff --git a/hyde/tests/test_model.py b/hyde/tests/test_model.py index 831dcc8..f05ae53 100644 --- a/hyde/tests/test_model.py +++ b/hyde/tests/test_model.py @@ -8,18 +8,21 @@ from hyde.model import Config, Expando from fswrap import File, Folder + def test_expando_one_level(): d = {"a": 123, "b": "abc"} x = Expando(d) assert x.a == d['a'] assert x.b == d['b'] + def test_expando_two_levels(): d = {"a": 123, "b": {"c": 456}} x = Expando(d) assert x.a == d['a'] assert x.b.c == d['b']['c'] + def test_expando_three_levels(): d = {"a": 123, "b": {"c": 456, "d": {"e": "abc"}}} x = Expando(d) @@ -27,6 +30,7 @@ def test_expando_three_levels(): assert x.b.c == d['b']['c'] assert x.b.d.e == d['b']['d']['e'] + def test_expando_update(): d1 = {"a": 123, "b": "abc"} x = Expando(d1) @@ -34,7 +38,7 @@ def test_expando_update(): assert x.b == d1['b'] d = {"b": {"c": 456, "d": {"e": "abc"}}, "f": "lmn"} x.update(d) - assert x.a == d1['a'] + assert x.a == d1['a'] assert x.b.c == d['b']['c'] assert x.b.d.e == d['b']['d']['e'] assert x.f == d["f"] @@ -44,11 +48,13 @@ def test_expando_update(): assert x.a == 789 assert x.f == "opq" + def test_expando_to_dict(): d = {"a": 123, "b": {"c": 456, "d": {"e": "abc"}}} x = Expando(d) assert d == x.to_dict() + def test_expando_to_dict_with_update(): d1 = {"a": 123, "b": "abc"} x = Expando(d1) @@ -67,6 +73,8 @@ def test_expando_to_dict_with_update(): TEST_SITE = File(__file__).parent.child_folder('_test') import yaml + + class TestConfig(object): @classmethod @@ -94,7 +102,8 @@ class TestConfig(object): def setUp(self): 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): TEST_SITE.delete() diff --git a/hyde/tests/test_plugin.py b/hyde/tests/test_plugin.py index 05557c0..9517b0d 100644 --- a/hyde/tests/test_plugin.py +++ b/hyde/tests/test_plugin.py @@ -16,15 +16,18 @@ from fswrap import File, Folder TEST_SITE = File(__file__).parent.child_folder('_test') + class PluginLoaderStub(Plugin): pass + class NoReturnPlugin(Plugin): def begin_text_resource(self, resource, text): print "NoReturnPlugin" return None + class ConstantReturnPlugin(Plugin): def begin_text_resource(self, resource, text): @@ -37,7 +40,8 @@ class TestPlugins(object): @classmethod def setup_class(cls): 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 = [] text_files = [] binary_files = [] @@ -58,14 +62,13 @@ class TestPlugins(object): cls.content_text_resources = sorted(text_files) cls.content_binary_resources = sorted(binary_files) - @classmethod def teardown_class(cls): TEST_SITE.delete() 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): assert not len(self.site.plugins) @@ -74,25 +77,27 @@ class TestPlugins(object): assert len(self.site.plugins) == 1 assert self.site.plugins[0].__class__.__name__ == 'PluginLoaderStub' - def test_generator_loads_plugins(self): - gen = Generator(self.site) + Generator(self.site) assert len(self.site.plugins) == 1 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.generate_all() assert template_loaded_stub.call_count == 1 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.generate_all() 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) path = self.site.content.source_folder.child('about.html') gen.generate_resource_at_path(path) @@ -100,29 +105,32 @@ class TestPlugins(object): assert begin_generation_stub.call_count == 1 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) path = self.site.content.source_folder gen.generate_node_at_path(path) assert begin_generation_stub.call_count == 1 - 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.generate_all() 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) path = self.site.content.source_folder.child('about.html') gen.generate_resource_at_path(path) 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) path = self.site.content.source_folder gen.generate_node_at_path(path) @@ -141,7 +149,7 @@ class TestPlugins(object): gen.generate_resource_at_path(path) 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: gen = Generator(self.site) gen.generate_all() @@ -158,7 +166,7 @@ class TestPlugins(object): 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: gen = Generator(self.site) gen.generate_all() @@ -169,24 +177,26 @@ class TestPlugins(object): assert begin_site_stub.call_count == 1 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.generate_all() assert site_complete_stub.call_count == 1 - 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) path = self.site.content.source_folder.child('about.html') gen.generate_resource_at_path(path) 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.generate_all() 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): - with patch.object(PluginLoaderStub, 'site_complete') as site_complete_stub: + with patch.object(PluginLoaderStub, + 'site_complete') as site_complete_stub: gen = Generator(self.site) path = self.site.content.source_folder gen.generate_node_at_path(path) 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.generate_all() path = self.site.content.source_folder @@ -221,67 +233,81 @@ class TestPlugins(object): gen.generate_all() 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 def test_generator_template_begin_node_called_for_single_resource(self): with patch.object(PluginLoaderStub, 'begin_node') as begin_node_stub: 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) - - 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: gen = Generator(self.site) gen.generate_all() 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): - 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.generate_all() 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 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.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) - 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.generate_all() 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): - 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.return_value = '' gen = Generator(self.site) 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 = '' gen = Generator(self.site) gen.generate_all() @@ -290,81 +316,105 @@ class TestPlugins(object): gen = Generator(self.site) 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 called_with_resources[0] == path 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.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 - 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.generate_all() begin_binary_resource_stub.reset_mock() path = self.site.content.source_folder.child('favicon.ico') 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 called_with_resources[0] == path def test_plugin_chaining(self): - self.site.config.plugins = [ + self.site.config.plugins = [ 'hyde.tests.test_plugin.ConstantReturnPlugin', '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 empty_return(self, resource, 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 = [ 'hyde.tests.test_plugin.ConstantReturnPlugin', '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.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(mock2_args) == 4 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 empty_return(*args, **kwargs): 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 = [ 'hyde.tests.test_plugin.ConstantReturnPlugin', '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.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(mock2_args) == 1 assert mock1_args == ["site.css"] - assert mock2_args == ["merry-christmas.html"] \ No newline at end of file + assert mock2_args == ["merry-christmas.html"] diff --git a/hyde/tests/test_simple_copy.py b/hyde/tests/test_simple_copy.py index b773db3..0930dd4 100644 --- a/hyde/tests/test_simple_copy.py +++ b/hyde/tests/test_simple_copy.py @@ -29,10 +29,13 @@ from nose.tools import nottest TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') + class TestSimpleCopy(object): + @classmethod 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() 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') assert res 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.simple_copy @@ -81,7 +85,8 @@ class TestSimpleCopy(object): res = s.content.resource_from_relative_path('about.html') assert res 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.simple_copy 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) g = Generator(s) 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() right = target.read_all() assert left == right @@ -129,11 +136,13 @@ twitter: @me ]) conf = {'plugins': ['hyde.ext.plugins.meta.MetaPlugin']} 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.generate_all() 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() right = target.read_all() - assert left == right \ No newline at end of file + assert left == right diff --git a/hyde/tests/test_site.py b/hyde/tests/test_site.py index 6845703..1aef6f3 100644 --- a/hyde/tests/test_site.py +++ b/hyde/tests/test_site.py @@ -14,6 +14,7 @@ from fswrap import File, Folder TEST_SITE_ROOT = File(__file__).parent.child_folder('sites/test_jinja') + def test_node_site(): s = Site(TEST_SITE_ROOT) 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) assert n.site == s + def test_node_root(): s = Site(TEST_SITE_ROOT) 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) assert n.root == r + def test_node_parent(): s = Site(TEST_SITE_ROOT) r = RootNode(TEST_SITE_ROOT.child_folder('content'), s) c = r.add_node(TEST_SITE_ROOT.child_folder('content/blog/2010/december')) assert c.parent == r.node_from_relative_path('blog/2010') + def test_node_module(): s = Site(TEST_SITE_ROOT) 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')) assert c.module == n + def test_node_url(): s = Site(TEST_SITE_ROOT) 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 == '/blog/2010/december' + def test_node_full_url(): s = Site(TEST_SITE_ROOT) 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')) assert c.full_url == 'http://localhost/blog/2010/december' + def test_node_full_url_quoted(): s = Site(TEST_SITE_ROOT) 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')) assert c.full_url == 'http://localhost/' + quote('blo~g/2010/december') + def test_node_relative_path(): s = Site(TEST_SITE_ROOT) 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')) assert c.relative_path == 'blog/2010/december' + def test_load(): s = Site(TEST_SITE_ROOT) s.load() @@ -96,6 +105,7 @@ def test_load(): assert resource.relative_path == path assert not s.content.resource_from_relative_path('/happy-festivus.html') + def test_walk_resources(): s = Site(TEST_SITE_ROOT) s.load() @@ -113,6 +123,7 @@ def test_walk_resources(): expected.sort() assert pages == expected + def test_contains_resource(): s = Site(TEST_SITE_ROOT) s.load() @@ -120,13 +131,16 @@ def test_contains_resource(): node = s.content.node_from_relative_path(path) assert node.contains_resource('merry-christmas.html') + def test_get_resource(): s = Site(TEST_SITE_ROOT) s.load() path = 'blog/2010/december' node = s.content.node_from_relative_path(path) 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(): s = Site(TEST_SITE_ROOT) @@ -143,9 +157,12 @@ def test_get_resource_from_relative_deploy_path(): path = 'blog/2010/december' node = s.content.node_from_relative_path(path) 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') - 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(): s = Site(TEST_SITE_ROOT) @@ -153,6 +170,7 @@ def test_is_processable_default_true(): for page in s.content.walk_resources(): assert page.is_processable + def test_relative_deploy_path(): s = Site(TEST_SITE_ROOT) s.load() @@ -160,28 +178,35 @@ def test_relative_deploy_path(): assert page.relative_deploy_path == Folder(page.relative_path) assert page.url == '/' + page.relative_deploy_path + def test_relative_deploy_path_override(): s = Site(TEST_SITE_ROOT) 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' for page in s.content.walk_resources(): 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: assert page.relative_deploy_path == Folder(page.relative_path) + class TestSiteWithConfig(object): @classmethod 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() TEST_SITE_ROOT.copy_contents_to(cls.SITE_PATH) cls.config_file = File(cls.SITE_PATH.child('alternate.yaml')) 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 def teardown_class(cls): @@ -198,7 +223,8 @@ class TestSiteWithConfig(object): resource = s.content.resource_from_relative_path(path) assert resource 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): s = Site(self.SITE_PATH, config=self.config) @@ -217,7 +243,7 @@ class TestSiteWithConfig(object): s.load() path = '".jpg/abc' print s.content_url(path, "") - print "/" + quote(path, "") + print "/" + quote(path, "") assert s.content_url(path, "") == "/" + quote(path, "") def test_media_url(self): @@ -254,7 +280,7 @@ class TestSiteWithConfig(object): s.load() path = 'css/site.css' resource = s.content.resource_from_relative_path( - Folder("media").child(path)) + Folder("media").child(path)) assert resource assert resource.full_url == "/media/" + path @@ -265,7 +291,7 @@ class TestSiteWithConfig(object): path = 'apple-touch-icon.png' resource = s.content.resource_from_relative_path(path) assert resource - assert resource.full_url == "/" + path + assert resource.full_url == "/" + path s = Site(self.SITE_PATH, config=c) s.config.ignore.append('*.png') resource = s.content.resource_from_relative_path(path) @@ -281,10 +307,10 @@ class TestSiteWithConfig(object): assert not git_node blog_node = s.content.node_from_relative_path('blog') assert blog_node - assert blog_node.full_url == "/blog" + assert blog_node.full_url == "/blog" s = Site(self.SITE_PATH, config=c) s.config.ignore.append('blog') blog_node = s.content.node_from_relative_path('blog') assert not blog_node git_node = s.content.node_from_relative_path('.git') - assert not git_node \ No newline at end of file + assert not git_node diff --git a/hyde/tests/util.py b/hyde/tests/util.py index 563babf..3443a57 100644 --- a/hyde/tests/util.py +++ b/hyde/tests/util.py @@ -1,6 +1,7 @@ import re import difflib + def strip_spaces_between_tags(value): """ Stolen from `django.util.html` @@ -8,10 +9,11 @@ def strip_spaces_between_tags(value): """ return re.sub(r'>\s+<', '><', unicode(value)) + def assert_no_diff(expected, out): 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) @@ -23,6 +25,7 @@ def assert_html_equals(expected, actual, sanitize=None): actual = sanitize(actual) assert expected == actual + def trap_exit_fail(f): def test_wrapper(*args): try: @@ -32,6 +35,7 @@ def trap_exit_fail(f): test_wrapper.__name__ = f.__name__ return test_wrapper + def trap_exit_pass(f): def test_wrapper(*args): try: @@ -39,4 +43,4 @@ def trap_exit_pass(f): except SystemExit: pass test_wrapper.__name__ = f.__name__ - return test_wrapper \ No newline at end of file + return test_wrapper diff --git a/hyde/util.py b/hyde/util.py index 2f61830..c9ea268 100644 --- a/hyde/util.py +++ b/hyde/util.py @@ -54,4 +54,4 @@ def discover_executable(name, sitepath): full_name = os.path.join(path, name) if os.path.exists(full_name): return full_name - return None \ No newline at end of file + return None diff --git a/setup.py b/setup.py index b08329c..6021076 100644 --- a/setup.py +++ b/setup.py @@ -132,7 +132,8 @@ setup(name=PROJECT, 'pyquery==1.2.9', 'docutils==0.12', 'Pillow==2.7.0', - 'pyScss==1.3.4' + 'pyScss==1.3.4', + 'flake8==2.4.1' ), test_suite='nose.collector', include_package_data = True,