From bf7869ad56ebaef3600c61d3406e7b0bf35b76d9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:31:35 +0000 Subject: [PATCH 01/12] Update all minor versions --- doc/poetry.lock | 10 +- doc/pyproject.toml | 2 +- docker-compose.yaml | 2 +- docker/qgisserver/poetry.lock | 25 +-- docker/qgisserver/pyproject.toml | 6 +- .../docker-compose-lib.yaml | 2 +- geoportal/package-lock.json | 169 +++++++----------- geoportal/package.json | 14 +- poetry.lock | 45 ++--- pyproject.toml | 12 +- 10 files changed, 128 insertions(+), 159 deletions(-) diff --git a/doc/poetry.lock b/doc/poetry.lock index ba216a820d..1c14eda591 100644 --- a/doc/poetry.lock +++ b/doc/poetry.lock @@ -2257,13 +2257,13 @@ wsgi = ["pyramid"] [[package]] name = "tilecloud-chain" -version = "1.21.0" +version = "1.22.0" description = "Tools to generate tiles from WMS or Mapnik, to S3, Berkeley DB, MBTiles, or local filesystem in WMTS layout using Amazon cloud services." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "tilecloud_chain-1.21.0-py3-none-any.whl", hash = "sha256:a73914cbc8742a1e6cc5b1a9804a78e1af2fcdc62993950f42e2949737faa739"}, - {file = "tilecloud_chain-1.21.0.tar.gz", hash = "sha256:7cfb46c09d3ff604a46f3d8dfc7deafac8f0d771c68e55726cdd1d88a206cec8"}, + {file = "tilecloud_chain-1.22.0-py3-none-any.whl", hash = "sha256:32dabc07c98287220d914c59baf4d6c7802a01bb855e363744a564a2f0e1704d"}, + {file = "tilecloud_chain-1.22.0.tar.gz", hash = "sha256:c61cd96c2fc6b568e173399dd8171fa43f4e29dd4c62b584f1892ce66890c21c"}, ] [package.dependencies] @@ -2610,4 +2610,4 @@ test = ["zope.testing"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "28c7611638e048ab7a91df45fc0ab246375795ffc471f0b0cccc961a82e84743" +content-hash = "215b13d0de25b366213f6b79ea957b73aa24917bdd9ececa56512e08a49a66bc" diff --git a/doc/pyproject.toml b/doc/pyproject.toml index e44cd81bf6..1bb00bb8ba 100644 --- a/doc/pyproject.toml +++ b/doc/pyproject.toml @@ -23,5 +23,5 @@ sphinx-argparse = "0.5.2" Sphinx-Substitution-Extensions = { extras = ["prompt"], version = "2024.10.17" } sphinxcontrib-mermaid = "1.0.0" c2cwsgiutils = ">=6.0.0.dev148" -tilecloud-chain = "1.21.0" +tilecloud-chain = "1.22.0" boto3 = { version = "1.35.72", optional = true } diff --git a/docker-compose.yaml b/docker-compose.yaml index f43052214c..913a6c4f4c 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -48,7 +48,7 @@ services: lra.restrictionarea_id = ra.id AND lra.layer_id = la.id AND la.name = ' tilecloudchain: - image: camptocamp/tilecloud-chain:1.21 + image: camptocamp/tilecloud-chain:1.22 user: www-data restart: unless-stopped environment: diff --git a/docker/qgisserver/poetry.lock b/docker/qgisserver/poetry.lock index c3c6e2afb2..7687f32186 100644 --- a/docker/qgisserver/poetry.lock +++ b/docker/qgisserver/poetry.lock @@ -367,13 +367,13 @@ tqdm = ["tqdm"] [[package]] name = "geoalchemy2" -version = "0.15.2" +version = "0.16.0" description = "Using SQLAlchemy with Spatial Databases" optional = false python-versions = ">=3.7" files = [ - {file = "GeoAlchemy2-0.15.2-py3-none-any.whl", hash = "sha256:546455dc39f5bcdfc5b871e57d3f7546c8a6f798eb364c474200f488ace6fd32"}, - {file = "geoalchemy2-0.15.2.tar.gz", hash = "sha256:3af0272db927373e74ee3b064cdc9464ba08defdb945c51745db1b841482f5dc"}, + {file = "GeoAlchemy2-0.16.0-py3-none-any.whl", hash = "sha256:b0f27d5500ee757af4654c6262e0f834b7a843504d193653ec747ef1128d2ab5"}, + {file = "geoalchemy2-0.16.0.tar.gz", hash = "sha256:df64bb72af70daafaac3f359492c96501c37ab85ed20f9510c99cc6d02881100"}, ] [package.dependencies] @@ -1021,26 +1021,29 @@ with-vulture = ["vulture (>=1.5)"] [[package]] name = "prospector-profile-duplicated" -version = "1.6.0" +version = "1.9.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"}, + {file = "prospector_profile_duplicated-1.9.0-py2.py3-none-any.whl", hash = "sha256:7b7a665e6fa8b44fac597d2bbef1a91de5b6f090b8d64890a5f38ee60e8473c2"}, + {file = "prospector_profile_duplicated-1.9.0.tar.gz", hash = "sha256:bb0b0d0946232d570ada944ee4a180fd142529db0978579b58e766d28f3c2a27"}, ] [[package]] name = "prospector-profile-utils" -version = "1.9.1" +version = "1.14.1" description = "Some utility Prospector profiles." optional = false -python-versions = "*" +python-versions = "<4.0,>=3.9" 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"}, + {file = "prospector_profile_utils-1.14.1-py3-none-any.whl", hash = "sha256:1b7d79e4293c76f9ea5107b691c888e76933aa29d33e8f8b8750dc0aeea3b657"}, + {file = "prospector_profile_utils-1.14.1.tar.gz", hash = "sha256:5447086f9a7ddba02d8ee7322baa30c80c1376328677a6593165fab23e2e4bf2"}, ] +[package.dependencies] +prospector = ">=1.13.0" + [[package]] name = "psycopg2" version = "2.9.10" @@ -2012,4 +2015,4 @@ test = ["zope.testing"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "5e211dc9d324e8a046a70cbc4009f1cfce1d806f52a2ed7a0df3049a951b8a11" +content-hash = "c6b2084aefc7f9107b7503d5eb177884274a045a9474fb82adff14b9e5687606" diff --git a/docker/qgisserver/pyproject.toml b/docker/qgisserver/pyproject.toml index 14bbfdfbf0..b2b157c575 100644 --- a/docker/qgisserver/pyproject.toml +++ b/docker/qgisserver/pyproject.toml @@ -28,7 +28,7 @@ c2cwsgiutils = { extras = ["broadcast"], version = "6.1.5" } papyrus = "2.6.2" # commons transaction = "5.0" # commons "c2c.template" = "2.4.2" -GeoAlchemy2 = "0.15.2" +GeoAlchemy2 = "0.16.0" SQLAlchemy = "2.0.36" # commons "zope.event" = "5.0" # commons "zope.sqlalchemy" = "3.1" # commons @@ -39,7 +39,7 @@ pytz = "2024.2" [tool.poetry.dev-dependencies] prospector = { extras = ["with_mypy", "with_bandit"], version = "1.13.3" } -prospector-profile-duplicated = "1.6.0" -prospector-profile-utils = "1.9.1" +prospector-profile-duplicated = "1.9.0" +prospector-profile-utils = "1.14.1" types-pyyaml = "6.0.12.20240917" pytest = "8.3.4" diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml index 9210b816e5..6612b23f2c 100644 --- a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml +++ b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/docker-compose-lib.yaml @@ -192,7 +192,7 @@ services: - redis_slave tilecloudchain: - image: &tilecloudchain-image camptocamp/tilecloud-chain:1.21 + image: &tilecloudchain-image camptocamp/tilecloud-chain:1.22 user: www-data restart: unless-stopped environment: diff --git a/geoportal/package-lock.json b/geoportal/package-lock.json index 7b53e455a7..91b791db97 100644 --- a/geoportal/package-lock.json +++ b/geoportal/package-lock.json @@ -10,13 +10,13 @@ "dependencies": { "@snyk/protect": "1.1294.2", "ngeo": "2.9.0-version-2.9-latest.20241204T095745Z.b6b15744f.2.9", - "puppeteer": "23.7.1" + "puppeteer": "23.9.0" }, "devDependencies": { - "@eslint/eslintrc": "3.1.0", - "@eslint/js": "9.14.0", + "@eslint/eslintrc": "3.2.0", + "@eslint/js": "9.16.0", "@popperjs/core": "2.11.8", - "@sentry/browser": "8.37.1", + "@sentry/browser": "8.42.0", "angular-gettext-tools": "2.5.3", "angular-mocks": "1.8.3", "commander": "12.1.0", @@ -25,7 +25,7 @@ "cy-mobile-commands": "0.3.0", "ejs-loader": "0.5.0", "eslint": "8.57.1", - "eslint-plugin-jsdoc": "50.4.3", + "eslint-plugin-jsdoc": "50.6.0", "eslint-plugin-lit": "1.15.0", "eslint-plugin-storybook": "0.11.1", "eslint-plugin-wc": "2.2.0", @@ -40,7 +40,7 @@ "minify-html-literals-loader": "1.1.1", "neat-csv": "7.0.0", "parse-absolute-css-unit": "1.0.2", - "sass": "1.80.7", + "sass": "1.81.1", "sass-loader": "16.0.3", "simple-html-tokenizer": "0.5.11", "svgo": "3.3.2", @@ -48,7 +48,7 @@ "terser": "5.36.0", "terser-webpack-plugin": "5.3.10", "url-parse": "1.5.10", - "webpack": "5.96.1", + "webpack": "5.97.0", "webpack-cli": "5.1.4", "webpack-dev-server": "5.1.0", "webpack-merge": "6.0.1" @@ -577,9 +577,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", "dev": true, "license": "MIT", "dependencies": { @@ -601,9 +601,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", - "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.16.0.tgz", + "integrity": "sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==", "dev": true, "license": "MIT", "engines": { @@ -1315,123 +1315,86 @@ } }, "node_modules/@sentry-internal/browser-utils": { - "version": "8.37.1", - "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.37.1.tgz", - "integrity": "sha512-OSR/V5GCsSCG7iapWtXCT/y22uo3HlawdEgfM1NIKk1mkP15UyGQtGEzZDdih2H+SNuX1mp9jQLTjr5FFp1A5w==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.42.0.tgz", + "integrity": "sha512-xzgRI0wglKYsPrna574w1t38aftuvo44gjOKFvPNGPnYfiW9y4m+64kUz3JFbtanvOrKPcaITpdYiB4DeJXEbA==", "dev": true, "license": "MIT", "dependencies": { - "@sentry/core": "8.37.1", - "@sentry/types": "8.37.1", - "@sentry/utils": "8.37.1" + "@sentry/core": "8.42.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/feedback": { - "version": "8.37.1", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.37.1.tgz", - "integrity": "sha512-Se25NXbSapgS2S+JssR5YZ48b3OY4UGmAuBOafgnMW91LXMxRNWRbehZuNUmjjHwuywABMxjgu+Yp5uJDATX+g==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.42.0.tgz", + "integrity": "sha512-dkIw5Wdukwzngg5gNJ0QcK48LyJaMAnBspqTqZ3ItR01STi6Z+6+/Bt5XgmrvDgRD+FNBinflc5zMmfdFXXhvw==", "dev": true, "license": "MIT", "dependencies": { - "@sentry/core": "8.37.1", - "@sentry/types": "8.37.1", - "@sentry/utils": "8.37.1" + "@sentry/core": "8.42.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/replay": { - "version": "8.37.1", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.37.1.tgz", - "integrity": "sha512-E/Plhisk/pXJjOdOU12sg8m/APTXTA21iEniidP6jW3/+O0tD/H/UovEqa4odNTqxPMa798xHQSQNt5loYiaLA==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.42.0.tgz", + "integrity": "sha512-oNcJEBlDfXnRFYC5Mxj5fairyZHNqlnU4g8kPuztB9G5zlsyLgWfPxzcn1ixVQunth2/WZRklDi4o1ZfyHww7w==", "dev": true, "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "8.37.1", - "@sentry/core": "8.37.1", - "@sentry/types": "8.37.1", - "@sentry/utils": "8.37.1" + "@sentry-internal/browser-utils": "8.42.0", + "@sentry/core": "8.42.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/replay-canvas": { - "version": "8.37.1", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.37.1.tgz", - "integrity": "sha512-1JLAaPtn1VL5vblB0BMELFV0D+KUm/iMGsrl4/JpRm0Ws5ESzQl33DhXVv1IX/ZAbx9i14EjR7MG9+Hj70tieQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.42.0.tgz", + "integrity": "sha512-XrPErqVhPsPh/oFLVKvz7Wb+Fi2J1zCPLeZCxWqFuPWI2agRyLVu0KvqJyzSpSrRAEJC/XFzuSVILlYlXXSfgA==", "dev": true, "license": "MIT", "dependencies": { - "@sentry-internal/replay": "8.37.1", - "@sentry/core": "8.37.1", - "@sentry/types": "8.37.1", - "@sentry/utils": "8.37.1" + "@sentry-internal/replay": "8.42.0", + "@sentry/core": "8.42.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/browser": { - "version": "8.37.1", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.37.1.tgz", - "integrity": "sha512-5ym+iGiIpjIKKpMWi9S3/tXh9xneS+jqxwRTJqed3cb8i4ydfMAAP8sM3U8xMCWWABpWyIUW+fpewC0tkhE1aQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.42.0.tgz", + "integrity": "sha512-lStrEk609KJHwXfDrOgoYVVoFFExixHywxSExk7ZDtwj2YPv6r6Y1gogvgr7dAZj7jWzadHkxZ33l9EOSJBfug==", "dev": true, "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "8.37.1", - "@sentry-internal/feedback": "8.37.1", - "@sentry-internal/replay": "8.37.1", - "@sentry-internal/replay-canvas": "8.37.1", - "@sentry/core": "8.37.1", - "@sentry/types": "8.37.1", - "@sentry/utils": "8.37.1" + "@sentry-internal/browser-utils": "8.42.0", + "@sentry-internal/feedback": "8.42.0", + "@sentry-internal/replay": "8.42.0", + "@sentry-internal/replay-canvas": "8.42.0", + "@sentry/core": "8.42.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/core": { - "version": "8.37.1", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.37.1.tgz", - "integrity": "sha512-82csXby589iDupM3VgCHJeWZagUyEEaDnbFcoZ/Z91QX2Sjq8FcF5OsforoXjw09i0XTFqlkFAnQVpDBmMXcpQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/types": "8.37.1", - "@sentry/utils": "8.37.1" - }, - "engines": { - "node": ">=14.18" - } - }, - "node_modules/@sentry/types": { - "version": "8.37.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.37.1.tgz", - "integrity": "sha512-ryMOTROLSLINKFEbHWvi7GigNrsQhsaScw2NddybJGztJQ5UhxIGESnxGxWCufBmWFDwd7+5u0jDPCVUJybp7w==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.42.0.tgz", + "integrity": "sha512-ac6O3pgoIbU6rpwz6LlwW0wp3/GAHuSI0C5IsTgIY6baN8rOBnlAtG6KrHDDkGmUQ2srxkDJu9n1O6Td3cBCqw==", "dev": true, "license": "MIT", "engines": { "node": ">=14.18" } }, - "node_modules/@sentry/utils": { - "version": "8.37.1", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.37.1.tgz", - "integrity": "sha512-Qtn2IfpII12K17txG/ZtTci35XYjYi4CxbQ3j7nXY7toGv/+MqPXwV5q2i9g94XaSXlE5Wy9/hoCZoZpZs/djA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/types": "8.37.1" - }, - "engines": { - "node": ">=14.18" - } - }, "node_modules/@sindresorhus/merge-streams": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", @@ -5309,9 +5272,9 @@ "license": "MIT" }, "node_modules/devtools-protocol": { - "version": "0.0.1354347", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1354347.tgz", - "integrity": "sha512-BlmkSqV0V84E2WnEnoPnwyix57rQxAM5SKJjf4TbYOCGLAWtz8CDH8RIaGOjPgPCXo2Mce3kxSY497OySidY3Q==", + "version": "0.0.1367902", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz", + "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==", "license": "BSD-3-Clause" }, "node_modules/dns-packet": { @@ -5770,9 +5733,9 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "50.4.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.4.3.tgz", - "integrity": "sha512-uWtwFxGRv6B8sU63HZM5dAGDhgsatb+LONwmILZJhdRALLOkCX2HFZhdL/Kw2ls8SQMAVEfK+LmnEfxInRN8HA==", + "version": "50.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.6.0.tgz", + "integrity": "sha512-tCNp4fR79Le3dYTPB0dKEv7yFyvGkUCa+Z3yuTrrNGGOxBlXo9Pn0PEgroOZikUQOGjxoGMVKNjrOHcYEdfszg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -10409,17 +10372,17 @@ } }, "node_modules/puppeteer": { - "version": "23.7.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.7.1.tgz", - "integrity": "sha512-jS6XehagMvxQ12etwY/4EOYZ0Sm8GAsrtGhdQn4AqpJAyHc3RYl7tGd4QYh/MmShDw8sF9FWYQqGidhoXaqokQ==", + "version": "23.9.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.9.0.tgz", + "integrity": "sha512-WfB8jGwFV+qrD9dcJJVvWPFJBU6kxeu2wxJz9WooDGfM3vIiKLgzImEDBxUQnCBK/2cXB3d4dV6gs/LLpgfLDg==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.4.1", "chromium-bidi": "0.8.0", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1354347", - "puppeteer-core": "23.7.1", + "devtools-protocol": "0.0.1367902", + "puppeteer-core": "23.9.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -10430,15 +10393,15 @@ } }, "node_modules/puppeteer-core": { - "version": "23.7.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.7.1.tgz", - "integrity": "sha512-Om/qCZhd+HLoAr7GltrRAZpS3uOXwHu7tXAoDbNcJADHjG2zeAlDArgyIPXYGG4QB/EQUHk13Q6RklNxGM73Pg==", + "version": "23.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.9.0.tgz", + "integrity": "sha512-hLVrav2HYMVdK0YILtfJwtnkBAwNOztUdR4aJ5YKDvgsbtagNr6urUJk9HyjRA9e+PaLI3jzJ0wM7A4jSZ7Qxw==", "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.4.1", "chromium-bidi": "0.8.0", "debug": "^4.3.7", - "devtools-protocol": "0.0.1354347", + "devtools-protocol": "0.0.1367902", "typed-query-selector": "^2.12.0", "ws": "^8.18.0" }, @@ -11148,9 +11111,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.80.7", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.7.tgz", - "integrity": "sha512-MVWvN0u5meytrSjsU7AWsbhoXi1sc58zADXFllfZzbsBT1GHjjar6JwBINYPRrkx/zqnQ6uqbQuHgE95O+C+eQ==", + "version": "1.81.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.81.1.tgz", + "integrity": "sha512-VNLgf4FC5yFyKwAumAAwwNh8X4SevlVREq3Y8aDZIkm0lI/zO1feycMXQ4hn+eB6FVhRbleSQ1Yb/q8juSldTA==", "dev": true, "license": "MIT", "dependencies": { @@ -12877,17 +12840,17 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.96.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", - "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", + "version": "5.97.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.0.tgz", + "integrity": "sha512-CWT8v7ShSfj7tGs4TLRtaOLmOCPWhoKEvp+eA7FVx8Xrjb3XfT0aXdxDItnRZmE8sHcH+a8ayDrJCOjXKxVFfQ==", "dev": true, "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.14.0", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", diff --git a/geoportal/package.json b/geoportal/package.json index 4ad69a66c4..691f8809e4 100644 --- a/geoportal/package.json +++ b/geoportal/package.json @@ -10,13 +10,13 @@ "dependencies": { "@snyk/protect": "1.1294.2", "ngeo": "2.9.0-version-2.9-latest.20241204T095745Z.b6b15744f.2.9", - "puppeteer": "23.7.1" + "puppeteer": "23.9.0" }, "devDependencies": { - "@eslint/eslintrc": "3.1.0", - "@eslint/js": "9.14.0", + "@eslint/eslintrc": "3.2.0", + "@eslint/js": "9.16.0", "@popperjs/core": "2.11.8", - "@sentry/browser": "8.37.1", + "@sentry/browser": "8.42.0", "angular-gettext-tools": "2.5.3", "angular-mocks": "1.8.3", "commander": "12.1.0", @@ -25,7 +25,7 @@ "cy-mobile-commands": "0.3.0", "ejs-loader": "0.5.0", "eslint": "8.57.1", - "eslint-plugin-jsdoc": "50.4.3", + "eslint-plugin-jsdoc": "50.6.0", "eslint-plugin-lit": "1.15.0", "eslint-plugin-storybook": "0.11.1", "eslint-plugin-wc": "2.2.0", @@ -40,7 +40,7 @@ "minify-html-literals-loader": "1.1.1", "neat-csv": "7.0.0", "parse-absolute-css-unit": "1.0.2", - "sass": "1.80.7", + "sass": "1.81.1", "sass-loader": "16.0.3", "simple-html-tokenizer": "0.5.11", "svgo": "3.3.2", @@ -48,7 +48,7 @@ "terser": "5.36.0", "terser-webpack-plugin": "5.3.10", "url-parse": "1.5.10", - "webpack": "5.96.1", + "webpack": "5.97.0", "webpack-cli": "5.1.4", "webpack-dev-server": "5.1.0", "webpack-merge": "6.0.1" diff --git a/poetry.lock b/poetry.lock index 3c1cc5d925..688330eb9a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -129,13 +129,13 @@ aio = ["aiohttp (>=3.0)"] [[package]] name = "azure-storage-blob" -version = "12.23.1" +version = "12.24.0" description = "Microsoft Azure Blob Storage Client Library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "azure_storage_blob-12.23.1-py3-none-any.whl", hash = "sha256:1c2238aa841d1545f42714a5017c010366137a44a0605da2d45f770174bfc6b4"}, - {file = "azure_storage_blob-12.23.1.tar.gz", hash = "sha256:a587e54d4e39d2a27bd75109db164ffa2058fe194061e5446c5a89bca918272f"}, + {file = "azure_storage_blob-12.24.0-py3-none-any.whl", hash = "sha256:4f0bb4592ea79a2d986063696514c781c9e62be240f09f6397986e01755bc071"}, + {file = "azure_storage_blob-12.24.0.tar.gz", hash = "sha256:eaaaa1507c8c363d6e1d1342bd549938fdf1adec9b1ada8658c8f5bf3aea844e"}, ] [package.dependencies] @@ -1246,13 +1246,13 @@ six = ">=1.8.0" [[package]] name = "geoalchemy2" -version = "0.15.2" +version = "0.16.0" description = "Using SQLAlchemy with Spatial Databases" optional = false python-versions = ">=3.7" files = [ - {file = "GeoAlchemy2-0.15.2-py3-none-any.whl", hash = "sha256:546455dc39f5bcdfc5b871e57d3f7546c8a6f798eb364c474200f488ace6fd32"}, - {file = "geoalchemy2-0.15.2.tar.gz", hash = "sha256:3af0272db927373e74ee3b064cdc9464ba08defdb945c51745db1b841482f5dc"}, + {file = "GeoAlchemy2-0.16.0-py3-none-any.whl", hash = "sha256:b0f27d5500ee757af4654c6262e0f834b7a843504d193653ec747ef1128d2ab5"}, + {file = "geoalchemy2-0.16.0.tar.gz", hash = "sha256:df64bb72af70daafaac3f359492c96501c37ab85ed20f9510c99cc6d02881100"}, ] [package.dependencies] @@ -2654,26 +2654,29 @@ with-vulture = ["vulture (>=1.5)"] [[package]] name = "prospector-profile-duplicated" -version = "1.6.0" +version = "1.9.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"}, + {file = "prospector_profile_duplicated-1.9.0-py2.py3-none-any.whl", hash = "sha256:7b7a665e6fa8b44fac597d2bbef1a91de5b6f090b8d64890a5f38ee60e8473c2"}, + {file = "prospector_profile_duplicated-1.9.0.tar.gz", hash = "sha256:bb0b0d0946232d570ada944ee4a180fd142529db0978579b58e766d28f3c2a27"}, ] [[package]] name = "prospector-profile-utils" -version = "1.9.1" +version = "1.14.1" description = "Some utility Prospector profiles." optional = false -python-versions = "*" +python-versions = "<4.0,>=3.9" 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"}, + {file = "prospector_profile_utils-1.14.1-py3-none-any.whl", hash = "sha256:1b7d79e4293c76f9ea5107b691c888e76933aa29d33e8f8b8750dc0aeea3b657"}, + {file = "prospector_profile_utils-1.14.1.tar.gz", hash = "sha256:5447086f9a7ddba02d8ee7322baa30c80c1376328677a6593165fab23e2e4bf2"}, ] +[package.dependencies] +prospector = ">=1.13.0" + [[package]] name = "protobuf" version = "5.29.0" @@ -3442,13 +3445,13 @@ dev = ["pytest", "pytest-cache", "pytest-cov", "ruff"] [[package]] name = "pyramid-tm" -version = "2.5" +version = "2.6" description = "A package which allows Pyramid requests to join the active transaction" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "pyramid_tm-2.5-py2.py3-none-any.whl", hash = "sha256:6638721946e809de8b4bf3f405bd2daaaa76d58442cbdf46be30ebc259f1a354"}, - {file = "pyramid_tm-2.5.tar.gz", hash = "sha256:5c81dcecd33770f5e3596687d2be35ffc4f8ce5eda00a31acb00ae35a51430d0"}, + {file = "pyramid_tm-2.6-py3-none-any.whl", hash = "sha256:665a4ee1d6f41f0c7ffa5e54d9b01d70cd3e8e5bd76277529acbdd6b6bd6fe9e"}, + {file = "pyramid_tm-2.6.tar.gz", hash = "sha256:8148d2191285280c9a0c23e6df1018b3514b4cef02115b872dd0350a4d78709c"}, ] [package.dependencies] @@ -4520,13 +4523,13 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "75.3.0.20241112" +version = "75.6.0.20241126" description = "Typing stubs for setuptools" optional = false python-versions = ">=3.8" files = [ - {file = "types-setuptools-75.3.0.20241112.tar.gz", hash = "sha256:f9e1ebd17a56f606e16395c4ee4efa1cdc394b9a2a0ee898a624058b4b62ef8f"}, - {file = "types_setuptools-75.3.0.20241112-py3-none-any.whl", hash = "sha256:78cb5fef4a6056d2f37114d27da90f4655a306e4e38042d7034a8a880bc3f5dd"}, + {file = "types_setuptools-75.6.0.20241126-py3-none-any.whl", hash = "sha256:aaae310a0e27033c1da8457d4d26ac673b0c8a0de7272d6d4708e263f2ea3b9b"}, + {file = "types_setuptools-75.6.0.20241126.tar.gz", hash = "sha256:7bf25ad4be39740e469f9268b6beddda6e088891fa5a27e985c6ce68bf62ace0"}, ] [[package]] @@ -4862,4 +4865,4 @@ test = ["zope.testing"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "5cc5d16690ab634e8c0d47984b08a9e6d2f0c913cb84011fa9901c467df269f2" +content-hash = "17be9d85b8164aa82f5206c01e5d79bf526176ff253d43c9bcc49d60cbacb9cc" diff --git a/pyproject.toml b/pyproject.toml index 503a754b1f..1a8e7b4980 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ deform = "2.0.15" # commons, admin defusedxml = "0.7.1" # geoportal "dogpile.cache" = "1.3.3" # geoportal Fiona = "1.10.1" # geoportal raster -GeoAlchemy2 = "0.15.2" # commons, geoportal +GeoAlchemy2 = "0.16.0" # commons, geoportal geojson = "3.1.0" # geoportal getitfixed = "1.0.29" # geoportal isodate = "0.7.2" # geoportal @@ -56,7 +56,7 @@ pyramid_debugtoolbar = "4.12.1" # geoportal pyramid-jinja2 = "2.10.1" # admin pyramid_mako = "1.1.0" # geoportal pyramid_multiauth = "1.0.2" # geoportal -pyramid_tm = "2.5" # geoportal +pyramid_tm = "2.6" # geoportal python-dateutil = "2.9.0.post0" # geoportal PyYAML = "6.0.1" # geoportal rasterio = "1.4.2" # geoportal raster @@ -70,14 +70,14 @@ translationstring = "1.4" # admin c2cwsgiutils = { version = "6.1.5", extras = ["broadcast", "standard", "oauth2", "debug"] } oauthlib = "3.2.2" tilecloud = "1.12.3" # geoportal -azure-storage-blob = "12.23.1" +azure-storage-blob = "12.24.0" # simple_openid_connect = '1.0.1' # geoportal simple_openid_connect = { git = "https://github.com/sbrunner/py_simple_openid_connect.git", branch = "allows-pkce" } # geoportal pkce = '1.0.3' # geoportal basicauth = "1.0.0" prospector = { extras = ["with_mypy", "with_bandit", "with_pyroma"], version = "1.13.3" } -prospector-profile-duplicated = "1.6.0" -prospector-profile-utils = "1.9.1" +prospector-profile-duplicated = "1.9.0" +prospector-profile-utils = "1.14.1" beautifulsoup4 = "4.12.3" [tool.poetry.group.dev.dependencies] @@ -94,7 +94,7 @@ types-pytz = "2024.2.0.20241003" types-pyyaml = "6.0.12.20240917" types-python-dateutil = "2.9.0.20241003" types-requests = "2.32.0.20241016" -types-setuptools = "75.3.0.20241112" +types-setuptools = "75.6.0.20241126" mappyfile = "1.0.2" # To be able to upgrade project from version <= 2.8 c2cciutils = { version = "1.5.8", extras = ["checks"] } From bcc9dd69dda47dbfd5c033ded51da02d973df2cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Mon, 2 Dec 2024 18:21:09 +0100 Subject: [PATCH 02/12] Add Ruff --- .bandit.yaml | 31 -- .github/workflows/main.yaml | 2 +- .pre-commit-config.yaml | 52 +-- .prospector.yaml | 30 +- admin/c2cgeoportal_admin/__init__.py | 4 +- .../lib/ogcserver_synchronizer.py | 15 +- .../c2cgeoportal_admin/schemas/dimensions.py | 3 +- .../schemas/functionalities.py | 4 +- .../c2cgeoportal_admin/schemas/interfaces.py | 3 +- admin/c2cgeoportal_admin/schemas/metadata.py | 5 +- .../schemas/restriction_areas.py | 3 +- admin/c2cgeoportal_admin/schemas/roles.py | 3 +- admin/c2cgeoportal_admin/schemas/treegroup.py | 9 +- admin/c2cgeoportal_admin/views/__init__.py | 4 +- .../views/dimension_layers.py | 2 +- .../views/functionalities.py | 6 +- admin/c2cgeoportal_admin/views/interfaces.py | 2 +- .../c2cgeoportal_admin/views/layer_groups.py | 2 +- admin/c2cgeoportal_admin/views/layers.py | 2 +- admin/c2cgeoportal_admin/views/layers_cog.py | 4 +- .../views/layers_vectortiles.py | 4 +- admin/c2cgeoportal_admin/views/layers_wms.py | 9 +- admin/c2cgeoportal_admin/views/layers_wmts.py | 9 +- admin/c2cgeoportal_admin/views/layertree.py | 8 +- .../c2cgeoportal_admin/views/logged_views.py | 3 +- admin/c2cgeoportal_admin/views/logs.py | 11 +- .../views/oauth2_clients.py | 2 +- admin/c2cgeoportal_admin/views/ogc_servers.py | 6 +- .../views/restriction_areas.py | 2 +- admin/c2cgeoportal_admin/views/roles.py | 4 +- admin/c2cgeoportal_admin/views/themes.py | 2 +- .../views/themes_ordering.py | 6 +- admin/c2cgeoportal_admin/views/treeitems.py | 7 +- admin/c2cgeoportal_admin/views/users.py | 6 +- admin/c2cgeoportal_admin/widgets.py | 3 +- admin/tests/__init__.py | 23 +- admin/tests/conftest.py | 10 +- .../tests/lib/test_ogcserver_synchronizer.py | 16 +- admin/tests/schemas/test_metadata.py | 4 +- admin/tests/test_edit_url.py | 2 +- admin/tests/test_functionalities.py | 8 +- admin/tests/test_home.py | 2 +- admin/tests/test_interface.py | 12 +- admin/tests/test_layer_groups.py | 48 +-- admin/tests/test_layers_cog.py | 30 +- admin/tests/test_layers_vectortiles.py | 32 +- admin/tests/test_layers_wms.py | 84 ++-- admin/tests/test_layers_wmts.py | 48 ++- admin/tests/test_layertree.py | 12 +- admin/tests/test_lingva_extractor_config.py | 4 - admin/tests/test_logs.py | 1 - admin/tests/test_main.py | 4 +- admin/tests/test_metadatas.py | 13 +- admin/tests/test_oauth2_clients.py | 16 +- admin/tests/test_ogc_servers.py | 12 +- admin/tests/test_restriction_areas.py | 17 +- admin/tests/test_role.py | 14 +- admin/tests/test_themes.py | 32 +- admin/tests/test_treegroup.py | 2 +- admin/tests/test_user.py | 16 +- admin/tests/themes_ordering.py | 2 +- bin/azure | 2 +- bin/build-l10n | 2 +- ci/changelog | 2 +- commons/c2cgeoportal_commons/alembic/env.py | 6 +- .../main/166ff2dcc48d_create_database.py | 25 +- ..._trigger_on_role_updates_user_in_static.py | 6 +- .../main/56dc90838d90_fix_removing_layerv1.py | 8 +- ..._trigger_on_role_updates_user_in_static.py | 6 +- ...527cd_add_layer_column_in_layerv1_table.py | 6 +- ...c0_add_trigger_to_be_able_to_correctly_.py | 10 +- ...345672454_merge_2_4_and_master_branches.py | 1 - ...a88908_move_user_table_to_static_schema.py | 34 +- ...1b17b20_add_timezone_on_datetime_fields.py | 12 +- ...e9613256_wip_add_openid_connect_support.py | 1 - .../c2cgeoportal_commons/models/__init__.py | 2 +- commons/c2cgeoportal_commons/models/main.py | 23 +- commons/c2cgeoportal_commons/models/static.py | 2 +- .../c2cgeoportal_commons/testing/__init__.py | 5 +- .../testing/initializedb.py | 22 +- commons/tests/conftest.py | 10 +- commons/tests/lib/test_url.py | 5 +- commons/tests/lib/test_validators.py | 1 - commons/tests/models/test_interface.py | 5 +- commons/tests/models/test_roles.py | 7 +- commons/tests/models/test_users.py | 14 +- doc/import_ngeo_config.py | 2 +- doc/integrator/requirements.rst | 2 +- doc/pyproject.toml | 8 - docker/config/bin/eval-templates | 6 +- docker/qgisserver/.bandit.yaml | 2 - docker/qgisserver/.prospector.yaml | 5 +- .../geomapfish_qgisserver/__init__.py | 5 +- .../geomapfish_qgisserver/accesscontrol.py | 68 ++-- .../geomapfish_qgisserver/gmf_logging.py | 11 +- docker/qgisserver/poetry.lock | 32 +- docker/qgisserver/pyproject.toml | 21 +- .../accesscontrol_allow_to_edit_test.py | 9 +- ...accesscontrol_protected_attributes_test.py | 24 +- .../tests/functional/accesscontrol_test.py | 26 +- .../qgisserver/tests/functional/conftest.py | 12 +- geoportal/c2cgeoportal_geoportal/__init__.py | 44 +- .../c2cgeoportal_geoportal/lib/__init__.py | 28 +- .../lib/authentication.py | 2 +- .../c2cgeoportal_geoportal/lib/caching.py | 6 +- .../c2cgeoportal_geoportal/lib/checker.py | 14 +- .../lib/common_headers.py | 1 - .../lib/dbreflection.py | 17 +- .../lib/filter_capabilities.py | 27 +- .../lib/functionality.py | 6 +- .../c2cgeoportal_geoportal/lib/layers.py | 27 +- .../lib/lingva_extractor.py | 58 ++- .../c2cgeoportal_geoportal/lib/oauth2.py | 381 +++++++++++------- geoportal/c2cgeoportal_geoportal/lib/oidc.py | 38 +- .../c2cgeoportal_geoportal/lib/wmstparsing.py | 10 +- geoportal/c2cgeoportal_geoportal/lib/xsd.py | 22 +- .../geoportal/.prospector.yaml | 29 +- .../geoportal/gunicorn.conf.py | 3 - .../authentication.py | 3 +- .../models.py | 3 +- .../multi_organization.py | 1 - .../{{cookiecutter.project}}/pyproject.toml | 9 +- .../tests/test_testapp.py | 2 +- .../scripts/c2cupgrade.py | 138 +++---- .../scripts/manage_users.py | 10 +- .../c2cgeoportal_geoportal/scripts/pcreate.py | 19 +- .../scripts/theme2fts.py | 11 +- .../scripts/urllogin.py | 2 - geoportal/c2cgeoportal_geoportal/views/dev.py | 4 +- .../c2cgeoportal_geoportal/views/dynamic.py | 7 +- .../c2cgeoportal_geoportal/views/entry.py | 2 - .../views/fulltextsearch.py | 4 +- .../views/geometry_processing.py | 3 +- .../c2cgeoportal_geoportal/views/i18n.py | 1 - .../c2cgeoportal_geoportal/views/layers.py | 30 +- .../c2cgeoportal_geoportal/views/login.py | 19 +- .../views/mapserverproxy.py | 11 +- .../c2cgeoportal_geoportal/views/ogcproxy.py | 4 +- .../c2cgeoportal_geoportal/views/pdfreport.py | 8 +- .../views/printproxy.py | 2 +- .../c2cgeoportal_geoportal/views/proxy.py | 2 +- .../c2cgeoportal_geoportal/views/raster.py | 6 +- .../views/resourceproxy.py | 2 +- .../c2cgeoportal_geoportal/views/shortener.py | 13 +- .../c2cgeoportal_geoportal/views/theme.py | 50 +-- .../views/tinyowsproxy.py | 22 +- .../views/vector_tiles.py | 2 +- geoportal/tests/__init__.py | 9 +- geoportal/tests/functional/__init__.py | 29 +- geoportal/tests/functional/conftest.py | 3 +- .../tests/functional/test_authentication.py | 13 +- .../tests/functional/test_dbreflection.py | 38 +- .../tests/functional/test_dynamicview.py | 16 +- geoportal/tests/functional/test_entry.py | 19 +- .../tests/functional/test_fulltextsearch.py | 69 ++-- .../tests/functional/test_functionalities.py | 10 +- .../functional/test_geometry_processing.py | 4 +- .../tests/functional/test_groups_finder.py | 4 +- geoportal/tests/functional/test_layers.py | 112 ++--- .../test_lingva_extractor_config.py | 5 +- .../test_lingva_extractor_themes.py | 2 - geoportal/tests/functional/test_login.py | 31 +- geoportal/tests/functional/test_login_2fa.py | 6 +- .../tests/functional/test_mapserverproxy.py | 6 +- .../test_mapserverproxy_capabilities.py | 10 +- .../functional/test_mapserverproxy_group.py | 16 +- .../tests/functional/test_mobile_desktop.py | 16 +- geoportal/tests/functional/test_oauth2.py | 13 +- geoportal/tests/functional/test_ogcproxy.py | 6 +- geoportal/tests/functional/test_oidc.py | 7 +- .../tests/functional/test_request_property.py | 8 +- geoportal/tests/functional/test_shortener.py | 32 +- geoportal/tests/functional/test_theme2fts.py | 2 +- geoportal/tests/functional/test_themes.py | 5 + .../functional/test_themes_dimensions.py | 8 +- .../functional/test_themes_edit_columns.py | 18 +- .../tests/functional/test_themes_editing.py | 17 +- .../tests/functional/test_themes_entry.py | 20 +- .../test_themes_layermultinameerror.py | 8 +- .../tests/functional/test_themes_loop.py | 15 +- .../tests/functional/test_themes_metadata.py | 9 +- .../tests/functional/test_themes_mixed.py | 9 +- .../tests/functional/test_themes_nameerror.py | 16 +- .../test_themes_ogc_server_cache_clean.py | 10 +- .../tests/functional/test_themes_private.py | 9 +- .../tests/functional/test_themes_scale.py | 17 +- .../tests/functional/test_themes_time.py | 9 +- .../tests/functional/test_tinyowsproxy.py | 55 +-- .../tests/functional/test_vector_tiles.py | 3 +- geoportal/tests/functional/test_xsd.py | 15 +- geoportal/tests/test_cachebuster.py | 6 +- geoportal/tests/test_caching.py | 18 +- geoportal/tests/test_checker.py | 6 +- geoportal/tests/test_headerstween.py | 3 +- geoportal/tests/test_init.py | 12 +- geoportal/tests/test_locale_negociator.py | 8 +- .../test_mapserverproxy_route_predicate.py | 5 +- geoportal/tests/test_raster.py | 26 +- geoportal/tests/test_wmstparsing.py | 29 +- poetry.lock | 32 +- pyproject.toml | 26 +- scripts/get-version | 4 +- scripts/updated_version | 2 +- scripts/upgrade | 4 +- 204 files changed, 1668 insertions(+), 1527 deletions(-) delete mode 100644 .bandit.yaml delete mode 100644 docker/qgisserver/.bandit.yaml diff --git a/.bandit.yaml b/.bandit.yaml deleted file mode 100644 index a04a4bb1dc..0000000000 --- a/.bandit.yaml +++ /dev/null @@ -1,31 +0,0 @@ -profile: - sql: - exclude: - - /commons/c2cgeoportal_commons/alembic/main/ - - /commons/c2cgeoportal_commons/alembic/static/ - tests: - - B608 # Possible SQL injection vector through string-based query construction. - subprocess: - exclude: - - /commons/c2cgeoportal_commons/testing/ - - /commons/tests/ - - /geoportal/tests/ - - /geoportal/c2cgeoportal_geoportal/scripts/c2cupgrade.py - - /admin/tests/ - tests: - - B603 # subprocess call - check for execution of untrusted input. - - B607 # Starting a process with a partial executable path - - B404 # Consider possible security implications associated with call module. - tmp: - exclude: - - /geoportal/c2cgeoportal_geoportal/scripts/c2cupgrade.py - tests: - - B108 # Probable insecure usage of temp file/directory. -skips: - - B101 # Test for use of assert - - B603 # subprocess call - check for execution of untrusted input. - - B607 # Starting a process with a partial executable path - - B608 # Possible SQL injection vector through string-based query construction. - - B108 # Probable insecure usage of temp file/directory. - - B404 # Consider possible security implications associated with call module. - - B113 # Requests call without timeout. Done by c2cwsgiutils diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 0688a4e1c0..58ef79b732 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -34,7 +34,7 @@ jobs: # When we upgrade this we should also upgrade the requirements # in the documentation: doc/integrator/requirements.rst # and the first pyupgrade pre-commit hook in .pre-commit-config.yaml - MIN_PYTHON_VERSION: '3.8' + MIN_PYTHON_VERSION: '3.10' steps: - run: '! ls BACKPORT_TODO' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3be1489d3b..274d5b640e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -104,64 +104,28 @@ repos: docker/config/haproxy_dev/localhost\.pem |geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter\.project}}/mapserver/data/TM_EUROPE_BORDERS-0.3\.sql )$ - - repo: https://github.com/asottile/pyupgrade - rev: v3.19.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.8.1 hooks: - # The script that will run on the project host - - id: pyupgrade + - id: ruff-format args: - - --py38-plus - files: |- - (?x)^( - geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter\.project}}/(build - |scripts/.*) - |scripts/(get-version - |upgrade) - )$ - # All other - - id: pyupgrade - args: - - --py310-plus - # geoportal/c2cgeoportal_geoportal/views/theme\.py is present because of issue: - # https://bugs.launchpad.net/lxml/+bug/2079018 - exclude: |- - (?x)^( - geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter\.project}}/(build - |scripts/.*) - |scripts/(get-version - |upgrade) - |geoportal/c2cgeoportal_geoportal/views/theme\.py - )$ - - repo: https://github.com/PyCQA/autoflake - rev: v2.3.1 - hooks: - - id: autoflake - - repo: https://github.com/PyCQA/isort - rev: 5.13.2 - hooks: - - id: isort - - repo: https://github.com/psf/black - rev: 24.10.0 - hooks: - - id: black + - --line-length=110 exclude: |- (?x)^( - commons/c2cgeoportal_commons/alembic/script\.py\.mako - |.*\.rst - |.*\.rst\.tmpl - |geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__\.py + geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/__init__\.py )$ - repo: https://github.com/PyCQA/prospector rev: v1.13.3 hooks: - id: prospector args: - - --tool=pydocstyle + - --tool=ruff - --die-on-tool-error - --output-format=pylint additional_dependencies: - - prospector-profile-duplicated==1.8.0 # pypi + - prospector-profile-duplicated==1.8.1 # pypi - prospector-profile-utils==1.13.0 # pypi + - ruff==0.8.1 # pypi - repo: https://github.com/sbrunner/jsonschema-validator rev: 0.3.2 hooks: diff --git a/.prospector.yaml b/.prospector.yaml index 95964a60c7..c6b2c654c5 100644 --- a/.prospector.yaml +++ b/.prospector.yaml @@ -3,6 +3,8 @@ inherits: - utils:base - utils:fix - utils:no-design-checks + - utils:unsafe + - utils:c2cwsgiutils ignore-paths: - commons/setup.py @@ -10,28 +12,14 @@ ignore-paths: - admin/setup.py - geoportal/c2cgeoportal_geoportal/scaffolds - docker/qgisserver + - commons/c2cgeoportal_commons/alembic/main + - commons/c2cgeoportal_commons/alembic/static -pycodestyle: - disable: - # Buggy checks with Python 3.12 - - W604 # backticks are deprecated, use 'repr()' - - W603 # '<>' is deprecated, use '!=' - - E702 # multiple statements on one line (semicolon) - - E713 # test for membership should be 'not in' +mypy: + options: + python_version: '3.10' -pydocstyle: +ruff: disable: - D102 # Missing docstring in public method - - D104 # Missing docstring in public package - - D105 # Missing docstring in magic method - - D107 # Missing docstring in __init__ - - D200 # One-line docstring should fit on one line with quotes - - 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') - -bandit: - options: - config: .bandit.yaml + - D107 # Missing docstring in `__init__` diff --git a/admin/c2cgeoportal_admin/__init__.py b/admin/c2cgeoportal_admin/__init__.py index 4d4227d388..2747e3c5a7 100644 --- a/admin/c2cgeoportal_admin/__init__.py +++ b/admin/c2cgeoportal_admin/__init__.py @@ -93,7 +93,9 @@ def get_tm_session( ) # Add fake user as we do not have authentication from geoportal - from c2cgeoportal_commons.models.static import User # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models.static import ( # pylint: disable=import-outside-toplevel + User, + ) config.add_request_method( lambda request: User( diff --git a/admin/c2cgeoportal_admin/lib/ogcserver_synchronizer.py b/admin/c2cgeoportal_admin/lib/ogcserver_synchronizer.py index 64905a6f47..3ca3f03444 100644 --- a/admin/c2cgeoportal_admin/lib/ogcserver_synchronizer.py +++ b/admin/c2cgeoportal_admin/lib/ogcserver_synchronizer.py @@ -29,16 +29,15 @@ import functools import logging from io import StringIO -from typing import Any, Optional, cast +from typing import Any, cast from xml.etree.ElementTree import Element # nosec import pyramid.request import requests -from defusedxml import ElementTree -from sqlalchemy.orm.session import Session - from c2cgeoportal_commons.lib.url import get_url2 from c2cgeoportal_commons.models import main +from defusedxml import ElementTree +from sqlalchemy.orm.session import Session class dry_run_transaction: # noqa ignore=N801: class names should use CapWords convention @@ -238,7 +237,7 @@ def get_theme(self, el: ElementTree) -> main.Theme: name = name_el.text theme = cast( - Optional[main.Theme], + main.Theme | None, self._request.dbsession.query(main.Theme).filter(main.Theme.name == name).one_or_none(), ) @@ -263,7 +262,7 @@ def get_layer_group(self, el: Element, parent: main.TreeGroup) -> main.LayerGrou assert name is not None group = cast( - Optional[main.LayerGroup], + main.LayerGroup | None, ( self._request.dbsession.query(main.LayerGroup) .filter(main.LayerGroup.name == name) @@ -294,7 +293,7 @@ def get_layer_wms(self, el: Element, parent: main.TreeGroup | None = None) -> ma assert name is not None layer = cast( - Optional[main.LayerWMS], + main.LayerWMS | None, self._request.dbsession.query(main.LayerWMS).filter(main.LayerWMS.name == name).one_or_none(), ) @@ -358,7 +357,7 @@ def get_layer_wms(self, el: Element, parent: main.TreeGroup | None = None) -> ma return layer - @functools.lru_cache(maxsize=10) + @functools.lru_cache(maxsize=10) # noqa: B019 def wms_capabilities(self) -> bytes: errors: set[str] = set() url = get_url2( diff --git a/admin/c2cgeoportal_admin/schemas/dimensions.py b/admin/c2cgeoportal_admin/schemas/dimensions.py index 3326057d16..4a9fb0c27e 100644 --- a/admin/c2cgeoportal_admin/schemas/dimensions.py +++ b/admin/c2cgeoportal_admin/schemas/dimensions.py @@ -30,11 +30,10 @@ import colander from c2cgeoform.schema import GeoFormSchemaNode +from c2cgeoportal_commons.models.main import Dimension from deform.widget import MappingWidget, SequenceWidget from sqlalchemy.orm.attributes import InstrumentedAttribute -from c2cgeoportal_commons.models.main import Dimension - def dimensions_schema_node( prop: InstrumentedAttribute[Any], # pylint: disable=unsubscriptable-object diff --git a/admin/c2cgeoportal_admin/schemas/functionalities.py b/admin/c2cgeoportal_admin/schemas/functionalities.py index 327aa56b56..a05d544032 100644 --- a/admin/c2cgeoportal_admin/schemas/functionalities.py +++ b/admin/c2cgeoportal_admin/schemas/functionalities.py @@ -31,12 +31,11 @@ import colander from c2cgeoform.ext.deform_ext import RelationCheckBoxListWidget from c2cgeoform.schema import GeoFormManyToManySchemaNode, manytomany_validator +from c2cgeoportal_commons.models.main import Functionality from sqlalchemy import inspect, select from sqlalchemy.orm.attributes import InstrumentedAttribute from sqlalchemy.sql.functions import concat -from c2cgeoportal_commons.models.main import Functionality - def available_functionalities_for(settings: dict[str, Any], model: type[Any]) -> list[dict[str, Any]]: """Return filtered list of functionality definitions.""" @@ -81,7 +80,6 @@ def functionalities_schema_node( prop: InstrumentedAttribute[Any], model: type[Any] ) -> colander.SequenceSchema: """Get the schema of the functionalities.""" - return colander.SequenceSchema( GeoFormManyToManySchemaNode(Functionality, None), name=prop.key, diff --git a/admin/c2cgeoportal_admin/schemas/interfaces.py b/admin/c2cgeoportal_admin/schemas/interfaces.py index 212aefc3c1..2bf697e42a 100644 --- a/admin/c2cgeoportal_admin/schemas/interfaces.py +++ b/admin/c2cgeoportal_admin/schemas/interfaces.py @@ -31,9 +31,8 @@ import colander from c2cgeoform.ext.deform_ext import RelationCheckBoxListWidget from c2cgeoform.schema import GeoFormManyToManySchemaNode, manytomany_validator -from sqlalchemy.orm.attributes import InstrumentedAttribute - from c2cgeoportal_commons.models.main import Interface +from sqlalchemy.orm.attributes import InstrumentedAttribute def interfaces_schema_node( diff --git a/admin/c2cgeoportal_admin/schemas/metadata.py b/admin/c2cgeoportal_admin/schemas/metadata.py index 4fa216ff61..cb0c738392 100644 --- a/admin/c2cgeoportal_admin/schemas/metadata.py +++ b/admin/c2cgeoportal_admin/schemas/metadata.py @@ -31,14 +31,14 @@ import colander import pyramid.request from c2cgeoform.schema import GeoFormSchemaNode +from c2cgeoportal_commons.lib.validators import url +from c2cgeoportal_commons.models.main import Metadata from deform.widget import MappingWidget, SelectWidget, SequenceWidget, TextAreaWidget from sqlalchemy import inspect from sqlalchemy.orm.attributes import InstrumentedAttribute from sqlalchemy.orm.mapper import Mapper from c2cgeoportal_admin import _ -from c2cgeoportal_commons.lib.validators import url -from c2cgeoportal_commons.models.main import Metadata def get_relevant_for(model: type[Any] | Mapper[Any]) -> set[str]: @@ -202,7 +202,6 @@ def _translate_available_metadata( def metadata_schema_node(prop: InstrumentedAttribute[Any], model: type[Any]) -> colander.SequenceSchema: """Get the schema of a collection of metadata.""" - # Deferred which returns a dictionary with metadata name as key and metadata definition as value. # Needed to get the metadata types on UI side. metadata_definitions_dict = colander.deferred( diff --git a/admin/c2cgeoportal_admin/schemas/restriction_areas.py b/admin/c2cgeoportal_admin/schemas/restriction_areas.py index d13e70b3de..cd22bd6ea2 100644 --- a/admin/c2cgeoportal_admin/schemas/restriction_areas.py +++ b/admin/c2cgeoportal_admin/schemas/restriction_areas.py @@ -31,9 +31,8 @@ import colander from c2cgeoform.ext.deform_ext import RelationCheckBoxListWidget from c2cgeoform.schema import GeoFormManyToManySchemaNode, manytomany_validator -from sqlalchemy.orm.attributes import InstrumentedAttribute - from c2cgeoportal_commons.models.main import RestrictionArea +from sqlalchemy.orm.attributes import InstrumentedAttribute def restrictionareas_schema_node( diff --git a/admin/c2cgeoportal_admin/schemas/roles.py b/admin/c2cgeoportal_admin/schemas/roles.py index 308a322b43..8ec1357ee3 100644 --- a/admin/c2cgeoportal_admin/schemas/roles.py +++ b/admin/c2cgeoportal_admin/schemas/roles.py @@ -31,9 +31,8 @@ import colander from c2cgeoform.ext.deform_ext import RelationCheckBoxListWidget from c2cgeoform.schema import GeoFormManyToManySchemaNode, manytomany_validator -from sqlalchemy.orm.attributes import InstrumentedAttribute - from c2cgeoportal_commons.models.main import Role +from sqlalchemy.orm.attributes import InstrumentedAttribute def roles_schema_node( diff --git a/admin/c2cgeoportal_admin/schemas/treegroup.py b/admin/c2cgeoportal_admin/schemas/treegroup.py index ea47cb501d..187fb37f29 100644 --- a/admin/c2cgeoportal_admin/schemas/treegroup.py +++ b/admin/c2cgeoportal_admin/schemas/treegroup.py @@ -34,13 +34,13 @@ import pyramid.request import sqlalchemy from c2cgeoform.schema import GeoFormSchemaNode +from c2cgeoportal_commons.lib.literal import Literal +from c2cgeoportal_commons.models.main import LayergroupTreeitem, TreeGroup, TreeItem from sqlalchemy.orm import aliased from sqlalchemy.sql.expression import case, func from c2cgeoportal_admin import _ from c2cgeoportal_admin.widgets import ChildrenWidget, ChildWidget -from c2cgeoportal_commons.lib.literal import Literal -from c2cgeoportal_commons.models.main import LayergroupTreeitem, TreeGroup, TreeItem _LOG = logging.getLogger(__name__) @@ -74,7 +74,8 @@ def treeitems( assert isinstance(dbsession, sqlalchemy.orm.Session) group = case( - (func.count(LayergroupTreeitem.id) == 0, "Unlinked"), else_="Others" # pylint: disable=not-callable + (func.count(LayergroupTreeitem.id) == 0, "Unlinked"), # pylint: disable=not-callable + else_="Others", ) query = ( @@ -123,7 +124,7 @@ def treeitems( def children_validator(node, cstruct): """Get the validator on the children nodes.""" for dict_ in cstruct: - if not dict_["treeitem_id"] in [item["id"] for item in node.candidates]: + if dict_["treeitem_id"] not in [item["id"] for item in node.candidates]: raise colander.Invalid( node, _("Value {} does not exist in table {} or is not allowed to avoid cycles").format( diff --git a/admin/c2cgeoportal_admin/views/__init__.py b/admin/c2cgeoportal_admin/views/__init__.py index 611546b28b..189e935184 100644 --- a/admin/c2cgeoportal_admin/views/__init__.py +++ b/admin/c2cgeoportal_admin/views/__init__.py @@ -4,9 +4,7 @@ class IsAdminPredicate: - """ - A custom predicate that checks if the request is for the admin interface. - """ + """A custom predicate that checks if the request is for the admin interface.""" def __init__(self, val, info): del info diff --git a/admin/c2cgeoportal_admin/views/dimension_layers.py b/admin/c2cgeoportal_admin/views/dimension_layers.py index 169627eb40..51209e8891 100644 --- a/admin/c2cgeoportal_admin/views/dimension_layers.py +++ b/admin/c2cgeoportal_admin/views/dimension_layers.py @@ -32,10 +32,10 @@ import sqlalchemy.orm.query from c2cgeoform.views.abstract_views import ListField +from c2cgeoportal_commons.models.main import DimensionLayer from sqlalchemy.orm import subqueryload from c2cgeoportal_admin.views.layers import LayerViews -from c2cgeoportal_commons.models.main import DimensionLayer _list_field = partial(ListField, DimensionLayer) diff --git a/admin/c2cgeoportal_admin/views/functionalities.py b/admin/c2cgeoportal_admin/views/functionalities.py index 71de35fcfc..ea4ab57a41 100644 --- a/admin/c2cgeoportal_admin/views/functionalities.py +++ b/admin/c2cgeoportal_admin/views/functionalities.py @@ -40,12 +40,12 @@ ObjectResponse, SaveResponse, ) +from c2cgeoportal_commons.models.main import Functionality from deform.widget import FormWidget from pyramid.view import view_config, view_defaults from c2cgeoportal_admin import _ from c2cgeoportal_admin.views.logged_views import LoggedViews -from c2cgeoportal_commons.models.main import Functionality _list_field = partial(ListField, Functionality) @@ -103,7 +103,9 @@ def delete(self) -> DeleteResponse: return super().delete() @view_config( - route_name="c2cgeoform_item_duplicate", request_method="GET", renderer="../templates/edit.jinja2" # type: ignore[misc] + route_name="c2cgeoform_item_duplicate", + request_method="GET", + renderer="../templates/edit.jinja2", # type: ignore[misc] ) def duplicate(self) -> ObjectResponse: return super().duplicate() diff --git a/admin/c2cgeoportal_admin/views/interfaces.py b/admin/c2cgeoportal_admin/views/interfaces.py index 806534cc6a..2a8b6406ae 100644 --- a/admin/c2cgeoportal_admin/views/interfaces.py +++ b/admin/c2cgeoportal_admin/views/interfaces.py @@ -37,10 +37,10 @@ ObjectResponse, SaveResponse, ) +from c2cgeoportal_commons.models.main import Interface from pyramid.view import view_config, view_defaults from c2cgeoportal_admin.views.logged_views import LoggedViews -from c2cgeoportal_commons.models.main import Interface _list_field = partial(ListField, Interface) diff --git a/admin/c2cgeoportal_admin/views/layer_groups.py b/admin/c2cgeoportal_admin/views/layer_groups.py index fc5a61d93a..ae68c2f2f5 100644 --- a/admin/c2cgeoportal_admin/views/layer_groups.py +++ b/admin/c2cgeoportal_admin/views/layer_groups.py @@ -39,6 +39,7 @@ ObjectResponse, SaveResponse, ) +from c2cgeoportal_commons.models.main import LayerGroup, TreeGroup from deform.widget import FormWidget from pyramid.view import view_config, view_defaults @@ -46,7 +47,6 @@ from c2cgeoportal_admin.schemas.treegroup import children_schema_node from c2cgeoportal_admin.schemas.treeitem import parent_id_node from c2cgeoportal_admin.views.treeitems import TreeItemViews -from c2cgeoportal_commons.models.main import LayerGroup, TreeGroup _list_field = partial(ListField, LayerGroup) diff --git a/admin/c2cgeoportal_admin/views/layers.py b/admin/c2cgeoportal_admin/views/layers.py index 038472b15f..b2b47521bd 100644 --- a/admin/c2cgeoportal_admin/views/layers.py +++ b/admin/c2cgeoportal_admin/views/layers.py @@ -32,10 +32,10 @@ import sqlalchemy import sqlalchemy.orm.query from c2cgeoform.views.abstract_views import ListField +from c2cgeoportal_commons.models.main import Interface, Layer from sqlalchemy.orm import subqueryload from c2cgeoportal_admin.views.treeitems import TreeItemViews -from c2cgeoportal_commons.models.main import Interface, Layer _list_field = partial(ListField, Layer) diff --git a/admin/c2cgeoportal_admin/views/layers_cog.py b/admin/c2cgeoportal_admin/views/layers_cog.py index 7ba329eba9..f655304baf 100644 --- a/admin/c2cgeoportal_admin/views/layers_cog.py +++ b/admin/c2cgeoportal_admin/views/layers_cog.py @@ -40,6 +40,8 @@ ObjectResponse, SaveResponse, ) +from c2cgeoportal_commons.lib.literal import Literal +from c2cgeoportal_commons.models.main import LayerCOG, LayerGroup from deform.widget import FormWidget from pyramid.httpexceptions import HTTPNotFound from pyramid.view import view_config, view_defaults @@ -50,8 +52,6 @@ from c2cgeoportal_admin.schemas.restriction_areas import restrictionareas_schema_node from c2cgeoportal_admin.schemas.treeitem import parent_id_node from c2cgeoportal_admin.views.layers import LayerViews -from c2cgeoportal_commons.lib.literal import Literal -from c2cgeoportal_commons.models.main import LayerCOG, LayerGroup _list_field = partial(ListField, LayerCOG) diff --git a/admin/c2cgeoportal_admin/views/layers_vectortiles.py b/admin/c2cgeoportal_admin/views/layers_vectortiles.py index c451da4128..6648c9ddf8 100644 --- a/admin/c2cgeoportal_admin/views/layers_vectortiles.py +++ b/admin/c2cgeoportal_admin/views/layers_vectortiles.py @@ -40,6 +40,8 @@ ObjectResponse, SaveResponse, ) +from c2cgeoportal_commons.lib.literal import Literal +from c2cgeoportal_commons.models.main import LayerGroup, LayerVectorTiles from deform.widget import FormWidget from pyramid.httpexceptions import HTTPNotFound from pyramid.view import view_config, view_defaults @@ -50,8 +52,6 @@ from c2cgeoportal_admin.schemas.restriction_areas import restrictionareas_schema_node from c2cgeoportal_admin.schemas.treeitem import parent_id_node from c2cgeoportal_admin.views.dimension_layers import DimensionLayerViews -from c2cgeoportal_commons.lib.literal import Literal -from c2cgeoportal_commons.models.main import LayerGroup, LayerVectorTiles _list_field = partial(ListField, LayerVectorTiles) diff --git a/admin/c2cgeoportal_admin/views/layers_wms.py b/admin/c2cgeoportal_admin/views/layers_wms.py index 8acd8bab2b..520fd2ffd8 100644 --- a/admin/c2cgeoportal_admin/views/layers_wms.py +++ b/admin/c2cgeoportal_admin/views/layers_wms.py @@ -40,6 +40,14 @@ ObjectResponse, SaveResponse, ) +from c2cgeoportal_commons.models.main import ( + LayerGroup, + LayerWMS, + LayerWMTS, + LogAction, + OGCServer, + TreeItem, +) from deform.widget import FormWidget from pyramid.view import view_config, view_defaults from sqlalchemy import delete, insert, inspect, update @@ -52,7 +60,6 @@ from c2cgeoportal_admin.schemas.restriction_areas import restrictionareas_schema_node from c2cgeoportal_admin.schemas.treeitem import parent_id_node from c2cgeoportal_admin.views.dimension_layers import DimensionLayerViews -from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, LayerWMTS, LogAction, OGCServer, TreeItem _list_field = partial(ListField, LayerWMS) diff --git a/admin/c2cgeoportal_admin/views/layers_wmts.py b/admin/c2cgeoportal_admin/views/layers_wmts.py index 1798d6317c..cdbf4416ba 100644 --- a/admin/c2cgeoportal_admin/views/layers_wmts.py +++ b/admin/c2cgeoportal_admin/views/layers_wmts.py @@ -40,6 +40,14 @@ ObjectResponse, SaveResponse, ) +from c2cgeoportal_commons.models.main import ( + LayerGroup, + LayerWMS, + LayerWMTS, + LogAction, + OGCServer, + TreeItem, +) from deform.widget import FormWidget from pyramid.view import view_config, view_defaults from sqlalchemy import delete, insert, inspect, update @@ -52,7 +60,6 @@ from c2cgeoportal_admin.schemas.restriction_areas import restrictionareas_schema_node from c2cgeoportal_admin.schemas.treeitem import parent_id_node from c2cgeoportal_admin.views.dimension_layers import DimensionLayerViews -from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, LayerWMTS, LogAction, OGCServer, TreeItem _list_field = partial(ListField, LayerWMTS) diff --git a/admin/c2cgeoportal_admin/views/layertree.py b/admin/c2cgeoportal_admin/views/layertree.py index c9a1b5ebab..e229418c34 100644 --- a/admin/c2cgeoportal_admin/views/layertree.py +++ b/admin/c2cgeoportal_admin/views/layertree.py @@ -30,12 +30,18 @@ import pyramid.request from c2cgeoform.views.abstract_views import DeleteResponse, ItemAction +from c2cgeoportal_commons.models.main import ( + Interface, + Layer, + LayergroupTreeitem, + Theme, + TreeItem, +) from pyramid.httpexceptions import HTTPNotFound from pyramid.view import view_config, view_defaults from translationstring import TranslationStringFactory from c2cgeoportal_admin import _ -from c2cgeoportal_commons.models.main import Interface, Layer, LayergroupTreeitem, Theme, TreeItem itemtypes_tables = { "theme": "themes", diff --git a/admin/c2cgeoportal_admin/views/logged_views.py b/admin/c2cgeoportal_admin/views/logged_views.py index 85f287ecbe..da1464c16f 100644 --- a/admin/c2cgeoportal_admin/views/logged_views.py +++ b/admin/c2cgeoportal_admin/views/logged_views.py @@ -29,10 +29,9 @@ from typing import Generic, TypeVar from c2cgeoform.views.abstract_views import AbstractViews, DeleteResponse, SaveResponse -from pyramid.httpexceptions import HTTPFound - from c2cgeoportal_commons.models import Base from c2cgeoportal_commons.models.main import Log, LogAction +from pyramid.httpexceptions import HTTPFound _T = TypeVar("_T", bound=Log) diff --git a/admin/c2cgeoportal_admin/views/logs.py b/admin/c2cgeoportal_admin/views/logs.py index 922df76cb0..0e20869e1c 100644 --- a/admin/c2cgeoportal_admin/views/logs.py +++ b/admin/c2cgeoportal_admin/views/logs.py @@ -28,11 +28,16 @@ from functools import partial from c2cgeoform import JSONDict -from c2cgeoform.views.abstract_views import AbstractViews, GridResponse, IndexResponse, ItemAction, ListField -from pyramid.view import view_config, view_defaults - +from c2cgeoform.views.abstract_views import ( + AbstractViews, + GridResponse, + IndexResponse, + ItemAction, + ListField, +) from c2cgeoportal_commons.models import _ from c2cgeoportal_commons.models.main import AbstractLog +from pyramid.view import view_config, view_defaults _list_field = partial(ListField, AbstractLog) diff --git a/admin/c2cgeoportal_admin/views/oauth2_clients.py b/admin/c2cgeoportal_admin/views/oauth2_clients.py index e30d5618a5..44297b2b49 100644 --- a/admin/c2cgeoportal_admin/views/oauth2_clients.py +++ b/admin/c2cgeoportal_admin/views/oauth2_clients.py @@ -37,10 +37,10 @@ ObjectResponse, SaveResponse, ) +from c2cgeoportal_commons.models.static import Log, OAuth2Client from pyramid.view import view_config, view_defaults from c2cgeoportal_admin.views.logged_views import LoggedViews -from c2cgeoportal_commons.models.static import Log, OAuth2Client _list_field = partial(ListField, OAuth2Client) diff --git a/admin/c2cgeoportal_admin/views/ogc_servers.py b/admin/c2cgeoportal_admin/views/ogc_servers.py index 92e97c6b44..a1599cf0ed 100644 --- a/admin/c2cgeoportal_admin/views/ogc_servers.py +++ b/admin/c2cgeoportal_admin/views/ogc_servers.py @@ -45,6 +45,9 @@ SaveResponse, UserMessage, ) +from c2cgeoportal_commons.lib.literal import Literal +from c2cgeoportal_commons.models import cache_invalidate_cb +from c2cgeoportal_commons.models.main import LogAction, OGCServer from deform.widget import FormWidget from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config, view_defaults @@ -53,9 +56,6 @@ from c2cgeoportal_admin import _ from c2cgeoportal_admin.lib.ogcserver_synchronizer import OGCServerSynchronizer from c2cgeoportal_admin.views.logged_views import LoggedViews -from c2cgeoportal_commons.lib.literal import Literal -from c2cgeoportal_commons.models import cache_invalidate_cb -from c2cgeoportal_commons.models.main import LogAction, OGCServer _list_field = partial(ListField, OGCServer) diff --git a/admin/c2cgeoportal_admin/views/restriction_areas.py b/admin/c2cgeoportal_admin/views/restriction_areas.py index 17b63d499f..3edcb63f3f 100644 --- a/admin/c2cgeoportal_admin/views/restriction_areas.py +++ b/admin/c2cgeoportal_admin/views/restriction_areas.py @@ -39,6 +39,7 @@ ObjectResponse, SaveResponse, ) +from c2cgeoportal_commons.models.main import Layer, RestrictionArea from deform.widget import FormWidget from pyramid.view import view_config, view_defaults from sqlalchemy.orm import subqueryload @@ -47,7 +48,6 @@ from c2cgeoportal_admin.schemas.treegroup import treeitem_edit_url from c2cgeoportal_admin.views.logged_views import LoggedViews from c2cgeoportal_admin.widgets import ChildrenWidget, ChildWidget -from c2cgeoportal_commons.models.main import Layer, RestrictionArea _list_field = partial(ListField, RestrictionArea) diff --git a/admin/c2cgeoportal_admin/views/roles.py b/admin/c2cgeoportal_admin/views/roles.py index ec8ee1706d..f26e1572d5 100644 --- a/admin/c2cgeoportal_admin/views/roles.py +++ b/admin/c2cgeoportal_admin/views/roles.py @@ -39,6 +39,8 @@ ObjectResponse, SaveResponse, ) +from c2cgeoportal_commons.models.main import Role +from c2cgeoportal_commons.models.static import User from deform.widget import FormWidget from pyramid.view import view_config, view_defaults from sqlalchemy.orm import subqueryload @@ -47,8 +49,6 @@ from c2cgeoportal_admin.schemas.restriction_areas import restrictionareas_schema_node from c2cgeoportal_admin.views.logged_views import LoggedViews from c2cgeoportal_admin.widgets import ChildrenWidget, ChildWidget -from c2cgeoportal_commons.models.main import Role -from c2cgeoportal_commons.models.static import User _list_field = partial(ListField, Role) diff --git a/admin/c2cgeoportal_admin/views/themes.py b/admin/c2cgeoportal_admin/views/themes.py index 15fc45980d..7914988884 100644 --- a/admin/c2cgeoportal_admin/views/themes.py +++ b/admin/c2cgeoportal_admin/views/themes.py @@ -39,6 +39,7 @@ ObjectResponse, SaveResponse, ) +from c2cgeoportal_commons.models.main import Functionality, Interface, Role, Theme from deform.widget import FormWidget from pyramid.view import view_config, view_defaults from sqlalchemy.orm import subqueryload @@ -50,7 +51,6 @@ from c2cgeoportal_admin.schemas.roles import roles_schema_node from c2cgeoportal_admin.schemas.treegroup import children_schema_node from c2cgeoportal_admin.views.treeitems import TreeItemViews -from c2cgeoportal_commons.models.main import Functionality, Interface, Role, Theme _list_field = partial(ListField, Theme) diff --git a/admin/c2cgeoportal_admin/views/themes_ordering.py b/admin/c2cgeoportal_admin/views/themes_ordering.py index c9faa7175a..80f4615039 100644 --- a/admin/c2cgeoportal_admin/views/themes_ordering.py +++ b/admin/c2cgeoportal_admin/views/themes_ordering.py @@ -29,6 +29,7 @@ import colander from c2cgeoform.schema import GeoFormSchemaNode from c2cgeoform.views.abstract_views import AbstractViews, ObjectResponse, SaveResponse +from c2cgeoportal_commons.models.main import Theme, TreeItem from deform import ValidationFailure from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config @@ -36,7 +37,6 @@ from c2cgeoportal_admin import _ from c2cgeoportal_admin.schemas.treegroup import treeitem_edit_url from c2cgeoportal_admin.widgets import ChildrenWidget, ChildWidget -from c2cgeoportal_commons.models.main import Theme, TreeItem class ThemeOrderSchema(GeoFormSchemaNode): # pylint: disable=abstract-method @@ -61,7 +61,7 @@ def themes(node, kw): # pylint: disable=unused-argument def themes_validator(node, cstruct): """Validate the theme.""" for dict_ in cstruct: - if not dict_["id"] in [item["id"] for item in node.candidates]: + if dict_["id"] not in [item["id"] for item in node.candidates]: raise colander.Invalid( node, _("Value {} does not exist in table {}").format(dict_["id"], Theme.__tablename__), @@ -126,7 +126,7 @@ def save(self) -> SaveResponse: self._request.dbsession.flush() return HTTPFound(self._request.route_url("layertree")) except ValidationFailure as e: - # FIXME see https://github.com/Pylons/deform/pull/243 + # FIXME see https://github.com/Pylons/deform/pull/243 # pylint: disable=fixme self._populate_widgets(form.schema) return { "title": form.title, diff --git a/admin/c2cgeoportal_admin/views/treeitems.py b/admin/c2cgeoportal_admin/views/treeitems.py index f762d9f22b..0c3d2a5196 100644 --- a/admin/c2cgeoportal_admin/views/treeitems.py +++ b/admin/c2cgeoportal_admin/views/treeitems.py @@ -31,12 +31,17 @@ import sqlalchemy from c2cgeoform.views.abstract_views import ListField, SaveResponse +from c2cgeoportal_commons.models.main import ( + LayergroupTreeitem, + Metadata, + TreeGroup, + TreeItem, +) from pyramid.view import view_config from sqlalchemy.orm import subqueryload from sqlalchemy.sql.functions import concat from c2cgeoportal_admin.views.logged_views import LoggedViews -from c2cgeoportal_commons.models.main import LayergroupTreeitem, Metadata, TreeGroup, TreeItem _list_field = partial(ListField, TreeItem) diff --git a/admin/c2cgeoportal_admin/views/users.py b/admin/c2cgeoportal_admin/views/users.py index 769de9aa64..f42c4943a9 100644 --- a/admin/c2cgeoportal_admin/views/users.py +++ b/admin/c2cgeoportal_admin/views/users.py @@ -38,6 +38,9 @@ ObjectResponse, SaveResponse, ) +from c2cgeoportal_commons.lib.email_ import send_email_config +from c2cgeoportal_commons.models.main import Role +from c2cgeoportal_commons.models.static import Log, User from deform.widget import FormWidget from passwordgenerator import pwgenerator from pyramid.httpexceptions import HTTPFound @@ -46,9 +49,6 @@ from c2cgeoportal_admin.schemas.roles import roles_schema_node from c2cgeoportal_admin.views.logged_views import LoggedViews -from c2cgeoportal_commons.lib.email_ import send_email_config -from c2cgeoportal_commons.models.main import Role -from c2cgeoportal_commons.models.static import Log, User _list_field = partial(ListField, User) diff --git a/admin/c2cgeoportal_admin/widgets.py b/admin/c2cgeoportal_admin/widgets.py index bce6599c96..57c8e1a07a 100644 --- a/admin/c2cgeoportal_admin/widgets.py +++ b/admin/c2cgeoportal_admin/widgets.py @@ -29,12 +29,11 @@ import colander import pyramid.request +from c2cgeoportal_commons.models.main import TreeItem from colander import Mapping, SchemaNode from deform import widget from deform.widget import MappingWidget, SequenceWidget -from c2cgeoportal_commons.models.main import TreeItem - registry = widget.default_resource_registry registry.set_js_resources( "magicsuggest", None, "c2cgeoportal_admin:node_modules/magicsuggest-alpine/magicsuggest-min.js" diff --git a/admin/tests/__init__.py b/admin/tests/__init__.py index 2fdfcf47c8..efcfac140e 100644 --- a/admin/tests/__init__.py +++ b/admin/tests/__init__.py @@ -9,7 +9,12 @@ def get_test_default_layers(dbsession, default_ogc_server): - from c2cgeoportal_commons.models.main import LayerCOG, LayerVectorTiles, LayerWMS, LayerWMTS + from c2cgeoportal_commons.models.main import ( + LayerCOG, + LayerVectorTiles, + LayerWMS, + LayerWMTS, + ) default_wms = None if default_ogc_server: @@ -122,9 +127,9 @@ def check_grid_headers(self, resp, expected_col_headers, new="New"): "\n\n{}\n\n differs from \n\n{}", pp.pformat(expected_col_headers), pp.pformat(effective_cols) ) actions = resp.html.select_one('th[data-field="actions"]') - assert "false" == actions.attrs["data-sortable"] + assert actions.attrs["data-sortable"] == "false" if new is not False: - assert 1 == len(list(filter(lambda x: next(x.stripped_strings) == new, resp.html.findAll("a")))) + assert len(list(filter(lambda x: next(x.stripped_strings) == new, resp.html.findAll("a")))) == 1 def check_search(self, test_app, search="", offset=0, limit=10, sort="", order="", total=None): json = test_app.post( @@ -184,7 +189,7 @@ def set_first_field_named(self, form, name, value): def _check_sequence(self, sequence, expected): seq_items = sequence.select(".deform-seq-item") assert len(expected) == len(seq_items) - for seq_item, exp in zip(seq_items, expected): + for seq_item, exp in zip(seq_items, expected, strict=False): self._check_mapping(seq_item, exp) def _check_mapping(self, mapping_item, expected): @@ -203,13 +208,13 @@ def _check_mapping(self, mapping_item, expected): else: assert (exp["value"] or "") == input_tag.attrs.get("value", "") if exp.get("hidden", False): - assert "hidden" == input_tag["type"] + assert input_tag["type"] == "hidden" if "label" in exp: label_tag = mapping_item.select_one('label[for="{}"]'.format(input_tag["id"])) assert exp["label"] == label_tag.getText().strip() def _check_select(self, select, expected): - for exp, option in zip(expected, select.find_all("option")): + for exp, option in zip(expected, select.find_all("option"), strict=False): if "text" in exp: assert exp["text"] == option.text if "value" in exp: @@ -219,11 +224,11 @@ def _check_select(self, select, expected): def _check_submission_problem(self, resp, expected_msg): assert ( - "There was a problem with your submission" - == resp.html.select_one('div[class="error-msg-lbl"]').text + resp.html.select_one('div[class="error-msg-lbl"]').text + == "There was a problem with your submission" ) assert ( - "Errors have been highlighted below" == resp.html.select_one('div[class="error-msg-detail"]').text + resp.html.select_one('div[class="error-msg-detail"]').text == "Errors have been highlighted below" ) assert ( expected_msg diff --git a/admin/tests/conftest.py b/admin/tests/conftest.py index dc32b465aa..e35b3a5679 100644 --- a/admin/tests/conftest.py +++ b/admin/tests/conftest.py @@ -4,6 +4,13 @@ import pytest import sqlalchemy.exc import transaction +from c2cgeoportal_commons.testing import ( + generate_mappers, + get_engine, + get_session_factory, + get_tm_session, +) +from c2cgeoportal_commons.testing.initializedb import truncate_tables from pyramid import testing from pyramid.paster import bootstrap from pyramid.router import Router @@ -12,9 +19,6 @@ from sqlalchemy.orm import Session, SessionTransaction from webtest import TestApp as WebTestApp # Avoid warning with pytest -from c2cgeoportal_commons.testing import generate_mappers, get_engine, get_session_factory, get_tm_session -from c2cgeoportal_commons.testing.initializedb import truncate_tables - @pytest.fixture(scope="session") @pytest.mark.usefixtures("settings") diff --git a/admin/tests/lib/test_ogcserver_synchronizer.py b/admin/tests/lib/test_ogcserver_synchronizer.py index c193facbb5..c5278d4031 100644 --- a/admin/tests/lib/test_ogcserver_synchronizer.py +++ b/admin/tests/lib/test_ogcserver_synchronizer.py @@ -29,18 +29,16 @@ def wms_capabilities(content=DEFAULT_CONTENT): - return """ + return f""" OGC:WMS - {} + {content} -""".format( - content - ) +""" @pytest.fixture(scope="function") @@ -443,9 +441,7 @@ def test_synchronize_clean(self, cap_mock, web_request, dbsession): ) def test_get_layer_wms_defaut(self, web_request, dbsession): - """ - We should copy properties from default LayerWMS. - """ + """We should copy properties from default LayerWMS.""" from c2cgeoportal_commons.models import main synchronizer = self.synchronizer(web_request) @@ -490,9 +486,7 @@ def test_get_layer_wms_defaut(self, web_request, dbsession): assert layer.style == "default_style" def test_get_layer_wms_defaut_style_not_exists(self, web_request, dbsession): - """ - We should not copy style from default LayerWMS if does not exist in capabilities. - """ + """We should not copy style from default LayerWMS if does not exist in capabilities.""" from c2cgeoportal_commons.models import main synchronizer = self.synchronizer(web_request) diff --git a/admin/tests/schemas/test_metadata.py b/admin/tests/schemas/test_metadata.py index 3931872747..8e05e4ac65 100644 --- a/admin/tests/schemas/test_metadata.py +++ b/admin/tests/schemas/test_metadata.py @@ -11,7 +11,9 @@ def test_get_relevant_for(): def test_metadata_definitions(): - from c2cgeoportal_admin.schemas.metadata import get_relevant_for, metadata_definitions + from c2cgeoportal_admin.schemas.metadata import ( + metadata_definitions, + ) from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS settings = { diff --git a/admin/tests/test_edit_url.py b/admin/tests/test_edit_url.py index 24e20f0c2b..bf8f96af60 100644 --- a/admin/tests/test_edit_url.py +++ b/admin/tests/test_edit_url.py @@ -45,7 +45,7 @@ def edit_url_test_data(dbsession, transact): layer_wmts.restrictionareas = [restrictionareas[i % 5], restrictionareas[(i + 2) % 5]] if i % 10 != 1: layer_wmts.interfaces = [interfaces[i % 4], interfaces[(i + 2) % 4]] - layer_wmts.public = 1 == i % 2 + layer_wmts.public = i % 2 == 1 layer_wmts.image_type = "image/jpeg" dbsession.add(layer_wmts) layers_wmts.append(layer_wmts) diff --git a/admin/tests/test_functionalities.py b/admin/tests/test_functionalities.py index 5331d3dcad..a907b6a66a 100644 --- a/admin/tests/test_functionalities.py +++ b/admin/tests/test_functionalities.py @@ -66,7 +66,7 @@ def test_submit_new(self, dbsession, test_app): assert functionality.name == "new_name" log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.INSERT assert log.element_type == "functionality" assert log.element_id == functionality.id @@ -80,14 +80,14 @@ def test_edit(self, test_app, functionality_test_data, dbsession): resp = test_app.get(f"/admin/functionalities/{functionality.id}", status=200) form = resp.form assert str(functionality.id) == self.get_first_field_named(form, "id").value - assert "hidden" == self.get_first_field_named(form, "id").attrs["type"] + assert self.get_first_field_named(form, "id").attrs["type"] == "hidden" assert functionality.name == form["name"].value form["description"] = "new_description" assert form.submit().status_int == 302 assert functionality.description == "new_description" log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "functionality" assert log.element_id == functionality.id @@ -103,7 +103,7 @@ def test_delete(self, test_app, functionality_test_data, dbsession): assert dbsession.query(Functionality).get(deleted_id) is None log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "functionality" assert log.element_id == functionality.id diff --git a/admin/tests/test_home.py b/admin/tests/test_home.py index 5c1a8cb0d4..1445238436 100644 --- a/admin/tests/test_home.py +++ b/admin/tests/test_home.py @@ -9,6 +9,6 @@ class TestHome(AbstractViewsTests): def test_index_rendering(self, test_app): resp = self.get(test_app, status=302) - assert "http://localhost/admin/layertree" == resp.location + assert resp.location == "http://localhost/admin/layertree" to_layer_tree = resp.follow() assert to_layer_tree.html.select_one('div[id="layertree"]') is not None diff --git a/admin/tests/test_interface.py b/admin/tests/test_interface.py index 0b9dab763f..8a407fc9f3 100644 --- a/admin/tests/test_interface.py +++ b/admin/tests/test_interface.py @@ -24,7 +24,7 @@ def interface_test_data(dbsession, transact): layers = [] for i in range(0, 15): layer = LayerWMS(name=f"layer_wms_{i}") - layer.public = 1 == i % 2 + layer.public = i % 2 == 1 layer.ogc_server = servers[i % 4] dbsession.add(layer) layers.append(layer) @@ -94,7 +94,7 @@ def test_submit_new(self, dbsession, test_app): assert interface.name == "new_name" log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.INSERT assert log.element_type == "interface" assert log.element_id == interface.id @@ -112,13 +112,13 @@ def test_edit(self, test_app, interface_test_data, dbsession): form = resp.form form["description"] = descriptions assert str(interface.id) == self.get_first_field_named(form, "id").value - assert "hidden" == self.get_first_field_named(form, "id").attrs["type"] + assert self.get_first_field_named(form, "id").attrs["type"] == "hidden" assert interface.name == self.get_first_field_named(form, "name").value assert form.submit().status_int == 302 assert len(dbsession.query(Interface).filter(Interface.description == descriptions).all()) == 1 log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "interface" assert log.element_id == interface.id @@ -133,7 +133,7 @@ def test_delete(self, test_app, interface_test_data, dbsession): assert len(dbsession.query(Interface).filter(Interface.id == interface.id).all()) == 0 log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "interface" assert log.element_id == interface.id @@ -144,6 +144,6 @@ def test_duplicate(self, interface_test_data, test_app): interface = interface_test_data["interfaces"][3] resp = test_app.get(f"/admin/interfaces/{interface.id}/duplicate", status=200) form = resp.form - assert "" == self.get_first_field_named(form, "id").value + assert self.get_first_field_named(form, "id").value == "" assert str(interface.description or "") == "description_3" assert form.submit().status_int == 302 diff --git a/admin/tests/test_layer_groups.py b/admin/tests/test_layer_groups.py index 804c6189c5..82c9d66559 100644 --- a/admin/tests/test_layer_groups.py +++ b/admin/tests/test_layer_groups.py @@ -12,7 +12,11 @@ def layer_groups_test_data(dbsession, transact): del transact - from c2cgeoportal_commons.models.main import LayerGroup, LayergroupTreeitem, Metadata + from c2cgeoportal_commons.models.main import ( + LayerGroup, + LayergroupTreeitem, + Metadata, + ) metadatas_protos = [ ("copyable", "true"), @@ -47,7 +51,7 @@ def flatten_tree(key, value): for val in value: add_relation(key, val) else: - for val in value.keys(): + for val in value: add_relation(key, val) flatten_tree(val, value[val]) @@ -85,8 +89,8 @@ def test_grid_complex_column_val(self, test_app, layer_groups_test_data): assert group.id == int(row["_id_"]) assert group.name == row["name"] - assert "groups_02, groups_06" == row["parents_relation"] - assert "disclaimer: © le momo, copyable: true" == row["metadatas"] + assert row["parents_relation"] == "groups_02, groups_06" + assert row["metadatas"] == "disclaimer: © le momo, copyable: true" def test_grid_search(self, test_app): # search on metadatas @@ -100,7 +104,7 @@ def test_edit(self, test_app, layer_groups_test_data, dbsession): form = self.get_item(test_app, group.id).form assert str(group.id) == self.get_first_field_named(form, "id").value - assert "hidden" == self.get_first_field_named(form, "id").attrs["type"] + assert self.get_first_field_named(form, "id").attrs["type"] == "hidden" assert group.name == self.get_first_field_named(form, "name").value assert str(group.description or "") == self.get_first_field_named(form, "description").value @@ -136,7 +140,7 @@ def test_edit(self, test_app, layer_groups_test_data, dbsession): assert str(value or "") == str(getattr(group, key) or "") log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "layergroup" assert log.element_id == group.id @@ -144,9 +148,7 @@ def test_edit(self, test_app, layer_groups_test_data, dbsession): assert log.username == "test_user" def test_post_new_with_children_invalid(self, test_app, layer_groups_test_data): - """ - Check there is no rendering error when validation fails. - """ + """Check there is no rendering error when validation fails.""" groups = layer_groups_test_data["groups"] resp = test_app.post( f"{self._prefix}/new", @@ -165,7 +167,7 @@ def test_post_new_with_children_invalid(self, test_app, layer_groups_test_data): ), status=200, ) - assert "Required" == resp.html.select_one(".item-name .help-block").getText().strip() + assert resp.html.select_one(".item-name .help-block").getText().strip() == "Required" def test_post_new_with_children_success(self, test_app, dbsession, layer_groups_test_data): from c2cgeoportal_commons.models.main import Log, LogAction @@ -214,7 +216,7 @@ def test_post_new_with_children_success(self, test_app, dbsession, layer_groups_ ] log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.INSERT assert log.element_type == "layergroup" assert log.element_id == group.id @@ -222,9 +224,7 @@ def test_post_new_with_children_success(self, test_app, dbsession, layer_groups_ assert log.username == "test_user" def test_post_with_ancestor(self, layer_groups_test_data, test_app): - """ - Check that ancestors are refused to avoid cycles. - """ + """Check that ancestors are refused to avoid cycles.""" groups = layer_groups_test_data["groups"] resp = test_app.post( f"{self._prefix}/{groups[3].id}", @@ -259,7 +259,7 @@ def test_duplicate(self, layer_groups_test_data, test_app, dbsession): group = dbsession.query(LayerGroup).filter(LayerGroup.id == group.id).one() - assert "" == self.get_first_field_named(form, "id").value + assert self.get_first_field_named(form, "id").value == "" assert group.name == self.get_first_field_named(form, "name").value assert str(group.description or "") == self.get_first_field_named(form, "description").value @@ -305,13 +305,13 @@ def test_delete(self, test_app, dbsession, layer_groups_test_data): group = layer_groups_test_data["groups"][9] assert ( - 3 - == dbsession.query(LayergroupTreeitem).filter(LayergroupTreeitem.treegroup_id == group.id).count() + dbsession.query(LayergroupTreeitem).filter(LayergroupTreeitem.treegroup_id == group.id).count() + == 3 ) assert ( - 1 - == dbsession.query(LayergroupTreeitem).filter(LayergroupTreeitem.treeitem_id == group.id).count() + dbsession.query(LayergroupTreeitem).filter(LayergroupTreeitem.treeitem_id == group.id).count() + == 1 ) test_app.delete(f"/admin/layer_groups/{group.id}", status=200) @@ -323,17 +323,17 @@ def test_delete(self, test_app, dbsession, layer_groups_test_data): assert dbsession.query(TreeItem).get(group.id) is None assert ( - 0 - == dbsession.query(LayergroupTreeitem).filter(LayergroupTreeitem.treegroup_id == group.id).count() + dbsession.query(LayergroupTreeitem).filter(LayergroupTreeitem.treegroup_id == group.id).count() + == 0 ) assert ( - 0 - == dbsession.query(LayergroupTreeitem).filter(LayergroupTreeitem.treeitem_id == group.id).count() + dbsession.query(LayergroupTreeitem).filter(LayergroupTreeitem.treeitem_id == group.id).count() + == 0 ) log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "layergroup" assert log.element_id == group.id diff --git a/admin/tests/test_layers_cog.py b/admin/tests/test_layers_cog.py index a89f013634..70d5cbaeb2 100644 --- a/admin/tests/test_layers_cog.py +++ b/admin/tests/test_layers_cog.py @@ -20,7 +20,7 @@ def layer_cog_test_data(dbsession: Session, transact: SessionTransaction) -> dic def layer_builder(i: int) -> LayerCOG: name = f"layer_cog_{i}" layer = LayerCOG(name=name) - layer.public = 1 == i % 2 + layer.public = i % 2 == 1 layer.url = "https://example.com/image.tiff" return layer @@ -73,9 +73,9 @@ def test_new(self, test_app: WebTestApp, layer_cog_test_data: dict[str, Any], db form = self.get_item(test_app, "new").form - assert "" == self.get_first_field_named(form, "name").value - assert "" == self.get_first_field_named(form, "id").value - assert "" == self.get_first_field_named(form, "url").value + assert self.get_first_field_named(form, "name").value == "" + assert self.get_first_field_named(form, "id").value == "" + assert self.get_first_field_named(form, "url").value == "" def test_grid_search(self, test_app: WebTestApp) -> None: self.check_search(test_app, "layer_cog_10", total=1) @@ -85,8 +85,8 @@ def test_base_edit(self, test_app: WebTestApp, layer_cog_test_data: dict[str, An form = self.get_item(test_app, layer.id).form - assert "layer_cog_10" == self.get_first_field_named(form, "name").value - assert "" == self.get_first_field_named(form, "description").value + assert self.get_first_field_named(form, "name").value == "layer_cog_10" + assert self.get_first_field_named(form, "description").value == "" def test_public_checkbox_edit(self, test_app: WebTestApp, layer_cog_test_data: dict[str, Any]) -> None: layer = layer_cog_test_data["layers"][10] @@ -107,7 +107,7 @@ def test_edit( form = self.get_item(test_app, layer.id).form assert str(layer.id) == self.get_first_field_named(form, "id").value - assert "hidden" == self.get_first_field_named(form, "id").attrs["type"] + assert self.get_first_field_named(form, "id").attrs["type"] == "hidden" assert layer.name == self.get_first_field_named(form, "name").value assert str(layer.description or "") == self.get_first_field_named(form, "description").value assert layer.public is False @@ -153,7 +153,7 @@ def test_edit( assert {ras[1].id, ras[3].id} == {ra.id for ra in layer.restrictionareas} log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "layer_cog" assert log.element_id == layer.id @@ -182,7 +182,7 @@ def test_submit_new( ).group(1) log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.INSERT assert log.element_type == "layer_cog" assert log.element_id == layer.id @@ -199,7 +199,7 @@ def test_duplicate( resp = test_app.get(f"/admin/layers_cog/{layer.id}/duplicate", status=200) form = resp.form - assert "" == self.get_first_field_named(form, "id").value + assert self.get_first_field_named(form, "id").value == "" assert layer.name == self.get_first_field_named(form, "name").value assert str(layer.description or "") == self.get_first_field_named(form, "description").value assert layer.public is True @@ -224,7 +224,13 @@ def test_duplicate( assert layer_cog_test_data["layers"][3].metadatas[1].name == layer.metadatas[1].name def test_delete(self, test_app: WebTestApp, dbsession: Session) -> None: - from c2cgeoportal_commons.models.main import Layer, LayerCOG, Log, LogAction, TreeItem + from c2cgeoportal_commons.models.main import ( + Layer, + LayerCOG, + Log, + LogAction, + TreeItem, + ) layer = dbsession.query(LayerCOG).first() @@ -235,7 +241,7 @@ def test_delete(self, test_app: WebTestApp, dbsession: Session) -> None: assert dbsession.query(TreeItem).get(layer.id) is None log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "layer_cog" assert log.element_id == layer.id diff --git a/admin/tests/test_layers_vectortiles.py b/admin/tests/test_layers_vectortiles.py index 6e1c757284..0e8280c893 100644 --- a/admin/tests/test_layers_vectortiles.py +++ b/admin/tests/test_layers_vectortiles.py @@ -18,7 +18,7 @@ def layer_builder(i): name = f"layer_vectortiles_{i}" layer = LayerVectorTiles(name=name) layer.layer = name - layer.public = 1 == i % 2 + layer.public = i % 2 == 1 layer.style = "https://vectortiles-staging.geoportail.lu/styles/roadmap/style.json" layer.xyz = "https://vectortiles-staging.geoportail.lu/styles/roadmap/{z}/{x}/{y}.png" return layer @@ -74,10 +74,10 @@ def test_new(self, test_app, layer_vectortiles_test_data, dbsession): form = self.get_item(test_app, "new").form - assert "" == self.get_first_field_named(form, "id").value - assert "" == self.get_first_field_named(form, "name").value - assert "" == self.get_first_field_named(form, "style").value - assert "" == self.get_first_field_named(form, "xyz").value + assert self.get_first_field_named(form, "id").value == "" + assert self.get_first_field_named(form, "name").value == "" + assert self.get_first_field_named(form, "style").value == "" + assert self.get_first_field_named(form, "xyz").value == "" def test_grid_search(self, test_app): self.check_search(test_app, "layer_vectortiles_10", total=1) @@ -87,8 +87,8 @@ def test_base_edit(self, test_app, layer_vectortiles_test_data): form = self.get_item(test_app, layer.id).form - assert "layer_vectortiles_10" == self.get_first_field_named(form, "name").value - assert "" == self.get_first_field_named(form, "description").value + assert self.get_first_field_named(form, "name").value == "layer_vectortiles_10" + assert self.get_first_field_named(form, "description").value == "" def test_public_checkbox_edit(self, test_app, layer_vectortiles_test_data): layer = layer_vectortiles_test_data["layers"][10] @@ -107,7 +107,7 @@ def test_edit(self, test_app, layer_vectortiles_test_data, dbsession): form = self.get_item(test_app, layer.id).form assert str(layer.id) == self.get_first_field_named(form, "id").value - assert "hidden" == self.get_first_field_named(form, "id").attrs["type"] + assert self.get_first_field_named(form, "id").attrs["type"] == "hidden" assert layer.name == self.get_first_field_named(form, "name").value assert str(layer.description or "") == self.get_first_field_named(form, "description").value assert layer.public is False @@ -155,7 +155,7 @@ def test_edit(self, test_app, layer_vectortiles_test_data, dbsession): assert {ras[1].id, ras[3].id} == {ra.id for ra in layer.restrictionareas} log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "layer_vectortiles" assert log.element_id == layer.id @@ -183,7 +183,7 @@ def test_submit_new(self, dbsession, test_app, layer_vectortiles_test_data): ).group(1) log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.INSERT assert log.element_type == "layer_vectortiles" assert log.element_id == layer.id @@ -198,7 +198,7 @@ def test_duplicate(self, layer_vectortiles_test_data, test_app, dbsession): resp = test_app.get(f"/admin/layers_vectortiles/{layer.id}/duplicate", status=200) form = resp.form - assert "" == self.get_first_field_named(form, "id").value + assert self.get_first_field_named(form, "id").value == "" assert layer.name == self.get_first_field_named(form, "name").value assert str(layer.description or "") == self.get_first_field_named(form, "description").value assert layer.public is True @@ -224,7 +224,13 @@ def test_duplicate(self, layer_vectortiles_test_data, test_app, dbsession): assert layer_vectortiles_test_data["layers"][3].metadatas[1].name == layer.metadatas[1].name def test_delete(self, test_app, dbsession): - from c2cgeoportal_commons.models.main import Layer, LayerVectorTiles, Log, LogAction, TreeItem + from c2cgeoportal_commons.models.main import ( + Layer, + LayerVectorTiles, + Log, + LogAction, + TreeItem, + ) layer = dbsession.query(LayerVectorTiles).first() @@ -235,7 +241,7 @@ def test_delete(self, test_app, dbsession): assert dbsession.query(TreeItem).get(layer.id) is None log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "layer_vectortiles" assert log.element_id == layer.id diff --git a/admin/tests/test_layers_wms.py b/admin/tests/test_layers_wms.py index d636574442..08dcaad703 100644 --- a/admin/tests/test_layers_wms.py +++ b/admin/tests/test_layers_wms.py @@ -22,7 +22,7 @@ def layer_wms_test_data(dbsession, transact): def layer_builder(i): layer = LayerWMS(name=f"layer_wms_{i}") layer.layer = f"layer_{i}" - layer.public = 1 == i % 2 + layer.public = i % 2 == 1 layer.geo_table = f"geotable_{i}" layer.ogc_server = servers[i % 4] layer.style = "décontrasté" @@ -77,12 +77,12 @@ def test_grid_complex_column_val(self, test_app, layer_wms_test_data): assert layer.id == int(row["_id_"]) assert layer.name == row["name"] - assert "restrictionarea_0, restrictionarea_2" == row["restrictionareas"] - assert "server_0" == row["ogc_server"] - assert "desktop, edit" == row["interfaces"] - assert "Date: 2017, 1988; CLC: all" == row["dimensions"] - assert "layer_group_0, layer_group_3" == row["parents_relation"] - assert 'copyable: true, snappingConfig: {"tolerance": 50}' == row["metadatas"] + assert row["restrictionareas"] == "restrictionarea_0, restrictionarea_2" + assert row["ogc_server"] == "server_0" + assert row["interfaces"] == "desktop, edit" + assert row["dimensions"] == "Date: 2017, 1988; CLC: all" + assert row["parents_relation"] == "layer_group_0, layer_group_3" + assert row["metadatas"] == 'copyable: true, snappingConfig: {"tolerance": 50}' def test_grid_sort_on_ogc_server(self, test_app, layer_wms_test_data): json = self.check_search(test_app, sort="ogc_server") @@ -116,21 +116,21 @@ def test_new_no_default(self, test_app, layer_wms_test_data, dbsession): form = self.get_item(test_app, "new").form - assert "" == self.get_first_field_named(form, "id").value - assert "" == self.get_first_field_named(form, "name").value - assert "" == self.get_first_field_named(form, "layer").value - assert "" == self.get_first_field_named(form, "ogc_server_id").value - assert "disabled" == self.get_first_field_named(form, "time_mode").value - assert "slider" == self.get_first_field_named(form, "time_widget").value + assert self.get_first_field_named(form, "id").value == "" + assert self.get_first_field_named(form, "name").value == "" + assert self.get_first_field_named(form, "layer").value == "" + assert self.get_first_field_named(form, "ogc_server_id").value == "" + assert self.get_first_field_named(form, "time_mode").value == "disabled" + assert self.get_first_field_named(form, "time_widget").value == "slider" def test_new_default(self, test_app, layer_wms_test_data): default_wms = layer_wms_test_data["default"]["wms"] form = self.get_item(test_app, "new").form - assert "" == self.get_first_field_named(form, "id").value - assert "" == self.get_first_field_named(form, "name").value - assert "" == self.get_first_field_named(form, "layer").value + assert self.get_first_field_named(form, "id").value == "" + assert self.get_first_field_named(form, "name").value == "" + assert self.get_first_field_named(form, "layer").value == "" assert str(default_wms.ogc_server.id) == self.get_first_field_named(form, "ogc_server_id").value assert default_wms.time_mode == self.get_first_field_named(form, "time_mode").value assert default_wms.time_widget == self.get_first_field_named(form, "time_widget").value @@ -140,8 +140,8 @@ def test_base_edit(self, test_app, layer_wms_test_data): form = self.get_item(test_app, layer.id).form - assert "layer_wms_10" == self.get_first_field_named(form, "name").value - assert "" == self.get_first_field_named(form, "description").value + assert self.get_first_field_named(form, "name").value == "layer_wms_10" + assert self.get_first_field_named(form, "description").value == "" def test_public_checkbox_edit(self, test_app, layer_wms_test_data): layer = layer_wms_test_data["layers"][10] @@ -160,7 +160,7 @@ def test_edit(self, test_app, layer_wms_test_data, dbsession): form = self.get_item(test_app, layer.id).form assert str(layer.id) == self.get_first_field_named(form, "id").value - assert "hidden" == self.get_first_field_named(form, "id").attrs["type"] + assert self.get_first_field_named(form, "id").attrs["type"] == "hidden" assert layer.name == self.get_first_field_named(form, "name").value assert str(layer.description or "") == self.get_first_field_named(form, "description").value assert layer.public is False @@ -214,7 +214,7 @@ def test_edit(self, test_app, layer_wms_test_data, dbsession): assert {ras[1].id, ras[3].id} == {ra.id for ra in layer.restrictionareas} log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "layer_wms" assert log.element_id == layer.id @@ -247,7 +247,7 @@ def test_submit_new(self, dbsession, test_app, layer_wms_test_data): ).group(1) log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.INSERT assert log.element_type == "layer_wms" assert log.element_id == layer.id @@ -262,7 +262,7 @@ def test_duplicate(self, layer_wms_test_data, test_app, dbsession): resp = test_app.get(f"/admin/layers_wms/{layer.id}/duplicate", status=200) form = resp.form - assert "" == self.get_first_field_named(form, "id").value + assert self.get_first_field_named(form, "id").value == "" assert layer.name == self.get_first_field_named(form, "name").value assert str(layer.description or "") == self.get_first_field_named(form, "description").value assert layer.public is True @@ -301,15 +301,15 @@ def test_convert_common_fields_copied(self, layer_wms_test_data, test_app, dbses layer = layer_wms_test_data["layers"][3] - assert 0 == dbsession.query(LayerWMTS).filter(LayerWMTS.name == layer.name).count() - assert 1 == dbsession.query(LayerWMS).filter(LayerWMS.name == layer.name).count() + assert dbsession.query(LayerWMTS).filter(LayerWMTS.name == layer.name).count() == 0 + assert dbsession.query(LayerWMS).filter(LayerWMS.name == layer.name).count() == 1 resp = test_app.post(f"/admin/layers_wms/{layer.id}/convert_to_wmts", status=200) assert resp.json["success"] assert f"http://localhost/admin/layers_wmts/{layer.id}?msg_col=submit_ok" == resp.json["redirect"] - assert 1 == dbsession.query(LayerWMTS).filter(LayerWMTS.name == layer.name).count() - assert 0 == dbsession.query(LayerWMS).filter(LayerWMS.name == layer.name).count() + assert dbsession.query(LayerWMTS).filter(LayerWMTS.name == layer.name).count() == 1 + assert dbsession.query(LayerWMS).filter(LayerWMS.name == layer.name).count() == 0 resp = test_app.get(resp.json["redirect"], status=200) form = resp.form @@ -333,8 +333,8 @@ def test_convert_common_fields_copied(self, layer_wms_test_data, test_app, dbses self._check_dimensions(resp.html, layer.dimensions) assert ( - "Your submission has been taken into account." - == resp.html.find("div", {"class": "msg-lbl"}).getText() + resp.html.find("div", {"class": "msg-lbl"}).getText() + == "Your submission has been taken into account." ) def test_convert_image_type_from_ogcserver(self, layer_wms_test_data, test_app): @@ -345,7 +345,7 @@ def test_convert_image_type_from_ogcserver(self, layer_wms_test_data, test_app): assert f"http://localhost/admin/layers_wmts/{layer.id}?msg_col=submit_ok" == resp.json["redirect"] resp = test_app.get(resp.json["redirect"], status=200) - assert "image/png" == resp.form["image_type"].value + assert resp.form["image_type"].value == "image/png" layer = layer_wms_test_data["layers"][2] resp = test_app.post(f"/admin/layers_wms/{layer.id}/convert_to_wmts", status=200) @@ -353,7 +353,7 @@ def test_convert_image_type_from_ogcserver(self, layer_wms_test_data, test_app): assert f"http://localhost/admin/layers_wmts/{layer.id}?msg_col=submit_ok" == resp.json["redirect"] resp = test_app.get(resp.json["redirect"], status=200) - assert "image/jpeg" == resp.form["image_type"].value + assert resp.form["image_type"].value == "image/jpeg" def test_convert_without_wmts_defaults(self, test_app, layer_wms_test_data, dbsession): from c2cgeoportal_commons.models.main import LayerWMTS, Log, LogAction @@ -363,7 +363,7 @@ def test_convert_without_wmts_defaults(self, test_app, layer_wms_test_data, dbse test_app.post(f"/admin/layers_wms/{layer.id}/convert_to_wmts", status=200) log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.CONVERT_TO_WMTS assert log.element_type == "layer_wms" assert log.element_id == layer.id @@ -381,7 +381,7 @@ def test_unicity_validator(self, layer_wms_test_data, test_app): def test_unicity_validator_does_not_matter_amongst_cousin(self, layer_wms_test_data, test_app, dbsession): from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS - assert 1 == dbsession.query(LayerGroup).filter(LayerGroup.name == "layer_group_0").count() + assert dbsession.query(LayerGroup).filter(LayerGroup.name == "layer_group_0").count() == 1 assert dbsession.query(LayerWMS).filter(LayerWMS.name == "layer_group_0").one_or_none() is None @@ -396,7 +396,13 @@ def test_unicity_validator_does_not_matter_amongst_cousin(self, layer_wms_test_d # assert str(layer.id) == re.match('http://localhost/admin/layers_wms/(.*)', resp.location).group(1) def test_delete(self, test_app, dbsession): - from c2cgeoportal_commons.models.main import Layer, LayerWMS, Log, LogAction, TreeItem + from c2cgeoportal_commons.models.main import ( + Layer, + LayerWMS, + Log, + LogAction, + TreeItem, + ) layer = dbsession.query(LayerWMS).first() @@ -407,7 +413,7 @@ def test_delete(self, test_app, dbsession): assert dbsession.query(TreeItem).get(layer.id) is None log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "layer_wms" assert log.element_id == layer.id @@ -432,12 +438,12 @@ def test_submit_new_no_layer_name(self, test_app, layer_wms_test_data, dbsession ) assert ( - "There was a problem with your submission" - == resp.html.select_one('div[class="error-msg-lbl"]').text + resp.html.select_one('div[class="error-msg-lbl"]').text + == "There was a problem with your submission" ) assert ( - "Errors have been highlighted below" == resp.html.select_one('div[class="error-msg-detail"]').text + resp.html.select_one('div[class="error-msg-detail"]').text == "Errors have been highlighted below" ) - assert ["WMS layer name"] == sorted( + assert sorted( (x.select_one("label").text.strip()) for x in resp.html.select("[class~='has-error']") - ) + ) == ["WMS layer name"] diff --git a/admin/tests/test_layers_wmts.py b/admin/tests/test_layers_wmts.py index 5eed5e5f99..c47a612af9 100644 --- a/admin/tests/test_layers_wmts.py +++ b/admin/tests/test_layers_wmts.py @@ -23,7 +23,7 @@ def layer_builder(i): layer = LayerWMTS(name=name) layer.layer = name layer.url = f"https:///wms.geo.admin.ch_{i}.org?service=wms&request=GetCapabilities" - layer.public = 1 == i % 2 + layer.public = i % 2 == 1 layer.geo_table = f"geotable_{i}" layer.image_type = "image/jpeg" layer.style = "décontrasté" @@ -83,20 +83,20 @@ def test_new_no_default(self, test_app, layer_wmts_test_data, dbsession): form = self.get_item(test_app, "new").form - assert "" == self.get_first_field_named(form, "id").value - assert "" == self.get_first_field_named(form, "name").value - assert "" == self.get_first_field_named(form, "layer").value - assert "" == self.get_first_field_named(form, "url").value - assert "" == self.get_first_field_named(form, "matrix_set").value + assert self.get_first_field_named(form, "id").value == "" + assert self.get_first_field_named(form, "name").value == "" + assert self.get_first_field_named(form, "layer").value == "" + assert self.get_first_field_named(form, "url").value == "" + assert self.get_first_field_named(form, "matrix_set").value == "" def test_new_default(self, test_app, layer_wmts_test_data): default_wmts = layer_wmts_test_data["default"]["wmts"] form = self.get_item(test_app, "new").form - assert "" == self.get_first_field_named(form, "id").value - assert "" == self.get_first_field_named(form, "name").value - assert "" == self.get_first_field_named(form, "layer").value + assert self.get_first_field_named(form, "id").value == "" + assert self.get_first_field_named(form, "name").value == "" + assert self.get_first_field_named(form, "layer").value == "" assert default_wmts.url == self.get_first_field_named(form, "url").value assert default_wmts.matrix_set == self.get_first_field_named(form, "matrix_set").value @@ -108,7 +108,7 @@ def test_edit(self, test_app, layer_wmts_test_data, dbsession): form = self.get_item(test_app, layer.id).form assert str(layer.id) == self.get_first_field_named(form, "id").value - assert "hidden" == self.get_first_field_named(form, "id").attrs["type"] + assert self.get_first_field_named(form, "id").attrs["type"] == "hidden" assert layer.name == self.get_first_field_named(form, "name").value assert str(layer.description or "") == self.get_first_field_named(form, "description").value assert layer.public is False @@ -162,7 +162,7 @@ def test_edit(self, test_app, layer_wmts_test_data, dbsession): assert {ras[1].id, ras[3].id} == {ra.id for ra in layer.restrictionareas} log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "layer_wmts" assert log.element_id == layer.id @@ -177,7 +177,7 @@ def test_duplicate(self, layer_wmts_test_data, test_app, dbsession): resp = test_app.get(f"/admin/layers_wmts/{layer.id}/duplicate", status=200) form = resp.form - assert "" == self.get_first_field_named(form, "id").value + assert self.get_first_field_named(form, "id").value == "" assert layer.name == self.get_first_field_named(form, "name").value assert str(layer.description or "") == self.get_first_field_named(form, "description").value assert layer.public is True @@ -199,7 +199,13 @@ def test_duplicate(self, layer_wmts_test_data, test_app, dbsession): ).group(1) def test_delete(self, test_app, dbsession): - from c2cgeoportal_commons.models.main import Layer, LayerWMTS, Log, LogAction, TreeItem + from c2cgeoportal_commons.models.main import ( + Layer, + LayerWMTS, + Log, + LogAction, + TreeItem, + ) layer = dbsession.query(LayerWMTS).first() @@ -210,7 +216,7 @@ def test_delete(self, test_app, dbsession): assert dbsession.query(TreeItem).get(layer.id) is None log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "layer_wmts" assert log.element_id == layer.id @@ -230,14 +236,14 @@ def test_convert_common_fields_copied(self, layer_wmts_test_data, test_app, dbse layer = layer_wmts_test_data["layers"][3] - assert 0 == dbsession.query(LayerWMS).filter(LayerWMS.name == layer.name).count() - assert 1 == dbsession.query(LayerWMTS).filter(LayerWMTS.name == layer.name).count() + assert dbsession.query(LayerWMS).filter(LayerWMS.name == layer.name).count() == 0 + assert dbsession.query(LayerWMTS).filter(LayerWMTS.name == layer.name).count() == 1 resp = test_app.post(f"/admin/layers_wmts/{layer.id}/convert_to_wms", status=200) assert f"http://localhost/admin/layers_wms/{layer.id}?msg_col=submit_ok" == resp.json["redirect"] - assert 1 == dbsession.query(LayerWMS).filter(LayerWMS.name == layer.name).count() - assert 0 == dbsession.query(LayerWMTS).filter(LayerWMTS.name == layer.name).count() + assert dbsession.query(LayerWMS).filter(LayerWMS.name == layer.name).count() == 1 + assert dbsession.query(LayerWMTS).filter(LayerWMTS.name == layer.name).count() == 0 resp = test_app.get(resp.json["redirect"], status=200) form = resp.form @@ -260,8 +266,8 @@ def test_convert_common_fields_copied(self, layer_wmts_test_data, test_app, dbse self._check_restrictionsareas(form, ras, layer) self._check_dimensions(resp.html, layer.dimensions) assert ( - "Your submission has been taken into account." - == resp.html.find("div", {"class": "msg-lbl"}).getText() + resp.html.find("div", {"class": "msg-lbl"}).getText() + == "Your submission has been taken into account." ) def test_convert_without_wms_defaults(self, test_app, layer_wmts_test_data, dbsession): @@ -272,7 +278,7 @@ def test_convert_without_wms_defaults(self, test_app, layer_wmts_test_data, dbse test_app.post(f"/admin/layers_wmts/{layer.id}/convert_to_wms", status=200) log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.CONVERT_TO_WMS assert log.element_type == "layer_wmts" assert log.element_id == layer.id diff --git a/admin/tests/test_layertree.py b/admin/tests/test_layertree.py index e135321470..857d5dbf84 100644 --- a/admin/tests/test_layertree.py +++ b/admin/tests/test_layertree.py @@ -140,7 +140,7 @@ def check_new_action(self, test_app, nodes, parent_id, action_name, label, route def test_themes(self, test_app, layertree_test_data): resp = self.get(test_app, "/children", status=200) nodes = resp.json - assert 5 == len(nodes) + assert len(nodes) == 5 # check themes are sorted by ordering expected = [ @@ -154,7 +154,7 @@ def test_themes(self, test_app, layertree_test_data): # no unlink on theme theme_node = next(n for n in nodes if n["id"] == theme.id) - assert 0 == len([a for a in theme_node["actions"] if a["name"] == "unlink"]) + assert len([a for a in theme_node["actions"] if a["name"] == "unlink"]) == 0 self.check_translation(nodes, theme) @@ -176,9 +176,9 @@ def test_groups(self, test_app, layertree_test_data, dbsession): theme.children_relation[1].ordering = 0 dbsession.flush() - resp = self.get(test_app, "/children?group_id={0}&path=_{0}".format(theme.id), status=200) + resp = self.get(test_app, f"/children?group_id={theme.id}&path=_{theme.id}", status=200) nodes = resp.json - assert 2 == len(nodes) + assert len(nodes) == 2 # check groups are sorted by ordering expected = [ @@ -227,9 +227,7 @@ def test_layers(self, test_app, layertree_test_data, dbsession): group.children_relation[1].ordering = 0 dbsession.flush() - resp = self.get( - test_app, "/children?group_id={0}&path=_{1}_{0}".format(group.id, theme.id), status=200 - ) + resp = self.get(test_app, f"/children?group_id={group.id}&path=_{theme.id}_{group.id}", status=200) nodes = resp.json assert len(nodes) == 2 diff --git a/admin/tests/test_lingva_extractor_config.py b/admin/tests/test_lingva_extractor_config.py index 89b9035d7a..7e7a16a638 100644 --- a/admin/tests/test_lingva_extractor_config.py +++ b/admin/tests/test_lingva_extractor_config.py @@ -29,10 +29,6 @@ from unittest.mock import Mock, mock_open, patch -import pytest -import yaml -from c2c.template.config import config as configuration - from c2cgeoportal_admin.lib.lingva_extractor import GeomapfishConfigExtractor GMF_CONFIG = """ diff --git a/admin/tests/test_logs.py b/admin/tests/test_logs.py index 48c4439b77..875098b9e1 100644 --- a/admin/tests/test_logs.py +++ b/admin/tests/test_logs.py @@ -13,7 +13,6 @@ def logs_test_data(dbsession, transact): del transact - from c2cgeoportal_commons.models.main import AbstractLog from c2cgeoportal_commons.models.main import Log as MainLog from c2cgeoportal_commons.models.main import LogAction from c2cgeoportal_commons.models.static import Log as StaticLog diff --git a/admin/tests/test_main.py b/admin/tests/test_main.py index fb9c0287d1..10bc80e8b9 100644 --- a/admin/tests/test_main.py +++ b/admin/tests/test_main.py @@ -5,9 +5,7 @@ @pytest.mark.usefixtures("app_env") def test_main(app_env): - """ - Test dev environment. - """ + """Test dev environment.""" config = testing.setUp(registry=app_env["registry"]) app = config.make_wsgi_app() testapp = WebTestApp(app) diff --git a/admin/tests/test_metadatas.py b/admin/tests/test_metadatas.py index cfd8600c86..8f9d7bc6e4 100644 --- a/admin/tests/test_metadatas.py +++ b/admin/tests/test_metadatas.py @@ -10,7 +10,14 @@ def metadatas_test_data(dbsession, transact): del transact - from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, LayerWMTS, Metadata, OGCServer, Theme + from c2cgeoportal_commons.models.main import ( + LayerGroup, + LayerWMS, + LayerWMTS, + Metadata, + OGCServer, + Theme, + ) ogc_server = OGCServer(name="ogc_server") @@ -333,9 +340,7 @@ def test_group_metadatas(self, metadatas_test_data, test_app): self._test_edit_treeitem("layer_groups", metadatas_test_data["group"], test_app, LayerGroup) def test_undefined_metadata(self, metadatas_test_data, test_app): - """ - Undefined metadata must be kept intact across submissions. - """ + """Undefined metadata must be kept intact across submissions.""" from c2cgeoportal_commons.models.main import Metadata layer = metadatas_test_data["layer_wms"] diff --git a/admin/tests/test_oauth2_clients.py b/admin/tests/test_oauth2_clients.py index b1febe74b4..6289f1d18e 100644 --- a/admin/tests/test_oauth2_clients.py +++ b/admin/tests/test_oauth2_clients.py @@ -18,7 +18,7 @@ def oauth2_clients_test_data(dbsession, transact): from c2cgeoportal_commons.models.static import OAuth2Client clients = [] - for i in range(23): + for _i in range(23): client = OAuth2Client() client.client_id = str(uuid4()) client.secret = "1234" @@ -86,7 +86,7 @@ def test_submit_new(self, dbsession, test_app, oauth2_clients_test_data): assert oauth2_client.redirect_uri == "http://127.0.0.1:7070/bis" log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.INSERT assert log.element_type == "oauth2_client" assert log.element_id == oauth2_client.id @@ -120,12 +120,12 @@ def test_edit_then_save(self, dbsession, test_app, oauth2_clients_test_data): dbsession.expire(oauth2_client) - assert "New client ID" == oauth2_client.client_id - assert "New secret" == oauth2_client.secret - assert "New redirect URI" == oauth2_client.redirect_uri + assert oauth2_client.client_id == "New client ID" + assert oauth2_client.secret == "New secret" + assert oauth2_client.redirect_uri == "New redirect URI" log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "oauth2_client" assert log.element_id == oauth2_client.id @@ -140,7 +140,7 @@ def test_duplicate(self, oauth2_clients_test_data, test_app, dbsession): resp = test_app.get(f"/admin/oauth2_clients/{oauth2_client_proto.id}/duplicate", status=200) form = resp.form - assert "" == self.get_first_field_named(form, "id").value + assert self.get_first_field_named(form, "id").value == "" assert oauth2_client_proto.client_id == form["client_id"].value assert oauth2_client_proto.secret == form["secret"].value assert oauth2_client_proto.redirect_uri == form["redirect_uri"].value @@ -162,7 +162,7 @@ def test_delete(self, test_app, dbsession): assert dbsession.query(OAuth2Client).get(oauth2_client.id) is None log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "oauth2_client" assert log.element_id == oauth2_client.id diff --git a/admin/tests/test_ogc_servers.py b/admin/tests/test_ogc_servers.py index c072a25707..16a0c7aec8 100644 --- a/admin/tests/test_ogc_servers.py +++ b/admin/tests/test_ogc_servers.py @@ -81,7 +81,7 @@ def test_submit_new(self, dbsession, test_app): assert ogc_server.name == "new_name" log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.INSERT assert log.element_type == "ogc_server" assert log.element_id == ogc_server.id @@ -95,7 +95,7 @@ def test_edit(self, test_app, ogc_server_test_data, dbsession): resp = test_app.get(f"/admin/ogc_servers/{ogc_server.id}", status=200) form = resp.form assert str(ogc_server.id) == self.get_first_field_named(form, "id").value - assert "hidden" == self.get_first_field_named(form, "id").attrs["type"] + assert self.get_first_field_named(form, "id").attrs["type"] == "hidden" assert ogc_server.name == form["name"].value form["description"] = "new_description" with patch("c2cgeoportal_admin.views.ogc_servers.OGCServerViews._update_cache"): @@ -103,7 +103,7 @@ def test_edit(self, test_app, ogc_server_test_data, dbsession): assert ogc_server.description == "new_description" log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "ogc_server" assert log.element_id == ogc_server.id @@ -118,7 +118,7 @@ def test_delete(self, test_app, ogc_server_test_data, dbsession): assert dbsession.query(OGCServer).get(ogc_server.id) is None log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "ogc_server" assert log.element_id == ogc_server.id @@ -131,7 +131,7 @@ def test_duplicate(self, ogc_server_test_data, test_app, dbsession): ogc_server = ogc_server_test_data["ogc_servers"][3] resp = test_app.get(f"/admin/ogc_servers/{ogc_server.id}/duplicate", status=200) form = resp.form - assert "" == self.get_first_field_named(form, "id").value + assert self.get_first_field_named(form, "id").value == "" self.set_first_field_named(form, "name", "clone") with patch("c2cgeoportal_admin.views.ogc_servers.OGCServerViews._update_cache"): resp = form.submit("submit") @@ -181,7 +181,7 @@ def test_synchronize_success(self, ogc_server_test_data, test_app, dbsession): resp = resp.forms["form-synchronize"].submit("synchronize") log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.SYNCHRONIZE assert log.element_type == "ogc_server" assert log.element_id == ogc_server.id diff --git a/admin/tests/test_restriction_areas.py b/admin/tests/test_restriction_areas.py index 71edeae5c5..be5e85832c 100644 --- a/admin/tests/test_restriction_areas.py +++ b/admin/tests/test_restriction_areas.py @@ -15,7 +15,12 @@ @pytest.mark.usefixtures("dbsession", "transact") def restriction_area_test_data(dbsession, transact): del transact - from c2cgeoportal_commons.models.main import LayerWMS, OGCServer, RestrictionArea, Role + from c2cgeoportal_commons.models.main import ( + LayerWMS, + OGCServer, + RestrictionArea, + Role, + ) roles = [] for i in range(0, 4): @@ -116,7 +121,7 @@ def test_submit_new(self, dbsession, test_app, restriction_area_test_data): assert set(restriction_area.layers) == {layers[0], layers[1]} log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.INSERT assert log.element_type == "restrictionarea" assert log.element_id == restriction_area.id @@ -143,7 +148,7 @@ def test_edit(self, test_app, restriction_area_test_data, dbsession): form = self.get_item(test_app, restriction_area.id).form assert str(restriction_area.id) == self.get_first_field_named(form, "id").value - assert "hidden" == self.get_first_field_named(form, "id").attrs["type"] + assert self.get_first_field_named(form, "id").attrs["type"] == "hidden" assert restriction_area.name == form["name"].value expected = Polygon( [ @@ -174,7 +179,7 @@ def test_edit(self, test_app, restriction_area_test_data, dbsession): assert set(restriction_area.roles) == {roles[i] for i in range(0, 3)} log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "restrictionarea" assert log.element_id == restriction_area.id @@ -190,7 +195,7 @@ def test_delete(self, test_app, restriction_area_test_data, dbsession): assert dbsession.query(RestrictionArea).get(deleted_id) is None log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "restrictionarea" assert log.element_id == restriction_area.id @@ -208,7 +213,7 @@ def test_duplicate(self, restriction_area_test_data, test_app, dbsession): form = test_app.get(f"/admin/restriction_areas/{restriction_area.id}/duplicate", status=200).form - assert "" == self.get_first_field_named(form, "id").value + assert self.get_first_field_named(form, "id").value == "" self._check_roles(form, roles, restriction_area) self.check_children( form, diff --git a/admin/tests/test_role.py b/admin/tests/test_role.py index ed0248e4f3..94b65e4057 100644 --- a/admin/tests/test_role.py +++ b/admin/tests/test_role.py @@ -155,7 +155,7 @@ def test_submit_new(self, dbsession, test_app, roles_test_data): assert set(role.users) == {users[0], users[1]} log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.INSERT assert log.element_type == "role" assert log.element_id == role.id @@ -172,7 +172,7 @@ def test_edit(self, dbsession, test_app, roles_test_data): form = self.get_item(test_app, role.id).form - assert "secretary_10" == form["name"].value + assert form["name"].value == "secretary_10" expected = Polygon( [ @@ -266,8 +266,8 @@ def test_edit(self, dbsession, test_app, roles_test_data): dbsession.expire(role) - assert "New name" == role.name - assert "New description" == role.description + assert role.name == "New name" + assert role.description == "New description" expected = Polygon( [ @@ -284,7 +284,7 @@ def test_edit(self, dbsession, test_app, roles_test_data): assert set(ra_ids) == {f.id for f in role.restrictionareas} log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "role" assert log.element_id == role.id @@ -299,7 +299,7 @@ def test_duplicate(self, roles_test_data, test_app, dbsession): resp = test_app.get(f"/admin/roles/{role_proto.id}/duplicate", status=200) form = resp.form - assert "" == self.get_first_field_named(form, "id").value + assert self.get_first_field_named(form, "id").value == "" assert role_proto.name == form["name"].value assert role_proto.description == form["description"].value form["name"].value = "clone" @@ -322,7 +322,7 @@ def test_delete(self, test_app, dbsession): assert dbsession.query(Role).get(role.id) is None log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "role" assert log.element_id == role.id diff --git a/admin/tests/test_themes.py b/admin/tests/test_themes.py index 20418f77a0..ee93610f45 100644 --- a/admin/tests/test_themes.py +++ b/admin/tests/test_themes.py @@ -54,7 +54,7 @@ def theme_test_data(dbsession, transact): themes = [] for i in range(0, 25): theme = Theme(name=f"theme_{i}", ordering=1, icon=f"icon_{i}") - theme.public = 1 == i % 2 + theme.public = i % 2 == 1 theme.interfaces = [interfaces[i % 4], interfaces[(i + 2) % 4]] theme.metadatas = [ Metadata(name=metadatas_protos[id][0], value=metadatas_protos[id][1]) @@ -121,10 +121,10 @@ def test_grid_complex_column_val(self, test_app, theme_test_data): assert first_theme.id == int(first_row["_id_"]) assert first_theme.name == first_row["name"] - assert "default_basemap=value_0" == first_row["functionalities"] - assert "secretary_0, secretary_2" == first_row["restricted_roles"] - assert "desktop, edit" == first_row["interfaces"] - assert 'copyable: true, snappingConfig: {"tolerance": 50}' == first_row["metadatas"] + assert first_row["functionalities"] == "default_basemap=value_0" + assert first_row["restricted_roles"] == "secretary_0, secretary_2" + assert first_row["interfaces"] == "desktop, edit" + assert first_row["metadatas"] == 'copyable: true, snappingConfig: {"tolerance": 50}' def test_grid_search(self, test_app): # search on metadatas key and value parts @@ -165,7 +165,7 @@ def test_edit(self, test_app, theme_test_data, dbsession): form = resp.form assert str(theme.id) == self.get_first_field_named(form, "id").value - assert "hidden" == self.get_first_field_named(form, "id").attrs["type"] + assert self.get_first_field_named(form, "id").attrs["type"] == "hidden" assert theme.name == self.get_first_field_named(form, "name").value assert str(theme.description or "") == self.get_first_field_named(form, "description").value assert str(theme.ordering or "") == self.get_first_field_named(form, "ordering").value @@ -231,10 +231,10 @@ def test_edit(self, test_app, theme_test_data, dbsession): assert str(value or "") == str(getattr(theme, key) or "") assert {interfaces[1].id, interfaces[3].id} == {interface.id for interface in theme.interfaces} assert {functionalities[2].id} == {functionality.id for functionality in theme.functionalities} - assert 0 == len(theme.restricted_roles) + assert len(theme.restricted_roles) == 0 log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "theme" assert log.element_id == theme.id @@ -242,9 +242,7 @@ def test_edit(self, test_app, theme_test_data, dbsession): assert log.username == "test_user" def test_post_new_with_children_invalid(self, test_app, theme_test_data): - """ - Check there is no rendering error when validation fails. - """ + """Check there is no rendering error when validation fails.""" groups = theme_test_data["groups"] resp = test_app.post( f"{self._prefix}/new", @@ -314,7 +312,7 @@ def test_post_new_with_children_success(self, test_app, dbsession, theme_test_da ] log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.INSERT assert log.element_type == "theme" assert log.element_id == theme.id @@ -322,9 +320,7 @@ def test_post_new_with_children_success(self, test_app, dbsession, theme_test_da assert log.username == "test_user" def test_post_new_with_child_layer(self, theme_test_data, test_app): - """ - Check layers are rejected by the validator (also means that they are not proposed to the user). - """ + """Check layers are rejected by the validator (also means that they are not proposed to the user).""" layers = theme_test_data["layers"] resp = test_app.post( f"{self._prefix}/new", @@ -359,8 +355,8 @@ def test_duplicate(self, theme_test_data, test_app, dbsession): resp = test_app.get(f"{self._prefix}/{theme.id}/duplicate", status=200) form = resp.form - assert "" == self.get_first_field_named(form, "id").value - assert "hidden" == self.get_first_field_named(form, "id").attrs["type"] + assert self.get_first_field_named(form, "id").value == "" + assert self.get_first_field_named(form, "id").attrs["type"] == "hidden" assert theme.name == self.get_first_field_named(form, "name").value assert str(theme.description or "") == self.get_first_field_named(form, "description").value assert str(theme.ordering or "") == self.get_first_field_named(form, "ordering").value @@ -421,7 +417,7 @@ def test_delete(self, test_app, dbsession): assert dbsession.query(Theme).get(theme.id) is None log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "theme" assert log.element_id == theme.id diff --git a/admin/tests/test_treegroup.py b/admin/tests/test_treegroup.py index b2a4aa2c5d..ac063d188f 100644 --- a/admin/tests/test_treegroup.py +++ b/admin/tests/test_treegroup.py @@ -8,7 +8,7 @@ def check_children(self, form, group, expected): form_group = form.html.select_one(f".item-{group}") items = form_group.select(".deform-seq-item") assert len(expected) == len(items) - for item, exp in zip(items, expected): + for item, exp in zip(items, expected, strict=False): assert exp["label"] == item.select_one(".well").getText().strip() for key, value in exp["values"].items(): assert value == item.select_one(f'input[name="{key}"]')["value"] diff --git a/admin/tests/test_user.py b/admin/tests/test_user.py index 956a7660ac..d16a36fc4c 100644 --- a/admin/tests/test_user.py +++ b/admin/tests/test_user.py @@ -127,7 +127,7 @@ def test_view_edit(self, test_app, users_test_data, dbsession): assert {roles[2].id, roles[3].id} == {role.id for role in user.roles} log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.UPDATE assert log.element_type == "user" assert log.element_id == user.id @@ -145,7 +145,7 @@ def test_delete(self, test_app, users_test_data, dbsession): assert dbsession.query(user_role).filter(user_role.c.user_id == user.id).count() == 0 log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.DELETE assert log.element_type == "user" assert log.element_id == user.id @@ -224,7 +224,7 @@ def test_duplicate(self, pw_gen_mock, smtp_mock, users_test_data, test_app, dbse resp = test_app.get(f"/admin/users/{user.id}/duplicate", status=200) form = resp.form - assert "" == form["id"].value + assert form["id"].value == "" assert user.username == form["username"].value assert user.email == form["email"].value assert str(user.settings_role_id) == form["settings_role_id"].value @@ -249,7 +249,7 @@ def test_duplicate(self, pw_gen_mock, smtp_mock, users_test_data, test_app, dbse assert EXPECTED_WELCOME_MAIL.format("clone", "clone", "basile") == parts[1].get_payload( decode=True ).decode("utf8") - assert "mail7@valid.net" == parts[0].items()[3][1] + assert parts[0].items()[3][1] == "mail7@valid.net" @patch("c2cgeoportal_commons.lib.email_.smtplib.SMTP") @patch("c2cgeoportal_admin.views.users.pwgenerator.generate") @@ -299,10 +299,10 @@ def test_submit_new(self, pw_gen_mock, smtp_mock, dbsession, test_app, users_tes assert EXPECTED_WELCOME_MAIL.format("new_user", "new_user", "basile") == parts[1].get_payload( decode=True ).decode("utf8") - assert "valid@email.net" == parts[0].items()[3][1] + assert parts[0].items()[3][1] == "valid@email.net" log = dbsession.query(Log).one() - assert log.date != None + assert log.date is not None assert log.action == LogAction.INSERT assert log.element_type == "user" assert log.element_id == user.id @@ -339,9 +339,7 @@ def test_grid_dberror(self, dbsession): UserViews(request).grid() def test_grid_settings_role_none(self, dbsession, test_app): - """ - Grid view must work even if a user's settings_role is None. - """ + """Grid view must work even if a user's settings_role is None.""" from c2cgeoportal_commons.models.static import User dbsession.add(User("test", email="test@valid.net")) diff --git a/admin/tests/themes_ordering.py b/admin/tests/themes_ordering.py index edba54a859..afba47dcdc 100644 --- a/admin/tests/themes_ordering.py +++ b/admin/tests/themes_ordering.py @@ -41,7 +41,7 @@ def test_edit(self, test_app, themes_ordering_test_data): ) resp = form.submit("submit", status=302) - assert "http://localhost/admin/layertree" == resp.location + assert resp.location == "http://localhost/admin/layertree" for i, theme in enumerate(sorted(themes_ordering_test_data["themes"], key=lambda t: t.name)): assert i == theme.ordering diff --git a/bin/azure b/bin/azure index dd09b06725..24c4cea98f 100755 --- a/bin/azure +++ b/bin/azure @@ -25,7 +25,7 @@ import argparse import glob import os -import subprocess +import subprocess # nosec import yaml from azure.storage.blob import BlobServiceClient, ContainerClient, __version__ diff --git a/bin/build-l10n b/bin/build-l10n index ce85daa550..ea64b9a151 100755 --- a/bin/build-l10n +++ b/bin/build-l10n @@ -4,7 +4,7 @@ import argparse import glob import os import shutil -import subprocess +import subprocess # nosec def main() -> None: diff --git a/ci/changelog b/ci/changelog index 661004c953..665b61d1cc 100755 --- a/ci/changelog +++ b/ci/changelog @@ -31,7 +31,7 @@ import json import os import re -import subprocess +import subprocess # nosec import sys from typing import Any diff --git a/commons/c2cgeoportal_commons/alembic/env.py b/commons/c2cgeoportal_commons/alembic/env.py index 6c05079fe3..99667969b5 100755 --- a/commons/c2cgeoportal_commons/alembic/env.py +++ b/commons/c2cgeoportal_commons/alembic/env.py @@ -103,7 +103,11 @@ def run_migrations_online() -> None: # Autogenerate config alembic_name = context.config.get_main_option("type") - from c2cgeoportal_commons.models import Base, main, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + Base, + main, + static, + ) _schema = main._schema if alembic_name == "main" else static._schema # pylint: disable=protected-access diff --git a/commons/c2cgeoportal_commons/alembic/main/166ff2dcc48d_create_database.py b/commons/c2cgeoportal_commons/alembic/main/166ff2dcc48d_create_database.py index 3b40b6fddb..d745fb9b7f 100644 --- a/commons/c2cgeoportal_commons/alembic/main/166ff2dcc48d_create_database.py +++ b/commons/c2cgeoportal_commons/alembic/main/166ff2dcc48d_create_database.py @@ -40,7 +40,15 @@ from alembic import op from c2c.template.config import config from sqlalchemy import Column, ForeignKey, MetaData, Table -from sqlalchemy.types import Boolean, DateTime, Float, Integer, String, Unicode, UserDefinedType +from sqlalchemy.types import ( + Boolean, + DateTime, + Float, + Integer, + String, + Unicode, + UserDefinedType, +) # revision identifiers, used by Alembic. revision = "166ff2dcc48d" @@ -93,10 +101,7 @@ def upgrade() -> None: Column("readwrite", Boolean, default=False), schema=schema, ) - op.execute( - "SELECT AddGeometryColumn('%(schema)s', 'restrictionarea', " - "'area', %(srid)s, 'POLYGON', 2)" % {"schema": schema, "srid": srid} - ) + op.execute(f"SELECT AddGeometryColumn('{schema}', 'restrictionarea', " f"'area', {srid}, 'POLYGON', 2)") op.create_table( "shorturl", Column("id", Integer, primary_key=True), @@ -116,10 +121,7 @@ def upgrade() -> None: Column("description", Unicode), schema=schema, ) - op.execute( - "SELECT AddGeometryColumn('%(schema)s', 'role', " - "'extent', %(srid)s, 'POLYGON', 2)" % {"schema": schema, "srid": srid} - ) + op.execute(f"SELECT AddGeometryColumn('{schema}', 'role', " f"'extent', {srid}, 'POLYGON', 2)") role = Table("role", MetaData(), Column("name", Unicode, unique=True, nullable=False), schema=schema) op.bulk_insert(role, [{"name": "role_admin"}]) @@ -172,10 +174,7 @@ def upgrade() -> None: Column("params", Unicode, nullable=True), schema=schema, ) - op.execute( - "SELECT AddGeometryColumn('%(schema)s', 'tsearch', 'the_geom', " - "%(srid)s, 'GEOMETRY', 2)" % {"schema": schema, "srid": srid} - ) + op.execute(f"SELECT AddGeometryColumn('{schema}', 'tsearch', 'the_geom', " f"{srid}, 'GEOMETRY', 2)") op.create_index("tsearch_ts_idx", "tsearch", ["ts"], schema=schema, postgresql_using="gin") op.create_table( "treegroup", diff --git a/commons/c2cgeoportal_commons/alembic/main/21f11066f8ec_trigger_on_role_updates_user_in_static.py b/commons/c2cgeoportal_commons/alembic/main/21f11066f8ec_trigger_on_role_updates_user_in_static.py index 777bdf654f..b2ad419528 100644 --- a/commons/c2cgeoportal_commons/alembic/main/21f11066f8ec_trigger_on_role_updates_user_in_static.py +++ b/commons/c2cgeoportal_commons/alembic/main/21f11066f8ec_trigger_on_role_updates_user_in_static.py @@ -71,7 +71,7 @@ def downgrade() -> None: schema = config["schema"] op.execute( - """ + f""" CREATE OR REPLACE FUNCTION {schema}.on_role_name_change() RETURNS trigger AS $$ @@ -82,7 +82,5 @@ def downgrade() -> None: RETURN NEW; END; $$ -LANGUAGE plpgsql""".format( - schema=schema - ) +LANGUAGE plpgsql""" ) diff --git a/commons/c2cgeoportal_commons/alembic/main/56dc90838d90_fix_removing_layerv1.py b/commons/c2cgeoportal_commons/alembic/main/56dc90838d90_fix_removing_layerv1.py index c6298f2232..cbacc5cece 100644 --- a/commons/c2cgeoportal_commons/alembic/main/56dc90838d90_fix_removing_layerv1.py +++ b/commons/c2cgeoportal_commons/alembic/main/56dc90838d90_fix_removing_layerv1.py @@ -50,11 +50,9 @@ def upgrade() -> None: schema = config["schema"] op.execute( - ( - "DELETE from {schema}.layer_restrictionarea WHERE layer_id IN (" - "SELECT id from {schema}.treeitem WHERE type = 'layerv1'" - ");" - ).format(schema=schema) + f"DELETE from {schema}.layer_restrictionarea WHERE layer_id IN (" + f"SELECT id from {schema}.treeitem WHERE type = 'layerv1'" + ");" ) op.execute(f"DELETE from {schema}.treeitem WHERE type = 'layerv1';") diff --git a/commons/c2cgeoportal_commons/alembic/main/7530011a66a7_trigger_on_role_updates_user_in_static.py b/commons/c2cgeoportal_commons/alembic/main/7530011a66a7_trigger_on_role_updates_user_in_static.py index 4107a44967..0a8278fe29 100644 --- a/commons/c2cgeoportal_commons/alembic/main/7530011a66a7_trigger_on_role_updates_user_in_static.py +++ b/commons/c2cgeoportal_commons/alembic/main/7530011a66a7_trigger_on_role_updates_user_in_static.py @@ -71,7 +71,7 @@ def downgrade() -> None: schema = config["schema"] op.execute( - """ + f""" CREATE OR REPLACE FUNCTION {schema}.on_role_name_change() RETURNS trigger AS $$ @@ -82,7 +82,5 @@ def downgrade() -> None: RETURN NEW; END; $$ -LANGUAGE plpgsql""".format( - schema=schema - ) +LANGUAGE plpgsql""" ) diff --git a/commons/c2cgeoportal_commons/alembic/main/7d271f4527cd_add_layer_column_in_layerv1_table.py b/commons/c2cgeoportal_commons/alembic/main/7d271f4527cd_add_layer_column_in_layerv1_table.py index 1129d0194a..ed914b6974 100644 --- a/commons/c2cgeoportal_commons/alembic/main/7d271f4527cd_add_layer_column_in_layerv1_table.py +++ b/commons/c2cgeoportal_commons/alembic/main/7d271f4527cd_add_layer_column_in_layerv1_table.py @@ -53,10 +53,10 @@ def upgrade() -> None: op.add_column("layerv1", Column("layer", Unicode), schema=schema) op.execute( - "UPDATE {schema}.layerv1 AS l1 " + f"UPDATE {schema}.layerv1 AS l1 " "SET layer = name " - "FROM {schema}.treeitem AS ti " - "WHERE l1.id = ti.id".format(schema=schema) + f"FROM {schema}.treeitem AS ti " + "WHERE l1.id = ti.id" ) diff --git a/commons/c2cgeoportal_commons/alembic/main/9268a1dffac0_add_trigger_to_be_able_to_correctly_.py b/commons/c2cgeoportal_commons/alembic/main/9268a1dffac0_add_trigger_to_be_able_to_correctly_.py index 143943fe90..de7fef0297 100644 --- a/commons/c2cgeoportal_commons/alembic/main/9268a1dffac0_add_trigger_to_be_able_to_correctly_.py +++ b/commons/c2cgeoportal_commons/alembic/main/9268a1dffac0_add_trigger_to_be_able_to_correctly_.py @@ -50,7 +50,7 @@ def upgrade() -> None: schema = config["schema"] op.execute( - """ + f""" CREATE FUNCTION {schema}.on_role_name_change() RETURNS trigger AS $$ @@ -61,14 +61,12 @@ def upgrade() -> None: RETURN NEW; END; $$ -LANGUAGE plpgsql""".format( - schema=schema - ) +LANGUAGE plpgsql""" ) op.execute( - "CREATE TRIGGER on_role_name_change AFTER UPDATE ON {schema}.role FOR EACH ROW " - "EXECUTE PROCEDURE {schema}.on_role_name_change()".format(schema=schema) + f"CREATE TRIGGER on_role_name_change AFTER UPDATE ON {schema}.role FOR EACH ROW " + f"EXECUTE PROCEDURE {schema}.on_role_name_change()" ) diff --git a/commons/c2cgeoportal_commons/alembic/main/eeb345672454_merge_2_4_and_master_branches.py b/commons/c2cgeoportal_commons/alembic/main/eeb345672454_merge_2_4_and_master_branches.py index 50ba1783b8..190e3b3380 100644 --- a/commons/c2cgeoportal_commons/alembic/main/eeb345672454_merge_2_4_and_master_branches.py +++ b/commons/c2cgeoportal_commons/alembic/main/eeb345672454_merge_2_4_and_master_branches.py @@ -35,7 +35,6 @@ Create Date: 2019-09-03 09:11:57.786920 """ - # revision identifiers, used by Alembic. revision = "eeb345672454" down_revision = ("78fd093c8393", "56dc90838d90") diff --git a/commons/c2cgeoportal_commons/alembic/static/1da396a88908_move_user_table_to_static_schema.py b/commons/c2cgeoportal_commons/alembic/static/1da396a88908_move_user_table_to_static_schema.py index 68f7f3294a..b21d92d217 100644 --- a/commons/c2cgeoportal_commons/alembic/static/1da396a88908_move_user_table_to_static_schema.py +++ b/commons/c2cgeoportal_commons/alembic/static/1da396a88908_move_user_table_to_static_schema.py @@ -82,20 +82,13 @@ def upgrade() -> None: try: op.execute( - "INSERT INTO %(staticschema)s.user " - "(type, username, password, email, is_password_changed, role_name%(parent_column)s) (" + f"INSERT INTO {staticschema}.user " + f"(type, username, password, email, is_password_changed, role_name{parent_column}) (" "SELECT u.type, u.username, u.password, u.email, " - "u.is_password_changed, r.name%(parent_select)s " - "FROM %(schema)s.user AS u " - "LEFT OUTER JOIN %(schema)s.role AS r ON (r.id = u.role_id) %(parent_join)s" + f"u.is_password_changed, r.name{parent_select} " + f"FROM {schema}.user AS u " + f"LEFT OUTER JOIN {schema}.role AS r ON (r.id = u.role_id) {parent_join}" ")" - % { - "staticschema": staticschema, - "schema": schema, - "parent_select": parent_select, - "parent_column": parent_column, - "parent_join": parent_join, - } ) op.drop_table("user", schema=schema) except Exception: # pylint: disable=broad-exception-caught @@ -135,20 +128,13 @@ def downgrade() -> None: parent_join = f"LEFT OUTER JOIN {parentschema}.role AS pr ON (pr.name = u.parent_role_name)" op.execute( - "INSERT INTO %(schema)s.user " - "(type, username, password, email, is_password_changed, role_id%(parent_column)s) (" + f"INSERT INTO {schema}.user " + f"(type, username, password, email, is_password_changed, role_id{parent_column}) (" "SELECT u.type, u.username, u.password, u.email, " - "u.is_password_changed, r.id%(parent_select)s " - "FROM %(staticschema)s.user AS u " - "LEFT OUTER JOIN %(schema)s.role AS r ON (r.name = u.role_name) %(parent_join)s" + f"u.is_password_changed, r.id{parent_select} " + f"FROM {staticschema}.user AS u " + f"LEFT OUTER JOIN {schema}.role AS r ON (r.name = u.role_name) {parent_join}" ")" - % { - "staticschema": staticschema, - "schema": schema, - "parent_select": parent_select, - "parent_column": parent_column, - "parent_join": parent_join, - } ) op.drop_table("user", schema=staticschema) diff --git a/commons/c2cgeoportal_commons/alembic/static/53d671b17b20_add_timezone_on_datetime_fields.py b/commons/c2cgeoportal_commons/alembic/static/53d671b17b20_add_timezone_on_datetime_fields.py index 99d9d4ee71..1d5917363c 100644 --- a/commons/c2cgeoportal_commons/alembic/static/53d671b17b20_add_timezone_on_datetime_fields.py +++ b/commons/c2cgeoportal_commons/alembic/static/53d671b17b20_add_timezone_on_datetime_fields.py @@ -50,14 +50,12 @@ def upgrade() -> None: staticschema = config["schema_static"] op.execute( - """ + f""" SET TIME ZONE 'UTC'; ALTER TABLE {staticschema}.user ALTER COLUMN last_login TYPE timestamp with time zone; SET TIME ZONE LOCAL; ALTER TABLE {staticschema}.user ALTER COLUMN expire_on TYPE timestamp with time zone; -""".format( - staticschema=staticschema - ) +""" ) @@ -66,12 +64,10 @@ def downgrade() -> None: staticschema = config["schema_static"] op.execute( - """ + f""" SET TIME ZONE 'UTC'; ALTER TABLE {staticschema}.user ALTER COLUMN last_login TYPE timestamp without time zone; SET TIME ZONE LOCAL; ALTER TABLE {staticschema}.user ALTER COLUMN expire_on TYPE timestamp without time zone; -""".format( - staticschema=staticschema - ) +""" ) diff --git a/commons/c2cgeoportal_commons/alembic/static/aa41e9613256_wip_add_openid_connect_support.py b/commons/c2cgeoportal_commons/alembic/static/aa41e9613256_wip_add_openid_connect_support.py index 59b750d800..5f3625a956 100644 --- a/commons/c2cgeoportal_commons/alembic/static/aa41e9613256_wip_add_openid_connect_support.py +++ b/commons/c2cgeoportal_commons/alembic/static/aa41e9613256_wip_add_openid_connect_support.py @@ -35,7 +35,6 @@ Create Date: 2024-08-30 15:56:31.163378 """ - from alembic import op from c2c.template.config import config diff --git a/commons/c2cgeoportal_commons/models/__init__.py b/commons/c2cgeoportal_commons/models/__init__.py index d12489f7f4..275aaefe16 100644 --- a/commons/c2cgeoportal_commons/models/__init__.py +++ b/commons/c2cgeoportal_commons/models/__init__.py @@ -47,7 +47,7 @@ class BaseType(sqlalchemy.ext.declarative.DeclarativeMeta, type): - pass + """Base type for all the models class.""" Base: BaseType = sqlalchemy.orm.declarative_base() diff --git a/commons/c2cgeoportal_commons/models/main.py b/commons/c2cgeoportal_commons/models/main.py index ffc87dd9f4..0e008f48cb 100644 --- a/commons/c2cgeoportal_commons/models/main.py +++ b/commons/c2cgeoportal_commons/models/main.py @@ -56,7 +56,13 @@ from c2cgeoform.ext.colander_ext import Geometry as ColanderGeometry from c2cgeoform.ext.deform_ext import MapWidget, RelationSelect2Widget from colander import drop - from deform.widget import CheckboxWidget, HiddenWidget, SelectWidget, TextAreaWidget, TextInputWidget + from deform.widget import ( + CheckboxWidget, + HiddenWidget, + SelectWidget, + TextAreaWidget, + TextInputWidget, + ) colander_null = colander.null except ModuleNotFoundError: @@ -84,7 +90,6 @@ def __init__(self, *args: Any, **kwargs: Any): def state_str(state: Any) -> str: """Return a string describing an instance via its InstanceState.""" - return "None" if state is None else f"<{state.class_.__name__} {state.obj()}>" # In the original function sqlalchemy use the id of the object that don't allow us to give some useful @@ -308,7 +313,7 @@ def __str__(self) -> str: return f"{self.name}[{self.id}]>" @property - def bounds(self) -> tuple[float, float, float, float] | None: # TODO + def bounds(self) -> tuple[float, float, float, float] | None: if self.extent is None: return None return cast(tuple[float, float, float, float], to_shape(self.extent).bounds) @@ -367,11 +372,7 @@ def is_in_interface(self, name: str) -> bool: if not hasattr(self, "interfaces"): return False - for interface in self.interfaces: - if interface.name == name: - return True - - return False + return any(interface.name == name for interface in self.interfaces) def get_metadata(self, name: str) -> list["Metadata"]: return [metadata for metadata in self.metadatas if metadata.name == name] @@ -1035,7 +1036,7 @@ def __init__( @staticmethod def get_default(dbsession: Session) -> DimensionLayer | None: return cast( - Optional[DimensionLayer], + DimensionLayer | None, dbsession.query(LayerWMS).filter(LayerWMS.name == "wms-defaults").one_or_none(), ) @@ -1179,7 +1180,7 @@ def __init__(self, name: str = "", public: bool = True, image_type: ImageType = @staticmethod def get_default(dbsession: Session) -> DimensionLayer | None: return cast( - Optional[DimensionLayer], + DimensionLayer | None, dbsession.query(LayerWMTS).filter(LayerWMTS.name == "wmts-defaults").one_or_none(), ) @@ -1374,7 +1375,7 @@ def __init__(self, name: str = "", public: bool = True, style: str = "", sql: st @staticmethod def get_default(dbsession: Session) -> DimensionLayer | None: return cast( - Optional[DimensionLayer], + DimensionLayer | None, dbsession.query(LayerVectorTiles) .filter(LayerVectorTiles.name == "vector-tiles-defaults") .one_or_none(), diff --git a/commons/c2cgeoportal_commons/models/static.py b/commons/c2cgeoportal_commons/models/static.py index 0e60d1f80d..3327f93962 100644 --- a/commons/c2cgeoportal_commons/models/static.py +++ b/commons/c2cgeoportal_commons/models/static.py @@ -324,7 +324,7 @@ def set_temp_password(self, password: str) -> None: @staticmethod def __encrypt_password_legacy(password: str) -> str: """Hash the given password with SHA1.""" - return sha1(password.encode("utf8")).hexdigest() # nosec + return sha1(password.encode("utf8")).hexdigest() # noqa: S324 @staticmethod def __encrypt_password(password: str) -> str: diff --git a/commons/c2cgeoportal_commons/testing/__init__.py b/commons/c2cgeoportal_commons/testing/__init__.py index 14a6ff17af..debb5ae292 100644 --- a/commons/c2cgeoportal_commons/testing/__init__.py +++ b/commons/c2cgeoportal_commons/testing/__init__.py @@ -78,11 +78,10 @@ def get_tm_session( def generate_mappers() -> None: """Initialize the model for a Pyramid app.""" - # import or define all models here to ensure they are attached to the # Base.metadata prior to any initialization routines - import c2cgeoportal_commons.models.main # pylint: disable=unused-import,import-outside-toplevel - import c2cgeoportal_commons.models.static # pylint: disable=import-outside-toplevel + import c2cgeoportal_commons.models.main # pylint: disable=unused-import,import-outside-toplevel # noqa: F401 + import c2cgeoportal_commons.models.static # pylint: disable=import-outside-toplevel # noqa: F401 # run configure_mappers after defining all of the models to ensure # all relationships can be setup diff --git a/commons/c2cgeoportal_commons/testing/initializedb.py b/commons/c2cgeoportal_commons/testing/initializedb.py index e3951d17c6..e9167ce9dc 100644 --- a/commons/c2cgeoportal_commons/testing/initializedb.py +++ b/commons/c2cgeoportal_commons/testing/initializedb.py @@ -46,11 +46,15 @@ def usage(argv: list[str]) -> None: def schema_exists(connection: Connection, schema_name: str) -> bool: """Get if the schema exist.""" - sql = f""" -SELECT count(*) AS count -FROM information_schema.schemata -WHERE schema_name = '{schema_name}'; -""" + del schema_name + + sql = " ".join( + [ # noqa: S608 + "SELECT count(*) AS count", + "FROM information_schema.schemata", + "WHERE schema_name = '{schema_name}';", + ] + ) result = connection.execute(sqlalchemy.text(sql)) row = result.first() return cast(bool, row[0] == 1) # type: ignore[index] @@ -64,8 +68,12 @@ def truncate_tables(connection: Connection) -> None: def setup_test_data(dbsession: Session) -> None: """Initialize the testing data.""" - from c2cgeoportal_commons.models.main import Role # pylint: disable=import-outside-toplevel - from c2cgeoportal_commons.models.static import User # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + Role, + ) + from c2cgeoportal_commons.models.static import ( # pylint: disable=import-outside-toplevel + User, + ) role_admin = dbsession.merge(Role(name="role_admin")) role_user = dbsession.merge(Role(name="role_user")) diff --git a/commons/tests/conftest.py b/commons/tests/conftest.py index dd7d28ccf5..1ce9c4c3ac 100644 --- a/commons/tests/conftest.py +++ b/commons/tests/conftest.py @@ -5,10 +5,14 @@ import pytest import transaction from c2c.template.config import config -from sqlalchemy.exc import DBAPIError - -from c2cgeoportal_commons.testing import generate_mappers, get_engine, get_session_factory, get_tm_session +from c2cgeoportal_commons.testing import ( + generate_mappers, + get_engine, + get_session_factory, + get_tm_session, +) from c2cgeoportal_commons.testing.initializedb import truncate_tables +from sqlalchemy.exc import DBAPIError @pytest.fixture(scope="session") diff --git a/commons/tests/lib/test_url.py b/commons/tests/lib/test_url.py index 7477789912..506e8e3bf4 100644 --- a/commons/tests/lib/test_url.py +++ b/commons/tests/lib/test_url.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2023, Camptocamp SA +# Copyright (c) 2013-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -29,9 +29,8 @@ from unittest import TestCase -from pyramid.testing import DummyRequest - from c2cgeoportal_commons.lib.url import Url, get_url2 +from pyramid.testing import DummyRequest class TestUrl(TestCase): diff --git a/commons/tests/lib/test_validators.py b/commons/tests/lib/test_validators.py index 3f28cf13f1..a176740e21 100644 --- a/commons/tests/lib/test_validators.py +++ b/commons/tests/lib/test_validators.py @@ -1,7 +1,6 @@ # pylint: disable=no-self-use import pytest - from c2cgeoportal_commons.lib.validators import url diff --git a/commons/tests/models/test_interface.py b/commons/tests/models/test_interface.py index b64d7b1b0c..ad7afbcd22 100644 --- a/commons/tests/models/test_interface.py +++ b/commons/tests/models/test_interface.py @@ -6,9 +6,8 @@ @pytest.mark.usefixtures("transact") class TestInterface: def test_delete_cascade_to_tsearch(self, dbsession): - from sqlalchemy import func - from c2cgeoportal_commons.models.main import FullTextSearch, Interface + from sqlalchemy import func interface = Interface("desktop", "Desktop interface") interface_id = interface.id @@ -24,5 +23,5 @@ def test_delete_cascade_to_tsearch(self, dbsession): dbsession.flush() assert ( - 0 == dbsession.query(FullTextSearch).filter(FullTextSearch.interface_id == interface_id).count() + dbsession.query(FullTextSearch).filter(FullTextSearch.interface_id == interface_id).count() == 0 ) diff --git a/commons/tests/models/test_roles.py b/commons/tests/models/test_roles.py index b4a227807b..ac68c3537a 100644 --- a/commons/tests/models/test_roles.py +++ b/commons/tests/models/test_roles.py @@ -41,12 +41,11 @@ def test_delete(self, dbsession): dbsession.delete(roles[0]) roles = dbsession.query(Role).all() assert len(roles) == 0, "removed a role" - assert 0 == dbsession.query(user_role).count() + assert dbsession.query(user_role).count() == 0 def test_delete_cascade_to_tsearch(self, dbsession): - from sqlalchemy import func - from c2cgeoportal_commons.models.main import FullTextSearch, Role + from sqlalchemy import func role = dbsession.query(Role).filter(Role.name == "secretary").one() role_id = role.id @@ -61,4 +60,4 @@ def test_delete_cascade_to_tsearch(self, dbsession): dbsession.delete(role) dbsession.flush() - assert 0 == dbsession.query(FullTextSearch).filter(FullTextSearch.role_id == role_id).count() + assert dbsession.query(FullTextSearch).filter(FullTextSearch.role_id == role_id).count() == 0 diff --git a/commons/tests/models/test_users.py b/commons/tests/models/test_users.py index 43086621fb..2908ab8d00 100644 --- a/commons/tests/models/test_users.py +++ b/commons/tests/models/test_users.py @@ -30,7 +30,7 @@ def test_select(self, dbsession): users = dbsession.query(User).all() assert len(users) == 1, "querying for users" assert users[0].username == "babar", "user from test data is babar" - assert 2 == len(users[0].roles) + assert len(users[0].roles) == 2 def test_remove(self, dbsession): from c2cgeoportal_commons.models.static import User, user_role @@ -39,7 +39,7 @@ def test_remove(self, dbsession): dbsession.delete(users[0]) users = dbsession.query(User).all() assert len(users) == 0, "removed a user" - assert 0 == dbsession.query(user_role).count() + assert dbsession.query(user_role).count() == 0 def test_add(self, dbsession): from c2cgeoportal_commons.models.main import Role @@ -52,9 +52,9 @@ def test_add(self, dbsession): assert dbsession.query(User).count() == 2, "added a user" dbsession.expire(user) assert user.username == "momo", "added user is momo" - assert 1 == len(user.roles) + assert len(user.roles) == 1 assert user.roles[0].name == "Role3" - assert 1 == dbsession.query(user_role).filter(user_role.c.user_id == user.id).count() + assert dbsession.query(user_role).filter(user_role.c.user_id == user.id).count() == 1 @staticmethod def test_edit(dbsession): @@ -62,10 +62,10 @@ def test_edit(dbsession): from c2cgeoportal_commons.models.static import User, user_role user = dbsession.query(User).first() - assert 2 == len(user.roles) + assert len(user.roles) == 2 user.roles = [Role(name="Role4")] dbsession.flush() dbsession.expire(user) - assert 1 == len(user.roles) + assert len(user.roles) == 1 assert user.roles[0].name == "Role4" - assert 1 == dbsession.query(user_role).filter(user_role.c.user_id == user.id).count() + assert dbsession.query(user_role).filter(user_role.c.user_id == user.id).count() == 1 diff --git a/doc/import_ngeo_config.py b/doc/import_ngeo_config.py index ffb417e34b..83c47c20e9 100755 --- a/doc/import_ngeo_config.py +++ b/doc/import_ngeo_config.py @@ -30,7 +30,7 @@ def _format_type(type_definition: dict[str, Any]) -> str: f"{_format_type(type_definition['declaration']['indexSignature']['type'])}}}" ) - assert False, f"Unknown type '{type_definition['type']}':\n{type_definition}" + raise AssertionError(f"Unknown type '{type_definition['type']}':\n{type_definition}") def _get_type(type_definition: dict[str, Any]) -> list[str]: diff --git a/doc/integrator/requirements.rst b/doc/integrator/requirements.rst index 7181204e53..3f3cad91c2 100644 --- a/doc/integrator/requirements.rst +++ b/doc/integrator/requirements.rst @@ -11,7 +11,7 @@ components installed on your system: * **Git** * **Docker** >= 17.05 -* **Python** >= 3.8, with ``pip`` +* **Python** >= 3.10, with ``pip`` * **Apache** >= 2.4 (optional, can be used as a front for SSL) * **PostgreSQL** >= 9.1/**PostGIS** >= 2.1 diff --git a/doc/pyproject.toml b/doc/pyproject.toml index 1bb00bb8ba..d894991c3d 100644 --- a/doc/pyproject.toml +++ b/doc/pyproject.toml @@ -1,11 +1,3 @@ -[tool.black] -line-length = 110 -target-version = ['py39'] - -[tool.isort] -profile = "black" -line_length = 110 - [tool.poetry] name = 'c2cgeoportal' version = '0.0.0' diff --git a/docker/config/bin/eval-templates b/docker/config/bin/eval-templates index 3680ed0321..05d9bfa97c 100755 --- a/docker/config/bin/eval-templates +++ b/docker/config/bin/eval-templates @@ -5,7 +5,7 @@ import glob import os import random import re -import subprocess +import subprocess # nosec import urllib.parse import zipfile from typing import cast @@ -115,14 +115,14 @@ def _main() -> None: if os.environ.get("VISIBLE_ENTRY_POINT"): os.environ["VISIBLE_ENTRY_POINT_RE_ESCAPED"] = re.escape(os.environ["VISIBLE_ENTRY_POINT"]) os.environ["MAPSERVER_DATA_SUBSELECT"] = ( - "SELECT {ST_JOIN}(ra.area) " + "SELECT {ST_JOIN}(ra.area) " # nosec "FROM {PGSCHEMA}.restrictionarea AS ra, {PGSCHEMA}.role_restrictionarea AS rra, " "{PGSCHEMA}.layer_restrictionarea AS lra, {PGSCHEMA}.treeitem AS la " "WHERE rra.role_id in (%role_ids%) AND rra.restrictionarea_id = ra.id " "AND lra.restrictionarea_id = ra.id AND lra.layer_id = la.id AND la.name = " ).format(PGSCHEMA=os.environ["PGSCHEMA"], ST_JOIN=os.environ.get("ST_JOIN", "ST_Collect")) os.environ["MAPSERVER_DATA_NOAREA_SUBSELECT"] = ( - "SELECT rra.role_id " + "SELECT rra.role_id " # nosec "FROM {PGSCHEMA}.restrictionarea AS ra, {PGSCHEMA}.role_restrictionarea AS rra, " "{PGSCHEMA}.layer_restrictionarea AS lra, {PGSCHEMA}.treeitem AS la " "WHERE rra.restrictionarea_id = ra.id AND lra.restrictionarea_id = ra.id " diff --git a/docker/qgisserver/.bandit.yaml b/docker/qgisserver/.bandit.yaml deleted file mode 100644 index 1bf9b483e1..0000000000 --- a/docker/qgisserver/.bandit.yaml +++ /dev/null @@ -1,2 +0,0 @@ -skips: - - B101 # Use of assert detected. diff --git a/docker/qgisserver/.prospector.yaml b/docker/qgisserver/.prospector.yaml index 7c072ce7a2..98eacd5fdb 100644 --- a/docker/qgisserver/.prospector.yaml +++ b/docker/qgisserver/.prospector.yaml @@ -3,10 +3,11 @@ inherits: - utils:base - utils:fix - utils:no-design-checks + - utils:unsafe pyroma: run: False -bandit: +mypy: options: - config: .bandit.yaml + python_version: '3.10' diff --git a/docker/qgisserver/geomapfish_qgisserver/__init__.py b/docker/qgisserver/geomapfish_qgisserver/__init__.py index f0e30ef830..d8da365e3b 100644 --- a/docker/qgisserver/geomapfish_qgisserver/__init__.py +++ b/docker/qgisserver/geomapfish_qgisserver/__init__.py @@ -20,6 +20,7 @@ def serverClassFactory( # pylint: disable=invalid-name serverIface: qgis.server.QgsServerInterface, # pylint: disable=invalid-name ) -> qgis.server.QgsAccessControlFilter | None: + """Create a new instance of the access control filter.""" QgsMessageLog.logMessage("Configure logging...", "GeoMapFish-init", level=Qgis.Info) try: @@ -34,7 +35,9 @@ def serverClassFactory( # pylint: disable=invalid-name _LOG.info("Starting GeoMapFish access restriction...") try: - from .accesscontrol import GeoMapFishAccessControl # pylint: disable=import-outside-toplevel + from .accesscontrol import ( # pylint: disable=import-outside-toplevel + GeoMapFishAccessControl, + ) return GeoMapFishAccessControl(serverIface) except Exception: # pylint: disable=broad-exception-caught diff --git a/docker/qgisserver/geomapfish_qgisserver/accesscontrol.py b/docker/qgisserver/geomapfish_qgisserver/accesscontrol.py index 7e421bceef..eb9793b064 100644 --- a/docker/qgisserver/geomapfish_qgisserver/accesscontrol.py +++ b/docker/qgisserver/geomapfish_qgisserver/accesscontrol.py @@ -20,6 +20,7 @@ import yaml import zope.event.classhandler from c2c.template.config import config +from c2cgeoportal_commons.lib.url import Url, get_url2 from qgis.core import ( QgsDataSourceUri, QgsFeature, @@ -35,15 +36,16 @@ from sqlalchemy.orm import configure_mappers, joinedload, sessionmaker, subqueryload from sqlalchemy.orm.session import Session -from c2cgeoportal_commons.lib.url import Url, get_url2 - if TYPE_CHECKING: - from c2cgeoportal_commons.models import main # pylint: disable=ungrouped-imports,useless-suppression + from c2cgeoportal_commons.models import ( + main, # pylint: disable=ungrouped-imports,useless-suppression + ) _LOG = logging.getLogger(__name__) def create_session_factory(url: str, configuration: dict[str, Any]) -> sessionmaker: + """Create a SQLAlchemy session factory.""" configure_mappers() db_match = re.match(".*(@[^@]+)$", url) _LOG.info( @@ -58,17 +60,22 @@ def create_session_factory(url: str, configuration: dict[str, Any]) -> sessionma class GMFException(Exception): - """Standard exception""" + """Standard exception.""" class Access(Enum): + """Access mode.""" + NO = 1 AREA = 2 FULL = 3 class GeoMapFishAccessControl(QgsAccessControlFilter): + """Implements GeoMapFish access restriction.""" + def __init__(self, server_iface: qgis.server.QgsServerInterface): + """Initialize the plugin.""" super().__init__(server_iface) self.server_iface = server_iface @@ -225,12 +232,14 @@ def __init__(self, server_iface: qgis.server.QgsServerInterface): server_iface.registerAccessControl(self, int(os.environ.get("GEOMAPFISH_POSITION", 100))) def get_ogcserver_accesscontrol_config(self) -> None: + """Get the config.""" if self.single: raise GMFException( "The method 'get_ogcserver_accesscontrol_config' can't be called on 'single' server" ) def get_ogcserver_accesscontrol(self) -> "OGCServerAccessControl": + """Get the OGCServerAccessControl instance.""" parameters = self.serverInterface().requestHandler().parameterMap() if self.single: @@ -309,10 +318,11 @@ def allowToEdit(self, layer: QgsVectorLayer, feature: QgsFeature) -> bool: # py raise def cacheKey(self) -> str: # pylint: disable=invalid-name + """Get the cache key.""" try: if not self.initialized: _LOG.error("Call on uninitialized plugin") - return str(random.randrange(1000000)) # nosec + return str(random.randrange(1000000)) # noqa: S311 return self.get_ogcserver_accesscontrol().cacheKey() except Exception: _LOG.exception("Unhandled error") @@ -320,7 +330,7 @@ def cacheKey(self) -> str: # pylint: disable=invalid-name class OGCServerAccessControl(QgsAccessControlFilter): - """Implements GeoMapFish access restriction.""" + """Implements GeoMapFish access restriction for one project.""" SUBSETSTRING_TYPE = ["PostgreSQL database with PostGIS extension"] @@ -333,6 +343,7 @@ def __init__( DBSession: sessionmaker, # pylint: disable=invalid-name ogcserver: Optional["main.OGCServer"] = None, ): + """Initialize the plugin.""" super().__init__(server_iface) self.server_iface = server_iface @@ -341,7 +352,7 @@ def __init__( self.DBSession = DBSession # pylint: disable=invalid-name self.area_cache: dict[Any, tuple[Access, BaseGeometry]] = {} - self.layers: dict[str, list["main.LayerWMS"]] | None = None + self.layers: dict[str, list[main.LayerWMS]] | None = None self.lock = Lock() self.srid = srid self.ogcserver = ogcserver @@ -358,6 +369,7 @@ def handle(_: InvalidateCacheEvent) -> None: self._init(ogcserver_name) def project(self) -> QgsProject: + """Get the project.""" return QgsConfigCache.instance().project(self.map_file) def _init(self, ogcserver_name: str) -> None: @@ -387,6 +399,7 @@ def _init(self, ogcserver_name: str) -> None: _LOG.exception("Cannot setup OGCServerAccessControl") def ogc_layer_name(self, layer: QgsVectorLayer) -> str: + """Get the OGC layer name.""" use_layer_id, _ = self.project().readBoolEntry("WMSUseLayerIDs", "/", False) if use_layer_id: return layer.id() @@ -394,9 +407,9 @@ def ogc_layer_name(self, layer: QgsVectorLayer) -> str: def get_layers(self, session: Session) -> dict[str, list["main.Layer"]]: """ - Get the list of GMF WMS layers that can give access to each QGIS layer or group. That is, for each - QGIS layer tree node, the list of GMF WMS layers that: + Get the list of GMF WMS layers that can give access to each QGIS layer or group. + That is, for each QGIS layer tree node, the list of GMF WMS layers that: - correspond to this ogc_server - contains QGIS node name in the layer_wms.layer field. Returns a dict with: @@ -435,7 +448,7 @@ def browse(path: list[str], node: QgsLayerTreeNode) -> None: browse([], self.project().layerTreeRoot()) - for ogc_layer_name, ancestors in nodes.items(): + for ogc_layer_name, _ in nodes.items(): _LOG.debug("QGIS layer: %s", ogc_layer_name) # Transform ancestor names in LayerWMS instances @@ -473,10 +486,14 @@ def browse(path: list[str], node: QgsLayerTreeNode) -> None: def get_roles(self, session: Session) -> str | list["main.Role"]: """ Get the current user's available roles based on request parameter USER_ID. + Returns: - List of c2cgeoportal_commons.models.main.Role instances. + """ - from c2cgeoportal_commons.models.main import Role # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + Role, + ) parameters = self.serverInterface().requestHandler().parameterMap() @@ -514,8 +531,8 @@ def get_restriction_areas( Returns: - Access mode (NO | AREA | FULL) - List of access areas as shapely geometric objects - """ + """ # Root... if roles == "ROOT": return Access.FULL, None @@ -528,7 +545,9 @@ def get_restriction_areas( if not roles: return Access.NO, None - from c2cgeoportal_commons.models.main import Role # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + Role, + ) restriction_areas = set() for layer in gmf_layers: @@ -554,9 +573,11 @@ def get_restriction_areas( def get_area(self, layer: str, session: Session, read_write: bool = False) -> tuple[Access, BaseGeometry]: """ Calculate access area for a QgsMapLayer and an access mode. + Returns: - Access mode (NO | AREA | FULL) - Access area as WKT or None + """ roles = self.get_roles(session) if roles == "ROOT": @@ -588,10 +609,7 @@ def get_area(self, layer: str, session: Session, read_write: bool = False) -> tu return (Access.AREA, area) def layerFilterSubsetString(self, layer: QgsVectorLayer) -> str | None: # pylint: disable=invalid-name - """ - Returns an additional subset string (typically SQL) filter. - """ - + """Get an additional subset string (typically SQL) filter.""" _LOG.debug("layerFilterSubsetString %s %s", layer.name(), layer.dataProvider().storageType()) if self.ogcserver is None: @@ -633,10 +651,7 @@ def layerFilterSubsetString(self, layer: QgsVectorLayer) -> str | None: # pylin raise def layerFilterExpression(self, layer: QgsVectorLayer) -> str | None: # pylint: disable=invalid-name - """ - Returns an additional expression filter. - """ - + """Get an additional expression filter.""" _LOG.debug("layerFilterExpression %s %s", layer.name(), layer.dataProvider().storageType()) if self.ogcserver is None: @@ -678,10 +693,7 @@ def layerFilterExpression(self, layer: QgsVectorLayer) -> str | None: # pylint: def layerPermissions( # pylint: disable=invalid-name self, layer: QgsVectorLayer ) -> qgis.server.QgsAccessControlFilter.LayerPermissions: - """ - Returns the layer rights. - """ - + """Get the layer rights.""" _LOG.debug("layerPermissions %s", layer.name()) try: @@ -725,10 +737,7 @@ def layerPermissions( # pylint: disable=invalid-name def authorizedLayerAttributes( # pylint: disable=invalid-name self, layer: QgsVectorLayer, attributes: list[str] ) -> list[str]: - """ - Returns the authorized layer attributes. - """ - + """Get the authorized layer attributes.""" roles = self.get_roles(self.DBSession()) if roles == "ROOT": return attributes @@ -803,6 +812,7 @@ def allowToEdit(self, layer: QgsVectorLayer, feature: QgsFeature) -> bool: # py raise def cacheKey(self) -> str: # pylint: disable=invalid-name + """Get the cache key.""" # Root... session = self.DBSession() try: diff --git a/docker/qgisserver/geomapfish_qgisserver/gmf_logging.py b/docker/qgisserver/geomapfish_qgisserver/gmf_logging.py index f8f82d5794..ee7a9bef14 100644 --- a/docker/qgisserver/geomapfish_qgisserver/gmf_logging.py +++ b/docker/qgisserver/geomapfish_qgisserver/gmf_logging.py @@ -30,6 +30,7 @@ class LogHandler(logging.Handler): """Python logging handle for QGIS.""" def emit(self, record: logging.LogRecord) -> None: + """Emit the record.""" # To be visible in the CI print(self.format(record)) @@ -37,9 +38,7 @@ def emit(self, record: logging.LogRecord) -> None: class _RequestFilter(logging.Filter): - """ - A logging filter that adds request information to CEE logs. - """ + """A logging filter that adds request information to CEE logs.""" def filter(self, record: Any) -> bool: request_handler = SERVER_IFACE.requestHandler() if SERVER_IFACE else None @@ -66,13 +65,13 @@ def filter(self, record: Any) -> bool: class JsonLogHandler(c2cwsgiutils.pyramid_logging.JsonLogHandler): - """ - Log to stdout in JSON. - """ + """Log to stdout in JSON.""" def __init__(self, stream: TextIO | None) -> None: + """Initialize the handler.""" super().__init__(stream) self.addFilter(_REQUEST_FILTER) def emit(self, record: logging.LogRecord) -> None: + """Emit the record.""" QgsMessageLog.logMessage(self.format(record), record.name, level=Qgis.Critical) diff --git a/docker/qgisserver/poetry.lock b/docker/qgisserver/poetry.lock index 7687f32186..b2da6902eb 100644 --- a/docker/qgisserver/poetry.lock +++ b/docker/qgisserver/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "astroid" @@ -1007,6 +1007,7 @@ pylint-django = ">=2.6.1" pylint-flask = "0.6" PyYAML = "*" requirements-detector = ">=1.3.2" +ruff = {version = "*", optional = true, markers = "extra == \"with-ruff\" or extra == \"with_everything\""} setoptconf-tmp = ">=0.3.1,<0.4.0" toml = ">=0.10.2,<0.11.0" @@ -1416,6 +1417,33 @@ typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.1 [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "ruff" +version = "0.8.1" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.8.1-py3-none-linux_armv6l.whl", hash = "sha256:fae0805bd514066f20309f6742f6ee7904a773eb9e6c17c45d6b1600ca65c9b5"}, + {file = "ruff-0.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8a4f7385c2285c30f34b200ca5511fcc865f17578383db154e098150ce0a087"}, + {file = "ruff-0.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd054486da0c53e41e0086e1730eb77d1f698154f910e0cd9e0d64274979a209"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2029b8c22da147c50ae577e621a5bfbc5d1fed75d86af53643d7a7aee1d23871"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2666520828dee7dfc7e47ee4ea0d928f40de72056d929a7c5292d95071d881d1"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:333c57013ef8c97a53892aa56042831c372e0bb1785ab7026187b7abd0135ad5"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:288326162804f34088ac007139488dcb43de590a5ccfec3166396530b58fb89d"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b12c39b9448632284561cbf4191aa1b005882acbc81900ffa9f9f471c8ff7e26"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:364e6674450cbac8e998f7b30639040c99d81dfb5bbc6dfad69bc7a8f916b3d1"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b22346f845fec132aa39cd29acb94451d030c10874408dbf776af3aaeb53284c"}, + {file = "ruff-0.8.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b2f2f7a7e7648a2bfe6ead4e0a16745db956da0e3a231ad443d2a66a105c04fa"}, + {file = "ruff-0.8.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:adf314fc458374c25c5c4a4a9270c3e8a6a807b1bec018cfa2813d6546215540"}, + {file = "ruff-0.8.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a885d68342a231b5ba4d30b8c6e1b1ee3a65cf37e3d29b3c74069cdf1ee1e3c9"}, + {file = "ruff-0.8.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d2c16e3508c8cc73e96aa5127d0df8913d2290098f776416a4b157657bee44c5"}, + {file = "ruff-0.8.1-py3-none-win32.whl", hash = "sha256:93335cd7c0eaedb44882d75a7acb7df4b77cd7cd0d2255c93b28791716e81790"}, + {file = "ruff-0.8.1-py3-none-win_amd64.whl", hash = "sha256:2954cdbe8dfd8ab359d4a30cd971b589d335a44d444b6ca2cb3d1da21b75e4b6"}, + {file = "ruff-0.8.1-py3-none-win_arm64.whl", hash = "sha256:55873cc1a473e5ac129d15eccb3c008c096b94809d693fc7053f588b67822737"}, + {file = "ruff-0.8.1.tar.gz", hash = "sha256:3583db9a6450364ed5ca3f3b4225958b24f78178908d5c4bc0f46251ccca898f"}, +] + [[package]] name = "semver" version = "3.0.2" @@ -2015,4 +2043,4 @@ test = ["zope.testing"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "c6b2084aefc7f9107b7503d5eb177884274a045a9474fb82adff14b9e5687606" +content-hash = "dfddb80abb0d577ba46d1a7f52171f21ffcfcd5f133a3494ec85080d20e2a207" diff --git a/docker/qgisserver/pyproject.toml b/docker/qgisserver/pyproject.toml index b2b157c575..d10c29cf23 100644 --- a/docker/qgisserver/pyproject.toml +++ b/docker/qgisserver/pyproject.toml @@ -1,20 +1,5 @@ -[tool.mypy] -python_version = 3.10 -warn_redundant_casts = true -warn_unused_ignores = true -check_untyped_defs = true -skip_version_check = true -ignore_missing_imports = true - -[tool.black] -line-length = 110 -target-version = ['py310'] - -[tool.isort] -profile = "black" -line_length = 110 -known_third_party = "c2cwsgiutils,c2cgeoform,qgis,pytest" -known_first_party = ["geomapfish_qgisserver", "c2cgeoportal_commons"] +[tool.ruff] +target-version = 'py310' [tool.poetry] name = 'c2cgeoportal' @@ -38,7 +23,7 @@ psycopg2 = "2.9.10" pytz = "2024.2" [tool.poetry.dev-dependencies] -prospector = { extras = ["with_mypy", "with_bandit"], version = "1.13.3" } +prospector = { version = "1.13.3", extras = ["with_mypy", "with_bandit", "with_ruff"] } prospector-profile-duplicated = "1.9.0" prospector-profile-utils = "1.14.1" types-pyyaml = "6.0.12.20240917" diff --git a/docker/qgisserver/tests/functional/accesscontrol_allow_to_edit_test.py b/docker/qgisserver/tests/functional/accesscontrol_allow_to_edit_test.py index dba24217fd..739a333560 100644 --- a/docker/qgisserver/tests/functional/accesscontrol_allow_to_edit_test.py +++ b/docker/qgisserver/tests/functional/accesscontrol_allow_to_edit_test.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018-2023, Camptocamp SA +# Copyright (c) 2018-2024, Camptocamp SA # All rights reserved. # This program is free software; you can redistribute it and/or modify it under the terms of the @@ -12,9 +12,10 @@ from qgis.core import QgsFeature, QgsGeometry, QgsProject from shapely.geometry import LineString, box -from .accesscontrol_test import add_node_in_qgis_project, set_request_parameters from geomapfish_qgisserver.accesscontrol import OGCServerAccessControl +from .accesscontrol_test import add_node_in_qgis_project, set_request_parameters + area1 = box(400000, 70000, 800000, 100000) geom_in = LineString([[500000, 80000], [500000, 90000]]) geom_intersects = LineString([[500000, 50000], [500000, 90000]]) @@ -155,6 +156,4 @@ def test_allow_to_edit(self, server_iface, DBSession, test_data2): # noqa: igno geom.fromWkb(geometry.wkb) feature.setGeometry(geom) result = ogcserver_accesscontrol.allowToEdit(layer, feature) - assert expected == result, "allowToEdit with '{}', should return '{}'.".format( - user_name, expected - ) + assert expected == result, f"allowToEdit with '{user_name}', should return '{expected}'." diff --git a/docker/qgisserver/tests/functional/accesscontrol_protected_attributes_test.py b/docker/qgisserver/tests/functional/accesscontrol_protected_attributes_test.py index 7cb3d899fe..8e8a7d30d0 100644 --- a/docker/qgisserver/tests/functional/accesscontrol_protected_attributes_test.py +++ b/docker/qgisserver/tests/functional/accesscontrol_protected_attributes_test.py @@ -8,11 +8,15 @@ from unittest.mock import patch import pytest -from qgis.core import QgsProject, QgsVectorLayer +from qgis.core import QgsProject from shapely.geometry import box +from geomapfish_qgisserver.accesscontrol import ( + GeoMapFishAccessControl, + OGCServerAccessControl, +) + from .accesscontrol_test import add_node_in_qgis_project, set_request_parameters -from geomapfish_qgisserver.accesscontrol import GeoMapFishAccessControl, OGCServerAccessControl area1 = box(485869.5728, 76443.1884, 837076.5648, 299941.7864) @@ -124,7 +128,13 @@ def _test_data_protected(clean_dbsession, protected: bool): @pytest.fixture(scope="module") def test_data_not_protected(clean_dbsession): - from c2cgeoportal_commons.models.main import Functionality, LayerWMS, Metadata, OGCServer, Role + from c2cgeoportal_commons.models.main import ( + Functionality, + LayerWMS, + Metadata, + OGCServer, + Role, + ) from c2cgeoportal_commons.models.static import User test_data = _test_data_protected(clean_dbsession, False) @@ -140,7 +150,13 @@ def test_data_not_protected(clean_dbsession): @pytest.fixture(scope="module") def test_data_protected(clean_dbsession): - from c2cgeoportal_commons.models.main import Functionality, LayerWMS, Metadata, OGCServer, Role + from c2cgeoportal_commons.models.main import ( + Functionality, + LayerWMS, + Metadata, + OGCServer, + Role, + ) from c2cgeoportal_commons.models.static import User test_data = _test_data_protected(clean_dbsession, True) diff --git a/docker/qgisserver/tests/functional/accesscontrol_test.py b/docker/qgisserver/tests/functional/accesscontrol_test.py index c210f311b0..6d6d161be5 100644 --- a/docker/qgisserver/tests/functional/accesscontrol_test.py +++ b/docker/qgisserver/tests/functional/accesscontrol_test.py @@ -22,7 +22,9 @@ area1 = box(485869.5728, 76443.1884, 837076.5648, 299941.7864) -def set_request_parameters(server_iface, params, env={}): +def set_request_parameters(server_iface, params, env=None): + if env is None: + env = {} server_iface.configure_mock( **{ "configFilePath.return_value": params.get("MAP", None), @@ -199,9 +201,7 @@ def test_data(clean_dbsession): @pytest.fixture(scope="function") def wms_use_layer_ids(test_data): - """ - Activate WMSUseLayerIDs. - """ + """Activate WMSUseLayerIDs.""" project = test_data["project"] try: project.writeEntry("WMSUseLayerIDs", "/", True) @@ -267,7 +267,7 @@ def test_get_layers(self, server_iface, DBSession, test_data): # noqa: ignore=N layers = ogcserver_accesscontrol.get_layers(dbsession) assert set(expected.keys()) == set(layers.keys()) - for key in expected.keys(): + for key in expected: assert set(expected[key]) == {layer.name for layer in layers[key]} def test_get_roles(self, server_iface, DBSession, test_data): # noqa: ignore=N803 @@ -277,7 +277,7 @@ def test_get_roles(self, server_iface, DBSession, test_data): # noqa: ignore=N8 ) set_request_parameters(server_iface, {"USER_ID": "0"}) - assert "ROOT" == ogcserver_accesscontrol.get_roles(dbsession) + assert ogcserver_accesscontrol.get_roles(dbsession) == "ROOT" test_users = test_data["users"] test_roles = test_data["roles"] @@ -306,11 +306,11 @@ def test_get_restriction_areas(self, server_iface, DBSession, test_data): # noq server_iface, "qgisserver1", "no_project", 21781, lambda: dbsession ) - assert (Access.FULL, None) == ogcserver_accesscontrol.get_restriction_areas( + assert ogcserver_accesscontrol.get_restriction_areas( dbsession.query(LayerWMS).filter(LayerWMS.name == "private_layer1").one(), read_write=True, roles="ROOT", - ) + ) == (Access.FULL, None) for layer_names, rw, role_names, expected in ( (("private_layer1",), False, ("role1",), (Access.AREA, [area1])), @@ -322,9 +322,9 @@ def test_get_restriction_areas(self, server_iface, DBSession, test_data): # noq ] roles = [dbsession.query(Role).filter(Role.name == role_name).one() for role_name in role_names] ras = ogcserver_accesscontrol.get_restriction_areas(layers, rw, roles) - assert expected == ras, "get_restriction_areas with {} should return {}".format( - (layer_names, rw, role_names), expected - ) + assert ( + expected == ras + ), f"get_restriction_areas with {(layer_names, rw, role_names)} should return {expected}" def test_get_area(self, server_iface, DBSession, test_data): # noqa: ignore=N803 dbsession = DBSession() @@ -395,8 +395,8 @@ def test_cache_key(server_iface, DBSession, test_data): # noqa: ignore=N803 ) set_request_parameters(server_iface, {"USER_ID": "0"}, {"HTTP_HOST": "example.com"}) - assert "0" == server_iface.requestHandler().parameter("USER_ID") - assert "example.com-ROOT" == ogcserver_accesscontrol.cacheKey() + assert server_iface.requestHandler().parameter("USER_ID") == "0" + assert ogcserver_accesscontrol.cacheKey() == "example.com-ROOT" user = test_data["users"]["user12"] role1 = test_data["roles"]["role1"] diff --git a/docker/qgisserver/tests/functional/conftest.py b/docker/qgisserver/tests/functional/conftest.py index 9988539040..340a3280ce 100644 --- a/docker/qgisserver/tests/functional/conftest.py +++ b/docker/qgisserver/tests/functional/conftest.py @@ -3,12 +3,11 @@ import pytest from c2c.template.config import config +from c2cgeoportal_commons.testing import generate_mappers from qgis.server import QgsServerInterface from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from c2cgeoportal_commons.testing import generate_mappers - @pytest.fixture(scope="session") def settings(): @@ -33,7 +32,14 @@ def DBSession(settings): # noqa: ignore=N802 @pytest.fixture(scope="module") @pytest.mark.usefixtures("DBSession") def clean_dbsession(DBSession): # noqa: ignore=N803 - from c2cgeoportal_commons.models.main import OGCServer, RestrictionArea, Role, TreeItem, layer_ra, role_ra + from c2cgeoportal_commons.models.main import ( + OGCServer, + RestrictionArea, + Role, + TreeItem, + layer_ra, + role_ra, + ) from c2cgeoportal_commons.models.static import User, user_role def clean(): diff --git a/geoportal/c2cgeoportal_geoportal/__init__.py b/geoportal/c2cgeoportal_geoportal/__init__.py index 2e38add0fe..857f3c3c30 100644 --- a/geoportal/c2cgeoportal_geoportal/__init__.py +++ b/geoportal/c2cgeoportal_geoportal/__init__.py @@ -37,6 +37,7 @@ from typing import TYPE_CHECKING, Any, Optional, cast import c2cgeoform +import c2cgeoportal_commons.models import c2cwsgiutils import c2cwsgiutils.db import c2cwsgiutils.index @@ -50,6 +51,7 @@ import sqlalchemy.orm import zope.event.classhandler from c2cgeoform import translator +from c2cgeoportal_commons.models import InvalidateCacheEvent from c2cwsgiutils.health_check import HealthCheck from c2cwsgiutils.prometheus import MemoryMapCollector from deform import Form @@ -62,10 +64,14 @@ from pyramid_mako import add_mako_renderer from sqlalchemy.orm import joinedload -import c2cgeoportal_commons.models import c2cgeoportal_geoportal.views -from c2cgeoportal_commons.models import InvalidateCacheEvent -from c2cgeoportal_geoportal.lib import C2CPregenerator, caching, check_collector, checker, oidc +from c2cgeoportal_geoportal.lib import ( + C2CPregenerator, + caching, + check_collector, + checker, + oidc, +) from c2cgeoportal_geoportal.lib.cacheversion import version_cache_buster from c2cgeoportal_geoportal.lib.caching import get_region from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers @@ -79,7 +85,9 @@ from c2cgeoportal_geoportal.views.entry import Entry, canvas_view, custom_view if TYPE_CHECKING: - from c2cgeoportal_commons.models import static # pylint: disable=ungrouped-imports,useless-suppression + from c2cgeoportal_commons.models import ( + static, # pylint: disable=ungrouped-imports,useless-suppression + ) _LOG = logging.getLogger(__name__) @@ -174,7 +182,6 @@ def add_interface_ngeo( permission: str | None = None, ) -> None: """Add the ngeo interfaces views and routes.""" - config.add_route(route_name, route, request_method="GET") # Permalink theme: recover the theme for generating custom viewer.js url config.add_route( @@ -202,7 +209,6 @@ def add_interface_canvas( permission: str | None = None, ) -> None: """Add the ngeo interfaces views and routes.""" - renderer = f"/etc/geomapfish/interfaces/{route_name}.html.mako" config.add_route(route_name, route, request_method="GET") # Permalink theme: recover the theme for generating custom viewer.js URL @@ -235,7 +241,6 @@ def add_interface_custom( permission: str | None = None, ) -> None: """Add custom interfaces views and routes.""" - config.add_route(route_name, route, request_method="GET") # Permalink theme: recover the theme for generating custom viewer.js URL config.add_route( @@ -259,7 +264,6 @@ def add_interface_custom( def add_admin_interface(config: pyramid.config.Configurator) -> None: """Add the administration interface views and routes.""" - assert c2cgeoportal_commons.models.DBSession is not None config.add_request_method( @@ -318,7 +322,6 @@ def is_allowed_url( Allowed if URL netloc is request host or is found in allowed hosts. """ - url_netloc = urllib.parse.urlparse(url).netloc return url_netloc, url_netloc == request.host or url_netloc in _get_netlocs(allowed_hosts) @@ -329,7 +332,6 @@ def is_allowed_host(request: pyramid.request.Request) -> bool: Allowed if URL netloc is request host or is found in allowed hosts. """ - return request.host in request.registry.settings.get("allowed_hosts", []) @@ -354,7 +356,7 @@ def is_valid_referrer(request: pyramid.request.Request, settings: dict[str, Any] def create_get_user_from_request( - settings: dict[str, Any] + settings: dict[str, Any], ) -> Callable[[pyramid.request.Request, str | None], Optional["static.User"]]: """Get the get_user_from_request function.""" @@ -370,8 +372,12 @@ def get_user_from_request( * it has been deactivated * the referrer is invalid """ - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel - from c2cgeoportal_commons.models.static import User # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) + from c2cgeoportal_commons.models.static import ( # pylint: disable=import-outside-toplevel + User, + ) assert DBSession is not None @@ -467,8 +473,12 @@ def default_user_validator(request: pyramid.request.Request, username: str, pass otherwise. """ del request # unused - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel - from c2cgeoportal_commons.models.static import User # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) + from c2cgeoportal_commons.models.static import ( # pylint: disable=import-outside-toplevel + User, + ) assert DBSession is not None @@ -930,7 +940,9 @@ def init_db_sessions( ) c2cgeoportal_commons.models.Base.metadata.clear() - from c2cgeoportal_commons.models import main # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + main, + ) if health_check is not None: for name, session in c2cgeoportal_commons.models.DBSessions.items(): diff --git a/geoportal/c2cgeoportal_geoportal/lib/__init__.py b/geoportal/c2cgeoportal_geoportal/lib/__init__.py index 3590649165..b531a7dbee 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/__init__.py +++ b/geoportal/c2cgeoportal_geoportal/lib/__init__.py @@ -38,10 +38,10 @@ import dateutil import pyramid.request import pyramid.response +from c2cgeoportal_commons.lib.url import get_url2 from pyramid.interfaces import IRoutePregenerator from zope.interface import implementer -from c2cgeoportal_commons.lib.url import get_url2 from c2cgeoportal_geoportal.lib.cacheversion import get_cache_version from c2cgeoportal_geoportal.lib.caching import get_region @@ -145,8 +145,12 @@ def get_setting(settings: Any, path: Iterable[str], default: Any = None) -> Any: @_CACHE_REGION_OBJ.cache_on_arguments() def get_ogc_server_wms_url_ids(request: pyramid.request.Request, host: str) -> dict[str, list[int]]: """Get the OGCServer ids mapped on the WMS URL.""" - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel - from c2cgeoportal_commons.models.main import OGCServer # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + OGCServer, + ) del host # used for cache assert DBSession is not None @@ -163,8 +167,12 @@ def get_ogc_server_wms_url_ids(request: pyramid.request.Request, host: str) -> d @_CACHE_REGION_OBJ.cache_on_arguments() def get_ogc_server_wfs_url_ids(request: pyramid.request.Request, host: str) -> dict[str, list[int]]: """Get the OGCServer ids mapped on the WFS URL.""" - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel - from c2cgeoportal_commons.models.main import OGCServer # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + OGCServer, + ) del host # used for cache assert DBSession is not None @@ -218,7 +226,10 @@ def _get_intranet_networks( @_CACHE_REGION.cache_on_arguments() def get_role_id(name: str) -> int: """Get the role ID.""" - from c2cgeoportal_commons.models import DBSession, main # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + main, + ) assert DBSession is not None @@ -250,7 +261,4 @@ def get_roles_name(request: pyramid.request.Request) -> pyramid.response.Respons def is_intranet(request: pyramid.request.Request) -> bool: """Get if it's an intranet user.""" address = ipaddress.ip_address(request.client_addr) - for network in _get_intranet_networks(request): - if address in network: - return True - return False + return any(address in network for network in _get_intranet_networks(request)) diff --git a/geoportal/c2cgeoportal_geoportal/lib/authentication.py b/geoportal/c2cgeoportal_geoportal/lib/authentication.py index b8feb9dd2e..e311524f3a 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/authentication.py +++ b/geoportal/c2cgeoportal_geoportal/lib/authentication.py @@ -67,7 +67,7 @@ def __init__( self.debug = debug def unauthenticated_userid(self, request: pyramid.request.Request) -> str | None: - if not request.method == "GET" or "auth" not in request.params: + if request.method != "GET" or "auth" not in request.params: return None auth_enc = request.params.get("auth") if auth_enc is None: diff --git a/geoportal/c2cgeoportal_geoportal/lib/caching.py b/geoportal/c2cgeoportal_geoportal/lib/caching.py index 826c5ef254..f887b7c728 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/caching.py +++ b/geoportal/c2cgeoportal_geoportal/lib/caching.py @@ -33,6 +33,7 @@ import pyramid.interfaces import zope.interface +from c2cgeoportal_commons.models import Base from dogpile.cache.api import NO_VALUE, CacheBackend, CachedValue, NoValue from dogpile.cache.backends.memory import MemoryBackend from dogpile.cache.backends.redis import RedisBackend, RedisSentinelBackend @@ -40,8 +41,6 @@ from dogpile.cache.util import sha1_mangle_key from sqlalchemy.orm.util import identity_key -from c2cgeoportal_commons.models import Base - if TYPE_CHECKING: from dogpile.cache.api import SerializedReturnType else: @@ -65,7 +64,6 @@ def keygen_function(namespace: Any, function: Callable[..., Any]) -> Callable[.. This is used by :meth:`.CacheRegion.cache_on_arguments` to generate a cache key from a decorated function. """ - if namespace is None: namespace = (function.__module__, function.__name__) else: @@ -140,7 +138,7 @@ def get(self, key: str) -> CachedValue | bytes | NoValue: assert isinstance(val, bytes) value = self._redis.deserializer(val) # type: ignore[misc] if value != NO_VALUE and self._use_memory_cache: - assert isinstance(value, (CachedValue, bytes)) + assert isinstance(value, CachedValue | bytes) self._memory.set(key, value) return value diff --git a/geoportal/c2cgeoportal_geoportal/lib/checker.py b/geoportal/c2cgeoportal_geoportal/lib/checker.py index 710ba551ae..9b9abdd5a0 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/checker.py +++ b/geoportal/c2cgeoportal_geoportal/lib/checker.py @@ -147,8 +147,8 @@ def check(_request: pyramid.request.Request, response: pyramid.response.Response health_check.add_url_check( name="checker_fulltextsearch", - url=lambda r: get_both(r)["url"], # type: ignore - headers=lambda r: get_both(r)["headers"], # type: ignore + url=lambda r: get_both(r)["url"], # type: ignore[misc] + headers=lambda r: get_both(r)["headers"], # type: ignore[misc] params={"query": fts_settings["search"], "limit": "1"}, check_cb=check, level=fts_settings["level"], @@ -156,8 +156,12 @@ def check(_request: pyramid.request.Request, response: pyramid.response.Response def _themes_errors(settings: dict[str, Any], health_check: c2cwsgiutils.health_check.HealthCheck) -> None: - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel - from c2cgeoportal_commons.models.main import Interface # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + Interface, + ) assert DBSession is not None @@ -256,7 +260,7 @@ def __call__(self, request: pyramid.request.Request) -> None: cmd: list[str] = ["check-example", url] env = dict(os.environ) for name, value in self.route.get("environment", {}).items(): - if isinstance(value, (list, dict)): + if isinstance(value, list | dict): value = json.dumps(value) elif not isinstance(value, str): value = str(value) diff --git a/geoportal/c2cgeoportal_geoportal/lib/common_headers.py b/geoportal/c2cgeoportal_geoportal/lib/common_headers.py index c80a59c271..3d276abfcc 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/common_headers.py +++ b/geoportal/c2cgeoportal_geoportal/lib/common_headers.py @@ -123,7 +123,6 @@ def _set_common_headers( content_type: str | None, ) -> pyramid.response.Response: """Set the common headers.""" - response.headers.update(service_headers_settings.get("headers", {})) if cache in (Cache.PRIVATE, Cache.PRIVATE_NO): diff --git a/geoportal/c2cgeoportal_geoportal/lib/dbreflection.py b/geoportal/c2cgeoportal_geoportal/lib/dbreflection.py index 6fcaaaa8b5..feb0c0b989 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/dbreflection.py +++ b/geoportal/c2cgeoportal_geoportal/lib/dbreflection.py @@ -76,7 +76,9 @@ def __get__( return getattr(target, self.value_attr) if target else None def __set__(self, obj: str, val: str) -> None: - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) assert DBSession is not None @@ -118,8 +120,10 @@ def get_table( engine = session.bind.engine metadata = MetaData() else: - from c2cgeoportal_commons.models import Base # pylint: disable=import-outside-toplevel - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + Base, + DBSession, + ) assert DBSession is not None assert DBSession.bind is not None @@ -162,7 +166,6 @@ def get_class( valid string. If there is no table identified by tablename in the database a NoSuchTableError SQLAlchemy exception is raised. """ - tablename, schema = _get_schema(tablename) table = get_table(tablename, schema, None, primary_key=primary_key) @@ -187,7 +190,9 @@ def _create_class( readonly_attributes: list[str] | None = None, pk_name: str | None = None, ) -> type: - from c2cgeoportal_commons.models import Base # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + Base, + ) exclude_properties = exclude_properties or () attributes = { @@ -201,7 +206,7 @@ def _create_class( # The randint is to fix the SAWarning: This declarative base already contains a class with the same # class name and module name cls = type( - f"{table.name.capitalize()}_{random.randint(0, 9999999)}", # nosec + f"{table.name.capitalize()}_{random.randint(0, 9999999)}", # noqa: S311 (GeoInterface, Base), attributes, ) diff --git a/geoportal/c2cgeoportal_geoportal/lib/filter_capabilities.py b/geoportal/c2cgeoportal_geoportal/lib/filter_capabilities.py index c1f2f566b8..ea5946a499 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/filter_capabilities.py +++ b/geoportal/c2cgeoportal_geoportal/lib/filter_capabilities.py @@ -32,25 +32,33 @@ import xml.sax.xmlreader # nosec from collections.abc import Callable from io import StringIO -from typing import Any, Union +from typing import Any from xml.sax.saxutils import XMLFilterBase, XMLGenerator # nosec import defusedxml.expatreader import pyramid.httpexceptions import pyramid.request import requests +from c2cgeoportal_commons.lib.url import Url from owslib.map.wms111 import ContentMetadata as ContentMetadata111 from owslib.map.wms130 import ContentMetadata as ContentMetadata130 from owslib.wms import WebMapService from pyramid.httpexceptions import HTTPBadGateway -from c2cgeoportal_commons.lib.url import Url -from c2cgeoportal_geoportal.lib import caching, get_ogc_server_wfs_url_ids, get_ogc_server_wms_url_ids -from c2cgeoportal_geoportal.lib.layers import get_private_layers, get_protected_layers, get_writable_layers +from c2cgeoportal_geoportal.lib import ( + caching, + get_ogc_server_wfs_url_ids, + get_ogc_server_wms_url_ids, +) +from c2cgeoportal_geoportal.lib.layers import ( + get_private_layers, + get_protected_layers, + get_writable_layers, +) _CACHE_REGION = caching.get_region("std") _LOG = logging.getLogger(__name__) -ContentMetadata = Union[ContentMetadata111, ContentMetadata130] +ContentMetadata = ContentMetadata111 | ContentMetadata130 @_CACHE_REGION.cache_on_arguments() @@ -111,7 +119,6 @@ def filter_capabilities( request: pyramid.request.Request, content: str, wms: bool, url: Url, headers: dict[str, str] ) -> str: """Filter the WMS/WFS capabilities.""" - wms_structure_ = wms_structure(request, url, headers.get("Host")) ogc_server_ids = ( @@ -153,7 +160,6 @@ def filter_capabilities( def filter_wfst_capabilities(content: str, wfs_url: Url, request: pyramid.request.Request) -> str: """Filter the WTS capabilities.""" - writable_layers: set[str] = set() ogc_server_ids = get_ogc_server_wfs_url_ids(request, request.host).get(wfs_url.url()) if ogc_server_ids is None: @@ -312,7 +318,7 @@ def _keep_layer(self, layer_name: str) -> bool: ) def characters(self, content: str) -> None: - if self.in_name and self.layers_path and not self.layers_path[-1].self_hidden is True: + if self.in_name and self.layers_path and self.layers_path[-1].self_hidden is not True: layer_name = normalize_typename(content) if self._keep_layer(layer_name): for layer in self.layers_path: @@ -342,9 +348,8 @@ def normalize_tag(tag: str) -> str: e.g. '{https://....}TypeName' -> 'TypeName' """ normalized = tag - if len(tag) >= 3: - if tag[0] == "{": - normalized = tag[1:].split("}")[1] + if len(tag) >= 3 and tag[0] == "{": + normalized = tag[1:].split("}")[1] return normalized.lower() diff --git a/geoportal/c2cgeoportal_geoportal/lib/functionality.py b/geoportal/c2cgeoportal_geoportal/lib/functionality.py index bcc2179f94..74563b3ae1 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/functionality.py +++ b/geoportal/c2cgeoportal_geoportal/lib/functionality.py @@ -30,9 +30,9 @@ from typing import Any, cast import pyramid.request +from c2cgeoportal_commons.models import main, static from sqlalchemy.orm import joinedload -from c2cgeoportal_commons.models import main, static from c2cgeoportal_geoportal.lib import get_typed, get_types_map, is_intranet from c2cgeoportal_geoportal.lib.caching import get_region @@ -43,7 +43,9 @@ @_CACHE_REGION_OBJ.cache_on_arguments() def _get_role(name: str) -> dict[str, Any]: - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) assert DBSession is not None diff --git a/geoportal/c2cgeoportal_geoportal/lib/layers.py b/geoportal/c2cgeoportal_geoportal/lib/layers.py index 99fdb22165..115a44fb79 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/layers.py +++ b/geoportal/c2cgeoportal_geoportal/lib/layers.py @@ -42,7 +42,10 @@ def _get_layers_query(request: Request, what: sqlalchemy.orm.Mapper[Any] | type[Any]) -> Query[Any]: - from c2cgeoportal_commons.models import DBSession, main # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + main, + ) assert DBSession is not None @@ -64,7 +67,9 @@ def get_protected_layers_query( Private layers but accessible to the user. """ - from c2cgeoportal_commons.models import main # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + main, + ) q = _get_layers_query(request, what) q = q.filter(main.Layer.public.is_(False)) @@ -76,7 +81,9 @@ def get_protected_layers_query( def get_writable_layers_query(request: Request, ogc_server_ids: Iterable[int]) -> Query["main.LayerWMS"]: """Get the writable layers query.""" - from c2cgeoportal_commons.models import main # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + main, + ) q = _get_layers_query(request, main.LayerWMS) return ( @@ -92,7 +99,10 @@ def get_protected_layers(request: Request, ogc_server_ids: Iterable[int]) -> dic Private layers but accessible to the user. """ - from c2cgeoportal_commons.models import DBSession, main # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + main, + ) assert DBSession is not None @@ -104,7 +114,9 @@ def get_protected_layers(request: Request, ogc_server_ids: Iterable[int]) -> dic def get_writable_layers(request: Request, ogc_server_ids: Iterable[int]) -> dict[int, "main.LayerWMS"]: """Get the writable layers.""" - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) assert DBSession is not None @@ -117,7 +129,10 @@ def get_writable_layers(request: Request, ogc_server_ids: Iterable[int]) -> dict @CACHE_REGION.cache_on_arguments() def get_private_layers(ogc_server_ids: Iterable[int]) -> dict[int, "main.LayerWMS"]: """Get the private layers.""" - from c2cgeoportal_commons.models import DBSession, main # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + main, + ) assert DBSession is not None diff --git a/geoportal/c2cgeoportal_geoportal/lib/lingva_extractor.py b/geoportal/c2cgeoportal_geoportal/lib/lingva_extractor.py index 253606f9fa..59a72a6210 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/lingva_extractor.py +++ b/geoportal/c2cgeoportal_geoportal/lib/lingva_extractor.py @@ -29,10 +29,10 @@ import json import os import re -import subprocess +import subprocess # nosec import traceback from collections.abc import Callable -from typing import TYPE_CHECKING, Any, Optional, cast +from typing import TYPE_CHECKING, Any, cast from xml.dom import Node from xml.parsers.expat import ExpatError @@ -42,6 +42,7 @@ import yaml from bottle import MakoTemplate, template from c2c.template.config import config +from c2cgeoportal_commons.lib.url import Url, get_url2 from defusedxml.minidom import parseString from geoalchemy2.types import Geometry from lingva.extractors import Extractor, Message @@ -54,13 +55,14 @@ from sqlalchemy.orm.util import class_mapper import c2cgeoportal_geoportal -from c2cgeoportal_commons.lib.url import Url, get_url2 from c2cgeoportal_geoportal.lib.bashcolor import Color, colorize from c2cgeoportal_geoportal.lib.caching import init_region from c2cgeoportal_geoportal.views.layers import Layers, get_layer_class if TYPE_CHECKING: - from c2cgeoportal_commons.models import main # pylint: disable=ungrouped-imports,useless-suppression + from c2cgeoportal_commons.models import ( + main, # pylint: disable=ungrouped-imports,useless-suppression + ) class LinguaExtractorException(Exception): @@ -76,7 +78,7 @@ def _get_config(key: str, default: str | None = None) -> str | None: """ request = pyramid.threadlocal.get_current_request() if request is not None: - return cast(Optional[str], request.params.get(key.lower(), default)) + return cast(str | None, request.params.get(key.lower(), default)) return os.environ.get(key, default) @@ -174,23 +176,23 @@ def __call__( int_filename = filename if re.match("^" + re.escape(f"./{self.config['package']}/templates"), filename): try: - empty_template = Template("") # nosec + empty_template = Template("") # noqa: S702 - class Lookup(TemplateLookup): # type: ignore + class Lookup(TemplateLookup): # type: ignore[misc] def get_template(self, uri: str) -> Template: del uri # unused return empty_template - class MyTemplate(MakoTemplate): # type: ignore + class MyTemplate(MakoTemplate): # type: ignore[misc] tpl = None def prepare(self, **kwargs: Any) -> None: options.update({"input_encoding": self.encoding}) lookup = Lookup(**kwargs) if self.source: - self.tpl = Template(self.source, lookup=lookup, **kwargs) # nosec + self.tpl = Template(self.source, lookup=lookup, **kwargs) # noqa: S702 else: - self.tpl = Template( # nosec + self.tpl = Template( # noqa: S702 uri=self.name, filename=self.filename, lookup=lookup, **kwargs ) @@ -252,10 +254,13 @@ def init_db(settings: dict[str, Any]) -> None: First test the connection, on when environment it should be OK, with the command line we should get an exception ind initialize the connection. """ - try: - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel - from c2cgeoportal_commons.models.main import Theme # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + Theme, + ) assert DBSession is not None @@ -302,7 +307,7 @@ def __call__( init_region({"backend": "dogpile.cache.memory"}, "obj") with open(filename, encoding="utf8") as config_file: - gmf_config = yaml.load(config_file, Loader=yaml.BaseLoader) # nosec + gmf_config = yaml.load(config_file, Loader=yaml.BaseLoader) # noqa: S506 # For application config (config.yaml) if "vars" in gmf_config: return self._collect_app_config(filename) @@ -332,7 +337,9 @@ def _collect_app_config(self, filename: str) -> list[Message]: DBSession, DBSessions, ) - from c2cgeoportal_commons.models.main import Metadata # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + Metadata, + ) assert DBSession is not None @@ -385,7 +392,6 @@ def _collect_app_config(self, filename: str) -> list[Message]: interface_config.get("constants", {}) .get("gmfDisplayQueryGridOptions", {}) .get("mergeTabs", {}) - .keys() ): location = ( f"interfaces_config/{interface}/constants/gmfDisplayQueryGridOptions/" @@ -479,7 +485,9 @@ def __call__( try: init_db(self.config) - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) assert DBSession is not None @@ -588,8 +596,12 @@ def _import( has_interfaces: bool = True, name_regex: str = ".*", ) -> None: - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel - from c2cgeoportal_commons.models.main import Interface # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + Interface, + ) assert DBSession is not None @@ -667,8 +679,12 @@ def _import_layer_wms(self, layer: "main.Layer", messages: list[str]) -> None: raise def _import_layer_wmts(self, layer: "main.Layer", messages: list[str]) -> None: - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel - from c2cgeoportal_commons.models.main import OGCServer # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + OGCServer, + ) assert DBSession is not None diff --git a/geoportal/c2cgeoportal_geoportal/lib/oauth2.py b/geoportal/c2cgeoportal_geoportal/lib/oauth2.py index a43daed74f..9becfd4aa2 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/oauth2.py +++ b/geoportal/c2cgeoportal_geoportal/lib/oauth2.py @@ -30,12 +30,12 @@ from typing import Any, TypedDict import basicauth +import c2cgeoportal_commons import oauthlib.common import oauthlib.oauth2 import pyramid.threadlocal from pyramid.httpexceptions import HTTPBadRequest -import c2cgeoportal_commons from c2cgeoportal_geoportal.lib.caching import get_region _LOG = logging.getLogger(__name__) @@ -73,9 +73,11 @@ def authenticate_client( both body and query can be obtained by direct attribute access, i.e. request.client_id for client_id in the URL query. - Keyword Arguments: - - request: oauthlib.common.Request + Arguments: + --------- + request: oauthlib.common.Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Returns: True or False @@ -86,6 +88,7 @@ def authenticate_client( - Refresh Token Grant .. _`HTTP Basic Authentication Scheme`: https://tools.ietf.org/html/rfc1945#section-11.1 + """ del args, kwargs @@ -117,7 +120,10 @@ def authenticate_client_id( _LOG.debug("authenticate_client_id %s", client_id) - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None @@ -163,9 +169,11 @@ def client_authentication_required( client credentials or whenever Client provided client authentication, see `Section 6`_ - Keyword Arguments: - - request: oauthlib.common.Request + Arguments: + --------- + request: oauthlib.common.Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Returns: True or False @@ -177,6 +185,7 @@ def client_authentication_required( .. _`Section 4.3.2`: https://tools.ietf.org/html/rfc6749#section-4.3.2 .. _`Section 4.1.3`: https://tools.ietf.org/html/rfc6749#section-4.1.3 .. _`Section 6`: https://tools.ietf.org/html/rfc6749#section-6 + """ del request, args, kwargs @@ -205,24 +214,30 @@ def confirm_redirect_uri( the client's allowed redirect URIs, but against the URI used when the code was saved. - Keyword Arguments: - - client_id: Unicode client identifier - code: Unicode authorization_code. - redirect_uri: Unicode absolute URI - client: Client object set by you, see authenticate_client. - request: The HTTP Request + Arguments: + --------- + client_id: Unicode client identifier + code: Unicode authorization_code. + redirect_uri: Unicode absolute URI + client: Client object set by you, see authenticate_client. + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Returns: True or False Method is used by: - Authorization Code Grant (during token request) + """ del args, kwargs _LOG.debug("confirm_redirect_uri %s %s", client_id, redirect_uri) - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None @@ -248,16 +263,19 @@ def get_default_redirect_uri( """ Get the default redirect URI for the client. - Keyword Arguments: - - client_id: Unicode client identifier - request: The HTTP Request + Arguments: + --------- + client_id: Unicode client identifier + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Returns: The default redirect URI for the client Method is used by: - Authorization Code Grant - Implicit Grant + """ del request, args, kwargs @@ -275,10 +293,12 @@ def get_default_scopes( """ Get the default scopes for the client. - Keyword Arguments: - - client_id: Unicode client identifier - request: The HTTP Request + Arguments: + --------- + client_id: Unicode client identifier + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Returns: List of default scopes @@ -287,6 +307,7 @@ def get_default_scopes( - Implicit Grant - Resource Owner Password Credentials Grant - Client Credentials grant + """ del request, args, kwargs @@ -304,15 +325,18 @@ def get_original_scopes( """ Get the list of scopes associated with the refresh token. - Keyword Arguments: - - refresh_token: Unicode refresh token - request: The HTTP Request + Arguments: + --------- + refresh_token: Unicode refresh token + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Returns: List of scopes. Method is used by: - Refresh token grant + """ del refresh_token, request, args, kwargs @@ -352,17 +376,20 @@ def introspect_token( efficiency, but must fallback to other types to be compliant with RFC. The dict of claims is added to request.token after this method. - Keyword Arguments: - - token: The token string. - token_type_hint: access_token or refresh_token. - request: OAuthlib request. + Arguments: + --------- + token: The token string. + token_type_hint: access_token or refresh_token. + request: OAuthlib request. + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by: - Introspect Endpoint (all grants are compatible) .. _`Introspect Claims`: https://tools.ietf.org/html/rfc7662#section-2.2 .. _`JWT Claims`: https://tools.ietf.org/html/rfc7519#section-4 + """ del token, request, args, kwargs @@ -381,20 +408,26 @@ def invalidate_authorization_code( """ Invalidate an authorization code after use. - Keyword Arguments: - - client_id: Unicode client identifier - code: The authorization code grant (request.code). - request: The HTTP Request + Arguments: + --------- + client_id: Unicode client identifier + code: The authorization code grant (request.code). + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by: - Authorization Code Grant + """ del args, kwargs _LOG.debug("invalidate_authorization_code %s", client_id) - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None @@ -426,14 +459,17 @@ def is_within_original_scope( used in situations where returning all valid scopes from the get_original_scopes is not practical. - Keyword Arguments: - - request_scopes: A list of scopes that were requested by client - refresh_token: Unicode refresh_token - request: The HTTP Request + Arguments: + --------- + request_scopes: A list of scopes that were requested by client + refresh_token: Unicode refresh_token + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by: - Refresh token grant + """ del request, args, kwargs @@ -452,14 +488,17 @@ def revoke_token( """ Revoke an access or refresh token. - Keyword Arguments: - - token: The token string. - token_type_hint: access_token or refresh_token. - request: The HTTP Request + Arguments: + --------- + token: The token string. + token_type_hint: access_token or refresh_token. + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by: - Revocation Endpoint + """ del token, request, args, kwargs @@ -475,12 +514,13 @@ def rotate_refresh_token(self, request: oauthlib.common.Request) -> bool: or replaced with a new one (rotated). Return True to rotate and and False for keeping original. - Keyword Arguments: - - request: oauthlib.common.Request + Arguments: + --------- + request: oauthlib.common.Request Method is used by: - Refresh Token Grant + """ del request @@ -515,11 +555,13 @@ def save_authorization_code( chose to send one. That value should be saved and used in 'validate_code'. - Keyword Arguments: - - client_id: Unicode client identifier - code: A dict of the authorization code grant and, optionally, state. - request: The HTTP Request + Arguments: + --------- + client_id: Unicode client identifier + code: A dict of the authorization code grant and, optionally, state. + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by: - Authorization Code Grant @@ -528,12 +570,16 @@ def save_authorization_code( Code Challenge (request.code_challenge) and Code Challenge Method (request.code_challenge_method) + """ del args, kwargs _LOG.debug("save_authorization_code %s", client_id) - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None @@ -604,11 +650,13 @@ def save_bearer_token( Note that while "scope" is a string-separated list of authorized scopes, the original list is still available in request.scopes - Keyword Arguments: - - client_id: Unicode client identifier - token: A Bearer token dict - request: The HTTP Request + Arguments: + --------- + client_id: Unicode client identifier + token: A Bearer token dict + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Returns: The default redirect URI for the client @@ -617,12 +665,16 @@ def save_bearer_token( - Implicit Grant - Resource Owner Password Credentials Grant (might not associate a client) - Client Credentials grant + """ del args, kwargs _LOG.debug("save_bearer_token") - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None @@ -654,11 +706,11 @@ def validate_bearer_token( """ Ensure the Bearer token is valid and authorized access to scopes. - Keyword Arguments: - - token: A string of random characters. - scopes: A list of scopes associated with the protected resource. - request: The HTTP Request + Arguments: + --------- + token: A string of random characters. + scopes: A list of scopes associated with the protected resource. + request: The HTTP Request A key to OAuth 2 security and restricting impact of leaked tokens is the short expiration time of tokens, *always ensure the token has not @@ -690,22 +742,25 @@ def validate_bearer_token( one provided for django these attributes will be made available in all protected views as keyword arguments. - Keyword Arguments: - - token: Unicode Bearer token - scopes: List of scopes (defined by you) - request: The HTTP Request + Arguments: + --------- + token: Unicode Bearer token + scopes: List of scopes (defined by you) + request: The HTTP Request Method is indirectly used by all core Bearer token issuing grant types: - Authorization Code Grant - Implicit Grant - Resource Owner Password Credentials Grant - Client Credentials Grant - """ + """ _LOG.debug("validate_bearer_token %s", scopes) - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None @@ -736,20 +791,26 @@ def validate_client_id( to set request.client to the client object associated with the given client_id. - Keyword Arguments: - - client_id: Unicode client identifier - request: oauthlib.common.Request + Arguments: + --------- + client_id: Unicode client identifier + request: oauthlib.common.Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by: - Authorization Code Grant - Implicit Grant + """ del args, kwargs _LOG.debug("validate_client_id") - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None @@ -784,21 +845,27 @@ def validate_code( associated with this authorization code. Similarly request.scopes must also be set. - Keyword Arguments: - - client_id: Unicode client identifier - code: Unicode authorization code - client: Client object set by you, see authenticate_client. - request: The HTTP Request + Arguments: + --------- + client_id: Unicode client identifier + code: Unicode authorization code + client: Client object set by you, see authenticate_client. + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by: - Authorization Code Grant + """ del args, kwargs _LOG.debug("validate_code %s", client_id) - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None @@ -839,18 +906,21 @@ def validate_grant_type( """ Ensure client is authorized to use the grant_type requested. - Keyword Arguments: - - client_id: Unicode client identifier - grant_type: Unicode grant type, i.e. authorization_code, password. - client: Client object set by you, see authenticate_client. - request: The HTTP Request + Arguments: + --------- + client_id: Unicode client identifier + grant_type: Unicode grant type, i.e. authorization_code, password. + client: Client object set by you, see authenticate_client. + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by: - Authorization Code Grant - Resource Owner Password Credentials Grant - Client Credentials Grant - Refresh Token Grant + """ del client, request, args, kwargs @@ -877,21 +947,27 @@ def validate_redirect_uri( All clients should register the absolute URIs of all URIs they intend to redirect to. The registration is outside of the scope of oauthlib. - Keyword Arguments: - - client_id: Unicode client identifier - redirect_uri: Unicode absolute URI - request: The HTTP Request + Arguments: + --------- + client_id: Unicode client identifier + redirect_uri: Unicode absolute URI + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by: - Authorization Code Grant - Implicit Grant + """ del request, args, kwargs _LOG.debug("validate_redirect_uri %s %s", client_id, redirect_uri) - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None @@ -918,22 +994,28 @@ def validate_refresh_token( OBS! The request.user attribute should be set to the resource owner associated with this refresh token. - Keyword Arguments: - - refresh_token: Unicode refresh token - client: Client object set by you, see authenticate_client. - request: The HTTP Request + Arguments: + --------- + refresh_token: Unicode refresh token + client: Client object set by you, see authenticate_client. + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by: - Authorization Code Grant (indirectly by issuing refresh tokens) - Resource Owner Password Credentials Grant (also indirectly) - Refresh Token Grant + """ del args, kwargs _LOG.debug("validate_refresh_token %s", client.client_id if client else None) - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None @@ -961,16 +1043,19 @@ def validate_response_type( """ Ensure client is authorized to use the response_type requested. - Keyword Arguments: - - client_id: Unicode client identifier - response_type: Unicode response type, i.e. code, token. - client: Client object set by you, see authenticate_client. - request: The HTTP Request + Arguments: + --------- + client_id: Unicode client identifier + response_type: Unicode response type, i.e. code, token. + client: Client object set by you, see authenticate_client. + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by: - Authorization Code Grant - Implicit Grant + """ del client, request, args, kwargs @@ -990,18 +1075,21 @@ def validate_scopes( """ Ensure the client is authorized access to requested scopes. - Keyword Arguments: - - client_id: Unicode client identifier - scopes: List of scopes (defined by you) - client: Client object set by you, see authenticate_client. - request: The HTTP Request + Arguments: + --------- + client_id: Unicode client identifier + scopes: List of scopes (defined by you) + client: Client object set by you, see authenticate_client. + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by all core grant types: - Authorization Code Grant - Implicit Grant - Resource Owner Password Credentials Grant - Client Credentials Grant + """ del client, request, args, kwargs @@ -1026,15 +1114,18 @@ def validate_user( not set you will be unable to associate a token with a user in the persistence method used (commonly, save_bearer_token). - Keyword Arguments: - - username: Unicode username - password: Unicode password - client: Client object set by you, see authenticate_client. - request: The HTTP Request + Argument: + --------- + username: Unicode username + password: Unicode password + client: Client object set by you, see authenticate_client. + request: The HTTP Request + args: Additional arguments, ignored + kwargs: Additional keyword arguments, ignored Method is used by: - Resource Owner Password Credentials Grant + """ del password, client, request, args, kwargs @@ -1056,10 +1147,10 @@ def is_pkce_required(self, client_id: int, request: oauthlib.common.Request) -> a technique to mitigate against the threat through the use of Proof Key for Code Exchange (PKCE, pronounced “pixy”). See RFC7636. - Keyword Arguments: - - client_id: Client identifier. - request (oauthlib.common.Request): OAuthlib request. + Arguments: + --------- + client_id: Client identifier. + request (oauthlib.common.Request): OAuthlib request. Method is used by: @@ -1068,7 +1159,10 @@ def is_pkce_required(self, client_id: int, request: oauthlib.common.Request) -> """ - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None @@ -1096,20 +1190,23 @@ def get_code_challenge(self, code: str, request: oauthlib.common.Request) -> str Return the code_challenge associated to the code. If None is returned, code is considered to not be associated to any challenges. - Keyword Arguments: - - code: Authorization code. - request: OAuthlib request. + Arguments: + --------- + code: Authorization code. + request: OAuthlib request. Return: - code_challenge string Method is used by: Authorization Code Grant - when PKCE is active + """ - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None @@ -1136,10 +1233,10 @@ def get_code_challenge_method(self, code: str, request: oauthlib.common.Request) Must return plain or S256. You can return a custom value if you have implemented your own AuthorizationCodeGrant class. - Keyword Arguments: - - code: Authorization code. - request: OAuthlib request. + Arguments: + --------- + code: Authorization code. + request: OAuthlib request. Return type: @@ -1148,8 +1245,12 @@ def get_code_challenge_method(self, code: str, request: oauthlib.common.Request) Method is used by: Authorization Code Grant - when PKCE is active + """ - from c2cgeoportal_commons.models import DBSession, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + static, + ) assert DBSession is not None diff --git a/geoportal/c2cgeoportal_geoportal/lib/oidc.py b/geoportal/c2cgeoportal_geoportal/lib/oidc.py index 083aa2b116..71964b445c 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/oidc.py +++ b/geoportal/c2cgeoportal_geoportal/lib/oidc.py @@ -34,7 +34,11 @@ import pyramid.response import simple_openid_connect.client import simple_openid_connect.data -from pyramid.httpexceptions import HTTPBadRequest, HTTPInternalServerError, HTTPUnauthorized +from pyramid.httpexceptions import ( + HTTPBadRequest, + HTTPInternalServerError, + HTTPUnauthorized, +) from pyramid.security import remember from c2cgeoportal_geoportal.lib.caching import get_region @@ -48,9 +52,7 @@ # User create on demand class DynamicUser(NamedTuple): - """ - User created dynamically. - """ + """User created dynamically.""" id: int username: str @@ -62,10 +64,7 @@ class DynamicUser(NamedTuple): @_CACHE_REGION_OBJ.cache_on_arguments() def get_oidc_client(request: pyramid.request.Request, host: str) -> simple_openid_connect.client.OpenidClient: - """ - Get the OpenID Connect client from the request settings. - """ - + """Get the OpenID Connect client from the request settings.""" del host # used for cache key authentication_settings = request.registry.settings.get("authentication", {}) @@ -83,9 +82,7 @@ def get_oidc_client(request: pyramid.request.Request, host: str) -> simple_openi class OidcRememberObject(TypedDict): - """ - The JSON object that is stored in a cookie to remember the user. - """ + """The JSON object that is stored in a cookie to remember the user.""" access_token: str access_token_expires: str @@ -146,11 +143,13 @@ def get_user_from_remember( :param settings: The OpenID Connect configuration. :param update_create_user: If the user should be updated or created if it does not exist. """ - # Those imports are here to avoid initializing the models module before the database schema are # correctly initialized. from c2cgeoportal_commons import models # pylint: disable=import-outside-toplevel - from c2cgeoportal_commons.models import main, static # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + main, + static, + ) assert models.DBSession is not None @@ -213,9 +212,7 @@ def get_user_from_remember( class OidcRemember: - """ - Build the abject that we want to remember in the cookie. - """ + """Build the abject that we want to remember in the cookie.""" def __init__(self, request: pyramid.request.Request): self.request = request @@ -229,10 +226,7 @@ def remember( ), host: str, ) -> OidcRememberObject: - """ - Remember the user in the cookie. - """ - + """Remember the user in the cookie.""" del host # Used for cache key if isinstance(token_response, simple_openid_connect.data.TokenErrorResponse): @@ -297,8 +291,6 @@ def remember( def includeme(config: pyramid.config.Configurator) -> None: - """ - Pyramid includeme function. - """ + """Pyramid includeme function.""" config.add_request_method(get_remember_from_user_info, name="get_remember_from_user_info") config.add_request_method(get_user_from_remember, name="get_user_from_remember") diff --git a/geoportal/c2cgeoportal_geoportal/lib/wmstparsing.py b/geoportal/c2cgeoportal_geoportal/lib/wmstparsing.py index c6d4c1d7f1..6376d63d58 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/wmstparsing.py +++ b/geoportal/c2cgeoportal_geoportal/lib/wmstparsing.py @@ -57,10 +57,10 @@ class TimeInformation: Collect the WMS time information. Arguments: - extent: A time extent instance (``TimeExtentValue`` or ``TimeExtentInterval``) mode: The layer mode ("single", "range" or "disabled") widget: The layer mode ("slider" (default) or "datepicker") + """ def __init__(self) -> None: @@ -98,7 +98,7 @@ def merge_mode(self, mode: str) -> None: self.mode = mode def merge_widget(self, widget: str | None) -> None: - widget = "slider" if not widget else widget + widget = widget if widget else "slider" assert widget is not None if self.widget is not None: @@ -134,11 +134,11 @@ def __init__( Initialize. Arguments: - values: A set() of datetime resolution: The resolution from the mapfile time definition min_def_value: the minimum default value as a datetime max_def_value: the maximum default value as a datetime + """ self.values = values self.resolution = resolution @@ -183,13 +183,13 @@ def __init__( Initialize. Arguments: - start: The start value as a datetime end: The end value as a datetime interval: The interval as a tuple (years, months, days, seconds) resolution: The resolution from the mapfile time definition min_def_value: the minimum default value as a datetime max_def_value: the maximum default value as a datetime + """ self.start = start self.end = end @@ -311,7 +311,7 @@ def _parse_date(date: str) -> tuple[str, datetime.datetime]: try: dt = datetime.datetime.strptime(date, pattern) return resolution, dt.replace(tzinfo=isodate.UTC) - except Exception: # pylint: disable=broad-exception-caught # nosec + except Exception: # pylint: disable=broad-exception-caught pass try: diff --git a/geoportal/c2cgeoportal_geoportal/lib/xsd.py b/geoportal/c2cgeoportal_geoportal/lib/xsd.py index 96611f793e..6c2a8e6104 100644 --- a/geoportal/c2cgeoportal_geoportal/lib/xsd.py +++ b/geoportal/c2cgeoportal_geoportal/lib/xsd.py @@ -42,10 +42,8 @@ def _element_callback(tb: str, column: sqlalchemy.sql.elements.NamedColumn[Any]) -> None: if column.info.get("readonly"): - with tag(tb, "xsd:annotation"): - with tag(tb, "xsd:appinfo"): - with tag(tb, "readonly", {"value": "true"}): - pass + with tag(tb, "xsd:annotation"), tag(tb, "xsd:appinfo"), tag(tb, "readonly", {"value": "true"}): + pass class XSDGenerator(PapyrusXSDGenerator): # type: ignore @@ -105,7 +103,9 @@ def add_column_property_xsd(self, tb: str, column_property: ColumnProperty[Any]) super().add_column_property_xsd(tb, column_property) def add_association_proxy_xsd(self, tb: str, column_property: ColumnProperty[Any]) -> None: - from c2cgeoportal_commons.models import DBSession # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models import ( # pylint: disable=import-outside-toplevel + DBSession, + ) assert DBSession is not None @@ -125,11 +125,13 @@ def add_association_proxy_xsd(self, tb: str, column_property: ColumnProperty[Any attrs["nillable"] = "true" attrs["name"] = proxy with tag(tb, "xsd:element", attrs) as tb2: - with tag(tb2, "xsd:simpleType") as tb3: - with tag(tb3, "xsd:restriction", {"base": "xsd:string"}) as tb4: - for (value,) in query: - with tag(tb4, "xsd:enumeration", {"value": value}): - pass + with ( + tag(tb2, "xsd:simpleType") as tb3, + tag(tb3, "xsd:restriction", {"base": "xsd:string"}) as tb4, + ): + for (value,) in query: + with tag(tb4, "xsd:enumeration", {"value": value}): + pass self.element_callback(tb4, column) diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml b/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml index 1123c6f3a0..fb7d19ccf0 100644 --- a/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml +++ b/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/.prospector.yaml @@ -1,30 +1,3 @@ inherits: - duplicated - - utils:no-design-checks - -max-line-length: 110 - -pycodestyle: - options: - max-line-length: 110 - disable: - - E203 - -pylint: - options: - max-line-length: 110 - disable: - - too-many-locals - - too-many-statements - - too-many-branches - - c-extension-no-member - -pyflakes: - disable: - - F401 - -mccabe: - run: false - -bandit: - run: true + - utils:base-less-strict diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py b/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py index 4e5dc9fffd..7d5ae13b42 100644 --- a/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +++ b/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py @@ -109,7 +109,6 @@ def on_starting(server: gunicorn.arbiter.Arbiter) -> None: Called just before the master process is initialized. """ - del server prometheus.start() @@ -121,7 +120,6 @@ def post_fork(server: gunicorn.arbiter.Arbiter, worker: gunicorn.workers.base.Wo Called just after a worker has been forked. """ - del server, worker prometheus.cleanup() @@ -133,7 +131,6 @@ def child_exit(server: gunicorn.arbiter.Arbiter, worker: gunicorn.workers.base.W Called just after a worker has been exited, in the master process. """ - del server multiprocess.mark_process_dead(worker.pid) diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py b/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py index b101439948..98cec23448 100644 --- a/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py +++ b/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/authentication.py @@ -1,8 +1,7 @@ +from c2cgeoportal_geoportal.lib.authentication import create_authentication from pyramid.authorization import ACLAuthorizationPolicy from pyramid.config import Configurator -from c2cgeoportal_geoportal.lib.authentication import create_authentication - def includeme(config: Configurator) -> None: """Initialize the authentication( for a Pyramid app.""" diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py b/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py index 1882e6726a..18e8462241 100644 --- a/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py +++ b/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/models.py @@ -1,8 +1,7 @@ import logging -from pyramid.i18n import TranslationStringFactory - from c2cgeoportal_commons.models.main import * # noqa: ignore=F401, pylint: disable=unused-wildcard-import +from pyramid.i18n import TranslationStringFactory _ = TranslationStringFactory("{{cookiecutter.package}}_geoportal-server") _LOG = logging.getLogger(__name__) diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py b/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py index 25d9664418..2be921b74e 100644 --- a/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py +++ b/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/{{cookiecutter.package}}_geoportal/multi_organization.py @@ -3,5 +3,4 @@ def includeme(config: Configurator) -> None: """Initialize the multi organization.""" - del config # Unused diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/pyproject.toml b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/pyproject.toml index f1c3219848..354842adf7 100644 --- a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/pyproject.toml +++ b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/pyproject.toml @@ -1,7 +1,2 @@ -[tool.black] -line-length = 110 -target-version = ['py39'] - -[tool.isort] -profile = "black" -line_length = 110 +[tool.ruff] +target-version = 'py310' diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py b/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py index 2440797b94..b1d34188d6 100644 --- a/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py +++ b/geoportal/c2cgeoportal_geoportal/scaffolds/update/{{cookiecutter.project}}/CONST_create_template/tests/test_testapp.py @@ -41,7 +41,7 @@ def test_desktop_alt(url: str) -> None: def test_enum() -> None: - """Test the enumerations view""" + """Test the enumerations view.""" response = requests.get("https://front/layers/test/values/type", verify=False, timeout=30) # nosec assert response.status_code == 200, response.text diff --git a/geoportal/c2cgeoportal_geoportal/scripts/c2cupgrade.py b/geoportal/c2cgeoportal_geoportal/scripts/c2cupgrade.py index f9d2305ca3..8d8080f011 100644 --- a/geoportal/c2cgeoportal_geoportal/scripts/c2cupgrade.py +++ b/geoportal/c2cgeoportal_geoportal/scripts/c2cupgrade.py @@ -32,13 +32,14 @@ import os import re import shutil -import subprocess +import subprocess # nosec import sys +import tempfile from argparse import ArgumentParser, Namespace from collections.abc import Callable from json.decoder import JSONDecodeError -from subprocess import call, check_call, check_output -from typing import Any, Union, cast +from subprocess import call, check_call, check_output # nosec +from typing import Any, cast import pkg_resources import requests @@ -61,51 +62,10 @@ def fix_style() -> None: """Fix the style of all the project files using isort, Black and Prettier.""" - - file_to_clean = [] - for filename, content in ( - (".prettierignore", "*.min.js\n"), - ("pyproject.toml", "[tool.black]\nline-length = 110\ntarget-version = ['py39']\n"), - (".prettierrc.yaml", "bracketSpacing: false\nquoteProps: preserve\n"), - ( - ".editorconfig", - """root = true -[*] -max_line_length = 110 -""", - ), - ): - if not os.path.exists(filename): - file_to_clean.append(filename) - if os.path.exists(os.path.join("CONST_create_template", filename)): - shutil.copyfile(os.path.join("CONST_create_template", filename), filename) - else: - with open(filename, "w", encoding="utf8") as file_: - file_.write(content) - - if os.path.exists("ci/config.yaml"): - os.rename("ci/config.yaml", "ci/config.yaml_") if os.path.exists(".pre-commit-config.yaml"): print("Run pre-commit to fix the style.") sys.stdout.flush() subprocess.run(["pre-commit", "run", "--all-files"]) # pylint: disable=subprocess-run-check - else: - print("Run c2cciutils-checks to fix the style.") - sys.stdout.flush() - subprocess.run( # pylint: disable=subprocess-run-check - ["c2cciutils-checks", "--fix", "--check=isort"] - ) - subprocess.run( # pylint: disable=subprocess-run-check - ["c2cciutils-checks", "--fix", "--check=black"] - ) - subprocess.run( # pylint: disable=subprocess-run-check - ["c2cciutils-checks", "--fix", "--check=prettier"] - ) - if os.path.exists("ci/config.yaml_"): - os.rename("ci/config.yaml_", "ci/config.yaml") - - for filename in file_to_clean: - os.remove(filename) def main() -> None: @@ -217,7 +177,7 @@ def get_upgrade(section: str) -> list[Any] | dict[str, Any]: sys.exit(1) with open(".upgrade.yaml", encoding="utf8") as project_file: - return cast(Union[list[Any], dict[str, Any]], yaml.safe_load(project_file)[section]) + return cast(list[Any] | dict[str, Any], yaml.safe_load(project_file)[section]) def print_step( self, @@ -254,7 +214,7 @@ def test_checkers(self) -> tuple[bool, str | None]: resp = requests.get( self.project["checker_url"], headers=self.project.get("checker_headers"), - verify=False, # nosec + verify=False, # noqa: S501 timeout=120, ) except requests.exceptions.ConnectionError as exception: @@ -324,12 +284,13 @@ def step0(self, step: int) -> None: @Step(1) def step1(self, step: int) -> None: - shutil.copyfile("project.yaml", "/tmp/project.yaml") - try: - check_call(["git", "reset", "--hard"]) - check_call(["git", "clean", "--force", "-d"]) - finally: - shutil.copyfile("/tmp/project.yaml", "project.yaml") + with tempfile.NamedTemporaryFile("w") as project_temp_file: + shutil.copyfile("project.yaml", project_temp_file) + try: + check_call(["git", "reset", "--hard"]) + check_call(["git", "clean", "--force", "-d"]) + finally: + shutil.copyfile(project_temp_file, "project.yaml") self.run_step(step + 1) @@ -343,36 +304,36 @@ def step2(self, step: int) -> None: @Step(3) def step3(self, step: int) -> None: - project_path = os.path.join("/tmp", self.project["project_folder"]) - os.mkdir(project_path) - shutil.copyfile("/src/project.yaml", os.path.join(project_path, "project.yaml")) - check_call( - [ - "pcreate", - "--overwrite", - "--scaffold=update", - project_path, - ] - ) - if self.get_project().get("advance", False): + with tempfile.TemporaryDirectory() as temp_directory_name: + project_path = os.path.join(temp_directory_name, self.project["project_folder"]) + os.mkdir(project_path) + shutil.copyfile("/src/project.yaml", os.path.join(project_path, "project.yaml")) check_call( [ "pcreate", "--overwrite", - "--scaffold=advance_update", + "--scaffold=update", project_path, ] ) + if self.get_project().get("advance", False): + check_call( + [ + "pcreate", + "--overwrite", + "--scaffold=advance_update", + project_path, + ] + ) - shutil.copyfile(os.path.join(project_path, ".upgrade.yaml"), ".upgrade.yaml") - for upgrade_file in cast(list[dict[str, Any]], self.get_upgrade("upgrade_files")): - action = upgrade_file["action"] - if action == "remove": - self.files_to_remove(upgrade_file, prefix="CONST_create_template", force=True) - if action == "move": - self.files_to_move(upgrade_file, prefix="CONST_create_template", force=True) + shutil.copyfile(os.path.join(project_path, ".upgrade.yaml"), ".upgrade.yaml") + for upgrade_file in cast(list[dict[str, Any]], self.get_upgrade("upgrade_files")): + action = upgrade_file["action"] + if action == "remove": + self.files_to_remove(upgrade_file, prefix="CONST_create_template", force=True) + if action == "move": + self.files_to_move(upgrade_file, prefix="CONST_create_template", force=True) - shutil.rmtree(project_path) os.remove(".upgrade.yaml") check_call(["git", "add", "--all", "--force", "CONST_create_template/"]) @@ -385,26 +346,26 @@ def step4(self, step: int) -> None: if os.path.exists("CONST_create_template"): check_call(["git", "rm", "-r", "--force", "CONST_create_template/"]) - project_path = os.path.join("/tmp", self.project["project_folder"]) - check_call(["ln", "-s", "/src", project_path]) - check_call( - [ - "pcreate", - "--overwrite", - "--scaffold=update", - project_path, - ] - ) - if self.get_project().get("advance", False): + with tempfile.TemporaryDirectory() as temp_directory_name: + project_path = os.path.join(temp_directory_name, self.project["project_folder"]) + check_call(["ln", "-s", "/src", project_path]) check_call( [ "pcreate", "--overwrite", - "--scaffold=advance_update", + "--scaffold=update", project_path, ] ) - os.remove(project_path) + if self.get_project().get("advance", False): + check_call( + [ + "pcreate", + "--overwrite", + "--scaffold=advance_update", + project_path, + ] + ) check_call(["git", "add", "--all", "CONST_create_template/"]) @@ -644,10 +605,7 @@ def is_managed(self, file_: str, files_to_get: bool = False) -> bool: if not managed: for files in self.project["managed_files"]: - if isinstance(files, str): - pattern = files - else: - pattern = files["pattern"] + pattern = files if isinstance(files, str) else files["pattern"] if re.match(pattern + "$", file_): print(f"File '{file_}' included by project config pattern `managed_files` '{pattern}'.") print("managed", file_, pattern) diff --git a/geoportal/c2cgeoportal_geoportal/scripts/manage_users.py b/geoportal/c2cgeoportal_geoportal/scripts/manage_users.py index 9890e2ed82..de184a6b93 100644 --- a/geoportal/c2cgeoportal_geoportal/scripts/manage_users.py +++ b/geoportal/c2cgeoportal_geoportal/scripts/manage_users.py @@ -37,7 +37,6 @@ def get_argparser() -> argparse.ArgumentParser: """Get the argument parser for this script.""" - usage = """Reset a user password. The username is used as password if the password is not provided with the corresponding option. User can be created if it does not exist yet.""" @@ -66,7 +65,6 @@ def main() -> None: to get the options list, do: docker compose exec geoportal manage-users --help """ - parser = get_argparser() options = parser.parse_args() username = options.user @@ -76,8 +74,12 @@ def main() -> None: session = get_session(settings, transaction.manager) # Must be done only once we have loaded the project config - from c2cgeoportal_commons.models.main import Role # pylint: disable=import-outside-toplevel - from c2cgeoportal_commons.models.static import User # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + Role, + ) + from c2cgeoportal_commons.models.static import ( # pylint: disable=import-outside-toplevel + User, + ) print("\n") diff --git a/geoportal/c2cgeoportal_geoportal/scripts/pcreate.py b/geoportal/c2cgeoportal_geoportal/scripts/pcreate.py index c292b002d9..978475a448 100644 --- a/geoportal/c2cgeoportal_geoportal/scripts/pcreate.py +++ b/geoportal/c2cgeoportal_geoportal/scripts/pcreate.py @@ -29,10 +29,10 @@ import json import os import re -import subprocess +import subprocess # nosec import sys from argparse import ArgumentParser -from typing import Any, Union, cast +from typing import Any, cast import pkg_resources import requests @@ -46,7 +46,6 @@ def get_argparser() -> ArgumentParser: """Get the argument parser for this script.""" - parser = ArgumentParser( prog=sys.argv[0], add_help=True, @@ -183,9 +182,7 @@ def get_context(self) -> dict[str, str | int]: } context.update(self.read_project_file()) if os.environ.get("CI") == "true": - context["authtkt_secret"] = ( # nosec - "io7heoDui8xaikie1rushaeGeiph8Bequei6ohchaequob6viejei0xooWeuvohf" - ) + context["authtkt_secret"] = "io7heoDui8xaikie1rushaeGeiph8Bequei6ohchaequob6viejei0xooWeuvohf" # noqa: S105 self.get_var(context, "srid", "Spatial Reference System Identifier (e.g. 2056): ", int) srid = cast(int, context["srid"]) @@ -246,7 +243,7 @@ def read_project_file(self) -> dict[str, str | int]: if os.path.exists(project_file): with open(project_file, encoding="utf8") as f: project = yaml.safe_load(f) - return cast(dict[str, Union[str, int]], project.get("template_vars", {})) + return cast(dict[str, str | int], project.get("template_vars", {})) else: return {} @@ -280,16 +277,12 @@ def epsg2bbox(srid: int) -> list[str] | None: r = requests.get(f"https://epsg.io/?format=json&q={srid}", timeout=60) bbox = r.json()["results"][0]["bbox"] r = requests.get( - "https://epsg.io/trans?s_srs=4326&t_srs={srid}&data={bbox[1]},{bbox[0]}".format( - srid=srid, bbox=bbox - ), + f"https://epsg.io/trans?s_srs=4326&t_srs={srid}&data={bbox[1]},{bbox[0]}", timeout=60, ) r1 = r.json()[0] r = requests.get( - "https://epsg.io/trans?s_srs=4326&t_srs={srid}&data={bbox[3]},{bbox[2]}".format( - srid=srid, bbox=bbox - ), + f"https://epsg.io/trans?s_srs=4326&t_srs={srid}&data={bbox[3]},{bbox[2]}", timeout=60, ) r2 = r.json()[0] diff --git a/geoportal/c2cgeoportal_geoportal/scripts/theme2fts.py b/geoportal/c2cgeoportal_geoportal/scripts/theme2fts.py index 3256786eff..04c8951f99 100644 --- a/geoportal/c2cgeoportal_geoportal/scripts/theme2fts.py +++ b/geoportal/c2cgeoportal_geoportal/scripts/theme2fts.py @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2023, Camptocamp SA +# Copyright (c) 2014-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -95,7 +95,6 @@ def get_argparser() -> ArgumentParser: def main() -> None: """Run the command.""" - options = get_argparser().parse_args() settings = get_appsettings(options) @@ -180,7 +179,9 @@ def _add_fts( action: str, role: Optional["c2cgeoportal_commons.models.main.Role"], ) -> None: - from c2cgeoportal_commons.models.main import FullTextSearch # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + FullTextSearch, + ) key = ( item.name if self.options.name else item.id, @@ -248,7 +249,9 @@ def _add_group( export: bool, role: Optional["c2cgeoportal_commons.models.main.Role"], ) -> bool: - from c2cgeoportal_commons.models.main import LayerGroup # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + LayerGroup, + ) fill = False for child in group.children: diff --git a/geoportal/c2cgeoportal_geoportal/scripts/urllogin.py b/geoportal/c2cgeoportal_geoportal/scripts/urllogin.py index bedf0e6153..a1fb840214 100644 --- a/geoportal/c2cgeoportal_geoportal/scripts/urllogin.py +++ b/geoportal/c2cgeoportal_geoportal/scripts/urllogin.py @@ -56,7 +56,6 @@ def create_token(aeskey: str, user: str, password: str, valid: bool) -> str: def get_argparser() -> argparse.ArgumentParser: """Get the argument parser for this script.""" - parser = argparse.ArgumentParser(description="Generate an auth token") fill_arguments(parser, use_attribute=True) parser.add_argument("user", help="The username") @@ -67,7 +66,6 @@ def get_argparser() -> argparse.ArgumentParser: def main() -> None: """Run the command.""" - args = get_argparser().parse_args() settings = get_appsettings(args) urllogin = settings.get("urllogin", {}) diff --git a/geoportal/c2cgeoportal_geoportal/views/dev.py b/geoportal/c2cgeoportal_geoportal/views/dev.py index dc11dad84f..d61f78bef7 100644 --- a/geoportal/c2cgeoportal_geoportal/views/dev.py +++ b/geoportal/c2cgeoportal_geoportal/views/dev.py @@ -1,4 +1,4 @@ -# Copyright (c) 2011-2021, Camptocamp SA +# Copyright (c) 2011-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -31,10 +31,10 @@ import pyramid.request import pyramid.response +from c2cgeoportal_commons.lib.url import Url from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config -from c2cgeoportal_commons.lib.url import Url from c2cgeoportal_geoportal.views.proxy import Proxy logger = logging.getLogger(__name__) diff --git a/geoportal/c2cgeoportal_geoportal/views/dynamic.py b/geoportal/c2cgeoportal_geoportal/views/dynamic.py index 7441ab71ab..b12813a7a2 100644 --- a/geoportal/c2cgeoportal_geoportal/views/dynamic.py +++ b/geoportal/c2cgeoportal_geoportal/views/dynamic.py @@ -31,12 +31,12 @@ from typing import Any, cast import pyramid.request +from c2cgeoportal_commons import models +from c2cgeoportal_commons.models import main from pyramid.httpexceptions import HTTPNotFound from pyramid.view import view_config from sqlalchemy import func -from c2cgeoportal_commons import models -from c2cgeoportal_commons.models import main from c2cgeoportal_geoportal import is_allowed_host from c2cgeoportal_geoportal.lib.cacheversion import get_cache_version from c2cgeoportal_geoportal.lib.caching import get_region @@ -78,13 +78,12 @@ def _interface( Get the interface configuration. Arguments: - interface_config: Current interface configuration interface_name: Interface name (we use in the configuration) original_interface_name: Original interface name (directly for the query string) dynamic: The values that's dynamically generated - """ + """ if "extends" in interface_config: constants = self._interface( self.interfaces_config[interface_config["extends"]], diff --git a/geoportal/c2cgeoportal_geoportal/views/entry.py b/geoportal/c2cgeoportal_geoportal/views/entry.py index ec566170c3..444008d203 100644 --- a/geoportal/c2cgeoportal_geoportal/views/entry.py +++ b/geoportal/c2cgeoportal_geoportal/views/entry.py @@ -114,7 +114,6 @@ def _get_ngeo_resources(pattern: str) -> list[str]: def canvas_view(request: pyramid.request.Request, interface_config: dict[str, Any]) -> dict[str, Any]: """Get view used as entry point of a canvas interface.""" - js_files = _get_ngeo_resources(f"{interface_config.get('layout', interface_config['name'])}*.js") css_files = _get_ngeo_resources(f"{interface_config.get('layout', interface_config['name'])}*.css") css = "\n ".join( @@ -149,7 +148,6 @@ def custom_view( request: pyramid.request.Request, interface_config: dict[str, Any] ) -> pyramid.response.Response: """Get view used as entry point of a canvas interface.""" - set_common_headers(request, "index", Cache.PUBLIC_NO, content_type="text/html") html_filename = interface_config.get("html_filename", f"{interface_config['name']}.html") diff --git a/geoportal/c2cgeoportal_geoportal/views/fulltextsearch.py b/geoportal/c2cgeoportal_geoportal/views/fulltextsearch.py index d2f1f8cbd1..ee041b8e21 100644 --- a/geoportal/c2cgeoportal_geoportal/views/fulltextsearch.py +++ b/geoportal/c2cgeoportal_geoportal/views/fulltextsearch.py @@ -29,14 +29,14 @@ import re import pyramid.request +from c2cgeoportal_commons.models import DBSession +from c2cgeoportal_commons.models.main import FullTextSearch, Interface from geoalchemy2.shape import to_shape from geojson import Feature, FeatureCollection from pyramid.httpexceptions import HTTPBadRequest, HTTPInternalServerError from pyramid.view import view_config from sqlalchemy import ColumnElement, and_, desc, func, or_ -from c2cgeoportal_commons.models import DBSession -from c2cgeoportal_commons.models.main import FullTextSearch, Interface from c2cgeoportal_geoportal import locale_negotiator from c2cgeoportal_geoportal.lib.caching import get_region from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers diff --git a/geoportal/c2cgeoportal_geoportal/views/geometry_processing.py b/geoportal/c2cgeoportal_geoportal/views/geometry_processing.py index 47f66abdc7..de7620570d 100644 --- a/geoportal/c2cgeoportal_geoportal/views/geometry_processing.py +++ b/geoportal/c2cgeoportal_geoportal/views/geometry_processing.py @@ -27,6 +27,7 @@ import pyramid.request +from c2cgeoportal_commons.models import DBSession from geoalchemy2.shape import from_shape, to_shape from geojson import loads from pyramid.httpexceptions import HTTPBadRequest @@ -35,8 +36,6 @@ from shapely.geometry.base import BaseGeometry from sqlalchemy import func -from c2cgeoportal_commons.models import DBSession - class GeometryProcessing: """ diff --git a/geoportal/c2cgeoportal_geoportal/views/i18n.py b/geoportal/c2cgeoportal_geoportal/views/i18n.py index aa21d76eed..5e7cdb1e78 100644 --- a/geoportal/c2cgeoportal_geoportal/views/i18n.py +++ b/geoportal/c2cgeoportal_geoportal/views/i18n.py @@ -68,7 +68,6 @@ def locale(request: pyramid.request.Request) -> pyramid.response.Response: @view_config(route_name="localepot") # type: ignore def localepot(request: pyramid.request.Request) -> pyramid.response.Response: """Get the pot from an HTTP request.""" - # Build the list of files to be processed sources = [] sources += glob.glob(f"/app/{request.registry.package_name}/static-ngeo/js/apps/*.html.ejs") diff --git a/geoportal/c2cgeoportal_geoportal/views/layers.py b/geoportal/c2cgeoportal_geoportal/views/layers.py index 491d2a8e14..13328badbe 100644 --- a/geoportal/c2cgeoportal_geoportal/views/layers.py +++ b/geoportal/c2cgeoportal_geoportal/views/layers.py @@ -40,6 +40,7 @@ import sqlalchemy.ext.declarative import sqlalchemy.orm import sqlalchemy.orm.query +from c2cgeoportal_commons import models from geoalchemy2 import Geometry from geoalchemy2.shape import from_shape, to_shape from geojson.feature import Feature, FeatureCollection @@ -56,19 +57,27 @@ from shapely import unary_union from shapely.errors import TopologicalError from sqlalchemy import Enum, Numeric, String, Text, Unicode, UnicodeText, exc, func -from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound # type: ignore[attr-defined] +from sqlalchemy.orm.exc import ( # type: ignore[attr-defined] + MultipleResultsFound, + NoResultFound, +) from sqlalchemy.orm.properties import ColumnProperty from sqlalchemy.orm.util import class_mapper from sqlalchemy.sql import and_, or_ -from c2cgeoportal_commons import models from c2cgeoportal_geoportal.lib import get_roles_id from c2cgeoportal_geoportal.lib.caching import get_region from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers -from c2cgeoportal_geoportal.lib.dbreflection import _AssociationProxy, get_class, get_table +from c2cgeoportal_geoportal.lib.dbreflection import ( + _AssociationProxy, + get_class, + get_table, +) if TYPE_CHECKING: - from c2cgeoportal_commons.models import main # pylint: disable=ungrouped-imports.useless-suppression + from c2cgeoportal_commons.models import ( + main, # pylint: disable=ungrouped-imports.useless-suppression + ) _LOG = logging.getLogger(__name__) @@ -222,7 +231,9 @@ def _get_geom_col_info(layer: "main.Layer") -> tuple[str, int]: @staticmethod def _get_layer(layer_id: int) -> "main.Layer": """Return a ``Layer`` object for ``layer_id``.""" - from c2cgeoportal_commons.models.main import Layer # pylint: disable=import-outside-toplevel + from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel + Layer, + ) assert models.DBSession is not None @@ -281,7 +292,6 @@ def _get_protocol_for_request(self) -> Protocol: def _proto_read(self, layer: "main.Layer") -> FeatureCollection: """Read features for the layer based on the self.request.""" - from c2cgeoportal_commons.models.main import ( # pylint: disable=import-outside-toplevel Layer, RestrictionArea, @@ -495,7 +505,7 @@ def enumerate_attribute_values(self) -> dict[str, Any]: raise HTTPInternalServerError("Missing configuration") layername = self.request.matchdict["layer_name"] fieldname = self.request.matchdict["field_name"] - # TODO check if layer is public or not + # TODO check if layer is public or not # pylint: disable=fixme return cast(dict[str, Any], self._enumerate_attribute_values(layername, fieldname)) @@ -541,15 +551,14 @@ def get_layer_class(layer: "main.Layer", with_last_update_columns: bool = False) Get the SQLAlchemy class to edit a GeoMapFish layer. Keyword Arguments: - layer: The GeoMapFish layer with_last_update_columns: False to just have a class to access to the table and be able to modify the last_update_columns, True to have a correct class to build the UI (without the hidden column). Returns: SQLAlchemy class - """ + """ assert layer.geo_table is not None # Exclude the columns used to record the last features update @@ -616,7 +625,6 @@ class ColumnProperties(TypedDict, total=False): def get_layer_metadata(layer: "main.Layer") -> list[ColumnProperties]: """Get the metadata related to a layer.""" - assert models.DBSession is not None cls = get_layer_class(layer, with_last_update_columns=True) @@ -690,7 +698,7 @@ def _convert_column_type(column_type: object) -> ColumnProperties: return restriction # String type - if isinstance(column_type, (String, Text, Unicode, UnicodeText)): + if isinstance(column_type, String | Text | Unicode | UnicodeText): if column_type.length is None: return {"type": "xsd:string"} return {"type": "xsd:string", "maxLength": int(column_type.length)} diff --git a/geoportal/c2cgeoportal_geoportal/views/login.py b/geoportal/c2cgeoportal_geoportal/views/login.py index 2d812a3c94..fd061bf6a0 100644 --- a/geoportal/c2cgeoportal_geoportal/views/login.py +++ b/geoportal/c2cgeoportal_geoportal/views/login.py @@ -38,6 +38,9 @@ import pyotp import pyramid.request import pyramid.response +from c2cgeoportal_commons import models +from c2cgeoportal_commons.lib.email_ import send_email_config +from c2cgeoportal_commons.models import static from pyramid.httpexceptions import ( HTTPBadRequest, HTTPForbidden, @@ -50,9 +53,6 @@ from pyramid.view import forbidden_view_config, view_config from sqlalchemy.orm.exc import NoResultFound # type: ignore[attr-defined] -from c2cgeoportal_commons import models -from c2cgeoportal_commons.lib.email_ import send_email_config -from c2cgeoportal_commons.models import static from c2cgeoportal_geoportal import is_allowed_url, is_valid_referrer from c2cgeoportal_geoportal.lib import get_setting, is_intranet, oauth2, oidc from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers @@ -126,9 +126,7 @@ def loginform(self) -> dict[str, Any]: @staticmethod def _validate_2fa_totp(user: static.User, otp: str) -> bool: - if pyotp.TOTP(user.tech_data.get("2fa_totp_secret", "")).verify(otp): - return True - return False + return bool(pyotp.TOTP(user.tech_data.get("2fa_totp_secret", "")).verify(otp)) @view_config(route_name="login") # type: ignore def login(self) -> pyramid.response.Response: @@ -374,10 +372,9 @@ def change_password(self) -> pyramid.response.Response: _LOG.info("The login '%s' does not exist.", login) raise HTTPUnauthorized("See server logs for details") - if self.two_factor_auth: - if not self._validate_2fa_totp(user, otp): - _LOG.info("The second factor is wrong for user '%s'.", login) - raise HTTPUnauthorized("See server logs for details") + if self.two_factor_auth and not self._validate_2fa_totp(user, otp): + _LOG.info("The second factor is wrong for user '%s'.", login) + raise HTTPUnauthorized("See server logs for details") else: user = self.request.user @@ -662,7 +659,7 @@ def oidc_callback(self) -> pyramid.response.Response: "login", Cache.PRIVATE_NO, response=Response( - # TODO respect the user interface... + # TODO respect the user interface... # pylint: disable=fixme json.dumps( { "username": user.display_name, diff --git a/geoportal/c2cgeoportal_geoportal/views/mapserverproxy.py b/geoportal/c2cgeoportal_geoportal/views/mapserverproxy.py index 474530112f..3e9875a416 100644 --- a/geoportal/c2cgeoportal_geoportal/views/mapserverproxy.py +++ b/geoportal/c2cgeoportal_geoportal/views/mapserverproxy.py @@ -29,13 +29,18 @@ import logging from typing import Any -from pyramid.httpexceptions import HTTPForbidden, HTTPFound, HTTPInternalServerError, HTTPUnauthorized +from c2cgeoportal_commons.lib.url import Url +from c2cgeoportal_commons.models import main +from pyramid.httpexceptions import ( + HTTPForbidden, + HTTPFound, + HTTPInternalServerError, + HTTPUnauthorized, +) from pyramid.request import Request from pyramid.response import Response from pyramid.view import view_config -from c2cgeoportal_commons.lib.url import Url -from c2cgeoportal_commons.models import main from c2cgeoportal_geoportal.lib import get_roles_id, get_roles_name from c2cgeoportal_geoportal.lib.caching import get_region from c2cgeoportal_geoportal.lib.common_headers import Cache diff --git a/geoportal/c2cgeoportal_geoportal/views/ogcproxy.py b/geoportal/c2cgeoportal_geoportal/views/ogcproxy.py index ca0298e3de..dd1d12c2ca 100644 --- a/geoportal/c2cgeoportal_geoportal/views/ogcproxy.py +++ b/geoportal/c2cgeoportal_geoportal/views/ogcproxy.py @@ -28,11 +28,11 @@ import logging import pyramid.request +from c2cgeoportal_commons.lib.url import Url, get_url2 +from c2cgeoportal_commons.models import DBSession, main from pyramid.httpexceptions import HTTPBadRequest from sqlalchemy.orm.exc import NoResultFound # type: ignore[attr-defined] -from c2cgeoportal_commons.lib.url import Url, get_url2 -from c2cgeoportal_commons.models import DBSession, main from c2cgeoportal_geoportal.lib.caching import get_region from c2cgeoportal_geoportal.views.proxy import Proxy diff --git a/geoportal/c2cgeoportal_geoportal/views/pdfreport.py b/geoportal/c2cgeoportal_geoportal/views/pdfreport.py index 4342daf0cd..3d5d2b9505 100644 --- a/geoportal/c2cgeoportal_geoportal/views/pdfreport.py +++ b/geoportal/c2cgeoportal_geoportal/views/pdfreport.py @@ -31,12 +31,12 @@ import pyramid.request import pyramid.response -from pyramid.httpexceptions import HTTPBadRequest, HTTPForbidden -from pyramid.view import view_config - from c2cgeoportal_commons import models from c2cgeoportal_commons.lib.url import Url from c2cgeoportal_commons.models import main +from pyramid.httpexceptions import HTTPBadRequest, HTTPForbidden +from pyramid.view import view_config + from c2cgeoportal_geoportal.lib.common_headers import Cache from c2cgeoportal_geoportal.lib.layers import get_private_layers, get_protected_layers from c2cgeoportal_geoportal.views.ogcproxy import OGCProxy @@ -121,7 +121,7 @@ def get_report(self) -> pyramid.response.Response: ) if layer_config["check_credentials"]: - # FIXME: support of mapserver groups + # FIXME: support of mapserver groups # pylint: disable=fixme ogc_server = ( models.DBSession.query(main.OGCServer) .filter(main.OGCServer.name == layer_config["ogc_server"]) diff --git a/geoportal/c2cgeoportal_geoportal/views/printproxy.py b/geoportal/c2cgeoportal_geoportal/views/printproxy.py index a46f2736f8..2cd8bc1384 100644 --- a/geoportal/c2cgeoportal_geoportal/views/printproxy.py +++ b/geoportal/c2cgeoportal_geoportal/views/printproxy.py @@ -33,10 +33,10 @@ import pyramid.request import pyramid.response import requests +from c2cgeoportal_commons.lib.url import Url from pyramid.httpexceptions import HTTPBadGateway, HTTPFound from pyramid.view import view_config -from c2cgeoportal_commons.lib.url import Url from c2cgeoportal_geoportal.lib import is_intranet from c2cgeoportal_geoportal.lib.caching import get_region from c2cgeoportal_geoportal.lib.common_headers import Cache diff --git a/geoportal/c2cgeoportal_geoportal/views/proxy.py b/geoportal/c2cgeoportal_geoportal/views/proxy.py index f18a2c8c80..427308ebcf 100644 --- a/geoportal/c2cgeoportal_geoportal/views/proxy.py +++ b/geoportal/c2cgeoportal_geoportal/views/proxy.py @@ -33,9 +33,9 @@ import pyramid.request import pyramid.response import requests +from c2cgeoportal_commons.lib.url import Url from pyramid.httpexceptions import HTTPBadGateway, exception_response -from c2cgeoportal_commons.lib.url import Url from c2cgeoportal_geoportal.lib.caching import get_region from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers from c2cgeoportal_geoportal.views import restrict_headers diff --git a/geoportal/c2cgeoportal_geoportal/views/raster.py b/geoportal/c2cgeoportal_geoportal/views/raster.py index 5f476cc2b6..5c742dcf95 100644 --- a/geoportal/c2cgeoportal_geoportal/views/raster.py +++ b/geoportal/c2cgeoportal_geoportal/views/raster.py @@ -36,11 +36,11 @@ import numpy import pyramid.request import zope.event.classhandler +from c2cgeoportal_commons.models import InvalidateCacheEvent from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound from pyramid.view import view_config from rasterio.io import DatasetReader -from c2cgeoportal_commons.models import InvalidateCacheEvent from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers if TYPE_CHECKING: @@ -108,7 +108,9 @@ def _get_data(self, layer: dict[str, Any], name: str) -> "fiona.collection.Colle path = layer["file"] if layer.get("type", "shp_index") == "shp_index": # Avoid loading if not needed - from fiona.collection import Collection # pylint: disable=import-outside-toplevel + from fiona.collection import ( # pylint: disable=import-outside-toplevel + Collection, + ) self.data[name] = Collection(path) elif layer.get("type") == "gdal": diff --git a/geoportal/c2cgeoportal_geoportal/views/resourceproxy.py b/geoportal/c2cgeoportal_geoportal/views/resourceproxy.py index 3ccef3c067..460c9423d1 100644 --- a/geoportal/c2cgeoportal_geoportal/views/resourceproxy.py +++ b/geoportal/c2cgeoportal_geoportal/views/resourceproxy.py @@ -65,7 +65,7 @@ def proxy(self) -> pyramid.response.Response: response = self._build_response( response, response.content, cache_control, "externalresource", content_type=content_type ) - for header in response.headers.keys(): + for header in response.headers: if header not in self.settings["allowed_headers"]: response.headers.pop(header) return response diff --git a/geoportal/c2cgeoportal_geoportal/views/shortener.py b/geoportal/c2cgeoportal_geoportal/views/shortener.py index 2480734a62..ba2aaf2d43 100644 --- a/geoportal/c2cgeoportal_geoportal/views/shortener.py +++ b/geoportal/c2cgeoportal_geoportal/views/shortener.py @@ -34,11 +34,16 @@ from urllib.parse import urlparse import pyramid.request -from pyramid.httpexceptions import HTTPBadRequest, HTTPFound, HTTPInternalServerError, HTTPNotFound -from pyramid.view import view_config - from c2cgeoportal_commons.lib.email_ import send_email_config from c2cgeoportal_commons.models import DBSession, static +from pyramid.httpexceptions import ( + HTTPBadRequest, + HTTPFound, + HTTPInternalServerError, + HTTPNotFound, +) +from pyramid.view import view_config + from c2cgeoportal_geoportal import is_allowed_url from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers @@ -107,7 +112,7 @@ def create(self) -> dict[str, str]: tries = 0 while not shortened: ref = "".join( - random.choice(string.ascii_letters + string.digits) # nosec + random.choice(string.ascii_letters + string.digits) # noqa: S311 for i in range(self.settings.get("length", 4)) ) test_url = DBSession.query(static.Shorturl).filter(static.Shorturl.ref == ref).all() diff --git a/geoportal/c2cgeoportal_geoportal/views/theme.py b/geoportal/c2cgeoportal_geoportal/views/theme.py index 42f2b2c5fb..2fa7060a2b 100644 --- a/geoportal/c2cgeoportal_geoportal/views/theme.py +++ b/geoportal/c2cgeoportal_geoportal/views/theme.py @@ -35,7 +35,7 @@ import time from collections import Counter from math import sqrt -from typing import Any, Optional, Union, cast +from typing import Any, cast import dogpile.cache.api import pyramid.httpexceptions @@ -43,6 +43,9 @@ import requests import sqlalchemy import sqlalchemy.orm.query +from c2cgeoportal_commons import models +from c2cgeoportal_commons.lib.url import Url, get_url2 +from c2cgeoportal_commons.models import cache_invalidate_cb, main from c2cwsgiutils.auth import auth_view from defusedxml import lxml from lxml import etree # nosec @@ -51,11 +54,13 @@ from sqlalchemy.orm import subqueryload from sqlalchemy.orm.exc import NoResultFound # type: ignore[attr-defined] -from c2cgeoportal_commons import models -from c2cgeoportal_commons.lib.url import Url, get_url2 -from c2cgeoportal_commons.models import cache_invalidate_cb, main from c2cgeoportal_geoportal import is_allowed_host, is_allowed_url -from c2cgeoportal_geoportal.lib import get_roles_id, get_typed, get_types_map, is_intranet +from c2cgeoportal_geoportal.lib import ( + get_roles_id, + get_typed, + get_types_map, + is_intranet, +) from c2cgeoportal_geoportal.lib.caching import get_region from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers from c2cgeoportal_geoportal.lib.functionality import get_mapserver_substitution_params @@ -72,7 +77,7 @@ _CACHE_OGC_SERVER_REGION = get_region("ogc-server") _TIMEOUT = int(os.environ.get("C2CGEOPORTAL_THEME_TIMEOUT", "300")) -Metadata = Union[str, int, float, bool, list[Any], dict[str, Any]] +Metadata = str | int | float | bool | list[Any] | dict[str, Any] async def get_http_cached( @@ -323,7 +328,6 @@ async def _wms_getcap_cached( def _create_layer_query(self, interface: str) -> sqlalchemy.orm.query.RowReturningQuery[tuple[str]]: """Create an SQLAlchemy query for Layer and for the role identified to by ``role_id``.""" - assert models.DBSession is not None query: sqlalchemy.orm.query.RowReturningQuery[tuple[str]] = models.DBSession.query( @@ -596,7 +600,6 @@ def _layer_included(tree_item: main.TreeItem) -> bool: def _get_ogc_servers(self, group: main.LayerGroup, depth: int) -> set[str | bool]: """Get unique identifier for each child by recursing on all the children.""" - ogc_servers: set[str | bool] = set() # escape loop @@ -713,15 +716,14 @@ async def _group( ) group_theme["mixed"] = mixed - if org_depth == 1: - if not mixed: - assert time_ is not None - assert dim is not None - group_theme["ogcServer"] = cast(list[Any], ogc_servers)[0] - if time_.has_time() and time_.layer is None: - group_theme["time"] = time_.to_dict() + if org_depth == 1 and not mixed: + assert time_ is not None + assert dim is not None + group_theme["ogcServer"] = cast(list[Any], ogc_servers)[0] + if time_.has_time() and time_.layer is None: + group_theme["time"] = time_.to_dict() - group_theme["dimensions"] = dim.get_dimensions() + group_theme["dimensions"] = dim.get_dimensions() return group_theme, errors return None, errors @@ -775,7 +777,6 @@ async def _themes( self, interface: str = "desktop", filter_themes: bool = True, min_levels: int = 1 ) -> tuple[list[dict[str, Any]], set[str]]: """Return theme information for the role identified by ``role_id``.""" - assert models.DBSession is not None self._load_tree_items() @@ -894,7 +895,7 @@ def _get_role_ids(self) -> set[int] | None: async def _wfs_get_features_type( self, wfs_url: Url, ogc_server: main.OGCServer, preload: bool = False, cache: bool = True - ) -> tuple[Optional[etree.Element], set[str]]: # pylint: disable=c-extension-no-member + ) -> tuple[etree.Element | None, set[str]]: # pylint: disable=c-extension-no-member errors = set() wfs_url.add_query( @@ -987,7 +988,8 @@ async def _preload(self, errors: set[str]) -> None: for ogc_server, nb_layers in ( models.DBSession.query( - main.OGCServer, sqlalchemy.func.count(main.LayerWMS.id) # pylint: disable=not-callable + main.OGCServer, + sqlalchemy.func.count(main.LayerWMS.id), # pylint: disable=not-callable ) .filter(main.LayerWMS.ogc_server_id == main.OGCServer.id) .group_by(main.OGCServer.id) @@ -1037,7 +1039,7 @@ def _get_features_attributes_cache( "available namespaces: %s (OGC server: %s)", type_namespace, name, - ", ".join([str(k) for k in child.nsmap.keys()]), + ", ".join([str(k) for k in child.nsmap]), ogc_server_name, ) elif child.nsmap[type_namespace] != namespace: @@ -1069,7 +1071,7 @@ def _get_features_attributes_cache( "available namespaces: %s (OGC server: %s)", type_namespace, name, - ", ".join([str(k) for k in child.nsmap.keys()]), + ", ".join([str(k) for k in child.nsmap]), ogc_server_name, ) for key, value in children.attrib.items(): @@ -1114,7 +1116,6 @@ def _get_features_attributes_cache( @view_config(route_name="themes", renderer="json") # type: ignore[misc] def themes(self) -> dict[str, dict[str, dict[str, Any]] | list[str]]: - is_allowed_host(self.request) interface = self.request.params.get("interface", "desktop") @@ -1145,7 +1146,8 @@ async def get_theme() -> dict[str, dict[str, Any] | list[str]]: result["ogcServers"] = {} for ogc_server, nb_layers in ( models.DBSession.query( - main.OGCServer, sqlalchemy.func.count(main.LayerWMS.id) # pylint: disable=not-callable + main.OGCServer, + sqlalchemy.func.count(main.LayerWMS.id), # pylint: disable=not-callable ) .filter(main.LayerWMS.ogc_server_id == main.OGCServer.id) .group_by(main.OGCServer.id) @@ -1239,7 +1241,7 @@ def get_theme_anonymous( if self.request.user is None: return cast( - dict[str, Union[dict[str, dict[str, Any]], list[str]]], + dict[str, dict[str, dict[str, Any]] | list[str]], get_theme_anonymous( is_intranet(self.request), interface, diff --git a/geoportal/c2cgeoportal_geoportal/views/tinyowsproxy.py b/geoportal/c2cgeoportal_geoportal/views/tinyowsproxy.py index 9edd8efb1f..c33ca1c3c2 100644 --- a/geoportal/c2cgeoportal_geoportal/views/tinyowsproxy.py +++ b/geoportal/c2cgeoportal_geoportal/views/tinyowsproxy.py @@ -30,13 +30,18 @@ from typing import Any import pyramid.request -from defusedxml import ElementTree -from pyramid.httpexceptions import HTTPBadRequest, HTTPForbidden, HTTPInternalServerError, HTTPUnauthorized -from pyramid.view import view_config - from c2cgeoportal_commons import models from c2cgeoportal_commons.lib.url import Url from c2cgeoportal_commons.models import main +from defusedxml import ElementTree +from pyramid.httpexceptions import ( + HTTPBadRequest, + HTTPForbidden, + HTTPInternalServerError, + HTTPUnauthorized, +) +from pyramid.view import view_config + from c2cgeoportal_geoportal.lib.common_headers import Cache from c2cgeoportal_geoportal.lib.filter_capabilities import ( filter_wfst_capabilities, @@ -97,11 +102,10 @@ def proxy(self) -> pyramid.response.Response: if operation is None or operation == "": operation = "getcapabilities" - if operation == "describefeaturetype": - # for DescribeFeatureType we require that exactly one type-name - # is given, otherwise we would have to filter the result - if len(typenames) != 1: - raise HTTPBadRequest("Exactly one type-name must be given for DescribeFeatureType requests") + # for DescribeFeatureType we require that exactly one type-name + # is given, otherwise we would have to filter the result + if operation == "describefeaturetype" and len(typenames) != 1: + raise HTTPBadRequest("Exactly one type-name must be given for DescribeFeatureType requests") if not self._is_allowed(typenames): raise HTTPForbidden("No access rights for at least one of the given type-names") diff --git a/geoportal/c2cgeoportal_geoportal/views/vector_tiles.py b/geoportal/c2cgeoportal_geoportal/views/vector_tiles.py index 9fd929ba9e..78c8062bd4 100644 --- a/geoportal/c2cgeoportal_geoportal/views/vector_tiles.py +++ b/geoportal/c2cgeoportal_geoportal/views/vector_tiles.py @@ -28,6 +28,7 @@ import logging import sqlalchemy +from c2cgeoportal_commons.models import DBSession, main from pyramid.httpexceptions import HTTPNotFound from pyramid.request import Request from pyramid.response import Response @@ -35,7 +36,6 @@ from tilecloud import TileCoord from tilecloud.grid.free import FreeTileGrid -from c2cgeoportal_commons.models import DBSession, main from c2cgeoportal_geoportal.lib.common_headers import Cache, set_common_headers _LOG = logging.getLogger(__name__) diff --git a/geoportal/tests/__init__.py b/geoportal/tests/__init__.py index 465a966acb..b981949bc6 100644 --- a/geoportal/tests/__init__.py +++ b/geoportal/tests/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2011-2023, Camptocamp SA +# Copyright (c) 2011-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -26,9 +26,7 @@ # either expressed or implied, of the FreeBSD Project. -""" -Pyramid application test package. -""" +"""Pyramid application test package.""" import logging import os @@ -36,9 +34,8 @@ import warnings import sqlalchemy.exc -from pyramid.testing import DummyRequest as PyramidDummyRequest - from c2cgeoportal_geoportal.lib import caching +from pyramid.testing import DummyRequest as PyramidDummyRequest with warnings.catch_warnings(): warnings.simplefilter("ignore", category=sqlalchemy.exc.SAWarning) diff --git a/geoportal/tests/functional/__init__.py b/geoportal/tests/functional/__init__.py index 855e0dbf1e..235eb55737 100644 --- a/geoportal/tests/functional/__init__.py +++ b/geoportal/tests/functional/__init__.py @@ -26,17 +26,18 @@ # either expressed or implied, of the FreeBSD Project. -""" -Pyramid application test package. -""" +"""Pyramid application test package.""" import logging from configparser import ConfigParser from typing import TYPE_CHECKING, Any +import c2cgeoportal_geoportal +import c2cgeoportal_geoportal.lib import pyramid.registry import pyramid.request import tests +import sqlalchemy.exc import transaction import webob.acceptparse from c2c.template.config import config as configuration @@ -47,6 +48,9 @@ import c2cgeoportal_geoportal.lib from c2cgeoportal_commons import models from c2cgeoportal_geoportal.lib import caching +from pyramid import testing + +import tests if TYPE_CHECKING: from c2cgeoportal_commons.models import main, static @@ -64,9 +68,7 @@ class DummyRoute: def cleanup_db() -> None: - """ - Cleanup the database. - """ + """Cleanup the database.""" from c2cgeoportal_commons import models from c2cgeoportal_commons.models.main import ( FullTextSearch, @@ -103,9 +105,7 @@ def cleanup_db() -> None: def setup_db() -> None: - """ - Cleanup the database. - """ + """Cleanup the database.""" cleanup_db() from c2cgeoportal_commons.models import DBSession @@ -211,8 +211,12 @@ def init_registry(registry=None): def testing_legacySecurityPolicy( config, userid=None, groupids=(), permissive=True, remember_result=None, forget_result=None ): - """Compatibility mode for deprecated AuthorizationPolicy and AuthenticationPolicy in our tests""" - from pyramid.interfaces import IAuthenticationPolicy, IAuthorizationPolicy, ISecurityPolicy + """Compatibility mode for deprecated AuthorizationPolicy and AuthenticationPolicy in our tests.""" + from pyramid.interfaces import ( + IAuthenticationPolicy, + IAuthorizationPolicy, + ISecurityPolicy, + ) from pyramid.security import LegacySecurityPolicy from pyramid.testing import DummySecurityPolicy @@ -228,10 +232,9 @@ def testing_legacySecurityPolicy( def create_dummy_request( additional_settings=None, authentication=True, user=None, *args: Any, **kargs: Any ) -> pyramid.request.Request: - from pyramid.interfaces import IAuthenticationPolicy - from c2cgeoportal_geoportal import create_get_user_from_request from c2cgeoportal_geoportal.lib.authentication import create_authentication + from pyramid.interfaces import IAuthenticationPolicy if additional_settings is None: additional_settings = {} diff --git a/geoportal/tests/functional/conftest.py b/geoportal/tests/functional/conftest.py index 049cbdc8cc..5ea871be7a 100644 --- a/geoportal/tests/functional/conftest.py +++ b/geoportal/tests/functional/conftest.py @@ -31,8 +31,6 @@ import pyramid.request import pytest -import sqlalchemy.orm -import transaction from c2c.template.config import config as configuration from pyramid.testing import DummyRequest from sqlalchemy.orm.session import Session, SessionTransaction @@ -41,6 +39,7 @@ from c2cgeoportal_commons.testing import generate_mappers, get_engine, get_session_factory, get_tm_session from c2cgeoportal_commons.testing.initializedb import truncate_tables from c2cgeoportal_geoportal.lib import caching +from tests.functional import setup_common as setup_module _LOG = logging.getLogger(__name__) mapserv_url = "http://mapserver:8080/" diff --git a/geoportal/tests/functional/test_authentication.py b/geoportal/tests/functional/test_authentication.py index aad67c7a73..c228fa529d 100644 --- a/geoportal/tests/functional/test_authentication.py +++ b/geoportal/tests/functional/test_authentication.py @@ -32,15 +32,14 @@ from unittest.mock import patch import transaction -from tests.functional import create_dummy_request -from tests.functional import setup_common as setup_module # noqa -from tests.functional import teardown_common as teardown_module # noqa - -from c2cgeoportal_geoportal.lib import authentication from c2cgeoportal_geoportal.lib.authentication import UrlAuthenticationPolicy from c2cgeoportal_geoportal.resources import defaultgroupsfinder from c2cgeoportal_geoportal.scripts.urllogin import create_token +from tests.functional import create_dummy_request +from tests.functional import setup_common as setup_module # noqa +from tests.functional import teardown_common as teardown_module # noqa + class TestUrlAuthenticationPolicy(TestCase): def setup_method(self, _): @@ -110,9 +109,7 @@ def test_wrong_key(self): @patch("c2cgeoportal_geoportal.lib.authentication._LOG.error", side_effect=Exception()) def test_wrong_method(self, log_mock): # pylint: disable=unused-argument - """ - POST requests with input named "auth" must not raise exceptions due to urllogin. - """ + """POST requests with input named "auth" must not raise exceptions due to urllogin.""" def _get_user(method): request = create_dummy_request(params={"auth": "this is a wrong field value"}, method=method) diff --git a/geoportal/tests/functional/test_dbreflection.py b/geoportal/tests/functional/test_dbreflection.py index 01ac7b22d6..30b004a13a 100644 --- a/geoportal/tests/functional/test_dbreflection.py +++ b/geoportal/tests/functional/test_dbreflection.py @@ -30,11 +30,11 @@ from unittest import TestCase +from c2cgeoportal_geoportal.lib.caching import init_region + from tests.functional import setup_common as setup_module from tests.functional import teardown_common as teardown_module # noqa -from c2cgeoportal_geoportal.lib.caching import init_region - class TestReflection(TestCase): _tables = None @@ -60,11 +60,10 @@ def _create_table(self, tablename): Each test function should call this function only once. And there should not be two test functions that call this function with the same ptable_name value. """ - from geoalchemy2 import Geometry - from sqlalchemy import Column, ForeignKey, Table, types - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import Base + from geoalchemy2 import Geometry + from sqlalchemy import Column, ForeignKey, Table, types if self._tables is None: self._tables = [] @@ -104,16 +103,14 @@ def _create_table(self, tablename): self.metadata = Base.metadata def test_get_class_nonexisting_table(self): - from sqlalchemy.exc import NoSuchTableError - from c2cgeoportal_geoportal.lib.dbreflection import get_class + from sqlalchemy.exc import NoSuchTableError self.assertRaises(NoSuchTableError, get_class, "nonexisting") def test_get_class(self): - from geoalchemy2 import Geometry - from c2cgeoportal_geoportal.lib.dbreflection import _AssociationProxy, get_class + from geoalchemy2 import Geometry init_region({"backend": "dogpile.cache.memory"}, "std") init_region({"backend": "dogpile.cache.memory"}, "obj") @@ -142,8 +139,8 @@ def test_get_class(self): assert modelclass.child2_id.info.get("association_proxy") == "child2" child1_asso_proxy = getattr(modelclass, modelclass.child1_id.info["association_proxy"]) - assert "name" == child1_asso_proxy.value_attr - assert "name" == child1_asso_proxy.order_by + assert child1_asso_proxy.value_attr == "name" + assert child1_asso_proxy.order_by == "name" # test the Table object table = modelclass.__table__ @@ -192,14 +189,11 @@ def test_get_class_dotted_notation(self): assert modelclass.__table__.schema == "public" def test_mixing_get_class_and_queries(self): - """ - This test shows that we can mix the use of DBSession and the db reflection API. - """ + """This test shows that we can mix the use of DBSession and the db reflection API.""" import transaction - from sqlalchemy import text - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_geoportal.lib.dbreflection import get_class + from sqlalchemy import text self._create_table("table_c") @@ -239,16 +233,16 @@ def test_get_class_enumerations_config(self): assert enumerations_config == cls.__enumerations_config__ association_proxy = getattr(cls, cls.child1_id.info["association_proxy"]) - assert "id" == association_proxy.value_attr - assert "name" == association_proxy.order_by + assert association_proxy.value_attr == "id" + assert association_proxy.order_by == "name" # Without order_by. enumerations_config = {"child1_id": {"value": "id"}} cls = get_class("table_d", enumerations_config=enumerations_config) association_proxy = getattr(cls, cls.child1_id.info["association_proxy"]) - assert "id" == association_proxy.value_attr - assert "id" == association_proxy.order_by + assert association_proxy.value_attr == "id" + assert association_proxy.order_by == "id" def test_get_class_readonly_attributes(self): from c2cgeoportal_geoportal.lib.dbreflection import get_class @@ -258,5 +252,5 @@ def test_get_class_readonly_attributes(self): self._create_table("table_d") cls = get_class("table_d", readonly_attributes=readonly_attributes) - assert True == cls.child1_id.info.get("readonly") - assert True == cls.point.info.get("readonly") + assert True is cls.child1_id.info.get("readonly") + assert True is cls.point.info.get("readonly") diff --git a/geoportal/tests/functional/test_dynamicview.py b/geoportal/tests/functional/test_dynamicview.py index 1c70b54af1..367bb7ed0d 100644 --- a/geoportal/tests/functional/test_dynamicview.py +++ b/geoportal/tests/functional/test_dynamicview.py @@ -31,23 +31,20 @@ from unittest import TestCase import pyramid.url +from c2cgeoportal_geoportal.lib.caching import init_region from pyramid import testing from pyramid.testing import testConfig -from tests import DummyRequest -from tests.functional import setup_common as setup_module # noqa, pylint: disable=unused-import -from tests.functional import teardown_common as teardown_module # noqa, pylint: disable=unused-import -from c2cgeoportal_geoportal.lib.caching import init_region +from tests import DummyRequest class TestDynamicView(TestCase): def setup_method(self, _): import transaction - from geoalchemy2 import WKTElement - from sqlalchemy import func - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import FullTextSearch + from geoalchemy2 import WKTElement + from sqlalchemy import func entry1 = FullTextSearch() entry1.label = "label 1" @@ -81,7 +78,6 @@ def teardown_method(self, _): testing.tearDown() import transaction - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import FullTextSearch @@ -101,7 +97,9 @@ def _get_settings(settings): } @staticmethod - def _request(query={}): + def _request(query=None): + if query is None: + query = {} query_ = {"interface": "test"} query_.update(query) request = DummyRequest(query_) diff --git a/geoportal/tests/functional/test_entry.py b/geoportal/tests/functional/test_entry.py index ab84ca9fc8..0134636935 100644 --- a/geoportal/tests/functional/test_entry.py +++ b/geoportal/tests/functional/test_entry.py @@ -35,10 +35,14 @@ import transaction from geoalchemy2 import WKTElement from pyramid import testing -from tests.functional import cleanup_db, create_default_ogcserver, create_dummy_request, mapserv_url -from tests.functional import setup_common as setup_module # noqa, pylint: disable=unused-import -from tests.functional import setup_db -from tests.functional import teardown_common as teardown_module # noqa, pylint: disable=unused-import + +from tests.functional import ( + cleanup_db, + create_default_ogcserver, + create_dummy_request, + mapserv_url, + setup_db, +) _LOG = logging.getLogger(__name__) @@ -50,10 +54,6 @@ def setup_method(self, _): self.maxDiff = None # pylint: disable=invalid-name self._tables = [] - from geoalchemy2 import Geometry - from sqlalchemy import Column, Table, func, types - from sqlalchemy.ext.declarative import declarative_base - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import ( OGCSERVER_AUTH_GEOSERVER, @@ -69,6 +69,9 @@ def setup_method(self, _): Theme, ) from c2cgeoportal_commons.models.static import User + from geoalchemy2 import Geometry + from sqlalchemy import Column, Table, func, types + from sqlalchemy.ext.declarative import declarative_base setup_db() diff --git a/geoportal/tests/functional/test_fulltextsearch.py b/geoportal/tests/functional/test_fulltextsearch.py index 5a399c2790..e59a5b29c0 100644 --- a/geoportal/tests/functional/test_fulltextsearch.py +++ b/geoportal/tests/functional/test_fulltextsearch.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2023, Camptocamp SA +# Copyright (c) 2013-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -33,6 +33,7 @@ import webob.acceptparse from pyramid import testing from pyramid.response import Response + from tests.functional import create_dummy_request from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa @@ -41,12 +42,11 @@ class TestFulltextsearchView(TestCase): def setup_method(self, _): import transaction - from geoalchemy2 import WKTElement - from sqlalchemy import func - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import FullTextSearch, Interface, Role from c2cgeoportal_commons.models.static import User + from geoalchemy2 import WKTElement + from sqlalchemy import func role1 = Role(name="__test_role1", description="__test_role1") user1 = User(username="__test_user1", password="__test_user1", settings_role=role1, roles=[role1]) @@ -135,7 +135,6 @@ def teardown_method(self, _): testing.tearDown() import transaction - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import FullTextSearch, Interface, Role from c2cgeoportal_commons.models.static import User @@ -176,9 +175,8 @@ def _create_dummy_request(username=None, params=None): return request def test_no_default_laguage(self): - from pyramid.httpexceptions import HTTPInternalServerError - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from pyramid.httpexceptions import HTTPInternalServerError request = self._create_dummy_request() del request.registry.settings["default_locale_name"] @@ -189,9 +187,8 @@ def test_no_default_laguage(self): self.assertTrue(isinstance(response, HTTPInternalServerError)) def test_unknown_laguage(self): - from pyramid.httpexceptions import HTTPInternalServerError - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from pyramid.httpexceptions import HTTPInternalServerError request = self._create_dummy_request() request.registry.settings["default_locale_name"] = "it" @@ -201,9 +198,8 @@ def test_unknown_laguage(self): self.assertTrue(isinstance(response, HTTPInternalServerError)) def test_badrequest_noquery(self): - from pyramid.httpexceptions import HTTPBadRequest - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from pyramid.httpexceptions import HTTPBadRequest request = self._create_dummy_request() fts = FullTextSearchView(request) @@ -211,9 +207,8 @@ def test_badrequest_noquery(self): self.assertTrue(isinstance(response, HTTPBadRequest)) def test_badrequest_limit(self): - from pyramid.httpexceptions import HTTPBadRequest - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from pyramid.httpexceptions import HTTPBadRequest request = self._create_dummy_request(params=dict(query="text", limit="bad")) fts = FullTextSearchView(request) @@ -221,9 +216,8 @@ def test_badrequest_limit(self): self.assertTrue(isinstance(response, HTTPBadRequest)) def test_badrequest_partitionlimit(self): - from pyramid.httpexceptions import HTTPBadRequest - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from pyramid.httpexceptions import HTTPBadRequest request = self._create_dummy_request(params=dict(query="text", partitionlimit="bad")) fts = FullTextSearchView(request) @@ -231,9 +225,8 @@ def test_badrequest_partitionlimit(self): self.assertTrue(isinstance(response, HTTPBadRequest)) def test_limit(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="tra sol", limit=1)) fts = FullTextSearchView(request) @@ -244,9 +237,8 @@ def test_limit(self): assert response.features[0].properties["layer_name"] == "layer1" def test_toobig_limit(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="tra sol", limit=2000)) fts = FullTextSearchView(request) @@ -259,9 +251,8 @@ def test_toobig_limit(self): assert response.features[1].properties["layer_name"] == "layer1" def test_toobig_partitionlimit(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="tra sol", partitionlimit=2000)) fts = FullTextSearchView(request) @@ -274,9 +265,8 @@ def test_toobig_partitionlimit(self): assert response.features[1].properties["layer_name"] == "layer1" def test_match(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="tra sol", limit=40)) fts = FullTextSearchView(request) @@ -289,9 +279,8 @@ def test_match(self): assert response.features[1].properties["layer_name"] == "layer1" def test_nomatch(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="foo")) fts = FullTextSearchView(request) @@ -300,9 +289,8 @@ def test_nomatch(self): assert len(response.features) == 0 def test_private_nomatch(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="pl sem", limit=40)) fts = FullTextSearchView(request) @@ -311,9 +299,8 @@ def test_private_nomatch(self): assert len(response.features) == 0 def test_private_match(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="pl sem", limit=40), username="__test_user1") fts = FullTextSearchView(request) @@ -324,9 +311,8 @@ def test_private_match(self): assert response.features[0].properties["layer_name"] == "layer2" def test_private_with_role_nomatch(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="ven nei", limit=40), username="__test_user1") fts = FullTextSearchView(request) @@ -335,9 +321,8 @@ def test_private_with_role_nomatch(self): assert len(response.features) == 0 def test_private_with_role_match(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="ven nei", limit=40), username="__test_user2") fts = FullTextSearchView(request) @@ -348,9 +333,8 @@ def test_private_with_role_match(self): assert response.features[0].properties["layer_name"] == "layer3" def test_match_partitionlimit(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="tra sol", limit=40, partitionlimit=1)) fts = FullTextSearchView(request) @@ -361,9 +345,8 @@ def test_match_partitionlimit(self): assert response.features[0].properties["layer_name"] == "layer1" def test_params_actions(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="lausanne", limit=10)) fts = FullTextSearchView(request) @@ -377,9 +360,8 @@ def test_params_actions(self): ) def test_interface(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="lausanne", limit=10, interface="main")) fts = FullTextSearchView(request) @@ -388,9 +370,8 @@ def test_interface(self): self.assertEqual({feature.properties["label"] for feature in response.features}, {"label5", "label6"}) def test_rank_order_with_similarity(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="simi", limit=3)) fts = FullTextSearchView(request) @@ -402,9 +383,8 @@ def test_rank_order_with_similarity(self): assert response.features[2].properties["label"] == "A 71 simi" def test_rank_order_with_ts_rank_cd(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="simi", limit=3, ranksystem="ts_rank_cd")) fts = FullTextSearchView(request) @@ -416,9 +396,8 @@ def test_rank_order_with_ts_rank_cd(self): assert [f.properties["label"] for f in response.features] == ["A 7 simi", "A 70 simi", "A 71 simi"] def test_extra_quote(self): - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.fulltextsearch import FullTextSearchView + from geojson.feature import FeatureCollection request = self._create_dummy_request(params=dict(query="tra 'sol")) fts = FullTextSearchView(request) diff --git a/geoportal/tests/functional/test_functionalities.py b/geoportal/tests/functional/test_functionalities.py index 9aea693bce..f56c150f12 100644 --- a/geoportal/tests/functional/test_functionalities.py +++ b/geoportal/tests/functional/test_functionalities.py @@ -38,7 +38,6 @@ class TestFunctionalities(TestCase): def setup_method(self, _): import transaction - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import Functionality, Role from c2cgeoportal_commons.models.static import User @@ -67,7 +66,6 @@ def setup_method(self, _): def teardown_method(self, _): import transaction - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import Functionality, OGCServer, Role from c2cgeoportal_commons.models.static import User @@ -89,12 +87,12 @@ def teardown_method(self, _): transaction.commit() def test_functionalities(self): - from tests.functional import create_dummy_request - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.static import User from c2cgeoportal_geoportal.lib.functionality import get_functionality + from tests.functional import create_dummy_request + request = create_dummy_request() request.user = None request1 = create_dummy_request() @@ -189,11 +187,11 @@ def test_functionalities(self): self.assertEqual(set(get_functionality("__test_a", request2, False)), {"db1", "db2"}) def test_web_client_functionalities(self): - from tests.functional import create_dummy_request - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.static import User + from tests.functional import create_dummy_request + request = create_dummy_request() request.static_url = lambda url: "http://example.com/dummy/static/url" request1 = create_dummy_request() diff --git a/geoportal/tests/functional/test_geometry_processing.py b/geoportal/tests/functional/test_geometry_processing.py index 1966ef9db5..aa2273394a 100644 --- a/geoportal/tests/functional/test_geometry_processing.py +++ b/geoportal/tests/functional/test_geometry_processing.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2023, Camptocamp SA +# Copyright (c) 2013-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -37,8 +37,6 @@ class TestGeometryProcessing(TestCase): def test_difference(self): - from shapely.geometry import Polygon - from c2cgeoportal_geoportal.views.geometry_processing import GeometryProcessing request = create_dummy_request() diff --git a/geoportal/tests/functional/test_groups_finder.py b/geoportal/tests/functional/test_groups_finder.py index 9aaf9e9edd..1602a5e42a 100644 --- a/geoportal/tests/functional/test_groups_finder.py +++ b/geoportal/tests/functional/test_groups_finder.py @@ -1,4 +1,4 @@ -# Copyright (c) 2011-2023, Camptocamp SA +# Copyright (c) 2011-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -38,7 +38,6 @@ class TestGroupsFinder(TestCase): def setup_method(self, _): import transaction - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import Role from c2cgeoportal_commons.models.static import User @@ -51,7 +50,6 @@ def setup_method(self, _): def teardown_method(self, _): import transaction - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import Role from c2cgeoportal_commons.models.static import User diff --git a/geoportal/tests/functional/test_layers.py b/geoportal/tests/functional/test_layers.py index 0c06fd87c6..31a8decd50 100644 --- a/geoportal/tests/functional/test_layers.py +++ b/geoportal/tests/functional/test_layers.py @@ -31,9 +31,13 @@ from typing import TYPE_CHECKING, Any from unittest import TestCase -from tests.functional import cleanup_db, create_default_ogcserver, create_dummy_request +from tests.functional import ( + cleanup_db, + create_default_ogcserver, + create_dummy_request, + setup_db, +) from tests.functional import setup_common as setup_module # noqa -from tests.functional import setup_db from tests.functional import teardown_common as teardown_module # noqa if TYPE_CHECKING: @@ -48,7 +52,6 @@ def setup_method(self, _: Any) -> None: setup_module() import transaction - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import Interface, Role from c2cgeoportal_commons.models.static import User @@ -77,7 +80,6 @@ def setup_method(self, _: Any) -> None: def teardown_method(self, _: Any) -> None: import transaction - from c2cgeoportal_commons.models import DBSession transaction.abort() @@ -105,13 +107,16 @@ def _create_layer( It creates a layer with two features, and associates a restriction area to it. """ import transaction + from c2cgeoportal_commons.models import DBSession + from c2cgeoportal_commons.models.main import ( + LayerWMS, + OGCServer, + RestrictionArea, + ) from geoalchemy2 import Geometry, WKTElement from sqlalchemy import CheckConstraint, Column, ForeignKey, Table, types from sqlalchemy.ext.declarative import declarative_base - from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import LayerWMS, OGCServer, RestrictionArea - if self._tables is None: self._tables = [] @@ -228,9 +233,8 @@ def _get_request(layerid, username=None) -> None: return request def test_read_public(self) -> None: - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import FeatureCollection layer_id = self._create_layer(public=True) request = self._get_request(layer_id) @@ -240,9 +244,8 @@ def test_read_public(self) -> None: self.assertEqual([f.properties["child"] for f in collection.features], ["c1é", "c2é"]) def test_read_many_no_auth(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer() request = self._get_request(layer_id) @@ -251,9 +254,8 @@ def test_read_many_no_auth(self) -> None: self.assertRaises(HTTPForbidden, layers.read_many) def test_read_many(self) -> None: - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import FeatureCollection layer_id = self._create_layer() request = self._get_request(layer_id, username="__test_user") @@ -264,9 +266,8 @@ def test_read_many(self) -> None: assert [f.properties["child"] for f in collection.features] == ["c1é"] def test_read_many_layer_not_found(self) -> None: - from pyramid.httpexceptions import HTTPNotFound - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPNotFound self._create_layer() request = self._get_request(10000, username="__test_user") @@ -275,9 +276,8 @@ def test_read_many_layer_not_found(self) -> None: self.assertRaises(HTTPNotFound, layers.read_many) def test_read_many_multi(self) -> None: - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import FeatureCollection layer_id1 = self._create_layer() layer_id2 = self._create_layer() @@ -294,9 +294,8 @@ def test_read_many_multi(self) -> None: ) def test_read_one_public(self) -> None: - from geojson.feature import Feature - from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import Feature layer_id = self._create_layer(public=True) request = self._get_request(layer_id) @@ -310,9 +309,8 @@ def test_read_one_public(self) -> None: assert feature.properties["child"] == "c1é" def test_read_one_public_notfound(self) -> None: - from pyramid.httpexceptions import HTTPNotFound - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPNotFound layer_id = self._create_layer(public=True) request = self._get_request(layer_id) @@ -323,9 +321,8 @@ def test_read_one_public_notfound(self) -> None: self.assertTrue(isinstance(feature, HTTPNotFound)) def test_read_one_no_auth(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer() request = self._get_request(layer_id) @@ -335,9 +332,8 @@ def test_read_one_no_auth(self) -> None: self.assertRaises(HTTPForbidden, layers.read_one) def test_read_one_no_perm(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer() request = self._get_request(layer_id, username="__test_user") @@ -347,9 +343,8 @@ def test_read_one_no_perm(self) -> None: self.assertRaises(HTTPForbidden, layers.read_one) def test_read_one(self) -> None: - from geojson.feature import Feature - from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import Feature layer_id = self._create_layer() request = self._get_request(layer_id, username="__test_user") @@ -373,9 +368,8 @@ def test_count(self) -> None: assert response == 2 def test_create_no_auth(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer() request = self._get_request(layer_id) @@ -385,9 +379,8 @@ def test_create_no_auth(self) -> None: self.assertRaises(HTTPForbidden, layers.create) def test_create_no_perm(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer() request = self._get_request(layer_id, username="__test_user") @@ -397,9 +390,8 @@ def test_create_no_perm(self) -> None: self.assertRaises(HTTPForbidden, layers.create) def test_create(self) -> None: - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import FeatureCollection layer_id = self._create_layer() request = self._get_request(layer_id, username="__test_user") @@ -427,12 +419,11 @@ def test_create_with_constraint_fail_integrity(self) -> None: def test_create_log(self) -> None: from datetime import datetime - from geojson.feature import FeatureCollection - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import Metadata from c2cgeoportal_commons.models.static import User from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import FeatureCollection self.assertEqual(DBSession.query(User.username).all(), [("__test_user",)]) @@ -472,10 +463,9 @@ def test_create_validation_fails(self) -> None: assert response["message"] == "Too few points in geometry component[5 45]" def test_create_no_validation(self) -> None: - from geojson.feature import FeatureCollection - from c2cgeoportal_commons.models.main import Metadata from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import FeatureCollection metadatas = [Metadata("geometryValidation", "False")] layer_id = self._create_layer(metadatas=metadatas, geom_type=False) @@ -489,9 +479,8 @@ def test_create_no_validation(self) -> None: assert len(collection.features) == 2 def test_update_no_auth(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer() request = self._get_request(layer_id) @@ -502,9 +491,8 @@ def test_update_no_auth(self) -> None: self.assertRaises(HTTPForbidden, layers.update) def test_update_no_perm_dst_geom(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer() request = self._get_request(layer_id, username="__test_user") @@ -515,9 +503,8 @@ def test_update_no_perm_dst_geom(self) -> None: self.assertRaises(HTTPForbidden, layers.update) def test_update_no_perm_src_geom(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer() request = self._get_request(layer_id, username="__test_user") @@ -626,9 +613,8 @@ def test_update_no_validation(self) -> None: assert feature.child == "c2é" def test_delete_no_auth(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer() request = self._get_request(layer_id) @@ -638,9 +624,8 @@ def test_delete_no_auth(self) -> None: self.assertRaises(HTTPForbidden, layers.delete) def test_delete_no_perm(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer() request = self._get_request(layer_id, username="__test_user") @@ -661,9 +646,8 @@ def test_delete(self) -> None: assert response.status_int == 204 def test_metadata_no_auth(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer() request = self._get_request(layer_id) @@ -742,9 +726,8 @@ def test_metadata_editing_enumeration_config(self) -> None: # # # With None area # # # def test_read_public_none_area(self) -> None: - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import FeatureCollection layer_id = self._create_layer(public=True, none_area=True) request = self._get_request(layer_id) @@ -755,9 +738,8 @@ def test_read_public_none_area(self) -> None: self.assertEqual([f.properties["child"] for f in collection.features], ["c1é", "c2é"]) def test_read_many_no_auth_none_area(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer(none_area=True) request = self._get_request(layer_id) @@ -766,9 +748,8 @@ def test_read_many_no_auth_none_area(self) -> None: self.assertRaises(HTTPForbidden, layers.read_many) def test_read_many_none_area(self) -> None: - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import FeatureCollection layer_id = self._create_layer(none_area=True) request = self._get_request(layer_id, username="__test_user") @@ -781,9 +762,8 @@ def test_read_many_none_area(self) -> None: assert collection.features[1].properties["child"] == "c2é" def test_read_one_public_none_area(self) -> None: - from geojson.feature import Feature - from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import Feature layer_id = self._create_layer(public=True, none_area=True) request = self._get_request(layer_id) @@ -797,9 +777,8 @@ def test_read_one_public_none_area(self) -> None: assert feature.properties["child"] == "c1é" def test_read_one_no_auth_none_area(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer(none_area=True) request = self._get_request(layer_id) @@ -809,9 +788,8 @@ def test_read_one_no_auth_none_area(self) -> None: self.assertRaises(HTTPForbidden, layers.read_one) def test_read_one_none_area(self) -> None: - from geojson.feature import Feature - from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import Feature layer_id = self._create_layer(none_area=True) request = self._get_request(layer_id, username="__test_user") @@ -835,9 +813,8 @@ def test_count_none_area(self) -> None: assert response == 2 def test_create_no_auth_none_area(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer(none_area=True) request = self._get_request(layer_id) @@ -847,9 +824,8 @@ def test_create_no_auth_none_area(self) -> None: self.assertRaises(HTTPForbidden, layers.create) def test_create_none_area(self) -> None: - from geojson.feature import FeatureCollection - from c2cgeoportal_geoportal.views.layers import Layers + from geojson.feature import FeatureCollection layer_id = self._create_layer(none_area=True) request = self._get_request(layer_id, username="__test_user") @@ -861,9 +837,8 @@ def test_create_none_area(self) -> None: assert len(collection.features) == 2 def test_update_no_auth_none_area(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer(none_area=True) request = self._get_request(layer_id) @@ -888,9 +863,8 @@ def test_update_none_area(self) -> None: assert feature.child == "c2é" def test_delete_no_auth_none_area(self) -> None: - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.layers import Layers + from pyramid.httpexceptions import HTTPForbidden layer_id = self._create_layer(none_area=True) request = self._get_request(layer_id) diff --git a/geoportal/tests/functional/test_lingva_extractor_config.py b/geoportal/tests/functional/test_lingva_extractor_config.py index 8c3c75b528..fea4d8a3b0 100644 --- a/geoportal/tests/functional/test_lingva_extractor_config.py +++ b/geoportal/tests/functional/test_lingva_extractor_config.py @@ -32,10 +32,10 @@ import pytest import yaml from c2c.template.config import config as configuration -from tests.functional import setup_common as setup_module - from c2cgeoportal_geoportal.lib.lingva_extractor import GeomapfishConfigExtractor +from tests.functional import setup_common as setup_module + GMF_CONFIG = """ vars: interfaces_config: @@ -96,6 +96,7 @@ def test_data(dbsession_db1, transact_old): from sqlalchemy import text from c2cgeoportal_commons.models import main + from sqlalchemy import text dbsession_db1.execute( text( diff --git a/geoportal/tests/functional/test_lingva_extractor_themes.py b/geoportal/tests/functional/test_lingva_extractor_themes.py index 55f2556f62..ee5600423e 100644 --- a/geoportal/tests/functional/test_lingva_extractor_themes.py +++ b/geoportal/tests/functional/test_lingva_extractor_themes.py @@ -29,8 +29,6 @@ from unittest.mock import Mock -import pytest - from c2cgeoportal_geoportal.lib.lingva_extractor import GeomapfishThemeExtractor diff --git a/geoportal/tests/functional/test_login.py b/geoportal/tests/functional/test_login.py index 80d8e67148..4a1b2297e8 100644 --- a/geoportal/tests/functional/test_login.py +++ b/geoportal/tests/functional/test_login.py @@ -36,10 +36,14 @@ import transaction from geoalchemy2 import WKTElement from pyramid import testing -from tests.functional import cleanup_db, create_dummy_request, fill_tech_user_functionality, mapserv_url -from tests.functional import setup_common as setup_module # noqa, pylint: disable=unused-import -from tests.functional import setup_db -from tests.functional import teardown_common as teardown_module # noqa, pylint: disable=unused-import + +from tests.functional import ( + cleanup_db, + create_dummy_request, + fill_tech_user_functionality, + mapserv_url, + setup_db, +) _LOG = logging.getLogger(__name__) @@ -102,11 +106,10 @@ def _create_request_obj(username=None, params=None, **kwargs): # def test_login_success(self): - from pyramid.httpexceptions import HTTPUnauthorized - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.static import User from c2cgeoportal_geoportal.views.login import Login + from pyramid.httpexceptions import HTTPUnauthorized user = DBSession.query(User).filter_by(username="__test_user1").one() user.is_password_changed = True @@ -136,9 +139,8 @@ def test_login_success(self): self.assertRaises(HTTPUnauthorized, login.login) def test_logout_no_auth(self): - from pyramid.httpexceptions import HTTPUnauthorized - from c2cgeoportal_geoportal.views.login import Login + from pyramid.httpexceptions import HTTPUnauthorized request = self._create_request_obj(path="/", params={"came_from": "/came_from"}) login = Login(request) @@ -209,18 +211,16 @@ def test_reset_password(self): self.assertNotEqual(len(user.password), 0) def test_change_password_no_params(self): - from pyramid.httpexceptions import HTTPBadRequest - from c2cgeoportal_geoportal.views.login import Login + from pyramid.httpexceptions import HTTPBadRequest request = self._create_request_obj(username="__test_user1", params={"lang": "en"}, POST={}) login = Login(request) self.assertRaises(HTTPBadRequest, login.change_password) def test_change_password_wrong_old(self): - from pyramid.httpexceptions import HTTPUnauthorized - from c2cgeoportal_geoportal.views.login import Login + from pyramid.httpexceptions import HTTPUnauthorized request = self._create_request_obj( username="__test_user1", @@ -232,9 +232,8 @@ def test_change_password_wrong_old(self): login.change_password() def test_change_password_different(self): - from pyramid.httpexceptions import HTTPBadRequest - from c2cgeoportal_geoportal.views.login import Login + from pyramid.httpexceptions import HTTPBadRequest request = self._create_request_obj( username="__test_user1", @@ -362,10 +361,10 @@ class F: assert request.response.headers["Vary"] == "Origin, Access-Control-Request-Headers, Cookie" def test_intranet(self): - from tests import DummyRequest - from c2cgeoportal_geoportal.views.login import Login + from tests import DummyRequest + request = DummyRequest() request.registry.settings = {"intranet": {"networks": ["192.168.1.0/255.255.255.0"]}} request.user = None diff --git a/geoportal/tests/functional/test_login_2fa.py b/geoportal/tests/functional/test_login_2fa.py index 164abfbbb5..c69db084ff 100644 --- a/geoportal/tests/functional/test_login_2fa.py +++ b/geoportal/tests/functional/test_login_2fa.py @@ -37,10 +37,8 @@ import pytest import transaction from pyramid import testing -from tests.functional import cleanup_db, create_dummy_request -from tests.functional import setup_common as setup_module # noqa, pylint: disable=unused-import -from tests.functional import setup_db -from tests.functional import teardown_common as teardown_module # noqa, pylint: disable=unused-import + +from tests.functional import cleanup_db, create_dummy_request, setup_db _LOG = logging.getLogger(__name__) diff --git a/geoportal/tests/functional/test_mapserverproxy.py b/geoportal/tests/functional/test_mapserverproxy.py index 9250dc5cb8..1d31d04826 100644 --- a/geoportal/tests/functional/test_mapserverproxy.py +++ b/geoportal/tests/functional/test_mapserverproxy.py @@ -62,15 +62,16 @@ import transaction from geoalchemy2 import WKTElement + from tests.functional import ( cleanup_db, create_default_ogcserver, create_dummy_request, fill_tech_user_functionality, mapserv_url, + setup_db, ) from tests.functional import setup_common as setup_module # noqa -from tests.functional import setup_db from tests.functional import teardown_common as teardown_module # noqa from tests.functional.geodata_model import PointTest @@ -1006,9 +1007,8 @@ def test_geoserver(self): assert "testpoint_protected" in response.body.decode("utf-8") def test_authentication_required(self): - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.mapserverproxy import MapservProxy + from pyramid.httpexceptions import HTTPForbidden request = self._create_getcap_request() request.params.update( diff --git a/geoportal/tests/functional/test_mapserverproxy_capabilities.py b/geoportal/tests/functional/test_mapserverproxy_capabilities.py index f3c824d892..5d00dbff0f 100644 --- a/geoportal/tests/functional/test_mapserverproxy_capabilities.py +++ b/geoportal/tests/functional/test_mapserverproxy_capabilities.py @@ -31,9 +31,15 @@ from unittest import TestCase import transaction -from tests.functional import cleanup_db, create_default_ogcserver, create_dummy_request, mapserv_url + +from tests.functional import ( + cleanup_db, + create_default_ogcserver, + create_dummy_request, + mapserv_url, + setup_db, +) from tests.functional import setup_common as setup_module # noqa -from tests.functional import setup_db from tests.functional import teardown_common as teardown_module # noqa diff --git a/geoportal/tests/functional/test_mapserverproxy_group.py b/geoportal/tests/functional/test_mapserverproxy_group.py index 55cdba395d..56930856ff 100644 --- a/geoportal/tests/functional/test_mapserverproxy_group.py +++ b/geoportal/tests/functional/test_mapserverproxy_group.py @@ -32,6 +32,7 @@ import transaction from geoalchemy2 import WKTElement + from tests.functional import create_default_ogcserver, create_dummy_request from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa @@ -40,7 +41,12 @@ class TestMapserverproxyViewGroup(TestCase): def setup_method(self, _): from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, LayerWMS, RestrictionArea, Role + from c2cgeoportal_commons.models.main import ( + Interface, + LayerWMS, + RestrictionArea, + Role, + ) from c2cgeoportal_commons.models.static import User ogc_server_internal = create_default_ogcserver(DBSession) @@ -72,7 +78,13 @@ def setup_method(self, _): def teardown_method(self, _): from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, LayerWMS, OGCServer, RestrictionArea, Role + from c2cgeoportal_commons.models.main import ( + Interface, + LayerWMS, + OGCServer, + RestrictionArea, + Role, + ) from c2cgeoportal_commons.models.static import User DBSession.delete(DBSession.query(User).filter(User.username == "__test_user1").one()) diff --git a/geoportal/tests/functional/test_mobile_desktop.py b/geoportal/tests/functional/test_mobile_desktop.py index c64c1cad26..6ec5d0ac0b 100644 --- a/geoportal/tests/functional/test_mobile_desktop.py +++ b/geoportal/tests/functional/test_mobile_desktop.py @@ -32,6 +32,7 @@ import transaction from pyramid import testing + from tests.functional import create_default_ogcserver, create_dummy_request, mapserv_url from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa @@ -44,7 +45,12 @@ def setup_method(self, _): self.maxDiff = None from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, LayerGroup, LayerWMS, Theme + from c2cgeoportal_commons.models.main import ( + Interface, + LayerGroup, + LayerWMS, + Theme, + ) create_default_ogcserver(DBSession) main = Interface(name="main") @@ -102,7 +108,13 @@ def teardown_method(self, _): testing.tearDown() from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, LayerGroup, LayerWMS, OGCServer, Theme + from c2cgeoportal_commons.models.main import ( + Interface, + LayerGroup, + LayerWMS, + OGCServer, + Theme, + ) for t in DBSession.query(Theme).all(): DBSession.delete(t) diff --git a/geoportal/tests/functional/test_oauth2.py b/geoportal/tests/functional/test_oauth2.py index 145de44097..5d925c63d6 100644 --- a/geoportal/tests/functional/test_oauth2.py +++ b/geoportal/tests/functional/test_oauth2.py @@ -37,9 +37,8 @@ import pyramid.testing import pytest import transaction + from tests.functional import cleanup_db, create_dummy_request, init_registry -from tests.functional import setup_common as setup_module # pylint: disable=unused-import -from tests.functional import teardown_common as teardown_module # pylint: disable=unused-import _LOG = logging.getLogger(__name__) @@ -588,7 +587,7 @@ def test_pkce_oauth2_protocol_test_login_get_token_refresh_token_is_login(self) request.method = "POST" request.body = "" with pytest.raises(pyramid.httpexceptions.HTTPFound) as exc_info: - assert False, Login(request).login().body + raise AssertionError(Login(request).login().body) url = exc_info.value.headers["Location"] url_split = urllib.parse.urlsplit(url) query = dict(urllib.parse.parse_qsl(url_split.fragment)) @@ -684,7 +683,7 @@ def test_pkce_state_oauth2_protocol_test_login_get_token_refresh_token_is_login( request.method = "POST" request.body = "" with pytest.raises(pyramid.httpexceptions.HTTPFound) as exc_info: - assert False, Login(request).login().body + raise AssertionError(Login(request).login().body) url = exc_info.value.headers["Location"] url_split = urllib.parse.urlsplit(url) query = dict(urllib.parse.parse_qsl(url_split.fragment)) @@ -766,7 +765,7 @@ def test_oauth2_protocol_test_login_no_state(self) -> None: request.host = "127.0.0.1:7070" request.method = "POST" request.body = "" - with pytest.raises(pyramid.httpexceptions.HTTPBadRequest) as exc_info: + with pytest.raises(pyramid.httpexceptions.HTTPBadRequest): Login(request).login() def test_oauth2_protocol_test_login_no_pkce(self) -> None: @@ -814,7 +813,7 @@ def test_pkce_oauth2_protocol_test_login_no_state(self) -> None: request.host = "127.0.0.1:7070" request.method = "POST" request.body = "" - with pytest.raises(pyramid.httpexceptions.HTTPBadRequest) as exc_info: + with pytest.raises(pyramid.httpexceptions.HTTPBadRequest): Login(request).login() def test_pkce_oauth2_protocol_test_login_wrong_code(self) -> None: @@ -846,7 +845,7 @@ def test_pkce_oauth2_protocol_test_login_wrong_code(self) -> None: request.method = "POST" request.body = "" with pytest.raises(pyramid.httpexceptions.HTTPFound) as exc_info: - assert False, Login(request).login().body + raise AssertionError(Login(request).login().body) url = exc_info.value.headers["Location"] url_split = urllib.parse.urlsplit(url) query = dict(urllib.parse.parse_qsl(url_split.fragment)) diff --git a/geoportal/tests/functional/test_ogcproxy.py b/geoportal/tests/functional/test_ogcproxy.py index d026f48e66..7e5c92c022 100644 --- a/geoportal/tests/functional/test_ogcproxy.py +++ b/geoportal/tests/functional/test_ogcproxy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022-2023, Camptocamp SA +# Copyright (c) 2022-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -31,9 +31,9 @@ from unittest import TestCase import transaction -from tests.functional import cleanup_db, create_dummy_request + +from tests.functional import cleanup_db, create_dummy_request, setup_db from tests.functional import setup_common as setup_module # noqa -from tests.functional import setup_db from tests.functional import teardown_common as teardown_module # noqa diff --git a/geoportal/tests/functional/test_oidc.py b/geoportal/tests/functional/test_oidc.py index 21e7e8d688..6a6c92a5c6 100644 --- a/geoportal/tests/functional/test_oidc.py +++ b/geoportal/tests/functional/test_oidc.py @@ -7,14 +7,11 @@ import jwt import responses +from c2cgeoportal_geoportal.lib import oidc from cryptography.hazmat.primitives.asymmetric import rsa from pyramid import testing -from tests.functional import cleanup_db, create_dummy_request -from tests.functional import setup_common as setup_module # noqa, pylint: disable=unused-import -from tests.functional import setup_db -from tests.functional import teardown_common as teardown_module # noqa, pylint: disable=unused-import -from c2cgeoportal_geoportal.lib import oidc +from tests.functional import cleanup_db, create_dummy_request, setup_db _OIDC_CONFIGURATION = { "issuer": "https://sso.example.com", diff --git a/geoportal/tests/functional/test_request_property.py b/geoportal/tests/functional/test_request_property.py index 8b0f74bfaf..3f17926336 100644 --- a/geoportal/tests/functional/test_request_property.py +++ b/geoportal/tests/functional/test_request_property.py @@ -1,4 +1,4 @@ -# Copyright (c) 2011-2023, Camptocamp SA +# Copyright (c) 2011-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -39,7 +39,6 @@ class TestRequestProperty(TestCase): def setup_method(self, _): import transaction - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import Role from c2cgeoportal_commons.models.static import User @@ -52,7 +51,6 @@ def setup_method(self, _): def teardown_method(self, _): import transaction - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import Role from c2cgeoportal_commons.models.static import User @@ -65,7 +63,7 @@ def teardown_method(self, _): def test_request_no_auth(self): request = create_dummy_request() - assert request.user == None + assert request.user is None def test_request_auth(self): request = create_dummy_request(authentication=False, user="__test_user") @@ -91,7 +89,7 @@ def test_request_wrong_auth(self): } ) - assert request.user == None + assert request.user is None def test_request_auth_overwritten_property(self): def setter(request): diff --git a/geoportal/tests/functional/test_shortener.py b/geoportal/tests/functional/test_shortener.py index 23b1b76189..3f27f21bfc 100644 --- a/geoportal/tests/functional/test_shortener.py +++ b/geoportal/tests/functional/test_shortener.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2023, Camptocamp SA +# Copyright (c) 2013-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -31,6 +31,7 @@ from unittest import TestCase from pyramid import testing + from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa @@ -40,7 +41,6 @@ def teardown_method(self, _): testing.tearDown() import transaction - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.static import Shorturl @@ -48,10 +48,10 @@ def teardown_method(self, _): transaction.commit() def test_shortener(self): + from c2cgeoportal_geoportal.views.shortener import Shortener from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound - from tests import DummyRequest - from c2cgeoportal_geoportal.views.shortener import Shortener + from tests import DummyRequest def route_url(name, *args, **kwargs): del name # Unused @@ -78,10 +78,10 @@ def route_url(name, *args, **kwargs): self.assertRaises(HTTPBadRequest, shortener.create) def test_shortener_create_1(self): + from c2cgeoportal_geoportal.views.shortener import Shortener from pyramid.httpexceptions import HTTPFound - from tests import DummyRequest - from c2cgeoportal_geoportal.views.shortener import Shortener + from tests import DummyRequest def route_url(name, *elements, **kw): return "https://example.com/short/" + kw["ref"] @@ -106,10 +106,10 @@ def route_url(name, *elements, **kw): assert result.location == "https://example.com/hi" def test_shortener_create_2(self): + from c2cgeoportal_geoportal.views.shortener import Shortener from pyramid.httpexceptions import HTTPFound - from tests import DummyRequest - from c2cgeoportal_geoportal.views.shortener import Shortener + from tests import DummyRequest def route_url(name, *elements, **kw): return "https://example.com/short/" + kw["ref"] @@ -136,10 +136,10 @@ def route_url(name, *elements, **kw): assert result.location == "https://example.com/hi" def test_shortener_noreplace_1(self): - from tests import DummyRequest - from c2cgeoportal_geoportal.views.shortener import Shortener + from tests import DummyRequest + def route_url(name, *elements, **kw): return "https://example.com/short/" + kw["ref"] @@ -156,10 +156,10 @@ def route_url(name, *elements, **kw): assert result["short_url"] == "https://example.com/s/truite" def test_shortener_noreplace_2(self): - from tests import DummyRequest - from c2cgeoportal_geoportal.views.shortener import Shortener + from tests import DummyRequest + def route_url(name, *elements, **kw): return "https://example.com/short/" + kw["ref"] @@ -176,10 +176,10 @@ def route_url(name, *elements, **kw): assert result["short_url"] == "https://example.com/s/truite" def test_shortener_baseurl(self): - from tests import DummyRequest - from c2cgeoportal_geoportal.views.shortener import Shortener + from tests import DummyRequest + def route_url(name, *elements, **kw): return "https://example.com/short/" + kw["ref"] @@ -197,10 +197,10 @@ def route_url(name, *elements, **kw): assert result["short_url"][:index] == "http://my_host/my_short" def test_shortener_dev(self): - from tests import DummyRequest - from c2cgeoportal_geoportal.views.shortener import Shortener + from tests import DummyRequest + def route_url(name, *elements, **kw): return "https://localhost:8484/s/" + kw["ref"] diff --git a/geoportal/tests/functional/test_theme2fts.py b/geoportal/tests/functional/test_theme2fts.py index 302fd93cc7..5ac877309a 100644 --- a/geoportal/tests/functional/test_theme2fts.py +++ b/geoportal/tests/functional/test_theme2fts.py @@ -32,7 +32,7 @@ import pytest from c2c.template.config import config as configuration -from sqlalchemy import func + from tests.functional import setup_common as setup_module diff --git a/geoportal/tests/functional/test_themes.py b/geoportal/tests/functional/test_themes.py index 9261669204..969ca64392 100644 --- a/geoportal/tests/functional/test_themes.py +++ b/geoportal/tests/functional/test_themes.py @@ -31,6 +31,11 @@ import pytest import transaction from tests.functional import create_dummy_request +from pyramid import testing + +from tests.functional import create_default_ogcserver, create_dummy_request +from tests.functional import setup_common as setup_module # noqa +from tests.functional import teardown_common as teardown_module # noqa @pytest.fixture() diff --git a/geoportal/tests/functional/test_themes_dimensions.py b/geoportal/tests/functional/test_themes_dimensions.py index 4fca063143..aec8eaf697 100644 --- a/geoportal/tests/functional/test_themes_dimensions.py +++ b/geoportal/tests/functional/test_themes_dimensions.py @@ -33,6 +33,7 @@ import transaction from pyramid import testing + from tests.functional import create_default_ogcserver, create_dummy_request, mapserv_url from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa @@ -152,7 +153,12 @@ def teardown_method(self, _): testing.tearDown() from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Dimension, Interface, OGCServer, TreeItem + from c2cgeoportal_commons.models.main import ( + Dimension, + Interface, + OGCServer, + TreeItem, + ) DBSession.query(Dimension).delete() for item in DBSession.query(TreeItem).all(): diff --git a/geoportal/tests/functional/test_themes_edit_columns.py b/geoportal/tests/functional/test_themes_edit_columns.py index d4ee1a6e4a..595534b242 100644 --- a/geoportal/tests/functional/test_themes_edit_columns.py +++ b/geoportal/tests/functional/test_themes_edit_columns.py @@ -32,9 +32,13 @@ from unittest import TestCase from urllib.parse import urlencode -from tests.functional import cleanup_db, create_default_ogcserver, create_dummy_request +from tests.functional import ( + cleanup_db, + create_default_ogcserver, + create_dummy_request, + setup_db, +) from tests.functional import setup_common as setup_module # noqa -from tests.functional import setup_db from tests.functional import teardown_common as teardown_module # noqa if TYPE_CHECKING: @@ -54,7 +58,6 @@ def setup_method(self, _: Any) -> None: self._tables = [] import transaction - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import ( OGCSERVER_AUTH_NOAUTH, @@ -93,7 +96,6 @@ def setup_method(self, _: Any) -> None: def teardown_method(self, _: Any) -> None: import transaction - from c2cgeoportal_commons.models import DBSession cleanup_db() @@ -115,13 +117,12 @@ def _create_layer( It creates a layer with two features, and associates a restriction area to it. """ import transaction + from c2cgeoportal_commons.models import DBSession + from c2cgeoportal_commons.models.main import LayerWMS, RestrictionArea from geoalchemy2 import Geometry from sqlalchemy import Column, ForeignKey, Table, types from sqlalchemy.ext.declarative import declarative_base - from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import LayerWMS, RestrictionArea - self.__class__._table_index += 1 id = self.__class__._table_index @@ -224,7 +225,6 @@ def _get_request(layerid, username=None, params=None) -> None: return request def test_themes_edit_columns(self) -> None: - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_geoportal.views.theme import Theme layer_id = self._create_layer(geom_type=True) @@ -297,7 +297,7 @@ def test_themes_edit_columns_extras(self) -> None: theme_view = Theme(self._get_request(layer_id, username="__test_user", params={"interface": "main"})) themes = theme_view.themes() - assert [] == themes["errors"] + assert themes["errors"] == [] layers = themes["themes"][0]["children"][0]["children"] self.assertEqual( diff --git a/geoportal/tests/functional/test_themes_editing.py b/geoportal/tests/functional/test_themes_editing.py index 7a3bb496fe..59187db73a 100644 --- a/geoportal/tests/functional/test_themes_editing.py +++ b/geoportal/tests/functional/test_themes_editing.py @@ -33,9 +33,15 @@ import transaction from geoalchemy2 import WKTElement from pyramid import testing -from tests.functional import cleanup_db, create_default_ogcserver, create_dummy_request, mapserv_url + +from tests.functional import ( + cleanup_db, + create_default_ogcserver, + create_dummy_request, + mapserv_url, + setup_db, +) from tests.functional import setup_common as setup_module # noqa -from tests.functional import setup_db from tests.functional import teardown_common as teardown_module # noqa @@ -46,10 +52,6 @@ def setup_method(self, _): self.maxDiff = None self._tables = [] - from geoalchemy2 import Geometry - from sqlalchemy import Column, Table, types - from sqlalchemy.ext.declarative import declarative_base - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import ( Interface, @@ -60,6 +62,9 @@ def setup_method(self, _): Theme, ) from c2cgeoportal_commons.models.static import User + from geoalchemy2 import Geometry + from sqlalchemy import Column, Table, types + from sqlalchemy.ext.declarative import declarative_base setup_db() diff --git a/geoportal/tests/functional/test_themes_entry.py b/geoportal/tests/functional/test_themes_entry.py index cd702bd676..0080268407 100644 --- a/geoportal/tests/functional/test_themes_entry.py +++ b/geoportal/tests/functional/test_themes_entry.py @@ -32,14 +32,17 @@ from unittest import TestCase import transaction +from c2cgeoportal_geoportal.lib.caching import invalidate_region from geoalchemy2 import WKTElement from pyramid import testing -from tests.functional import cleanup_db, create_default_ogcserver, create_dummy_request, mapserv_url -from tests.functional import setup_common as setup_module # noqa, pylint: disable=unused-import -from tests.functional import setup_db -from tests.functional import teardown_common as teardown_module # noqa, pylint: disable=unused-import -from c2cgeoportal_geoportal.lib.caching import invalidate_region +from tests.functional import ( + cleanup_db, + create_default_ogcserver, + create_dummy_request, + mapserv_url, + setup_db, +) _LOG = logging.getLogger(__name__) @@ -51,10 +54,6 @@ def setup_method(self, _): self.maxDiff = None # pylint: disable=invalid-name self._tables = [] - from geoalchemy2 import Geometry - from sqlalchemy import Column, Table, func, types - from sqlalchemy.ext.declarative import declarative_base - from c2cgeoportal_commons.models import DBSession from c2cgeoportal_commons.models.main import ( OGCSERVER_AUTH_GEOSERVER, @@ -70,6 +69,9 @@ def setup_method(self, _): Theme, ) from c2cgeoportal_commons.models.static import User + from geoalchemy2 import Geometry + from sqlalchemy import Column, Table, func, types + from sqlalchemy.ext.declarative import declarative_base setup_db() diff --git a/geoportal/tests/functional/test_themes_layermultinameerror.py b/geoportal/tests/functional/test_themes_layermultinameerror.py index c9c4f7f553..75be46c4dd 100644 --- a/geoportal/tests/functional/test_themes_layermultinameerror.py +++ b/geoportal/tests/functional/test_themes_layermultinameerror.py @@ -32,6 +32,7 @@ import transaction from pyramid import testing + from tests.functional import create_default_ogcserver, create_dummy_request, mapserv_url from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa @@ -44,7 +45,12 @@ def setup_method(self, _): self.maxDiff = None from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, LayerGroup, LayerWMS, Theme + from c2cgeoportal_commons.models.main import ( + Interface, + LayerGroup, + LayerWMS, + Theme, + ) main = Interface(name="desktop") diff --git a/geoportal/tests/functional/test_themes_loop.py b/geoportal/tests/functional/test_themes_loop.py index 2731c10edc..8186a58e30 100644 --- a/geoportal/tests/functional/test_themes_loop.py +++ b/geoportal/tests/functional/test_themes_loop.py @@ -32,6 +32,7 @@ import transaction from pyramid import testing + from tests import DummyRequest from tests.functional import create_default_ogcserver, mapserv_url from tests.functional import setup_common as setup_module # noqa @@ -45,7 +46,12 @@ def setup_method(self, _): self.maxDiff = None from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, LayerGroup, LayerWMS, Theme + from c2cgeoportal_commons.models.main import ( + Interface, + LayerGroup, + LayerWMS, + Theme, + ) ogc_server = create_default_ogcserver(DBSession) main = Interface(name="desktop2") @@ -69,7 +75,12 @@ def teardown_method(self, _): testing.tearDown() from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, OGCServer, Theme + from c2cgeoportal_commons.models.main import ( + LayerGroup, + LayerWMS, + OGCServer, + Theme, + ) for t in DBSession.query(Theme).filter(Theme.name == "__test_theme").all(): DBSession.delete(t) diff --git a/geoportal/tests/functional/test_themes_metadata.py b/geoportal/tests/functional/test_themes_metadata.py index d1e262b98a..34330b99bb 100644 --- a/geoportal/tests/functional/test_themes_metadata.py +++ b/geoportal/tests/functional/test_themes_metadata.py @@ -33,6 +33,7 @@ import transaction from pyramid import testing + from tests.functional import create_default_ogcserver, create_dummy_request from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa @@ -45,7 +46,13 @@ def setup_method(self, _): self.maxDiff = None from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, LayerGroup, LayerWMS, Metadata, Theme + from c2cgeoportal_commons.models.main import ( + Interface, + LayerGroup, + LayerWMS, + Metadata, + Theme, + ) desktop = Interface(name="desktop") diff --git a/geoportal/tests/functional/test_themes_mixed.py b/geoportal/tests/functional/test_themes_mixed.py index fb920569d3..3a678a3097 100644 --- a/geoportal/tests/functional/test_themes_mixed.py +++ b/geoportal/tests/functional/test_themes_mixed.py @@ -33,6 +33,7 @@ import transaction from pyramid import testing + from tests.functional import create_default_ogcserver, create_dummy_request, mapserv_url from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa @@ -152,7 +153,13 @@ def teardown_method(self, _): testing.tearDown() from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Dimension, Interface, Metadata, OGCServer, TreeItem + from c2cgeoportal_commons.models.main import ( + Dimension, + Interface, + Metadata, + OGCServer, + TreeItem, + ) DBSession.query(Metadata).delete() DBSession.query(Dimension).delete() diff --git a/geoportal/tests/functional/test_themes_nameerror.py b/geoportal/tests/functional/test_themes_nameerror.py index e309fc7c09..316c46b5af 100644 --- a/geoportal/tests/functional/test_themes_nameerror.py +++ b/geoportal/tests/functional/test_themes_nameerror.py @@ -32,6 +32,7 @@ import transaction from pyramid import testing + from tests.functional import create_default_ogcserver, create_dummy_request, mapserv_url from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa @@ -44,7 +45,12 @@ def setup_method(self, _): self.maxDiff = None from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, LayerGroup, LayerWMS, Theme + from c2cgeoportal_commons.models.main import ( + Interface, + LayerGroup, + LayerWMS, + Theme, + ) main = Interface(name="desktop") @@ -69,7 +75,13 @@ def teardown_method(self, _): testing.tearDown() from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, Layer, LayerGroup, OGCServer, Theme + from c2cgeoportal_commons.models.main import ( + Interface, + Layer, + LayerGroup, + OGCServer, + Theme, + ) for layer in DBSession.query(Layer).all(): DBSession.delete(layer) diff --git a/geoportal/tests/functional/test_themes_ogc_server_cache_clean.py b/geoportal/tests/functional/test_themes_ogc_server_cache_clean.py index 9ad8151e65..6f75bba8e5 100644 --- a/geoportal/tests/functional/test_themes_ogc_server_cache_clean.py +++ b/geoportal/tests/functional/test_themes_ogc_server_cache_clean.py @@ -34,13 +34,13 @@ import pytest import responses import transaction +from c2cgeoportal_geoportal.lib import caching from pyramid import testing + from tests.functional import create_default_ogcserver, create_dummy_request from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa -from c2cgeoportal_geoportal.lib import caching - CAP = """ @@ -215,7 +215,11 @@ def teardown_method(self, _): testing.tearDown() from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Dimension, Interface, Metadata, OGCServer, TreeItem + from c2cgeoportal_commons.models.main import ( + Interface, + OGCServer, + TreeItem, + ) for item in DBSession.query(TreeItem).all(): DBSession.delete(item) diff --git a/geoportal/tests/functional/test_themes_private.py b/geoportal/tests/functional/test_themes_private.py index 6a58695601..e188c556f2 100644 --- a/geoportal/tests/functional/test_themes_private.py +++ b/geoportal/tests/functional/test_themes_private.py @@ -31,6 +31,7 @@ import transaction from pyramid import testing + from tests.functional import create_default_ogcserver, create_dummy_request from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa @@ -120,7 +121,13 @@ def tearDown(self): # noqa @staticmethod def clean(): from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, OGCServer, RestrictionArea, Role, TreeItem + from c2cgeoportal_commons.models.main import ( + Interface, + OGCServer, + RestrictionArea, + Role, + TreeItem, + ) from c2cgeoportal_commons.models.static import User for obj in DBSession.query(RestrictionArea).all(): diff --git a/geoportal/tests/functional/test_themes_scale.py b/geoportal/tests/functional/test_themes_scale.py index 87658ae1dd..46e9b52dfc 100644 --- a/geoportal/tests/functional/test_themes_scale.py +++ b/geoportal/tests/functional/test_themes_scale.py @@ -31,6 +31,7 @@ import transaction from pyramid import testing + from tests.functional import create_default_ogcserver, create_dummy_request, mapserv_url from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa @@ -43,7 +44,13 @@ def setup_method(self, _): self.maxDiff = None from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, LayerGroup, LayerWMS, Metadata, Theme + from c2cgeoportal_commons.models.main import ( + Interface, + LayerGroup, + LayerWMS, + Metadata, + Theme, + ) main = Interface(name="desktop") @@ -96,7 +103,13 @@ def teardown_method(self, _): testing.tearDown() from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, Layer, LayerGroup, OGCServer, Theme + from c2cgeoportal_commons.models.main import ( + Interface, + Layer, + LayerGroup, + OGCServer, + Theme, + ) for layer in DBSession.query(Layer).all(): DBSession.delete(layer) diff --git a/geoportal/tests/functional/test_themes_time.py b/geoportal/tests/functional/test_themes_time.py index 1d8c3ce765..946d1d1a85 100644 --- a/geoportal/tests/functional/test_themes_time.py +++ b/geoportal/tests/functional/test_themes_time.py @@ -38,6 +38,7 @@ from pyramid import testing from sqlalchemy import Column from sqlalchemy.types import DateTime, Integer, Unicode + from tests.functional import create_default_ogcserver, create_dummy_request, mapserv_url from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa @@ -61,7 +62,13 @@ def setup_method(self, _): self.maxDiff = None from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, LayerGroup, LayerWMS, LayerWMTS, Theme + from c2cgeoportal_commons.models.main import ( + Interface, + LayerGroup, + LayerWMS, + LayerWMTS, + Theme, + ) DBSession.query(PointTest).delete() diff --git a/geoportal/tests/functional/test_tinyowsproxy.py b/geoportal/tests/functional/test_tinyowsproxy.py index defaacd638..bc3ca1827a 100644 --- a/geoportal/tests/functional/test_tinyowsproxy.py +++ b/geoportal/tests/functional/test_tinyowsproxy.py @@ -33,11 +33,15 @@ import responses import transaction from geoalchemy2 import WKTElement -from pyramid.response import Response + from tests import load_file -from tests.functional import cleanup_db, create_default_ogcserver, create_dummy_request +from tests.functional import ( + cleanup_db, + create_default_ogcserver, + create_dummy_request, + setup_db, +) from tests.functional import setup_common as setup_module # noqa -from tests.functional import setup_db from tests.functional import teardown_common as teardown_module # noqa @@ -74,7 +78,12 @@ def setup_method(self, _): self.maxDiff = None from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_commons.models.main import Interface, LayerWMS, RestrictionArea, Role + from c2cgeoportal_commons.models.main import ( + Interface, + LayerWMS, + RestrictionArea, + Role, + ) from c2cgeoportal_commons.models.static import User setup_db() @@ -124,9 +133,8 @@ def teardown_method(self, _): cleanup_db() def test_proxy_not_auth(self): - from pyramid.httpexceptions import HTTPUnauthorized - from c2cgeoportal_geoportal.views.tinyowsproxy import TinyOWSProxy + from pyramid.httpexceptions import HTTPUnauthorized request = _create_dummy_request() @@ -150,7 +158,7 @@ def test_proxy_get_capabilities_user1(self): load_file(self.capabilities_response_filtered_file1).strip(), response.body.decode().replace(" \n", "").strip(), ) - assert "200 OK" == response.status + assert response.status == "200 OK" @responses.activate def test_proxy_get_capabilities_user2(self): @@ -169,7 +177,7 @@ def test_proxy_get_capabilities_user2(self): load_file(self.capabilities_response_filtered_file2).strip(), response.body.decode().replace(" \n", "").strip(), ) - assert "200 OK" == response.status + assert response.status == "200 OK" @responses.activate def test_proxy_get_capabilities_get(self): @@ -189,7 +197,7 @@ def test_proxy_get_capabilities_get(self): load_file(self.capabilities_response_filtered_file1).strip(), response.body.decode().replace(" \n", "").strip(), ) - assert "200 OK" == response.status + assert response.status == "200 OK" @responses.activate def test_proxy_get_capabilities_post(self): @@ -211,12 +219,11 @@ def test_proxy_get_capabilities_post(self): response.body.decode().replace(" \n", "").strip(), ) - assert "200 OK" == response.status + assert response.status == "200 OK" def test_proxy_get_capabilities_post_invalid_body(self): - from pyramid.httpexceptions import HTTPBadRequest - from c2cgeoportal_geoportal.views.tinyowsproxy import TinyOWSProxy + from pyramid.httpexceptions import HTTPBadRequest request = _create_dummy_request(username="__test_user1") request.method = "POST" @@ -248,12 +255,11 @@ def test_proxy_describe_feature_type_get(self): response = TinyOWSProxy(request).proxy() - assert "200 OK" == response.status + assert response.status == "200 OK" def test_proxy_describe_feature_type_invalid_layer(self): - from pyramid.httpexceptions import HTTPForbidden - from c2cgeoportal_geoportal.views.tinyowsproxy import TinyOWSProxy + from pyramid.httpexceptions import HTTPForbidden request = _create_dummy_request(username="__test_user1") request.params.update( @@ -278,12 +284,11 @@ def test_proxy_describe_feature_type_post(self): response = TinyOWSProxy(request).proxy() - assert "200 OK" == response.status + assert response.status == "200 OK" def test_proxy_describe_feature_type_post_multiple_types(self): - from pyramid.httpexceptions import HTTPBadRequest - from c2cgeoportal_geoportal.views.tinyowsproxy import TinyOWSProxy + from pyramid.httpexceptions import HTTPBadRequest request = _create_dummy_request(username="__test_user1") request.method = "POST" @@ -324,7 +329,7 @@ def test_parse_body_getcapabilities(self): (operation, typename) = TinyOWSProxy(request)._parse_body(request.body) - assert "getcapabilities" == operation + assert operation == "getcapabilities" assert set() == typename def test_parse_body_describefeaturetype(self): @@ -335,7 +340,7 @@ def test_parse_body_describefeaturetype(self): (operation, typename) = TinyOWSProxy(request)._parse_body(request.body) - assert "describefeaturetype" == operation + assert operation == "describefeaturetype" assert {"layer_1"} == typename def test_parse_body_getfeature(self): @@ -346,7 +351,7 @@ def test_parse_body_getfeature(self): (operation, typename) = TinyOWSProxy(request)._parse_body(request.body) - assert "getfeature" == operation + assert operation == "getfeature" assert {"parks"} == typename def test_parse_body_lockfeature(self): @@ -357,7 +362,7 @@ def test_parse_body_lockfeature(self): (operation, typename) = TinyOWSProxy(request)._parse_body(request.body) - assert "lockfeature" == operation + assert operation == "lockfeature" assert {"parks"} == typename def test_parse_body_transaction_update(self): @@ -368,7 +373,7 @@ def test_parse_body_transaction_update(self): (operation, typename) = TinyOWSProxy(request)._parse_body(request.body) - assert "transaction" == operation + assert operation == "transaction" assert {"parks"} == typename def test_parse_body_transaction_delete(self): @@ -379,7 +384,7 @@ def test_parse_body_transaction_delete(self): (operation, typename) = TinyOWSProxy(request)._parse_body(request.body) - assert "transaction" == operation + assert operation == "transaction" assert {"parks"} == typename def test_parse_body_transaction_insert(self): @@ -390,5 +395,5 @@ def test_parse_body_transaction_insert(self): (operation, typename) = TinyOWSProxy(request)._parse_body(request.body) - assert "transaction" == operation + assert operation == "transaction" assert {"parks"} == typename diff --git a/geoportal/tests/functional/test_vector_tiles.py b/geoportal/tests/functional/test_vector_tiles.py index ddd038abc4..2ebf9b77a5 100644 --- a/geoportal/tests/functional/test_vector_tiles.py +++ b/geoportal/tests/functional/test_vector_tiles.py @@ -31,6 +31,7 @@ import pytest from geoalchemy2 import WKTElement from pyramid.httpexceptions import HTTPNotFound + from tests.functional.geodata_model import PointTest @@ -110,6 +111,6 @@ def test_vector_tiles_layer_not_found(self, dummy_request, test_data): request.matchdict["x"] = 0 request.matchdict["y"] = 0 - with pytest.raises(HTTPNotFound) as excinfo: + with pytest.raises(HTTPNotFound): VectorTilesViews(request).vector_tiles() assert "Not found any vector tile layer named not_existing_layer_name" diff --git a/geoportal/tests/functional/test_xsd.py b/geoportal/tests/functional/test_xsd.py index 18d65c9cb3..cb8bab0cb1 100644 --- a/geoportal/tests/functional/test_xsd.py +++ b/geoportal/tests/functional/test_xsd.py @@ -42,13 +42,12 @@ def setup_method(self, _): setup_module() import transaction + from c2cgeoportal_commons.models import DBSession + from c2cgeoportal_geoportal.lib.dbreflection import _AssociationProxy from sqlalchemy import Column, ForeignKey, types from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship - from c2cgeoportal_commons.models import DBSession - from c2cgeoportal_geoportal.lib.dbreflection import _AssociationProxy - # Always see the diff # https://docs.python.org/2/library/unittest.html#unittest.TestCase.maxDiff self.maxDiff = None @@ -90,7 +89,6 @@ class Parent(Base): # type: ignore def teardown_method(self, _): import transaction - from c2cgeoportal_commons.models import DBSession transaction.commit() @@ -116,9 +114,8 @@ def test_add_class_properties_xsd_column_order(self, column_mock): @patch("c2cgeoportal_geoportal.lib.xsd.XSDGenerator.add_association_proxy_xsd") @patch("c2cgeoportal_geoportal.lib.xsd.PapyrusXSDGenerator.add_column_property_xsd") def test_add_column_property_xsd(self, column_mock, proxy_mock): - from sqlalchemy.orm.util import class_mapper - from c2cgeoportal_geoportal.lib.xsd import XSDGenerator + from sqlalchemy.orm.util import class_mapper gen = XSDGenerator(include_foreign_keys=True) @@ -136,9 +133,8 @@ def test_add_column_property_xsd(self, column_mock, proxy_mock): def test_add_column_readonly(self): from xml.etree.ElementTree import TreeBuilder, tostring - from sqlalchemy.orm.util import class_mapper - from c2cgeoportal_geoportal.lib.xsd import XSDGenerator + from sqlalchemy.orm.util import class_mapper gen = XSDGenerator(include_foreign_keys=True) mapper = class_mapper(self.cls) @@ -162,9 +158,8 @@ def test_add_column_readonly(self): def test_add_association_proxy_xsd(self): from xml.etree.ElementTree import TreeBuilder, tostring - from sqlalchemy.orm.util import class_mapper - from c2cgeoportal_geoportal.lib.xsd import XSDGenerator + from sqlalchemy.orm.util import class_mapper gen = XSDGenerator(include_foreign_keys=True) diff --git a/geoportal/tests/test_cachebuster.py b/geoportal/tests/test_cachebuster.py index ff8017e735..fd379b22a4 100644 --- a/geoportal/tests/test_cachebuster.py +++ b/geoportal/tests/test_cachebuster.py @@ -1,4 +1,4 @@ -# Copyright (c) 2011-2023, Camptocamp SA +# Copyright (c) 2011-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -31,10 +31,10 @@ from unittest import TestCase import pyramid.registry -from tests import DummyRequest - from c2cgeoportal_geoportal.lib.caching import init_region +from tests import DummyRequest + def handler(request): return request.response diff --git a/geoportal/tests/test_caching.py b/geoportal/tests/test_caching.py index 2e5b5a5ae7..1261cdd3b7 100644 --- a/geoportal/tests/test_caching.py +++ b/geoportal/tests/test_caching.py @@ -30,11 +30,15 @@ from unittest import TestCase -from tests import DummyRequest - from c2cgeoportal_geoportal.lib.cacheversion import get_cache_version from c2cgeoportal_geoportal.lib.caching import init_region, invalidate_region -from c2cgeoportal_geoportal.lib.common_headers import CORS_METHODS, Cache, set_common_headers +from c2cgeoportal_geoportal.lib.common_headers import ( + CORS_METHODS, + Cache, + set_common_headers, +) + +from tests import DummyRequest class TestSetCorsHeaders(TestCase): @@ -55,9 +59,7 @@ def _do(method, headers, credentials=False, settings=SETTINGS): return dict(request.response.headers) def test_simple(self): - """ - Tests specified in http://www.w3.org/TR/cors/#resource-requests. - """ + """Tests specified in http://www.w3.org/TR/cors/#resource-requests.""" # 1. If the Origin header is not present terminate this set of steps. # The request is outside the scope of this specification. assert self._do("POST", {}) == { @@ -102,9 +104,7 @@ def test_simple(self): # Not implemented def test_preflight(self): - """ - Tests specified in http://www.w3.org/TR/cors/#resource-preflight-requests. - """ + """Tests specified in http://www.w3.org/TR/cors/#resource-preflight-requests.""" # 1. If the Origin header is not present terminate this set of steps. # The request is outside the scope of this specification. assert self._do("OPTIONS", {"Access-Control-Request-Method": "GET"}) == { diff --git a/geoportal/tests/test_checker.py b/geoportal/tests/test_checker.py index adcef25660..fb1b8fed19 100644 --- a/geoportal/tests/test_checker.py +++ b/geoportal/tests/test_checker.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2023, Camptocamp SA +# Copyright (c) 2013-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -30,10 +30,10 @@ from unittest import TestCase -from tests import DummyRequest - from c2cgeoportal_geoportal.lib.checker import build_url +from tests import DummyRequest + class TestExportCSVView(TestCase): def test_build_url_docker(self): diff --git a/geoportal/tests/test_headerstween.py b/geoportal/tests/test_headerstween.py index d8a815da88..64fe41d351 100644 --- a/geoportal/tests/test_headerstween.py +++ b/geoportal/tests/test_headerstween.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018-2023, Camptocamp SA +# Copyright (c) 2018-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -31,6 +31,7 @@ from unittest import TestCase import pyramid.registry + from tests import DummyRequest diff --git a/geoportal/tests/test_init.py b/geoportal/tests/test_init.py index 996a5ceb5f..1c94ef14da 100644 --- a/geoportal/tests/test_init.py +++ b/geoportal/tests/test_init.py @@ -31,12 +31,9 @@ from unittest import TestCase from unittest.mock import patch +import c2cgeoportal_geoportal import pytest from c2c.template.config import config -from pyramid import testing -from tests import DummyRequest - -import c2cgeoportal_geoportal from c2cgeoportal_geoportal import ( call_hook, create_get_user_from_request, @@ -44,6 +41,9 @@ is_valid_referrer, set_user_validator, ) +from pyramid import testing + +from tests import DummyRequest class TestIncludeme(TestCase): @@ -127,9 +127,7 @@ def test_is_valid_referrer(authorized, value, expected): class TestReferer(TestCase): - """ - Check that accessing something with a bad HTTP referrer is equivalent to a not authenticated query. - """ + """Check that accessing something with a bad HTTP referrer is equivalent to a not authenticated query.""" BASE1 = "http://example.com/app" BASE2 = "http://friend.com/app2" diff --git a/geoportal/tests/test_locale_negociator.py b/geoportal/tests/test_locale_negociator.py index a26b8f8b3a..5bb76ca24c 100644 --- a/geoportal/tests/test_locale_negociator.py +++ b/geoportal/tests/test_locale_negociator.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2023, Camptocamp SA +# Copyright (c) 2013-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -42,11 +42,10 @@ def test_lang_param(self): assert lang == "fr" def test_lang_is_not_available(self): + from c2cgeoportal_geoportal import locale_negotiator from pyramid.request import Request from pyramid.threadlocal import get_current_registry - from c2cgeoportal_geoportal import locale_negotiator - request = Request.blank("/") request.registry = get_current_registry() request.registry.settings = {"default_locale_name": "de", "available_locale_names": ["de", "es"]} @@ -56,11 +55,10 @@ def test_lang_is_not_available(self): assert lang == "de" def test_lang_is_available(self): + from c2cgeoportal_geoportal import locale_negotiator from pyramid.request import Request from pyramid.threadlocal import get_current_registry - from c2cgeoportal_geoportal import locale_negotiator - request = Request.blank("/") request.registry = get_current_registry() request.registry.settings = {"default_locale_name": "de", "available_locale_names": ["de", "es"]} diff --git a/geoportal/tests/test_mapserverproxy_route_predicate.py b/geoportal/tests/test_mapserverproxy_route_predicate.py index 3711365764..9d0ced65f5 100644 --- a/geoportal/tests/test_mapserverproxy_route_predicate.py +++ b/geoportal/tests/test_mapserverproxy_route_predicate.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2023, Camptocamp SA +# Copyright (c) 2013-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -30,11 +30,10 @@ from unittest import TestCase +from c2cgeoportal_geoportal import MapserverproxyRoutePredicate from pyramid.request import Request from pyramid.threadlocal import get_current_registry -from c2cgeoportal_geoportal import MapserverproxyRoutePredicate - class TestMapserverproxyRoutePredicate(TestCase): predicate = MapserverproxyRoutePredicate(None, None) diff --git a/geoportal/tests/test_raster.py b/geoportal/tests/test_raster.py index 56b23ff3d8..d81ea9b161 100644 --- a/geoportal/tests/test_raster.py +++ b/geoportal/tests/test_raster.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2023, Camptocamp SA +# Copyright (c) 2013-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -35,10 +35,10 @@ class TestRasterViews(TestCase): def test_raster(self): from decimal import Decimal + from c2cgeoportal_geoportal.views.raster import Raster from pyramid.httpexceptions import HTTPNotFound - from tests import DummyRequest - from c2cgeoportal_geoportal.views.raster import Raster + from tests import DummyRequest request = DummyRequest() request.registry.settings = { @@ -53,9 +53,9 @@ def test_raster(self): request.params["lon"] = "565000" request.params["lat"] = "218000" result = raster.raster() - assert result["dem1"] == None - assert result["dem2"] == None - assert result["dem3"] == None + assert result["dem1"] is None + assert result["dem2"] is None + assert result["dem3"] is None request.params["lon"] = "548000" request.params["lat"] = "216000" @@ -77,10 +77,10 @@ def test_raster(self): def test_raster_angle(self): from decimal import Decimal - from tests import DummyRequest - from c2cgeoportal_geoportal.views.raster import Raster + from tests import DummyRequest + request = DummyRequest() request.registry.settings = { "raster": { @@ -117,15 +117,15 @@ def test_raster_angle(self): request.params["lon"] = "547997.4" request.params["lat"] = "216003.5" result = raster.raster() - assert result["dem5"] == None + assert result["dem5"] is None def test_raster_vrt(self): from decimal import Decimal - from tests import DummyRequest - from c2cgeoportal_geoportal.views.raster import Raster + from tests import DummyRequest + request = DummyRequest() request.registry.settings = { "raster": { @@ -158,10 +158,10 @@ def test_absolute_path(self): def test_profile_json(self): from decimal import Decimal + from c2cgeoportal_geoportal.views.profile import Profile from pyramid.httpexceptions import HTTPNotFound - from tests import DummyRequest - from c2cgeoportal_geoportal.views.profile import Profile + from tests import DummyRequest request = DummyRequest() request.registry.settings = { diff --git a/geoportal/tests/test_wmstparsing.py b/geoportal/tests/test_wmstparsing.py index e4ccca04eb..9e0eb29429 100644 --- a/geoportal/tests/test_wmstparsing.py +++ b/geoportal/tests/test_wmstparsing.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2023, Camptocamp SA +# Copyright (c) 2013-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -42,7 +42,10 @@ def test_parse_values(self): self.assertTrue(isinstance(extent, TimeExtentValue)) def test_parse_interval(self): - from c2cgeoportal_geoportal.lib.wmstparsing import TimeExtentInterval, parse_extent + from c2cgeoportal_geoportal.lib.wmstparsing import ( + TimeExtentInterval, + parse_extent, + ) extent = parse_extent(["2000/2005/P1Y"], "2002") self.assertTrue(isinstance(extent, TimeExtentInterval)) @@ -80,7 +83,10 @@ def test_merge_values(self): ) def test_merge_interval(self): - from c2cgeoportal_geoportal.lib.wmstparsing import TimeExtentInterval, parse_extent + from c2cgeoportal_geoportal.lib.wmstparsing import ( + TimeExtentInterval, + parse_extent, + ) e1 = parse_extent(["2000/2005/P1Y"], "2000/2005") e2 = parse_extent(["2006/2010/P1Y"], "2006/2010") @@ -121,35 +127,35 @@ def test_parse_date_year(self): from c2cgeoportal_geoportal.lib.wmstparsing import _parse_date date = _parse_date("2010") - assert "year" == date[0] + assert date[0] == "year" self.assertEqual(datetime.datetime(2010, 0o1, 0o1, tzinfo=isodate.UTC), date[1]) def test_parse_date_month(self): from c2cgeoportal_geoportal.lib.wmstparsing import _parse_date date = _parse_date("2010-02") - assert "month" == date[0] + assert date[0] == "month" self.assertEqual(datetime.datetime(2010, 0o2, 0o1, tzinfo=isodate.UTC), date[1]) def test_parse_date(self): from c2cgeoportal_geoportal.lib.wmstparsing import _parse_date date = _parse_date("2010-02-03") - assert "day" == date[0] + assert date[0] == "day" self.assertEqual(datetime.datetime(2010, 0o2, 0o3, tzinfo=isodate.UTC), date[1]) def test_parse_datetime(self): from c2cgeoportal_geoportal.lib.wmstparsing import _parse_date date = _parse_date("2010-02-03T12:34") - assert "second" == date[0] + assert date[0] == "second" self.assertEqual(datetime.datetime(2010, 0o2, 0o3, 12, 34, tzinfo=isodate.UTC), date[1]) def test_parse_datetime_tz(self): from c2cgeoportal_geoportal.lib.wmstparsing import _parse_date date = _parse_date("2010-02-03T12:34Z") - assert "second" == date[0] + assert date[0] == "second" self.assertEqual(datetime.datetime(2010, 0o2, 0o3, 12, 34, tzinfo=isodate.UTC), date[1]) def test_unsupported_format(self): @@ -163,13 +169,13 @@ def test_format(self): from c2cgeoportal_geoportal.lib.wmstparsing import _format_date dt = datetime.datetime(2010, 0o2, 0o1, 00, 00) - assert "2010-02-01T00:00:00Z" == _format_date(dt) + assert _format_date(dt) == "2010-02-01T00:00:00Z" def test_format_tz(self): from c2cgeoportal_geoportal.lib.wmstparsing import _format_date, _parse_date dt = _parse_date("2010-02-03T12:34:00+01:00") - assert "2010-02-03T12:34:00+01:00" == _format_date(dt[1]) + assert _format_date(dt[1]) == "2010-02-03T12:34:00+01:00" class TestParseDuration(TestCase): @@ -204,9 +210,8 @@ def test_second(self): self.assertEqual((0, 0, 0, 10), _parse_duration("PT10S")) def test_invalid(self): - from isodate import ISO8601Error - from c2cgeoportal_geoportal.lib.wmstparsing import _parse_duration + from isodate import ISO8601Error self.assertRaises(ISO8601Error, _parse_duration, "10S") diff --git a/poetry.lock b/poetry.lock index 688330eb9a..413a316cde 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "affine" @@ -2640,6 +2640,7 @@ pylint-flask = "0.6" pyroma = {version = ">=2.4", optional = true, markers = "extra == \"with-pyroma\" or extra == \"with_everything\""} PyYAML = "*" requirements-detector = ">=1.3.2" +ruff = {version = "*", optional = true, markers = "extra == \"with-ruff\" or extra == \"with_everything\""} setoptconf-tmp = ">=0.3.1,<0.4.0" toml = ">=0.10.2,<0.11.0" @@ -3967,6 +3968,33 @@ files = [ {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"}, ] +[[package]] +name = "ruff" +version = "0.8.1" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.8.1-py3-none-linux_armv6l.whl", hash = "sha256:fae0805bd514066f20309f6742f6ee7904a773eb9e6c17c45d6b1600ca65c9b5"}, + {file = "ruff-0.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8a4f7385c2285c30f34b200ca5511fcc865f17578383db154e098150ce0a087"}, + {file = "ruff-0.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd054486da0c53e41e0086e1730eb77d1f698154f910e0cd9e0d64274979a209"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2029b8c22da147c50ae577e621a5bfbc5d1fed75d86af53643d7a7aee1d23871"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2666520828dee7dfc7e47ee4ea0d928f40de72056d929a7c5292d95071d881d1"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:333c57013ef8c97a53892aa56042831c372e0bb1785ab7026187b7abd0135ad5"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:288326162804f34088ac007139488dcb43de590a5ccfec3166396530b58fb89d"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b12c39b9448632284561cbf4191aa1b005882acbc81900ffa9f9f471c8ff7e26"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:364e6674450cbac8e998f7b30639040c99d81dfb5bbc6dfad69bc7a8f916b3d1"}, + {file = "ruff-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b22346f845fec132aa39cd29acb94451d030c10874408dbf776af3aaeb53284c"}, + {file = "ruff-0.8.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b2f2f7a7e7648a2bfe6ead4e0a16745db956da0e3a231ad443d2a66a105c04fa"}, + {file = "ruff-0.8.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:adf314fc458374c25c5c4a4a9270c3e8a6a807b1bec018cfa2813d6546215540"}, + {file = "ruff-0.8.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a885d68342a231b5ba4d30b8c6e1b1ee3a65cf37e3d29b3c74069cdf1ee1e3c9"}, + {file = "ruff-0.8.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d2c16e3508c8cc73e96aa5127d0df8913d2290098f776416a4b157657bee44c5"}, + {file = "ruff-0.8.1-py3-none-win32.whl", hash = "sha256:93335cd7c0eaedb44882d75a7acb7df4b77cd7cd0d2255c93b28791716e81790"}, + {file = "ruff-0.8.1-py3-none-win_amd64.whl", hash = "sha256:2954cdbe8dfd8ab359d4a30cd971b589d335a44d444b6ca2cb3d1da21b75e4b6"}, + {file = "ruff-0.8.1-py3-none-win_arm64.whl", hash = "sha256:55873cc1a473e5ac129d15eccb3c008c096b94809d693fc7053f588b67822737"}, + {file = "ruff-0.8.1.tar.gz", hash = "sha256:3583db9a6450364ed5ca3f3b4225958b24f78178908d5c4bc0f46251ccca898f"}, +] + [[package]] name = "semver" version = "3.0.2" @@ -4865,4 +4893,4 @@ test = ["zope.testing"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "17be9d85b8164aa82f5206c01e5d79bf526176ff253d43c9bcc49d60cbacb9cc" +content-hash = "80e474f923a3d4698658b1dd37c7992c113b525fac34bbd51abec1ce38526c13" diff --git a/pyproject.toml b/pyproject.toml index 1a8e7b4980..97a0522615 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,25 +1,5 @@ -[tool.mypy] -python_version = "3.10" -warn_redundant_casts = true -warn_unused_ignores = true -warn_return_any = true -ignore_missing_imports = true -disallow_untyped_defs = true -strict_optional = true -strict = true - -[[tool.mypy.overrides]] -module = "c2cgeoportal_admin.*" -disallow_untyped_defs = false - -[tool.black] -line-length = 110 -target-version = ['py310'] - -[tool.isort] -profile = "black" -line_length = 110 -known_local_folder = ["c2cgeoportal_commons", "c2cgeoportal_geoportal", "c2cgeoportal_admin", "geomapfish_qgisserver", "{{cookiecutter.package}}_geoportal"] +[tool.ruff] +target-version = 'py310' [tool.poetry] name = "c2cgeoportal" @@ -75,7 +55,7 @@ azure-storage-blob = "12.24.0" simple_openid_connect = { git = "https://github.com/sbrunner/py_simple_openid_connect.git", branch = "allows-pkce" } # geoportal pkce = '1.0.3' # geoportal basicauth = "1.0.0" -prospector = { extras = ["with_mypy", "with_bandit", "with_pyroma"], version = "1.13.3" } +prospector = { version = "1.13.3", extras = ["with_mypy", "with_bandit", "with_pyroma", "with_ruff"] } prospector-profile-duplicated = "1.9.0" prospector-profile-utils = "1.14.1" beautifulsoup4 = "4.12.3" diff --git a/scripts/get-version b/scripts/get-version index 827ea37c3a..0f1eca4af9 100755 --- a/scripts/get-version +++ b/scripts/get-version @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2023, Camptocamp SA +# Copyright (c) 2018-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -31,7 +31,7 @@ import argparse import os import re -import subprocess +import subprocess # nosec import sys import requests diff --git a/scripts/updated_version b/scripts/updated_version index 02707db4d3..11dffcd58d 100755 --- a/scripts/updated_version +++ b/scripts/updated_version @@ -29,7 +29,7 @@ import argparse import json -import subprocess +import subprocess # nosec from packaging.version import Version diff --git a/scripts/upgrade b/scripts/upgrade index c523410956..53801b87aa 100755 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2023, Camptocamp SA +# Copyright (c) 2019-2024, Camptocamp SA # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ import argparse import os import platform import re -import subprocess +import subprocess # nosec import sys parser = argparse.ArgumentParser(description="Upgrade the project") From 8aa8ba4590ebae19ca27f8532bd13bf26b0d4132 Mon Sep 17 00:00:00 2001 From: "geo-ghci-int[bot]" <146321879+geo-ghci-int[bot]@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:30:03 +0000 Subject: [PATCH 03/12] Update dpkg versions list From the artifact of the previous workflow run --- geoportal/tests/functional/__init__.py | 7 +------ geoportal/tests/functional/conftest.py | 12 ++++++++---- .../tests/functional/test_lingva_extractor_config.py | 2 -- geoportal/tests/functional/test_theme2fts.py | 4 +--- geoportal/tests/functional/test_themes.py | 5 +---- 5 files changed, 11 insertions(+), 19 deletions(-) diff --git a/geoportal/tests/functional/__init__.py b/geoportal/tests/functional/__init__.py index 235eb55737..aa8a397c13 100644 --- a/geoportal/tests/functional/__init__.py +++ b/geoportal/tests/functional/__init__.py @@ -36,19 +36,14 @@ import c2cgeoportal_geoportal.lib import pyramid.registry import pyramid.request -import tests import sqlalchemy.exc import transaction import webob.acceptparse from c2c.template.config import config as configuration -from pyramid import testing -from sqlalchemy.orm.session import Session - -import c2cgeoportal_geoportal -import c2cgeoportal_geoportal.lib from c2cgeoportal_commons import models from c2cgeoportal_geoportal.lib import caching from pyramid import testing +from sqlalchemy.orm.session import Session import tests diff --git a/geoportal/tests/functional/conftest.py b/geoportal/tests/functional/conftest.py index 5ea871be7a..7f7d376e1d 100644 --- a/geoportal/tests/functional/conftest.py +++ b/geoportal/tests/functional/conftest.py @@ -32,13 +32,17 @@ import pyramid.request import pytest from c2c.template.config import config as configuration +from c2cgeoportal_commons.testing import ( + generate_mappers, + get_engine, + get_session_factory, + get_tm_session, +) +from c2cgeoportal_commons.testing.initializedb import truncate_tables +from c2cgeoportal_geoportal.lib import caching from pyramid.testing import DummyRequest from sqlalchemy.orm.session import Session, SessionTransaction -from tests.functional import setup_common as setup_module -from c2cgeoportal_commons.testing import generate_mappers, get_engine, get_session_factory, get_tm_session -from c2cgeoportal_commons.testing.initializedb import truncate_tables -from c2cgeoportal_geoportal.lib import caching from tests.functional import setup_common as setup_module _LOG = logging.getLogger(__name__) diff --git a/geoportal/tests/functional/test_lingva_extractor_config.py b/geoportal/tests/functional/test_lingva_extractor_config.py index fea4d8a3b0..fc9113a54a 100644 --- a/geoportal/tests/functional/test_lingva_extractor_config.py +++ b/geoportal/tests/functional/test_lingva_extractor_config.py @@ -93,8 +93,6 @@ def dbsession_db1(settings, dbsession_old): @pytest.fixture(scope="function") def test_data(dbsession_db1, transact_old): - from sqlalchemy import text - from c2cgeoportal_commons.models import main from sqlalchemy import text diff --git a/geoportal/tests/functional/test_theme2fts.py b/geoportal/tests/functional/test_theme2fts.py index 5ac877309a..82d0ca8405 100644 --- a/geoportal/tests/functional/test_theme2fts.py +++ b/geoportal/tests/functional/test_theme2fts.py @@ -54,9 +54,7 @@ def settings(): def add_parent(dbsession_old, item, group): - """ - Utility function to add a TreeItem in a TreeGroup. - """ + """Utility function to add a TreeItem in a TreeGroup.""" from c2cgeoportal_commons.models import main dbsession_old.add(main.LayergroupTreeitem(group=group, item=item, ordering=len(group.children_relation))) diff --git a/geoportal/tests/functional/test_themes.py b/geoportal/tests/functional/test_themes.py index 969ca64392..6796c19217 100644 --- a/geoportal/tests/functional/test_themes.py +++ b/geoportal/tests/functional/test_themes.py @@ -29,11 +29,8 @@ import pytest -import transaction -from tests.functional import create_dummy_request -from pyramid import testing -from tests.functional import create_default_ogcserver, create_dummy_request +from tests.functional import create_dummy_request from tests.functional import setup_common as setup_module # noqa from tests.functional import teardown_common as teardown_module # noqa From af1ddc3318dcd5318d2e93e9ad774475b74ef52b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Tue, 3 Dec 2024 21:40:09 +0100 Subject: [PATCH 04/12] f --- geoportal/tests/functional/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/geoportal/tests/functional/conftest.py b/geoportal/tests/functional/conftest.py index 7f7d376e1d..686b1500f8 100644 --- a/geoportal/tests/functional/conftest.py +++ b/geoportal/tests/functional/conftest.py @@ -31,6 +31,7 @@ import pyramid.request import pytest +import transaction from c2c.template.config import config as configuration from c2cgeoportal_commons.testing import ( generate_mappers, From 96c5db787cdc3e626a849f387e021365a14f72c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Tue, 3 Dec 2024 21:40:09 +0100 Subject: [PATCH 05/12] f --- geoportal/c2cgeoportal_geoportal/views/theme.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/geoportal/c2cgeoportal_geoportal/views/theme.py b/geoportal/c2cgeoportal_geoportal/views/theme.py index 2fa7060a2b..754390cd95 100644 --- a/geoportal/c2cgeoportal_geoportal/views/theme.py +++ b/geoportal/c2cgeoportal_geoportal/views/theme.py @@ -33,6 +33,8 @@ import re import sys import time +import xml.etree +import xml.etree.ElementTree # nosec from collections import Counter from math import sqrt from typing import Any, cast @@ -48,7 +50,6 @@ from c2cgeoportal_commons.models import cache_invalidate_cb, main from c2cwsgiutils.auth import auth_view from defusedxml import lxml -from lxml import etree # nosec from owslib.wms import WebMapService from pyramid.view import view_config from sqlalchemy.orm import subqueryload @@ -895,7 +896,7 @@ def _get_role_ids(self) -> set[int] | None: async def _wfs_get_features_type( self, wfs_url: Url, ogc_server: main.OGCServer, preload: bool = False, cache: bool = True - ) -> tuple[etree.Element | None, set[str]]: # pylint: disable=c-extension-no-member + ) -> tuple[xml.etree.ElementTree.Element | None, set[str]]: # pylint: disable=c-extension-no-member errors = set() wfs_url.add_query( From 10b93cdde840d29bbeb0633834c44c080fca4536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Wed, 4 Dec 2024 12:17:27 +0100 Subject: [PATCH 06/12] f --- geoportal/tests/functional/test_dynamicview.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/geoportal/tests/functional/test_dynamicview.py b/geoportal/tests/functional/test_dynamicview.py index 367bb7ed0d..b76ed2c84f 100644 --- a/geoportal/tests/functional/test_dynamicview.py +++ b/geoportal/tests/functional/test_dynamicview.py @@ -36,6 +36,8 @@ from pyramid.testing import testConfig from tests import DummyRequest +from tests.functional import setup_common as setup_module # pylint: disable=unused-import +from tests.functional import teardown_common as teardown_module # pylint: disable=unused-import class TestDynamicView(TestCase): From d14770a93f9a6f02fe042daeb9632647a94262c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Wed, 4 Dec 2024 12:18:13 +0100 Subject: [PATCH 07/12] todo revert --- .prospector.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.prospector.yaml b/.prospector.yaml index c6b2c654c5..6ff5aca624 100644 --- a/.prospector.yaml +++ b/.prospector.yaml @@ -20,6 +20,8 @@ mypy: python_version: '3.10' ruff: + options: + fix: false disable: - D102 # Missing docstring in public method - D107 # Missing docstring in `__init__` From da938fb9b3b09d1c6b5268f24dc6eb3a68db2ccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Wed, 4 Dec 2024 12:47:14 +0100 Subject: [PATCH 08/12] f --- geoportal/tests/functional/test_entry.py | 2 ++ geoportal/tests/functional/test_oauth2.py | 2 ++ geoportal/tests/functional/test_oidc.py | 2 ++ 3 files changed, 6 insertions(+) diff --git a/geoportal/tests/functional/test_entry.py b/geoportal/tests/functional/test_entry.py index 0134636935..b0fbdb1f7b 100644 --- a/geoportal/tests/functional/test_entry.py +++ b/geoportal/tests/functional/test_entry.py @@ -43,6 +43,8 @@ mapserv_url, setup_db, ) +from tests.functional import setup_common as setup_module # noqa, pylint: disable=unused-import +from tests.functional import teardown_common as teardown_module # noqa, pylint: disable=unused-import _LOG = logging.getLogger(__name__) diff --git a/geoportal/tests/functional/test_oauth2.py b/geoportal/tests/functional/test_oauth2.py index 5d925c63d6..8ace7a1ded 100644 --- a/geoportal/tests/functional/test_oauth2.py +++ b/geoportal/tests/functional/test_oauth2.py @@ -39,6 +39,8 @@ import transaction from tests.functional import cleanup_db, create_dummy_request, init_registry +from tests.functional import setup_common as setup_module # pylint: disable=unused-import +from tests.functional import teardown_common as teardown_module # pylint: disable=unused-import _LOG = logging.getLogger(__name__) diff --git a/geoportal/tests/functional/test_oidc.py b/geoportal/tests/functional/test_oidc.py index 6a6c92a5c6..98326b9026 100644 --- a/geoportal/tests/functional/test_oidc.py +++ b/geoportal/tests/functional/test_oidc.py @@ -12,6 +12,8 @@ from pyramid import testing from tests.functional import cleanup_db, create_dummy_request, setup_db +from tests.functional import setup_common as setup_module # noqa, pylint: disable=unused-import +from tests.functional import teardown_common as teardown_module # noqa, pylint: disable=unused-import _OIDC_CONFIGURATION = { "issuer": "https://sso.example.com", From b3a151ed387e21f3812a5f98dfdcb8452702ffab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Wed, 4 Dec 2024 13:41:48 +0100 Subject: [PATCH 09/12] f --- geoportal/tests/functional/test_themes_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/geoportal/tests/functional/test_themes_entry.py b/geoportal/tests/functional/test_themes_entry.py index 0080268407..2dad788b78 100644 --- a/geoportal/tests/functional/test_themes_entry.py +++ b/geoportal/tests/functional/test_themes_entry.py @@ -43,6 +43,8 @@ mapserv_url, setup_db, ) +from tests.functional import setup_common as setup_module # pylint: disable=unused-import +from tests.functional import teardown_common as teardown_module # pylint: disable=unused-import _LOG = logging.getLogger(__name__) From 3aa1763418166eaa029b60eef82633147d66ab96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Wed, 4 Dec 2024 14:08:46 +0100 Subject: [PATCH 10/12] f --- .../{{cookiecutter.project}}/geoportal/gunicorn.conf.py | 2 +- .../scaffolds/create/{{cookiecutter.project}}/Makefile | 2 +- geoportal/tests/functional/conftest.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py b/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py index 7d5ae13b42..b256426f93 100644 --- a/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py +++ b/geoportal/c2cgeoportal_geoportal/scaffolds/advance_create/{{cookiecutter.project}}/geoportal/gunicorn.conf.py @@ -47,7 +47,7 @@ max_requests = int(os.environ.get("GUNICORN_MAX_REQUESTS", 1000)) max_requests_jitter = int(os.environ.get("GUNICORN_MAX_REQUESTS_JITTER", 100)) -worker_tmp_dir = "/dev/shm" # nosec +worker_tmp_dir = "/dev/shm" # noqa: S108 limit_request_line = int(os.environ.get("GUNICORN_LIMIT_REQUEST_LINE", 8190)) accesslog = "-" diff --git a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile index ae0343adca..c5deb1f8dc 100644 --- a/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile +++ b/geoportal/c2cgeoportal_geoportal/scaffolds/create/{{cookiecutter.project}}/Makefile @@ -28,7 +28,7 @@ checks: prospector eslint ## Runs the checks .PHONY: prospector prospector: ## Runs the Prospector checks docker compose run --entrypoint= --no-deps --rm --volume=$(CURDIR)/geoportal:/app geoportal \ - prospector --output-format=pylint --die-on-tool-error + prospector --without=ruff --output-format=pylint --die-on-tool-error .PHONY: eslint eslint: ## Runs the eslint checks diff --git a/geoportal/tests/functional/conftest.py b/geoportal/tests/functional/conftest.py index 686b1500f8..b31a7c0e11 100644 --- a/geoportal/tests/functional/conftest.py +++ b/geoportal/tests/functional/conftest.py @@ -31,6 +31,7 @@ import pyramid.request import pytest +import sqlalchemy.orm import transaction from c2c.template.config import config as configuration from c2cgeoportal_commons.testing import ( From 8c6afda8bf7886aec5323de24fca559fdfe40c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Wed, 4 Dec 2024 17:51:36 +0100 Subject: [PATCH 11/12] f --- ci/requirements-project.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/requirements-project.txt b/ci/requirements-project.txt index 5f07c9c4a5..a8c6fdf453 100644 --- a/ci/requirements-project.txt +++ b/ci/requirements-project.txt @@ -1,4 +1,4 @@ # When we upgrade this we should also upgrade the requirements # in the documentation: doc/integrator/requirements.rst -PyYAML==3.13 +PyYAML==6.0 requests==2.31.0 From 7efb285c2c2cfad55b94c3f228685e733ddd66fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Brunner?= Date: Wed, 4 Dec 2024 20:32:40 +0100 Subject: [PATCH 12/12] f --- geoportal/c2cgeoportal_geoportal/scripts/c2cupgrade.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/geoportal/c2cgeoportal_geoportal/scripts/c2cupgrade.py b/geoportal/c2cgeoportal_geoportal/scripts/c2cupgrade.py index 8d8080f011..de2bf2d6e2 100644 --- a/geoportal/c2cgeoportal_geoportal/scripts/c2cupgrade.py +++ b/geoportal/c2cgeoportal_geoportal/scripts/c2cupgrade.py @@ -285,12 +285,12 @@ def step0(self, step: int) -> None: @Step(1) def step1(self, step: int) -> None: with tempfile.NamedTemporaryFile("w") as project_temp_file: - shutil.copyfile("project.yaml", project_temp_file) + shutil.copyfile("project.yaml", project_temp_file.name) try: check_call(["git", "reset", "--hard"]) check_call(["git", "clean", "--force", "-d"]) finally: - shutil.copyfile(project_temp_file, "project.yaml") + shutil.copyfile(project_temp_file.name, "project.yaml") self.run_step(step + 1)