Skip to content

Commit c2efd36

Browse files
committed
adds docs fixes , compatibility fixes , lint , ci , precommit improvements
1 parent 2d369d2 commit c2efd36

48 files changed

Lines changed: 2298 additions & 1678 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-documentation.yml

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
name: Build Documentation
22

33
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'docs/**'
8+
- 'dev/mkdocs.yml'
9+
- '.readthedocs.yaml'
10+
- 'dev/requirements-rtd.txt'
11+
- 'ccbt/**'
12+
pull_request:
13+
branches: [main]
14+
paths:
15+
- 'docs/**'
16+
- 'dev/mkdocs.yml'
17+
- '.readthedocs.yaml'
18+
- 'dev/requirements-rtd.txt'
419
workflow_dispatch:
520
# Can be triggered manually from any branch for testing
621
# Documentation is automatically published to Read the Docs when changes are pushed
@@ -94,16 +109,13 @@ jobs:
94109
95110
- name: Generate coverage report
96111
run: |
97-
uv run pytest -c dev/pytest.ini tests/ --cov=ccbt --cov-report=html:site/reports/htmlcov || echo "⚠️ Coverage report generation failed, continuing..."
112+
uv run pytest -c dev/pytest.ini tests/ --cov=ccbt --cov-report=html:site/reports/htmlcov
98113
continue-on-error: true
99114

100-
- name: Generate Bandit reports
115+
- name: Generate Bandit report
101116
run: |
102117
uv run python tests/scripts/ensure_bandit_dir.py
103-
# Generate main bandit report
104-
uv run bandit -r ccbt/ -f json -o docs/reports/bandit/bandit-report.json --severity-level medium -x tests,benchmarks,dev,dist,docs,htmlcov,site,.venv,.pre-commit-cache,.pre-commit-home,.pytest_cache,.ruff_cache,.hypothesis,.github,.ccbt,.cursor,.benchmarks || echo "⚠️ Bandit report generation failed"
105-
# Generate all severity levels report
106-
uv run bandit -r ccbt/ -f json -o docs/reports/bandit/bandit-report-all.json --severity-level all -x tests,benchmarks,dev,dist,docs,htmlcov,site,.venv,.pre-commit-cache,.pre-commit-home,.pytest_cache,.ruff_cache,.hypothesis,.github,.ccbt,.cursor,.benchmarks || echo "⚠️ Bandit all report generation failed"
118+
uv run bandit -r ccbt/ -f json -o docs/reports/bandit/bandit-report.json --severity-level medium -x tests,benchmarks,dev,dist,docs,htmlcov,site,.venv,.pre-commit-cache,.pre-commit-home,.pytest_cache,.ruff_cache,.hypothesis,.github,.ccbt,.cursor,.benchmarks
107119
continue-on-error: true
108120

109121
- name: Ensure report files exist in documentation location
@@ -162,6 +174,34 @@ jobs:
162174
path: site/
163175
retention-days: 7
164176

177+
- name: Trigger Read the Docs build
178+
if: env.RTD_API_TOKEN != ''
179+
env:
180+
RTD_API_TOKEN: ${{ secrets.RTD_API_TOKEN }}
181+
RTD_PROJECT_SLUG: ${{ secrets.RTD_PROJECT_SLUG || 'ccbittorrent' }}
182+
BRANCH_NAME: ${{ github.ref_name }}
183+
run: |
184+
echo "Triggering Read the Docs build for branch: $BRANCH_NAME"
185+
curl -X POST \
186+
-H "Authorization: Token $RTD_API_TOKEN" \
187+
-H "Content-Type: application/json" \
188+
"https://readthedocs.org/api/v3/projects/$RTD_PROJECT_SLUG/versions/$BRANCH_NAME/builds/" \
189+
-d "{}" || echo "⚠️ Failed to trigger Read the Docs build. This may be expected if the branch is not configured in Read the Docs."
190+
continue-on-error: true
191+
192+
- name: Read the Docs build info
193+
if: env.RTD_API_TOKEN == ''
194+
run: |
195+
echo "ℹ️ Read the Docs API token not configured."
196+
echo " To enable automatic Read the Docs builds from any branch:"
197+
echo " 1. Get your Read the Docs API token from https://readthedocs.org/accounts/token/"
198+
echo " 2. Add it as a GitHub secret named RTD_API_TOKEN"
199+
echo " 3. Optionally set RTD_PROJECT_SLUG secret (defaults to 'ccbittorrent')"
200+
echo ""
201+
echo " Note: Read the Docs will only build branches configured in your project settings."
202+
echo " By default, only 'main' and 'dev' branches are built automatically."
203+
165204
# Note: Documentation is automatically published to Read the Docs
166-
# when changes are pushed to the repository. No GitHub Pages deployment needed.
205+
# when changes are pushed to the repository for configured branches (main/dev by default).
206+
# To build other branches, configure them in Read the Docs project settings or use the API trigger above.
167207

ccbt/cli/main.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1236,7 +1236,7 @@ def _apply_nat_overrides(cfg: Config, options: dict[str, Any]) -> None:
12361236

12371237
def _apply_protocol_v2_overrides(cfg: Config, options: dict[str, Any]) -> None:
12381238
"""Apply Protocol v2-related CLI overrides."""
1239-
# v2_only flag sets all v2 options
1239+
# v2_only flag sets all v2 options (takes precedence)
12401240
if options.get("v2_only"):
12411241
cfg.network.protocol_v2.enable_protocol_v2 = True
12421242
cfg.network.protocol_v2.prefer_protocol_v2 = True
@@ -1435,6 +1435,10 @@ def cli(ctx, config, verbose, debug):
14351435
)
14361436
@click.option("--unchoke-interval", type=float, help=_("Unchoke interval (s)"))
14371437
@click.option("--metrics-interval", type=float, help=_("Metrics interval (s)"))
1438+
@click.option("--enable-v2", "enable_v2", is_flag=True, help=_("Enable Protocol v2 (BEP 52)"))
1439+
@click.option("--disable-v2", "disable_v2", is_flag=True, help=_("Disable Protocol v2 (BEP 52)"))
1440+
@click.option("--prefer-v2", "prefer_v2", is_flag=True, help=_("Prefer Protocol v2 when available"))
1441+
@click.option("--v2-only", "v2_only", is_flag=True, help=_("Use Protocol v2 only (disable v1)"))
14381442
@click.pass_context
14391443
def download(
14401444
ctx,
@@ -1771,6 +1775,10 @@ async def _add_torrent_to_daemon():
17711775
)
17721776
@click.option("--unchoke-interval", type=float, help=_("Unchoke interval (s)"))
17731777
@click.option("--metrics-interval", type=float, help=_("Metrics interval (s)"))
1778+
@click.option("--enable-v2", "enable_v2", is_flag=True, help=_("Enable Protocol v2 (BEP 52)"))
1779+
@click.option("--disable-v2", "disable_v2", is_flag=True, help=_("Disable Protocol v2 (BEP 52)"))
1780+
@click.option("--prefer-v2", "prefer_v2", is_flag=True, help=_("Prefer Protocol v2 when available"))
1781+
@click.option("--v2-only", "v2_only", is_flag=True, help=_("Use Protocol v2 only (disable v1)"))
17741782
@click.pass_context
17751783
def magnet(
17761784
ctx,

ccbt/cli/overrides.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,11 +488,14 @@ def _apply_utp_overrides(cfg: Config, options: dict[str, Any]) -> None:
488488

489489

490490
def _apply_protocol_v2_overrides(cfg: Config, options: dict[str, Any]) -> None:
491+
"""Apply Protocol v2-related CLI overrides."""
492+
# v2_only flag sets all v2 options (takes precedence)
491493
if options.get("v2_only"):
492494
cfg.network.protocol_v2.enable_protocol_v2 = True
493495
cfg.network.protocol_v2.prefer_protocol_v2 = True
494496
cfg.network.protocol_v2.support_hybrid = False
495-
if not options.get("v2_only"):
497+
else:
498+
# Individual flags (only if v2_only is not set)
496499
if options.get("enable_v2"):
497500
cfg.network.protocol_v2.enable_protocol_v2 = True
498501
if options.get("disable_v2"):

ccbt/consensus/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,9 @@
2525
"RaftState",
2626
"RaftStateType",
2727
]
28+
29+
30+
31+
32+
33+

ccbt/nat/port_mapping.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
import time
88
from collections.abc import Awaitable, Callable
99
from dataclasses import dataclass, field
10-
from typing import Optional
10+
from typing import Optional, Tuple
1111

1212
logger = logging.getLogger(__name__)
1313

1414
# Type alias for renewal callback (using string for forward reference)
15-
RenewalCallback = Callable[["PortMapping"], Awaitable[tuple[bool, Optional[int]]]]
15+
RenewalCallback = Callable[["PortMapping"], Awaitable[Tuple[bool, Optional[int]]]]
1616

1717

1818
@dataclass

ccbt/session/checkpointing.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,9 @@ async def resume_from_checkpoint(
679679
# Restore security state if available
680680
await self._restore_security_state(checkpoint, session)
681681

682+
# Restore rate limits if available
683+
await self._restore_rate_limits(checkpoint, session)
684+
682685
# Restore session state if available
683686
await self._restore_session_state(checkpoint, session)
684687

@@ -1106,6 +1109,44 @@ async def _restore_security_state(
11061109
if self._ctx.logger:
11071110
self._ctx.logger.debug("Failed to restore security state: %s", e)
11081111

1112+
async def _restore_rate_limits(
1113+
self, checkpoint: TorrentCheckpoint, session: Any
1114+
) -> None:
1115+
"""Restore rate limits from checkpoint."""
1116+
try:
1117+
if not checkpoint.rate_limits:
1118+
return
1119+
1120+
# Get session manager
1121+
session_manager = getattr(session, "session_manager", None)
1122+
if not session_manager:
1123+
return
1124+
1125+
# Get info hash
1126+
info_hash = getattr(self._ctx.info, "info_hash", None)
1127+
if not info_hash:
1128+
return
1129+
1130+
# Convert info hash to hex string for set_rate_limits
1131+
info_hash_hex = info_hash.hex()
1132+
1133+
# Restore rate limits via session manager
1134+
if hasattr(session_manager, "set_rate_limits"):
1135+
down_kib = checkpoint.rate_limits.get("down_kib", 0)
1136+
up_kib = checkpoint.rate_limits.get("up_kib", 0)
1137+
await session_manager.set_rate_limits(
1138+
info_hash_hex, down_kib, up_kib
1139+
)
1140+
if self._ctx.logger:
1141+
self._ctx.logger.debug(
1142+
"Restored rate limits: down=%d KiB/s, up=%d KiB/s",
1143+
down_kib,
1144+
up_kib,
1145+
)
1146+
except Exception as e:
1147+
if self._ctx.logger:
1148+
self._ctx.logger.debug("Failed to restore rate limits: %s", e)
1149+
11091150
async def _restore_session_state(
11101151
self, checkpoint: TorrentCheckpoint, session: Any
11111152
) -> None:

ccbt/session/download_startup.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,9 @@
33
This module handles the initialization and startup sequence for torrent downloads,
44
including metadata retrieval, piece manager setup, and initial peer connections.
55
"""
6+
7+
8+
9+
10+
11+

ccbt/session/manager_startup.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,9 @@
33
This module handles the startup sequence for the session manager, including
44
component initialization, service startup, and background task coordination.
55
"""
6+
7+
8+
9+
10+
11+

ccbt/session/session.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4091,6 +4091,10 @@ async def add_torrent(
40914091
session = AsyncTorrentSession(torrent_data, session_output_dir, self)
40924092
self.torrents[info_hash] = session
40934093

4094+
# Add to private_torrents set if torrent is private (BEP 27)
4095+
if session.is_private:
4096+
self.private_torrents.add(info_hash)
4097+
40944098
# Get torrent name for callback
40954099
if isinstance(torrent_data, dict):
40964100
torrent_name = torrent_data.get("name", "Unknown")

ccbt/transport/utp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import time
2121
from dataclasses import dataclass, field
2222
from enum import Enum
23-
from typing import Callable, Optional
23+
from typing import Callable, Optional, Tuple
2424

2525
from ccbt.config.config import get_config
2626

@@ -230,7 +230,7 @@ def unpack(data: bytes) -> UTPPacket:
230230

231231

232232
# Connection state tracking tuple: (packet, send_time, retry_count)
233-
_PacketInfo = tuple[UTPPacket, float, int]
233+
_PacketInfo = Tuple[UTPPacket, float, int]
234234

235235

236236
class UTPConnection:

0 commit comments

Comments
 (0)