From b8a2d6d5103bc0ac74db45e39413370bd6b23ecf Mon Sep 17 00:00:00 2001 From: Sam Lee Date: Fri, 29 Jul 2016 22:34:23 -0400 Subject: [PATCH 1/9] make lispify py3-compatible --- .travis.yml | 1 + lispify/lispify.py | 143 +++++++++++++++++++----------------------- tests/test_lispify.py | 72 +++++++++++++-------- 3 files changed, 111 insertions(+), 105 deletions(-) diff --git a/.travis.yml b/.travis.yml index 17306a6..5c64470 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: python python: - "2.7" + - "3.3" install: - pip install codecov pep8 - python setup.py install diff --git a/lispify/lispify.py b/lispify/lispify.py index 85a2832..6316f85 100644 --- a/lispify/lispify.py +++ b/lispify/lispify.py @@ -10,10 +10,14 @@ """ from numbers import Number +from sys import version_info import warnings from .util import subclasses, camel_case_to_lisp_name +# python 3 compatibility functions +def string_types(): + return str if version_info.major > 2 else basestring # For fully deterministic lisp types use this priority. MIN_PRIORITY = 0 @@ -45,7 +49,7 @@ class LispType(object): To use this subclass it and provide any of the following: - - val_str: string representation + - val_str: string representation, should return unicode - should_parse - parse_val """ @@ -57,63 +61,61 @@ def __init__(self, val): """ Lispify a piece of data. Throws ValueError on failure. """ + self.original_val = val - self.val = val - - if self.should_parse(): + if self.should_parse(val): self.val = self.parse_val(val) else: - raise ValueError("failed to lispify %s" % str(self.val)) + raise ValueError(u'failed to lispify {}'.format(val)) - def should_parse(self): + def should_parse(self, val): """ If this returns False, LispType is invalid whatever the value. """ - return True def parse_val(self, val): """ Override this to have special value manipulation. """ - return val def __repr__(self): - r = u"<%s object %s>" % (self.__class__.__name__, - self.__str__()) - return r.encode('utf-8') + return u'{0}({1})'.format(self.__class__.__name__, + repr(self.original_val)) def __str__(self): - return unicode(self).encode('utf-8') + if version_info.major >= 3: + return self.__unicode__() + else: + return unicode(self).encode('utf-8') def __unicode__(self): - return u"%s" % self.val_str() + # we do u'{}'.format twice so bad val_str implementation still works + return u'{}'.format(self.val_str()) def val_str(self): - return unicode(self.val) + return u'{}'.format(self.val) def __nonzero__(self): return True def __eq__(self, other): - # compare LispType objects based on their string representation - if isinstance(other, self.__class__): - return self.__str__() == other.__str__() - elif isinstance(other, basestring): - return self.__str__() == other + if isinstance(other, LispType): + # compare LispType objects based on their string representation + return str(self) == str(other) return False def __hash__(self): - return hash(self.__str__()) + return hash(str(self)) class LispString(LispType): priority = next(MID_PRIORITY) - def should_parse(self): - return isinstance(self.val, basestring) + def should_parse(self, val): + return isinstance(val, string_types()) def val_str(self): v = self.val.replace('"', '\\"') # escape double quotes @@ -130,20 +132,21 @@ class LispList(LispType): priority = next(MID_PRIORITY) literal = True - def should_parse(self): - return hasattr(self.val, '__iter__') + def should_parse(self, val): + return hasattr(val, '__iter__') and not isinstance(val, string_types()) def erepr(self, v): if isinstance(v, LispType): - return unicode(v) + return v.val_str() try: - return unicode(lispify(v)) + return u'{}'.format(lispify(v)) except NotImplementedError: + warnings.warn('Lispifying an unknown type!') return repr(v) def val_str(self): - return u"(%s)" % (" ".join([self.erepr(v) for v in self.val])) + return u'({})'.format(' '.join([self.erepr(v) for v in self.val])) def __contains__(self, val): return (self.erepr(val) in map(self.erepr, self.val)) @@ -157,26 +160,26 @@ class LispDict(LispType): priority = next(MID_PRIORITY) - def should_parse(self): - return isinstance(self.val, dict) + def should_parse(self, val): + return isinstance(val, dict) def val_str(self): pairs = sorted(self.val.items()) - return u'(%s)' % self._plist(pairs) + return u'({})'.format(self._plist(pairs)) def _plist(self, pairs): """ A lispy plist without the parens. """ - - return " ".join(list(self._paren_content_iter(pairs))) + return u' '.join(list(self._paren_content_iter(pairs))) def _kv_pair(self, k, v): if k is None: - return u"%s" % v - - if isinstance(k, basestring): - return u":%s %s" % (k, v) + return u':{}'.format(v) + elif isinstance(k, string_types()): + return u':{} {}'.format(k, v) + else: + raise ValueError('Key {} must be None or string'.format(k)) def _paren_content_iter(self, pairs): for k, v in pairs: @@ -199,10 +202,10 @@ class LispDate(LispDict): # dictionary keys that indicate date format should be treated differently keywords = ["yyyymmdd"] - def should_parse(self): - if isinstance(self.val, dict): - if any([(kw in self.val) for kw in self.keywords]): - return True + def should_parse(self, val): + if super(LispDate, self).should_parse(val): + return any([(kw in val) for kw in self.keywords]) + return False def _paren_content_iter(self, pairs): for k, v in pairs: @@ -213,7 +216,7 @@ def _paren_content_iter(self, pairs): yield self._kv_pair(k, lispify(v)) -class LispError(LispType): +class LispError(LispDict): """ An error with a symbol. The expected value should be an exception @@ -225,37 +228,22 @@ class LispError(LispType): priority = MAX_PRIORITY - def should_parse(self): - return isinstance(self.val, Exception) + def should_parse(self, val): + return isinstance(val, Exception) def val_str(self): symbol = camel_case_to_lisp_name(type(self.val).__name__) kw = self.val.__dict__ - if self.val.message: - kw.update({'message': self.val.message}) + if len(self.val.args) > 0: + kw.update({'message': self.val.args[0]}) if kw: - return u"(:error {symbol} {keys})".format( + return u'(:error {symbol} {keys})'.format( symbol=symbol, keys=self._plist(kw.items()) ) else: - return u"(:error %s)" % symbol - - def _plist(self, pairs): - return " ".join(list(self._paren_content_iter(pairs))) - - def _kv_pair(self, k, v): - if k is None: - return u"%s" % v - - if isinstance(k, basestring): - return u":%s %s" % (k, v) - - def _paren_content_iter(self, pairs): - for k, v in pairs: - if v is not None: - yield self._kv_pair(k, lispify(v)) + return u'(:error %s)'.format(symbol) def __nonzero__(self): """ @@ -264,7 +252,6 @@ def __nonzero__(self): if potential_error_or_none: # do stuff """ - return False @@ -273,6 +260,7 @@ class _LispLiteral(LispType): """ Lisp literals. These are not. """ + priority = MAX_PRIORITY literal = True @@ -283,12 +271,10 @@ class LispKeyword(_LispLiteral): Just a keyword. No content. """ - def should_parse(self): - return isinstance(self.val, basestring) and self.val.startswith(':') \ - and ' ' not in self.val - - def val_str(self): - return self.val + def should_parse(self, val): + return (isinstance(val, string_types()) and + val.startswith(':') and + ' ' not in val) class LispBool(_LispLiteral): @@ -297,8 +283,8 @@ class LispBool(_LispLiteral): Lispify a boolean value """ - def should_parse(self): - return isinstance(self.val, bool) + def should_parse(self, val): + return isinstance(val, bool) def val_str(self): return u't' if self.val else u'nil' @@ -310,8 +296,8 @@ class LispNone(_LispLiteral): Lispified none object """ - def should_parse(self): - return self.val is None + def should_parse(self, val): + return val is None def val_str(self): return u'nil' @@ -319,11 +305,8 @@ def val_str(self): class LispNumber(_LispLiteral): - def should_parse(self): - return isinstance(self.val, Number) - - def val_str(self): - return str(self.val) + def should_parse(self, val): + return isinstance(val, Number) LISP_TYPES = subclasses(LispType, instantiate=False) @@ -343,7 +326,7 @@ def lispify(obj): except ValueError: pass - raise NotImplementedError( - "Implement LispType for val: %s or provide fallback error." % obj) + raise NotImplementedError('Implement LispType for val: {} or provide ' + 'fallback error.'.format(obj)) __all__ = ['lispify'] diff --git a/tests/test_lispify.py b/tests/test_lispify.py index 402da86..f05b3a5 100644 --- a/tests/test_lispify.py +++ b/tests/test_lispify.py @@ -8,6 +8,8 @@ Tests for `lispify` module. """ +from sys import version_info + try: import unittest2 as unittest except ImportError: @@ -19,36 +21,51 @@ class TestLispify(unittest.TestCase): def test_string(self): - self.assertEqual(lispify("foo"), '"foo"') + self.assertEqual(str(lispify("foo")), + '"foo"') def test_string_escaped(self): - self.assertEqual(lispify('foo \\ "bar"'), '"foo \\ \\"bar\\""') + self.assertEqual(str(lispify('foo \\ "bar"')), + '"foo \\ \\"bar\\""') + + @unittest.skipIf(version_info.major == 2, 'python 3 string behavior') + def test_encodings_py3(self): + self.assertEqual(str(lispify(u"föø ” ")), + u'"föø ” "') - def test_encodings(self): - self.assertEqual(str(lispify(u"föø ” ")), u'"föø ” "'.encode('utf-8')) - self.assertEqual(unicode(lispify(u"föø ” ")), u'"föø ” "') + @unittest.skipUnless(version_info.major == 2, 'python 2 string behavior') + def test_encodings_py2(self): + self.assertEqual(str(lispify(u"föø ” ")), + u'"föø ” "'.encode('utf-8')) + self.assertEqual(unicode(lispify(u"föø ” ")), + u'"föø ” "') def test_list(self): l = ['wikipedia-class1', 'wikipedia-class2'] - self.assertEqual(lispify(l), '("wikipedia-class1" "wikipedia-class2")') + self.assertEqual(str(lispify(l)), + '("wikipedia-class1" "wikipedia-class2")') def test_nested_list(self): l = [[0, 'foo'], [1, '"bar"']] - self.assertEqual(lispify(l), '((0 "foo") (1 "\\"bar\\""))') + self.assertEqual(str(lispify(l)), + '((0 "foo") (1 "\\"bar\\""))') def test_nested_tuple(self): # this test needs to be separated from nested list because tuples # are used in string formatting (see #222 for an example of failure) t = ('foo', ('bar', 'baz')) - self.assertEqual(lispify(t), '("foo" ("bar" "baz"))') + self.assertEqual(str(lispify(t)), + '("foo" ("bar" "baz"))') def test_double_nested_list(self): l = [[0, ['v0', 'foo']], [1, ['v1', 'bar']]] - self.assertEqual(lispify(l), '((0 ("v0" "foo")) (1 ("v1" "bar")))') + self.assertEqual(str(lispify(l)), + '((0 ("v0" "foo")) (1 ("v1" "bar")))') def test_list_of_dict(self): l = [{'foo': 'bar'}, {'foo': 'baz'}] - self.assertEqual(lispify(l), '((:foo "bar") (:foo "baz"))') + self.assertEqual(str(lispify(l)), + '((:foo "bar") (:foo "baz"))') def test_list_contains(self): ll = lispify(["1", "3"]) @@ -56,44 +73,49 @@ def test_list_contains(self): self.assertNotIn(1, ll) def test_date_simple(self): - ed = lispify({'yyyymmdd': '00000808'}) - self.assertEqual(ed, '(:yyyymmdd 00000808)') + date = {'yyyymmdd': '00000808'} + self.assertEqual(str(lispify(date)), + '(:yyyymmdd 00000808)') def test_date_multiple_keys(self): - ed = lispify({'yyyymmdd': '19491001', 'html': 'Oct 1, 1949'}) - self.assertEqual(ed, '(:html "Oct 1, 1949" :yyyymmdd 19491001)') + date = {'yyyymmdd': '19491001', 'html': 'Oct 1, 1949'} + self.assertEqual(str(lispify(date)), + '(:html "Oct 1, 1949" :yyyymmdd 19491001)') def test_bool(self): - self.assertEqual(lispify(True), 't') - self.assertEqual(lispify(False), 'nil') + self.assertEqual(str(lispify(True)), 't') + self.assertEqual(str(lispify(False)), 'nil') def test_keyword(self): - self.assertEqual(lispify(':feminine'), ":feminine") + self.assertEqual(str(lispify(':feminine')), + ':feminine') def test_string_not_keyword(self): - self.assertEqual(lispify(':not a keyword'), '":not a keyword"') + self.assertEqual(str(lispify(':not a keyword')), + '":not a keyword"') def test_dict(self): - self.assertEqual(lispify({'a': 1, 'b': "foo"}), + self.assertEqual(str(lispify({'a': 1, 'b': "foo"})), '(:a 1 :b "foo")') def test_dict_with_escaped_string(self): - self.assertEqual(lispify({'a': 1, 'b': '"foo"'}), + self.assertEqual(str(lispify({'a': 1, 'b': '"foo"'})), '(:a 1 :b "\\"foo\\"")') def test_dict_with_list(self): - self.assertEqual(lispify({'a': 1, 'b': ['foo', 'bar']}), + self.assertEqual(str(lispify({'a': 1, 'b': ['foo', 'bar']})), '(:a 1 :b ("foo" "bar"))') def test_error_from_exception(self): - err = lispify(ValueError('Wrong thing')) - self.assertEqual(err, '(:error value-error :message "Wrong thing")') + err = ValueError('Wrong thing') + self.assertEqual(str(lispify(err)), + '(:error value-error :message "Wrong thing")') def test_none(self): - self.assertEqual(lispify(None), 'nil') + self.assertEqual(str(lispify(None)), 'nil') def test_number(self): - self.assertEqual(lispify(5), '5') + self.assertEqual(str(lispify(5)), '5') def test_lispified(self): val = {"hello": "world"} From e4cd22ac2796041adc5a97ab52cbe5a73c443b89 Mon Sep 17 00:00:00 2001 From: Sam Lee Date: Fri, 29 Jul 2016 22:41:14 -0400 Subject: [PATCH 2/9] Fix style issue --- .travis.yml | 2 +- lispify/lispify.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5c64470..a206660 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: python python: - "2.7" - - "3.3" + - "3.4" install: - pip install codecov pep8 - python setup.py install diff --git a/lispify/lispify.py b/lispify/lispify.py index 759237b..38ef761 100644 --- a/lispify/lispify.py +++ b/lispify/lispify.py @@ -15,6 +15,7 @@ from util import subclasses, camel_case_to_lisp_name + # python 3 compatibility functions def string_types(): return str if version_info.major > 2 else basestring From 03581a1a1221f55f4d3ae48e83a7036224bd8a4b Mon Sep 17 00:00:00 2001 From: Sam Lee Date: Fri, 29 Jul 2016 23:01:50 -0400 Subject: [PATCH 3/9] Fix import problem in 2.x --- lispify/lispify.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lispify/lispify.py b/lispify/lispify.py index 38ef761..6ecc73d 100644 --- a/lispify/lispify.py +++ b/lispify/lispify.py @@ -9,11 +9,13 @@ so that the lispify method ignores it. """ +from __future__ import absolute_import + from numbers import Number from sys import version_info import warnings -from util import subclasses, camel_case_to_lisp_name +from lispify.util import subclasses, camel_case_to_lisp_name # python 3 compatibility functions From 18d2d650513b8baf166cc647d2fbfdbaaa34d035 Mon Sep 17 00:00:00 2001 From: Sam Lee Date: Sat, 30 Jul 2016 15:46:05 -0400 Subject: [PATCH 4/9] Use future for compatibility --- .travis.yml | 6 ++++++ lispify/lispify.py | 22 +++++++------------- requirements.txt | 1 + tests/test_lispify.py | 48 +++++++++++++++++++++++-------------------- 4 files changed, 40 insertions(+), 37 deletions(-) create mode 100644 requirements.txt diff --git a/.travis.yml b/.travis.yml index a206660..9f24047 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,14 @@ language: python python: + - "2.6" - "2.7" + - "3.3" - "3.4" + - "3.5" + - "3.5-dev" + - "nightly" install: + - pip install -r requirements.txt - pip install codecov pep8 - python setup.py install script: diff --git a/lispify/lispify.py b/lispify/lispify.py index 6ecc73d..e3b5fef 100644 --- a/lispify/lispify.py +++ b/lispify/lispify.py @@ -12,15 +12,13 @@ from __future__ import absolute_import from numbers import Number -from sys import version_info import warnings from lispify.util import subclasses, camel_case_to_lisp_name +from future.utils import python_2_unicode_compatible +from past.builtins import basestring -# python 3 compatibility functions -def string_types(): - return str if version_info.major > 2 else basestring # For fully deterministic lisp types use this priority. MIN_PRIORITY = 0 @@ -31,7 +29,6 @@ def mid_priority(): """ Just count. Later classes override previous ones. """ - for i in range(MIN_PRIORITY + 1, MAX_PRIORITY): yield i @@ -42,6 +39,7 @@ def mid_priority(): MID_PRIORITY = mid_priority() +@python_2_unicode_compatible class LispType(object): """ @@ -88,12 +86,6 @@ def __repr__(self): repr(self.original_val)) def __str__(self): - if version_info.major >= 3: - return self.__unicode__() - else: - return unicode(self).encode('utf-8') - - def __unicode__(self): # we do u'{}'.format twice so bad val_str implementation still works return u'{}'.format(self.val_str()) @@ -118,7 +110,7 @@ class LispString(LispType): priority = next(MID_PRIORITY) def should_parse(self, val): - return isinstance(val, string_types()) + return isinstance(val, basestring) def val_str(self): v = self.val.replace('"', '\\"') # escape double quotes @@ -136,7 +128,7 @@ class LispList(LispType): literal = True def should_parse(self, val): - return hasattr(val, '__iter__') and not isinstance(val, string_types()) + return hasattr(val, '__iter__') and not isinstance(val, basestring) def erepr(self, v): if isinstance(v, LispType): @@ -179,7 +171,7 @@ def _plist(self, pairs): def _kv_pair(self, k, v): if k is None: return u':{}'.format(v) - elif isinstance(k, string_types()): + elif isinstance(k, basestring): return u':{} {}'.format(k, v) else: raise ValueError('Key {} must be None or string'.format(k)) @@ -275,7 +267,7 @@ class LispKeyword(_LispLiteral): """ def should_parse(self, val): - return (isinstance(val, string_types()) and + return (isinstance(val, basestring) and val.startswith(':') and ' ' not in val) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..970c6de --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +future==0.15.2 diff --git a/tests/test_lispify.py b/tests/test_lispify.py index f05b3a5..a46eb9e 100644 --- a/tests/test_lispify.py +++ b/tests/test_lispify.py @@ -18,53 +18,57 @@ from lispify.lispify import lispify +def to_lisp_string(obj): + return str(lispify(obj)) + + class TestLispify(unittest.TestCase): def test_string(self): - self.assertEqual(str(lispify("foo")), + self.assertEqual(to_lisp_string("foo"), '"foo"') def test_string_escaped(self): - self.assertEqual(str(lispify('foo \\ "bar"')), + self.assertEqual(to_lisp_string('foo \\ "bar"'), '"foo \\ \\"bar\\""') - @unittest.skipIf(version_info.major == 2, 'python 3 string behavior') + @unittest.skipUnless(version_info.major == 3, 'python 3 string behavior') def test_encodings_py3(self): - self.assertEqual(str(lispify(u"föø ” ")), + self.assertEqual(to_lisp_string(u"föø ” "), u'"föø ” "') @unittest.skipUnless(version_info.major == 2, 'python 2 string behavior') def test_encodings_py2(self): - self.assertEqual(str(lispify(u"föø ” ")), + self.assertEqual(to_lisp_string(u"föø ” "), u'"föø ” "'.encode('utf-8')) self.assertEqual(unicode(lispify(u"föø ” ")), u'"föø ” "') def test_list(self): l = ['wikipedia-class1', 'wikipedia-class2'] - self.assertEqual(str(lispify(l)), + self.assertEqual(to_lisp_string(l), '("wikipedia-class1" "wikipedia-class2")') def test_nested_list(self): l = [[0, 'foo'], [1, '"bar"']] - self.assertEqual(str(lispify(l)), + self.assertEqual(to_lisp_string(l), '((0 "foo") (1 "\\"bar\\""))') def test_nested_tuple(self): # this test needs to be separated from nested list because tuples # are used in string formatting (see #222 for an example of failure) t = ('foo', ('bar', 'baz')) - self.assertEqual(str(lispify(t)), + self.assertEqual(to_lisp_string(t), '("foo" ("bar" "baz"))') def test_double_nested_list(self): l = [[0, ['v0', 'foo']], [1, ['v1', 'bar']]] - self.assertEqual(str(lispify(l)), + self.assertEqual(to_lisp_string(l), '((0 ("v0" "foo")) (1 ("v1" "bar")))') def test_list_of_dict(self): l = [{'foo': 'bar'}, {'foo': 'baz'}] - self.assertEqual(str(lispify(l)), + self.assertEqual(to_lisp_string(l), '((:foo "bar") (:foo "baz"))') def test_list_contains(self): @@ -74,48 +78,48 @@ def test_list_contains(self): def test_date_simple(self): date = {'yyyymmdd': '00000808'} - self.assertEqual(str(lispify(date)), + self.assertEqual(to_lisp_string(date), '(:yyyymmdd 00000808)') def test_date_multiple_keys(self): date = {'yyyymmdd': '19491001', 'html': 'Oct 1, 1949'} - self.assertEqual(str(lispify(date)), + self.assertEqual(to_lisp_string(date), '(:html "Oct 1, 1949" :yyyymmdd 19491001)') def test_bool(self): - self.assertEqual(str(lispify(True)), 't') - self.assertEqual(str(lispify(False)), 'nil') + self.assertEqual(to_lisp_string(True), 't') + self.assertEqual(to_lisp_string(False), 'nil') def test_keyword(self): - self.assertEqual(str(lispify(':feminine')), + self.assertEqual(to_lisp_string(':feminine'), ':feminine') def test_string_not_keyword(self): - self.assertEqual(str(lispify(':not a keyword')), + self.assertEqual(to_lisp_string(':not a keyword'), '":not a keyword"') def test_dict(self): - self.assertEqual(str(lispify({'a': 1, 'b': "foo"})), + self.assertEqual(to_lisp_string({'a': 1, 'b': "foo"}), '(:a 1 :b "foo")') def test_dict_with_escaped_string(self): - self.assertEqual(str(lispify({'a': 1, 'b': '"foo"'})), + self.assertEqual(to_lisp_string({'a': 1, 'b': '"foo"'}), '(:a 1 :b "\\"foo\\"")') def test_dict_with_list(self): - self.assertEqual(str(lispify({'a': 1, 'b': ['foo', 'bar']})), + self.assertEqual(to_lisp_string({'a': 1, 'b': ['foo', 'bar']}), '(:a 1 :b ("foo" "bar"))') def test_error_from_exception(self): err = ValueError('Wrong thing') - self.assertEqual(str(lispify(err)), + self.assertEqual(to_lisp_string(err), '(:error value-error :message "Wrong thing")') def test_none(self): - self.assertEqual(str(lispify(None)), 'nil') + self.assertEqual(to_lisp_string(None), 'nil') def test_number(self): - self.assertEqual(str(lispify(5)), '5') + self.assertEqual(to_lisp_string(5), '5') def test_lispified(self): val = {"hello": "world"} From caca086b8a2eff54a297b54cf095c217a25fa320 Mon Sep 17 00:00:00 2001 From: Sam Lee Date: Sat, 30 Jul 2016 15:53:00 -0400 Subject: [PATCH 5/9] Fix 2.6 test problem by using unittest2 --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 970c6de..00fdb8e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ future==0.15.2 +unittest2==1.1.0 From 982513eac46f6093de3cb1bb80a07b18a174fbbd Mon Sep 17 00:00:00 2001 From: Sam Lee Date: Sat, 30 Jul 2016 16:12:17 -0400 Subject: [PATCH 6/9] Fix docs, remove unneeded code, add tests and 2.6 support --- lispify/lispify.py | 12 ++++++++---- lispify/util.py | 11 +++-------- tests/test_lispify.py | 12 ++++++++++-- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/lispify/lispify.py b/lispify/lispify.py index e3b5fef..317ba3f 100644 --- a/lispify/lispify.py +++ b/lispify/lispify.py @@ -107,6 +107,11 @@ def __hash__(self): class LispString(LispType): + + """ + Encodes a Lisp string. + """ + priority = next(MID_PRIORITY) def should_parse(self, val): @@ -121,7 +126,7 @@ def val_str(self): class LispList(LispType): """ - This is coordinates and other things like that + Encodes iterable objects (except for strings) as a list. """ priority = next(MID_PRIORITY) @@ -238,7 +243,7 @@ def val_str(self): keys=self._plist(kw.items()) ) else: - return u'(:error %s)'.format(symbol) + return u'(:error {})'.format(symbol) def __nonzero__(self): """ @@ -304,14 +309,13 @@ def should_parse(self, val): return isinstance(val, Number) -LISP_TYPES = subclasses(LispType, instantiate=False) +LISP_TYPES = subclasses(LispType) def lispify(obj): """ Return a Lisp-like encoded string. """ - if isinstance(obj, LispType): return obj diff --git a/lispify/util.py b/lispify/util.py index e64b669..f194cf4 100644 --- a/lispify/util.py +++ b/lispify/util.py @@ -2,10 +2,9 @@ import re -def subclasses(cls, instantiate=True, **kw): +def subclasses(cls): """ - A list of instances of subclasses of cls. Instantiate wit kw and - return just the classes with instantiate=False. + A list of subclasses of cls. """ lcls = cls.__subclasses__() @@ -16,11 +15,7 @@ def subclasses(cls, instantiate=True, **kw): clss = sorted(rcls, key=lambda c: c.priority, reverse=True) - if not instantiate: - return [C for C in clss - if not C.__name__.startswith("_")] - - return [C(**kw) for C in clss + return [C for C in clss if not C.__name__.startswith("_")] diff --git a/tests/test_lispify.py b/tests/test_lispify.py index a46eb9e..3371265 100644 --- a/tests/test_lispify.py +++ b/tests/test_lispify.py @@ -32,12 +32,12 @@ def test_string_escaped(self): self.assertEqual(to_lisp_string('foo \\ "bar"'), '"foo \\ \\"bar\\""') - @unittest.skipUnless(version_info.major == 3, 'python 3 string behavior') + @unittest.skipIf(version_info < (3,), 'python 3 string behavior') def test_encodings_py3(self): self.assertEqual(to_lisp_string(u"föø ” "), u'"föø ” "') - @unittest.skipUnless(version_info.major == 2, 'python 2 string behavior') + @unittest.skipIf(version_info > (3,), 'python 2 string behavior') def test_encodings_py2(self): self.assertEqual(to_lisp_string(u"föø ” "), u'"föø ” "'.encode('utf-8')) @@ -115,6 +115,10 @@ def test_error_from_exception(self): self.assertEqual(to_lisp_string(err), '(:error value-error :message "Wrong thing")') + err = NotImplementedError() + self.assertEqual(to_lisp_string(err), + '(:error not-implemented-error)') + def test_none(self): self.assertEqual(to_lisp_string(None), 'nil') @@ -125,6 +129,10 @@ def test_lispified(self): val = {"hello": "world"} self.assertEqual(lispify(val), lispify(lispify(val))) + def test_partially_lispified(self): + val = [lispify(1), lispify(2)] + self.assertEqual(to_lisp_string(val), '(1 2)') + @unittest.expectedFailure def test_unimplemented(self): lispify(lambda x: x) From ef3170924051964f9c635ad217800ccbd1fa6d9c Mon Sep 17 00:00:00 2001 From: Sam Lee Date: Sat, 30 Jul 2016 16:18:23 -0400 Subject: [PATCH 7/9] F**k 2.6, better not run into more errors --- lispify/lispify.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lispify/lispify.py b/lispify/lispify.py index 317ba3f..931499e 100644 --- a/lispify/lispify.py +++ b/lispify/lispify.py @@ -67,7 +67,7 @@ def __init__(self, val): if self.should_parse(val): self.val = self.parse_val(val) else: - raise ValueError(u'failed to lispify {}'.format(val)) + raise ValueError(u'failed to lispify {0}'.format(val)) def should_parse(self, val): """ @@ -87,10 +87,10 @@ def __repr__(self): def __str__(self): # we do u'{}'.format twice so bad val_str implementation still works - return u'{}'.format(self.val_str()) + return u'{0}'.format(self.val_str()) def val_str(self): - return u'{}'.format(self.val) + return u'{0}'.format(self.val) def __nonzero__(self): return True @@ -140,13 +140,13 @@ def erepr(self, v): return v.val_str() try: - return u'{}'.format(lispify(v)) + return u'{0}'.format(lispify(v)) except NotImplementedError: warnings.warn('Lispifying an unknown type!') return repr(v) def val_str(self): - return u'({})'.format(' '.join([self.erepr(v) for v in self.val])) + return u'({0})'.format(' '.join([self.erepr(v) for v in self.val])) def __contains__(self, val): return (self.erepr(val) in map(self.erepr, self.val)) @@ -165,7 +165,7 @@ def should_parse(self, val): def val_str(self): pairs = sorted(self.val.items()) - return u'({})'.format(self._plist(pairs)) + return u'({0})'.format(self._plist(pairs)) def _plist(self, pairs): """ @@ -175,11 +175,11 @@ def _plist(self, pairs): def _kv_pair(self, k, v): if k is None: - return u':{}'.format(v) + return u':{0}'.format(v) elif isinstance(k, basestring): - return u':{} {}'.format(k, v) + return u':{0} {1}'.format(k, v) else: - raise ValueError('Key {} must be None or string'.format(k)) + raise ValueError('Key {0} must be None or string'.format(k)) def _paren_content_iter(self, pairs): for k, v in pairs: @@ -243,7 +243,7 @@ def val_str(self): keys=self._plist(kw.items()) ) else: - return u'(:error {})'.format(symbol) + return u'(:error {symbol})'.format(symbol=symbol) def __nonzero__(self): """ @@ -325,7 +325,7 @@ def lispify(obj): except ValueError: pass - raise NotImplementedError('Implement LispType for val: {} or provide ' + raise NotImplementedError('Implement LispType for val: {0} or provide ' 'fallback error.'.format(obj)) __all__ = ['lispify'] From f468423a5751fa423ee765b733a02185208741f8 Mon Sep 17 00:00:00 2001 From: Sam Lee Date: Sat, 30 Jul 2016 16:27:53 -0400 Subject: [PATCH 8/9] I want to drop 2.6 support... --- README.md | 2 ++ tests/test_lispify.py | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 38ef1cc..6f26128 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # lispify Lispify converts Python objects into Lisp-like encoded strings that are interpretable in Common Lisp. + +Requires Python 2.6, 2.7, or 3.3+. \ No newline at end of file diff --git a/tests/test_lispify.py b/tests/test_lispify.py index 3371265..d0af5f3 100644 --- a/tests/test_lispify.py +++ b/tests/test_lispify.py @@ -32,17 +32,17 @@ def test_string_escaped(self): self.assertEqual(to_lisp_string('foo \\ "bar"'), '"foo \\ \\"bar\\""') - @unittest.skipIf(version_info < (3,), 'python 3 string behavior') - def test_encodings_py3(self): - self.assertEqual(to_lisp_string(u"föø ” "), - u'"föø ” "') - - @unittest.skipIf(version_info > (3,), 'python 2 string behavior') - def test_encodings_py2(self): - self.assertEqual(to_lisp_string(u"föø ” "), - u'"föø ” "'.encode('utf-8')) - self.assertEqual(unicode(lispify(u"föø ” ")), - u'"föø ” "') + def test_encodings(self): + if version_info < (3,): + # python 2 behavior + self.assertEqual(to_lisp_string(u"föø ” "), + u'"föø ” "'.encode('utf-8')) + self.assertEqual(unicode(lispify(u"föø ” ")), + u'"föø ” "') + else: + # python 3 behavior + self.assertEqual(to_lisp_string(u"föø ” "), + u'"föø ” "') def test_list(self): l = ['wikipedia-class1', 'wikipedia-class2'] From b7b3b854be1a2d56957937b346c340f13c390c61 Mon Sep 17 00:00:00 2001 From: Sam Lee Date: Sat, 30 Jul 2016 18:18:01 -0400 Subject: [PATCH 9/9] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index bd5877e..5f7b606 100644 --- a/README.md +++ b/README.md @@ -12,4 +12,3 @@ Version numbers `MAJOR.MINOR.PATCH` should follow a system inspired by [semantic 1. PATCH version when you make backwards-compatible bug fixes. Create a [release](https://help.github.com/articles/creating-releases/) on Github for each new version. ->>>>>>> master