diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a14da37e8..05b7ad761 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -93,6 +93,7 @@ repos: - --output-format=pylint additional_dependencies: - prospector-profile-duplicated==1.6.0 # pypi + - prospector-profile-utils==1.9.1 # pypi - repo: https://github.com/sbrunner/jsonschema-validator rev: 0.1.0 hooks: diff --git a/.prospector.yaml b/.prospector.yaml index 896206e25..730e625d3 100644 --- a/.prospector.yaml +++ b/.prospector.yaml @@ -1,7 +1,9 @@ inherits: + - utils:base + - utils:no-design-checks + - utils:fix - duplicated -strictness: veryhigh -max-line-length: 110 + doc-warnings: true ignore-paths: @@ -15,56 +17,23 @@ pylint: - ujson - lxml disable: - - too-many-return-statements - - too-many-arguments - - too-many-branches - - too-many-instance-attributes - - too-few-public-methods - - global-statement - - line-too-long - - import-outside-toplevel - - invalid-name - no-else-return - no-else-raise - - no-self-use - - import-error - - unused-argument - - use-symbolic-message-instead - missing-module-docstring - - missing-function-docstring - missing-timeout # A default timeout is set -pycodestyle: - options: - max-line-length: 110 - disable: - - E722 # do not use bare 'except', duplicated with pylint - - E261 # at least two spaces before inline comment, duplicated with black - pydocstyle: disable: - - D102 # Missing docstring in public method - D104 # Missing docstring in public package - D105 # Missing docstring in magic method - D107 # Missing docstring in __init__ - - D202 # No blank lines allowed after function docstring (found 1) - - D203 # 1 blank line required before class docstring (found 0) - - D212 # Multi-line docstring summary should start at the first line - - D407 # Missing dashed underline after section ('Arguments') - - D412 # No blank lines allowed between a section header and its content ('Arguments') -mypy: - run: true + +pycodestyle: + disable: + # Buggy checks with Python 3.12 + - E221 # multiple spaces before operator + - E702 # multiple statements on one line (semicolon) bandit: - run: true options: config: .bandit.yaml - -pyroma: - run: true - -mccabe: - run: false - -dodgy: - run: false diff --git a/Dockerfile b/Dockerfile index 5c72fb1cf..b09ff9754 100644 --- a/Dockerfile +++ b/Dockerfile @@ -98,7 +98,7 @@ ENV C2C_BASE_PATH=/c2c \ SENTRY_CLIENT_RELEASE=latest \ SENTRY_TAG_SERVICE=app -CMD ["/usr/local/bin/gunicorn"] +CMD ["/venv/bin/gunicorn"] COPY production.ini /app/ diff --git a/acceptance_tests/app/poetry.lock b/acceptance_tests/app/poetry.lock index eb2f1862f..19852bd23 100644 --- a/acceptance_tests/app/poetry.lock +++ b/acceptance_tests/app/poetry.lock @@ -1,23 +1,18 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "astroid" -version = "2.15.8" +version = "3.3.5" description = "An abstract syntax tree for Python with inference support." optional = false -python-versions = ">=3.7.2" +python-versions = ">=3.9.0" files = [ - {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"}, - {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"}, + {file = "astroid-3.3.5-py3-none-any.whl", hash = "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8"}, + {file = "astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d"}, ] [package.dependencies] -lazy-object-proxy = ">=1.4.0" typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} -wrapt = [ - {version = ">=1.11,<2", markers = "python_version < \"3.11\""}, - {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, -] [[package]] name = "bandit" @@ -240,52 +235,6 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] -[[package]] -name = "lazy-object-proxy" -version = "1.10.0" -description = "A fast and thorough lazy object proxy." -optional = false -python-versions = ">=3.8" -files = [ - {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"}, - {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, -] - [[package]] name = "markdown-it-py" version = "3.0.0" @@ -455,13 +404,13 @@ type = ["mypy (>=1.11.2)"] [[package]] name = "prospector" -version = "1.11.0" +version = "1.12.0" description = "Prospector is a tool to analyse Python code by aggregating the result of other tools." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "prospector-1.11.0-py3-none-any.whl", hash = "sha256:2b112842e5a19d6f77c886d89f57b619c19b47afdf6db31019d85d93e0bdc29d"}, - {file = "prospector-1.11.0.tar.gz", hash = "sha256:49236fe334652d1229a85bc10789ac4390cc5027f6becda989ea473f893dde57"}, + {file = "prospector-1.12.0-py3-none-any.whl", hash = "sha256:485d889543c8b47e495b2abbf22151e64b3494564874c6e554564f550a891e37"}, + {file = "prospector-1.12.0.tar.gz", hash = "sha256:e598dddc406cbfe8b31a20d4391c7551841ed6772897a290ecaf272ee1ffabf6"}, ] [package.dependencies] @@ -476,7 +425,7 @@ pep8-naming = ">=0.3.3,<=0.10.0" pycodestyle = ">=2.9.0" pydocstyle = ">=2.0.0" pyflakes = ">=2.2.0,<4" -pylint = ">=2.8.3,<3.0" +pylint = ">=3.0" pylint-celery = "0.3" pylint-django = ">=2.5,<2.6" pylint-flask = "0.6" @@ -494,6 +443,28 @@ with-pyright = ["pyright (>=1.1.3)"] with-pyroma = ["pyroma (>=2.4)"] with-vulture = ["vulture (>=1.5)"] +[[package]] +name = "prospector-profile-duplicated" +version = "1.6.0" +description = "Profile that can be used to disable the duplicated or conflict rules between Prospector and other tools" +optional = false +python-versions = "*" +files = [ + {file = "prospector_profile_duplicated-1.6.0-py2.py3-none-any.whl", hash = "sha256:bf6a6aae0c7de48043b95e4d42e23ccd090c6c7115b6ee8c8ca472ffb1a2022b"}, + {file = "prospector_profile_duplicated-1.6.0.tar.gz", hash = "sha256:9c2d541076537405e8b2484cb6222276a2df17492391b6af1b192695770aab83"}, +] + +[[package]] +name = "prospector-profile-utils" +version = "1.9.1" +description = "Some utility Prospector profiles." +optional = false +python-versions = "*" +files = [ + {file = "prospector_profile_utils-1.9.1-py2.py3-none-any.whl", hash = "sha256:b458d8c4d59bdb1547e4630a2c6de4971946c4f0999443db6a9eef6d216b26b8"}, + {file = "prospector_profile_utils-1.9.1.tar.gz", hash = "sha256:008efa6797a85233fd8093dcb9d86f5fa5d89673e431c15cb1496a91c9b2c601"}, +] + [[package]] name = "pycodestyle" version = "2.12.1" @@ -549,23 +520,24 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pylint" -version = "2.17.7" +version = "3.3.1" description = "python code static checker" optional = false -python-versions = ">=3.7.2" +python-versions = ">=3.9.0" files = [ - {file = "pylint-2.17.7-py3-none-any.whl", hash = "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87"}, - {file = "pylint-2.17.7.tar.gz", hash = "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad"}, + {file = "pylint-3.3.1-py3-none-any.whl", hash = "sha256:2f846a466dd023513240bc140ad2dd73bfc080a5d85a710afdb728c420a5a2b9"}, + {file = "pylint-3.3.1.tar.gz", hash = "sha256:9f3dcc87b1203e612b78d91a896407787e708b3f189b5fa0b307712d49ff0c6e"}, ] [package.dependencies] -astroid = ">=2.15.8,<=2.17.0-dev0" +astroid = ">=3.3.4,<=3.4.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] -isort = ">=4.2.5,<6" +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -592,21 +564,21 @@ pylint-plugin-utils = ">=0.2.1" [[package]] name = "pylint-django" -version = "2.5.3" +version = "2.5.2" description = "A Pylint plugin to help Pylint understand the Django web framework" optional = false python-versions = "*" files = [ - {file = "pylint-django-2.5.3.tar.gz", hash = "sha256:0ac090d106c62fe33782a1d01bda1610b761bb1c9bf5035ced9d5f23a13d8591"}, - {file = "pylint_django-2.5.3-py3-none-any.whl", hash = "sha256:56b12b6adf56d548412445bd35483034394a1a94901c3f8571980a13882299d5"}, + {file = "pylint-django-2.5.2.tar.gz", hash = "sha256:1933d82b4a92538a3b12aef91adfd7d866befd051d7a02d6245b0f965587d97c"}, + {file = "pylint_django-2.5.2-py3-none-any.whl", hash = "sha256:286dce8a31bc8ed5a523e8d8742b5a0e083b87f5f140ea4cde9aad612c03bd2d"}, ] [package.dependencies] -pylint = ">=2.0,<3" +pylint = ">=2.0" pylint-plugin-utils = ">=0.7" [package.extras] -for-tests = ["coverage", "django-tables2", "django-tastypie", "factory-boy", "pylint (>=2.13)", "pytest", "wheel"] +for-tests = ["coverage", "django-tables2", "django-tastypie", "factory-boy", "pytest", "wheel"] with-django = ["Django"] [[package]] @@ -700,17 +672,17 @@ files = [ [[package]] name = "requirements-detector" -version = "1.2.2" +version = "1.3.1" description = "Python tool to find and list requirements of a Python project" optional = false -python-versions = ">=3.7,<4.0" +python-versions = "<4.0,>=3.8" files = [ - {file = "requirements_detector-1.2.2-py3-none-any.whl", hash = "sha256:d7c60493bf166da3dd59de0e6cb25765e0e32a1931aeae92614034e5786d0bd0"}, - {file = "requirements_detector-1.2.2.tar.gz", hash = "sha256:3642cd7a5b261d79536c36bb7ecacf2adabd902d2e0e42bfb2ba82515da10501"}, + {file = "requirements_detector-1.3.1-py3-none-any.whl", hash = "sha256:3ef72e1c5c3ad11100058e8f074a5762a4902985e698099d2e7f1283758d4045"}, + {file = "requirements_detector-1.3.1.tar.gz", hash = "sha256:b89e34faf0e4d17f5736923918bd5401949cbe723294ccfefd698b3cda28e676"}, ] [package.dependencies] -astroid = ">=2.0,<3.0" +astroid = ">=3.0,<4.0" packaging = ">=21.3" semver = ">=3.0.0,<4.0.0" toml = ">=0.10.2,<0.11.0" @@ -839,86 +811,7 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -[[package]] -name = "wrapt" -version = "1.16.0" -description = "Module for decorators, wrappers and monkey patching." -optional = false -python-versions = ">=3.6" -files = [ - {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, - {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, - {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, - {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, - {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, - {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, - {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, - {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, - {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, - {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, - {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, - {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, - {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, - {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, - {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, - {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, - {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, - {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, -] - [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "7fea579c1d945b815ec2cc1510cb99edbbf9b97955ff8c0735a17a7959d2b963" +content-hash = "7ec9c729d2ffd7cdd9eae80e6058ae588f49ea1c6c10293ebe587b5b4dbc64b8" diff --git a/acceptance_tests/app/pyproject.toml b/acceptance_tests/app/pyproject.toml index 1e43e4c64..c777e6655 100644 --- a/acceptance_tests/app/pyproject.toml +++ b/acceptance_tests/app/pyproject.toml @@ -40,7 +40,9 @@ coverage = "7.6.1" [tool.poetry.dev-dependencies] # pylint = { version = "2.15.6" } -prospector = { extras = ["with_bandit", "with_mypy"], version = "1.11.0" } +prospector = { extras = ["with_bandit", "with_mypy"], version = "1.12.0" } +prospector-profile-duplicated = "1.6.0" +prospector-profile-utils = "1.9.1" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/acceptance_tests/tests/tests/test_db_maintenance.py b/acceptance_tests/tests/tests/test_db_maintenance.py index 41691fe4b..bedcfd599 100644 --- a/acceptance_tests/tests/tests/test_db_maintenance.py +++ b/acceptance_tests/tests/tests/test_db_maintenance.py @@ -3,7 +3,7 @@ import pytest -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) def _query(app_connection, params, expected=None): diff --git a/c2cwsgiutils/__init__.py b/c2cwsgiutils/__init__.py index 12f343e1b..70a7fff0d 100644 --- a/c2cwsgiutils/__init__.py +++ b/c2cwsgiutils/__init__.py @@ -6,7 +6,7 @@ from configparser import SectionProxy from typing import Any -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) def get_config_defaults() -> dict[str, str]: @@ -22,7 +22,7 @@ def get_config_defaults() -> dict[str, str]: lowercase_keys: set[str] = set() for key, value in os.environ.items(): if key.lower() in lowercase_keys: - LOG.warning("The environment variable '%s' is duplicated with different case, ignoring", key) + _LOG.warning("The environment variable '%s' is duplicated with different case, ignoring", key) continue lowercase_keys.add(key.lower()) result[key] = value.replace("%", "%%") diff --git a/c2cwsgiutils/acceptance/__init__.py b/c2cwsgiutils/acceptance/__init__.py index 9bbdc60ad..2a08e32e7 100644 --- a/c2cwsgiutils/acceptance/__init__.py +++ b/c2cwsgiutils/acceptance/__init__.py @@ -3,7 +3,7 @@ import typing from functools import wraps -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) def retry( @@ -16,7 +16,6 @@ def retry( original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry Arguments: - exception_to_check: the exception to check. may be a tuple of exceptions to check tries: number of times to try (not retry) before giving up delay: initial delay between retries in seconds @@ -32,7 +31,7 @@ def f_retry(*args: typing.Any, **kwargs: typing.Any) -> typing.Any: return f(*args, **kwargs) except exception_to_check as e: msg = f"{e:s}, Retrying in {mdelay:d} seconds..." - LOG.warning(msg) + _LOG.warning(msg) time.sleep(mdelay) mtries -= 1 mdelay *= backoff diff --git a/c2cwsgiutils/acceptance/connection.py b/c2cwsgiutils/acceptance/connection.py index 316767102..b6938701e 100644 --- a/c2cwsgiutils/acceptance/connection.py +++ b/c2cwsgiutils/acceptance/connection.py @@ -82,8 +82,7 @@ def get_xml( **kwargs: Any, ) -> Any: """Get the given URL (relative to the root of API).""" - - from lxml import etree # nosec + from lxml import etree # nosec # pylint: disable=import-outside-toplevel with self.session.get( self.base_url + url, diff --git a/c2cwsgiutils/acceptance/image.py b/c2cwsgiutils/acceptance/image.py index 3a1b3ed5e..6bb123b5a 100644 --- a/c2cwsgiutils/acceptance/image.py +++ b/c2cwsgiutils/acceptance/image.py @@ -3,11 +3,11 @@ import subprocess # nosec from typing import TYPE_CHECKING, Any, Optional -import numpy as np -import skimage.color -import skimage.io -import skimage.metrics -import skimage.transform +import numpy as np # pylint: disable=import-error +import skimage.color # pylint: disable=import-error +import skimage.io # pylint: disable=import-error +import skimage.metrics # pylint: disable=import-error +import skimage.transform # pylint: disable=import-error if TYPE_CHECKING: from typing import TypeAlias @@ -189,7 +189,7 @@ def check_screenshot( See also `check_image` for the other parameters. - Args: + Arguments: url: The URL to screenshot width: The width of the generated screenshot height: The height of the generated screenshot @@ -202,7 +202,6 @@ def check_screenshot( generate_expected_image: See `check_image` use_mask: See `check_image` """ - if headers is None: headers = {} if media is None: diff --git a/c2cwsgiutils/acceptance/print.py b/c2cwsgiutils/acceptance/print.py index 6ff808748..e3b59618a 100644 --- a/c2cwsgiutils/acceptance/print.py +++ b/c2cwsgiutils/acceptance/print.py @@ -7,7 +7,7 @@ from c2cwsgiutils.acceptance import connection, utils -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) class PrintConnection(connection.Connection): @@ -30,9 +30,11 @@ def wait_ready(self, timeout: int = 60, app: str = "default") -> None: utils.retry_timeout(functools.partial(self.get_capabilities, app=app), timeout=timeout) def get_capabilities(self, app: str) -> Any: + """Get the capabilities for the given application.""" return self.get_json(app + "/capabilities.json", cache_expected=connection.CacheExpected.YES) def get_example_requests(self, app: str) -> dict[str, Any]: + """Get the example requests for the given application.""" samples = self.get_json(app + "/exampleRequest.json", cache_expected=connection.CacheExpected.YES) out = {} for name, value in samples.items(): @@ -40,12 +42,13 @@ def get_example_requests(self, app: str) -> dict[str, Any]: return out def get_pdf(self, app: str, request: dict[str, Any], timeout: int = 60) -> requests.Response: + """Create a report and wait for it to be ready.""" create_report = self.post_json(app + "/report.pdf", json=request) - LOG.debug("create_report=%s", create_report) + _LOG.debug("create_report=%s", create_report) ref = create_report["ref"] status = utils.retry_timeout(functools.partial(self._check_completion, ref), timeout=timeout) - LOG.debug("status=%s", repr(status)) + _LOG.debug("status=%s", repr(status)) assert status["status"] == "finished" report = self.get_raw("report/" + ref) @@ -59,4 +62,5 @@ def _check_completion(self, ref: str) -> Optional[Any]: return None def get_apps(self) -> Any: + """Get the list of available applications.""" return self.get_json("apps.json", cache_expected=connection.CacheExpected.YES) diff --git a/c2cwsgiutils/acceptance/utils.py b/c2cwsgiutils/acceptance/utils.py index adf8d3e34..60f9b2eff 100644 --- a/c2cwsgiutils/acceptance/utils.py +++ b/c2cwsgiutils/acceptance/utils.py @@ -29,12 +29,10 @@ def retry_timeout(what: Callable[[], Any], timeout: float = _DEFAULT_TIMEOUT, in Retry the function until the timeout. Arguments: - what: the function to try timeout: the timeout to get a success interval: the interval between try """ - timeout = time.perf_counter() + timeout while True: error = "" @@ -58,7 +56,7 @@ def approx(struct: Any, **kwargs: Any) -> Any: See pytest.approx """ - import boltons.iterutils + import boltons.iterutils # pylint: disable=import-outside-toplevel if isinstance(struct, float): return pytest.approx(struct, **kwargs) diff --git a/c2cwsgiutils/auth.py b/c2cwsgiutils/auth.py index 89aa3affe..9e6909d8b 100644 --- a/c2cwsgiutils/auth.py +++ b/c2cwsgiutils/auth.py @@ -11,21 +11,21 @@ from c2cwsgiutils.config_utils import config_bool, env_or_config, env_or_settings -COOKIE_AGE = 7 * 24 * 3600 +_COOKIE_AGE = 7 * 24 * 3600 SECRET_PROP = "c2c.secret" # nosec # noqa SECRET_ENV = "C2C_SECRET" # nosec # noqa -GITHUB_REPOSITORY_PROP = "c2c.auth.github.repository" -GITHUB_REPOSITORY_ENV = "C2C_AUTH_GITHUB_REPOSITORY" -GITHUB_ACCESS_TYPE_PROP = "c2c.auth.github.access_type" -GITHUB_ACCESS_TYPE_ENV = "C2C_AUTH_GITHUB_ACCESS_TYPE" +_GITHUB_REPOSITORY_PROP = "c2c.auth.github.repository" +_GITHUB_REPOSITORY_ENV = "C2C_AUTH_GITHUB_REPOSITORY" +_GITHUB_ACCESS_TYPE_PROP = "c2c.auth.github.access_type" +_GITHUB_ACCESS_TYPE_ENV = "C2C_AUTH_GITHUB_ACCESS_TYPE" GITHUB_AUTH_URL_PROP = "c2c.auth.github.auth_url" GITHUB_AUTH_URL_ENV = "C2C_AUTH_GITHUB_AUTH_URL" GITHUB_TOKEN_URL_PROP = "c2c.auth.github.token_url" # nosec GITHUB_TOKEN_URL_ENV = "C2C_AUTH_GITHUB_TOKEN_URL" # nosec GITHUB_USER_URL_PROP = "c2c.auth.github.user_url" GITHUB_USER_URL_ENV = "C2C_AUTH_GITHUB_USER_URL" -GITHUB_REPO_URL_PROP = "c2c.auth.github.repo_url" -GITHUB_REPO_URL_ENV = "C2C_AUTH_GITHUB_REPO_URL" +_GITHUB_REPO_URL_PROP = "c2c.auth.github.repo_url" +_GITHUB_REPO_URL_ENV = "C2C_AUTH_GITHUB_REPO_URL" GITHUB_CLIENT_ID_PROP = "c2c.auth.github.client_id" GITHUB_CLIENT_ID_ENV = "C2C_AUTH_GITHUB_CLIENT_ID" GITHUB_CLIENT_SECRET_PROP = "c2c.auth.github.client_secret" # nosec # noqa @@ -44,7 +44,7 @@ USE_SESSION_ENV = "C2C_USE_SESSION" -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) class AuthConfig(TypedDict, total=False): @@ -92,11 +92,11 @@ def _is_auth_secret(request: pyramid.request.Request) -> bool: return False if secret_hash != _hash_secret(expected): return False - # login or refresh the cookie + # Login or refresh the cookie request.response.set_cookie( - SECRET_ENV, secret_hash, max_age=COOKIE_AGE, httponly=True, secure=True, samesite="Strict" + SECRET_ENV, secret_hash, max_age=_COOKIE_AGE, httponly=True, secure=True, samesite="Strict" ) - # since this could be used from outside c2cwsgiutils views, we cannot set the path to c2c + # Since this could be used from outside c2cwsgiutils views, we cannot set the path to c2c return True return False @@ -127,7 +127,7 @@ def _is_auth_user_github(request: pyramid.request.Request) -> tuple[bool, UserDe ), ) except jwt.exceptions.InvalidTokenError as e: - LOG.warning("Error on decoding JWT token: %s", e) + _LOG.warning("Error on decoding JWT token: %s", e) return False, {} @@ -181,11 +181,11 @@ def auth_type(settings: Optional[Mapping[str, Any]]) -> Optional[AuthenticationT has_client_secret = ( env_or_settings(settings, GITHUB_CLIENT_SECRET_ENV, GITHUB_CLIENT_SECRET_PROP, "") != "" ) - has_repo = env_or_settings(settings, GITHUB_REPOSITORY_ENV, GITHUB_REPOSITORY_PROP, "") != "" + has_repo = env_or_settings(settings, _GITHUB_REPOSITORY_ENV, _GITHUB_REPOSITORY_PROP, "") != "" secret = env_or_settings(settings, GITHUB_AUTH_SECRET_ENV, GITHUB_AUTH_SECRET_PROP, "") has_secret = len(secret) >= 16 if secret and not has_secret: - LOG.error( + _LOG.error( "You set a too short secret (length: %i) to protect the admin page, it should have " "at lease a length of 16", len(secret), @@ -205,10 +205,11 @@ def check_access( If the authentication type is not GitHub, this function is equivalent to is_auth. - `repo` is the repository to check access to (/). - `access_type` is the type of access to check (admin|push|pull). + Arguments: + request: is the request object. + repo: is the repository to check access to (/). + access_type: is the type of access to check (admin|push|pull). """ - if not is_auth(request): return False @@ -222,8 +223,8 @@ def check_access( "github_repository": ( env_or_settings( settings, - GITHUB_REPOSITORY_ENV, - GITHUB_REPOSITORY_PROP, + _GITHUB_REPOSITORY_ENV, + _GITHUB_REPOSITORY_PROP, "", ) if repo is None @@ -232,8 +233,8 @@ def check_access( "github_access_type": ( env_or_settings( settings, - GITHUB_ACCESS_TYPE_ENV, - GITHUB_ACCESS_TYPE_PROP, + _GITHUB_ACCESS_TYPE_ENV, + _GITHUB_ACCESS_TYPE_PROP, "pull", ) if access_type is None @@ -245,7 +246,6 @@ def check_access( def check_access_config(request: pyramid.request.Request, auth_config: AuthConfig) -> bool: """Check if the user has access to the resource.""" - auth, user = is_auth_user(request) if not auth: return False @@ -261,8 +261,8 @@ def check_access_config(request: pyramid.request.Request, auth_config: AuthConfi repo_url = env_or_settings( settings, - GITHUB_REPO_URL_ENV, - GITHUB_REPO_URL_PROP, + _GITHUB_REPO_URL_ENV, + _GITHUB_REPO_URL_PROP, "https://api.github.com/repos", ) repository = oauth.get(f"{repo_url}/{auth_config.get('github_repository')}").json() diff --git a/c2cwsgiutils/broadcast/__init__.py b/c2cwsgiutils/broadcast/__init__.py index c7b4d605d..aa8350cef 100644 --- a/c2cwsgiutils/broadcast/__init__.py +++ b/c2cwsgiutils/broadcast/__init__.py @@ -10,9 +10,9 @@ from c2cwsgiutils import config_utils, redis_utils from c2cwsgiutils.broadcast import interface, local, redis -LOG = logging.getLogger(__name__) -BROADCAST_ENV_KEY = "C2C_BROADCAST_PREFIX" -BROADCAST_CONFIG_KEY = "c2c.broadcast_prefix" +_LOG = logging.getLogger(__name__) +_BROADCAST_ENV_KEY = "C2C_BROADCAST_PREFIX" +_BROADCAST_CONFIG_KEY = "c2c.broadcast_prefix" _broadcaster: Optional[interface.BaseBroadcaster] = None @@ -29,38 +29,37 @@ def includeme(config: Optional[pyramid.config.Configurator] = None) -> None: Otherwise, fall back to a fake local implementation. """ - global _broadcaster + global _broadcaster # pylint: disable=global-statement broadcast_prefix = config_utils.env_or_config( - config, BROADCAST_ENV_KEY, BROADCAST_CONFIG_KEY, "broadcast_api_" + config, _BROADCAST_ENV_KEY, _BROADCAST_CONFIG_KEY, "broadcast_api_" ) master, slave, _ = redis_utils.get(config.get_settings() if config else None) if _broadcaster is None: if master is not None and slave is not None: _broadcaster = redis.RedisBroadcaster(broadcast_prefix, master, slave) - LOG.info("Broadcast service setup using Redis implementation") + _LOG.info("Broadcast service setup using Redis implementation") else: _broadcaster = local.LocalBroadcaster() - LOG.info("Broadcast service setup using local implementation") + _LOG.info("Broadcast service setup using local implementation") elif isinstance(_broadcaster, local.LocalBroadcaster) and master is not None and slave is not None: - LOG.info("Switching from a local broadcaster to a Redis broadcaster") + _LOG.info("Switching from a local broadcaster to a Redis broadcaster") prev_broadcaster = _broadcaster _broadcaster = redis.RedisBroadcaster(broadcast_prefix, master, slave) _broadcaster.copy_local_subscriptions(prev_broadcaster) def _get(need_init: bool = False) -> interface.BaseBroadcaster: - global _broadcaster + global _broadcaster # pylint: disable=global-statement if _broadcaster is None: if need_init: - LOG.error("Broadcast functionality used before it is setup") + _LOG.error("Broadcast functionality used before it is setup") _broadcaster = local.LocalBroadcaster() return _broadcaster def cleanup() -> None: """Cleanup the broadcaster to force to reinitialize it.""" - - global _broadcaster + global _broadcaster # pylint: disable=global-statement _broadcaster = None @@ -96,21 +95,21 @@ def broadcast( # We can also templatise the argument with Python 3.10 # See: https://www.python.org/dev/peps/pep-0612/ -_DECORATOR_RETURN = TypeVar("_DECORATOR_RETURN") +_DecoratorReturn = TypeVar("_DecoratorReturn") def decorator( channel: Optional[str] = None, expect_answers: bool = False, timeout: float = 10 -) -> Callable[[Callable[..., _DECORATOR_RETURN]], Callable[..., Optional[list[_DECORATOR_RETURN]]]]: +) -> Callable[[Callable[..., _DecoratorReturn]], Callable[..., Optional[list[_DecoratorReturn]]]]: """ Decorate function will be called through the broadcast functionality. If expect_answers is set to True, the returned value will be a list of all the answers. """ - def impl(func: Callable[..., _DECORATOR_RETURN]) -> Callable[..., Optional[list[_DECORATOR_RETURN]]]: + def impl(func: Callable[..., _DecoratorReturn]) -> Callable[..., Optional[list[_DecoratorReturn]]]: @functools.wraps(func) - def wrapper(**kwargs: Any) -> Optional[list[_DECORATOR_RETURN]]: + def wrapper(**kwargs: Any) -> Optional[list[_DecoratorReturn]]: return broadcast(_channel, params=kwargs, expect_answers=expect_answers, timeout=timeout) if channel is None: diff --git a/c2cwsgiutils/broadcast/interface.py b/c2cwsgiutils/broadcast/interface.py index 4fba288ba..8c6a71cfa 100644 --- a/c2cwsgiutils/broadcast/interface.py +++ b/c2cwsgiutils/broadcast/interface.py @@ -8,14 +8,14 @@ class BaseBroadcaster: @abstractmethod def subscribe(self, channel: str, callback: Callable[..., Any]) -> None: - pass # pragma: no cover + """Subscribe to a channel.""" @abstractmethod def unsubscribe(self, channel: str) -> None: - pass # pragma: no cover + """Unsubscribe from a channel.""" @abstractmethod def broadcast( self, channel: str, params: Mapping[str, Any], expect_answers: bool, timeout: float ) -> Optional[list[Any]]: - pass # pragma: no cover + """Broadcast a message to a channel.""" diff --git a/c2cwsgiutils/broadcast/local.py b/c2cwsgiutils/broadcast/local.py index ea0be57da..6b4dbd837 100644 --- a/c2cwsgiutils/broadcast/local.py +++ b/c2cwsgiutils/broadcast/local.py @@ -25,4 +25,5 @@ def broadcast( return answers if expect_answers else None def get_subscribers(self) -> Mapping[str, Callable[..., Any]]: + """Get the subscribers for testing purposes.""" return self._subscribers diff --git a/c2cwsgiutils/broadcast/redis.py b/c2cwsgiutils/broadcast/redis.py index 916253678..173b4add6 100644 --- a/c2cwsgiutils/broadcast/redis.py +++ b/c2cwsgiutils/broadcast/redis.py @@ -11,7 +11,7 @@ from c2cwsgiutils.broadcast import interface, local, utils -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) class RedisBroadcaster(interface.BaseBroadcaster): @@ -23,7 +23,7 @@ def __init__( master: "redis.client.Redis[str]", slave: "redis.client.Redis[str]", ) -> None: - from c2cwsgiutils import redis_utils + from c2cwsgiutils import redis_utils # pylint: disable=import-outside-toplevel self._master = master self._slave = slave @@ -41,24 +41,24 @@ def _get_channel(self, channel: str) -> str: def subscribe(self, channel: str, callback: Callable[..., Any]) -> None: def wrapper(message: Mapping[str, Any]) -> None: - LOG.debug("Received a broadcast on %s: %s", message["channel"], repr(message["data"])) + _LOG.debug("Received a broadcast on %s: %s", message["channel"], repr(message["data"])) data = json.loads(message["data"]) try: response = callback(**data["params"]) except Exception as e: # pragma: no cover # pylint: disable=broad-except - LOG.error("Failed handling a broadcast message", exc_info=True) + _LOG.error("Failed handling a broadcast message", exc_info=True) response = {"status": 500, "message": str(e)} answer_channel = data.get("answer_channel") if answer_channel is not None: - LOG.debug("Sending broadcast answer on %s", answer_channel) + _LOG.debug("Sending broadcast answer on %s", answer_channel) self._master.publish(answer_channel, json.dumps(utils.add_host_info(response))) actual_channel = self._get_channel(channel) - LOG.debug("Subscribing %s.%s to %s", callback.__module__, callback.__name__, actual_channel) + _LOG.debug("Subscribing %s.%s to %s", callback.__module__, callback.__name__, actual_channel) self._pub_sub.subscribe(**{actual_channel: wrapper}) def unsubscribe(self, channel: str) -> None: - LOG.debug("Unsubscribing from %s") + _LOG.debug("Unsubscribing from %s") actual_channel = self._get_channel(channel) self._pub_sub.unsubscribe(actual_channel) @@ -79,7 +79,7 @@ def _broadcast_with_answer( assert self._thread.is_alive() def callback(msg: Mapping[str, Any]) -> None: - LOG.debug("Received a broadcast answer on %s", msg["channel"]) + _LOG.debug("Received a broadcast answer on %s", msg["channel"]) with cond: answers.append(json.loads(msg["data"])) cond.notify() @@ -87,7 +87,7 @@ def callback(msg: Mapping[str, Any]) -> None: answer_channel = self._get_channel(channel) + "".join( random.choice(string.ascii_uppercase + string.digits) for _ in range(10) # nosec ) - LOG.debug("Subscribing for broadcast answers on %s", answer_channel) + _LOG.debug("Subscribing for broadcast answers on %s", answer_channel) self._pub_sub.subscribe(**{answer_channel: callback}) message = {"params": params, "answer_channel": answer_channel} @@ -99,7 +99,7 @@ def callback(msg: Mapping[str, Any]) -> None: while len(answers) < nb_received: to_wait = timeout_time - time.perf_counter() if to_wait <= 0.0: # pragma: no cover - LOG.warning( + _LOG.warning( "timeout waiting for %d/%d answers on %s", len(answers), nb_received, @@ -115,11 +115,12 @@ def callback(msg: Mapping[str, Any]) -> None: def _broadcast(self, channel: str, message: Mapping[str, Any]) -> int: actual_channel = self._get_channel(channel) - LOG.debug("Sending a broadcast on %s", actual_channel) + _LOG.debug("Sending a broadcast on %s", actual_channel) nb_received = self._master.publish(actual_channel, json.dumps(message)) - LOG.debug("Broadcast on %s sent to %d listeners", actual_channel, nb_received) + _LOG.debug("Broadcast on %s sent to %d listeners", actual_channel, nb_received) return nb_received def copy_local_subscriptions(self, prev_broadcaster: local.LocalBroadcaster) -> None: + """Copy the subscriptions from a local broadcaster.""" for channel, callback in prev_broadcaster.get_subscribers().items(): self.subscribe(channel, callback) diff --git a/c2cwsgiutils/client_info.py b/c2cwsgiutils/client_info.py index 0af7cbfab..f4714232f 100644 --- a/c2cwsgiutils/client_info.py +++ b/c2cwsgiutils/client_info.py @@ -71,4 +71,5 @@ def _handle_forwarded(environ: dict[str, str]) -> None: def filter_factory(*args: Any, **kwargs: Any) -> Callable[..., Any]: """Get the filter.""" + del args, kwargs # unused return Filter diff --git a/c2cwsgiutils/coverage_setup.py b/c2cwsgiutils/coverage_setup.py index e184da1a6..59df37037 100644 --- a/c2cwsgiutils/coverage_setup.py +++ b/c2cwsgiutils/coverage_setup.py @@ -5,7 +5,7 @@ import pyramid.config -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) def init() -> None: @@ -16,11 +16,12 @@ def init() -> None: def includeme(config: Optional[pyramid.config.Configurator] = None) -> None: """Initialize the code coverage.""" + del config # unused if os.environ.get("COVERAGE", "0") != "1": return - import coverage + import coverage # pylint: disable=import-outside-toplevel - LOG.warning("Setting up code coverage") + _LOG.warning("Setting up code coverage") report_dir = "/tmp/coverage/api" # nosec os.makedirs(report_dir, exist_ok=True) cov = coverage.Coverage( diff --git a/c2cwsgiutils/db.py b/c2cwsgiutils/db.py index 8230dbf56..7b6c3a818 100644 --- a/c2cwsgiutils/db.py +++ b/c2cwsgiutils/db.py @@ -5,7 +5,7 @@ import warnings from collections.abc import Iterable from re import Pattern -from typing import TYPE_CHECKING, Any, Callable, Optional, Union, cast +from typing import Any, Callable, Optional, Union, cast import pyramid.config import pyramid.config.settings @@ -17,18 +17,14 @@ from sqlalchemy import engine_from_config from zope.sqlalchemy import register -if TYPE_CHECKING: - scoped_session = sqlalchemy.orm.scoped_session[sqlalchemy.orm.Session] - sessionmaker = sqlalchemy.orm.sessionmaker[sqlalchemy.orm.Session] -else: - scoped_session = sqlalchemy.orm.scoped_session - sessionmaker = sqlalchemy.orm.sessionmaker +_scoped_session = sqlalchemy.orm.scoped_session[sqlalchemy.orm.Session] +_sessionmaker = sqlalchemy.orm.sessionmaker[sqlalchemy.orm.Session] # pylint: disable=unsubscriptable-object -LOG = logging.getLogger(__name__) -RE_COMPILE: Callable[[str], Pattern[str]] = re.compile +_LOG = logging.getLogger(__name__) +_RE_COMPILE: Callable[[str], Pattern[str]] = re.compile -force_readonly = False +FORCE_READONLY = False class Tweens: @@ -45,7 +41,7 @@ def setup_session( force_master: Optional[Iterable[str]] = None, force_slave: Optional[Iterable[str]] = None, ) -> tuple[ - Union[sqlalchemy.orm.Session, scoped_session], + Union[sqlalchemy.orm.Session, _scoped_session], sqlalchemy.engine.Engine, sqlalchemy.engine.Engine, ]: @@ -60,8 +56,7 @@ def setup_session( Those parameters are lists of regex that are going to be matched against "{VERB} {PATH}". Warning, the path includes the route_prefix. - Keyword Arguments: - + Arguments: config: The pyramid Configuration object master_prefix: The prefix for the master connection configuration entries in the application \ settings @@ -84,7 +79,7 @@ def setup_session( # Setup a slave DB connection and add a tween to use it. if settings[master_prefix + ".url"] != settings.get(slave_prefix + ".url"): - LOG.info("Using a slave DB for reading %s", master_prefix) + _LOG.info("Using a slave DB for reading %s", master_prefix) ro_engine = sqlalchemy.engine_from_config(config.get_settings(), slave_prefix + ".") ro_engine.c2c_name = slave_prefix # type: ignore tween_name = master_prefix.replace(".", "_") @@ -105,7 +100,7 @@ def create_session( force_master: Optional[Iterable[str]] = None, force_slave: Optional[Iterable[str]] = None, **engine_config: Any, -) -> Union[sqlalchemy.orm.Session, scoped_session]: +) -> Union[sqlalchemy.orm.Session, _scoped_session]: """ Create a SQLAlchemy session. @@ -117,8 +112,7 @@ def create_session( Those parameters are lists of regex that are going to be matched against "{VERB} {PATH}". Warning, the path includes the route_prefix. - Keyword Arguments: - + Arguments: config: The pyramid Configuration object. If None, only master is used name: The name of the check url: The URL for the master DB @@ -140,7 +134,7 @@ def create_session( # Setup a slave DB connection and add a tween to use it. if url != slave_url and config is not None: - LOG.info("Using a slave DB for reading %s", name) + _LOG.info("Using a slave DB for reading %s", name) ro_engine = sqlalchemy.create_engine(slave_url, **engine_config) _add_tween(config, name, db_session, force_master, force_slave) rw_engine.c2c_name = name + "_master" # type: ignore @@ -157,15 +151,15 @@ def create_session( def _add_tween( config: pyramid.config.Configurator, name: str, - db_session: scoped_session, + db_session: _scoped_session, force_master: Optional[Iterable[str]], force_slave: Optional[Iterable[str]], ) -> None: master_paths: Iterable[Pattern[str]] = ( - list(map(RE_COMPILE, force_master)) if force_master is not None else [] + list(map(_RE_COMPILE, force_master)) if force_master is not None else [] ) slave_paths: Iterable[Pattern[str]] = ( - list(map(RE_COMPILE, force_slave)) if force_slave is not None else [] + list(map(_RE_COMPILE, force_slave)) if force_slave is not None else [] ) def db_chooser_tween_factory( @@ -182,18 +176,18 @@ def db_chooser_tween(request: pyramid.request.Request) -> Any: old = session.bind method_path: Any = f"{request.method} {request.path}" has_force_master = any(r.match(method_path) for r in master_paths) - if force_readonly or ( + if FORCE_READONLY or ( not has_force_master and (request.method in ("GET", "OPTIONS") or any(r.match(method_path) for r in slave_paths)) ): - LOG.debug( + _LOG.debug( "Using %s database for: %s", db_session.c2c_ro_bind.c2c_name, # type: ignore method_path, ) session.bind = db_session.c2c_ro_bind # type: ignore else: - LOG.debug( + _LOG.debug( "Using %s database for: %s", db_session.c2c_rw_bind.c2c_name, # type: ignore method_path, @@ -211,7 +205,7 @@ def db_chooser_tween(request: pyramid.request.Request) -> Any: config.add_tween("c2cwsgiutils.db.tweens." + name, over="pyramid_tm.tm_tween_factory") -class SessionFactory(sessionmaker): +class SessionFactory(_sessionmaker): """The custom session factory that manage the read only and read write sessions.""" def __init__( @@ -223,42 +217,43 @@ def __init__( ): super().__init__() self.master_paths: Iterable[Pattern[str]] = ( - list(map(RE_COMPILE, force_master)) if force_master else [] + list(map(_RE_COMPILE, force_master)) if force_master else [] ) - self.slave_paths: Iterable[Pattern[str]] = list(map(RE_COMPILE, force_slave)) if force_slave else [] + self.slave_paths: Iterable[Pattern[str]] = list(map(_RE_COMPILE, force_slave)) if force_slave else [] self.ro_engine = ro_engine self.rw_engine = rw_engine def engine_name(self, readwrite: bool) -> str: + """Get the engine name.""" if readwrite: return cast(str, self.rw_engine.c2c_name) # type: ignore return cast(str, self.ro_engine.c2c_name) # type: ignore def __call__( # type: ignore self, request: Optional[pyramid.request.Request], readwrite: Optional[bool] = None, **local_kw: Any - ) -> scoped_session: + ) -> _scoped_session: if readwrite is not None: - if readwrite and not force_readonly: - LOG.debug("Using %s database", self.rw_engine.c2c_name) # type: ignore + if readwrite and not FORCE_READONLY: + _LOG.debug("Using %s database", self.rw_engine.c2c_name) # type: ignore self.configure(bind=self.rw_engine) else: - LOG.debug("Using %s database", self.ro_engine.c2c_name) # type: ignore + _LOG.debug("Using %s database", self.ro_engine.c2c_name) # type: ignore self.configure(bind=self.ro_engine) else: assert request is not None method_path: str = f"{request.method} {request.path}" if request is not None else "" has_force_master = any(r.match(method_path) for r in self.master_paths) - if force_readonly or ( + if FORCE_READONLY or ( not has_force_master and ( request.method in ("GET", "OPTIONS") or any(r.match(method_path) for r in self.slave_paths) ) ): - LOG.debug("Using %s database for: %s", self.ro_engine.c2c_name, method_path) # type: ignore + _LOG.debug("Using %s database for: %s", self.ro_engine.c2c_name, method_path) # type: ignore self.configure(bind=self.ro_engine) else: - LOG.debug("Using %s database for: %s", self.rw_engine.c2c_name, method_path) # type: ignore + _LOG.debug("Using %s database for: %s", self.rw_engine.c2c_name, method_path) # type: ignore self.configure(bind=self.rw_engine) return super().__call__(**local_kw) # type: ignore @@ -272,15 +267,15 @@ def get_engine( def get_session_factory( engine: sqlalchemy.engine.Engine, -) -> sessionmaker: +) -> _sessionmaker: """Get the session factory from the engine.""" - factory = sessionmaker() + factory = _sessionmaker() factory.configure(bind=engine) return factory def get_tm_session( - session_factory: sessionmaker, + session_factory: _sessionmaker, transaction_manager: transaction.TransactionManager, ) -> sqlalchemy.orm.Session: """ @@ -348,7 +343,7 @@ def get_tm_session_pyramid( session_factory: SessionFactory, transaction_manager: transaction.TransactionManager, request: pyramid.request.Request, -) -> scoped_session: +) -> _scoped_session: """ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. @@ -369,8 +364,7 @@ def init( """ Initialize the database for a Pyramid app. - Keyword Arguments: - + Arguments: config: The pyramid Configuration object master_prefix: The prefix for the master connection configuration entries in the application \ settings @@ -392,7 +386,7 @@ def init( # Setup a slave DB connection and add a tween to use it. if slave_prefix and settings[master_prefix + ".url"] != settings.get(slave_prefix + ".url"): - LOG.info("Using a slave DB for reading %s", master_prefix) + _LOG.info("Using a slave DB for reading %s", master_prefix) ro_engine = get_engine(config.get_settings(), slave_prefix + ".") ro_engine.c2c_name = slave_prefix # type: ignore else: diff --git a/c2cwsgiutils/db_maintenance_view.py b/c2cwsgiutils/db_maintenance_view.py index 2a29af38c..21822025f 100644 --- a/c2cwsgiutils/db_maintenance_view.py +++ b/c2cwsgiutils/db_maintenance_view.py @@ -7,10 +7,10 @@ from c2cwsgiutils import auth, broadcast, config_utils, db, redis_utils -LOG = logging.getLogger(__name__) -CONFIG_KEY = "c2c.db_maintenance_view_enabled" -ENV_KEY = "C2C_DB_MAINTENANCE_VIEW_ENABLED" -REDIS_PREFIX = "c2c_db_maintenance_" +_LOG = logging.getLogger(__name__) +_CONFIG_KEY = "c2c.db_maintenance_view_enabled" +_ENV_KEY = "C2C_DB_MAINTENANCE_VIEW_ENABLED" +_REDIS_PREFIX = "c2c_db_maintenance_" def install_subscriber(config: pyramid.config.Configurator) -> None: @@ -21,7 +21,7 @@ def install_subscriber(config: pyramid.config.Configurator) -> None: def includeme(config: pyramid.config.Configurator) -> None: """Install the view to configure the loggers, if configured to do so.""" - if auth.is_enabled(config, ENV_KEY, CONFIG_KEY): + if auth.is_enabled(config, _ENV_KEY, _CONFIG_KEY): config.add_route( "c2c_db_maintenance", config_utils.get_base_path(config) + r"/db/maintenance", @@ -29,7 +29,7 @@ def includeme(config: pyramid.config.Configurator) -> None: ) config.add_view(_db_maintenance, route_name="c2c_db_maintenance", renderer="fast_json", http_cache=0) _restore(config) - LOG.info("Enabled the /db/maintenance API") + _LOG.info("Enabled the /db/maintenance API") def _db_maintenance(request: pyramid.request.Request) -> Mapping[str, Any]: @@ -38,7 +38,7 @@ def _db_maintenance(request: pyramid.request.Request) -> Mapping[str, Any]: if readonly_param is not None: readonly = readonly_param.lower() == "true" - LOG.critical("Readonly DB status changed from %s to %s", db.force_readonly, readonly) + _LOG.critical("Readonly DB status changed from %s to %s", db.FORCE_READONLY, readonly) _set_readonly(value=readonly) _store(request.registry.settings, readonly) return {"status": 200, "readonly": readonly} @@ -51,7 +51,7 @@ def _db_maintenance(request: pyramid.request.Request) -> Mapping[str, Any]: @broadcast.decorator(expect_answers=True) def _set_readonly(value: bool) -> bool: - db.force_readonly = value + db.FORCE_READONLY = value return True @@ -59,24 +59,24 @@ def _restore(config: pyramid.config.Configurator) -> None: try: readonly = _get_redis_value(config.get_settings()) if readonly is not None: - LOG.debug("Restoring readonly DB status to %s", readonly) - db.force_readonly = readonly == "true" + _LOG.debug("Restoring readonly DB status to %s", readonly) + db.FORCE_READONLY = readonly == "true" except ImportError: pass # don't have redis except Exception: # pylint: disable=broad-except # survive an error since crashing now can have bad consequences for the service. :/ - LOG.error("Cannot restore readonly DB status.", exc_info=True) + _LOG.error("Cannot restore readonly DB status.", exc_info=True) def _store(settings: Mapping[str, Any], readonly: bool) -> None: master, _, _ = redis_utils.get(settings) if master is not None: - master.set(REDIS_PREFIX + "force_readonly", "true" if readonly else "false") + master.set(_REDIS_PREFIX + "force_readonly", "true" if readonly else "false") def _get_redis_value(settings: Mapping[str, Any]) -> Optional[str]: _, slave, _ = redis_utils.get(settings) if slave is not None: - value = slave.get(REDIS_PREFIX + "force_readonly") + value = slave.get(_REDIS_PREFIX + "force_readonly") return str(value) if value else None return None diff --git a/c2cwsgiutils/debug/__init__.py b/c2cwsgiutils/debug/__init__.py index 0ab27b77d..7b3b1b97e 100644 --- a/c2cwsgiutils/debug/__init__.py +++ b/c2cwsgiutils/debug/__init__.py @@ -23,7 +23,7 @@ def init(config: pyramid.config.Configurator) -> None: def includeme(config: pyramid.config.Configurator) -> None: """Initialize the debug tools.""" if auth.is_enabled(config, ENV_KEY, CONFIG_KEY): - from c2cwsgiutils.debug import _views + from c2cwsgiutils.debug import _views # pylint: disable=import-outside-toplevel init_daemon(config) _views.init(config) @@ -37,6 +37,6 @@ def init_daemon(config: Optional[pyramid.config.Configurator] = None) -> None: those requests. """ if config_utils.env_or_config(config, ENV_KEY, CONFIG_KEY, type_=config_utils.config_bool): - from c2cwsgiutils.debug import _listeners + from c2cwsgiutils.debug import _listeners # pylint: disable=import-outside-toplevel _listeners.init() diff --git a/c2cwsgiutils/debug/_listeners.py b/c2cwsgiutils/debug/_listeners.py index f19a419b2..1696806ac 100644 --- a/c2cwsgiutils/debug/_listeners.py +++ b/c2cwsgiutils/debug/_listeners.py @@ -90,7 +90,7 @@ def _dump_memory_impl( for i in sorted(mod_counts.items(), key=lambda x: -x[1])[:limit] ] elif analyze_type == "linecache": - import linecache + import linecache # pylint: disable=import-outside-toplevel cache = linecache.cache result[analyze_type]["biggest_objects"] = sorted( diff --git a/c2cwsgiutils/debug/_views.py b/c2cwsgiutils/debug/_views.py index 8cd7020da..d30a0e56b 100644 --- a/c2cwsgiutils/debug/_views.py +++ b/c2cwsgiutils/debug/_views.py @@ -16,8 +16,8 @@ from c2cwsgiutils import auth, broadcast, config_utils from c2cwsgiutils.debug.utils import dump_memory_maps, get_size -LOG = logging.getLogger(__name__) -SPACE_RE = re.compile(r" +") +_LOG = logging.getLogger(__name__) +_SPACE_RE = re.compile(r" +") def _beautify_stacks(source: list[Mapping[str, Any]]) -> list[Mapping[str, Any]]: @@ -80,7 +80,7 @@ def _dump_memory_diff(request: pyramid.request.Request) -> list[Any]: except Exception: # nosec # pylint: disable=broad-except pass - LOG.debug("checking memory growth for %s", path) + _LOG.debug("checking memory growth for %s", path) peak_stats: dict[Any, Any] = {} for i in range(3): @@ -91,10 +91,10 @@ def _dump_memory_diff(request: pyramid.request.Request) -> list[Any]: response = None try: response = request.invoke_subrequest(sub_request) - LOG.debug("response was %d", response.status_code) + _LOG.debug("response was %d", response.status_code) except HTTPException as ex: - LOG.debug("response was %s", str(ex)) + _LOG.debug("response was %s", str(ex)) del response @@ -142,6 +142,7 @@ def _error(request: pyramid.request.Request) -> Any: def _time(request: pyramid.request.Request) -> Any: + del request # unused return { "local_time": str(datetime.now()), "gmt_time": str(datetime.utcnow()), @@ -211,4 +212,4 @@ def init(config: pyramid.config.Configurator) -> None: _add_view(config, "error", "error", _error) _add_view(config, "time", "time", _time) _add_view(config, "show_refs", "show_refs.dot", _show_refs) - LOG.info("Enabled the /debug/... API") + _LOG.info("Enabled the /debug/... API") diff --git a/c2cwsgiutils/debug/utils.py b/c2cwsgiutils/debug/utils.py index 1957e5dde..11094cd2f 100644 --- a/c2cwsgiutils/debug/utils.py +++ b/c2cwsgiutils/debug/utils.py @@ -8,18 +8,18 @@ from typing import Any # 7ff7d33bd000-7ff7d33be000 r--p 00000000 00:65 49 /usr/lib/toto.so -SMAPS_LOCATION_RE = re.compile(r"^[0-9a-f]+-[0-9a-f]+ +.... +[0-9a-f]+ +[^ ]+ +\d+ +(.*)$") +_SMAPS_LOCATION_RE = re.compile(r"^[0-9a-f]+-[0-9a-f]+ +.... +[0-9a-f]+ +[^ ]+ +\d+ +(.*)$") # Size: 4 kB -SMAPS_ENTRY_RE = re.compile(r"^([\w]+): +(\d+) kB$") +_SMAPS_ENTRY_RE = re.compile(r"^([\w]+): +(\d+) kB$") -BLACKLIST = type, ModuleType, FunctionType -LOG = logging.getLogger(__name__) +_BLACKLIST = type, ModuleType, FunctionType +_LOG = logging.getLogger(__name__) def get_size(obj: Any) -> int: """Get the sum size of object & members.""" - if isinstance(obj, BLACKLIST): + if isinstance(obj, _BLACKLIST): return 0 seen_ids: set[int] = set() size = 0 @@ -27,7 +27,7 @@ def get_size(obj: Any) -> int: while objects: need_referents = [] for obj_ in objects: - if not isinstance(obj_, BLACKLIST) and id(obj_) not in seen_ids: + if not isinstance(obj_, _BLACKLIST) and id(obj_) not in seen_ids: seen_ids.add(id(obj_)) size += sys.getsizeof(obj_) need_referents.append(obj_) @@ -45,11 +45,11 @@ def dump_memory_maps(pid: str = "self") -> list[dict[str, Any]]: sizes: dict[str, Any] = {} for line in input_: line = line.rstrip("\n") - matcher = SMAPS_LOCATION_RE.match(line) + matcher = _SMAPS_LOCATION_RE.match(line) if matcher: cur_dict = sizes.setdefault(matcher.group(1), defaultdict(int)) else: - matcher = SMAPS_ENTRY_RE.match(line) + matcher = _SMAPS_ENTRY_RE.match(line) if matcher: name = matcher.group(1) if name in ("Size", "Rss", "Pss"): @@ -59,5 +59,5 @@ def dump_memory_maps(pid: str = "self") -> list[dict[str, Any]]: and not line.startswith("ProtectionKey:") and not line.startswith("THPeligible:") ): - LOG.debug("Don't know how to parse /proc/%s/smaps line: %s", pid, line) + _LOG.debug("Don't know how to parse /proc/%s/smaps line: %s", pid, line) return [{"name": name, **value} for name, value in sizes.items() if value.get("pss_kb", 0) > 0] diff --git a/c2cwsgiutils/errors.py b/c2cwsgiutils/errors.py index 3150e7ebd..0dd69b212 100644 --- a/c2cwsgiutils/errors.py +++ b/c2cwsgiutils/errors.py @@ -14,14 +14,12 @@ from c2cwsgiutils import auth, config_utils -DEVELOPMENT = os.environ.get("DEVELOPMENT", "0") != "0" -DEPRECATED_CONFIG_KEY = "c2c.error_details_secret" -DEPRECATED_ENV_KEY = "ERROR_DETAILS_SECRET" - -LOG = logging.getLogger(__name__) -STATUS_LOGGER = { - 401: LOG.debug, - 500: LOG.error, +_DEVELOPMENT = os.environ.get("DEVELOPMENT", "0") != "0" + +_LOG = logging.getLogger(__name__) +_STATUS_LOGGER = { + 401: _LOG.debug, + 500: _LOG.error, # The rest are warnings } @@ -53,7 +51,7 @@ def _do_error( request: pyramid.request.Request, status: int, exception: Exception, - logger: Callable[..., None] = LOG.error, + logger: Callable[..., None] = _LOG.error, reduce_info_sent: Callable[[Exception], None] = lambda e: None, ) -> pyramid.response.Response: logger( @@ -82,7 +80,7 @@ def _do_error( def _http_error(exception: HTTPException, request: pyramid.request.Request) -> Any: if request.method != "OPTIONS": - log = STATUS_LOGGER.get(exception.status_code, LOG.warning) + log = _STATUS_LOGGER.get(exception.status_code, _LOG.warning) log( "%s %s returned status code %s: %s", request.method, @@ -102,7 +100,7 @@ def _http_error(exception: HTTPException, request: pyramid.request.Request) -> A def _include_dev_details(request: pyramid.request.Request) -> bool: - return DEVELOPMENT or auth.is_auth(request) + return _DEVELOPMENT or auth.is_auth(request) def _integrity_error( @@ -121,7 +119,7 @@ def _client_interrupted_error( exception: Exception, request: pyramid.request.Request ) -> pyramid.response.Response: # No need to cry wolf if it's just the client that interrupted the connection - return _do_error(request, 500, exception, logger=LOG.info) + return _do_error(request, 500, exception, logger=_LOG.info) def _boto_client_error(exception: Any, request: pyramid.request.Request) -> pyramid.response.Response: @@ -132,7 +130,7 @@ def _boto_client_error(exception: Any, request: pyramid.request.Request) -> pyra status_code = exception.response["ResponseMetadata"]["HTTPStatusCode"] else: status_code = int(exception.response["Error"]["Code"]) - log = STATUS_LOGGER.get(status_code, LOG.warning) + log = _STATUS_LOGGER.get(status_code, _LOG.warning) return _do_error(request, status_code, exception, logger=log) @@ -143,7 +141,7 @@ def _other_error(exception: Exception, request: pyramid.request.Request) -> pyra status = 500 if exception_class == "beaker.exceptions.BeakerException" and str(exception) == "Invalid signature": status = 401 - LOG.debug("Actual exception: %s.%s", exception.__class__.__module__, exception.__class__.__name__) + _LOG.debug("Actual exception: %s.%s", exception.__class__.__module__, exception.__class__.__name__) return _do_error(request, status, exception) @@ -179,4 +177,4 @@ def includeme(config: pyramid.config.Configurator) -> None: config.add_view(view=_client_interrupted_error, context=exception, **common_options) config.add_view(view=_other_error, context=Exception, **common_options) - LOG.info("Installed the error catching views") + _LOG.info("Installed the error catching views") diff --git a/c2cwsgiutils/health_check.py b/c2cwsgiutils/health_check.py index 047387651..643e87c01 100644 --- a/c2cwsgiutils/health_check.py +++ b/c2cwsgiutils/health_check.py @@ -16,7 +16,7 @@ from collections.abc import Mapping from enum import Enum from types import TracebackType -from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Union, cast +from typing import Any, Callable, Literal, Optional, Union, cast import prometheus_client import pyramid.config @@ -30,13 +30,10 @@ import c2cwsgiutils.db from c2cwsgiutils import auth, broadcast, config_utils, prometheus, redis_utils, version -if TYPE_CHECKING: - scoped_session = sqlalchemy.orm.scoped_session[sqlalchemy.orm.Session] -else: - scoped_session = sqlalchemy.orm.scoped_session +_scoped_session = sqlalchemy.orm.scoped_session[sqlalchemy.orm.Session] -LOG = logging.getLogger(__name__) -ALEMBIC_HEAD_RE = re.compile(r"^([a-f0-9]+) \(head\)\n$") +_LOG = logging.getLogger(__name__) +_ALEMBIC_HEAD_RE = re.compile(r"^([a-f0-9]+) \(head\)\n$") _PROMETHEUS_DB_SUMMARY = prometheus_client.Summary( prometheus.build_metric_name("health_check_db"), @@ -78,14 +75,16 @@ def __str__(self) -> str: return self.message def json_data(self) -> Any: + """Return the JSON data to be returned in the response.""" return self.json class _Binding: def name(self) -> str: + """Return the name of the binding.""" raise NotImplementedError() - def __enter__(self) -> scoped_session: + def __enter__(self) -> _scoped_session: raise NotImplementedError() def __exit__( @@ -105,12 +104,12 @@ def __init__(self, session: c2cwsgiutils.db.SessionFactory, readwrite: bool): def name(self) -> str: return self.session.engine_name(self.readwrite) - def __enter__(self) -> scoped_session: + def __enter__(self) -> _scoped_session: return self.session(None, self.readwrite) class _OldBinding(_Binding): - def __init__(self, session: scoped_session, engine: sqlalchemy.engine.Engine): + def __init__(self, session: _scoped_session, engine: sqlalchemy.engine.Engine): self.session = session self.engine = engine self.prev_bind = None @@ -118,7 +117,7 @@ def __init__(self, session: scoped_session, engine: sqlalchemy.engine.Engine): def name(self) -> str: return cast(str, self.engine.c2c_name) # type: ignore - def __enter__(self) -> scoped_session: + def __enter__(self) -> _scoped_session: self.prev_bind = self.session.bind # type: ignore self.session.bind = self.engine return self.session @@ -134,7 +133,7 @@ def __exit__( def _get_binding_class( - session: Union[scoped_session, c2cwsgiutils.db.SessionFactory], + session: Union[_scoped_session, c2cwsgiutils.db.SessionFactory], ro_engin: sqlalchemy.engine.Engine, rw_engin: sqlalchemy.engine.Engine, readwrite: bool, @@ -146,7 +145,7 @@ def _get_binding_class( def _get_bindings( - session: Union[scoped_session, c2cwsgiutils.db.SessionFactory], + session: Union[_scoped_session, c2cwsgiutils.db.SessionFactory], engine_type: EngineType, ) -> list[_Binding]: if isinstance(session, c2cwsgiutils.db.SessionFactory): @@ -184,7 +183,7 @@ def _get_alembic_version(alembic_ini_path: str, name: str) -> str: out = subprocess.check_output( # nosec ["alembic", "--config", alembic_ini_path, "--name", name, "heads"], cwd=dirname, env=env ).decode("utf-8") - out_match = ALEMBIC_HEAD_RE.match(out) + out_match = _ALEMBIC_HEAD_RE.match(out) if not out_match: raise Exception( # pylint: disable=broad-exception-raised "Cannot get the alembic HEAD version from: " + out @@ -219,8 +218,8 @@ def __init__(self, config: pyramid.config.Configurator) -> None: def add_db_session_check( self, - session: Union[scoped_session, c2cwsgiutils.db.SessionFactory], - query_cb: Optional[Callable[[scoped_session], Any]] = None, + session: Union[_scoped_session, c2cwsgiutils.db.SessionFactory], + query_cb: Optional[Callable[[_scoped_session], Any]] = None, at_least_one_model: Optional[object] = None, level: int = 1, engine_type: EngineType = EngineType.READ_AND_WRITE, @@ -229,7 +228,6 @@ def add_db_session_check( Check a DB session is working. You can specify either query_cb or at_least_one_model. Arguments: - session: a DB session created by c2cwsgiutils.db.init() query_cb: a callable that take a session as parameter and check it works at_least_one_model: a model that must have at least one entry in the DB @@ -247,7 +245,7 @@ def add_db_session_check( def add_alembic_check( self, - session: scoped_session, + session: _scoped_session, alembic_ini_path: str, level: int = 2, name: str = "alembic", @@ -258,7 +256,6 @@ def add_alembic_check( Check the DB version against the HEAD version of Alembic. Arguments: - session: A DB session created by c2cwsgiutils.db.init() giving access to the DB \ managed by Alembic alembic_ini_path: Path to the Alembic INI file @@ -283,7 +280,7 @@ def add_alembic_check( assert version_table class _Check: - def __init__(self, session: scoped_session) -> None: + def __init__(self, session: _scoped_session) -> None: self.session = session def __call__(self, request: pyramid.request.Request) -> str: @@ -334,7 +331,6 @@ def add_url_check( Check that a GET on an URL returns 2xx. Arguments: - url: the URL to query or a function taking the request and returning it params: the parameters or a function taking the request and returning them headers: the headers or a function taking the request and returning them @@ -367,7 +363,6 @@ def add_redis_check(self, name: Optional[str] = None, level: int = 1) -> None: One such check is automatically added if the broadcaster is configured with redis. Arguments: - name: the name of the check (defaults to url) level: the level of the health check """ @@ -415,13 +410,13 @@ def add_version_check(self, name: str = "version", level: int = 2) -> None: """ Check that the version matches across all instances. - Keyword Arguments: - + Arguments: name: the name of the check (defaults to "version") level: the level of the health check """ def check(request: pyramid.request.Request) -> dict[str, Any]: + del request # unused ref = version.get_version() all_versions = _get_all_versions() assert all_versions @@ -443,7 +438,6 @@ def add_custom_check( in the response. In case of failure it must raise an exception. Arguments: - name: the name of the check check_cb: the callback to call (takes the request as parameter) level: the level of the health check @@ -489,7 +483,7 @@ def _run_one( _set_success(check_name=name) except Exception as e: # pylint: disable=broad-except _PROMETHEUS_HEALTH_CHECKS_FAILURE.labels(name=name).set(1) - LOG.warning("Health check %s failed", name, exc_info=True) + _LOG.warning("Health check %s failed", name, exc_info=True) failure = {"message": str(e), "timing": time.perf_counter() - start, "level": level} if isinstance(e, JsonCheckException) and e.json_data() is not None: failure["result"] = e.json_data() @@ -500,9 +494,10 @@ def _run_one( @staticmethod def _create_db_engine_check( binding: _Binding, - query_cb: Callable[[scoped_session], None], + query_cb: Callable[[_scoped_session], None], ) -> tuple[str, Callable[[pyramid.request.Request], None]]: def check(request: pyramid.request.Request) -> None: + del request # unused with binding as session: with _PROMETHEUS_DB_SUMMARY.labels( connection=binding.name(), check="database", configuration="" @@ -512,8 +507,8 @@ def check(request: pyramid.request.Request) -> None: return "db_engine_" + binding.name(), check @staticmethod - def _at_least_one(model: Any) -> Callable[[scoped_session], Any]: - def query(session: scoped_session) -> None: + def _at_least_one(model: Any) -> Callable[[_scoped_session], Any]: + def query(session: _scoped_session) -> None: result = session.query(model).first() if result is None: raise HTTPNotFound(model.__name__ + " record not found") @@ -528,7 +523,6 @@ def _maybe_function(what: Any, request: pyramid.request.Request) -> Any: @broadcast.decorator(expect_answers=False) def _set_success(check_name: str) -> None: """Set check in success in all process.""" - _PROMETHEUS_HEALTH_CHECKS_FAILURE.labels(name=check_name).set(0) diff --git a/c2cwsgiutils/index.py b/c2cwsgiutils/index.py index eea0b853b..d7931ef4b 100644 --- a/c2cwsgiutils/index.py +++ b/c2cwsgiutils/index.py @@ -42,12 +42,12 @@ ) from c2cwsgiutils.config_utils import env_or_settings -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) -additional_title: Optional[str] = None -additional_noauth: list[str] = [] -additional_auth: list[str] = [] -ELEM_ID = 0 +_additional_title: Optional[str] = None +_additional_noauth: list[str] = [] +_additional_auth: list[str] = [] +_ELEM_ID = 0 def _url( @@ -113,9 +113,9 @@ def input_( name: str, label: Optional[str] = None, type_: Optional[str] = None, value: Union[str, int] = "" ) -> str: """Get an HTML input.""" - global ELEM_ID - id_ = ELEM_ID - ELEM_ID += 1 + global _ELEM_ID # pylint: disable=global-statement + id_ = _ELEM_ID + _ELEM_ID += 1 if label is None and type_ != "hidden": label = name.replace("_", " ").capitalize() @@ -138,7 +138,6 @@ def input_( def button(label: str) -> str: """Get en HTML button.""" - return f'' @@ -160,15 +159,15 @@ def _index(request: pyramid.request.Request) -> dict[str, str]: body += _logging(request) body += _profiler(request) - if additional_title is not None and (has_access or additional_noauth): - body += additional_title + if _additional_title is not None and (has_access or _additional_noauth): + body += _additional_title body += "\n" if has_access: - body += "\n".join(additional_auth) + body += "\n".join(_additional_auth) body += "\n" - body += "\n".join(additional_noauth) + body += "\n".join(_additional_noauth) settings = request.registry.settings auth_type_ = auth_type(settings) @@ -346,7 +345,6 @@ def _health_check(request: pyramid.request.Request) -> str: def _github_login(request: pyramid.request.Request) -> dict[str, Any]: """Get the view that start the authentication on GitHub.""" - settings = request.registry.settings params = dict(request.params) callback_url = _url( @@ -500,7 +498,7 @@ def includeme(config: pyramid.config.Configurator) -> None: settings = config.get_settings() auth_type_ = auth_type(settings) if auth_type_ == AuthenticationType.SECRET: - LOG.warning( + _LOG.warning( "It is recommended to use OAuth2 with GitHub login instead of the `C2C_SECRET` because it " "protects from brute force attacks and the access grant is personal and can be revoked." ) diff --git a/c2cwsgiutils/loader.py b/c2cwsgiutils/loader.py index 0881a33d5..2d351420e 100644 --- a/c2cwsgiutils/loader.py +++ b/c2cwsgiutils/loader.py @@ -5,7 +5,7 @@ from c2cwsgiutils import get_config_defaults -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) class Loader(BaseLoader): # type: ignore diff --git a/c2cwsgiutils/logging_view.py b/c2cwsgiutils/logging_view.py index 160ae2f95..3fa968f39 100644 --- a/c2cwsgiutils/logging_view.py +++ b/c2cwsgiutils/logging_view.py @@ -7,10 +7,10 @@ from c2cwsgiutils import auth, broadcast, config_utils, redis_utils -LOG = logging.getLogger(__name__) -CONFIG_KEY = "c2c.log_view_enabled" -ENV_KEY = "C2C_LOG_VIEW_ENABLED" -REDIS_PREFIX = "c2c_logging_level_" +_LOG = logging.getLogger(__name__) +_CONFIG_KEY = "c2c.log_view_enabled" +_ENV_KEY = "C2C_LOG_VIEW_ENABLED" +_REDIS_PREFIX = "c2c_logging_level_" def install_subscriber(config: pyramid.config.Configurator) -> None: @@ -21,7 +21,7 @@ def install_subscriber(config: pyramid.config.Configurator) -> None: def includeme(config: pyramid.config.Configurator) -> None: """Install the view to configure the loggers, if configured to do so.""" - if auth.is_enabled(config, ENV_KEY, CONFIG_KEY): + if auth.is_enabled(config, _ENV_KEY, _CONFIG_KEY): config.add_route( "c2c_logging_level", config_utils.get_base_path(config) + r"/logging/level", request_method="GET" ) @@ -29,7 +29,7 @@ def includeme(config: pyramid.config.Configurator) -> None: _logging_change_level, route_name="c2c_logging_level", renderer="fast_json", http_cache=0 ) _restore_overrides(config) - LOG.info("Enabled the /logging/level API") + _LOG.info("Enabled the /logging/level API") def _logging_change_level(request: pyramid.request.Request) -> Mapping[str, Any]: @@ -39,7 +39,7 @@ def _logging_change_level(request: pyramid.request.Request) -> Mapping[str, Any] level = request.params.get("level") logger = logging.getLogger(name) if level is not None: - LOG.critical( + _LOG.critical( "Logging of %s changed from %s to %s", name, logging.getLevelName(logger.level), level ) _set_level(name=name, level=level) @@ -63,20 +63,20 @@ def _set_level(name: str, level: str) -> bool: def _restore_overrides(config: pyramid.config.Configurator) -> None: try: for name, level in _list_overrides(config.get_settings()): - LOG.debug("Restoring logging level override for %s: %s", name, level) + _LOG.debug("Restoring logging level override for %s: %s", name, level) logging.getLogger(name).setLevel(level) except ImportError: pass # don't have redis except Exception: # pylint: disable=broad-except # survive an error there. Logging levels is not business critical... - LOG.warning("Cannot restore logging levels", exc_info=True) + _LOG.warning("Cannot restore logging levels", exc_info=True) def _store_override(settings: Mapping[str, Any], name: str, level: str) -> None: try: master, _, _ = redis_utils.get(settings) if master: - master.set(REDIS_PREFIX + name, level) + master.set(_REDIS_PREFIX + name, level) except ImportError: pass @@ -84,8 +84,8 @@ def _store_override(settings: Mapping[str, Any], name: str, level: str) -> None: def _list_overrides(settings: Mapping[str, Any]) -> Generator[tuple[str, str], None, None]: _, slave, _ = redis_utils.get(settings) if slave is not None: - for key in slave.scan_iter(REDIS_PREFIX + "*"): + for key in slave.scan_iter(_REDIS_PREFIX + "*"): level = slave.get(key) - name = key[len(REDIS_PREFIX) :] + name = key[len(_REDIS_PREFIX) :] if level is not None: yield name, str(level) diff --git a/c2cwsgiutils/models_graph.py b/c2cwsgiutils/models_graph.py index c2417dde2..fc92e580f 100644 --- a/c2cwsgiutils/models_graph.py +++ b/c2cwsgiutils/models_graph.py @@ -7,7 +7,6 @@ def generate_model_graph(module: Any) -> None: """Generate a graphical model of the database classes.""" - base_name = None if len(sys.argv) == 1: base_name = "Base" diff --git a/c2cwsgiutils/pretty_json.py b/c2cwsgiutils/pretty_json.py index 8e56a60d9..67bdede45 100644 --- a/c2cwsgiutils/pretty_json.py +++ b/c2cwsgiutils/pretty_json.py @@ -34,7 +34,6 @@ def init(config: pyramid.config.Configurator) -> None: def includeme(config: pyramid.config.Configurator) -> None: """Initialize json and fast_json renderer.""" - pretty_print = config_bool( env_or_config(config, "C2C_JSON_PRETTY_PRINT", "c2c.json.pretty_print", "false") ) diff --git a/c2cwsgiutils/prometheus.py b/c2cwsgiutils/prometheus.py index 16102b3a9..5b654eade 100644 --- a/c2cwsgiutils/prometheus.py +++ b/c2cwsgiutils/prometheus.py @@ -24,7 +24,6 @@ def start(registry: Optional[prometheus_client.CollectorRegistry] = None) -> None: """Start separate HTTP server to provide the Prometheus metrics.""" - if os.environ.get("C2C_PROMETHEUS_PORT") is not None: broadcast.includeme() @@ -38,20 +37,18 @@ def start(registry: Optional[prometheus_client.CollectorRegistry] = None) -> Non def includeme(config: pyramid.config.Configurator) -> None: """Initialize prometheus_client in pyramid context.""" - + del config # unused broadcast.subscribe("c2cwsgiutils_prometheus_collector_gc", _broadcast_collector_gc) broadcast.subscribe("c2cwsgiutils_prometheus_collector_process", _broadcast_collector_process) def build_metric_name(postfix: str) -> str: """Build the metric name with the prefix from the environment variable.""" - return os.environ.get("C2C_PROMETHEUS_PREFIX", "c2cwsgiutils_") + postfix def cleanup() -> None: """Cleanup the prometheus_client registry.""" - redis_utils.cleanup() broadcast.cleanup() @@ -74,7 +71,6 @@ class SerializedMetric(TypedDict): def _broadcast_collector_gc() -> list[SerializedMetric]: """Get the collected GC gauges.""" - return serialize_collected_data(prometheus_client.GC_COLLECTOR) @@ -85,7 +81,6 @@ def _broadcast_collector_process() -> list[SerializedMetric]: def serialize_collected_data(collector: prometheus_client.registry.Collector) -> list[SerializedMetric]: """Serialize the data from the custom collector.""" - gauges: list[SerializedMetric] = [] for process_gauge in collector.collect(): gauge: SerializedMetric = { @@ -162,7 +157,6 @@ def __init__(self, memory_type: str = "pss", pids: Optional[list[str]] = None): Initialize. Arguments: - memory_type: can be rss, pss or size pids: the list of pids or none """ diff --git a/c2cwsgiutils/pyramid.py b/c2cwsgiutils/pyramid.py index ba103ca32..7c3bdf7b2 100644 --- a/c2cwsgiutils/pyramid.py +++ b/c2cwsgiutils/pyramid.py @@ -30,7 +30,6 @@ def includeme(config: pyramid.config.Configurator) -> None: Initialize all the pyramid services and event handlers provided by this library. Arguments: - config: The pyramid Configuration """ logging.captureWarnings(True) diff --git a/c2cwsgiutils/pyramid_logging.py b/c2cwsgiutils/pyramid_logging.py index 34001d834..fb6d3da42 100644 --- a/c2cwsgiutils/pyramid_logging.py +++ b/c2cwsgiutils/pyramid_logging.py @@ -21,7 +21,7 @@ import cee_syslog_handler from pyramid.threadlocal import get_current_request -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) class _PyramidFilter(logging.Filter): diff --git a/c2cwsgiutils/redis_stats.py b/c2cwsgiutils/redis_stats.py index f974f6025..552005b2d 100644 --- a/c2cwsgiutils/redis_stats.py +++ b/c2cwsgiutils/redis_stats.py @@ -7,8 +7,8 @@ from c2cwsgiutils import config_utils, prometheus -LOG = logging.getLogger(__name__) -ORIG: Optional[Callable[..., Any]] = None +_LOG = logging.getLogger(__name__) +_ORIG: Optional[Callable[..., Any]] = None _PROMETHEUS_REDIS_SUMMARY = prometheus_client.Summary( prometheus.build_metric_name("redis"), @@ -19,9 +19,9 @@ def _execute_command_patch(self: Any, command: str, *args: Any, **options: Any) -> Any: - assert ORIG is not None + assert _ORIG is not None with _PROMETHEUS_REDIS_SUMMARY.labels(command=command).time(): - return ORIG(self, command, *args, **options) + return _ORIG(self, command, *args, **options) def init(config: Optional[pyramid.config.Configurator] = None) -> None: @@ -32,15 +32,15 @@ def init(config: Optional[pyramid.config.Configurator] = None) -> None: def includeme(config: Optional[pyramid.config.Configurator] = None) -> None: """Initialize the Redis tracking.""" - global ORIG + global _ORIG # pylint: disable=global-statement if config_utils.env_or_config( config, "C2C_TRACK_REDIS", "c2c.track_redis", True, config_utils.config_bool ): try: - import redis.client + import redis.client # pylint: disable=import-outside-toplevel - ORIG = redis.client.Redis.execute_command + _ORIG = redis.client.Redis.execute_command redis.client.Redis.execute_command = _execute_command_patch # type: ignore - LOG.info("Enabled the redis tracking") + _LOG.info("Enabled the redis tracking") except Exception: # pragma: nocover # pylint: disable=broad-except - LOG.warning("Cannot enable redis tracking", exc_info=True) + _LOG.warning("Cannot enable redis tracking", exc_info=True) diff --git a/c2cwsgiutils/redis_utils.py b/c2cwsgiutils/redis_utils.py index 3afba2454..0675b1fe0 100644 --- a/c2cwsgiutils/redis_utils.py +++ b/c2cwsgiutils/redis_utils.py @@ -11,19 +11,19 @@ import c2cwsgiutils.config_utils -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) REDIS_URL_KEY = "C2C_REDIS_URL" -REDIS_OPTIONS_KEY = "C2C_REDIS_OPTIONS" +_REDIS_OPTIONS_KEY = "C2C_REDIS_OPTIONS" REDIS_SENTINELS_KEY = "C2C_REDIS_SENTINELS" REDIS_SERVICENAME_KEY = "C2C_REDIS_SERVICENAME" -REDIS_DB_KEY = "C2C_REDIS_DB" +_REDIS_DB_KEY = "C2C_REDIS_DB" REDIS_URL_KEY_PROP = "c2c.redis_url" -REDIS_OPTIONS_KEY_PROP = "c2c.redis_options" +_REDIS_OPTIONS_KEY_PROP = "c2c.redis_options" REDIS_SENTINELS_KEY_PROP = "c2c.redis_sentinels" REDIS_SERVICENAME_KEY_PROP = "c2c.redis_servicename" -REDIS_DB_KEY_PROP = "c2c.redis_db" +_REDIS_DB_KEY_PROP = "c2c.redis_db" _master: Optional["redis.client.Redis[str]"] = None _slave: Optional["redis.client.Redis[str]"] = None @@ -32,7 +32,7 @@ def cleanup() -> None: """Cleanup the redis connections.""" - global _master, _slave, _sentinel + global _master, _slave, _sentinel # pylint: disable=global-statement _master = None _slave = None _sentinel = None @@ -52,17 +52,17 @@ def get( def _init(settings: Optional[Mapping[str, Any]]) -> None: - global _master, _slave, _sentinel + global _master, _slave, _sentinel # pylint: disable=global-statement sentinels = c2cwsgiutils.config_utils.env_or_settings( settings, REDIS_SENTINELS_KEY, REDIS_SENTINELS_KEY_PROP ) service_name = c2cwsgiutils.config_utils.env_or_settings( settings, REDIS_SERVICENAME_KEY, REDIS_SERVICENAME_KEY_PROP ) - db = c2cwsgiutils.config_utils.env_or_settings(settings, REDIS_DB_KEY, REDIS_DB_KEY_PROP) + db = c2cwsgiutils.config_utils.env_or_settings(settings, _REDIS_DB_KEY, _REDIS_DB_KEY_PROP) url = c2cwsgiutils.config_utils.env_or_settings(settings, REDIS_URL_KEY, REDIS_URL_KEY_PROP) redis_options_ = c2cwsgiutils.config_utils.env_or_settings( - settings, REDIS_OPTIONS_KEY, REDIS_OPTIONS_KEY_PROP + settings, _REDIS_OPTIONS_KEY, _REDIS_OPTIONS_KEY_PROP ) redis_options = ( @@ -82,16 +82,16 @@ def _init(settings: Optional[Mapping[str, Any]]) -> None: db=db, **redis_options, ) - LOG.info("Redis setup using: %s, %s, %s", sentinels, service_name, redis_options_) + _LOG.info("Redis setup using: %s, %s, %s", sentinels, service_name, redis_options_) _master = _sentinel.master_for(service_name) _slave = _sentinel.slave_for(service_name) return if url: - LOG.info("Redis setup using: %s, with options: %s", url, redis_options_) + _LOG.info("Redis setup using: %s, with options: %s", url, redis_options_) _master = redis.client.Redis.from_url(url, decode_responses=True, **redis_options) _slave = _master else: - LOG.info( + _LOG.info( "No Redis configuration found, use %s or %s to configure it", REDIS_URL_KEY, REDIS_SENTINELS_KEY ) @@ -114,22 +114,23 @@ def run(self) -> None: try: pubsub.get_message(ignore_subscribe_messages=True, timeout=1) if not last_was_ok: - LOG.info("Redis is back") + _LOG.info("Redis is back") last_was_ok = True except redis.exceptions.RedisError: if last_was_ok: - LOG.warning("Redis connection problem") + _LOG.warning("Redis connection problem") last_was_ok = False time.sleep(0.5) except Exception: # pylint: disable=broad-except - LOG.warning("Unexpected error", exc_info=True) - LOG.info("Redis subscription worker stopped") + _LOG.warning("Unexpected error", exc_info=True) + _LOG.info("Redis subscription worker stopped") pubsub.close() self._running = False def stop(self) -> None: - # stopping simply unsubscribes from all channels and patterns. - # the unsubscribe responses that are generated will short circuit + """Stop the worker.""" + # Stopping simply unsubscribes from all channels and patterns. + # The unsubscribe responses that are generated will short circuit # the loop in run(), calling pubsub.close() to clean up the connection self.pubsub.unsubscribe() self.pubsub.punsubscribe() diff --git a/c2cwsgiutils/request_tracking/__init__.py b/c2cwsgiutils/request_tracking/__init__.py index 28f355138..4a86c7ca7 100644 --- a/c2cwsgiutils/request_tracking/__init__.py +++ b/c2cwsgiutils/request_tracking/__init__.py @@ -20,10 +20,10 @@ from c2cwsgiutils import config_utils, prometheus -ID_HEADERS: list[str] = [] +_ID_HEADERS: list[str] = [] _HTTPAdapter_send = requests.adapters.HTTPAdapter.send -LOG = logging.getLogger(__name__) -DEFAULT_TIMEOUT: Optional[float] = None +_LOG = logging.getLogger(__name__) +_DEFAULT_TIMEOUT: Optional[float] = None _PROMETHEUS_REQUESTS_SUMMARY = prometheus_client.Summary( prometheus.build_metric_name("requests"), "Requests requests", @@ -33,7 +33,7 @@ def _gen_request_id(request: pyramid.request.Request) -> str: - for id_header in ID_HEADERS: + for id_header in _ID_HEADERS: if id_header in request.headers: return request.headers[id_header] # type: ignore return str(uuid.uuid4()) @@ -50,15 +50,15 @@ def send_wrapper( proxies: Optional[Mapping[str, str]] = None, ) -> requests.Response: pyramid_request = get_current_request() - header = ID_HEADERS[0] + header = _ID_HEADERS[0] if pyramid_request is not None and header not in request.headers: request.headers[header] = pyramid_request.c2c_request_id if timeout is None: - if DEFAULT_TIMEOUT is not None: - timeout = DEFAULT_TIMEOUT + if _DEFAULT_TIMEOUT is not None: + timeout = _DEFAULT_TIMEOUT else: - LOG.warning("Doing a %s request without timeout to %s", request.method, request.url) + _LOG.warning("Doing a %s request without timeout to %s", request.method, request.url) assert request.url parsed = urllib.parse.urlparse(request.url) @@ -94,20 +94,20 @@ def includeme(config: Optional[pyramid.config.Configurator] = None) -> None: Use a X-Request-ID (or other) header to track all the logs related to a request including on the sub services. """ - global ID_HEADERS, DEFAULT_TIMEOUT - ID_HEADERS = ["X-Request-ID", "X-Correlation-ID", "Request-ID", "X-Varnish", "X-Amzn-Trace-Id"] + global _ID_HEADERS, _DEFAULT_TIMEOUT # pylint: disable=global-statement + _ID_HEADERS = ["X-Request-ID", "X-Correlation-ID", "Request-ID", "X-Varnish", "X-Amzn-Trace-Id"] if config is not None: extra_header = config_utils.env_or_config(config, "C2C_REQUEST_ID_HEADER", "c2c.request_id_header") if extra_header: - ID_HEADERS.insert(0, extra_header) + _ID_HEADERS.insert(0, extra_header) config.add_request_method(_gen_request_id, "c2c_request_id", reify=True) - DEFAULT_TIMEOUT = config_utils.env_or_config( + _DEFAULT_TIMEOUT = config_utils.env_or_config( config, "C2C_REQUESTS_DEFAULT_TIMEOUT", "c2c.requests_default_timeout", type_=float ) _patch_requests() if config_utils.env_or_config(config, "C2C_SQL_REQUEST_ID", "c2c.sql_request_id", False): - from . import _sql + from . import _sql # pylint: disable=import-outside-toplevel _sql.init() diff --git a/c2cwsgiutils/request_tracking/_sql.py b/c2cwsgiutils/request_tracking/_sql.py index e0f9585a6..92afe71f0 100644 --- a/c2cwsgiutils/request_tracking/_sql.py +++ b/c2cwsgiutils/request_tracking/_sql.py @@ -15,5 +15,4 @@ def _add_session_id(session: Session, _transaction: Any) -> None: def init() -> None: """Initialize the SQL alchemy session selector.""" - sqlalchemy.event.listen(Session, "after_transaction_create", _add_session_id) diff --git a/c2cwsgiutils/scripts/genversion.py b/c2cwsgiutils/scripts/genversion.py index 953061012..84669847c 100755 --- a/c2cwsgiutils/scripts/genversion.py +++ b/c2cwsgiutils/scripts/genversion.py @@ -8,9 +8,9 @@ import warnings from typing import Optional, cast -SRC_VERSION_RE = re.compile(r"^.*\(([^=]*)===?([^=]*)\)$") -VERSION_RE = re.compile(r"^([^=]*)==([^=]*)$") -LOG = logging.getLogger(__name__) +_SRC_VERSION_RE = re.compile(r"^.*\(([^=]*)===?([^=]*)\)$") +_VERSION_RE = re.compile(r"^([^=]*)==([^=]*)$") +_LOG = logging.getLogger(__name__) def _get_package_version(comp: str) -> tuple[Optional[str], Optional[str]]: @@ -19,8 +19,8 @@ def _get_package_version(comp: str) -> tuple[Optional[str], Optional[str]]: See test_genversion.py for examples. """ - src_matcher = SRC_VERSION_RE.match(comp) - matcher = src_matcher or VERSION_RE.match(comp) + src_matcher = _SRC_VERSION_RE.match(comp) + matcher = src_matcher or _VERSION_RE.match(comp) if matcher: return cast(tuple[str, str], matcher.groups()) else: diff --git a/c2cwsgiutils/scripts/stats_db.py b/c2cwsgiutils/scripts/stats_db.py index c85aa6808..28d55fca1 100755 --- a/c2cwsgiutils/scripts/stats_db.py +++ b/c2cwsgiutils/scripts/stats_db.py @@ -6,7 +6,7 @@ import os import sys import time -from typing import TYPE_CHECKING, Optional +from typing import Optional from wsgiref.simple_server import make_server import sqlalchemy @@ -20,12 +20,9 @@ import c2cwsgiutils.setup_process from c2cwsgiutils import prometheus -if TYPE_CHECKING: - scoped_session = sqlalchemy.orm.scoped_session[sqlalchemy.orm.Session] -else: - scoped_session = sqlalchemy.orm.scoped_session +scoped_session = sqlalchemy.orm.scoped_session[sqlalchemy.orm.Session] -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) def _parse_args() -> argparse.Namespace: @@ -72,6 +69,7 @@ def __init__(self, args: argparse.Namespace) -> None: self.gauges: dict[str, Gauge] = {} def get_gauge(self, kind: str, kind_help: str, labels: list[str]) -> Gauge: + """Get a gauge.""" if kind not in self.gauges: self.gauges[kind] = Gauge( prometheus.build_metric_name(f"database_{kind}"), @@ -84,25 +82,30 @@ def get_gauge(self, kind: str, kind_help: str, labels: list[str]) -> Gauge: def do_report( self, metric: list[str], value: int, kind: str, kind_help: str, tags: dict[str, str] ) -> None: - LOG.debug("%s.%s -> %d", kind, ".".join(metric), value) + """Report a metric.""" + _LOG.debug("%s.%s -> %d", kind, ".".join(metric), value) gauge = self.get_gauge(kind, kind_help, list(tags.keys())) gauge.labels(**tags).set(value) def commit(self) -> None: + """Commit the metrics.""" if self.prometheus_push: push_to_gateway(self.args.prometheus_url, job="db_counts", registry=self.registry) else: port = int(os.environ.get("C2C_PROMETHEUS_PORT", "9090")) app = make_wsgi_app(self.registry) with make_server("", port, app) as httpd: - LOG.info("Waiting that Prometheus get the metrics served on port %s...", port) + _LOG.info("Waiting that Prometheus get the metrics served on port %s...", port) httpd.handle_request() def error(self, metric: list[str], error_: Exception) -> None: + """Report an error.""" + del metric if self._error is None: self._error = error_ def report_error(self) -> None: + """Raise the error if any.""" if self._error is not None: raise self._error @@ -225,7 +228,6 @@ def _do_table_count( def do_extra(session: scoped_session, sql: str, kind: str, gauge_help: str, reporter: Reporter) -> None: """Do an extra report.""" - for metric, count in session.execute(sqlalchemy.text(sql)): reporter.do_report( str(metric).split("."), count, kind=kind, kind_help=gauge_help, tags={"metric": metric} @@ -253,29 +255,29 @@ def _do_dtats_db(args: argparse.Namespace) -> None: params={"schemas": tuple(args.schema)}, ).fetchall() for schema, table in tables: - LOG.info("Process table %s.%s.", schema, table) + _LOG.info("Process table %s.%s.", schema, table) try: do_table(session, schema, table, reporter) except Exception as e: # pylint: disable=broad-except - LOG.exception("Process table %s.%s error.", schema, table) + _LOG.exception("Process table %s.%s error.", schema, table) reporter.error([schema, table], e) if args.extra: for pos, extra in enumerate(args.extra): - LOG.info("Process extra %s.", extra) + _LOG.info("Process extra %s.", extra) try: do_extra(session, extra, "extra", "Extra metric", reporter) except Exception as e: # pylint: disable=broad-except - LOG.exception("Process extra %s error.", extra) + _LOG.exception("Process extra %s error.", extra) reporter.error(["extra", str(pos + 1)], e) if args.extra_gauge: for pos, extra in enumerate(args.extra_gauge): sql, gauge, gauge_help = extra - LOG.info("Process extra %s.", extra) + _LOG.info("Process extra %s.", extra) try: do_extra(session, sql, gauge, gauge_help, reporter) except Exception as e: # pylint: disable=broad-except - LOG.exception("Process extra %s error.", extra) + _LOG.exception("Process extra %s error.", extra) reporter.error(["extra", str(len(args.extra) + pos + 1)], e) reporter.commit() @@ -294,11 +296,11 @@ def main() -> None: success = True break except: # pylint: disable=bare-except - LOG.exception("Exception during run") + _LOG.exception("Exception during run") time.sleep(float(os.environ.get("C2CWSGIUTILS_STATS_DB_SLEEP", 1))) if not success: - LOG.error("Not in success, exiting") + _LOG.error("Not in success, exiting") sys.exit(1) diff --git a/c2cwsgiutils/scripts/test_print.py b/c2cwsgiutils/scripts/test_print.py index e3834780b..bcdab60ca 100755 --- a/c2cwsgiutils/scripts/test_print.py +++ b/c2cwsgiutils/scripts/test_print.py @@ -8,7 +8,7 @@ import c2cwsgiutils.setup_process from c2cwsgiutils.acceptance.print import PrintConnection -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) def _parse_args() -> argparse.Namespace: @@ -38,7 +38,7 @@ def main() -> None: if args.app is None: for app in print_.get_apps(): if app != "default": - LOG.info("\n\n%s=================", app) + _LOG.info("\n\n%s=================", app) test_app(print_, app) else: test_app(print_, args.app) @@ -47,13 +47,13 @@ def main() -> None: def test_app(print_: PrintConnection, app: str) -> None: """Test the application.""" capabilities = print_.get_capabilities(app) - LOG.debug("Capabilities:\n%s", pprint.pformat(capabilities)) + _LOG.debug("Capabilities:\n%s", pprint.pformat(capabilities)) examples = print_.get_example_requests(app) for name, request in examples.items(): - LOG.info("\n%s-----------------", name) + _LOG.info("\n%s-----------------", name) pdf = print_.get_pdf(app, request) size = len(pdf.content) - LOG.info("Size=%d", size) + _LOG.info("Size=%d", size) if __name__ == "__main__": diff --git a/c2cwsgiutils/sentry.py b/c2cwsgiutils/sentry.py index 64243834f..e6213cdde 100644 --- a/c2cwsgiutils/sentry.py +++ b/c2cwsgiutils/sentry.py @@ -16,14 +16,15 @@ from c2cwsgiutils import config_utils -LOG = logging.getLogger(__name__) -_client_setup = False +_LOG = logging.getLogger(__name__) +_CLIENT_SETUP = False def _create_before_send_filter(tags: MutableMapping[str, str]) -> Callable[[Any, Any], Any]: """Create a filter that adds tags to every events.""" def do_filter(event: Any, hint: Any) -> Any: + del hint event.setdefault("tags", {}).update(tags) return event @@ -32,17 +33,15 @@ def do_filter(event: Any, hint: Any) -> Any: def init(config: Optional[pyramid.config.Configurator] = None) -> None: """Initialize the Sentry integration, for backward compatibility.""" - warnings.warn("init function is deprecated; use includeme instead") includeme(config) def includeme(config: Optional[pyramid.config.Configurator] = None) -> None: """Initialize the Sentry integration.""" - - global _client_setup + global _CLIENT_SETUP # pylint: disable=global-statement sentry_url = config_utils.env_or_config(config, "SENTRY_URL", "c2c.sentry.url") - if sentry_url is not None and not _client_setup: + if sentry_url is not None and not _CLIENT_SETUP: client_info: MutableMapping[str, Any] = { key[14:].lower(): value for key, value in os.environ.items() if key.startswith("SENTRY_CLIENT_") } @@ -126,7 +125,7 @@ def includeme(config: Optional[pyramid.config.Configurator] = None) -> None: before_send=_create_before_send_filter(tags), **client_info, ) - _client_setup = True + _CLIENT_SETUP = True excludes = config_utils.env_or_config( config, "SENTRY_EXCLUDES", "c2c.sentry.excludes", "sentry_sdk" @@ -134,7 +133,7 @@ def includeme(config: Optional[pyramid.config.Configurator] = None) -> None: for exclude in excludes: ignore_logger(exclude) - LOG.info("Configured sentry reporting with client=%s and tags=%s", repr(client_info), repr(tags)) + _LOG.info("Configured sentry reporting with client=%s and tags=%s", repr(client_info), repr(tags)) @contextlib.contextmanager @@ -145,7 +144,7 @@ def capture_exceptions() -> Generator[None, None, None]: You don't need to use that for exception terminating the process (those not caught). Sentry does that already. """ - if _client_setup: + if _CLIENT_SETUP: try: yield except Exception: @@ -157,12 +156,12 @@ def capture_exceptions() -> Generator[None, None, None]: def filter_wsgi_app(application: Callable[..., Any]) -> Callable[..., Any]: """If sentry is configured, add a Sentry filter around the application.""" - if _client_setup: + if _CLIENT_SETUP: try: - LOG.info("Enable WSGI filter for Sentry") + _LOG.info("Enable WSGI filter for Sentry") return SentryWsgiMiddleware(application) except Exception: # pylint: disable=broad-except - LOG.error("Failed enabling sentry. Continuing without it.", exc_info=True) + _LOG.error("Failed enabling sentry. Continuing without it.", exc_info=True) return application else: return application @@ -170,4 +169,5 @@ def filter_wsgi_app(application: Callable[..., Any]) -> Callable[..., Any]: def filter_factory(*args: Any, **kwargs: Any) -> Callable[..., Any]: """Get the filter.""" + del args, kwargs return filter_wsgi_app diff --git a/c2cwsgiutils/services.py b/c2cwsgiutils/services.py index 9cba12c50..77691b391 100644 --- a/c2cwsgiutils/services.py +++ b/c2cwsgiutils/services.py @@ -5,7 +5,7 @@ from pyramid.request import Request from pyramid.response import Response -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) def create(name: str, path: str, *args: Any, **kwargs: Any) -> Service: @@ -31,6 +31,6 @@ def _cache_cors(response: Response, request: Request) -> Response: except Exception: # cornice catches exceptions from filters, and tries call back the filter with only the request. # This leads to a useless message in case of error... - LOG.error("Failed fixing cache headers for CORS", exc_info=True) + _LOG.error("Failed fixing cache headers for CORS", exc_info=True) raise return response diff --git a/c2cwsgiutils/setup_process.py b/c2cwsgiutils/setup_process.py index 0890ae08c..b0ccec6b7 100644 --- a/c2cwsgiutils/setup_process.py +++ b/c2cwsgiutils/setup_process.py @@ -24,7 +24,6 @@ def fill_arguments( default_config_uri: str = "c2c:///app/production.ini", ) -> None: """Add the needed arguments to the parser like it's done in pshell.""" - parser.add_argument( "--config-uri" if use_attribute else "config_uri", nargs="?", diff --git a/c2cwsgiutils/sql_profiler/__init__.py b/c2cwsgiutils/sql_profiler/__init__.py index ba71000da..c52d77764 100644 --- a/c2cwsgiutils/sql_profiler/__init__.py +++ b/c2cwsgiutils/sql_profiler/__init__.py @@ -11,10 +11,9 @@ from c2cwsgiutils import auth -ENV_KEY = "C2C_SQL_PROFILER_ENABLED" -CONFIG_KEY = "c2c.sql_profiler_enabled" -LOG = logging.getLogger(__name__) -repository = None +_ENV_KEY = "C2C_SQL_PROFILER_ENABLED" +_CONFIG_KEY = "c2c.sql_profiler_enabled" +_LOG = logging.getLogger(__name__) def init(config: pyramid.config.Configurator) -> None: @@ -25,7 +24,7 @@ def init(config: pyramid.config.Configurator) -> None: def includeme(config: pyramid.config.Configurator) -> None: """Install a pyramid event handler that adds the request information.""" - if auth.is_enabled(config, ENV_KEY, CONFIG_KEY): - from . import _impl + if auth.is_enabled(config, _ENV_KEY, _CONFIG_KEY): + from . import _impl # pylint: disable=import-outside-toplevel _impl.init(config) diff --git a/c2cwsgiutils/sql_profiler/_impl.py b/c2cwsgiutils/sql_profiler/_impl.py index e7129e9c9..a05754f8c 100644 --- a/c2cwsgiutils/sql_profiler/_impl.py +++ b/c2cwsgiutils/sql_profiler/_impl.py @@ -16,8 +16,8 @@ from c2cwsgiutils import auth, broadcast, config_utils -LOG = logging.getLogger(__name__) -repository = None +_LOG = logging.getLogger(__name__) +_REPOSITORY = None class _Repository: @@ -35,7 +35,8 @@ def profile( _context: Any, _executemany: Any, ) -> None: - if statement.startswith("SELECT ") and LOG.isEnabledFor(logging.INFO): + """Profile the SQL statement.""" + if statement.startswith("SELECT ") and _LOG.isEnabledFor(logging.INFO): do_it = False with self._lock: if statement not in self._repo: @@ -43,8 +44,8 @@ def profile( self._repo.add(statement) if do_it: try: - LOG.info("statement:\n%s", _indent(_beautify_sql(statement))) - LOG.info("parameters: %s", repr(parameters)) + _LOG.info("statement:\n%s", _indent(_beautify_sql(statement))) + _LOG.info("parameters: %s", repr(parameters)) with conn.engine.begin() as c: output = "\n ".join( [ @@ -54,7 +55,7 @@ def profile( ) ] ) - LOG.info(output) + _LOG.info(output) except Exception: # nosec # pylint: disable=broad-except pass @@ -64,21 +65,21 @@ def _sql_profiler_view(request: pyramid.request.Request) -> Mapping[str, Any]: enable = request.params.get("enable") if enable is not None: broadcast.broadcast("c2c_sql_profiler", params={"enable": enable}, expect_answers=True) - return {"status": 200, "enabled": repository is not None} + return {"status": 200, "enabled": _REPOSITORY is not None} def _setup_profiler(enable: str) -> None: - global repository + global _REPOSITORY # pylint: disable=global-statement if config_utils.config_bool(enable): - if repository is None: - LOG.info("Enabling the SQL profiler") - repository = _Repository() - sqlalchemy.event.listen(sqlalchemy.engine.Engine, "before_cursor_execute", repository.profile) + if _REPOSITORY is None: + _LOG.info("Enabling the SQL profiler") + _REPOSITORY = _Repository() + sqlalchemy.event.listen(sqlalchemy.engine.Engine, "before_cursor_execute", _REPOSITORY.profile) else: - if repository is not None: - LOG.info("Disabling the SQL profiler") - sqlalchemy.event.remove(sqlalchemy.engine.Engine, "before_cursor_execute", repository.profile) - repository = None + if _REPOSITORY is not None: + _LOG.info("Disabling the SQL profiler") + sqlalchemy.event.remove(sqlalchemy.engine.Engine, "before_cursor_execute", _REPOSITORY.profile) + _REPOSITORY = None def _beautify_sql(statement: str) -> str: @@ -102,4 +103,4 @@ def init(config: pyramid.config.Configurator) -> None: "c2c_sql_profiler", config_utils.get_base_path(config) + r"/sql_profiler", request_method="GET" ) config.add_view(_sql_profiler_view, route_name="c2c_sql_profiler", renderer="fast_json", http_cache=0) - LOG.info("Enabled the /sql_profiler API") + _LOG.info("Enabled the /sql_profiler API") diff --git a/c2cwsgiutils/sqlalchemylogger/handlers.py b/c2cwsgiutils/sqlalchemylogger/handlers.py index c94f8d8e7..ce6c405c9 100644 --- a/c2cwsgiutils/sqlalchemylogger/handlers.py +++ b/c2cwsgiutils/sqlalchemylogger/handlers.py @@ -14,7 +14,7 @@ from c2cwsgiutils.sqlalchemylogger._filters import ContainsExpression, DoesNotContainExpression from c2cwsgiutils.sqlalchemylogger._models import Base, create_log_class -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) class SQLAlchemyHandler(logging.Handler): @@ -30,28 +30,28 @@ def __init__( contains_expression: str = "", ) -> None: super().__init__() - # initialize DB session + # Initialize DB session self.engine = create_engine(sqlalchemy_url["url"]) - self.Log = create_log_class( + self.Log = create_log_class( # pylint: disable=invalid-name tablename=sqlalchemy_url.get("tablename", "logs"), tableargs=sqlalchemy_url.get("tableargs", None), # type: ignore ) Base.metadata.bind = self.engine self.session = sessionmaker(bind=self.engine)() # noqa - # initialize log queue + # Initialize log queue self.log_queue: Any = queue.Queue() - # initialize a thread to process the logs Asynchronously + # Initialize a thread to process the logs Asynchronously self.condition = threading.Condition() self.processor_thread = threading.Thread(target=self._processor, daemon=True) self.processor_thread.start() - # initialize filters + # Initialize filters if does_not_contain_expression: self.addFilter(DoesNotContainExpression(does_not_contain_expression)) if contains_expression: self.addFilter(ContainsExpression(contains_expression)) def _processor(self) -> None: - LOG.debug("%s: starting processor thread", __name__) + _LOG.debug("%s: starting processor thread", __name__) while True: logs = [] time_since_last = time.perf_counter() @@ -70,7 +70,7 @@ def _processor(self) -> None: ): self._write_logs(logs) break - LOG.debug("%s: stopping processor thread", __name__) + _LOG.debug("%s: stopping processor thread", __name__) def _write_logs(self, logs: list[Any]) -> None: try: @@ -85,12 +85,13 @@ def _write_logs(self, logs: list[Any]) -> None: except Exception as e: # pylint: disable=broad-except # if we really cannot commit the log to DB, do not lock the # thread and do not crash the application - LOG.critical(e) + _LOG.critical(e) finally: self.session.expunge_all() def create_db(self) -> None: - LOG.info("%s: creating new database", __name__) + """Create the database if it does not exist.""" + _LOG.info("%s: creating new database", __name__) if not database_exists(self.engine.url): create_database(self.engine.url) # FIXME: we should not access directly the private __table_args__ diff --git a/c2cwsgiutils/stats_pyramid/__init__.py b/c2cwsgiutils/stats_pyramid/__init__.py index 80150e178..2a25a7bce 100644 --- a/c2cwsgiutils/stats_pyramid/__init__.py +++ b/c2cwsgiutils/stats_pyramid/__init__.py @@ -10,7 +10,6 @@ def init(config: pyramid.config.Configurator) -> None: """Initialize the whole stats module, for backward compatibility.""" - warnings.warn("init function is deprecated; use includeme instead") includeme(config) @@ -20,17 +19,14 @@ def includeme(config: pyramid.config.Configurator) -> None: Initialize the whole stats pyramid module. Arguments: - config: The Pyramid config """ - _pyramid_spy.init(config) init_db_spy() def init_db_spy() -> None: """Initialize the database spy.""" - - from . import _db_spy + from . import _db_spy # pylint: disable=import-outside-toplevel _db_spy.init() diff --git a/c2cwsgiutils/stats_pyramid/_db_spy.py b/c2cwsgiutils/stats_pyramid/_db_spy.py index ef4df0073..8b442ac4e 100644 --- a/c2cwsgiutils/stats_pyramid/_db_spy.py +++ b/c2cwsgiutils/stats_pyramid/_db_spy.py @@ -10,7 +10,7 @@ from c2cwsgiutils import prometheus -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) _PROMETHEUS_DB_SUMMARY = prometheus_client.Summary( prometheus.build_metric_name("database"), @@ -71,7 +71,7 @@ def _create_sqlalchemy_timer_cb(what: str) -> Callable[..., Any]: def after(*_args: Any, **_kwargs: Any) -> None: _PROMETHEUS_DB_SUMMARY.labels({"query": what}).observe(time.perf_counter() - start) - LOG.debug("Execute statement '%s' in %d.", what, time.perf_counter() - start) + _LOG.debug("Execute statement '%s' in %d.", what, time.perf_counter() - start) return after diff --git a/c2cwsgiutils/stats_pyramid/_pyramid_spy.py b/c2cwsgiutils/stats_pyramid/_pyramid_spy.py index d449c8ef0..0c701de8e 100644 --- a/c2cwsgiutils/stats_pyramid/_pyramid_spy.py +++ b/c2cwsgiutils/stats_pyramid/_pyramid_spy.py @@ -86,7 +86,6 @@ def init(config: pyramid.config.Configurator) -> None: # pragma: nocover Subscribe to Pyramid events in order to get some stats on route time execution. Arguments: - config: The Pyramid config """ config.add_subscriber(_request_callback, pyramid.events.NewRequest) diff --git a/c2cwsgiutils/version.py b/c2cwsgiutils/version.py index 826d5c0b6..1536236eb 100644 --- a/c2cwsgiutils/version.py +++ b/c2cwsgiutils/version.py @@ -52,7 +52,6 @@ def __call__(self, request: pyramid.request.Request) -> dict[str, dict[str, str] def includeme(config: pyramid.config.Configurator) -> None: """Initialize the versions view.""" - if os.path.isfile(_VERSIONS_PATH): versions = _read_versions() config.add_route( diff --git a/poetry.lock b/poetry.lock index 9db577862..ecaadfd40 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "alembic" @@ -21,22 +21,17 @@ tz = ["backports.zoneinfo"] [[package]] name = "astroid" -version = "2.15.8" +version = "3.3.5" description = "An abstract syntax tree for Python with inference support." optional = false -python-versions = ">=3.7.2" +python-versions = ">=3.9.0" files = [ - {file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"}, - {file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"}, + {file = "astroid-3.3.5-py3-none-any.whl", hash = "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8"}, + {file = "astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d"}, ] [package.dependencies] -lazy-object-proxy = ">=1.4.0" typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} -wrapt = [ - {version = ">=1.11,<2", markers = "python_version < \"3.11\""}, - {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, -] [[package]] name = "async-timeout" @@ -852,52 +847,6 @@ dev = ["changelist (==0.5)"] lint = ["pre-commit (==3.7.0)"] test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"] -[[package]] -name = "lazy-object-proxy" -version = "1.10.0" -description = "A fast and thorough lazy object proxy." -optional = false -python-versions = ">=3.8" -files = [ - {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"}, - {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"}, - {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"}, - {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"}, - {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"}, - {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"}, - {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, -] - [[package]] name = "lxml" version = "5.3.0" @@ -1583,13 +1532,13 @@ twisted = ["twisted"] [[package]] name = "prospector" -version = "1.11.0" +version = "1.12.0" description = "Prospector is a tool to analyse Python code by aggregating the result of other tools." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "prospector-1.11.0-py3-none-any.whl", hash = "sha256:2b112842e5a19d6f77c886d89f57b619c19b47afdf6db31019d85d93e0bdc29d"}, - {file = "prospector-1.11.0.tar.gz", hash = "sha256:49236fe334652d1229a85bc10789ac4390cc5027f6becda989ea473f893dde57"}, + {file = "prospector-1.12.0-py3-none-any.whl", hash = "sha256:485d889543c8b47e495b2abbf22151e64b3494564874c6e554564f550a891e37"}, + {file = "prospector-1.12.0.tar.gz", hash = "sha256:e598dddc406cbfe8b31a20d4391c7551841ed6772897a290ecaf272ee1ffabf6"}, ] [package.dependencies] @@ -1604,7 +1553,7 @@ pep8-naming = ">=0.3.3,<=0.10.0" pycodestyle = ">=2.9.0" pydocstyle = ">=2.0.0" pyflakes = ">=2.2.0,<4" -pylint = ">=2.8.3,<3.0" +pylint = ">=3.0" pylint-celery = "0.3" pylint-django = ">=2.5,<2.6" pylint-flask = "0.6" @@ -1625,13 +1574,24 @@ with-vulture = ["vulture (>=1.5)"] [[package]] name = "prospector-profile-duplicated" -version = "1.5.0" +version = "1.6.0" description = "Profile that can be used to disable the duplicated or conflict rules between Prospector and other tools" optional = false python-versions = "*" files = [ - {file = "prospector_profile_duplicated-1.5.0-py2.py3-none-any.whl", hash = "sha256:fd4d3272ff6b5ceead036f4a60da6dd7aa5317057f66c735994b75542c9a0815"}, - {file = "prospector_profile_duplicated-1.5.0.tar.gz", hash = "sha256:c1d6074ebb44fd3ea6a7b85e96d58a27f6e525687b019ef94924a9c8d2ccd5d5"}, + {file = "prospector_profile_duplicated-1.6.0-py2.py3-none-any.whl", hash = "sha256:bf6a6aae0c7de48043b95e4d42e23ccd090c6c7115b6ee8c8ca472ffb1a2022b"}, + {file = "prospector_profile_duplicated-1.6.0.tar.gz", hash = "sha256:9c2d541076537405e8b2484cb6222276a2df17492391b6af1b192695770aab83"}, +] + +[[package]] +name = "prospector-profile-utils" +version = "1.9.1" +description = "Some utility Prospector profiles." +optional = false +python-versions = "*" +files = [ + {file = "prospector_profile_utils-1.9.1-py2.py3-none-any.whl", hash = "sha256:b458d8c4d59bdb1547e4630a2c6de4971946c4f0999443db6a9eef6d216b26b8"}, + {file = "prospector_profile_utils-1.9.1.tar.gz", hash = "sha256:008efa6797a85233fd8093dcb9d86f5fa5d89673e431c15cb1496a91c9b2c601"}, ] [[package]] @@ -1739,23 +1699,24 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] name = "pylint" -version = "2.17.7" +version = "3.3.1" description = "python code static checker" optional = false -python-versions = ">=3.7.2" +python-versions = ">=3.9.0" files = [ - {file = "pylint-2.17.7-py3-none-any.whl", hash = "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87"}, - {file = "pylint-2.17.7.tar.gz", hash = "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad"}, + {file = "pylint-3.3.1-py3-none-any.whl", hash = "sha256:2f846a466dd023513240bc140ad2dd73bfc080a5d85a710afdb728c420a5a2b9"}, + {file = "pylint-3.3.1.tar.gz", hash = "sha256:9f3dcc87b1203e612b78d91a896407787e708b3f189b5fa0b307712d49ff0c6e"}, ] [package.dependencies] -astroid = ">=2.15.8,<=2.17.0-dev0" +astroid = ">=3.3.4,<=3.4.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, + {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] -isort = ">=4.2.5,<6" +isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -1782,21 +1743,21 @@ pylint-plugin-utils = ">=0.2.1" [[package]] name = "pylint-django" -version = "2.5.3" +version = "2.5.2" description = "A Pylint plugin to help Pylint understand the Django web framework" optional = false python-versions = "*" files = [ - {file = "pylint-django-2.5.3.tar.gz", hash = "sha256:0ac090d106c62fe33782a1d01bda1610b761bb1c9bf5035ced9d5f23a13d8591"}, - {file = "pylint_django-2.5.3-py3-none-any.whl", hash = "sha256:56b12b6adf56d548412445bd35483034394a1a94901c3f8571980a13882299d5"}, + {file = "pylint-django-2.5.2.tar.gz", hash = "sha256:1933d82b4a92538a3b12aef91adfd7d866befd051d7a02d6245b0f965587d97c"}, + {file = "pylint_django-2.5.2-py3-none-any.whl", hash = "sha256:286dce8a31bc8ed5a523e8d8742b5a0e083b87f5f140ea4cde9aad612c03bd2d"}, ] [package.dependencies] -pylint = ">=2.0,<3" +pylint = ">=2.0" pylint-plugin-utils = ">=0.7" [package.extras] -for-tests = ["coverage", "django-tables2", "django-tastypie", "factory-boy", "pylint (>=2.13)", "pytest", "wheel"] +for-tests = ["coverage", "django-tables2", "django-tastypie", "factory-boy", "pytest", "wheel"] with-django = ["Django"] [[package]] @@ -2122,17 +2083,17 @@ rsa = ["oauthlib[signedtoken] (>=3.0.0)"] [[package]] name = "requirements-detector" -version = "1.2.2" +version = "1.3.1" description = "Python tool to find and list requirements of a Python project" optional = false -python-versions = ">=3.7,<4.0" +python-versions = "<4.0,>=3.8" files = [ - {file = "requirements_detector-1.2.2-py3-none-any.whl", hash = "sha256:d7c60493bf166da3dd59de0e6cb25765e0e32a1931aeae92614034e5786d0bd0"}, - {file = "requirements_detector-1.2.2.tar.gz", hash = "sha256:3642cd7a5b261d79536c36bb7ecacf2adabd902d2e0e42bfb2ba82515da10501"}, + {file = "requirements_detector-1.3.1-py3-none-any.whl", hash = "sha256:3ef72e1c5c3ad11100058e8f074a5762a4902985e698099d2e7f1283758d4045"}, + {file = "requirements_detector-1.3.1.tar.gz", hash = "sha256:b89e34faf0e4d17f5736923918bd5401949cbe723294ccfefd698b3cda28e676"}, ] [package.dependencies] -astroid = ">=2.0,<3.0" +astroid = ">=3.0,<4.0" packaging = ">=21.3" semver = ">=3.0.0,<4.0.0" toml = ">=0.10.2,<0.11.0" @@ -2861,85 +2822,6 @@ files = [ docs = ["Sphinx (>=1.7.5)", "pylons-sphinx-themes"] testing = ["coverage", "pytest (>=3.1.0)", "pytest-cov", "pytest-xdist"] -[[package]] -name = "wrapt" -version = "1.16.0" -description = "Module for decorators, wrappers and monkey patching." -optional = false -python-versions = ">=3.6" -files = [ - {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, - {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, - {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, - {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, - {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, - {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, - {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, - {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, - {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, - {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, - {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, - {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, - {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, - {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, - {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, - {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, - {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, - {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, -] - [[package]] name = "zipp" version = "3.20.2" @@ -3065,4 +2947,4 @@ webserver = ["SQLAlchemy", "SQLAlchemy-Utils", "cornice", "gunicorn", "prometheu [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "ee07dffb717c0a9d152ca005bb375145f76eb74afaffcbc83fa2877e1fecc47d" +content-hash = "6129f6186db1e3c4897710a72c0264610768697c2da9a97d2f0c1e1d77bfdcd8" diff --git a/pyproject.toml b/pyproject.toml index 81634ef77..2a29d5154 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -168,9 +168,11 @@ all = [ ] test_images = ["scikit-image"] -[tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] # pylint = { version = "2.15.6" } -prospector = { extras = ["with_bandit", "with_mypy", "with_pyroma"], version = "1.11.0" } +prospector = { extras = ["with_bandit", "with_mypy", "with_pyroma"], version = "1.12.0" } +prospector-profile-duplicated = "1.6.0" +prospector-profile-utils = "1.9.1" coverage = "7.6.1" junit2html = "31.0.2" pytest = "8.3.3" @@ -183,9 +185,6 @@ types-ujson = "5.10.0.20240515" types-python-dateutil = "2.9.0.20241003" typing_extensions = "4.12.2" -[tool.poetry.group.dev.dependencies] -prospector-profile-duplicated = "1.5.0" - [build-system] requires = [ "poetry-core>=1.0.0",