From 92e82b06f676aa4dabeb5718a5de29a872589ac9 Mon Sep 17 00:00:00 2001 From: Miyamura80 Date: Mon, 26 Jan 2026 16:31:43 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=94=A8=20replace=20Black=20with=20Ruf?= =?UTF-8?q?f=20formatter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/rules/makefile-commands.md | 2 +- CLAUDE.md | 2 +- Makefile | 13 ++-- README.md | 5 +- docs/package.json | 2 +- docs/tsconfig.json | 18 ++++-- pyproject.toml | 1 - tests/healthcheck/test_env_var_loading.py | 14 ++--- .../test_pydantic_type_coercion.py | 62 +++++++++---------- tests/test_logging_thread_safety.py | 6 +- uv.lock | 53 ---------------- 11 files changed, 66 insertions(+), 112 deletions(-) diff --git a/.cursor/rules/makefile-commands.md b/.cursor/rules/makefile-commands.md index dbb21c3..f2ed7f6 100644 --- a/.cursor/rules/makefile-commands.md +++ b/.cursor/rules/makefile-commands.md @@ -5,5 +5,5 @@ Some helpful commands have been defined in the `Makefile` already. If the user is confused, point them towards these resources. - `make all` - runs `main.py` -- `make lint` - runs `black` linter, an opinionated linter +- `make fmt` - runs `ruff format` + JSON formatting - `make test` - runs all tests defined by `TEST_TARGETS = tests/folder1 tests/folder2` diff --git a/CLAUDE.md b/CLAUDE.md index b68b1e6..06e8759 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -21,7 +21,7 @@ make test_slow # Run slow tests only make test_nondeterministic # Run nondeterministic tests only # Code Quality (run after major changes) -make fmt # Run black formatter + JSON formatting +make fmt # Run ruff formatter + JSON formatting make ruff # Run ruff linter make vulture # Find dead code make ty # Run typer type checker diff --git a/Makefile b/Makefile index be82d2b..c3e7c4d 100644 --- a/Makefile +++ b/Makefile @@ -171,24 +171,23 @@ test_flaky: check_uv ## Repeat fast tests to detect flaky tests ######################################################## # Linter will ignore these directories -IGNORE_LINT_DIRS = .venv|venv -LINE_LENGTH = 88 +IGNORE_LINT_DIRS = .venv venv +FIND_PRUNE = $(foreach dir,$(IGNORE_LINT_DIRS),-path "./$(dir)" -o) -false ### Code Quality install_tools: check_uv ## Install linting/formatting tools @echo "$(YELLOW)🔧Installing tools...$(RESET)" - @uv tool install black --force @uv tool install ruff --force @uv tool install ty --force @uv tool install vulture --force @echo "$(GREEN)✅Tools installed.$(RESET)" -fmt: install_tools check_jq ## Format code with black and jq - @echo "$(YELLOW)✨Formatting project with Black...$(RESET)" - @uv tool run black --exclude '/($(IGNORE_LINT_DIRS))/' . --line-length $(LINE_LENGTH) +fmt: install_tools check_jq ## Format code with ruff and jq + @echo "$(YELLOW)✨Formatting project with Ruff...$(RESET)" + @uv tool run ruff format @echo "$(YELLOW)✨Formatting JSONs with jq...$(RESET)" @count=0; \ - find . \( $(IGNORE_LINT_DIRS:%=-path './%' -prune -o) \) -type f -name '*.json' -print0 | \ + find . \( $(FIND_PRUNE) \) -prune -o -type f -name '*.json' -print0 | \ while IFS= read -r -d '' file; do \ if jq . "$$file" > "$$file.tmp" 2>/dev/null && mv "$$file.tmp" "$$file"; then \ count=$$((count + 1)); \ diff --git a/README.md b/README.md index ac95f97..6af8942 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Opinionated Python stack for fast development. The `saas` branch extends `main` | Feature | `main` | `saas` | |---------|:------:|:------:| | UV + Pydantic config | ✅ | ✅ | -| CI/Linters (Ruff, Black, Vulture) | ✅ | ✅ | +| CI/Linters (Ruff, Vulture) | ✅ | ✅ | | Pre-commit hooks (prek) | ✅ | ✅ | | LLM (DSPY + LangFuse Observability) | ✅ | ✅ | | FastAPI + Uvicorn | ❌ | ✅ | @@ -55,7 +55,7 @@ Opinionated Python stack for fast development. The `saas` branch extends `main` ## Quick Start - `make all` - runs `main.py` -- `make fmt` - runs `black` linter, an opinionated linter +- `make fmt` - runs `ruff format` + JSON formatting - `make banner` - create a new banner that makes the README nice 😊 - `make test` - runs all tests defined by `TEST_TARGETS = tests/folder1 tests/folder2` @@ -91,4 +91,3 @@ This software uses the following tools: Made with [contrib.rocks](https://contrib.rocks). - diff --git a/docs/package.json b/docs/package.json index 94a2a81..c56f0c9 100644 --- a/docs/package.json +++ b/docs/package.json @@ -34,4 +34,4 @@ "tailwindcss": "^4.1.18", "typescript": "^5.9.3" } -} \ No newline at end of file +} diff --git a/docs/tsconfig.json b/docs/tsconfig.json index 3838223..797119b 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -2,7 +2,11 @@ "compilerOptions": { "baseUrl": ".", "target": "ESNext", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -16,8 +20,12 @@ "jsx": "react-jsx", "incremental": true, "paths": { - "@/*": ["./*"], - "fumadocs-mdx:collections/*": [".source/*"] + "@/*": [ + "./*" + ], + "fumadocs-mdx:collections/*": [ + ".source/*" + ] }, "plugins": [ { @@ -32,5 +40,7 @@ ".next/types/**/*.ts", ".next/dev/types/**/*.ts" ], - "exclude": ["node_modules"] + "exclude": [ + "node_modules" + ] } diff --git a/pyproject.toml b/pyproject.toml index ff2cae7..2b117e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,6 @@ authors = [ { name = "Miyamura80", email = "eitomiyamura@gmail.com" } ] dependencies = [ - "black>=24.8.0", "pyyaml>=6.0.2", "python-dotenv>=1.0.1", "human-id>=0.2.0", diff --git a/tests/healthcheck/test_env_var_loading.py b/tests/healthcheck/test_env_var_loading.py index 628c924..c1b92da 100644 --- a/tests/healthcheck/test_env_var_loading.py +++ b/tests/healthcheck/test_env_var_loading.py @@ -31,7 +31,7 @@ def test_env_var_loading_precedence(monkeypatch): monkeypatch.setenv("OPENAI_API_KEY", "system_openai_key") # 2. Create a temporary .env file - dot_env_content = "DEV_ENV=dotenv\n" "OPENAI_API_KEY=dotenv_openai_key\n" + dot_env_content = "DEV_ENV=dotenv\nOPENAI_API_KEY=dotenv_openai_key\n" with open(dot_env_path, "w") as f: f.write(dot_env_content) @@ -41,12 +41,12 @@ def test_env_var_loading_precedence(monkeypatch): # 4. Assert that the variables are loaded with the correct precedence assert reloaded_config.DEV_ENV == "dotenv", "Should load from .env first" - assert ( - reloaded_config.ANTHROPIC_API_KEY == "system_anthropic_key" - ), "Should fall back to system env" - assert ( - reloaded_config.OPENAI_API_KEY == "dotenv_openai_key" - ), "Should load from .env" + assert reloaded_config.ANTHROPIC_API_KEY == "system_anthropic_key", ( + "Should fall back to system env" + ) + assert reloaded_config.OPENAI_API_KEY == "dotenv_openai_key", ( + "Should load from .env" + ) finally: # Clean up and restore the original .env file if it existed diff --git a/tests/healthcheck/test_pydantic_type_coercion.py b/tests/healthcheck/test_pydantic_type_coercion.py index 0cde858..2c49e3a 100644 --- a/tests/healthcheck/test_pydantic_type_coercion.py +++ b/tests/healthcheck/test_pydantic_type_coercion.py @@ -39,51 +39,51 @@ def test_pydantic_type_coercion(monkeypatch): config = common_module.global_config # type: ignore[attr-defined] # Verify integer coercion - assert isinstance( - config.default_llm.default_max_tokens, int - ), "default_max_tokens should be int" - assert ( - config.default_llm.default_max_tokens == 50000 - ), "default_max_tokens should be 50000" - - assert isinstance( - config.llm_config.retry.max_attempts, int - ), "max_attempts should be int" + assert isinstance(config.default_llm.default_max_tokens, int), ( + "default_max_tokens should be int" + ) + assert config.default_llm.default_max_tokens == 50000, ( + "default_max_tokens should be 50000" + ) + + assert isinstance(config.llm_config.retry.max_attempts, int), ( + "max_attempts should be int" + ) assert config.llm_config.retry.max_attempts == 5, "max_attempts should be 5" - assert isinstance( - config.llm_config.retry.min_wait_seconds, int - ), "min_wait_seconds should be int" + assert isinstance(config.llm_config.retry.min_wait_seconds, int), ( + "min_wait_seconds should be int" + ) assert config.llm_config.retry.min_wait_seconds == 2, "min_wait_seconds should be 2" - assert isinstance( - config.llm_config.retry.max_wait_seconds, int - ), "max_wait_seconds should be int" - assert ( - config.llm_config.retry.max_wait_seconds == 10 - ), "max_wait_seconds should be 10" + assert isinstance(config.llm_config.retry.max_wait_seconds, int), ( + "max_wait_seconds should be int" + ) + assert config.llm_config.retry.max_wait_seconds == 10, ( + "max_wait_seconds should be 10" + ) # Verify float coercion - assert isinstance( - config.default_llm.default_temperature, float - ), "default_temperature should be float" - assert ( - config.default_llm.default_temperature == 0.7 - ), "default_temperature should be 0.7" + assert isinstance(config.default_llm.default_temperature, float), ( + "default_temperature should be float" + ) + assert config.default_llm.default_temperature == 0.7, ( + "default_temperature should be 0.7" + ) # Verify boolean coercion - assert isinstance( - config.llm_config.cache_enabled, bool - ), "cache_enabled should be bool" + assert isinstance(config.llm_config.cache_enabled, bool), ( + "cache_enabled should be bool" + ) assert config.llm_config.cache_enabled is True, "cache_enabled should be True" assert isinstance(config.logging.verbose, bool), "verbose should be bool" assert config.logging.verbose is False, "verbose should be False" assert isinstance(config.logging.format.show_time, bool), "show_time should be bool" - assert ( - config.logging.format.show_time is True - ), "show_time should be True (from '1')" + assert config.logging.format.show_time is True, ( + "show_time should be True (from '1')" + ) assert isinstance(config.logging.levels.debug, bool), "debug should be bool" assert config.logging.levels.debug is True, "debug should be True" diff --git a/tests/test_logging_thread_safety.py b/tests/test_logging_thread_safety.py index 47efc7c..9aef2eb 100644 --- a/tests/test_logging_thread_safety.py +++ b/tests/test_logging_thread_safety.py @@ -43,7 +43,7 @@ def call_setup(): logger_any.remove = original_remove assert not errors, f"Errors during concurrent setup: {errors}" - assert ( - call_count == 1 - ), f"logger.remove() called {call_count} times, expected exactly 1" + assert call_count == 1, ( + f"logger.remove() called {call_count} times, expected exactly 1" + ) assert logging_module._logging_initialized is True diff --git a/uv.lock b/uv.lock index 39cc9af..817cefc 100644 --- a/uv.lock +++ b/uv.lock @@ -146,30 +146,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, ] -[[package]] -name = "black" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "mypy-extensions" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988 }, - { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985 }, - { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816 }, - { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860 }, - { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673 }, - { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190 }, - { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926 }, - { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613 }, - { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646 }, -] - [[package]] name = "cachetools" version = "5.5.2" @@ -1010,15 +986,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/d9/f7f9379981e39b8c2511c9e0326d212accacb82f12fbfdc1aa2ce2a7b2b6/multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3", size = 133351 }, ] -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 }, -] - [[package]] name = "numpy" version = "2.3.2" @@ -1162,15 +1129,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/f9/07086f5b0f2a19872554abeea7658200824f5835c58a106fa8f2ae96a46c/pandas-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5db9637dbc24b631ff3707269ae4559bce4b7fd75c1c4d7e13f40edc42df4444", size = 13189044 }, ] -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, -] - [[package]] name = "pillow" version = "11.3.0" @@ -1237,15 +1195,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835 }, ] -[[package]] -name = "platformdirs" -version = "4.3.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567 }, -] - [[package]] name = "pluggy" version = "1.6.0" @@ -1535,7 +1484,6 @@ name = "python-template" version = "0.1.0" source = { editable = "." } dependencies = [ - { name = "black" }, { name = "dspy" }, { name = "google-genai" }, { name = "human-id" }, @@ -1559,7 +1507,6 @@ dependencies = [ [package.metadata] requires-dist = [ - { name = "black", specifier = ">=24.8.0" }, { name = "dspy", specifier = ">=2.6.24" }, { name = "google-genai", specifier = ">=1.15.0" }, { name = "human-id", specifier = ">=0.2.0" }, From 7dc829119a542d23bdd45d117dfcffee251a51b5 Mon Sep 17 00:00:00 2001 From: Miyamura80 Date: Mon, 26 Jan 2026 16:45:34 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20restore=20line=20lengt?= =?UTF-8?q?h=20setting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index c3e7c4d..8720de0 100644 --- a/Makefile +++ b/Makefile @@ -172,6 +172,7 @@ test_flaky: check_uv ## Repeat fast tests to detect flaky tests # Linter will ignore these directories IGNORE_LINT_DIRS = .venv venv +LINE_LENGTH = 88 FIND_PRUNE = $(foreach dir,$(IGNORE_LINT_DIRS),-path "./$(dir)" -o) -false ### Code Quality From 4b17b8c5b3d19315ded3f6d07f6dfb63aea605ea Mon Sep 17 00:00:00 2001 From: Miyamura80 Date: Mon, 26 Jan 2026 16:55:13 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20update=20branch=20comp?= =?UTF-8?q?arison=20for=20Ruff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manual_docs/branch_comparison.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual_docs/branch_comparison.md b/manual_docs/branch_comparison.md index 5020950..57280cf 100644 --- a/manual_docs/branch_comparison.md +++ b/manual_docs/branch_comparison.md @@ -25,7 +25,7 @@ This document provides a detailed comparison of features available in the `main` | Feature | `main` | `saas` | Notes | |---------|:------:|:------:|-------| | Ruff linter | ✅ | ✅ | Fast Python linter | -| Black formatter | ✅ | ✅ | Opinionated code formatting | +| Ruff formatter | ✅ | ✅ | Opinionated code formatting | | Vulture dead code detection | ✅ | ✅ | Find unused code | | ty type checker | ✅ | ✅ | Type checking | | GitHub Actions CI | ✅ | ✅ | Automated testing & linting |