code-analysis-client 1.6.240__tar.gz → 1.6.242__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/PKG-INFO +68 -37
  2. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/README.md +66 -35
  3. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/__init__.py +3 -11
  4. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/check_chain.py +2 -2
  5. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/client.py +111 -78
  6. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/config.py +46 -0
  7. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/exceptions.py +16 -6
  8. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/file_session.py +2 -2
  9. code_analysis_client-1.6.242/code_analysis_client/queue_wait.py +301 -0
  10. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/responses.py +44 -1
  11. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/universal_file.py +2 -2
  12. code_analysis_client-1.6.242/code_analysis_client/version.txt +1 -0
  13. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/worktree.py +2 -2
  14. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client.egg-info/PKG-INFO +68 -37
  15. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client.egg-info/requires.txt +1 -1
  16. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/pyproject.toml +1 -1
  17. code_analysis_client-1.6.240/code_analysis_client/queue_wait.py +0 -396
  18. code_analysis_client-1.6.240/code_analysis_client/version.txt +0 -1
  19. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/commands_proxy.py +0 -0
  20. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/py.typed +0 -0
  21. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/schema_disk_cache.py +0 -0
  22. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/server_api.py +0 -0
  23. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/server_schema.py +0 -0
  24. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/validation.py +0 -0
  25. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client/vectorizer_state.py +0 -0
  26. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client.egg-info/SOURCES.txt +0 -0
  27. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client.egg-info/dependency_links.txt +0 -0
  28. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/code_analysis_client.egg-info/top_level.txt +0 -0
  29. {code_analysis_client-1.6.240 → code_analysis_client-1.6.242}/setup.cfg +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-analysis-client
3
- Version: 1.6.240
3
+ Version: 1.6.242
4
4
  Summary: Async JSON-RPC client for the code-analysis MCP server (mcp-proxy-adapter JsonRpcClient)
5
5
  Author-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
6
6
  Requires-Python: >=3.10
7
7
  Description-Content-Type: text/markdown
8
- Requires-Dist: mcp-proxy-adapter>=8.11.3
8
+ Requires-Dist: mcp-proxy-adapter>=8.11.5
9
9
  Provides-Extra: dev
10
10
  Requires-Dist: pytest>=8.0; extra == "dev"
11
11
  Requires-Dist: pytest-asyncio>=0.25.0; extra == "dev"
@@ -54,58 +54,89 @@ from code_analysis_client import CodeAnalysisAsyncClient
54
54
  client = CodeAnalysisAsyncClient.from_server_config(config_dict, timeout=60.0)
55
55
  ```
56
56
 
57
- ### Queued commands are handled automatically
57
+ ### Every call is the adapter's standard call
58
58
 
59
59
  Every entry point — `call`, `call_validated`, `client.commands.<name>`, and the
60
- `file_sessions` / `universal_files` facades built on `call_validated` — routes
61
- through one queue-aware core. If the server's immediate response is a queued-job
62
- envelope (either deployed shape: `poll_with`/`store: "queuemgr"`, or
63
- `queued_after_timeout`), the client polls `queue_get_job_status` for you until
64
- the job reaches a terminal state, then returns the unwrapped inner result — the
65
- same shape you'd get from a synchronous call. You never see the raw envelope.
60
+ `file_sessions` / `universal_files` / `worktrees` / `check_chains` facades built on
61
+ `call_validated` — goes through one core that uses `mcp-proxy-adapter`'s
62
+ `JsonRpcClient.execute_command_unified`: the command runs in a WebSocket command
63
+ session and the call returns when the terminal event arrives (no polling while the
64
+ socket is up). If `/ws` is unreachable the adapter falls back to RPC polling of
65
+ `queue_get_job_status` and logs one WARNING `WS_UNREACHABLE_RPC_POLLING_FALLBACK`;
66
+ the result is identical. The client has no polling loop of its own.
66
67
 
67
68
  ```python
68
- # No special handling needed: queued or not, this returns the real result.
69
+ # Queued or not, this waits and returns the command's own result envelope.
69
70
  out = await client.call("some_long_running_command", {...})
70
71
  ```
71
72
 
72
73
  Failures raise instead of returning an error envelope:
73
74
 
74
- * `CommandFailedError` — the job completed but the command itself failed
75
- (`inner result {"success": false}` / `command_success is False` /
76
- `completed_with_error`). Carries `.command`, `.job_id`, `.error`.
77
- * `JobFailedError` — the job failed/stopped/cancelled, or reported `error`.
78
- Carries `.job_id`, `.error`, `.status`.
79
- * `JobTimeoutError` — only raised when you pass an explicit `timeout` and it
80
- elapses; the job keeps running server-side. By default (`timeout=None`) the
81
- client polls until the job finishes, however long that takes.
75
+ * `CommandFailedError` — the command ran but its inner result is
76
+ `{"success": false}` (immediate and queued commands alike; a failing result is
77
+ never returned as a dict). Carries `.command`, `.job_id`, `.error` (the server's
78
+ error object, e.g. `.error["code"]`) and `.result` (the inner envelope).
79
+ * `JobFailedError` — the job failed/stopped/cancelled. Carries `.job_id`, `.status`
80
+ and `.error` (always a structured error dict).
81
+ * `JobTimeoutError` — only when a positive `timeout` elapses; the call detaches and
82
+ the job keeps running server-side (`websocket.on_timeout` is `detach`).
83
+ * `ClientValidationError` — the server refused the parameters (JSON-RPC `-32602`);
84
+ `details` carries the server's `violation` payload.
85
+ * `ValueError` — a negative `timeout` or a non-positive `poll_interval`, before
86
+ anything is sent.
82
87
 
83
88
  Optional keyword args on `call` / `call_validated` (and their `call_unified*`
84
- counterparts): `timeout` (seconds, default `None` = wait until terminal),
85
- `poll_interval` (seconds between polls, default `1.0`), `status_hook` (sync or
86
- async callable invoked with each poll's status dict).
87
-
88
- `call` / `call_validated` also take `auto_poll` (default `True`, matching the
89
- behavior above exactly). Pass `auto_poll=False` to opt out of automatic
90
- polling: a non-queued response still comes back as the plain domain result,
91
- but a queued-job response comes back immediately as a `QueuedJob` handle
92
- instead of blocking until the job finishes.
89
+ counterparts):
90
+
91
+ * `timeout` — total wait in seconds. `None` (default) uses the client config
92
+ `websocket.job_wait_timeout`, shipped as `0` = no time limit, so a long queued
93
+ command is never cut off. A value above 0 raises `JobTimeoutError` and detaches.
94
+ * `poll_interval` — safety-net status-poll cadence in seconds. `None` (default)
95
+ uses `websocket.terminal_poll_interval` (2 s); a value overrides it per call.
96
+ * `status_hook` — sync or async callable invoked with each adapter WebSocket event
97
+ (at least the accepted event first and the terminal completed event last). An
98
+ exception raised by the hook propagates.
99
+
100
+ `call` / `call_validated` also take `auto_poll` (default `True`). Pass
101
+ `auto_poll=False` to detach as soon as the job id is known: the call returns a
102
+ `QueuedJob` handle immediately — for every command, including ones that finish at
103
+ once — and `timeout`, `poll_interval` and `status_hook` are ignored.
93
104
 
94
105
  ```python
95
106
  from code_analysis_client import QueuedJob
96
107
 
97
- result = await client.call("some_long_running_command", {...}, auto_poll=False)
98
- if isinstance(result, QueuedJob):
99
- # do other work here, then block on it whenever you're ready
100
- result = await result.wait()
101
- # `result` is now the same dict shape a default (auto_poll=True) call returns
108
+ job = await client.call("some_long_running_command", {...}, auto_poll=False)
109
+ assert isinstance(job, QueuedJob)
110
+ # do other work here, then block on it whenever you're ready
111
+ result = await job.wait() # same result / errors as the default path
102
112
  ```
103
113
 
104
- `QueuedJob` exposes `.job_id`, `.envelope` (the raw queue-service response),
105
- and two async methods: `.wait(timeout=None, poll_interval=1.0, status_hook=None)`
106
- — polls to completion and returns/raises exactly like the default path — and
107
- `.status()` — a single `queue_get_job_status` fetch without polling to
108
- completion.
114
+ `QueuedJob` exposes `.job_id`, `.envelope` (the adapter's acceptance envelope) and two
115
+ async methods: `.wait(timeout=None, poll_interval=None, status_hook=None)` — reads the
116
+ job once and, if it is still running, uses the adapter's WebSocket wait for the job id
117
+ (public API `wait_for_job_via_websocket`), then returns/raises exactly like the default
118
+ path; there is no RPC-polling fallback for an existing job id (a WebSocket failure
119
+ propagates) — and `.status()` — a single `queue_get_job_status` fetch.
120
+
121
+ #### Explicit adapter timeout configuration
122
+
123
+ The client hands `JsonRpcClient(ws_config=...)` the explicit defaults
124
+ `code_analysis_client.DEFAULT_CLIENT_ADAPTER_CONFIG` (a `ws_config=` keyword you pass is
125
+ merged over them key by key), so none of the adapter's own 60 s / `cancel` defaults is
126
+ effective:
127
+
128
+ | `websocket` key | value | meaning |
129
+ |---|---|---|
130
+ | `job_wait_timeout` | 0 | no total bound on a call's wait |
131
+ | `on_timeout` | `detach` | a wait timeout leaves the job running |
132
+ | `receive_timeout` / `receive_timeout_buffer` | 60 / 5 | one receive cap (never limits the total wait) |
133
+ | `heartbeat_interval` | 30 | WebSocket heartbeat |
134
+ | `terminal_poll_interval` | 2 | safety-net poll cadence |
135
+ | `close_timeout` | 5 | socket close bound |
136
+ | `subscribe_settle_delay` / `cancel_grace_seconds` | 0.5 / 0.5 | settle delay, cancel grace |
137
+
138
+ `job_push`: `on_caller_loss` `detach` (a job survives a lost client), `poll_interval` `0.1`.
139
+ The server ships and applies the same values.
109
140
 
110
141
  `call_unified` / `call_unified_validated` are kept as **deprecated aliases** of
111
142
  `call` / `call_validated` for backward compatibility and emit
@@ -42,58 +42,89 @@ from code_analysis_client import CodeAnalysisAsyncClient
42
42
  client = CodeAnalysisAsyncClient.from_server_config(config_dict, timeout=60.0)
43
43
  ```
44
44
 
45
- ### Queued commands are handled automatically
45
+ ### Every call is the adapter's standard call
46
46
 
47
47
  Every entry point — `call`, `call_validated`, `client.commands.<name>`, and the
48
- `file_sessions` / `universal_files` facades built on `call_validated` — routes
49
- through one queue-aware core. If the server's immediate response is a queued-job
50
- envelope (either deployed shape: `poll_with`/`store: "queuemgr"`, or
51
- `queued_after_timeout`), the client polls `queue_get_job_status` for you until
52
- the job reaches a terminal state, then returns the unwrapped inner result — the
53
- same shape you'd get from a synchronous call. You never see the raw envelope.
48
+ `file_sessions` / `universal_files` / `worktrees` / `check_chains` facades built on
49
+ `call_validated` — goes through one core that uses `mcp-proxy-adapter`'s
50
+ `JsonRpcClient.execute_command_unified`: the command runs in a WebSocket command
51
+ session and the call returns when the terminal event arrives (no polling while the
52
+ socket is up). If `/ws` is unreachable the adapter falls back to RPC polling of
53
+ `queue_get_job_status` and logs one WARNING `WS_UNREACHABLE_RPC_POLLING_FALLBACK`;
54
+ the result is identical. The client has no polling loop of its own.
54
55
 
55
56
  ```python
56
- # No special handling needed: queued or not, this returns the real result.
57
+ # Queued or not, this waits and returns the command's own result envelope.
57
58
  out = await client.call("some_long_running_command", {...})
58
59
  ```
59
60
 
60
61
  Failures raise instead of returning an error envelope:
61
62
 
62
- * `CommandFailedError` — the job completed but the command itself failed
63
- (`inner result {"success": false}` / `command_success is False` /
64
- `completed_with_error`). Carries `.command`, `.job_id`, `.error`.
65
- * `JobFailedError` — the job failed/stopped/cancelled, or reported `error`.
66
- Carries `.job_id`, `.error`, `.status`.
67
- * `JobTimeoutError` — only raised when you pass an explicit `timeout` and it
68
- elapses; the job keeps running server-side. By default (`timeout=None`) the
69
- client polls until the job finishes, however long that takes.
63
+ * `CommandFailedError` — the command ran but its inner result is
64
+ `{"success": false}` (immediate and queued commands alike; a failing result is
65
+ never returned as a dict). Carries `.command`, `.job_id`, `.error` (the server's
66
+ error object, e.g. `.error["code"]`) and `.result` (the inner envelope).
67
+ * `JobFailedError` — the job failed/stopped/cancelled. Carries `.job_id`, `.status`
68
+ and `.error` (always a structured error dict).
69
+ * `JobTimeoutError` — only when a positive `timeout` elapses; the call detaches and
70
+ the job keeps running server-side (`websocket.on_timeout` is `detach`).
71
+ * `ClientValidationError` — the server refused the parameters (JSON-RPC `-32602`);
72
+ `details` carries the server's `violation` payload.
73
+ * `ValueError` — a negative `timeout` or a non-positive `poll_interval`, before
74
+ anything is sent.
70
75
 
71
76
  Optional keyword args on `call` / `call_validated` (and their `call_unified*`
72
- counterparts): `timeout` (seconds, default `None` = wait until terminal),
73
- `poll_interval` (seconds between polls, default `1.0`), `status_hook` (sync or
74
- async callable invoked with each poll's status dict).
75
-
76
- `call` / `call_validated` also take `auto_poll` (default `True`, matching the
77
- behavior above exactly). Pass `auto_poll=False` to opt out of automatic
78
- polling: a non-queued response still comes back as the plain domain result,
79
- but a queued-job response comes back immediately as a `QueuedJob` handle
80
- instead of blocking until the job finishes.
77
+ counterparts):
78
+
79
+ * `timeout` — total wait in seconds. `None` (default) uses the client config
80
+ `websocket.job_wait_timeout`, shipped as `0` = no time limit, so a long queued
81
+ command is never cut off. A value above 0 raises `JobTimeoutError` and detaches.
82
+ * `poll_interval` — safety-net status-poll cadence in seconds. `None` (default)
83
+ uses `websocket.terminal_poll_interval` (2 s); a value overrides it per call.
84
+ * `status_hook` — sync or async callable invoked with each adapter WebSocket event
85
+ (at least the accepted event first and the terminal completed event last). An
86
+ exception raised by the hook propagates.
87
+
88
+ `call` / `call_validated` also take `auto_poll` (default `True`). Pass
89
+ `auto_poll=False` to detach as soon as the job id is known: the call returns a
90
+ `QueuedJob` handle immediately — for every command, including ones that finish at
91
+ once — and `timeout`, `poll_interval` and `status_hook` are ignored.
81
92
 
82
93
  ```python
83
94
  from code_analysis_client import QueuedJob
84
95
 
85
- result = await client.call("some_long_running_command", {...}, auto_poll=False)
86
- if isinstance(result, QueuedJob):
87
- # do other work here, then block on it whenever you're ready
88
- result = await result.wait()
89
- # `result` is now the same dict shape a default (auto_poll=True) call returns
96
+ job = await client.call("some_long_running_command", {...}, auto_poll=False)
97
+ assert isinstance(job, QueuedJob)
98
+ # do other work here, then block on it whenever you're ready
99
+ result = await job.wait() # same result / errors as the default path
90
100
  ```
91
101
 
92
- `QueuedJob` exposes `.job_id`, `.envelope` (the raw queue-service response),
93
- and two async methods: `.wait(timeout=None, poll_interval=1.0, status_hook=None)`
94
- — polls to completion and returns/raises exactly like the default path — and
95
- `.status()` — a single `queue_get_job_status` fetch without polling to
96
- completion.
102
+ `QueuedJob` exposes `.job_id`, `.envelope` (the adapter's acceptance envelope) and two
103
+ async methods: `.wait(timeout=None, poll_interval=None, status_hook=None)` — reads the
104
+ job once and, if it is still running, uses the adapter's WebSocket wait for the job id
105
+ (public API `wait_for_job_via_websocket`), then returns/raises exactly like the default
106
+ path; there is no RPC-polling fallback for an existing job id (a WebSocket failure
107
+ propagates) — and `.status()` — a single `queue_get_job_status` fetch.
108
+
109
+ #### Explicit adapter timeout configuration
110
+
111
+ The client hands `JsonRpcClient(ws_config=...)` the explicit defaults
112
+ `code_analysis_client.DEFAULT_CLIENT_ADAPTER_CONFIG` (a `ws_config=` keyword you pass is
113
+ merged over them key by key), so none of the adapter's own 60 s / `cancel` defaults is
114
+ effective:
115
+
116
+ | `websocket` key | value | meaning |
117
+ |---|---|---|
118
+ | `job_wait_timeout` | 0 | no total bound on a call's wait |
119
+ | `on_timeout` | `detach` | a wait timeout leaves the job running |
120
+ | `receive_timeout` / `receive_timeout_buffer` | 60 / 5 | one receive cap (never limits the total wait) |
121
+ | `heartbeat_interval` | 30 | WebSocket heartbeat |
122
+ | `terminal_poll_interval` | 2 | safety-net poll cadence |
123
+ | `close_timeout` | 5 | socket close bound |
124
+ | `subscribe_settle_delay` / `cancel_grace_seconds` | 0.5 / 0.5 | settle delay, cancel grace |
125
+
126
+ `job_push`: `on_caller_loss` `detach` (a job survives a lost client), `poll_interval` `0.1`.
127
+ The server ships and applies the same values.
97
128
 
98
129
  `call_unified` / `call_unified_validated` are kept as **deprecated aliases** of
99
130
  `call` / `call_validated` for backward compatibility and emit
@@ -12,6 +12,7 @@ from pathlib import Path
12
12
  from code_analysis_client.client import CodeAnalysisAsyncClient
13
13
  from code_analysis_client.commands_proxy import ValidatedCommandsProxy
14
14
  from code_analysis_client.config import (
15
+ DEFAULT_CLIENT_ADAPTER_CONFIG,
15
16
  adapter_settings_from_server_config,
16
17
  adapter_settings_to_jsonrpc_kwargs,
17
18
  load_server_config,
@@ -38,13 +39,7 @@ from code_analysis_client.check_chain import (
38
39
  FileCheckAndPromoteResult,
39
40
  )
40
41
  from code_analysis_client.worktree import WorktreeClient, WorktreeConflictError
41
- from code_analysis_client.queue_wait import (
42
- QueuedJob,
43
- extract_job_id,
44
- is_queued_envelope,
45
- unwrap_job_result,
46
- wait_for_job,
47
- )
42
+ from code_analysis_client.queue_wait import QueuedJob
48
43
  from code_analysis_client.server_api import (
49
44
  CLIENT_FACADE_COMMANDS,
50
45
  CST_REMOVED_COMMANDS,
@@ -76,6 +71,7 @@ from code_analysis_client.vectorizer_state import (
76
71
  )
77
72
 
78
73
  __all__ = [
74
+ "DEFAULT_CLIENT_ADAPTER_CONFIG",
79
75
  "CLIENT_FACADE_COMMANDS",
80
76
  "CST_REMOVED_COMMANDS",
81
77
  "ClientValidationError",
@@ -114,19 +110,15 @@ __all__ = [
114
110
  "WorktreeConflictError",
115
111
  "adapter_settings_from_server_config",
116
112
  "adapter_settings_to_jsonrpc_kwargs",
117
- "extract_job_id",
118
113
  "extract_vectorizer_state_from_response",
119
114
  "fetch_command_schema_from_server",
120
- "is_queued_envelope",
121
115
  "load_server_config",
122
116
  "parse_schema_from_help_payload",
123
117
  "prepare_params_for_schema",
124
118
  "safe_get_vectorizer_state",
125
119
  "safe_get_warnings",
126
120
  "safe_get_vectorization_worker",
127
- "unwrap_job_result",
128
121
  "validate_params_against_schema",
129
- "wait_for_job",
130
122
  ]
131
123
 
132
124
 
@@ -17,7 +17,7 @@ from dataclasses import dataclass, field
17
17
  from typing import TYPE_CHECKING, Any, Dict, List, Optional
18
18
 
19
19
  from code_analysis_client.exceptions import ClientValidationError
20
- from code_analysis_client.responses import command_error_code
20
+ from code_analysis_client.responses import command_error_code, FacadeCaller
21
21
  from code_analysis_client.worktree import WorktreeConflictError
22
22
 
23
23
  if TYPE_CHECKING:
@@ -252,7 +252,7 @@ class CheckChainClient:
252
252
 
253
253
  def __init__(self, client: CodeAnalysisAsyncClient) -> None:
254
254
  """Store the underlying async client."""
255
- self._client = client
255
+ self._client = FacadeCaller(client) # type: ignore[assignment]
256
256
 
257
257
  async def list(self, project_id: str) -> CheckChainListResult:
258
258
  """Return the resolved chain of every configured extension.