1+ from __future__ import annotations
2+
13import ipaddress
24import itertools
35import socket
46from collections import defaultdict
7+ from collections .abc import Iterator
58from dataclasses import dataclass , field
9+ from typing import Any
610
711import pytest
812
1115
1216
1317class SocketBlockedError (RuntimeError ):
14- def __init__ (self , * _args , ** _kwargs ) :
18+ def __init__ (self , * _args : Any , ** _kwargs : Any ) -> None :
1519 super ().__init__ ("A test tried to use socket.socket." )
1620
1721
1822class SocketConnectBlockedError (RuntimeError ):
19- def __init__ (self , allowed , host , * _args , ** _kwargs ):
20- if allowed :
21- allowed = "," .join (allowed )
23+ def __init__ (
24+ self ,
25+ allowed : list [str ],
26+ host : str | None ,
27+ * _args : Any ,
28+ ** _kwargs : Any ,
29+ ) -> None :
30+ allowed_str = "," .join (allowed )
2231 super ().__init__ (
2332 "A test tried to use socket.socket.connect() "
24- f'with host "{ host } " (allowed: "{ allowed } ").'
33+ f'with host "{ host } " (allowed: "{ allowed_str } ").'
2534 )
2635
2736
28- def pytest_addoption (parser ) :
37+ def pytest_addoption (parser : pytest . Parser ) -> None :
2938 group = parser .getgroup ("socket" )
3039 group .addoption (
3140 "--disable-socket" ,
@@ -54,15 +63,15 @@ def pytest_addoption(parser):
5463
5564
5665@pytest .fixture
57- def socket_disabled (pytestconfig ) :
66+ def socket_disabled (pytestconfig : pytest . Config ) -> Iterator [ None ] :
5867 """disable socket.socket for duration of this test function"""
5968 socket_config = pytestconfig .stash [_STASH_KEY ]
6069 disable_socket (allow_unix_socket = socket_config .allow_unix_socket )
6170 yield
6271
6372
6473@pytest .fixture
65- def socket_enabled (pytestconfig ) :
74+ def socket_enabled (pytestconfig : pytest . Config ) -> Iterator [ None ] :
6675 """enable socket.socket for duration of this test function"""
6776 enable_socket ()
6877 yield
@@ -80,31 +89,37 @@ class _PytestSocketConfig:
8089_STASH_KEY = pytest .StashKey [_PytestSocketConfig ]()
8190
8291
83- def _is_unix_socket (family ) -> bool :
92+ def _is_unix_socket (family : int ) -> bool :
8493 return hasattr (socket , "AF_UNIX" ) and family == socket .AF_UNIX
8594
8695
87- def disable_socket (allow_unix_socket = False ):
96+ def disable_socket (allow_unix_socket : bool = False ) -> None :
8897 """disable socket.socket to disable the Internet. useful in testing."""
8998
9099 class GuardedSocket (socket .socket ):
91100 """socket guard to disable socket creation (from pytest-socket)"""
92101
93- def __new__ (cls , family = - 1 , type = - 1 , proto = - 1 , fileno = None ):
102+ def __new__ (
103+ cls ,
104+ family : socket .AddressFamily | int = - 1 ,
105+ type : socket .SocketKind | int = - 1 ,
106+ proto : int = - 1 ,
107+ fileno : int | None = None ,
108+ ) -> GuardedSocket :
94109 if _is_unix_socket (family ) and allow_unix_socket :
95- return super ().__new__ (cls , family , type , proto , fileno )
110+ return super ().__new__ (cls , family , type , proto , fileno ) # type: ignore[call-arg] # noqa E501
96111
97112 raise SocketBlockedError ()
98113
99- socket .socket = GuardedSocket
114+ socket .socket = GuardedSocket # type: ignore[misc]
100115
101116
102- def enable_socket ():
117+ def enable_socket () -> None :
103118 """re-enable socket.socket to enable the Internet. useful in testing."""
104- socket .socket = _true_socket
119+ socket .socket = _true_socket # type: ignore[misc]
105120
106121
107- def pytest_configure (config ) :
122+ def pytest_configure (config : pytest . Config ) -> None :
108123 config .addinivalue_line (
109124 "markers" , "disable_socket(): Disable socket connections for a specific test"
110125 )
@@ -125,7 +140,7 @@ def pytest_configure(config):
125140 )
126141
127142
128- def pytest_runtest_setup (item ) -> None :
143+ def pytest_runtest_setup (item : pytest . Item ) -> None :
129144 """During each test item's setup phase,
130145 choose the behavior based on the configurations supplied.
131146
@@ -166,7 +181,7 @@ def pytest_runtest_setup(item) -> None:
166181 disable_socket (socket_config .allow_unix_socket )
167182
168183
169- def _resolve_allow_hosts (item ) :
184+ def _resolve_allow_hosts (item : pytest . Item ) -> str | list [ str ] | None :
170185 """Resolve `allow_hosts` behaviors."""
171186 socket_config = item .config .stash [_STASH_KEY ]
172187
@@ -186,21 +201,23 @@ def _resolve_allow_hosts(item):
186201 return hosts
187202
188203
189- def pytest_runtest_teardown ():
204+ def pytest_runtest_teardown () -> None :
190205 _remove_restrictions ()
191206
192207
193- def host_from_address (address ) :
208+ def host_from_address (address : tuple [ Any , ...]) -> str | None :
194209 host = address [0 ]
195210 if isinstance (host , str ):
196211 return host
212+ return None
197213
198214
199- def host_from_connect_args (args ) :
215+ def host_from_connect_args (args : tuple [ Any , ...]) -> str | None :
200216 address = args [0 ]
201217
202218 if isinstance (address , tuple ):
203219 return host_from_address (address )
220+ return None
204221
205222
206223def is_ipaddress (address : str ) -> bool :
@@ -217,15 +234,16 @@ def is_ipaddress(address: str) -> bool:
217234def resolve_hostnames (hostname : str ) -> set [str ]:
218235 try :
219236 return {
220- addr_struct [0 ] for * _ , addr_struct in socket .getaddrinfo (hostname , None )
237+ addr_struct [0 ] # type: ignore[misc]
238+ for * _ , addr_struct in socket .getaddrinfo (hostname , None )
221239 }
222240 except socket .gaierror :
223241 return set ()
224242
225243
226244def normalize_allowed_hosts (
227245 allowed_hosts : list [str ],
228- resolution_cache : dict [str , list [str ]] | None = None ,
246+ resolution_cache : dict [str , set [str ]] | None = None ,
229247) -> dict [str , set [str ]]:
230248 """Map all items in `allowed_hosts` to IP addresses."""
231249 if resolution_cache is None :
@@ -246,7 +264,7 @@ def normalize_allowed_hosts(
246264def socket_allow_hosts (
247265 allowed : str | list [str ] | None = None ,
248266 allow_unix_socket : bool = False ,
249- resolution_cache : dict [str , list [str ]] | None = None ,
267+ resolution_cache : dict [str , set [str ]] | None = None ,
250268) -> None :
251269 """disable socket.socket.connect() to disable the Internet. useful in testing."""
252270 if isinstance (allowed , str ):
@@ -270,7 +288,7 @@ def socket_allow_hosts(
270288 ]
271289 )
272290
273- def guarded_connect (inst , * args ) :
291+ def guarded_connect (inst : socket . socket , * args : Any ) -> None :
274292 host = host_from_connect_args (args )
275293 if host in allowed_ip_hosts_and_hostnames or (
276294 _is_unix_socket (inst .family ) and allow_unix_socket
@@ -279,10 +297,10 @@ def guarded_connect(inst, *args):
279297
280298 raise SocketConnectBlockedError (allowed_list , host )
281299
282- socket .socket .connect = guarded_connect
300+ socket .socket .connect = guarded_connect # type: ignore[assignment,method-assign]
283301
284302
285- def _remove_restrictions ():
303+ def _remove_restrictions () -> None :
286304 """restore socket.socket.* to allow access to the Internet. useful in testing."""
287- socket .socket = _true_socket
288- socket .socket .connect = _true_connect
305+ socket .socket = _true_socket # type: ignore[misc]
306+ socket .socket .connect = _true_connect # type: ignore[method-assign]
0 commit comments