Skip to content

Commit

Permalink
Merge pull request #8001 from kozlovsky/fix/cx_freeze_support
Browse files Browse the repository at this point in the history
Fix `is_frozen`, `get_core_path()`, `get_gui_path()` when `cx_freeze` is used for building binaries
  • Loading branch information
kozlovsky authored Apr 24, 2024
2 parents 30afec6 + 61c0c08 commit 2ba7d6f
Show file tree
Hide file tree
Showing 12 changed files with 63 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
from tribler.core.components.libtorrent.settings import DownloadDefaultsSettings, get_default_download_dir
from tribler.core.components.libtorrent.utils.libtorrent_helper import libtorrent as lt
from tribler.core.exceptions import InvalidConfigException
from tribler.core.utilities.install_dir import get_lib_path
from tribler.core.utilities.install_dir import get_core_path
from tribler.core.utilities.path_util import Path
from tribler.core.utilities.utilities import bdecode_compat

SPEC_FILENAME = 'download_config.spec'
CONFIG_SPEC_PATH = get_lib_path() / 'components/libtorrent/download_manager' / SPEC_FILENAME
CONFIG_SPEC_PATH = get_core_path() / 'components/libtorrent/download_manager' / SPEC_FILENAME
NONPERSISTENT_DEFAULTS = {}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@

from tribler.core.components.metadata_store.category_filter.family_filter import default_xxx_filter
from tribler.core.components.metadata_store.category_filter.init_category import getCategoryInfo
from tribler.core.utilities.install_dir import get_lib_path
from tribler.core.utilities.install_dir import get_core_path
from tribler.core.utilities.unicode import recursive_unicode

CATEGORY_CONFIG_FILE = get_lib_path() / 'components/metadata_store/category_filter/category.conf'
CATEGORY_CONFIG_FILE = get_core_path() / 'components/metadata_store/category_filter/category.conf'


def cmp_rank(a, b):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
import logging
import re

from tribler.core.utilities.install_dir import get_lib_path
from tribler.core.utilities.install_dir import get_core_path

WORDS_REGEXP = re.compile('[a-zA-Z0-9]+')

termfilename = get_lib_path() / 'components' / 'metadata_store' / 'category_filter' / 'filter_terms.filter'
termfilename = get_core_path() / 'components/metadata_store/category_filter/filter_terms.filter'


def initTerms(filename):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import re

from tribler.core.utilities.install_dir import get_lib_path
from tribler.core.utilities.install_dir import get_core_path

# !ACHTUNG! We must first read the line into a file, then release the lock, and only then pass it to regex compiler.
# Otherwise, there is an annoying race condition that reads in an empty file!
with open(get_lib_path() / 'components' / 'metadata_store' / 'category_filter' / 'level2.regex', encoding="utf-8") as f:
with open(get_core_path() / 'components/metadata_store/category_filter/level2.regex', encoding="utf-8") as f:
regex = f.read().strip()
stoplist_expression = re.compile(regex, re.IGNORECASE)

Expand Down
4 changes: 2 additions & 2 deletions src/tribler/core/components/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from tribler.core.sentry_reporter.sentry_reporter import SentryReporter
from tribler.core.utilities.async_group.async_group import AsyncGroup
from tribler.core.utilities.crypto_patcher import patch_crypto_be_discovery
from tribler.core.utilities.install_dir import get_lib_path
from tribler.core.utilities.install_dir import get_core_path
from tribler.core.utilities.network_utils import default_network_utils
from tribler.core.utilities.notifier import Notifier
from tribler.core.utilities.simpledefs import STATEDIR_CHANNELS_DIR, STATEDIR_DB_DIR
Expand Down Expand Up @@ -88,7 +88,7 @@ async def start_components(self):
# On Mac, we bundle the root certificate for the SSL validation since Twisted is not using the root
# certificates provided by the system trust store.
if sys.platform == 'darwin':
os.environ['SSL_CERT_FILE'] = str(get_lib_path() / 'root_certs_mac.pem')
os.environ['SSL_CERT_FILE'] = str(get_core_path() / 'root_certs_mac.pem')

coros = [comp.start() for comp in self.components.values()]
await gather(*coros, return_exceptions=not self.failfast)
Expand Down
8 changes: 3 additions & 5 deletions src/tribler/core/logger/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import yaml

from tribler.core.utilities.install_dir import get_core_path

LOG_CONFIG_FILENAME = 'logger.yaml'

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -37,11 +39,7 @@ def load_logger_config(app_mode, log_dir, current_process_is_primary=True):


def get_logger_config_path():
if not hasattr(sys, '_MEIPASS'):
dirname = Path(__file__).absolute().parent
else:
dirname = Path(getattr(sys, '_MEIPASS')) / "tribler_source/tribler/core/logger"
return dirname / LOG_CONFIG_FILENAME
return get_core_path() / 'logger' / LOG_CONFIG_FILENAME


def setup_logging(app_mode, log_dir: Path, config_path: Path):
Expand Down
14 changes: 11 additions & 3 deletions src/tribler/core/logger/tests/test_logger.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import sys
from io import BytesIO, TextIOWrapper
from unittest.mock import MagicMock, Mock, call, patch

Expand All @@ -6,15 +7,22 @@
from tribler.core.utilities.path_util import Path


@patch('tribler.core.logger.logger.__file__', '/a/b/c/logger.py')
@patch('tribler.core.__file__', 'xyz/tribler/core/__init__.py')
def test_get_logger_config_path():
config_path = get_logger_config_path()
# take the last part of the path to ignore a drive name on Windows
assert config_path.parts[-4:] == ('a', 'b', 'c', 'logger.yaml')
assert config_path.parts[-4:] == ('tribler', 'core', 'logger', 'logger.yaml')

win_prefix = '//?/' if sys.platform.startswith('win') else '' # added by Path.fix_win_long_file in get_base_path

with patch('sys.frozen', True, create=True):
with patch('sys.executable', '/a/b/c/tribler.exe', create=True):
config_path = get_logger_config_path()
assert config_path == Path(win_prefix + '/a/b/c/tribler_source/tribler/core/logger/logger.yaml')

with patch('sys._MEIPASS', '/x/y/z/', create=True):
config_path = get_logger_config_path()
assert config_path == Path('/x/y/z/tribler_source/tribler/core/logger/logger.yaml')
assert config_path == Path(win_prefix + '/x/y/z/tribler_source/tribler/core/logger/logger.yaml')


@patch('logging.basicConfig')
Expand Down
4 changes: 2 additions & 2 deletions src/tribler/core/tests/test_configparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

from tribler.core.exceptions import OperationNotPossibleAtRuntimeException
from tribler.core.utilities.configparser import CallbackConfigParser
from tribler.core.utilities.install_dir import get_lib_path
from tribler.core.utilities.install_dir import get_core_path

CONFIG_FILES_DIR = get_lib_path() / "tests/tools/data/config_files/"
CONFIG_FILES_DIR = get_core_path() / "tests/tools/data/config_files/"


def test_configparser_config1():
Expand Down
17 changes: 10 additions & 7 deletions src/tribler/core/utilities/install_dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,27 @@
"""
import sys

import tribler.core
import tribler
from tribler.core.utilities.path_util import Path
from tribler.core.utilities.utilities import is_frozen


def get_base_path():
""" Get absolute path to resource, works for dev and for PyInstaller """
""" Get absolute path to resource, works for dev and for PyInstaller/cx_freeze"""
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = Path(sys._MEIPASS)
except Exception:
base_path = Path(tribler.core.__file__).parent
base_path = Path(getattr(sys, '_MEIPASS'))
except AttributeError:
if getattr(sys, 'frozen', False): # cx_freeze
base_path = Path(sys.executable).parent
else:
base_path = Path(tribler.__file__).parent

fixed_filename = Path.fix_win_long_file(base_path)
return Path(fixed_filename)


def get_lib_path():
def get_core_path():
if is_frozen():
return get_base_path() / 'tribler_source/tribler/core'
return get_base_path()
return get_base_path() / 'core'
13 changes: 7 additions & 6 deletions src/tribler/core/utilities/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,13 @@ def is_frozen():
"""
Return whether we are running in a frozen environment
"""
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
sys._MEIPASS # pylint: disable=protected-access
except Exception: # pylint: disable=broad-except
return False
return True
if hasattr(sys, '_MEIPASS'):
return True # PyInstaller creates a temp folder and stores path in _MEIPASS

if getattr(sys, 'frozen', False):
return True # cx_freeze creates 'frozen' attribute

return False


fts_query_re = re.compile(r'\w+', re.UNICODE)
Expand Down
11 changes: 9 additions & 2 deletions src/tribler/gui/start_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
from tribler.core.logger.logger import load_logger_config
from tribler.core.sentry_reporter.sentry_reporter import SentryStrategy
from tribler.core.utilities.exit_codes import EXITCODE_ANOTHER_GUI_PROCESS_IS_RUNNING
from tribler.core.utilities.install_dir import get_base_path, get_core_path
from tribler.core.utilities.process_locking import GUI_LOCK_FILENAME, try_acquire_file_lock
from tribler.core.utilities.process_manager import ProcessKind
from tribler.core.utilities.process_manager.manager import setup_process_manager
from tribler.core.utilities.utilities import show_system_popup
from tribler.core.utilities.utilities import is_frozen, show_system_popup
from tribler.gui import gui_sentry_reporter
from tribler.gui.app_manager import AppManager
from tribler.gui.tribler_app import TriblerApplication
from tribler.gui.tribler_window import TriblerWindow
from tribler.gui.utilities import get_translator
from tribler.gui.utilities import get_translator, get_gui_path

logger = logging.getLogger(__name__)

Expand All @@ -47,6 +48,12 @@ def run_gui(api_port: Optional[int], api_key: Optional[str], root_state_dir: Pat

load_logger_config('tribler-gui', root_state_dir, current_process_owns_lock)

logger.info(f"Root state dir: {root_state_dir}")
logger.info(f"is_frozen: {is_frozen()}")
logger.info(f"Base path: {get_base_path()}")
logger.info(f"Core path: {get_core_path()}")
logger.info(f"GUI path: {get_gui_path()}")

# Enable tracer using commandline args: --trace-debug or --trace-exceptions
trace_logger = check_and_enable_code_tracing('gui', root_state_dir)

Expand Down
23 changes: 11 additions & 12 deletions src/tribler/gui/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

import tribler.gui
from tribler.core.components.knowledge.db.knowledge_db import ResourceType
from tribler.core.utilities.install_dir import get_base_path
from tribler.core.utilities.utilities import is_frozen
from tribler.gui.defs import CORRUPTED_DB_WAS_FIXED_MESSAGE, HEALTH_DEAD, HEALTH_GOOD, HEALTH_MOOT, HEALTH_UNCHECKED

# fmt: off
Expand Down Expand Up @@ -194,17 +196,14 @@ def duration_to_string(seconds):
return tr("%(seconds)is") % data


def get_base_path():
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.dirname(tribler.gui.__file__)
return base_path
def get_gui_path():
""" Get absolute path to resource, works for dev and for PyInstaller/cx_freeze"""
if is_frozen():
return get_base_path() / 'tribler_source/tribler/gui'
return get_base_path() / 'gui'


TRANSLATIONS_DIR = os.path.join(get_base_path(), "i18n")
TRANSLATIONS_DIR = os.path.join(get_gui_path(), "i18n")


def get_available_translations():
Expand All @@ -224,7 +223,7 @@ def get_available_translations():


def get_ui_file_path(filename):
return os.path.join(get_base_path(), 'qt_resources', filename)
return os.path.join(get_gui_path(), 'qt_resources', filename)


def get_image_path(filename: str, convert_slashes_to_forward: bool = False) -> str:
Expand All @@ -235,7 +234,7 @@ def get_image_path(filename: str, convert_slashes_to_forward: bool = False) -> s
This can be used to ensure that images on Windows can be correctly loaded.
Also see https://stackoverflow.com/questions/26121737/qt-stylesheet-background-image-from-filepath.
"""
path = os.path.join(get_base_path(), 'images', filename)
path = os.path.join(get_gui_path(), 'images', filename)
if convert_slashes_to_forward:
path = path.replace("\\", "/")
return path
Expand All @@ -245,7 +244,7 @@ def get_font_path(filename: str) -> str:
"""
Return a path to a particular font in the fonts directory.
"""
return os.path.join(get_base_path(), 'fonts', filename)
return os.path.join(get_gui_path(), 'fonts', filename)


def get_gui_setting(gui_settings, value, default, is_bool=False):
Expand Down

0 comments on commit 2ba7d6f

Please sign in to comment.