python-socks 2.8.2__py3-none-any.whl → 3.0.0__py3-none-any.whl

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 (76) hide show
  1. python_socks/__init__.py +11 -13
  2. python_socks/_abc.py +35 -22
  3. python_socks/_connectors/abc.py +5 -3
  4. python_socks/_connectors/factory_async.py +8 -8
  5. python_socks/_connectors/factory_sync.py +8 -8
  6. python_socks/_connectors/http_async.py +7 -7
  7. python_socks/_connectors/http_sync.py +7 -7
  8. python_socks/_connectors/socks4_async.py +10 -10
  9. python_socks/_connectors/socks4_sync.py +10 -10
  10. python_socks/_connectors/socks5_async.py +29 -26
  11. python_socks/_connectors/socks5_sync.py +36 -28
  12. python_socks/_errors.py +15 -2
  13. python_socks/_helpers.py +35 -32
  14. python_socks/_protocols/errors.py +10 -1
  15. python_socks/_protocols/http.py +55 -60
  16. python_socks/_protocols/socks4.py +22 -26
  17. python_socks/_protocols/socks5.py +77 -70
  18. python_socks/_types.py +4 -0
  19. python_socks/_version.py +2 -2
  20. python_socks/async_/__init__.py +0 -3
  21. python_socks/async_/anyio/__init__.py +1 -2
  22. python_socks/async_/anyio/_connect.py +6 -4
  23. python_socks/async_/anyio/_proxy.py +85 -92
  24. python_socks/async_/anyio/_resolver.py +10 -3
  25. python_socks/async_/anyio/_stream.py +11 -26
  26. python_socks/async_/anyio/v2/__init__.py +1 -5
  27. python_socks/async_/anyio/v2/_connect.py +7 -4
  28. python_socks/async_/anyio/v2/_proxy.py +36 -26
  29. python_socks/async_/anyio/v2/_stream.py +11 -11
  30. python_socks/async_/asyncio/__init__.py +1 -2
  31. python_socks/async_/asyncio/_connect.py +12 -7
  32. python_socks/async_/asyncio/_proxy.py +48 -49
  33. python_socks/async_/asyncio/_resolver.py +11 -4
  34. python_socks/async_/asyncio/_stream.py +9 -13
  35. python_socks/async_/asyncio/v2/__init__.py +1 -2
  36. python_socks/async_/asyncio/v2/_connect.py +8 -5
  37. python_socks/async_/asyncio/v2/_proxy.py +38 -28
  38. python_socks/async_/asyncio/v2/_stream.py +16 -18
  39. python_socks/async_/trio/__init__.py +1 -1
  40. python_socks/async_/trio/_connect.py +5 -4
  41. python_socks/async_/trio/_proxy.py +46 -48
  42. python_socks/async_/trio/_resolver.py +11 -3
  43. python_socks/async_/trio/_stream.py +9 -9
  44. python_socks/async_/trio/v2/__init__.py +1 -5
  45. python_socks/async_/trio/v2/_connect.py +3 -2
  46. python_socks/async_/trio/v2/_proxy.py +34 -24
  47. python_socks/async_/trio/v2/_stream.py +13 -13
  48. python_socks/sync/__init__.py +1 -3
  49. python_socks/sync/_connect.py +4 -3
  50. python_socks/sync/_proxy.py +42 -43
  51. python_socks/sync/_resolver.py +17 -5
  52. python_socks/sync/_stream.py +9 -9
  53. python_socks/sync/v2/__init__.py +1 -5
  54. python_socks/sync/v2/_connect.py +5 -3
  55. python_socks/sync/v2/_proxy.py +35 -26
  56. python_socks/sync/v2/_ssl_transport.py +3 -0
  57. python_socks/sync/v2/_stream.py +19 -14
  58. python_socks-3.0.0.dist-info/METADATA +346 -0
  59. python_socks-3.0.0.dist-info/RECORD +65 -0
  60. {python_socks-2.8.2.dist-info → python_socks-3.0.0.dist-info}/WHEEL +1 -1
  61. python_socks/async_/_proxy_chain.py +0 -34
  62. python_socks/async_/anyio/_chain.py +0 -42
  63. python_socks/async_/anyio/v2/_chain.py +0 -32
  64. python_socks/async_/asyncio/v2/_chain.py +0 -32
  65. python_socks/async_/curio/__init__.py +0 -4
  66. python_socks/async_/curio/_connect.py +0 -17
  67. python_socks/async_/curio/_proxy.py +0 -132
  68. python_socks/async_/curio/_resolver.py +0 -25
  69. python_socks/async_/curio/_stream.py +0 -32
  70. python_socks/async_/trio/v2/_chain.py +0 -32
  71. python_socks/sync/_chain.py +0 -32
  72. python_socks/sync/v2/_chain.py +0 -26
  73. python_socks-2.8.2.dist-info/METADATA +0 -295
  74. python_socks-2.8.2.dist-info/RECORD +0 -77
  75. {python_socks-2.8.2.dist-info → python_socks-3.0.0.dist-info}/licenses/LICENSE.txt +0 -0
  76. {python_socks-2.8.2.dist-info → python_socks-3.0.0.dist-info}/top_level.txt +0 -0
@@ -1,23 +1,27 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import ssl
3
- from typing import Any, Optional, Tuple
4
- import warnings
5
5
  import sys
6
+ import warnings
7
+ from typing import Any
6
8
 
7
-
8
- from ...._types import ProxyType
9
+ from ...._connectors.factory_async import create_connector
10
+ from ...._errors import (
11
+ IncompleteReadError,
12
+ ProxyConnectionError,
13
+ ProxyError,
14
+ ProxyTimeoutError,
15
+ )
9
16
  from ...._helpers import parse_proxy_url
10
- from ...._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError
11
-
12
17
  from ...._protocols.errors import ReplyError
13
- from ...._connectors.factory_async import create_connector
14
-
18
+ from ...._types import ProxyType
15
19
  from .._resolver import Resolver
16
- from ._stream import AsyncioSocketStream
17
20
  from ._connect import connect_tcp
21
+ from ._stream import AsyncioSocketStream
18
22
 
19
23
  if sys.version_info >= (3, 11):
20
- import asyncio as async_timeout # pylint:disable=reimported
24
+ import asyncio as async_timeout
21
25
  else:
22
26
  import async_timeout
23
27
 
@@ -26,21 +30,22 @@ DEFAULT_TIMEOUT = 60
26
30
 
27
31
 
28
32
  class AsyncioProxy:
29
- def __init__(
33
+ def __init__( # noqa: PLR0913, PLR0917
30
34
  self,
31
35
  proxy_type: ProxyType,
32
36
  host: str,
33
37
  port: int,
34
- username: Optional[str] = None,
35
- password: Optional[str] = None,
36
- rdns: Optional[bool] = None,
37
- proxy_ssl: Optional[ssl.SSLContext] = None,
38
- forward: Optional['AsyncioProxy'] = None,
39
- loop: Optional[asyncio.AbstractEventLoop] = None,
40
- ):
38
+ username: str | None = None,
39
+ password: str | None = None,
40
+ rdns: bool | None = None, # noqa: FBT001
41
+ proxy_ssl: ssl.SSLContext | None = None,
42
+ forward: AsyncioProxy | None = None,
43
+ loop: asyncio.AbstractEventLoop | None = None,
44
+ ) -> None:
41
45
  if loop is not None: # pragma: no cover
42
46
  warnings.warn(
43
- 'The loop argument is deprecated and scheduled for removal in the future.',
47
+ "The loop argument is deprecated "
48
+ "and scheduled for removal in the future.",
44
49
  DeprecationWarning,
45
50
  stacklevel=2,
46
51
  )
@@ -66,14 +71,14 @@ class AsyncioProxy:
66
71
  self,
67
72
  dest_host: str,
68
73
  dest_port: int,
69
- dest_ssl: Optional[ssl.SSLContext] = None,
70
- timeout: Optional[float] = None,
74
+ dest_ssl: ssl.SSLContext | None = None,
75
+ timeout: float | None = None,
71
76
  **kwargs: Any,
72
77
  ) -> AsyncioSocketStream:
73
78
  if timeout is None:
74
79
  timeout = DEFAULT_TIMEOUT
75
80
 
76
- local_addr = kwargs.get('local_addr')
81
+ local_addr = kwargs.get("local_addr")
77
82
  try:
78
83
  async with async_timeout.timeout(timeout):
79
84
  return await self._connect(
@@ -83,14 +88,14 @@ class AsyncioProxy:
83
88
  local_addr=local_addr,
84
89
  )
85
90
  except asyncio.TimeoutError as e:
86
- raise ProxyTimeoutError('Proxy connection timed out: {}'.format(timeout)) from e
91
+ raise ProxyTimeoutError(f"Proxy connection timed out: {timeout}") from e
87
92
 
88
93
  async def _connect(
89
94
  self,
90
95
  dest_host: str,
91
96
  dest_port: int,
92
- dest_ssl: Optional[ssl.SSLContext] = None,
93
- local_addr: Optional[Tuple[str, int]] = None,
97
+ dest_ssl: ssl.SSLContext | None = None,
98
+ local_addr: tuple[str, int] | None = None,
94
99
  ) -> AsyncioSocketStream:
95
100
  if self._forward is None:
96
101
  try:
@@ -140,7 +145,10 @@ class AsyncioProxy:
140
145
  )
141
146
  except ReplyError as e:
142
147
  await stream.close()
143
- raise ProxyError(e, error_code=e.error_code)
148
+ raise ProxyError(e, error_code=e.error_code) from e
149
+ except IncompleteReadError as e:
150
+ await stream.close()
151
+ raise ProxyError(e) from e
144
152
  except (asyncio.CancelledError, Exception):
145
153
  await stream.close()
146
154
  raise
@@ -148,10 +156,12 @@ class AsyncioProxy:
148
156
  return stream
149
157
 
150
158
  @classmethod
151
- def create(cls, *args, **kwargs): # for backward compatibility
159
+ def create(
160
+ cls, *args: Any, **kwargs: Any
161
+ ) -> AsyncioProxy: # for backward compatibility
152
162
  return cls(*args, **kwargs)
153
163
 
154
164
  @classmethod
155
- def from_url(cls, url: str, **kwargs) -> 'AsyncioProxy':
165
+ def from_url(cls, url: str, **kwargs: Any) -> AsyncioProxy:
156
166
  url_args = parse_proxy_url(url)
157
167
  return cls(*url_args, **kwargs)
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import ssl
3
5
 
@@ -7,37 +9,33 @@ DEFAULT_RECEIVE_SIZE = 65536
7
9
 
8
10
 
9
11
  class AsyncioSocketStream(abc.AsyncSocketStream):
10
- _loop: asyncio.AbstractEventLoop
11
- _reader: asyncio.StreamReader
12
- _writer: asyncio.StreamWriter
13
-
14
12
  def __init__(
15
13
  self,
16
14
  loop: asyncio.AbstractEventLoop,
17
15
  reader: asyncio.StreamReader,
18
16
  writer: asyncio.StreamWriter,
19
- ):
17
+ ) -> None:
20
18
  self._loop = loop
21
19
  self._reader = reader
22
20
  self._writer = writer
23
21
 
24
- async def write_all(self, data):
22
+ async def write(self, data: bytes) -> None:
25
23
  self._writer.write(data)
26
24
  await self._writer.drain()
27
25
 
28
- async def read(self, max_bytes=DEFAULT_RECEIVE_SIZE):
26
+ async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE) -> bytes:
29
27
  return await self._reader.read(max_bytes)
30
28
 
31
- async def read_exact(self, n):
29
+ async def read_exactly(self, n: int) -> bytes:
32
30
  return await self._reader.readexactly(n)
33
31
 
34
32
  async def start_tls(
35
33
  self,
36
34
  hostname: str,
37
35
  ssl_context: ssl.SSLContext,
38
- ssl_handshake_timeout=None,
39
- ) -> 'AsyncioSocketStream':
40
- if hasattr(self._writer, 'start_tls'): # Python>=3.11
36
+ ssl_handshake_timeout: float | None = None,
37
+ ) -> AsyncioSocketStream:
38
+ if hasattr(self._writer, "start_tls"): # Python>=3.11
41
39
  await self._writer.start_tls(
42
40
  ssl_context,
43
41
  server_hostname=hostname,
@@ -49,7 +47,7 @@ class AsyncioSocketStream(abc.AsyncSocketStream):
49
47
  protocol = asyncio.StreamReaderProtocol(reader)
50
48
 
51
49
  transport: asyncio.Transport = await self._loop.start_tls(
52
- self._writer.transport, # type: ignore
50
+ self._writer.transport, # type: ignore[assignment]
53
51
  protocol,
54
52
  ssl_context,
55
53
  server_side=False,
@@ -61,7 +59,7 @@ class AsyncioSocketStream(abc.AsyncSocketStream):
61
59
 
62
60
  # Initialize the protocol, so it is made aware of being tied to
63
61
  # a TLS connection.
64
- # See: https://github.com/encode/httpx/issues/859
62
+ # See also: https://github.com/encode/httpx/issues/859
65
63
  protocol.connection_made(transport)
66
64
 
67
65
  writer = asyncio.StreamWriter(
@@ -75,17 +73,17 @@ class AsyncioSocketStream(abc.AsyncSocketStream):
75
73
  # When we return a new SocketStream with new StreamReader/StreamWriter instances
76
74
  # we need to keep references to the old StreamReader/StreamWriter so that they
77
75
  # are not garbage collected and closed while we're still using them.
78
- stream._inner = self # type: ignore # pylint:disable=W0212,W0201
76
+ stream._inner = self # type: ignore[attr-defined]
79
77
  return stream
80
78
 
81
- async def close(self):
79
+ async def close(self) -> None:
82
80
  self._writer.close()
83
- self._writer.transport.abort() # noqa
81
+ self._writer.transport.abort()
84
82
 
85
83
  @property
86
- def reader(self):
84
+ def reader(self) -> asyncio.StreamReader:
87
85
  return self._reader # pragma: no cover
88
86
 
89
87
  @property
90
- def writer(self):
88
+ def writer(self) -> asyncio.StreamWriter:
91
89
  return self._writer # pragma: no cover
@@ -1,3 +1,3 @@
1
1
  from ._proxy import TrioProxy as Proxy
2
2
 
3
- __all__ = ('Proxy',)
3
+ __all__ = ("Proxy",)
@@ -1,15 +1,16 @@
1
- from typing import Optional, Tuple
1
+ from __future__ import annotations
2
2
 
3
3
  import trio
4
4
 
5
- from ._resolver import Resolver
6
5
  from ..._helpers import is_ipv4_address, is_ipv6_address
6
+ from ..._types import ResolvedAddress
7
+ from ._resolver import Resolver
7
8
 
8
9
 
9
10
  async def connect_tcp(
10
11
  host: str,
11
12
  port: int,
12
- local_addr: Optional[Tuple[str, int]] = None,
13
+ local_addr: tuple[str, int] | None = None,
13
14
  ) -> trio.socket.SocketType:
14
15
 
15
16
  family, host = await _resolve_host(host)
@@ -26,7 +27,7 @@ async def connect_tcp(
26
27
  return sock
27
28
 
28
29
 
29
- async def _resolve_host(host):
30
+ async def _resolve_host(host: str) -> ResolvedAddress:
30
31
  if is_ipv4_address(host):
31
32
  return trio.socket.AF_INET, host
32
33
  if is_ipv6_address(host):
@@ -1,18 +1,22 @@
1
- from typing import Any, Optional
2
- import warnings
3
- import trio
1
+ from __future__ import annotations
4
2
 
5
- from ..._types import ProxyType
6
- from ..._helpers import parse_proxy_url
7
- from ..._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError
3
+ from typing import Any
8
4
 
9
- from ._stream import TrioSocketStream
10
- from ._resolver import Resolver
11
- from ._connect import connect_tcp
5
+ import trio
12
6
 
13
- from ..._protocols.errors import ReplyError
14
7
  from ..._connectors.factory_async import create_connector
15
-
8
+ from ..._errors import (
9
+ IncompleteReadError,
10
+ ProxyConnectionError,
11
+ ProxyError,
12
+ ProxyTimeoutError,
13
+ )
14
+ from ..._helpers import parse_proxy_url
15
+ from ..._protocols.errors import ReplyError
16
+ from ..._types import ProxyType
17
+ from ._connect import connect_tcp
18
+ from ._resolver import Resolver
19
+ from ._stream import TrioSocketStream
16
20
 
17
21
  DEFAULT_TIMEOUT = 60
18
22
 
@@ -23,16 +27,18 @@ class TrioProxy:
23
27
  proxy_type: ProxyType,
24
28
  host: str,
25
29
  port: int,
26
- username: Optional[str] = None,
27
- password: Optional[str] = None,
28
- rdns: Optional[bool] = None,
29
- ):
30
+ username: str | None = None,
31
+ password: str | None = None,
32
+ rdns: bool | None = None, # noqa: FBT001
33
+ forward: TrioProxy | None = None,
34
+ ) -> None:
30
35
  self._proxy_type = proxy_type
31
36
  self._proxy_host = host
32
37
  self._proxy_port = port
33
38
  self._password = password
34
39
  self._username = username
35
40
  self._rdns = rdns
41
+ self._forward = forward
36
42
 
37
43
  self._resolver = Resolver()
38
44
 
@@ -40,55 +46,50 @@ class TrioProxy:
40
46
  self,
41
47
  dest_host: str,
42
48
  dest_port: int,
43
- timeout: Optional[float] = None,
49
+ timeout: float | None = None,
44
50
  **kwargs: Any,
45
51
  ) -> trio.socket.SocketType:
46
52
  if timeout is None:
47
53
  timeout = DEFAULT_TIMEOUT
48
54
 
49
- _socket = kwargs.get('_socket')
50
- if _socket is not None:
51
- warnings.warn(
52
- "The '_socket' argument is deprecated and will be removed in the future",
53
- DeprecationWarning,
54
- stacklevel=2,
55
- )
56
-
57
- local_addr = kwargs.get('local_addr')
55
+ local_addr = kwargs.get("local_addr")
58
56
  try:
59
57
  with trio.fail_after(timeout):
60
58
  return await self._connect(
61
59
  dest_host=dest_host,
62
60
  dest_port=dest_port,
63
- _socket=_socket,
64
61
  local_addr=local_addr,
65
62
  )
66
63
  except trio.TooSlowError as e:
67
- raise ProxyTimeoutError('Proxy connection timed out: {}'.format(timeout)) from e
64
+ raise ProxyTimeoutError(f"Proxy connection timed out: {timeout}") from e
68
65
 
69
66
  async def _connect(
70
67
  self,
71
68
  dest_host: str,
72
69
  dest_port: int,
73
- _socket=None,
74
- local_addr=None,
70
+ local_addr: tuple[str, int] | None = None,
75
71
  ) -> trio.socket.SocketType:
76
- if _socket is None:
72
+ if self._forward is not None:
73
+ sock = await self._forward.connect(
74
+ dest_host=self._proxy_host,
75
+ dest_port=self._proxy_port,
76
+ timeout=None,
77
+ )
78
+ else:
77
79
  try:
78
- _socket = await connect_tcp(
80
+ sock = await connect_tcp(
79
81
  host=self._proxy_host,
80
82
  port=self._proxy_port,
81
83
  local_addr=local_addr,
82
84
  )
83
85
  except OSError as e:
84
- msg = 'Could not connect to proxy {}:{} [{}]'.format(
85
- self._proxy_host,
86
- self._proxy_port,
87
- e.strerror,
86
+ msg = (
87
+ f"Could not connect to proxy "
88
+ f"{self._proxy_host}:{self._proxy_port} [{e.strerror}]"
88
89
  )
89
90
  raise ProxyConnectionError(e.errno, msg) from e
90
91
 
91
- stream = TrioSocketStream(sock=_socket)
92
+ stream = TrioSocketStream(sock=sock)
92
93
 
93
94
  try:
94
95
  connector = create_connector(
@@ -103,29 +104,26 @@ class TrioProxy:
103
104
  host=dest_host,
104
105
  port=dest_port,
105
106
  )
106
- return _socket
107
+ return sock
107
108
 
108
109
  except ReplyError as e:
109
110
  await stream.close()
110
- raise ProxyError(e, error_code=e.error_code)
111
+ raise ProxyError(e, error_code=e.error_code) from e
112
+ except IncompleteReadError as e:
113
+ await stream.close()
114
+ raise ProxyError(e) from e
111
115
  except BaseException: # trio.Cancelled...
112
116
  with trio.CancelScope(shield=True):
113
117
  await stream.close()
114
118
  raise
115
119
 
116
- @property
117
- def proxy_host(self):
118
- return self._proxy_host
119
-
120
- @property
121
- def proxy_port(self):
122
- return self._proxy_port
123
-
124
120
  @classmethod
125
- def create(cls, *args, **kwargs): # for backward compatibility
121
+ def create(
122
+ cls, *args: Any, **kwargs: Any
123
+ ) -> TrioProxy: # for backward compatibility
126
124
  return cls(*args, **kwargs)
127
125
 
128
126
  @classmethod
129
- def from_url(cls, url: str, **kwargs) -> 'TrioProxy':
127
+ def from_url(cls, url: str, **kwargs: Any) -> TrioProxy:
130
128
  url_args = parse_proxy_url(url)
131
129
  return cls(*url_args, **kwargs)
@@ -1,10 +1,18 @@
1
+ import socket
2
+
1
3
  import trio
2
4
 
3
5
  from ... import _abc as abc
6
+ from ..._types import ResolvedAddress
4
7
 
5
8
 
6
9
  class Resolver(abc.AsyncResolver):
7
- async def resolve(self, host, port=0, family=trio.socket.AF_UNSPEC):
10
+ async def resolve(
11
+ self,
12
+ host: str,
13
+ port: int = 0,
14
+ family: socket.AddressFamily = trio.socket.AF_UNSPEC,
15
+ ) -> ResolvedAddress:
8
16
  infos = await trio.socket.getaddrinfo(
9
17
  host=host,
10
18
  port=port,
@@ -13,9 +21,9 @@ class Resolver(abc.AsyncResolver):
13
21
  )
14
22
 
15
23
  if not infos: # pragma: no cover
16
- raise OSError('Can`t resolve address {}:{} [{}]'.format(host, port, family))
24
+ raise OSError(f"Can`t resolve address {host}:{port} [{family}]")
17
25
 
18
26
  infos = sorted(infos, key=lambda info: info[0])
19
27
 
20
28
  family, _, _, _, address = infos[0]
21
- return family, address[0]
29
+ return family, address[0] # type:ignore[return-value]
@@ -1,35 +1,35 @@
1
1
  import trio
2
2
 
3
- from ..._errors import ProxyError
4
3
  from ... import _abc as abc
4
+ from ..._errors import IncompleteReadError
5
5
 
6
6
  DEFAULT_RECEIVE_SIZE = 65536
7
7
 
8
8
 
9
9
  class TrioSocketStream(abc.AsyncSocketStream):
10
- def __init__(self, sock):
10
+ def __init__(self, sock: trio.socket.SocketType) -> None:
11
11
  self._socket = sock
12
12
 
13
- async def write_all(self, data):
13
+ async def write(self, data: bytes) -> None:
14
14
  total_sent = 0
15
15
  while total_sent < len(data):
16
16
  remaining = data[total_sent:]
17
17
  sent = await self._socket.send(remaining)
18
18
  total_sent += sent
19
19
 
20
- async def read(self, max_bytes=DEFAULT_RECEIVE_SIZE):
20
+ async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE) -> bytes:
21
21
  return await self._socket.recv(max_bytes)
22
22
 
23
- async def read_exact(self, n):
23
+ async def read_exactly(self, n: int) -> bytes:
24
24
  data = bytearray()
25
25
  while len(data) < n:
26
26
  packet = await self._socket.recv(n - len(data))
27
27
  if not packet: # pragma: no cover
28
- raise ProxyError('Connection closed unexpectedly')
29
- data += packet
30
- return data
28
+ raise IncompleteReadError("Connection closed unexpectedly")
29
+ data.extend(packet)
30
+ return data # type:ignore[return-value]
31
31
 
32
- async def close(self):
32
+ async def close(self) -> None:
33
33
  if self._socket is not None:
34
34
  self._socket.close()
35
35
  await trio.lowlevel.checkpoint()
@@ -1,7 +1,3 @@
1
1
  from ._proxy import TrioProxy as Proxy
2
- from ._chain import ProxyChain
3
2
 
4
- __all__ = (
5
- 'Proxy',
6
- 'ProxyChain',
7
- )
3
+ __all__ = ("Proxy",)
@@ -1,13 +1,14 @@
1
- from typing import Optional
1
+ from __future__ import annotations
2
2
 
3
3
  import trio
4
+
4
5
  from ._stream import TrioSocketStream
5
6
 
6
7
 
7
8
  async def connect_tcp(
8
9
  host: str,
9
10
  port: int,
10
- local_addr: Optional[str] = None,
11
+ local_addr: str | None = None,
11
12
  ) -> TrioSocketStream:
12
13
  trio_stream = await trio.open_tcp_stream(
13
14
  host=host,
@@ -1,18 +1,23 @@
1
+ from __future__ import annotations
2
+
1
3
  import ssl
2
- from typing import Any, Optional
4
+ from typing import Any
3
5
 
4
6
  import trio
5
7
 
6
- from ._connect import connect_tcp
7
- from ._stream import TrioSocketStream
8
- from .._resolver import Resolver
9
-
10
- from ...._types import ProxyType
8
+ from ...._connectors.factory_async import create_connector
9
+ from ...._errors import (
10
+ IncompleteReadError,
11
+ ProxyConnectionError,
12
+ ProxyError,
13
+ ProxyTimeoutError,
14
+ )
11
15
  from ...._helpers import parse_proxy_url
12
- from ...._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError
13
-
14
16
  from ...._protocols.errors import ReplyError
15
- from ...._connectors.factory_async import create_connector
17
+ from ...._types import ProxyType
18
+ from .._resolver import Resolver
19
+ from ._connect import connect_tcp
20
+ from ._stream import TrioSocketStream
16
21
 
17
22
  DEFAULT_TIMEOUT = 60
18
23
 
@@ -23,12 +28,12 @@ class TrioProxy:
23
28
  proxy_type: ProxyType,
24
29
  host: str,
25
30
  port: int,
26
- username: Optional[str] = None,
27
- password: Optional[str] = None,
28
- rdns: Optional[bool] = None,
29
- proxy_ssl: Optional[ssl.SSLContext] = None,
30
- forward: Optional['TrioProxy'] = None,
31
- ):
31
+ username: str | None = None,
32
+ password: str | None = None,
33
+ rdns: bool | None = None, # noqa: FBT001
34
+ proxy_ssl: ssl.SSLContext | None = None,
35
+ forward: TrioProxy | None = None,
36
+ ) -> None:
32
37
  self._proxy_type = proxy_type
33
38
  self._proxy_host = host
34
39
  self._proxy_port = port
@@ -45,14 +50,14 @@ class TrioProxy:
45
50
  self,
46
51
  dest_host: str,
47
52
  dest_port: int,
48
- dest_ssl: Optional[ssl.SSLContext] = None,
49
- timeout: Optional[float] = None,
53
+ dest_ssl: ssl.SSLContext | None = None,
54
+ timeout: float | None = None,
50
55
  **kwargs: Any,
51
56
  ) -> TrioSocketStream:
52
57
  if timeout is None:
53
58
  timeout = DEFAULT_TIMEOUT
54
59
 
55
- local_addr = kwargs.get('local_addr')
60
+ local_addr = kwargs.get("local_addr")
56
61
  try:
57
62
  with trio.fail_after(timeout):
58
63
  return await self._connect(
@@ -62,14 +67,14 @@ class TrioProxy:
62
67
  local_addr=local_addr,
63
68
  )
64
69
  except trio.TooSlowError as e:
65
- raise ProxyTimeoutError(f'Proxy connection timed out: {timeout}') from e
70
+ raise ProxyTimeoutError(f"Proxy connection timed out: {timeout}") from e
66
71
 
67
72
  async def _connect(
68
73
  self,
69
74
  dest_host: str,
70
75
  dest_port: int,
71
- dest_ssl: Optional[ssl.SSLContext] = None,
72
- local_addr: Optional[str] = None,
76
+ dest_ssl: ssl.SSLContext | None = None,
77
+ local_addr: str | None = None,
73
78
  ) -> TrioSocketStream:
74
79
  if self._forward is None:
75
80
  try:
@@ -117,7 +122,10 @@ class TrioProxy:
117
122
  )
118
123
  except ReplyError as e:
119
124
  await stream.close()
120
- raise ProxyError(e, error_code=e.error_code)
125
+ raise ProxyError(e, error_code=e.error_code) from e
126
+ except IncompleteReadError as e:
127
+ await stream.close()
128
+ raise ProxyError(e) from e
121
129
  except BaseException: # trio.Cancelled...
122
130
  with trio.CancelScope(shield=True):
123
131
  await stream.close()
@@ -126,10 +134,12 @@ class TrioProxy:
126
134
  return stream
127
135
 
128
136
  @classmethod
129
- def create(cls, *args, **kwargs): # for backward compatibility
137
+ def create(
138
+ cls, *args: Any, **kwargs: Any
139
+ ) -> TrioProxy: # for backward compatibility
130
140
  return cls(*args, **kwargs)
131
141
 
132
142
  @classmethod
133
- def from_url(cls, url: str, **kwargs) -> 'TrioProxy':
143
+ def from_url(cls, url: str, **kwargs: Any) -> TrioProxy:
134
144
  url_args = parse_proxy_url(url)
135
145
  return cls(*url_args, **kwargs)