Skip to content

Commit c9edf76

Browse files
Allow using pydevd as a regular dependency.
1 parent 6a7f7c0 commit c9edf76

File tree

6 files changed

+132
-78
lines changed

6 files changed

+132
-78
lines changed

setup.py

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

1313

14+
# pydevd is unbundled iff BUNDLE_DEBUGPY is set to 0
15+
BUNDLE_DEBUGPY = bool(os.getenv("BUNDLE_DEBUGPY", "1").strip().lower() != "0")
16+
17+
1418
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
1519
import versioneer # noqa
1620

1721
del sys.path[0]
1822

1923
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "src"))
2024
import debugpy
21-
import debugpy._vendored
2225

23-
del sys.path[0]
26+
if BUNDLE_DEBUGPY:
27+
import debugpy._vendored
2428

29+
del sys.path[0]
2530

26-
PYDEVD_ROOT = debugpy._vendored.project_root("pydevd")
31+
if BUNDLE_DEBUGPY:
32+
PYDEVD_ROOT = debugpy._vendored.project_root("pydevd")
2733
DEBUGBY_ROOT = os.path.dirname(os.path.abspath(debugpy.__file__))
2834

2935

@@ -46,7 +52,7 @@ def get_buildplatform():
4652
# relevant setuptools versions.
4753
class ExtModules(list):
4854
def __bool__(self):
49-
return True
55+
return BUNDLE_DEBUGPY
5056

5157

5258
def override_build(cmds):
@@ -147,7 +153,28 @@ def tail_is(*suffixes):
147153

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

152179
setuptools.setup(
153180
name="debugpy",
@@ -177,23 +204,10 @@ def tail_is(*suffixes):
177204
"License :: OSI Approved :: MIT License",
178205
],
179206
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-
},
207+
packages=packages,
208+
package_data=data,
195209
ext_modules=ExtModules(),
196-
has_ext_modules=lambda: True,
210+
has_ext_modules=lambda: BUNDLE_DEBUGPY,
197211
cmdclass=cmds,
198212
# allow the user to call "debugpy" instead of "python -m debugpy"
199213
entry_points={"console_scripts": ["debugpy = debugpy.server.cli:main"]},

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: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,69 @@
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+
# Constants must be set before importing any other pydevd module
26+
# due to heavy use of "from" in them.
27+
if debugpy.is_pydevd_bundled:
28+
try:
29+
import debugpy._vendored.force_pydevd # noqa
30+
pydevd_constants = debugpy._vendored.force_pydevd.pydevd_constants
31+
except Exception as e:
32+
raise e
33+
else:
34+
pydevd_constants = import_module("_pydevd_bundle.pydevd_constants")
35+
36+
# We limit representation size in our representation provider when needed.
37+
pydevd_constants.MAXIMUM_VARIABLE_REPRESENTATION_SIZE = 2**32
38+
39+
# When pydevd is imported it sets the breakpoint behavior, but it needs to be
40+
# overridden because by default pydevd will connect to the remote debugger using
41+
# its own custom protocol rather than DAP.
42+
import pydevd # noqa
43+
import debugpy # noqa
44+
45+
46+
def debugpy_breakpointhook():
47+
debugpy.breakpoint()
48+
49+
50+
pydevd.install_breakpointhook(debugpy_breakpointhook)
51+
52+
# Ensure that pydevd uses JSON protocol
53+
from _pydevd_bundle import pydevd_constants
54+
from _pydevd_bundle import pydevd_defaults
55+
56+
pydevd_defaults.PydevdCustomization.DEFAULT_PROTOCOL = (
57+
pydevd_constants.HTTP_JSON_PROTOCOL
58+
)
59+
60+
# Enable some defaults related to debugpy such as sending a single notification when
61+
# threads pause and stopping on any exception.
62+
pydevd_defaults.PydevdCustomization.DEBUG_MODE = "debugpy-dap"
63+
64+
# This is important when pydevd attaches automatically to a subprocess. In this case, we have to
65+
# make sure that debugpy is properly put back in the game for users to be able to use it.
66+
if not(debugpy.is_pydevd_bundled):
67+
pydevd_defaults.PydevdCustomization.PREIMPORT = "%s;%s" % (
68+
os.path.dirname(os.path.dirname(debugpy.__file__)),
69+
"debugpy._vendored.force_pydevd",
70+
)

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)