Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve the actual data path for windows distrubutions #2292

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
28 changes: 25 additions & 3 deletions mu/config.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
import os

import sys
import tempfile
from functools import lru_cache
import platformdirs


# The default directory for application data (i.e., configuration).
DATA_DIR = platformdirs.user_data_dir(appname="mu", appauthor="python")
@lru_cache()
def get_data_dir():
path = platformdirs.user_data_dir(appname="mu", appauthor="python")
if sys.platform == "win32":
# Locate the actual path for Windows by making a temporary file
# then resolving the real path. Solves a bug in the Windows store
# distribution of Python 3.8+
# See https://github.com/mu-editor/mu/issues/2293
os.makedirs(path, exist_ok=True)
fd, tmp = tempfile.mkstemp(dir=path)
realpath = os.path.dirname(os.path.realpath(tmp))
os.close(fd)
os.remove(tmp)
return realpath
else:
return path


# The name of the default virtual environment used by Mu.
VENV_NAME = "mu_venv"


# The directory containing default virtual environment.
VENV_DIR = os.path.join(DATA_DIR, VENV_NAME)
def get_venv_dir():
return os.path.join(get_data_dir(), VENV_NAME)


# Maximum line length for using both in Check and Tidy
MAX_LINE_LENGTH = 88
Expand Down
13 changes: 7 additions & 6 deletions mu/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
from . import i18n
from .resources import path
from .debugger.utils import is_breakpoint_line
from .config import DATA_DIR, VENV_DIR, MAX_LINE_LENGTH
from .config import get_data_dir, get_venv_dir, MAX_LINE_LENGTH
from . import settings
from .virtual_environment import venv

Expand Down Expand Up @@ -795,11 +795,12 @@ def __init__(self, view):
self.current_path = "" # Directory of last loaded file.
self.global_replace = False
self.selecting_mode = False # Flag to stop auto-detection of modes.
if not os.path.exists(DATA_DIR):
logger.debug("Creating directory: {}".format(DATA_DIR))
os.makedirs(DATA_DIR)
data_dir = get_data_dir()
if not os.path.exists(data_dir):
logger.debug("Creating directory: {}".format(data_dir))
os.makedirs(data_dir)
logger.info("Log directory: {}".format(LOG_DIR))
logger.info("Data directory: {}".format(DATA_DIR))
logger.info("Data directory: {}".format(data_dir))

@view.open_file.connect
def on_open_file(file):
Expand Down Expand Up @@ -1518,7 +1519,7 @@ def sync_package_state(self, old_packages, new_packages):
if to_remove or to_add:
logger.info("To add: {}".format(to_add))
logger.info("To remove: {}".format(to_remove))
logger.info("Virtualenv: {}".format(VENV_DIR))
logger.info("Virtualenv: {}".format(get_venv_dir))
self._view.sync_packages(to_remove, to_add)

def select_mode(self, event=None):
Expand Down
2 changes: 1 addition & 1 deletion mu/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def default_file_locations():
os.path.dirname(os.path.dirname(app_dir))
)

return [app_dir, config.DATA_DIR]
return [app_dir, config.get_data_dir()]

def register_for_autosave(self):
"""Ensure the settings are saved at least when the Python session finishes"""
Expand Down
2 changes: 1 addition & 1 deletion mu/virtual_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ def _generate_dirpath():
construct one which includes the Python version and a timestamp
"""
return "%s-%s-%s" % (
config.VENV_DIR,
config.get_venv_dir(),
"%s%s" % sys.version_info[:2],
time.strftime("%Y%m%d-%H%M%S"),
)
Expand Down
5 changes: 2 additions & 3 deletions tests/test_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def test_CONSTANTS():
Ensure the expected constants exist.
"""
assert mu.config.HOME_DIRECTORY
assert mu.config.DATA_DIR
assert mu.config.get_data_dir()
assert mu.config.WORKSPACE_NAME


Expand Down Expand Up @@ -811,8 +811,7 @@ def test_editor_init():
assert e.replace == ""
assert e.global_replace is False
assert e.selecting_mode is False
assert mkd.call_count == 1
assert mkd.call_args_list[0][0][0] == mu.logic.DATA_DIR
assert mkd.call_count >= 1


def test_editor_setup():
Expand Down
14 changes: 8 additions & 6 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ def test_expandvars_nonexistent_envvar():
#
# The next set of tests exercises the logic in default_file_location to ensure
# that a settings file will be discovered first in the same directory as
# the program's executable; and then in the DATA_DIR directory (typically
# the program's executable; and then in the get_data_dir directory (typically
# in a user-specific data area on whichever platform)
#
# NB the introduction of PR #1200 changed the logic so the file is not
Expand Down Expand Up @@ -475,10 +475,12 @@ def test_default_file_location_with_data_path(mocked_settings):
Find an admin file in the data location.

In this case the logic won't find a file in the sys.argv0/sys.executable
path and will drop back to DATA_DIR
path and will drop back to get_data_dir()
"""
settings, filepath, items = mocked_settings
with mock.patch.object(mu.config, "DATA_DIR", os.path.dirname(filepath)):
with mock.patch.object(
mu.config, "get_data_dir", lambda: os.path.dirname(filepath)
):
settings.init()

assert settings.filepath == filepath
Expand All @@ -491,7 +493,7 @@ def test_default_file_location_with_data_path(mocked_settings):
# ~ with mock.patch("os.path.exists", mock_exists), mock.patch(
# ~ "builtins.open", mock_open
# ~ ), mock.patch("json.dump", mock_json_dump), mock.patch(
# ~ "mu.logic.DATA_DIR", "fake_path"
# ~ "mu.logic.get_data_dir", lambda: "fake_path"
# ~ ):
# ~ result = mu.logic.default_file_location("settings.json")
# ~ assert result == os.path.join("fake_path", "settings.json")
Expand All @@ -508,7 +510,7 @@ def test_default_file_location_no_files():
with mock.patch("os.path.exists", return_value=False), mock.patch(
"builtins.open", mock_open
), mock.patch("json.dump", mock_json_dump), mock.patch(
"mu.logic.DATA_DIR", "fake_path"
"mu.logic.get_data_dir", lambda: "fake_path"
):
result = mu.logic.default_file_location("settings.json")
assert result == os.path.join("fake_path", "settings.json")
Expand All @@ -528,7 +530,7 @@ def test_default_file_location_no_files_cannot_create():
with mock.patch("os.path.exists", return_value=False), mock.patch(
"builtins.open", mock_open
), mock.patch("json.dump", mock_json_dump), mock.patch(
"mu.logic.DATA_DIR", "fake_path"
"mu.logic.get_data_dir", lambda: "fake_path"
), mock.patch(
"mu.logic.logger", return_value=None
) as logger:
Expand Down
4 changes: 3 additions & 1 deletion tests/virtual_environment/test_virtual_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ def pipped():
def workspace_dirpath(tmp_path):
workspace_dirpath = tmp_path / uuid.uuid1().hex
workspace_dirpath.mkdir()
with mock.patch.object(mu.config, "DATA_DIR", workspace_dirpath):
with mock.patch.object(
mu.config, "get_data_dir", lambda: workspace_dirpath
):
yield workspace_dirpath


Expand Down