Skip to content

Remove Python 2.7 legacy: Simple syntax and import cleanups #13883

@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

This task removes simple Python 2.7 compatibility code including outdated import statements, deprecated file operations, and compatibility shims. This is a great first issue for new contributors!

Difficulty: Beginner-friendly
Risk: Very low
Estimated changes: ~25 instances across ~15 files

Tasks

1. Remove from __future__ import statements

File: kolibri/plugins/qti_viewer/kolibri_plugin.py

Current (lines 1-3):

from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals

Action: Delete all three lines. These imports were needed for Python 2.7 compatibility and are unnecessary in Python 3.


2. Replace io.open() with built-in open()

In Python 3, the built-in open() function is equivalent to io.open(). Replace all instances and remove unnecessary import io statements (unless io.BytesIO or io.StringIO is also used in the file).

Pattern to find:

import io
# ...
with io.open(file_path, mode="r", encoding="utf-8") as f:
    ...

Pattern to replace with:

with open(file_path, mode="r", encoding="utf-8") as f:
    ...

Files to update (18 instances):

  1. build_tools/i18n/utils.py (lines 55, 91)
  2. build_tools/i18n/fonts.py (lines 357, 420)
  3. kolibri/utils/i18n.py (line 35)
  4. kolibri/utils/pskolibri/common.py (lines 132, 141)
  5. kolibri/core/webpack/hooks.py (line 179)
  6. kolibri/core/content/utils/paths.py (line 296)
  7. kolibri/core/deviceadmin/utils.py (lines 108, 143) - Also remove the comment "Setting encoding=utf-8: io.open() is Python 2 compatible"
  8. kolibri/core/content/utils/stopwords.py (line 11)
  9. kolibri/core/sqlite/utils.py (line 113)
  10. kolibri/core/logger/management/commands/generateuserdata.py (line 114)
  11. kolibri/core/auth/constants/facility_presets.py (line 8)
  12. kolibri/core/content/management/commands/generate_schema.py (lines 140, 160)
  13. kolibri/core/content/test/test_channel_import.py (lines 423, 539, 556)
  14. kolibri/core/analytics/test/test_utils.py (line 82)

3. Fix __nonzero__ method

File: kolibri/core/utils/nothing.py (line 13)

Current:

def __nonzero__(self):  # Python 2
    return False

__bool__ = __nonzero__  # this is for python3

Replace with:

def __bool__(self):
    return False

Explanation: In Python 2, truthiness was defined by __nonzero__. Python 3 uses __bool__. We can now remove the Python 2 method.


4. Remove urllib2 compatibility

File: docker/entrypoint.py (lines 18-22)

Current:

# py2+py3 compatible imports via http://python-future.org/compatible_idioms.html
try:
    from urllib.request import Request, build_opener, HTTPRedirectHandler
except ImportError:
    from urllib2 import Request, HTTPRedirectHandler, build_opener

Replace with:

from urllib.request import Request, build_opener, HTTPRedirectHandler

Explanation: urllib2 only exists in Python 2. In Python 3, the functionality is in urllib.request.


Acceptance Criteria

  • All from __future__ import statements removed
  • All io.open() calls replaced with built-in open()
  • Unnecessary import io statements removed (check if io.BytesIO or io.StringIO is used first)
  • __nonzero__ method replaced with __bool__ in nothing.py
  • urllib2 try/except block removed from docker/entrypoint.py
  • All Python 2.7-related comments removed from modified code
  • All tests pass

Testing

Run all tests:

pytest

Run relevant tests for specific modules:

For utils changes:

pytest kolibri/utils/tests/

For core module changes:

pytest kolibri/core/utils/tests/test_nothing.py
pytest kolibri/core/content/test/
pytest kolibri/core/deviceadmin/tests/

For build_tools changes, no specific tests exist - ensure overall test suite passes.

How to find tests: Tests for a module kolibri/path/to/module.py are typically located at kolibri/path/to/test/test_module.py or kolibri/path/to/tests/test_module.py.

Tips

  • Make changes incrementally and run tests frequently
  • You can test individual files: pytest path/to/test_file.py
  • Use search to find all instances: grep -r "io.open" kolibri/
  • Don't forget to remove associated Python 2.7 comments when you remove code

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

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions