diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 34e9765..fce7583 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,12 @@ Change Log Unreleased ~~~~~~~~~~ +[3.2.0] - 2024-01-11 +~~~~~~~~~~~~~~~~~~~~ +Added +_____ +* Add ``codejail_service`` app for transition to containerized codejail + [3.1.1] - 2023-11-06 ~~~~~~~~~~~~~~~~~~~~ Fixed diff --git a/edx_arch_experiments/__init__.py b/edx_arch_experiments/__init__.py index 2bf91e3..0960636 100644 --- a/edx_arch_experiments/__init__.py +++ b/edx_arch_experiments/__init__.py @@ -2,4 +2,4 @@ A plugin to include applications under development by the architecture team at 2U. """ -__version__ = '3.1.1' +__version__ = '3.2.0' diff --git a/edx_arch_experiments/codejail_service/README.rst b/edx_arch_experiments/codejail_service/README.rst new file mode 100644 index 0000000..ec97d4b --- /dev/null +++ b/edx_arch_experiments/codejail_service/README.rst @@ -0,0 +1,27 @@ +Codejail Service +################ + +When installed in the LMS as a plugin app, the ``codejail_service`` app allows the CMS to delegate codejail executions to the LMS across the network. + +This is intended as a `temporary situation `_ with the following goals: + +- Unblock containerization of the CMS. Codejail cannot be readily containerized due to its reliance on AppArmor, but if codejail execution is outsourced, then we can containerize CMS first and will be in a better position to containerize the LMS afterwards. +- Exercise the remote-codejail pathway and have an opportunity to discover and implement needed improvements before fully building out a separate, dedicated codejail service. + +Usage +***** + +In LMS: + +- Install ``edx-arch-experiments`` as a dependency +- Identify a service account that will be permitted to make calls to the codejail service and ensure it has the ``is_staff`` Django flag. In devstack, this would be ``cms_worker``. +- In Djano admin, under ``Django OAuth Toolkit > Applications``, find or create a Client Credentials application for that service user. + +In CMS: + +- Set ``ENABLE_CODEJAIL_REST_SERVICE`` to ``True`` +- Set ``CODE_JAIL_REST_SERVICE_HOST`` to the URL origin of the LMS (e.g. ``http://edx.devstack.lms:18000`` in devstack) +- Keep ``CODE_JAIL_REST_SERVICE_REMOTE_EXEC`` at its default of ``xmodule.capa.safe_exec.remote_exec.send_safe_exec_request_v0`` +- Adjust ``CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT`` and ``CODE_JAIL_REST_SERVICE_READ_TIMEOUT`` if needed +- Set ``CODE_JAIL_REST_SERVICE_OAUTH_URL`` to the LMS OAuth endpoint (e.g. ``http://edx.devstack.lms:18000`` in devstack) +- Set ``CODE_JAIL_REST_SERVICE_OAUTH_CLIENT_ID`` and ``CODE_JAIL_REST_SERVICE_OAUTH_CLIENT_SECRET`` to the client credentials app ID and secret that you identified in the LMS. (In devstack, these would be ``cms-backend-service-key`` and ``cms-backend-service-secret``.) diff --git a/edx_arch_experiments/codejail_service/__init__.py b/edx_arch_experiments/codejail_service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/edx_arch_experiments/codejail_service/apps.py b/edx_arch_experiments/codejail_service/apps.py new file mode 100644 index 0000000..6cdace6 --- /dev/null +++ b/edx_arch_experiments/codejail_service/apps.py @@ -0,0 +1,21 @@ +""" +App for running answer submissions inside of codejail. +""" + +from django.apps import AppConfig +from edx_django_utils.plugins.constants import PluginURLs + + +class CodejailService(AppConfig): + """ + Django application to run things in codejail. + """ + name = 'edx_arch_experiments.codejail_service' + + plugin_app = { + PluginURLs.CONFIG: { + 'lms.djangoapp': { + PluginURLs.NAMESPACE: 'codejail_service', + } + }, + } diff --git a/edx_arch_experiments/codejail_service/tests/test_course_library.zip b/edx_arch_experiments/codejail_service/tests/test_course_library.zip new file mode 100644 index 0000000..42409e3 Binary files /dev/null and b/edx_arch_experiments/codejail_service/tests/test_course_library.zip differ diff --git a/edx_arch_experiments/codejail_service/tests/test_views.py b/edx_arch_experiments/codejail_service/tests/test_views.py new file mode 100644 index 0000000..80823ac --- /dev/null +++ b/edx_arch_experiments/codejail_service/tests/test_views.py @@ -0,0 +1,152 @@ +""" +Test codejail service views. +""" + +import json +import textwrap +from os import path + +import ddt +from django.contrib.auth import get_user_model +from django.test import TestCase, override_settings +from django.urls import reverse +from rest_framework.test import APIClient + +from edx_arch_experiments.codejail_service import views + + +@override_settings( + ROOT_URLCONF='edx_arch_experiments.codejail_service.urls', + MIDDLEWARE=[ + 'django.contrib.sessions.middleware.SessionMiddleware', + ], +) +@ddt.ddt +class TestExecService(TestCase): + """Test the v0 code exec view.""" + + def setUp(self): + super().setUp() + user_model = get_user_model() + self.admin_user = user_model.objects.create_user('cms_worker', is_staff=True) + self.other_user = user_model.objects.create_user('student', is_staff=False) + self.standard_params = {'code': 'retval = 3 + 4', 'globals_dict': {}} + + def _test_codejail_api(self, *, user=None, skip_auth=False, params=None, files=None, exp_status, exp_body): + """ + Call the view and make assertions. + + Args: + user: User to authenticate as when calling view, defaulting to an is_staff user + skip_auth: If true, do not send authentication headers (incompatible with `user` argument) + params: Payload of codejail parameters, defaulting to a simple arithmetic check + files: Files to include in the API call, as dict of filenames to file objects + exp_status: Assert that the response HTTP status code is this value + exp_body: Assert that the response body JSON is this value + """ + assert not (user and skip_auth) + + client = APIClient() + user = user or self.admin_user + if not skip_auth: + client.force_authenticate(user) + + params = self.standard_params if params is None else params + payload = json.dumps(params) + req_body = {'payload': payload, **(files or {})} + + resp = client.post(reverse('code_exec_v0'), req_body, format='multipart') + + assert resp.status_code == exp_status + assert json.loads(resp.content) == exp_body + + def test_success(self): + """Regular successful call.""" + self._test_codejail_api( + exp_status=200, exp_body={'globals_dict': {'retval': 7}}, + ) + + @override_settings(CODEJAIL_SERVICE_ENABLED=False) + def test_feature_disabled(self): + """Service can be disabled.""" + self._test_codejail_api( + exp_status=500, exp_body={'error': "Codejail service not enabled"}, + ) + + @override_settings(ENABLE_CODEJAIL_REST_SERVICE=True) + def test_misconfigured_as_relay(self): + """Don't accept codejail requests if we're going to send them elsewhere.""" + self._test_codejail_api( + exp_status=500, exp_body={'error': "Codejail service is misconfigured. (Refusing to act as relay.)"}, + ) + + def test_unauthenticated(self): + """Anonymous requests are rejected.""" + self._test_codejail_api( + skip_auth=True, + exp_status=403, exp_body={'detail': "Authentication credentials were not provided."}, + ) + + def test_unprivileged(self): + """Anonymous requests are rejected.""" + self._test_codejail_api( + user=self.other_user, + exp_status=403, exp_body={'detail': "You do not have permission to perform this action."}, + ) + + def test_unsafely(self): + """unsafely=true is rejected""" + self._test_codejail_api( + params=dict(**self.standard_params, unsafely=True), + exp_status=400, exp_body={'error': "Refusing codejail execution with unsafely=true"}, + ) + + @ddt.unpack + @ddt.data( + ({'globals_dict': {}}, 'code'), + ({'code': 'retval = 3 + 4'}, 'globals_dict'), + ({}, 'code'), + ) + def test_missing_params(self, params, missing): + """Two code and globals_dict params are required.""" + self._test_codejail_api( + params=params, + exp_status=400, exp_body={ + 'error': f"Payload JSON did not match schema: '{missing}' is a required property", + }, + ) + + def test_extra_files(self): + """Check that we can include a course library.""" + # "Course library" containing `course_library.triangular_number`. + # + # It's tempting to use zipfile to write to an io.BytesIO so + # that the test library is in plaintext. Django's request + # factory will indeed see that as a file to use in a multipart + # upload, but it will see it as an empty bytestring. (read() + # returns empty bytestring, while getvalue() returns the + # desired data). So instead we just have a small zip file on + # disk here. + library_path = path.join(path.dirname(__file__), 'test_course_library.zip') + + with open(library_path, 'rb') as lib_zip: + self._test_codejail_api( + params={ + 'code': textwrap.dedent(""" + from course_library import triangular_number + + result = triangular_number(6) + """), + 'globals_dict': {}, + 'python_path': ['python_lib.zip'], + }, + files={'python_lib.zip': lib_zip}, + exp_status=200, exp_body={'globals_dict': {'result': 21}}, + ) + + def test_exception(self): + """Report exceptions from jailed code.""" + self._test_codejail_api( + params={'code': '1/0', 'globals_dict': {}}, + exp_status=200, exp_body={'emsg': 'ZeroDivisionError: division by zero'}, + ) diff --git a/edx_arch_experiments/codejail_service/urls.py b/edx_arch_experiments/codejail_service/urls.py new file mode 100644 index 0000000..a86b591 --- /dev/null +++ b/edx_arch_experiments/codejail_service/urls.py @@ -0,0 +1,11 @@ +""" +Codejail service URLs. +""" + +from django.urls import path + +from . import views + +urlpatterns = [ + path('api/v0/code-exec', views.code_exec_view_v0, name='code_exec_v0'), +] diff --git a/edx_arch_experiments/codejail_service/views.py b/edx_arch_experiments/codejail_service/views.py new file mode 100644 index 0000000..209300f --- /dev/null +++ b/edx_arch_experiments/codejail_service/views.py @@ -0,0 +1,171 @@ +""" +Codejail service API. +""" + +import json +import logging +from copy import deepcopy + +from codejail.safe_exec import SafeExecException, safe_exec +from django.conf import settings +from edx_toggles.toggles import SettingToggle +from jsonschema.exceptions import best_match as json_error_best_match +from jsonschema.validators import Draft202012Validator +from rest_framework.decorators import api_view, parser_classes, permission_classes +from rest_framework.parsers import FormParser, MultiPartParser +from rest_framework.permissions import IsAdminUser +from rest_framework.response import Response + +log = logging.getLogger(__name__) + +# .. toggle_name: CODEJAIL_SERVICE_ENABLED +# .. toggle_implementation: SettingToggle +# .. toggle_default: True +# .. toggle_description: If True, codejail execution calls will be accepted over the network, +# allowing this IDA to act as a codejail service for another IDA. +# .. toggle_use_cases: circuit_breaker +# .. toggle_creation_date: 2023-12-21 +# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/33538 +CODEJAIL_SERVICE_ENABLED = SettingToggle('CODEJAIL_SERVICE_ENABLED', default=True, module_name=__name__) + +# Schema for the JSON passed in the v0 API's 'payload' field. +payload_schema = { + 'type': 'object', + 'properties': { + 'code': {'type': 'string'}, + 'globals_dict': {'type': 'object'}, + # Some of these are configured as union types because + # edx-platform appears to currently default to None for some + # of them (rather than omitting the keys.) + 'python_path': { + 'anyOf': [ + { + 'type': 'array', + 'items': {'type': 'string'}, + }, + {'type': 'null'}, + ], + }, + 'limit_overrides_context': { + 'anyOf': [ + {'type': 'string'}, + {'type': 'null'}, + ], + }, + 'slug': { + 'anyOf': [ + {'type': 'string'}, + {'type': 'null'}, + ], + }, + 'unsafely': {'type': 'boolean'}, + }, + 'required': ['code', 'globals_dict'], +} +# Use this rather than jsonschema.validate, since that would check the schema +# every time it is called. Best to do it just once at startup. +Draft202012Validator.check_schema(payload_schema) +payload_validator = Draft202012Validator(payload_schema) + + +# A note on the authorization model used here: +# +# We really just need one service account to be able to call this, and +# then also allow is_staff to call it for convenience and debugging +# purposes. +# +# To do this "right", I'd probably have to create an empty abstract +# model, create a permission on it, create a group, add the permission +# to the group, and add the service account to the group. Then I could +# check the calling user's has_perm. If I wanted to use bridgekeeper +# (as we're trying to do more of) I might be able to give bridgekeeper +# a `@blanket_rule` that checks membership in the group, then use +# bridgekeeper here instead of checking permissions directly, but it's +# possible this wouldn't work because bridgekeeper might require there +# to be a model instance to pass in (and there wouldn't be one, since +# it's just an abstract model.) +# +# But... given that the service account will be is_staff, and I'll be +# opening this up to is_staff alongside the intended service account, +# and this is already a hacky intermediate solution... we can just use +# the `IsAdminUser` permission class and be done with it. + + +@api_view(['POST']) +@parser_classes([FormParser, MultiPartParser]) +@permission_classes([IsAdminUser]) +def code_exec_view_v0(request): + """ + Executes code in a codejail sandbox for a remote caller. + + This implements the API used by edxapp's xmodule.capa.safe_exec.remote_exec. + It accepts a POST of a form containing a `payload` value and zero or more + extra files. + + The payload is JSON and contains the parameters to be sent to codejail's + safe_exec (aside from `extra_files`). See payload_schema for type information. + + This API does not permit `unsafely=true`. + + If the response is a 200, the codejail execution completed. The response + will be JSON containing either a single key `globals_dict` (containing + the global scope values at the end of a run to completion) or `emsg` (the + exception the submitted code raised). + + Other responses are errors, with a JSON body containing further details. + """ + if not CODEJAIL_SERVICE_ENABLED.is_enabled(): + return Response({'error': "Codejail service not enabled"}, status=500) + + # There's a risk of getting into a loop if e.g. the CMS asks the + # LMS to run codejail executions on its behalf, and the LMS is + # *also* inadvertently configured to call the LMS (itself). + # There's no good reason to have a chain of >2 services passing + # codejail requests along, so only allow execution here if we + # aren't going to pass it along to someone else. + if getattr(settings, 'ENABLE_CODEJAIL_REST_SERVICE', False): + log.error( + "Refusing to run codejail request from over the network " + "when we're going to pass it to another IDA anyway" + ) + return Response({'error': "Codejail service is misconfigured. (Refusing to act as relay.)"}, status=500) + + params_json = request.data['payload'] + params = json.loads(params_json) + + if json_error := json_error_best_match(payload_validator.iter_errors(params)): + return Response({'error': f"Payload JSON did not match schema: {json_error.message}"}, status=400) + + complete_code = params['code'] # includes standard prolog + input_globals_dict = params['globals_dict'] + python_path = params.get('python_path') or [] + limit_overrides_context = params.get('limit_overrides_context') + slug = params.get('slug') + unsafely = params.get('unsafely') + + # Convert to a list of (string, bytestring) pairs. Any duplicated file names + # are resolved as last-wins. + extra_files = [(filename, file.read()) for filename, file in request.FILES.items()] + + # Far too dangerous to allow unsafe executions to come in over the + # network, no matter who we think the caller is. The caller is the + # one who has the context on safety. + if unsafely: + return Response({'error': "Refusing codejail execution with unsafely=true"}, status=400) + + output_globals_dict = deepcopy(input_globals_dict) # Output dict will be mutated by safe_exec + try: + safe_exec( + complete_code, + output_globals_dict, + python_path=python_path, + extra_files=extra_files, + limit_overrides_context=limit_overrides_context, + slug=slug, + ) + except SafeExecException as e: + log.debug("CodejailService execution failed for {slug=} with: {e}") + return Response({'emsg': str(e)}) + + log.debug("CodejailService execution succeeded for {slug=}, with globals={output_globals_dict!r}") + return Response({'globals_dict': output_globals_dict}) diff --git a/requirements/base.in b/requirements/base.in index 0935bef..924dd37 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -4,3 +4,8 @@ Django # Web application framework edx_django_utils django-waffle # Configuration switches and flags -- used by config_watcher app +edx-codejail # Actual codejail library; used by codejail_service app +djangorestframework # Used by codejail_service app +edx-drf-extensions # Used by codejail_service app +edx-toggles # Used by codejail_service app +jsonschema # Parse and validate JSON; used by codejail_service app diff --git a/requirements/base.txt b/requirements/base.txt index bb88a7c..9fbaedb 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -6,40 +6,136 @@ # asgiref==3.7.2 # via django +attrs==23.2.0 + # via + # jsonschema + # referencing +certifi==2023.11.17 + # via requests cffi==1.16.0 - # via pynacl + # via + # cryptography + # pynacl +charset-normalizer==3.3.2 + # via requests click==8.1.7 - # via edx-django-utils + # via + # code-annotations + # edx-django-utils +code-annotations==1.5.0 + # via edx-toggles +cryptography==41.0.7 + # via pyjwt django==3.2.23 # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/base.in # django-crum # django-waffle + # djangorestframework + # drf-jwt # edx-django-utils + # edx-drf-extensions + # edx-toggles django-crum==0.7.9 - # via edx-django-utils + # via + # edx-django-utils + # edx-toggles django-waffle==4.1.0 # via # -r requirements/base.in # edx-django-utils + # edx-drf-extensions + # edx-toggles +djangorestframework==3.14.0 + # via + # -r requirements/base.in + # drf-jwt + # edx-drf-extensions +drf-jwt==1.19.2 + # via edx-drf-extensions +edx-codejail==3.3.3 + # via -r requirements/base.in edx-django-utils==5.9.0 + # via + # -r requirements/base.in + # edx-drf-extensions + # edx-toggles +edx-drf-extensions==9.1.2 # via -r requirements/base.in -newrelic==9.3.0 +edx-opaque-keys==2.5.1 + # via edx-drf-extensions +edx-toggles==5.1.0 + # via -r requirements/base.in +idna==3.6 + # via requests +importlib-resources==6.1.1 + # via + # jsonschema + # jsonschema-specifications +jinja2==3.1.3 + # via code-annotations +jsonschema==4.20.0 + # via -r requirements/base.in +jsonschema-specifications==2023.12.1 + # via jsonschema +markupsafe==2.1.3 + # via jinja2 +newrelic==9.4.0 # via edx-django-utils pbr==6.0.0 # via stevedore -psutil==5.9.6 +pkgutil-resolve-name==1.3.10 + # via jsonschema +psutil==5.9.7 # via edx-django-utils pycparser==2.21 # via cffi +pyjwt[crypto]==2.8.0 + # via + # drf-jwt + # edx-drf-extensions + # pyjwt +pymongo==3.13.0 + # via edx-opaque-keys pynacl==1.5.0 # via edx-django-utils +python-slugify==8.0.1 + # via code-annotations pytz==2023.3.post1 - # via django + # via + # django + # djangorestframework +pyyaml==6.0.1 + # via code-annotations +referencing==0.32.1 + # via + # jsonschema + # jsonschema-specifications +requests==2.31.0 + # via edx-drf-extensions +rpds-py==0.16.2 + # via + # jsonschema + # referencing +semantic-version==2.10.0 + # via edx-drf-extensions +six==1.16.0 + # via edx-codejail sqlparse==0.4.4 # via django stevedore==5.1.0 - # via edx-django-utils + # via + # code-annotations + # edx-django-utils + # edx-opaque-keys +text-unidecode==1.3 + # via python-slugify typing-extensions==4.9.0 - # via asgiref + # via + # asgiref + # edx-opaque-keys +urllib3==2.1.0 + # via requests +zipp==3.17.0 + # via importlib-resources diff --git a/requirements/dev.txt b/requirements/dev.txt index 6c5aa7e..ca87a77 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -13,6 +13,11 @@ astroid==3.0.2 # -r requirements/quality.txt # pylint # pylint-celery +attrs==23.2.0 + # via + # -r requirements/quality.txt + # jsonschema + # referencing build==1.0.3 # via # -r requirements/pip-tools.txt @@ -56,11 +61,12 @@ code-annotations==1.5.0 # via # -r requirements/quality.txt # edx-lint + # edx-toggles colorama==0.4.6 # via # -r requirements/ci.txt # tox -coverage[toml]==7.3.3 +coverage[toml]==7.4.0 # via # -r requirements/quality.txt # coverage @@ -68,7 +74,10 @@ coverage[toml]==7.3.3 cryptography==41.0.7 # via # -r requirements/quality.txt + # pyjwt # secretstorage +ddt==1.7.1 + # via -r requirements/quality.txt diff-cover==8.0.2 # via -r requirements/dev.in dill==0.3.7 @@ -85,26 +94,55 @@ django==3.2.23 # -r requirements/quality.txt # django-crum # django-waffle + # djangorestframework + # drf-jwt # edx-django-utils + # edx-drf-extensions # edx-i18n-tools + # edx-toggles django-crum==0.7.9 # via # -r requirements/quality.txt # edx-django-utils + # edx-toggles django-waffle==4.1.0 # via # -r requirements/quality.txt # edx-django-utils + # edx-drf-extensions + # edx-toggles +djangorestframework==3.14.0 + # via + # -r requirements/quality.txt + # drf-jwt + # edx-drf-extensions docutils==0.20.1 # via # -r requirements/quality.txt # readme-renderer +drf-jwt==1.19.2 + # via + # -r requirements/quality.txt + # edx-drf-extensions +edx-codejail==3.3.3 + # via -r requirements/quality.txt edx-django-utils==5.9.0 + # via + # -r requirements/quality.txt + # edx-drf-extensions + # edx-toggles +edx-drf-extensions==9.1.2 # via -r requirements/quality.txt edx-i18n-tools==1.3.0 # via -r requirements/dev.in edx-lint==5.3.6 # via -r requirements/quality.txt +edx-opaque-keys==2.5.1 + # via + # -r requirements/quality.txt + # edx-drf-extensions +edx-toggles==5.1.0 + # via -r requirements/quality.txt exceptiongroup==1.2.0 # via # -r requirements/quality.txt @@ -118,7 +156,7 @@ idna==3.6 # via # -r requirements/quality.txt # requests -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 # via # -r requirements/pip-tools.txt # -r requirements/quality.txt @@ -128,6 +166,8 @@ importlib-metadata==7.0.0 importlib-resources==6.1.1 # via # -r requirements/quality.txt + # jsonschema + # jsonschema-specifications # keyring iniconfig==2.0.0 # via @@ -146,16 +186,22 @@ jeepney==0.8.0 # -r requirements/quality.txt # keyring # secretstorage -jinja2==3.1.2 +jinja2==3.1.3 # via # -r requirements/quality.txt # code-annotations # diff-cover +jsonschema==4.20.0 + # via -r requirements/quality.txt +jsonschema-specifications==2023.12.1 + # via + # -r requirements/quality.txt + # jsonschema keyring==24.3.0 # via # -r requirements/quality.txt # twine -lxml==4.9.3 +lxml==5.1.0 # via edx-i18n-tools markdown-it-py==3.0.0 # via @@ -173,11 +219,11 @@ mdurl==0.1.2 # via # -r requirements/quality.txt # markdown-it-py -more-itertools==10.1.0 +more-itertools==10.2.0 # via # -r requirements/quality.txt # jaraco-classes -newrelic==9.3.0 +newrelic==9.4.0 # via # -r requirements/quality.txt # edx-django-utils @@ -206,6 +252,10 @@ pkginfo==1.9.6 # via # -r requirements/quality.txt # twine +pkgutil-resolve-name==1.3.10 + # via + # -r requirements/quality.txt + # jsonschema platformdirs==4.1.0 # via # -r requirements/ci.txt @@ -222,7 +272,7 @@ pluggy==1.3.0 # tox polib==1.2.0 # via edx-i18n-tools -psutil==5.9.6 +psutil==5.9.7 # via # -r requirements/quality.txt # edx-django-utils @@ -240,6 +290,12 @@ pygments==2.17.2 # diff-cover # readme-renderer # rich +pyjwt[crypto]==2.8.0 + # via + # -r requirements/quality.txt + # drf-jwt + # edx-drf-extensions + # pyjwt pylint==3.0.3 # via # -r requirements/quality.txt @@ -260,6 +316,10 @@ pylint-plugin-utils==0.8.2 # -r requirements/quality.txt # pylint-celery # pylint-django +pymongo==3.13.0 + # via + # -r requirements/quality.txt + # edx-opaque-keys pynacl==1.5.0 # via # -r requirements/quality.txt @@ -272,7 +332,7 @@ pyproject-hooks==1.0.0 # via # -r requirements/pip-tools.txt # build -pytest==7.4.3 +pytest==7.4.4 # via # -r requirements/quality.txt # pytest-cov @@ -289,6 +349,7 @@ pytz==2023.3.post1 # via # -r requirements/quality.txt # django + # djangorestframework pyyaml==6.0.1 # via # -r requirements/quality.txt @@ -298,9 +359,15 @@ readme-renderer==42.0 # via # -r requirements/quality.txt # twine +referencing==0.32.1 + # via + # -r requirements/quality.txt + # jsonschema + # jsonschema-specifications requests==2.31.0 # via # -r requirements/quality.txt + # edx-drf-extensions # requests-toolbelt # twine requests-toolbelt==1.0.0 @@ -315,13 +382,23 @@ rich==13.7.0 # via # -r requirements/quality.txt # twine +rpds-py==0.16.2 + # via + # -r requirements/quality.txt + # jsonschema + # referencing secretstorage==3.3.3 # via # -r requirements/quality.txt # keyring +semantic-version==2.10.0 + # via + # -r requirements/quality.txt + # edx-drf-extensions six==1.16.0 # via # -r requirements/quality.txt + # edx-codejail # edx-lint snowballstemmer==2.2.0 # via @@ -336,6 +413,7 @@ stevedore==5.1.0 # -r requirements/quality.txt # code-annotations # edx-django-utils + # edx-opaque-keys text-unidecode==1.3 # via # -r requirements/quality.txt @@ -366,6 +444,7 @@ typing-extensions==4.9.0 # -r requirements/quality.txt # asgiref # astroid + # edx-opaque-keys # pylint # rich urllib3==2.1.0 diff --git a/requirements/doc.txt b/requirements/doc.txt index 793120e..37a1ae6 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -10,43 +10,73 @@ asgiref==3.7.2 # via # -r requirements/test.txt # django +attrs==23.2.0 + # via + # -r requirements/test.txt + # jsonschema + # referencing babel==2.14.0 # via sphinx certifi==2023.11.17 - # via requests + # via + # -r requirements/test.txt + # requests cffi==1.16.0 # via # -r requirements/test.txt + # cryptography # pynacl charset-normalizer==3.3.2 - # via requests + # via + # -r requirements/test.txt + # requests click==8.1.7 # via # -r requirements/test.txt # code-annotations # edx-django-utils code-annotations==1.5.0 - # via -r requirements/test.txt -coverage[toml]==7.3.3 + # via + # -r requirements/test.txt + # edx-toggles +coverage[toml]==7.4.0 # via # -r requirements/test.txt # coverage # pytest-cov +cryptography==41.0.7 + # via + # -r requirements/test.txt + # pyjwt +ddt==1.7.1 + # via -r requirements/test.txt django==3.2.23 # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/test.txt # django-crum # django-waffle + # djangorestframework + # drf-jwt # edx-django-utils + # edx-drf-extensions + # edx-toggles django-crum==0.7.9 # via # -r requirements/test.txt # edx-django-utils + # edx-toggles django-waffle==4.1.0 # via # -r requirements/test.txt # edx-django-utils + # edx-drf-extensions + # edx-toggles +djangorestframework==3.14.0 + # via + # -r requirements/test.txt + # drf-jwt + # edx-drf-extensions doc8==1.1.1 # via -r requirements/doc.in docutils==0.19 @@ -55,34 +85,64 @@ docutils==0.19 # readme-renderer # restructuredtext-lint # sphinx +drf-jwt==1.19.2 + # via + # -r requirements/test.txt + # edx-drf-extensions +edx-codejail==3.3.3 + # via -r requirements/test.txt edx-django-utils==5.9.0 + # via + # -r requirements/test.txt + # edx-drf-extensions + # edx-toggles +edx-drf-extensions==9.1.2 # via -r requirements/test.txt +edx-opaque-keys==2.5.1 + # via + # -r requirements/test.txt + # edx-drf-extensions edx-sphinx-theme==3.1.0 # via -r requirements/doc.in +edx-toggles==5.1.0 + # via -r requirements/test.txt exceptiongroup==1.2.0 # via # -r requirements/test.txt # pytest idna==3.6 - # via requests + # via + # -r requirements/test.txt + # requests imagesize==1.4.1 # via sphinx -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 # via sphinx +importlib-resources==6.1.1 + # via + # -r requirements/test.txt + # jsonschema + # jsonschema-specifications iniconfig==2.0.0 # via # -r requirements/test.txt # pytest -jinja2==3.1.2 +jinja2==3.1.3 # via # -r requirements/test.txt # code-annotations # sphinx +jsonschema==4.20.0 + # via -r requirements/test.txt +jsonschema-specifications==2023.12.1 + # via + # -r requirements/test.txt + # jsonschema markupsafe==2.1.3 # via # -r requirements/test.txt # jinja2 -newrelic==9.3.0 +newrelic==9.4.0 # via # -r requirements/test.txt # edx-django-utils @@ -97,11 +157,15 @@ pbr==6.0.0 # via # -r requirements/test.txt # stevedore +pkgutil-resolve-name==1.3.10 + # via + # -r requirements/test.txt + # jsonschema pluggy==1.3.0 # via # -r requirements/test.txt # pytest -psutil==5.9.6 +psutil==5.9.7 # via # -r requirements/test.txt # edx-django-utils @@ -114,11 +178,21 @@ pygments==2.17.2 # doc8 # readme-renderer # sphinx +pyjwt[crypto]==2.8.0 + # via + # -r requirements/test.txt + # drf-jwt + # edx-drf-extensions + # pyjwt +pymongo==3.13.0 + # via + # -r requirements/test.txt + # edx-opaque-keys pynacl==1.5.0 # via # -r requirements/test.txt # edx-django-utils -pytest==7.4.3 +pytest==7.4.4 # via # -r requirements/test.txt # pytest-cov @@ -136,18 +210,39 @@ pytz==2023.3.post1 # -r requirements/test.txt # babel # django + # djangorestframework pyyaml==6.0.1 # via # -r requirements/test.txt # code-annotations readme-renderer==42.0 # via -r requirements/doc.in +referencing==0.32.1 + # via + # -r requirements/test.txt + # jsonschema + # jsonschema-specifications requests==2.31.0 - # via sphinx + # via + # -r requirements/test.txt + # edx-drf-extensions + # sphinx restructuredtext-lint==1.4.0 # via doc8 +rpds-py==0.16.2 + # via + # -r requirements/test.txt + # jsonschema + # referencing +semantic-version==2.10.0 + # via + # -r requirements/test.txt + # edx-drf-extensions six==1.16.0 - # via edx-sphinx-theme + # via + # -r requirements/test.txt + # edx-codejail + # edx-sphinx-theme snowballstemmer==2.2.0 # via sphinx sphinx==5.3.0 @@ -176,6 +271,7 @@ stevedore==5.1.0 # code-annotations # doc8 # edx-django-utils + # edx-opaque-keys text-unidecode==1.3 # via # -r requirements/test.txt @@ -190,7 +286,13 @@ typing-extensions==4.9.0 # via # -r requirements/test.txt # asgiref + # edx-opaque-keys urllib3==2.1.0 - # via requests + # via + # -r requirements/test.txt + # requests zipp==3.17.0 - # via importlib-metadata + # via + # -r requirements/test.txt + # importlib-metadata + # importlib-resources diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt index 93a9cee..0e88226 100644 --- a/requirements/pip-tools.txt +++ b/requirements/pip-tools.txt @@ -8,7 +8,7 @@ build==1.0.3 # via pip-tools click==8.1.7 # via pip-tools -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 # via build packaging==23.2 # via build diff --git a/requirements/pip.txt b/requirements/pip.txt index 14cb99c..a4cf530 100644 --- a/requirements/pip.txt +++ b/requirements/pip.txt @@ -8,7 +8,7 @@ wheel==0.42.0 # via -r requirements/pip.in # The following packages are considered to be unsafe in a requirements file: -pip==23.3.1 +pip==23.3.2 # via -r requirements/pip.in -setuptools==69.0.2 +setuptools==69.0.3 # via -r requirements/pip.in diff --git a/requirements/quality.txt b/requirements/quality.txt index d0dc4f3..4a4ef9b 100644 --- a/requirements/quality.txt +++ b/requirements/quality.txt @@ -12,15 +12,24 @@ astroid==3.0.2 # via # pylint # pylint-celery +attrs==23.2.0 + # via + # -r requirements/test.txt + # jsonschema + # referencing certifi==2023.11.17 - # via requests + # via + # -r requirements/test.txt + # requests cffi==1.16.0 # via # -r requirements/test.txt # cryptography # pynacl charset-normalizer==3.3.2 - # via requests + # via + # -r requirements/test.txt + # requests click==8.1.7 # via # -r requirements/test.txt @@ -34,13 +43,19 @@ code-annotations==1.5.0 # via # -r requirements/test.txt # edx-lint -coverage[toml]==7.3.3 + # edx-toggles +coverage[toml]==7.4.0 # via # -r requirements/test.txt # coverage # pytest-cov cryptography==41.0.7 - # via secretstorage + # via + # -r requirements/test.txt + # pyjwt + # secretstorage +ddt==1.7.1 + # via -r requirements/test.txt dill==0.3.7 # via pylint django==3.2.23 @@ -49,33 +64,68 @@ django==3.2.23 # -r requirements/test.txt # django-crum # django-waffle + # djangorestframework + # drf-jwt # edx-django-utils + # edx-drf-extensions + # edx-toggles django-crum==0.7.9 # via # -r requirements/test.txt # edx-django-utils + # edx-toggles django-waffle==4.1.0 # via # -r requirements/test.txt # edx-django-utils + # edx-drf-extensions + # edx-toggles +djangorestframework==3.14.0 + # via + # -r requirements/test.txt + # drf-jwt + # edx-drf-extensions docutils==0.20.1 # via readme-renderer +drf-jwt==1.19.2 + # via + # -r requirements/test.txt + # edx-drf-extensions +edx-codejail==3.3.3 + # via -r requirements/test.txt edx-django-utils==5.9.0 + # via + # -r requirements/test.txt + # edx-drf-extensions + # edx-toggles +edx-drf-extensions==9.1.2 # via -r requirements/test.txt edx-lint==5.3.6 # via -r requirements/quality.in +edx-opaque-keys==2.5.1 + # via + # -r requirements/test.txt + # edx-drf-extensions +edx-toggles==5.1.0 + # via -r requirements/test.txt exceptiongroup==1.2.0 # via # -r requirements/test.txt # pytest idna==3.6 - # via requests -importlib-metadata==7.0.0 + # via + # -r requirements/test.txt + # requests +importlib-metadata==7.0.1 # via # keyring # twine importlib-resources==6.1.1 - # via keyring + # via + # -r requirements/test.txt + # jsonschema + # jsonschema-specifications + # keyring iniconfig==2.0.0 # via # -r requirements/test.txt @@ -90,10 +140,16 @@ jeepney==0.8.0 # via # keyring # secretstorage -jinja2==3.1.2 +jinja2==3.1.3 # via # -r requirements/test.txt # code-annotations +jsonschema==4.20.0 + # via -r requirements/test.txt +jsonschema-specifications==2023.12.1 + # via + # -r requirements/test.txt + # jsonschema keyring==24.3.0 # via twine markdown-it-py==3.0.0 @@ -106,9 +162,9 @@ mccabe==0.7.0 # via pylint mdurl==0.1.2 # via markdown-it-py -more-itertools==10.1.0 +more-itertools==10.2.0 # via jaraco-classes -newrelic==9.3.0 +newrelic==9.4.0 # via # -r requirements/test.txt # edx-django-utils @@ -124,13 +180,17 @@ pbr==6.0.0 # stevedore pkginfo==1.9.6 # via twine +pkgutil-resolve-name==1.3.10 + # via + # -r requirements/test.txt + # jsonschema platformdirs==4.1.0 # via pylint pluggy==1.3.0 # via # -r requirements/test.txt # pytest -psutil==5.9.6 +psutil==5.9.7 # via # -r requirements/test.txt # edx-django-utils @@ -146,6 +206,12 @@ pygments==2.17.2 # via # readme-renderer # rich +pyjwt[crypto]==2.8.0 + # via + # -r requirements/test.txt + # drf-jwt + # edx-drf-extensions + # pyjwt pylint==3.0.3 # via # edx-lint @@ -160,11 +226,15 @@ pylint-plugin-utils==0.8.2 # via # pylint-celery # pylint-django +pymongo==3.13.0 + # via + # -r requirements/test.txt + # edx-opaque-keys pynacl==1.5.0 # via # -r requirements/test.txt # edx-django-utils -pytest==7.4.3 +pytest==7.4.4 # via # -r requirements/test.txt # pytest-cov @@ -181,14 +251,22 @@ pytz==2023.3.post1 # via # -r requirements/test.txt # django + # djangorestframework pyyaml==6.0.1 # via # -r requirements/test.txt # code-annotations readme-renderer==42.0 # via twine +referencing==0.32.1 + # via + # -r requirements/test.txt + # jsonschema + # jsonschema-specifications requests==2.31.0 # via + # -r requirements/test.txt + # edx-drf-extensions # requests-toolbelt # twine requests-toolbelt==1.0.0 @@ -197,10 +275,22 @@ rfc3986==2.0.0 # via twine rich==13.7.0 # via twine +rpds-py==0.16.2 + # via + # -r requirements/test.txt + # jsonschema + # referencing secretstorage==3.3.3 # via keyring +semantic-version==2.10.0 + # via + # -r requirements/test.txt + # edx-drf-extensions six==1.16.0 - # via edx-lint + # via + # -r requirements/test.txt + # edx-codejail + # edx-lint snowballstemmer==2.2.0 # via pydocstyle sqlparse==0.4.4 @@ -212,6 +302,7 @@ stevedore==5.1.0 # -r requirements/test.txt # code-annotations # edx-django-utils + # edx-opaque-keys text-unidecode==1.3 # via # -r requirements/test.txt @@ -231,13 +322,16 @@ typing-extensions==4.9.0 # -r requirements/test.txt # asgiref # astroid + # edx-opaque-keys # pylint # rich urllib3==2.1.0 # via + # -r requirements/test.txt # requests # twine zipp==3.17.0 # via + # -r requirements/test.txt # importlib-metadata # importlib-resources diff --git a/requirements/scripts.txt b/requirements/scripts.txt index 90ce1d7..35ed440 100644 --- a/requirements/scripts.txt +++ b/requirements/scripts.txt @@ -8,34 +8,52 @@ asgiref==3.7.2 # via # -r requirements/base.txt # django -attrs==23.1.0 - # via openedx-events +attrs==23.2.0 + # via + # -r requirements/base.txt + # jsonschema + # openedx-events + # referencing avro==1.11.3 # via confluent-kafka certifi==2023.11.17 - # via requests + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via # -r requirements/base.txt + # cryptography # pynacl charset-normalizer==3.3.2 - # via requests + # via + # -r requirements/base.txt + # requests click==8.1.7 # via # -r requirements/base.txt # code-annotations # edx-django-utils code-annotations==1.5.0 - # via edx-toggles + # via + # -r requirements/base.txt + # edx-toggles confluent-kafka[avro]==2.3.0 # via -r requirements/scripts.in +cryptography==41.0.7 + # via + # -r requirements/base.txt + # pyjwt django==3.2.23 # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/base.txt # django-crum # django-waffle + # djangorestframework + # drf-jwt # edx-django-utils + # edx-drf-extensions # edx-event-bus-kafka # edx-toggles # openedx-events @@ -48,29 +66,66 @@ django-waffle==4.1.0 # via # -r requirements/base.txt # edx-django-utils + # edx-drf-extensions # edx-toggles +djangorestframework==3.14.0 + # via + # -r requirements/base.txt + # drf-jwt + # edx-drf-extensions +drf-jwt==1.19.2 + # via + # -r requirements/base.txt + # edx-drf-extensions +edx-codejail==3.3.3 + # via -r requirements/base.txt edx-django-utils==5.9.0 # via # -r requirements/base.txt + # edx-drf-extensions # edx-event-bus-kafka # edx-toggles +edx-drf-extensions==9.1.2 + # via -r requirements/base.txt edx-event-bus-kafka==5.5.0 # via -r requirements/scripts.in edx-opaque-keys[django]==2.5.1 - # via openedx-events + # via + # -r requirements/base.txt + # edx-drf-extensions + # openedx-events edx-toggles==5.1.0 - # via edx-event-bus-kafka -fastavro==1.9.1 + # via + # -r requirements/base.txt + # edx-event-bus-kafka +fastavro==1.9.3 # via # confluent-kafka # openedx-events idna==3.6 - # via requests -jinja2==3.1.2 - # via code-annotations + # via + # -r requirements/base.txt + # requests +importlib-resources==6.1.1 + # via + # -r requirements/base.txt + # jsonschema + # jsonschema-specifications +jinja2==3.1.3 + # via + # -r requirements/base.txt + # code-annotations +jsonschema==4.20.0 + # via -r requirements/base.txt +jsonschema-specifications==2023.12.1 + # via + # -r requirements/base.txt + # jsonschema markupsafe==2.1.3 - # via jinja2 -newrelic==9.3.0 + # via + # -r requirements/base.txt + # jinja2 +newrelic==9.4.0 # via # -r requirements/base.txt # edx-django-utils @@ -80,7 +135,11 @@ pbr==6.0.0 # via # -r requirements/base.txt # stevedore -psutil==5.9.6 +pkgutil-resolve-name==1.3.10 + # via + # -r requirements/base.txt + # jsonschema +psutil==5.9.7 # via # -r requirements/base.txt # edx-django-utils @@ -88,22 +147,56 @@ pycparser==2.21 # via # -r requirements/base.txt # cffi +pyjwt[crypto]==2.8.0 + # via + # -r requirements/base.txt + # drf-jwt + # edx-drf-extensions + # pyjwt pymongo==3.13.0 - # via edx-opaque-keys + # via + # -r requirements/base.txt + # edx-opaque-keys pynacl==1.5.0 # via # -r requirements/base.txt # edx-django-utils python-slugify==8.0.1 - # via code-annotations + # via + # -r requirements/base.txt + # code-annotations pytz==2023.3.post1 # via # -r requirements/base.txt # django + # djangorestframework pyyaml==6.0.1 - # via code-annotations + # via + # -r requirements/base.txt + # code-annotations +referencing==0.32.1 + # via + # -r requirements/base.txt + # jsonschema + # jsonschema-specifications requests==2.31.0 - # via confluent-kafka + # via + # -r requirements/base.txt + # confluent-kafka + # edx-drf-extensions +rpds-py==0.16.2 + # via + # -r requirements/base.txt + # jsonschema + # referencing +semantic-version==2.10.0 + # via + # -r requirements/base.txt + # edx-drf-extensions +six==1.16.0 + # via + # -r requirements/base.txt + # edx-codejail sqlparse==0.4.4 # via # -r requirements/base.txt @@ -115,11 +208,19 @@ stevedore==5.1.0 # edx-django-utils # edx-opaque-keys text-unidecode==1.3 - # via python-slugify + # via + # -r requirements/base.txt + # python-slugify typing-extensions==4.9.0 # via # -r requirements/base.txt # asgiref # edx-opaque-keys urllib3==2.1.0 - # via requests + # via + # -r requirements/base.txt + # requests +zipp==3.17.0 + # via + # -r requirements/base.txt + # importlib-resources diff --git a/requirements/test.in b/requirements/test.in index 6797160..f2e6548 100644 --- a/requirements/test.in +++ b/requirements/test.in @@ -6,3 +6,4 @@ pytest-cov # pytest extension for code coverage statistics pytest-django # pytest extension for better Django support code-annotations # provides commands used by the pii_check make target. +ddt # data-driven tests \ No newline at end of file diff --git a/requirements/test.txt b/requirements/test.txt index 5c89cd3..86e1fdd 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -8,46 +8,117 @@ asgiref==3.7.2 # via # -r requirements/base.txt # django +attrs==23.2.0 + # via + # -r requirements/base.txt + # jsonschema + # referencing +certifi==2023.11.17 + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via # -r requirements/base.txt + # cryptography # pynacl +charset-normalizer==3.3.2 + # via + # -r requirements/base.txt + # requests click==8.1.7 # via # -r requirements/base.txt # code-annotations # edx-django-utils code-annotations==1.5.0 - # via -r requirements/test.in -coverage[toml]==7.3.3 + # via + # -r requirements/base.txt + # -r requirements/test.in + # edx-toggles +coverage[toml]==7.4.0 # via # coverage # pytest-cov +cryptography==41.0.7 + # via + # -r requirements/base.txt + # pyjwt +ddt==1.7.1 + # via -r requirements/test.in # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/base.txt # django-crum # django-waffle + # djangorestframework + # drf-jwt # edx-django-utils + # edx-drf-extensions + # edx-toggles django-crum==0.7.9 # via # -r requirements/base.txt # edx-django-utils + # edx-toggles django-waffle==4.1.0 # via # -r requirements/base.txt # edx-django-utils + # edx-drf-extensions + # edx-toggles +djangorestframework==3.14.0 + # via + # -r requirements/base.txt + # drf-jwt + # edx-drf-extensions +drf-jwt==1.19.2 + # via + # -r requirements/base.txt + # edx-drf-extensions +edx-codejail==3.3.3 + # via -r requirements/base.txt edx-django-utils==5.9.0 + # via + # -r requirements/base.txt + # edx-drf-extensions + # edx-toggles +edx-drf-extensions==9.1.2 + # via -r requirements/base.txt +edx-opaque-keys==2.5.1 + # via + # -r requirements/base.txt + # edx-drf-extensions +edx-toggles==5.1.0 # via -r requirements/base.txt exceptiongroup==1.2.0 # via pytest +idna==3.6 + # via + # -r requirements/base.txt + # requests +importlib-resources==6.1.1 + # via + # -r requirements/base.txt + # jsonschema + # jsonschema-specifications iniconfig==2.0.0 # via pytest -jinja2==3.1.2 - # via code-annotations +jinja2==3.1.3 + # via + # -r requirements/base.txt + # code-annotations +jsonschema==4.20.0 + # via -r requirements/base.txt +jsonschema-specifications==2023.12.1 + # via + # -r requirements/base.txt + # jsonschema markupsafe==2.1.3 - # via jinja2 -newrelic==9.3.0 + # via + # -r requirements/base.txt + # jinja2 +newrelic==9.4.0 # via # -r requirements/base.txt # edx-django-utils @@ -57,9 +128,13 @@ pbr==6.0.0 # via # -r requirements/base.txt # stevedore +pkgutil-resolve-name==1.3.10 + # via + # -r requirements/base.txt + # jsonschema pluggy==1.3.0 # via pytest -psutil==5.9.6 +psutil==5.9.7 # via # -r requirements/base.txt # edx-django-utils @@ -67,11 +142,21 @@ pycparser==2.21 # via # -r requirements/base.txt # cffi +pyjwt[crypto]==2.8.0 + # via + # -r requirements/base.txt + # drf-jwt + # edx-drf-extensions + # pyjwt +pymongo==3.13.0 + # via + # -r requirements/base.txt + # edx-opaque-keys pynacl==1.5.0 # via # -r requirements/base.txt # edx-django-utils -pytest==7.4.3 +pytest==7.4.4 # via # pytest-cov # pytest-django @@ -80,13 +165,40 @@ pytest-cov==4.1.0 pytest-django==4.7.0 # via -r requirements/test.in python-slugify==8.0.1 - # via code-annotations + # via + # -r requirements/base.txt + # code-annotations pytz==2023.3.post1 # via # -r requirements/base.txt # django + # djangorestframework pyyaml==6.0.1 - # via code-annotations + # via + # -r requirements/base.txt + # code-annotations +referencing==0.32.1 + # via + # -r requirements/base.txt + # jsonschema + # jsonschema-specifications +requests==2.31.0 + # via + # -r requirements/base.txt + # edx-drf-extensions +rpds-py==0.16.2 + # via + # -r requirements/base.txt + # jsonschema + # referencing +semantic-version==2.10.0 + # via + # -r requirements/base.txt + # edx-drf-extensions +six==1.16.0 + # via + # -r requirements/base.txt + # edx-codejail sqlparse==0.4.4 # via # -r requirements/base.txt @@ -96,8 +208,11 @@ stevedore==5.1.0 # -r requirements/base.txt # code-annotations # edx-django-utils + # edx-opaque-keys text-unidecode==1.3 - # via python-slugify + # via + # -r requirements/base.txt + # python-slugify tomli==2.0.1 # via # coverage @@ -106,3 +221,12 @@ typing-extensions==4.9.0 # via # -r requirements/base.txt # asgiref + # edx-opaque-keys +urllib3==2.1.0 + # via + # -r requirements/base.txt + # requests +zipp==3.17.0 + # via + # -r requirements/base.txt + # importlib-resources diff --git a/setup.py b/setup.py index 0a6fb7e..4b0d2af 100644 --- a/setup.py +++ b/setup.py @@ -163,6 +163,7 @@ def is_requirement(line): "lms.djangoapp": [ "arch_experiments = edx_arch_experiments.apps:EdxArchExperimentsConfig", "config_watcher = edx_arch_experiments.config_watcher.apps:ConfigWatcher", + "codejail_service = edx_arch_experiments.codejail_service.apps:CodejailService", ], }, )