Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: drop Python 3.8 and add Python 3.12 support #514

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ jobs:
matrix:
os: [ubuntu-latest]
python-version:
- '3.8'
- '3.11'
- '3.12'
toxenv: [django42, quality]

steps:
Expand All @@ -38,7 +38,7 @@ jobs:
run: tox

- name: Run Coverage
if: matrix.python-version == '3.8' && matrix.toxenv=='py38-django42'
if: matrix.python-version == '3.11' && matrix.toxenv=='py311-django42'
uses: codecov/codecov-action@v1
with:
flags: unittests
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ Please See the `releases tab <https://github.com/openedx/xblock-lti-consumer/rel
Unreleased
~~~~~~~~~~

9.12.0 - 2024-11-14
-------------------
* Dropped support for Python 3.8 and added support for Python 3.12.
* Removed importlib-metadata<7 pin from constraints as it's unpin issue was closed.
* Updated pylintrc file with latest version of edx-lint.
* Fixed app_label error for model named CourseAllowPIISharingInLTIFlag.

9.11.3 - 2024-05-30
-------------------
* Remove utf-8 encoding for user full name
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ One Time Setup
cd xblock-lti-consumer

# Set up a virtualenv using virtualenvwrapper with the same name as the repo and activate it
mkvirtualenv -p python3.8 xblock-lti-consumer
mkvirtualenv -p python3.11 xblock-lti-consumer


Every time you develop something in this repo
Expand Down
2 changes: 1 addition & 1 deletion lti_consumer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
from .apps import LTIConsumerApp
from .lti_xblock import LtiConsumerXBlock

__version__ = '9.11.3'
__version__ = '9.12.0'
1 change: 1 addition & 0 deletions lti_consumer/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -920,4 +920,5 @@ def __str__(self):
class Meta:
# This model was moved from edx-platform, with intention of retaining existing data.
# This is referencing the original table name.
app_label = "lti_consumer"
db_table = "xblock_config_courseeditltifieldsenabledflag"
17 changes: 12 additions & 5 deletions lti_consumer/plugin/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ def public_keyset_endpoint(
external_id = f"{external_app}:{external_slug}"

try:
version = None
public_jwk = {}
if usage_id:
lti_config = LtiConfiguration.objects.get(location=UsageKey.from_string(usage_id))
version = lti_config.version
Expand All @@ -127,7 +129,7 @@ def public_keyset_endpoint(
version = lti_config.get("version")
public_jwk = lti_config.get("lti_1p3_public_jwk", {})

if version != LtiConfiguration.LTI_1P3:
if version is None or version != LtiConfiguration.LTI_1P3:
raise LtiError(
"LTI Error: LTI 1.1 blocks do not have a public keyset endpoint."
)
Expand Down Expand Up @@ -413,12 +415,14 @@ def access_token_endpoint(
JsonResponse or Http404

References:
Sucess: https://tools.ietf.org/html/rfc6749#section-4.4.3
Success: https://tools.ietf.org/html/rfc6749#section-4.4.3
Failure: https://tools.ietf.org/html/rfc6749#section-5.2
"""
external_id = f"{external_app}:{external_slug}"

try:
version = None
lti_consumer = None
if usage_id:
lti_config = LtiConfiguration.objects.get(location=UsageKey.from_string(usage_id))
version = lti_config.version
Expand Down Expand Up @@ -457,9 +461,12 @@ def access_token_endpoint(
)
raise Http404 from exc

if version != LtiConfiguration.LTI_1P3:
if version is None or version != LtiConfiguration.LTI_1P3:
return JsonResponse({"error": "invalid_lti_version"}, status=HTTP_404_NOT_FOUND)

if lti_consumer is None:
return JsonResponse({"error": "lti_consumer_not_initialized"}, status=HTTP_404_NOT_FOUND)

try:
token = lti_consumer.access_token(
dict(urllib.parse.parse_qsl(
Expand All @@ -471,10 +478,10 @@ def access_token_endpoint(

# Handle errors and return a proper response
except MissingRequiredClaim:
# Missing request attibutes
# Missing request attributes
return JsonResponse({"error": "invalid_request"}, status=HTTP_400_BAD_REQUEST)
except (MalformedJwtToken, TokenSignatureExpired):
# Triggered when a invalid grant token is used
# Triggered when an invalid grant token is used
return JsonResponse({"error": "invalid_grant"}, status=HTTP_400_BAD_REQUEST)
except (NoSuitableKeys, UnknownClientId):
# Client ID is not registered in the block or
Expand Down
27 changes: 21 additions & 6 deletions lti_consumer/tests/unit/plugin/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,9 +697,13 @@ def test_access_token_endpoint(self):
self.mock_client.access_token.return_value = token

body = self.get_body(create_jwt(self.key, {}))
response = self.client.post(self.url, data=body)
response = self.client.post(self.url, data=json.dumps(body), content_type='application/json')
self.mock_client.access_token.assert_called_once()
called_args = self.mock_client.access_token.call_args[0]
actual_arg = called_args[0]
actual_dict = json.loads(next(iter(actual_arg.keys())))

self.mock_client.access_token.called_once_with(body)
self.assertEqual(actual_dict, body)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), token)

Expand All @@ -715,9 +719,14 @@ def test_access_token_endpoint_with_location_in_url(self):
args=[str(self.config.location)]
)
body = self.get_body(create_jwt(self.key, {}))
response = self.client.post(url, data=body)
response = self.client.post(url, data=json.dumps(body), content_type='application/json')

self.mock_client.access_token.called_once_with(body)
self.mock_client.access_token.assert_called_once()
called_args = self.mock_client.access_token.call_args[0]
actual_arg = called_args[0]
actual_dict = json.loads(next(iter(actual_arg.keys())))

self.assertEqual(actual_dict, body)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), token)

Expand Down Expand Up @@ -748,7 +757,8 @@ def test_access_token_endpoint_with_external_id_in_url(
body = self.get_body(create_jwt(self.key, {}))
response = self.client.post(
reverse('lti_consumer:lti_consumer.access_token_via_external_id', args=['x', 'x']),
data=body,
data=json.dumps(body),
content_type='application/json'
)

get_external_config_from_filter.assert_called_once_with({}, 'x:x')
Expand All @@ -765,7 +775,12 @@ def test_access_token_endpoint_with_external_id_in_url(
tool_key=external_config['lti_1p3_tool_public_key'],
tool_keyset_url=external_config['lti_1p3_tool_keyset_url'],
)
lti_consumer().access_token.called_once_with(body)
lti_consumer().access_token.assert_called_once()
called_args = lti_consumer().access_token.call_args[0]
actual_arg = called_args[0]
actual_dict = json.loads(next(iter(actual_arg.keys())))

self.assertEqual(actual_dict, body)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(), token)

Expand Down
5 changes: 3 additions & 2 deletions pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
# SERIOUSLY.
#
# ------------------------------
# Generated by edx-lint version: 5.3.6
# Generated by edx-lint version: 5.4.1
# ------------------------------
[MASTER]
ignore =
Expand Down Expand Up @@ -288,6 +288,7 @@ disable =

logging-fstring-interpolation,
django-not-configured,
too-many-positional-arguments,

[REPORTS]
output-format = text
Expand Down Expand Up @@ -384,4 +385,4 @@ int-import-graph =
[EXCEPTIONS]
overgeneral-exceptions = builtins.Exception

# 9cc29d4fbd4438c7636de003db74bef089bad86e
# 181f52c3a503ef58edecad11109d1ed21e793de8
82 changes: 38 additions & 44 deletions requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# This file is autogenerated by pip-compile with Python 3.8
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# make upgrade
Expand All @@ -8,30 +8,25 @@ appdirs==1.4.4
# via fs
asgiref==3.8.1
# via django
attrs==23.2.0
attrs==24.2.0
# via -r requirements/base.in
backports-zoneinfo==0.2.1 ; python_version < "3.9"
# via
# -c requirements/constraints.txt
# django
# djangorestframework
bleach==6.1.0
bleach==6.2.0
# via -r requirements/base.in
boto3==1.34.90
boto3==1.35.60
# via fs-s3fs
botocore==1.34.90
botocore==1.35.60
# via
# boto3
# s3transfer
certifi==2024.2.2
certifi==2024.8.30
# via requests
cffi==1.16.0
cffi==1.17.1
# via pynacl
charset-normalizer==3.3.2
charset-normalizer==3.4.0
# via requests
click==8.1.7
# via edx-django-utils
django==4.2.11
django==4.2.16
# via
# -c requirements/common_constraints.txt
# -r requirements/base.in
Expand All @@ -52,19 +47,21 @@ django-config-models==2.7.0
# via -r requirements/base.in
django-crum==0.7.9
# via edx-django-utils
django-filter==24.2
django-filter==24.3
# via -r requirements/base.in
django-statici18n==2.5.0
# via -r requirements/base.in
django-waffle==4.1.0
# via edx-django-utils
djangorestframework==3.15.1
djangorestframework==3.15.2
# via django-config-models
dnspython==2.7.0
# via pymongo
edx-ccx-keys==1.3.0
# via -r requirements/base.in
edx-django-utils==5.12.0
edx-django-utils==7.0.0
# via django-config-models
edx-opaque-keys[django]==2.8.0
edx-opaque-keys[django]==2.11.0
# via
# -r requirements/base.in
# edx-ccx-keys
Expand All @@ -77,7 +74,7 @@ fs-s3fs==1.1.1
# via openedx-django-pyfs
future==1.0.0
# via pyjwkest
idna==3.7
idna==3.10
# via requests
jmespath==1.0.1
# via
Expand All @@ -87,75 +84,72 @@ jsonfield==3.1.0
# via -r requirements/base.in
lazy==1.6
# via -r requirements/base.in
lxml==5.2.1
lxml==5.3.0
# via
# -r requirements/base.in
# xblock
mako==1.3.3
mako==1.3.6
# via
# -r requirements/base.in
# xblock
markupsafe==2.1.5
markupsafe==3.0.2
# via
# mako
# xblock
newrelic==9.9.0
newrelic==10.2.0
# via edx-django-utils
oauthlib==3.2.2
# via -r requirements/base.in
openedx-django-pyfs==3.6.0
openedx-django-pyfs==3.7.0
# via -r requirements/base.in
openedx-filters==1.8.1
openedx-filters==1.11.0
# via -r requirements/base.in
pbr==6.0.0
pbr==6.1.0
# via stevedore
psutil==5.9.8
psutil==6.1.0
# via edx-django-utils
pycparser==2.22
# via cffi
pycryptodomex==3.20.0
pycryptodomex==3.21.0
# via
# -r requirements/base.in
# pyjwkest
pyjwkest==1.4.2
# via -r requirements/base.in
pymongo==3.13.0
pymongo==4.10.1
# via edx-opaque-keys
pynacl==1.5.0
# via edx-django-utils
python-dateutil==2.9.0.post0
# via
# botocore
# xblock
pytz==2024.1
pytz==2024.2
# via xblock
pyyaml==6.0.1
pyyaml==6.0.2
# via xblock
requests==2.31.0
requests==2.32.3
# via pyjwkest
s3transfer==0.10.1
s3transfer==0.10.3
# via boto3
simplejson==3.19.2
simplejson==3.19.3
# via xblock
six==1.16.0
# via
# bleach
# edx-ccx-keys
# fs
# fs-s3fs
# pyjwkest
# python-dateutil
sqlparse==0.5.0
sqlparse==0.5.2
# via django
stevedore==5.2.0
stevedore==5.3.0
# via
# edx-django-utils
# edx-opaque-keys
typing-extensions==4.11.0
# via
# asgiref
# edx-opaque-keys
urllib3==1.26.18
typing-extensions==4.12.2
# via edx-opaque-keys
urllib3==1.26.20
# via
# -c requirements/constraints.txt
# botocore
Expand All @@ -164,9 +158,9 @@ web-fragments==2.2.0
# via xblock
webencodings==0.5.1
# via bleach
webob==1.8.7
webob==1.8.9
# via xblock
xblock==4.0.0
xblock==5.1.0
# via -r requirements/base.in

# The following packages are considered to be unsafe in a requirements file:
Expand Down
Loading
Loading