diff --git a/Makefile b/Makefile index 1e478c4e..b08fde22 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .DEFAULT_GOAL := help -.PHONY: upgrade help requirements lint format test docs +.PHONY: upgrade help requirements quality format test docs .PHONY: extract_translations compile_translations .PHONY: detect_changed_source_translations dummy_translations build_dummy_translations .PHONY: validate_translations pull_translations push_translations install_transifex_clients @@ -13,7 +13,7 @@ JS_TARGET := $(PACKAGE_NAME)/public/js/translations help: @perl -nle'print $& if m{^[\.a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m %-25s\033[0m %s\n", $$1, $$2}' -lint: ## run linting checks +quality: ## run quality checks tox -e quality format: ## auto-fix ruff lint and formatting issues diff --git a/pylintrc b/pylintrc new file mode 100644 index 00000000..2f598368 --- /dev/null +++ b/pylintrc @@ -0,0 +1,393 @@ +# *************************** +# ** 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: 6.2.0 +# ------------------------------ +[MASTER] +ignore = migrations +persistent = yes +load-plugins = edx_lint.pylint + +[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, + + pii-invalid-no-pii-annotation, +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, + +[REPORTS] +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 +ignore-long-lines = ^\s*(# )?((?)|(\.\. \w+: .*))$ +single-line-if-stmt = no +max-module-lines = 1000 +indent-string = ' ' + +[MISCELLANEOUS] +notes = FIXME,XXX,TODO + +[SIMILARITIES] +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 + +[PII] +pii-terms = + email, + username + +# 392427fdbbd4a12222e7d799e8274519ea0fc507 diff --git a/pylintrc_tweaks b/pylintrc_tweaks new file mode 100644 index 00000000..7f2439ca --- /dev/null +++ b/pylintrc_tweaks @@ -0,0 +1,5 @@ +# pylintrc tweaks for use with edx_lint. +[MASTER] +ignore = migrations +load-plugins = edx_lint.pylint + diff --git a/pyproject.toml b/pyproject.toml index ab1cb680..93a498f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,6 +101,7 @@ django42 = [ quality = [ { include-group = "test" }, "ruff", + "edx-lint", ] doc = [ { include-group = "test" }, @@ -162,7 +163,9 @@ select = [ "DJ", # flake8-django ] ignore = [ - "E501", # line too long (handled by formatter) + # pylint owns these and takes preference (avoids ruff/pylint conflicts) + "E501", # line-too-long -> pylint C0301 + "F401", # unused-import -> pylint W0611 (keeps __init__ re-exports plain) ] [tool.ruff.lint.isort] diff --git a/tox.ini b/tox.ini index 0b945438..45cee062 100644 --- a/tox.ini +++ b/tox.ini @@ -50,4 +50,5 @@ dependency_groups = quality commands = ruff check . ruff format --check . + pylint xblocks_contrib xblock_pdf make selfcheck diff --git a/uv.lock b/uv.lock index d29bf38d..4cd6a8bc 100644 --- a/uv.lock +++ b/uv.lock @@ -8,15 +8,15 @@ resolution-markers = [ conflicts = [[ { package = "xblocks-contrib", group = "django42" }, { package = "xblocks-contrib", group = "test" }, -], [ - { package = "xblocks-contrib", group = "django42" }, - { package = "xblocks-contrib", group = "quality" }, ], [ { package = "xblocks-contrib", group = "dev" }, { package = "xblocks-contrib", group = "django42" }, ], [ { package = "xblocks-contrib", group = "django42" }, { package = "xblocks-contrib", group = "doc" }, +], [ + { package = "xblocks-contrib", group = "django42" }, + { package = "xblocks-contrib", group = "quality" }, ]] [manifest] @@ -66,6 +66,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c0/1b/54f4ad77cd8a584fa70746c47df988e002cf1ee1eba43364d46f87803647/asgiref-3.12.1-py3-none-any.whl", hash = "sha256:fe386d1c2bff7259ea95929266d12a8cf9a8b5a1c2598402967d8792e7a7c094", size = 25478, upload-time = "2026-07-14T09:56:16.926Z" }, ] +[[package]] +name = "astroid" +version = "4.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/07/63/0adf26577da5eff6eb7a177876c1cfa213856be9926a000f65c4add9692b/astroid-4.0.4.tar.gz", hash = "sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0", size = 406358, upload-time = "2026-02-07T23:35:07.509Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/cf/1c5f42b110e57bc5502eb80dbc3b03d256926062519224835ef08134f1f9/astroid-4.0.4-py3-none-any.whl", hash = "sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753", size = 276445, upload-time = "2026-02-07T23:35:05.344Z" }, +] + [[package]] name = "babel" version = "2.18.0" @@ -391,6 +400,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, ] +[[package]] +name = "click-log" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/32/228be4f971e4bd556c33d52a22682bfe318ffe57a1ddb7a546f347a90260/click-log-0.4.0.tar.gz", hash = "sha256:3970f8570ac54491237bcdb3d8ab5e3eef6c057df29f8c3d1151a51a9c23b975", size = 9985, upload-time = "2022-03-13T11:10:15.262Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/5a/4f025bc751087833686892e17e7564828e409c43b632878afeae554870cd/click_log-0.4.0-py2.py3-none-any.whl", hash = "sha256:a43e394b528d52112af599f2fc9e4b7cf3c15f94e53581f74fa6867e68c91756", size = 4273, upload-time = "2022-03-13T11:10:17.594Z" }, +] + [[package]] name = "code-annotations" version = "3.0.0" @@ -583,6 +604,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, ] +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + [[package]] name = "distlib" version = "0.4.3" @@ -829,6 +859,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d6/a7/60ce0beb203878005010c396026b3666a4e604e7963608dff94571e0a6f2/edx_drf_extensions-10.6.0-py2.py3-none-any.whl", hash = "sha256:82603edc63f7f34a3d1ab024808320ad0e175e608484f392f3c76e594d7715d1", size = 76298, upload-time = "2025-04-04T11:43:47.913Z" }, ] +[[package]] +name = "edx-lint" +version = "6.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "click-log" }, + { name = "code-annotations" }, + { name = "pylint" }, + { name = "pylint-celery" }, + { name = "pylint-django" }, + { name = "six" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/e2/20b4782eba5bbbaeb949877666f871912db05fb22cee2eafdbc83566cffc/edx_lint-6.2.0.tar.gz", hash = "sha256:2ce94b0b1235024e7714792442ea674e1bbe2877e7ca2ca970609e74aa73dfdf", size = 55456, upload-time = "2026-08-18T12:40:35.178Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/f3/72bbb973cb654aa9d8291c438dd02b21ffb3bf2bdc9e653f9ea9048e3b20/edx_lint-6.2.0-py3-none-any.whl", hash = "sha256:0e1491f9a049927e35eb05a0fd33464b4ec912b83e387568e37f4ce5d10acb30", size = 64109, upload-time = "2026-08-18T12:40:34.102Z" }, +] + [[package]] name = "edx-opaque-keys" version = "4.0.0" @@ -964,6 +1013,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] +[[package]] +name = "isort" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592, upload-time = "2026-02-28T10:08:20.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, +] + [[package]] name = "jinja2" version = "3.1.6" @@ -1162,6 +1220,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, ] +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, +] + [[package]] name = "mpmath" version = "1.3.0" @@ -1505,6 +1572,60 @@ crypto = [ { name = "cryptography" }, ] +[[package]] +name = "pylint" +version = "4.0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "dill" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "platformdirs" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/92/98dace02f2d11b88160354c53944f77ea7327aa78bce1c75971e7aaa4347/pylint-4.0.7.tar.gz", hash = "sha256:9b2d1d15791c84b77a4fe2aafe8f0d9570717e2dea06d53b19c105cf60275a52", size = 1594770, upload-time = "2026-08-09T19:13:23.289Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/b0/3a8040e53df6c5c1e04b0e23ed53fdbeb64f333723a334d313fba2f581ce/pylint-4.0.7-py3-none-any.whl", hash = "sha256:be4a3111557a614411ed1fc89347ce4a8e1013a59e1f33d11485227a02e3304d", size = 539710, upload-time = "2026-08-09T19:13:21.228Z" }, +] + +[[package]] +name = "pylint-celery" +version = "0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "pylint" }, + { name = "pylint-plugin-utils" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/11/7241fec486839a853cee783e3a00950d46ee1e3e993884341b1d4208a287/pylint-celery-0.3.tar.gz", hash = "sha256:41e32094e7408d15c044178ea828dd524beedbdbe6f83f712c5e35bde1de4beb", size = 1899, upload-time = "2014-09-25T07:23:50.824Z" } + +[[package]] +name = "pylint-django" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pylint" }, + { name = "pylint-plugin-utils" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/a1/b92e5d5cf320b603c9bcc5174da7e9ba4c6ce71087354322a2b83536df13/pylint_django-2.8.0.tar.gz", hash = "sha256:42accea9098e4a3298b4bfbae0e4da81f909f8bff0deda9485efbd6035a86d6a", size = 32038, upload-time = "2026-07-11T10:19:14.844Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/4a/3dae8a09e12a28ccf3d7204cc4fb69f28dfb30d2b19567eb5ff094fe1265/pylint_django-2.8.0-py3-none-any.whl", hash = "sha256:706eb2cc8d7692236be9fd033a341042afe3bbbf99df9234a659db931016ef5d", size = 44672, upload-time = "2026-07-11T10:05:29.281Z" }, +] + +[[package]] +name = "pylint-plugin-utils" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pylint" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/85/24eaf5d0d078fc8799ae6d89faf326d6e4d27d862fc9a710a52ab07b7bb5/pylint_plugin_utils-0.9.0.tar.gz", hash = "sha256:5468d763878a18d5cc4db46eaffdda14313b043c962a263a7d78151b90132055", size = 10474, upload-time = "2025-06-24T07:14:00.534Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/c9/a3b871b0b590c49e38884af6dab58ab9711053bd5c39b8899b72e367b9f6/pylint_plugin_utils-0.9.0-py3-none-any.whl", hash = "sha256:16e9b84e5326ba893a319a0323fcc8b4bcc9c71fc654fcabba0605596c673818", size = 11129, upload-time = "2025-06-24T07:13:58.993Z" }, +] + [[package]] name = "pymongo" version = "4.17.0" @@ -2255,6 +2376,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, ] +[[package]] +name = "tomlkit" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/96/e07752635b98536177fa1f37671c8f3cdde2e724c6bcf6034b2cfb571565/tomlkit-0.15.1.tar.gz", hash = "sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97", size = 180129, upload-time = "2026-07-17T01:48:04.562Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/bc/8c13eb66537dce1d2bd3a57132902f38d0e7f5bb46fa9f4daed9fe9d76ee/tomlkit-0.15.1-py3-none-any.whl", hash = "sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304", size = 49449, upload-time = "2026-07-17T01:48:05.728Z" }, +] + [[package]] name = "tox" version = "4.60.0" @@ -2532,6 +2662,7 @@ dev = [ { name = "ddt" }, { name = "django", version = "5.2.17", source = { registry = "https://pypi.org/simple" } }, { name = "doc8" }, + { name = "edx-lint" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "pytest-django" }, @@ -2564,6 +2695,7 @@ doc = [ quality = [ { name = "ddt" }, { name = "django", version = "5.2.17", source = { registry = "https://pypi.org/simple" } }, + { name = "edx-lint" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "pytest-django" }, @@ -2627,6 +2759,7 @@ dev = [ { name = "ddt" }, { name = "django", specifier = ">=5.2,<6.0" }, { name = "doc8" }, + { name = "edx-lint" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "pytest-django" }, @@ -2659,6 +2792,7 @@ doc = [ quality = [ { name = "ddt" }, { name = "django", specifier = ">=5.2,<6.0" }, + { name = "edx-lint" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "pytest-django" }, diff --git a/xblock_pdf/__init__.py b/xblock_pdf/__init__.py index f41b34bc..ae2b9e92 100644 --- a/xblock_pdf/__init__.py +++ b/xblock_pdf/__init__.py @@ -1,3 +1,3 @@ """Init for PDFBlock.""" -from .pdf import PDFBlock as PDFBlock +from .pdf import PDFBlock diff --git a/xblocks_contrib/__init__.py b/xblocks_contrib/__init__.py index 458587bf..e5dddc2d 100644 --- a/xblocks_contrib/__init__.py +++ b/xblocks_contrib/__init__.py @@ -1,10 +1,10 @@ """Init for the xblocks_contrib package.""" -from .annotatable import AnnotatableBlock as AnnotatableBlock -from .discussion import DiscussionXBlock as DiscussionXBlock -from .html import HtmlBlock as HtmlBlock -from .lti import LTIBlock as LTIBlock -from .poll import PollBlock as PollBlock -from .problem import ProblemBlock as ProblemBlock -from .video import VideoBlock as VideoBlock -from .word_cloud import WordCloudBlock as WordCloudBlock +from .annotatable import AnnotatableBlock +from .discussion import DiscussionXBlock +from .html import HtmlBlock +from .lti import LTIBlock +from .poll import PollBlock +from .problem import ProblemBlock +from .video import VideoBlock +from .word_cloud import WordCloudBlock diff --git a/xblocks_contrib/annotatable/__init__.py b/xblocks_contrib/annotatable/__init__.py index 55a846a1..19cb87a1 100644 --- a/xblocks_contrib/annotatable/__init__.py +++ b/xblocks_contrib/annotatable/__init__.py @@ -2,4 +2,4 @@ Init for the AnnotatableBlock. """ -from .annotatable import AnnotatableBlock as AnnotatableBlock +from .annotatable import AnnotatableBlock diff --git a/xblocks_contrib/annotatable/annotatable.py b/xblocks_contrib/annotatable/annotatable.py index d7db7610..d1257f9c 100644 --- a/xblocks_contrib/annotatable/annotatable.py +++ b/xblocks_contrib/annotatable/annotatable.py @@ -302,5 +302,6 @@ def definition_to_xml(self, resource_fs): # re-raise lines = self.data.split("\n") line, offset = err.position # lint-amnesty, pylint: disable=unpacking-non-sequence - msg = f"Unable to create xml for block {self.usage_key}. Context: '{lines[line - 1][offset - 40 : offset + 40]}'" + context = lines[line - 1][offset - 40 : offset + 40] + msg = f"Unable to create xml for block {self.usage_key}. Context: '{context}'" raise SerializationError(self.usage_key, msg) from err diff --git a/xblocks_contrib/discussion/__init__.py b/xblocks_contrib/discussion/__init__.py index 4e6609a8..223f080f 100644 --- a/xblocks_contrib/discussion/__init__.py +++ b/xblocks_contrib/discussion/__init__.py @@ -2,4 +2,4 @@ Init for the DiscussionXBlock. """ -from .discussion import DiscussionXBlock as DiscussionXBlock +from .discussion import DiscussionXBlock diff --git a/xblocks_contrib/html/__init__.py b/xblocks_contrib/html/__init__.py index 474a2bf6..0fe36653 100644 --- a/xblocks_contrib/html/__init__.py +++ b/xblocks_contrib/html/__init__.py @@ -2,5 +2,4 @@ Init for the HtmlBlock. """ -from .html import HtmlBlock as HtmlBlock -from .html import HtmlBlockMixin as HtmlBlockMixin +from .html import HtmlBlock, HtmlBlockMixin diff --git a/xblocks_contrib/legacy_utils/xml_utils.py b/xblocks_contrib/legacy_utils/xml_utils.py index 9d23ad7e..ab6afb39 100644 --- a/xblocks_contrib/legacy_utils/xml_utils.py +++ b/xblocks_contrib/legacy_utils/xml_utils.py @@ -154,7 +154,7 @@ def own_metadata(block: XBlock) -> dict[str, Any]: result[field.name] = field.read_json(block) except TypeError as exception: exception_message = f"{str(exception)}, Block-location:{str(block.usage_key)}, Field-name:{field.name}" - raise TypeError(exception_message) from None # lint-amnesty, pylint: disable=raise-missing-from + raise TypeError(exception_message) from None return result diff --git a/xblocks_contrib/lti/__init__.py b/xblocks_contrib/lti/__init__.py index cc65289f..23a58683 100644 --- a/xblocks_contrib/lti/__init__.py +++ b/xblocks_contrib/lti/__init__.py @@ -4,4 +4,4 @@ Learning Tools Interoperability (LTI) module. """ -from .lti import LTIBlock as LTIBlock +from .lti import LTIBlock diff --git a/xblocks_contrib/lti/lti.py b/xblocks_contrib/lti/lti.py index 968956b7..e32c1b78 100644 --- a/xblocks_contrib/lti/lti.py +++ b/xblocks_contrib/lti/lti.py @@ -443,7 +443,7 @@ def get_input_fields(self): # lint-amnesty, pylint: disable=missing-function-do msg = _('Could not parse custom parameter: {custom_parameter}. Should be "x=y" string.').format( custom_parameter=f"{custom_parameter!r}" ) - raise LTIError(msg) from None # lint-amnesty, pylint: disable=raise-missing-from + raise LTIError(msg) from None # LTI specs: 'custom_' should be prepended before each custom parameter, as pointed in link above. if param_name not in PARAMETERS: @@ -989,7 +989,7 @@ def get_client_key_secret(self): msg = _('Could not parse LTI passport: {lti_passport}. Should be "id:key:secret" string.').format( lti_passport=f"{lti_passport!r}" ) - raise LTIError(msg) from None # lint-amnesty, pylint: disable=raise-missing-from + raise LTIError(msg) from None if lti_id == self.lti_id.strip(): return key, secret diff --git a/xblocks_contrib/lti/lti_2_util.py b/xblocks_contrib/lti/lti_2_util.py index b1e912dd..0861d486 100644 --- a/xblocks_contrib/lti/lti_2_util.py +++ b/xblocks_contrib/lti/lti_2_util.py @@ -313,7 +313,7 @@ def verify_lti_2_0_result_rest_headers(self, request, verify_content_type=True): self.verify_oauth_body_sign(request, content_type=LTI_2_0_JSON_CONTENT_TYPE) except (ValueError, LTIError) as err: log.info(f"[LTI]: v2.0 result service -- OAuth body verification failed: {str(err)}") - raise LTIError(str(err)) from None # lint-amnesty, pylint: disable=raise-missing-from + raise LTIError(str(err)) from None def parse_lti_2_0_result_json(self, json_str): """ @@ -340,7 +340,7 @@ def parse_lti_2_0_result_json(self, json_str): except (ValueError, TypeError): msg = f"Supplied JSON string in request body could not be decoded: {json_str}" log.info(f"[LTI] {msg}") - raise LTIError(msg) from None # lint-amnesty, pylint: disable=raise-missing-from + raise LTIError(msg) from None # the standard supports a list of objects, who knows why. It must contain at least 1 element, and the # first element must be a dict @@ -383,6 +383,6 @@ def parse_lti_2_0_result_json(self, json_str): except (TypeError, ValueError) as err: msg = f"Could not convert resultScore to float: {str(err)}" log.info(f"[LTI] {msg}") - raise LTIError(msg) from None # lint-amnesty, pylint: disable=raise-missing-from + raise LTIError(msg) from None return score, json_obj.get("comment", "") diff --git a/xblocks_contrib/lti/tests/helpers.py b/xblocks_contrib/lti/tests/helpers.py index d6e0a396..5d1bc792 100644 --- a/xblocks_contrib/lti/tests/helpers.py +++ b/xblocks_contrib/lti/tests/helpers.py @@ -76,8 +76,8 @@ class StubUserService(UserService): Stub UserService for testing the sequence block. """ - def __init__( - self, # pylint: disable=too-many-positional-arguments + def __init__( # pylint: disable=too-many-positional-arguments + self, user=None, user_is_staff=False, user_role=None, @@ -129,7 +129,7 @@ def __init__(self, anonymous_student_id, services=None): super().__init__(id_reader=lambda: None, id_generator=lambda: None, services=services) self.anonymous_student_id = anonymous_student_id - def handler_url(self, block, handler_name, suffix="", query="", thirdparty=False): # pylint: disable=too-many-positional-arguments + def handler_url(self, block, handler_name, suffix="", query="", thirdparty=False): return f"/mock_url/{handler_name}" def local_resource_url(self, block, resource): # pylint: disable=arguments-renamed diff --git a/xblocks_contrib/poll/__init__.py b/xblocks_contrib/poll/__init__.py index db5a1e2a..6eddb6a0 100644 --- a/xblocks_contrib/poll/__init__.py +++ b/xblocks_contrib/poll/__init__.py @@ -2,4 +2,4 @@ Init for the PollBlock. """ -from .poll import PollBlock as PollBlock +from .poll import PollBlock diff --git a/xblocks_contrib/problem/__init__.py b/xblocks_contrib/problem/__init__.py index c7738676..95757cba 100644 --- a/xblocks_contrib/problem/__init__.py +++ b/xblocks_contrib/problem/__init__.py @@ -2,4 +2,4 @@ Init for the ProblemBlock. """ -from .capa_block import ProblemBlock as ProblemBlock +from .capa_block import ProblemBlock diff --git a/xblocks_contrib/problem/capa/inputtypes.py b/xblocks_contrib/problem/capa/inputtypes.py index c87017d0..5ce6b3a7 100644 --- a/xblocks_contrib/problem/capa/inputtypes.py +++ b/xblocks_contrib/problem/capa/inputtypes.py @@ -128,9 +128,7 @@ class Attribute: # want to allow default to be None, but also allow required objects _sentinel = object() - def __init__( # pylint: disable=too-many-positional-arguments,too-many-arguments - self, name, default=_sentinel, transform=None, validate=None, render=True - ): + def __init__(self, name, default=_sentinel, transform=None, validate=None, render=True): """ Define an attribute diff --git a/xblocks_contrib/problem/capa/safe_exec/__init__.py b/xblocks_contrib/problem/capa/safe_exec/__init__.py index 14b74d70..ffbe8f23 100644 --- a/xblocks_contrib/problem/capa/safe_exec/__init__.py +++ b/xblocks_contrib/problem/capa/safe_exec/__init__.py @@ -1,4 +1,3 @@ """Capa's specialized use of codejail.safe_exec.""" -from .safe_exec import safe_exec as safe_exec -from .safe_exec import update_hash as update_hash +from .safe_exec import safe_exec, update_hash diff --git a/xblocks_contrib/problem/capa/tests/test_responsetypes.py b/xblocks_contrib/problem/capa/tests/test_responsetypes.py index 5f0ee1cb..58a4b287 100644 --- a/xblocks_contrib/problem/capa/tests/test_responsetypes.py +++ b/xblocks_contrib/problem/capa/tests/test_responsetypes.py @@ -378,9 +378,7 @@ def test_multiple_inputs_exception(self): with pytest.raises(Exception): # noqa: B017 self.build_problem(math_display=True, expect="2*x+3*y", num_inputs=3) - def _assert_symbolic_grade( # pylint: disable=too-many-arguments,too-many-positional-arguments - self, problem, student_input, dynamath_input, expected_correctness, snuggletex_resp="" - ): + def _assert_symbolic_grade(self, problem, student_input, dynamath_input, expected_correctness, snuggletex_resp=""): """ Assert that the symbolic response has a certain grade. diff --git a/xblocks_contrib/problem/capa/xqueue_interface.py b/xblocks_contrib/problem/capa/xqueue_interface.py index c4d5ca71..e13ba1e4 100644 --- a/xblocks_contrib/problem/capa/xqueue_interface.py +++ b/xblocks_contrib/problem/capa/xqueue_interface.py @@ -72,7 +72,7 @@ def parse_xreply(xreply): class XQueueInterface: """Initializes the XQueue interface.""" - def __init__( # pylint: disable=too-many-positional-arguments + def __init__( self, url: str, django_auth: dict[str, str], diff --git a/xblocks_contrib/problem/tests/__init__.py b/xblocks_contrib/problem/tests/__init__.py index a2bbe977..b624cba5 100644 --- a/xblocks_contrib/problem/tests/__init__.py +++ b/xblocks_contrib/problem/tests/__init__.py @@ -168,7 +168,7 @@ def resource_url(self, *args, **kwargs): """Satisfy abstract method.""" return "" - def handler_url( # pylint: disable=arguments-differ,too-many-positional-arguments,unused-argument + def handler_url( # pylint: disable=arguments-differ,unused-argument self, block, handler_name, suffix="", query="", thirdparty=False ): """Mock handler URL generation to look like edx-platform URLs.""" diff --git a/xblocks_contrib/video/__init__.py b/xblocks_contrib/video/__init__.py index 472e0903..680674b2 100644 --- a/xblocks_contrib/video/__init__.py +++ b/xblocks_contrib/video/__init__.py @@ -2,4 +2,4 @@ Init for the VideoBlock. """ -from .video import VideoBlock as VideoBlock +from .video import VideoBlock diff --git a/xblocks_contrib/video/validation.py b/xblocks_contrib/video/validation.py index 11379164..6d26c1ff 100644 --- a/xblocks_contrib/video/validation.py +++ b/xblocks_contrib/video/validation.py @@ -16,9 +16,7 @@ class StudioValidationMessage(ValidationMessage): TYPES = [ValidationMessage.WARNING, ValidationMessage.ERROR, NOT_CONFIGURED] - def __init__( # pylint: disable=too-many-positional-arguments - self, message_type, message_text, action_label=None, action_class=None, action_runtime_event=None - ): + def __init__(self, message_type, message_text, action_label=None, action_class=None, action_runtime_event=None): """ Create a new message. diff --git a/xblocks_contrib/word_cloud/__init__.py b/xblocks_contrib/word_cloud/__init__.py index 4ae1aa5f..97c9c00c 100644 --- a/xblocks_contrib/word_cloud/__init__.py +++ b/xblocks_contrib/word_cloud/__init__.py @@ -1,3 +1,3 @@ """Word cloud is ungraded xblock used by students to generate and view word cloud.""" -from .word_cloud import WordCloudBlock as WordCloudBlock +from .word_cloud import WordCloudBlock