From 688d6553b4dd009ddb1a2e50b6d45c9177a782ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20F=C3=A4hrmann?= Date: Thu, 19 May 2022 13:24:37 +0200 Subject: [PATCH] replace calls to print() with stdout_write() (#2529) --- gallery_dl/__init__.py | 30 ++++++++++++++++----------- gallery_dl/extractor/oauth.py | 27 +++++++++++++----------- gallery_dl/job.py | 39 ++++++++++++++++++++--------------- gallery_dl/option.py | 5 +++-- test/test_job.py | 10 +++++++++ 5 files changed, 68 insertions(+), 43 deletions(-) diff --git a/gallery_dl/__init__.py b/gallery_dl/__init__.py index 3ae1ddae..d66e285c 100644 --- a/gallery_dl/__init__.py +++ b/gallery_dl/__init__.py @@ -12,7 +12,7 @@ import logging from . import version, config, option, output, extractor, job, util, exception __author__ = "Mike Fährmann" -__copyright__ = "Copyright 2014-2021 Mike Fährmann" +__copyright__ = "Copyright 2014-2022 Mike Fährmann" __license__ = "GPLv2" __maintainer__ = "Mike Fährmann" __email__ = "mike_faehrmann@web.de" @@ -22,10 +22,13 @@ __version__ = version.__version__ def progress(urls, pformat): """Wrapper around urls to output a simple progress indicator""" if pformat is True: - pformat = "[{current}/{total}] {url}" + pformat = "[{current}/{total}] {url}\n" + else: + pformat += "\n" + pinfo = {"total": len(urls)} 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"] @@ -196,20 +199,23 @@ def main(): pass if args.list_modules: - for module_name in extractor.modules: - print(module_name) + extractor.modules.append("") + sys.stdout.write("\n".join(extractor.modules)) + elif args.list_extractors: + write = sys.stdout.write + fmt = "{}\n{}\nCategory: {} - Subcategory: {}{}\n\n".format + for extr in extractor.extractors(): if not extr.__doc__: continue - print(extr.__name__) - print(extr.__doc__) - print("Category:", extr.category, - "- Subcategory:", extr.subcategory) test = next(extr._get_tests(), None) - if test: - print("Example :", test[0]) - print() + write(fmt( + extr.__name__, extr.__doc__, + extr.category, extr.subcategory, + "\nExample : " + test[0] if test else "", + )) + elif args.clear_cache: from . import cache log = logging.getLogger("cache") diff --git a/gallery_dl/extractor/oauth.py b/gallery_dl/extractor/oauth.py index 428f772c..653822f9 100644 --- a/gallery_dl/extractor/oauth.py +++ b/gallery_dl/extractor/oauth.py @@ -11,6 +11,7 @@ from .common import Extractor, Message from . import deviantart, flickr, mastodon, pixiv, reddit, smugmug, tumblr from .. import text, oauth, util, config, exception +from ..output import stdout_write from ..cache import cache import urllib.parse import hashlib @@ -37,7 +38,7 @@ class OAuthBase(Extractor): def recv(self): """Open local HTTP server and recv callback parameters""" 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.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(("localhost", self.config("port", 6414))) @@ -60,7 +61,7 @@ class OAuthBase(Extractor): def send(self, msg): """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.close() @@ -69,12 +70,13 @@ class OAuthBase(Extractor): import webbrowser url += "?" + urllib.parse.urlencode(params) if not self.config("browser", True) or not webbrowser.open(url): - print("Please open this URL in your browser:") - print(url, end="\n\n", flush=True) + stdout_write( + "Please open this URL in your browser:\n\n" + url + "\n\n") return (recv or self.recv)() 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( self, request_token_url, authorize_url, access_token_url): @@ -133,7 +135,7 @@ class OAuthBase(Extractor): # check authorization response if state != params.get("state"): - self.send("'state' mismatch: expected {}, got {}.".format( + self.send("'state' mismatch: expected {}, got {}.\n".format( state, params.get("state") )) return @@ -188,7 +190,7 @@ class OAuthBase(Extractor): opt = self.oauth_config(names[0]) 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: msg += "Put " + _va + " into your configuration file as \n" msg += " and\n".join( @@ -200,7 +202,7 @@ class OAuthBase(Extractor): "\nor set\n'extractor.{}.{}' to \"cache\"" .format(self.subcategory, names[0]) ) - msg += "\nto use {}.".format(_it) + msg += "\nto use {}.\n".format(_it) return msg @@ -398,9 +400,9 @@ class OAuthPixiv(OAuthBase): data = self.session.post(url, headers=headers, data=data).json() if "error" in data: - print(data) + stdout_write("\n{}\n".format(data)) if data["error"] in ("invalid_request", "invalid_grant"): - print("'code' expired, try again") + stdout_write("'code' expired, try again\n\n") return token = data["refresh_token"] @@ -409,10 +411,10 @@ class OAuthPixiv(OAuthBase): pixiv._refresh_token_cache.update(username, token) 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): - print(""" + stdout_write("""\ 1) Open your browser's Developer Tools (F12) and switch to the Network tab 2) Login 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. - Copy-pasting more than just the 'code' value will work as well, like the entire URL or several query parameters. + """) code = input("code: ") return code.rpartition("=")[2].strip() diff --git a/gallery_dl/job.py b/gallery_dl/job.py index 044369ab..66d643c9 100644 --- a/gallery_dl/job.py +++ b/gallery_dl/job.py @@ -16,6 +16,7 @@ import collections from . import extractor, downloader, postprocessor from . import config, text, util, path, formatter, output, exception from .extractor.message import Message +from .output import stdout_write class Job(): @@ -537,14 +538,14 @@ class KeywordJob(Job): self.private = config.get(("output",), "private") def handle_url(self, url, kwdict): - print("\nKeywords for filenames and --filter:") - print("------------------------------------") + stdout_write("\nKeywords for filenames and --filter:\n" + "------------------------------------\n") self.print_kwdict(kwdict) raise exception.StopExtraction() def handle_directory(self, kwdict): - print("Keywords for directory names:") - print("-----------------------------") + stdout_write("Keywords for directory names:\n" + "-----------------------------\n") self.print_kwdict(kwdict) def handle_queue(self, url, kwdict): @@ -565,16 +566,17 @@ class KeywordJob(Job): self.extractor.log.info( "Try 'gallery-dl -K \"%s\"' instead.", url) else: - print("Keywords for --chapter-filter:") - print("------------------------------") + stdout_write("Keywords for --chapter-filter:\n" + "------------------------------\n") self.print_kwdict(kwdict) if extr or self.extractor.categorytransfer: - print() + stdout_write("\n") KeywordJob(extr or url, self).run() raise exception.StopExtraction() def print_kwdict(self, kwdict, prefix=""): """Print key-value pairs in 'kwdict' with formatting""" + write = sys.stdout.write suffix = "]" if prefix else "" for key, value in sorted(kwdict.items()): if key[0] == "_" and not self.private: @@ -588,13 +590,13 @@ class KeywordJob(Job): if value and isinstance(value[0], dict): self.print_kwdict(value[0], key + "[][") else: - print(key, "[]", sep="") + write(key + "[]\n") for val in value: - print(" -", val) + write(" - " + str(val) + "\n") else: # string or number - print(key, "\n ", value, sep="") + write("{}\n {}\n".format(key, value)) class UrlJob(Job): @@ -609,14 +611,14 @@ class UrlJob(Job): @staticmethod def handle_url(url, _): - print(url) + stdout_write(url + "\n") @staticmethod def handle_url_fallback(url, kwdict): - print(url) + stdout_write(url + "\n") if "_fallback" in kwdict: for url in kwdict["_fallback"]: - print("|", url) + stdout_write("| " + url + "\n") def handle_queue(self, url, kwdict): cls = kwdict.get("_extractor") @@ -653,15 +655,18 @@ class InfoJob(Job): return 0 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): optval = self.extractor.config(optname, util.SENTINEL) if optval is not util.SENTINEL: - print(title, "(custom):\n ", json.dumps(optval)) - print(title, "(default):\n ", json.dumps(value)) + stdout_write( + "{} (custom):\n {}\n{} (default):\n {}\n\n".format( + title, json.dumps(optval), title, json.dumps(value))) elif value: - print(title, "(default):\n ", json.dumps(value)) + stdout_write( + "{} (default):\n {}\n\n".format(title, json.dumps(value))) class DataJob(Job): diff --git a/gallery_dl/option.py b/gallery_dl/option.py index 598385d4..b2a9aa8f 100644 --- a/gallery_dl/option.py +++ b/gallery_dl/option.py @@ -39,8 +39,9 @@ class AppendCommandAction(argparse.Action): class DeprecatedConfigConstAction(argparse.Action): """Set argparse const values as config values + deprecation warning""" def __call__(self, parser, namespace, values, option_string=None): - print("warning: {} is deprecated. Use {} instead.".format( - "/".join(self.option_strings), self.choices), file=sys.stderr) + sys.stderr.write( + "warning: {} is deprecated. Use {} instead.\n".format( + "/".join(self.option_strings), self.choices)) namespace.options.append(((), self.dest, self.const)) diff --git a/test/test_job.py b/test/test_job.py index 02765555..fec6997a 100644 --- a/test/test_job.py +++ b/test/test_job.py @@ -149,10 +149,13 @@ class TestInfoJob(TestJob): self.assertEqual(self._capture_stdout(extr), """\ Category / Subcategory "test_category" / "test_subcategory" + Filename format (default): "test_{filename}.{extension}" + Directory format (default): ["{category}"] + """) def test_custom(self): @@ -165,18 +168,22 @@ Directory format (default): self.assertEqual(self._capture_stdout(extr), """\ Category / Subcategory "test_category" / "test_subcategory" + Filename format (custom): "custom" Filename format (default): "test_{filename}.{extension}" + Directory format (custom): ["custom"] Directory format (default): ["{category}"] + Request interval (custom): 321 Request interval (default): 123.456 + """) def test_base_category(self): @@ -186,10 +193,13 @@ Request interval (default): self.assertEqual(self._capture_stdout(extr), """\ Category / Subcategory / Basecategory "test_category" / "test_subcategory" / "test_basecategory" + Filename format (default): "test_{filename}.{extension}" + Directory format (default): ["{category}"] + """)