Skip to content

Commit 0db0dac

Browse files
author
Muhammad Faraz Maqsood
committed
feat: drop Python 3.8 and add Python 3.12 support
- 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. - Pinned pymongo version to same as that of edx-platform to avoid conflicts. - Fixed app_label error for model named CourseAllowPIISharingInLTIFlag. - Released a new version.
1 parent 4bacb6e commit 0db0dac

19 files changed

Lines changed: 649 additions & 680 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ jobs:
1515
matrix:
1616
os: [ubuntu-latest]
1717
python-version:
18-
- '3.8'
1918
- '3.11'
19+
- '3.12'
2020
toxenv: [django42, quality]
2121

2222
steps:
@@ -38,7 +38,7 @@ jobs:
3838
run: tox
3939

4040
- name: Run Coverage
41-
if: matrix.python-version == '3.8' && matrix.toxenv=='py38-django42'
41+
if: matrix.python-version == '3.11' && matrix.toxenv=='py311-django42'
4242
uses: codecov/codecov-action@v1
4343
with:
4444
flags: unittests

CHANGELOG.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ Please See the `releases tab <https://github.com/openedx/xblock-lti-consumer/rel
1616
Unreleased
1717
~~~~~~~~~~
1818

19+
9.12.0 - 2024-11-14
20+
-------------------
21+
* Dropped support for Python 3.8 and added support for Python 3.12.
22+
* Removed importlib-metadata<7 pin from constraints as it's unpin issue was closed.
23+
* Updated pylintrc file with latest version of edx-lint.
24+
* Pinned pymongo version to same as that of edx-platform to avoid conflicts.
25+
* Fixed app_label error for model named CourseAllowPIISharingInLTIFlag.
26+
1927
9.11.3 - 2024-05-30
2028
-------------------
2129
* Remove utf-8 encoding for user full name

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ One Time Setup
7272
cd xblock-lti-consumer
7373
7474
# Set up a virtualenv using virtualenvwrapper with the same name as the repo and activate it
75-
mkvirtualenv -p python3.8 xblock-lti-consumer
75+
mkvirtualenv -p python3.11 xblock-lti-consumer
7676
7777
7878
Every time you develop something in this repo

lti_consumer/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
from .apps import LTIConsumerApp
55
from .lti_xblock import LtiConsumerXBlock
66

7-
__version__ = '9.11.3'
7+
__version__ = '9.12.0'

lti_consumer/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,4 +920,5 @@ def __str__(self):
920920
class Meta:
921921
# This model was moved from edx-platform, with intention of retaining existing data.
922922
# This is referencing the original table name.
923+
app_label = "lti_consumer"
923924
db_table = "xblock_config_courseeditltifieldsenabledflag"

lti_consumer/plugin/views.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ def public_keyset_endpoint(
110110
external_id = f"{external_app}:{external_slug}"
111111

112112
try:
113+
version = None
114+
public_jwk = {}
113115
if usage_id:
114116
lti_config = LtiConfiguration.objects.get(location=UsageKey.from_string(usage_id))
115117
version = lti_config.version
@@ -127,7 +129,7 @@ def public_keyset_endpoint(
127129
version = lti_config.get("version")
128130
public_jwk = lti_config.get("lti_1p3_public_jwk", {})
129131

130-
if version != LtiConfiguration.LTI_1P3:
132+
if version is None or version != LtiConfiguration.LTI_1P3:
131133
raise LtiError(
132134
"LTI Error: LTI 1.1 blocks do not have a public keyset endpoint."
133135
)
@@ -413,12 +415,14 @@ def access_token_endpoint(
413415
JsonResponse or Http404
414416
415417
References:
416-
Sucess: https://tools.ietf.org/html/rfc6749#section-4.4.3
418+
Success: https://tools.ietf.org/html/rfc6749#section-4.4.3
417419
Failure: https://tools.ietf.org/html/rfc6749#section-5.2
418420
"""
419421
external_id = f"{external_app}:{external_slug}"
420422

421423
try:
424+
version = None
425+
lti_consumer = None
422426
if usage_id:
423427
lti_config = LtiConfiguration.objects.get(location=UsageKey.from_string(usage_id))
424428
version = lti_config.version
@@ -457,9 +461,12 @@ def access_token_endpoint(
457461
)
458462
raise Http404 from exc
459463

460-
if version != LtiConfiguration.LTI_1P3:
464+
if version is None or version != LtiConfiguration.LTI_1P3:
461465
return JsonResponse({"error": "invalid_lti_version"}, status=HTTP_404_NOT_FOUND)
462466

467+
if lti_consumer is None:
468+
return JsonResponse({"error": "lti_consumer_not_initialized"}, status=HTTP_404_NOT_FOUND)
469+
463470
try:
464471
token = lti_consumer.access_token(
465472
dict(urllib.parse.parse_qsl(
@@ -471,10 +478,10 @@ def access_token_endpoint(
471478

472479
# Handle errors and return a proper response
473480
except MissingRequiredClaim:
474-
# Missing request attibutes
481+
# Missing request attributes
475482
return JsonResponse({"error": "invalid_request"}, status=HTTP_400_BAD_REQUEST)
476483
except (MalformedJwtToken, TokenSignatureExpired):
477-
# Triggered when a invalid grant token is used
484+
# Triggered when an invalid grant token is used
478485
return JsonResponse({"error": "invalid_grant"}, status=HTTP_400_BAD_REQUEST)
479486
except (NoSuitableKeys, UnknownClientId):
480487
# Client ID is not registered in the block or

lti_consumer/tests/unit/plugin/test_views.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -697,9 +697,13 @@ def test_access_token_endpoint(self):
697697
self.mock_client.access_token.return_value = token
698698

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

702-
self.mock_client.access_token.called_once_with(body)
706+
self.assertEqual(actual_dict, body)
703707
self.assertEqual(response.status_code, 200)
704708
self.assertEqual(response.json(), token)
705709

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

720-
self.mock_client.access_token.called_once_with(body)
724+
self.mock_client.access_token.assert_called_once()
725+
called_args = self.mock_client.access_token.call_args[0]
726+
actual_arg = called_args[0]
727+
actual_dict = json.loads(next(iter(actual_arg.keys())))
728+
729+
self.assertEqual(actual_dict, body)
721730
self.assertEqual(response.status_code, 200)
722731
self.assertEqual(response.json(), token)
723732

@@ -748,7 +757,8 @@ def test_access_token_endpoint_with_external_id_in_url(
748757
body = self.get_body(create_jwt(self.key, {}))
749758
response = self.client.post(
750759
reverse('lti_consumer:lti_consumer.access_token_via_external_id', args=['x', 'x']),
751-
data=body,
760+
data=json.dumps(body),
761+
content_type='application/json'
752762
)
753763

754764
get_external_config_from_filter.assert_called_once_with({}, 'x:x')
@@ -765,7 +775,12 @@ def test_access_token_endpoint_with_external_id_in_url(
765775
tool_key=external_config['lti_1p3_tool_public_key'],
766776
tool_keyset_url=external_config['lti_1p3_tool_keyset_url'],
767777
)
768-
lti_consumer().access_token.called_once_with(body)
778+
lti_consumer().access_token.assert_called_once()
779+
called_args = lti_consumer().access_token.call_args[0]
780+
actual_arg = called_args[0]
781+
actual_dict = json.loads(next(iter(actual_arg.keys())))
782+
783+
self.assertEqual(actual_dict, body)
769784
self.assertEqual(response.status_code, 200)
770785
self.assertEqual(response.json(), token)
771786

pylintrc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
# SERIOUSLY.
6565
#
6666
# ------------------------------
67-
# Generated by edx-lint version: 5.3.6
67+
# Generated by edx-lint version: 5.4.1
6868
# ------------------------------
6969
[MASTER]
7070
ignore =
@@ -288,6 +288,7 @@ disable =
288288

289289
logging-fstring-interpolation,
290290
django-not-configured,
291+
too-many-positional-arguments,
291292

292293
[REPORTS]
293294
output-format = text
@@ -384,4 +385,4 @@ int-import-graph =
384385
[EXCEPTIONS]
385386
overgeneral-exceptions = builtins.Exception
386387

387-
# 9cc29d4fbd4438c7636de003db74bef089bad86e
388+
# 181f52c3a503ef58edecad11109d1ed21e793de8

requirements/base.txt

Lines changed: 43 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# This file is autogenerated by pip-compile with Python 3.8
2+
# This file is autogenerated by pip-compile with Python 3.11
33
# by the following command:
44
#
55
# make upgrade
@@ -8,32 +8,27 @@ appdirs==1.4.4
88
# via fs
99
asgiref==3.8.1
1010
# via django
11-
attrs==23.2.0
11+
attrs==24.2.0
1212
# via -r requirements/base.in
13-
backports-zoneinfo==0.2.1 ; python_version < "3.9"
14-
# via
15-
# -c requirements/constraints.txt
16-
# django
17-
# djangorestframework
18-
bleach==6.1.0
13+
bleach==6.2.0
1914
# via -r requirements/base.in
20-
boto3==1.34.90
15+
boto3==1.35.59
2116
# via fs-s3fs
22-
botocore==1.34.90
17+
botocore==1.35.59
2318
# via
2419
# boto3
2520
# s3transfer
26-
certifi==2024.2.2
21+
certifi==2024.8.30
2722
# via requests
28-
cffi==1.16.0
23+
cffi==1.17.1
2924
# via pynacl
30-
charset-normalizer==3.3.2
25+
charset-normalizer==3.4.0
3126
# via requests
3227
click==8.1.7
3328
# via edx-django-utils
34-
django==4.2.11
29+
django==4.2.16
3530
# via
36-
# -c requirements/common_constraints.txt
31+
# -c /Users/faraz.maqsood/Desktop/test/xblock-lti-consumer/requirements/common_constraints.txt
3732
# -r requirements/base.in
3833
# django-appconf
3934
# django-config-models
@@ -52,19 +47,21 @@ django-config-models==2.7.0
5247
# via -r requirements/base.in
5348
django-crum==0.7.9
5449
# via edx-django-utils
55-
django-filter==24.2
50+
django-filter==24.3
5651
# via -r requirements/base.in
5752
django-statici18n==2.5.0
5853
# via -r requirements/base.in
5954
django-waffle==4.1.0
6055
# via edx-django-utils
61-
djangorestframework==3.15.1
56+
djangorestframework==3.15.2
6257
# via django-config-models
58+
dnspython==2.7.0
59+
# via pymongo
6360
edx-ccx-keys==1.3.0
6461
# via -r requirements/base.in
65-
edx-django-utils==5.12.0
62+
edx-django-utils==7.0.0
6663
# via django-config-models
67-
edx-opaque-keys[django]==2.8.0
64+
edx-opaque-keys[django]==2.11.0
6865
# via
6966
# -r requirements/base.in
7067
# edx-ccx-keys
@@ -77,7 +74,7 @@ fs-s3fs==1.1.1
7774
# via openedx-django-pyfs
7875
future==1.0.0
7976
# via pyjwkest
80-
idna==3.7
77+
idna==3.10
8178
# via requests
8279
jmespath==1.0.1
8380
# via
@@ -87,86 +84,85 @@ jsonfield==3.1.0
8784
# via -r requirements/base.in
8885
lazy==1.6
8986
# via -r requirements/base.in
90-
lxml==5.2.1
87+
lxml==5.3.0
9188
# via
9289
# -r requirements/base.in
9390
# xblock
94-
mako==1.3.3
91+
mako==1.3.6
9592
# via
9693
# -r requirements/base.in
9794
# xblock
98-
markupsafe==2.1.5
95+
markupsafe==3.0.2
9996
# via
10097
# mako
10198
# xblock
102-
newrelic==9.9.0
99+
newrelic==10.2.0
103100
# via edx-django-utils
104101
oauthlib==3.2.2
105102
# via -r requirements/base.in
106-
openedx-django-pyfs==3.6.0
103+
openedx-django-pyfs==3.7.0
107104
# via -r requirements/base.in
108-
openedx-filters==1.8.1
105+
openedx-filters==1.11.0
109106
# via -r requirements/base.in
110-
pbr==6.0.0
107+
pbr==6.1.0
111108
# via stevedore
112-
psutil==5.9.8
109+
psutil==6.1.0
113110
# via edx-django-utils
114111
pycparser==2.22
115112
# via cffi
116-
pycryptodomex==3.20.0
113+
pycryptodomex==3.21.0
117114
# via
118115
# -r requirements/base.in
119116
# pyjwkest
120117
pyjwkest==1.4.2
121118
# via -r requirements/base.in
122-
pymongo==3.13.0
123-
# via edx-opaque-keys
119+
pymongo==4.4.0
120+
# via
121+
# -c /Users/faraz.maqsood/Desktop/test/xblock-lti-consumer/requirements/common_constraints.txt
122+
# edx-opaque-keys
124123
pynacl==1.5.0
125124
# via edx-django-utils
126125
python-dateutil==2.9.0.post0
127126
# via
128127
# botocore
129128
# xblock
130-
pytz==2024.1
129+
pytz==2024.2
131130
# via xblock
132-
pyyaml==6.0.1
131+
pyyaml==6.0.2
133132
# via xblock
134-
requests==2.31.0
133+
requests==2.32.3
135134
# via pyjwkest
136-
s3transfer==0.10.1
135+
s3transfer==0.10.3
137136
# via boto3
138-
simplejson==3.19.2
137+
simplejson==3.19.3
139138
# via xblock
140139
six==1.16.0
141140
# via
142-
# bleach
143141
# edx-ccx-keys
144142
# fs
145143
# fs-s3fs
146144
# pyjwkest
147145
# python-dateutil
148-
sqlparse==0.5.0
146+
sqlparse==0.5.1
149147
# via django
150-
stevedore==5.2.0
148+
stevedore==5.3.0
151149
# via
152150
# edx-django-utils
153151
# edx-opaque-keys
154-
typing-extensions==4.11.0
155-
# via
156-
# asgiref
157-
# edx-opaque-keys
158-
urllib3==1.26.18
152+
typing-extensions==4.12.2
153+
# via edx-opaque-keys
154+
urllib3==1.26.20
159155
# via
160-
# -c requirements/constraints.txt
156+
# -c /Users/faraz.maqsood/Desktop/test/xblock-lti-consumer/requirements/constraints.txt
161157
# botocore
162158
# requests
163159
web-fragments==2.2.0
164160
# via xblock
165161
webencodings==0.5.1
166162
# via bleach
167-
webob==1.8.7
163+
webob==1.8.9
168164
# via xblock
169-
xblock==4.0.0
165+
xblock==5.1.0
170166
# via -r requirements/base.in
171167

172168
# The following packages are considered to be unsafe in a requirements file:

0 commit comments

Comments
 (0)