Skip to content

Commit 9543d56

Browse files
Allow using pydevd as a regular dependency.
1 parent 42853a9 commit 9543d56

File tree

6 files changed

+131
-78
lines changed

6 files changed

+131
-78
lines changed

setup.py

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,23 @@
1111
import sys
1212

1313

14+
BUNDLE_DEBUGPY = not (os.getenv("BUNDLE_DEBUGPY").strip().lower() == "0")
15+
1416
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
1517
import versioneer # noqa
1618

1719
del sys.path[0]
1820

1921
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "src"))
2022
import debugpy
21-
import debugpy._vendored
2223

23-
del sys.path[0]
24+
if BUNDLE_DEBUGPY:
25+
import debugpy._vendored
2426

27+
del sys.path[0]
2528

26-
PYDEVD_ROOT = debugpy._vendored.project_root("pydevd")
29+
if BUNDLE_DEBUGPY:
30+
PYDEVD_ROOT = debugpy._vendored.project_root("pydevd")
2731
DEBUGBY_ROOT = os.path.dirname(os.path.abspath(debugpy.__file__))
2832

2933

@@ -46,7 +50,7 @@ def get_buildplatform():
4650
# relevant setuptools versions.
4751
class ExtModules(list):
4852
def __bool__(self):
49-
return True
53+
return BUNDLE_DEBUGPY
5054

5155

5256
def override_build(cmds):
@@ -147,7 +151,28 @@ def tail_is(*suffixes):
147151

148152
cmds = versioneer.get_cmdclass()
149153
override_build(cmds)
150-
override_build_py(cmds)
154+
if BUNDLE_DEBUGPY:
155+
override_build_py(cmds)
156+
157+
data = {"debugpy": ["ThirdPartyNotices.txt"]}
158+
packages = [
159+
"debugpy",
160+
"debugpy.adapter",
161+
"debugpy.common",
162+
"debugpy.launcher",
163+
"debugpy.server",
164+
]
165+
if BUNDLE_DEBUGPY:
166+
data.update(
167+
{
168+
"debugpy._vendored": [
169+
# pydevd extensions must be built before this list can
170+
# be computed properly, so it is populated in the
171+
# overridden build_py.finalize_options().
172+
]
173+
}
174+
)
175+
packages.append("debugpy._vendored")
151176

152177
setuptools.setup(
153178
name="debugpy",
@@ -177,23 +202,10 @@ def tail_is(*suffixes):
177202
"License :: OSI Approved :: MIT License",
178203
],
179204
package_dir={"": "src"},
180-
packages=[
181-
"debugpy",
182-
"debugpy.adapter",
183-
"debugpy.common",
184-
"debugpy.launcher",
185-
"debugpy.server",
186-
"debugpy._vendored",
187-
],
188-
package_data={
189-
"debugpy": ["ThirdPartyNotices.txt"],
190-
"debugpy._vendored": [
191-
# pydevd extensions must be built before this list can be computed properly,
192-
# so it is populated in the overridden build_py.finalize_options().
193-
],
194-
},
205+
packages=packages,
206+
package_data=data,
195207
ext_modules=ExtModules(),
196-
has_ext_modules=lambda: True,
208+
has_ext_modules=lambda: BUNDLE_DEBUGPY,
197209
cmdclass=cmds,
198210
**extras
199211
)

src/debugpy/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@
2424

2525
import sys
2626

27+
try:
28+
import debugpy._vendored # noqa
29+
30+
is_pydevd_bundled = True
31+
except ImportError:
32+
is_pydevd_bundled = False
33+
2734
assert sys.version_info >= (3, 7), (
2835
"Python 3.6 and below is not supported by this version of debugpy; "
2936
"use debugpy 1.5.1 or earlier."

src/debugpy/_vendored/force_pydevd.py

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# for license information.
44

55
from importlib import import_module
6-
import os
76
import warnings
87

98
from . import check_modules, prefix_matcher, preimport, vendored
@@ -17,23 +16,12 @@
1716
# raise ImportError(msg)
1817
warnings.warn(msg + ':\n {}'.format('\n '.join(_unvendored)))
1918

20-
# If debugpy logging is enabled, enable it for pydevd as well
21-
if "DEBUGPY_LOG_DIR" in os.environ:
22-
os.environ[str("PYDEVD_DEBUG")] = str("True")
23-
os.environ[str("PYDEVD_DEBUG_FILE")] = os.environ["DEBUGPY_LOG_DIR"] + str("/debugpy.pydevd.log")
24-
25-
# Disable pydevd frame-eval optimizations only if unset, to allow opt-in.
26-
if "PYDEVD_USE_FRAME_EVAL" not in os.environ:
27-
os.environ[str("PYDEVD_USE_FRAME_EVAL")] = str("NO")
28-
2919
# Constants must be set before importing any other pydevd module
30-
# # due to heavy use of "from" in them.
20+
# due to heavy use of "from" in them.
3121
with warnings.catch_warnings():
3222
warnings.simplefilter("ignore", category=DeprecationWarning)
3323
with vendored('pydevd'):
3424
pydevd_constants = import_module('_pydevd_bundle.pydevd_constants')
35-
# We limit representation size in our representation provider when needed.
36-
pydevd_constants.MAXIMUM_VARIABLE_REPRESENTATION_SIZE = 2 ** 32
3725

3826
# Now make sure all the top-level modules and packages in pydevd are
3927
# loaded. Any pydevd modules that aren't loaded at this point, will
@@ -50,32 +38,3 @@
5038
'pydevd_plugins',
5139
'pydevd',
5240
])
53-
54-
# When pydevd is imported it sets the breakpoint behavior, but it needs to be
55-
# overridden because by default pydevd will connect to the remote debugger using
56-
# its own custom protocol rather than DAP.
57-
import pydevd # noqa
58-
import debugpy # noqa
59-
60-
61-
def debugpy_breakpointhook():
62-
debugpy.breakpoint()
63-
64-
65-
pydevd.install_breakpointhook(debugpy_breakpointhook)
66-
67-
# Ensure that pydevd uses JSON protocol
68-
from _pydevd_bundle import pydevd_constants
69-
from _pydevd_bundle import pydevd_defaults
70-
pydevd_defaults.PydevdCustomization.DEFAULT_PROTOCOL = pydevd_constants.HTTP_JSON_PROTOCOL
71-
72-
# Enable some defaults related to debugpy such as sending a single notification when
73-
# threads pause and stopping on any exception.
74-
pydevd_defaults.PydevdCustomization.DEBUG_MODE = 'debugpy-dap'
75-
76-
# This is important when pydevd attaches automatically to a subprocess. In this case, we have to
77-
# make sure that debugpy is properly put back in the game for users to be able to use it.
78-
pydevd_defaults.PydevdCustomization.PREIMPORT = '%s;%s' % (
79-
os.path.dirname(os.path.dirname(debugpy.__file__)),
80-
'debugpy._vendored.force_pydevd'
81-
)

src/debugpy/server/__init__.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,70 @@
22
# Licensed under the MIT License. See LICENSE in the project root
33
# for license information.
44

5+
from __future__ import absolute_import, division, print_function, unicode_literals
6+
7+
from importlib import import_module
8+
import os
9+
510
# "force_pydevd" must be imported first to ensure (via side effects)
611
# that the debugpy-vendored copy of pydevd gets used.
7-
import debugpy._vendored.force_pydevd # noqa
12+
import debugpy
13+
14+
# If debugpy logging is enabled, enable it for pydevd as well
15+
if "DEBUGPY_LOG_DIR" in os.environ:
16+
os.environ[str("PYDEVD_DEBUG")] = str("True")
17+
os.environ[str("PYDEVD_DEBUG_FILE")] = os.environ["DEBUGPY_LOG_DIR"] + str(
18+
"/debugpy.pydevd.log"
19+
)
20+
21+
# Disable pydevd frame-eval optimizations only if unset, to allow opt-in.
22+
if "PYDEVD_USE_FRAME_EVAL" not in os.environ:
23+
os.environ[str("PYDEVD_USE_FRAME_EVAL")] = str("NO")
24+
25+
BUNDLE_DEBUGPY = bool(os.getenv("BUNDLE_DEBUGPY"))
26+
27+
# Constants must be set before importing any other pydevd module
28+
# due to heavy use of "from" in them.
29+
if BUNDLE_DEBUGPY:
30+
try:
31+
import debugpy._vendored.force_pydevd # noqa
32+
except Exception as e:
33+
raise e
34+
else:
35+
pydevd_constants = import_module("_pydevd_bundle.pydevd_constants")
36+
37+
# We limit representation size in our representation provider when needed.
38+
pydevd_constants.MAXIMUM_VARIABLE_REPRESENTATION_SIZE = 2**32
39+
40+
# When pydevd is imported it sets the breakpoint behavior, but it needs to be
41+
# overridden because by default pydevd will connect to the remote debugger using
42+
# its own custom protocol rather than DAP.
43+
import pydevd # noqa
44+
import debugpy # noqa
45+
46+
47+
def debugpy_breakpointhook():
48+
debugpy.breakpoint()
49+
50+
51+
pydevd.install_breakpointhook(debugpy_breakpointhook)
52+
53+
# Ensure that pydevd uses JSON protocol
54+
from _pydevd_bundle import pydevd_constants
55+
from _pydevd_bundle import pydevd_defaults
56+
57+
pydevd_defaults.PydevdCustomization.DEFAULT_PROTOCOL = (
58+
pydevd_constants.HTTP_JSON_PROTOCOL
59+
)
60+
61+
# Enable some defaults related to debugpy such as sending a single notification when
62+
# threads pause and stopping on any exception.
63+
pydevd_defaults.PydevdCustomization.DEBUG_MODE = "debugpy-dap"
64+
65+
# This is important when pydevd attaches automatically to a subprocess. In this case, we have to
66+
# make sure that debugpy is properly put back in the game for users to be able to use it.
67+
if not BUNDLE_DEBUGPY:
68+
pydevd_defaults.PydevdCustomization.PREIMPORT = "%s;%s" % (
69+
os.path.dirname(os.path.dirname(debugpy.__file__)),
70+
"debugpy._vendored.force_pydevd",
71+
)

src/debugpy/server/attach_pid_injected.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import os
88

9+
import debugpy
910

1011
__file__ = os.path.abspath(__file__)
1112
_debugpy_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
@@ -28,25 +29,29 @@ def on_exception(msg):
2829
def on_critical(msg):
2930
print(msg, file=sys.stderr)
3031

31-
pydevd_attach_to_process_path = os.path.join(
32-
_debugpy_dir,
33-
"debugpy",
34-
"_vendored",
35-
"pydevd",
36-
"pydevd_attach_to_process",
37-
)
38-
assert os.path.exists(pydevd_attach_to_process_path)
39-
sys.path.insert(0, pydevd_attach_to_process_path)
40-
41-
# NOTE: that it's not a part of the pydevd PYTHONPATH
42-
import attach_script
32+
if not debugpy.is_pydevd_bundled:
33+
from pydevd_attach_to_process import attach_script
34+
else:
35+
pydevd_attach_to_process_path = os.path.join(
36+
_debugpy_dir,
37+
"debugpy",
38+
"_vendored",
39+
"pydevd",
40+
"pydevd_attach_to_process",
41+
)
42+
assert os.path.exists(pydevd_attach_to_process_path)
43+
sys.path.insert(0, pydevd_attach_to_process_path)
44+
45+
# NOTE: that it's not a part of the pydevd PYTHONPATH
46+
import attach_script
4347

4448
attach_script.fix_main_thread_id(
4549
on_warn=on_warn, on_exception=on_exception, on_critical=on_critical
4650
)
4751

48-
# NOTE: At this point it should be safe to remove this.
49-
sys.path.remove(pydevd_attach_to_process_path)
52+
if debugpy.is_pydevd_bundled:
53+
# NOTE: At this point it should be safe to remove this.
54+
sys.path.remove(pydevd_attach_to_process_path)
5055
except:
5156
import traceback
5257

tests/tests/test_vendoring.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
# for license information.
44

55

6+
import pytest
7+
8+
import debugpy
9+
10+
11+
@pytest.mark.skipif(not debugpy.is_pydevd_bundled, reason="pydevd is not bundled")
612
def test_vendoring(pyfile):
713
@pyfile
814
def import_debugpy():

0 commit comments

Comments
 (0)