From 1965b470345a3a696bf61ac0dae57d6fe116e9b1 Mon Sep 17 00:00:00 2001 From: Pavel Minaev Date: Fri, 8 Apr 2022 16:34:45 -0700 Subject: [PATCH] Get rid of fmt() and use idiomatic format() and f"" instead. --- src/debugpy/adapter/__main__.py | 9 +- src/debugpy/adapter/clients.py | 22 ++--- src/debugpy/adapter/components.py | 18 ++-- src/debugpy/adapter/launchers.py | 6 +- src/debugpy/adapter/servers.py | 14 ++- src/debugpy/adapter/sessions.py | 4 +- src/debugpy/common/compat.py | 5 +- src/debugpy/common/fmt.py | 55 ----------- src/debugpy/common/json.py | 16 ++-- src/debugpy/common/log.py | 29 +++--- src/debugpy/common/messaging.py | 92 +++++++++---------- src/debugpy/common/modules.py | 52 ----------- src/debugpy/launcher/debuggee.py | 6 +- src/debugpy/launcher/handlers.py | 6 +- src/debugpy/server/api.py | 17 ++-- src/debugpy/server/attach_pid_injected.py | 4 +- src/debugpy/server/cli.py | 14 +-- .../debuggee/backchannel.py | 4 +- tests/debug/comms.py | 8 +- tests/debug/output.py | 8 +- tests/debug/runners.py | 20 ++-- tests/debug/session.py | 36 ++++---- tests/debug/targets.py | 14 +-- tests/debugpy/common/test_messaging.py | 7 +- tests/debugpy/test_breakpoints.py | 7 +- tests/debugpy/test_exclude_rules.py | 6 +- tests/logs.py | 4 +- tests/net.py | 8 +- tests/patterns/_impl.py | 20 ++-- tests/pytest_fixtures.py | 4 +- tests/pytest_hooks.py | 6 +- tests/timeline.py | 41 ++++----- tests/watchdog/__init__.py | 12 +-- tests/watchdog/worker.py | 6 +- 34 files changed, 221 insertions(+), 359 deletions(-) delete mode 100644 src/debugpy/common/fmt.py delete mode 100644 src/debugpy/common/modules.py diff --git a/src/debugpy/adapter/__main__.py b/src/debugpy/adapter/__main__.py index 4c9c831d..e18652c9 100644 --- a/src/debugpy/adapter/__main__.py +++ b/src/debugpy/adapter/__main__.py @@ -5,7 +5,6 @@ import argparse import atexit import codecs -import json import locale import os import sys @@ -26,7 +25,7 @@ def main(args): atexit.register(stderr.close) from debugpy import adapter - from debugpy.common import compat, log, sockets + from debugpy.common import compat, json, log, sockets from debugpy.adapter import clients, servers, sessions if args.for_server is not None: @@ -73,9 +72,9 @@ def main(args): endpoints["server"] = {"host": server_host, "port": server_port} log.info( - "Sending endpoints info to debug server at localhost:{0}:\n{1!j}", + "Sending endpoints info to debug server at localhost:{0}:\n{1}", args.for_server, - endpoints, + json.repr(endpoints), ) try: @@ -99,7 +98,7 @@ def main(args): listener_file = os.getenv("DEBUGPY_ADAPTER_ENDPOINTS") if listener_file is not None: - log.info("Writing endpoints info to {0!r}:\n{1!j}", listener_file, endpoints) + log.info("Writing endpoints info to {0!r}:\n{1}", listener_file, json.repr(endpoints)) def delete_listener_file(): log.info("Listener ports closed; deleting {0!r}", listener_file) diff --git a/src/debugpy/adapter/clients.py b/src/debugpy/adapter/clients.py index d579ac61..d4476391 100644 --- a/src/debugpy/adapter/clients.py +++ b/src/debugpy/adapter/clients.py @@ -8,7 +8,7 @@ import sys import debugpy from debugpy import adapter, common, launcher -from debugpy.common import compat, fmt, json, log, messaging, sockets +from debugpy.common import compat, json, log, messaging, sockets from debugpy.common.compat import unicode from debugpy.adapter import components, servers, sessions @@ -244,10 +244,9 @@ class Client(components.Component): # here at this point, either, so just bail out. request.respond({}) self.session.finalize( - fmt( - "{0} disconnected before responding to {1!j}", + "{0} disconnected before responding to {1}".format( self.server, - request.command, + json.repr(request.command), ) ) return @@ -309,9 +308,9 @@ class Client(components.Component): if flag_name in debug_options: if value is False: raise request.isnt_valid( - '{0!j}:false and "debugOptions":[{1!j}] are mutually exclusive', - prop_name, - flag_name, + '{0}:false and "debugOptions":[{1}] are mutually exclusive', + json.repr(prop_name), + json.repr(flag_name), ) value = True @@ -517,7 +516,7 @@ class Client(components.Component): # request was successful, but that the session terminated immediately. request.respond({}) self.session.finalize( - fmt('No known subprocess with "subProcessId":{0}', sub_pid) + 'No known subprocess with "subProcessId":{0}'.format(sub_pid) ) return @@ -555,10 +554,9 @@ class Client(components.Component): request.respond({}) self.start_request.respond({}) self.session.finalize( - fmt( - "{0} disconnected before responding to {1!j}", + "{0} disconnected before responding to {1}".format( self.server, - request.command, + json.repr(request.command), ) ) return @@ -651,7 +649,7 @@ class Client(components.Component): for key in "processId", "listen", "preLaunchTask", "postDebugTask": body.pop(key, None) - body["name"] = fmt("Subprocess {0}", conn.pid) + body["name"] = "Subprocess {0}".format(conn.pid) body["request"] = "attach" body["subProcessId"] = conn.pid diff --git a/src/debugpy/adapter/components.py b/src/debugpy/adapter/components.py index ba5415a7..f8bbe847 100644 --- a/src/debugpy/adapter/components.py +++ b/src/debugpy/adapter/components.py @@ -4,7 +4,7 @@ import functools -from debugpy.common import fmt, json, log, messaging, util +from debugpy.common import json, log, messaging, util ACCEPT_CONNECTIONS_TIMEOUT = 10 @@ -13,7 +13,7 @@ ACCEPT_CONNECTIONS_TIMEOUT = 10 class ComponentNotAvailable(Exception): def __init__(self, type): super(ComponentNotAvailable, self).__init__( - fmt("{0} is not available", type.__name__) + f"{type.__name__} is not available" ) @@ -60,7 +60,7 @@ class Component(util.Observable): self.observers += [lambda *_: self.session.notify_changed()] def __str__(self): - return fmt("{0}[{1}]", type(self).__name__, self.session.id) + return f"{type(self).__name__}[{self.session.id}]" @property def client(self): @@ -108,7 +108,7 @@ class Component(util.Observable): def disconnect(self): with self.session: self.is_connected = False - self.session.finalize(fmt("{0} has disconnected", self)) + self.session.finalize("{0} has disconnected".format(self)) def missing(session, type): @@ -165,21 +165,19 @@ class Capabilities(dict): try: value = validate(value) except Exception as exc: - raise message.isnt_valid("{0!j} {1}", name, exc) + raise message.isnt_valid("{0} {1}", json.repr(name), exc) - assert value != (), fmt( - "{0!j} must provide a default value for missing properties.", validate - ) + assert value != (), f"{validate} must provide a default value for missing properties." self[name] = value log.debug("{0}", self) def __repr__(self): - return fmt("{0}: {1!j}", type(self).__name__, dict(self)) + return f"{type(self).__name__}: {json.repr(dict(self))}" def require(self, *keys): for key in keys: if not self[key]: raise messaging.MessageHandlingError( - fmt("{0} does not have capability {1!j}", self.component, key) + f"{self.component} does not have capability {json.repr(key)}", ) diff --git a/src/debugpy/adapter/launchers.py b/src/debugpy/adapter/launchers.py index 1fff62dd..6a3bc1a4 100644 --- a/src/debugpy/adapter/launchers.py +++ b/src/debugpy/adapter/launchers.py @@ -7,7 +7,7 @@ import subprocess import sys from debugpy import adapter, common -from debugpy.common import compat, fmt, log, messaging, sockets +from debugpy.common import compat, json, log, messaging, sockets from debugpy.adapter import components, servers @@ -106,7 +106,7 @@ def spawn_debuggee( launcher_addr = ( launcher_port if launcher_host == "127.0.0.1" - else fmt("{0}:{1}", launcher_host, launcher_port) + else f"{launcher_host}:{launcher_port}" ) cmdline += [str(launcher_addr), "--"] cmdline += args @@ -124,7 +124,7 @@ def spawn_debuggee( cmdline[i] = compat.filename_str(arg) except UnicodeEncodeError as exc: raise start_request.cant_handle( - "Invalid command line argument {0!j}: {1}", arg, exc + "Invalid command line argument {0}: {1}", json.repr(arg), exc ) # If we are talking to the client over stdio, sys.stdin and sys.stdout diff --git a/src/debugpy/adapter/servers.py b/src/debugpy/adapter/servers.py index 418d5016..9cf59467 100644 --- a/src/debugpy/adapter/servers.py +++ b/src/debugpy/adapter/servers.py @@ -10,7 +10,7 @@ import time import debugpy from debugpy import adapter -from debugpy.common import compat, fmt, json, log, messaging, sockets +from debugpy.common import compat, json, log, messaging, sockets from debugpy.adapter import components @@ -89,7 +89,7 @@ if 'debugpy' not in sys.modules: finally: del sys.path[0] """ - inject_debugpy = fmt(inject_debugpy, debugpy_dir=debugpy_dir) + inject_debugpy = inject_debugpy.format(debugpy_dir=debugpy_dir) try: self.channel.request("evaluate", {"expression": inject_debugpy}) @@ -111,7 +111,7 @@ if 'debugpy' not in sys.modules: if any(conn.pid == self.pid for conn in _connections): raise KeyError( - fmt("{0} is already connected to this adapter", self) + f"{self} is already connected to this adapter" ) is_first_server = len(_connections) == 0 @@ -167,7 +167,7 @@ if 'debugpy' not in sys.modules: self.channel.close() def __str__(self): - return "Server" + fmt("[?]" if self.pid is None else "[pid={0}]", self.pid) + return "Server" + ("[?]" if self.pid is None else f"[pid={self.pid}]") def authenticate(self): if access_token is None and adapter.access_token is None: @@ -468,9 +468,7 @@ def inject(pid, debugpy_args): "Failed to inject debug server into process with PID={0}", pid ) raise messaging.MessageHandlingError( - fmt( - "Failed to inject debug server into process with PID={0}: {1}", pid, exc - ) + "Failed to inject debug server into process with PID={0}: {1}".format(pid, exc) ) # We need to capture the output of the injector - otherwise it can get blocked @@ -485,7 +483,7 @@ def inject(pid, debugpy_args): log.info("Injector[PID={0}] exited.", pid) thread = threading.Thread( - target=capture_output, name=fmt("Injector[PID={0}] output", pid) + target=capture_output, name=f"Injector[PID={pid}] output", ) thread.daemon = True thread.start() diff --git a/src/debugpy/adapter/sessions.py b/src/debugpy/adapter/sessions.py index 81c5c3d6..c4a0bd55 100644 --- a/src/debugpy/adapter/sessions.py +++ b/src/debugpy/adapter/sessions.py @@ -9,7 +9,7 @@ import threading import time from debugpy import common -from debugpy.common import fmt, log, util +from debugpy.common import log, util from debugpy.adapter import components, launchers, servers @@ -64,7 +64,7 @@ class Session(util.Observable): self.observers += [lambda *_: self.notify_changed()] def __str__(self): - return fmt("Session[{0}]", self.id) + return f"Session[{self.id}]" def __enter__(self): """Lock the session for exclusive access.""" diff --git a/src/debugpy/common/compat.py b/src/debugpy/common/compat.py index d827f413..24802007 100644 --- a/src/debugpy/common/compat.py +++ b/src/debugpy/common/compat.py @@ -10,7 +10,6 @@ import inspect import itertools import sys -from debugpy.common import fmt if sys.version_info[0] < 3: # Py2 @@ -159,13 +158,13 @@ def srcnameof(obj): except Exception: pass else: - name += fmt(" (file {0!r}", src_file) + name += " (file {0!r}".format(src_file) try: _, src_lineno = inspect.getsourcelines(obj) except Exception: pass else: - name += fmt(", line {0}", src_lineno) + name += ", line {0}".format(src_lineno) name += ")" return name diff --git a/src/debugpy/common/fmt.py b/src/debugpy/common/fmt.py deleted file mode 100644 index 1e5c2146..00000000 --- a/src/debugpy/common/fmt.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See LICENSE in the project root -# for license information. - -"""Provides a custom string.Formatter with JSON support. - -The formatter object is directly exposed as a module, such that all its members -can be invoked directly after it has been imported:: - - from debugpy.common import fmt - fmt("{0} is {value}", name, value=x) -""" - -import string -import sys -import types - - -class Formatter(string.Formatter, types.ModuleType): - """A custom string.Formatter with support for JSON pretty-printing. - - Adds {!j} format specification. When used, the corresponding value is converted - to string using json_encoder.encode(). - - Since string.Formatter in Python <3.4 does not support unnumbered placeholders, - they must always be numbered explicitly - "{0} {1}" rather than "{} {}". Named - placeholders are supported. - """ - - # Because globals() go away after the module object substitution, all method bodies - # below must access globals via self instead, or re-import modules locally. - - from debugpy.common import json - - def __init__(self): - # Set self up as a proper module, and copy globals. - # types must be re-imported, because globals aren't there yet at this point. - import types - - types.ModuleType.__init__(self, __name__) - self.__dict__.update(sys.modules[__name__].__dict__) - - def __call__(self, format_string, *args, **kwargs): - """Same as self.format(). - """ - return self.format(format_string, *args, **kwargs) - - def convert_field(self, value, conversion): - if conversion == "j": - return self.json.JsonObject(value) - return super(self.Formatter, self).convert_field(value, conversion) - - -# Replace the standard module object for this module with a Formatter instance. -sys.modules[__name__] = Formatter() diff --git a/src/debugpy/common/json.py b/src/debugpy/common/json.py index e379f984..0b794c1e 100644 --- a/src/debugpy/common/json.py +++ b/src/debugpy/common/json.py @@ -56,7 +56,7 @@ class JsonObject(object): Example:: - fmt("{0!j} {0!j:indent=4,sort_keys=True}", x) + format("{0} {0:indent=4,sort_keys=True}", json.repr(x)) """ if format_spec: # At this point, format_spec is a string that looks something like @@ -223,7 +223,7 @@ def array(validate_item=False, vectorize=False, size=None): try: value[i] = validate_item(item) except (TypeError, ValueError) as exc: - raise type(exc)(fmt("[{0!j}] {1}", i, exc)) + raise type(exc)(f"[{repr(i)}] {exc}") return value return validate @@ -257,15 +257,15 @@ def object(validate_value=False): try: value[k] = validate_value(v) except (TypeError, ValueError) as exc: - raise type(exc)(fmt("[{0!j}] {1}", k, exc)) + raise type(exc)(f"[{repr(k)}] {exc}") return value return validate -# A helper to resolve the circular dependency between common.fmt and common.json -# on Python 2. -def fmt(*args, **kwargs): - from debugpy.common import fmt +def repr(value): + return JsonObject(value) - return fmt(*args, **kwargs) + +dumps = json.dumps +loads = json.loads \ No newline at end of file diff --git a/src/debugpy/common/log.py b/src/debugpy/common/log.py index 3a50cf57..1a00a37d 100644 --- a/src/debugpy/common/log.py +++ b/src/debugpy/common/log.py @@ -14,7 +14,7 @@ import threading import traceback import debugpy -from debugpy.common import compat, fmt, timestamp, util +from debugpy.common import compat, json, timestamp, util LEVELS = ("debug", "info", "warning", "error") @@ -43,8 +43,7 @@ def _update_levels(): class LogFile(object): def __init__(self, filename, file, levels=LEVELS, close_file=True): - info("Also logging to {0!j}.", filename) - + info("Also logging to {0}.", json.repr(filename)) self.filename = filename self.file = file self.close_file = close_file @@ -86,7 +85,7 @@ class LogFile(object): with _lock: del _files[self.filename] _update_levels() - info("Not logging to {0!j} anymore.", self.filename) + info("Not logging to {0} anymore.", json.repr(self.filename)) if self.close_file: try: @@ -128,7 +127,7 @@ def write(level, text, _to_files=all): t = timestamp.current() format_string = "{0}+{1:" + timestamp_format + "}: " - prefix = fmt(format_string, level[0].upper(), t) + prefix = format_string.format(level[0].upper(), t) text = getattr(_tls, "prefix", "") + text indent = "\n" + (" " * len(prefix)) @@ -151,7 +150,7 @@ def write_format(level, format_string, *args, **kwargs): return try: - text = fmt(format_string, *args, **kwargs) + text = format_string.format(*args, **kwargs) except Exception: reraise_exception() @@ -168,12 +167,12 @@ def error(*args, **kwargs): Returns the output wrapped in AssertionError. Thus, the following:: - raise log.error(...) + raise log.error(s, ...) has the same effect as:: log.error(...) - assert False, fmt(...) + assert False, (s.format(...)) """ return AssertionError(write_format("error", *args, **kwargs)) @@ -203,7 +202,7 @@ def _exception(format_string="", *args, **kwargs): def swallow_exception(format_string="", *args, **kwargs): """Logs an exception with full traceback. - If format_string is specified, it is formatted with fmt(*args, **kwargs), and + If format_string is specified, it is formatted with format(*args, **kwargs), and prepended to the exception traceback on a separate line. If exc_info is specified, the exception it describes will be logged. Otherwise, @@ -257,7 +256,7 @@ def to_file(filename=None, prefix=None, levels=LEVELS): os.makedirs(log_dir) except OSError: pass - filename = fmt("{0}/{1}-{2}.log", log_dir, prefix, os.getpid()) + filename = f"{log_dir}/{prefix}-{os.getpid()}.log" file = _files.get(filename) if file is None: @@ -272,7 +271,7 @@ def prefixed(format_string, *args, **kwargs): """Adds a prefix to all messages logged from the current thread for the duration of the context manager. """ - prefix = fmt(format_string, *args, **kwargs) + prefix = format_string.format(*args, **kwargs) old_prefix = getattr(_tls, "prefix", "") _tls.prefix = prefix + old_prefix try: @@ -287,11 +286,11 @@ def describe_environment(header): result = [header, "\n\n"] - def report(*args, **kwargs): - result.append(fmt(*args, **kwargs)) + def report(s, *args, **kwargs): + result.append(s.format(*args, **kwargs)) def report_paths(get_paths, label=None): - prefix = fmt(" {0}: ", label or get_paths) + prefix = f" {label or get_paths}: " expr = None if not callable(get_paths): @@ -337,7 +336,7 @@ def describe_environment(header): report_paths(lambda: site_packages, "sys.path (site-packages)") for name in sysconfig.get_path_names(): - expr = fmt("sysconfig.get_path({0!r})", name) + expr = "sysconfig.get_path({0!r})".format(name) report_paths(expr) report_paths("os.__file__") diff --git a/src/debugpy/common/messaging.py b/src/debugpy/common/messaging.py index 6efc75e6..c0b9b5aa 100644 --- a/src/debugpy/common/messaging.py +++ b/src/debugpy/common/messaging.py @@ -18,7 +18,7 @@ import socket import sys import threading -from debugpy.common import compat, fmt, json, log +from debugpy.common import compat, json, log from debugpy.common.compat import unicode @@ -125,7 +125,7 @@ class JsonIOStream(object): """ if name is None: - name = fmt("reader={0!r}, writer={1!r}", reader, writer) + name = f"reader={reader!r}, writer={writer!r}" self.name = name self._reader = reader @@ -164,9 +164,9 @@ class JsonIOStream(object): def _log_message(self, dir, data, logger=log.debug): format_string = "{0} {1} " + ( - "{2!j:indent=None}" if isinstance(data, list) else "{2!j}" + "{2:indent=None}" if isinstance(data, list) else "{2}" ) - return logger(format_string, self.name, dir, data) + return logger(format_string, self.name, dir, json.repr(data)) def _read_line(self, reader): line = b"" @@ -297,9 +297,7 @@ class JsonIOStream(object): if not isinstance(body, bytes): body = body.encode("utf-8") - header = fmt("Content-Length: {0}\r\n\r\n", len(body)) - header = header.encode("ascii") - + header = f"Content-Length: {len(body)}\r\n\r\n".encode("ascii") data = header + body data_written = 0 try: @@ -319,7 +317,7 @@ class JsonIOStream(object): self._log_message("<--", value) def __repr__(self): - return fmt("{0}({1!r})", type(self).__name__, self.name) + return f"{type(self).__name__}({self.name!r})" class MessageDict(collections.OrderedDict): @@ -357,7 +355,7 @@ class MessageDict(collections.OrderedDict): """ def __repr__(self): - return fmt("{0!j}", self) + return json.repr(self) def __call__(self, key, validate, optional=False): """Like get(), but with validation. @@ -396,10 +394,10 @@ class MessageDict(collections.OrderedDict): value = validate(value) except (TypeError, ValueError) as exc: message = Message if self.message is None else self.message - err = fmt("{0}", exc) + err = str(exc) if not err.startswith("["): err = " " + err - raise message.isnt_valid("{0!j}{1}", key, err) + raise message.isnt_valid("{0}{1}", json.repr(key), err) return value def _invalid_if_no_key(func): @@ -463,7 +461,7 @@ class Message(object): """ def __str__(self): - return fmt("{0!j}", self.json) if self.json is not None else repr(self) + return json.repr(self.json) if self.json is not None else repr(self) def describe(self): """A brief description of the message that is enough to identify it. @@ -523,7 +521,7 @@ class Message(object): assert issubclass(exc_type, MessageHandlingError) silent = kwargs.pop("silent", False) - reason = fmt(format_string, *args, **kwargs) + reason = format_string.format(*args, **kwargs) exc = exc_type(reason, self, silent) # will log it if isinstance(self, Request): @@ -576,7 +574,7 @@ class Event(Message): self.body = body def describe(self): - return fmt("#{0} event {1!j} from {2}", self.seq, self.event, self.channel) + return f"#{self.seq} event {json.repr(self.event)} from {self.channel}" @property def payload(self): @@ -596,11 +594,8 @@ class Event(Message): try: try: result = handler(self) - assert result is None, fmt( - "Handler {0} tried to respond to {1}.", - compat.srcnameof(handler), - self.describe(), - ) + assert result is None, \ + f"Handler {compat.srcnameof(handler)} tried to respond to {self.describe()}." except MessageHandlingError as exc: if not exc.applies_to(self): raise @@ -681,7 +676,7 @@ class Request(Message): """ def describe(self): - return fmt("#{0} request {1!j} from {2}", self.seq, self.command, self.channel) + return f"#{self.seq} request {json.repr(self.command)} from {self.channel}" @property def payload(self): @@ -738,25 +733,28 @@ class Request(Message): ) if result is NO_RESPONSE: - assert self.response is None, fmt( + assert self.response is None, ( "Handler {0} for {1} must not return NO_RESPONSE if it has already " - "invoked request.respond().", - compat.srcnameof(handler), - self.describe(), + "invoked request.respond().".format( + compat.srcnameof(handler), + self.describe() + ) ) elif self.response is not None: - assert result is None or result is self.response.body, fmt( + assert result is None or result is self.response.body, ( "Handler {0} for {1} must not return a response body if it has " - "already invoked request.respond().", - compat.srcnameof(handler), - self.describe(), + "already invoked request.respond().".format( + compat.srcnameof(handler), + self.describe() + ) ) else: - assert result is not None, fmt( + assert result is not None, ( "Handler {0} for {1} must either call request.respond() before it " - "returns, or return the response body, or return NO_RESPONSE.", - compat.srcnameof(handler), - self.describe(), + "returns, or return the response body, or return NO_RESPONSE.".format( + compat.srcnameof(handler), + self.describe() + ) ) try: self.respond(result) @@ -787,7 +785,7 @@ class OutgoingRequest(Request): self._response_handlers = [] def describe(self): - return fmt("#{0} request {1!j} to {2}", self.seq, self.command, self.channel) + return f"{self.seq} request {json.repr(self.command)} to {self.channel}" def wait_for_response(self, raise_if_failed=True): """Waits until a response is received for this request, records the Response @@ -913,7 +911,7 @@ class Response(Message): """ def describe(self): - return fmt("#{0} response to {1}", self.seq, self.request.describe()) + return f"#{self.seq} response to {self.request.describe()}" @property def payload(self): @@ -988,7 +986,7 @@ class Disconnect(Message): super(Disconnect, self).__init__(channel, None) def describe(self): - return fmt("disconnect from {0}", self.channel) + return f"disconnect from {self.channel}" class MessageHandlingError(Exception): @@ -1068,14 +1066,9 @@ class MessageHandlingError(Exception): def __repr__(self): s = type(self).__name__ if self.cause is None: - s += fmt("(reason={0!r})", self.reason) + s += f"reason={self.reason!r})" else: - s += fmt( - "(channel={0!r}, cause={1!r}, reason={2!r})", - self.cause.channel.name, - self.cause.seq, - self.reason, - ) + s += f"channel={self.cause.channel.name!r}, cause={self.cause.seq!r}, reason={self.reason!r})" return s def applies_to(self, message): @@ -1145,7 +1138,7 @@ class JsonMessageChannel(object): return self.name def __repr__(self): - return fmt("{0}({1!r})", type(self).__name__, self.name) + return f"{type(self).__name__}({self.name!r})" def __enter__(self): self._lock.acquire() @@ -1180,7 +1173,7 @@ class JsonMessageChannel(object): self.started = True self._parser_thread = threading.Thread( - target=self._parse_incoming_messages, name=fmt("{0} message parser", self) + target=self._parse_incoming_messages, name=f"{self} message parser" ) self._parser_thread.pydev_do_not_trace = True self._parser_thread.is_pydev_daemon_thread = True @@ -1404,16 +1397,16 @@ class JsonMessageChannel(object): parser(self, message_dict) except InvalidMessageError as exc: log.error( - "Failed to parse message in channel {0}: {1} in:\n{2!j}", + "Failed to parse message in channel {0}: {1} in:\n{2}", self, str(exc), - message_dict, + json.repr(message_dict), ) except Exception as exc: if isinstance(exc, NoMoreMessages) and exc.stream is self.stream: raise log.swallow_exception( - "Fatal error in channel {0} while parsing:\n{1!j}", self, message_dict + "Fatal error in channel {0} while parsing:\n{1}", self, json.repr(message_dict) ) os._exit(1) @@ -1440,7 +1433,7 @@ class JsonMessageChannel(object): # of handlers to run. if len(self._handler_queue) and self._handler_thread is None: self._handler_thread = threading.Thread( - target=self._run_handlers, name=fmt("{0} message handler", self) + target=self._run_handlers, name=f"{self} message handler", ) self._handler_thread.pydev_do_not_trace = True self._handler_thread.is_pydev_daemon_thread = True @@ -1508,8 +1501,7 @@ class JsonMessageChannel(object): continue raise AttributeError( - fmt( - "handler object {0} for channel {1} has no handler for {2} {3!r}", + "handler object {0} for channel {1} has no handler for {2} {3!r}".format( compat.srcnameof(handlers), self, type, diff --git a/src/debugpy/common/modules.py b/src/debugpy/common/modules.py deleted file mode 100644 index fd052eb4..00000000 --- a/src/debugpy/common/modules.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See LICENSE in the project root -# for license information. - -"""Provides facilities to use objects as modules, enabling __getattr__, __call__ -etc on module level. -""" - -import sys -import types - - -def module(name): - """A decorator for classes that implement modules. - - Idiomatic usage is with __name__, so that an instance of the class replaces the - module in which it is defined:: - - # foo.py - @module(__name__) - class Foo(object): - def __call__(self): ... - - # bar.py - import foo - foo() - - "Regular" globals, including imports, don't work with class modules. Class or - instance attributes must be used consistently for this purpose, and accessed via - self inside method bodies:: - - @module(__name__) - class Foo(object): - import sys - - def __call__(self): - if self.sys.version_info < (3,): ... - """ - - def decorate(cls): - class Module(cls, types.ModuleType): - def __init__(self): - # Set self up as a proper module, and copy pre-existing globals. - types.ModuleType.__init__(self, name) - self.__dict__.update(sys.modules[name].__dict__) - - cls.__init__(self) - - Module.__name__ = cls.__name__ - sys.modules[name] = Module() - - return decorate diff --git a/src/debugpy/launcher/debuggee.py b/src/debugpy/launcher/debuggee.py index f5d126c8..57d06901 100644 --- a/src/debugpy/launcher/debuggee.py +++ b/src/debugpy/launcher/debuggee.py @@ -12,7 +12,7 @@ import sys import threading from debugpy import launcher -from debugpy.common import fmt, log, messaging, compat +from debugpy.common import log, messaging, compat from debugpy.launcher import output if sys.platform == "win32": @@ -34,7 +34,7 @@ returns True, the launcher pauses and waits for user input before exiting. def describe(): - return fmt("Debuggee[PID={0}]", process.pid) + return f"Debuggee[PID={process.pid}]" def spawn(process_name, cmdline, env, redirect_output): @@ -91,7 +91,7 @@ def spawn(process_name, cmdline, env, redirect_output): process = subprocess.Popen(cmdline, env=env, bufsize=0, **kwargs) except Exception as exc: raise messaging.MessageHandlingError( - fmt("Couldn't spawn debuggee: {0}\n\nCommand line:{1!r}", exc, cmdline) + "Couldn't spawn debuggee: {0}\n\nCommand line:{1!r}".format(exc, cmdline) ) log.info("Spawned {0}.", describe()) diff --git a/src/debugpy/launcher/handlers.py b/src/debugpy/launcher/handlers.py index 5b12849d..2c38a033 100644 --- a/src/debugpy/launcher/handlers.py +++ b/src/debugpy/launcher/handlers.py @@ -28,9 +28,9 @@ def launch_request(request): if flag_name in debug_options: if value is False: raise request.isnt_valid( - '{0!j}:false and "debugOptions":[{1!j}] are mutually exclusive', - prop_name, - flag_name, + '{0}:false and "debugOptions":[{1}] are mutually exclusive', + json.repr(prop_name), + json.repr(flag_name), ) value = True diff --git a/src/debugpy/server/api.py b/src/debugpy/server/api.py index 3d193fdc..3a98b2e0 100644 --- a/src/debugpy/server/api.py +++ b/src/debugpy/server/api.py @@ -3,7 +3,6 @@ # for license information. import codecs -import json import os import pydevd import socket @@ -12,7 +11,7 @@ import threading import debugpy from debugpy import adapter -from debugpy.common import compat, fmt, log, sockets +from debugpy.common import compat, json, log, sockets from _pydevd_bundle.pydevd_constants import get_global_debugger from pydevd_file_utils import absolute_path @@ -91,13 +90,13 @@ def configure(properties, **kwargs): for k, v in properties.items(): if k not in _config: - raise ValueError(fmt("Unknown property {0!r}", k)) + raise ValueError("Unknown property {0!r}".format(k)) expected_type = type(_config[k]) if type(v) is not expected_type: - raise ValueError(fmt("{0!r} must be a {1}", k, expected_type.__name__)) + raise ValueError("{0!r} must be a {1}".format(k, expected_type.__name__)) valid_values = _config_valid_values.get(k) if (valid_values is not None) and (v not in valid_values): - raise ValueError(fmt("{0!r} must be one of: {1!r}", k, valid_values)) + raise ValueError("{0!r} must be one of: {1!r}".format(k, valid_values)) _config[k] = v @@ -120,7 +119,7 @@ def _starts_debugging(func): ensure_logging() log.debug("{0}({1!r}, **{2!r})", func.__name__, address, kwargs) - log.info("Initial debug configuration: {0!j}", _config) + log.info("Initial debug configuration: {0}", json.repr(_config)) qt_mode = _config.get("qt", "none") if qt_mode != "none": @@ -181,7 +180,7 @@ def listen(address, settrace_kwargs): ] if log.log_dir is not None: adapter_args += ["--log-dir", log.log_dir] - log.info("debugpy.listen() spawning adapter: {0!j}", adapter_args) + log.info("debugpy.listen() spawning adapter: {0}", json.repr(adapter_args)) # On Windows, detach the adapter from our console, if any, so that it doesn't # receive Ctrl+C from it, and doesn't keep it open once we exit. @@ -235,7 +234,7 @@ def listen(address, settrace_kwargs): finally: endpoints_listener.close() - log.info("Endpoints received from adapter: {0!j}", endpoints) + log.info("Endpoints received from adapter: {0}", json.repr(endpoints)) if "error" in endpoints: raise RuntimeError(str(endpoints["error"])) @@ -247,7 +246,7 @@ def listen(address, settrace_kwargs): client_port = int(endpoints["client"]["port"]) except Exception as exc: log.swallow_exception( - "Error parsing adapter endpoints:\n{0!j}\n", endpoints, level="info" + "Error parsing adapter endpoints:\n{0}\n", json.repr(endpoints), level="info" ) raise RuntimeError("error parsing adapter endpoints: " + str(exc)) log.info( diff --git a/src/debugpy/server/attach_pid_injected.py b/src/debugpy/server/attach_pid_injected.py index a8aeed27..a8df6e1e 100644 --- a/src/debugpy/server/attach_pid_injected.py +++ b/src/debugpy/server/attach_pid_injected.py @@ -57,7 +57,7 @@ def attach(setup): try: import debugpy import debugpy.server - from debugpy.common import log + from debugpy.common import json, log import pydevd finally: assert sys.path[0] == _debugpy_dir @@ -69,7 +69,7 @@ def attach(setup): if setup["log_to"] is not None: debugpy.log_to(setup["log_to"]) - log.info("Configuring injected debugpy: {0!j}", setup) + log.info("Configuring injected debugpy: {0}", json.repr(setup)) if setup["mode"] == "listen": debugpy.listen(setup["address"]) diff --git a/src/debugpy/server/cli.py b/src/debugpy/server/cli.py index 8dd79e71..b36c33d5 100644 --- a/src/debugpy/server/cli.py +++ b/src/debugpy/server/cli.py @@ -16,7 +16,7 @@ assert "pydevd" in sys.modules import pydevd import debugpy -from debugpy.common import compat, fmt, log +from debugpy.common import compat, log from debugpy.server import api @@ -56,9 +56,9 @@ def in_range(parser, start, stop): def parse(s): n = parser(s) if start is not None and n < start: - raise ValueError(fmt("must be >= {0}", start)) + raise ValueError("must be >= {0}".format(start)) if stop is not None and n >= stop: - raise ValueError(fmt("must be < {0}", stop)) + raise ValueError("must be < {0}".format(stop)) return n return parse @@ -123,7 +123,7 @@ def set_config(arg, it): value = next(it) if name not in options.config: - raise ValueError(fmt("unknown property {0!r}", name)) + raise ValueError("unknown property {0!r}".format(name)) expected_type = type(options.config[name]) try: @@ -132,7 +132,7 @@ def set_config(arg, it): else: value = expected_type(value) except Exception: - raise ValueError(fmt("{0!r} must be a {1}", name, expected_type.__name__)) + raise ValueError("{0!r} must be a {1}".format(name, expected_type.__name__)) options.config[name] = value @@ -221,9 +221,9 @@ def parse_argv(): action(arg, it) except StopIteration: assert placeholder is not None - raise ValueError(fmt("{0}: missing {1}", switch, placeholder)) + raise ValueError("{0}: missing {1}".format(switch, placeholder)) except Exception as exc: - raise ValueError(fmt("invalid {0} {1}: {2}", switch, placeholder, exc)) + raise ValueError("invalid {0} {1}: {2}".format(switch, placeholder, exc)) if options.target is not None: break diff --git a/tests/DEBUGGEE_PYTHONPATH/debuggee/backchannel.py b/tests/DEBUGGEE_PYTHONPATH/debuggee/backchannel.py index 9bcbc718..714856e5 100644 --- a/tests/DEBUGGEE_PYTHONPATH/debuggee/backchannel.py +++ b/tests/DEBUGGEE_PYTHONPATH/debuggee/backchannel.py @@ -13,7 +13,7 @@ import os import socket import debuggee -from debugpy.common import fmt, log, messaging +from debugpy.common import log, messaging def send(value): @@ -52,7 +52,7 @@ class _stream: close = lambda: None -name = fmt("backchannel-{0}", debuggee.session_id) +name = f"backchannel-{debuggee.session_id}" port = os.environ.pop("DEBUGPY_TEST_BACKCHANNEL_PORT", None) if port is not None: port = int(port) diff --git a/tests/debug/comms.py b/tests/debug/comms.py index ea285ee0..f4d79817 100644 --- a/tests/debug/comms.py +++ b/tests/debug/comms.py @@ -7,7 +7,7 @@ import threading import socket -from debugpy.common import fmt, log, messaging, sockets +from debugpy.common import log, messaging, sockets class BackChannel(object): @@ -21,7 +21,7 @@ class BackChannel(object): self._server_socket = None def __str__(self): - return fmt("BackChannel[{0}]", self.session.id) + return f"BackChannel[{self.session.id}]" def listen(self): self._server_socket = sockets.create_server("127.0.0.1", 0, self.TIMEOUT) @@ -59,7 +59,7 @@ class BackChannel(object): self._setup_stream() accept_thread = threading.Thread( - target=accept_worker, name=fmt("{0} listener", self) + target=accept_worker, name=f"{self} listener" ) accept_thread.daemon = True accept_thread.start() @@ -118,5 +118,5 @@ class ScratchPad(object): """Sets debuggee.scratchpad[key] = value inside the debugged process. """ log.info("{0} debuggee.scratchpad[{1!r}] = {2!r}", self.session, key, value) - expr = fmt("sys.modules['debuggee'].scratchpad[{0!r}] = {1!r}", key, value) + expr = f"sys.modules['debuggee'].scratchpad[{key!r}] = {value!r}" self.session.request("evaluate", {"context": "repl", "expression": expr}) diff --git a/tests/debug/output.py b/tests/debug/output.py index 73b76fbb..a19a3af9 100644 --- a/tests/debug/output.py +++ b/tests/debug/output.py @@ -6,7 +6,7 @@ import os import re import threading -from debugpy.common import fmt, log +from debugpy.common import log class CapturedOutput(object): @@ -24,7 +24,7 @@ class CapturedOutput(object): self._capture(fd, stream_name) def __str__(self): - return fmt("CapturedOutput[{0}]", self.session.id) + return f"CapturedOutput[{self.session.id}]" def _worker(self, fd, name): chunks = self._chunks[name] @@ -52,7 +52,7 @@ class CapturedOutput(object): self._chunks[name] = [] thread = threading.Thread( - target=lambda: self._worker(fd, name), name=fmt("{0} {1}", self, name) + target=lambda: self._worker(fd, name), name=f"{self} {name}" ) thread.daemon = True thread.start() @@ -73,7 +73,7 @@ class CapturedOutput(object): result = self._chunks[which] except KeyError: raise AssertionError( - fmt("{0} was not captured for {1}", which, self.session.debuggee_id) + f"{which} was not captured for {self.session.debuggee_id}" ) with self._lock: diff --git a/tests/debug/runners.py b/tests/debug/runners.py index 826c8954..d2a8e81f 100644 --- a/tests/debug/runners.py +++ b/tests/debug/runners.py @@ -56,7 +56,7 @@ import pytest import sys import debugpy -from debugpy.common import compat, fmt, log +from debugpy.common import compat, json, log from tests import net, timeline from tests.debug import session from tests.patterns import some @@ -100,7 +100,7 @@ def _runner(f): def __repr__(self): result = type(self).__name__ args = [str(x) for x in self._args] + [ - fmt("{0}={1}", k, v) for k, v in self._kwargs.items() + f"{k}={v}" for k, v in self._kwargs.items() ] if len(args): result += "(" + ", ".join(args) + ")" @@ -118,7 +118,7 @@ def _runner(f): def launch(session, target, console=None, cwd=None): assert console in (None, "internalConsole", "integratedTerminal", "externalTerminal") - log.info("Launching {0} in {1} using {2!j}.", target, session, console) + log.info("Launching {0} in {1} using {2}.", target, session, json.repr(console)) target.configure(session) config = session.config @@ -144,8 +144,8 @@ def launch(session, target, console=None, cwd=None): def _attach_common_config(session, target, cwd): - assert target.code is None or "debuggee.setup()" in target.code, fmt( - "{0} must invoke debuggee.setup().", target.filename + assert target.code is None or "debuggee.setup()" in target.code, ( + f"{target.filename} must invoke debuggee.setup()." ) target.configure(session) @@ -239,8 +239,7 @@ debugpy.listen(({host!r}, {port!r})) if {wait!r}: debugpy.wait_for_client() """ - debuggee_setup = fmt( - debuggee_setup, + debuggee_setup = debuggee_setup.format( host=host, port=port, wait=wait, @@ -293,16 +292,13 @@ def attach_listen(session, target, method, cwd=None, log_dir=None): elif method == "api": args = [] api_config = {k: v for k, v in config.items() if k in {"subProcess"}} - debuggee_setup = """ + debuggee_setup = f""" import debugpy if {log_dir!r}: debugpy.log_to({log_dir!r}) debugpy.configure({api_config!r}) -debugpy.connect({address!r}) +debugpy.connect({(host, port)!r}) """ - debuggee_setup = fmt( - debuggee_setup, address=(host, port), log_dir=log_dir, api_config=api_config - ) else: raise ValueError args += target.cli(session.spawn_debuggee.env) diff --git a/tests/debug/session.py b/tests/debug/session.py index 165ec3fb..250e054a 100644 --- a/tests/debug/session.py +++ b/tests/debug/session.py @@ -12,7 +12,7 @@ import sys import time import debugpy.adapter -from debugpy.common import compat, fmt, json, log, messaging, sockets, util +from debugpy.common import compat, json, log, messaging, sockets, util from debugpy.common.compat import unicode import tests from tests import code, timeline, watchdog @@ -203,15 +203,15 @@ class Session(object): self.spawn_debuggee.env = util.Env() def __str__(self): - return fmt("Session[{0}]", self.id) + return f"Session[{self.id}]" @property def adapter_id(self): - return fmt("Adapter[{0}]", self.id) + return f"Adapter[{self.id}]" @property def debuggee_id(self): - return fmt("Debuggee[{0}]", self.id) + return f"Debuggee[{self.id}]" def __enter__(self): return self @@ -294,7 +294,7 @@ class Session(object): if self.log_dir is None: return False - log.info("Logs for {0} will be in {1!j}", self, self.log_dir) + log.info("Logs for {0} will be in {1}", self, json.repr(self.log_dir)) try: self.log_dir.remove() except Exception: @@ -358,13 +358,13 @@ class Session(object): log.info( "Spawning {0}:\n\n" - "Current directory: {1!j}\n\n" - "Command line: {2!j}\n\n" - "Environment variables: {3!j}\n\n", + "Current directory: {1}\n\n" + "Command line: {2}\n\n" + "Environment variables: {3}\n\n", self.debuggee_id, - cwd, - args, - env, + json.repr(cwd), + json.repr(args), + json.repr(env), ) popen_fds = {} @@ -405,11 +405,11 @@ class Session(object): log.info( "Spawning {0}:\n\n" - "Command line: {1!j}\n\n" - "Environment variables: {2!j}\n\n", + "Command line: {1}\n\n" + "Environment variables: {2}\n\n", self.adapter_id, - args, - env, + json.repr(args), + json.repr(env), ) self.adapter = psutil.Popen( args, @@ -446,7 +446,7 @@ class Session(object): return self.request_attach() else: raise ValueError( - fmt('Unsupported "request":{0!j} in session.config', request) + f'Unsupported "request":{json.repr(request)} in session.config' ) def request(self, *args, **kwargs): @@ -485,7 +485,7 @@ class Session(object): self.observe(occ) pid = event("subProcessId", int) watchdog.register_spawn( - pid, fmt("{0}-subprocess-{1}", self.debuggee_id, pid) + pid, f"{self.debuggee_id}-subprocess-{pid}" ) def run_in_terminal(self, args, cwd, env): @@ -662,7 +662,7 @@ class Session(object): else: marker = line line = get_marked_line_numbers()[marker] - descr = fmt("{0} (@{1})", line, marker) + descr = f"{line} (@{marker})" bp_log.append((line, descr)) return {"line": line} diff --git a/tests/debug/targets.py b/tests/debug/targets.py index 7e3623df..e6fb4714 100644 --- a/tests/debug/targets.py +++ b/tests/debug/targets.py @@ -5,7 +5,7 @@ import py import os -from debugpy.common import fmt, compat +from debugpy.common import compat, json from tests.patterns import some @@ -108,13 +108,9 @@ class Program(Target): def __repr__(self): if self._cwd: - return fmt( - "program (relative) {0!j} / {1!j}", - self._cwd, - self._get_relative_program(), - ) + return f"program (relative) {json.repr(self._cwd)} / {json.repr(self._get_relative_program())}" else: - return fmt("program {0!j}", compat.filename(self.filename.strpath)) + return f"program {json.repr(compat.filename(self.filename.strpath))}" def configure(self, session): if self._cwd: @@ -147,7 +143,7 @@ class Module(Target): self.name = name if name is not None else self.filename.purebasename def __repr__(self): - return fmt("module {0}", self.name) + return f"module {self.name}" def configure(self, session): session.config["module"] = self.name @@ -175,7 +171,7 @@ class Code(Target): def __repr__(self): lines = self.code.split("\n") - return fmt("code: {0!j}", lines) + return f"code: {json.repr(lines)}" def configure(self, session): session.config["code"] = self.code diff --git a/tests/debugpy/common/test_messaging.py b/tests/debugpy/common/test_messaging.py index 43ab90c2..7ba88e88 100644 --- a/tests/debugpy/common/test_messaging.py +++ b/tests/debugpy/common/test_messaging.py @@ -7,7 +7,6 @@ import collections import functools -import json import io import pytest import random @@ -16,7 +15,7 @@ import socket import threading import time -from debugpy.common import log, messaging +from debugpy.common import json, log, messaging from tests.patterns import some @@ -45,9 +44,9 @@ class JsonMemoryStream(object): def _log_message(self, dir, data): format_string = "{0} {1} " + ( - "{2!j:indent=None}" if isinstance(data, list) else "{2!j}" + "{2:indent=None}" if isinstance(data, list) else "{2}" ) - return log.debug(format_string, self.name, dir, data) + return log.debug(format_string, self.name, dir, json.repr(data)) def read_json(self, decoder=None): decoder = decoder if decoder is not None else self.json_decoder_factory() diff --git a/tests/debugpy/test_breakpoints.py b/tests/debugpy/test_breakpoints.py index 7c135c88..6a0466fd 100644 --- a/tests/debugpy/test_breakpoints.py +++ b/tests/debugpy/test_breakpoints.py @@ -7,7 +7,6 @@ import pytest import re import sys -from debugpy.common import fmt import tests from tests import debug, test_data from tests.debug import runners, targets @@ -229,10 +228,10 @@ def test_log_point(pyfile, target, run, condition): assert not session.captured_stdout() expected_stdout = "".join( - (fmt(r"{0}\r?\n", re.escape(str(i))) for i in range(0, 10)) + (r"{0}\r?\n".format(re.escape(str(i))) for i in range(0, 10)) ) expected_stderr = "".join( - (fmt(r"{0}\r?\n", re.escape(str(i * 10))) for i in range(0, 10)) + (r"{0}\r?\n".format(re.escape(str(i * 10))) for i in range(0, 10)) ) assert session.output("stdout") == some.str.matching(expected_stdout) assert session.output("stderr") == some.str.matching(expected_stderr) @@ -286,7 +285,7 @@ def test_add_and_remove_breakpoint(pyfile, target, run): ) session.request_continue() - expected_stdout = "".join((fmt("{0}\n", i) for i in range(0, 10))) + expected_stdout = "".join(f"{i}\n" for i in range(0, 10)) assert session.output("stdout") == expected_stdout diff --git a/tests/debugpy/test_exclude_rules.py b/tests/debugpy/test_exclude_rules.py index 2d022d55..36f6e091 100644 --- a/tests/debugpy/test_exclude_rules.py +++ b/tests/debugpy/test_exclude_rules.py @@ -4,6 +4,8 @@ import pytest +from debugpy.common import json + import tests from tests import code, debug, log, test_data from tests.debug import targets @@ -48,7 +50,7 @@ def test_exceptions_and_exclude_rules(pyfile, target, run, scenario, exc_type): rules = [{"path": code_to_debug.dirname, "include": False}] else: pytest.fail(scenario) - log.info("Rules: {0!j}", rules) + log.info("Rules: {0}", json.repr(rules)) with debug.Session() as session: session.expected_exit_code = some.int @@ -94,7 +96,7 @@ def test_exceptions_and_partial_exclude_rules(pyfile, target, run, scenario): rules = [{"path": call_me_back_dir, "include": False}] else: pytest.fail(scenario) - log.info("Rules: {0!j}", rules) + log.info("Rules: {0}", json.repr(rules)) with debug.Session() as session: session.expected_exit_code = some.int diff --git a/tests/logs.py b/tests/logs.py index cacb2de3..7d3089ca 100644 --- a/tests/logs.py +++ b/tests/logs.py @@ -7,13 +7,13 @@ import os import pytest_timeout import sys -from debugpy.common import log +from debugpy.common import json, log def dump(): if log.log_dir is None: return - log.info("Dumping logs from {0!j}", log.log_dir) + log.info("Dumping logs from {0}", json.repr(log.log_dir)) for dirpath, dirnames, filenames in os.walk(log.log_dir): for name in sorted(filenames): diff --git a/tests/net.py b/tests/net.py index c5d3d0df..26265ff2 100644 --- a/tests/net.py +++ b/tests/net.py @@ -12,7 +12,7 @@ import socket import threading import time -from debugpy.common import compat, fmt, log +from debugpy.common import compat, log from tests.patterns import some @@ -115,13 +115,13 @@ class WebRequest(object): func = getattr(requests, method) self._worker_thread = threading.Thread( target=lambda: self._worker(func, *args, **kwargs), - name=fmt("WebRequest({0})", self), + name=f"WebRequest({self})", ) self._worker_thread.daemon = True self._worker_thread.start() def __str__(self): - return fmt("HTTP {0} {1}", self.method.upper(), self.url) + return f"HTTP {self.method.upper()} {self.url}" def _worker(self, func, *args, **kwargs): try: @@ -158,7 +158,7 @@ class WebServer(object): def __init__(self, port): self.port = port - self.url = fmt("http://localhost:{0}", port) + self.url = f"http://localhost:{port}" def __enter__(self): """Blocks until the server starts listening on self.port. diff --git a/tests/patterns/_impl.py b/tests/patterns/_impl.py index e09f26f3..6f3af210 100644 --- a/tests/patterns/_impl.py +++ b/tests/patterns/_impl.py @@ -12,7 +12,7 @@ import py.path import re import sys -from debugpy.common import compat, fmt +from debugpy.common import compat from debugpy.common.compat import unicode, xrange import pydevd_file_utils @@ -90,7 +90,7 @@ class Not(Some): self.pattern = pattern def __repr__(self): - return fmt("~{0!r}", self.pattern) + return f"~{self.pattern!r}" def matches(self, value): return value != self.pattern @@ -108,7 +108,7 @@ class Either(Some): try: return self.name except AttributeError: - return fmt("({0})", " | ".join(repr(pat) for pat in self.patterns)) + return "({0})".format(" | ".join(repr(pat) for pat in self.patterns)) def matches(self, value): return any(pattern == value for pattern in self.patterns) @@ -156,7 +156,7 @@ class InstanceOf(Some): name = self.name else: name = " | ".join(cls.__name__ for cls in self.classinfo) - return fmt("<{0}>", name) + return f"<{name}>" def matches(self, value): return isinstance(value, self.classinfo) @@ -180,7 +180,7 @@ class Path(Some): self.path = path def __repr__(self): - return fmt("path({0!r})", self.path) + return "path({self.path!r})" def __str__(self): return compat.filename_str(self.path) @@ -218,7 +218,7 @@ class ListContaining(Some): if not self.items: return "[...]" s = repr(list(self.items)) - return fmt("[..., {0}, ...]", s[1:-1]) + return f"[..., {s[1:-1]}, ...]" def __getstate__(self): items = ["\002...\003"] @@ -302,7 +302,7 @@ class SuchThat(Also): try: return self.name except AttributeError: - return fmt("({0!r} if {1})", self.pattern, compat.nameof(self.condition)) + return f"({self.pattern!r} if {compat.nameof(self.condition)})" def _also(self, value): return self.condition(value) @@ -321,7 +321,7 @@ class InRange(Also): try: return self.name except AttributeError: - return fmt("({0!r} <= {1!r} < {2!r})", self.start, self.pattern, self.stop) + return f"({self.start!r} <= {self.pattern!r} < {self.stop!r})" def _also(self, value): return self.start <= value < self.stop @@ -360,7 +360,7 @@ class NotEqualTo(Also): self.obj = obj def __repr__(self): - return fmt("", self.obj) + return f"" def _also(self, value): return self.obj != value @@ -375,7 +375,7 @@ class SameAs(Also): self.obj = obj def __repr__(self): - return fmt("", self.obj) + return f"" def _also(self, value): return self.obj is value diff --git a/tests/pytest_fixtures.py b/tests/pytest_fixtures.py index fc9e5e06..ad1f4c24 100644 --- a/tests/pytest_fixtures.py +++ b/tests/pytest_fixtures.py @@ -10,7 +10,7 @@ import sys import threading import types -from debugpy.common import compat, fmt, log, timestamp +from debugpy.common import compat, log, timestamp import tests from tests import code, logs from tests.debug import runners, session, targets @@ -57,7 +57,7 @@ def test_wrapper(request, long_tmpdir): log_subdir = request.node.nodeid log_subdir = log_subdir.replace("::", "/") for ch in r":?*|<>": - log_subdir = log_subdir.replace(ch, fmt("&#{0};", ord(ch))) + log_subdir = log_subdir.replace(ch, f"&#{ord(ch)};") log.log_dir += "/" + log_subdir try: diff --git a/tests/pytest_hooks.py b/tests/pytest_hooks.py index 8a47db9c..10b653e0 100644 --- a/tests/pytest_hooks.py +++ b/tests/pytest_hooks.py @@ -7,7 +7,7 @@ import pytest import pytest_timeout import sys -from debugpy.common import fmt, log +from debugpy.common import log import tests from tests import logs @@ -30,13 +30,13 @@ def pytest_configure(config): log.log_dir = config.option.debugpy_log_dir else: bits = 64 if sys.maxsize > 2 ** 32 else 32 - ver = fmt("{0}.{1}-{bits}", *sys.version_info, bits=bits) + ver = "{0}.{1}-{bits}".format(*sys.version_info, bits=bits) log.log_dir = (tests.root / "_logs" / ver).strpath log.info("debugpy and pydevd logs will be under {0}", log.log_dir) def pytest_report_header(config): - log.describe_environment(fmt("Test environment for tests-{0}", os.getpid())) + log.describe_environment(f"Test environment for tests-{os.getpid()}") @pytest.hookimpl(hookwrapper=True, tryfirst=True) diff --git a/tests/timeline.py b/tests/timeline.py index 70da1898..41990642 100644 --- a/tests/timeline.py +++ b/tests/timeline.py @@ -7,7 +7,7 @@ import contextlib import itertools import threading -from debugpy.common import compat, fmt, log, messaging, timestamp +from debugpy.common import compat, json, log, messaging, timestamp from debugpy.common.compat import queue from tests.patterns import some @@ -18,9 +18,9 @@ SINGLE_LINE_REPR_LIMIT = 120 be formatted to use multiple shorter lines if possible. """ -# For use by Expectation.__repr__. Uses fmt() to create unique instances. -_INDENT = fmt("{0}", "_INDENT") -_DEDENT = fmt("{0}", "_DEDENT") +# For use by Expectation.__repr__. Uses format() to create unique instances. +_INDENT = "{0}".format("_INDENT") +_DEDENT = "{0}".format("_DEDENT") class Timeline(object): @@ -36,7 +36,7 @@ class Timeline(object): self._record_queue = queue.Queue() self._recorder_thread = threading.Thread( - target=self._recorder_worker, name=fmt("{0} recorder", self) + target=self._recorder_worker, name=f"{self} recorder" ) self._recorder_thread.daemon = True self._recorder_thread.start() @@ -275,7 +275,7 @@ class Timeline(object): assert expectation not in self.new() def _explain_how_realized(self, expectation, reasons): - message = fmt("Realized {0!r}", expectation) + message = f"Realized {expectation!r}" # For the breakdown, we want to skip any expectations that were exact occurrences, # since there's no point explaining that occurrence was realized by itself. @@ -288,14 +288,14 @@ class Timeline(object): # one, then we have already printed it, so just add the explanation. reason = reasons[expectation] if "\n" in message: - message += fmt(" == {0!r}", reason) + message += f" == {reason!r}" else: - message += fmt("\n == {0!r}", reason) + message += f"\n == {reason!r}" elif reasons: # Otherwise, break it down expectation by expectation. message += ":" for exp, reason in reasons.items(): - message += fmt("\n\n where {0!r}\n == {1!r}", exp, reason) + message += "\n\n where {exp!r}\n == {reason!r}" else: message += "." @@ -501,7 +501,7 @@ class DerivativeExpectation(Expectation): if len(timelines) > 1: offending_expectations = "" for tl_id, tl in timelines.items(): - offending_expectations += fmt("\n {0}: {1!r}\n", tl_id, tl) + offending_expectations += f"\n {tl_id}: {tl!r}\n" raise log.error( "Cannot mix expectations from multiple timelines:\n{0}", offending_expectations, @@ -753,7 +753,7 @@ class PatternExpectation(Expectation): rest = repr(self.circumstances[1:]) if rest.endswith(",)"): rest = rest[:-2] + ")" - return fmt("<{0}{1}>", self.circumstances[0], rest) + return f"<{self.circumstances[0]}{rest}>" def __repr__(self): return self.describe() @@ -768,8 +768,8 @@ def _describe_message(message_type, *items): d = collections.OrderedDict(items) # Keep it all on one line if it's short enough, but indent longer ones. - for format_string in "{0!j:indent=None}", "{0!j}": - s = fmt(format_string, d) + for format_string in "{0:indent=None}", "{0}": + s = format_string.format(json.repr(d)) s = "{..., " + s[1:] # Used by some.dict.containing to inject ... as needed. @@ -816,7 +816,7 @@ def Response(request, body=some.object): # Try to be as specific as possible. if isinstance(request, Expectation): if request.circumstances[0] != "request": - exp.describe = lambda: fmt("response to {0!r}", request) + exp.describe = lambda: f"response to {request!r}" return else: items = (("command", request.circumstances[1]),) @@ -951,18 +951,13 @@ class Occurrence(Expectation): return hash(id(self)) def __repr__(self): - return fmt( - "{2}{0}.{1}", - self.index, - self.describe_circumstances(), - "" if self.observed else "*", - ) + return ("" if self.observed else "*") + f"{self.index}.{self.describe_circumstances()}" def describe_circumstances(self): rest = repr(self.circumstances[1:]) if rest.endswith(",)"): rest = rest[:-2] + ")" - return fmt("{0}{1}", self.circumstances[0], rest) + return f"{self.circumstances[0]}{rest}" class MessageOccurrence(Occurrence): @@ -1019,9 +1014,9 @@ class MessageOccurrence(Occurrence): id = collections.OrderedDict(self._id) # Keep it all on one line if it's short enough, but indent longer ones. - s = fmt("{0!j:indent=None}", id) + s = f"{json.repr(id):indent=None}" if len(s) > SINGLE_LINE_REPR_LIMIT: - s = fmt("{0!j}", id) + s = f"{json.repr(id)}" return s # For messages, we don't want to include their index, because they already have diff --git a/tests/watchdog/__init__.py b/tests/watchdog/__init__.py index 810b9c85..30e7ab02 100644 --- a/tests/watchdog/__init__.py +++ b/tests/watchdog/__init__.py @@ -19,13 +19,13 @@ import sys import threading import time -from debugpy.common import fmt, log, messaging +from debugpy.common import log, messaging from tests.watchdog import worker WATCHDOG_TIMEOUT = 3 -_name = fmt("watchdog-{0}", os.getpid()) +_name = f"watchdog-{os.getpid()}" _stream = None _process = None _worker_log_filename = None @@ -57,7 +57,7 @@ def start(): def _dump_worker_log(command, problem, exc_info=None): - reason = fmt("{0}.{1}() {2}", _name, command, problem) + reason = f"{_name}.{command}() {problem}" if _worker_log_filename is None: reason += ", but there is no log." else: @@ -65,9 +65,9 @@ def _dump_worker_log(command, problem, exc_info=None): with open(_worker_log_filename) as f: worker_log = f.read() except Exception: - reason += fmt(", but log {0} could not be retrieved.", _worker_log_filename) + reason += f", but log {_worker_log_filename} could not be retrieved." else: - reason += fmt("; watchdog worker process log:\n\n{0}", worker_log) + reason += f"; watchdog worker process log:\n\n{worker_log}" if exc_info is None: log.error("{0}", reason) @@ -91,7 +91,7 @@ def _invoke(command, *args): try: _stream.write_json([command] + list(args)) response = _stream.read_json() - assert response == ["ok"], fmt("{0} {1!r}", _name, response) + assert response == ["ok"], f"{_name} {response!r}" finally: timeout.occurred = False except Exception: diff --git a/tests/watchdog/worker.py b/tests/watchdog/worker.py index 7ae185fc..6e9a4fcf 100644 --- a/tests/watchdog/worker.py +++ b/tests/watchdog/worker.py @@ -24,13 +24,13 @@ def main(tests_pid): if "" in sys.path: sys.path.remove("") - from debugpy.common import fmt, log, messaging + from debugpy.common import log, messaging # log.stderr_levels |= {"info"} log.timestamp_format = "06.3f" log_file = log.to_file(prefix="tests.watchdog") - stream = messaging.JsonIOStream.from_stdio(fmt("tests-{0}", tests_pid)) + stream = messaging.JsonIOStream.from_stdio(f"tests-{tests_pid}") log.info("Spawned WatchDog-{0} for tests-{0}", tests_pid) tests_process = psutil.Process(tests_pid) stream.write_json(["watchdog", log_file.filename]) @@ -87,7 +87,7 @@ def main(tests_pid): spawned_processes.pop(pid, None) else: - raise AssertionError(fmt("Unknown watchdog command: {0!r}", command)) + raise AssertionError(f"Unknown watchdog command: {command!r}") stream.write_json(["ok"])