Skip to content

Commit

Permalink
Drop py3.8 support | Replace pkg_resources lib with importlib.resourc…
Browse files Browse the repository at this point in the history
…es (#716)

* chore: transitioned from pkg_resources api to importlib-resources api
* feat!: drop support for python 3.8
  • Loading branch information
farhan authored Jul 25, 2024
1 parent 52c17a5 commit a63390b
Show file tree
Hide file tree
Showing 14 changed files with 59 additions and 38 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.8', '3.11', '3.12']
python-version: ['3.11', '3.12']
toxenv: [quality, django42]

steps:
Expand All @@ -34,7 +34,7 @@ jobs:
run: tox -e ${{ matrix.toxenv }}

- name: Run Coverage
if: matrix.python-version == '3.8' && matrix.toxenv == 'django42'
if: matrix.python-version == '3.11' && matrix.toxenv == 'django42'
uses: codecov/codecov-action@v4
with:
flags: unittests
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pypi-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: setup python
uses: actions/setup-python@v2
with:
python-version: 3.8
python-version: 3.11

- name: Install pip
run: pip install wheel setuptools
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ formats:
build:
os: "ubuntu-22.04"
tools:
python: "3.8"
python: "3.11"

# Optionally set the version of Python and requirements required to build your docs
python:
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ Change history for XBlock
Unreleased
----------

5.0.0 - 2024-05-30
------------------

* dropped python 3.8 support
* transitioned from deprecated pkg_resources lib to importlib.resources


4.1.0 - 2024-05-16
------------------

Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ One Time Setup
cd XBlock
# Set up a virtualenv using virtualenvwrapper with the same name as the repo and activate it
mkvirtualenv -p python3.8 XBlock
mkvirtualenv -p python3.11 XBlock
Every time you develop something in this repo
---------------------------------------------
Expand Down
10 changes: 5 additions & 5 deletions docs/xblock-tutorial/getting_started/prereqs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ To build an XBlock, you must have the following tools on your computer.
:depth: 1


**********
Python 3.8
**********
***********
Python 3.11
***********

To run the a virtual environment and the XBlock SDK, and to build an XBlock,
you must have Python 3.8 installed on your computer.
you must have Python 3.11 installed on your computer.

`Download Python`_ for your operating system and follow the installation
instructions.
Expand Down Expand Up @@ -48,7 +48,7 @@ applications you might need.
The instructions and examples in this tutorial use `VirtualEnv`_ and
`VirtualEnvWrapper`_ to build XBlocks. You can also use `PyEnv`_.

After you have installed Python 3.8, follow the `VirtualEnv Installation`_
After you have installed Python 3.11, follow the `VirtualEnv Installation`_
instructions.

For information on creating the virtual environment for your XBlock, see
Expand Down
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ def get_version(*file_paths):
'License :: OSI Approved :: Apache Software License',
'Natural Language :: English',
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
Expand Down
4 changes: 2 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py{38,311,312}-django{42}, quality, docs
envlist = py{311,312}-django{42}, quality, docs

[pytest]
DJANGO_SETTINGS_MODULE = xblock.test.settings
Expand All @@ -22,7 +22,7 @@ allowlist_externals =

[testenv:docs]
basepython =
python3.8
python3.11
changedir =
{toxinidir}/docs
deps =
Expand Down
2 changes: 1 addition & 1 deletion xblock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
XBlock Courseware Components
"""

__version__ = '4.1.0'
__version__ = '5.0.0'
15 changes: 12 additions & 3 deletions xblock/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
import inspect
import json
import logging
import os
import warnings
from collections import OrderedDict, defaultdict

import pkg_resources
import importlib.resources
from lxml import etree
from webob import Response

Expand Down Expand Up @@ -157,7 +156,17 @@ def open_local_resource(cls, uri):
if "/." in uri:
raise DisallowedFileError("Only safe file names are allowed: %r" % uri)

return pkg_resources.resource_stream(cls.__module__, os.path.join(cls.resources_dir, uri))
return cls._open_resource(uri)

@classmethod
def _open_resource(cls, uri):
return importlib.resources.files(
inspect.getmodule(cls).__package__
).joinpath(
cls.resources_dir
).joinpath(
uri
).open('rb')

@classmethod
def json_handler(cls, func):
Expand Down
6 changes: 3 additions & 3 deletions xblock/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
This code is in the Runtime layer.
"""
import functools
import importlib.metadata
import itertools
import logging
import pkg_resources

from xblock.internal import class_lazy

Expand Down Expand Up @@ -100,7 +100,7 @@ def select(identifier, all_entry_points):
if select is None:
select = default_select

all_entry_points = list(pkg_resources.iter_entry_points(cls.entry_point, name=identifier))
all_entry_points = list(importlib.metadata.entry_points(group=cls.entry_point, name=identifier))
for extra_identifier, extra_entry_point in iter(cls.extra_entry_points):
if identifier == extra_identifier:
all_entry_points.append(extra_entry_point)
Expand Down Expand Up @@ -133,7 +133,7 @@ def load_classes(cls, fail_silently=True):
contexts. Hence, the flag.
"""
all_classes = itertools.chain(
pkg_resources.iter_entry_points(cls.entry_point),
importlib.metadata.entry_points(group=cls.entry_point),
(entry_point for identifier, entry_point in iter(cls.extra_entry_points)),
)
for class_ in all_classes:
Expand Down
17 changes: 8 additions & 9 deletions xblock/test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -961,10 +961,9 @@ class UnloadableXBlock(XBlock):
"""Just something to load resources from."""
resources_dir = None

def stub_resource_stream(self, module, name):
"""Act like pkg_resources.resource_stream, for testing."""
assert module == "xblock.test.test_core"
return "!" + name + "!"
def stub_open_resource(self, uri):
"""Act like xblock.core.Blocklike._open_resource, for testing."""
return "!" + uri + "!"

@ddt.data(
"public/hey.js",
Expand All @@ -976,7 +975,7 @@ def stub_resource_stream(self, module, name):
)
def test_open_good_local_resource(self, uri):
loadable = self.LoadableXBlock(None, scope_ids=Mock())
with patch('pkg_resources.resource_stream', self.stub_resource_stream):
with patch('xblock.core.Blocklike._open_resource', self.stub_open_resource):
assert loadable.open_local_resource(uri) == "!" + uri + "!"
assert loadable.open_local_resource(uri.encode('utf-8')) == "!" + uri + "!"

Expand All @@ -990,7 +989,7 @@ def test_open_good_local_resource(self, uri):
)
def test_open_good_local_resource_binary(self, uri):
loadable = self.LoadableXBlock(None, scope_ids=Mock())
with patch('pkg_resources.resource_stream', self.stub_resource_stream):
with patch('xblock.core.Blocklike._open_resource', self.stub_open_resource):
assert loadable.open_local_resource(uri) == "!" + uri.decode('utf-8') + "!"

@ddt.data(
Expand All @@ -1004,7 +1003,7 @@ def test_open_good_local_resource_binary(self, uri):
)
def test_open_bad_local_resource(self, uri):
loadable = self.LoadableXBlock(None, scope_ids=Mock())
with patch('pkg_resources.resource_stream', self.stub_resource_stream):
with patch('xblock.core.Blocklike._open_resource', self.stub_open_resource):
msg_pattern = ".*: %s" % re.escape(repr(uri))
with pytest.raises(DisallowedFileError, match=msg_pattern):
loadable.open_local_resource(uri)
Expand All @@ -1020,7 +1019,7 @@ def test_open_bad_local_resource(self, uri):
)
def test_open_bad_local_resource_binary(self, uri):
loadable = self.LoadableXBlock(None, scope_ids=Mock())
with patch('pkg_resources.resource_stream', self.stub_resource_stream):
with patch('xblock.core.Blocklike._open_resource', self.stub_open_resource):
msg = ".*: %s" % re.escape(repr(uri.decode('utf-8')))
with pytest.raises(DisallowedFileError, match=msg):
loadable.open_local_resource(uri)
Expand All @@ -1043,7 +1042,7 @@ def test_open_bad_local_resource_binary(self, uri):
def test_open_local_resource_with_no_resources_dir(self, uri):
unloadable = self.UnloadableXBlock(None, scope_ids=Mock())

with patch('pkg_resources.resource_stream', self.stub_resource_stream):
with patch('xblock.core.Blocklike._open_resource', self.stub_open_resource):
msg = "not configured to serve local resources"
with pytest.raises(DisallowedFileError, match=msg):
unloadable.open_local_resource(uri)
Expand Down
6 changes: 3 additions & 3 deletions xblock/test/utils/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

import gettext
import unittest
from unittest.mock import patch, DEFAULT
from unittest.mock import DEFAULT, patch

from pkg_resources import resource_filename
import importlib.resources

from xblock.utils.resources import ResourceLoader

Expand Down Expand Up @@ -136,7 +136,7 @@ class MockI18nService:
def __init__(self):

locale_dir = 'data/translations'
locale_path = resource_filename(__name__, locale_dir)
locale_path = str(importlib.resources.files(__package__) / locale_dir)
domain = 'text'
self.mock_translator = gettext.translation(
domain,
Expand Down
19 changes: 13 additions & 6 deletions xblock/utils/resources.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
"""
Helper class (ResourceLoader) for loading resources used by an XBlock
"""

import os
import sys
import warnings

import pkg_resources
from django.template import Context, Template, Engine
import importlib.resources
from django.template import Context, Engine, Template
from django.template.backends.django import get_installed_libraries
from mako.lookup import TemplateLookup as MakoTemplateLookup
from mako.template import Template as MakoTemplate
Expand All @@ -22,8 +21,13 @@ def load_unicode(self, resource_path):
"""
Gets the content of a resource
"""
resource_content = pkg_resources.resource_string(self.module_name, resource_path)
return resource_content.decode('utf-8')
package_name = importlib.import_module(self.module_name).__package__
# TODO: Add encoding on other places as well
# resource_path should be a relative path, but historically some callers passed it in
# with a leading slash, which pkg_resources tolerated and ignored. importlib is less
# forgiving, so in order to maintain backwards compatibility, we must strip off the
# leading slash is there is one to ensure we actually have a relative path.
return importlib.resources.files(package_name).joinpath(resource_path.lstrip('/')).read_text(encoding="utf-8")

def render_django_template(self, template_path, context=None, i18n_service=None):
"""
Expand Down Expand Up @@ -57,7 +61,10 @@ def render_mako_template(self, template_path, context=None):
)
context = context or {}
template_str = self.load_unicode(template_path)
lookup = MakoTemplateLookup(directories=[pkg_resources.resource_filename(self.module_name, '')])

package_name = importlib.import_module(self.module_name).__package__
directory = str(importlib.resources.files(package_name))
lookup = MakoTemplateLookup(directories=[directory])
template = MakoTemplate(template_str, lookup=lookup)
return template.render(**context)

Expand Down

0 comments on commit a63390b

Please sign in to comment.