Add initial (experimental) Python 3 support.main
@@ -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: | ||||
@@ -1,3 +1,9 @@ | |||||
Version TBA | |||||
=========== | |||||
* Experimental Python 3 support | |||||
Version 0.8.9 (2015-11-09) | Version 0.8.9 (2015-11-09) | ||||
=========================================================== | =========================================================== | ||||
@@ -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, {}) |
@@ -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) |
@@ -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 |
@@ -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 | ||||
@@ -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) |
@@ -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) |
@@ -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) | ||||
@@ -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: | ||||
@@ -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 | ||||
@@ -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) |
@@ -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: | ||||
@@ -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: | ||||
@@ -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) |
@@ -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) | ||||
@@ -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)), | ||||
@@ -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 |
@@ -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. | ||||
@@ -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,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( | ||||
@@ -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)) | ||||
@@ -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): | ||||
@@ -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__) | ||||
@@ -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): | ||||
@@ -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', | ||||
@@ -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) |
@@ -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)) | ||||
@@ -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'])) |
@@ -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 == | ||||
@@ -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' | ||||
@@ -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" | ||||
@@ -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): | ||||
@@ -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): | ||||
@@ -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 | ||||