Skip to content
Merged
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
32 changes: 12 additions & 20 deletions src/debugpy/common/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import atexit
import contextlib
import functools
from importlib import metadata as importlib_metadata
import inspect
import io
import os
Expand All @@ -16,7 +17,6 @@
import debugpy
from debugpy.common import json, timestamp, util


LEVELS = ("debug", "info", "warning", "error")
"""Logging levels, lowest to highest importance.
"""
Expand Down Expand Up @@ -284,6 +284,7 @@ def get_environment_description(header):
import site # noqa

result = [header, "\n\n"]
missing = object()

def report(s, *args, **kwargs):
result.append(s.format(*args, **kwargs))
Expand All @@ -308,6 +309,10 @@ def report_paths(get_paths, label=None):
)
return

if paths is missing:
report("{0}<missing>\n", prefix)
return

if not isinstance(paths, (list, tuple)):
paths = [paths]

Expand All @@ -325,7 +330,7 @@ def report_paths(get_paths, label=None):
report_paths("sys.executable")
report_paths("sys.prefix")
report_paths("sys.base_prefix")
report_paths("sys.real_prefix")
report_paths(lambda: getattr(sys, "real_prefix", missing), "sys.real_prefix")
report_paths("site.getsitepackages()")
report_paths("site.getusersitepackages()")

Expand All @@ -345,25 +350,12 @@ def report_paths(get_paths, label=None):
report_paths("debugpy.__file__")
report("\n")

importlib_metadata = None
report("Installed packages:\n")
try:
import importlib_metadata
except ImportError: # pragma: no cover
try:
from importlib import metadata as importlib_metadata
except ImportError:
pass
if importlib_metadata is None: # pragma: no cover
report("Cannot enumerate installed packages - missing importlib_metadata.")
else:
report("Installed packages:\n")
try:
for pkg in importlib_metadata.distributions():
report(" {0}=={1}\n", pkg.name, pkg.version)
except Exception: # pragma: no cover
swallow_exception(
"Error while enumerating installed packages.", level="info"
)
for pkg in importlib_metadata.distributions():
report(" {0}=={1}\n", pkg.name, pkg.version)
except Exception: # pragma: no cover
swallow_exception("Error while enumerating installed packages.", level="info")

return "".join(result).rstrip("\n")

Expand Down
37 changes: 37 additions & 0 deletions tests/debugpy/test_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,49 @@
# for license information.

import contextlib
import os
import sys

import pytest

import debugpy
from debugpy.common import log
from tests import debug
from tests.debug import runners, targets


def test_environment_description_does_not_raise_internal_exceptions(monkeypatch):
from importlib import metadata as importlib_metadata

monkeypatch.delattr(sys, "real_prefix", raising=False)
monkeypatch.setattr(importlib_metadata, "distributions", lambda: ())

debugpy_root = os.path.dirname(debugpy.__file__)
real_prefix_exceptions = []

def trace(frame, event, arg):
# Only record AttributeErrors that mention ``real_prefix``. ``exception``
# events fire for every exception raised in a debugpy frame, including
# ones that unrelated environment probes intentionally raise and catch
# (e.g. ``site.getsitepackages()``), so an unfiltered assertion would be
# environment-dependent. Narrow it to the behavior this test targets.
if event == "exception" and frame.f_code.co_filename.startswith(debugpy_root):
exc = arg[1]
if isinstance(exc, AttributeError) and "real_prefix" in str(exc):
real_prefix_exceptions.append(exc)
return trace

previous_trace = sys.gettrace()
sys.settrace(trace)
try:
description = log.get_environment_description("Environment:")
finally:
sys.settrace(previous_trace)

assert "sys.real_prefix: <missing>" in description
assert real_prefix_exceptions == []


@contextlib.contextmanager
def check_logs(tmpdir, run):
# For attach_pid, there's ptvsd.server process that performs the injection,
Expand Down
Loading