briefcase-debugger 0.3.26__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2015 Russell Keith-Magee.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+
14
+ 3. Neither the name of Briefcase-Debugger nor the names of its contributors may
15
+ be used to endorse or promote products derived from this software without
16
+ specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: briefcase-debugger
3
+ Version: 0.3.26
4
+ Summary: A Briefcase plugin adding remote debugging support for PDB and Visual Studio Code.
5
+ License-Expression: BSD-3-Clause
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Provides-Extra: pdb
9
+ Requires-Dist: remote-pdb<3.0.0,>=2.1.0; extra == "pdb"
10
+ Provides-Extra: debugpy
11
+ Requires-Dist: debugpy<2.0.0,>=1.8.17; extra == "debugpy"
12
+ Dynamic: license-file
13
+
14
+ # Briefcase Debugger Support
15
+ [![Python Versions](https://img.shields.io/pypi/pyversions/briefcase-debugger.svg)](https://pypi.python.org/pypi/briefcase-debugger)
16
+ [![PyPI Version](https://img.shields.io/pypi/v/briefcase-debugger.svg)](https://pypi.python.org/pypi/briefcase-debugger)
17
+ [![Maturity](https://img.shields.io/pypi/status/briefcase-debugger.svg)](https://pypi.python.org/pypi/briefcase-debugger)
18
+ [![BSD License](https://img.shields.io/pypi/l/briefcase-debugger.svg)](https://github.com/beeware/briefcase/blob/main/debugger/LICENSE)
19
+ [![Build Status](https://github.com/beeware/briefcase/workflows/CI/badge.svg?branch=main)](https://github.com/beeware/briefcase/actions)
20
+ [![Discord server](https://img.shields.io/discord/836455665257021440?label=Discord%20Chat&logo=discord&style=plastic)](https://beeware.org/bee/chat/)
21
+
22
+ This package contains the debugger support package for the `pdb` and `debugpy` debuggers.
23
+
24
+ It starts the remote debugger automatically at startup through an .pth file, if a `BRIEFCASE_DEBUGGER` environment variable is set.
25
+
26
+ ## Installation
27
+ As an end-user, you won't normally need to install this package. It will be installed automatically by Briefcase if you specify the `--debug=pdb` or `--debug=debugpy` option when running your application.
28
+
29
+ ## Financial support
30
+
31
+ The BeeWare project would not be possible without the generous support
32
+ of our financial members:
33
+
34
+ [![Anaconda logo](https://beeware.org/community/members/anaconda/anaconda-large.png)](https://anaconda.com/)
35
+
36
+ Anaconda Inc. - Advancing AI through open source.
37
+
38
+ Plus individual contributions from [users like
39
+ you](https://beeware.org/community/members/). If you find Briefcase, or
40
+ other BeeWare tools useful, please consider becoming a financial member.
41
+
42
+ ## Documentation
43
+
44
+ Documentation for Briefcase can be found on [Read The
45
+ Docs](https://briefcase.readthedocs.io).
46
+
47
+ ## Community
48
+
49
+ Briefcase is part of the [BeeWare suite](https://beeware.org). You can
50
+ talk to the community through:
51
+
52
+ - [@beeware@fosstodon.org on Mastodon](https://fosstodon.org/@beeware)
53
+ - [Discord](https://beeware.org/bee/chat/)
54
+ - The Briefcase [GitHub Discussions
55
+ forum](https://github.com/beeware/briefcase/discussions)
56
+
57
+ We foster a welcoming and respectful community as described in our
58
+ [BeeWare Community Code of
59
+ Conduct](https://beeware.org/community/behavior/).
60
+
61
+ ## Contributing
62
+
63
+ If you experience problems with Briefcase, [log them on
64
+ GitHub](https://github.com/beeware/briefcase/issues).
65
+
66
+ If you'd like to contribute to Briefcase development, our [contribution
67
+ guide](https://briefcase.readthedocs.io/en/latest/how-to/contribute/index.html)
68
+ details how to set up a development environment, and other requirements
69
+ we have as part of our contribution process.
@@ -0,0 +1,56 @@
1
+ # Briefcase Debugger Support
2
+ [![Python Versions](https://img.shields.io/pypi/pyversions/briefcase-debugger.svg)](https://pypi.python.org/pypi/briefcase-debugger)
3
+ [![PyPI Version](https://img.shields.io/pypi/v/briefcase-debugger.svg)](https://pypi.python.org/pypi/briefcase-debugger)
4
+ [![Maturity](https://img.shields.io/pypi/status/briefcase-debugger.svg)](https://pypi.python.org/pypi/briefcase-debugger)
5
+ [![BSD License](https://img.shields.io/pypi/l/briefcase-debugger.svg)](https://github.com/beeware/briefcase/blob/main/debugger/LICENSE)
6
+ [![Build Status](https://github.com/beeware/briefcase/workflows/CI/badge.svg?branch=main)](https://github.com/beeware/briefcase/actions)
7
+ [![Discord server](https://img.shields.io/discord/836455665257021440?label=Discord%20Chat&logo=discord&style=plastic)](https://beeware.org/bee/chat/)
8
+
9
+ This package contains the debugger support package for the `pdb` and `debugpy` debuggers.
10
+
11
+ It starts the remote debugger automatically at startup through an .pth file, if a `BRIEFCASE_DEBUGGER` environment variable is set.
12
+
13
+ ## Installation
14
+ As an end-user, you won't normally need to install this package. It will be installed automatically by Briefcase if you specify the `--debug=pdb` or `--debug=debugpy` option when running your application.
15
+
16
+ ## Financial support
17
+
18
+ The BeeWare project would not be possible without the generous support
19
+ of our financial members:
20
+
21
+ [![Anaconda logo](https://beeware.org/community/members/anaconda/anaconda-large.png)](https://anaconda.com/)
22
+
23
+ Anaconda Inc. - Advancing AI through open source.
24
+
25
+ Plus individual contributions from [users like
26
+ you](https://beeware.org/community/members/). If you find Briefcase, or
27
+ other BeeWare tools useful, please consider becoming a financial member.
28
+
29
+ ## Documentation
30
+
31
+ Documentation for Briefcase can be found on [Read The
32
+ Docs](https://briefcase.readthedocs.io).
33
+
34
+ ## Community
35
+
36
+ Briefcase is part of the [BeeWare suite](https://beeware.org). You can
37
+ talk to the community through:
38
+
39
+ - [@beeware@fosstodon.org on Mastodon](https://fosstodon.org/@beeware)
40
+ - [Discord](https://beeware.org/bee/chat/)
41
+ - The Briefcase [GitHub Discussions
42
+ forum](https://github.com/beeware/briefcase/discussions)
43
+
44
+ We foster a welcoming and respectful community as described in our
45
+ [BeeWare Community Code of
46
+ Conduct](https://beeware.org/community/behavior/).
47
+
48
+ ## Contributing
49
+
50
+ If you experience problems with Briefcase, [log them on
51
+ GitHub](https://github.com/beeware/briefcase/issues).
52
+
53
+ If you'd like to contribute to Briefcase development, our [contribution
54
+ guide](https://briefcase.readthedocs.io/en/latest/how-to/contribute/index.html)
55
+ details how to set up a development environment, and other requirements
56
+ we have as part of our contribution process.
@@ -0,0 +1,55 @@
1
+ [build-system]
2
+ requires = [
3
+ # keep versions in sync with automation/pyproject.toml and ../pyproject.toml
4
+ "setuptools==80.9.0",
5
+ "setuptools_scm==9.2.2",
6
+ ]
7
+ build-backend = "setuptools.build_meta"
8
+
9
+ [project]
10
+ name = "briefcase-debugger"
11
+ description = "A Briefcase plugin adding remote debugging support for PDB and Visual Studio Code."
12
+ readme = "README.md"
13
+ license = "BSD-3-Clause"
14
+ license-files = ["LICENSE"]
15
+ dependencies = [
16
+ # see "pdb" or "debugpy" optional dependencies below
17
+ ]
18
+ dynamic = ["version"]
19
+
20
+ [project.optional-dependencies]
21
+ pdb = ["remote-pdb>=2.1.0,<3.0.0"]
22
+ debugpy = ["debugpy>=1.8.17,<2.0.0"]
23
+
24
+ [dependency-groups]
25
+ test = [
26
+ "coverage[toml] == 7.12.0",
27
+ "pytest == 9.0.1",
28
+ "pytest-xdist == 3.8.0",
29
+ ]
30
+
31
+ [tool.coverage.run]
32
+ parallel = true
33
+ branch = true
34
+ relative_files = true
35
+ source_pkgs = ["briefcase_debugger"]
36
+
37
+ [tool.coverage.paths]
38
+ source = [
39
+ "src",
40
+ "**/site-packages",
41
+ ]
42
+
43
+ [tool.coverage.report]
44
+ show_missing = true
45
+ skip_covered = true
46
+ skip_empty = true
47
+ precision = 1
48
+
49
+ [tool.ruff.lint]
50
+ ignore = [
51
+ "T20", # flake8-print (useful for briefcase, but not in the debugger)
52
+ ]
53
+
54
+ [tool.setuptools_scm]
55
+ root = "../"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,44 @@
1
+ import os
2
+
3
+ import setuptools
4
+ from setuptools.command.install import install
5
+
6
+
7
+ # Copied from setuptools:
8
+ # (https://github.com/pypa/setuptools/blob/7c859e017368360ba66c8cc591279d8964c031bc/setup.py#L40C6-L82)
9
+ class install_with_pth(install):
10
+ """Custom install command to install a .pth file for distutils patching.
11
+
12
+ This hack is necessary because there's no standard way to install behavior
13
+ on startup (and it's debatable if there should be one). This hack (ab)uses
14
+ the `extra_path` behavior in Setuptools to install a `.pth` file with
15
+ implicit behavior on startup to give higher precedence to the local version
16
+ of `distutils` over the version from the standard library.
17
+
18
+ Please do not replicate this behavior.
19
+ """
20
+
21
+ _pth_name = "briefcase_debugger"
22
+ _pth_contents = (
23
+ "import briefcase_debugger; briefcase_debugger.start_remote_debugger()"
24
+ )
25
+
26
+ def initialize_options(self):
27
+ install.initialize_options(self)
28
+ self.extra_path = self._pth_name, self._pth_contents
29
+
30
+ def finalize_options(self):
31
+ install.finalize_options(self)
32
+ self._restore_install_lib()
33
+
34
+ def _restore_install_lib(self):
35
+ """Undo secondary effect of `extra_path` adding to `install_lib`"""
36
+ suffix = os.path.relpath(self.install_lib, self.install_libbase)
37
+
38
+ if suffix.strip() == self._pth_contents.strip():
39
+ self.install_lib = self.install_libbase
40
+
41
+
42
+ setuptools.setup(
43
+ cmdclass={"install": install_with_pth},
44
+ )
@@ -0,0 +1,44 @@
1
+ import json
2
+ import os
3
+ import sys
4
+ import traceback
5
+
6
+
7
+ def start_remote_debugger():
8
+ try:
9
+ # check verbose output
10
+ verbose = os.environ.get("BRIEFCASE_DEBUG", "0") == "1"
11
+
12
+ # reading config
13
+ config_str = os.environ.get("BRIEFCASE_DEBUGGER", None)
14
+
15
+ # skip debugger if no config is set
16
+ if config_str is None:
17
+ if verbose:
18
+ print(
19
+ "No 'BRIEFCASE_DEBUGGER' environment variable found. Debugger not starting."
20
+ )
21
+ return # If BRIEFCASE_DEBUGGER is not set, this packages does nothing...
22
+
23
+ if verbose:
24
+ print(f"'BRIEFCASE_DEBUGGER'={config_str}")
25
+
26
+ # Parsing config json
27
+ config = json.loads(config_str)
28
+
29
+ # start debugger
30
+ print("Starting remote debugger...")
31
+ if config["debugger"] == "debugpy":
32
+ from briefcase_debugger.debugpy import start_debugpy
33
+
34
+ start_debugpy(config, verbose)
35
+ elif config["debugger"] == "pdb":
36
+ from briefcase_debugger.pdb import start_pdb
37
+
38
+ start_pdb(config, verbose)
39
+ else:
40
+ raise ValueError(f"Unknown debugger '{config['debugger']}'")
41
+ except Exception:
42
+ # Show exception and stop the whole application when an error occurs
43
+ print(traceback.format_exc())
44
+ sys.exit(-1)
@@ -0,0 +1,21 @@
1
+ from typing import TypedDict
2
+
3
+
4
+ class AppPathMappings(TypedDict):
5
+ device_sys_path_regex: str
6
+ device_subfolders: list[str]
7
+ host_folders: list[str]
8
+
9
+
10
+ class AppPackagesPathMappings(TypedDict):
11
+ sys_path_regex: str
12
+ host_folder: str
13
+
14
+
15
+ class DebuggerConfig(TypedDict):
16
+ debugger: str
17
+ host: str
18
+ port: int
19
+ host_os: str
20
+ app_path_mappings: AppPathMappings | None
21
+ app_packages_path_mappings: AppPackagesPathMappings | None
@@ -0,0 +1,108 @@
1
+ import re
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ import debugpy
6
+
7
+ from briefcase_debugger.config import DebuggerConfig
8
+
9
+
10
+ def find_first_matching_path(regex: str) -> str:
11
+ """Returns the first element of sys.paths that matches regex, otherwise None."""
12
+ for path in sys.path:
13
+ if re.search(regex, path):
14
+ return path
15
+ raise ValueError(f"No sys.path entry matches regex '{regex}'")
16
+
17
+
18
+ def load_path_mappings(config: DebuggerConfig, verbose: bool) -> list[tuple[str, str]]:
19
+ app_path_mappings = config.get("app_path_mappings", None)
20
+ app_packages_path_mappings = config.get("app_packages_path_mappings", None)
21
+
22
+ mappings_list = []
23
+ if app_path_mappings:
24
+ device_app_folder = find_first_matching_path(
25
+ app_path_mappings["device_sys_path_regex"]
26
+ )
27
+ for app_subfolder_device, app_subfolder_host in zip(
28
+ app_path_mappings["device_subfolders"],
29
+ app_path_mappings["host_folders"],
30
+ strict=False,
31
+ ):
32
+ mappings_list.append(
33
+ (
34
+ app_subfolder_host,
35
+ str(Path(device_app_folder) / app_subfolder_device),
36
+ )
37
+ )
38
+ if app_packages_path_mappings:
39
+ device_app_packages_folder = find_first_matching_path(
40
+ app_packages_path_mappings["sys_path_regex"]
41
+ )
42
+ mappings_list.append(
43
+ (
44
+ app_packages_path_mappings["host_folder"],
45
+ str(Path(device_app_packages_folder)),
46
+ )
47
+ )
48
+
49
+ if verbose:
50
+ print("Extracted path mappings:")
51
+ for idx, mapping in enumerate(mappings_list):
52
+ print(f"[{idx}] host = {mapping[0]}")
53
+ print(f"[{idx}] device = {mapping[1]}")
54
+
55
+ return mappings_list
56
+
57
+
58
+ def start_debugpy(config: DebuggerConfig, verbose: bool):
59
+ host = config["host"]
60
+ port = config["port"]
61
+ path_mappings = load_path_mappings(config, verbose)
62
+
63
+ # Starting remote debugger...
64
+ print(f"Starting debugpy in server mode at {host}:{port}...")
65
+ debugpy.listen((host, port), in_process_debug_adapter=True)
66
+
67
+ if verbose:
68
+ # pydevd is dynamically loaded and only available after debugpy is started
69
+ import pydevd
70
+
71
+ pydevd.DebugInfoHolder.DEBUG_TRACE_LEVEL = 3
72
+
73
+ if len(path_mappings) > 0:
74
+ if verbose:
75
+ print("Adding path mappings...")
76
+
77
+ # pydevd is dynamically loaded and only available after a debugger has connected
78
+ import pydevd_file_utils
79
+
80
+ pydevd_file_utils.setup_client_server_paths(path_mappings)
81
+
82
+ print("The debugpy server started. Waiting for debugger to attach...")
83
+ print(
84
+ f"""
85
+ To connect to debugpy using VS Code add the following configuration to '.vscode/launch.json':
86
+ {{
87
+ "version": "0.2.0",
88
+ "configurations": [
89
+ {{
90
+ "name": "Briefcase: Attach (Connect)",
91
+ "type": "debugpy",
92
+ "request": "attach",
93
+ "connect": {{
94
+ "host": "{host}",
95
+ "port": {port}
96
+ }},
97
+ "justMyCode": false
98
+ }}
99
+ ]
100
+ }}
101
+
102
+ For more information see: https://briefcase.beeware.org/en/stable/how-to/debugging/vscode/#bundled-app
103
+ """
104
+ )
105
+ debugpy.wait_for_client()
106
+
107
+ print("Debugger attached.")
108
+ print("-" * 75)
@@ -0,0 +1,44 @@
1
+ import sys
2
+
3
+ from remote_pdb import RemotePdb
4
+
5
+ from briefcase_debugger.config import DebuggerConfig
6
+
7
+
8
+ def start_pdb(config: DebuggerConfig, verbose: bool):
9
+ """Start remote PDB server."""
10
+ # Parsing host/port
11
+ host = config["host"]
12
+ port = config["port"]
13
+
14
+ # Print help message
15
+ host_os = config["host_os"]
16
+ telnet_cmd = f"telnet {host} {port}"
17
+ nc_cmd = f"nc {host} {port}"
18
+ if host_os == "Windows":
19
+ cmds_hint = f" {telnet_cmd}"
20
+ elif host_os in ("Linux", "Darwin"):
21
+ cmds_hint = f" {nc_cmd}"
22
+ else:
23
+ cmds_hint = f"""\
24
+ - {telnet_cmd}
25
+ - {nc_cmd}
26
+ """
27
+ print(f"""
28
+ Remote PDB server opened at {host}:{port}.
29
+ Waiting for debugger to attach...
30
+ To connect to remote PDB use for example:
31
+
32
+ {cmds_hint}
33
+
34
+ For more information see: https://briefcase.beeware.org/en/stable/how-to/debugging/pdb/#bundled-app
35
+ """)
36
+
37
+ # Create a RemotePdb instance
38
+ remote_pdb = RemotePdb(host, port, quiet=True)
39
+
40
+ # Connect the remote PDB with the "breakpoint()" function
41
+ sys.breakpointhook = remote_pdb.set_trace
42
+
43
+ print("Debugger client attached.")
44
+ print("-" * 75)
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: briefcase-debugger
3
+ Version: 0.3.26
4
+ Summary: A Briefcase plugin adding remote debugging support for PDB and Visual Studio Code.
5
+ License-Expression: BSD-3-Clause
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Provides-Extra: pdb
9
+ Requires-Dist: remote-pdb<3.0.0,>=2.1.0; extra == "pdb"
10
+ Provides-Extra: debugpy
11
+ Requires-Dist: debugpy<2.0.0,>=1.8.17; extra == "debugpy"
12
+ Dynamic: license-file
13
+
14
+ # Briefcase Debugger Support
15
+ [![Python Versions](https://img.shields.io/pypi/pyversions/briefcase-debugger.svg)](https://pypi.python.org/pypi/briefcase-debugger)
16
+ [![PyPI Version](https://img.shields.io/pypi/v/briefcase-debugger.svg)](https://pypi.python.org/pypi/briefcase-debugger)
17
+ [![Maturity](https://img.shields.io/pypi/status/briefcase-debugger.svg)](https://pypi.python.org/pypi/briefcase-debugger)
18
+ [![BSD License](https://img.shields.io/pypi/l/briefcase-debugger.svg)](https://github.com/beeware/briefcase/blob/main/debugger/LICENSE)
19
+ [![Build Status](https://github.com/beeware/briefcase/workflows/CI/badge.svg?branch=main)](https://github.com/beeware/briefcase/actions)
20
+ [![Discord server](https://img.shields.io/discord/836455665257021440?label=Discord%20Chat&logo=discord&style=plastic)](https://beeware.org/bee/chat/)
21
+
22
+ This package contains the debugger support package for the `pdb` and `debugpy` debuggers.
23
+
24
+ It starts the remote debugger automatically at startup through an .pth file, if a `BRIEFCASE_DEBUGGER` environment variable is set.
25
+
26
+ ## Installation
27
+ As an end-user, you won't normally need to install this package. It will be installed automatically by Briefcase if you specify the `--debug=pdb` or `--debug=debugpy` option when running your application.
28
+
29
+ ## Financial support
30
+
31
+ The BeeWare project would not be possible without the generous support
32
+ of our financial members:
33
+
34
+ [![Anaconda logo](https://beeware.org/community/members/anaconda/anaconda-large.png)](https://anaconda.com/)
35
+
36
+ Anaconda Inc. - Advancing AI through open source.
37
+
38
+ Plus individual contributions from [users like
39
+ you](https://beeware.org/community/members/). If you find Briefcase, or
40
+ other BeeWare tools useful, please consider becoming a financial member.
41
+
42
+ ## Documentation
43
+
44
+ Documentation for Briefcase can be found on [Read The
45
+ Docs](https://briefcase.readthedocs.io).
46
+
47
+ ## Community
48
+
49
+ Briefcase is part of the [BeeWare suite](https://beeware.org). You can
50
+ talk to the community through:
51
+
52
+ - [@beeware@fosstodon.org on Mastodon](https://fosstodon.org/@beeware)
53
+ - [Discord](https://beeware.org/bee/chat/)
54
+ - The Briefcase [GitHub Discussions
55
+ forum](https://github.com/beeware/briefcase/discussions)
56
+
57
+ We foster a welcoming and respectful community as described in our
58
+ [BeeWare Community Code of
59
+ Conduct](https://beeware.org/community/behavior/).
60
+
61
+ ## Contributing
62
+
63
+ If you experience problems with Briefcase, [log them on
64
+ GitHub](https://github.com/beeware/briefcase/issues).
65
+
66
+ If you'd like to contribute to Briefcase development, our [contribution
67
+ guide](https://briefcase.readthedocs.io/en/latest/how-to/contribute/index.html)
68
+ details how to set up a development environment, and other requirements
69
+ we have as part of our contribution process.
@@ -0,0 +1,17 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ setup.py
5
+ src/briefcase_debugger/__init__.py
6
+ src/briefcase_debugger/config.py
7
+ src/briefcase_debugger/debugpy.py
8
+ src/briefcase_debugger/pdb.py
9
+ src/briefcase_debugger.egg-info/PKG-INFO
10
+ src/briefcase_debugger.egg-info/SOURCES.txt
11
+ src/briefcase_debugger.egg-info/dependency_links.txt
12
+ src/briefcase_debugger.egg-info/requires.txt
13
+ src/briefcase_debugger.egg-info/top_level.txt
14
+ tests/test_base.py
15
+ tests/test_debugpy.py
16
+ tests/test_debugpy_path_mappings.py
17
+ tests/test_pdb.py
@@ -0,0 +1,6 @@
1
+
2
+ [debugpy]
3
+ debugpy<2.0.0,>=1.8.17
4
+
5
+ [pdb]
6
+ remote-pdb<3.0.0,>=2.1.0
@@ -0,0 +1,39 @@
1
+ import importlib
2
+ import json
3
+ import os
4
+ import sys
5
+ from unittest.mock import MagicMock
6
+
7
+ import briefcase_debugger
8
+
9
+
10
+ def test_import_for_code_coverage(monkeypatch, capsys):
11
+ """Get 100% code coverage."""
12
+ # The module `briefcase_debugger` is already imported through the .pth
13
+ # file. Code executed during .pth files are not covered by coverage.py.
14
+ # So we need to reload the module to get a 100% code coverage.
15
+ importlib.reload(importlib.import_module("briefcase_debugger"))
16
+
17
+
18
+ def test_unknown_debugger(monkeypatch, capsys):
19
+ """An unknown debugger raises an error and stops the application."""
20
+ os_environ = {}
21
+ os_environ["BRIEFCASE_DEBUGGER"] = json.dumps(
22
+ {
23
+ "debugger": "unknown",
24
+ "host": "somehost",
25
+ "port": 9999,
26
+ "host_os": "Windows",
27
+ }
28
+ )
29
+ monkeypatch.setattr(os, "environ", os_environ)
30
+
31
+ fake_sys_exit = MagicMock()
32
+ monkeypatch.setattr(sys, "exit", fake_sys_exit)
33
+
34
+ briefcase_debugger.start_remote_debugger()
35
+
36
+ fake_sys_exit.assert_called_once_with(-1)
37
+
38
+ captured = capsys.readouterr()
39
+ assert "Unknown debugger" in captured.out
@@ -0,0 +1,197 @@
1
+ import json
2
+ import os
3
+ import sys
4
+ from unittest.mock import MagicMock
5
+
6
+ import briefcase_debugger
7
+ import debugpy
8
+ import pytest
9
+ from briefcase_debugger.config import AppPathMappings
10
+
11
+
12
+ def test_no_env_vars(monkeypatch, capsys):
13
+ """Nothing happens, when no env vars are set."""
14
+ os_environ = {}
15
+ monkeypatch.setattr(os, "environ", os_environ)
16
+
17
+ # start test function
18
+ briefcase_debugger.start_remote_debugger()
19
+
20
+ captured = capsys.readouterr()
21
+ assert captured.out == ""
22
+ assert captured.err == ""
23
+
24
+
25
+ def test_no_debugger_verbose(monkeypatch, capsys):
26
+ """Nothing happens except a short message, when only verbose is requested."""
27
+ os_environ = {}
28
+ os_environ["BRIEFCASE_DEBUG"] = "1"
29
+ monkeypatch.setattr(os, "environ", os_environ)
30
+
31
+ # start test function
32
+ briefcase_debugger.start_remote_debugger()
33
+
34
+ captured = capsys.readouterr()
35
+ assert (
36
+ captured.out
37
+ == "No 'BRIEFCASE_DEBUGGER' environment variable found. Debugger not starting.\n"
38
+ )
39
+ assert captured.err == ""
40
+
41
+
42
+ @pytest.mark.parametrize(
43
+ ("os_name", "app_path_mappings", "sys_path", "expected_path_mappings"),
44
+ [
45
+ (
46
+ "nt",
47
+ AppPathMappings(
48
+ device_sys_path_regex="app$",
49
+ device_subfolders=["helloworld"],
50
+ host_folders=["C:\\PROJECT_ROOT\\src\\helloworld"],
51
+ ),
52
+ ["C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\app"],
53
+ [
54
+ (
55
+ "C:\\PROJECT_ROOT\\src\\helloworld",
56
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\app\\helloworld",
57
+ )
58
+ ],
59
+ ),
60
+ (
61
+ "posix",
62
+ AppPathMappings(
63
+ device_sys_path_regex="app$",
64
+ device_subfolders=["helloworld"],
65
+ host_folders=["/PROJECT_ROOT/src/helloworld"],
66
+ ),
67
+ [
68
+ "/PROJECT_ROOT/build/helloworld/macos/app/Hello World.app/Contents/Resources/app"
69
+ ],
70
+ [
71
+ (
72
+ "/PROJECT_ROOT/src/helloworld",
73
+ "/PROJECT_ROOT/build/helloworld/macos/app/Hello World.app/Contents/Resources/app/helloworld",
74
+ )
75
+ ],
76
+ ),
77
+ ],
78
+ )
79
+ @pytest.mark.parametrize(
80
+ ("verbose", "some_verbose_output", "pydevd_trace_level"),
81
+ [
82
+ (True, "Extracted path mappings:\n[0] host = ", 3),
83
+ (False, "", 0),
84
+ ],
85
+ )
86
+ def test_with_debugger(
87
+ os_name: str,
88
+ app_path_mappings: AppPathMappings,
89
+ sys_path: list[str],
90
+ expected_path_mappings: list[tuple[str, str]],
91
+ verbose: bool,
92
+ some_verbose_output: str,
93
+ pydevd_trace_level: int,
94
+ monkeypatch,
95
+ capsys,
96
+ ):
97
+ """Normal debug session."""
98
+ if os.name != os_name:
99
+ pytest.skip(f"Test only runs on {os_name} systems")
100
+
101
+ os_environ = {}
102
+ os_environ["BRIEFCASE_DEBUG"] = "1" if verbose else "0"
103
+ os_environ["BRIEFCASE_DEBUGGER"] = json.dumps(
104
+ {
105
+ "debugger": "debugpy",
106
+ "host": "somehost",
107
+ "port": 9999,
108
+ "host_os": "SomeOS",
109
+ "app_path_mappings": app_path_mappings,
110
+ "app_packages_path_mappings": None,
111
+ }
112
+ )
113
+ monkeypatch.setattr(os, "environ", os_environ)
114
+
115
+ monkeypatch.setattr(sys, "path", sys_path)
116
+
117
+ fake_debugpy_listen = MagicMock()
118
+ monkeypatch.setattr(debugpy, "listen", fake_debugpy_listen)
119
+
120
+ fake_debugpy_wait_for_client = MagicMock()
121
+ monkeypatch.setattr(debugpy, "wait_for_client", fake_debugpy_wait_for_client)
122
+
123
+ # pydevd is dynamically loaded and only available when a real debugger is attached. So
124
+ # we fake the whole module, as otherwise the import in start_remote_debugger would fail
125
+ fake_pydevd = MagicMock()
126
+ monkeypatch.setitem(sys.modules, "pydevd", fake_pydevd)
127
+ fake_pydevd.DebugInfoHolder.DEBUG_TRACE_LEVEL = 0
128
+ fake_pydevd_file_utils = MagicMock()
129
+ fake_pydevd_file_utils.setup_client_server_paths.return_value = None
130
+ monkeypatch.setitem(sys.modules, "pydevd_file_utils", fake_pydevd_file_utils)
131
+
132
+ # start test function
133
+ briefcase_debugger.start_remote_debugger()
134
+
135
+ fake_debugpy_listen.assert_called_once_with(
136
+ ("somehost", 9999),
137
+ in_process_debug_adapter=True,
138
+ )
139
+
140
+ fake_debugpy_wait_for_client.assert_called_once()
141
+ fake_pydevd_file_utils.setup_client_server_paths.assert_called_once_with(
142
+ expected_path_mappings
143
+ )
144
+
145
+ captured = capsys.readouterr()
146
+ assert "Waiting for debugger to attach..." in captured.out
147
+ assert captured.err == ""
148
+
149
+ assert some_verbose_output in captured.out
150
+ assert pydevd_trace_level == fake_pydevd.DebugInfoHolder.DEBUG_TRACE_LEVEL
151
+
152
+
153
+ def test_with_debugger_without_path_mappings(monkeypatch, capsys):
154
+ """Debug session without path mappings."""
155
+ os_environ = {}
156
+ os_environ["BRIEFCASE_DEBUG"] = "0"
157
+ os_environ["BRIEFCASE_DEBUGGER"] = json.dumps(
158
+ {
159
+ "debugger": "debugpy",
160
+ "host": "somehost",
161
+ "port": 9999,
162
+ "host_os": "SomeOS",
163
+ "app_path_mappings": None,
164
+ "app_packages_path_mappings": None,
165
+ }
166
+ )
167
+ monkeypatch.setattr(os, "environ", os_environ)
168
+
169
+ fake_debugpy_listen = MagicMock()
170
+ monkeypatch.setattr(debugpy, "listen", fake_debugpy_listen)
171
+
172
+ fake_debugpy_wait_for_client = MagicMock()
173
+ monkeypatch.setattr(debugpy, "wait_for_client", fake_debugpy_wait_for_client)
174
+
175
+ # pydevd is dynamically loaded and only available when a real debugger is attached. So
176
+ # we fake the whole module, as otherwise the import in start_remote_debugger would fail
177
+ fake_pydevd = MagicMock()
178
+ monkeypatch.setitem(sys.modules, "pydevd", fake_pydevd)
179
+ fake_pydevd.DebugInfoHolder.DEBUG_TRACE_LEVEL = 0
180
+ fake_pydevd_file_utils = MagicMock()
181
+ fake_pydevd_file_utils.setup_client_server_paths.return_value = None
182
+ monkeypatch.setitem(sys.modules, "pydevd_file_utils", fake_pydevd_file_utils)
183
+
184
+ # start test function
185
+ briefcase_debugger.start_remote_debugger()
186
+
187
+ fake_debugpy_listen.assert_called_once_with(
188
+ ("somehost", 9999),
189
+ in_process_debug_adapter=True,
190
+ )
191
+
192
+ fake_debugpy_wait_for_client.assert_called_once()
193
+ fake_pydevd_file_utils.setup_client_server_paths.assert_not_called()
194
+
195
+ captured = capsys.readouterr()
196
+ assert "Waiting for debugger to attach..." in captured.out
197
+ assert captured.err == ""
@@ -0,0 +1,287 @@
1
+ import os
2
+ import sys
3
+
4
+ import briefcase_debugger.debugpy
5
+ import pytest
6
+ from briefcase_debugger.config import (
7
+ AppPackagesPathMappings,
8
+ AppPathMappings,
9
+ DebuggerConfig,
10
+ )
11
+
12
+
13
+ def test_mappings_not_existing():
14
+ """Complete empty config."""
15
+ path_mappings = briefcase_debugger.debugpy.load_path_mappings({}, False)
16
+ assert path_mappings == []
17
+
18
+
19
+ def test_mappings_none(monkeypatch):
20
+ """Config with no mappings set."""
21
+ config = DebuggerConfig(
22
+ debugger="debugpy",
23
+ host="",
24
+ port=0,
25
+ app_path_mappings=None,
26
+ app_packages_path_mappings=None,
27
+ )
28
+ path_mappings = briefcase_debugger.debugpy.load_path_mappings(config, False)
29
+ assert path_mappings == []
30
+
31
+
32
+ @pytest.mark.parametrize(
33
+ (
34
+ "os_name",
35
+ "app_path_mappings",
36
+ "app_packages_path_mappings",
37
+ "sys_path",
38
+ "expected_path_mappings",
39
+ ),
40
+ [
41
+ # Windows
42
+ pytest.param(
43
+ "nt",
44
+ AppPathMappings(
45
+ device_sys_path_regex="app$",
46
+ device_subfolders=["helloworld"],
47
+ host_folders=["C:\\PROJECT_ROOT\\src\\helloworld"],
48
+ ),
49
+ None,
50
+ [
51
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\python313.zip",
52
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src",
53
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\app",
54
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\app_packages",
55
+ ],
56
+ [
57
+ (
58
+ "C:\\PROJECT_ROOT\\src\\helloworld",
59
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\app\\helloworld",
60
+ ),
61
+ ],
62
+ id="windows",
63
+ ),
64
+ # Windows with `app_packages_path_mappings` (currently not used by briefcase, but principally possible)
65
+ pytest.param(
66
+ "nt",
67
+ AppPathMappings(
68
+ device_sys_path_regex="app$",
69
+ device_subfolders=["helloworld"],
70
+ host_folders=["C:\\PROJECT_ROOT\\src\\helloworld"],
71
+ ),
72
+ AppPackagesPathMappings(
73
+ sys_path_regex="app_packages$",
74
+ host_folder="C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\app_packages",
75
+ ),
76
+ [
77
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\python313.zip",
78
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src",
79
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\app",
80
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\app_packages",
81
+ ],
82
+ [
83
+ (
84
+ "C:\\PROJECT_ROOT\\src\\helloworld",
85
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\app\\helloworld",
86
+ ),
87
+ (
88
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\app_packages",
89
+ "C:\\PROJECT_ROOT\\build\\helloworld\\windows\\app\\src\\app_packages",
90
+ ),
91
+ ],
92
+ id="windows-with-app-packages",
93
+ ),
94
+ # macOS
95
+ pytest.param(
96
+ "posix",
97
+ AppPathMappings(
98
+ device_sys_path_regex="app$",
99
+ device_subfolders=["helloworld"],
100
+ host_folders=["/PROJECT_ROOT/src/helloworld"],
101
+ ),
102
+ None,
103
+ [
104
+ "/PROJECT_ROOT/build/helloworld/macos/app/Hello World.app/Contents/Frameworks/Python.framework/Versions/3.13/lib/python3.13",
105
+ "/PROJECT_ROOT/build/helloworld/macos/app/Hello World.app/Contents/Frameworks/Python.framework/Versions/3.13/lib/python3.13/lib-dynload",
106
+ "/PROJECT_ROOT/build/helloworld/macos/app/Hello World.app/Contents/Resources/app",
107
+ "/PROJECT_ROOT/build/helloworld/macos/app/Hello World.app/Contents/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages",
108
+ "/PROJECT_ROOT/build/helloworld/macos/app/Hello World.app/Contents/Resources/app_packages",
109
+ ],
110
+ [
111
+ (
112
+ "/PROJECT_ROOT/src/helloworld",
113
+ "/PROJECT_ROOT/build/helloworld/macos/app/Hello World.app/Contents/Resources/app/helloworld",
114
+ )
115
+ ],
116
+ id="macos",
117
+ ),
118
+ # iOS
119
+ pytest.param(
120
+ "posix",
121
+ AppPathMappings(
122
+ device_sys_path_regex="app$",
123
+ device_subfolders=["helloworld"],
124
+ host_folders=["/PROJECT_ROOT/src/helloworld"],
125
+ ),
126
+ AppPackagesPathMappings(
127
+ sys_path_regex="app_packages$",
128
+ host_folder="/APP_PACKAGES_PATH/app_packages.iphonesimulator",
129
+ ),
130
+ [
131
+ "CoreSimulator/Devices/RANDOM_NUMBER/data/Containers/Bundle/Application/RANDOM_NUMBER/Hello World.app/python/lib/python3.13",
132
+ "CoreSimulator/Devices/RANDOM_NUMBER/data/Containers/Bundle/Application/RANDOM_NUMBER/Hello World.app/python/lib/python3.13/lib-dynload",
133
+ "CoreSimulator/Devices/RANDOM_NUMBER/data/Containers/Bundle/Application/RANDOM_NUMBER/Hello World.app/app",
134
+ "CoreSimulator/Devices/RANDOM_NUMBER/data/Containers/Bundle/Application/RANDOM_NUMBER/Hello World.app/python/lib/python3.13/site-packages",
135
+ "CoreSimulator/Devices/RANDOM_NUMBER/data/Containers/Bundle/Application/RANDOM_NUMBER/Hello World.app/app_packages",
136
+ ],
137
+ [
138
+ (
139
+ "/PROJECT_ROOT/src/helloworld",
140
+ "CoreSimulator/Devices/RANDOM_NUMBER/data/Containers/Bundle/Application/RANDOM_NUMBER/Hello World.app/app/helloworld",
141
+ ),
142
+ (
143
+ "/APP_PACKAGES_PATH/app_packages.iphonesimulator",
144
+ "CoreSimulator/Devices/RANDOM_NUMBER/data/Containers/Bundle/Application/RANDOM_NUMBER/Hello World.app/app_packages",
145
+ ),
146
+ ],
147
+ id="ios",
148
+ ),
149
+ # Android (with VS Code running on Windows)
150
+ pytest.param(
151
+ "posix",
152
+ AppPathMappings(
153
+ device_sys_path_regex="app$",
154
+ device_subfolders=["helloworld"],
155
+ host_folders=["C:\\PROJECT_ROOT\\src\\helloworld"],
156
+ ),
157
+ AppPackagesPathMappings(
158
+ sys_path_regex="requirements$",
159
+ host_folder="C:\\BUNDLE_PATH\\app\\build\\python\\pip\\debug\\common",
160
+ ),
161
+ [
162
+ "/data/data/com.example.helloworld/files/chaquopy/AssetFinder/app",
163
+ "/data/data/com.example.helloworld/files/chaquopy/AssetFinder/requirements",
164
+ "/data/data/com.example.helloworld/files/chaquopy/AssetFinder/stdlib-x86_64",
165
+ "/data/user/0/com.example.helloworld/files/chaquopy/stdlib-common.imy",
166
+ "/data/user/0/com.example.helloworld/files/chaquopy/bootstrap.imy",
167
+ "/data/user/0/com.example.helloworld/files/chaquopy/bootstrap-native/x86_64",
168
+ ],
169
+ [
170
+ (
171
+ "C:\\PROJECT_ROOT\\src\\helloworld",
172
+ "/data/data/com.example.helloworld/files/chaquopy/AssetFinder/app/helloworld",
173
+ ),
174
+ (
175
+ "C:\\BUNDLE_PATH\\app\\build\\python\\pip\\debug\\common",
176
+ "/data/data/com.example.helloworld/files/chaquopy/AssetFinder/requirements",
177
+ ),
178
+ ],
179
+ id="android-on-windows-host",
180
+ ),
181
+ # Android (with VS Code running on POSIX system)
182
+ pytest.param(
183
+ "posix",
184
+ AppPathMappings(
185
+ device_sys_path_regex="app$",
186
+ device_subfolders=["helloworld"],
187
+ host_folders=["/PROJECT_ROOT/src/helloworld"],
188
+ ),
189
+ AppPackagesPathMappings(
190
+ sys_path_regex="requirements$",
191
+ host_folder="/BUNDLE_PATH/app/build/python/pip/debug/common",
192
+ ),
193
+ [
194
+ "/data/data/com.example.helloworld/files/chaquopy/AssetFinder/app",
195
+ "/data/data/com.example.helloworld/files/chaquopy/AssetFinder/requirements",
196
+ "/data/data/com.example.helloworld/files/chaquopy/AssetFinder/stdlib-x86_64",
197
+ "/data/user/0/com.example.helloworld/files/chaquopy/stdlib-common.imy",
198
+ "/data/user/0/com.example.helloworld/files/chaquopy/bootstrap.imy",
199
+ "/data/user/0/com.example.helloworld/files/chaquopy/bootstrap-native/x86_64",
200
+ ],
201
+ [
202
+ (
203
+ "/PROJECT_ROOT/src/helloworld",
204
+ "/data/data/com.example.helloworld/files/chaquopy/AssetFinder/app/helloworld",
205
+ ),
206
+ (
207
+ "/BUNDLE_PATH/app/build/python/pip/debug/common",
208
+ "/data/data/com.example.helloworld/files/chaquopy/AssetFinder/requirements",
209
+ ),
210
+ ],
211
+ id="android-on-posix-host",
212
+ ),
213
+ ],
214
+ )
215
+ def test_mappings(
216
+ os_name: str,
217
+ app_path_mappings: AppPathMappings,
218
+ app_packages_path_mappings: AppPackagesPathMappings | None,
219
+ sys_path: list[str],
220
+ expected_path_mappings: list[tuple[str, str]],
221
+ monkeypatch,
222
+ ):
223
+ if os.name != os_name:
224
+ pytest.skip(f"Test only runs on {os_name} systems")
225
+
226
+ config = DebuggerConfig(
227
+ debugger="debugpy",
228
+ host="",
229
+ port=0,
230
+ app_path_mappings=app_path_mappings,
231
+ app_packages_path_mappings=app_packages_path_mappings,
232
+ )
233
+
234
+ monkeypatch.setattr(sys, "path", sys_path)
235
+
236
+ path_mappings = briefcase_debugger.debugpy.load_path_mappings(config, False)
237
+
238
+ assert path_mappings == expected_path_mappings
239
+
240
+
241
+ @pytest.mark.parametrize(
242
+ ("os_name", "app_path_mappings"),
243
+ [
244
+ # Windows
245
+ pytest.param(
246
+ "nt",
247
+ AppPathMappings(
248
+ device_sys_path_regex="app$",
249
+ device_subfolders=["helloworld"],
250
+ host_folders=["C:\\PROJECT_ROOT\\src\\helloworld"],
251
+ ),
252
+ id="windows",
253
+ ),
254
+ # POSIX (macOS/iOS/Android)
255
+ pytest.param(
256
+ "posix",
257
+ AppPathMappings(
258
+ device_sys_path_regex="app$",
259
+ device_subfolders=["helloworld"],
260
+ host_folders=["/PROJECT_ROOT/src/helloworld"],
261
+ ),
262
+ id="posix",
263
+ ),
264
+ ],
265
+ )
266
+ def test_mappings_wrong_sys_path(
267
+ os_name: str,
268
+ app_path_mappings: AppPathMappings,
269
+ monkeypatch,
270
+ ):
271
+ """Path mappings with a wrong sys path set."""
272
+ if os.name != os_name:
273
+ pytest.skip(f"Test only runs on {os_name} systems")
274
+
275
+ config = DebuggerConfig(
276
+ debugger="debugpy",
277
+ host="",
278
+ port=0,
279
+ app_path_mappings=app_path_mappings,
280
+ app_packages_path_mappings=None,
281
+ )
282
+
283
+ sys_path = []
284
+ monkeypatch.setattr(sys, "path", sys_path)
285
+
286
+ with pytest.raises(ValueError, match=r"No sys.path entry matches regex"):
287
+ briefcase_debugger.debugpy.load_path_mappings(config, False)
@@ -0,0 +1,80 @@
1
+ import json
2
+ import os
3
+ from unittest.mock import MagicMock
4
+
5
+ import briefcase_debugger
6
+ import briefcase_debugger.pdb
7
+ import pytest
8
+
9
+
10
+ def test_no_env_vars(monkeypatch, capsys):
11
+ """Nothing happens, when no env vars are set."""
12
+ os_environ = {}
13
+ monkeypatch.setattr(os, "environ", os_environ)
14
+
15
+ # start test function
16
+ briefcase_debugger.start_remote_debugger()
17
+
18
+ captured = capsys.readouterr()
19
+ assert captured.out == ""
20
+ assert captured.err == ""
21
+
22
+
23
+ def test_no_debugger_verbose(monkeypatch, capsys):
24
+ """Nothing happens except a short message, when only verbose is requested."""
25
+ os_environ = {}
26
+ os_environ["BRIEFCASE_DEBUG"] = "1"
27
+ monkeypatch.setattr(os, "environ", os_environ)
28
+
29
+ # start test function
30
+ briefcase_debugger.start_remote_debugger()
31
+
32
+ captured = capsys.readouterr()
33
+ assert (
34
+ captured.out
35
+ == "No 'BRIEFCASE_DEBUGGER' environment variable found. Debugger not starting.\n"
36
+ )
37
+ assert captured.err == ""
38
+
39
+
40
+ @pytest.mark.parametrize("verbose", [True, False])
41
+ @pytest.mark.parametrize(
42
+ ("host_os", "expected_host_cmds"),
43
+ [
44
+ ("Windows", ["telnet somehost 9999"]),
45
+ ("Darwin", ["nc somehost 9999"]),
46
+ ("Linux", ["nc somehost 9999"]),
47
+ ("UnknownOS", ["nc somehost 9999", "telnet somehost 9999"]),
48
+ ],
49
+ )
50
+ def test_with_debugger(monkeypatch, host_os, expected_host_cmds, capsys, verbose):
51
+ """Normal debug session."""
52
+ os_environ = {}
53
+ os_environ["BRIEFCASE_DEBUG"] = "1" if verbose else "0"
54
+ os_environ["BRIEFCASE_DEBUGGER"] = json.dumps(
55
+ {
56
+ "debugger": "pdb",
57
+ "host": "somehost",
58
+ "port": 9999,
59
+ "host_os": host_os,
60
+ }
61
+ )
62
+ monkeypatch.setattr(os, "environ", os_environ)
63
+
64
+ fake_remote_pdb = MagicMock()
65
+ monkeypatch.setattr(briefcase_debugger.pdb, "RemotePdb", fake_remote_pdb)
66
+
67
+ # start test function
68
+ briefcase_debugger.start_remote_debugger()
69
+
70
+ fake_remote_pdb.assert_called_once_with(
71
+ "somehost",
72
+ 9999,
73
+ quiet=True,
74
+ )
75
+
76
+ captured = capsys.readouterr()
77
+ assert "Waiting for debugger to attach..." in captured.out
78
+ for cmd in expected_host_cmds:
79
+ assert cmd in captured.out
80
+ assert captured.err == ""