aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2024-11-11 01:12:29 +0800
committerYuqian Yang <crupest@crupest.life>2025-01-07 21:38:21 +0800
commitc3308421b665e5d8dcb70b78acf137541a026555 (patch)
tree398740ccf1ee9b079c6357694385d0da08547807
parent0c7905bfc7bd19b70c7ec213dac041a07f53fdd5 (diff)
downloadcrupest-c3308421b665e5d8dcb70b78acf137541a026555.tar.gz
crupest-c3308421b665e5d8dcb70b78acf137541a026555.tar.bz2
crupest-c3308421b665e5d8dcb70b78acf137541a026555.zip
HALF WORK: 2024.1.7
-rw-r--r--crupest-words.txt5
-rw-r--r--tools/cru-py/.flake84
-rw-r--r--tools/cru-py/cru/service/_base.py1
-rw-r--r--tools/cru-py/cru/service/_config.py4
-rw-r--r--tools/cru-py/cru/service/_template.py54
-rw-r--r--tools/cru-py/cru/template.py135
-rw-r--r--tools/cru-py/poetry.lock209
-rw-r--r--tools/cru-py/pyproject.toml10
8 files changed, 108 insertions, 314 deletions
diff --git a/crupest-words.txt b/crupest-words.txt
index b0a9936..6b9961b 100644
--- a/crupest-words.txt
+++ b/crupest-words.txt
@@ -1,7 +1,3 @@
-# debian
-# secret
-# software
-# university
2fauth
aarch64
buildpackage
@@ -32,3 +28,4 @@ userid
ustc
vmess
vnext
+yuqian
diff --git a/tools/cru-py/.flake8 b/tools/cru-py/.flake8
deleted file mode 100644
index a77cb08..0000000
--- a/tools/cru-py/.flake8
+++ /dev/null
@@ -1,4 +0,0 @@
-[flake8]
-max-line-length = 80
-extend-select = B950
-extend-ignore = E203,E501,E701
diff --git a/tools/cru-py/cru/service/_base.py b/tools/cru-py/cru/service/_base.py
index f91eadd..98eed89 100644
--- a/tools/cru-py/cru/service/_base.py
+++ b/tools/cru-py/cru/service/_base.py
@@ -10,6 +10,7 @@ from cru import CruException, CruInternalError, CruPath
_F = TypeVar("_F")
+OWNER_NAME = "crupest"
class InternalAppException(CruInternalError):
pass
diff --git a/tools/cru-py/cru/service/_config.py b/tools/cru-py/cru/service/_config.py
index a387ef7..63b73b3 100644
--- a/tools/cru-py/cru/service/_config.py
+++ b/tools/cru-py/cru/service/_config.py
@@ -12,3 +12,7 @@ class ConfigManager(AppFeatureProvider):
@property
def config_path(self) -> AppFeaturePath:
return self._config_path
+
+ @property
+ def config_map(self) -> dict[str, str]:
+ raise NotImplementedError()
diff --git a/tools/cru-py/cru/service/_template.py b/tools/cru-py/cru/service/_template.py
index fcc5658..5f0252a 100644
--- a/tools/cru-py/cru/service/_template.py
+++ b/tools/cru-py/cru/service/_template.py
@@ -1,18 +1,23 @@
from argparse import ArgumentParser, Namespace
+from cru import CruIterator
from cru.template import TemplateTree
-from ._base import AppCommandFeatureProvider, AppFeaturePath
+from ._base import AppCommandFeatureProvider, AppFeaturePath, OWNER_NAME
+from ._config import ConfigManager
class TemplateManager(AppCommandFeatureProvider):
- def __init__(self, prefix: str = "CRUPEST"):
+ def __init__(self, prefix: str = OWNER_NAME.upper()):
super().__init__("template-manager")
+ self._prefix = prefix
self._templates_dir = self.app.add_path("templates", True)
self._generated_dir = self.app.add_path("generated", True)
- self._template_tree = TemplateTree(
- prefix, self._templates_dir.full_path_str, self._generated_dir.full_path_str
- )
+ self._template_tree: TemplateTree | None = None
+
+ @property
+ def prefix(self) -> str:
+ return self._prefix
@property
def templates_dir(self) -> AppFeaturePath:
@@ -22,9 +27,42 @@ class TemplateManager(AppCommandFeatureProvider):
def generated_dir(self) -> AppFeaturePath:
return self._generated_dir
+ @property
+ def template_tree(self) -> TemplateTree:
+ if self._template_tree is None:
+ return self.reload()
+ return self._template_tree
+
+ def reload(self) -> TemplateTree:
+ self._template_tree = TemplateTree(
+ self.prefix, self.templates_dir.full_path_str
+ )
+ return self._template_tree
+
+ def list_files(self) -> list[str]:
+ return (
+ CruIterator(self.template_tree.templates)
+ .transform(lambda t: t[0])
+ .to_list()
+ )
+
+ def print_file_lists(self) -> None:
+ for file in self.list_files():
+ print(file)
+
+ def generate_files(self) -> None:
+ config_manager = self.app.get_feature(ConfigManager)
+ self.template_tree.generate_to(
+ self.generated_dir.full_path_str, config_manager.config_map
+ )
+
def add_arg_parser(self, arg_parser: ArgumentParser) -> None:
subparsers = arg_parser.add_subparsers(dest="template_command")
- list_parser = subparsers.add_parser("list", help="List templates.")
- generate_parser = subparsers.add_parser("generate", help="Generate template.")
+ _list_parser = subparsers.add_parser("list", help="List templates.")
+ _generate_parser = subparsers.add_parser("generate", help="Generate template.")
- def run_command(self, args: Namespace) -> None: ...
+ def run_command(self, args: Namespace) -> None:
+ if args.template_command == "list":
+ self.print_file_lists()
+ elif args.template_command == "generate":
+ self.generate_files()
diff --git a/tools/cru-py/cru/template.py b/tools/cru-py/cru/template.py
index ccb3ad8..a02ea0e 100644
--- a/tools/cru-py/cru/template.py
+++ b/tools/cru-py/cru/template.py
@@ -1,4 +1,4 @@
-from collections.abc import Iterable, Mapping
+from collections.abc import Mapping
import os
import os.path
from string import Template
@@ -27,7 +27,7 @@ class CruTemplate:
return self._prefix
@property
- def real_template(self) -> Template:
+ def py_template(self) -> Template:
return self._template
@property
@@ -39,8 +39,16 @@ class CruTemplate:
return self._all_variables
@property
- def has_no_variables(self) -> bool:
- return len(self._variables) == 0
+ def has_variables(self) -> bool:
+ """
+ If the template does not has any variables that starts with the given prefix,
+ it returns False. This usually indicates that the template is not a real
+ template and should be copied as is. Otherwise, it returns True.
+
+ This can be used as a guard to prevent invalid templates created accidentally
+ without notice.
+ """
+ return len(self.variables) > 0
def generate(self, mapping: Mapping[str, str], allow_extra: bool = True) -> str:
values = dict(mapping)
@@ -51,118 +59,79 @@ class CruTemplate:
return self._template.safe_substitute(values)
-class CruTemplateFile(CruTemplate):
- def __init__(self, prefix: str, source: str, destination_path: str):
- self._source = source
- self._destination = destination_path
- with open(source, "r") as f:
- super().__init__(prefix, f.read())
-
- @property
- def source(self) -> str:
- return self._source
-
- @property
- def destination(self) -> str | None:
- return self._destination
-
- def generate_to_destination(
- self, mapping: Mapping[str, str], allow_extra: bool = True
- ) -> None:
- with open(self._destination, "w") as f:
- f.write(self.generate(mapping, allow_extra))
-
-
class TemplateTree:
def __init__(
self,
prefix: str,
source: str,
- destination: str,
- exclude: Iterable[str] | None = None,
- template_file_suffix: str = ".template",
+ template_file_suffix: str | None = ".template",
):
+ """
+ If template_file_suffix is not None, the files will be checked according to the
+ suffix of the file name. If the suffix matches, the file will be regarded as a
+ template file. Otherwise, it will be regarded as a non-template file.
+ Content of template file must contain variables that need to be replaced, while
+ content of non-template file may not contain any variables.
+ If either case is false, it generally means whether the file is a template is
+ wrongly handled.
+ """
self._prefix = prefix
- self._files: list[CruTemplateFile] | None = None
+ self._files: list[tuple[str, CruTemplate]] = []
self._source = source
- self._destination = destination
- self._exclude = [os.path.normpath(p) for p in exclude or []]
self._template_file_suffix = template_file_suffix
+ self._load()
@property
def prefix(self) -> str:
return self._prefix
@property
- def files(self) -> list[CruTemplateFile]:
- if self._files is None:
- self.reload()
- return self._files # type: ignore
-
- @property
- def template_files(self) -> list[CruTemplateFile]:
- return (
- CruIterator(self.files).filter(lambda f: not f.has_no_variables).to_list()
- )
-
- @property
- def non_template_files(self) -> list[CruTemplateFile]:
- return CruIterator(self.files).filter(lambda f: f.has_no_variables).to_list()
+ def templates(self) -> list[tuple[str, CruTemplate]]:
+ return self._files
@property
def source(self) -> str:
return self._source
@property
- def destination(self) -> str:
- return self._destination
-
- @property
- def exclude(self) -> list[str]:
- return self._exclude
-
- @property
- def template_file_suffix(self) -> str:
+ def template_file_suffix(self) -> str | None:
return self._template_file_suffix
@staticmethod
- def _scan_files(root_path: str, exclude: list[str]) -> Iterable[str]:
+ def _scan_files(root_path: str) -> list[str]:
+ files: list[str] = []
for root, _dirs, files in os.walk(root_path):
for file in files:
path = os.path.join(root, file)
path = os.path.relpath(path, root_path)
- is_exclude = False
- for exclude_path in exclude:
- if path.startswith(exclude_path):
- is_exclude = True
- break
- if not is_exclude:
- yield path
-
- def reload(self, strict=True) -> None:
- self._files = []
- file_names = self._scan_files(self.source, self.exclude)
- for file_name in file_names:
- source = os.path.join(self.source, file_name)
- destination = os.path.join(self.destination, file_name)
- file = CruTemplateFile(self._prefix, source, destination)
- if file_name.endswith(self.template_file_suffix):
- if strict and file.has_no_variables:
+ files.append(path)
+ return files
+
+ def _load(self) -> None:
+ files = self._scan_files(self.source)
+ for file_path in files:
+ template_file = os.path.join(self.source, file_path)
+ with open(template_file, "r") as f:
+ content = f.read()
+ template = CruTemplate(self.prefix, content)
+ if self.template_file_suffix is not None:
+ should_be_template = file_path.endswith(self.template_file_suffix)
+ if should_be_template and not template.has_variables:
raise CruTemplateError(
- f"Template file {file_name} has no variables."
+ f"Template file {file_path} has no variables."
)
- else:
- if strict and not file.has_no_variables:
- raise CruTemplateError(f"Non-template {file_name} has variables.")
- self._files.append(file)
+ elif not should_be_template and template.has_variables:
+ raise CruTemplateError(f"Non-template {file_path} has variables.")
+ self._files.append((file_path, template))
@property
def variables(self) -> set[str]:
s = set()
- for file in self.files:
- s.update(file.variables)
+ for _, template in self.templates:
+ s.update(template.variables)
return s
- def generate_to_destination(self, variables: Mapping[str, str]) -> None:
- for file in self.files:
- file.generate_to_destination(variables)
+ def generate_to(self, destination: str, variables: Mapping[str, str]) -> None:
+ for file, template in self.templates:
+ with open(os.path.join(destination, file), "w") as f:
+ f.write(template.generate(variables))
diff --git a/tools/cru-py/poetry.lock b/tools/cru-py/poetry.lock
index f0d3507..305aaee 100644
--- a/tools/cru-py/poetry.lock
+++ b/tools/cru-py/poetry.lock
@@ -1,153 +1,6 @@
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
[[package]]
-name = "attrs"
-version = "24.3.0"
-description = "Classes Without Boilerplate"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"},
- {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"},
-]
-
-[package.extras]
-benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
-cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
-dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
-docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
-tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
-tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
-
-[[package]]
-name = "black"
-version = "24.10.0"
-description = "The uncompromising code formatter."
-optional = false
-python-versions = ">=3.9"
-files = [
- {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"},
- {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"},
- {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"},
- {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"},
- {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"},
- {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"},
- {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"},
- {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"},
- {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"},
- {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"},
- {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"},
- {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"},
- {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"},
- {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"},
- {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"},
- {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"},
- {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"},
- {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"},
- {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"},
- {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"},
- {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"},
- {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"},
-]
-
-[package.dependencies]
-click = ">=8.0.0"
-mypy-extensions = ">=0.4.3"
-packaging = ">=22.0"
-pathspec = ">=0.9.0"
-platformdirs = ">=2"
-
-[package.extras]
-colorama = ["colorama (>=0.4.3)"]
-d = ["aiohttp (>=3.10)"]
-jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
-uvloop = ["uvloop (>=0.15.2)"]
-
-[[package]]
-name = "click"
-version = "8.1.8"
-description = "Composable command line interface toolkit"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
- {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "platform_system == \"Windows\""}
-
-[[package]]
-name = "colorama"
-version = "0.4.6"
-description = "Cross-platform colored terminal text."
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
-files = [
- {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
- {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
-]
-
-[[package]]
-name = "flake8"
-version = "7.1.1"
-description = "the modular source code checker: pep8 pyflakes and co"
-optional = false
-python-versions = ">=3.8.1"
-files = [
- {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"},
- {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"},
-]
-
-[package.dependencies]
-mccabe = ">=0.7.0,<0.8.0"
-pycodestyle = ">=2.12.0,<2.13.0"
-pyflakes = ">=3.2.0,<3.3.0"
-
-[[package]]
-name = "flake8-bugbear"
-version = "24.12.12"
-description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
-optional = false
-python-versions = ">=3.8.1"
-files = [
- {file = "flake8_bugbear-24.12.12-py3-none-any.whl", hash = "sha256:1b6967436f65ca22a42e5373aaa6f2d87966ade9aa38d4baf2a1be550767545e"},
- {file = "flake8_bugbear-24.12.12.tar.gz", hash = "sha256:46273cef0a6b6ff48ca2d69e472f41420a42a46e24b2a8972e4f0d6733d12a64"},
-]
-
-[package.dependencies]
-attrs = ">=22.2.0"
-flake8 = ">=6.0.0"
-
-[package.extras]
-dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"]
-
-[[package]]
-name = "isort"
-version = "5.13.2"
-description = "A Python utility / library to sort Python imports."
-optional = false
-python-versions = ">=3.8.0"
-files = [
- {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
- {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
-]
-
-[package.extras]
-colors = ["colorama (>=0.4.6)"]
-
-[[package]]
-name = "mccabe"
-version = "0.7.0"
-description = "McCabe checker, plugin for flake8"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
- {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
-]
-
-[[package]]
name = "mypy"
version = "1.14.0"
description = "Optional static typing for Python"
@@ -211,66 +64,6 @@ files = [
]
[[package]]
-name = "packaging"
-version = "24.2"
-description = "Core utilities for Python packages"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
- {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
-]
-
-[[package]]
-name = "pathspec"
-version = "0.12.1"
-description = "Utility library for gitignore style pattern matching of file paths."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
- {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
-]
-
-[[package]]
-name = "platformdirs"
-version = "4.3.6"
-description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
- {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
-]
-
-[package.extras]
-docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"]
-type = ["mypy (>=1.11.2)"]
-
-[[package]]
-name = "pycodestyle"
-version = "2.12.1"
-description = "Python style guide checker"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"},
- {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"},
-]
-
-[[package]]
-name = "pyflakes"
-version = "3.2.0"
-description = "passive checker of Python programs"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"},
- {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"},
-]
-
-[[package]]
name = "typing-extensions"
version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
@@ -284,4 +77,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
-content-hash = "3c8ab8fb983214158a2f8628056f049759bc453240b3c2dc5524b7a501e3c213"
+content-hash = "34a84c9f444021c048be3a70dbb3246bb73c4e7e8f0cc980b8050debcf21a6f9"
diff --git a/tools/cru-py/pyproject.toml b/tools/cru-py/pyproject.toml
index 76c2d79..15da910 100644
--- a/tools/cru-py/pyproject.toml
+++ b/tools/cru-py/pyproject.toml
@@ -11,15 +11,11 @@ readme = "README.md"
python = "^3.11"
[tool.poetry.group.dev.dependencies]
-black = "^24.10.0"
-isort = "^5.13.2"
-flake8 = "^7.1.1"
-flake8-bugbear = "^24.10.31"
mypy = "^1.13.0"
+[tool.ruff.lint]
+select = ["E", "F", "B"]
+
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
-
-[tool.isort]
-profile = "black"