Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/werkzeug/wsgi.py: 26%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

239 statements  

1from __future__ import annotations 

2 

3import io 

4import typing as t 

5import wsgiref.types as wt 

6from functools import partial 

7from functools import update_wrapper 

8 

9from ._internal import _wsgi_decoding_dance 

10from .exceptions import ClientDisconnected 

11from .exceptions import RequestEntityTooLarge 

12from .sansio import utils as _sansio_utils 

13 

14 

15def _responder(f: t.Callable[..., wt.WSGIApplication]) -> wt.WSGIApplication: 

16 """Marks a function as responder. Decorate a function with it and it 

17 will automatically call the return value as WSGI application. 

18 

19 Example:: 

20 

21 @responder 

22 def application(environ, start_response): 

23 return Response('Hello World!') 

24 

25 .. deprecated:: 3.2 

26 Will be removed in Werkzeug 4.0. Use ``Request.application`` instead. 

27 """ 

28 return update_wrapper(lambda *a: f(*a)(*a[-2:]), f) 

29 

30 

31def _get_current_url( 

32 environ: wt.WSGIEnvironment, 

33 root_only: bool = False, 

34 strip_querystring: bool = False, 

35 host_only: bool = False, 

36 trusted_hosts: t.Collection[str] | None = None, 

37) -> str: 

38 """Recreate the URL for a request from the parts in a WSGI 

39 environment. 

40 

41 The URL is an IRI, not a URI, so it may contain Unicode characters. 

42 Use :func:`~werkzeug.urls.iri_to_uri` to convert it to ASCII. 

43 

44 :param environ: The WSGI environment to get the URL parts from. 

45 :param root_only: Only build the root path, don't include the 

46 remaining path or query string. 

47 :param strip_querystring: Don't include the query string. 

48 :param host_only: Only build the scheme and host. 

49 :param trusted_hosts: A list of trusted host names to validate the 

50 host against. 

51 

52 .. deprecated:: 3.2 

53 Will be removed in Werkzeug 4.0. Use ``request.url``, ``base_url``, 

54 ``root_url``, or ``host_url`` instead. 

55 """ 

56 parts = { 

57 "scheme": environ["wsgi.url_scheme"], 

58 "host": _get_host(environ, trusted_hosts), 

59 } 

60 

61 if not host_only: 

62 parts["root_path"] = environ.get("SCRIPT_NAME", "") 

63 

64 if not root_only: 

65 parts["path"] = environ.get("PATH_INFO", "") 

66 

67 if not strip_querystring: 

68 parts["query_string"] = environ.get("QUERY_STRING", "").encode("latin1") 

69 

70 return _sansio_utils.get_current_url(**parts) 

71 

72 

73def _get_server( 

74 environ: wt.WSGIEnvironment, 

75) -> tuple[str, int | None] | None: 

76 name = environ.get("SERVER_NAME") 

77 

78 if name is None: 

79 return None 

80 

81 try: 

82 port: int | None = int(environ.get("SERVER_PORT", None)) # type: ignore[arg-type] 

83 except (TypeError, ValueError): 

84 # unix socket 

85 port = None 

86 

87 return name, port 

88 

89 

90def _get_host( 

91 environ: wt.WSGIEnvironment, trusted_hosts: t.Collection[str] | None = None 

92) -> str: 

93 """Get and validate a request's ``host:port`` based on the values in the 

94 given WSGI environ. 

95 

96 The ``Host`` header sent by the client is preferred. Otherwise, the server's 

97 configured address is used. If the server address is a Unix socket, it is 

98 ignored. The port is omitted if it matches the standard HTTP or HTTPS ports. 

99 

100 The value is passed through :func:`host_is_trusted`. The host must be made 

101 up of valid characters, but this does not check validity beyond that. If a 

102 list of trusted domains is given, the domain must match one. 

103 

104 :param environ: The WSGI environ. 

105 :param trusted_hosts: A list of trusted domains to match. These should 

106 already be IDNA encoded, but will be encoded if needed. The port is 

107 ignored for this check. If a name starts with a dot it will match as a 

108 suffix, accepting all subdomains. If empty or ``None``, all domains are 

109 allowed. 

110 

111 :return: Host, with port if necessary. 

112 :raise .SecurityError: If the host is not trusted. 

113 

114 .. deprecated:: 3.2 

115 Will be removed in Werkzeug 4.0. Use ``Request.trusted_hosts`` and 

116 ``Request.host`` instead. 

117 

118 .. versionchanged:: 3.2 

119 When using the server address, Unix sockets are ignored. 

120 

121 .. versionchanged:: 3.1.8 

122 The empty string is again returned if no host header value is available, 

123 or if the characters are invalid. 

124 

125 .. versionchanged:: 3.1.7 

126 The characters of the host value are validated. The empty string is no 

127 longer allowed if no header value is available. 

128 

129 .. versionchanged:: 3.1.3 

130 If ``SERVER_NAME`` is IPv6, it is wrapped in ``[]``. 

131 """ 

132 return _sansio_utils.get_host( 

133 environ["wsgi.url_scheme"], 

134 environ.get("HTTP_HOST"), 

135 _get_server(environ), 

136 trusted_hosts, 

137 ) 

138 

139 

140def _get_content_length(environ: wt.WSGIEnvironment) -> int | None: 

141 """Return the ``Content-Length`` header value as an int. If the header is not given 

142 or the ``Transfer-Encoding`` header is ``chunked``, ``None`` is returned to indicate 

143 a streaming request. If the value is not an integer, or negative, 0 is returned. 

144 

145 :param environ: The WSGI environ to get the content length from. 

146 

147 .. deprecated:: 3.2 

148 Will be removed in Werkzeug 4.0. Use ``Request.content_length`` instead. 

149 

150 .. versionadded:: 0.9 

151 """ 

152 return _sansio_utils._get_content_length( 

153 http_content_length=environ.get("CONTENT_LENGTH"), 

154 http_transfer_encoding=environ.get("HTTP_TRANSFER_ENCODING"), 

155 ) 

156 

157 

158def _get_input_stream( 

159 environ: wt.WSGIEnvironment, 

160 safe_fallback: bool = True, 

161 max_content_length: int | None = None, 

162) -> t.IO[bytes]: 

163 """Return the WSGI input stream, wrapped so that it may be read safely 

164 without going past the ``Content-Length`` header value or 

165 ``max_content_length``. This must be used to pass a safe stream to other 

166 functions, which are not responsible for enforcing this limit. 

167 

168 If ``Content-Length`` exceeds ``max_content_length``, a 

169 :exc:`RequestEntityTooLarge`` ``413 Content Too Large`` error is raised. 

170 

171 If the WSGI server sets ``environ["wsgi.input_terminated"]``, it indicates that the 

172 server handles terminating the stream, so it is safe to read directly. For example, 

173 a server that knows how to handle chunked requests safely would set this. 

174 

175 If ``max_content_length`` is set, it can be enforced on streams if 

176 ``wsgi.input_terminated`` is set. Otherwise, an empty stream is returned unless the 

177 user explicitly disables this safe fallback. 

178 

179 If the limit is reached before the underlying stream is exhausted (such as a file 

180 that is too large, or an infinite stream), the remaining contents of the stream 

181 cannot be read safely. Depending on how the server handles this, clients may show a 

182 "connection reset" failure instead of seeing the 413 response. 

183 

184 :param environ: The WSGI environ containing the stream. 

185 :param safe_fallback: Return an empty stream when ``Content-Length`` is not set. 

186 Disabling this allows infinite streams, which can be a denial-of-service risk. 

187 :param max_content_length: The maximum length that content-length or streaming 

188 requests may not exceed. 

189 

190 .. deprecated:: 3.2 

191 Will be removed in Werkzeug 4.0. Use ``request.stream`` instead. 

192 

193 .. versionchanged:: 2.3.2 

194 ``max_content_length`` is only applied to streaming requests if the server sets 

195 ``wsgi.input_terminated``. 

196 

197 .. versionchanged:: 2.3 

198 Check ``max_content_length`` and raise an error if it is exceeded. 

199 

200 .. versionadded:: 0.9 

201 """ 

202 from .wrappers.request import Request 

203 

204 request = Request(environ) 

205 request.max_content_length = max_content_length 

206 stream = request.stream 

207 

208 if ( 

209 not safe_fallback 

210 and isinstance(stream, io.BytesIO) 

211 and stream.getbuffer().nbytes == 0 

212 ): 

213 return environ["wsgi.input_stream"] # type: ignore[no-any-return] 

214 

215 return stream 

216 

217 

218def _get_path_info(environ: wt.WSGIEnvironment) -> str: 

219 """Return ``PATH_INFO`` from the WSGI environment. 

220 

221 :param environ: WSGI environment to get the path from. 

222 

223 .. deprecated:: 3.2 

224 Will be removed in Werkzeug 4.0. Use ``request.path`` instead. 

225 

226 .. versionchanged:: 3.0 

227 The ``charset`` and ``errors`` parameters were removed. 

228 

229 .. versionadded:: 0.9 

230 """ 

231 return _wsgi_decoding_dance(environ.get("PATH_INFO", "")) 

232 

233 

234class ClosingIterator: 

235 """Wrap an iterable that may or may not have a ``close`` method, adding that 

236 and additional functions to its own ``close`` method. 

237 

238 Rather than using this directly, build a :class:`.Response` and use its 

239 :meth:`~.Response.call_on_close` method to add cleanup functions. It will 

240 handle creating the closing iterator. 

241 

242 If a WSGI application returns an iterable with a ``close`` method, it 

243 will be called by the server at the end of the response. This class can be 

244 used to add additional cleanup when receiving an iterable from some other 

245 code. 

246 

247 This does not handle the case where an exception interrupts the application 

248 before it returns the iterable. A higher level wrapper to handle safe 

249 execution and resource cleanup is needed. 

250 """ 

251 

252 def __init__( 

253 self, 

254 iterable: t.Iterable[bytes], 

255 callbacks: None 

256 | (t.Callable[[], None] | t.Iterable[t.Callable[[], None]]) = None, 

257 ) -> None: 

258 iterator = iter(iterable) 

259 self._next = t.cast(t.Callable[[], bytes], partial(next, iterator)) 

260 if callbacks is None: 

261 callbacks = [] 

262 elif callable(callbacks): 

263 callbacks = [callbacks] 

264 else: 

265 callbacks = list(callbacks) 

266 iterable_close = getattr(iterable, "close", None) 

267 if iterable_close: 

268 callbacks.insert(0, iterable_close) 

269 self._callbacks = callbacks 

270 

271 def __iter__(self) -> ClosingIterator: 

272 return self 

273 

274 def __next__(self) -> bytes: 

275 return self._next() 

276 

277 def close(self) -> None: 

278 for callback in self._callbacks: 

279 callback() 

280 

281 

282def _wrap_file( 

283 environ: wt.WSGIEnvironment, file: t.IO[bytes], buffer_size: int = 128 * 1024 

284) -> t.Iterable[bytes]: 

285 """Wrap a file with the ``wsgi.file_wrapper`` provided by the WSGI server. 

286 The WSGI server provides this if it has a way to send file data more 

287 efficiently. If it's not provided, uses a basic wrapper for consistent 

288 iteration. 

289 

290 You'll also want to set :attr:`.Response.direct_passthrough` to ``True`` to 

291 tell the response not to do its own iteration. 

292 

293 Use :attr:`send_file` instead of generating a file response manually. It 

294 will handle caching, range requests, wrapping, and more. 

295 

296 :param file: A file-like object in ``rb`` mode. 

297 :param buffer_size: Number of bytes to read for each iteration. 

298 

299 .. deprecated:: 3.2 

300 Will be removed in Werkzeug 4.0. Use ``Request.wrap_file`` instead. 

301 

302 .. versionchanged:: 3.2 

303 Increase default ``buffer_size`` from 8 KiB to 128 KiB. 

304 

305 .. versionadded:: 0.5 

306 """ 

307 cls: wt.FileWrapper = environ.get("wsgi.file_wrapper", _FileWrapper) 

308 return cls(file, buffer_size) 

309 

310 

311class _FileWrapper: 

312 """Iterate over a binary file in predictable chunks instead of arbitrary 

313 lines. Used as the default by :func:`wrap_file` if the WSGI server does not 

314 provide its own wrapper. 

315 

316 :param file: A file in ``rb`` mode. 

317 :param buffer_size: Number of bytes to read for each iteration. 

318 

319 .. deprecated:: 3.2 

320 Will be removed in Werkzeug 4.0. Use ``wrap_file`` instead. 

321 

322 .. versionchanged:: 3.2 

323 Increase default ``buffer_size`` from 8 KiB to 128 KiB. 

324 

325 .. versionadded:: 0.5 

326 """ 

327 

328 def __init__(self, file: t.IO[bytes], buffer_size: int = 128 * 1024) -> None: 

329 self.file = file 

330 self.buffer_size = buffer_size 

331 

332 def close(self) -> None: 

333 try: 

334 self.file.close() 

335 except AttributeError: 

336 pass 

337 

338 def __iter__(self) -> t.Iterator[bytes]: 

339 return self 

340 

341 def __next__(self) -> bytes: 

342 if data := self.file.read(self.buffer_size): 

343 return data 

344 

345 raise StopIteration() 

346 

347 

348class _RangeWrapper: 

349 """This class can be used to convert an iterable object into 

350 an iterable that will only yield a piece of the underlying content. 

351 It yields blocks until the underlying stream range is fully read. 

352 The yielded blocks will have a size that can't exceed the original 

353 iterator defined block size, but that can be smaller. 

354 

355 If you're using this object together with a :class:`Response` you have 

356 to use the `direct_passthrough` mode. 

357 

358 :param iterable: an iterable object with a :meth:`__next__` method. 

359 :param start_byte: byte from which read will start. 

360 :param byte_range: how many bytes to read. 

361 """ 

362 

363 def __init__( 

364 self, 

365 iterable: t.Iterable[bytes] | t.IO[bytes], 

366 start_byte: int = 0, 

367 byte_range: int | None = None, 

368 ): 

369 self.iterable = iter(iterable) 

370 self.byte_range = byte_range 

371 self.start_byte = start_byte 

372 self.end_byte = None 

373 

374 if byte_range is not None: 

375 self.end_byte = start_byte + byte_range 

376 

377 self.read_length = 0 

378 self.seekable = hasattr(iterable, "seekable") and iterable.seekable() 

379 self.end_reached = False 

380 

381 def __iter__(self) -> _RangeWrapper: 

382 return self 

383 

384 def _next_chunk(self) -> bytes: 

385 try: 

386 chunk = next(self.iterable) 

387 self.read_length += len(chunk) 

388 return chunk 

389 except StopIteration: 

390 self.end_reached = True 

391 raise 

392 

393 def _first_iteration(self) -> tuple[bytes | None, int]: 

394 chunk = None 

395 if self.seekable: 

396 self.iterable.seek(self.start_byte) # type: ignore[attr-defined] 

397 self.read_length = self.iterable.tell() # type: ignore[attr-defined] 

398 contextual_read_length = self.read_length 

399 else: 

400 while self.read_length <= self.start_byte: 

401 chunk = self._next_chunk() 

402 if chunk is not None: 

403 chunk = chunk[self.start_byte - self.read_length :] 

404 contextual_read_length = self.start_byte 

405 return chunk, contextual_read_length 

406 

407 def _next(self) -> bytes: 

408 if self.end_reached: 

409 raise StopIteration() 

410 chunk = None 

411 contextual_read_length = self.read_length 

412 if self.read_length == 0: 

413 chunk, contextual_read_length = self._first_iteration() 

414 if chunk is None: 

415 chunk = self._next_chunk() 

416 if self.end_byte is not None and self.read_length >= self.end_byte: 

417 self.end_reached = True 

418 return chunk[: self.end_byte - contextual_read_length] 

419 return chunk 

420 

421 def __next__(self) -> bytes: 

422 chunk = self._next() 

423 if chunk: 

424 return chunk 

425 self.end_reached = True 

426 raise StopIteration() 

427 

428 def close(self) -> None: 

429 if hasattr(self.iterable, "close"): 

430 self.iterable.close() 

431 

432 

433class _LimitedStream(io.RawIOBase): 

434 """Wrap a stream so that it doesn't read more than a given limit. This is 

435 used to limit ``wsgi.input`` to the ``Content-Length`` header value or 

436 :attr:`.Request.max_content_length`. 

437 

438 If the limit is reached before the underlying stream is exhausted (such as a 

439 file that is too large, or an infinite stream), the remaining contents of 

440 the stream cannot be read safely. Depending on how the server handles this, 

441 clients may show a "connection reset" failure instead of seeing the 413 

442 response. 

443 

444 :param stream: The stream to read from. Must be a readable binary IO object. 

445 :param limit: The limit in bytes to not read past. Should be either the 

446 ``Content-Length`` header value or ``request.max_content_length``. 

447 :param is_max: Whether the given ``limit`` is ``request.max_content_length`` 

448 instead of the ``Content-Length`` header value. This changes how 

449 exhausted and disconnect events are handled. 

450 :raises .RequestEntityTooLarge: Attempted to read after a max limit. 

451 :raises .ClientDisconnected: Reading returned zero bytes or raised an error, 

452 when the limit is not a max. 

453 

454 .. deprecated:: 3.2 

455 Will be private in Werkzeug 4.0. Use ``Request.stream`` instead. 

456 

457 .. versionchanged:: 2.3 

458 Handle ``max_content_length`` differently than ``Content-Length``. 

459 

460 .. versionchanged:: 2.3 

461 Implements ``io.RawIOBase`` rather than ``io.IOBase``. 

462 """ 

463 

464 def __init__(self, stream: t.IO[bytes], limit: int, is_max: bool = False) -> None: 

465 self._stream = stream 

466 self._pos = 0 

467 self.limit = limit 

468 self._limit_is_max = is_max 

469 

470 @property 

471 def is_exhausted(self) -> bool: 

472 """Whether the current stream position has reached the limit.""" 

473 return self._pos >= self.limit 

474 

475 def on_exhausted(self) -> None: 

476 """Called when attempting to read after the limit has been reached. If 

477 the limit is a maximum, raises :exc:`.RequestEntityTooLarge`. 

478 

479 .. versionchanged:: 2.3 

480 Raises ``RequestEntityTooLarge`` if the limit is a maximum. 

481 

482 .. versionchanged:: 2.3 

483 Any return value is ignored. 

484 """ 

485 if self._limit_is_max: 

486 raise RequestEntityTooLarge() 

487 

488 def on_disconnect(self, error: Exception | None = None) -> None: 

489 """Called when an attempted read receives zero bytes before the limit 

490 was reached. This indicates that the client disconnected before sending 

491 the full request body. Raises :exc:`.ClientDisconnected`, unless the 

492 limit is a maximum and no error was raised. 

493 

494 .. versionchanged:: 2.3 

495 Added the ``error`` parameter. Do nothing if the limit is a maximum and no 

496 error was raised. 

497 

498 .. versionchanged:: 2.3 

499 Any return value is ignored. 

500 """ 

501 if not self._limit_is_max or error is not None: 

502 raise ClientDisconnected() 

503 

504 # If the limit is a maximum, then we may have read zero bytes because the 

505 # streaming body is complete. There's no way to distinguish that from the 

506 # client disconnecting early. 

507 

508 def exhaust(self) -> bytes: 

509 """Exhaust the stream by reading until the limit is reached or the client 

510 disconnects, returning the remaining data. 

511 

512 .. deprecated:: 3.2 

513 Will be removed in Werkzeug 4.0. 

514 

515 .. versionchanged:: 2.3 

516 Return the remaining data. 

517 

518 .. versionchanged:: 2.2.3 

519 Handle case where wrapped stream returns fewer bytes than requested. 

520 """ 

521 if not self.is_exhausted: 

522 return self.readall() 

523 

524 return b"" 

525 

526 def readinto(self, b: bytearray) -> int | None: # type: ignore[override] 

527 size = len(b) 

528 remaining = self.limit - self._pos 

529 

530 if remaining <= 0: 

531 self.on_exhausted() 

532 return 0 

533 

534 if hasattr(self._stream, "readinto"): 

535 # Use stream.readinto if it's available. 

536 if size <= remaining: 

537 # The size fits in the remaining limit, use the buffer directly. 

538 try: 

539 out_size: int | None = self._stream.readinto(b) 

540 except (OSError, ValueError) as e: 

541 self.on_disconnect(error=e) 

542 return 0 

543 else: 

544 # Use a temp buffer with the remaining limit as the size. 

545 temp_b = bytearray(remaining) 

546 

547 try: 

548 out_size = self._stream.readinto(temp_b) 

549 except (OSError, ValueError) as e: 

550 self.on_disconnect(error=e) 

551 return 0 

552 

553 if out_size: 

554 # Need to slice both sides. Math here is complicated. 

555 # size=10, remaining=5, out_size=3, remaining > out_size 

556 # Without slicing temp_b (b[:s] = tb), would try to 

557 # put 5 bytes into 3 bytes, causing b to resize to 12. 

558 b[:out_size] = temp_b[:out_size] 

559 else: 

560 # WSGI requires that stream.read is available. 

561 try: 

562 data = self._stream.read(min(size, remaining)) 

563 except (OSError, ValueError) as e: 

564 self.on_disconnect(error=e) 

565 return 0 

566 

567 out_size = len(data) 

568 b[:out_size] = data 

569 

570 if not out_size: 

571 # Read zero bytes from the stream. 

572 self.on_disconnect() 

573 return 0 

574 

575 self._pos += out_size 

576 return out_size 

577 

578 def readall(self) -> bytes: 

579 if self.is_exhausted: 

580 self.on_exhausted() 

581 return b"" 

582 

583 out = bytearray() 

584 

585 # The parent implementation uses "while True", which results in an extra read. 

586 while not self.is_exhausted: 

587 data = self.read(1024 * 64) 

588 

589 # Stream may return empty before a max limit is reached. 

590 if not data: 

591 break 

592 

593 out.extend(data) 

594 

595 return bytes(out) 

596 

597 def tell(self) -> int: 

598 """Return the current stream position. 

599 

600 .. versionadded:: 0.9 

601 """ 

602 return self._pos 

603 

604 def readable(self) -> bool: 

605 return True 

606 

607 

608if not t.TYPE_CHECKING: 

609 

610 def __getattr__(name: str) -> t.Any: 

611 import warnings 

612 

613 if name == "responder": 

614 warnings.warn( 

615 "'responder' is deprecated and will be removed in Werkzeug 4.0. Use" 

616 " 'Request.application' instead.", 

617 DeprecationWarning, 

618 stacklevel=2, 

619 ) 

620 return _responder 

621 

622 if name == "FileWrapper": 

623 warnings.warn( 

624 "'FileWrapper' is deprecated and will be removed in Werkzeug 4.0. Use" 

625 " 'wrap_file' instead.", 

626 DeprecationWarning, 

627 stacklevel=2, 

628 ) 

629 return _FileWrapper 

630 

631 if name == "get_input_stream": 

632 warnings.warn( 

633 "The 'get_input_stream' function is deprecated and will be removed in" 

634 " Werkzeug 4.0. Use 'request.stream' instead.", 

635 DeprecationWarning, 

636 stacklevel=2, 

637 ) 

638 return _get_input_stream 

639 

640 if name == "LimitedStream": 

641 warnings.warn( 

642 "The 'LimitedStream' class is deprecated and will be private in" 

643 " Werkzeug 4.0. Use 'request.stream' instead.", 

644 DeprecationWarning, 

645 stacklevel=2, 

646 ) 

647 return _LimitedStream 

648 

649 if name == "get_content_length": 

650 warnings.warn( 

651 "The 'get_content_length' function is deprecated and will be removed in" 

652 " Werkzeug 4.0. Use 'Request.content_length' instead.", 

653 DeprecationWarning, 

654 stacklevel=2, 

655 ) 

656 return _get_content_length 

657 

658 if name == "get_path_info": 

659 warnings.warn( 

660 "The 'get_path_info' function is deprecated and will be removed in" 

661 " Werkzeug 4.0. Use 'Request.path' instead.", 

662 DeprecationWarning, 

663 stacklevel=2, 

664 ) 

665 return _get_path_info 

666 

667 if name == "get_current_url": 

668 warnings.warn( 

669 "The 'get_current_url' function is deprecated and will be" 

670 " removed in Werkzeug 4.0. Use 'Request.url', 'base_url'," 

671 " 'root_url', or 'host_url' instead.", 

672 DeprecationWarning, 

673 stacklevel=2, 

674 ) 

675 return _get_current_url 

676 

677 if name == "get_host": 

678 warnings.warn( 

679 "The 'get_host' function is deprecated and will be removed in Werkzeug" 

680 " 4.0. Use 'Request.host' instead.", 

681 DeprecationWarning, 

682 stacklevel=2, 

683 ) 

684 return _get_host 

685 

686 if name == "host_is_trusted": 

687 from .sansio.utils import host_is_trusted 

688 

689 warnings.warn( 

690 "The 'host_is_trusted' function is deprecated and will be" 

691 " removed in Werkzeug 4.0. Use 'Request.trusted_hosts' and" 

692 " 'Request.host' instead.", 

693 DeprecationWarning, 

694 stacklevel=2, 

695 ) 

696 return host_is_trusted 

697 

698 if name == "wrap_file": 

699 warnings.warn( 

700 "The 'wrap_file' function is deprecated and will be removed in" 

701 " Werkzeug 4.0. Use 'Request.wrap_file' instead.", 

702 DeprecationWarning, 

703 stacklevel=2, 

704 ) 

705 return _wrap_file 

706 

707 raise AttributeError(name)