Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions 17 Doc/library/urllib.request.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,23 @@ The following example uses no proxies at all, overriding environment settings::
... f.read().decode('utf-8')
...

.. _urllib-request-cli:

:mod:`urllib.request` can also be invoked directly using the :option:`-m`
switch of the interpreter with an ``URL`` argument::

python -m urllib.request https://python.org/

By default, the downloaded data is printed to stdout. The option ``-o/--output``
specifies an output file where the downloaded data is stored instead of being
printed::

python -m urllib.request https://python.org/ --output python.html

If the output file already exists, its content is overwritten.

.. versionadded:: 3.11


Legacy interface
----------------
Expand Down
35 changes: 35 additions & 0 deletions 35 Lib/test/test_urllib2_localnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import threading
import unittest
import hashlib
import subprocess
import sys
import tempfile

from test.support import hashlib_helper
from test.support import threading_helper
Expand Down Expand Up @@ -660,6 +663,38 @@ def test_line_iteration(self):
(index, len(lines[index]), len(line)))
self.assertEqual(index + 1, len(lines))

def test_download_to_stdout(self):
content = b"My hovercraft is full of eels."
handler = self.start_server([(200, [], content)])
proc = subprocess.run(
[sys.executable, "-m", "urllib.request",
f"http://localhost:{handler.port}"],
capture_output=True
)
self.assertEqual(proc.stdout, content)
self.assertEqual(proc.stderr, b"")
Comment thread
pohlt marked this conversation as resolved.

def test_download_to_file(self):
content = b"I will not buy this record; it is scratched."
handler = self.start_server([(200, [], content)]*2)

with tempfile.TemporaryDirectory() as directory:
for option in ["--output", "-o"]:
filename = os.path.join(
directory, f"download-test{option}.txt"
)
proc = subprocess.run(
[sys.executable, "-m", "urllib.request",
f"http://localhost:{handler.port}",
option, filename],
capture_output=True
)
with open(filename, "rb") as f:
file_content = f.read()
self.assertEqual(proc.stdout, b"")
self.assertEqual(proc.stderr, b"")
Comment thread
pohlt marked this conversation as resolved.
self.assertEqual(file_content, content)


def setUpModule():
thread_info = threading_helper.threading_setup()
Expand Down
33 changes: 33 additions & 0 deletions 33 Lib/urllib/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -2781,3 +2781,36 @@ def proxy_bypass(host):
# By default use environment variables
getproxies = getproxies_environment
proxy_bypass = proxy_bypass_environment


def _download():
import argparse

parser = argparse.ArgumentParser(
description="Download the provided URL (FTP/HTTP/HTTPS supported) "
"and print it to stdout by default. If specified, write to OUTPUT "
"instead."
)
parser.add_argument("URL", help="(encoded) URL to download")
parser.add_argument(
"-o",
"--output",
type=argparse.FileType('wb'), default=sys.stdout.buffer,
help="write to OUTPUT instead of stdout"
)
args = parser.parse_args()

buffer = memoryview(bytearray(32768))
try:
with urlopen(args.URL) as response:
while n_bytes_read := response.readinto(buffer):
args.output.write(buffer[:n_bytes_read])
except URLError as exc:
print(f"Error while downloading '{args.URL}': {exc.reason}")

if args.output is not sys.stdout.buffer:
args.output.close()


if __name__ == "__main__":
_download()
6 changes: 3 additions & 3 deletions 6 Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Jason Asbahr
David Ascher
Ammar Askar
Neil Aspinall
Peter Åstrand
Chris AtLee
Aymeric Augustin
Andres Ayala
Expand Down Expand Up @@ -491,6 +492,7 @@ Daniel Ellis
Phil Elson
David Ely
Victor van den Elzen
Vlad Emelianov
Jeff Epler
Tom Epperly
Gökcen Eraslan
Expand Down Expand Up @@ -1384,6 +1386,7 @@ Jean-François Piéronne
Oleg Plakhotnyuk
Anatoliy Platonov
Marcel Plch
Thomas Pohl
Remi Pointel
Jon Poler
Ariel Poliak
Expand Down Expand Up @@ -1998,8 +2001,5 @@ Tarek Ziadé
Jelle Zijlstra
Gennadiy Zlobin
Doug Zongker
Peter Åstrand
Vlad Emelianov
Andrey Doroschenko

(Entries should be added in rough alphabetical order by last names)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The :mod:`urllib.request` module can now download files (e.g.
``python -m urllib.request https://python.org/``). For more
info, see ``python -m urllib.request -h``. Patch by Thomas Pohl.
Morty Proxy This is a proxified and sanitized view of the page, visit original site.