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( - '
\n\n' - % (code, caption)) + '\n\n' + % (code, caption)) + class Spaceless(Extension): + """ Emulates the django spaceless template tag. """ @@ -220,10 +240,10 @@ class Spaceless(Extension): """ lineno = parser.stream.next().lineno body = parser.parse_statements(['name:endspaceless'], - drop_needle=True) + drop_needle=True) return nodes.CallBlock( - self.call_method('_render_spaceless'), - [], [], body).set_lineno(lineno) + self.call_method('_render_spaceless'), + [], [], body).set_lineno(lineno) def _render_spaceless(self, caller=None): """ @@ -235,7 +255,9 @@ class Spaceless(Extension): return '' return re.sub(r'>\s+<', '><', unicode(caller().strip())) + class Asciidoc(Extension): + """ A wrapper around the asciidoc filter for syntactic sugar. """ @@ -243,14 +265,15 @@ class Asciidoc(Extension): def parse(self, parser): """ - Parses the statements and defers to the callback for asciidoc processing. + Parses the statements and defers to the callback + for asciidoc processing. """ lineno = parser.stream.next().lineno body = parser.parse_statements(['name:endasciidoc'], drop_needle=True) return nodes.CallBlock( - self.call_method('_render_asciidoc'), - [], [], body).set_lineno(lineno) + self.call_method('_render_asciidoc'), + [], [], body).set_lineno(lineno) def _render_asciidoc(self, caller=None): """ @@ -261,7 +284,9 @@ class Asciidoc(Extension): output = caller().strip() return asciidoc(self.environment, output) + class Markdown(Extension): + """ A wrapper around the markdown filter for syntactic sugar. """ @@ -269,14 +294,15 @@ class Markdown(Extension): def parse(self, parser): """ - Parses the statements and defers to the callback for markdown processing. + Parses the statements and defers to the callback + for markdown processing. """ lineno = parser.stream.next().lineno body = parser.parse_statements(['name:endmarkdown'], drop_needle=True) return nodes.CallBlock( - self.call_method('_render_markdown'), - [], [], body).set_lineno(lineno) + self.call_method('_render_markdown'), + [], [], body).set_lineno(lineno) def _render_markdown(self, caller=None): """ @@ -287,7 +313,9 @@ class Markdown(Extension): output = caller().strip() return markdown(self.environment, output) + class restructuredText(Extension): + """ A wrapper around the restructuredtext filter for syntactic sugar """ @@ -298,10 +326,11 @@ class restructuredText(Extension): Simply extract our content """ lineno = parser.stream.next().lineno - body = parser.parse_statements(['name:endrestructuredtext'], drop_needle=True) + body = parser.parse_statements( + ['name:endrestructuredtext'], drop_needle=True) return nodes.CallBlock(self.call_method('_render_rst'), [], [], body - ).set_lineno(lineno) + ).set_lineno(lineno) def _render_rst(self, caller=None): """ @@ -312,7 +341,9 @@ class restructuredText(Extension): output = caller().strip() return restructuredtext(self.environment, output) + class YamlVar(Extension): + """ An extension that converts the content between the tags into an yaml object and sets the value in the given @@ -330,16 +361,15 @@ class YamlVar(Extension): var = parser.stream.expect('name').value body = parser.parse_statements(['name:endyaml'], drop_needle=True) return [ - nodes.Assign( - nodes.Name(var, 'store'), - nodes.Const({}) - ).set_lineno(lineno), - nodes.CallBlock( - self.call_method('_set_yaml', - args=[nodes.Name(var, 'load')]), - [], [], body).set_lineno(lineno) - ] - + nodes.Assign( + nodes.Name(var, 'store'), + nodes.Const({}) + ).set_lineno(lineno), + nodes.CallBlock( + self.call_method('_set_yaml', + args=[nodes.Name(var, 'load')]), + [], [], body).set_lineno(lineno) + ] def _set_yaml(self, var, caller=None): """ @@ -356,6 +386,7 @@ class YamlVar(Extension): var.update(yaml.load(out)) return '' + def parse_kwargs(parser): """ Parses keyword arguments in tags. @@ -368,17 +399,19 @@ def parse_kwargs(parser): value = nodes.Const(parser.stream.next().value) return (name, value) + class Syntax(Extension): + """ A wrapper around the syntax filter for syntactic sugar. """ tags = set(['syntax']) - def parse(self, parser): """ - Parses the statements and defers to the callback for pygments processing. + Parses the statements and defers to the callback for + pygments processing. """ lineno = parser.stream.next().lineno lex = nodes.Const(None) @@ -392,8 +425,8 @@ class Syntax(Extension): (_, value1) = parse_kwargs(parser) (lex, filename) = (value, value1) \ - if name == 'lex' \ - else (value1, value) + if name == 'lex' \ + else (value1, value) else: lex = nodes.Const(parser.stream.next().value) if parser.stream.skip_if('comma'): @@ -401,10 +434,9 @@ class Syntax(Extension): body = parser.parse_statements(['name:endsyntax'], drop_needle=True) return nodes.CallBlock( - self.call_method('_render_syntax', - args=[lex, filename]), - [], [], body).set_lineno(lineno) - + self.call_method('_render_syntax', + args=[lex, filename]), + [], [], body).set_lineno(lineno) def _render_syntax(self, lex, filename, caller=None): """ @@ -415,7 +447,9 @@ class Syntax(Extension): output = caller().strip() return syntax(self.environment, output, lex, filename) + class IncludeText(Extension): + """ Automatically runs `markdown` and `typogrify` on included files. @@ -429,8 +463,8 @@ class IncludeText(Extension): """ node = parser.parse_include() return nodes.CallBlock( - self.call_method('_render_include_text'), - [], [], [node]).set_lineno(node.lineno) + self.call_method('_render_include_text'), + [], [], [node]).set_lineno(node.lineno) def _render_include_text(self, caller=None): """ @@ -448,7 +482,9 @@ class IncludeText(Extension): MARKINGS = '_markings_' + class Reference(Extension): + """ Marks a block in a template such that its available for use when referenced using a `refer` tag. @@ -465,11 +501,13 @@ class Reference(Extension): tag = token.value name = parser.stream.next().value body = parser.parse_statements(['name:end%s' % tag], drop_needle=True) - return nodes.CallBlock( - self.call_method('_render_output', - args=[nodes.Name(MARKINGS, 'load'), nodes.Const(name)]), - [], [], body).set_lineno(lineno) - + return nodes.CallBlock(self.call_method('_render_output', + args=[ + nodes.Name(MARKINGS, + 'load'), + nodes.Const(name) + ]), [], [], + body).set_lineno(lineno) def _render_output(self, markings, name, caller=None): """ @@ -482,7 +520,9 @@ class Reference(Extension): markings[name] = out return out + class Refer(Extension): + """ Imports content blocks specified in the referred template as variables in a given namespace. @@ -507,43 +547,43 @@ class Refer(Extension): temp = parser.free_identifier(lineno) return [ - nodes.Assign( - nodes.Name(temp.name, 'store'), - nodes.Name(MARKINGS, 'load') - ).set_lineno(lineno), - nodes.Assign( - nodes.Name(MARKINGS, 'store'), - nodes.Const({})).set_lineno(lineno), - nodes.Assign( - nodes.Name(namespace, 'store'), - nodes.Const({})).set_lineno(lineno), - nodes.CallBlock( - self.call_method('_push_resource', - args=[ - nodes.Name(namespace, 'load'), - nodes.Name('site', 'load'), - nodes.Name('resource', 'load'), - template]), - [], [], []).set_lineno(lineno), - nodes.Assign( - nodes.Name('resource', 'store'), - nodes.Getitem(nodes.Name(namespace, 'load'), - nodes.Const('resource'), 'load') - ).set_lineno(lineno), - nodes.CallBlock( - self.call_method('_assign_reference', - args=[ - nodes.Name(MARKINGS, 'load'), - nodes.Name(namespace, 'load')]), - [], [], [includeNode]).set_lineno(lineno), - nodes.Assign(nodes.Name('resource', 'store'), - nodes.Getitem(nodes.Name(namespace, 'load'), - nodes.Const('parent_resource'), 'load') - ).set_lineno(lineno), - nodes.Assign( - nodes.Name(MARKINGS, 'store'), - nodes.Name(temp.name, 'load') - ).set_lineno(lineno), + nodes.Assign( + nodes.Name(temp.name, 'store'), + nodes.Name(MARKINGS, 'load') + ).set_lineno(lineno), + nodes.Assign( + nodes.Name(MARKINGS, 'store'), + nodes.Const({})).set_lineno(lineno), + nodes.Assign( + nodes.Name(namespace, 'store'), + nodes.Const({})).set_lineno(lineno), + nodes.CallBlock( + self.call_method('_push_resource', + args=[ + nodes.Name(namespace, 'load'), + nodes.Name('site', 'load'), + nodes.Name('resource', 'load'), + template]), + [], [], []).set_lineno(lineno), + nodes.Assign( + nodes.Name('resource', 'store'), + nodes.Getitem(nodes.Name(namespace, 'load'), + nodes.Const('resource'), 'load') + ).set_lineno(lineno), + nodes.CallBlock( + self.call_method('_assign_reference', + args=[ + nodes.Name(MARKINGS, 'load'), + nodes.Name(namespace, 'load')]), + [], [], [includeNode]).set_lineno(lineno), + nodes.Assign(nodes.Name('resource', 'store'), + nodes.Getitem(nodes.Name(namespace, 'load'), + nodes.Const('parent_resource'), 'load') + ).set_lineno(lineno), + nodes.Assign( + nodes.Name(MARKINGS, 'store'), + nodes.Name(temp.name, 'load') + ).set_lineno(lineno), ] def _push_resource(self, namespace, site, resource, template, caller): @@ -553,10 +593,10 @@ class Refer(Extension): namespace['parent_resource'] = resource if not hasattr(resource, 'depends'): resource.depends = [] - if not template in resource.depends: + if template not in resource.depends: resource.depends.append(template) namespace['resource'] = site.content.resource_from_relative_path( - template) + template) return '' def _assign_reference(self, markings, namespace, caller): @@ -573,6 +613,7 @@ class Refer(Extension): class HydeLoader(FileSystemLoader): + """ A wrapper around the file system loader that performs hyde specific tweaks. @@ -582,9 +623,9 @@ class HydeLoader(FileSystemLoader): config = site.config if hasattr(site, 'config') else None if config: super(HydeLoader, self).__init__([ - unicode(config.content_root_path), - unicode(config.layout_root_path), - ]) + unicode(config.content_root_path), + unicode(config.layout_root_path), + ]) else: super(HydeLoader, self).__init__(unicode(sitepath)) @@ -604,8 +645,8 @@ class HydeLoader(FileSystemLoader): try: (contents, filename, - date) = super(HydeLoader, self).get_source( - environment, template) + date) = super(HydeLoader, self).get_source( + environment, template) except UnicodeDecodeError: HydeException.reraise( "Unicode error when processing %s" % template, sys.exc_info()) @@ -624,6 +665,7 @@ class HydeLoader(FileSystemLoader): # pylint: disable-msg=W0104,E0602,W0613,R0201 class Jinja2Template(Template): + """ The Jinja2 Template implementation """ @@ -638,23 +680,23 @@ class Jinja2Template(Template): self.site = site self.engine = engine self.preprocessor = (engine.preprocessor - if hasattr(engine, 'preprocessor') else None) + if hasattr(engine, 'preprocessor') else None) self.loader = HydeLoader(self.sitepath, site, self.preprocessor) default_extensions = [ - IncludeText, - Spaceless, - Asciidoc, - Markdown, - restructuredText, - Syntax, - Reference, - Refer, - YamlVar, - 'jinja2.ext.do', - 'jinja2.ext.loopcontrols', - 'jinja2.ext.with_' + IncludeText, + Spaceless, + Asciidoc, + Markdown, + restructuredText, + Syntax, + Reference, + Refer, + YamlVar, + 'jinja2.ext.do', + 'jinja2.ext.loopcontrols', + 'jinja2.ext.with_' ] defaults = { @@ -694,12 +736,12 @@ class Jinja2Template(Template): settings['filters'][name] = getattr(module, function_name) self.env = Environment( - loader=self.loader, - undefined=SilentUndefined, - line_statement_prefix=settings['line_statement_prefix'], - trim_blocks=True, - bytecode_cache=FileSystemBytecodeCache(), - extensions=settings['extensions']) + loader=self.loader, + undefined=SilentUndefined, + line_statement_prefix=settings['line_statement_prefix'], + trim_blocks=True, + bytecode_cache=FileSystemBytecodeCache(), + extensions=settings['extensions']) self.env.globals['media_url'] = media_url self.env.globals['content_url'] = content_url self.env.globals['full_url'] = full_url @@ -738,7 +780,6 @@ class Jinja2Template(Template): if self.env.bytecode_cache: self.env.bytecode_cache.clear() - def get_dependencies(self, path): """ Finds dependencies hierarchically based on the included @@ -774,10 +815,12 @@ class Jinja2Template(Template): The pattern for matching selected template statements. """ return { - "block_open": '\s*\{\%\s*block\s*([^\s]+)\s*\%\}', - "block_close": '\s*\{\%\s*endblock\s*([^\s]*)\s*\%\}', - "include": '\s*\{\%\s*include\s*(?:\'|\")(.+?\.[^.]*)(?:\'|\")\s*\%\}', - "extends": '\s*\{\%\s*extends\s*(?:\'|\")(.+?\.[^.]*)(?:\'|\")\s*\%\}' + "block_open": '\s*\{\%\s*block\s*([^\s]+)\s*\%\}', + "block_close": '\s*\{\%\s*endblock\s*([^\s]*)\s*\%\}', + "include": + '\s*\{\%\s*include\s*(?:\'|\")(.+?\.[^.]*)(?:\'|\")\s*\%\}', + "extends": + '\s*\{\%\s*extends\s*(?:\'|\")(.+?\.[^.]*)(?:\'|\")\s*\%\}' } def get_include_statement(self, path_to_include): diff --git a/hyde/generator.py b/hyde/generator.py index c0142a8..8eaa16d 100644 --- a/hyde/generator.py +++ b/hyde/generator.py @@ -43,7 +43,6 @@ class Generator(object): site.context = Context.load(site.sitepath, site.config.context) self.__context__.update(site.context) - @contextmanager def context_for_resource(self, resource): """ @@ -77,7 +76,8 @@ class Generator(object): providing restricted access to the methods. """ - def __init__(self, preprocessor=None, postprocessor=None, context_for_path=None): + def __init__(self, preprocessor=None, postprocessor=None, + context_for_path=None): self.preprocessor = preprocessor self.postprocessor = postprocessor self.context_for_path = context_for_path @@ -86,14 +86,16 @@ class Generator(object): logger.info("Generating site at [%s]" % self.site.sitepath) self.template = Template.find_template(self.site) logger.debug("Using [%s] as the template", - self.template.__class__.__name__) + self.template.__class__.__name__) logger.info("Configuring the template environment") + preprocessor = self.events.begin_text_resource + postprocessor = self.events.text_resource_complete + proxy = GeneratorProxy(context_for_path=self.context_for_path, + preprocessor=preprocessor, + postprocessor=postprocessor) self.template.configure(self.site, - engine=GeneratorProxy( - context_for_path=self.context_for_path, - preprocessor=self.events.begin_text_resource, - postprocessor=self.events.text_resource_complete)) + engine=proxy) self.events.template_loaded(self.template) def initialize(self): @@ -124,7 +126,7 @@ class Generator(object): rel_path = resource.relative_path deps = self.deps[rel_path] if rel_path in self.deps \ - else self.update_deps(resource) + else self.update_deps(resource) return deps def update_deps(self, resource): @@ -143,7 +145,8 @@ class Generator(object): dep_res = self.site.content.resource_from_relative_path(dep) if dep_res: if dep_res.relative_path in self.waiting_deps.keys(): - self.waiting_deps[dep_res.relative_path].append(rel_path) + self.waiting_deps[ + dep_res.relative_path].append(rel_path) else: deps.extend(self.get_dependencies(dep_res)) if resource.uses_template: @@ -166,7 +169,7 @@ class Generator(object): self.load_site_if_needed() target = File(self.site.config.deploy_root_path.child( - resource.relative_deploy_path)) + resource.relative_deploy_path)) if not target.exists or target.older_than(resource.source_file): logger.debug("Found changes in %s" % resource) return True @@ -209,7 +212,7 @@ class Generator(object): self.load_site_if_needed() self.events.begin_site() logger.info("Generating site to [%s]" % - self.site.config.deploy_root_path) + self.site.config.deploy_root_path) self.__generate_node__(self.site.content, incremental) self.events.site_complete() self.finalize() @@ -261,9 +264,8 @@ class Generator(object): except HydeException: self.generate_all() - def generate_resource_at_path(self, - resource_path=None, - incremental=False): + def generate_resource_at_path(self, resource_path=None, + incremental=False): """ Generates a single resource. If resource_path is non-existent or empty, generates the entire website. @@ -311,7 +313,6 @@ class Generator(object): self.__generate_resource__(resource, incremental) self.events.node_complete(node) - def __generate_resource__(self, resource, incremental=False): self.refresh_config() if not resource.is_processable: @@ -323,7 +324,7 @@ class Generator(object): logger.debug("Processing [%s]", resource) with self.context_for_resource(resource) as context: target = File(self.site.config.deploy_root_path.child( - resource.relative_deploy_path)) + resource.relative_deploy_path)) target.parent.make() if resource.simple_copy: logger.debug("Simply Copying [%s]", resource) @@ -334,19 +335,19 @@ class Generator(object): logger.debug("Rendering [%s]", resource) try: text = self.template.render_resource(resource, - context) + context) except Exception, e: - HydeException.reraise("Error occurred when" - " processing template: [%s]: %s" % - (resource, repr(e)), - sys.exc_info() - ) + HydeException.reraise("Error occurred when processing" + "template: [%s]: %s" % + (resource, repr(e)), + sys.exc_info()) else: text = resource.source_file.read_all() - text = self.events.begin_text_resource(resource, text) or text + text = self.events.begin_text_resource( + resource, text) or text text = self.events.text_resource_complete( - resource, text) or text + resource, text) or text target.write(text) copymode(resource.source_file.path, target.path) else: diff --git a/hyde/layout.py b/hyde/layout.py index fd83a68..31defe1 100644 --- a/hyde/layout.py +++ b/hyde/layout.py @@ -11,6 +11,7 @@ LAYOUTS = "layouts" class Layout(object): + """ Represents a layout package """ @@ -26,10 +27,10 @@ class Layout(object): layout_folder = None if HYDE_DATA in os.environ: layout_folder = Layout._get_layout_folder( - os.environ[HYDE_DATA], layout_name) + os.environ[HYDE_DATA], layout_name) if not layout_folder: layout_folder = Layout._get_layout_folder( - File(__file__).parent, layout_name) + File(__file__).parent, layout_name) return layout_folder @staticmethod diff --git a/hyde/lib/pygments/rst_directive.py b/hyde/lib/pygments/rst_directive.py index 973398a..c157679 100644 --- a/hyde/lib/pygments/rst_directive.py +++ b/hyde/lib/pygments/rst_directive.py @@ -58,7 +58,9 @@ from docutils.parsers.rst import directives, Directive from pygments import highlight from pygments.lexers import get_lexer_by_name, TextLexer + class Pygments(Directive): + """ Source code syntax hightlighting. """ required_arguments = 1 @@ -75,7 +77,8 @@ class Pygments(Directive): # no lexer found - use the text one instead of an exception lexer = TextLexer() # take an arbitrary option if more than one is given - formatter = self.options and VARIANTS[self.options.keys()[0]] or DEFAULT + formatter = self.options and VARIANTS[ + self.options.keys()[0]] or DEFAULT parsed = highlight(u'\n'.join(self.content), lexer, formatter) return [nodes.raw('', parsed, format='html')] diff --git a/hyde/main.py b/hyde/main.py index 7ce4f36..6278484 100644 --- a/hyde/main.py +++ b/hyde/main.py @@ -5,9 +5,10 @@ The hyde executable """ from hyde.engine import Engine + def main(): """Main""" Engine().run() if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/hyde/model.py b/hyde/model.py index 43d026f..c1d91ac 100644 --- a/hyde/model.py +++ b/hyde/model.py @@ -14,6 +14,7 @@ logger = getLoggerWithNullHandler('hyde.engine') SEQS = (tuple, list, set, frozenset) + def make_expando(primitive): """ Creates an expando object, a sequence of expando objects or just @@ -29,6 +30,7 @@ def make_expando(primitive): class Expando(object): + """ A generic expando class that creates attributes from the passed in dictionary. @@ -63,7 +65,6 @@ class Expando(object): """ setattr(self, unicode(key).encode('utf-8'), make_expando(value)) - def __repr__(self): return unicode(self.to_dict()) @@ -79,9 +80,9 @@ class Expando(object): elif isinstance(v, SEQS): seq = type(v) result[k] = seq(item.to_dict() - if isinstance(item, Expando) - else item for item in v - ) + if isinstance(item, Expando) + else item for item in v + ) else: result[k] = v return result @@ -94,6 +95,7 @@ class Expando(object): class Context(object): + """ Wraps the context related functions and utilities. """ @@ -125,7 +127,9 @@ class Context(object): return context + class Dependents(IterableUserDict): + """ Represents the dependency graph for hyde. """ @@ -146,11 +150,14 @@ class Dependents(IterableUserDict): if self.deps_file.parent.exists: self.deps_file.write(yaml.dump(self.data)) + def _expand_path(sitepath, path): child = sitepath.child_folder(path) return Folder(child.fully_expanded_path) + class Config(Expando): + """ Represents the hyde configuration file """ @@ -158,7 +165,7 @@ class Config(Expando): def __init__(self, sitepath, config_file=None, config_dict=None): self.default_config = dict( mode='production', - simple_copy = [], + simple_copy=[], content_root='content', deploy_root='deploy', media_root='media', @@ -167,9 +174,9 @@ class Config(Expando): base_url="/", encode_safe=None, not_found='404.html', - plugins = [], - ignore = [ "*~", "*.bak", ".hg", ".git", ".svn"], - meta = { + plugins=[], + ignore=["*~", "*.bak", ".hg", ".git", ".svn"], + meta={ "nodemeta": 'meta.yaml' } ) @@ -188,7 +195,7 @@ class Config(Expando): if not self.config_files: return True return any((conf.has_changed_since(self.load_time) - for conf in self.config_files)) + for conf in self.config_files)) def load(self): conf = dict(**self.default_config) @@ -202,15 +209,14 @@ class Config(Expando): return self.update(self.load()) - def read_config(self, config_file): """ Reads the configuration file and updates this object while allowing for inherited configurations. """ conf_file = self.sitepath.child( - config_file if - config_file else 'site.yaml') + config_file if + config_file else 'site.yaml') conf = {} if File(conf_file).exists: self.config_files.append(File(conf_file)) @@ -224,7 +230,6 @@ class Config(Expando): self.load_time = datetime.now() return conf - @property def deploy_root_path(self): """ diff --git a/hyde/plugin.py b/hyde/plugin.py index d0839cb..37bc7ff 100644 --- a/hyde/plugin.py +++ b/hyde/plugin.py @@ -13,7 +13,6 @@ import os import re import subprocess import sys -import traceback from commando.util import getLoggerWithNullHandler, load_python_object from fswrap import File @@ -22,30 +21,53 @@ logger = getLoggerWithNullHandler('hyde.engine') # Plugins have been reorganized. Map old plugin paths to new. PLUGINS_OLD_AND_NEW = { - "hyde.ext.plugins.less.LessCSSPlugin" : "hyde.ext.plugins.css.LessCSSPlugin", - "hyde.ext.plugins.stylus.StylusPlugin" : "hyde.ext.plugins.css.StylusPlugin", - "hyde.ext.plugins.jpegoptim.JPEGOptimPlugin" : "hyde.ext.plugins.images.JPEGOptimPlugin", - "hyde.ext.plugins.optipng.OptiPNGPlugin" : "hyde.ext.plugins.images.OptiPNGPlugin", - "hyde.ext.plugins.jpegtran.JPEGTranPlugin" : "hyde.ext.plugins.images.JPEGTranPlugin", - "hyde.ext.plugins.uglify.UglifyPlugin": "hyde.ext.plugins.js.UglifyPlugin", - "hyde.ext.plugins.requirejs.RequireJSPlugin": "hyde.ext.plugins.js.RequireJSPlugin", - "hyde.ext.plugins.coffee.CoffeePlugin": "hyde.ext.plugins.js.CoffeePlugin", - "hyde.ext.plugins.sorter.SorterPlugin": "hyde.ext.plugins.meta.SorterPlugin", - "hyde.ext.plugins.grouper.GrouperPlugin": "hyde.ext.plugins.meta.GrouperPlugin", - "hyde.ext.plugins.tagger.TaggerPlugin": "hyde.ext.plugins.meta.TaggerPlugin", - "hyde.ext.plugins.auto_extend.AutoExtendPlugin": "hyde.ext.plugins.meta.AutoExtendPlugin", - "hyde.ext.plugins.folders.FlattenerPlugin": "hyde.ext.plugins.structure.FlattenerPlugin", - "hyde.ext.plugins.combine.CombinePlugin": "hyde.ext.plugins.structure.CombinePlugin", - "hyde.ext.plugins.paginator.PaginatorPlugin": "hyde.ext.plugins.structure.PaginatorPlugin", - "hyde.ext.plugins.blockdown.BlockdownPlugin": "hyde.ext.plugins.text.BlockdownPlugin", - "hyde.ext.plugins.markings.MarkingsPlugin": "hyde.ext.plugins.text.MarkingsPlugin", - "hyde.ext.plugins.markings.ReferencePlugin": "hyde.ext.plugins.text.ReferencePlugin", - "hyde.ext.plugins.syntext.SyntextPlugin": "hyde.ext.plugins.text.SyntextPlugin", - "hyde.ext.plugins.textlinks.TextlinksPlugin": "hyde.ext.plugins.text.TextlinksPlugin", - "hyde.ext.plugins.git.GitDatesPlugin": "hyde.ext.plugins.vcs.GitDatesPlugin" + "hyde.ext.plugins.less.LessCSSPlugin": + "hyde.ext.plugins.css.LessCSSPlugin", + "hyde.ext.plugins.stylus.StylusPlugin": + "hyde.ext.plugins.css.StylusPlugin", + "hyde.ext.plugins.jpegoptim.JPEGOptimPlugin": + "hyde.ext.plugins.images.JPEGOptimPlugin", + "hyde.ext.plugins.optipng.OptiPNGPlugin": + "hyde.ext.plugins.images.OptiPNGPlugin", + "hyde.ext.plugins.jpegtran.JPEGTranPlugin": + "hyde.ext.plugins.images.JPEGTranPlugin", + "hyde.ext.plugins.uglify.UglifyPlugin": + "hyde.ext.plugins.js.UglifyPlugin", + "hyde.ext.plugins.requirejs.RequireJSPlugin": + "hyde.ext.plugins.js.RequireJSPlugin", + "hyde.ext.plugins.coffee.CoffeePlugin": + "hyde.ext.plugins.js.CoffeePlugin", + "hyde.ext.plugins.sorter.SorterPlugin": + "hyde.ext.plugins.meta.SorterPlugin", + "hyde.ext.plugins.grouper.GrouperPlugin": + "hyde.ext.plugins.meta.GrouperPlugin", + "hyde.ext.plugins.tagger.TaggerPlugin": + "hyde.ext.plugins.meta.TaggerPlugin", + "hyde.ext.plugins.auto_extend.AutoExtendPlugin": + "hyde.ext.plugins.meta.AutoExtendPlugin", + "hyde.ext.plugins.folders.FlattenerPlugin": + "hyde.ext.plugins.structure.FlattenerPlugin", + "hyde.ext.plugins.combine.CombinePlugin": + "hyde.ext.plugins.structure.CombinePlugin", + "hyde.ext.plugins.paginator.PaginatorPlugin": + "hyde.ext.plugins.structure.PaginatorPlugin", + "hyde.ext.plugins.blockdown.BlockdownPlugin": + "hyde.ext.plugins.text.BlockdownPlugin", + "hyde.ext.plugins.markings.MarkingsPlugin": + "hyde.ext.plugins.text.MarkingsPlugin", + "hyde.ext.plugins.markings.ReferencePlugin": + "hyde.ext.plugins.text.ReferencePlugin", + "hyde.ext.plugins.syntext.SyntextPlugin": + "hyde.ext.plugins.text.SyntextPlugin", + "hyde.ext.plugins.textlinks.TextlinksPlugin": + "hyde.ext.plugins.text.TextlinksPlugin", + "hyde.ext.plugins.git.GitDatesPlugin": + "hyde.ext.plugins.vcs.GitDatesPlugin" } + class PluginProxy(object): + """ A proxy class to raise events in registered plugins """ @@ -61,7 +83,8 @@ class PluginProxy(object): if self.site.plugins: for plugin in self.site.plugins: if hasattr(plugin, method_name): - checker = getattr(plugin, 'should_call__' + method_name) + checker = getattr( + plugin, 'should_call__' + method_name) if checker(*args): function = getattr(plugin, method_name) try: @@ -80,9 +103,11 @@ class PluginProxy(object): return __call_plugins__ raise HydeException( - "Unknown plugin method [%s] called." % method_name) + "Unknown plugin method [%s] called." % method_name) + class Plugin(object): + """ The plugin protocol """ @@ -92,10 +117,9 @@ class Plugin(object): super(Plugin, self).__init__() self.site = site self.logger = getLoggerWithNullHandler( - 'hyde.engine.%s' % self.__class__.__name__) + 'hyde.engine.%s' % self.__class__.__name__) self.template = None - def template_loaded(self, template): """ Called when the template for the site has been identified. @@ -123,7 +147,8 @@ class Plugin(object): elif name.startswith('should_call__'): (_, _, method) = name.rpartition('__') if (method in ('begin_text_resource', 'text_resource_complete', - 'begin_binary_resource', 'binary_resource_complete')): + 'begin_binary_resource', + 'binary_resource_complete')): result = self._file_filter elif (method in ('begin_node', 'node_complete')): result = self._dir_filter @@ -132,7 +157,7 @@ class Plugin(object): return True result = always_true - return result if result else super(Plugin, self).__getattribute__(name) + return result if result else super(Plugin, self).__getattribute__(name) @property def settings(self): @@ -147,7 +172,6 @@ class Plugin(object): pass return opts - @property def plugin_name(self): """ @@ -195,26 +219,26 @@ class Plugin(object): except AttributeError: filters = None result = any(fnmatch.fnmatch(resource.path, f) - for f in filters) if filters else True + for f in filters) if filters else True return result def _dir_filter(self, node, *args, **kwargs): """ - Returns True if the node path is a descendant of the include_paths property in - plugin settings. + Returns True if the node path is a descendant of the + include_paths property in plugin settings. """ try: node_filters = self.settings.include_paths if not isinstance(node_filters, list): node_filters = [node_filters] node_filters = [self.site.content.node_from_relative_path(f) - for f in node_filters] + for f in node_filters] except AttributeError: node_filters = None result = any(node.source == f.source or - node.source.is_descendant_of(f.source) - for f in node_filters if f) \ - if node_filters else True + node.source.is_descendant_of(f.source) + for f in node_filters if f) \ + if node_filters else True return result def begin_text_resource(self, resource, text): @@ -293,7 +317,7 @@ class Plugin(object): return load_python_object(plugin_name)(site) site.plugins = [load_plugin(name) - for name in site.config.plugins] + for name in site.config.plugins] @staticmethod def get_proxy(site): @@ -302,7 +326,9 @@ class Plugin(object): """ return PluginProxy(site) + class CLTransformer(Plugin): + """ Handy class for plugins that simply call a command line app to transform resources. @@ -333,11 +359,11 @@ class CLTransformer(Plugin): """ return ("%(name)s executable path not configured properly. " - "This plugin expects `%(name)s.app` to point " - "to the full path of the `%(exec)s` executable." % - { - "name":self.plugin_name, "exec": self.executable_name - }) + "This plugin expects `%(name)s.app` to point " + "to the full path of the `%(exec)s` executable." % + { + "name": self.plugin_name, "exec": self.executable_name + }) @property def app(self): @@ -398,7 +424,7 @@ class CLTransformer(Plugin): if match: val = args[match] param = "%s%s" % (self.option_prefix(descriptive), - descriptive) + descriptive) if descriptive.endswith("="): param += val val = None @@ -414,13 +440,15 @@ class CLTransformer(Plugin): try: self.logger.debug( "Calling executable [%s] with arguments %s" % - (args[0], unicode(args[1:]))) + (args[0], unicode(args[1:]))) return subprocess.check_output(args) except subprocess.CalledProcessError, error: self.logger.error(error.output) raise + class TextyPlugin(Plugin): + """ Base class for text preprocessing plugins. @@ -493,10 +521,11 @@ class TextyPlugin(Plugin): """ Replace a text base pattern with a template statement. """ - text_open = re.compile(self.open_pattern, re.UNICODE|re.MULTILINE) + text_open = re.compile(self.open_pattern, re.UNICODE | re.MULTILINE) text = text_open.sub(self.text_to_tag, text) if self.close_pattern: - text_close = re.compile(self.close_pattern, re.UNICODE|re.MULTILINE) + text_close = re.compile( + self.close_pattern, re.UNICODE | re.MULTILINE) text = text_close.sub( - partial(self.text_to_tag, start=False), text) + partial(self.text_to_tag, start=False), text) return text diff --git a/hyde/publisher.py b/hyde/publisher.py index 10142d5..1eb0b20 100644 --- a/hyde/publisher.py +++ b/hyde/publisher.py @@ -8,7 +8,9 @@ Contains abstract classes and utilities that help publishing a website to a server. """ + class Publisher(object): + """ The abstract base class for publishers. """ @@ -18,13 +20,14 @@ class Publisher(object): def __init__(self, site, settings, message): super(Publisher, self).__init__() self.logger = getLoggerWithNullHandler( - 'hyde.engine.%s' % self.__class__.__name__) + 'hyde.engine.%s' % self.__class__.__name__) self.site = site self.message = message self.initialize(settings) @abc.abstractmethod - def initialize(self, settings): pass + def initialize(self, settings): + pass @abc.abstractmethod def publish(self): @@ -43,7 +46,8 @@ class Publisher(object): # Find the first configured publisher try: publisher = site.config.publisher.__dict__.iterkeys().next() - logger.warning("No default publisher configured. Using: %s" % publisher) + logger.warning( + "No default publisher configured. Using: %s" % publisher) settings = attrgetter("publisher.%s" % publisher)(site.config) except (AttributeError, StopIteration): logger.error( @@ -56,4 +60,4 @@ class Publisher(object): raise Exception("Please specify the publisher type in config.") pub_class = load_python_object(settings.type) - return pub_class(site, settings, message) \ No newline at end of file + return pub_class(site, settings, message) diff --git a/hyde/server.py b/hyde/server.py index 0223b3b..79be549 100644 --- a/hyde/server.py +++ b/hyde/server.py @@ -18,7 +18,9 @@ from fswrap import File, Folder from commando.util import getLoggerWithNullHandler logger = getLoggerWithNullHandler('hyde.server') + class HydeRequestHandler(SimpleHTTPRequestHandler): + """ Serves files by regenerating the resource (or) everything when a request is issued. @@ -35,7 +37,7 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): logger.debug("Processing request: [%s]" % self.path) result = urlparse.urlparse(self.path) query = urlparse.parse_qs(result.query) - if 'refresh' in query or result.query=='refresh': + if 'refresh' in query or result.query == 'refresh': self.server.regenerate() if 'refresh' in query: del query['refresh'] @@ -48,7 +50,6 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): else: SimpleHTTPRequestHandler.do_GET(self) - def translate_path(self, path): """ Finds the absolute path of the requested file by @@ -56,7 +57,8 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): """ site = self.server.site result = urlparse.urlparse(urllib.unquote(self.path).decode('utf-8')) - logger.debug("Trying to load file based on request: [%s]" % result.path) + logger.debug( + "Trying to load file based on request: [%s]" % result.path) path = result.path.lstrip('/') res = None if path.strip() == "" or File(path).kind.strip() == "": @@ -65,13 +67,16 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): if isinstance(deployed, Folder): node = site.content.node_from_relative_path(path) res = node.get_resource('index.html') - elif hasattr(site.config, 'urlcleaner') and hasattr(site.config.urlcleaner, 'strip_extensions'): + elif hasattr(site.config, 'urlcleaner') and hasattr( + site.config.urlcleaner, 'strip_extensions'): for ext in site.config.urlcleaner.strip_extensions: - res = site.content.resource_from_relative_deploy_path(path + '.' + ext) + res = site.content.resource_from_relative_deploy_path( + path + '.' + ext) if res: break for ext in site.config.urlcleaner.strip_extensions: - new_path = site.config.deploy_root_path.child(path + '.' + ext) + new_path = site.config.deploy_root_path.child( + path + '.' + ext) if File(new_path).exists: return new_path else: @@ -83,7 +88,7 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): else: self.server.generate_resource(res) new_path = site.config.deploy_root_path.child( - res.relative_deploy_path) + res.relative_deploy_path) return new_path def do_404(self): @@ -95,13 +100,13 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): self.redirect(site.config.not_found) else: res = site.content.resource_from_relative_deploy_path( - site.config.not_found) + site.config.not_found) message = "Requested resource not found" if not res: logger.error( "Cannot find the 404 template [%s]." - % site.config.not_found) + % site.config.not_found) else: f404 = File(self.translate_path(site.config.not_found)) if f404.exists: @@ -118,6 +123,7 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): class HydeWebServer(HTTPServer): + """ The hyde web server that regenerates the resource, node or site when a request is issued. @@ -133,7 +139,7 @@ class HydeWebServer(HTTPServer): self.__shutdown_request = False self.map_extensions() HTTPServer.__init__(self, (address, port), - HydeRequestHandler) + HydeRequestHandler) def map_extensions(self): """ @@ -161,7 +167,7 @@ class HydeWebServer(HTTPServer): self.generator.generate_all(incremental=False) except Exception, exception: logger.error('Error occured when regenerating the site [%s]' - % exception.message) + % exception.message) logger.debug(traceback.format_exc()) def generate_node(self, node): @@ -179,7 +185,7 @@ class HydeWebServer(HTTPServer): except Exception, exception: logger.error( 'Error [%s] occured when generating the node [%s]' - % (repr(exception), node)) + % (repr(exception), node)) logger.debug(traceback.format_exc()) def generate_resource(self, resource): @@ -198,5 +204,5 @@ class HydeWebServer(HTTPServer): except Exception, exception: logger.error( 'Error [%s] occured when serving the resource [%s]' - % (repr(exception), resource)) + % (repr(exception), resource)) logger.debug(traceback.format_exc()) diff --git a/hyde/site.py b/hyde/site.py index a20d324..29ccd08 100644 --- a/hyde/site.py +++ b/hyde/site.py @@ -15,6 +15,7 @@ from hyde.model import Config from commando.util import getLoggerWithNullHandler from fswrap import FS, File, Folder + def path_normalized(f): @wraps(f) def wrapper(self, path): @@ -23,6 +24,7 @@ def path_normalized(f): logger = getLoggerWithNullHandler('hyde.engine') + class Processable(object): """ A node or resource. @@ -64,8 +66,8 @@ class Processable(object): after its been processed. """ return self._relative_deploy_path \ - if self._relative_deploy_path is not None \ - else self.relative_path + if self._relative_deploy_path is not None \ + else self.relative_path def set_relative_deploy_path(self, path): """ @@ -75,7 +77,8 @@ class Processable(object): self._relative_deploy_path = path self.site.content.deploy_path_changed(self) - relative_deploy_path = property(get_relative_deploy_path, set_relative_deploy_path) + relative_deploy_path = property(get_relative_deploy_path, + set_relative_deploy_path) @property def url(self): @@ -118,9 +121,10 @@ class Resource(Processable): @property def slug(self): - #TODO: Add a more sophisticated slugify method + # TODO: Add a more sophisticated slugify method return self.source.name_without_extension + class Node(Processable): """ Represents any folder that is processed by hyde @@ -159,7 +163,7 @@ class Node(Processable): if self.contains_resource(resource_name): return self.root.resource_from_path( - self.source_folder.child(resource_name)) + self.source_folder.child(resource_name)) return None def add_child_node(self, folder): @@ -223,6 +227,7 @@ class Node(Processable): """ return self.source_folder.get_relative_path(self.root.source_folder) + class RootNode(Node): """ Represents one of the roots of site: Content, Media or Layout @@ -253,7 +258,7 @@ class RootNode(Node): If no match is found it returns None. """ return self.node_from_path( - self.source_folder.child(unicode(relative_path))) + self.source_folder.child(unicode(relative_path))) @path_normalized def resource_from_path(self, path): @@ -270,7 +275,7 @@ class RootNode(Node): If no match is found it returns None. """ return self.resource_from_path( - self.source_folder.child(relative_path)) + self.source_folder.child(relative_path)) def deploy_path_changed(self, item): """ @@ -320,7 +325,7 @@ class RootNode(Node): node = node.add_child_node(h_folder) self.node_map[unicode(h_folder)] = node logger.debug("Added node [%s] to [%s]" % ( - node.relative_path, self.source_folder)) + node.relative_path, self.source_folder)) return node @@ -350,11 +355,10 @@ class RootNode(Node): self.resource_map[unicode(afile)] = resource relative_path = resource.relative_path resource.simple_copy = any(fnmatch.fnmatch(relative_path, pattern) - for pattern - in self.site.config.simple_copy) + for pattern in self.site.config.simple_copy) logger.debug("Added resource [%s] to [%s]" % - (resource.relative_path, self.source_folder)) + (resource.relative_path, self.source_folder)) return resource def load(self): @@ -396,6 +400,7 @@ def _encode_path(base, path, safe): path = quote(path, safe) if safe is not None else quote(path) return base.rstrip('/') + '/' + path.lstrip('/') + class Site(object): """ Represents the site to be generated. @@ -420,9 +425,8 @@ class Site(object): """ if self.config.needs_refresh(): logger.debug("Refreshing config data") - self.config = Config(self.sitepath, - self.config.config_file, - self.config.config_dict) + self.config = Config(self.sitepath, self.config.config_file, + self.config.config_dict) def reload_if_needed(self): """ @@ -453,13 +457,13 @@ class Site(object): """ return _encode_path(self.config.base_url, path, self._safe_chars(safe)) - def media_url(self, path, safe=None): """ Returns the media url by appending the media base url from the config with the given path. The return value is url encoded. """ - return _encode_path(self.config.media_url, path, self._safe_chars(safe)) + return _encode_path(self.config.media_url, path, + self._safe_chars(safe)) def full_url(self, path, safe=None): """ @@ -467,12 +471,12 @@ class Site(object): configuration and returns the appropriate url. The return value is url encoded. """ - if urlparse.urlparse(path)[:2] != ("",""): + if urlparse.urlparse(path)[:2] != ("", ""): return path if self.is_media(path): relative_path = File(path).get_relative_path( - Folder(self.config.media_root)) + Folder(self.config.media_root)) return self.media_url(relative_path, safe) else: return self.content_url(path, safe) @@ -482,4 +486,4 @@ class Site(object): Given the relative path, determines if it is content or media. """ folder = self.content.source.child_folder(path) - return folder.is_descendant_of(self.config.media_root_path) \ No newline at end of file + return folder.is_descendant_of(self.config.media_root_path) diff --git a/hyde/template.py b/hyde/template.py index 85841b9..94888df 100644 --- a/hyde/template.py +++ b/hyde/template.py @@ -11,6 +11,7 @@ from commando.util import getLoggerWithNullHandler class HtmlWrap(object): + """ A wrapper class for raw html. @@ -36,7 +37,9 @@ class HtmlWrap(object): return self.raw return self.q(selector).html() + class Template(object): + """ Interface for hyde template engines. To use a different template engine, the following interface must be implemented. @@ -50,7 +53,6 @@ class Template(object): @abc.abstractmethod def configure(self, site, engine): - """ The site object should contain a config attribute. The config object is a simple YAML object with required settings. The template diff --git a/hyde/tests/ext/test_auto_extend.py b/hyde/tests/ext/test_auto_extend.py index 3e9be2b..3bb83e6 100644 --- a/hyde/tests/ext/test_auto_extend.py +++ b/hyde/tests/ext/test_auto_extend.py @@ -20,7 +20,7 @@ class TestAutoExtend(object): def setUp(self): TEST_SITE.make() TEST_SITE.parent.child_folder( - 'sites/test_jinja').copy_contents_to(TEST_SITE) + 'sites/test_jinja').copy_contents_to(TEST_SITE) def tearDown(self): TEST_SITE.delete() @@ -33,7 +33,8 @@ class TestAutoExtend(object): gen = Generator(s) gen.generate_resource_at_path(bd.path) res = s.content.resource_from_path(bd.path) - target = File(s.config.deploy_root_path.child(res.relative_deploy_path)) + target = File( + s.config.deploy_root_path.child(res.relative_deploy_path)) assert target.exists text = target.read_all() q = PyQuery(text) @@ -44,7 +45,8 @@ class TestAutoExtend(object): s.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin', 'hyde.ext.plugins.meta.AutoExtendPlugin', 'hyde.ext.plugins.text.BlockdownPlugin'] - txt ="This template tests to make sure blocks can be replaced with markdownish syntax." + txt = ("This template tests to make sure blocks can be replaced with" + "markdownish syntax.") templ = """ --- extends: base.html @@ -54,14 +56,13 @@ extends: base.html ====/title========""" self.assert_extended(s, txt, templ) - - def test_can_auto_extend_with_default_blocks(self): s = Site(TEST_SITE) s.config.plugins = ['hyde.ext.plugins.meta.MetaPlugin', 'hyde.ext.plugins.meta.AutoExtendPlugin', 'hyde.ext.plugins.text.BlockdownPlugin'] - txt ="This template tests to make sure blocks can be replaced with markdownish syntax." + txt = ("This template tests to make sure blocks can be replaced with" + "markdownish syntax.") templ = """ --- extends: base.html diff --git a/hyde/tests/ext/test_blockdown.py b/hyde/tests/ext/test_blockdown.py index 58ed17b..9f87760 100644 --- a/hyde/tests/ext/test_blockdown.py +++ b/hyde/tests/ext/test_blockdown.py @@ -18,7 +18,7 @@ class TestBlockdown(object): def setUp(self): TEST_SITE.make() TEST_SITE.parent.child_folder( - 'sites/test_jinja').copy_contents_to(TEST_SITE) + 'sites/test_jinja').copy_contents_to(TEST_SITE) def tearDown(self): TEST_SITE.delete() @@ -26,7 +26,8 @@ class TestBlockdown(object): def test_can_parse_blockdown(self): s = Site(TEST_SITE) s.config.plugins = ['hyde.ext.plugins.text.BlockdownPlugin'] - txt ="This template tests to make sure blocks can be replaced with markdownish syntax." + txt = ("This template tests to make sure blocks can be replaced" + "with markdownish syntax.") templ = """ {%% extends "base.html" %%} =====title======== @@ -39,7 +40,8 @@ class TestBlockdown(object): gen = Generator(s) gen.generate_resource_at_path(bd.path) res = s.content.resource_from_path(bd.path) - target = File(s.config.deploy_root_path.child(res.relative_deploy_path)) + target = File( + s.config.deploy_root_path.child(res.relative_deploy_path)) assert target.exists text = target.read_all() q = PyQuery(text) diff --git a/hyde/tests/ext/test_combine.py b/hyde/tests/ext/test_combine.py index 69c6929..3359438 100644 --- a/hyde/tests/ext/test_combine.py +++ b/hyde/tests/ext/test_combine.py @@ -12,14 +12,17 @@ from fswrap import File, Folder COMBINE_SOURCE = File(__file__).parent.child_folder('combine') TEST_SITE = File(__file__).parent.parent.child_folder('_test') + class CombineTester(object): + def _test_combine(self, content): s = Site(TEST_SITE) s.config.plugins = [ 'hyde.ext.plugins.meta.MetaPlugin', 'hyde.ext.plugins.structure.CombinePlugin'] source = TEST_SITE.child('content/media/js/script.js') - target = File(Folder(s.config.deploy_root_path).child('media/js/script.js')) + target = File( + Folder(s.config.deploy_root_path).child('media/js/script.js')) File(source).write(content) gen = Generator(s) @@ -29,12 +32,13 @@ class CombineTester(object): text = target.read_all() return text, s + class TestCombine(CombineTester): def setUp(self): TEST_SITE.make() TEST_SITE.parent.child_folder( - 'sites/test_jinja').copy_contents_to(TEST_SITE) + 'sites/test_jinja').copy_contents_to(TEST_SITE) TEST_SITE.child_folder('content/media/js').make() COMBINE_SOURCE.copy_contents_to(TEST_SITE.child('content/media/js')) @@ -107,7 +111,7 @@ combine: --- First line""") - for i in range(1,4): + for i in range(1, 4): assert not File(Folder(s.config.deploy_root_path). child('media/js/script.%d.js' % i)).exists @@ -117,7 +121,7 @@ class TestCombinePaths(CombineTester): def setUp(self): TEST_SITE.make() TEST_SITE.parent.child_folder( - 'sites/test_jinja').copy_contents_to(TEST_SITE) + 'sites/test_jinja').copy_contents_to(TEST_SITE) TEST_SITE.child_folder('content/media/js').make() JS = TEST_SITE.child_folder('content/scripts').make() S1 = JS.child_folder('s1').make() diff --git a/hyde/tests/ext/test_depends.py b/hyde/tests/ext/test_depends.py index 7258648..3bf2b92 100644 --- a/hyde/tests/ext/test_depends.py +++ b/hyde/tests/ext/test_depends.py @@ -17,10 +17,10 @@ class TestDepends(object): def setUp(self): TEST_SITE.make() TEST_SITE.parent.child_folder( - 'sites/test_jinja').copy_contents_to(TEST_SITE) + 'sites/test_jinja').copy_contents_to(TEST_SITE) TEST_SITE.parent.child_folder( - 'templates/jinja2').copy_contents_to( - TEST_SITE.child_folder('content')) + 'templates/jinja2').copy_contents_to( + TEST_SITE.child_folder('content')) def tearDown(self): TEST_SITE.delete() @@ -40,6 +40,7 @@ depends: index.html gen = Generator(s) gen.load_site_if_needed() gen.load_template_if_needed() + def dateformat(x): return x.strftime('%Y-%m-%d') gen.template.env.filters['dateformat'] = dateformat @@ -52,4 +53,4 @@ depends: index.html assert 'helpers.html' in deps assert 'layout.html' in deps - assert 'index.html' in deps \ No newline at end of file + assert 'index.html' in deps diff --git a/hyde/tests/ext/test_drafts.py b/hyde/tests/ext/test_drafts.py index c42459d..7a8e792 100644 --- a/hyde/tests/ext/test_drafts.py +++ b/hyde/tests/ext/test_drafts.py @@ -23,12 +23,13 @@ A draft post. """ + class TestDrafts(object): def setUp(self): TEST_SITE.make() TEST_SITE.parent.child_folder( - 'sites/test_jinja').copy_contents_to(TEST_SITE) + 'sites/test_jinja').copy_contents_to(TEST_SITE) draft = TEST_SITE.child_file('content/blog/2013/may/draft-post.html') draft.parent.make() draft.write(DRAFT_POST) @@ -50,7 +51,7 @@ class TestDrafts(object): gen = Generator(s) gen.generate_all() assert not s.config.deploy_root_path.child_file( - 'blog/2013/may/draft-post.html').exists + 'blog/2013/may/draft-post.html').exists def test_drafts_are_published_in_development(self): s = Site(TEST_SITE) @@ -66,6 +67,4 @@ class TestDrafts(object): gen = Generator(s) gen.generate_all() assert s.config.deploy_root_path.child_file( - 'blog/2013/may/draft-post.html').exists - - + 'blog/2013/may/draft-post.html').exists diff --git a/hyde/tests/ext/test_flattener.py b/hyde/tests/ext/test_flattener.py index d72b876..5111cdf 100644 --- a/hyde/tests/ext/test_flattener.py +++ b/hyde/tests/ext/test_flattener.py @@ -19,7 +19,7 @@ class TestFlattner(object): def setUp(self): TEST_SITE.make() TEST_SITE.parent.child_folder( - 'sites/test_jinja').copy_contents_to(TEST_SITE) + 'sites/test_jinja').copy_contents_to(TEST_SITE) def tearDown(self): TEST_SITE.delete() @@ -42,7 +42,8 @@ class TestFlattner(object): gen.generate_all() assert not s.config.deploy_root_path.child_folder('blog').exists - assert File(s.config.deploy_root_path.child('merry-christmas.html')).exists + assert File( + s.config.deploy_root_path.child('merry-christmas.html')).exists def test_flattener_fixes_nodes(self): s = Site(TEST_SITE) @@ -64,5 +65,3 @@ class TestFlattner(object): assert blog_node assert blog_node.url == '/' - - diff --git a/hyde/tests/ext/test_grouper.py b/hyde/tests/ext/test_grouper.py index 9184cb6..4cfb458 100644 --- a/hyde/tests/ext/test_grouper.py +++ b/hyde/tests/ext/test_grouper.py @@ -21,7 +21,7 @@ class TestGrouperSingleLevel(object): def setUp(self): TEST_SITE.make() TEST_SITE.parent.child_folder( - 'sites/test_grouper').copy_contents_to(TEST_SITE) + 'sites/test_grouper').copy_contents_to(TEST_SITE) self.s = Site(TEST_SITE) cfg = """ @@ -55,7 +55,8 @@ class TestGrouperSingleLevel(object): SorterPlugin(self.s).begin_site() GrouperPlugin(self.s).begin_site() - self.all = ['installation.html', 'overview.html', 'templating.html', 'plugins.html', 'tags.html'] + self.all = ['installation.html', 'overview.html', + 'templating.html', 'plugins.html', 'tags.html'] self.start = ['installation.html', 'overview.html', 'templating.html'] self.plugins = ['plugins.html', 'tags.html'] self.section = self.all @@ -72,7 +73,8 @@ class TestGrouperSingleLevel(object): def test_site_grouper_walk_groups(self): - groups = dict([(g.name, g) for g in self.s.grouper['section'].walk_groups()]) + groups = dict([(g.name, g) + for g in self.s.grouper['section'].walk_groups()]) assert len(groups) == 3 assert 'section' in groups assert 'start' in groups @@ -81,7 +83,8 @@ class TestGrouperSingleLevel(object): def test_walk_section_groups(self): assert hasattr(self.s.content, 'walk_section_groups') - groups = dict([(grouper.group.name, grouper) for grouper in self.s.content.walk_section_groups()]) + groups = dict([(grouper.group.name, grouper) + for grouper in self.s.content.walk_section_groups()]) assert len(groups) == 3 assert 'section' in groups assert 'start' in groups @@ -93,15 +96,16 @@ class TestGrouperSingleLevel(object): def test_walk_start_groups(self): assert hasattr(self.s.content, 'walk_start_groups') - groups = dict([(g.name, g) for g, resources in self.s.content.walk_start_groups()]) + groups = dict([(g.name, g) + for g, resources in self.s.content.walk_start_groups()]) assert len(groups) == 1 assert 'start' in groups - def test_walk_plugins_groups(self): assert hasattr(self.s.content, 'walk_plugins_groups') - groups = dict([(g.name, g) for g, resources in self.s.content.walk_plugins_groups()]) + groups = dict([(g.name, g) for g, resources in + self.s.content.walk_plugins_groups()]) assert len(groups) == 1 assert 'plugins' in groups @@ -109,22 +113,24 @@ class TestGrouperSingleLevel(object): assert hasattr(self.s.content, 'walk_resources_grouped_by_section') - resources = [resource.name for resource in self.s.content.walk_resources_grouped_by_section()] + resources = [resource.name for resource in + self.s.content.walk_resources_grouped_by_section()] assert resources == self.all - def test_walk_start_resources(self): assert hasattr(self.s.content, 'walk_resources_grouped_by_start') - start_resources = [resource.name for resource in self.s.content.walk_resources_grouped_by_start()] + start_resources = [resource.name for resource in + self.s.content.walk_resources_grouped_by_start()] assert start_resources == self.start def test_walk_plugins_resources(self): assert hasattr(self.s.content, 'walk_resources_grouped_by_plugins') - plugin_resources = [resource.name for resource in self.s.content.walk_resources_grouped_by_plugins()] + plugin_resources = [resource.name for resource in + self.s.content.walk_resources_grouped_by_plugins()] assert plugin_resources == self.plugins def test_resource_group(self): @@ -134,7 +140,8 @@ class TestGrouperSingleLevel(object): for name, group in groups.items(): pages = getattr(self, name) for page in pages: - res = self.s.content.resource_from_relative_path('blog/' + page) + res = self.s.content.resource_from_relative_path( + 'blog/' + page) assert hasattr(res, 'section_group') res_group = getattr(res, 'section_group') assert res_group == group @@ -146,7 +153,8 @@ class TestGrouperSingleLevel(object): for name, group in groups.items(): pages = getattr(self, name) for page in pages: - res = self.s.content.resource_from_relative_path('blog/' + page) + res = self.s.content.resource_from_relative_path( + 'blog/' + page) res_groups = getattr(res, 'walk_%s_groups' % name)() assert group in res_groups @@ -154,7 +162,8 @@ class TestGrouperSingleLevel(object): resources = [] for page in self.all: - resources.append(self.s.content.resource_from_relative_path('blog/' + page)) + resources.append( + self.s.content.resource_from_relative_path('blog/' + page)) index = 0 for res in resources: @@ -174,7 +183,7 @@ class TestGrouperSingleLevel(object): def test_nav_with_grouper(self): - text =""" + text = """ {% for group, resources in site.content.walk_section_groups() %}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