From 4c73fe61c1cc4a59bf9829eaba9d6a2322c6b6c3 Mon Sep 17 00:00:00 2001 From: piglei Date: Wed, 13 Dec 2023 11:24:42 +0800 Subject: [PATCH] Introduce ruff linter to lint all Python packages & remove duplicated ruff configs (#140) --- .pre-commit-config.yaml | 47 ++---- pyproject.toml | 144 +++++++++++++----- sdks/apigw-manager/poetry.lock | 30 +--- sdks/apigw-manager/pyproject.toml | 114 ++------------ sdks/bkapi-client-core/poetry.lock | 35 +---- sdks/bkapi-client-core/pyproject.toml | 136 +++-------------- .../blue_krill/editions/editionctl.py | 58 +++---- 7 files changed, 184 insertions(+), 380 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 55ef0536..e812857f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,24 +12,25 @@ repos: language: system exclude: "^(.*/poetry.lock|.*/requirements.txt|.*/requirements_dev.txt)|sdks/apigw-manager/build.yml" entry: bash -c "if [[ -d pre_commit_hooks ]]; then pre_commit_hooks/ip.sh $@; fi" + # See ./pyproject.toml for below tool's version and settings - - id: isort - name: isort - # Set language to disable pre-commit's virtual-env + # 总是使用 --force-exclude 来让 ruff 配置文件中的 exclude 生效 + - id: ruff-check-fix + name: ruff-check-fix language: system types: [python] - exclude: "^(sdks/apigw-manager/.*|sdks/bkapi-client-core/.*)$" - entry: poetry run isort --settings-path=pyproject.toml - - id: black - name: black + # 修复包导入顺序问题,类似 isort 工具 + entry: poetry run ruff check --force-exclude --select I --fix + - id: ruff-format + name: ruff-format language: system types: [python] - entry: poetry run black --config=pyproject.toml - - id: flake8 - name: flak8 + entry: poetry run ruff format --force-exclude + - id: ruff-check + name: ruff-check language: system types: [python] - entry: poetry run pflake8 --config=pyproject.toml + entry: poetry run ruff check --force-exclude # mypy hooks for each Python project, the hooks must be separated because different # project might use their own configurations @@ -50,18 +51,6 @@ repos: # sdk bkapi-client-core - repo: local hooks: - - id: format - name: run ruff-formatter for "bkapi-client-core" - language: python - types: [python] - entry: bash -c 'cd sdks/bkapi-client-core/ && ruff format --config=pyproject.toml --force-exclude .' - files: sdks/bkapi-client-core/ - - id: ruff - name: run ruff for "bkapi-client-core" - language: python - types: [python] - entry: bash -c 'cd sdks/bkapi-client-core/ && ruff --config=pyproject.toml --force-exclude --fix .' - files: sdks/bkapi-client-core/ - id: mypy name: run mypy for "bkapi-client-core" language: python @@ -71,18 +60,6 @@ repos: # sdk apigw-manager - repo: local hooks: - - id: format - name: run ruff-formatter for "apigw-manager" - language: python - types: [python] - entry: bash -c 'cd sdks/apigw-manager/ && ruff format --config=pyproject.toml --force-exclude .' - files: sdks/apigw-manager/ - - id: ruff - name: run ruff for "apigw-manager" - language: python - types: [python] - entry: bash -c 'cd sdks/apigw-manager/ && ruff --config=pyproject.toml --force-exclude --fix .' - files: sdks/apigw-manager/ - id: mypy name: run mypy for "apigw-manager" language: python diff --git a/pyproject.toml b/pyproject.toml index 8342d801..6515671b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,46 +5,122 @@ description = "BlueKing PaaS Python SDK" authors = ["blueking "] [tool.poetry.dependencies] -python = ">=3.6.2,<4.0" - -[tool.poetry.dev-dependencies] -# black -black = "^21.7b0" -# isort -isort = "^5.9.2" -# flake8 -pyproject-flake8 = "^0.0.1-alpha.2" -flake8-comprehensions = "^3.5.0" -# pytest -pytest = "^6.2.4" -pytest-django = "^3.9.0" -pytest-cov = "^2.8.1" - -[tool.black] +python = ">=3.8.1,<3.9" + +[tool.poetry.group.dev.dependencies] +ruff = "^0.1.7" + +[tool.ruff] line-length = 119 -skip-string-normalization = 'true' - -[tool.isort] -multi_line_output = 3 -include_trailing_comma = 'true' -force_grid_wrap = 0 -use_parentheses = 'true' -line_length = 119 -skip_glob = ["*/migrations/**", "*/node_modules/**"] -known_local_folder = [ + +# 每个规则集的具体内容请参考:https://docs.astral.sh/ruff/rules/ +select = [ + "E", + "F", + "W", + "C90", + "B", + "PIE", + "C4", + "PL", + "RET", + "N", + "PERF", + "G", + "TRY", + "SIM", + "PT", +] + +# 理论上来说,ignore 规则列表中的条目越少越好。但在实际项目中,选择性地忽略某些规则常常是必须的。 +# 下面收集了常见的待忽略规则集,并将其分为两类:“推荐忽略”和“谨慎忽略”。“推荐忽略”类中所包含的规 +# 则,所有项目建议默认启用。“谨慎忽略”类中的规则,项目可酌情添加。 +# +# 除此处列出的规则以外,不建议随意扩充忽略规则列表。 +ignore = [ + # === 推荐忽略 === + # pyflakes: 忽略后,允许使用 import *,仅在个别情况下搭配 __all__ 使用 + "F403", + "F405", + # pycodestyle: 忽略后,不检查行长,最大的每行长度直接由 ruff formatter 来保证 + "E501", + # pycodestyle: 忽略后,不再对多行字符串的版权头信息报错, 假如将其批量调整成普通 # 号注释后可解 + "E402", + # flake8-comprehensions: 忽略后,允许使用 dict() 函数来构建字典对象 + "C408", + # flake8-bugbear: 忽略后,允许对字面量属性名使用 getattr/setattr 函数 + "B009", + "B010", + # flake8-bugbear: 忽略后,允许在 except 语句块中使用裸的 raise 语句,不要求必须使用 raise ... from + "B904", + # pylint:忽略后,允许定义超过 5 个参数的函数 + "PLR0913", + # pylint:忽略后,允许直接和 magic value 字面量做比较,因为并非所有情况都适合抽象常量 + "PLR2004", + # flake8-return: 忽略后,允许在 return 前定义一个临时变量,这有时对可读性有帮助 + "RET504", + # flake8-return: 忽略后,允许使用非必须的分支包裹 return/raise 语句,这有时对可读性有帮助 + "RET505", + "RET506", + # pep8-naming: 忽略后,不强制 class 内的变量使用蛇形命名,因为部分情况下驼峰命名更契合上下文 + "N815", + # pep8-naming: 忽略后,允许使用短名字作为导入时的别名 + "N817", + # pep8-naming: 忽略后,不强制要求异常类的名字必须以 Error 结尾 + "N818", + # perflint: 忽略后,不强制要求一定用推导式替代 for 循环,因为有时循环的可读性更好 + "PERF401", + # perflint: 忽略后,允许在循环中使用 try except 代码块,因为这对性能影响微乎其微,有时可读性更好 + "PERF203", + # tryceratops: 忽略后,允许抛出异常时,使用较长的字符串作为异常信息,可能会降低异常的可使用性 + "TRY003", + # tryceratops: 忽略后,允许在 except 语句块中使用裸的 raise 语句,不要求必须使用 raise ... from + "TRY200", + # flake8-simplify: 忽略后,允许使用 exception: pass 语句,不要求必须用 contextlib.suppress 替换 + "SIM105", + # flake8-simplify: 忽略后,不强制要求用三元表达式替换普通的短 if-else 语句 + "SIM108", + # flake8-pytest-style:忽略后,允许 fixture 显式指定 scope="function" + "PT003", + + # === 谨慎忽略 === + # pep8-naming: 忽略后,不强制 class 必须使用驼峰命名,部分单元测试代码中用下划线起名可能更方便 + "N801", + # flake8-logging-format: 忽略后,允许在打印日志时使用 .format() 方法,可能降低性能 + "G001", + # flake8-logging-format: 忽略后,允许在打印日志时使用 f-string 表达式,更可读但可能降低性能 + "G004", + # pylint: 忽略后,允许使用 global 关键字修改全局变量 + "PLW0603", +] + +# 添加需要额外忽略的目录,比如 Django 自动生成的 migrations 目录 +extend-exclude = ["*/migrations"] + +[tool.ruff.format] +quote-style = "double" + +[tool.ruff.isort] +# 总是显式制定 import section 的顺序 +section-order = [ + "future", + "standard-library", + "third-party", + "first-party", + "local-folder", +] +relative-imports-order = "closest-to-furthest" +# 添加那些不能被默认识别的 first party 的模块名 +known-first-party = [ "tests", - # Knonwn Python packages "blue_krill", "bkpaas_auth", "bkapi_client_core", "bkapi_component", "bk_storages", - "apigw_manager" + "apigw_manager", ] -[tool.flake8] -ignore = "F405,C901,E203,W503" -max-line-length = 119 -max-complexity = 8 -format = "pylint" -exclude = "*migrations*,*.pyc,.git,__pycache__,*/node_modules/*,*/bin/*" +[tool.ruff.lint.mccabe] +# 调大所允许的最大圈复杂度 +max-complexity = 12 diff --git a/sdks/apigw-manager/poetry.lock b/sdks/apigw-manager/poetry.lock index 35698010..9731634d 100644 --- a/sdks/apigw-manager/poetry.lock +++ b/sdks/apigw-manager/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "appnope" @@ -1343,32 +1343,6 @@ files = [ [package.dependencies] pyasn1 = ">=0.1.3" -[[package]] -name = "ruff" -version = "0.1.6" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -files = [ - {file = "ruff-0.1.6-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:88b8cdf6abf98130991cbc9f6438f35f6e8d41a02622cc5ee130a02a0ed28703"}, - {file = "ruff-0.1.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c549ed437680b6105a1299d2cd30e4964211606eeb48a0ff7a93ef70b902248"}, - {file = "ruff-0.1.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cf5f701062e294f2167e66d11b092bba7af6a057668ed618a9253e1e90cfd76"}, - {file = "ruff-0.1.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:05991ee20d4ac4bb78385360c684e4b417edd971030ab12a4fbd075ff535050e"}, - {file = "ruff-0.1.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87455a0c1f739b3c069e2f4c43b66479a54dea0276dd5d4d67b091265f6fd1dc"}, - {file = "ruff-0.1.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:683aa5bdda5a48cb8266fcde8eea2a6af4e5700a392c56ea5fb5f0d4bfdc0240"}, - {file = "ruff-0.1.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:137852105586dcbf80c1717facb6781555c4e99f520c9c827bd414fac67ddfb6"}, - {file = "ruff-0.1.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd98138a98d48a1c36c394fd6b84cd943ac92a08278aa8ac8c0fdefcf7138f35"}, - {file = "ruff-0.1.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a0cd909d25f227ac5c36d4e7e681577275fb74ba3b11d288aff7ec47e3ae745"}, - {file = "ruff-0.1.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8fd1c62a47aa88a02707b5dd20c5ff20d035d634aa74826b42a1da77861b5ff"}, - {file = "ruff-0.1.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fd89b45d374935829134a082617954120d7a1470a9f0ec0e7f3ead983edc48cc"}, - {file = "ruff-0.1.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:491262006e92f825b145cd1e52948073c56560243b55fb3b4ecb142f6f0e9543"}, - {file = "ruff-0.1.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ea284789861b8b5ca9d5443591a92a397ac183d4351882ab52f6296b4fdd5462"}, - {file = "ruff-0.1.6-py3-none-win32.whl", hash = "sha256:1610e14750826dfc207ccbcdd7331b6bd285607d4181df9c1c6ae26646d6848a"}, - {file = "ruff-0.1.6-py3-none-win_amd64.whl", hash = "sha256:4558b3e178145491e9bc3b2ee3c4b42f19d19384eaa5c59d10acf6e8f8b57e33"}, - {file = "ruff-0.1.6-py3-none-win_arm64.whl", hash = "sha256:03910e81df0d8db0e30050725a5802441c2022ea3ae4fe0609b76081731accbc"}, - {file = "ruff-0.1.6.tar.gz", hash = "sha256:1b09f29b16c6ead5ea6b097ef2764b42372aebe363722f1605ecbcd2b9207184"}, -] - [[package]] name = "secretstorage" version = "3.3.1" @@ -1704,4 +1678,4 @@ kubernetes = ["kubernetes"] [metadata] lock-version = "2.0" python-versions = "^3.6.1" -content-hash = "a3b159e317addca68ab16f47c91a784dd5b60dc263df85f4b6dfd17fdcfa7172" +content-hash = "999bc9cab42810b11d9f453632a077ff7e599096d8aecb9d63ffe219e9026e1b" diff --git a/sdks/apigw-manager/pyproject.toml b/sdks/apigw-manager/pyproject.toml index db7efd0c..67be774b 100644 --- a/sdks/apigw-manager/pyproject.toml +++ b/sdks/apigw-manager/pyproject.toml @@ -22,13 +22,13 @@ future = "0.18.2" pyyaml = ">=5.4.1" bkapi-client-core = ">=1.2.0" bkapi-bk-apigateway = "^1.0.11" -pyjwt = {version = ">=1.6.4", optional = true} -django-environ = {version = ">=0.8.1", optional = true} -Django = {version = ">=1.11.1", optional = true} -cryptography = {version = ">=3.1.1", optional = true} -packaging = {version = ">=21.0"} -PyMySQL = {version = "^1.0.2", optional = true} -kubernetes = {version = "*", optional = true} +pyjwt = { version = ">=1.6.4", optional = true } +django-environ = { version = ">=0.8.1", optional = true } +Django = { version = ">=1.11.1", optional = true } +cryptography = { version = ">=3.1.1", optional = true } +packaging = { version = ">=21.0" } +PyMySQL = { version = "^1.0.2", optional = true } +kubernetes = { version = "*", optional = true } [tool.poetry.extras] cryptography = ["cryptography", "pyjwt"] @@ -41,9 +41,8 @@ pytest = "^7.0.1" pytest-cov = "^4.0.0" pytest-mock = "^3.6.1" pytest-django = "^4.5.0" -pytest-pretty = {version = "^1.1.0", python = "~3.7.0"} +pytest-pretty = { version = "^1.1.0", python = "~3.7.0" } Faker = "14.2.1" -ruff = {version = "^0.1.3", python = "~3.7.0"} mypy = "*" pre-commit = "^2.17.0" m2r = "^0.2.1" @@ -68,104 +67,9 @@ strict_optional = true pretty = true [[tool.mypy.overrides]] -module = [ - "*.migrations.*", -] +module = ["*.migrations.*"] ignore_errors = true [tool.pytest.ini_options] addopts = "-p no:pastebin -p no:nose -p no:doctest -p no:warnings" testpaths = ["tests"] - -[tool.ruff] -# https://docs.astral.sh/ruff/rules/ -select = [ - "E", - "F", - "W", - "I", - "C90", - "B", - "PIE", - "C4", - "PL", - "RET", - "N", - "PERF", - "G", - "TRY", - "SIM", - "PT", -] - -# Disable E501 until this issue is fixed: https://github.com/astral-sh/ruff/issues/3825 -ignore = [ - # https://beta.ruff.rs/docs/rules/assert-raises-exception/ - "B017", - # https://beta.ruff.rs/docs/rules/raise-without-from-inside-except/ - "B904", - # https://beta.ruff.rs/docs/rules/zip-without-explicit-strict/ - "B905", - # https://beta.ruff.rs/docs/rules/line-too-long/ - "E501", - # https://beta.ruff.rs/docs/rules/ambiguous-variable-name/ - "E741", - # https://beta.ruff.rs/docs/rules/unused-variable/ - "F841", - # https://beta.ruff.rs/docs/rules/error-suffix-on-exception-name/ - "N818", - # https://beta.ruff.rs/docs/rules/try-except-in-loop/ - "PERF203", - # https://beta.ruff.rs/docs/rules/too-many-arguments/ - "PLR0913", - # https://beta.ruff.rs/docs/rules/raise-vanilla-args/ - "TRY003", - # https://beta.ruff.rs/docs/rules/reraise-no-cause/ - "TRY200", - # https://beta.ruff.rs/docs/rules/try-consider-else/ - "TRY300", - # https://beta.ruff.rs/docs/rules/raise-within-try/ - "TRY301", - # https://beta.ruff.rs/docs/rules/magic-value-comparison/ - "PLR2004", - # https://beta.ruff.rs/docs/rules/suppressible-exception/ - "SIM105", - # https://beta.ruff.rs/docs/rules/open-file-with-context-handler/ - "SIM115", -] - -# Exclude a variety of commonly ignored directories. -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".hg", - ".mypy_cache", - ".nox", - ".pants.d", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "venv", - "*/migrations/*", -] - -# Same as Black. -line-length = 119 - -[tool.ruff.mccabe] -# Unlike Flake8, default to a complexity level of 10. -max-complexity = 12 - -[tool.ruff.isort] -relative-imports-order = "closest-to-furthest" -known-first-party = ["apigw_manager"] \ No newline at end of file diff --git a/sdks/bkapi-client-core/poetry.lock b/sdks/bkapi-client-core/poetry.lock index e8ea0340..bdcb920a 100644 --- a/sdks/bkapi-client-core/poetry.lock +++ b/sdks/bkapi-client-core/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "atomicwrites" @@ -929,37 +929,6 @@ type = "legacy" url = "https://mirrors.cloud.tencent.com/pypi/simple" reference = "tencent" -[[package]] -name = "ruff" -version = "0.1.4" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -files = [ - {file = "ruff-0.1.4-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:864958706b669cce31d629902175138ad8a069d99ca53514611521f532d91495"}, - {file = "ruff-0.1.4-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:9fdd61883bb34317c788af87f4cd75dfee3a73f5ded714b77ba928e418d6e39e"}, - {file = "ruff-0.1.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4eaca8c9cc39aa7f0f0d7b8fe24ecb51232d1bb620fc4441a61161be4a17539"}, - {file = "ruff-0.1.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a9a1301dc43cbf633fb603242bccd0aaa34834750a14a4c1817e2e5c8d60de17"}, - {file = "ruff-0.1.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78e8db8ab6f100f02e28b3d713270c857d370b8d61871d5c7d1702ae411df683"}, - {file = "ruff-0.1.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:80fea754eaae06335784b8ea053d6eb8e9aac75359ebddd6fee0858e87c8d510"}, - {file = "ruff-0.1.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bc02a480d4bfffd163a723698da15d1a9aec2fced4c06f2a753f87f4ce6969c"}, - {file = "ruff-0.1.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862811b403063765b03e716dac0fda8fdbe78b675cd947ed5873506448acea4"}, - {file = "ruff-0.1.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58826efb8b3efbb59bb306f4b19640b7e366967a31c049d49311d9eb3a4c60cb"}, - {file = "ruff-0.1.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fdfd453fc91d9d86d6aaa33b1bafa69d114cf7421057868f0b79104079d3e66e"}, - {file = "ruff-0.1.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e8791482d508bd0b36c76481ad3117987301b86072158bdb69d796503e1c84a8"}, - {file = "ruff-0.1.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01206e361021426e3c1b7fba06ddcb20dbc5037d64f6841e5f2b21084dc51800"}, - {file = "ruff-0.1.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:645591a613a42cb7e5c2b667cbefd3877b21e0252b59272ba7212c3d35a5819f"}, - {file = "ruff-0.1.4-py3-none-win32.whl", hash = "sha256:99908ca2b3b85bffe7e1414275d004917d1e0dfc99d497ccd2ecd19ad115fd0d"}, - {file = "ruff-0.1.4-py3-none-win_amd64.whl", hash = "sha256:1dfd6bf8f6ad0a4ac99333f437e0ec168989adc5d837ecd38ddb2cc4a2e3db8a"}, - {file = "ruff-0.1.4-py3-none-win_arm64.whl", hash = "sha256:d98ae9ebf56444e18a3e3652b3383204748f73e247dea6caaf8b52d37e6b32da"}, - {file = "ruff-0.1.4.tar.gz", hash = "sha256:21520ecca4cc555162068d87c747b8f95e1e95f8ecfcbbe59e8dd00710586315"}, -] - -[package.source] -type = "legacy" -url = "https://mirrors.cloud.tencent.com/pypi/simple" -reference = "tencent" - [[package]] name = "setuptools" version = "68.0.0" @@ -1259,4 +1228,4 @@ monitor = ["prometheus-client"] [metadata] lock-version = "2.0" python-versions = "^2.7 || ^3.6" -content-hash = "3baac9cc03396b5e940a187cba8e9f7acaa6fe24466102aa8b9ceff3127d6993" +content-hash = "593bcd880f848e466acdeb4d9655da039282a0ab463a0ccb8939c95b147f7fa0" diff --git a/sdks/bkapi-client-core/pyproject.toml b/sdks/bkapi-client-core/pyproject.toml index 15bb254a..7e97fe0b 100644 --- a/sdks/bkapi-client-core/pyproject.toml +++ b/sdks/bkapi-client-core/pyproject.toml @@ -17,35 +17,34 @@ Repository = "https://github.com/TencentBlueKing/bkpaas-python-sdk/" [tool.poetry.dependencies] python = "^2.7 || ^3.6" requests = ">=2.20" -typing = {version = "^3.10.0", python = "2.7"} +typing = { version = "^3.10.0", python = "2.7" } curlify = ">=2.0" -bkoauth = {version = ">=0.0.10", optional = true} +bkoauth = { version = ">=0.0.10", optional = true } typing-extensions = ">=3.7.4" -prometheus-client = {version = ">=0.9.0", optional = true} +prometheus-client = { version = ">=0.9.0", optional = true } six = "*" [tool.poetry.extras] -django=["bkoauth", "prometheus-client"] -monitor=["prometheus-client"] +django = ["bkoauth", "prometheus-client"] +monitor = ["prometheus-client"] [tool.poetry.dev-dependencies] -pytest = {version = "^7.0.1", python = "^3.6"} -pytest-cov = {version = "^4.0.0", python = "^3.6"} -pytest-mock = {version = "^3.6.1", python = "^3.6"} -pytest-django = {version = "^4.5.0", python = "^3.6"} -Faker = {version = "^14.2.1", python = "^3.6"} -ruff = {version = "^0.1.3", python = "~3.7.0"} -mypy = {version = "^0.971", python = "^3.7"} -pre-commit = {version = "^2.17.0", python = "^3.7"} -requests-mock = {version = "^1.9.3"} -tox = {version = "^3.23.0", python = "^3.6"} +pytest = { version = "^7.0.1", python = "^3.6" } +pytest-cov = { version = "^4.0.0", python = "^3.6" } +pytest-mock = { version = "^3.6.1", python = "^3.6" } +pytest-django = { version = "^4.5.0", python = "^3.6" } +Faker = { version = "^14.2.1", python = "^3.6" } +mypy = { version = "^0.971", python = "^3.7" } +pre-commit = { version = "^2.17.0", python = "^3.7" } +requests-mock = { version = "^1.9.3" } +tox = { version = "^3.23.0", python = "^3.6" } more-itertools = "^5.0" -types-requests = {version = "^2.25.0", python = "^3.6"} -types-six = {version = "1.16.21.9", python = "^3.6"} -dataclasses = {version = "0.8", python = "~3.6"} +types-requests = { version = "^2.25.0", python = "^3.6" } +types-six = { version = "1.16.21.9", python = "^3.6" } +dataclasses = { version = "0.8", python = "~3.6" } django = "1.11.20" -prometheus-client = {version = "*"} -bkoauth = {version = "*", optional = true} +prometheus-client = { version = "*" } +bkoauth = { version = "*", optional = true } [build-system] requires = ["poetry-core>=1.0.0"] @@ -65,108 +64,13 @@ strict_optional = true pretty = true [[tool.mypy.overrides]] -module = [ - "*.migrations.*", -] +module = ["*.migrations.*"] ignore_errors = true [tool.pytest.ini_options] addopts = "-p no:pastebin -p no:nose -p no:doctest -p no:warnings" testpaths = ["tests"] -[tool.ruff] -# https://docs.astral.sh/ruff/rules/ -select = [ - "E", - "F", - "W", - "I", - "C90", - "B", - "PIE", - "C4", - "PL", - "RET", - "N", - "PERF", - "G", - "TRY", - "SIM", - "PT", -] - -# Disable E501 until this issue is fixed: https://github.com/astral-sh/ruff/issues/3825 -ignore = [ - # https://beta.ruff.rs/docs/rules/assert-raises-exception/ - "B017", - # https://beta.ruff.rs/docs/rules/raise-without-from-inside-except/ - "B904", - # https://beta.ruff.rs/docs/rules/zip-without-explicit-strict/ - "B905", - # https://beta.ruff.rs/docs/rules/line-too-long/ - "E501", - # https://beta.ruff.rs/docs/rules/ambiguous-variable-name/ - "E741", - # https://beta.ruff.rs/docs/rules/unused-variable/ - "F841", - # https://beta.ruff.rs/docs/rules/error-suffix-on-exception-name/ - "N818", - # https://beta.ruff.rs/docs/rules/try-except-in-loop/ - "PERF203", - # https://beta.ruff.rs/docs/rules/too-many-arguments/ - "PLR0913", - # https://beta.ruff.rs/docs/rules/raise-vanilla-args/ - "TRY003", - # https://beta.ruff.rs/docs/rules/reraise-no-cause/ - "TRY200", - # https://beta.ruff.rs/docs/rules/try-consider-else/ - "TRY300", - # https://beta.ruff.rs/docs/rules/raise-within-try/ - "TRY301", - # https://beta.ruff.rs/docs/rules/magic-value-comparison/ - "PLR2004", - # https://beta.ruff.rs/docs/rules/suppressible-exception/ - "SIM105", - # https://beta.ruff.rs/docs/rules/open-file-with-context-handler/ - "SIM115", -] - -# Exclude a variety of commonly ignored directories. -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".hg", - ".mypy_cache", - ".nox", - ".pants.d", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "venv", - "*/migrations/*", -] - -# Same as Black. -line-length = 119 - -[tool.ruff.mccabe] -# Unlike Flake8, default to a complexity level of 10. -max-complexity = 12 - -[tool.ruff.isort] -relative-imports-order = "closest-to-furthest" -known-first-party = ["bkapi_client_core"] - [tool.tox] legacy_tox_ini = """ [tox] diff --git a/sdks/blue-krill/blue_krill/editions/editionctl.py b/sdks/blue-krill/blue_krill/editions/editionctl.py index b95cc2f4..d7bbe9f9 100644 --- a/sdks/blue-krill/blue_krill/editions/editionctl.py +++ b/sdks/blue-krill/blue_krill/editions/editionctl.py @@ -25,21 +25,21 @@ import click import toml -from pydantic import BaseModel, ValidationError, Field +from pydantic import BaseModel, Field, ValidationError from toml.decoder import TomlDecodeError from watchdog.events import FileSystemEventHandler from watchdog.observers import Observer # Set up logging as a console output hdr = logging.StreamHandler() -hdr.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)s: %(message)s')) +hdr.setFormatter(logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s")) logger = logging.getLogger(__name__) logger.addHandler(hdr) -__version__ = '0.1.0' +__version__ = "0.1.0" # The default file name of config file -DEFAULT_CONFIG_FILE_NAME = 'editionctl.toml' +DEFAULT_CONFIG_FILE_NAME = "editionctl.toml" class FileLinker(abc.ABC): @@ -114,9 +114,9 @@ def get_linker(type_: str) -> FileLinker: @click.group() -@click.option('--log-level', default="INFO", type=click.Choice(['DEBUG', "INFO", "WARNING", "ERROR", "CRITICAL"])) +@click.option("--log-level", default="INFO", type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"])) @click.option( - '--settings-path', + "--settings-path", required=False, help='Path fo config file, by default editionctl will try to use "editionctl.toml" in current directory', ) @@ -129,7 +129,7 @@ def main(ctx, settings_path, log_level): settings_path = Path.cwd() / DEFAULT_CONFIG_FILE_NAME ctx.ensure_object(dict) - ctx.obj['settings_path'] = settings_path + ctx.obj["settings_path"] = settings_path class EditionConf(BaseModel): @@ -166,14 +166,14 @@ def get_editions_root(self) -> Path: return self.editions_root or self.get_project_root() def get_linker_type(self) -> str: - return self.linker_type or 'default' + return self.linker_type or "default" def get_edition(self, name: str) -> EditionConf: """Get edition conf object by name""" for obj in self.editions: if obj.name == name: return obj - raise KeyError(f'Edition with name: {name} not found') + raise KeyError(f"Edition with name: {name} not found") def get_edition_directory(self, name: str) -> Path: """Get edition's directory""" @@ -205,7 +205,7 @@ def get_configuration_or_quit(settings_path: PathLike, **overwrites) -> Configur logger.critical('Settings file: "%s" is not a valid TOML file, detail: %s', settings_path, e) sys.exit(1) except Exception as e: # pylint: disable=broad-except - logger.critical('Unable to process settings file, error detail: %s', settings_path, e) + logger.critical("Unable to process settings file, file: %s, error detail: %s", settings_path, e) sys.exit(1) @@ -229,7 +229,7 @@ def activate(ctx, edition_name_in_pos, edition_name, linker_type): if linker_type: settings["linker_type"] = linker_type - config = get_configuration_or_quit(ctx.obj['settings_path'], **settings) + config = get_configuration_or_quit(ctx.obj["settings_path"], **settings) edition_name = edition_name_in_pos or edition_name if not edition_name: @@ -239,8 +239,8 @@ def activate(ctx, edition_name_in_pos, edition_name, linker_type): try: migrator = EditionFileMigrater(config, edition_name) migrator.migrate() - except RuntimeError as err: - logger.exception("unable to finish the migration: %s", err) + except RuntimeError: + logger.exception("unable to finish the migration") except ConfigurationError as err: logger.critical("Configuration error: %s", err) else: @@ -300,8 +300,8 @@ def from_existed_project(cls, project_root: PathLike): ) result.external_files = data["external_files"] return result - except Exception as e: - logger.exception(f"Unable to load metadata file: {e}") + except Exception: + logger.exception("Unable to load metadata file") raise InvalidMetadataFile("metadata file is invalid") def add_files(self, relative_paths: Collection[Path]): @@ -360,7 +360,7 @@ def get_edition_conf(self) -> EditionConf: try: return self.config.get_edition(self.edition_name) except KeyError as e: - raise RuntimeError('Can not read edition config: %s', e) + raise RuntimeError("Can not read edition config: %s", e) def migrate(self): """Migration edition specified files to project root directory @@ -371,7 +371,7 @@ def migrate(self): edition_dir = self.config.get_edition_directory(self.edition_name) if not edition_dir.exists(): logger.critical(f"Edition directory: {edition_dir} does not exists, please check your files.") - raise ConfigurationError('Wrong edition dir') + raise ConfigurationError("Wrong edition dir") last_metadata = load_current_metadata(self.config) if last_metadata and self.should_reset(last_metadata): @@ -379,7 +379,7 @@ def migrate(self): delete_files = [] if last_metadata: - delete_files = [Path(f['file']) for f in last_metadata.external_files] + delete_files = [Path(f["file"]) for f in last_metadata.external_files] result = DirectorySyncer(self.linker).sync_files( source_dir=edition_dir, @@ -404,7 +404,7 @@ def should_reset(self, last_metadata: EditionMetaData): @click.pass_context def reset(ctx): """Reset activated edition""" - config = get_configuration_or_quit(ctx.obj['settings_path']) + config = get_configuration_or_quit(ctx.obj["settings_path"]) reset_project(config) @@ -416,10 +416,10 @@ def reset_project(config: Configuration): """ metadata = load_current_metadata(config) if metadata is None: - logger.info('No metadata can be found, resetting aborted') + logger.info("No metadata can be found, resetting aborted") return - logger.info('Resetting project.') + logger.info("Resetting project.") for filepath in metadata.list_managed_files(): logger.debug(f"Unlinking file {filepath}.") try: @@ -448,7 +448,7 @@ class SyncResult: class DirectorySyncer: """A simple directory syncer""" - ignore_patterns = ('*.pyc', '*.pyo', 'CVS', 'tmp', '.git', '.svn', '__pycache__') + ignore_patterns = ("*.pyc", "*.pyo", "CVS", "tmp", ".git", ".svn", "__pycache__") def __init__(self, file_linker: FileLinker): self.file_linker = file_linker @@ -472,11 +472,11 @@ def sync_files( delete_files = set(delete_files) result = SyncResult() - for root, dirs, files in os.walk(source_dir, followlinks=False): + for root, dirs, raw_files in os.walk(source_dir, followlinks=False): # Ignore dirs and files which should be ignored # See: https://stackoverflow.com/questions/19859840/excluding-directories-in-os-walk dirs[:] = [d for d in dirs if not self.should_ignore(d)] - files = [f for f in files if not self.should_ignore(f)] + files = [f for f in raw_files if not self.should_ignore(f)] rel_path = root[len(str(source_dir)) :].lstrip(os.path.sep) dst_path = target_dir / rel_path @@ -502,7 +502,7 @@ def sync_files( if file_path.is_absolute(): continue if file_path not in result.added_files_relative: - logger.debug('Delete file %s.', file_path) + logger.debug("Delete file %s.", file_path) try: self.file_linker.unlink(target_dir / file_path) except FileNotFoundError: @@ -515,7 +515,7 @@ def sync_files( @click.pass_context def info(ctx): """Print current edition information""" - config = get_configuration_or_quit(ctx.obj['settings_path']) + config = get_configuration_or_quit(ctx.obj["settings_path"]) try: metadata = EditionMetaData.from_existed_project(config.get_project_root()) except MetadataNotFound: @@ -533,7 +533,7 @@ def help(ctx): logger.info("Example config file:") print( dedent( - ''' + """ # 存放主版本源码文件目录,留空默认为当前目录 # project_root = 'src/' # 存放各版本特殊源码文件的目录,留空默认等于 `project_root` @@ -553,7 +553,7 @@ def help(ctx): name = "EE" # 源码在 `editions_root` 里的相对路径,默认与 `name` 相等 rel_directory = 'ee' - ''' + """ ) ) @@ -564,7 +564,7 @@ def develop(ctx): """Enter develop mode, auto trigger edition activate procedure after files under current edtion directory have been modified. """ - config = get_configuration_or_quit(ctx.obj['settings_path']) + config = get_configuration_or_quit(ctx.obj["settings_path"]) project_root = config.get_project_root() try: metadata = EditionMetaData.from_existed_project(project_root)