Migrate linting from flake8 to ruff - #5
Conversation
Convert agent and server to standalone uv projects using the hatchling build backend, with metadata moved from setup.cfg into pyproject.toml ([project] table) and committed uv.lock files. Remove setup.py/setup.cfg. Add e2e_tests as a third uv project that pulls in agent and server via editable path sources, and move its test modules into e2e_tests/tests/ to match the agent/server layout. Add per-component Makefiles (check = lint + test) and make the root Makefile delegate via $(MAKE) -C; add a server `image` target that builds the container with podman and smoke-checks the entrypoint. Keep run_e2e_tests as a backwards-compatible alias. Rewrite the Dockerfile and the python-tests/e2e-tests workflows to use uv (astral-sh/setup-uv, uv sync --frozen). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Bundle the MIT LICENSE into the agent and server wheels by symlinking the root LICENSE into each component (hatchling picks it up via PEP 639). - server/Dockerfile: bump base image to python:3.14 and install dependencies in their own cached layer (uv sync --no-install-project) before copying the source, so source changes no longer invalidate the dependency install. - CLAUDE.md: add an "Agent workflow" note to review changes via a background subagent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Martin Hanzík <martin@hanzik.com>
The debug log in test_rotate_log_file referenced expected_dst_second_file, which is only defined in test_new_log_file_gets_copied. Use expected_dst_file instead: the destination path actually in scope, and the same variable the analogous first wait-loop already logs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a lint target to e2e_tests/Makefile, identical to the agent and server ones, and make it part of the default check (check: lint test). Recurse into e2e_tests from the root lint target so the aggregate lint now covers all three components, matching the existing check and test aggregates. Previously the e2e test code was the only Python in the repo not covered by flake8. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Point the "project readme file" link at leadhub-code/logline instead of the old messa/logline location, matching the homepage URLs already corrected in the pyproject files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch both components from hatchling to uv's own uv_build backend: - requires = ["uv_build>=0.11,<0.12"], build-backend = "uv_build" - [tool.uv.build-backend] module-root = "" for the flat layout, since the package dir lives in the project root (not under src/) and uv_build defaults module-root to "src" - license-files = ["LICENSE"] because uv_build, unlike hatchling, does not auto-glob license files Replace the agent/server LICENSE symlinks with real copies of the repo-root MIT license. uv_build validates license-files while building the project, and the upward ../LICENSE symlink escaped the build root, which broke both the server Docker build (context is server/) and isolated sdist builds. Real copies fix every build path (wheel, sdist, Docker). Copy LICENSE into the server image so uv_build's metadata validation passes during uv sync. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace flake8 with ruff across all three components (agent, server, e2e_tests). The ruff configuration lives in each component's pyproject.toml: line length 150, rule sets E/F/W/I, and isort with two blank lines after the import block. force-sort-within-sections keeps the existing "sort by module name" import style, so reordering churn is small. Update the Makefiles and GitHub Actions workflows to lint with `uvx ruff check .`. The e2e_tests component is now linted as well (previously it had no lint step), which surfaced a latent bug: a debug log in test_rotate_log_file referenced an undefined name (expected_dst_second_file instead of expected_dst_file). Apply ruff's autofixes (import sorting, unused imports, redundant f-string prefix, unused exception variable) and fix the remaining findings by hand: - re-export the entry points via __all__ in the package __init__ modules - reference the imported module in the smoke tests - use `is not None` / `not ...` instead of `!= None` / `== False` - fix the undefined name in the e2e rotate test Document the Python style conventions (line length, two blank lines after imports, preferring `from X import Y`) in CLAUDE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hoist function-local imports to module level where there is no reason to defer them: - logging name imports in setup_logging/setup_log_file (agent and server) - the logging import in the e2e conftest - the lazy ssl imports in the TLS branches (agent client, server) - the lazy yaml imports in the configuration loaders (agent and server) Also switch hashlib usage to `from hashlib import sha1` to match the `from X import Y` convention. Conditional compatibility shims (try/except ImportError in asyncio_helpers and util), the optional zstandard/zstd fallbacks, and the smoke-test imports are intentionally left local. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The minimum supported Python version is 3.9, where asyncio.run, asyncio.create_task and asyncio.get_running_loop (all 3.7+) and asyncio.to_thread (3.9+) are always available, so the fallback shims are dead code. Delete the agent's asyncio_helpers module and import run/create_task/ to_thread directly from asyncio. Drop the to_thread shim from the server's util module the same way. The zstandard/zstd fallback in util is intentionally kept: it selects between two optional libraries at runtime and is not a Python-version shim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In the agent's obfuscate_secrets(), re.ASCII was passed as the 4th positional argument to re.sub(), which is `count`, not `flags`. That made the flag a no-op (256 used as the replacement count) and triggered a DeprecationWarning on Python 3.13+. Pass it as `flags=re.ASCII`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
datetime.utcnow() returns a naive datetime and is deprecated since Python
3.12. Use datetime.now(timezone.utc) instead. The rendered string is
unchanged: the strftime format ('%Y%m%dT%H%M%SZ') has no %z/%Z field and
the trailing Z is a literal, so the rotated-file suffix stays identical.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR migrates Python linting across the repo from flake8 to ruff (agent/server/e2e_tests), updates CI/Makefiles accordingly, and applies small code cleanups and compatibility-shim removals to satisfy the stricter linter and the project’s requires-python >= 3.9.
Changes:
- Replace flake8 with ruff: add per-component Ruff configuration in
pyproject.toml, and switchmake lint+ GitHub Actions lint steps touvx ruff check .. - Remove obsolete asyncio compatibility shims (e.g.,
asyncio_helpers.py) and importrun/create_task/to_threaddirectly fromasyncio. - Apply Ruff-driven cleanups (import ordering,
is not None/not ..., fixre.sub(..., flags=...), anddatetime.now(timezone.utc)).
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
server/tests/test_smoke.py |
Ensure the import is “used” to satisfy unused-import linting. |
server/tests/test_destination_path.py |
Import ordering cleanup consistent with Ruff/isort rules. |
server/pyproject.toml |
Add Ruff configuration (line length + E/F/W/I + isort settings). |
server/Makefile |
Switch lint target to uvx ruff check .. |
server/logline_server/util.py |
Remove legacy to_thread polyfill; rely on stdlib asyncio.to_thread. |
server/logline_server/main.py |
Import hoisting/cleanup; utcnow() deprecation fix; != None → is not None; SHA1 import style change. |
server/logline_server/configuration.py |
Hoist yaml import to module scope. |
server/logline_server/__main__.py |
Add spacing to satisfy import-block formatting. |
server/logline_server/__init__.py |
Add __all__ export and spacing per import-block formatting. |
e2e_tests/tests/test_tls.py |
Import cleanups and SHA1 import style change. |
e2e_tests/tests/test_e2e.py |
Import cleanups; boolean assertion simplification. |
e2e_tests/tests/conftest.py |
Hoist logging imports to module scope. |
e2e_tests/pyproject.toml |
Add Ruff configuration for the e2e project. |
e2e_tests/Makefile |
Switch lint target to uvx ruff check .. |
CLAUDE.md |
Document Ruff-based Python style/linting workflow and conventions. |
agent/tests/test_smoke.py |
Ensure the import is “used” to satisfy unused-import linting. |
agent/tests/test_main.py |
Import ordering cleanup consistent with Ruff/isort rules. |
agent/tests/conftest.py |
Add spacing per import-block formatting. |
agent/pyproject.toml |
Add Ruff configuration (line length + E/F/W/I + isort settings). |
agent/Makefile |
Switch lint target to uvx ruff check .. |
agent/logline_agent/main.py |
Remove asyncio helper imports and hoist logging imports. |
agent/logline_agent/configuration.py |
Hoist yaml import to module scope. |
agent/logline_agent/client.py |
Use stdlib asyncio.to_thread; fix re.sub flags argument; import cleanups. |
agent/logline_agent/asyncio_helpers.py |
Delete obsolete asyncio compatibility shim module. |
agent/logline_agent/__main__.py |
Add spacing to satisfy import-block formatting. |
agent/logline_agent/__init__.py |
Add __all__ export and spacing per import-block formatting. |
.github/workflows/python-tests.yml |
Switch lint steps from flake8 to ruff for agent/server jobs. |
.github/workflows/e2e-tests.yml |
Add Ruff lint step for e2e tests workflow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@copilot Why there are no checks running? |
Because all CI workflows are configured to run only when the PR base branch is |
Drop the `branches: [ main ]` filter from the pull_request trigger in all workflows so that PRs targeting a feature branch (e.g. stacked PRs) also get CI. The push trigger stays restricted to main, so feature-branch pushes don't produce a second, redundant run. Also drop the now-inaccurate "must be a subset" comment in the CodeQL workflow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Replaces flake8 with ruff across all three components (
agent,server,e2e_tests) and applies a few related code cleanups that the stricter linter surfaced.Changes
pyproject.toml: line length 150, rule setsE/F/W/I, and isort withforce-sort-within-sections+ two blank lines after the import block. The Makefiles and the GitHub Actions workflows now lint withuvx ruff check ..e2e_testsis now linted too (it previously had no lint step).!= None/== False→is not None/not ..., a redundant f-string prefix, an unusedexcept ... as e,__all__re-exports in the package__init__modules, and a latent bug — an undefined name in the e2e rotate-log test's debug logging (this overlaps with Migrate to uv with per-component projects and Makefiles #3's own fix and merged cleanly).ssl/yaml/loggingimports and switchhashlibusage tofrom hashlib import sha1. Conditional compatibility shims and optional-dependency fallbacks are intentionally left local.requires-python >= 3.9,asyncio.run/create_task/to_threadare always available, soasyncio_helpers.pyis deleted and they are imported directly fromasyncio.re.sub(..., re.ASCII)passed the flag as the positionalcountargument (nowflags=re.ASCII);datetime.utcnow()→datetime.now(timezone.utc).Testing
make checkis green for all three components: ruff clean, and 2 agent + 12 server + 7 e2e tests pass (including the real TLS round-trip).🤖 Generated with Claude Code