Skip to content

Safe JSON serializer & content-length fix (issue with Routes in Revit French)#3161

Merged
jmcouffin merged 5 commits intopyrevitlabs:developfrom
vinnividivicci:routes-http-bug-fix
Mar 12, 2026
Merged

Safe JSON serializer & content-length fix (issue with Routes in Revit French)#3161
jmcouffin merged 5 commits intopyrevitlabs:developfrom
vinnividivicci:routes-http-bug-fix

Conversation

@vinnividivicci
Copy link
Copy Markdown
Contributor

@vinnividivicci vinnividivicci commented Mar 4, 2026

Safe JSON serializer & content-length fix (issue with Routes in Revit French)

Description

Add a Unicode-safe JSON serializer (_safe_json_dumps) to handler.py to avoid json.dumps UnicodeDecodeError on IronPython by escaping non-ASCII as \uXXXX. Use json.dumps(..., ensure_ascii=True) with a fallback to _safe_json_dumps when encoding errors occur. Update HttpRequestHandler._write_response to always compute a bytes body, set a correct Content-Length header, skip duplicate Content-Length from response headers, and write the encoded body. Add unit tests covering response framing (Content-Length and body bytes) and Unicode-safe JSON serialization/parse_response behavior.


Checklist

Before submitting your pull request, ensure the following requirements are met:

  • Code follows the PEP 8 style guide.
  • Code has been formatted with Black using the command:
    pipenv run black {source_file_or_directory}
  • Changes are tested and verified to work as expected.

Related Issues

If applicable, link the issues resolved by this pull request:

  • Resolves #[issue number]

Additional Notes

Include any additional context, screenshots, or considerations for reviewers.


Thank you for contributing to pyRevit! 🎉

Add a Unicode-safe JSON serializer (_safe_json_dumps) to handler.py to avoid json.dumps UnicodeDecodeError on IronPython by escaping non-ASCII as \uXXXX. Use json.dumps(..., ensure_ascii=True) with a fallback to _safe_json_dumps when encoding errors occur. Update HttpRequestHandler._write_response to always compute a bytes body, set a correct Content-Length header, skip duplicate Content-Length from response headers, and write the encoded body. Add unit tests covering response framing (Content-Length and body bytes) and Unicode-safe JSON serialization/parse_response behavior.
Copy link
Copy Markdown
Contributor

@devloai devloai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary:

  • Adds _safe_json_dumps() fallback serializer to handle UnicodeDecodeError in IronPython 2.7 on French/accented Revit strings.
  • Updates HttpRequestHandler._write_response to always encode the body as bytes and set a correct Content-Length header, skipping any duplicate from response headers.
  • Adds two unit test files covering response framing and Unicode JSON serialization.

Review Summary:

The Content-Length framing fix is a solid, correct improvement and the overall approach (try json.dumps(ensure_ascii=True), fall back to _safe_json_dumps) is sound. However, there is a critical functional bug: the fallback function _safe_json_dumps checks isinstance(obj, str) which, in IronPython 2.7, will never match unicode objects — precisely the type that carries accented Revit strings. The docstring even incorrectly claims IronPython 2.7 unifies str and unicode (they are distinct; only IronPython 3 unifies them). This means the fallback is unreachable for the exact inputs it was designed to handle. Additionally, the new \n body written for all None-data responses (including 204 No Content) violates RFC 7230. The IPY2712 repo guideline was the key lens for catching the unicode type gap.

Follow-up suggestions:

  • @devloai fix the identified issues: add unicode to the isinstance check in _safe_json_dumps (using pyrevit.compat.PY2), guard the \n fallback against no-body status codes (204, 304), and handle inf/nan floats.

Handle float edge cases in _safe_json_dumps by detecting NaN and infinite values (using math.isnan/math.isinf) and returning "null" so the output remains valid JSON. Adds the math import and the corresponding check in pyrevitlib/pyrevit/routes/server/handler.py.
@vinnividivicci vinnividivicci marked this pull request as ready for review March 5, 2026 03:11
@vinnividivicci
Copy link
Copy Markdown
Contributor Author

@jmcouffin Bug fixes to the routes API to work properly with Revit, French, and other multilingual Revit installations with non English characters.

Apply consistent string quoting, spacing, and formatting across routes/server and tests. Changes include normalizing single to double quotes, consistent use of join and string concatenation, cleaning up pylint comment formatting, minor refactors (parentheses, trailing commas, line breaks), and small-safe-json-dumps tweaks. Tests updated to match the formatting changes. No functional behavior changes intended.
Make the _safe_json_dumps docstring a raw string literal to avoid accidental escape processing (e.g., \u sequences) on IronPython. Also adjust the unit test docstring capitalization for clarity in test_routes_server_unicode.py.
jmcouffin
jmcouffin previously approved these changes Mar 5, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses IronPython Unicode serialization failures in the routes server (notably seen with non-ASCII route data in localized Revit environments) and fixes HTTP response framing by ensuring response bodies are written as bytes with an accurate Content-Length.

Changes:

  • Added a Unicode-safe JSON serializer fallback (_safe_json_dumps) and integrated it into parse_response when json.dumps raises Unicode encoding/decoding errors.
  • Updated HttpRequestHandler._write_response to always compute a bytes body, send a single correct Content-Length, and write the encoded payload.
  • Added unit tests for Unicode-safe serialization and response framing (Content-Length and written bytes).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
pyrevitlib/pyrevit/routes/server/handler.py Adds _safe_json_dumps and uses it as a fallback in parse_response to avoid IronPython Unicode errors.
pyrevitlib/pyrevit/routes/server/server.py Ensures _write_response writes bytes and sets a correct Content-Length, avoiding duplicate Content-Length headers.
pyrevitlib/pyrevit/unittests/test_routes_server_unicode.py New tests covering Unicode-safe JSON serialization and parse_response behavior with accented characters.
pyrevitlib/pyrevit/unittests/test_routes_server_response.py New tests validating response framing (Content-Length, bytes body, header override behavior).

@vinnividivicci vinnividivicci marked this pull request as draft March 5, 2026 18:09
…N fallback

add Routes Module Tests button under pyRevitDev > Debug > Unit Tests
discover and run all pyrevit.unittests.test_routes_* modules
print detailed unittest error/failure tracebacks in button output
harden RequestHandler.parse_response JSON fallback for IronPython runtime exceptions
preserve numeric serialization for Python 2 long values via numbers.Integral
make routes response tests IronPython-safe (handler patching + method binding compatibility)
make unicode parse-response tests deterministic for fallback/exception string behavior
@vinnividivicci
Copy link
Copy Markdown
Contributor Author

Added test menu for some of the routes code that I touched (additional coverage needed in future for pre-existing code):
image

Tests pass:
image

@vinnividivicci vinnividivicci marked this pull request as ready for review March 5, 2026 18:19
@jmcouffin jmcouffin merged commit 92840a8 into pyrevitlabs:develop Mar 12, 2026
@jmcouffin
Copy link
Copy Markdown
Contributor

Thanks @vinnividivicci

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26071+0706-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26073+1714-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26073+2220-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26074+1648-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26076+0001-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26077+2319-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26079+2333-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26080+1314-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2031-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2042-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2045-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2048-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2106-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2130-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26086+2004-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26088+1318-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26089+1231-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+0549-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1533-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1536-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1540-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1540-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1556-wip

@github-actions
Copy link
Copy Markdown
Contributor

📦 New public release are available for 6.2.0.26090+1754

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants