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,12 +1,13 @@
1
- import ssl
1
+ from __future__ import annotations
2
+
2
3
  from typing import Union
3
4
 
4
5
  import anyio
5
6
  import anyio.abc
6
7
  from anyio.streams.tls import TLSStream
7
8
 
8
- from ..._errors import ProxyError
9
9
  from ... import _abc as abc
10
+ from ..._errors import IncompleteReadError
10
11
 
11
12
  DEFAULT_RECEIVE_SIZE = 65536
12
13
 
@@ -14,44 +15,28 @@ AnyioStreamType = Union[anyio.abc.SocketStream, TLSStream]
14
15
 
15
16
 
16
17
  class AnyioSocketStream(abc.AsyncSocketStream):
17
- _stream: AnyioStreamType
18
-
19
18
  def __init__(self, stream: AnyioStreamType) -> None:
20
19
  self._stream = stream
21
20
 
22
- async def write_all(self, data: bytes):
21
+ async def write(self, data: bytes) -> None:
23
22
  await self._stream.send(item=data)
24
23
 
25
- async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE):
24
+ async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE) -> bytes:
26
25
  try:
27
26
  return await self._stream.receive(max_bytes=max_bytes)
28
27
  except anyio.EndOfStream: # pragma: no cover
29
28
  return b""
30
29
 
31
- async def read_exact(self, n: int):
30
+ async def read_exactly(self, n: int) -> bytes:
32
31
  data = bytearray()
33
32
  while len(data) < n:
34
33
  packet = await self.read(n - len(data))
35
34
  if not packet: # pragma: no cover
36
- raise ProxyError('Connection closed unexpectedly')
37
- data += packet
38
- return data
39
-
40
- async def start_tls(
41
- self,
42
- hostname: str,
43
- ssl_context: ssl.SSLContext,
44
- ) -> 'AnyioSocketStream':
45
- ssl_stream = await TLSStream.wrap(
46
- self._stream,
47
- ssl_context=ssl_context,
48
- hostname=hostname,
49
- standard_compatible=False,
50
- server_side=False,
51
- )
52
- return AnyioSocketStream(ssl_stream)
53
-
54
- async def close(self):
35
+ raise IncompleteReadError("Connection closed unexpectedly")
36
+ data.extend(packet)
37
+ return data # type:ignore[return-value]
38
+
39
+ async def close(self) -> None:
55
40
  await self._stream.aclose()
56
41
 
57
42
  @property
@@ -1,7 +1,3 @@
1
1
  from ._proxy import AnyioProxy as Proxy
2
- from ._chain import ProxyChain
3
2
 
4
- __all__ = (
5
- 'Proxy',
6
- 'ProxyChain',
7
- )
3
+ __all__ = ("Proxy",)
@@ -1,17 +1,20 @@
1
- from typing import Optional
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
2
5
  import anyio
3
- import anyio.abc
6
+
4
7
  from ._stream import AnyioSocketStream
5
8
 
6
9
 
7
10
  async def connect_tcp(
8
11
  host: str,
9
12
  port: int,
10
- local_host: Optional[str] = None,
13
+ **kwargs: Any,
11
14
  ) -> AnyioSocketStream:
12
15
  s = await anyio.connect_tcp(
13
16
  remote_host=host,
14
17
  remote_port=port,
15
- local_host=local_host,
18
+ **kwargs,
16
19
  )
17
20
  return AnyioSocketStream(s)
@@ -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 anyio
5
7
 
6
- from ._connect import connect_tcp
7
- from ._stream import AnyioSocketStream
8
- from .._resolver import Resolver
9
- from ...._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError
10
-
11
- 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
+ )
12
15
  from ...._helpers import parse_proxy_url
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 AnyioSocketStream
16
21
 
17
22
  DEFAULT_TIMEOUT = 60
18
23
 
@@ -23,12 +28,12 @@ class AnyioProxy:
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['AnyioProxy'] = 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: AnyioProxy | None = None,
36
+ ) -> None:
32
37
  self._proxy_type = proxy_type
33
38
  self._proxy_host = host
34
39
  self._proxy_port = port
@@ -45,38 +50,37 @@ class AnyioProxy:
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
  ) -> AnyioSocketStream:
52
57
  if timeout is None:
53
58
  timeout = DEFAULT_TIMEOUT
54
59
 
55
- local_host = kwargs.get('local_host')
56
60
  try:
57
61
  with anyio.fail_after(timeout):
58
62
  return await self._connect(
59
63
  dest_host=dest_host,
60
64
  dest_port=dest_port,
61
65
  dest_ssl=dest_ssl,
62
- local_host=local_host,
66
+ **kwargs,
63
67
  )
64
68
  except TimeoutError as e:
65
- raise ProxyTimeoutError('Proxy connection timed out: {}'.format(timeout)) from e
69
+ raise ProxyTimeoutError(f"Proxy connection timed out: {timeout}") from e
66
70
 
67
71
  async def _connect(
68
72
  self,
69
73
  dest_host: str,
70
74
  dest_port: int,
71
- dest_ssl: Optional[ssl.SSLContext] = None,
72
- local_host: Optional[str] = None,
75
+ dest_ssl: ssl.SSLContext | None = None,
76
+ **kwargs: Any,
73
77
  ) -> AnyioSocketStream:
74
78
  if self._forward is None:
75
79
  try:
76
80
  stream = await connect_tcp(
77
81
  host=self._proxy_host,
78
82
  port=self._proxy_port,
79
- local_host=local_host,
83
+ **kwargs,
80
84
  )
81
85
  except OSError as e:
82
86
  raise ProxyConnectionError(
@@ -88,6 +92,7 @@ class AnyioProxy:
88
92
  stream = await self._forward.connect(
89
93
  dest_host=self._proxy_host,
90
94
  dest_port=self._proxy_port,
95
+ **kwargs,
91
96
  )
92
97
 
93
98
  try:
@@ -117,7 +122,10 @@ class AnyioProxy:
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:
122
130
  with anyio.CancelScope(shield=True):
123
131
  await stream.close()
@@ -126,10 +134,12 @@ class AnyioProxy:
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
+ ) -> AnyioProxy: # for backward compatibility
130
140
  return cls(*args, **kwargs)
131
141
 
132
142
  @classmethod
133
- def from_url(cls, url: str, **kwargs) -> 'AnyioProxy':
143
+ def from_url(cls, url: str, **kwargs: Any) -> AnyioProxy:
134
144
  url_args = parse_proxy_url(url)
135
145
  return cls(*url_args, **kwargs)
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import ssl
2
4
  from typing import Union
3
5
 
@@ -5,8 +7,8 @@ import anyio
5
7
  import anyio.abc
6
8
  from anyio.streams.tls import TLSStream
7
9
 
8
- from ...._errors import ProxyError
9
10
  from .... import _abc as abc
11
+ from ...._errors import IncompleteReadError
10
12
 
11
13
  DEFAULT_RECEIVE_SIZE = 65536
12
14
 
@@ -14,34 +16,32 @@ AnyioStreamType = Union[anyio.abc.SocketStream, TLSStream]
14
16
 
15
17
 
16
18
  class AnyioSocketStream(abc.AsyncSocketStream):
17
- _stream: AnyioStreamType
18
-
19
19
  def __init__(self, stream: AnyioStreamType) -> None:
20
20
  self._stream = stream
21
21
 
22
- async def write_all(self, data: bytes):
22
+ async def write(self, data: bytes) -> None:
23
23
  await self._stream.send(item=data)
24
24
 
25
- async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE):
25
+ async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE) -> bytes:
26
26
  try:
27
27
  return await self._stream.receive(max_bytes=max_bytes)
28
28
  except anyio.EndOfStream: # pragma: no cover
29
29
  return b""
30
30
 
31
- async def read_exact(self, n: int):
31
+ async def read_exactly(self, n: int) -> bytes:
32
32
  data = bytearray()
33
33
  while len(data) < n:
34
34
  packet = await self.read(n - len(data))
35
35
  if not packet: # pragma: no cover
36
- raise ProxyError('Connection closed unexpectedly')
37
- data += packet
38
- return data
36
+ raise IncompleteReadError("Connection closed unexpectedly")
37
+ data.extend(packet)
38
+ return data # type:ignore[return-value]
39
39
 
40
40
  async def start_tls(
41
41
  self,
42
42
  hostname: str,
43
43
  ssl_context: ssl.SSLContext,
44
- ) -> 'AnyioSocketStream':
44
+ ) -> AnyioSocketStream:
45
45
  ssl_stream = await TLSStream.wrap(
46
46
  self._stream,
47
47
  ssl_context=ssl_context,
@@ -51,7 +51,7 @@ class AnyioSocketStream(abc.AsyncSocketStream):
51
51
  )
52
52
  return AnyioSocketStream(ssl_stream)
53
53
 
54
- async def close(self):
54
+ async def close(self) -> None:
55
55
  await self._stream.aclose()
56
56
 
57
57
  @property
@@ -1,4 +1,3 @@
1
1
  from ._proxy import AsyncioProxy as Proxy
2
2
 
3
-
4
- __all__ = ('Proxy',)
3
+ __all__ = ("Proxy",)
@@ -1,26 +1,28 @@
1
- import socket
1
+ from __future__ import annotations
2
+
2
3
  import asyncio
3
- from typing import Optional, Tuple
4
+ import socket
4
5
 
5
- from ._resolver import Resolver
6
6
  from ..._helpers import is_ipv4_address, is_ipv6_address
7
+ from ..._types import ResolvedAddress
8
+ from ._resolver import Resolver
7
9
 
8
10
 
9
11
  async def connect_tcp(
10
12
  host: str,
11
13
  port: int,
12
14
  loop: asyncio.AbstractEventLoop,
13
- local_addr: Optional[Tuple[str, int]] = None,
15
+ local_addr: tuple[str, int] | None = None,
14
16
  ) -> socket.socket:
15
17
 
16
18
  family, host = await _resolve_host(host, loop)
17
19
 
18
20
  sock = socket.socket(family=family, type=socket.SOCK_STREAM)
19
- sock.setblocking(False)
21
+ sock.setblocking(False) # noqa: FBT003
20
22
  if local_addr is not None: # pragma: no cover
21
23
  sock.bind(local_addr)
22
24
 
23
- if is_ipv6_address(host):
25
+ if is_ipv6_address(host): # noqa: SIM108
24
26
  address = (host, port, 0, 0) # to fix OSError: [WinError 10022]
25
27
  else:
26
28
  address = (host, port) # type: ignore[assignment]
@@ -33,7 +35,10 @@ async def connect_tcp(
33
35
  return sock
34
36
 
35
37
 
36
- async def _resolve_host(host, loop):
38
+ async def _resolve_host(
39
+ host: str,
40
+ loop: asyncio.AbstractEventLoop,
41
+ ) -> ResolvedAddress:
37
42
  if is_ipv4_address(host):
38
43
  return socket.AF_INET, host
39
44
  if is_ipv6_address(host):
@@ -1,22 +1,26 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import socket
3
5
  import sys
4
- from typing import Any, Optional
5
- import warnings
6
+ from typing import Any
6
7
 
7
- 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
+ )
8
15
  from ..._helpers import parse_proxy_url
9
- from ..._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError
10
- from ._stream import AsyncioSocketStream
11
- from ._resolver import Resolver
12
-
13
16
  from ..._protocols.errors import ReplyError
14
- from ..._connectors.factory_async import create_connector
15
-
17
+ from ..._types import ProxyType
16
18
  from ._connect import connect_tcp
19
+ from ._resolver import Resolver
20
+ from ._stream import AsyncioSocketStream
17
21
 
18
22
  if sys.version_info >= (3, 11):
19
- import asyncio as async_timeout # pylint:disable=reimported
23
+ import asyncio as async_timeout
20
24
  else:
21
25
  import async_timeout
22
26
 
@@ -29,11 +33,12 @@ class AsyncioProxy:
29
33
  proxy_type: ProxyType,
30
34
  host: str,
31
35
  port: int,
32
- username: Optional[str] = None,
33
- password: Optional[str] = None,
34
- rdns: Optional[bool] = None,
35
- loop: Optional[asyncio.AbstractEventLoop] = None,
36
- ):
36
+ username: str | None = None,
37
+ password: str | None = None,
38
+ rdns: bool | None = None, # noqa: FBT001
39
+ forward: AsyncioProxy | None = None,
40
+ loop: asyncio.AbstractEventLoop | None = None,
41
+ ) -> None:
37
42
  if loop is None:
38
43
  loop = asyncio.get_event_loop()
39
44
 
@@ -45,6 +50,7 @@ class AsyncioProxy:
45
50
  self._password = password
46
51
  self._username = username
47
52
  self._rdns = rdns
53
+ self._forward = forward
48
54
 
49
55
  self._resolver = Resolver(loop=loop)
50
56
 
@@ -52,56 +58,52 @@ class AsyncioProxy:
52
58
  self,
53
59
  dest_host: str,
54
60
  dest_port: int,
55
- timeout: Optional[float] = None,
61
+ timeout: float | None = None,
56
62
  **kwargs: Any,
57
63
  ) -> socket.socket:
58
64
  if timeout is None:
59
65
  timeout = DEFAULT_TIMEOUT
60
66
 
61
- _socket = kwargs.get('_socket')
62
- if _socket is not None:
63
- warnings.warn(
64
- "The '_socket' argument is deprecated and will be removed in the future",
65
- DeprecationWarning,
66
- stacklevel=2,
67
- )
67
+ local_addr = kwargs.get("local_addr")
68
68
 
69
- local_addr = kwargs.get('local_addr')
70
69
  try:
71
70
  async with async_timeout.timeout(timeout):
72
71
  return await self._connect(
73
72
  dest_host=dest_host,
74
73
  dest_port=dest_port,
75
- _socket=_socket,
76
74
  local_addr=local_addr,
77
75
  )
78
76
  except asyncio.TimeoutError as e:
79
- raise ProxyTimeoutError(f'Proxy connection timed out: {timeout}') from e
77
+ raise ProxyTimeoutError(f"Proxy connection timed out: {timeout}") from e
80
78
 
81
79
  async def _connect(
82
80
  self,
83
- dest_host,
84
- dest_port,
85
- _socket=None,
86
- local_addr=None,
81
+ dest_host: str,
82
+ dest_port: int,
83
+ local_addr: tuple[str, int] | None = None,
87
84
  ) -> socket.socket:
88
- if _socket is None:
85
+ if self._forward is not None:
86
+ sock = await self._forward.connect(
87
+ dest_host=self._proxy_host,
88
+ dest_port=self._proxy_port,
89
+ timeout=None,
90
+ )
91
+ else:
89
92
  try:
90
- _socket = await connect_tcp(
93
+ sock = await connect_tcp(
91
94
  host=self._proxy_host,
92
95
  port=self._proxy_port,
93
96
  loop=self._loop,
94
97
  local_addr=local_addr,
95
98
  )
96
99
  except OSError as e:
97
- msg = 'Could not connect to proxy {}:{} [{}]'.format(
98
- self._proxy_host,
99
- self._proxy_port,
100
- e.strerror,
100
+ msg = (
101
+ f"Could not connect to proxy "
102
+ f"{self._proxy_host}:{self._proxy_port} [{e.strerror}]"
101
103
  )
102
104
  raise ProxyConnectionError(e.errno, msg) from e
103
105
 
104
- stream = AsyncioSocketStream(sock=_socket, loop=self._loop)
106
+ stream = AsyncioSocketStream(sock=sock, loop=self._loop)
105
107
 
106
108
  try:
107
109
  connector = create_connector(
@@ -117,27 +119,24 @@ class AsyncioProxy:
117
119
  port=dest_port,
118
120
  )
119
121
 
120
- return _socket
122
+ return sock
121
123
  except ReplyError as e:
122
124
  await stream.close()
123
- 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
124
129
  except (asyncio.CancelledError, Exception): # pragma: no cover
125
130
  await stream.close()
126
131
  raise
127
132
 
128
- @property
129
- def proxy_host(self):
130
- return self._proxy_host
131
-
132
- @property
133
- def proxy_port(self):
134
- return self._proxy_port
135
-
136
133
  @classmethod
137
- def create(cls, *args, **kwargs): # for backward compatibility
134
+ def create(
135
+ cls, *args: Any, **kwargs: Any
136
+ ) -> AsyncioProxy: # for backward compatibility
138
137
  return cls(*args, **kwargs)
139
138
 
140
139
  @classmethod
141
- def from_url(cls, url: str, **kwargs) -> 'AsyncioProxy':
140
+ def from_url(cls, url: str, **kwargs: Any) -> AsyncioProxy:
142
141
  url_args = parse_proxy_url(url)
143
142
  return cls(*url_args, **kwargs)
@@ -2,13 +2,19 @@ import asyncio
2
2
  import socket
3
3
 
4
4
  from ... import _abc as abc
5
+ from ..._types import ResolvedAddress
5
6
 
6
7
 
7
8
  class Resolver(abc.AsyncResolver):
8
- def __init__(self, loop: asyncio.AbstractEventLoop):
9
+ def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
9
10
  self._loop = loop
10
11
 
11
- async def resolve(self, host, port=0, family=socket.AF_UNSPEC):
12
+ async def resolve(
13
+ self,
14
+ host: str,
15
+ port: int = 0,
16
+ family: socket.AddressFamily = socket.AF_UNSPEC,
17
+ ) -> ResolvedAddress:
12
18
  infos = await self._loop.getaddrinfo(
13
19
  host=host,
14
20
  port=port,
@@ -17,9 +23,10 @@ class Resolver(abc.AsyncResolver):
17
23
  )
18
24
 
19
25
  if not infos: # pragma: no cover
20
- raise OSError('Can`t resolve address {}:{} [{}]'.format(host, port, family))
26
+ raise OSError(f"Can`t resolve address {host}:{port} [{family}]")
21
27
 
28
+ # use IPv4 address first
22
29
  infos = sorted(infos, key=lambda info: info[0])
23
30
 
24
31
  family, _, _, _, address = infos[0]
25
- return family, address[0]
32
+ return family, address[0] # type:ignore[return-value]
@@ -1,36 +1,32 @@
1
1
  import asyncio
2
2
  import socket
3
3
 
4
- from ..._errors import ProxyError
5
-
6
4
  from ... import _abc as abc
5
+ from ..._errors import IncompleteReadError
7
6
 
8
7
  DEFAULT_RECEIVE_SIZE = 65536
9
8
 
10
9
 
11
10
  class AsyncioSocketStream(abc.AsyncSocketStream):
12
- _loop: asyncio.AbstractEventLoop = None
13
- _socket = None
14
-
15
- def __init__(self, sock: socket.socket, loop: asyncio.AbstractEventLoop):
11
+ def __init__(self, sock: socket.socket, loop: asyncio.AbstractEventLoop) -> None:
16
12
  self._loop = loop
17
13
  self._socket = sock
18
14
 
19
- async def write_all(self, data):
15
+ async def write(self, data: bytes) -> None:
20
16
  await self._loop.sock_sendall(self._socket, data)
21
17
 
22
- async def read(self, max_bytes=DEFAULT_RECEIVE_SIZE):
18
+ async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE) -> bytes:
23
19
  return await self._loop.sock_recv(self._socket, max_bytes)
24
20
 
25
- async def read_exact(self, n):
21
+ async def read_exactly(self, n: int) -> bytes:
26
22
  data = bytearray()
27
23
  while len(data) < n:
28
24
  packet = await self._loop.sock_recv(self._socket, n - len(data))
29
25
  if not packet: # pragma: no cover
30
- raise ProxyError('Connection closed unexpectedly')
31
- data += packet
32
- return data
26
+ raise IncompleteReadError("Connection closed unexpectedly")
27
+ data.extend(packet)
28
+ return data # type:ignore[return-value]
33
29
 
34
- async def close(self):
30
+ async def close(self) -> None:
35
31
  if self._socket is not None:
36
32
  self._socket.close()
@@ -1,4 +1,3 @@
1
1
  from ._proxy import AsyncioProxy as Proxy
2
- from ._chain import ProxyChain
3
2
 
4
- __all__ = ('Proxy', 'ProxyChain')
3
+ __all__ = ("Proxy",)
@@ -1,5 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
- from typing import Optional, Tuple
4
+ from typing import Any
5
+
3
6
  from ._stream import AsyncioSocketStream
4
7
 
5
8
 
@@ -7,16 +10,16 @@ async def connect_tcp(
7
10
  host: str,
8
11
  port: int,
9
12
  loop: asyncio.AbstractEventLoop,
10
- local_addr: Optional[Tuple[str, int]] = None,
13
+ local_addr: tuple[str, int] | None = None,
11
14
  ) -> AsyncioSocketStream:
12
- kwargs = {}
15
+ kwargs: dict[str, Any] = {}
13
16
  if local_addr is not None:
14
- kwargs['local_addr'] = local_addr # pragma: no cover
17
+ kwargs["local_addr"] = local_addr # pragma: no cover
15
18
 
16
19
  reader, writer = await asyncio.open_connection(
17
20
  host=host,
18
21
  port=port,
19
- **kwargs, # type: ignore
22
+ **kwargs,
20
23
  )
21
24
 
22
25
  return AsyncioSocketStream(