or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-api.mdcore-api.mdindex.mdsync-api.mdv2-api.md

async-api.mddocs/

0

# Asynchronous API

1

2

Non-blocking proxy implementations for asyncio, trio, curio, and anyio frameworks. Each framework has its own optimized implementation while maintaining a consistent API interface. The async implementations return sockets configured for non-blocking operation, suitable for integration with async I/O frameworks.

3

4

## Framework-Specific Imports

5

6

```python

7

# Asyncio

8

from python_socks.async_.asyncio import Proxy as AsyncioProxy

9

10

# Trio

11

from python_socks.async_.trio import Proxy as TrioProxy

12

13

# Curio

14

from python_socks.async_.curio import Proxy as CurioProxy

15

16

# AnyIO (with proxy chaining)

17

from python_socks.async_.anyio import Proxy as AnyioProxy, ProxyChain as AnyioProxyChain

18

```

19

20

## Capabilities

21

22

### Asyncio Proxy

23

24

Asyncio-specific proxy implementation optimized for asyncio event loops.

25

26

```python { .api }

27

import asyncio

28

import socket

29

from typing import Optional

30

31

class AsyncioProxy:

32

def __init__(

33

self,

34

proxy_type: ProxyType,

35

host: str,

36

port: int,

37

username: Optional[str] = None,

38

password: Optional[str] = None,

39

rdns: Optional[bool] = None,

40

loop: Optional[asyncio.AbstractEventLoop] = None

41

): ...

42

43

async def connect(

44

self,

45

dest_host: str,

46

dest_port: int,

47

timeout: Optional[float] = None,

48

**kwargs

49

) -> socket.socket: ...

50

51

@property

52

def proxy_host(self) -> str:

53

"""Get proxy host address."""

54

...

55

56

@property

57

def proxy_port(self) -> int:

58

"""Get proxy port number."""

59

...

60

61

@classmethod

62

def create(cls, *args, **kwargs) -> 'AsyncioProxy':

63

"""Create proxy instance (deprecated, use __init__ directly)."""

64

...

65

66

@classmethod

67

def from_url(cls, url: str, **kwargs) -> 'AsyncioProxy':

68

"""Create proxy instance from URL."""

69

...

70

```

71

72

**Asyncio-Specific Parameters:**

73

- `loop`: Optional event loop (defaults to current loop)

74

75

**Usage:**

76

77

```python

78

import asyncio

79

import ssl

80

from python_socks.async_.asyncio import Proxy

81

82

async def main():

83

# Create proxy

84

proxy = Proxy.from_url('socks5://user:pass@127.0.0.1:1080')

85

86

# Connect through proxy

87

sock = await proxy.connect('httpbin.org', 443, timeout=30)

88

89

# Use with asyncio high-level API

90

reader, writer = await asyncio.open_connection(

91

host=None,

92

port=None,

93

sock=sock,

94

ssl=ssl.create_default_context(),

95

server_hostname='httpbin.org'

96

)

97

98

# Make request

99

writer.write(b'GET /ip HTTP/1.1\r\nHost: httpbin.org\r\n\r\n')

100

await writer.drain()

101

102

response = await reader.read(4096)

103

print(response.decode())

104

105

writer.close()

106

await writer.wait_closed()

107

108

asyncio.run(main())

109

```

110

111

### Trio Proxy

112

113

Trio-specific proxy implementation for structured concurrency.

114

115

```python { .api }

116

import trio

117

from typing import Optional

118

119

class TrioProxy:

120

def __init__(

121

self,

122

proxy_type: ProxyType,

123

host: str,

124

port: int,

125

username: Optional[str] = None,

126

password: Optional[str] = None,

127

rdns: Optional[bool] = None

128

): ...

129

130

async def connect(

131

self,

132

dest_host: str,

133

dest_port: int,

134

timeout: Optional[float] = None,

135

**kwargs

136

) -> trio.socket.SocketType: ...

137

138

@property

139

def proxy_host(self) -> str:

140

"""Get proxy host address."""

141

...

142

143

@property

144

def proxy_port(self) -> int:

145

"""Get proxy port number."""

146

...

147

148

@classmethod

149

def create(cls, *args, **kwargs) -> 'TrioProxy':

150

"""Create proxy instance (deprecated, use __init__ directly)."""

151

...

152

153

@classmethod

154

def from_url(cls, url: str, **kwargs) -> 'TrioProxy':

155

"""Create proxy instance from URL."""

156

...

157

```

158

159

**Usage:**

160

161

```python

162

import trio

163

import ssl

164

from python_socks.async_.trio import Proxy

165

166

async def main():

167

# Create proxy

168

proxy = Proxy.from_url('socks5://proxy.example.com:1080')

169

170

# Connect through proxy

171

sock = await proxy.connect('example.com', 443)

172

173

# Use with trio's SSL wrapper

174

ssl_context = ssl.create_default_context()

175

stream = trio.SSLStream(

176

trio.SocketStream(sock),

177

ssl_context,

178

server_hostname='example.com'

179

)

180

181

# Make request

182

await stream.send_all(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')

183

response = await stream.receive_some(4096)

184

print(response.decode())

185

186

await stream.aclose()

187

188

trio.run(main)

189

```

190

191

### Curio Proxy

192

193

Curio-specific proxy implementation for async/await concurrency.

194

195

```python { .api }

196

import curio.io

197

from typing import Optional

198

199

class CurioProxy:

200

def __init__(

201

self,

202

proxy_type: ProxyType,

203

host: str,

204

port: int,

205

username: Optional[str] = None,

206

password: Optional[str] = None,

207

rdns: Optional[bool] = None

208

): ...

209

210

async def connect(

211

self,

212

dest_host: str,

213

dest_port: int,

214

timeout: Optional[float] = None,

215

**kwargs

216

) -> curio.io.Socket: ...

217

218

@property

219

def proxy_host(self) -> str:

220

"""Get proxy host address."""

221

...

222

223

@property

224

def proxy_port(self) -> int:

225

"""Get proxy port number."""

226

...

227

228

@classmethod

229

def create(cls, *args, **kwargs) -> 'CurioProxy':

230

"""Create proxy instance (deprecated, use __init__ directly)."""

231

...

232

233

@classmethod

234

def from_url(cls, url: str, **kwargs) -> 'CurioProxy':

235

"""Create proxy instance from URL."""

236

...

237

```

238

239

**Usage:**

240

241

```python

242

import curio

243

import ssl

244

from python_socks.async_.curio import Proxy

245

246

async def main():

247

# Create proxy

248

proxy = Proxy.from_url('socks5://proxy.example.com:1080')

249

250

# Connect through proxy

251

sock = await proxy.connect('example.com', 80)

252

253

# Use with curio

254

stream = sock.as_stream()

255

256

await stream.write(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')

257

response = await stream.read(4096)

258

print(response.decode())

259

260

await stream.close()

261

262

curio.run(main)

263

```

264

265

### AnyIO Proxy

266

267

AnyIO-compatible proxy implementation that works across multiple async frameworks.

268

269

```python { .api }

270

import ssl

271

from typing import Optional

272

273

class AnyioProxy:

274

def __init__(

275

self,

276

proxy_type: ProxyType,

277

host: str,

278

port: int,

279

username: Optional[str] = None,

280

password: Optional[str] = None,

281

rdns: Optional[bool] = None,

282

proxy_ssl: Optional[ssl.SSLContext] = None

283

): ...

284

285

async def connect(

286

self,

287

dest_host: str,

288

dest_port: int,

289

dest_ssl: Optional[ssl.SSLContext] = None,

290

timeout: Optional[float] = None,

291

**kwargs

292

) -> 'AnyioSocketStream': ...

293

294

@property

295

def proxy_host(self) -> str:

296

"""Get proxy host address."""

297

...

298

299

@property

300

def proxy_port(self) -> int:

301

"""Get proxy port number."""

302

...

303

304

@classmethod

305

def create(cls, *args, **kwargs) -> 'AnyioProxy':

306

"""Create proxy instance (deprecated, use __init__ directly)."""

307

...

308

309

@classmethod

310

def from_url(cls, url: str, **kwargs) -> 'AnyioProxy':

311

"""Create proxy instance from URL."""

312

...

313

```

314

315

**Usage:**

316

317

```python

318

import anyio

319

import ssl

320

from python_socks.async_.anyio import Proxy

321

322

async def main():

323

# Create proxy

324

proxy = Proxy.from_url('socks5://proxy.example.com:1080')

325

326

# Connect through proxy

327

sock = await proxy.connect('httpbin.org', 443)

328

329

# Use with anyio

330

ssl_context = ssl.create_default_context()

331

stream = await anyio.connect_tcp(

332

remote_host='httpbin.org',

333

remote_port=443,

334

sock=sock,

335

tls=True,

336

tls_hostname='httpbin.org'

337

)

338

339

await stream.send(b'GET /ip HTTP/1.1\r\nHost: httpbin.org\r\n\r\n')

340

response = await stream.receive(4096)

341

print(response.decode())

342

343

await stream.aclose()

344

345

# Works with asyncio

346

anyio.run(main, backend='asyncio')

347

348

# Also works with trio

349

anyio.run(main, backend='trio')

350

```

351

352

### AnyIO Proxy Chain

353

354

Asynchronous proxy chaining with AnyIO compatibility.

355

356

```python { .api }

357

class ProxyChain:

358

def __init__(self, proxies: Iterable[AnyioProxy]): ...

359

360

async def connect(

361

self,

362

dest_host: str,

363

dest_port: int,

364

timeout: Optional[float] = None

365

) -> socket.socket: ...

366

```

367

368

**Note:** This is the only non-deprecated proxy chain implementation in the async API.

369

370

**Usage:**

371

372

```python

373

import anyio

374

from python_socks.async_.anyio import Proxy, ProxyChain

375

376

async def main():

377

# Create proxy chain

378

proxy1 = Proxy.from_url('socks5://first-proxy:1080')

379

proxy2 = Proxy.from_url('http://second-proxy:8080')

380

381

chain = ProxyChain([proxy1, proxy2])

382

383

# Connect through chain

384

sock = await chain.connect('example.com', 80)

385

386

# Use the socket

387

stream = anyio.SocketStream(sock)

388

await stream.send(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')

389

response = await stream.receive(4096)

390

print(response.decode())

391

392

await stream.aclose()

393

394

anyio.run(main, backend='asyncio')

395

```

396

397

## Advanced Usage Examples

398

399

### Concurrent Connections

400

401

```python

402

import asyncio

403

from python_socks.async_.asyncio import Proxy

404

405

async def fetch_url(proxy, url_host, url_path):

406

"""Fetch URL through proxy."""

407

try:

408

sock = await proxy.connect(url_host, 80, timeout=10)

409

410

reader, writer = await asyncio.open_connection(

411

host=None, port=None, sock=sock

412

)

413

414

request = f'GET {url_path} HTTP/1.1\r\nHost: {url_host}\r\n\r\n'

415

writer.write(request.encode())

416

await writer.drain()

417

418

response = await reader.read(4096)

419

writer.close()

420

await writer.wait_closed()

421

422

return response.decode()

423

424

except Exception as e:

425

return f"Error: {e}"

426

427

async def main():

428

proxy = Proxy.from_url('socks5://proxy.example.com:1080')

429

430

# Concurrent requests

431

tasks = [

432

fetch_url(proxy, 'httpbin.org', '/ip'),

433

fetch_url(proxy, 'httpbin.org', '/user-agent'),

434

fetch_url(proxy, 'httpbin.org', '/headers')

435

]

436

437

results = await asyncio.gather(*tasks)

438

for i, result in enumerate(results):

439

print(f"Request {i + 1}: {result[:100]}...")

440

441

asyncio.run(main())

442

```

443

444

### Timeout and Error Handling

445

446

```python

447

import asyncio

448

from python_socks import ProxyError, ProxyTimeoutError, ProxyConnectionError

449

from python_socks.async_.asyncio import Proxy

450

451

async def robust_connect(proxy_url, dest_host, dest_port, max_retries=3):

452

"""Async connection with retry logic."""

453

454

for attempt in range(max_retries):

455

try:

456

proxy = Proxy.from_url(proxy_url)

457

return await proxy.connect(dest_host, dest_port, timeout=10)

458

459

except ProxyConnectionError as e:

460

if attempt < max_retries - 1:

461

await asyncio.sleep(2 ** attempt) # Exponential backoff

462

continue

463

raise

464

465

except ProxyTimeoutError:

466

if attempt < max_retries - 1:

467

continue

468

raise

469

470

except ProxyError as e:

471

# Don't retry authentication errors

472

raise

473

474

async def main():

475

try:

476

sock = await robust_connect(

477

'socks5://proxy.example.com:1080',

478

'api.example.com',

479

443

480

)

481

print("Connected successfully!")

482

sock.close()

483

except Exception as e:

484

print(f"Connection failed: {e}")

485

486

asyncio.run(main())

487

```

488

489

### Framework Detection Pattern

490

491

```python

492

import sys

493

494

# Detect available async framework

495

if 'trio' in sys.modules:

496

from python_socks.async_.trio import Proxy

497

framework = 'trio'

498

elif 'curio' in sys.modules:

499

from python_socks.async_.curio import Proxy

500

framework = 'curio'

501

else:

502

from python_socks.async_.asyncio import Proxy

503

framework = 'asyncio'

504

505

print(f"Using {framework} proxy implementation")

506

```

507

508

### Cross-Framework Compatibility with AnyIO

509

510

```python

511

import anyio

512

from python_socks.async_.anyio import Proxy

513

514

async def universal_proxy_request(proxy_url, dest_host, dest_port, request_data):

515

"""Request that works with any anyio-supported backend."""

516

517

proxy = Proxy.from_url(proxy_url)

518

sock = await proxy.connect(dest_host, dest_port)

519

520

stream = anyio.SocketStream(sock)

521

522

try:

523

await stream.send(request_data)

524

response = await stream.receive(4096)

525

return response

526

finally:

527

await stream.aclose()

528

529

# Works with asyncio

530

async def asyncio_main():

531

result = await universal_proxy_request(

532

'socks5://proxy:1080',

533

'example.com',

534

80,

535

b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'

536

)

537

print("Asyncio result:", result.decode()[:100])

538

539

# Works with trio

540

async def trio_main():

541

result = await universal_proxy_request(

542

'socks5://proxy:1080',

543

'example.com',

544

80,

545

b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'

546

)

547

print("Trio result:", result.decode()[:100])

548

549

# Run with different backends

550

anyio.run(asyncio_main, backend='asyncio')

551

anyio.run(trio_main, backend='trio')

552

```

553

554

## Framework Integration Notes

555

556

### Asyncio Integration

557

- Returns sockets in non-blocking mode

558

- Compatible with `asyncio.open_connection()`

559

- Works with asyncio SSL contexts

560

- Integrates with asyncio timeout handling

561

562

### Trio Integration

563

- Optimized for trio's structured concurrency model

564

- Compatible with `trio.SocketStream`

565

- Works with trio's SSL wrapper

566

- Follows trio's cancellation semantics

567

568

### Curio Integration

569

- Designed for curio's async/await model

570

- Compatible with curio stream abstractions

571

- Integrates with curio's timeout mechanisms

572

- Follows curio's task management patterns

573

574

### AnyIO Integration

575

- Backend-agnostic implementation

576

- Works across asyncio, trio, and curio

577

- Provides consistent API regardless of backend

578

- Enables framework-portable applications