Skip to content

Remove Python 2.7 legacy: Update JSONDecodeError exception handling #13889

@rtibbles

Description

@rtibbles

🙂 Looking for an issue? Welcome! This issue is open for contribution. If this is the first time you’re requesting an issue, please:

  • Read Contributing guidelines carefully. Pay extra attention to Using generative AI. Pull requests and comments that don’t follow the guidelines won’t be answered.
  • Confirm that you’ve read the guidelines in your comment.

Overview

Python 2 didn't have json.JSONDecodeError - instead, JSON parsing errors raised ValueError. Python 3 introduced the more specific json.JSONDecodeError exception. Code catching ValueError for JSON parsing can now use the more precise exception type.

Difficulty: Beginner-friendly
Risk: Low
Estimated changes: ~1-2 instances

The Change

Python 2 compatible pattern (current):

import json

try:
    data = json.loads(json_string)
except ValueError as e:  # Use ValueError rather than JSONDecodeError for Py2 compatibility
    # handle JSON parsing error

Python 3 pattern (target):

import json

try:
    data = json.loads(json_string)
except json.JSONDecodeError as e:
    # handle JSON parsing error

Known Instance

File: kolibri/core/device/management/commands/provisiondevice.py (line 39)

Current:

except ValueError as e:  # Use ValueError rather than JSONDecodeError for Py2 compatibility

Action:

  1. Change ValueError to json.JSONDecodeError
  2. Remove the Python 2 compatibility comment
  3. Ensure json is imported

Benefits of This Change

  • More specific exception handling: json.JSONDecodeError only catches JSON parsing errors, not all ValueErrors
  • Better error messages: json.JSONDecodeError provides better error details (line, column, position)
  • Clearer intent: Code explicitly shows it's handling JSON parsing failures

Tasks

1. Find all instances

Search for JSON parsing with ValueError:

grep -r "json.loads" kolibri/ --include="*.py" -A 2 | grep -i valueerror
grep -r "json.load(" kolibri/ --include="*.py" -A 2 | grep -i valueerror

Look for comments mentioning JSONDecodeError:

grep -r "JSONDecodeError" kolibri/ --include="*.py"

2. Verify each instance

For each ValueError exception near JSON parsing:

  1. Confirm it's for JSON parsing: Check if it's in a try block with json.loads() or json.load()
  2. Check for other ValueErrors: Make sure the except block isn't also catching other ValueError instances
  3. Verify import: Ensure json is imported in the file

3. Update each instance

For JSON parsing error handling:

  1. Change except ValueError to except json.JSONDecodeError
  2. Remove Python 2 compatibility comments
  3. Keep the same error handling logic

Important: Only change ValueError to JSONDecodeError if it's specifically catching JSON parsing errors. Don't change ValueError exceptions used for other validation purposes!

Acceptance Criteria

  • All JSON parsing error handlers updated from ValueError to json.JSONDecodeError
  • Python 2 compatibility comments removed
  • No non-JSON ValueError exceptions changed
  • All tests pass

Testing

Run all tests:

pytest

Run device management tests:

pytest kolibri/core/device/test/

Run management command tests:

pytest kolibri/core/device/management/

Test the provisiondevice command specifically:

pytest kolibri/core/device/test/ -k provision

Find and run other relevant tests:

# Find test files for modules you modify
pytest kolibri/path/to/module/test/test_file.py

Tips

  • Be selective: Only change ValueError to JSONDecodeError for JSON parsing
  • Check context: Read the surrounding code to understand what the exception is for
  • Test with invalid JSON: If possible, test the code path with malformed JSON to verify the exception handling works
  • Look at error messages: Make sure error handling logic makes sense with the new exception type

Example

Before:

import json

def load_config(config_str):
    try:
        config = json.loads(config_str)
    except ValueError as e:  # Use ValueError rather than JSONDecodeError for Py2 compatibility
        logger.error(f"Invalid JSON: {e}")
        return None
    return config

After:

import json

def load_config(config_str):
    try:
        config = json.loads(config_str)
    except json.JSONDecodeError as e:
        logger.error(f"Invalid JSON: {e}")
        return None
    return config

Questions?

Comment on this issue if you need help or clarification!

Disclosure

🤖 This issue was written by Claude Code, under supervision, review and final edits by @rtibbles 🤖

Metadata

Metadata

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions