From 41289096bf72ed80c01ed2691a903b61b2764c13 Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Mon, 13 Jan 2025 09:45:46 +0100 Subject: [PATCH 01/11] feat: add linter for openedx-filters classes definitions --- edx_lint/pylint/filters_docstring/__init__.py | 10 ++ .../filters_docstring_check.py | 106 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 edx_lint/pylint/filters_docstring/__init__.py create mode 100644 edx_lint/pylint/filters_docstring/filters_docstring_check.py diff --git a/edx_lint/pylint/filters_docstring/__init__.py b/edx_lint/pylint/filters_docstring/__init__.py new file mode 100644 index 0000000..588ee6e --- /dev/null +++ b/edx_lint/pylint/filters_docstring/__init__.py @@ -0,0 +1,10 @@ +""" +edx_lint filters_docstring module (optional plugin for filters docstrings). + +Add this to your pylintrc:: + load-plugins=edx_lint.pylint.filters_docstring +""" + +from .filters_docstring_check import register_checkers + +register = register_checkers diff --git a/edx_lint/pylint/filters_docstring/filters_docstring_check.py b/edx_lint/pylint/filters_docstring/filters_docstring_check.py new file mode 100644 index 0000000..2c2dfad --- /dev/null +++ b/edx_lint/pylint/filters_docstring/filters_docstring_check.py @@ -0,0 +1,106 @@ +""" +Pylint checker for the format of the docstrings of filters. + +A filter's docstring should have the following structure: + +1. Description: Any non-empty text followed by a blank line. +2. Filter Type: A line that starts with "Filter Type:". +3. Trigger: A line that starts with "Trigger:". +""" + +from pylint.checkers import BaseChecker, utils +import re +from ..common import BASE_ID + + +def register_checkers(linter): + """ + Register checkers. + """ + linter.register_checker(FiltersDocstringFormatChecker(linter)) + + +class FiltersDocstringFormatChecker(BaseChecker): + """Pylint checker for the format of the docstrings of filters.""" + + name = "docstring-format-checker" + + FILTER_DOCSTRING_MISSING_DESCRIPTION = "filter-docstring-missing-description" + FILTER_DOCSTRING_MISSING_TYPE = "filter-docstring-missing-type" + FILTER_DOCSTRING_MISSING_TRIGGER = "filter-docstring-missing-trigger" + + msgs = { + ("E%d90" % BASE_ID): ( + "Filter's (%s) docstring is missing the required description section", + FILTER_DOCSTRING_MISSING_DESCRIPTION, + "filters docstring is missing the required description section", + ), + ("E%d91" % BASE_ID): ( + "Filter's (%s) docstring is missing the required filter type section", + FILTER_DOCSTRING_MISSING_TYPE, + "filters docstring is missing the required filter type section", + ), + ("E%d92" % BASE_ID): ( + "Filter's (%s) docstring is missing the required trigger section", + FILTER_DOCSTRING_MISSING_TRIGGER, + "filters docstring is missing the required trigger section", + ), + } + + options = () + + @utils.only_required_for_messages( + FILTER_DOCSTRING_MISSING_DESCRIPTION, + FILTER_DOCSTRING_MISSING_TYPE, + FILTER_DOCSTRING_MISSING_TRIGGER, + ) + def visit_classdef(self, node): + """Visit a class definition and check its docstring.""" + if not node.is_subtype_of("openedx_filters.tooling.OpenEdxPublicFilter"): + return + + docstring = node.doc_node.value if node.doc_node else "" + if not (error_messages := self._check_docstring_format(docstring)): + return + for error_message in error_messages: + self.add_message(error_message, node=node, args=(node.name,)) + + def _check_docstring_format(self, docstring): + """ + Check the format of the docstring for errors and return a list of error messages. + + The docstring should have the following structure: + 1. Description: Any non-empty text followed by a blank line. + 2. Filter Type: A line that starts with "Filter Type:". + 3. Trigger: A line that starts with "Trigger:". + + For example: + + ``` + Description: + Filter used to modify the certificate rendering process. + + ... (more description) + + Filter Type: + org.openedx.learning.certificate.render.started.v1 + + Trigger: + - Repository: openedx/edx-platform + - Path: lms/djangoapps/certificates/views/webview.py + - Function or Method: render_html_view + ``` + """ + required_sections = [ + (r"Description:\s*.*\n", self.FILTER_DOCSTRING_MISSING_DESCRIPTION), + (r"Filter Type:\s*.*\n", self.FILTER_DOCSTRING_MISSING_TYPE), + ( + r"Trigger:\s*(NA|-\s*Repository:\s*[^\n]+\s*-\s*Path:\s*[^\n]+\s*-\s*Function\s*or\s*Method:\s*[^\n]+)", + self.FILTER_DOCSTRING_MISSING_TRIGGER, + ), + ] + error_messages = [] + for pattern, error_message in required_sections: + if not re.search(pattern, docstring, re.MULTILINE): + error_messages.append(error_message) + return error_messages From b0d2c023741b6c7d6731b053e134bf4128eff891 Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Mon, 13 Jan 2025 12:11:35 +0100 Subject: [PATCH 02/11] refactor: address quality errors --- .../pylint/filters_docstring/filters_docstring_check.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/edx_lint/pylint/filters_docstring/filters_docstring_check.py b/edx_lint/pylint/filters_docstring/filters_docstring_check.py index 2c2dfad..20c283a 100644 --- a/edx_lint/pylint/filters_docstring/filters_docstring_check.py +++ b/edx_lint/pylint/filters_docstring/filters_docstring_check.py @@ -8,9 +8,11 @@ 3. Trigger: A line that starts with "Trigger:". """ -from pylint.checkers import BaseChecker, utils import re -from ..common import BASE_ID + +from pylint.checkers import BaseChecker, utils + +from edx_lint.pylint.common import BASE_ID def register_checkers(linter): From 7a0545488b525c34ff06f8b6938f45640ebbb1ad Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Mon, 13 Jan 2025 12:22:56 +0100 Subject: [PATCH 03/11] refactor: drop filter prefix from variables names --- .../filters_docstring_check.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/edx_lint/pylint/filters_docstring/filters_docstring_check.py b/edx_lint/pylint/filters_docstring/filters_docstring_check.py index 20c283a..9aaf50c 100644 --- a/edx_lint/pylint/filters_docstring/filters_docstring_check.py +++ b/edx_lint/pylint/filters_docstring/filters_docstring_check.py @@ -27,24 +27,24 @@ class FiltersDocstringFormatChecker(BaseChecker): name = "docstring-format-checker" - FILTER_DOCSTRING_MISSING_DESCRIPTION = "filter-docstring-missing-description" - FILTER_DOCSTRING_MISSING_TYPE = "filter-docstring-missing-type" - FILTER_DOCSTRING_MISSING_TRIGGER = "filter-docstring-missing-trigger" + DOCSTRING_MISSING_DESCRIPTION = "filter-docstring-missing-description" + DOCSTRING_MISSING_TYPE = "filter-docstring-missing-type" + DOCSTRING_MISSING_TRIGGER = "filter-docstring-missing-trigger" msgs = { ("E%d90" % BASE_ID): ( "Filter's (%s) docstring is missing the required description section", - FILTER_DOCSTRING_MISSING_DESCRIPTION, + DOCSTRING_MISSING_DESCRIPTION, "filters docstring is missing the required description section", ), ("E%d91" % BASE_ID): ( "Filter's (%s) docstring is missing the required filter type section", - FILTER_DOCSTRING_MISSING_TYPE, + DOCSTRING_MISSING_TYPE, "filters docstring is missing the required filter type section", ), ("E%d92" % BASE_ID): ( "Filter's (%s) docstring is missing the required trigger section", - FILTER_DOCSTRING_MISSING_TRIGGER, + DOCSTRING_MISSING_TRIGGER, "filters docstring is missing the required trigger section", ), } @@ -52,9 +52,9 @@ class FiltersDocstringFormatChecker(BaseChecker): options = () @utils.only_required_for_messages( - FILTER_DOCSTRING_MISSING_DESCRIPTION, - FILTER_DOCSTRING_MISSING_TYPE, - FILTER_DOCSTRING_MISSING_TRIGGER, + DOCSTRING_MISSING_DESCRIPTION, + DOCSTRING_MISSING_TYPE, + DOCSTRING_MISSING_TRIGGER, ) def visit_classdef(self, node): """Visit a class definition and check its docstring.""" @@ -94,11 +94,11 @@ def _check_docstring_format(self, docstring): ``` """ required_sections = [ - (r"Description:\s*.*\n", self.FILTER_DOCSTRING_MISSING_DESCRIPTION), - (r"Filter Type:\s*.*\n", self.FILTER_DOCSTRING_MISSING_TYPE), + (r"Description:\s*.*\n", self.DOCSTRING_MISSING_DESCRIPTION), + (r"Filter Type:\s*.*\n", self.DOCSTRING_MISSING_TYPE), ( r"Trigger:\s*(NA|-\s*Repository:\s*[^\n]+\s*-\s*Path:\s*[^\n]+\s*-\s*Function\s*or\s*Method:\s*[^\n]+)", - self.FILTER_DOCSTRING_MISSING_TRIGGER, + self.DOCSTRING_MISSING_TRIGGER, ), ] error_messages = [] From 9c6bd816e3ed8e665c34a0ba77f0fbb735581b72 Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Mon, 13 Jan 2025 12:27:14 +0100 Subject: [PATCH 04/11] refactor: add additional details for error message --- .../filters_docstring/filters_docstring_check.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/edx_lint/pylint/filters_docstring/filters_docstring_check.py b/edx_lint/pylint/filters_docstring/filters_docstring_check.py index 9aaf50c..f8a16f4 100644 --- a/edx_lint/pylint/filters_docstring/filters_docstring_check.py +++ b/edx_lint/pylint/filters_docstring/filters_docstring_check.py @@ -33,19 +33,19 @@ class FiltersDocstringFormatChecker(BaseChecker): msgs = { ("E%d90" % BASE_ID): ( - "Filter's (%s) docstring is missing the required description section", + "Filter's (%s) docstring is missing the required description section or is badly formatted", DOCSTRING_MISSING_DESCRIPTION, - "filters docstring is missing the required description section", + "filters docstring is missing the required description section or is badly formatted", ), ("E%d91" % BASE_ID): ( - "Filter's (%s) docstring is missing the required filter type section", + "Filter's (%s) docstring is missing the required filter type section or is badly formatted", DOCSTRING_MISSING_TYPE, - "filters docstring is missing the required filter type section", + "filters docstring is missing the required filter type section or is badly formatted", ), ("E%d92" % BASE_ID): ( - "Filter's (%s) docstring is missing the required trigger section", + "Filter's (%s) docstring is missing the required trigger section or is badly formatted", DOCSTRING_MISSING_TRIGGER, - "filters docstring is missing the required trigger section", + "filters docstring is missing the required trigger section or is badly formatted", ), } From 6b956465e206c6b4cde8927734714e4e450bf8ff Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Mon, 13 Jan 2025 12:55:17 +0100 Subject: [PATCH 05/11] refactor: skip OpenEdxPublicFilter base class --- .../filters_docstring/filters_docstring_check.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/edx_lint/pylint/filters_docstring/filters_docstring_check.py b/edx_lint/pylint/filters_docstring/filters_docstring_check.py index f8a16f4..fdaf62f 100644 --- a/edx_lint/pylint/filters_docstring/filters_docstring_check.py +++ b/edx_lint/pylint/filters_docstring/filters_docstring_check.py @@ -57,10 +57,19 @@ class FiltersDocstringFormatChecker(BaseChecker): DOCSTRING_MISSING_TRIGGER, ) def visit_classdef(self, node): - """Visit a class definition and check its docstring.""" + """ + Visit a class definition and check its docstring. + + If the class is a subclass of OpenEdxPublicFilter, check the format of its docstring. Skip the + OpenEdxPublicFilter class itself. + + """ if not node.is_subtype_of("openedx_filters.tooling.OpenEdxPublicFilter"): return + if node.name == "OpenEdxPublicFilter": + return + docstring = node.doc_node.value if node.doc_node else "" if not (error_messages := self._check_docstring_format(docstring)): return From d88857b9ac1598e67137c73a12b859e18884efcc Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Mon, 13 Jan 2025 13:35:03 +0100 Subject: [PATCH 06/11] refactor: use purpose only instead of description --- .../filters_docstring_check.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/edx_lint/pylint/filters_docstring/filters_docstring_check.py b/edx_lint/pylint/filters_docstring/filters_docstring_check.py index fdaf62f..4c3e192 100644 --- a/edx_lint/pylint/filters_docstring/filters_docstring_check.py +++ b/edx_lint/pylint/filters_docstring/filters_docstring_check.py @@ -27,22 +27,22 @@ class FiltersDocstringFormatChecker(BaseChecker): name = "docstring-format-checker" - DOCSTRING_MISSING_DESCRIPTION = "filter-docstring-missing-description" + DOCSTRING_MISSING_PURPOSE = "filter-docstring-missing-purpose" DOCSTRING_MISSING_TYPE = "filter-docstring-missing-type" DOCSTRING_MISSING_TRIGGER = "filter-docstring-missing-trigger" msgs = { - ("E%d90" % BASE_ID): ( - "Filter's (%s) docstring is missing the required description section or is badly formatted", - DOCSTRING_MISSING_DESCRIPTION, - "filters docstring is missing the required description section or is badly formatted", - ), ("E%d91" % BASE_ID): ( - "Filter's (%s) docstring is missing the required filter type section or is badly formatted", - DOCSTRING_MISSING_TYPE, - "filters docstring is missing the required filter type section or is badly formatted", + "Filter's (%s) docstring is missing the required purpose section or is badly formatted", + DOCSTRING_MISSING_PURPOSE, + "filters docstring is missing the required purpose section or is badly formatted", ), ("E%d92" % BASE_ID): ( + "Filter's (%s) docstring is missing the required type section or is badly formatted", + DOCSTRING_MISSING_TYPE, + "filters docstring is missing the required type section or is badly formatted", + ), + ("E%d93" % BASE_ID): ( "Filter's (%s) docstring is missing the required trigger section or is badly formatted", DOCSTRING_MISSING_TRIGGER, "filters docstring is missing the required trigger section or is badly formatted", @@ -52,7 +52,7 @@ class FiltersDocstringFormatChecker(BaseChecker): options = () @utils.only_required_for_messages( - DOCSTRING_MISSING_DESCRIPTION, + DOCSTRING_MISSING_PURPOSE, DOCSTRING_MISSING_TYPE, DOCSTRING_MISSING_TRIGGER, ) @@ -103,7 +103,7 @@ def _check_docstring_format(self, docstring): ``` """ required_sections = [ - (r"Description:\s*.*\n", self.DOCSTRING_MISSING_DESCRIPTION), + (r"Purpose:\s*.*\n", self.DOCSTRING_MISSING_PURPOSE), (r"Filter Type:\s*.*\n", self.DOCSTRING_MISSING_TYPE), ( r"Trigger:\s*(NA|-\s*Repository:\s*[^\n]+\s*-\s*Path:\s*[^\n]+\s*-\s*Function\s*or\s*Method:\s*[^\n]+)", From b990554faa20ac1bff809b06bc0a41f14bb14d8c Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Wed, 15 Jan 2025 19:00:53 +0100 Subject: [PATCH 07/11] refactor: make checks more specific with separation of concerns --- .../filters_docstring_check.py | 96 ++++++++++++------- 1 file changed, 62 insertions(+), 34 deletions(-) diff --git a/edx_lint/pylint/filters_docstring/filters_docstring_check.py b/edx_lint/pylint/filters_docstring/filters_docstring_check.py index 4c3e192..0b53c4d 100644 --- a/edx_lint/pylint/filters_docstring/filters_docstring_check.py +++ b/edx_lint/pylint/filters_docstring/filters_docstring_check.py @@ -25,36 +25,34 @@ def register_checkers(linter): class FiltersDocstringFormatChecker(BaseChecker): """Pylint checker for the format of the docstrings of filters.""" - name = "docstring-format-checker" + name = "filters-docstring-format" - DOCSTRING_MISSING_PURPOSE = "filter-docstring-missing-purpose" - DOCSTRING_MISSING_TYPE = "filter-docstring-missing-type" - DOCSTRING_MISSING_TRIGGER = "filter-docstring-missing-trigger" + DOCSTRING_MISSING_PURPOSE_OR_BADLY_FORMATTED = "filter-docstring-missing-purpose" + DOCSTRING_MISSING_OR_INCORRECT_TYPE = "filter-docstring-missing-or-incorrect-type" + DOCSTRING_MISSING_TRIGGER_OR_BADLY_FORMATTED = "filter-docstring-missing-trigger" msgs = { ("E%d91" % BASE_ID): ( - "Filter's (%s) docstring is missing the required purpose section or is badly formatted", - DOCSTRING_MISSING_PURPOSE, - "filters docstring is missing the required purpose section or is badly formatted", - ), - ("E%d92" % BASE_ID): ( - "Filter's (%s) docstring is missing the required type section or is badly formatted", - DOCSTRING_MISSING_TYPE, - "filters docstring is missing the required type section or is badly formatted", + "Filter's (%s) docstring is missing the required `Purpose` section or is badly formatted", + DOCSTRING_MISSING_PURPOSE_OR_BADLY_FORMATTED, + "filters docstring is missing the required `Purpose` section or is badly formatted", ), ("E%d93" % BASE_ID): ( - "Filter's (%s) docstring is missing the required trigger section or is badly formatted", - DOCSTRING_MISSING_TRIGGER, - "filters docstring is missing the required trigger section or is badly formatted", + "Filter's (%s) docstring `Filter Type` section is missing or incorrect", + DOCSTRING_MISSING_OR_INCORRECT_TYPE, + "filters docstring `Filter Type` section is missing or incorrect", + ), + ("E%d94" % BASE_ID): ( + "Filter's (%s) docstring is missing the required `Trigger` section or is badly formatted", + DOCSTRING_MISSING_TRIGGER_OR_BADLY_FORMATTED, + "filters docstring is missing the required `Trigger` section or is badly formatted", ), } - options = () - @utils.only_required_for_messages( - DOCSTRING_MISSING_PURPOSE, - DOCSTRING_MISSING_TYPE, - DOCSTRING_MISSING_TRIGGER, + DOCSTRING_MISSING_PURPOSE_OR_BADLY_FORMATTED, + DOCSTRING_MISSING_OR_INCORRECT_TYPE, + DOCSTRING_MISSING_TRIGGER_OR_BADLY_FORMATTED, ) def visit_classdef(self, node): """ @@ -71,12 +69,12 @@ def visit_classdef(self, node): return docstring = node.doc_node.value if node.doc_node else "" - if not (error_messages := self._check_docstring_format(docstring)): + if not (error_messages := self._check_docstring_format(node, docstring)): return for error_message in error_messages: self.add_message(error_message, node=node, args=(node.name,)) - def _check_docstring_format(self, docstring): + def _check_docstring_format(self, node, docstring): """ Check the format of the docstring for errors and return a list of error messages. @@ -88,7 +86,7 @@ def _check_docstring_format(self, docstring): For example: ``` - Description: + Purpose: Filter used to modify the certificate rendering process. ... (more description) @@ -102,16 +100,46 @@ def _check_docstring_format(self, docstring): - Function or Method: render_html_view ``` """ - required_sections = [ - (r"Purpose:\s*.*\n", self.DOCSTRING_MISSING_PURPOSE), - (r"Filter Type:\s*.*\n", self.DOCSTRING_MISSING_TYPE), - ( - r"Trigger:\s*(NA|-\s*Repository:\s*[^\n]+\s*-\s*Path:\s*[^\n]+\s*-\s*Function\s*or\s*Method:\s*[^\n]+)", - self.DOCSTRING_MISSING_TRIGGER, - ), - ] error_messages = [] - for pattern, error_message in required_sections: - if not re.search(pattern, docstring, re.MULTILINE): - error_messages.append(error_message) + if error_message := self._check_purpose_missing_or_badly_formatted(docstring): + error_messages.append(error_message) + if error_message := self._check_filter_type_missing_or_incorrect(node, docstring): + error_messages.append(error_message) + if error_message := self._check_trigger_missing_or_badly_formatted(docstring): + error_messages.append(error_message) return error_messages + + def _check_purpose_missing_or_badly_formatted(self, docstring): + """ + Check if the purpose is missing or badly formatted. + + If the purpose is missing or badly formatted, return the error message. Otherwise, return. + """ + if not re.search(r"Purpose:\s*.*\n", docstring): + return self.DOCSTRING_MISSING_PURPOSE_OR_BADLY_FORMATTED + return + + def _check_filter_type_missing_or_incorrect(self, node, docstring): + """ + Check if the filter type is missing or incorrect. + + If the filter type is missing or incorrect, return the error message. Otherwise, return. + """ + filter_type = node.locals["filter_type"][0].statement().value.value + if not re.search(r"Filter Type:\s*%s" % filter_type, docstring): + return self.DOCSTRING_MISSING_OR_INCORRECT_TYPE + return + + def _check_trigger_missing_or_badly_formatted(self, docstring): + """ + Check if the trigger is missing or badly formatted. + + If the trigger is missing or badly formatted, return the error message. Otherwise, return. + """ + if not re.search( + r"Trigger:\s*(NA|-\s*Repository:\s*[^\n]+\s*-\s*Path:\s*[^\n]+\s*-\s*Function\s*or\s*Method:\s*[^\n]+)", + docstring, + re.MULTILINE, + ): + return self.DOCSTRING_MISSING_TRIGGER_OR_BADLY_FORMATTED + return From 96592329dfe443582a1ab0755dd198c97d64e6cf Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Wed, 15 Jan 2025 19:23:53 +0100 Subject: [PATCH 08/11] refactor: combine conditions in single statement --- edx_lint/pylint/filters_docstring/filters_docstring_check.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/edx_lint/pylint/filters_docstring/filters_docstring_check.py b/edx_lint/pylint/filters_docstring/filters_docstring_check.py index 0b53c4d..cec48b8 100644 --- a/edx_lint/pylint/filters_docstring/filters_docstring_check.py +++ b/edx_lint/pylint/filters_docstring/filters_docstring_check.py @@ -62,10 +62,7 @@ def visit_classdef(self, node): OpenEdxPublicFilter class itself. """ - if not node.is_subtype_of("openedx_filters.tooling.OpenEdxPublicFilter"): - return - - if node.name == "OpenEdxPublicFilter": + if not node.is_subtype_of("openedx_filters.tooling.OpenEdxPublicFilter") or node.name == "OpenEdxPublicFilter": return docstring = node.doc_node.value if node.doc_node else "" From bacec37f355ef9ab572f0b364c8025855d7df6eb Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Wed, 15 Jan 2025 19:27:15 +0100 Subject: [PATCH 09/11] refactor: address quality errors --- .../pylint/filters_docstring/filters_docstring_check.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/edx_lint/pylint/filters_docstring/filters_docstring_check.py b/edx_lint/pylint/filters_docstring/filters_docstring_check.py index cec48b8..9457420 100644 --- a/edx_lint/pylint/filters_docstring/filters_docstring_check.py +++ b/edx_lint/pylint/filters_docstring/filters_docstring_check.py @@ -114,7 +114,7 @@ def _check_purpose_missing_or_badly_formatted(self, docstring): """ if not re.search(r"Purpose:\s*.*\n", docstring): return self.DOCSTRING_MISSING_PURPOSE_OR_BADLY_FORMATTED - return + return None def _check_filter_type_missing_or_incorrect(self, node, docstring): """ @@ -125,7 +125,7 @@ def _check_filter_type_missing_or_incorrect(self, node, docstring): filter_type = node.locals["filter_type"][0].statement().value.value if not re.search(r"Filter Type:\s*%s" % filter_type, docstring): return self.DOCSTRING_MISSING_OR_INCORRECT_TYPE - return + return None def _check_trigger_missing_or_badly_formatted(self, docstring): """ @@ -139,4 +139,4 @@ def _check_trigger_missing_or_badly_formatted(self, docstring): re.MULTILINE, ): return self.DOCSTRING_MISSING_TRIGGER_OR_BADLY_FORMATTED - return + return None From ae8fbdf999ce94d15665d005c1333fdd0bc61fac Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Wed, 22 Jan 2025 10:01:26 +0100 Subject: [PATCH 10/11] refactor: add error handling when getting filter_type from class --- .../pylint/filters_docstring/filters_docstring_check.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/edx_lint/pylint/filters_docstring/filters_docstring_check.py b/edx_lint/pylint/filters_docstring/filters_docstring_check.py index 9457420..9527975 100644 --- a/edx_lint/pylint/filters_docstring/filters_docstring_check.py +++ b/edx_lint/pylint/filters_docstring/filters_docstring_check.py @@ -122,7 +122,11 @@ def _check_filter_type_missing_or_incorrect(self, node, docstring): If the filter type is missing or incorrect, return the error message. Otherwise, return. """ - filter_type = node.locals["filter_type"][0].statement().value.value + filter_type = node.locals.get("filter_type") + if not filter_type: + return self.DOCSTRING_MISSING_OR_INCORRECT_TYPE + + filter_type = filter_type[0].statement().value.value if filter_type else "" if not re.search(r"Filter Type:\s*%s" % filter_type, docstring): return self.DOCSTRING_MISSING_OR_INCORRECT_TYPE return None From 46ac9a3320235944be124561116feb6be783d1c5 Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Fri, 24 Jan 2025 11:52:32 +0100 Subject: [PATCH 11/11] docs: update docstrings for new release --- CHANGELOG.rst | 5 +++++ edx_lint/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 234e7d5..12ecb1e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,6 +13,11 @@ Change Log Unreleased ~~~~~~~~~~ +5.6.0 - 2025-01-24 +~~~~~~~~~~~~~~~~~~ + +* Add docstring linter for Open edX filters. + 5.5.0 - 2025-01-22 ~~~~~~~~~~~~~~~~~~ diff --git a/edx_lint/__init__.py b/edx_lint/__init__.py index f7cfbb9..e95073b 100644 --- a/edx_lint/__init__.py +++ b/edx_lint/__init__.py @@ -2,4 +2,4 @@ edx_lint standardizes lint configuration and additional plugins for use in Open edX code. """ -__version__ = "5.5.0" +__version__ = "5.6.0"