Skip to content

Commit

Permalink
Fix path detection algorithm
Browse files Browse the repository at this point in the history
This commit ...

1. uses a more robust algorithm to determine special ST paths at startup, which
   no longer relies on location of `Default.sort` module, but uses
   `sys.executable` variable to determine, whether PC is running within
   ST plugin environment or on an external CI runner which mocks ST API.

   The new approach fixes an issue, which causes path detection to resolve
   invalid paths, if users extract whole Default package.
   PC assumed sublimehq's development environment in that case.

2. moves forward to rely on `__spec__` in favor of `__file__` and `__loader__`
   as this is what python is moving to in general. The ladder ones are dropped
   step by step by recent python releases (3.13).
  • Loading branch information
deathaxe committed Jun 2, 2024
1 parent 96bfda9 commit e88cc41
Showing 1 changed file with 48 additions and 27 deletions.
75 changes: 48 additions & 27 deletions package_control/sys_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,30 @@

PREFIX = '\\\\?\\' if sys.platform == 'win32' else ''

# Determine default packages path
try:
import Default.sort as default_module

__default_packages_path = os.path.dirname(os.path.dirname(default_module.__file__))

__executable_path, __executable_name = os.path.split(sys.executable)
if __executable_name.startswith('plugin_host') or __executable_name.startswith('sublime_text'):
# Default packages are located in same directory as sublime_text and plugin_host executables
# in normal or portable setups.
__default_packages_path = os.path.join(__executable_path, 'Packages')
del __executable_name

# Determine user's data path.
# - for portable setups resolves to __executable_path/Data
# - for Normal setups resolves to %APPDATA%\Sublime Text or ~/.config/sublime-text
# When loaded as a .sublime-package file, __file__ ends up being
# {data_dir}/Installed Packages/Package Control.sublime-package/package_control/sys_path.py
if isinstance(__loader__, sublime_plugin.ZipLoader):
__installed_packages_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
# For a non-development build, the Packages are next to the Installed Packages dir
__packages_path = os.path.join(os.path.dirname(__installed_packages_path), 'Packages')
if not os.path.exists(__packages_path):
__packages_path = None

# When loaded as unpacked package, __file__ ends up being
# {data_dir}/Packages/Package Control/package_control/sys_path.py
else:
__packages_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
# For a non-development build, the Installed Packages are next to the Packages dir
__installed_packages_path = os.path.join(os.path.dirname(__packages_path), 'Installed Packages')
if not os.path.exists(__installed_packages_path):
__installed_packages_path = None
try:
__data_path = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(__spec__.origin))))
except AttributeError:
__data_path = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(__file__))))

if __packages_path is None:
# default package must not be zipped in dev environment
if isinstance(default_module.__loader__, sublime_plugin.ZipLoader):
raise FileNotFoundError('Packages')
__packages_path = __default_packages_path
__installed_packages_path = os.path.join(__data_path, 'Installed Packages')
if not os.path.isdir(__installed_packages_path):
__installed_packages_path = None

if __installed_packages_path is None:
if sys.platform == 'darwin':
Expand All @@ -58,21 +53,47 @@
if __installed_packages_path is None:
raise FileNotFoundError('Installed Packages')

except ImportError:
__packages_path = os.path.join(__data_path, 'Packages')
if not os.path.isdir(__packages_path):
__packages_path = None

if __packages_path is None:
import Default.sort as default_module

# default package must not be zipped in dev environment
try:
# rely on __spec__ on python 3.8+ as this is what python is moving to
if isinstance(default_module.__spec__.loader, sublime_plugin.ZipLoader):
raise FileNotFoundError('Packages')
__default_packages_path = os.path.dirname(
os.path.dirname(default_module.__spec__.origin))

except AttributeError:
# fallback for python 3.3
if isinstance(default_module.__loader__, sublime_plugin.ZipLoader):
raise FileNotFoundError('Packages')
__default_packages_path = os.path.dirname(
os.path.dirname(default_module.__file__))

__packages_path = __default_packages_path

else:
# All this song and dance, just to satisfy CI test runner!
# Import error of Default.sort indicates CI test environment
# Unfortunately we can't use this simple lines in production
# as API can be called at import time only with ST4088+.
__default_packages_path = os.path.join(os.path.dirname(sublime.executable_path()))
__executable_path = os.path.dirname(sublime.executable_path())
__default_packages_path = os.path.join(__executable_path, 'Packages')
__installed_packages_path = sublime.installed_packages_path()
__packages_path = sublime.packages_path()
__data_path = os.path.dirname(__packages_path)

if PREFIX:
__data_path = PREFIX + __data_path
__default_packages_path = PREFIX + __default_packages_path
__installed_packages_path = PREFIX + __installed_packages_path
__packages_path = PREFIX + __packages_path

__data_path = os.path.dirname(__installed_packages_path)
__cache_path = None
__package_control_cache_path = None
__python_libs_cache_path = None
Expand Down

0 comments on commit e88cc41

Please sign in to comment.