diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eca9e64df..00027e53d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,8 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04] - toxenv: [py38-django42, py311-django42, py312-django42, py38-quality, py311-quality, py312-quality, translations] + python-version: [3.8, 3.11, 3.12] + toxenv: [django42, quality, translations] steps: - name: checkout repo @@ -27,20 +28,10 @@ jobs: with: submodules: recursive - - name: Extract the Python version from tox environment - run: | - if [[ "${{ matrix.toxenv }}" =~ py[0-9]+ ]]; then - PYTHON_VERSION=$(echo "${{ matrix.toxenv }}" | sed -E 's/py([0-9])([0-9]+).*/\1.\2/') - else - # Default version - PYTHON_VERSION=3.8 - fi - echo "PYTHON_VERSION=$PYTHON_VERSION" >> $GITHUB_ENV - - name: setup python uses: actions/setup-python@v4 with: - python-version: ${{ env.PYTHON_VERSION }} + python-version: ${{ matrix.python-version }} - name: Install translations dependencies if: ${{ startsWith(matrix.toxenv, 'translations') }} diff --git a/Changelog.md b/Changelog.md index b66d82974..1f256fdce 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,8 @@ Drag and Drop XBlock changelog Unreleased --------------------------- +* Adjust code styling for newer pylint versions. + Version 4.0.2 (2024-04-24) -------------------------- diff --git a/drag_and_drop_v2/compat.py b/drag_and_drop_v2/compat.py index 60ef79e69..f4f809aee 100644 --- a/drag_and_drop_v2/compat.py +++ b/drag_and_drop_v2/compat.py @@ -29,4 +29,5 @@ def get_grading_ignore_decoys_waffle_flag(): # Ref: https://github.com/openedx/public-engineering/issues/28 return CourseWaffleFlag(WAFFLE_NAMESPACE, GRADING_IGNORE_DECOYS, __name__) except ValueError: + # pylint: disable=toggle-missing-annotation return CourseWaffleFlag(f'{WAFFLE_NAMESPACE}.{GRADING_IGNORE_DECOYS}', __name__) diff --git a/drag_and_drop_v2/drag_and_drop_v2.py b/drag_and_drop_v2/drag_and_drop_v2.py index 800b582bd..bcbb1b6e3 100644 --- a/drag_and_drop_v2/drag_and_drop_v2.py +++ b/drag_and_drop_v2/drag_and_drop_v2.py @@ -11,9 +11,9 @@ import json import logging -import six.moves.urllib.error # pylint: disable=import-error -import six.moves.urllib.parse # pylint: disable=import-error -import six.moves.urllib.request # pylint: disable=import-error +import six.moves.urllib.error +import six.moves.urllib.parse +import six.moves.urllib.request import six import webob @@ -45,7 +45,6 @@ # Classes ########################################################### -# pylint: disable=bad-continuation @XBlock.wants('settings') @XBlock.wants('replace_urls') @XBlock.wants('user') # Using `needs` breaks the Course Outline page in Maple. @@ -257,7 +256,7 @@ def score(self): """ return Score(self.raw_earned, self.raw_possible) - def max_score(self): # pylint: disable=no-self-use + def max_score(self): """ Return the problem's max score, which for DnDv2 always equals 1. Required by the grading system in the LMS. @@ -515,7 +514,7 @@ def _get_block_id(self): - In the workbench, use the usage_id. """ if hasattr(self, 'location'): - return self.location.html_id() # pylint: disable=no-member + return self.location.html_id() else: return six.text_type(self.scope_ids.usage_id) @@ -581,7 +580,6 @@ def do_attempt(self, data, suffix=''): self._validate_do_attempt() self.attempts += 1 - # pylint: disable=fixme # TODO: Refactor this method to "freeze" item_state and pass it to methods that need access to it. # These implicit dependencies between methods exist because most of them use `item_state` or other # fields, either as an "input" (i.e. read value) or as output (i.e. set value) or both. As a result, @@ -712,7 +710,7 @@ def has_submission_deadline_passed(self): functionality. """ if hasattr(self, "has_deadline_passed"): - return self.has_deadline_passed() # pylint: disable=no-member + return self.has_deadline_passed() else: return False @@ -949,7 +947,6 @@ def _mark_complete_and_publish_grade(self): """ Helper method to update `self.completed` and submit grade event if appropriate conditions met. """ - # pylint: disable=fixme # TODO: (arguable) split this method into "clean" functions (with no side effects and implicit state) # This method implicitly depends on self.item_state (via is_correct and _learner_raw_score) # and also updates self.raw_earned if some conditions are met. As a result this method implies some order of @@ -1052,8 +1049,9 @@ def _expand_static_url(self, url): # edX Studio uses a different runtime for 'studio_view' than 'student_view', # and the 'studio_view' runtime doesn't provide the replace_urls API. try: - from common.djangoapps.static_replace import replace_static_urls # pylint: disable=import-error - url = replace_static_urls(u'"{}"'.format(url), None, course_id=self.runtime.course_id)[1:-1] + # pylint: disable=import-outside-toplevel + from common.djangoapps.static_replace import replace_static_urls + url = replace_static_urls('"{}"'.format(url), None, course_id=self.runtime.course_id)[1:-1] except ImportError: pass return url @@ -1108,7 +1106,7 @@ def _get_preferred_zone(zone_count, zones): return preferred_zone # Set states of all items dropped in correct zones - for item_id in self.item_state: + for item_id in self.item_state: # pylint: disable=consider-using-dict-items if self.item_state[item_id]['correct']: state[item_id] = self.item_state[item_id] correct_items.add(item_id) @@ -1319,7 +1317,7 @@ def index_dictionary(self): # values may be numeric / string or dict # default implementation is an empty dict - xblock_body = super(DragAndDropBlock, self).index_dictionary() + xblock_body = super().index_dictionary() zones_display_names = { "zone_{}_display_name".format(zone_i): diff --git a/drag_and_drop_v2/utils.py b/drag_and_drop_v2/utils.py index 1ede9da06..b2cea9728 100644 --- a/drag_and_drop_v2/utils.py +++ b/drag_and_drop_v2/utils.py @@ -80,13 +80,14 @@ def sanitize_html(raw_body: str) -> str: """ Remove not allowed HTML tags to mitigate XSS vulnerabilities. """ - bleach_options = dict( - tags=ALLOWED_TAGS, - protocols=bleach.ALLOWED_PROTOCOLS, - strip=True, - attributes=ALLOWED_ATTRIBUTES, - ) - bleach_options['css_sanitizer'] = CSSSanitizer() + bleach_options = { + "tags": ALLOWED_TAGS, + "protocols": bleach.ALLOWED_PROTOCOLS, + "strip": True, + "attributes": ALLOWED_ATTRIBUTES, + "css_sanitizer": CSSSanitizer() + } + return bleach.clean(raw_body, **bleach_options) @@ -165,8 +166,8 @@ def not_placed(number, ngettext=ngettext_fallback): ).format(missing_count=number) -FeedbackMessage = namedtuple("FeedbackMessage", ["message", "message_class"]) # pylint: disable=invalid-name -ItemStats = namedtuple( # pylint: disable=invalid-name +FeedbackMessage = namedtuple("FeedbackMessage", ["message", "message_class"]) +ItemStats = namedtuple( 'ItemStats', ["required", "placed", "correctly_placed", "decoy", "decoy_in_bank"] ) diff --git a/pylintrc b/pylintrc index f1dcfe842..2c17a7bf2 100644 --- a/pylintrc +++ b/pylintrc @@ -1,27 +1,389 @@ +# *************************** +# ** DO NOT EDIT THIS FILE ** +# *************************** +# +# This file was generated by edx-lint: https://github.com/openedx/edx-lint +# +# If you want to change this file, you have two choices, depending on whether +# you want to make a local change that applies only to this repo, or whether +# you want to make a central change that applies to all repos using edx-lint. +# +# Note: If your pylintrc file is simply out-of-date relative to the latest +# pylintrc in edx-lint, ensure you have the latest edx-lint installed +# and then follow the steps for a "LOCAL CHANGE". +# +# LOCAL CHANGE: +# +# 1. Edit the local pylintrc_tweaks file to add changes just to this +# repo's file. +# +# 2. Run: +# +# $ edx_lint write pylintrc +# +# 3. This will modify the local file. Submit a pull request to get it +# checked in so that others will benefit. +# +# +# CENTRAL CHANGE: +# +# 1. Edit the pylintrc file in the edx-lint repo at +# https://github.com/openedx/edx-lint/blob/master/edx_lint/files/pylintrc +# +# 2. install the updated version of edx-lint (in edx-lint): +# +# $ pip install . +# +# 3. Run (in edx-lint): +# +# $ edx_lint write pylintrc +# +# 4. Make a new version of edx_lint, submit and review a pull request with the +# pylintrc update, and after merging, update the edx-lint version and +# publish the new version. +# +# 5. In your local repo, install the newer version of edx-lint. +# +# 6. Run: +# +# $ edx_lint write pylintrc +# +# 7. This will modify the local file. Submit a pull request to get it +# checked in so that others will benefit. +# +# +# +# +# +# STAY AWAY FROM THIS FILE! +# +# +# +# +# +# SERIOUSLY. +# +# ------------------------------ +# Generated by edx-lint version: 5.3.6 +# ------------------------------ +[MASTER] +ignore = migrations +persistent = yes +load-plugins = edx_lint.pylint,pylint_django,pylint_celery + +[MESSAGES CONTROL] +enable = + blacklisted-name, + line-too-long, + + abstract-class-instantiated, + abstract-method, + access-member-before-definition, + anomalous-backslash-in-string, + anomalous-unicode-escape-in-string, + arguments-differ, + assert-on-tuple, + assigning-non-slot, + assignment-from-no-return, + assignment-from-none, + attribute-defined-outside-init, + bad-except-order, + bad-format-character, + bad-format-string-key, + bad-format-string, + bad-open-mode, + bad-reversed-sequence, + bad-staticmethod-argument, + bad-str-strip-call, + bad-super-call, + binary-op-exception, + boolean-datetime, + catching-non-exception, + cell-var-from-loop, + confusing-with-statement, + continue-in-finally, + dangerous-default-value, + duplicate-argument-name, + duplicate-bases, + duplicate-except, + duplicate-key, + expression-not-assigned, + format-combined-specification, + format-needs-mapping, + function-redefined, + global-variable-undefined, + import-error, + import-self, + inconsistent-mro, + inherit-non-class, + init-is-generator, + invalid-all-object, + invalid-format-index, + invalid-length-returned, + invalid-sequence-index, + invalid-slice-index, + invalid-slots-object, + invalid-slots, + invalid-unary-operand-type, + logging-too-few-args, + logging-too-many-args, + logging-unsupported-format, + lost-exception, + method-hidden, + misplaced-bare-raise, + misplaced-future, + missing-format-argument-key, + missing-format-attribute, + missing-format-string-key, + no-member, + no-method-argument, + no-name-in-module, + no-self-argument, + no-value-for-parameter, + non-iterator-returned, + non-parent-method-called, + nonexistent-operator, + not-a-mapping, + not-an-iterable, + not-callable, + not-context-manager, + not-in-loop, + pointless-statement, + pointless-string-statement, + raising-bad-type, + raising-non-exception, + redefined-builtin, + redefined-outer-name, + redundant-keyword-arg, + repeated-keyword, + return-arg-in-generator, + return-in-init, + return-outside-function, + signature-differs, + super-init-not-called, + super-method-not-called, + syntax-error, + test-inherits-tests, + too-few-format-args, + too-many-format-args, + too-many-function-args, + translation-of-non-string, + truncated-format-string, + undefined-all-variable, + undefined-loop-variable, + undefined-variable, + unexpected-keyword-arg, + unexpected-special-method-signature, + unpacking-non-sequence, + unreachable, + unsubscriptable-object, + unsupported-binary-operation, + unsupported-membership-test, + unused-format-string-argument, + unused-format-string-key, + used-before-assignment, + using-constant-test, + yield-outside-function, + + astroid-error, + fatal, + method-check-failed, + parse-error, + raw-checker-failed, + + empty-docstring, + invalid-characters-in-docstring, + missing-docstring, + wrong-spelling-in-comment, + wrong-spelling-in-docstring, + + unused-argument, + unused-import, + unused-variable, + + eval-used, + exec-used, + + bad-classmethod-argument, + bad-mcs-classmethod-argument, + bad-mcs-method-argument, + bare-except, + broad-except, + consider-iterating-dictionary, + consider-using-enumerate, + global-at-module-level, + global-variable-not-assigned, + literal-used-as-attribute, + logging-format-interpolation, + logging-not-lazy, + multiple-imports, + multiple-statements, + no-classmethod-decorator, + no-staticmethod-decorator, + protected-access, + redundant-unittest-assert, + reimported, + simplifiable-if-statement, + simplifiable-range, + singleton-comparison, + superfluous-parens, + unidiomatic-typecheck, + unnecessary-lambda, + unnecessary-pass, + unnecessary-semicolon, + unneeded-not, + useless-else-on-loop, + wrong-assert-type, + + deprecated-method, + deprecated-module, + + too-many-boolean-expressions, + too-many-nested-blocks, + too-many-statements, + + wildcard-import, + wrong-import-order, + wrong-import-position, + + missing-final-newline, + mixed-line-endings, + trailing-newlines, + trailing-whitespace, + unexpected-line-ending-format, + + bad-inline-option, + bad-option-value, + deprecated-pragma, + unrecognized-inline-option, + useless-suppression, +disable = + bad-indentation, + broad-exception-raised, + consider-using-f-string, + duplicate-code, + file-ignored, + fixme, + global-statement, + invalid-name, + locally-disabled, + no-else-return, + suppressed-message, + too-few-public-methods, + too-many-ancestors, + too-many-arguments, + too-many-branches, + too-many-instance-attributes, + too-many-lines, + too-many-locals, + too-many-public-methods, + too-many-return-statements, + ungrouped-imports, + unspecified-encoding, + unused-wildcard-import, + use-maxsplit-arg, + + feature-toggle-needs-doc, + illegal-waffle-usage, + + logging-fstring-interpolation, + django-not-configured, + unused-argument, + unsubscriptable-object + [REPORTS] -reports=no +output-format = text +reports = no +score = no + +[BASIC] +module-rgx = (([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ +const-rgx = (([A-Z_][A-Z0-9_]*)|(__.*__)|log|urlpatterns)$ +class-rgx = [A-Z_][a-zA-Z0-9]+$ +function-rgx = ([a-z_][a-z0-9_]{2,40}|test_[a-z0-9_]+)$ +method-rgx = ([a-z_][a-z0-9_]{2,40}|setUp|set[Uu]pClass|tearDown|tear[Dd]ownClass|assert[A-Z]\w*|maxDiff|test_[a-z0-9_]+)$ +attr-rgx = [a-z_][a-z0-9_]{2,30}$ +argument-rgx = [a-z_][a-z0-9_]{2,30}$ +variable-rgx = [a-z_][a-z0-9_]{2,30}$ +class-attribute-rgx = ([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ +inlinevar-rgx = [A-Za-z_][A-Za-z0-9_]*$ +good-names = f,i,j,k,db,ex,Run,_,__ +bad-names = foo,bar,baz,toto,tutu,tata +no-docstring-rgx = __.*__$|test_.+|setUp$|setUpClass$|tearDown$|tearDownClass$|Meta$ +docstring-min-length = 5 [FORMAT] -max-line-length=120 -max-module-lines=1500 +max-line-length = 120 +ignore-long-lines = ^\s*(# )?((?)|(\.\. \w+: .*))$ +single-line-if-stmt = no +max-module-lines = 1000 +indent-string = ' ' -[MESSAGES CONTROL] -disable= - locally-disabled, - too-many-ancestors, - too-many-instance-attributes, - too-few-public-methods, - too-many-public-methods, - unused-argument, - unsubscriptable-object, - no-else-return +[MISCELLANEOUS] +notes = FIXME,XXX,TODO [SIMILARITIES] -min-similarity-lines=4 - -[OPTIONS] -good-names=_,__,logger,loader -method-rgx=_?[a-z_][a-z0-9_]{2,40}$ -function-rgx=_?[a-z_][a-z0-9_]{2,40}$ -method-name-hint=_?[a-z_][a-z0-9_]{2,40}$ -function-name-hint=_?[a-z_][a-z0-9_]{2,40}$ +min-similarity-lines = 4 +ignore-comments = yes +ignore-docstrings = yes +ignore-imports = no + +[TYPECHECK] +ignore-mixin-members = yes +ignored-classes = SQLObject +unsafe-load-any-extension = yes +generated-members = + REQUEST, + acl_users, + aq_parent, + objects, + DoesNotExist, + can_read, + can_write, + get_url, + size, + content, + status_code, + create, + build, + fields, + tag, + org, + course, + category, + name, + revision, + _meta, + +[VARIABLES] +init-import = no +dummy-variables-rgx = _|dummy|unused|.*_unused +additional-builtins = + +[CLASSES] +defining-attr-methods = __init__,__new__,setUp +valid-classmethod-first-arg = cls +valid-metaclass-classmethod-first-arg = mcs + +[DESIGN] +max-args = 5 +ignored-argument-names = _.* +max-locals = 15 +max-returns = 6 +max-branches = 12 +max-statements = 50 +max-parents = 7 +max-attributes = 7 +min-public-methods = 2 +max-public-methods = 20 + +[IMPORTS] +deprecated-modules = regsub,TERMIOS,Bastion,rexec +import-graph = +ext-import-graph = +int-import-graph = + +[EXCEPTIONS] +overgeneral-exceptions = builtins.Exception + +# 6114ba904f03712e1def5d0f459a5ce5a0927223 diff --git a/pylintrc_tweaks b/pylintrc_tweaks new file mode 100644 index 000000000..f24c49441 --- /dev/null +++ b/pylintrc_tweaks @@ -0,0 +1,10 @@ +# pylintrc tweaks for use with edx_lint. +[MASTER] +ignore = migrations +load-plugins = edx_lint.pylint,pylint_django,pylint_celery + +[MESSAGES CONTROL] +disable+= + django-not-configured, + unused-argument, + unsubscriptable-object diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 84a58bca5..5441db01c 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -11,7 +11,5 @@ # Common constraints for edx repos -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt -pylint==2.4.2 - # For python greater than or equal to 3.9 backports.zoneinfo causing failures backports.zoneinfo; python_version<"3.9" diff --git a/requirements/dev.txt b/requirements/dev.txt index 451b26368..dbe5ee471 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -16,7 +16,7 @@ asgiref==3.8.1 # via # -r requirements/quality.txt # django -astroid==2.3.3 +astroid==3.1.0 # via # -r requirements/quality.txt # pylint @@ -94,6 +94,10 @@ coverage[toml]==7.5.0 # pytest-cov ddt==1.7.2 # via -r requirements/quality.txt +dill==0.3.8 + # via + # -r requirements/quality.txt + # pylint distlib==0.3.8 # via # -r requirements/ci.txt @@ -150,7 +154,7 @@ iniconfig==2.0.0 # via # -r requirements/quality.txt # pytest -isort==4.3.21 +isort==5.13.2 # via # -r requirements/quality.txt # pylint @@ -168,10 +172,6 @@ lazy==1.6 # via # -r requirements/quality.txt # xblock -lazy-object-proxy==1.4.3 - # via - # -r requirements/quality.txt - # astroid lxml[html-clean]==5.2.1 # via # -r requirements/quality.txt @@ -195,7 +195,7 @@ markupsafe==2.1.5 # jinja2 # mako # xblock -mccabe==0.6.1 +mccabe==0.7.0 # via # -r requirements/quality.txt # pylint @@ -231,6 +231,8 @@ pip-tools==7.4.1 platformdirs==4.2.1 # via # -r requirements/ci.txt + # -r requirements/quality.txt + # pylint # tox # virtualenv pluggy==1.5.0 @@ -249,9 +251,8 @@ pygments==2.17.2 # via # -r requirements/quality.txt # rich -pylint==2.4.2 +pylint==3.1.0 # via - # -c requirements/constraints.txt # -r requirements/quality.txt # edx-lint # pylint-celery @@ -335,7 +336,6 @@ simplejson==3.19.2 six==1.16.0 # via # -r requirements/quality.txt - # astroid # bleach # edx-lint # fs @@ -365,10 +365,15 @@ tomli==2.0.1 # build # coverage # pip-tools + # pylint # pyproject-api # pyproject-hooks # pytest # tox +tomlkit==0.12.4 + # via + # -r requirements/quality.txt + # pylint tox==4.15.0 # via -r requirements/ci.txt types-python-dateutil==2.9.0.20240316 @@ -379,6 +384,8 @@ typing-extensions==4.11.0 # via # -r requirements/quality.txt # asgiref + # astroid + # pylint # rich urllib3==1.26.18 # via @@ -408,10 +415,6 @@ wheel==0.43.0 # via # -r requirements/pip-tools.txt # pip-tools -wrapt==1.11.2 - # via - # -r requirements/quality.txt - # astroid xblock[django]==4.0.1 # via # -r requirements/quality.txt diff --git a/requirements/quality.txt b/requirements/quality.txt index 99d986371..684fbdef2 100644 --- a/requirements/quality.txt +++ b/requirements/quality.txt @@ -16,7 +16,7 @@ asgiref==3.8.1 # via # -r requirements/test.txt # django -astroid==2.3.3 +astroid==3.1.0 # via # pylint # pylint-celery @@ -73,6 +73,8 @@ coverage[toml]==7.5.0 # pytest-cov ddt==1.7.2 # via -r requirements/test.txt +dill==0.3.8 + # via pylint django==4.2.11 # via # -c https://raw.githubusercontent.com/edx/edx-lint/master/edx_lint/files/common_constraints.txt @@ -115,7 +117,7 @@ iniconfig==2.0.0 # via # -r requirements/test.txt # pytest -isort==4.3.21 +isort==5.13.2 # via pylint jinja2==3.1.3 # via @@ -131,8 +133,6 @@ lazy==1.6 # via # -r requirements/test.txt # xblock -lazy-object-proxy==1.4.3 - # via astroid lxml[html-clean]==5.2.1 # via # -r requirements/test.txt @@ -156,7 +156,7 @@ markupsafe==2.1.5 # jinja2 # mako # xblock -mccabe==0.6.1 +mccabe==0.7.0 # via pylint mdurl==0.1.2 # via @@ -178,6 +178,8 @@ path==16.14.0 # edx-i18n-tools pbr==6.0.0 # via stevedore +platformdirs==4.2.1 + # via pylint pluggy==1.5.0 # via # -r requirements/test.txt @@ -192,9 +194,8 @@ pygments==2.17.2 # via # -r requirements/test.txt # rich -pylint==2.4.2 +pylint==3.1.0 # via - # -c requirements/constraints.txt # edx-lint # pylint-celery # pylint-django @@ -263,7 +264,6 @@ simplejson==3.19.2 six==1.16.0 # via # -r requirements/test.txt - # astroid # bleach # edx-lint # fs @@ -287,7 +287,10 @@ tomli==2.0.1 # via # -r requirements/test.txt # coverage + # pylint # pytest +tomlkit==0.12.4 + # via pylint types-python-dateutil==2.9.0.20240316 # via # -r requirements/test.txt @@ -296,6 +299,8 @@ typing-extensions==4.11.0 # via # -r requirements/test.txt # asgiref + # astroid + # pylint # rich urllib3==1.26.18 # via @@ -317,8 +322,6 @@ webob==1.8.7 # -r requirements/test.txt # xblock # xblock-sdk -wrapt==1.11.2 - # via astroid xblock[django]==4.0.1 # via # -r requirements/test.txt diff --git a/tests/unit/test_fixtures.py b/tests/unit/test_fixtures.py index e99839144..c2467b648 100644 --- a/tests/unit/test_fixtures.py +++ b/tests/unit/test_fixtures.py @@ -41,15 +41,15 @@ def _make_feedback_message(message=None, message_class=None): @classmethod def initial_data(cls): - return json.loads(loader.load_unicode('data/{}/data.json'.format(cls.FOLDER))) + return json.loads(loader.load_unicode(f'data/{cls.FOLDER}/data.json')) @classmethod def initial_settings(cls): - return json.loads(loader.load_unicode('data/{}/settings.json'.format(cls.FOLDER))) + return json.loads(loader.load_unicode(f'data/{cls.FOLDER}/settings.json')) @classmethod def expected_student_data(cls): - return json.loads(loader.load_unicode('data/{}/config_out.json'.format(cls.FOLDER))) + return json.loads(loader.load_unicode(f'data/{cls.FOLDER}/config_out.json')) def test_student_view_data(self): data = self.block.student_view_data() diff --git a/tox.ini b/tox.ini index 864a101a9..ab3b24bf4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{38,311,312}-django{42},py{38,311,312}-quality,translations +envlist = py{38,311,312}-django{42},quality,translations [pycodestyle] exclude = .git,.tox