All tests* currently pass under Python 2.7 and Python 3.3 - 3.5. This does not mean that Python 3 support is stable, however. As a plan forward, we should encourage interested users to run Hyde with Python 3, report bugs, and submit patches. This change references the Python 3 Support issue on GitHub (#270). Once this becomes a bit more tested and stable, that issue should be closed, and Hyde should announce official, stable support for Python 3 (i.e. new changes must also support 3, issues with running hyde under 3 should be considered bugs rather than feature requests, etc.). Changes: - Add ``hyde/_compat.py`` for 2-to-3 compatibility helpers. - Replace uses of 2-specific code with their ``hyde._compat`` versions. - Tests remain largely unchanged (for good reason). The exceptions here are to ``print`` function calls, and ``str`` type usage, and compatibility imports. - Replace explicit calls to ``foo.next()`` with ``next(foo)``. This keeps code portable between Python 2's ``next`` and Python 3's ``__next__`` methods without the need for any compatibility functions and is the recommended API for interacting with these methods. - Replace deprecated (and, in Python 3, removed) ``except Exception, e:`` statements with their updated ``except Exception as e:`` versions. - Replace print statements with print function calls (because of how parentheses work in Python, this is transparently cross-version-compatible). - Add Python 3.3, 3.4, and 3.5 to ``tox.ini``, ``.travis.yml``, and classifiers in ``setup.py``. - Add ``{posargs}`` to ``tox.ini`` command invocations to improve usage during development. - Add Python 3 note in changelog. * asciidoc does not support Python 3, and fixing that far beyond the scope of this task, so the test that deals with it is conditionally skipped when run using versions of Python 3.main
@@ -2,7 +2,7 @@ sudo: false | |||
language: python | |||
python: | |||
- "2.7" | |||
- "3.5" | |||
addons: | |||
apt: | |||
@@ -40,6 +40,9 @@ install: | |||
# Run each tox environment separately | |||
env: | |||
- TOX_ENV=py27 | |||
- TOX_ENV=py33 | |||
- TOX_ENV=py34 | |||
- TOX_ENV=py35 | |||
- TOX_ENV=pep8 | |||
before_script: | |||
@@ -1,3 +1,9 @@ | |||
Version TBA | |||
=========== | |||
* Experimental Python 3 support | |||
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 | |||
def reraise(message, exc_info): | |||
_, _, tb = exc_info | |||
raise HydeException(message), None, tb | |||
reraise(HydeException, HydeException(message), tb) |
@@ -3,7 +3,7 @@ | |||
CSS plugins | |||
""" | |||
from hyde._compat import str | |||
from hyde.plugin import CLTransformer, Plugin | |||
from hyde.exceptions import HydeException | |||
@@ -109,9 +109,9 @@ class LessCSSPlugin(CLTransformer): | |||
less = self.app | |||
source = File.make_temp(text) | |||
target = File.make_temp('') | |||
args = [unicode(less)] | |||
args = [str(less)] | |||
args.extend(self.process_args(supported)) | |||
args.extend([unicode(source), unicode(target)]) | |||
args.extend([str(source), str(target)]) | |||
try: | |||
self.call_app(args) | |||
except subprocess.CalledProcessError: | |||
@@ -223,9 +223,9 @@ class StylusPlugin(CLTransformer): | |||
source = File.make_temp(text.strip()) | |||
supported = [("compress", "c"), ("include", "I")] | |||
args = [unicode(stylus)] | |||
args = [str(stylus)] | |||
args.extend(self.process_args(supported)) | |||
args.append(unicode(source)) | |||
args.append(str(source)) | |||
try: | |||
self.call_app(args) | |||
except subprocess.CalledProcessError: | |||
@@ -251,7 +251,7 @@ class CleverCSSPlugin(Plugin): | |||
super(CleverCSSPlugin, self).__init__(site) | |||
try: | |||
import clevercss | |||
except ImportError, e: | |||
except ImportError as e: | |||
raise HydeException('Unable to import CleverCSS: ' + e.message) | |||
else: | |||
self.clevercss = clevercss | |||
@@ -329,7 +329,7 @@ class SassyCSSPlugin(Plugin): | |||
super(SassyCSSPlugin, self).__init__(site) | |||
try: | |||
import scss | |||
except ImportError, e: | |||
except ImportError as e: | |||
raise HydeException('Unable to import pyScss: ' + e.message) | |||
else: | |||
self.scss = scss | |||
@@ -419,7 +419,7 @@ class SassPlugin(Plugin): | |||
super(SassPlugin, self).__init__(site) | |||
try: | |||
import sass | |||
except ImportError, e: | |||
except ImportError as e: | |||
raise HydeException('Unable to import libsass: ' + e.message) | |||
else: | |||
self.sass = sass | |||
@@ -489,6 +489,6 @@ class SassPlugin(Plugin): | |||
self.logger.error(resource) | |||
try: | |||
return self.sass.compile(string=text, **options) | |||
except Exception, exc: | |||
except Exception as exc: | |||
self.logger.error(exc) | |||
raise |
@@ -5,6 +5,7 @@ Depends plugin | |||
/// Experimental: Not working yet. | |||
""" | |||
from hyde._compat import basestring | |||
from hyde.plugin import Plugin | |||
@@ -14,6 +14,7 @@ import re | |||
from fswrap import File | |||
from hyde._compat import str | |||
from hyde.exceptions import HydeException | |||
@@ -27,7 +28,7 @@ class PILPlugin(Plugin): | |||
# No pillow | |||
try: | |||
import Image | |||
except ImportError, e: | |||
except ImportError as e: | |||
raise HydeException('Unable to load PIL: ' + e.message) | |||
self.Image = Image | |||
@@ -442,9 +443,9 @@ class JPEGOptimPlugin(CLTransformer): | |||
target = File(self.site.config.deploy_root_path.child( | |||
resource.relative_deploy_path)) | |||
jpegoptim = self.app | |||
args = [unicode(jpegoptim)] | |||
args = [str(jpegoptim)] | |||
args.extend(self.process_args(supported)) | |||
args.extend(["-q", unicode(target)]) | |||
args.extend(["-q", str(target)]) | |||
self.call_app(args) | |||
@@ -499,9 +500,9 @@ class JPEGTranPlugin(CLTransformer): | |||
resource.relative_deploy_path)) | |||
target = File.make_temp('') | |||
jpegtran = self.app | |||
args = [unicode(jpegtran)] | |||
args = [str(jpegtran)] | |||
args.extend(self.process_args(supported)) | |||
args.extend(["-outfile", unicode(target), unicode(source)]) | |||
args.extend(["-outfile", str(target), str(source)]) | |||
self.call_app(args) | |||
target.copy_to(source) | |||
target.delete() | |||
@@ -570,7 +571,7 @@ class OptiPNGPlugin(CLTransformer): | |||
target = File(self.site.config.deploy_root_path.child( | |||
resource.relative_deploy_path)) | |||
optipng = self.app | |||
args = [unicode(optipng)] | |||
args = [str(optipng)] | |||
args.extend(self.process_args(supported)) | |||
args.extend([unicode(target)]) | |||
args.extend([str(target)]) | |||
self.call_app(args) |
@@ -5,6 +5,7 @@ JavaScript plugins | |||
import subprocess | |||
import sys | |||
from hyde._compat import str | |||
from hyde.exceptions import HydeException | |||
from hyde.plugin import CLTransformer | |||
@@ -79,9 +80,9 @@ class UglifyPlugin(CLTransformer): | |||
uglify = self.app | |||
source = File.make_temp(text) | |||
target = File.make_temp('') | |||
args = [unicode(uglify)] | |||
args = [str(uglify)] | |||
args.extend(self.process_args(supported)) | |||
args.extend(["-o", unicode(target), unicode(source)]) | |||
args.extend(["-o", str(target), str(source)]) | |||
self.call_app(args) | |||
out = target.read_all() | |||
return out | |||
@@ -127,9 +128,9 @@ class RequireJSPlugin(CLTransformer): | |||
rjs = self.app | |||
target = File.make_temp('') | |||
args = [unicode(rjs)] | |||
args = [str(rjs)] | |||
args.extend( | |||
['-o', unicode(resource), ("out=" + target.fully_expanded_path)]) | |||
['-o', str(resource), ("out=" + target.fully_expanded_path)]) | |||
try: | |||
self.call_app(args) | |||
@@ -184,6 +185,6 @@ class CoffeePlugin(CLTransformer): | |||
coffee = self.app | |||
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) |
@@ -5,11 +5,11 @@ Contains classes and utilities related to meta data in hyde. | |||
from collections import namedtuple | |||
from functools import partial | |||
from itertools import ifilter | |||
from operator import attrgetter | |||
import re | |||
import sys | |||
from hyde._compat import basestring, filter, iteritems, str | |||
from hyde.exceptions import HydeException | |||
from hyde.model import Expando | |||
from hyde.plugin import Plugin | |||
@@ -263,7 +263,7 @@ def get_tagger_sort_method(site): | |||
def walk_resources_tagged_with(node, tag): | |||
tags = set(unicode(tag).split('+')) | |||
tags = set(str(tag).split('+')) | |||
walker = get_tagger_sort_method(node.site) | |||
for resource in walker(): | |||
try: | |||
@@ -329,7 +329,7 @@ class TaggerPlugin(Plugin): | |||
except AttributeError: | |||
tag_meta = {} | |||
for tagname, meta in tag_meta.iteritems(): | |||
for tagname, meta in iteritems(tag_meta): | |||
# Don't allow name and resources in meta | |||
if 'resources' in meta: | |||
del(meta['resources']) | |||
@@ -376,7 +376,7 @@ class TaggerPlugin(Plugin): | |||
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) | |||
def _create_tag_archive(self, config): | |||
@@ -413,7 +413,7 @@ extends: false | |||
{%% set walker = source['walk_resources_tagged_with_%(tag)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": tagname, | |||
"node": source.name, | |||
@@ -482,8 +482,8 @@ def sort_method(node, settings=None): | |||
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, | |||
key=attrgetter(*attr), | |||
reverse=reverse) | |||
@@ -44,6 +44,7 @@ import os | |||
import json | |||
import tempfile | |||
from hyde._compat import execfile, iteritems | |||
from hyde.plugin import Plugin | |||
from hyde.model import Expando | |||
from hyde.ext.plugins.meta import MetaPlugin as _MetaPlugin | |||
@@ -166,7 +167,7 @@ class SphinxPlugin(Plugin): | |||
if not settings.block_map: | |||
output.append(sphinx_output["body"]) | |||
else: | |||
for (nm, content) in sphinx_output.iteritems(): | |||
for (nm, content) in iteritems(sphinx_output): | |||
try: | |||
block = getattr(settings.block_map, nm) | |||
except AttributeError: | |||
@@ -3,6 +3,7 @@ | |||
Plugins related to structure | |||
""" | |||
from hyde._compat import reduce | |||
from hyde.ext.plugins.meta import Metadata | |||
from hyde.plugin import Plugin | |||
from hyde.site import Resource | |||
@@ -3,14 +3,14 @@ Contains classes and utilities that help publishing a hyde website to | |||
distributed version control systems. | |||
""" | |||
from hyde._compat import str, with_metaclass | |||
from hyde.publisher import Publisher | |||
import abc | |||
from subprocess import Popen, PIPE | |||
class DVCS(Publisher): | |||
__metaclass__ = abc.ABCMeta | |||
class DVCS(with_metaclass(abc.ABCMeta, Publisher)): | |||
def initialize(self, settings): | |||
self.settings = settings | |||
@@ -62,7 +62,7 @@ class Git(DVCS): | |||
def add(self, path="."): | |||
cmd = Popen('git add "%s"' % path, | |||
cwd=unicode(self.path), stdout=PIPE, shell=True) | |||
cwd=str(self.path), stdout=PIPE, shell=True) | |||
cmdresult = cmd.communicate()[0] | |||
if cmd.returncode: | |||
raise Exception(cmdresult) | |||
@@ -70,7 +70,7 @@ class Git(DVCS): | |||
def pull(self): | |||
self.switch(self.branch) | |||
cmd = Popen("git pull origin %s" % self.branch, | |||
cwd=unicode(self.path), | |||
cwd=str(self.path), | |||
stdout=PIPE, | |||
shell=True) | |||
cmdresult = cmd.communicate()[0] | |||
@@ -79,7 +79,7 @@ class Git(DVCS): | |||
def push(self): | |||
cmd = Popen("git push origin %s" % self.branch, | |||
cwd=unicode(self.path), stdout=PIPE, | |||
cwd=str(self.path), stdout=PIPE, | |||
shell=True) | |||
cmdresult = cmd.communicate()[0] | |||
if cmd.returncode: | |||
@@ -87,7 +87,7 @@ class Git(DVCS): | |||
def commit(self, 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] | |||
if cmd.returncode: | |||
raise Exception(cmdresult) | |||
@@ -95,14 +95,14 @@ class Git(DVCS): | |||
def switch(self, branch): | |||
self.branch = 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] | |||
if cmd.returncode: | |||
raise Exception(cmdresult) | |||
def merge(self, 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] | |||
if cmd.returncode: | |||
raise Exception(cmdresult) |
@@ -15,6 +15,7 @@ import getpass | |||
import hashlib | |||
from hyde._compat import basestring, input | |||
from hyde.publisher import Publisher | |||
from commando.util import getLoggerWithNullHandler | |||
@@ -47,8 +48,8 @@ class PyFS(Publisher): | |||
def prompt_for_credentials(self): | |||
credentials = {} | |||
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: | |||
credentials["password"] = getpass.getpass("Password: ") | |||
if credentials: | |||
@@ -13,6 +13,7 @@ import urlparse | |||
from base64 import standard_b64encode | |||
import ConfigParser | |||
from hyde._compat import input | |||
from hyde.publisher import Publisher | |||
from commando.util import getLoggerWithNullHandler | |||
@@ -47,8 +48,8 @@ class PyPI(Publisher): | |||
pass | |||
# Prompt for username on command-line | |||
if self.username is None: | |||
print "Username: ", | |||
self.username = raw_input().strip() | |||
print("Username: ",) | |||
self.username = input().strip() | |||
# Try to find password in .pypirc | |||
if self.password is 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/ | |||
""" | |||
from hyde._compat import str | |||
from hyde.publisher import Publisher | |||
from subprocess import Popen, PIPE | |||
@@ -54,7 +55,7 @@ class SSH(Publisher): | |||
target=self.target) | |||
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] | |||
if cmd.returncode: | |||
raise Exception(cmdresult) |
@@ -8,8 +8,8 @@ import itertools | |||
import os | |||
import re | |||
import sys | |||
from urllib import quote, unquote | |||
from hyde._compat import PY3, quote, unquote, str, StringIO | |||
from hyde.exceptions import HydeException | |||
from hyde.model import Expando | |||
from hyde.template import HtmlWrap, Template | |||
@@ -79,7 +79,10 @@ def urlencode(ctx, url, safe=None): | |||
@contextfilter | |||
def urldecode(ctx, url): | |||
return unquote(url).decode('utf8') | |||
url = unquote(url) | |||
if not PY3: | |||
url = url.decode('utf8') | |||
return url | |||
@contextfilter | |||
@@ -125,18 +128,17 @@ def asciidoc(env, value): | |||
try: | |||
from asciidocapi import AsciiDocAPI | |||
except ImportError: | |||
print u"Requires AsciiDoc library to use AsciiDoc tag." | |||
print(u"Requires AsciiDoc library to use AsciiDoc tag.") | |||
raise | |||
import StringIO | |||
output = value | |||
asciidoc = AsciiDocAPI() | |||
asciidoc.options('--no-header-footer') | |||
result = StringIO.StringIO() | |||
result = StringIO() | |||
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 | |||
@@ -238,7 +240,7 @@ class Spaceless(Extension): | |||
""" | |||
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'], | |||
drop_needle=True) | |||
return nodes.CallBlock( | |||
@@ -253,7 +255,7 @@ class Spaceless(Extension): | |||
""" | |||
if not caller: | |||
return '' | |||
return re.sub(r'>\s+<', '><', unicode(caller().strip())) | |||
return re.sub(r'>\s+<', '><', str(caller().strip())) | |||
class Asciidoc(Extension): | |||
@@ -268,7 +270,7 @@ class Asciidoc(Extension): | |||
Parses the statements and defers to the callback | |||
for asciidoc processing. | |||
""" | |||
lineno = parser.stream.next().lineno | |||
lineno = next(parser.stream).lineno | |||
body = parser.parse_statements(['name:endasciidoc'], drop_needle=True) | |||
return nodes.CallBlock( | |||
@@ -297,7 +299,7 @@ class Markdown(Extension): | |||
Parses the statements and defers to the callback | |||
for markdown processing. | |||
""" | |||
lineno = parser.stream.next().lineno | |||
lineno = next(parser.stream).lineno | |||
body = parser.parse_statements(['name:endmarkdown'], drop_needle=True) | |||
return nodes.CallBlock( | |||
@@ -325,7 +327,7 @@ class restructuredText(Extension): | |||
""" | |||
Simply extract our content | |||
""" | |||
lineno = parser.stream.next().lineno | |||
lineno = next(parser.stream).lineno | |||
body = parser.parse_statements( | |||
['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 | |||
yaml. | |||
""" | |||
lineno = parser.stream.next().lineno | |||
lineno = next(parser.stream).lineno | |||
var = parser.stream.expect('name').value | |||
body = parser.parse_statements(['name:endyaml'], drop_needle=True) | |||
return [ | |||
@@ -396,7 +398,7 @@ def parse_kwargs(parser): | |||
if parser.stream.current.test('string'): | |||
value = parser.parse_expression() | |||
else: | |||
value = nodes.Const(parser.stream.next().value) | |||
value = nodes.Const(next(parser.stream).value) | |||
return (name, value) | |||
@@ -413,7 +415,7 @@ class Syntax(Extension): | |||
Parses the statements and defers to the callback for | |||
pygments processing. | |||
""" | |||
lineno = parser.stream.next().lineno | |||
lineno = next(parser.stream).lineno | |||
lex = nodes.Const(None) | |||
filename = nodes.Const(None) | |||
@@ -428,7 +430,7 @@ class Syntax(Extension): | |||
if name == 'lex' \ | |||
else (value1, value) | |||
else: | |||
lex = nodes.Const(parser.stream.next().value) | |||
lex = nodes.Const(next(parser.stream).value) | |||
if parser.stream.skip_if('comma'): | |||
filename = parser.parse_expression() | |||
@@ -496,10 +498,10 @@ class Reference(Extension): | |||
""" | |||
Parse the variable name that the content must be assigned to. | |||
""" | |||
token = parser.stream.next() | |||
token = next(parser.stream) | |||
lineno = token.lineno | |||
tag = token.value | |||
name = parser.stream.next().value | |||
name = next(parser.stream).value | |||
body = parser.parse_statements(['name:end%s' % tag], drop_needle=True) | |||
return nodes.CallBlock(self.call_method('_render_output', | |||
args=[ | |||
@@ -533,12 +535,12 @@ class Refer(Extension): | |||
""" | |||
Parse the referred template and the namespace. | |||
""" | |||
token = parser.stream.next() | |||
token = next(parser.stream) | |||
lineno = token.lineno | |||
parser.stream.expect('name:to') | |||
template = parser.parse_expression() | |||
parser.stream.expect('name:as') | |||
namespace = parser.stream.next().value | |||
namespace = next(parser.stream).value | |||
includeNode = nodes.Include(lineno=lineno) | |||
includeNode.with_context = True | |||
includeNode.ignore_missing = False | |||
@@ -623,11 +625,11 @@ class HydeLoader(FileSystemLoader): | |||
config = site.config if hasattr(site, 'config') else None | |||
if config: | |||
super(HydeLoader, self).__init__([ | |||
unicode(config.content_root_path), | |||
unicode(config.layout_root_path), | |||
str(config.content_root_path), | |||
str(config.layout_root_path), | |||
]) | |||
else: | |||
super(HydeLoader, self).__init__(unicode(sitepath)) | |||
super(HydeLoader, self).__init__(str(sitepath)) | |||
self.site = site | |||
self.preprocessor = preprocessor | |||
@@ -650,10 +652,10 @@ class HydeLoader(FileSystemLoader): | |||
except UnicodeDecodeError: | |||
HydeException.reraise( | |||
"Unicode error when processing %s" % template, sys.exc_info()) | |||
except TemplateError, exc: | |||
except TemplateError as exc: | |||
HydeException.reraise('Error when processing %s: %s' % ( | |||
template, | |||
unicode(exc) | |||
str(exc) | |||
), sys.exc_info()) | |||
if self.preprocessor: | |||
@@ -800,9 +802,9 @@ class Jinja2Template(Template): | |||
from jinja2.meta import find_referenced_templates | |||
try: | |||
ast = self.env.parse(text) | |||
except Exception, e: | |||
except Exception as e: | |||
HydeException.reraise( | |||
"Error processing %s: \n%s" % (path, unicode(e)), | |||
"Error processing %s: \n%s" % (path, str(e)), | |||
sys.exc_info()) | |||
tpls = find_referenced_templates(ast) | |||
@@ -336,7 +336,7 @@ class Generator(object): | |||
try: | |||
text = self.template.render_resource(resource, | |||
context) | |||
except Exception, e: | |||
except Exception as e: | |||
HydeException.reraise("Error occurred when processing" | |||
"template: [%s]: %s" % | |||
(resource, repr(e)), | |||
@@ -6,6 +6,8 @@ import os | |||
from fswrap import File, Folder | |||
from hyde._compat import str | |||
HYDE_DATA = "HYDE_DATA" | |||
LAYOUTS = "layouts" | |||
@@ -39,6 +41,6 @@ class Layout(object): | |||
Finds the layout folder from the given root folder. | |||
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) | |||
return layout_folder if layout_folder.exists else None |
@@ -5,11 +5,12 @@ Contains data structures and utilities for hyde. | |||
import codecs | |||
import yaml | |||
from datetime import datetime | |||
from UserDict import IterableUserDict | |||
from commando.util import getLoggerWithNullHandler | |||
from fswrap import File, Folder | |||
from hyde._compat import iteritems, str, UserDict | |||
logger = getLoggerWithNullHandler('hyde.engine') | |||
SEQS = (tuple, list, set, frozenset) | |||
@@ -45,7 +46,7 @@ class Expando(object): | |||
Returns an iterator for all the items in the | |||
dictionary as key value pairs. | |||
""" | |||
return self.__dict__.iteritems() | |||
return iteritems(self.__dict__) | |||
def update(self, d): | |||
""" | |||
@@ -63,10 +64,10 @@ class Expando(object): | |||
Sets the expando attribute after | |||
transforming the value. | |||
""" | |||
setattr(self, unicode(key).encode('utf-8'), make_expando(value)) | |||
setattr(self, str(key), make_expando(value)) | |||
def __repr__(self): | |||
return unicode(self.to_dict()) | |||
return str(self.to_dict()) | |||
def to_dict(self): | |||
""" | |||
@@ -128,7 +129,7 @@ class Context(object): | |||
return context | |||
class Dependents(IterableUserDict): | |||
class Dependents(UserDict): | |||
""" | |||
Represents the dependency graph for hyde. | |||
@@ -2,6 +2,7 @@ | |||
""" | |||
Contains definition for a plugin protocol and other utiltities. | |||
""" | |||
from hyde._compat import str | |||
from hyde.exceptions import HydeException | |||
from hyde.util import first_match, discover_executable | |||
from hyde.model import Expando | |||
@@ -17,6 +18,8 @@ import sys | |||
from commando.util import getLoggerWithNullHandler, load_python_object | |||
from fswrap import File | |||
from hyde._compat import with_metaclass | |||
logger = getLoggerWithNullHandler('hyde.engine') | |||
# Plugins have been reorganized. Map old plugin paths to new. | |||
@@ -106,12 +109,11 @@ class PluginProxy(object): | |||
"Unknown plugin method [%s] called." % method_name) | |||
class Plugin(object): | |||
class Plugin(with_metaclass(abc.ABCMeta)): | |||
""" | |||
The plugin protocol | |||
""" | |||
__metaclass__ = abc.ABCMeta | |||
def __init__(self, site): | |||
super(Plugin, self).__init__() | |||
@@ -440,14 +442,14 @@ class CLTransformer(Plugin): | |||
try: | |||
self.logger.debug( | |||
"Calling executable [%s] with arguments %s" % | |||
(args[0], unicode(args[1:]))) | |||
(args[0], str(args[1:]))) | |||
return subprocess.check_output(args) | |||
except subprocess.CalledProcessError, error: | |||
except subprocess.CalledProcessError as error: | |||
self.logger.error(error.output) | |||
raise | |||
class TextyPlugin(Plugin): | |||
class TextyPlugin(with_metaclass(abc.ABCMeta, Plugin)): | |||
""" | |||
Base class for text preprocessing plugins. | |||
@@ -457,8 +459,6 @@ class TextyPlugin(Plugin): | |||
can inherit from this class. | |||
""" | |||
__metaclass__ = abc.ABCMeta | |||
def __init__(self, site): | |||
super(TextyPlugin, self).__init__(site) | |||
self.open_pattern = self.default_open_pattern | |||
@@ -3,20 +3,20 @@ from operator import attrgetter | |||
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 | |||
server. | |||
""" | |||
class Publisher(object): | |||
class Publisher(with_metaclass(abc.ABCMeta)): | |||
""" | |||
The abstract base class for publishers. | |||
""" | |||
__metaclass__ = abc.ABCMeta | |||
def __init__(self, site, settings, message): | |||
super(Publisher, self).__init__() | |||
self.logger = getLoggerWithNullHandler( | |||
@@ -4,13 +4,13 @@ Contains classes and utilities for serving a site | |||
generated from hyde. | |||
""" | |||
import threading | |||
import urlparse | |||
import urllib | |||
import traceback | |||
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 fswrap import File, Folder | |||
@@ -35,8 +35,8 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): | |||
""" | |||
self.server.request_time = datetime.now() | |||
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': | |||
self.server.regenerate() | |||
if 'refresh' in query: | |||
@@ -44,7 +44,7 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): | |||
parts = list(tuple(result)) | |||
parts[4] = urllib.urlencode(query) | |||
parts = tuple(parts) | |||
new_url = urlparse.urlunparse(parts) | |||
new_url = parse.urlunparse(parts) | |||
logger.info('Redirecting... [%s]' % new_url) | |||
self.redirect(new_url) | |||
else: | |||
@@ -56,7 +56,10 @@ class HydeRequestHandler(SimpleHTTPRequestHandler): | |||
referring to the `site` variable in the server. | |||
""" | |||
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( | |||
"Trying to load file based on request: [%s]" % result.path) | |||
path = result.path.lstrip('/') | |||
@@ -150,7 +153,7 @@ class HydeWebServer(HTTPServer): | |||
except AttributeError: | |||
extensions = {} | |||
for extension, type in extensions.iteritems(): | |||
for extension, type in iteritems(extensions): | |||
ext = "." + extension if not extension == 'default' else '' | |||
HydeRequestHandler.extensions_map[ext] = type | |||
@@ -165,7 +168,7 @@ class HydeWebServer(HTTPServer): | |||
self.site.config.reload() | |||
self.site.load() | |||
self.generator.generate_all(incremental=False) | |||
except Exception, exception: | |||
except Exception as exception: | |||
logger.error('Error occured when regenerating the site [%s]' | |||
% exception.message) | |||
logger.debug(traceback.format_exc()) | |||
@@ -182,7 +185,7 @@ class HydeWebServer(HTTPServer): | |||
try: | |||
logger.debug('Serving node [%s]' % node) | |||
self.generator.generate_node(node, incremental=True) | |||
except Exception, exception: | |||
except Exception as exception: | |||
logger.error( | |||
'Error [%s] occured when generating the node [%s]' | |||
% (repr(exception), node)) | |||
@@ -201,7 +204,7 @@ class HydeWebServer(HTTPServer): | |||
try: | |||
logger.debug('Serving resource [%s]' % resource) | |||
self.generator.generate_resource(resource, incremental=True) | |||
except Exception, exception: | |||
except Exception as exception: | |||
logger.error( | |||
'Error [%s] occured when serving the resource [%s]' | |||
% (repr(exception), resource)) | |||
@@ -5,10 +5,9 @@ Parses & holds information about the site to be generated. | |||
import os | |||
import fnmatch | |||
import sys | |||
import urlparse | |||
from functools import wraps | |||
from urllib import quote | |||
from hyde._compat import parse, quote, str | |||
from hyde.exceptions import HydeException | |||
from hyde.model import Config | |||
@@ -19,7 +18,7 @@ from fswrap import FS, File, Folder | |||
def path_normalized(f): | |||
@wraps(f) | |||
def wrapper(self, path): | |||
return f(self, unicode(path).replace('/', os.sep)) | |||
return f(self, str(path).replace('/', os.sep)) | |||
return wrapper | |||
logger = getLoggerWithNullHandler('hyde.engine') | |||
@@ -138,7 +137,7 @@ class Node(Processable): | |||
self.root = self | |||
self.module = None | |||
self.site = None | |||
self.source_folder = Folder(unicode(source_folder)) | |||
self.source_folder = Folder(str(source_folder)) | |||
self.parent = parent | |||
if parent: | |||
self.root = self.parent.root | |||
@@ -249,7 +248,7 @@ class RootNode(Node): | |||
""" | |||
if Folder(path) == self.source_folder: | |||
return self | |||
return self.node_map.get(unicode(Folder(path)), None) | |||
return self.node_map.get(str(Folder(path)), None) | |||
@path_normalized | |||
def node_from_relative_path(self, relative_path): | |||
@@ -258,7 +257,7 @@ class RootNode(Node): | |||
If no match is found it returns None. | |||
""" | |||
return self.node_from_path( | |||
self.source_folder.child(unicode(relative_path))) | |||
self.source_folder.child(str(relative_path))) | |||
@path_normalized | |||
def resource_from_path(self, path): | |||
@@ -266,7 +265,7 @@ class RootNode(Node): | |||
Gets the resource that maps to the given path. | |||
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 | |||
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 | |||
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 | |||
def resource_from_relative_deploy_path(self, relative_deploy_path): | |||
@@ -323,7 +322,7 @@ class RootNode(Node): | |||
node = parent if parent else self | |||
for h_folder in hierarchy: | |||
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]" % ( | |||
node.relative_path, self.source_folder)) | |||
@@ -352,7 +351,7 @@ class RootNode(Node): | |||
if not node: | |||
node = self.add_node(afile.parent) | |||
resource = node.add_child_resource(afile) | |||
self.resource_map[unicode(afile)] = resource | |||
self.resource_map[str(afile)] = resource | |||
relative_path = resource.relative_path | |||
resource.simple_copy = any(fnmatch.fnmatch(relative_path, pattern) | |||
for pattern in self.site.config.simple_copy) | |||
@@ -395,10 +394,11 @@ class RootNode(Node): | |||
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) | |||
return base.rstrip('/') + '/' + path.lstrip('/') | |||
full_path = base.rstrip('/') + '/' + path.lstrip('/') | |||
return full_path | |||
class Site(object): | |||
@@ -471,7 +471,7 @@ class Site(object): | |||
configuration and returns the appropriate url. The return value | |||
is url encoded. | |||
""" | |||
if urlparse.urlparse(path)[:2] != ("", ""): | |||
if parse.urlparse(path)[:2] != ("", ""): | |||
return path | |||
if self.is_media(path): | |||
@@ -3,6 +3,7 @@ | |||
""" | |||
Abstract classes and utilities for template engines | |||
""" | |||
from hyde._compat import with_metaclass | |||
from hyde.exceptions import HydeException | |||
import abc | |||
@@ -30,24 +31,25 @@ class HtmlWrap(object): | |||
PyQuery = None | |||
self.q = PyQuery(html) if PyQuery else None | |||
def __unicode__(self): | |||
def __str__(self): | |||
return self.raw | |||
# Support __unicode__ as well as __str__ for backward compatibility. | |||
__unicode__ = __str__ | |||
def __call__(self, selector=None): | |||
if not self.q: | |||
return self.raw | |||
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, | |||
the following interface must be implemented. | |||
""" | |||
__metaclass__ = abc.ABCMeta | |||
def __init__(self, sitepath): | |||
self.sitepath = sitepath | |||
self.logger = getLoggerWithNullHandler(self.__class__.__name__) | |||
@@ -3,7 +3,9 @@ Module for python 2.6 compatibility. | |||
""" | |||
import os | |||
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_): | |||
@@ -26,7 +28,7 @@ def add_method(obj, method_name, method_, *args, **kwargs): | |||
def pairwalk(iterable): | |||
a, b = tee(iterable) | |||
next(b, None) | |||
return izip(a, b) | |||
return zip(a, b) | |||
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. | |||
# 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: | |||
full_name = os.path.join(path, name) | |||
if os.path.exists(full_name): | |||
@@ -72,9 +72,8 @@ def find_package_data( | |||
bad_name = True | |||
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 | |||
if bad_name: | |||
continue | |||
@@ -96,9 +95,8 @@ def find_package_data( | |||
bad_name = True | |||
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 | |||
if bad_name: | |||
continue | |||
@@ -157,6 +155,12 @@ setup(name=PROJECT, | |||
'Operating System :: POSIX', | |||
'Operating System :: Microsoft :: Windows', | |||
'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 :: Build Tools', | |||
'Topic :: Software Development :: Code Generators', | |||
@@ -41,8 +41,8 @@ class TestSass(object): | |||
assert target.exists | |||
text = target.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) |
@@ -3,9 +3,9 @@ Use nose | |||
`$ pip install nose` | |||
`$ nosetests` | |||
""" | |||
from hyde._compat import quote | |||
from hyde.generator import Generator | |||
from hyde.site import Site | |||
from urllib import quote | |||
from fswrap import File | |||
@@ -46,7 +46,7 @@ class TestTextlinks(object): | |||
site.config.media_url = '/media' | |||
tlink = File(site.content.source_folder.child('tlink.html')) | |||
tlink.write(text % d) | |||
print tlink.read_all() | |||
print(tlink.read_all()) | |||
gen = Generator(site) | |||
gen.generate_all() | |||
f = File(site.config.deploy_root_path.child(tlink.name)) | |||
@@ -5,7 +5,7 @@ Use nose | |||
`$ nosetests` | |||
""" | |||
from hyde._compat import str | |||
from hyde.engine import Engine | |||
from hyde.exceptions import HydeException | |||
from hyde.layout import Layout | |||
@@ -42,7 +42,7 @@ def delete_test_site_at_user(): | |||
def test_ensure_exception_when_site_yaml_exists(): | |||
e = Engine(raise_exceptions=True) | |||
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) | |||
@@ -50,7 +50,7 @@ def test_ensure_exception_when_site_yaml_exists(): | |||
def test_ensure_exception_when_content_folder_exists(): | |||
e = Engine(raise_exceptions=True) | |||
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) | |||
@@ -58,13 +58,13 @@ def test_ensure_exception_when_content_folder_exists(): | |||
def test_ensure_exception_when_layout_folder_exists(): | |||
e = Engine(raise_exceptions=True) | |||
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) | |||
def test_ensure_no_exception_when_empty_site_exists(): | |||
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()) | |||
@@ -72,16 +72,16 @@ def test_ensure_no_exception_when_empty_site_exists(): | |||
def test_ensure_no_exception_when_forced(): | |||
e = Engine(raise_exceptions=True) | |||
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()) | |||
TEST_SITE.delete() | |||
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()) | |||
TEST_SITE.delete() | |||
TEST_SITE.make() | |||
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()) | |||
@@ -89,7 +89,7 @@ def test_ensure_no_exception_when_forced(): | |||
def test_ensure_no_exception_when_sitepath_does_not_exist(): | |||
e = Engine(raise_exceptions=True) | |||
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()) | |||
@@ -97,7 +97,7 @@ def test_ensure_no_exception_when_sitepath_does_not_exist(): | |||
def test_ensure_can_create_site_at_user(): | |||
e = Engine(raise_exceptions=True) | |||
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()) | |||
@@ -107,9 +107,10 @@ def verify_site_contents(site, layout): | |||
assert site.child_folder('layout').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 expected | |||
@@ -122,4 +123,4 @@ def verify_site_contents(site, layout): | |||
@with_setup(create_test_site, delete_test_site) | |||
def test_ensure_exception_when_layout_is_invalid(): | |||
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 random import choice, randrange | |||
from hyde._compat import PY3 | |||
from hyde.ext.templates.jinja import Jinja2Template | |||
from hyde.site import Site | |||
from hyde.generator import Generator | |||
@@ -16,6 +17,7 @@ from hyde.model import Config | |||
from fswrap import File | |||
from jinja2.utils import generate_lorem_ipsum | |||
from nose.plugins.skip import SkipTest | |||
from nose.tools import nottest | |||
from pyquery import PyQuery | |||
@@ -49,7 +51,7 @@ class User(object): | |||
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)) | |||
navigation = [ | |||
('index', 'Index'), | |||
@@ -132,6 +134,11 @@ def test_spaceless(): | |||
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 = """ | |||
{%asciidoc%} | |||
== Heading 2 == | |||
@@ -6,6 +6,7 @@ Use nose | |||
""" | |||
import os | |||
from hyde._compat import str | |||
from hyde.layout import Layout, HYDE_DATA, LAYOUTS | |||
from fswrap import File | |||
@@ -36,7 +37,7 @@ def test_find_layout_from_env_var(): | |||
f = Layout.find_layout() | |||
LAYOUT_ROOT.make() | |||
f.copy_to(LAYOUT_ROOT) | |||
os.environ[HYDE_DATA] = unicode(DATA_ROOT) | |||
os.environ[HYDE_DATA] = str(DATA_ROOT) | |||
f = Layout.find_layout() | |||
assert f.parent == LAYOUT_ROOT | |||
assert f.name == 'basic' | |||
@@ -24,14 +24,14 @@ class PluginLoaderStub(Plugin): | |||
class NoReturnPlugin(Plugin): | |||
def begin_text_resource(self, resource, text): | |||
print "NoReturnPlugin" | |||
print("NoReturnPlugin") | |||
return None | |||
class ConstantReturnPlugin(Plugin): | |||
def begin_text_resource(self, resource, text): | |||
print "ConstantReturnPlugin" | |||
print("ConstantReturnPlugin") | |||
return "Jam" | |||
@@ -5,8 +5,8 @@ Use nose | |||
`$ nosetests` | |||
""" | |||
import yaml | |||
from urllib import quote | |||
from hyde._compat import quote | |||
from hyde.model import Config | |||
from hyde.site import Node, RootNode, Site | |||
@@ -242,8 +242,8 @@ class TestSiteWithConfig(object): | |||
s = Site(self.SITE_PATH, config=self.config) | |||
s.load() | |||
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, "") | |||
def test_media_url(self): | |||
@@ -1,13 +1,15 @@ | |||
import re | |||
import difflib | |||
from hyde._compat import str | |||
def strip_spaces_between_tags(value): | |||
""" | |||
Stolen from `django.util.html` | |||
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): | |||
@@ -1,5 +1,5 @@ | |||
[tox] | |||
envlist = py27,pep8 | |||
envlist = py{27,33,34,35},pep8 | |||
[testenv] | |||
usedevelop = True | |||
@@ -8,11 +8,11 @@ sitepackages = True | |||
# Needed for asciidoc | |||
passenv = PYTHONPATH | |||
deps = -r{toxinidir}/dev-req.txt | |||
commands = nosetests | |||
commands = nosetests {posargs} | |||
[testenv:pep8] | |||
deps = flake8 | |||
commands = flake8 | |||
commands = flake8 {posargs} | |||
[flake8] | |||
exclude = .tox | |||