Commit 23301834 by Chris Jerdonek

Merge branch 'development-python24' into development: code now works with Python 2.4.

All tests pass with Python 2.4 (with simplejson-2.0.9).
parents 135540b4 09906e25
...@@ -7,7 +7,14 @@ Run this script using the -h option for command-line help. ...@@ -7,7 +7,14 @@ Run this script using the -h option for command-line help.
""" """
import json
try:
import json
except:
# The json module is new in Python 2.6, whereas simplejson is
# compatible with earlier versions.
import simplejson as json
# The optparse module is deprecated in Python 2.7 in favor of argparse. # The optparse module is deprecated in Python 2.7 in favor of argparse.
# However, argparse is not available in Python 2.6 and earlier. # However, argparse is not available in Python 2.6 and earlier.
from optparse import OptionParser from optparse import OptionParser
......
...@@ -5,8 +5,8 @@ This module contains the initialization logic called by __init__.py. ...@@ -5,8 +5,8 @@ This module contains the initialization logic called by __init__.py.
""" """
from .renderer import Renderer from pystache.renderer import Renderer
from .template_spec import TemplateSpec from pystache.template_spec import TemplateSpec
__all__ = ['render', 'Renderer', 'TemplateSpec'] __all__ = ['render', 'Renderer', 'TemplateSpec']
......
...@@ -5,13 +5,11 @@ This module provides a Loader class for locating and reading templates. ...@@ -5,13 +5,11 @@ This module provides a Loader class for locating and reading templates.
""" """
from __future__ import with_statement
import os import os
import sys import sys
from . import defaults from pystache import defaults
from .locator import Locator from pystache.locator import Locator
def _to_unicode(s, encoding=None): def _to_unicode(s, encoding=None):
...@@ -108,8 +106,12 @@ class Loader(object): ...@@ -108,8 +106,12 @@ class Loader(object):
Read the template at the given path, and return it as a unicode string. Read the template at the given path, and return it as a unicode string.
""" """
with open(path, 'r') as f: # We avoid use of the with keyword for Python 2.4 support.
f = open(path, 'r')
try:
text = f.read() text = f.read()
finally:
f.close()
if encoding is None: if encoding is None:
encoding = self.file_encoding encoding = self.file_encoding
......
...@@ -9,7 +9,7 @@ import os ...@@ -9,7 +9,7 @@ import os
import re import re
import sys import sys
from . import defaults from pystache import defaults
class Locator(object): class Locator(object):
......
...@@ -37,7 +37,11 @@ class ParsedTemplate(object): ...@@ -37,7 +37,11 @@ class ParsedTemplate(object):
Returns: a string of type unicode. Returns: a string of type unicode.
""" """
get_unicode = lambda val: val(context) if callable(val) else val # We avoid use of the ternary operator for Python 2.4 support.
def get_unicode(val):
if callable(val):
return val(context)
return val
parts = map(get_unicode, self._parse_tree) parts = map(get_unicode, self._parse_tree)
s = ''.join(parts) s = ''.join(parts)
......
...@@ -5,12 +5,12 @@ This module provides a Renderer class to render templates. ...@@ -5,12 +5,12 @@ This module provides a Renderer class to render templates.
""" """
from . import defaults from pystache import defaults
from .context import Context from pystache.context import Context
from .loader import Loader from pystache.loader import Loader
from .renderengine import RenderEngine from pystache.renderengine import RenderEngine
from .spec_loader import SpecLoader from pystache.spec_loader import SpecLoader
from .template_spec import TemplateSpec from pystache.template_spec import TemplateSpec
class Renderer(object): class Renderer(object):
...@@ -138,8 +138,11 @@ class Renderer(object): ...@@ -138,8 +138,11 @@ class Renderer(object):
Convert a basestring to unicode, preserving any unicode subclass. Convert a basestring to unicode, preserving any unicode subclass.
""" """
# Avoid the "double-decoding" TypeError. # We type-check to avoid "TypeError: decoding Unicode is not supported".
return s if isinstance(s, unicode) else self.unicode(s) # We avoid the Python ternary operator for Python 2.4 support.
if isinstance(s, unicode):
return s
return self.unicode(s)
def _to_unicode_hard(self, s): def _to_unicode_hard(self, s):
""" """
......
...@@ -7,7 +7,7 @@ This module supports customized (aka special or specified) template loading. ...@@ -7,7 +7,7 @@ This module supports customized (aka special or specified) template loading.
import os.path import os.path
from .loader import Loader from pystache.loader import Loader
# TODO: add test cases for this class. # TODO: add test cases for this class.
...@@ -36,12 +36,15 @@ class SpecLoader(object): ...@@ -36,12 +36,15 @@ class SpecLoader(object):
""" """
if spec.template_rel_path is not None: if spec.template_rel_path is not None:
return os.path.split(spec.template_rel_path) return os.path.split(spec.template_rel_path)
# Otherwise, determine the file name separately. # Otherwise, determine the file name separately.
locator = self.loader._make_locator() locator = self.loader._make_locator()
template_name = (spec.template_name if spec.template_name is not None else # We do not use the ternary operator for Python 2.4 support.
locator.make_template_name(spec)) if spec.template_name is not None:
template_name = spec.template_name
else:
template_name = locator.make_template_name(spec)
file_name = locator.make_file_name(template_name, spec.template_extension) file_name = locator.make_file_name(template_name, spec.template_extension)
......
...@@ -157,6 +157,17 @@ class GetValueTests(unittest.TestCase, AssertIsMixin): ...@@ -157,6 +157,17 @@ class GetValueTests(unittest.TestCase, AssertIsMixin):
item1 = MyInt(10) item1 = MyInt(10)
item2 = 10 item2 = 10
try:
item2.real
except AttributeError:
# Then skip this unit test. The numeric type hierarchy was
# added only in Python 2.6, in which case integers inherit
# from complex numbers the "real" attribute, etc:
#
# http://docs.python.org/library/numbers.html
#
return
self.assertEquals(item1.real, 10) self.assertEquals(item1.real, 10)
self.assertEquals(item2.real, 10) self.assertEquals(item2.real, 10)
...@@ -316,7 +327,7 @@ class ContextTests(unittest.TestCase, AssertIsMixin): ...@@ -316,7 +327,7 @@ class ContextTests(unittest.TestCase, AssertIsMixin):
def test_get__default(self): def test_get__default(self):
""" """
Test that get() respects the default value . Test that get() respects the default value.
""" """
context = Context() context = Context()
......
...@@ -12,8 +12,8 @@ from examples.unicode_output import UnicodeOutput ...@@ -12,8 +12,8 @@ from examples.unicode_output import UnicodeOutput
from examples.unicode_input import UnicodeInput from examples.unicode_input import UnicodeInput
from examples.nested_context import NestedContext from examples.nested_context import NestedContext
from pystache import Renderer from pystache import Renderer
from .common import EXAMPLES_DIR from tests.common import EXAMPLES_DIR
from .common import AssertStringMixin from tests.common import AssertStringMixin
class TestView(unittest.TestCase, AssertStringMixin): class TestView(unittest.TestCase, AssertStringMixin):
......
...@@ -9,7 +9,7 @@ import os ...@@ -9,7 +9,7 @@ import os
import sys import sys
import unittest import unittest
from .common import AssertStringMixin from tests.common import AssertStringMixin
from pystache import defaults from pystache import defaults
from pystache.loader import Loader from pystache.loader import Loader
......
...@@ -14,7 +14,7 @@ import unittest ...@@ -14,7 +14,7 @@ import unittest
from pystache.loader import Loader as Reader from pystache.loader import Loader as Reader
from pystache.locator import Locator from pystache.locator import Locator
from .common import DATA_DIR from tests.common import DATA_DIR
from data.views import SayHello from data.views import SayHello
......
...@@ -15,9 +15,9 @@ from pystache import Renderer ...@@ -15,9 +15,9 @@ from pystache import Renderer
from pystache import TemplateSpec from pystache import TemplateSpec
from pystache.loader import Loader from pystache.loader import Loader
from .common import get_data_path from tests.common import get_data_path
from .common import AssertStringMixin from tests.common import AssertStringMixin
from .data.views import SayHello from tests.data.views import SayHello
class RendererInitTestCase(unittest.TestCase): class RendererInitTestCase(unittest.TestCase):
......
...@@ -8,8 +8,8 @@ from examples.lambdas import Lambdas ...@@ -8,8 +8,8 @@ from examples.lambdas import Lambdas
from examples.template_partial import TemplatePartial from examples.template_partial import TemplatePartial
from examples.simple import Simple from examples.simple import Simple
from .common import EXAMPLES_DIR from tests.common import EXAMPLES_DIR
from .common import AssertStringMixin from tests.common import AssertStringMixin
class TestSimple(unittest.TestCase, AssertStringMixin): class TestSimple(unittest.TestCase, AssertStringMixin):
......
...@@ -3,32 +3,28 @@ ...@@ -3,32 +3,28 @@
""" """
Creates a unittest.TestCase for the tests defined in the mustache spec. Creates a unittest.TestCase for the tests defined in the mustache spec.
We did not call this file something like "test_spec.py" to avoid matching """
nosetests's default regular expression "(?:^|[\b_\./-])[Tt]est".
This allows us to exclude the spec test cases by default when running
nosetests. To include the spec tests, one can use the following option,
for example--
nosetests -i spec # TODO: this module can be cleaned up somewhat.
""" try:
# We deserialize the json form rather than the yaml form because
# json libraries are available for Python 2.4.
import json
except:
# The json module is new in Python 2.6, whereas simplejson is
# compatible with earlier versions.
import simplejson as json
import glob import glob
import os.path import os.path
import unittest import unittest
import yaml
from pystache.renderer import Renderer from pystache.renderer import Renderer
def code_constructor(loader, node): root_path = os.path.join(os.path.dirname(__file__), '..', 'ext', 'spec', 'specs')
value = loader.construct_mapping(node) spec_paths = glob.glob(os.path.join(root_path, '*.json'))
return eval(value['python'], {})
yaml.add_constructor(u'!code', code_constructor)
specs = os.path.join(os.path.dirname(__file__), '..', 'ext', 'spec', 'specs')
specs = glob.glob(os.path.join(specs, '*.yml'))
class MustacheSpec(unittest.TestCase): class MustacheSpec(unittest.TestCase):
pass pass
...@@ -46,8 +42,16 @@ def buildTest(testData, spec_filename): ...@@ -46,8 +42,16 @@ def buildTest(testData, spec_filename):
expected = testData['expected'] expected = testData['expected']
data = testData['data'] data = testData['data']
# Convert code strings to functions.
# TODO: make this section of code easier to understand.
new_data = {}
for key, val in data.iteritems():
if isinstance(val, dict) and val.get('__tag__') == 'code':
val = eval(val['python'])
new_data[key] = val
renderer = Renderer(partials=partials) renderer = Renderer(partials=partials)
actual = renderer.render(template, data) actual = renderer.render(template, new_data)
actual = actual.encode('utf-8') actual = actual.encode('utf-8')
message = """%s message = """%s
...@@ -64,14 +68,28 @@ def buildTest(testData, spec_filename): ...@@ -64,14 +68,28 @@ def buildTest(testData, spec_filename):
self.assertEquals(actual, expected, message) self.assertEquals(actual, expected, message)
# The name must begin with "test" for nosetests test discovery to work. # The name must begin with "test" for nosetests test discovery to work.
test.__name__ = 'test: "%s"' % test_name name = 'test: "%s"' % test_name
# If we don't convert unicode to str, we get the following error:
# "TypeError: __name__ must be set to a string object"
test.__name__ = str(name)
return test return test
for spec in specs: for spec_path in spec_paths:
file_name = os.path.basename(spec)
file_name = os.path.basename(spec_path)
# We avoid use of the with keyword for Python 2.4 support.
f = open(spec_path, 'r')
try:
spec_data = json.load(f)
finally:
f.close()
tests = spec_data['tests']
for test in yaml.load(open(spec))['tests']: for test in tests:
test = buildTest(test, file_name) test = buildTest(test, file_name)
setattr(MustacheSpec, test.__name__, test) setattr(MustacheSpec, test.__name__, test)
# Prevent this variable from being interpreted as another test. # Prevent this variable from being interpreted as another test.
......
...@@ -19,12 +19,12 @@ from pystache import TemplateSpec ...@@ -19,12 +19,12 @@ from pystache import TemplateSpec
from pystache.locator import Locator from pystache.locator import Locator
from pystache.loader import Loader from pystache.loader import Loader
from pystache.spec_loader import SpecLoader from pystache.spec_loader import SpecLoader
from .common import DATA_DIR from tests.common import DATA_DIR
from .common import EXAMPLES_DIR from tests.common import EXAMPLES_DIR
from .common import AssertIsMixin from tests.common import AssertIsMixin
from .common import AssertStringMixin from tests.common import AssertStringMixin
from .data.views import SampleView from tests.data.views import SampleView
from .data.views import NonAscii from tests.data.views import NonAscii
class Thing(object): class Thing(object):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment