From 6f6dbfadefa68d44e3acb042d8da5411575f6925 Mon Sep 17 00:00:00 2001
From: Stephen Finucane <stephenfin@redhat.com>
Date: Tue, 30 Apr 2019 17:36:40 -0600
Subject: [PATCH] Remove simplejson support

This was allegedly included back when simplejson was more performant
that the 'json' library in Python's library. It's additional complexity
that we simple don't need. Remove it. This also allows us to clean up
the tox file significantly.

Note that we must also rename the 'json' module to 'json_utils', as this
was shadowing a built-in library. This is a latent issue that was
highlighted by removing of 'simplejson' imports.

Change-Id: Id9faa9859993e598e2fd08133de576b740790bf3
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
---
 README.rst                           |  3 +--
 tox.ini                              | 39 ----------------------------
 wsme/rest/args.py                    |  4 +--
 wsme/rest/{json.py => json_utils.py} | 11 ++------
 wsme/rest/protocol.py                |  4 +--
 wsme/rest/{xml.py => xml_utils.py}   |  0
 wsme/tests/test_api.py               |  8 +++---
 wsme/tests/test_restjson.py          | 22 ++++++----------
 wsme/tests/test_restxml.py           | 11 ++++----
 wsmeext/flask.py                     | 20 +++++++-------
 wsmeext/pecan.py                     | 17 ++++++------
 wsmeext/sphinxext.py                 | 19 ++++++--------
 12 files changed, 50 insertions(+), 108 deletions(-)
 rename wsme/rest/{json.py => json_utils.py} (98%)
 rename wsme/rest/{xml.py => xml_utils.py} (100%)

diff --git a/README.rst b/README.rst
index fbae79f..dd56e13 100644
--- a/README.rst
+++ b/README.rst
@@ -60,8 +60,7 @@ Main features
 -   Framework independence : adapters are provided to easily integrate
     your API in any web framework, for example a wsgi container,
     Pecan_, Flask_, ...
--   Very few runtime dependencies: webob, simplegeneric. Optionally
-    simplejson if you need better performances.
+-   Very few runtime dependencies: webob, simplegeneric.
 -   Integration in `Sphinx`_ for making clean documentation with
     ``wsmeext.sphinxext``.
 
diff --git a/tox.ini b/tox.ini
index c3eb013..f4c4452 100644
--- a/tox.ini
+++ b/tox.ini
@@ -87,16 +87,6 @@ deps =
   {[common]basedeps}
 basepython = python2.7
 
-[testenv:py27-simplejson]
-commands =
-  {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
-  {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
-  {[common]testtools}
-  {[common]basedeps}
-  simplejson
-basepython = python2.7
-
 [testenv:py35]
 commands =
   {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
@@ -106,16 +96,6 @@ deps =
   {[common]basedeps}
 basepython = python3.5
 
-[testenv:py35-simplejson]
-commands =
-  {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
-  {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
-  {[common]testtools}
-  {[common]basedeps}
-  simplejson
-basepython = python3.5
-
 [testenv:py36]
 commands =
   {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
@@ -125,16 +105,6 @@ deps =
   {[common]basedeps}
 basepython = python3.6
 
-[testenv:py36-simplejson]
-commands =
-  {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
-  {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
-  {[common]testtools}
-  {[common]basedeps}
-  simplejson
-basepython = python3.6
-
 [testenv:pypy]
 commands =
   {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
@@ -142,12 +112,3 @@ commands =
 deps =
   {[common]testtools}
   {[common]basedeps}
-
-[testenv:pypy-simplejson]
-commands =
-  {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
-  {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
-  {[common]testtools}
-  {[common]basedeps}
-  simplejson
diff --git a/wsme/rest/args.py b/wsme/rest/args.py
index 9dc16c7..2a49672 100644
--- a/wsme/rest/args.py
+++ b/wsme/rest/args.py
@@ -216,8 +216,8 @@ def args_from_params(funcdef, params):
 
 
 def args_from_body(funcdef, body, mimetype):
-    from wsme.rest import json as restjson
-    from wsme.rest import xml as restxml
+    from wsme.rest import json_utils as restjson
+    from wsme.rest import xml_utils as restxml
 
     if funcdef.body_type is not None:
         datatypes = {funcdef.arguments[-1].name: funcdef.body_type}
diff --git a/wsme/rest/json.py b/wsme/rest/json_utils.py
similarity index 98%
rename from wsme/rest/json.py
rename to wsme/rest/json_utils.py
index 48bd082..a7c2f4f 100644
--- a/wsme/rest/json.py
+++ b/wsme/rest/json_utils.py
@@ -1,11 +1,10 @@
 """REST+Json protocol implementation."""
-from __future__ import absolute_import
 import datetime
 import decimal
-
-import six
+import json
 
 from simplegeneric import generic
+import six
 
 import wsme.exc
 import wsme.types
@@ -13,12 +12,6 @@ from wsme.types import Unset
 import wsme.utils
 
 
-try:
-    import simplejson as json
-except ImportError:
-    import json  # noqa
-
-
 content_type = 'application/json'
 accept_content_types = [
     content_type,
diff --git a/wsme/rest/protocol.py b/wsme/rest/protocol.py
index 5201ccf..7748635 100644
--- a/wsme/rest/protocol.py
+++ b/wsme/rest/protocol.py
@@ -25,8 +25,8 @@ class RestProtocol(Protocol):
         self.content_types = []
 
         for dataformat in dataformats:
-            __import__('wsme.rest.' + dataformat)
-            dfmod = getattr(wsme.rest, dataformat)
+            __import__('wsme.rest.' + dataformat + '_utils')
+            dfmod = getattr(wsme.rest, dataformat + '_utils')
             self.dataformats[dataformat] = dfmod
             self.content_types.extend(dfmod.accept_content_types)
 
diff --git a/wsme/rest/xml.py b/wsme/rest/xml_utils.py
similarity index 100%
rename from wsme/rest/xml.py
rename to wsme/rest/xml_utils.py
diff --git a/wsme/tests/test_api.py b/wsme/tests/test_api.py
index fc1e157..f371340 100644
--- a/wsme/tests/test_api.py
+++ b/wsme/tests/test_api.py
@@ -80,8 +80,8 @@ class TestController(unittest.TestCase):
                             headers={'Accept': 'application/json'})
         self.assertTrue(
             res.json_body['faultstring'].startswith(
-                "Invalid input for field/attribute number. Value: 'arf'. \
-Value should be one of:"))
+                "Invalid input for field/attribute number. Value: 'arf'. "
+                "Value should be one of:"))
         self.assertIn('v1', res.json_body['faultstring'])
         self.assertIn('v2', res.json_body['faultstring'])
         self.assertIn('None', res.json_body['faultstring'])
@@ -104,8 +104,8 @@ Value should be one of:"))
                             headers={'Accept': 'application/json'})
         self.assertTrue(
             res.json_body['faultstring'].startswith(
-                "Invalid input for field/attribute number. Value: '1'. \
-Value should be one of:"))
+                "Invalid input for field/attribute number. Value: '1'. "
+                "Value should be one of:"))
         self.assertIn('v1', res.json_body['faultstring'])
         self.assertIn('v2', res.json_body['faultstring'])
         self.assertIn('None', res.json_body['faultstring'])
diff --git a/wsme/tests/test_restjson.py b/wsme/tests/test_restjson.py
index 7afbb86..eca8e47 100644
--- a/wsme/tests/test_restjson.py
+++ b/wsme/tests/test_restjson.py
@@ -1,21 +1,15 @@
 import base64
 import datetime
 import decimal
+import json
 
-import wsme.tests.protocol
-
-try:
-    import simplejson as json
-except ImportError:
-    import json  # noqa
-
-from wsme.rest.json import fromjson, tojson, parse
-from wsme.utils import parse_isodatetime, parse_isotime, parse_isodate
+from wsme.exc import ClientSideError, InvalidInput
 from wsme.types import isarray, isdict, isusertype, register_type
 from wsme.types import UserType, ArrayType, DictType
 from wsme.rest import expose, validate
-from wsme.exc import ClientSideError, InvalidInput
-
+from wsme.rest.json_utils import fromjson, tojson, parse
+import wsme.tests.protocol
+from wsme.utils import parse_isodatetime, parse_isotime, parse_isodate
 
 import six
 from six import b, u
@@ -569,7 +563,7 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
         v.aint = 4
         v.astr = 's'
 
-        r = wsme.rest.json.encode_sample_value(MyType, v, True)
+        r = wsme.rest.json_utils.encode_sample_value(MyType, v, True)
         print(r)
         assert r[0] == ('javascript')
         assert r[1] == json.dumps({'aint': 4, 'astr': 's'}, ensure_ascii=False,
@@ -580,7 +574,7 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
         assert tojson(wsme.types.bytes, b('ascii')) == u('ascii')
 
     def test_encode_sample_params(self):
-        r = wsme.rest.json.encode_sample_params(
+        r = wsme.rest.json_utils.encode_sample_params(
             [('a', int, 2)], True
         )
         assert r[0] == 'javascript', r[0]
@@ -589,7 +583,7 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
 }''', r[1]
 
     def test_encode_sample_result(self):
-        r = wsme.rest.json.encode_sample_result(
+        r = wsme.rest.json_utils.encode_sample_result(
             int, 2, True
         )
         assert r[0] == 'javascript', r[0]
diff --git a/wsme/tests/test_restxml.py b/wsme/tests/test_restxml.py
index b677a65..3fc01fe 100644
--- a/wsme/tests/test_restxml.py
+++ b/wsme/tests/test_restxml.py
@@ -5,11 +5,10 @@ import base64
 from six import u, b
 import six
 
+from wsme.rest.xml_utils import fromxml, toxml
 import wsme.tests.protocol
-from wsme.utils import parse_isodatetime, parse_isodate, parse_isotime
 from wsme.types import isarray, isdict, isusertype, register_type
-
-from wsme.rest.xml import fromxml, toxml
+from wsme.utils import parse_isodatetime, parse_isodate, parse_isotime
 
 try:
     import xml.etree.ElementTree as et
@@ -165,7 +164,7 @@ class TestRestXML(wsme.tests.protocol.RestOnlyProtocolTestCase):
         value.aint = 5
         value.atext = u('test')
 
-        language, sample = wsme.rest.xml.encode_sample_value(
+        language, sample = wsme.rest.xml_utils.encode_sample_value(
             MyType, value, True)
         print(language, sample)
 
@@ -176,13 +175,13 @@ class TestRestXML(wsme.tests.protocol.RestOnlyProtocolTestCase):
 </value>""")
 
     def test_encode_sample_params(self):
-        lang, content = wsme.rest.xml.encode_sample_params(
+        lang, content = wsme.rest.xml_utils.encode_sample_params(
             [('a', int, 2)], True)
         assert lang == 'xml', lang
         assert content == b('<parameters>\n  <a>2</a>\n</parameters>'), content
 
     def test_encode_sample_result(self):
-        lang, content = wsme.rest.xml.encode_sample_result(int, 2, True)
+        lang, content = wsme.rest.xml_utils.encode_sample_result(int, 2, True)
         assert lang == 'xml', lang
         assert content == b('<result>2</result>'), content
 
diff --git a/wsmeext/flask.py b/wsmeext/flask.py
index 4d9e446..d6e57ab 100644
--- a/wsmeext/flask.py
+++ b/wsmeext/flask.py
@@ -5,22 +5,22 @@ import logging
 import sys
 import inspect
 
+import flask
+
 import wsme
 import wsme.api
-import wsme.rest.json
-import wsme.rest.xml
 import wsme.rest.args
+import wsme.rest.json_utils
+import wsme.rest.xml_utils
 from wsme.utils import is_valid_code
 
-import flask
-
 log = logging.getLogger(__name__)
 
 
 TYPES = {
-    'application/json': wsme.rest.json,
-    'application/xml': wsme.rest.xml,
-    'text/xml': wsme.rest.xml
+    'application/json': wsme.rest.json_utils,
+    'application/xml': wsme.rest.xml_utils,
+    'text/xml': wsme.rest.xml_utils,
 }
 
 
@@ -35,9 +35,9 @@ def get_dataformat():
     if req_dataformat in TYPES:
         return TYPES[req_dataformat]
 
-    log.info('''Could not determine what format is wanted by the
-             caller, falling back to json''')
-    return wsme.rest.json
+    log.info('Could not determine what format is wanted by the caller, '
+             'falling back to JSON')
+    return wsme.rest.json_utils
 
 
 def signature(*args, **kw):
diff --git a/wsmeext/pecan.py b/wsmeext/pecan.py
index 9d63dde..474c45e 100644
--- a/wsmeext/pecan.py
+++ b/wsmeext/pecan.py
@@ -4,13 +4,12 @@ import functools
 import inspect
 import sys
 
-import wsme
-import wsme.rest.args
-import wsme.rest.json
-import wsme.rest.xml
-
 import pecan
 
+import wsme
+import wsme.rest.args
+import wsme.rest.json_utils
+import wsme.rest.xml_utils
 from wsme.utils import is_valid_code
 
 
@@ -22,8 +21,8 @@ class JSonRenderer(object):
     @staticmethod
     def render(template_path, namespace):
         if 'faultcode' in namespace:
-            return wsme.rest.json.encode_error(None, namespace)
-        return wsme.rest.json.encode_result(
+            return wsme.rest.json_utils.encode_error(None, namespace)
+        return wsme.rest.json_utils.encode_result(
             namespace['result'],
             namespace['datatype']
         )
@@ -37,8 +36,8 @@ class XMLRenderer(object):
     @staticmethod
     def render(template_path, namespace):
         if 'faultcode' in namespace:
-            return wsme.rest.xml.encode_error(None, namespace)
-        return wsme.rest.xml.encode_result(
+            return wsme.rest.xml_utils.encode_error(None, namespace)
+        return wsme.rest.xml_utils.encode_result(
             namespace['result'],
             namespace['datatype']
         )
diff --git a/wsmeext/sphinxext.py b/wsmeext/sphinxext.py
index 7c45a0f..4ca5370 100644
--- a/wsmeext/sphinxext.py
+++ b/wsmeext/sphinxext.py
@@ -2,26 +2,23 @@ import inspect
 import re
 import sys
 
+from docutils.parsers.rst import Directive
+from docutils.parsers.rst import directives
 import six
-
 from sphinx import addnodes
 from sphinx.ext import autodoc
 from sphinx.domains.python import PyClasslike, PyClassmember
 from sphinx.domains import Domain, ObjType
 from sphinx.directives import ObjectDescription
+from sphinx.locale import l_, _
+from sphinx.roles import XRefRole
 from sphinx.util.docfields import Field
 from sphinx.util.nodes import make_refnode
 
-from sphinx.roles import XRefRole
-from sphinx.locale import l_, _
-
-from docutils.parsers.rst import Directive
-from docutils.parsers.rst import directives
-
 import wsme
+import wsme.rest.json_utils
+import wsme.rest.xml_utils
 import wsme.types
-import wsme.rest.json
-import wsme.rest.xml
 
 field_re = re.compile(r':(?P<field>\w+)(\s+(?P<name>\w+))?:')
 
@@ -56,10 +53,10 @@ def get_protocols(names):
         protocols.extend('restjson', 'restxml')
     if 'restjson' in names:
         names.remove('restjson')
-        protocols.append(('Json', wsme.rest.json))
+        protocols.append(('Json', wsme.rest.json_utils))
     if 'restxml' in names:
         names.remove('restxml')
-        protocols.append(('XML', wsme.rest.xml))
+        protocols.append(('XML', wsme.rest.xml_utils))
     for name in names:
         p = wsme.protocol.getprotocol(name)
         protocols.append((p.displayname or p.name, p))