From 738d9d543edcb8d2b0bb740960972ce9bfe58cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=BE=D0=BA=D0=BE=D1=81=20=D0=90=D1=80=D1=82=D0=B5?= =?UTF-8?q?=D0=BC=20=D0=9D=D0=B8=D0=BA=D0=BE=D0=BB=D0=B0=D0=B5=D0=B2=D0=B8?= =?UTF-8?q?=D1=87?= Date: Mon, 29 Jun 2026 11:53:09 +0700 Subject: [PATCH] fix: mount retry adapter on Redmine engine session python-redmine exposes as a context manager, while the actual requests.Session lives in . Mounting the retry adapter on the wrong object caused: 'function' object has no attribute 'mount' on startup. Update the related test to mock the real session. --- redmine_reporter/client.py | 15 ++++++--------- tests/test_client.py | 8 +++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/redmine_reporter/client.py b/redmine_reporter/client.py index f3dc8b6..afa22f9 100644 --- a/redmine_reporter/client.py +++ b/redmine_reporter/client.py @@ -47,25 +47,22 @@ def _create_redmine() -> Redmine: }, ) - # Монтируем retry-адаптер на сессию для автоматических повторов + # Монтируем retry-адаптер на сессию для автоматических повторов. + # В python-redmine сессия живёт в engine, а redmine.session — контекстный менеджер. retry_adapter = _make_retry_adapter() - redmine.session.mount("https://", retry_adapter) - redmine.session.mount("http://", retry_adapter) + redmine.engine.session.mount("https://", retry_adapter) + redmine.engine.session.mount("http://", retry_adapter) return redmine -def _fetch_issues_chunked( - redmine: Redmine, issue_ids: List[int] -) -> List[Issue]: +def _fetch_issues_chunked(redmine: Redmine, issue_ids: List[int]) -> List[Issue]: """Загружает задачи чанками, чтобы не превышать лимит длины URL (#21).""" all_issues: List[Issue] = [] for i in range(0, len(issue_ids), ISSUE_ID_CHUNK_SIZE): chunk = issue_ids[i : i + ISSUE_ID_CHUNK_SIZE] issue_list_str = ",".join(str(x) for x in chunk) - issues = redmine.issue.filter( - issue_id=issue_list_str, status_id="*", sort="project:asc" - ) + issues = redmine.issue.filter(issue_id=issue_list_str, status_id="*", sort="project:asc") all_issues.extend(issues) return all_issues diff --git a/tests/test_client.py b/tests/test_client.py index 71a94fe..5198a08 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -196,16 +196,14 @@ def test_fetch_mounts_retry_adapter(mock_redmine_class): fetch_issues_with_spent_time("2026-01-01", "2026-01-31") - # Проверяем, что session.mount был вызван для http:// и https:// - mount_calls = mock_redmine.session.mount.call_args_list + # Проверяем, что engine.session.mount был вызван для http:// и https:// + mount_calls = mock_redmine.engine.session.mount.call_args_list prefixes = [call.args[0] for call in mount_calls] assert "https://" in prefixes assert "http://" in prefixes # Проверяем retry-конфигурацию адаптера - https_adapter = next( - call.args[1] for call in mount_calls if call.args[0] == "https://" - ) + https_adapter = next(call.args[1] for call in mount_calls if call.args[0] == "https://") max_retries = https_adapter.max_retries assert max_retries.total == 3 assert 429 in max_retries.status_forcelist