replace calls to print() with stdout_write() (#2529)

This commit is contained in:
Mike Fährmann
2022-05-19 13:24:37 +02:00
parent cf16f9a407
commit 688d6553b4
5 changed files with 68 additions and 43 deletions

View File

@@ -12,7 +12,7 @@ import logging
from . import version, config, option, output, extractor, job, util, exception from . import version, config, option, output, extractor, job, util, exception
__author__ = "Mike Fährmann" __author__ = "Mike Fährmann"
__copyright__ = "Copyright 2014-2021 Mike Fährmann" __copyright__ = "Copyright 2014-2022 Mike Fährmann"
__license__ = "GPLv2" __license__ = "GPLv2"
__maintainer__ = "Mike Fährmann" __maintainer__ = "Mike Fährmann"
__email__ = "mike_faehrmann@web.de" __email__ = "mike_faehrmann@web.de"
@@ -22,10 +22,13 @@ __version__ = version.__version__
def progress(urls, pformat): def progress(urls, pformat):
"""Wrapper around urls to output a simple progress indicator""" """Wrapper around urls to output a simple progress indicator"""
if pformat is True: if pformat is True:
pformat = "[{current}/{total}] {url}" pformat = "[{current}/{total}] {url}\n"
else:
pformat += "\n"
pinfo = {"total": len(urls)} pinfo = {"total": len(urls)}
for pinfo["current"], pinfo["url"] in enumerate(urls, 1): for pinfo["current"], pinfo["url"] in enumerate(urls, 1):
print(pformat.format_map(pinfo), file=sys.stderr) output.stderr_write(pformat.format_map(pinfo))
yield pinfo["url"] yield pinfo["url"]
@@ -196,20 +199,23 @@ def main():
pass pass
if args.list_modules: if args.list_modules:
for module_name in extractor.modules: extractor.modules.append("")
print(module_name) sys.stdout.write("\n".join(extractor.modules))
elif args.list_extractors: elif args.list_extractors:
write = sys.stdout.write
fmt = "{}\n{}\nCategory: {} - Subcategory: {}{}\n\n".format
for extr in extractor.extractors(): for extr in extractor.extractors():
if not extr.__doc__: if not extr.__doc__:
continue continue
print(extr.__name__)
print(extr.__doc__)
print("Category:", extr.category,
"- Subcategory:", extr.subcategory)
test = next(extr._get_tests(), None) test = next(extr._get_tests(), None)
if test: write(fmt(
print("Example :", test[0]) extr.__name__, extr.__doc__,
print() extr.category, extr.subcategory,
"\nExample : " + test[0] if test else "",
))
elif args.clear_cache: elif args.clear_cache:
from . import cache from . import cache
log = logging.getLogger("cache") log = logging.getLogger("cache")

View File

@@ -11,6 +11,7 @@
from .common import Extractor, Message from .common import Extractor, Message
from . import deviantart, flickr, mastodon, pixiv, reddit, smugmug, tumblr from . import deviantart, flickr, mastodon, pixiv, reddit, smugmug, tumblr
from .. import text, oauth, util, config, exception from .. import text, oauth, util, config, exception
from ..output import stdout_write
from ..cache import cache from ..cache import cache
import urllib.parse import urllib.parse
import hashlib import hashlib
@@ -37,7 +38,7 @@ class OAuthBase(Extractor):
def recv(self): def recv(self):
"""Open local HTTP server and recv callback parameters""" """Open local HTTP server and recv callback parameters"""
import socket import socket
print("Waiting for response. (Cancel with Ctrl+c)") stdout_write("Waiting for response. (Cancel with Ctrl+c)\n")
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("localhost", self.config("port", 6414))) server.bind(("localhost", self.config("port", 6414)))
@@ -60,7 +61,7 @@ class OAuthBase(Extractor):
def send(self, msg): def send(self, msg):
"""Send 'msg' to the socket opened in 'recv()'""" """Send 'msg' to the socket opened in 'recv()'"""
print(msg) stdout_write(msg)
self.client.send(b"HTTP/1.1 200 OK\r\n\r\n" + msg.encode()) self.client.send(b"HTTP/1.1 200 OK\r\n\r\n" + msg.encode())
self.client.close() self.client.close()
@@ -69,12 +70,13 @@ class OAuthBase(Extractor):
import webbrowser import webbrowser
url += "?" + urllib.parse.urlencode(params) url += "?" + urllib.parse.urlencode(params)
if not self.config("browser", True) or not webbrowser.open(url): if not self.config("browser", True) or not webbrowser.open(url):
print("Please open this URL in your browser:") stdout_write(
print(url, end="\n\n", flush=True) "Please open this URL in your browser:\n\n" + url + "\n\n")
return (recv or self.recv)() return (recv or self.recv)()
def error(self, msg): def error(self, msg):
return self.send("Remote server reported an error:\n\n" + str(msg)) return self.send(
"Remote server reported an error:\n\n{}\n".format(msg))
def _oauth1_authorization_flow( def _oauth1_authorization_flow(
self, request_token_url, authorize_url, access_token_url): self, request_token_url, authorize_url, access_token_url):
@@ -133,7 +135,7 @@ class OAuthBase(Extractor):
# check authorization response # check authorization response
if state != params.get("state"): if state != params.get("state"):
self.send("'state' mismatch: expected {}, got {}.".format( self.send("'state' mismatch: expected {}, got {}.\n".format(
state, params.get("state") state, params.get("state")
)) ))
return return
@@ -188,7 +190,7 @@ class OAuthBase(Extractor):
opt = self.oauth_config(names[0]) opt = self.oauth_config(names[0])
if self.cache and (opt is None or opt == "cache"): if self.cache and (opt is None or opt == "cache"):
msg += _vh + " been cached and will automatically be used." msg += _vh + " been cached and will automatically be used.\n"
else: else:
msg += "Put " + _va + " into your configuration file as \n" msg += "Put " + _va + " into your configuration file as \n"
msg += " and\n".join( msg += " and\n".join(
@@ -200,7 +202,7 @@ class OAuthBase(Extractor):
"\nor set\n'extractor.{}.{}' to \"cache\"" "\nor set\n'extractor.{}.{}' to \"cache\""
.format(self.subcategory, names[0]) .format(self.subcategory, names[0])
) )
msg += "\nto use {}.".format(_it) msg += "\nto use {}.\n".format(_it)
return msg return msg
@@ -398,9 +400,9 @@ class OAuthPixiv(OAuthBase):
data = self.session.post(url, headers=headers, data=data).json() data = self.session.post(url, headers=headers, data=data).json()
if "error" in data: if "error" in data:
print(data) stdout_write("\n{}\n".format(data))
if data["error"] in ("invalid_request", "invalid_grant"): if data["error"] in ("invalid_request", "invalid_grant"):
print("'code' expired, try again") stdout_write("'code' expired, try again\n\n")
return return
token = data["refresh_token"] token = data["refresh_token"]
@@ -409,10 +411,10 @@ class OAuthPixiv(OAuthBase):
pixiv._refresh_token_cache.update(username, token) pixiv._refresh_token_cache.update(username, token)
self.log.info("Writing 'refresh-token' to cache") self.log.info("Writing 'refresh-token' to cache")
print(self._generate_message(("refresh-token",), (token,))) stdout_write(self._generate_message(("refresh-token",), (token,)))
def _input(self): def _input(self):
print(""" stdout_write("""\
1) Open your browser's Developer Tools (F12) and switch to the Network tab 1) Open your browser's Developer Tools (F12) and switch to the Network tab
2) Login 2) Login
3) Select the last network monitor entry ('callback?state=...') 3) Select the last network monitor entry ('callback?state=...')
@@ -421,6 +423,7 @@ class OAuthPixiv(OAuthBase):
- This 'code' will expire 30 seconds after logging in. - This 'code' will expire 30 seconds after logging in.
- Copy-pasting more than just the 'code' value will work as well, - Copy-pasting more than just the 'code' value will work as well,
like the entire URL or several query parameters. like the entire URL or several query parameters.
""") """)
code = input("code: ") code = input("code: ")
return code.rpartition("=")[2].strip() return code.rpartition("=")[2].strip()

View File

@@ -16,6 +16,7 @@ import collections
from . import extractor, downloader, postprocessor from . import extractor, downloader, postprocessor
from . import config, text, util, path, formatter, output, exception from . import config, text, util, path, formatter, output, exception
from .extractor.message import Message from .extractor.message import Message
from .output import stdout_write
class Job(): class Job():
@@ -537,14 +538,14 @@ class KeywordJob(Job):
self.private = config.get(("output",), "private") self.private = config.get(("output",), "private")
def handle_url(self, url, kwdict): def handle_url(self, url, kwdict):
print("\nKeywords for filenames and --filter:") stdout_write("\nKeywords for filenames and --filter:\n"
print("------------------------------------") "------------------------------------\n")
self.print_kwdict(kwdict) self.print_kwdict(kwdict)
raise exception.StopExtraction() raise exception.StopExtraction()
def handle_directory(self, kwdict): def handle_directory(self, kwdict):
print("Keywords for directory names:") stdout_write("Keywords for directory names:\n"
print("-----------------------------") "-----------------------------\n")
self.print_kwdict(kwdict) self.print_kwdict(kwdict)
def handle_queue(self, url, kwdict): def handle_queue(self, url, kwdict):
@@ -565,16 +566,17 @@ class KeywordJob(Job):
self.extractor.log.info( self.extractor.log.info(
"Try 'gallery-dl -K \"%s\"' instead.", url) "Try 'gallery-dl -K \"%s\"' instead.", url)
else: else:
print("Keywords for --chapter-filter:") stdout_write("Keywords for --chapter-filter:\n"
print("------------------------------") "------------------------------\n")
self.print_kwdict(kwdict) self.print_kwdict(kwdict)
if extr or self.extractor.categorytransfer: if extr or self.extractor.categorytransfer:
print() stdout_write("\n")
KeywordJob(extr or url, self).run() KeywordJob(extr or url, self).run()
raise exception.StopExtraction() raise exception.StopExtraction()
def print_kwdict(self, kwdict, prefix=""): def print_kwdict(self, kwdict, prefix=""):
"""Print key-value pairs in 'kwdict' with formatting""" """Print key-value pairs in 'kwdict' with formatting"""
write = sys.stdout.write
suffix = "]" if prefix else "" suffix = "]" if prefix else ""
for key, value in sorted(kwdict.items()): for key, value in sorted(kwdict.items()):
if key[0] == "_" and not self.private: if key[0] == "_" and not self.private:
@@ -588,13 +590,13 @@ class KeywordJob(Job):
if value and isinstance(value[0], dict): if value and isinstance(value[0], dict):
self.print_kwdict(value[0], key + "[][") self.print_kwdict(value[0], key + "[][")
else: else:
print(key, "[]", sep="") write(key + "[]\n")
for val in value: for val in value:
print(" -", val) write(" - " + str(val) + "\n")
else: else:
# string or number # string or number
print(key, "\n ", value, sep="") write("{}\n {}\n".format(key, value))
class UrlJob(Job): class UrlJob(Job):
@@ -609,14 +611,14 @@ class UrlJob(Job):
@staticmethod @staticmethod
def handle_url(url, _): def handle_url(url, _):
print(url) stdout_write(url + "\n")
@staticmethod @staticmethod
def handle_url_fallback(url, kwdict): def handle_url_fallback(url, kwdict):
print(url) stdout_write(url + "\n")
if "_fallback" in kwdict: if "_fallback" in kwdict:
for url in kwdict["_fallback"]: for url in kwdict["_fallback"]:
print("|", url) stdout_write("| " + url + "\n")
def handle_queue(self, url, kwdict): def handle_queue(self, url, kwdict):
cls = kwdict.get("_extractor") cls = kwdict.get("_extractor")
@@ -653,15 +655,18 @@ class InfoJob(Job):
return 0 return 0
def _print_multi(self, title, *values): def _print_multi(self, title, *values):
print(title, "\n ", " / ".join(json.dumps(v) for v in values), sep="") stdout_write("{}\n {}\n\n".format(
title, " / ".join(json.dumps(v) for v in values)))
def _print_config(self, title, optname, value): def _print_config(self, title, optname, value):
optval = self.extractor.config(optname, util.SENTINEL) optval = self.extractor.config(optname, util.SENTINEL)
if optval is not util.SENTINEL: if optval is not util.SENTINEL:
print(title, "(custom):\n ", json.dumps(optval)) stdout_write(
print(title, "(default):\n ", json.dumps(value)) "{} (custom):\n {}\n{} (default):\n {}\n\n".format(
title, json.dumps(optval), title, json.dumps(value)))
elif value: elif value:
print(title, "(default):\n ", json.dumps(value)) stdout_write(
"{} (default):\n {}\n\n".format(title, json.dumps(value)))
class DataJob(Job): class DataJob(Job):

View File

@@ -39,8 +39,9 @@ class AppendCommandAction(argparse.Action):
class DeprecatedConfigConstAction(argparse.Action): class DeprecatedConfigConstAction(argparse.Action):
"""Set argparse const values as config values + deprecation warning""" """Set argparse const values as config values + deprecation warning"""
def __call__(self, parser, namespace, values, option_string=None): def __call__(self, parser, namespace, values, option_string=None):
print("warning: {} is deprecated. Use {} instead.".format( sys.stderr.write(
"/".join(self.option_strings), self.choices), file=sys.stderr) "warning: {} is deprecated. Use {} instead.\n".format(
"/".join(self.option_strings), self.choices))
namespace.options.append(((), self.dest, self.const)) namespace.options.append(((), self.dest, self.const))

View File

@@ -149,10 +149,13 @@ class TestInfoJob(TestJob):
self.assertEqual(self._capture_stdout(extr), """\ self.assertEqual(self._capture_stdout(extr), """\
Category / Subcategory Category / Subcategory
"test_category" / "test_subcategory" "test_category" / "test_subcategory"
Filename format (default): Filename format (default):
"test_{filename}.{extension}" "test_{filename}.{extension}"
Directory format (default): Directory format (default):
["{category}"] ["{category}"]
""") """)
def test_custom(self): def test_custom(self):
@@ -165,18 +168,22 @@ Directory format (default):
self.assertEqual(self._capture_stdout(extr), """\ self.assertEqual(self._capture_stdout(extr), """\
Category / Subcategory Category / Subcategory
"test_category" / "test_subcategory" "test_category" / "test_subcategory"
Filename format (custom): Filename format (custom):
"custom" "custom"
Filename format (default): Filename format (default):
"test_{filename}.{extension}" "test_{filename}.{extension}"
Directory format (custom): Directory format (custom):
["custom"] ["custom"]
Directory format (default): Directory format (default):
["{category}"] ["{category}"]
Request interval (custom): Request interval (custom):
321 321
Request interval (default): Request interval (default):
123.456 123.456
""") """)
def test_base_category(self): def test_base_category(self):
@@ -186,10 +193,13 @@ Request interval (default):
self.assertEqual(self._capture_stdout(extr), """\ self.assertEqual(self._capture_stdout(extr), """\
Category / Subcategory / Basecategory Category / Subcategory / Basecategory
"test_category" / "test_subcategory" / "test_basecategory" "test_category" / "test_subcategory" / "test_basecategory"
Filename format (default): Filename format (default):
"test_{filename}.{extension}" "test_{filename}.{extension}"
Directory format (default): Directory format (default):
["{category}"] ["{category}"]
""") """)