From 39df01cba6824a8fe0cba62346cc554f6b43c057 Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 19 Sep 2025 15:26:29 +1200 Subject: [PATCH 1/5] macOS support initial implementation --- CONTRIBUTING.md | 9 +-- .../PyCharmDebug/Config/tool_config.json | 2 +- .../Content/Python/init_unreal.py | 2 +- .../Content/Python/pycharmdebug/utils.py | 52 ++++++++++--- requirements.txt | 78 ++++++++++++++++++- tests/unit/test_utils.py | 11 ++- 6 files changed, 131 insertions(+), 23 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9faef7f..13194be 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,13 +55,8 @@ Project dependencies are available in the [requirements.in](requirements.in) fil pip-compile --output-file requirements.txt requirements.in requirements-dev.in requirements-test.in ``` 4. Install dependencies: - * Windows: - * ```sh - pip-sync --python-executable %VIRTUAL_ENV%/scripts/python.exe requirements.txt - ``` - * Linux: - * ```sh - pip-sync --python-executable $VIRTUAL_ENV/bin/python.exe requirements.txt + * ```sh + pip-sync ``` diff --git a/plugin_src/PyCharmDebug/Config/tool_config.json b/plugin_src/PyCharmDebug/Config/tool_config.json index 7c9d283..ac95390 100644 --- a/plugin_src/PyCharmDebug/Config/tool_config.json +++ b/plugin_src/PyCharmDebug/Config/tool_config.json @@ -1,4 +1,4 @@ { "port_number": 5678, - "debug_egg": "" + "debug_egg": "/Applications/PyCharm.app/Contents/debug-eggs/pydevd-pycharm.egg" } \ No newline at end of file diff --git a/plugin_src/PyCharmDebug/Content/Python/init_unreal.py b/plugin_src/PyCharmDebug/Content/Python/init_unreal.py index 4da91e2..1ccf9db 100644 --- a/plugin_src/PyCharmDebug/Content/Python/init_unreal.py +++ b/plugin_src/PyCharmDebug/Content/Python/init_unreal.py @@ -1,4 +1,4 @@ -""" Plugin initialization script """ +"""Plugin initialization script""" try: from pycharmdebug.menu import install # type: ignore diff --git a/plugin_src/PyCharmDebug/Content/Python/pycharmdebug/utils.py b/plugin_src/PyCharmDebug/Content/Python/pycharmdebug/utils.py index c750603..41c2be9 100644 --- a/plugin_src/PyCharmDebug/Content/Python/pycharmdebug/utils.py +++ b/plugin_src/PyCharmDebug/Content/Python/pycharmdebug/utils.py @@ -1,8 +1,14 @@ -from pathlib import Path import os +import sys import json +import subprocess +from pathlib import Path +from typing import Optional -from unreal import PluginBlueprintLibrary +from unreal import ( + PluginBlueprintLibrary, + log_error, +) from .exceptions import ( PyCharmDebugRuntimeError, @@ -115,23 +121,47 @@ def find_system_dbg_egg() -> str: PyCharm bin path not found System debug egg not found """ - pycharm_bin_dir = os.environ.get("PyCharm") - if pycharm_bin_dir is None: + pycharm_installation_path = resolve_os_specific_pycharm_path() + if not pycharm_installation_path or not pycharm_installation_path.is_dir(): raise PyCharmDebugRuntimeError("PyCharm installation not found") - pycharm_dir_path: Path = Path(pycharm_bin_dir.split(";")[0]) - - if pycharm_dir_path.is_dir() is False: - raise PyCharmDebugRuntimeError("PyCharm bin path not found") + egg_path: Path = pycharm_installation_path.joinpath("debug-eggs/pydevd-pycharm.egg") - egg_path: Path = pycharm_dir_path.parent.joinpath("debug-eggs/pydevd-pycharm.egg") - - if egg_path.is_file() is False: + if not egg_path.is_file(): raise PyCharmDebugRuntimeError("System debug egg not found") return egg_path.as_posix() +def resolve_os_specific_pycharm_path() -> Optional[Path]: + """Attempt to resolve the OS specific PyCharm installation path + + Returns: + Path: PyCharm installation path or None + """ + if sys.platform.startswith("win"): + pycharm_bin_dir = os.environ.get("PyCharm") + return Path(pycharm_bin_dir.split(";")[0]) + + elif sys.platform.startswith("darwin"): # macOS + try: + output = subprocess.check_output( + ["mdfind", "kMDItemCFBundleIdentifier == 'com.jetbrains.pycharm'"], + text=True, + ).strip() + return Path(output.split("\n")[0]) / "Contents" if output else None + except Exception: + log_error("Failed to import pydevd_pycharm") + + elif sys.platform.startswith("linux"): + log_error("Linux") + + else: + log_error(f"Unknown: {sys.platform}") + + return None + + def get_debug_egg() -> str: """Get the debug egg location from the config file. diff --git a/requirements.txt b/requirements.txt index 8b13789..286ae81 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,77 @@ - +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# pip-compile --output-file=requirements.txt requirements-dev.in requirements-test.in requirements.in +# +astroid==3.3.11 + # via pylint +black==25.9.0 + # via -r requirements-dev.in +click==8.1.8 + # via black +coverage[toml]==7.10.6 + # via pytest-cov +dill==0.4.0 + # via pylint +exceptiongroup==1.3.0 + # via pytest +iniconfig==2.1.0 + # via pytest +isort==6.0.1 + # via pylint +mccabe==0.7.0 + # via pylint +mypy==1.18.2 + # via -r requirements-dev.in +mypy-extensions==1.1.0 + # via + # black + # mypy +packaging==25.0 + # via + # black + # pytest +pathspec==0.12.1 + # via + # black + # mypy +platformdirs==4.4.0 + # via + # black + # pylint +pluggy==1.6.0 + # via + # pytest + # pytest-cov +pygments==2.19.2 + # via pytest +pylint==3.3.8 + # via -r requirements-dev.in +pytest==8.4.2 + # via + # -r requirements-test.in + # pytest-cov + # pytest-mock +pytest-cov==7.0.0 + # via -r requirements-test.in +pytest-mock==3.15.1 + # via -r requirements-test.in +pytokens==0.1.10 + # via black +tomli==2.2.1 + # via + # black + # coverage + # mypy + # pylint + # pytest +tomlkit==0.13.3 + # via pylint +typing-extensions==4.15.0 + # via + # astroid + # black + # exceptiongroup + # mypy + # pylint diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 63e6589..4c15ee9 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -1,3 +1,5 @@ +import sys + import pytest @@ -259,6 +261,7 @@ def test_set_debug_port_plugin_config_is_none_and_fails_to_create_expects_false( assert result is False +@pytest.mark.skipif(not sys.platform.startswith("win"), reason="This test is specific to Windows.") def test_find_system_dbg_egg_expects_path_to_egg(mocker): # Arrange from pycharmdebug.utils import find_system_dbg_egg @@ -273,12 +276,14 @@ def test_find_system_dbg_egg_expects_path_to_egg(mocker): assert result == "/foo/bar/debug-eggs/pydevd-pycharm.egg" -def test_find_system_dbg_egg_cant_resolve_pycharm_installation_raises_PyCharmDebugRuntimeError(mocker): +@pytest.mark.skipif(not sys.platform.startswith("win"), reason="This test is specific to Windows.") +def test_find_system_dbg_egg_windows_cant_resolve_pycharm_installation_raises_PyCharmDebugRuntimeError(mocker): # Arrange from pycharmdebug.utils import find_system_dbg_egg from pycharmdebug.exceptions import PyCharmDebugRuntimeError mocker.patch("os.environ.get", return_value=None) + # Act with pytest.raises(PyCharmDebugRuntimeError) as _ex: find_system_dbg_egg() @@ -287,6 +292,7 @@ def test_find_system_dbg_egg_cant_resolve_pycharm_installation_raises_PyCharmDeb assert "PyCharm installation not found" in str(_ex) +@pytest.mark.skipif(not sys.platform.startswith("win"), reason="This test is specific to Windows.") def test_find_system_dbg_egg_cant_resolve_pycharm_bin_dir_expects_raises_PyCharmDebugRuntimeError(mocker): # Arrange from pycharmdebug.utils import find_system_dbg_egg @@ -303,7 +309,8 @@ def test_find_system_dbg_egg_cant_resolve_pycharm_bin_dir_expects_raises_PyCharm assert "PyCharm bin path not found" in str(_ex) -# + +@pytest.mark.skipif(not sys.platform.startswith("win"), reason="This test is specific to Windows.") def test_find_system_dbg_egg_cant_resolve_debug_egg_file_expects_raises_PyCharmDebugRuntimeError(mocker): # Arrange from pycharmdebug.utils import find_system_dbg_egg From 271114d96a9eaaacabb138170c92d90361268757 Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 19 Sep 2025 15:29:50 +1200 Subject: [PATCH 2/5] removing build testing data --- .../PyCharmDebug/Config/tool_config.json | 2 +- requirements.txt | 77 ------------------- 2 files changed, 1 insertion(+), 78 deletions(-) diff --git a/plugin_src/PyCharmDebug/Config/tool_config.json b/plugin_src/PyCharmDebug/Config/tool_config.json index ac95390..7c9d283 100644 --- a/plugin_src/PyCharmDebug/Config/tool_config.json +++ b/plugin_src/PyCharmDebug/Config/tool_config.json @@ -1,4 +1,4 @@ { "port_number": 5678, - "debug_egg": "/Applications/PyCharm.app/Contents/debug-eggs/pydevd-pycharm.egg" + "debug_egg": "" } \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 286ae81..e69de29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,77 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# -# pip-compile --output-file=requirements.txt requirements-dev.in requirements-test.in requirements.in -# -astroid==3.3.11 - # via pylint -black==25.9.0 - # via -r requirements-dev.in -click==8.1.8 - # via black -coverage[toml]==7.10.6 - # via pytest-cov -dill==0.4.0 - # via pylint -exceptiongroup==1.3.0 - # via pytest -iniconfig==2.1.0 - # via pytest -isort==6.0.1 - # via pylint -mccabe==0.7.0 - # via pylint -mypy==1.18.2 - # via -r requirements-dev.in -mypy-extensions==1.1.0 - # via - # black - # mypy -packaging==25.0 - # via - # black - # pytest -pathspec==0.12.1 - # via - # black - # mypy -platformdirs==4.4.0 - # via - # black - # pylint -pluggy==1.6.0 - # via - # pytest - # pytest-cov -pygments==2.19.2 - # via pytest -pylint==3.3.8 - # via -r requirements-dev.in -pytest==8.4.2 - # via - # -r requirements-test.in - # pytest-cov - # pytest-mock -pytest-cov==7.0.0 - # via -r requirements-test.in -pytest-mock==3.15.1 - # via -r requirements-test.in -pytokens==0.1.10 - # via black -tomli==2.2.1 - # via - # black - # coverage - # mypy - # pylint - # pytest -tomlkit==0.13.3 - # via pylint -typing-extensions==4.15.0 - # via - # astroid - # black - # exceptiongroup - # mypy - # pylint From 11bbdcaeac0467eea9126c889e62c58d91231f25 Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 19 Sep 2025 15:33:12 +1200 Subject: [PATCH 3/5] updating gitignore with config files --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 44716ea..50c8521 100644 --- a/.gitignore +++ b/.gitignore @@ -232,3 +232,7 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser + +# local config files +requirements.txt +plugin_src/PyCharmDebug/Config/tool_config.json \ No newline at end of file From e5c3d41bc710368950378cd59c32e3517138af97 Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 19 Sep 2025 15:39:37 +1200 Subject: [PATCH 4/5] linting --- .../Content/Python/pycharmdebug/utils.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/plugin_src/PyCharmDebug/Content/Python/pycharmdebug/utils.py b/plugin_src/PyCharmDebug/Content/Python/pycharmdebug/utils.py index 41c2be9..93d45e0 100644 --- a/plugin_src/PyCharmDebug/Content/Python/pycharmdebug/utils.py +++ b/plugin_src/PyCharmDebug/Content/Python/pycharmdebug/utils.py @@ -143,21 +143,24 @@ def resolve_os_specific_pycharm_path() -> Optional[Path]: pycharm_bin_dir = os.environ.get("PyCharm") return Path(pycharm_bin_dir.split(";")[0]) - elif sys.platform.startswith("darwin"): # macOS + if sys.platform.startswith("darwin"): # macOS try: output = subprocess.check_output( ["mdfind", "kMDItemCFBundleIdentifier == 'com.jetbrains.pycharm'"], text=True, ).strip() - return Path(output.split("\n")[0]) / "Contents" if output else None - except Exception: + return ( + Path(output.split("\n", maxsplit=1)[0]) / "Contents" if output else None + ) + + except Exception: # pylint: disable=broad-exception-caught log_error("Failed to import pydevd_pycharm") - elif sys.platform.startswith("linux"): + if sys.platform.startswith("linux"): log_error("Linux") + return None - else: - log_error(f"Unknown: {sys.platform}") + log_error(f"Unknown: {sys.platform}") return None From 63f9ef10d4c7cf1ceeb9bb7847a080c0d99dadce Mon Sep 17 00:00:00 2001 From: Matthew Lee Date: Sat, 20 Sep 2025 17:49:02 +1200 Subject: [PATCH 5/5] wip, of windows testing with new os resolver --- .../Content/Python/pycharmdebug/utils.py | 4 +- tests/unit/test_utils.py | 155 ++++++++++-------- 2 files changed, 87 insertions(+), 72 deletions(-) diff --git a/plugin_src/PyCharmDebug/Content/Python/pycharmdebug/utils.py b/plugin_src/PyCharmDebug/Content/Python/pycharmdebug/utils.py index 93d45e0..d1fe5ff 100644 --- a/plugin_src/PyCharmDebug/Content/Python/pycharmdebug/utils.py +++ b/plugin_src/PyCharmDebug/Content/Python/pycharmdebug/utils.py @@ -141,7 +141,9 @@ def resolve_os_specific_pycharm_path() -> Optional[Path]: """ if sys.platform.startswith("win"): pycharm_bin_dir = os.environ.get("PyCharm") - return Path(pycharm_bin_dir.split(";")[0]) + if not pycharm_bin_dir: + return None + return Path(pycharm_bin_dir.split(";")[0]).parent if sys.platform.startswith("darwin"): # macOS try: diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 4c15ee9..cb13b8e 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -6,9 +6,9 @@ def test_get_plugin_config_expects_plugin_config_path(mocker): # Arrange from pycharmdebug.utils import get_plugin_config + mocker.patch( - "unreal.PluginBlueprintLibrary.get_plugin_base_dir", - return_value="/foo/bar" + "unreal.PluginBlueprintLibrary.get_plugin_base_dir", return_value="/foo/bar" ) mocker.patch("pathlib.Path.exists", return_value=True) @@ -22,9 +22,9 @@ def test_get_plugin_config_expects_plugin_config_path(mocker): def test_get_plugin_config_create_on_fail_expects_plugin_config_created(mocker): # Arrange from pycharmdebug.utils import get_plugin_config + mocker.patch( - "unreal.PluginBlueprintLibrary.get_plugin_base_dir", - return_value="/foo/bar" + "unreal.PluginBlueprintLibrary.get_plugin_base_dir", return_value="/foo/bar" ) mocker.patch("pathlib.Path.exists", return_value=False) mocked_path_touch = mocker.patch("pathlib.Path.touch") @@ -36,14 +36,16 @@ def test_get_plugin_config_create_on_fail_expects_plugin_config_created(mocker): mocked_path_touch.assert_called_once() -def test_get_plugin_config_plugin_config_not_found_expects_raises_PyCharmDebugRuntimeError(mocker): +def test_get_plugin_config_plugin_config_not_found_expects_raises_PyCharmDebugRuntimeError( + mocker, +): # Arrange from pycharmdebug.utils import get_plugin_config from pycharmdebug.exceptions import PyCharmDebugRuntimeError mocker.patch( "pycharmdebug.utils.PluginBlueprintLibrary.get_plugin_base_dir", - return_value=None + return_value=None, ) # Act @@ -57,6 +59,7 @@ def test_get_plugin_config_plugin_config_not_found_expects_raises_PyCharmDebugRu def test_get_debug_port_expects_42(mocker): # Arrange from pycharmdebug.utils import get_debug_port + mocker.patch("builtins.open") mocker.patch("json.load", return_value={"port_number": 42}) mocker.patch("pycharmdebug.utils.get_plugin_config") @@ -68,17 +71,14 @@ def test_get_debug_port_expects_42(mocker): assert result == 42 -def test_get_debug_port_plugin_config_not_found_expects_fallback_to_default_port(mocker): +def test_get_debug_port_plugin_config_not_found_expects_fallback_to_default_port( + mocker, +): # Arrange - from pycharmdebug.utils import ( - get_debug_port, - DEFAULT_PORT_NUMBER - ) + from pycharmdebug.utils import get_debug_port, DEFAULT_PORT_NUMBER + mocker.patch("builtins.open") - mocker.patch( - "pycharmdebug.utils.get_plugin_config", - return_value=None - ) + mocker.patch("pycharmdebug.utils.get_plugin_config", return_value=None) # Act result = get_debug_port() @@ -93,6 +93,7 @@ def test_get_debug_port_config_entry_empty_expects_fallback_to_default_port(mock get_debug_port, DEFAULT_PORT_NUMBER, ) + mocker.patch("builtins.open") mocker.patch("json.load", return_value={}) mocker.patch("pycharmdebug.utils.get_plugin_config") @@ -110,6 +111,7 @@ def test_get_debug_port_config_entry_is_none_expects_fallback_to_default_port(mo get_debug_port, DEFAULT_PORT_NUMBER, ) + mocker.patch("builtins.open") mocker.patch("json.load", return_value={"port_number": None}) mocker.patch("pycharmdebug.utils.get_plugin_config") @@ -127,6 +129,7 @@ def test_set_debug_port_expects_42_dumped_true_returned(mocker): set_debug_port, DEFAULT_PORT_NUMBER, ) + mocker.patch("builtins.open") mocker.patch("json.load", return_value={"port_number": DEFAULT_PORT_NUMBER}) mock_dump = mocker.patch("json.dump") @@ -136,20 +139,23 @@ def test_set_debug_port_expects_42_dumped_true_returned(mocker): result = set_debug_port(42) # Assert - mock_dump.assert_called_once_with( - {"port_number": 42}, mocker.ANY, indent=4 - ) + mock_dump.assert_called_once_with({"port_number": 42}, mocker.ANY, indent=4) assert result is True -def test_set_debug_port_existing_data_in_config_expects_existing_data_maintained(mocker): +def test_set_debug_port_existing_data_in_config_expects_existing_data_maintained( + mocker, +): # Arrange from pycharmdebug.utils import ( set_debug_port, DEFAULT_PORT_NUMBER, ) + mocker.patch("builtins.open") - mocker.patch("json.load", return_value={"port_number": DEFAULT_PORT_NUMBER, "foo": "bar"}) + mocker.patch( + "json.load", return_value={"port_number": DEFAULT_PORT_NUMBER, "foo": "bar"} + ) mock_dump = mocker.patch("json.dump") mocker.patch("pycharmdebug.utils.get_plugin_config") @@ -158,18 +164,20 @@ def test_set_debug_port_existing_data_in_config_expects_existing_data_maintained # Assert mock_dump.assert_called_once_with( - {"port_number": 42, "foo": "bar"}, mocker.ANY, indent=4 + {"port_number": 42, "foo": "bar"}, mocker.ANY, indent=4 ) assert result is True - def test_set_debug_port_incorrect_type_expects_raises_PyCharmDebugTypeError(mocker): # Arrange from pycharmdebug.exceptions import PyCharmDebugTypeError from pycharmdebug.utils import set_debug_port + mocker.patch("builtins.open") - mocker.patch("json.load", return_value={"port_number": 5678, "debug_egg": "/foo/bar.egg"}) + mocker.patch( + "json.load", return_value={"port_number": 5678, "debug_egg": "/foo/bar.egg"} + ) mocker.patch("pycharmdebug.utils.get_plugin_config") # Act @@ -188,15 +196,16 @@ def test_set_debug_port_higher_than_max_expects_raises_PyCharmDebugRuntimeError( DEFAULT_PORT_NUMBER, MAX_PORT_NUMBER, ) + mocker.patch("builtins.open") mocker.patch( "json.load", - return_value={"port_number": DEFAULT_PORT_NUMBER, "debug_egg": "/foo/bar.egg"} + return_value={"port_number": DEFAULT_PORT_NUMBER, "debug_egg": "/foo/bar.egg"}, ) # Act with pytest.raises(PyCharmDebugRuntimeError) as _ex: - set_debug_port(MAX_PORT_NUMBER+1) + set_debug_port(MAX_PORT_NUMBER + 1) # Assert assert "Port must be between 0 and 65535" in str(_ex) @@ -214,21 +223,23 @@ def test_set_debug_port_lower_than_min_expects_raises_PyCharmDebugRuntimeError(m mocker.patch("builtins.open") mocker.patch( "json.load", - return_value={"port_number": DEFAULT_PORT_NUMBER, "debug_egg": "/foo/bar.egg"} + return_value={"port_number": DEFAULT_PORT_NUMBER, "debug_egg": "/foo/bar.egg"}, ) # Act with pytest.raises(PyCharmDebugRuntimeError) as _ex: - set_debug_port(MIN_PORT_NUMBER-1) + set_debug_port(MIN_PORT_NUMBER - 1) # Assert assert "Port must be between 0 and 65535" in str(_ex) - -def test_set_debug_port_no_plugin_config_exists_expects_true_42_dumped_and_file_created(mocker): +def test_set_debug_port_no_plugin_config_exists_expects_true_42_dumped_and_file_created( + mocker, +): # Arrange from pycharmdebug.utils import set_debug_port + mocker.patch("builtins.open") mocker.patch("json.load", return_value={}) mocked_dump = mocker.patch("json.dump") @@ -249,6 +260,7 @@ def test_set_debug_port_plugin_config_is_none_and_fails_to_create_expects_false( set_debug_port, DEFAULT_PORT_NUMBER, ) + mocker.patch("builtins.open") mocker.patch("json.load", return_value={"port_number": DEFAULT_PORT_NUMBER}) mock_dump = mocker.patch("json.dump") @@ -261,10 +273,13 @@ def test_set_debug_port_plugin_config_is_none_and_fails_to_create_expects_false( assert result is False -@pytest.mark.skipif(not sys.platform.startswith("win"), reason="This test is specific to Windows.") +@pytest.mark.skipif( + not sys.platform.startswith("win"), reason="This test is specific to Windows." +) def test_find_system_dbg_egg_expects_path_to_egg(mocker): # Arrange from pycharmdebug.utils import find_system_dbg_egg + mocker.patch("os.environ.get", return_value="/foo/bar/bin;") mocker.patch("pathlib.Path.is_dir", return_value=True) mocker.patch("pathlib.Path.is_file", return_value=True) @@ -276,13 +291,18 @@ def test_find_system_dbg_egg_expects_path_to_egg(mocker): assert result == "/foo/bar/debug-eggs/pydevd-pycharm.egg" -@pytest.mark.skipif(not sys.platform.startswith("win"), reason="This test is specific to Windows.") -def test_find_system_dbg_egg_windows_cant_resolve_pycharm_installation_raises_PyCharmDebugRuntimeError(mocker): +def test_find_system_dbg_egg_windows_cant_resolve_pycharm_installation_raises_PyCharmDebugRuntimeError( + mocker, +): # Arrange + from pathlib import Path from pycharmdebug.utils import find_system_dbg_egg from pycharmdebug.exceptions import PyCharmDebugRuntimeError - mocker.patch("os.environ.get", return_value=None) + mocker.patch("os.environ.get", return_value=None) + mocker.patch( + Path("pycharmdebug.utils.resolve_os_specific_pycharm_path"), return_value="win" + ) # Act with pytest.raises(PyCharmDebugRuntimeError) as _ex: @@ -292,26 +312,12 @@ def test_find_system_dbg_egg_windows_cant_resolve_pycharm_installation_raises_Py assert "PyCharm installation not found" in str(_ex) -@pytest.mark.skipif(not sys.platform.startswith("win"), reason="This test is specific to Windows.") -def test_find_system_dbg_egg_cant_resolve_pycharm_bin_dir_expects_raises_PyCharmDebugRuntimeError(mocker): - # Arrange - from pycharmdebug.utils import find_system_dbg_egg - from pycharmdebug.exceptions import PyCharmDebugRuntimeError - - mocker.patch("os.environ.get", return_value="/foo/bar/bin;") - mocker.patch("pathlib.Path.is_dir", return_value=False) - - # Act - with pytest.raises(PyCharmDebugRuntimeError) as _ex: - find_system_dbg_egg() - - # Assert - - assert "PyCharm bin path not found" in str(_ex) - - -@pytest.mark.skipif(not sys.platform.startswith("win"), reason="This test is specific to Windows.") -def test_find_system_dbg_egg_cant_resolve_debug_egg_file_expects_raises_PyCharmDebugRuntimeError(mocker): +@pytest.mark.skipif( + not sys.platform.startswith("win"), reason="This test is specific to Windows." +) +def test_find_system_dbg_egg_cant_resolve_debug_egg_file_expects_raises_PyCharmDebugRuntimeError( + mocker, +): # Arrange from pycharmdebug.utils import find_system_dbg_egg from pycharmdebug.exceptions import PyCharmDebugRuntimeError @@ -328,15 +334,16 @@ def test_find_system_dbg_egg_cant_resolve_debug_egg_file_expects_raises_PyCharmD assert "System debug egg not found" in str(_ex) -def test_get_debug_egg_exists_in_config_expects_config_value_mocked_find_system_dbg_egg_not_called(mocker): +def test_get_debug_egg_exists_in_config_expects_config_value_mocked_find_system_dbg_egg_not_called( + mocker, +): # Arrange from pycharmdebug.utils import get_debug_egg + mocker.patch("builtins.open") mocker.patch("pycharmdebug.utils.get_plugin_config") mocker.patch("json.load", return_value={"debug_egg": "/foo/bar/pydevd-pycharm.egg"}) - mocked_find_system_dbg_egg = mocker.patch( - "pycharmdebug.utils.find_system_dbg_egg" - ) + mocked_find_system_dbg_egg = mocker.patch("pycharmdebug.utils.find_system_dbg_egg") mocker.patch("pathlib.Path.is_file", return_value=True) # Act @@ -347,9 +354,12 @@ def test_get_debug_egg_exists_in_config_expects_config_value_mocked_find_system_ mocked_find_system_dbg_egg.assert_not_called() -def test_get_debug_egg_serialized_data_is_empty_string_expects_empty_string_returned(mocker): +def test_get_debug_egg_serialized_data_is_empty_string_expects_empty_string_returned( + mocker, +): # Arrange from pycharmdebug.utils import get_debug_egg + mocker.patch("builtins.open") mocker.patch("pycharmdebug.utils.get_plugin_config") mocker.patch("json.load", return_value={"debug_egg": ""}) @@ -362,7 +372,9 @@ def test_get_debug_egg_serialized_data_is_empty_string_expects_empty_string_retu assert result == "" -def test_get_debug_egg_serialized_data_is_invalid_expects_PyCharmDebugRuntimeError_raised(mocker): +def test_get_debug_egg_serialized_data_is_invalid_expects_PyCharmDebugRuntimeError_raised( + mocker, +): # Arrange from pycharmdebug.utils import get_debug_egg from pycharmdebug.exceptions import PyCharmDebugRuntimeError @@ -379,10 +391,10 @@ def test_get_debug_egg_serialized_data_is_invalid_expects_PyCharmDebugRuntimeErr assert "No valid debug_egg location saved in the config" in str(_ex) - def test_set_debug_egg_expects_path_dumped_and_returns_true(mocker): # Arrange from pycharmdebug.utils import set_debug_egg + mocker.patch("pycharmdebug.utils.get_plugin_config") mocker.patch("builtins.open") mocker.patch("json.load", return_value={}) @@ -395,7 +407,8 @@ def test_set_debug_egg_expects_path_dumped_and_returns_true(mocker): # Assert assert result is True mocked_dump.assert_called_once_with( - {"debug_egg": "/foo/bar/pydevd-pycharm.egg"}, mocker.ANY, indent=4) + {"debug_egg": "/foo/bar/pydevd-pycharm.egg"}, mocker.ANY, indent=4 + ) def test_set_debug_egg_invalid_egg_file_expects_raises_PyCharmDebugTypeError(mocker): @@ -415,9 +428,12 @@ def test_set_debug_egg_invalid_egg_file_expects_raises_PyCharmDebugTypeError(moc assert "Invalid egg file" in str(_ex) -def test_set_debug_egg_config_doesnt_exist_gets_created_and_value_dumped_returns_true(mocker): +def test_set_debug_egg_config_doesnt_exist_gets_created_and_value_dumped_returns_true( + mocker, +): # Arrange from pycharmdebug.utils import set_debug_egg + mocker.patch("builtins.open") mocker.patch("json.load", return_value={}) mocker.patch("pathlib.Path.is_file", return_value=True) @@ -431,14 +447,15 @@ def test_set_debug_egg_config_doesnt_exist_gets_created_and_value_dumped_returns # Assert assert result is True mocked_dump.assert_called_once_with( - {"debug_egg": "/foo/bar/pydevd-pycharm.egg"}, mocker.ANY, indent=4) + {"debug_egg": "/foo/bar/pydevd-pycharm.egg"}, mocker.ANY, indent=4 + ) mocked_path_touch.assert_called_once() - def test_set_debug_egg_config_has_other_data_expects_other_data_maintained(mocker): # Arrange from pycharmdebug.utils import set_debug_egg + mocker.patch("pycharmdebug.utils.get_plugin_config") mocker.patch("builtins.open") mocker.patch("json.load", return_value={"foo": "bar"}) @@ -451,18 +468,14 @@ def test_set_debug_egg_config_has_other_data_expects_other_data_maintained(mocke # Assert assert result is True mocked_dump.assert_called_once_with( - { - "foo": "bar", - "debug_egg": "/foo/bar/pydevd-pycharm.egg" - }, - mocker.ANY, - indent=4 + {"foo": "bar", "debug_egg": "/foo/bar/pydevd-pycharm.egg"}, mocker.ANY, indent=4 ) def test_set_debug_egg_empty_string_set_expects_empty_string_serialized(mocker): # Arrange from pycharmdebug.utils import set_debug_egg + mocker.patch("pycharmdebug.utils.get_plugin_config") mocker.patch("builtins.open") mocker.patch("json.load", return_value={"debug_egg": "foo"}) @@ -473,7 +486,7 @@ def test_set_debug_egg_empty_string_set_expects_empty_string_serialized(mocker): # Assert assert result is True - mocked_dump.assert_called_once_with({"debug_egg": ""}, mocker.ANY,indent=4) + mocked_dump.assert_called_once_with({"debug_egg": ""}, mocker.ANY, indent=4) def test_set_debug_egg_no_config_found_expects_PyCharmDebugRuntimeError(mocker):