From 85c1101f6d8b3a41bfea068840a0c1d5c373020d Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Mon, 13 Jan 2025 17:55:54 +0100 Subject: [PATCH 1/6] feat: add static type checking for documentation purposes --- Makefile | 1 + mypy.ini | 9 +++++++++ openedx_filters/learning/tests/test_filters.py | 3 ++- openedx_filters/tests/test_tooling.py | 3 ++- openedx_filters/utils.py | 2 +- 5 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 mypy.ini diff --git a/Makefile b/Makefile index 1ce93dfb..1ef99929 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,7 @@ upgrade: ## update the requirements/*.txt files with the latest packages satisf quality: ## check coding style with pycodestyle and pylint pylint openedx_filters test_utils *.py + mypy pycodestyle openedx_filters *.py pydocstyle openedx_filters *.py isort --check-only --diff --recursive test_utils openedx_filters *.py test_settings.py diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 00000000..07043271 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,9 @@ +[mypy] +allow_untyped_globals = False +plugins = + mypy_django_plugin.main, +files = + openedx_filters + +[mypy.plugins.django-stubs] +django_settings_module = "test_utils.test_settings" diff --git a/openedx_filters/learning/tests/test_filters.py b/openedx_filters/learning/tests/test_filters.py index a84e31dc..ef317921 100644 --- a/openedx_filters/learning/tests/test_filters.py +++ b/openedx_filters/learning/tests/test_filters.py @@ -3,7 +3,8 @@ """ from unittest.mock import Mock, patch -from ddt import data, ddt, unpack +# Ignore the type error for ddt import since it is not recognized by mypy. +from ddt import data, ddt, unpack # type: ignore from django.test import TestCase from openedx_filters.learning.filters import ( diff --git a/openedx_filters/tests/test_tooling.py b/openedx_filters/tests/test_tooling.py index 6a0fd650..3cffbad2 100644 --- a/openedx_filters/tests/test_tooling.py +++ b/openedx_filters/tests/test_tooling.py @@ -3,7 +3,8 @@ """ from unittest.mock import Mock, patch -import ddt +# Ignore the type error for ddt import since it is not recognized by mypy. +import ddt # type: ignore from django.test import TestCase, override_settings from openedx_filters import PipelineStep diff --git a/openedx_filters/utils.py b/openedx_filters/utils.py index c3a82fc2..267e9b84 100644 --- a/openedx_filters/utils.py +++ b/openedx_filters/utils.py @@ -8,7 +8,7 @@ class SensitiveDataManagementMixin: Custom class used manage sensitive data within filter arguments. """ - sensitive_form_data = [] + sensitive_form_data: list[str] = [] @classmethod def extract_sensitive_data(cls, form_data): From ee23e0420516876487b7ad41778d0d746e9f2c4e Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Mon, 13 Jan 2025 17:56:05 +0100 Subject: [PATCH 2/6] build: add mypy requirements --- requirements/doc.txt | 22 ++++++++++++++++++++++ requirements/test.in | 2 ++ requirements/test.txt | 16 ++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/requirements/doc.txt b/requirements/doc.txt index 459c5439..dd7b0d2e 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -16,6 +16,7 @@ asgiref==3.8.1 # via # -r requirements/test.txt # django + # django-stubs babel==2.16.0 # via # pydata-sphinx-theme @@ -53,6 +54,14 @@ django==4.2.18 # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/test.txt + # django-stubs + # django-stubs-ext +django-stubs==5.1.1 + # via -r requirements/test.txt +django-stubs-ext==5.1.1 + # via + # -r requirements/test.txt + # django-stubs dnspython==2.7.0 # via # -r requirements/test.txt @@ -113,6 +122,12 @@ more-itertools==10.6.0 # via # jaraco-classes # jaraco-functools +mypy==1.14.1 + # via -r requirements/test.txt +mypy-extensions==1.0.0 + # via + # -r requirements/test.txt + # mypy nh3==0.2.20 # via readme-renderer packaging==24.2 @@ -239,11 +254,18 @@ text-unidecode==1.3 # python-slugify twine==6.1.0 # via -r requirements/doc.in +types-pyyaml==6.0.12.20241230 + # via + # -r requirements/test.txt + # django-stubs typing-extensions==4.12.2 # via # -r requirements/test.txt # anyio + # django-stubs + # django-stubs-ext # edx-opaque-keys + # mypy # pydata-sphinx-theme urllib3==2.2.3 # via diff --git a/requirements/test.in b/requirements/test.in index 3af36bf2..a3b03468 100644 --- a/requirements/test.in +++ b/requirements/test.in @@ -7,3 +7,5 @@ ddt # A library to multiply test cases 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. +django-stubs # Typing stubs for Django, so it works with mypy +mypy # static type checking diff --git a/requirements/test.txt b/requirements/test.txt index b13e89b2..302db73a 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -8,6 +8,7 @@ asgiref==3.8.1 # via # -r requirements/base.txt # django + # django-stubs click==8.1.8 # via code-annotations code-annotations==2.2.0 @@ -20,6 +21,12 @@ django==4.2.18 # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/base.txt + # django-stubs + # django-stubs-ext +django-stubs==5.1.1 + # via -r requirements/test.in +django-stubs-ext==5.1.1 + # via django-stubs dnspython==2.7.0 # via # -r requirements/base.txt @@ -32,6 +39,10 @@ jinja2==3.1.5 # via code-annotations markupsafe==3.0.2 # via jinja2 +mypy==1.14.1 + # via -r requirements/test.in +mypy-extensions==1.0.0 + # via mypy packaging==24.2 # via pytest pbr==6.1.0 @@ -67,7 +78,12 @@ stevedore==5.4.0 # edx-opaque-keys text-unidecode==1.3 # via python-slugify +types-pyyaml==6.0.12.20241230 + # via django-stubs typing-extensions==4.12.2 # via # -r requirements/base.txt + # django-stubs + # django-stubs-ext # edx-opaque-keys + # mypy From 8996b9172bca2ef354b057eb1ec9c85be9c73054 Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Tue, 14 Jan 2025 12:09:22 +0100 Subject: [PATCH 3/6] refactor: add type hints to tooling --- openedx_filters/course_authoring/filters.py | 2 +- openedx_filters/exceptions.py | 13 +++++- openedx_filters/learning/filters.py | 50 ++++++++++----------- openedx_filters/tooling.py | 17 ++++--- openedx_filters/utils.py | 9 ++-- 5 files changed, 53 insertions(+), 38 deletions(-) diff --git a/openedx_filters/course_authoring/filters.py b/openedx_filters/course_authoring/filters.py index 0ef802bf..a0d405a7 100644 --- a/openedx_filters/course_authoring/filters.py +++ b/openedx_filters/course_authoring/filters.py @@ -22,7 +22,7 @@ class LMSPageURLRequested(OpenEdxPublicFilter): - Function or Method: get_asset_json """ - filter_type = "org.openedx.course_authoring.lms.page.url.requested.v1" + filter_type: str = "org.openedx.course_authoring.lms.page.url.requested.v1" @classmethod def run_filter(cls, url: str, org: str) -> tuple[str, str]: diff --git a/openedx_filters/exceptions.py b/openedx_filters/exceptions.py index 86af73e7..fe04a218 100644 --- a/openedx_filters/exceptions.py +++ b/openedx_filters/exceptions.py @@ -3,6 +3,9 @@ """ +from typing import Optional + + class OpenEdxFilterException(Exception): """ Base exception for filters. @@ -18,7 +21,13 @@ class OpenEdxFilterException(Exception): exception. """ - def __init__(self, message="", redirect_to=None, status_code=None, **kwargs): + def __init__( + self, + message: Optional[str] = "", + redirect_to: Optional[str] = "", + status_code: Optional[int] = None, + **kwargs + ) -> None: """ Init method for OpenEdxFilterException. @@ -31,7 +40,7 @@ def __init__(self, message="", redirect_to=None, status_code=None, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) - def __str__(self): + def __str__(self) -> str | None: """ Show string representation of OpenEdxFilterException using its message. """ diff --git a/openedx_filters/learning/filters.py b/openedx_filters/learning/filters.py index 67606a5b..2cd747c3 100644 --- a/openedx_filters/learning/filters.py +++ b/openedx_filters/learning/filters.py @@ -33,7 +33,7 @@ class AccountSettingsRenderStarted(OpenEdxPublicFilter): This filter doesn't work alongside the account MFE, only with the legacy account settings page. """ - filter_type = "org.openedx.learning.student.settings.render.started.v1" + filter_type: str = "org.openedx.learning.student.settings.render.started.v1" class RedirectToPage(OpenEdxFilterException): """ @@ -107,7 +107,7 @@ def run_filter(cls, context: dict, template_name: str) -> tuple[dict, str]: - dict: context dictionary for the account settings page, possibly modified. - str: template name to be rendered by the account settings page, possibly modified. """ - data = super().run_pipeline(context=context, template_name=template_name) + data: dict = super().run_pipeline(context=context, template_name=template_name) return data.get("context"), data.get("template_name") @@ -128,8 +128,8 @@ class StudentRegistrationRequested(OpenEdxPublicFilter, SensitiveDataManagementM - Function or Method: RegistrationView.post """ - filter_type = "org.openedx.learning.student.registration.requested.v1" - sensitive_form_data = [ + filter_type: str = "org.openedx.learning.student.registration.requested.v1" + sensitive_form_data: list[str] = [ "password", "newpassword", "new_password", @@ -181,7 +181,7 @@ class StudentLoginRequested(OpenEdxPublicFilter): - Function or Method: login_user """ - filter_type = "org.openedx.learning.student.login.requested.v1" + filter_type: str = "org.openedx.learning.student.login.requested.v1" class PreventLogin(OpenEdxFilterException): """ @@ -238,7 +238,7 @@ class CourseEnrollmentStarted(OpenEdxPublicFilter): - Function or Method: enroll """ - filter_type = "org.openedx.learning.course.enrollment.started.v1" + filter_type: str = "org.openedx.learning.course.enrollment.started.v1" class PreventEnrollment(OpenEdxFilterException): """ @@ -286,7 +286,7 @@ class CourseUnenrollmentStarted(OpenEdxPublicFilter): - Function or Method: unenroll """ - filter_type = "org.openedx.learning.course.unenrollment.started.v1" + filter_type: str = "org.openedx.learning.course.unenrollment.started.v1" class PreventUnenrollment(OpenEdxFilterException): """ @@ -333,7 +333,7 @@ class CertificateCreationRequested(OpenEdxPublicFilter): - Function or Method: _generate_certificate_task """ - filter_type = "org.openedx.learning.certificate.creation.requested.v1" + filter_type:str = "org.openedx.learning.certificate.creation.requested.v1" class PreventCertificateCreation(OpenEdxFilterException): """ @@ -407,7 +407,7 @@ class CertificateRenderStarted(OpenEdxPublicFilter): - Function or Method: render_html_view """ - filter_type = "org.openedx.learning.certificate.render.started.v1" + filter_type: str = "org.openedx.learning.certificate.render.started.v1" class RedirectToPage(OpenEdxFilterException): """ @@ -494,7 +494,7 @@ class CohortChangeRequested(OpenEdxPublicFilter): - Function or Method: assign """ - filter_type = "org.openedx.learning.cohort.change.requested.v1" + filter_type: str = "org.openedx.learning.cohort.change.requested.v1" class PreventCohortChange(OpenEdxFilterException): """ @@ -538,7 +538,7 @@ class CohortAssignmentRequested(OpenEdxPublicFilter): - Function or Method: assign """ - filter_type = "org.openedx.learning.cohort.assignment.requested.v1" + filter_type: str = "org.openedx.learning.cohort.assignment.requested.v1" class PreventCohortAssignment(OpenEdxFilterException): """ @@ -582,7 +582,7 @@ class CourseAboutRenderStarted(OpenEdxPublicFilter): - Function or Method: course_about """ - filter_type = "org.openedx.learning.course_about.render.started.v1" + filter_type: str = "org.openedx.learning.course_about.render.started.v1" class RedirectToPage(OpenEdxFilterException): """ @@ -683,7 +683,7 @@ class DashboardRenderStarted(OpenEdxPublicFilter): This filter doesn't work alongside the dashboard MFE, only with the legacy student dashboard. """ - filter_type = "org.openedx.learning.dashboard.render.started.v1" + filter_type: str = "org.openedx.learning.dashboard.render.started.v1" class RedirectToPage(OpenEdxFilterException): """ @@ -779,7 +779,7 @@ class VerticalBlockChildRenderStarted(OpenEdxPublicFilter): - Function or Method: VerticalBlock._student_or_public_view """ - filter_type = "org.openedx.learning.vertical_block_child.render.started.v1" + filter_type: str = "org.openedx.learning.vertical_block_child.render.started.v1" class PreventChildBlockRender(OpenEdxFilterException): """ @@ -825,7 +825,7 @@ class CourseEnrollmentQuerysetRequested(OpenEdxPublicFilter): marked to be removed if it's not used. See openedx-filters#245 for more information. """ - filter_type = "org.openedx.learning.course_enrollment_queryset.requested.v1" + filter_type: str = "org.openedx.learning.course_enrollment_queryset.requested.v1" class PreventEnrollmentQuerysetRequest(OpenEdxFilterException): """ @@ -864,7 +864,7 @@ class RenderXBlockStarted(OpenEdxPublicFilter): - Function or Method: render_xblock """ - filter_type = "org.openedx.learning.xblock.render.started.v1" + filter_type: str = "org.openedx.learning.xblock.render.started.v1" class PreventXBlockBlockRender(OpenEdxFilterException): """ @@ -925,7 +925,7 @@ class VerticalBlockRenderCompleted(OpenEdxPublicFilter): - Function or Method: VerticalBlock._student_or_public_view """ - filter_type = "org.openedx.learning.vertical_block.render.completed.v1" + filter_type: str = "org.openedx.learning.vertical_block.render.completed.v1" class PreventVerticalBlockRender(OpenEdxFilterException): """ @@ -973,7 +973,7 @@ class CourseHomeUrlCreationStarted(OpenEdxPublicFilter): - Function or Method: course_home_url """ - filter_type = "org.openedx.learning.course.homepage.url.creation.started.v1" + filter_type: str = "org.openedx.learning.course.homepage.url.creation.started.v1" @classmethod def run_filter(cls, course_key: CourseKey, course_home_url: str) -> tuple[CourseKey, str]: @@ -1010,7 +1010,7 @@ class CourseEnrollmentAPIRenderStarted(OpenEdxPublicFilter): - Function or Method: EnrollmentSerializer.to_representation """ - filter_type = "org.openedx.learning.home.enrollment.api.rendered.v1" + filter_type: str = "org.openedx.learning.home.enrollment.api.rendered.v1" @classmethod def run_filter(cls, course_key: CourseKey, serialized_enrollment: dict) -> tuple[CourseKey, dict]: @@ -1047,7 +1047,7 @@ class CourseRunAPIRenderStarted(OpenEdxPublicFilter): - Function or Method: CourseRunSerializer.to_representation """ - filter_type = "org.openedx.learning.home.courserun.api.rendered.started.v1" + filter_type: str = "org.openedx.learning.home.courserun.api.rendered.started.v1" @classmethod def run_filter(cls, serialized_courserun: dict) -> dict: @@ -1081,7 +1081,7 @@ class InstructorDashboardRenderStarted(OpenEdxPublicFilter): - Function or Method: instructor_dashboard_2 """ - filter_type = "org.openedx.learning.instructor.dashboard.render.started.v1" + filter_type: str = "org.openedx.learning.instructor.dashboard.render.started.v1" class RedirectToPage(OpenEdxFilterException): """ @@ -1179,7 +1179,7 @@ class ORASubmissionViewRenderStarted(OpenEdxPublicFilter): - Function or Method: render_submission """ - filter_type = "org.openedx.learning.ora.submission_view.render.started.v1" + filter_type: str = "org.openedx.learning.ora.submission_view.render.started.v1" class RenderInvalidTemplate(OpenEdxFilterException): """ @@ -1237,7 +1237,7 @@ class IDVPageURLRequested(OpenEdxPublicFilter): - Function or Method: XBlockVerificationService.get_verify_location """ - filter_type = "org.openedx.learning.idv.page.url.requested.v1" + filter_type: str = "org.openedx.learning.idv.page.url.requested.v1" @classmethod def run_filter(cls, url: str) -> str: @@ -1271,7 +1271,7 @@ class CourseAboutPageURLRequested(OpenEdxPublicFilter): - Function or Method: get_link_for_about_page """ - filter_type = "org.openedx.learning.course_about.page.url.requested.v1" + filter_type: str = "org.openedx.learning.course_about.page.url.requested.v1" @classmethod def run_filter(cls, url: str, org: str) -> tuple[str, str]: @@ -1309,7 +1309,7 @@ class ScheduleQuerySetRequested(OpenEdxPublicFilter): - Function or Method: BinnedSchedulesBaseResolver.get_schedules_with_target_date_by_bin_and_orgs """ - filter_type = "org.openedx.learning.schedule.queryset.requested.v1" + filter_type: str = "org.openedx.learning.schedule.queryset.requested.v1" @classmethod def run_filter(cls, schedules: QuerySet) -> QuerySet: diff --git a/openedx_filters/tooling.py b/openedx_filters/tooling.py index 5c671efa..27d8cb4a 100644 --- a/openedx_filters/tooling.py +++ b/openedx_filters/tooling.py @@ -2,6 +2,7 @@ Tooling necessary to use Open edX Filters. """ from logging import getLogger +from typing import Any, Optional from django.conf import settings from django.utils.module_loading import import_string @@ -18,14 +19,14 @@ class OpenEdxPublicFilter: filter_type = "" - def __repr__(self): + def __repr__(self) -> str: """ Represent OpenEdxPublicFilter as a string. """ return "".format(filter_type=self.filter_type) @classmethod - def get_steps_for_pipeline(cls, pipeline, fail_silently): + def get_steps_for_pipeline(cls, pipeline: list, fail_silently: Optional[bool] = True) -> list: """ Get pipeline objects from paths. @@ -68,7 +69,7 @@ def get_steps_for_pipeline(cls, pipeline, fail_silently): return step_list @classmethod - def get_pipeline_configuration(cls): + def get_pipeline_configuration(cls) -> tuple[list, bool, dict]: """ Get pipeline configuration from filter settings. @@ -95,9 +96,11 @@ def get_pipeline_configuration(cls): False the opposite. extra_config: anything else defined in the dictionary. """ - filter_config = cls.get_filter_config() + filter_config: dict = cls.get_filter_config() - pipeline, fail_silently, extra_config = [], True, {} + pipeline: list = [] + fail_silently: bool = True + extra_config: dict = {} if not filter_config: return pipeline, fail_silently, extra_config @@ -119,7 +122,7 @@ def get_pipeline_configuration(cls): return pipeline, fail_silently, extra_config @classmethod - def get_filter_config(cls): + def get_filter_config(cls) -> dict: """ Get filters configuration from settings. @@ -161,7 +164,7 @@ def get_filter_config(cls): return filters_config.get(cls.filter_type, {}) @classmethod - def run_pipeline(cls, **kwargs): + def run_pipeline(cls, **kwargs: Any) -> dict: """ Execute filters in order. diff --git a/openedx_filters/utils.py b/openedx_filters/utils.py index 267e9b84..32fd1a18 100644 --- a/openedx_filters/utils.py +++ b/openedx_filters/utils.py @@ -3,6 +3,9 @@ """ +from django.http import QueryDict + + class SensitiveDataManagementMixin: """ Custom class used manage sensitive data within filter arguments. @@ -11,7 +14,7 @@ class SensitiveDataManagementMixin: sensitive_form_data: list[str] = [] @classmethod - def extract_sensitive_data(cls, form_data): + def extract_sensitive_data(cls, form_data: QueryDict) -> dict: """ Extract sensitive data from its child class input arguments. @@ -25,8 +28,8 @@ def extract_sensitive_data(cls, form_data): >> form_data {"username": "example"} """ - sensitive_data = {} - base_form_data = form_data.copy() + sensitive_data: dict = {} + base_form_data: QueryDict = form_data.copy() for key, value in base_form_data.items(): if key in cls.sensitive_form_data: form_data.pop(key) From 93e0105dc5e7c59fbf447e8126f43ed7ed4d9b65 Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Tue, 4 Feb 2025 09:24:44 +0100 Subject: [PATCH 4/6] refactor: drop unnecessary type hints --- openedx_filters/course_authoring/filters.py | 2 +- openedx_filters/exceptions.py | 4 +- openedx_filters/learning/filters.py | 50 ++++++++++----------- openedx_filters/tooling.py | 16 +++---- openedx_filters/utils.py | 6 +-- requirements/dev.txt | 26 ++++++++++- requirements/doc.txt | 11 ++--- requirements/quality.in | 1 + requirements/quality.txt | 26 ++++++++++- requirements/test.txt | 4 +- 10 files changed, 97 insertions(+), 49 deletions(-) diff --git a/openedx_filters/course_authoring/filters.py b/openedx_filters/course_authoring/filters.py index a0d405a7..0ef802bf 100644 --- a/openedx_filters/course_authoring/filters.py +++ b/openedx_filters/course_authoring/filters.py @@ -22,7 +22,7 @@ class LMSPageURLRequested(OpenEdxPublicFilter): - Function or Method: get_asset_json """ - filter_type: str = "org.openedx.course_authoring.lms.page.url.requested.v1" + filter_type = "org.openedx.course_authoring.lms.page.url.requested.v1" @classmethod def run_filter(cls, url: str, org: str) -> tuple[str, str]: diff --git a/openedx_filters/exceptions.py b/openedx_filters/exceptions.py index fe04a218..85677b63 100644 --- a/openedx_filters/exceptions.py +++ b/openedx_filters/exceptions.py @@ -23,8 +23,8 @@ class OpenEdxFilterException(Exception): def __init__( self, - message: Optional[str] = "", - redirect_to: Optional[str] = "", + message: str = "", + redirect_to: str = "", status_code: Optional[int] = None, **kwargs ) -> None: diff --git a/openedx_filters/learning/filters.py b/openedx_filters/learning/filters.py index 2cd747c3..67606a5b 100644 --- a/openedx_filters/learning/filters.py +++ b/openedx_filters/learning/filters.py @@ -33,7 +33,7 @@ class AccountSettingsRenderStarted(OpenEdxPublicFilter): This filter doesn't work alongside the account MFE, only with the legacy account settings page. """ - filter_type: str = "org.openedx.learning.student.settings.render.started.v1" + filter_type = "org.openedx.learning.student.settings.render.started.v1" class RedirectToPage(OpenEdxFilterException): """ @@ -107,7 +107,7 @@ def run_filter(cls, context: dict, template_name: str) -> tuple[dict, str]: - dict: context dictionary for the account settings page, possibly modified. - str: template name to be rendered by the account settings page, possibly modified. """ - data: dict = super().run_pipeline(context=context, template_name=template_name) + data = super().run_pipeline(context=context, template_name=template_name) return data.get("context"), data.get("template_name") @@ -128,8 +128,8 @@ class StudentRegistrationRequested(OpenEdxPublicFilter, SensitiveDataManagementM - Function or Method: RegistrationView.post """ - filter_type: str = "org.openedx.learning.student.registration.requested.v1" - sensitive_form_data: list[str] = [ + filter_type = "org.openedx.learning.student.registration.requested.v1" + sensitive_form_data = [ "password", "newpassword", "new_password", @@ -181,7 +181,7 @@ class StudentLoginRequested(OpenEdxPublicFilter): - Function or Method: login_user """ - filter_type: str = "org.openedx.learning.student.login.requested.v1" + filter_type = "org.openedx.learning.student.login.requested.v1" class PreventLogin(OpenEdxFilterException): """ @@ -238,7 +238,7 @@ class CourseEnrollmentStarted(OpenEdxPublicFilter): - Function or Method: enroll """ - filter_type: str = "org.openedx.learning.course.enrollment.started.v1" + filter_type = "org.openedx.learning.course.enrollment.started.v1" class PreventEnrollment(OpenEdxFilterException): """ @@ -286,7 +286,7 @@ class CourseUnenrollmentStarted(OpenEdxPublicFilter): - Function or Method: unenroll """ - filter_type: str = "org.openedx.learning.course.unenrollment.started.v1" + filter_type = "org.openedx.learning.course.unenrollment.started.v1" class PreventUnenrollment(OpenEdxFilterException): """ @@ -333,7 +333,7 @@ class CertificateCreationRequested(OpenEdxPublicFilter): - Function or Method: _generate_certificate_task """ - filter_type:str = "org.openedx.learning.certificate.creation.requested.v1" + filter_type = "org.openedx.learning.certificate.creation.requested.v1" class PreventCertificateCreation(OpenEdxFilterException): """ @@ -407,7 +407,7 @@ class CertificateRenderStarted(OpenEdxPublicFilter): - Function or Method: render_html_view """ - filter_type: str = "org.openedx.learning.certificate.render.started.v1" + filter_type = "org.openedx.learning.certificate.render.started.v1" class RedirectToPage(OpenEdxFilterException): """ @@ -494,7 +494,7 @@ class CohortChangeRequested(OpenEdxPublicFilter): - Function or Method: assign """ - filter_type: str = "org.openedx.learning.cohort.change.requested.v1" + filter_type = "org.openedx.learning.cohort.change.requested.v1" class PreventCohortChange(OpenEdxFilterException): """ @@ -538,7 +538,7 @@ class CohortAssignmentRequested(OpenEdxPublicFilter): - Function or Method: assign """ - filter_type: str = "org.openedx.learning.cohort.assignment.requested.v1" + filter_type = "org.openedx.learning.cohort.assignment.requested.v1" class PreventCohortAssignment(OpenEdxFilterException): """ @@ -582,7 +582,7 @@ class CourseAboutRenderStarted(OpenEdxPublicFilter): - Function or Method: course_about """ - filter_type: str = "org.openedx.learning.course_about.render.started.v1" + filter_type = "org.openedx.learning.course_about.render.started.v1" class RedirectToPage(OpenEdxFilterException): """ @@ -683,7 +683,7 @@ class DashboardRenderStarted(OpenEdxPublicFilter): This filter doesn't work alongside the dashboard MFE, only with the legacy student dashboard. """ - filter_type: str = "org.openedx.learning.dashboard.render.started.v1" + filter_type = "org.openedx.learning.dashboard.render.started.v1" class RedirectToPage(OpenEdxFilterException): """ @@ -779,7 +779,7 @@ class VerticalBlockChildRenderStarted(OpenEdxPublicFilter): - Function or Method: VerticalBlock._student_or_public_view """ - filter_type: str = "org.openedx.learning.vertical_block_child.render.started.v1" + filter_type = "org.openedx.learning.vertical_block_child.render.started.v1" class PreventChildBlockRender(OpenEdxFilterException): """ @@ -825,7 +825,7 @@ class CourseEnrollmentQuerysetRequested(OpenEdxPublicFilter): marked to be removed if it's not used. See openedx-filters#245 for more information. """ - filter_type: str = "org.openedx.learning.course_enrollment_queryset.requested.v1" + filter_type = "org.openedx.learning.course_enrollment_queryset.requested.v1" class PreventEnrollmentQuerysetRequest(OpenEdxFilterException): """ @@ -864,7 +864,7 @@ class RenderXBlockStarted(OpenEdxPublicFilter): - Function or Method: render_xblock """ - filter_type: str = "org.openedx.learning.xblock.render.started.v1" + filter_type = "org.openedx.learning.xblock.render.started.v1" class PreventXBlockBlockRender(OpenEdxFilterException): """ @@ -925,7 +925,7 @@ class VerticalBlockRenderCompleted(OpenEdxPublicFilter): - Function or Method: VerticalBlock._student_or_public_view """ - filter_type: str = "org.openedx.learning.vertical_block.render.completed.v1" + filter_type = "org.openedx.learning.vertical_block.render.completed.v1" class PreventVerticalBlockRender(OpenEdxFilterException): """ @@ -973,7 +973,7 @@ class CourseHomeUrlCreationStarted(OpenEdxPublicFilter): - Function or Method: course_home_url """ - filter_type: str = "org.openedx.learning.course.homepage.url.creation.started.v1" + filter_type = "org.openedx.learning.course.homepage.url.creation.started.v1" @classmethod def run_filter(cls, course_key: CourseKey, course_home_url: str) -> tuple[CourseKey, str]: @@ -1010,7 +1010,7 @@ class CourseEnrollmentAPIRenderStarted(OpenEdxPublicFilter): - Function or Method: EnrollmentSerializer.to_representation """ - filter_type: str = "org.openedx.learning.home.enrollment.api.rendered.v1" + filter_type = "org.openedx.learning.home.enrollment.api.rendered.v1" @classmethod def run_filter(cls, course_key: CourseKey, serialized_enrollment: dict) -> tuple[CourseKey, dict]: @@ -1047,7 +1047,7 @@ class CourseRunAPIRenderStarted(OpenEdxPublicFilter): - Function or Method: CourseRunSerializer.to_representation """ - filter_type: str = "org.openedx.learning.home.courserun.api.rendered.started.v1" + filter_type = "org.openedx.learning.home.courserun.api.rendered.started.v1" @classmethod def run_filter(cls, serialized_courserun: dict) -> dict: @@ -1081,7 +1081,7 @@ class InstructorDashboardRenderStarted(OpenEdxPublicFilter): - Function or Method: instructor_dashboard_2 """ - filter_type: str = "org.openedx.learning.instructor.dashboard.render.started.v1" + filter_type = "org.openedx.learning.instructor.dashboard.render.started.v1" class RedirectToPage(OpenEdxFilterException): """ @@ -1179,7 +1179,7 @@ class ORASubmissionViewRenderStarted(OpenEdxPublicFilter): - Function or Method: render_submission """ - filter_type: str = "org.openedx.learning.ora.submission_view.render.started.v1" + filter_type = "org.openedx.learning.ora.submission_view.render.started.v1" class RenderInvalidTemplate(OpenEdxFilterException): """ @@ -1237,7 +1237,7 @@ class IDVPageURLRequested(OpenEdxPublicFilter): - Function or Method: XBlockVerificationService.get_verify_location """ - filter_type: str = "org.openedx.learning.idv.page.url.requested.v1" + filter_type = "org.openedx.learning.idv.page.url.requested.v1" @classmethod def run_filter(cls, url: str) -> str: @@ -1271,7 +1271,7 @@ class CourseAboutPageURLRequested(OpenEdxPublicFilter): - Function or Method: get_link_for_about_page """ - filter_type: str = "org.openedx.learning.course_about.page.url.requested.v1" + filter_type = "org.openedx.learning.course_about.page.url.requested.v1" @classmethod def run_filter(cls, url: str, org: str) -> tuple[str, str]: @@ -1309,7 +1309,7 @@ class ScheduleQuerySetRequested(OpenEdxPublicFilter): - Function or Method: BinnedSchedulesBaseResolver.get_schedules_with_target_date_by_bin_and_orgs """ - filter_type: str = "org.openedx.learning.schedule.queryset.requested.v1" + filter_type = "org.openedx.learning.schedule.queryset.requested.v1" @classmethod def run_filter(cls, schedules: QuerySet) -> QuerySet: diff --git a/openedx_filters/tooling.py b/openedx_filters/tooling.py index 27d8cb4a..576d3f79 100644 --- a/openedx_filters/tooling.py +++ b/openedx_filters/tooling.py @@ -2,7 +2,7 @@ Tooling necessary to use Open edX Filters. """ from logging import getLogger -from typing import Any, Optional +from typing import Any, Union from django.conf import settings from django.utils.module_loading import import_string @@ -26,7 +26,7 @@ def __repr__(self) -> str: return "".format(filter_type=self.filter_type) @classmethod - def get_steps_for_pipeline(cls, pipeline: list, fail_silently: Optional[bool] = True) -> list: + def get_steps_for_pipeline(cls, pipeline: list, fail_silently: bool = True) -> list: """ Get pipeline objects from paths. @@ -96,11 +96,9 @@ def get_pipeline_configuration(cls) -> tuple[list, bool, dict]: False the opposite. extra_config: anything else defined in the dictionary. """ - filter_config: dict = cls.get_filter_config() + filter_config = cls.get_filter_config() - pipeline: list = [] - fail_silently: bool = True - extra_config: dict = {} + pipeline, fail_silently, extra_config = [], True, {} if not filter_config: return pipeline, fail_silently, extra_config @@ -201,12 +199,14 @@ def run_pipeline(cls, **kwargs: Any) -> dict: information check their Github repository: https://github.com/python-social-auth/social-core """ - pipeline, fail_silently, extra_config = cls.get_pipeline_configuration() + pipeline: list[str] = [] + fail_silently: bool = True + extra_config: dict[str, Any] = {} if not pipeline: return kwargs - steps = cls.get_steps_for_pipeline(pipeline, fail_silently) + steps: list[] = cls.get_steps_for_pipeline(pipeline, fail_silently) filter_metadata = { "filter_type": cls.filter_type, "running_pipeline": pipeline, diff --git a/openedx_filters/utils.py b/openedx_filters/utils.py index 32fd1a18..c4996a58 100644 --- a/openedx_filters/utils.py +++ b/openedx_filters/utils.py @@ -11,7 +11,7 @@ class SensitiveDataManagementMixin: Custom class used manage sensitive data within filter arguments. """ - sensitive_form_data: list[str] = [] + sensitive_form_data = [] @classmethod def extract_sensitive_data(cls, form_data: QueryDict) -> dict: @@ -28,8 +28,8 @@ def extract_sensitive_data(cls, form_data: QueryDict) -> dict: >> form_data {"username": "example"} """ - sensitive_data: dict = {} - base_form_data: QueryDict = form_data.copy() + sensitive_data = {} + base_form_data = form_data.copy() for key, value in base_form_data.items(): if key in cls.sensitive_form_data: form_data.pop(key) diff --git a/requirements/dev.txt b/requirements/dev.txt index cae6f46f..4b58d26c 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -8,6 +8,7 @@ asgiref==3.8.1 # via # -r requirements/quality.txt # django + # django-stubs astroid==3.3.8 # via # -r requirements/quality.txt @@ -25,7 +26,7 @@ cachetools==5.5.1 # via # -r requirements/ci.txt # tox -certifi==2024.12.14 +certifi==2025.1.31 # via # -r requirements/quality.txt # requests @@ -72,7 +73,7 @@ cryptography==44.0.0 # secretstorage ddt==1.7.2 # via -r requirements/quality.txt -diff-cover==9.2.1 +diff-cover==9.2.2 # via -r requirements/dev.in dill==0.3.9 # via @@ -86,6 +87,14 @@ django==4.2.18 # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/quality.txt + # django-stubs + # django-stubs-ext +django-stubs==5.1.2 + # via -r requirements/quality.txt +django-stubs-ext==5.1.2 + # via + # -r requirements/quality.txt + # django-stubs dnspython==2.7.0 # via # -r requirements/quality.txt @@ -170,6 +179,12 @@ more-itertools==10.6.0 # -r requirements/quality.txt # jaraco-classes # jaraco-functools +mypy==1.14.1 + # via -r requirements/quality.txt +mypy-extensions==1.0.0 + # via + # -r requirements/quality.txt + # mypy nh3==0.2.20 # via # -r requirements/quality.txt @@ -323,10 +338,17 @@ tox==4.24.1 # via -r requirements/ci.txt twine==6.1.0 # via -r requirements/quality.txt +types-pyyaml==6.0.12.20241230 + # via + # -r requirements/quality.txt + # django-stubs typing-extensions==4.12.2 # via # -r requirements/quality.txt + # django-stubs + # django-stubs-ext # edx-opaque-keys + # mypy urllib3==2.2.3 # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt diff --git a/requirements/doc.txt b/requirements/doc.txt index dd7b0d2e..d2258328 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -17,17 +17,17 @@ asgiref==3.8.1 # -r requirements/test.txt # django # django-stubs -babel==2.16.0 +babel==2.17.0 # via # pydata-sphinx-theme # sphinx backports-tarfile==1.2.0 # via jaraco-context -beautifulsoup4==4.12.3 +beautifulsoup4==4.13.1 # via pydata-sphinx-theme build==1.2.2.post1 # via -r requirements/doc.in -certifi==2024.12.14 +certifi==2025.1.31 # via requests cffi==1.17.1 # via cryptography @@ -56,9 +56,9 @@ django==4.2.18 # -r requirements/test.txt # django-stubs # django-stubs-ext -django-stubs==5.1.1 +django-stubs==5.1.2 # via -r requirements/test.txt -django-stubs-ext==5.1.1 +django-stubs-ext==5.1.2 # via # -r requirements/test.txt # django-stubs @@ -262,6 +262,7 @@ typing-extensions==4.12.2 # via # -r requirements/test.txt # anyio + # beautifulsoup4 # django-stubs # django-stubs-ext # edx-opaque-keys diff --git a/requirements/quality.in b/requirements/quality.in index d4773865..a59b20fd 100644 --- a/requirements/quality.in +++ b/requirements/quality.in @@ -8,3 +8,4 @@ isort # to standardize order of imports pycodestyle # PEP 8 compliance validation pydocstyle # PEP 257 compliance validation twine # Utility for publishing Python packages on PyPI. +mypy # Static type checker diff --git a/requirements/quality.txt b/requirements/quality.txt index 455b1d0e..4b77d5f8 100644 --- a/requirements/quality.txt +++ b/requirements/quality.txt @@ -8,13 +8,14 @@ asgiref==3.8.1 # via # -r requirements/test.txt # django + # django-stubs astroid==3.3.8 # via # pylint # pylint-celery backports-tarfile==1.2.0 # via jaraco-context -certifi==2024.12.14 +certifi==2025.1.31 # via requests cffi==1.17.1 # via cryptography @@ -46,6 +47,14 @@ django==4.2.18 # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt # -r requirements/test.txt + # django-stubs + # django-stubs-ext +django-stubs==5.1.2 + # via -r requirements/test.txt +django-stubs-ext==5.1.2 + # via + # -r requirements/test.txt + # django-stubs dnspython==2.7.0 # via # -r requirements/test.txt @@ -100,6 +109,14 @@ more-itertools==10.6.0 # via # jaraco-classes # jaraco-functools +mypy==1.14.1 + # via + # -r requirements/quality.in + # -r requirements/test.txt +mypy-extensions==1.0.0 + # via + # -r requirements/test.txt + # mypy nh3==0.2.20 # via readme-renderer packaging==24.2 @@ -198,10 +215,17 @@ tomlkit==0.13.2 # via pylint twine==6.1.0 # via -r requirements/quality.in +types-pyyaml==6.0.12.20241230 + # via + # -r requirements/test.txt + # django-stubs typing-extensions==4.12.2 # via # -r requirements/test.txt + # django-stubs + # django-stubs-ext # edx-opaque-keys + # mypy urllib3==2.2.3 # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt diff --git a/requirements/test.txt b/requirements/test.txt index 302db73a..92f05dee 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -23,9 +23,9 @@ django==4.2.18 # -r requirements/base.txt # django-stubs # django-stubs-ext -django-stubs==5.1.1 +django-stubs==5.1.2 # via -r requirements/test.in -django-stubs-ext==5.1.1 +django-stubs-ext==5.1.2 # via django-stubs dnspython==2.7.0 # via From b68520099e9c537d8a9f947d2e6df8461397d68d Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Tue, 4 Feb 2025 11:08:41 +0100 Subject: [PATCH 5/6] refactor: address quality issues with mypy and filter signatures --- openedx_filters/course_authoring/filters.py | 2 +- openedx_filters/exceptions.py | 2 +- openedx_filters/learning/filters.py | 56 +++++++++++++-------- openedx_filters/tooling.py | 20 ++++---- openedx_filters/utils.py | 9 ++-- 5 files changed, 52 insertions(+), 37 deletions(-) diff --git a/openedx_filters/course_authoring/filters.py b/openedx_filters/course_authoring/filters.py index 0ef802bf..22b38f69 100644 --- a/openedx_filters/course_authoring/filters.py +++ b/openedx_filters/course_authoring/filters.py @@ -25,7 +25,7 @@ class LMSPageURLRequested(OpenEdxPublicFilter): filter_type = "org.openedx.course_authoring.lms.page.url.requested.v1" @classmethod - def run_filter(cls, url: str, org: str) -> tuple[str, str]: + def run_filter(cls, url: str, org: str) -> tuple[str | None, str | None]: """ Process the inputs using the configured pipeline steps to modify the URL of the page requested by the user. diff --git a/openedx_filters/exceptions.py b/openedx_filters/exceptions.py index 85677b63..70087b22 100644 --- a/openedx_filters/exceptions.py +++ b/openedx_filters/exceptions.py @@ -40,7 +40,7 @@ def __init__( for key, value in kwargs.items(): setattr(self, key, value) - def __str__(self) -> str | None: + def __str__(self) -> str: """ Show string representation of OpenEdxFilterException using its message. """ diff --git a/openedx_filters/learning/filters.py b/openedx_filters/learning/filters.py index 67606a5b..3f130ebb 100644 --- a/openedx_filters/learning/filters.py +++ b/openedx_filters/learning/filters.py @@ -2,7 +2,7 @@ Package where filters related to the learning architectural subdomain are implemented. """ -from typing import Any, Optional +from typing import Any, Optional, Union from django.db.models.query import QuerySet from django.http import HttpResponse, QueryDict @@ -94,7 +94,7 @@ def __init__(self, message: str, response: Optional[HttpResponse] = None) -> Non super().__init__(message, response=response) @classmethod - def run_filter(cls, context: dict, template_name: str) -> tuple[dict, str]: + def run_filter(cls, context: dict[str, Any], template_name: str) -> tuple[dict[str, Any] | None, str | None]: """ Process the input context and template_name using the configured pipeline steps to modify the account settings. @@ -159,7 +159,7 @@ def run_filter(cls, form_data: QueryDict) -> QueryDict: """ sensitive_data = cls.extract_sensitive_data(form_data) data = super().run_pipeline(form_data=form_data) - form_data = data.get("form_data") + form_data = data.get("form_data", QueryDict()) form_data.update(sensitive_data) return form_data @@ -249,7 +249,7 @@ class PreventEnrollment(OpenEdxFilterException): """ @classmethod - def run_filter(cls, user: Any, course_key: CourseKey, mode: str) -> tuple[Any, CourseKey, str]: + def run_filter(cls, user: Any, course_key: CourseKey, mode: str) -> tuple[Any, CourseKey | None, str | None]: """ Process the user, course_key, and mode using the configured pipeline steps to modify the enrollment process. @@ -345,14 +345,14 @@ class PreventCertificateCreation(OpenEdxFilterException): @classmethod def run_filter( # pylint: disable=too-many-positional-arguments - cls: type, + cls, user: Any, course_key: CourseKey, mode: str, status: str, grade: float, generation_mode: str, - ) -> tuple[Any, CourseKey, str, str, float, str]: + ) -> tuple[Any, CourseKey | None, str | None, str | None, float | None, str | None]: """ Process the inputs using the configured pipeline steps to modify the certificate creation process. @@ -460,7 +460,7 @@ def __init__(self, message: str, response: HttpResponse) -> None: ) @classmethod - def run_filter(cls, context: dict, custom_template: Any) -> tuple[dict, Any]: + def run_filter(cls, context: dict, custom_template: Any) -> tuple[dict[str, Any] | None, Any]: """ Process the context and custom_template using the configured pipeline steps to modify the certificate rendering. @@ -646,7 +646,7 @@ def __init__(self, message: str, response: HttpResponse) -> None: ) @classmethod - def run_filter(cls, context: dict, template_name: str) -> tuple[dict, str]: + def run_filter(cls, context: dict[str, Any], template_name: str) -> tuple[dict[str, Any] | None, str | None]: """ Process the context and template_name using the configured pipeline steps to modify the course about rendering. @@ -745,7 +745,7 @@ def __init__(self, message: str, response: Optional[HttpResponse] = None) -> Non ) @classmethod - def run_filter(cls, context: dict, template_name: str) -> tuple[dict, str]: + def run_filter(cls, context: dict[str, Any], template_name: str) -> tuple[dict[str, Any] | None, str | None]: """ Process the context and template_name using the configured pipeline steps to modify the dashboard rendering. @@ -790,7 +790,7 @@ class PreventChildBlockRender(OpenEdxFilterException): """ @classmethod - def run_filter(cls, block: Any, context: dict) -> tuple[Any, dict]: + def run_filter(cls, block: Any, context: dict[str, Any]) -> tuple[Any, dict[str, Any] | None]: """ Process the block and context using the configured pipeline steps to modify the rendering of a child block. @@ -833,7 +833,7 @@ class PreventEnrollmentQuerysetRequest(OpenEdxFilterException): """ @classmethod - def run_filter(cls, enrollments: QuerySet) -> QuerySet: + def run_filter(cls, enrollments: QuerySet) -> QuerySet | None: """ Process the enrollments QuerySet using the configured pipeline steps to modify the course enrollment data. @@ -891,7 +891,11 @@ def __init__(self, message: str, response: Optional[HttpResponse] = None): super().__init__(message, response=response) @classmethod - def run_filter(cls, context: dict, student_view_context: dict): + def run_filter( + cls, + context: dict[str, Any], + student_view_context: dict + ) -> tuple[dict[str, Any] | None, dict[str, Any] | None]: """ Process the inputs using the configured pipeline steps to modify the rendering of an XBlock. @@ -936,7 +940,13 @@ class PreventVerticalBlockRender(OpenEdxFilterException): """ @classmethod - def run_filter(cls, block: Any, fragment: Any, context: dict, view: str) -> tuple[Any, Any, dict, str]: + def run_filter( + cls, + block: Any, + fragment: Any, + context: dict[str, Any], + view: str + ) -> tuple[Any, Any, dict[str, Any] | None, str | None]: """ Process the inputs using the configured pipeline steps to modify the rendering of a vertical block. @@ -976,7 +986,7 @@ class CourseHomeUrlCreationStarted(OpenEdxPublicFilter): filter_type = "org.openedx.learning.course.homepage.url.creation.started.v1" @classmethod - def run_filter(cls, course_key: CourseKey, course_home_url: str) -> tuple[CourseKey, str]: + def run_filter(cls, course_key: CourseKey, course_home_url: str) -> tuple[CourseKey | None, str | None]: """ Process the course_key and course_home_url using the configured pipeline steps to modify the course home url. @@ -1013,7 +1023,11 @@ class CourseEnrollmentAPIRenderStarted(OpenEdxPublicFilter): filter_type = "org.openedx.learning.home.enrollment.api.rendered.v1" @classmethod - def run_filter(cls, course_key: CourseKey, serialized_enrollment: dict) -> tuple[CourseKey, dict]: + def run_filter( + cls, + course_key: CourseKey, + serialized_enrollment: dict[str, Any] + ) -> tuple[CourseKey | None, dict[str, Any] | None]: """ Process the inputs using the configured pipeline steps to modify the course enrollment data. @@ -1050,7 +1064,7 @@ class CourseRunAPIRenderStarted(OpenEdxPublicFilter): filter_type = "org.openedx.learning.home.courserun.api.rendered.started.v1" @classmethod - def run_filter(cls, serialized_courserun: dict) -> dict: + def run_filter(cls, serialized_courserun: dict[str, Any]) -> dict[str, Any] | None: """ Process the serialized_courserun using the configured pipeline steps to modify the course run data. @@ -1145,7 +1159,7 @@ def __init__(self, message: str, response: Optional[HttpResponse] = None): ) @classmethod - def run_filter(cls, context: dict, template_name: str) -> tuple[dict, str]: + def run_filter(cls, context: dict[str, Any], template_name: str) -> tuple[dict[str, Any] | None, str | None]: """ Process the context and template_name using the configured pipeline steps to modify the instructor dashboard. @@ -1203,7 +1217,7 @@ def __init__( super().__init__(message, context=context, template_name=template_name) @classmethod - def run_filter(cls, context: dict, template_name: str) -> tuple[dict, str]: + def run_filter(cls, context: dict[str, Any], template_name: str) -> tuple[dict[str, Any] | None, str | None]: """ Process the context and template_name using the configured pipeline steps to modify the submission view. @@ -1240,7 +1254,7 @@ class IDVPageURLRequested(OpenEdxPublicFilter): filter_type = "org.openedx.learning.idv.page.url.requested.v1" @classmethod - def run_filter(cls, url: str) -> str: + def run_filter(cls, url: str) -> str | None: """ Process the URL using the configured pipeline steps to modify the ID verification page URL. @@ -1274,7 +1288,7 @@ class CourseAboutPageURLRequested(OpenEdxPublicFilter): filter_type = "org.openedx.learning.course_about.page.url.requested.v1" @classmethod - def run_filter(cls, url: str, org: str) -> tuple[str, str]: + def run_filter(cls, url: str, org: str) -> tuple[str | None, str | None]: """ Process the URL and org using the configured pipeline steps to modify the course about page URL. @@ -1312,7 +1326,7 @@ class ScheduleQuerySetRequested(OpenEdxPublicFilter): filter_type = "org.openedx.learning.schedule.queryset.requested.v1" @classmethod - def run_filter(cls, schedules: QuerySet) -> QuerySet: + def run_filter(cls, schedules: QuerySet) -> QuerySet | None: """ Process the schedules QuerySet using the configured pipeline steps to modify the schedules data. diff --git a/openedx_filters/tooling.py b/openedx_filters/tooling.py index 576d3f79..3a548d20 100644 --- a/openedx_filters/tooling.py +++ b/openedx_filters/tooling.py @@ -2,7 +2,7 @@ Tooling necessary to use Open edX Filters. """ from logging import getLogger -from typing import Any, Union +from typing import Any from django.conf import settings from django.utils.module_loading import import_string @@ -26,7 +26,7 @@ def __repr__(self) -> str: return "".format(filter_type=self.filter_type) @classmethod - def get_steps_for_pipeline(cls, pipeline: list, fail_silently: bool = True) -> list: + def get_steps_for_pipeline(cls, pipeline: list, fail_silently: bool = True) -> list[type]: """ Get pipeline objects from paths. @@ -69,7 +69,7 @@ def get_steps_for_pipeline(cls, pipeline: list, fail_silently: bool = True) -> l return step_list @classmethod - def get_pipeline_configuration(cls) -> tuple[list, bool, dict]: + def get_pipeline_configuration(cls) -> tuple[list[str], bool, dict[str, Any]]: """ Get pipeline configuration from filter settings. @@ -98,7 +98,9 @@ def get_pipeline_configuration(cls) -> tuple[list, bool, dict]: """ filter_config = cls.get_filter_config() - pipeline, fail_silently, extra_config = [], True, {} + pipeline: list = [] + fail_silently: bool = True + extra_config: dict = {} if not filter_config: return pipeline, fail_silently, extra_config @@ -120,7 +122,7 @@ def get_pipeline_configuration(cls) -> tuple[list, bool, dict]: return pipeline, fail_silently, extra_config @classmethod - def get_filter_config(cls) -> dict: + def get_filter_config(cls) -> dict[str, Any]: """ Get filters configuration from settings. @@ -162,7 +164,7 @@ def get_filter_config(cls) -> dict: return filters_config.get(cls.filter_type, {}) @classmethod - def run_pipeline(cls, **kwargs: Any) -> dict: + def run_pipeline(cls, **kwargs: Any) -> dict[str, Any] | Any: """ Execute filters in order. @@ -199,14 +201,12 @@ def run_pipeline(cls, **kwargs: Any) -> dict: information check their Github repository: https://github.com/python-social-auth/social-core """ - pipeline: list[str] = [] - fail_silently: bool = True - extra_config: dict[str, Any] = {} + pipeline, fail_silently, extra_config = cls.get_pipeline_configuration() if not pipeline: return kwargs - steps: list[] = cls.get_steps_for_pipeline(pipeline, fail_silently) + steps = cls.get_steps_for_pipeline(pipeline, fail_silently) filter_metadata = { "filter_type": cls.filter_type, "running_pipeline": pipeline, diff --git a/openedx_filters/utils.py b/openedx_filters/utils.py index c4996a58..fabd7c05 100644 --- a/openedx_filters/utils.py +++ b/openedx_filters/utils.py @@ -3,6 +3,7 @@ """ +from typing import Union from django.http import QueryDict @@ -11,10 +12,10 @@ class SensitiveDataManagementMixin: Custom class used manage sensitive data within filter arguments. """ - sensitive_form_data = [] + sensitive_form_data: list[str] = [] @classmethod - def extract_sensitive_data(cls, form_data: QueryDict) -> dict: + def extract_sensitive_data(cls, form_data: QueryDict) -> dict[str, str]: """ Extract sensitive data from its child class input arguments. @@ -29,10 +30,10 @@ def extract_sensitive_data(cls, form_data: QueryDict) -> dict: {"username": "example"} """ sensitive_data = {} - base_form_data = form_data.copy() + base_form_data = form_data.copy() for key, value in base_form_data.items(): if key in cls.sensitive_form_data: form_data.pop(key) - sensitive_data[key] = value + sensitive_data[key] = str(value) return sensitive_data From ea1b74b519aa992a16709fa42354bf69f86e9e5c Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Tue, 4 Feb 2025 11:15:13 +0100 Subject: [PATCH 6/6] refactor: address quality issues --- openedx_filters/learning/filters.py | 2 +- openedx_filters/utils.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/openedx_filters/learning/filters.py b/openedx_filters/learning/filters.py index 3f130ebb..fb315bdc 100644 --- a/openedx_filters/learning/filters.py +++ b/openedx_filters/learning/filters.py @@ -2,7 +2,7 @@ Package where filters related to the learning architectural subdomain are implemented. """ -from typing import Any, Optional, Union +from typing import Any, Optional from django.db.models.query import QuerySet from django.http import HttpResponse, QueryDict diff --git a/openedx_filters/utils.py b/openedx_filters/utils.py index fabd7c05..f17080bf 100644 --- a/openedx_filters/utils.py +++ b/openedx_filters/utils.py @@ -3,7 +3,6 @@ """ -from typing import Union from django.http import QueryDict @@ -30,7 +29,7 @@ def extract_sensitive_data(cls, form_data: QueryDict) -> dict[str, str]: {"username": "example"} """ sensitive_data = {} - base_form_data = form_data.copy() + base_form_data = form_data.copy() for key, value in base_form_data.items(): if key in cls.sensitive_form_data: form_data.pop(key)