Skip to content

test_routes_server_unicode

Tests for Unicode-safe JSON serialization in routes server handler.

Verifies that _safe_json_dumps correctly serializes objects containing non-ASCII characters (e.g. French accented letters) that cause UnicodeDecodeError in IronPython 2.7's json.dumps.

Also verifies that parse_response falls back to _safe_json_dumps when json.dumps raises Unicode errors.

Attributes

Classes

SafeJsonDumpsTests

Bases: TestCase

Tests for handler._safe_json_dumps.

Functions

test_none()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_none(self):
    self.assertEqual("null", handler._safe_json_dumps(None))
test_bool_true()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_bool_true(self):
    self.assertEqual("true", handler._safe_json_dumps(True))
test_bool_false()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_bool_false(self):
    self.assertEqual("false", handler._safe_json_dumps(False))
test_integer()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_integer(self):
    self.assertEqual("42", handler._safe_json_dumps(42))
test_zero()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_zero(self):
    self.assertEqual("0", handler._safe_json_dumps(0))
test_negative_integer()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_negative_integer(self):
    self.assertEqual("-7", handler._safe_json_dumps(-7))
test_long_integer()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
@unittest.skipUnless(PY2, "Python 2 only long integer behavior")
def test_long_integer(self):
    value = long(2 ** 40)  # pylint: disable=undefined-variable
    result = handler._safe_json_dumps(value)
    self.assertEqual(str(value), result)
    self.assertEqual(2 ** 40, json.loads(result))
test_float()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_float(self):
    result = handler._safe_json_dumps(3.14)
    self.assertAlmostEqual(3.14, float(result))
test_ascii_string()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_ascii_string(self):
    result = handler._safe_json_dumps("hello")
    self.assertEqual('"hello"', result)
    # Must be valid JSON
    self.assertEqual("hello", json.loads(result))
test_empty_string()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_empty_string(self):
    result = handler._safe_json_dumps("")
    self.assertEqual('""', result)
test_french_accented_string()

French accented chars like é (U+00E9) caused UnicodeDecodeError.

Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_french_accented_string(self):
    """French accented chars like é (U+00E9) caused UnicodeDecodeError."""
    result = handler._safe_json_dumps(u"caf\u00e9")
    self.assertIn("\\u00e9", result)
    # Must round-trip through json.loads
    self.assertEqual(u"caf\u00e9", json.loads(result))
test_multiple_accented_chars()

Typical French Revit view names with multiple accents.

Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_multiple_accented_chars(self):
    """Typical French Revit view names with multiple accents."""
    text = u"\u00c9L\u00c9VATIONS EXT\u00c9RIEURES"  # ÉLÉVATIONS EXTÉRIEURES
    result = handler._safe_json_dumps(text)
    self.assertEqual(text, json.loads(result))
test_e_grave()

è (U+00E8) as in 'Système'.

Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_e_grave(self):
    """è (U+00E8) as in 'Système'."""
    result = handler._safe_json_dumps(u"Syst\u00e8me")
    self.assertEqual(u"Syst\u00e8me", json.loads(result))
test_circumflex()

ê (U+00EA) as in 'Fenêtres'.

Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_circumflex(self):
    """ê (U+00EA) as in 'Fenêtres'."""
    result = handler._safe_json_dumps(u"Fen\u00eatres")
    self.assertEqual(u"Fen\u00eatres", json.loads(result))
test_mixed_ascii_and_unicode()

Mix of ASCII and non-ASCII in same string.

Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_mixed_ascii_and_unicode(self):
    """Mix of ASCII and non-ASCII in same string."""
    text = u"Level 1 - \u00c9tage"
    result = handler._safe_json_dumps(text)
    self.assertEqual(text, json.loads(result))
test_backslash()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_backslash(self):
    result = handler._safe_json_dumps("a\\b")
    self.assertEqual("a\\b", json.loads(result))
test_double_quote()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_double_quote(self):
    result = handler._safe_json_dumps('say "hi"')
    self.assertEqual('say "hi"', json.loads(result))
test_newline()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_newline(self):
    result = handler._safe_json_dumps("line1\nline2")
    self.assertEqual("line1\nline2", json.loads(result))
test_tab()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_tab(self):
    result = handler._safe_json_dumps("a\tb")
    self.assertEqual("a\tb", json.loads(result))
test_carriage_return()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_carriage_return(self):
    result = handler._safe_json_dumps("a\rb")
    self.assertEqual("a\rb", json.loads(result))
test_control_char()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_control_char(self):
    result = handler._safe_json_dumps("a\x01b")
    self.assertIn("\\u0001", result)
    self.assertEqual("a\x01b", json.loads(result))
test_list()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_list(self):
    result = handler._safe_json_dumps([1, "two", None])
    parsed = json.loads(result)
    self.assertEqual([1, "two", None], parsed)
test_dict()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_dict(self):
    result = handler._safe_json_dumps({"key": "value", "n": 42})
    parsed = json.loads(result)
    self.assertEqual("value", parsed["key"])
    self.assertEqual(42, parsed["n"])
test_nested_dict_with_unicode()

Nested structure with non-ASCII values — the real-world scenario.

Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_nested_dict_with_unicode(self):
    """Nested structure with non-ASCII values — the real-world scenario."""
    obj = {
        "views": [
            {"name": u"PLAN G\u00c9N\u00c9RALE"},
            {"name": u"Nomenclature des r\u00e9visions"},
        ],
        "total": 2,
    }
    result = handler._safe_json_dumps(obj)
    parsed = json.loads(result)
    self.assertEqual(u"PLAN G\u00c9N\u00c9RALE", parsed["views"][0]["name"])
    self.assertEqual(u"Nomenclature des r\u00e9visions", parsed["views"][1]["name"])
    self.assertEqual(2, parsed["total"])
test_tuple()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_tuple(self):
    result = handler._safe_json_dumps((1, 2, 3))
    self.assertEqual([1, 2, 3], json.loads(result))
test_empty_list()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_empty_list(self):
    self.assertEqual("[]", handler._safe_json_dumps([]))
test_empty_dict()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_empty_dict(self):
    self.assertEqual("{}", handler._safe_json_dumps({}))
test_dict_with_unicode_key()
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_dict_with_unicode_key(self):
    result = handler._safe_json_dumps({u"\u00e9tage": 1})
    parsed = json.loads(result)
    self.assertEqual(1, parsed[u"\u00e9tage"])
test_bool_not_treated_as_int()

Bool is a subclass of int; must check bool first.

Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_bool_not_treated_as_int(self):
    """Bool is a subclass of int; must check bool first."""
    self.assertEqual("true", handler._safe_json_dumps(True))
    self.assertNotEqual("1", handler._safe_json_dumps(True))

ParseResponseUnicodeTests

Bases: TestCase

Tests that parse_response handles non-ASCII data without crashing.

In IronPython 2.7, json.dumps raises UnicodeDecodeError on non-ASCII strings. parse_response must fall back to _safe_json_dumps.

Functions

test_string_response_with_accents()

Plain string response containing accented characters.

Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_string_response_with_accents(self):
    """Plain string response containing accented characters."""
    response = u"Fen\u00eatres ext\u00e9rieures"
    result = self._parse_response_forced_fallback(response)
    self.assertEqual(base.OK, result.status)
    self.assertIsNotNone(result.data)
    # The data should be valid JSON (a JSON-encoded string)
    parsed = json.loads(result.data)
    self.assertEqual(response, parsed)
test_dict_response_with_accents()

Response object with .data containing non-ASCII strings.

Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_dict_response_with_accents(self):
    """Response object with .data containing non-ASCII strings."""

    class _Resp(object):
        status = base.OK
        headers = {"Content-Type": "application/json"}
        data = {
            "views": [
                u"\u00c9L\u00c9VATIONS",
                u"R\u00c9SERVOIRS",
            ]
        }

    result = self._parse_response_forced_fallback(_Resp())
    self.assertIsNotNone(result.data)
    parsed = json.loads(result.data)
    self.assertEqual(u"\u00c9L\u00c9VATIONS", parsed["views"][0])
test_exception_response_with_accents()

Exception-like response with non-ASCII in message.

Source code in pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py
def test_exception_response_with_accents(self):
    """Exception-like response with non-ASCII in message."""

    class _ExcResp(object):
        message = u"Erreur: \u00e9l\u00e9ment introuvable"
        status = base.INTERNAL_SERVER_ERROR
        source = "test"

        def __str__(self):
            return self.message

    result = self._parse_response_forced_fallback(_ExcResp())
    self.assertEqual(base.INTERNAL_SERVER_ERROR, result.status)
    parsed = json.loads(result.data)
    self.assertIn(u"\u00e9l\u00e9ment", parsed["exception"]["message"])