Skip to content

Commit

Permalink
Merge pull request #106 from timofurrer/compat
Browse files Browse the repository at this point in the history
Make sure compatible with python 2.7, 3.4 and 3.5
  • Loading branch information
timofurrer committed Jun 5, 2016
2 parents 3b3f698 + b4b8107 commit ff4b104
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 122 deletions.
6 changes: 1 addition & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ language: python
sudo: false
python:
- "2.7"
- "3.4"
- "3.5"


matrix:
allow_failures:
- python: "3.5"

# command to install dependencies
install:
- python setup.py build sdist
Expand Down
104 changes: 104 additions & 0 deletions sure/compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from __future__ import unicode_literals
import six

try:
from collections import OrderedDict
except ImportError:
from sure.ordereddict import OrderedDict

from sure.terminal import red, green, yellow


if six.PY3:
def compat_repr(object_repr):
return object_repr
else:
def compat_repr(object_repr):
# compat_repr is designed to return all reprs with leading 'u's
# inserted to make all strings look like unicode strings.
# This makes testing between py2 and py3 much easier.
result = ''
in_quote = False
curr_quote = None
for char in object_repr:
if char in ['"', "'"] and (
not curr_quote or char == curr_quote):
if in_quote:
# Closing quote
curr_quote = None
in_quote = False
else:
# Opening quote
curr_quote = char
result += 'u'
in_quote = True
result += char
return result

# FIXME: move FakeOrderedDict to another module since it
# does not have anything todo with compat.
# The safe_repr function should already get a
# FakeOrderedDict instance. Maybe the _obj_with_safe_repr
# function should be part of the FakeOrderedDict
# classes __repr__ method.
class FakeOrderedDict(OrderedDict):
""" OrderedDict that has the repr of a normal dict
We must return a string whether in py2 or py3.
"""
def __unicode__(self):
if not self:
return '{}'
key_values = []
for key, value in self.items():
key, value = repr(key), repr(value)
if isinstance(value, six.binary_type) and not six.PY3:
value = value.decode("utf-8")
key_values.append("{0}: {1}".format(key, value))
res = "{{{0}}}".format(", ".join(key_values))
return res

if six.PY3:
def __repr__(self):
return self.__unicode__()
else:
def __repr__(self):
return self.__unicode__().encode('utf-8')


def _obj_with_safe_repr(obj):
if isinstance(obj, dict):
ret = FakeOrderedDict()
for key in sorted(obj.keys()):
ret[_obj_with_safe_repr(key)] = _obj_with_safe_repr(obj[key])
elif isinstance(obj, list):
ret = []
for x in obj:
if isinstance(x, dict):
ret.append(_obj_with_safe_repr(x))
else:
ret.append(x)
else:
ret = obj
return ret


def safe_repr(val):
try:
if isinstance(val, dict):
# We special case dicts to have a sorted repr. This makes testing
# significantly easier
val = _obj_with_safe_repr(val)
ret = repr(val)
if not six.PY3:
ret = ret.decode('utf-8')
except UnicodeEncodeError:
ret = red('a %r that cannot be represented' % type(val))
else:
ret = green(ret)

return ret



text_type_name = six.text_type().__class__.__name__
29 changes: 0 additions & 29 deletions sure/compat_py3.py

This file was deleted.

70 changes: 4 additions & 66 deletions sure/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals

try:
from collections import OrderedDict
except ImportError:
from sure.ordereddict import OrderedDict

import os

try:
Expand All @@ -31,69 +26,12 @@

import inspect
from six import (
text_type, integer_types, string_types, binary_type,
PY3, get_function_code
text_type, integer_types, string_types,
get_function_code
)
from sure.terminal import red, green, yellow


class FakeOrderedDict(OrderedDict):
""" OrderedDict that has the repr of a normal dict
We must return a string whether in py2 or py3.
"""
def __unicode__(self):
if not self:
return '{}'
key_values = []
for key, value in self.items():
key, value = repr(key), repr(value)
if isinstance(value, binary_type) and not PY3:
value = value.decode("utf-8")
key_values.append("{0}: {1}".format(key, value))
res = "{{{0}}}".format(", ".join(key_values))
return res

if PY3:
def __repr__(self):
return self.__unicode__()
else:
def __repr__(self):
return self.__unicode__().encode('utf-8')


def _obj_with_safe_repr(obj):
if isinstance(obj, dict):
ret = FakeOrderedDict()
for key in sorted(obj.keys()):
ret[_obj_with_safe_repr(key)] = _obj_with_safe_repr(obj[key])
elif isinstance(obj, list):
ret = []
for x in obj:
if isinstance(x, dict):
ret.append(_obj_with_safe_repr(x))
else:
ret.append(x)
else:
ret = obj
return ret


def safe_repr(val):
try:
if isinstance(val, dict):
# We special case dicts to have a sorted repr. This makes testing
# significantly easier
val = _obj_with_safe_repr(val)
ret = repr(val)
if not PY3:
ret = ret.decode('utf-8')
except UnicodeEncodeError:
ret = red('a %r that cannot be represented' % type(val))
else:
ret = green(ret)

return ret
from sure.terminal import red, green, yellow
from sure.compat import safe_repr


class DeepExplanation(text_type):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_assertion_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from datetime import datetime
from sure import this, these, those, it, expect, AssertionBuilder
from six import PY3
from sure.compat_py3 import compat_repr
from sure.compat import compat_repr


def test_assertion_builder_synonyms():
Expand Down
38 changes: 19 additions & 19 deletions tests/test_old_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from sure.deprecated import that
from sure import VariablesBag, expect
from nose.tools import assert_equals, assert_raises
from sure.compat_py3 import compat_repr, text_type_name
from sure.compat import compat_repr, safe_repr, text_type_name


def test_setup_with_context():
Expand Down Expand Up @@ -1137,11 +1137,11 @@ def assertions():
assert that(assertions).raises(
AssertionError, compat_repr(
"given\n"
"X = {'one': 'yeah'}\n"
"X = {{'one': 'yeah'}}\n"
" and\n"
"Y = {'two': 'yeah'}\n"
"X has the key \"u'one'\" whereas Y does not"
)
"Y = {{'two': 'yeah'}}\n"
"X has the key \"{0}\" whereas Y does not"
).format(safe_repr('one'))
)


Expand Down Expand Up @@ -1406,11 +1406,11 @@ def assertions():
assert that(assertions).raises(
AssertionError, compat_repr(
"given\n"
"X = {'my::all_users': [{'age': 33, 'name': 'John'}]}\n"
"X = {{'my::all_users': [{{'age': 33, 'name': 'John'}}]}}\n"
" and\n"
"Y = {'my::all_users': [{'age': 30, 'foo': 'bar', 'name': 'John'}]}\n"
"X['my::all_users'][0] does not have the key \"u'foo'\" whereas Y['my::all_users'][0] has it"
)
"Y = {{'my::all_users': [{{'age': 30, 'foo': 'bar', 'name': 'John'}}]}}\n"
"X['my::all_users'][0] does not have the key \"{0}\" whereas Y['my::all_users'][0] has it"
).format(safe_repr('foo'))
)


Expand All @@ -1432,12 +1432,12 @@ def assertions():

assert that(assertions).raises(
AssertionError, compat_repr(
"given\n" \
"X = {'my::all_users': [{'age': 33, 'foo': 'bar', 'name': 'John'}]}\n" \
" and\n" \
"Y = {'my::all_users': [{'age': 30, 'name': 'John'}]}\n" \
"X['my::all_users'][0] has the key \"u'foo'\" whereas Y['my::all_users'][0] does not",
))
"given\n"
"X = {{'my::all_users': [{{'age': 33, 'foo': 'bar', 'name': 'John'}}]}}\n"
" and\n"
"Y = {{'my::all_users': [{{'age': 30, 'name': 'John'}}]}}\n"
"X['my::all_users'][0] has the key \"{0}\" whereas Y['my::all_users'][0] does not"
).format(safe_repr('foo')))


def test_deep_equals_dict_level3_fails_different_key():
Expand All @@ -1459,11 +1459,11 @@ def assertions():
assert that(assertions).raises(
AssertionError, compat_repr(
"given\n"
"X = {'my::all_users': [{'age': 33, 'foo': 'bar', 'name': 'John'}]}\n"
"X = {{'my::all_users': [{{'age': 33, 'foo': 'bar', 'name': 'John'}}]}}\n"
" and\n"
"Y = {'my::all_users': [{'age': 33, 'bar': 'foo', 'name': 'John'}]}\n"
"X['my::all_users'][0] has the key \"u'foo'\" whereas Y['my::all_users'][0] does not"
))
"Y = {{'my::all_users': [{{'age': 33, 'bar': 'foo', 'name': 'John'}}]}}\n"
"X['my::all_users'][0] has the key \"{0}\" whereas Y['my::all_users'][0] does not"
).format(safe_repr('foo')))


def test_deep_equals_list_level2_fail_by_length_x_gt_y():
Expand Down
2 changes: 1 addition & 1 deletion tests/test_safe_repr.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from sure import expect
from sure.core import safe_repr
from sure.compat_py3 import compat_repr
from sure.compat import compat_repr


def test_basic_list():
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# and then run "tox" from this directory.

[tox]
envlist = py27, py35
envlist = py27, py34, py35

[testenv]
commands = nosetests
Expand Down

0 comments on commit ff4b104

Please sign in to comment.