Browse Source

Merge pull request #307 from hyde/python3

Add initial (experimental) Python 3 support.
main
Lakshmi 9 years ago
parent
commit
b619de2e7c
35 changed files with 297 additions and 162 deletions
  1. +4
    -1
      .travis.yml
  2. +6
    -0
      CHANGELOG.rst
  3. +91
    -0
      hyde/_compat.py
  4. +5
    -5
      hyde/exceptions.py
  5. +9
    -9
      hyde/ext/plugins/css.py
  6. +1
    -0
      hyde/ext/plugins/depends.py
  7. +8
    -7
      hyde/ext/plugins/images.py
  8. +7
    -6
      hyde/ext/plugins/js.py
  9. +7
    -7
      hyde/ext/plugins/meta.py
  10. +2
    -1
      hyde/ext/plugins/sphinx.py
  11. +1
    -0
      hyde/ext/plugins/structure.py
  12. +8
    -8
      hyde/ext/publishers/dvcs.py
  13. +3
    -2
      hyde/ext/publishers/pyfs.py
  14. +3
    -2
      hyde/ext/publishers/pypi.py
  15. +2
    -1
      hyde/ext/publishers/ssh.py
  16. +29
    -27
      hyde/ext/templates/jinja.py
  17. +1
    -1
      hyde/generator.py
  18. +3
    -1
      hyde/layout.py
  19. +6
    -5
      hyde/model.py
  20. +7
    -7
      hyde/plugin.py
  21. +3
    -3
      hyde/publisher.py
  22. +14
    -11
      hyde/server.py
  23. +14
    -14
      hyde/site.py
  24. +6
    -4
      hyde/template.py
  25. +5
    -3
      hyde/util.py
  26. +10
    -6
      setup.py
  27. +4
    -4
      tests/ext/test_sass.py
  28. +2
    -2
      tests/ext/test_textlinks.py
  29. +15
    -14
      tests/test_initialize.py
  30. +8
    -1
      tests/test_jinja2template.py
  31. +2
    -1
      tests/test_layout.py
  32. +2
    -2
      tests/test_plugin.py
  33. +3
    -3
      tests/test_site.py
  34. +3
    -1
      tests/util.py
  35. +3
    -3
      tox.ini

+ 4
- 1
.travis.yml View File

@@ -2,7 +2,7 @@ sudo: false


language: python language: python
python: python:
- "2.7"
- "3.5"


addons: addons:
apt: apt:
@@ -40,6 +40,9 @@ install:
# Run each tox environment separately # Run each tox environment separately
env: env:
- TOX_ENV=py27 - TOX_ENV=py27
- TOX_ENV=py33
- TOX_ENV=py34
- TOX_ENV=py35
- TOX_ENV=pep8 - TOX_ENV=pep8


before_script: before_script:


+ 6
- 0
CHANGELOG.rst View File

@@ -1,3 +1,9 @@
Version TBA
===========

* Experimental Python 3 support


Version 0.8.9 (2015-11-09) Version 0.8.9 (2015-11-09)
=========================================================== ===========================================================




+ 91
- 0
hyde/_compat.py View File

@@ -0,0 +1,91 @@
"""2/3 compatibility module for Hyde."""

# This module is for cross-version compatibility. As such, several
# assignments and import will look invalid to checkers like flake8.
# These lines are being marked with ``# NOQA`` to allow flake8 checking
# to pass.

import sys

PY3 = sys.version_info.major == 3

if PY3:
# Imports that have moved.
from collections import UserDict # NOQA
from functools import reduce # NOQA
from http.server import HTTPServer, SimpleHTTPRequestHandler # NOQA
from io import StringIO # NOQA
from urllib import parse # NOQA
from urllib.parse import quote, unquote # NOQA

# Types that have changed name.
filter = filter # NOQA
input = input # NOQA
basestring = str # NOQA
str = str # NOQA
zip = zip # NOQA

def execfile(filename, globals, locals):
"""Python 3 replacement for ``execfile``."""
# Credit: 2to3 and this StackOverflow answer
# (http://stackoverflow.com/a/437857/841994) take similar
# approaches.
with open(filename) as f:
code = compile(f.read(), filename, 'exec')
exec(code, globals, locals)

def reraise(tp, value, tb=None):
"""Reraise exceptions."""
if getattr(value, '__traceback__', tb) is not tb:
raise value.with_traceback(tb)
raise value

else:
# Imports that have moved.
from itertools import ifilter as filter, izip as zip # NOQA
reduce = reduce
from BaseHTTPServer import HTTPServer # NOQA
from SimpleHTTPServer import SimpleHTTPRequestHandler # NOQA
from cStringIO import StringIO # NOQA
from UserDict import IterableUserDict as UserDict # NOQA
import urlparse as parse # NOQA
from urllib import quote, unquote # NOQA

# Types that have changed name.
input = raw_input # NOQA
basestring = basestring # NOQA
str = unicode # NOQA
execfile = execfile # NOQA

exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')


def iteritems(d):
"""Return iterable items from a dict."""
if hasattr(d, 'iteritems'):
return d.iteritems()
else:
return iter(d.items())


def with_metaclass(meta, *bases):
"""Assign a metaclass in a 2/3 compatible fashion."""
# Note: borrowed from https://github.com/dirn/Simon/
# This requires a bit of explanation: the basic idea is to make a
# dummy metaclass for one level of class instantiation that replaces
# itself with the actual metaclass. Because of internal type checks
# we also need to make sure that we downgrade the custom metaclass
# for one level to something closer to type (that's why __call__ and
# __init__ comes back from type etc.).
#
# This has the advantage over six.with_metaclass in that it does not
# introduce dummy classes into the final MRO.
class metaclass(meta):
__call__ = type.__call__
__init__ = type.__init__

def __new__(cls, name, this_bases, d):
if this_bases is None:
return type.__new__(cls, name, (), d)
return meta(name, bases, d)
return metaclass('DummyMetaClass', None, {})

+ 5
- 5
hyde/exceptions.py View File

@@ -1,10 +1,10 @@
class HydeException(Exception):
from hyde._compat import reraise



"""
Base class for exceptions from hyde
"""
class HydeException(Exception):
"""Base class for exceptions from hyde."""


@staticmethod @staticmethod
def reraise(message, exc_info): def reraise(message, exc_info):
_, _, tb = exc_info _, _, tb = exc_info
raise HydeException(message), None, tb
reraise(HydeException, HydeException(message), tb)

+ 9
- 9
hyde/ext/plugins/css.py View File

@@ -3,7 +3,7 @@
CSS plugins CSS plugins
""" """


from hyde._compat import str
from hyde.plugin import CLTransformer, Plugin from hyde.plugin import CLTransformer, Plugin
from hyde.exceptions import HydeException from hyde.exceptions import HydeException


@@ -109,9 +109,9 @@ class LessCSSPlugin(CLTransformer):
less = self.app less = self.app
source = File.make_temp(text) source = File.make_temp(text)
target = File.make_temp('') target = File.make_temp('')
args = [unicode(less)]
args = [str(less)]
args.extend(self.process_args(supported)) args.extend(self.process_args(supported))
args.extend([unicode(source), unicode(target)])
args.extend([str(source), str(target)])
try: try:
self.call_app(args) self.call_app(args)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
@@ -223,9 +223,9 @@ class StylusPlugin(CLTransformer):
source = File.make_temp(text.strip()) source = File.make_temp(text.strip())
supported = [("compress", "c"), ("include", "I")] supported = [("compress", "c"), ("include", "I")]


args = [unicode(stylus)]
args = [str(stylus)]
args.extend(self.process_args(supported)) args.extend(self.process_args(supported))
args.append(unicode(source))
args.append(str(source))
try: try:
self.call_app(args) self.call_app(args)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
@@ -251,7 +251,7 @@ class CleverCSSPlugin(Plugin):
super(CleverCSSPlugin, self).__init__(site) super(CleverCSSPlugin, self).__init__(site)
try: try:
import clevercss import clevercss
except ImportError, e:
except ImportError as e:
raise HydeException('Unable to import CleverCSS: ' + e.message) raise HydeException('Unable to import CleverCSS: ' + e.message)
else: else:
self.clevercss = clevercss self.clevercss = clevercss
@@ -329,7 +329,7 @@ class SassyCSSPlugin(Plugin):
super(SassyCSSPlugin, self).__init__(site) super(SassyCSSPlugin, self).__init__(site)
try: try:
import scss import scss
except ImportError, e:
except ImportError as e:
raise HydeException('Unable to import pyScss: ' + e.message) raise HydeException('Unable to import pyScss: ' + e.message)
else: else:
self.scss = scss self.scss = scss
@@ -419,7 +419,7 @@ class SassPlugin(Plugin):
super(SassPlugin, self).__init__(site) super(SassPlugin, self).__init__(site)
try: try:
import sass import sass
except ImportError, e:
except ImportError as e:
raise HydeException('Unable to import libsass: ' + e.message) raise HydeException('Unable to import libsass: ' + e.message)
else: else:
self.sass = sass self.sass = sass
@@ -489,6 +489,6 @@ class SassPlugin(Plugin):
self.logger.error(resource) self.logger.error(resource)
try: try:
return self.sass.compile(string=text, **options) return self.sass.compile(string=text, **options)
except Exception, exc:
except Exception as exc:
self.logger.error(exc) self.logger.error(exc)
raise raise

+ 1
- 0
hyde/ext/plugins/depends.py View File

@@ -5,6 +5,7 @@ Depends plugin
/// Experimental: Not working yet. /// Experimental: Not working yet.
""" """


from hyde._compat import basestring
from hyde.plugin import Plugin from hyde.plugin import Plugin






+ 8
- 7
hyde/ext/plugins/images.py View File

@@ -14,6 +14,7 @@ import re


from fswrap import File from fswrap import File


from hyde._compat import str
from hyde.exceptions import HydeException from hyde.exceptions import HydeException




@@ -27,7 +28,7 @@ class PILPlugin(Plugin):
# No pillow # No pillow
try: try:
import Image import Image
except ImportError, e:
except ImportError as e:
raise HydeException('Unable to load PIL: ' + e.message) raise HydeException('Unable to load PIL: ' + e.message)


self.Image = Image self.Image = Image
@@ -442,9 +443,9 @@ class JPEGOptimPlugin(CLTransformer):
target = File(self.site.config.deploy_root_path.child( target = File(self.site.config.deploy_root_path.child(
resource.relative_deploy_path)) resource.relative_deploy_path))
jpegoptim = self.app jpegoptim = self.app
args = [unicode(jpegoptim)]
args = [str(jpegoptim)]
args.extend(self.process_args(supported)) args.extend(self.process_args(supported))
args.extend(["-q", unicode(target)])
args.extend(["-q", str(target)])
self.call_app(args) self.call_app(args)




@@ -499,9 +500,9 @@ class JPEGTranPlugin(CLTransformer):
resource.relative_deploy_path)) resource.relative_deploy_path))
target = File.make_temp('') target = File.make_temp('')
jpegtran = self.app jpegtran = self.app
args = [unicode(jpegtran)]
args = [str(jpegtran)]
args.extend(self.process_args(supported)) args.extend(self.process_args(supported))
args.extend(["-outfile", unicode(target), unicode(source)])
args.extend(["-outfile", str(target), str(source)])
self.call_app(args) self.call_app(args)
target.copy_to(source) target.copy_to(source)
target.delete() target.delete()
@@ -570,7 +571,7 @@ class OptiPNGPlugin(CLTransformer):
target = File(self.site.config.deploy_root_path.child( target = File(self.site.config.deploy_root_path.child(
resource.relative_deploy_path)) resource.relative_deploy_path))
optipng = self.app optipng = self.app
args = [unicode(optipng)]
args = [str(optipng)]
args.extend(self.process_args(supported)) args.extend(self.process_args(supported))
args.extend([unicode(target)])
args.extend([str(target)])
self.call_app(args) self.call_app(args)

+ 7
- 6
hyde/ext/plugins/js.py View File

@@ -5,6 +5,7 @@ JavaScript plugins
import subprocess import subprocess
import sys import sys


from hyde._compat import str
from hyde.exceptions import HydeException from hyde.exceptions import HydeException
from hyde.plugin import CLTransformer from hyde.plugin import CLTransformer


@@ -79,9 +80,9 @@ class UglifyPlugin(CLTransformer):
uglify = self.app uglify = self.app
source = File.make_temp(text) source = File.make_temp(text)
target = File.make_temp('') target = File.make_temp('')
args = [unicode(uglify)]
args = [str(uglify)]
args.extend(self.process_args(supported)) args.extend(self.process_args(supported))
args.extend(["-o", unicode(target), unicode(source)])
args.extend(["-o", str(target), str(source)])
self.call_app(args) self.call_app(args)
out = target.read_all() out = target.read_all()
return out return out
@@ -127,9 +128,9 @@ class RequireJSPlugin(CLTransformer):


rjs = self.app rjs = self.app
target = File.make_temp('') target = File.make_temp('')
args = [unicode(rjs)]
args = [str(rjs)]
args.extend( args.extend(
['-o', unicode(resource), ("out=" + target.fully_expanded_path)])
['-o', str(resource), ("out=" + target.fully_expanded_path)])


try: try:
self.call_app(args) self.call_app(args)
@@ -184,6 +185,6 @@ class CoffeePlugin(CLTransformer):


coffee = self.app coffee = self.app
source = File.make_temp(text) source = File.make_temp(text)
args = [unicode(coffee)]
args.extend(["-c", "-p", unicode(source)])
args = [str(coffee)]
args.extend(["-c", "-p", str(source)])
return self.call_app(args) return self.call_app(args)

+ 7
- 7
hyde/ext/plugins/meta.py View File

@@ -5,11 +5,11 @@ Contains classes and utilities related to meta data in hyde.


from collections import namedtuple from collections import namedtuple
from functools import partial from functools import partial
from itertools import ifilter
from operator import attrgetter from operator import attrgetter
import re import re
import sys import sys


from hyde._compat import basestring, filter, iteritems, str
from hyde.exceptions import HydeException from hyde.exceptions import HydeException
from hyde.model import Expando from hyde.model import Expando
from hyde.plugin import Plugin from hyde.plugin import Plugin
@@ -263,7 +263,7 @@ def get_tagger_sort_method(site):




def walk_resources_tagged_with(node, tag): def walk_resources_tagged_with(node, tag):
tags = set(unicode(tag).split('+'))
tags = set(str(tag).split('+'))
walker = get_tagger_sort_method(node.site) walker = get_tagger_sort_method(node.site)
for resource in walker(): for resource in walker():
try: try:
@@ -329,7 +329,7 @@ class TaggerPlugin(Plugin):
except AttributeError: except AttributeError:
tag_meta = {} tag_meta = {}


for tagname, meta in tag_meta.iteritems():
for tagname, meta in iteritems(tag_meta):
# Don't allow name and resources in meta # Don't allow name and resources in meta
if 'resources' in meta: if 'resources' in meta:
del(meta['resources']) del(meta['resources'])
@@ -376,7 +376,7 @@ class TaggerPlugin(Plugin):


self.logger.debug("Generating archives for tags") self.logger.debug("Generating archives for tags")


for name, config in archive_config.to_dict().iteritems():
for name, config in iteritems(archive_config.to_dict()):
self._create_tag_archive(config) self._create_tag_archive(config)


def _create_tag_archive(self, config): def _create_tag_archive(self, config):
@@ -413,7 +413,7 @@ extends: false
{%% set walker = source['walk_resources_tagged_with_%(tag)s'] %%} {%% set walker = source['walk_resources_tagged_with_%(tag)s'] %%}
{%% extends "%(template)s" %%} {%% extends "%(template)s" %%}
""" """
for tagname, tag in self.site.tagger.tags.to_dict().iteritems():
for tagname, tag in iteritems(self.site.tagger.tags.to_dict()):
tag_data = { tag_data = {
"tag": tagname, "tag": tagname,
"node": source.name, "node": source.name,
@@ -482,8 +482,8 @@ def sort_method(node, settings=None):


excluder_ = partial(attributes_checker, attributes=attr) excluder_ = partial(attributes_checker, attributes=attr)


resources = ifilter(lambda x: excluder_(x) and filter_(x),
node.walk_resources())
resources = filter(lambda x: excluder_(x) and filter_(x),
node.walk_resources())
return sorted(resources, return sorted(resources,
key=attrgetter(*attr), key=attrgetter(*attr),
reverse=reverse) reverse=reverse)


+ 2
- 1
hyde/ext/plugins/sphinx.py View File

@@ -44,6 +44,7 @@ import os
import json import json
import tempfile import tempfile


from hyde._compat import execfile, iteritems
from hyde.plugin import Plugin from hyde.plugin import Plugin
from hyde.model import Expando from hyde.model import Expando
from hyde.ext.plugins.meta import MetaPlugin as _MetaPlugin from hyde.ext.plugins.meta import MetaPlugin as _MetaPlugin
@@ -166,7 +167,7 @@ class SphinxPlugin(Plugin):
if not settings.block_map: if not settings.block_map:
output.append(sphinx_output["body"]) output.append(sphinx_output["body"])
else: else:
for (nm, content) in sphinx_output.iteritems():
for (nm, content) in iteritems(sphinx_output):
try: try:
block = getattr(settings.block_map, nm) block = getattr(settings.block_map, nm)
except AttributeError: except AttributeError:


+ 1
- 0
hyde/ext/plugins/structure.py View File

@@ -3,6 +3,7 @@
Plugins related to structure Plugins related to structure
""" """


from hyde._compat import reduce
from hyde.ext.plugins.meta import Metadata from hyde.ext.plugins.meta import Metadata
from hyde.plugin import Plugin from hyde.plugin import Plugin
from hyde.site import Resource from hyde.site import Resource


+ 8
- 8
hyde/ext/publishers/dvcs.py View File

@@ -3,14 +3,14 @@ Contains classes and utilities that help publishing a hyde website to
distributed version control systems. distributed version control systems.
""" """


from hyde._compat import str, with_metaclass
from hyde.publisher import Publisher from hyde.publisher import Publisher


import abc import abc
from subprocess import Popen, PIPE from subprocess import Popen, PIPE




class DVCS(Publisher):
__metaclass__ = abc.ABCMeta
class DVCS(with_metaclass(abc.ABCMeta, Publisher)):


def initialize(self, settings): def initialize(self, settings):
self.settings = settings self.settings = settings
@@ -62,7 +62,7 @@ class Git(DVCS):


def add(self, path="."): def add(self, path="."):
cmd = Popen('git add "%s"' % path, cmd = Popen('git add "%s"' % path,
cwd=unicode(self.path), stdout=PIPE, shell=True)
cwd=str(self.path), stdout=PIPE, shell=True)
cmdresult = cmd.communicate()[0] cmdresult = cmd.communicate()[0]
if cmd.returncode: if cmd.returncode:
raise Exception(cmdresult) raise Exception(cmdresult)
@@ -70,7 +70,7 @@ class Git(DVCS):
def pull(self): def pull(self):
self.switch(self.branch) self.switch(self.branch)
cmd = Popen("git pull origin %s" % self.branch, cmd = Popen("git pull origin %s" % self.branch,
cwd=unicode(self.path),
cwd=str(self.path),
stdout=PIPE, stdout=PIPE,
shell=True) shell=True)
cmdresult = cmd.communicate()[0] cmdresult = cmd.communicate()[0]
@@ -79,7 +79,7 @@ class Git(DVCS):


def push(self): def push(self):
cmd = Popen("git push origin %s" % self.branch, cmd = Popen("git push origin %s" % self.branch,
cwd=unicode(self.path), stdout=PIPE,
cwd=str(self.path), stdout=PIPE,
shell=True) shell=True)
cmdresult = cmd.communicate()[0] cmdresult = cmd.communicate()[0]
if cmd.returncode: if cmd.returncode:
@@ -87,7 +87,7 @@ class Git(DVCS):


def commit(self, message): def commit(self, message):
cmd = Popen('git commit -a -m"%s"' % message, cmd = Popen('git commit -a -m"%s"' % message,
cwd=unicode(self.path), stdout=PIPE, shell=True)
cwd=str(self.path), stdout=PIPE, shell=True)
cmdresult = cmd.communicate()[0] cmdresult = cmd.communicate()[0]
if cmd.returncode: if cmd.returncode:
raise Exception(cmdresult) raise Exception(cmdresult)
@@ -95,14 +95,14 @@ class Git(DVCS):
def switch(self, branch): def switch(self, branch):
self.branch = branch self.branch = branch
cmd = Popen('git checkout %s' % branch, cmd = Popen('git checkout %s' % branch,
cwd=unicode(self.path), stdout=PIPE, shell=True)
cwd=str(self.path), stdout=PIPE, shell=True)
cmdresult = cmd.communicate()[0] cmdresult = cmd.communicate()[0]
if cmd.returncode: if cmd.returncode:
raise Exception(cmdresult) raise Exception(cmdresult)


def merge(self, branch): def merge(self, branch):
cmd = Popen('git merge %s' % branch, cmd = Popen('git merge %s' % branch,
cwd=unicode(self.path), stdout=PIPE, shell=True)
cwd=str(self.path), stdout=PIPE, shell=True)
cmdresult = cmd.communicate()[0] cmdresult = cmd.communicate()[0]
if cmd.returncode: if cmd.returncode:
raise Exception(cmdresult) raise Exception(cmdresult)

+ 3
- 2
hyde/ext/publishers/pyfs.py View File

@@ -15,6 +15,7 @@ import getpass
import hashlib import hashlib




from hyde._compat import basestring, input
from hyde.publisher import Publisher from hyde.publisher import Publisher


from commando.util import getLoggerWithNullHandler from commando.util import getLoggerWithNullHandler
@@ -47,8 +48,8 @@ class PyFS(Publisher):
def prompt_for_credentials(self): def prompt_for_credentials(self):
credentials = {} credentials = {}
if "%(username)s" in self.url: if "%(username)s" in self.url:
print "Username: ",
credentials["username"] = raw_input().strip()
print("Username: ",)
credentials["username"] = input().strip()
if "%(password)s" in self.url: if "%(password)s" in self.url:
credentials["password"] = getpass.getpass("Password: ") credentials["password"] = getpass.getpass("Password: ")
if credentials: if credentials:


+ 3
- 2
hyde/ext/publishers/pypi.py View File

@@ -13,6 +13,7 @@ import urlparse
from base64 import standard_b64encode from base64 import standard_b64encode
import ConfigParser import ConfigParser


from hyde._compat import input
from hyde.publisher import Publisher from hyde.publisher import Publisher


from commando.util import getLoggerWithNullHandler from commando.util import getLoggerWithNullHandler
@@ -47,8 +48,8 @@ class PyPI(Publisher):
pass pass
# Prompt for username on command-line # Prompt for username on command-line
if self.username is None: if self.username is None:
print "Username: ",
self.username = raw_input().strip()
print("Username: ",)
self.username = input().strip()
# Try to find password in .pypirc # Try to find password in .pypirc
if self.password is None: if self.password is None:
if pypirc is not None: if pypirc is not None:


+ 2
- 1
hyde/ext/publishers/ssh.py View File

@@ -30,6 +30,7 @@ within the ``deploy/`` directory:
rsync -r -e ssh ./ username@ssh.server.com:/www/username/mysite/ rsync -r -e ssh ./ username@ssh.server.com:/www/username/mysite/


""" """
from hyde._compat import str
from hyde.publisher import Publisher from hyde.publisher import Publisher


from subprocess import Popen, PIPE from subprocess import Popen, PIPE
@@ -54,7 +55,7 @@ class SSH(Publisher):
target=self.target) target=self.target)
deploy_path = self.site.config.deploy_root_path.path deploy_path = self.site.config.deploy_root_path.path


cmd = Popen(command, cwd=unicode(deploy_path), stdout=PIPE, shell=True)
cmd = Popen(command, cwd=str(deploy_path), stdout=PIPE, shell=True)
cmdresult = cmd.communicate()[0] cmdresult = cmd.communicate()[0]
if cmd.returncode: if cmd.returncode:
raise Exception(cmdresult) raise Exception(cmdresult)

+ 29
- 27
hyde/ext/templates/jinja.py View File

@@ -8,8 +8,8 @@ import itertools
import os import os
import re import re
import sys import sys
from urllib import quote, unquote


from hyde._compat import PY3, quote, unquote, str, StringIO
from hyde.exceptions import HydeException from hyde.exceptions import HydeException
from hyde.model import Expando from hyde.model import Expando
from hyde.template import HtmlWrap, Template from hyde.template import HtmlWrap, Template
@@ -79,7 +79,10 @@ def urlencode(ctx, url, safe=None):


@contextfilter @contextfilter
def urldecode(ctx, url): def urldecode(ctx, url):
return unquote(url).decode('utf8')
url = unquote(url)
if not PY3:
url = url.decode('utf8')
return url




@contextfilter @contextfilter
@@ -125,18 +128,17 @@ def asciidoc(env, value):
try: try:
from asciidocapi import AsciiDocAPI from asciidocapi import AsciiDocAPI
except ImportError: except ImportError:
print u"Requires AsciiDoc library to use AsciiDoc tag."
print(u"Requires AsciiDoc library to use AsciiDoc tag.")
raise raise


import StringIO
output = value output = value


asciidoc = AsciiDocAPI() asciidoc = AsciiDocAPI()
asciidoc.options('--no-header-footer') asciidoc.options('--no-header-footer')
result = StringIO.StringIO()
result = StringIO()
asciidoc.execute( asciidoc.execute(
StringIO.StringIO(output.encode('utf-8')), result, backend='html4')
return unicode(result.getvalue(), "utf-8")
StringIO(output.encode('utf-8')), result, backend='html4')
return str(result.getvalue(), "utf-8")




@environmentfilter @environmentfilter
@@ -238,7 +240,7 @@ class Spaceless(Extension):
""" """
Parses the statements and calls back to strip spaces. Parses the statements and calls back to strip spaces.
""" """
lineno = parser.stream.next().lineno
lineno = next(parser.stream).lineno
body = parser.parse_statements(['name:endspaceless'], body = parser.parse_statements(['name:endspaceless'],
drop_needle=True) drop_needle=True)
return nodes.CallBlock( return nodes.CallBlock(
@@ -253,7 +255,7 @@ class Spaceless(Extension):
""" """
if not caller: if not caller:
return '' return ''
return re.sub(r'>\s+<', '><', unicode(caller().strip()))
return re.sub(r'>\s+<', '><', str(caller().strip()))




class Asciidoc(Extension): class Asciidoc(Extension):
@@ -268,7 +270,7 @@ class Asciidoc(Extension):
Parses the statements and defers to the callback Parses the statements and defers to the callback
for asciidoc processing. for asciidoc processing.
""" """
lineno = parser.stream.next().lineno
lineno = next(parser.stream).lineno
body = parser.parse_statements(['name:endasciidoc'], drop_needle=True) body = parser.parse_statements(['name:endasciidoc'], drop_needle=True)


return nodes.CallBlock( return nodes.CallBlock(
@@ -297,7 +299,7 @@ class Markdown(Extension):
Parses the statements and defers to the callback Parses the statements and defers to the callback
for markdown processing. for markdown processing.
""" """
lineno = parser.stream.next().lineno
lineno = next(parser.stream).lineno
body = parser.parse_statements(['name:endmarkdown'], drop_needle=True) body = parser.parse_statements(['name:endmarkdown'], drop_needle=True)


return nodes.CallBlock( return nodes.CallBlock(
@@ -325,7 +327,7 @@ class restructuredText(Extension):
""" """
Simply extract our content Simply extract our content
""" """
lineno = parser.stream.next().lineno
lineno = next(parser.stream).lineno
body = parser.parse_statements( body = parser.parse_statements(
['name:endrestructuredtext'], drop_needle=True) ['name:endrestructuredtext'], drop_needle=True)


@@ -357,7 +359,7 @@ class YamlVar(Extension):
Parses the contained data and defers to the callback to load it as Parses the contained data and defers to the callback to load it as
yaml. yaml.
""" """
lineno = parser.stream.next().lineno
lineno = next(parser.stream).lineno
var = parser.stream.expect('name').value var = parser.stream.expect('name').value
body = parser.parse_statements(['name:endyaml'], drop_needle=True) body = parser.parse_statements(['name:endyaml'], drop_needle=True)
return [ return [
@@ -396,7 +398,7 @@ def parse_kwargs(parser):
if parser.stream.current.test('string'): if parser.stream.current.test('string'):
value = parser.parse_expression() value = parser.parse_expression()
else: else:
value = nodes.Const(parser.stream.next().value)
value = nodes.Const(next(parser.stream).value)
return (name, value) return (name, value)




@@ -413,7 +415,7 @@ class Syntax(Extension):
Parses the statements and defers to the callback for Parses the statements and defers to the callback for
pygments processing. pygments processing.
""" """
lineno = parser.stream.next().lineno
lineno = next(parser.stream).lineno
lex = nodes.Const(None) lex = nodes.Const(None)
filename = nodes.Const(None) filename = nodes.Const(None)


@@ -428,7 +430,7 @@ class Syntax(Extension):
if name == 'lex' \ if name == 'lex' \
else (value1, value) else (value1, value)
else: else:
lex = nodes.Const(parser.stream.next().value)
lex = nodes.Const(next(parser.stream).value)
if parser.stream.skip_if('comma'): if parser.stream.skip_if('comma'):
filename = parser.parse_expression() filename = parser.parse_expression()


@@ -496,10 +498,10 @@ class Reference(Extension):
""" """
Parse the variable name that the content must be assigned to. Parse the variable name that the content must be assigned to.
""" """
token = parser.stream.next()
token = next(parser.stream)
lineno = token.lineno lineno = token.lineno
tag = token.value tag = token.value
name = parser.stream.next().value
name = next(parser.stream).value
body = parser.parse_statements(['name:end%s' % tag], drop_needle=True) body = parser.parse_statements(['name:end%s' % tag], drop_needle=True)
return nodes.CallBlock(self.call_method('_render_output', return nodes.CallBlock(self.call_method('_render_output',
args=[ args=[
@@ -533,12 +535,12 @@ class Refer(Extension):
""" """
Parse the referred template and the namespace. Parse the referred template and the namespace.
""" """
token = parser.stream.next()
token = next(parser.stream)
lineno = token.lineno lineno = token.lineno
parser.stream.expect('name:to') parser.stream.expect('name:to')
template = parser.parse_expression() template = parser.parse_expression()
parser.stream.expect('name:as') parser.stream.expect('name:as')
namespace = parser.stream.next().value
namespace = next(parser.stream).value
includeNode = nodes.Include(lineno=lineno) includeNode = nodes.Include(lineno=lineno)
includeNode.with_context = True includeNode.with_context = True
includeNode.ignore_missing = False includeNode.ignore_missing = False
@@ -623,11 +625,11 @@ class HydeLoader(FileSystemLoader):
config = site.config if hasattr(site, 'config') else None config = site.config if hasattr(site, 'config') else None
if config: if config:
super(HydeLoader, self).__init__([ super(HydeLoader, self).__init__([
unicode(config.content_root_path),
unicode(config.layout_root_path),
str(config.content_root_path),
str(config.layout_root_path),
]) ])
else: else:
super(HydeLoader, self).__init__(unicode(sitepath))
super(HydeLoader, self).__init__(str(sitepath))


self.site = site self.site = site
self.preprocessor = preprocessor self.preprocessor = preprocessor
@@ -650,10 +652,10 @@ class HydeLoader(FileSystemLoader):
except UnicodeDecodeError: except UnicodeDecodeError:
HydeException.reraise( HydeException.reraise(
"Unicode error when processing %s" % template, sys.exc_info()) "Unicode error when processing %s" % template, sys.exc_info())
except TemplateError, exc:
except TemplateError as exc:
HydeException.reraise('Error when processing %s: %s' % ( HydeException.reraise('Error when processing %s: %s' % (
template, template,
unicode(exc)
str(exc)
), sys.exc_info()) ), sys.exc_info())


if self.preprocessor: if self.preprocessor:
@@ -800,9 +802,9 @@ class Jinja2Template(Template):
from jinja2.meta import find_referenced_templates from jinja2.meta import find_referenced_templates
try: try:
ast = self.env.parse(text) ast = self.env.parse(text)
except Exception, e:
except Exception as e:
HydeException.reraise( HydeException.reraise(
"Error processing %s: \n%s" % (path, unicode(e)),
"Error processing %s: \n%s" % (path, str(e)),
sys.exc_info()) sys.exc_info())


tpls = find_referenced_templates(ast) tpls = find_referenced_templates(ast)


+ 1
- 1
hyde/generator.py View File

@@ -336,7 +336,7 @@ class Generator(object):
try: try:
text = self.template.render_resource(resource, text = self.template.render_resource(resource,
context) context)
except Exception, e:
except Exception as e:
HydeException.reraise("Error occurred when processing" HydeException.reraise("Error occurred when processing"
"template: [%s]: %s" % "template: [%s]: %s" %
(resource, repr(e)), (resource, repr(e)),


+ 3
- 1
hyde/layout.py View File

@@ -6,6 +6,8 @@ import os


from fswrap import File, Folder from fswrap import File, Folder


from hyde._compat import str

HYDE_DATA = "HYDE_DATA" HYDE_DATA = "HYDE_DATA"
LAYOUTS = "layouts" LAYOUTS = "layouts"


@@ -39,6 +41,6 @@ class Layout(object):
Finds the layout folder from the given root folder. Finds the layout folder from the given root folder.
If it does not exist, return None If it does not exist, return None
""" """
layouts_folder = Folder(unicode(root)).child_folder(LAYOUTS)
layouts_folder = Folder(str(root)).child_folder(LAYOUTS)
layout_folder = layouts_folder.child_folder(layout_name) layout_folder = layouts_folder.child_folder(layout_name)
return layout_folder if layout_folder.exists else None return layout_folder if layout_folder.exists else None

+ 6
- 5
hyde/model.py View File

@@ -5,11 +5,12 @@ Contains data structures and utilities for hyde.
import codecs import codecs
import yaml import yaml
from datetime import datetime from datetime import datetime
from UserDict import IterableUserDict


from commando.util import getLoggerWithNullHandler from commando.util import getLoggerWithNullHandler
from fswrap import File, Folder from fswrap import File, Folder


from hyde._compat import iteritems, str, UserDict

logger = getLoggerWithNullHandler('hyde.engine') logger = getLoggerWithNullHandler('hyde.engine')


SEQS = (tuple, list, set, frozenset) SEQS = (tuple, list, set, frozenset)
@@ -45,7 +46,7 @@ class Expando(object):
Returns an iterator for all the items in the Returns an iterator for all the items in the
dictionary as key value pairs. dictionary as key value pairs.
""" """
return self.__dict__.iteritems()
return iteritems(self.__dict__)


def update(self, d): def update(self, d):
""" """
@@ -63,10 +64,10 @@ class Expando(object):
Sets the expando attribute after Sets the expando attribute after
transforming the value. transforming the value.
""" """
setattr(self, unicode(key).encode('utf-8'), make_expando(value))
setattr(self, str(key), make_expando(value))


def __repr__(self): def __repr__(self):
return unicode(self.to_dict())
return str(self.to_dict())


def to_dict(self): def to_dict(self):
""" """
@@ -128,7 +129,7 @@ class Context(object):
return context return context




class Dependents(IterableUserDict):
class Dependents(UserDict):


""" """
Represents the dependency graph for hyde. Represents the dependency graph for hyde.


+ 7
- 7
hyde/plugin.py View File

@@ -2,6 +2,7 @@
""" """
Contains definition for a plugin protocol and other utiltities. Contains definition for a plugin protocol and other utiltities.
""" """
from hyde._compat import str
from hyde.exceptions import HydeException from hyde.exceptions import HydeException
from hyde.util import first_match, discover_executable from hyde.util import first_match, discover_executable
from hyde.model import Expando from hyde.model import Expando
@@ -17,6 +18,8 @@ import sys
from commando.util import getLoggerWithNullHandler, load_python_object from commando.util import getLoggerWithNullHandler, load_python_object
from fswrap import File from fswrap import File


from hyde._compat import with_metaclass

logger = getLoggerWithNullHandler('hyde.engine') logger = getLoggerWithNullHandler('hyde.engine')


# Plugins have been reorganized. Map old plugin paths to new. # Plugins have been reorganized. Map old plugin paths to new.
@@ -106,12 +109,11 @@ class PluginProxy(object):
"Unknown plugin method [%s] called." % method_name) "Unknown plugin method [%s] called." % method_name)




class Plugin(object):
class Plugin(with_metaclass(abc.ABCMeta)):


""" """
The plugin protocol The plugin protocol
""" """
__metaclass__ = abc.ABCMeta


def __init__(self, site): def __init__(self, site):
super(Plugin, self).__init__() super(Plugin, self).__init__()
@@ -440,14 +442,14 @@ class CLTransformer(Plugin):
try: try:
self.logger.debug( self.logger.debug(
"Calling executable [%s] with arguments %s" % "Calling executable [%s] with arguments %s" %
(args[0], unicode(args[1:])))
(args[0], str(args[1:])))
return subprocess.check_output(args) return subprocess.check_output(args)
except subprocess.CalledProcessError, error:
except subprocess.CalledProcessError as error:
self.logger.error(error.output) self.logger.error(error.output)
raise raise




class TextyPlugin(Plugin):
class TextyPlugin(with_metaclass(abc.ABCMeta, Plugin)):


""" """
Base class for text preprocessing plugins. Base class for text preprocessing plugins.
@@ -457,8 +459,6 @@ class TextyPlugin(Plugin):
can inherit from this class. can inherit from this class.
""" """


__metaclass__ = abc.ABCMeta

def __init__(self, site): def __init__(self, site):
super(TextyPlugin, self).__init__(site) super(TextyPlugin, self).__init__(site)
self.open_pattern = self.default_open_pattern self.open_pattern = self.default_open_pattern


+ 3
- 3
hyde/publisher.py View File

@@ -3,20 +3,20 @@ from operator import attrgetter


from commando.util import getLoggerWithNullHandler, load_python_object from commando.util import getLoggerWithNullHandler, load_python_object


from hyde._compat import with_metaclass

""" """
Contains abstract classes and utilities that help publishing a website to a Contains abstract classes and utilities that help publishing a website to a
server. server.
""" """




class Publisher(object):
class Publisher(with_metaclass(abc.ABCMeta)):


""" """
The abstract base class for publishers. The abstract base class for publishers.
""" """


__metaclass__ = abc.ABCMeta

def __init__(self, site, settings, message): def __init__(self, site, settings, message):
super(Publisher, self).__init__() super(Publisher, self).__init__()
self.logger = getLoggerWithNullHandler( self.logger = getLoggerWithNullHandler(


+ 14
- 11
hyde/server.py View File

@@ -4,13 +4,13 @@ Contains classes and utilities for serving a site
generated from hyde. generated from hyde.
""" """
import threading import threading
import urlparse
import urllib import urllib
import traceback import traceback
from datetime import datetime from datetime import datetime
from SimpleHTTPServer import SimpleHTTPRequestHandler
from BaseHTTPServer import HTTPServer



from hyde._compat import (HTTPServer, iteritems, parse, PY3,
SimpleHTTPRequestHandler, unquote)
from hyde.generator import Generator from hyde.generator import Generator


from fswrap import File, Folder from fswrap import File, Folder
@@ -35,8 +35,8 @@ class HydeRequestHandler(SimpleHTTPRequestHandler):
""" """
self.server.request_time = datetime.now() self.server.request_time = datetime.now()
logger.debug("Processing request: [%s]" % self.path) logger.debug("Processing request: [%s]" % self.path)
result = urlparse.urlparse(self.path)
query = urlparse.parse_qs(result.query)
result = parse.urlparse(self.path)
query = parse.parse_qs(result.query)
if 'refresh' in query or result.query == 'refresh': if 'refresh' in query or result.query == 'refresh':
self.server.regenerate() self.server.regenerate()
if 'refresh' in query: if 'refresh' in query:
@@ -44,7 +44,7 @@ class HydeRequestHandler(SimpleHTTPRequestHandler):
parts = list(tuple(result)) parts = list(tuple(result))
parts[4] = urllib.urlencode(query) parts[4] = urllib.urlencode(query)
parts = tuple(parts) parts = tuple(parts)
new_url = urlparse.urlunparse(parts)
new_url = parse.urlunparse(parts)
logger.info('Redirecting... [%s]' % new_url) logger.info('Redirecting... [%s]' % new_url)
self.redirect(new_url) self.redirect(new_url)
else: else:
@@ -56,7 +56,10 @@ class HydeRequestHandler(SimpleHTTPRequestHandler):
referring to the `site` variable in the server. referring to the `site` variable in the server.
""" """
site = self.server.site site = self.server.site
result = urlparse.urlparse(urllib.unquote(self.path).decode('utf-8'))
path = unquote(self.path)
if not PY3:
path = path.decode('utf-8')
result = parse.urlparse(path)
logger.debug( logger.debug(
"Trying to load file based on request: [%s]" % result.path) "Trying to load file based on request: [%s]" % result.path)
path = result.path.lstrip('/') path = result.path.lstrip('/')
@@ -150,7 +153,7 @@ class HydeWebServer(HTTPServer):
except AttributeError: except AttributeError:
extensions = {} extensions = {}


for extension, type in extensions.iteritems():
for extension, type in iteritems(extensions):
ext = "." + extension if not extension == 'default' else '' ext = "." + extension if not extension == 'default' else ''
HydeRequestHandler.extensions_map[ext] = type HydeRequestHandler.extensions_map[ext] = type


@@ -165,7 +168,7 @@ class HydeWebServer(HTTPServer):
self.site.config.reload() self.site.config.reload()
self.site.load() self.site.load()
self.generator.generate_all(incremental=False) self.generator.generate_all(incremental=False)
except Exception, exception:
except Exception as exception:
logger.error('Error occured when regenerating the site [%s]' logger.error('Error occured when regenerating the site [%s]'
% exception.message) % exception.message)
logger.debug(traceback.format_exc()) logger.debug(traceback.format_exc())
@@ -182,7 +185,7 @@ class HydeWebServer(HTTPServer):
try: try:
logger.debug('Serving node [%s]' % node) logger.debug('Serving node [%s]' % node)
self.generator.generate_node(node, incremental=True) self.generator.generate_node(node, incremental=True)
except Exception, exception:
except Exception as exception:
logger.error( logger.error(
'Error [%s] occured when generating the node [%s]' 'Error [%s] occured when generating the node [%s]'
% (repr(exception), node)) % (repr(exception), node))
@@ -201,7 +204,7 @@ class HydeWebServer(HTTPServer):
try: try:
logger.debug('Serving resource [%s]' % resource) logger.debug('Serving resource [%s]' % resource)
self.generator.generate_resource(resource, incremental=True) self.generator.generate_resource(resource, incremental=True)
except Exception, exception:
except Exception as exception:
logger.error( logger.error(
'Error [%s] occured when serving the resource [%s]' 'Error [%s] occured when serving the resource [%s]'
% (repr(exception), resource)) % (repr(exception), resource))


+ 14
- 14
hyde/site.py View File

@@ -5,10 +5,9 @@ Parses & holds information about the site to be generated.
import os import os
import fnmatch import fnmatch
import sys import sys
import urlparse
from functools import wraps from functools import wraps
from urllib import quote


from hyde._compat import parse, quote, str
from hyde.exceptions import HydeException from hyde.exceptions import HydeException
from hyde.model import Config from hyde.model import Config


@@ -19,7 +18,7 @@ from fswrap import FS, File, Folder
def path_normalized(f): def path_normalized(f):
@wraps(f) @wraps(f)
def wrapper(self, path): def wrapper(self, path):
return f(self, unicode(path).replace('/', os.sep))
return f(self, str(path).replace('/', os.sep))
return wrapper return wrapper


logger = getLoggerWithNullHandler('hyde.engine') logger = getLoggerWithNullHandler('hyde.engine')
@@ -138,7 +137,7 @@ class Node(Processable):
self.root = self self.root = self
self.module = None self.module = None
self.site = None self.site = None
self.source_folder = Folder(unicode(source_folder))
self.source_folder = Folder(str(source_folder))
self.parent = parent self.parent = parent
if parent: if parent:
self.root = self.parent.root self.root = self.parent.root
@@ -249,7 +248,7 @@ class RootNode(Node):
""" """
if Folder(path) == self.source_folder: if Folder(path) == self.source_folder:
return self return self
return self.node_map.get(unicode(Folder(path)), None)
return self.node_map.get(str(Folder(path)), None)


@path_normalized @path_normalized
def node_from_relative_path(self, relative_path): def node_from_relative_path(self, relative_path):
@@ -258,7 +257,7 @@ class RootNode(Node):
If no match is found it returns None. If no match is found it returns None.
""" """
return self.node_from_path( return self.node_from_path(
self.source_folder.child(unicode(relative_path)))
self.source_folder.child(str(relative_path)))


@path_normalized @path_normalized
def resource_from_path(self, path): def resource_from_path(self, path):
@@ -266,7 +265,7 @@ class RootNode(Node):
Gets the resource that maps to the given path. Gets the resource that maps to the given path.
If no match is found it returns None. If no match is found it returns None.
""" """
return self.resource_map.get(unicode(File(path)), None)
return self.resource_map.get(str(File(path)), None)


@path_normalized @path_normalized
def resource_from_relative_path(self, relative_path): def resource_from_relative_path(self, relative_path):
@@ -282,7 +281,7 @@ class RootNode(Node):
Handles the case where the relative deploy path of a Handles the case where the relative deploy path of a
resource has changed. resource has changed.
""" """
self.resource_deploy_map[unicode(item.relative_deploy_path)] = item
self.resource_deploy_map[str(item.relative_deploy_path)] = item


@path_normalized @path_normalized
def resource_from_relative_deploy_path(self, relative_deploy_path): def resource_from_relative_deploy_path(self, relative_deploy_path):
@@ -323,7 +322,7 @@ class RootNode(Node):
node = parent if parent else self node = parent if parent else self
for h_folder in hierarchy: for h_folder in hierarchy:
node = node.add_child_node(h_folder) node = node.add_child_node(h_folder)
self.node_map[unicode(h_folder)] = node
self.node_map[str(h_folder)] = node
logger.debug("Added node [%s] to [%s]" % ( logger.debug("Added node [%s] to [%s]" % (
node.relative_path, self.source_folder)) node.relative_path, self.source_folder))


@@ -352,7 +351,7 @@ class RootNode(Node):
if not node: if not node:
node = self.add_node(afile.parent) node = self.add_node(afile.parent)
resource = node.add_child_resource(afile) resource = node.add_child_resource(afile)
self.resource_map[unicode(afile)] = resource
self.resource_map[str(afile)] = resource
relative_path = resource.relative_path relative_path = resource.relative_path
resource.simple_copy = any(fnmatch.fnmatch(relative_path, pattern) resource.simple_copy = any(fnmatch.fnmatch(relative_path, pattern)
for pattern in self.site.config.simple_copy) for pattern in self.site.config.simple_copy)
@@ -395,10 +394,11 @@ class RootNode(Node):




def _encode_path(base, path, safe): def _encode_path(base, path, safe):
base = base.strip().replace(os.sep, '/').encode('utf-8')
path = path.strip().replace(os.sep, '/').encode('utf-8')
base = base.strip().replace(os.sep, '/')
path = path.strip().replace(os.sep, '/')
path = quote(path, safe) if safe is not None else quote(path) path = quote(path, safe) if safe is not None else quote(path)
return base.rstrip('/') + '/' + path.lstrip('/')
full_path = base.rstrip('/') + '/' + path.lstrip('/')
return full_path




class Site(object): class Site(object):
@@ -471,7 +471,7 @@ class Site(object):
configuration and returns the appropriate url. The return value configuration and returns the appropriate url. The return value
is url encoded. is url encoded.
""" """
if urlparse.urlparse(path)[:2] != ("", ""):
if parse.urlparse(path)[:2] != ("", ""):
return path return path


if self.is_media(path): if self.is_media(path):


+ 6
- 4
hyde/template.py View File

@@ -3,6 +3,7 @@
""" """
Abstract classes and utilities for template engines Abstract classes and utilities for template engines
""" """
from hyde._compat import with_metaclass
from hyde.exceptions import HydeException from hyde.exceptions import HydeException


import abc import abc
@@ -30,24 +31,25 @@ class HtmlWrap(object):
PyQuery = None PyQuery = None
self.q = PyQuery(html) if PyQuery else None self.q = PyQuery(html) if PyQuery else None


def __unicode__(self):
def __str__(self):
return self.raw return self.raw


# Support __unicode__ as well as __str__ for backward compatibility.
__unicode__ = __str__

def __call__(self, selector=None): def __call__(self, selector=None):
if not self.q: if not self.q:
return self.raw return self.raw
return self.q(selector).html() return self.q(selector).html()




class Template(object):
class Template(with_metaclass(abc.ABCMeta)):


""" """
Interface for hyde template engines. To use a different template engine, Interface for hyde template engines. To use a different template engine,
the following interface must be implemented. the following interface must be implemented.
""" """


__metaclass__ = abc.ABCMeta

def __init__(self, sitepath): def __init__(self, sitepath):
self.sitepath = sitepath self.sitepath = sitepath
self.logger = getLoggerWithNullHandler(self.__class__.__name__) self.logger = getLoggerWithNullHandler(self.__class__.__name__)


+ 5
- 3
hyde/util.py View File

@@ -3,7 +3,9 @@ Module for python 2.6 compatibility.
""" """
import os import os
from functools import partial from functools import partial
from itertools import izip, tee
from itertools import tee

from hyde._compat import str, zip




def make_method(method_name, method_): def make_method(method_name, method_):
@@ -26,7 +28,7 @@ def add_method(obj, method_name, method_, *args, **kwargs):
def pairwalk(iterable): def pairwalk(iterable):
a, b = tee(iterable) a, b = tee(iterable)
next(b, None) next(b, None)
return izip(a, b)
return zip(a, b)




def first_match(predicate, iterable): def first_match(predicate, iterable):
@@ -49,7 +51,7 @@ def discover_executable(name, sitepath):
# Check if an executable can be found in the site path first. # Check if an executable can be found in the site path first.
# If not check the os $PATH for its presence. # If not check the os $PATH for its presence.


paths = [unicode(sitepath)] + os.environ['PATH'].split(os.pathsep)
paths = [str(sitepath)] + os.environ['PATH'].split(os.pathsep)
for path in paths: for path in paths:
full_name = os.path.join(path, name) full_name = os.path.join(path, name)
if os.path.exists(full_name): if os.path.exists(full_name):


+ 10
- 6
setup.py View File

@@ -72,9 +72,8 @@ def find_package_data(


bad_name = True bad_name = True
if show_ignored: if show_ignored:
print >> sys.stderr, (
"Directory %s ignored by pattern %s"
% (fn, pattern))
msg = "Directory {} ignored by pattern {}"
sys.stderr.write(msg.format(fn, pattern))
break break
if bad_name: if bad_name:
continue continue
@@ -96,9 +95,8 @@ def find_package_data(


bad_name = True bad_name = True
if show_ignored: if show_ignored:
print >> sys.stderr, (
"File %s ignored by pattern %s"
% (fn, pattern))
msg = "File {} ignored by pattern {}"
sys.stderr.write(msg.format(fn, pattern))
break break
if bad_name: if bad_name:
continue continue
@@ -157,6 +155,12 @@ setup(name=PROJECT,
'Operating System :: POSIX', 'Operating System :: POSIX',
'Operating System :: Microsoft :: Windows', 'Operating System :: Microsoft :: Windows',
'Programming Language :: Python', 'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Topic :: Software Development', 'Topic :: Software Development',
'Topic :: Software Development :: Build Tools', 'Topic :: Software Development :: Build Tools',
'Topic :: Software Development :: Code Generators', 'Topic :: Software Development :: Code Generators',


+ 4
- 4
tests/ext/test_sass.py View File

@@ -41,8 +41,8 @@ class TestSass(object):
assert target.exists assert target.exists
text = target.read_all() text = target.read_all()
expected_text = File(SCSS_SOURCE.child('expected-sass.css')).read_all() expected_text = File(SCSS_SOURCE.child('expected-sass.css')).read_all()
print "TEXT" + "-" * 80
print text
print "-" * 80
print expected_text
print("TEXT" + "-" * 80)
print(text)
print("-" * 80)
print(expected_text)
assert_no_diff(expected_text, text) assert_no_diff(expected_text, text)

+ 2
- 2
tests/ext/test_textlinks.py View File

@@ -3,9 +3,9 @@ Use nose
`$ pip install nose` `$ pip install nose`
`$ nosetests` `$ nosetests`
""" """
from hyde._compat import quote
from hyde.generator import Generator from hyde.generator import Generator
from hyde.site import Site from hyde.site import Site
from urllib import quote


from fswrap import File from fswrap import File


@@ -46,7 +46,7 @@ class TestTextlinks(object):
site.config.media_url = '/media' site.config.media_url = '/media'
tlink = File(site.content.source_folder.child('tlink.html')) tlink = File(site.content.source_folder.child('tlink.html'))
tlink.write(text % d) tlink.write(text % d)
print tlink.read_all()
print(tlink.read_all())
gen = Generator(site) gen = Generator(site)
gen.generate_all() gen.generate_all()
f = File(site.config.deploy_root_path.child(tlink.name)) f = File(site.config.deploy_root_path.child(tlink.name))


+ 15
- 14
tests/test_initialize.py View File

@@ -5,7 +5,7 @@ Use nose
`$ nosetests` `$ nosetests`
""" """


from hyde._compat import str
from hyde.engine import Engine from hyde.engine import Engine
from hyde.exceptions import HydeException from hyde.exceptions import HydeException
from hyde.layout import Layout from hyde.layout import Layout
@@ -42,7 +42,7 @@ def delete_test_site_at_user():
def test_ensure_exception_when_site_yaml_exists(): def test_ensure_exception_when_site_yaml_exists():
e = Engine(raise_exceptions=True) e = Engine(raise_exceptions=True)
File(TEST_SITE.child('site.yaml')).write("Hey") File(TEST_SITE.child('site.yaml')).write("Hey")
e.run(e.parse(['-s', unicode(TEST_SITE), 'create']))
e.run(e.parse(['-s', str(TEST_SITE), 'create']))




@raises(HydeException) @raises(HydeException)
@@ -50,7 +50,7 @@ def test_ensure_exception_when_site_yaml_exists():
def test_ensure_exception_when_content_folder_exists(): def test_ensure_exception_when_content_folder_exists():
e = Engine(raise_exceptions=True) e = Engine(raise_exceptions=True)
TEST_SITE.child_folder('content').make() TEST_SITE.child_folder('content').make()
e.run(e.parse(['-s', unicode(TEST_SITE), 'create']))
e.run(e.parse(['-s', str(TEST_SITE), 'create']))




@raises(HydeException) @raises(HydeException)
@@ -58,13 +58,13 @@ def test_ensure_exception_when_content_folder_exists():
def test_ensure_exception_when_layout_folder_exists(): def test_ensure_exception_when_layout_folder_exists():
e = Engine(raise_exceptions=True) e = Engine(raise_exceptions=True)
TEST_SITE.child_folder('layout').make() TEST_SITE.child_folder('layout').make()
e.run(e.parse(['-s', unicode(TEST_SITE), 'create']))
e.run(e.parse(['-s', str(TEST_SITE), 'create']))




@with_setup(create_test_site, delete_test_site) @with_setup(create_test_site, delete_test_site)
def test_ensure_no_exception_when_empty_site_exists(): def test_ensure_no_exception_when_empty_site_exists():
e = Engine(raise_exceptions=True) e = Engine(raise_exceptions=True)
e.run(e.parse(['-s', unicode(TEST_SITE), 'create']))
e.run(e.parse(['-s', str(TEST_SITE), 'create']))
verify_site_contents(TEST_SITE, Layout.find_layout()) verify_site_contents(TEST_SITE, Layout.find_layout())




@@ -72,16 +72,16 @@ def test_ensure_no_exception_when_empty_site_exists():
def test_ensure_no_exception_when_forced(): def test_ensure_no_exception_when_forced():
e = Engine(raise_exceptions=True) e = Engine(raise_exceptions=True)
TEST_SITE.child_folder('layout').make() TEST_SITE.child_folder('layout').make()
e.run(e.parse(['-s', unicode(TEST_SITE), 'create', '-f']))
e.run(e.parse(['-s', str(TEST_SITE), 'create', '-f']))
verify_site_contents(TEST_SITE, Layout.find_layout()) verify_site_contents(TEST_SITE, Layout.find_layout())
TEST_SITE.delete() TEST_SITE.delete()
TEST_SITE.child_folder('content').make() TEST_SITE.child_folder('content').make()
e.run(e.parse(['-s', unicode(TEST_SITE), 'create', '-f']))
e.run(e.parse(['-s', str(TEST_SITE), 'create', '-f']))
verify_site_contents(TEST_SITE, Layout.find_layout()) verify_site_contents(TEST_SITE, Layout.find_layout())
TEST_SITE.delete() TEST_SITE.delete()
TEST_SITE.make() TEST_SITE.make()
File(TEST_SITE.child('site.yaml')).write("Hey") File(TEST_SITE.child('site.yaml')).write("Hey")
e.run(e.parse(['-s', unicode(TEST_SITE), 'create', '-f']))
e.run(e.parse(['-s', str(TEST_SITE), 'create', '-f']))
verify_site_contents(TEST_SITE, Layout.find_layout()) verify_site_contents(TEST_SITE, Layout.find_layout())




@@ -89,7 +89,7 @@ def test_ensure_no_exception_when_forced():
def test_ensure_no_exception_when_sitepath_does_not_exist(): def test_ensure_no_exception_when_sitepath_does_not_exist():
e = Engine(raise_exceptions=True) e = Engine(raise_exceptions=True)
TEST_SITE.delete() TEST_SITE.delete()
e.run(e.parse(['-s', unicode(TEST_SITE), 'create', '-f']))
e.run(e.parse(['-s', str(TEST_SITE), 'create', '-f']))
verify_site_contents(TEST_SITE, Layout.find_layout()) verify_site_contents(TEST_SITE, Layout.find_layout())




@@ -97,7 +97,7 @@ def test_ensure_no_exception_when_sitepath_does_not_exist():
def test_ensure_can_create_site_at_user(): def test_ensure_can_create_site_at_user():
e = Engine(raise_exceptions=True) e = Engine(raise_exceptions=True)
TEST_SITE_AT_USER.delete() TEST_SITE_AT_USER.delete()
e.run(e.parse(['-s', unicode(TEST_SITE_AT_USER), 'create', '-f']))
e.run(e.parse(['-s', str(TEST_SITE_AT_USER), 'create', '-f']))
verify_site_contents(TEST_SITE_AT_USER, Layout.find_layout()) verify_site_contents(TEST_SITE_AT_USER, Layout.find_layout())




@@ -107,9 +107,10 @@ def verify_site_contents(site, layout):
assert site.child_folder('layout').exists assert site.child_folder('layout').exists
assert File(site.child('info.yaml')).exists assert File(site.child('info.yaml')).exists


expected = map(
lambda f: f.get_relative_path(layout), layout.walker.walk_all())
actual = map(lambda f: f.get_relative_path(site), site.walker.walk_all())
expected = list(map(
lambda f: f.get_relative_path(layout), layout.walker.walk_all()))
actual = list(map(
lambda f: f.get_relative_path(site), site.walker.walk_all()))
assert actual assert actual
assert expected assert expected


@@ -122,4 +123,4 @@ def verify_site_contents(site, layout):
@with_setup(create_test_site, delete_test_site) @with_setup(create_test_site, delete_test_site)
def test_ensure_exception_when_layout_is_invalid(): def test_ensure_exception_when_layout_is_invalid():
e = Engine(raise_exceptions=True) e = Engine(raise_exceptions=True)
e.run(e.parse(['-s', unicode(TEST_SITE), 'create', '-l', 'junk']))
e.run(e.parse(['-s', str(TEST_SITE), 'create', '-l', 'junk']))

+ 8
- 1
tests/test_jinja2template.py View File

@@ -9,6 +9,7 @@ Some code borrowed from rwbench.py from the jinja2 examples
from datetime import datetime from datetime import datetime
from random import choice, randrange from random import choice, randrange


from hyde._compat import PY3
from hyde.ext.templates.jinja import Jinja2Template from hyde.ext.templates.jinja import Jinja2Template
from hyde.site import Site from hyde.site import Site
from hyde.generator import Generator from hyde.generator import Generator
@@ -16,6 +17,7 @@ from hyde.model import Config


from fswrap import File from fswrap import File
from jinja2.utils import generate_lorem_ipsum from jinja2.utils import generate_lorem_ipsum
from nose.plugins.skip import SkipTest
from nose.tools import nottest from nose.tools import nottest
from pyquery import PyQuery from pyquery import PyQuery


@@ -49,7 +51,7 @@ class User(object):
self.username = username self.username = username




users = map(User, [u'John Doe', u'Jane Doe', u'Peter Somewhat'])
users = list(map(User, [u'John Doe', u'Jane Doe', u'Peter Somewhat']))
articles = map(Article, range(20)) articles = map(Article, range(20))
navigation = [ navigation = [
('index', 'Index'), ('index', 'Index'),
@@ -132,6 +134,11 @@ def test_spaceless():




def test_asciidoc(): def test_asciidoc():
if PY3:
# asciidoc is not supported under Python 3. Supporting it is out
# of the scope of this project, so its tests are simply skipped
# when run under Python 3.
raise SkipTest
source = """ source = """
{%asciidoc%} {%asciidoc%}
== Heading 2 == == Heading 2 ==


+ 2
- 1
tests/test_layout.py View File

@@ -6,6 +6,7 @@ Use nose
""" """
import os import os


from hyde._compat import str
from hyde.layout import Layout, HYDE_DATA, LAYOUTS from hyde.layout import Layout, HYDE_DATA, LAYOUTS


from fswrap import File from fswrap import File
@@ -36,7 +37,7 @@ def test_find_layout_from_env_var():
f = Layout.find_layout() f = Layout.find_layout()
LAYOUT_ROOT.make() LAYOUT_ROOT.make()
f.copy_to(LAYOUT_ROOT) f.copy_to(LAYOUT_ROOT)
os.environ[HYDE_DATA] = unicode(DATA_ROOT)
os.environ[HYDE_DATA] = str(DATA_ROOT)
f = Layout.find_layout() f = Layout.find_layout()
assert f.parent == LAYOUT_ROOT assert f.parent == LAYOUT_ROOT
assert f.name == 'basic' assert f.name == 'basic'


+ 2
- 2
tests/test_plugin.py View File

@@ -24,14 +24,14 @@ class PluginLoaderStub(Plugin):
class NoReturnPlugin(Plugin): class NoReturnPlugin(Plugin):


def begin_text_resource(self, resource, text): def begin_text_resource(self, resource, text):
print "NoReturnPlugin"
print("NoReturnPlugin")
return None return None




class ConstantReturnPlugin(Plugin): class ConstantReturnPlugin(Plugin):


def begin_text_resource(self, resource, text): def begin_text_resource(self, resource, text):
print "ConstantReturnPlugin"
print("ConstantReturnPlugin")
return "Jam" return "Jam"






+ 3
- 3
tests/test_site.py View File

@@ -5,8 +5,8 @@ Use nose
`$ nosetests` `$ nosetests`
""" """
import yaml import yaml
from urllib import quote


from hyde._compat import quote
from hyde.model import Config from hyde.model import Config
from hyde.site import Node, RootNode, Site from hyde.site import Node, RootNode, Site


@@ -242,8 +242,8 @@ class TestSiteWithConfig(object):
s = Site(self.SITE_PATH, config=self.config) s = Site(self.SITE_PATH, config=self.config)
s.load() s.load()
path = '".jpg/abc' path = '".jpg/abc'
print s.content_url(path, "")
print "/" + quote(path, "")
print(s.content_url(path, ""))
print("/" + quote(path, ""))
assert s.content_url(path, "") == "/" + quote(path, "") assert s.content_url(path, "") == "/" + quote(path, "")


def test_media_url(self): def test_media_url(self):


+ 3
- 1
tests/util.py View File

@@ -1,13 +1,15 @@
import re import re
import difflib import difflib


from hyde._compat import str



def strip_spaces_between_tags(value): def strip_spaces_between_tags(value):
""" """
Stolen from `django.util.html` Stolen from `django.util.html`
Returns the given HTML with spaces between tags removed. Returns the given HTML with spaces between tags removed.
""" """
return re.sub(r'>\s+<', '><', unicode(value))
return re.sub(r'>\s+<', '><', str(value))




def assert_no_diff(expected, out): def assert_no_diff(expected, out):


+ 3
- 3
tox.ini View File

@@ -1,5 +1,5 @@
[tox] [tox]
envlist = py27,pep8
envlist = py{27,33,34,35},pep8


[testenv] [testenv]
usedevelop = True usedevelop = True
@@ -8,11 +8,11 @@ sitepackages = True
# Needed for asciidoc # Needed for asciidoc
passenv = PYTHONPATH passenv = PYTHONPATH
deps = -r{toxinidir}/dev-req.txt deps = -r{toxinidir}/dev-req.txt
commands = nosetests
commands = nosetests {posargs}


[testenv:pep8] [testenv:pep8]
deps = flake8 deps = flake8
commands = flake8
commands = flake8 {posargs}


[flake8] [flake8]
exclude = .tox exclude = .tox


Loading…
Cancel
Save