ctrader-api-client 0.5.0__tar.gz → 0.5.1__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.
Potentially problematic release.
This version of ctrader-api-client might be problematic. Click here for more details.
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/PKG-INFO +1 -1
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/pyproject.toml +1 -1
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/connection/heartbeat.py +12 -1
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/connection/protocol.py +70 -15
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/connection/test_protocol.py +83 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/uv.lock +1 -1
- ctrader_api_client-0.5.0/.claude/settings.local.json +0 -7
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/.github/workflows/docs.yml +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/.gitignore +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/.pre-commit-config.yaml +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/.python-version +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/Justfile +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/LICENSE +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/README.md +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/docs/api/accounts.md +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/docs/api/client.md +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/docs/api/enums.md +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/docs/api/events.md +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/docs/api/market-data.md +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/docs/api/models.md +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/docs/api/symbols.md +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/docs/api/trading.md +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/docs/getting-started.md +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/docs/index.md +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/mkdocs.yml +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/protos/SOURCE +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/protos/VERSION +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/protos/update.sh +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/protos/vendor/OpenApiCommonMessages.proto +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/protos/vendor/OpenApiCommonModelMessages.proto +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/protos/vendor/OpenApiMessages.proto +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/protos/vendor/OpenApiModelMessages.proto +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/scripts/fix_proto_imports.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/__init__.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/_internal/__init__.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/_internal/messages.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/_internal/proto/OpenApiCommonMessages.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/_internal/proto/OpenApiCommonModelMessages.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/_internal/proto/OpenApiMessages.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/_internal/proto/OpenApiModelMessages.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/_internal/proto/__init__.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/_internal/serialization.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/api/__init__.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/api/accounts.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/api/market_data.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/api/symbols.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/api/trading.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/auth/__init__.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/auth/credentials.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/auth/manager.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/auth/trigger.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/client.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/config.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/connection/__init__.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/connection/transport.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/enums.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/events/__init__.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/events/emitter.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/events/router.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/events/types.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/exceptions.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/__init__.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/_base.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/account.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/deal.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/market_data.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/order.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/position.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/requests.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/symbol.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/py.typed +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/_internal/test_messages.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/_internal/test_serialization.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/api/conftest.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/api/test_accounts.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/api/test_market_data_api.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/api/test_symbols.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/api/test_trading.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/auth/test_credentials.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/auth/test_manager.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/connection/test_heartbeat.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/connection/test_transport.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/events/test_emitter.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/events/test_router.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/events/test_types.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/models/test_account.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/models/test_deal.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/models/test_market_data.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/models/test_order.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/models/test_position.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/models/test_requests.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/models/test_symbol.py +0 -0
- {ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/test_client.py +0 -0
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/connection/heartbeat.py
RENAMED
|
@@ -84,6 +84,14 @@ class HeartbeatManager:
|
|
|
84
84
|
Resets the heartbeat timer and spawns a new heartbeat loop.
|
|
85
85
|
Should be called after the protocol has reconnected.
|
|
86
86
|
"""
|
|
87
|
+
# Cancel any heartbeat loop still running from the previous connection.
|
|
88
|
+
# When a drop is detected by the reader loop (not by a heartbeat
|
|
89
|
+
# timeout/send failure), the old loop is merely asleep and would
|
|
90
|
+
# otherwise survive the reconnect, leaving two loops running and
|
|
91
|
+
# accumulating one extra on every reconnect.
|
|
92
|
+
if self._task_scope is not None:
|
|
93
|
+
self._task_scope.cancel()
|
|
94
|
+
|
|
87
95
|
self._last_received = time.monotonic()
|
|
88
96
|
if self._task_group is not None:
|
|
89
97
|
self._task_group.start_soon(self._heartbeat_loop)
|
|
@@ -121,5 +129,8 @@ class HeartbeatManager:
|
|
|
121
129
|
logger.debug("Heartbeat sent to server")
|
|
122
130
|
except Exception as e:
|
|
123
131
|
logger.warning("Failed to send heartbeat: %s", e)
|
|
124
|
-
#
|
|
132
|
+
# A failed write can be the first (or only) sign of a
|
|
133
|
+
# half-open connection, so drive reconnection explicitly
|
|
134
|
+
# instead of relying on the reader loop to notice.
|
|
135
|
+
await self._protocol.handle_disconnect()
|
|
125
136
|
return
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/connection/protocol.py
RENAMED
|
@@ -83,6 +83,10 @@ class Protocol:
|
|
|
83
83
|
self._reconnect_min_wait = reconnect_min_wait
|
|
84
84
|
self._reconnect_max_wait = reconnect_max_wait
|
|
85
85
|
|
|
86
|
+
# Guards against multiple concurrent reconnection attempts (e.g. the
|
|
87
|
+
# reader loop and the heartbeat loop both detecting the same drop).
|
|
88
|
+
self._reconnecting = False
|
|
89
|
+
|
|
86
90
|
# Reconnection callback (set by CTraderClient)
|
|
87
91
|
self._on_reconnect: Callable[[], Awaitable[None]] | None = None
|
|
88
92
|
|
|
@@ -314,29 +318,74 @@ class Protocol:
|
|
|
314
318
|
logger.warning("Event handler error: %s", e)
|
|
315
319
|
|
|
316
320
|
async def handle_disconnect(self) -> None:
|
|
317
|
-
"""
|
|
318
|
-
|
|
321
|
+
"""Trigger reconnection after an unexpected disconnect.
|
|
322
|
+
|
|
323
|
+
Safe to call from any task — the reader loop, the heartbeat loop, or a
|
|
324
|
+
failed heartbeat write. The reconnection itself runs as a *separate*
|
|
325
|
+
task on the protocol task group (see :meth:`_reconnect_task`) rather
|
|
326
|
+
than inline.
|
|
327
|
+
|
|
328
|
+
This indirection is essential: the caller is usually the reader loop,
|
|
329
|
+
which invokes this method from inside its own ``anyio.CancelScope``.
|
|
330
|
+
Doing the reconnection inline used to cancel that scope (to stop the
|
|
331
|
+
reader) and thereby abort the in-flight reconnection at its very first
|
|
332
|
+
checkpoint, leaving the client permanently offline. Spawning a
|
|
333
|
+
standalone task decouples the reconnection from the cancel scope of
|
|
334
|
+
whichever loop detected the drop.
|
|
335
|
+
|
|
336
|
+
Idempotent: calls made while a reconnection is already in flight are
|
|
337
|
+
ignored, so the reader and heartbeat loops racing to report the same
|
|
338
|
+
drop only produce a single reconnection.
|
|
339
|
+
"""
|
|
340
|
+
if not self._running or self._reconnecting:
|
|
341
|
+
return
|
|
342
|
+
if self._task_group is None:
|
|
343
|
+
return
|
|
319
344
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
self._reader_scope = None
|
|
345
|
+
self._reconnecting = True
|
|
346
|
+
logger.warning("Connection lost, attempting to reconnect...")
|
|
347
|
+
self._task_group.start_soon(self._reconnect_task)
|
|
324
348
|
|
|
325
|
-
|
|
326
|
-
|
|
349
|
+
async def _reconnect_task(self) -> None:
|
|
350
|
+
"""Own the full reconnection lifecycle from a standalone task.
|
|
327
351
|
|
|
328
|
-
|
|
352
|
+
Runs outside the reader/heartbeat cancel scopes so it survives the death
|
|
353
|
+
of whichever task detected the drop. It must never let an exception
|
|
354
|
+
escape: an unhandled error here would propagate into the protocol task
|
|
355
|
+
group and tear down every other task. A terminal reconnection failure is
|
|
356
|
+
instead recorded by stopping the protocol and waking pending requests.
|
|
357
|
+
"""
|
|
329
358
|
try:
|
|
359
|
+
# The reader loop is reading a dead stream; cancel it before we
|
|
360
|
+
# reconnect so a stale reader can't race the fresh one we restart.
|
|
361
|
+
if self._reader_scope is not None:
|
|
362
|
+
self._reader_scope.cancel()
|
|
363
|
+
self._reader_scope = None
|
|
364
|
+
|
|
365
|
+
# Close the old transport (idempotent — the task that detected the
|
|
366
|
+
# drop may have already raced us to it).
|
|
367
|
+
await self._transport.close()
|
|
368
|
+
|
|
330
369
|
await self._reconnect()
|
|
331
370
|
logger.info("Reconnection successful")
|
|
332
371
|
except (CTraderConnectionFailedError, CTraderConnectionClosedError) as e:
|
|
333
|
-
logger.error("Reconnection failed: %s", e)
|
|
372
|
+
logger.error("Reconnection failed, giving up: %s", e)
|
|
334
373
|
self._running = False
|
|
335
|
-
#
|
|
374
|
+
# Wake all pending requests so callers fail fast instead of hanging.
|
|
336
375
|
for msg_id in list(self._pending.keys()):
|
|
337
|
-
self._errors[msg_id] = CTraderConnectionClosedError(
|
|
376
|
+
self._errors[msg_id] = CTraderConnectionClosedError(
|
|
377
|
+
"Connection lost and reconnection failed"
|
|
378
|
+
)
|
|
338
379
|
self._pending[msg_id].set()
|
|
339
|
-
|
|
380
|
+
except Exception:
|
|
381
|
+
# Defensive: never let the reconnection task crash the task group.
|
|
382
|
+
logger.exception("Unexpected error while reconnecting")
|
|
383
|
+
self._running = False
|
|
384
|
+
for msg_id in list(self._pending.keys()):
|
|
385
|
+
self._errors[msg_id] = CTraderConnectionClosedError("Reconnection error")
|
|
386
|
+
self._pending[msg_id].set()
|
|
387
|
+
finally:
|
|
388
|
+
self._reconnecting = False
|
|
340
389
|
|
|
341
390
|
async def _reconnect(self) -> None:
|
|
342
391
|
"""Attempt reconnection with exponential backoff.
|
|
@@ -369,6 +418,12 @@ class Protocol:
|
|
|
369
418
|
if self._task_group is not None:
|
|
370
419
|
self._task_group.start_soon(self._reader_loop)
|
|
371
420
|
|
|
372
|
-
# Notify callback for re-authentication
|
|
421
|
+
# Notify callback for re-authentication. The transport is already back
|
|
422
|
+
# up at this point, so a failure in the callback must not fail the
|
|
423
|
+
# reconnection itself — log it and let the caller's ReconnectedEvent
|
|
424
|
+
# surface the details.
|
|
373
425
|
if self._on_reconnect is not None:
|
|
374
|
-
|
|
426
|
+
try:
|
|
427
|
+
await self._on_reconnect()
|
|
428
|
+
except Exception:
|
|
429
|
+
logger.exception("Post-reconnect re-authentication callback failed")
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/connection/test_protocol.py
RENAMED
|
@@ -417,3 +417,86 @@ class TestReconnect:
|
|
|
417
417
|
await protocol._reconnect()
|
|
418
418
|
|
|
419
419
|
assert call_count == 3
|
|
420
|
+
|
|
421
|
+
@pytest.mark.anyio
|
|
422
|
+
async def test_reader_triggered_disconnect_actually_reconnects(self, mock_transport: MagicMock) -> None:
|
|
423
|
+
"""Regression: a drop detected by the reader loop must fully reconnect.
|
|
424
|
+
|
|
425
|
+
Previously ``handle_disconnect`` ran inline and cancelled the reader
|
|
426
|
+
loop's own cancel scope, aborting the reconnection at its first
|
|
427
|
+
checkpoint so the client stayed offline forever.
|
|
428
|
+
"""
|
|
429
|
+
# The stream reports EOF once (server closed the socket), then blocks so
|
|
430
|
+
# the restarted reader parks on a healthy connection instead of looping.
|
|
431
|
+
first_read = True
|
|
432
|
+
|
|
433
|
+
async def receive(_num_bytes: int) -> bytes:
|
|
434
|
+
nonlocal first_read
|
|
435
|
+
if first_read:
|
|
436
|
+
first_read = False
|
|
437
|
+
raise anyio.EndOfStream
|
|
438
|
+
await anyio.sleep_forever()
|
|
439
|
+
|
|
440
|
+
mock_stream = AsyncMock()
|
|
441
|
+
mock_stream.receive = receive
|
|
442
|
+
mock_transport.stream = mock_stream
|
|
443
|
+
|
|
444
|
+
# close() must contain a real suspension point (the true transport awaits
|
|
445
|
+
# stream.aclose()). That checkpoint is exactly where the pre-fix,
|
|
446
|
+
# self-cancelling handle_disconnect got aborted before reaching
|
|
447
|
+
# _reconnect() — an AsyncMock close() never suspends and would hide the bug.
|
|
448
|
+
close_calls = 0
|
|
449
|
+
|
|
450
|
+
async def close_with_checkpoint() -> None:
|
|
451
|
+
nonlocal close_calls
|
|
452
|
+
close_calls += 1
|
|
453
|
+
await anyio.sleep(0)
|
|
454
|
+
|
|
455
|
+
mock_transport.close = close_with_checkpoint
|
|
456
|
+
|
|
457
|
+
reconnected = anyio.Event()
|
|
458
|
+
|
|
459
|
+
async def on_reconnect() -> None:
|
|
460
|
+
reconnected.set()
|
|
461
|
+
|
|
462
|
+
protocol = Protocol(
|
|
463
|
+
mock_transport,
|
|
464
|
+
reconnect_attempts=3,
|
|
465
|
+
reconnect_min_wait=0.01,
|
|
466
|
+
reconnect_max_wait=0.02,
|
|
467
|
+
)
|
|
468
|
+
protocol._on_reconnect = on_reconnect
|
|
469
|
+
|
|
470
|
+
await protocol.start()
|
|
471
|
+
try:
|
|
472
|
+
with anyio.fail_after(2):
|
|
473
|
+
await reconnected.wait()
|
|
474
|
+
|
|
475
|
+
assert close_calls >= 1
|
|
476
|
+
assert mock_transport.connect.await_count >= 1
|
|
477
|
+
assert protocol._running is True
|
|
478
|
+
assert protocol._reconnecting is False
|
|
479
|
+
finally:
|
|
480
|
+
await protocol.stop()
|
|
481
|
+
|
|
482
|
+
@pytest.mark.anyio
|
|
483
|
+
async def test_handle_disconnect_noop_while_already_reconnecting(self, mock_transport: MagicMock) -> None:
|
|
484
|
+
"""Concurrent disconnect reports collapse into a single reconnection."""
|
|
485
|
+
protocol = Protocol(mock_transport)
|
|
486
|
+
protocol._running = True
|
|
487
|
+
protocol._reconnecting = True
|
|
488
|
+
|
|
489
|
+
await protocol.handle_disconnect()
|
|
490
|
+
|
|
491
|
+
mock_transport.connect.assert_not_called()
|
|
492
|
+
|
|
493
|
+
@pytest.mark.anyio
|
|
494
|
+
async def test_handle_disconnect_noop_when_not_running(self, mock_transport: MagicMock) -> None:
|
|
495
|
+
"""A disconnect during/after shutdown must not trigger reconnection."""
|
|
496
|
+
protocol = Protocol(mock_transport)
|
|
497
|
+
protocol._running = False
|
|
498
|
+
|
|
499
|
+
await protocol.handle_disconnect()
|
|
500
|
+
|
|
501
|
+
mock_transport.connect.assert_not_called()
|
|
502
|
+
assert protocol._reconnecting is False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/protos/vendor/OpenApiCommonMessages.proto
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/protos/vendor/OpenApiCommonModelMessages.proto
RENAMED
|
File without changes
|
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/protos/vendor/OpenApiModelMessages.proto
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/_internal/__init__.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/_internal/messages.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/api/__init__.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/api/accounts.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/api/market_data.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/auth/__init__.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/auth/credentials.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/auth/manager.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/auth/trigger.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/connection/__init__.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/connection/transport.py
RENAMED
|
File without changes
|
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/events/__init__.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/events/emitter.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/events/router.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/events/types.py
RENAMED
|
File without changes
|
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/__init__.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/_base.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/account.py
RENAMED
|
File without changes
|
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/market_data.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/order.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/position.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/requests.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/src/ctrader_api_client/models/symbol.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/_internal/test_serialization.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/api/test_market_data_api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/connection/test_heartbeat.py
RENAMED
|
File without changes
|
{ctrader_api_client-0.5.0 → ctrader_api_client-0.5.1}/tests/unit/connection/test_transport.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|