Skip to content
59 changes: 25 additions & 34 deletions package_control/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ def bootstrap():
by `plugin_loaded()` hook.
"""

_install_injectors()

if not os.path.exists(LOADER_PACKAGE_PATH):
# Start shortly after Sublime starts so package renames don't cause errors
# with key bindings, settings, etc. disappearing in the middle of parsing
Expand Down Expand Up @@ -131,25 +129,28 @@ def _install_injectors():
Makes sure the module injectors are in place
"""

injector_code = R"""
injector_code = R'''
"""
Public Package Control API
"""
import os
import sys
import zipfile

import sublime_plugin

__data_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))

# python 3.13 may no longer provide __file__
__data_path = os.path.dirname(os.path.dirname(os.path.dirname(
__spec__.origin if hasattr(globals(), '__spec__') else __file__))
)
__pkg_path = os.path.join(__data_path, 'Packages', 'Package Control', 'package_control')
__zip_path = os.path.join(__data_path, 'Installed Packages', 'Package Control.sublime-package')

__code = None

# We check the .sublime-package first, since the sublime_plugin.ZipLoader deals with overrides
if os.path.exists(__zip_path):
__pkg_path = os.path.join(__zip_path, 'package_control')
__file_path = os.path.join(__pkg_path, '__init__.py')

__loader__ = sublime_plugin.ZipLoader(__zip_path)

try:
Expand All @@ -162,7 +163,7 @@ def _install_injectors():
except (OSError, KeyError):
pass

# may be required before Package Control has been loaded
# required for events to be available on plugin_host Package Control is not running on
events = sys.modules.get('package_control.events')
if events is None:
events = __loader__.load_module("Package Control.package_control.events")
Expand All @@ -174,7 +175,6 @@ def _install_injectors():
from importlib.machinery import SourceFileLoader

__file_path = os.path.join(__pkg_path, '__init__.py')

__loader__ = SourceFileLoader('package_control', __file_path)

try:
Expand All @@ -183,7 +183,7 @@ def _install_injectors():
except (OSError):
pass

# may be required before Package Control has been loaded
# required for events to be available on plugin_host Package Control is not running on
events = sys.modules.get('package_control.events')
if events is None:
events = SourceFileLoader('events', os.path.join(__pkg_path, 'events.py')).load_module()
Expand All @@ -198,36 +198,25 @@ def _install_injectors():

__file__ = __file_path
__package__ = 'package_control'
__path__ = [__pkg_path]
__path__ = []
__data = {}
exec(__code, __data)
globals().update(__data)

# initial cleanup
# cleanup temporary globals
del globals()['__cached__']
del globals()['__code']
del globals()['__data']
del globals()['__data_path']
del globals()['__f']
del globals()['__file_path']
del globals()['__zip_path']
del globals()['__pkg_path']
del globals()['__data_path']
del globals()['__zip_path']
del globals()['os']
del globals()['sublime_plugin']
del globals()['zipfile']
del globals()['sys']
del globals()['os']

__data = {}
exec(__code, __data)
globals().update(__data)

# Python 3.3 doesn't have __spec__
if hasattr(globals(), '__spec__'):
__spec__.loader = __loader__
__spec__.origin = __file__
__spec__.submodule_search_locations = __path__
__spec__.cached = None

# final cleanup
del globals()['__data']
del globals()['__code']
# out-dated internals
del globals()['__cached__']
"""
del globals()['zipfile']
'''

injector_code = dedent(injector_code).lstrip()
injector_code = injector_code.encode('utf-8')
Expand All @@ -249,3 +238,5 @@ def _install_injectors():
pass
except OSError as e:
console_write('Unable to write injector to "%s" - %s' % (injector_path, e))

_install_injectors()
15 changes: 15 additions & 0 deletions package_control/clients/pypi_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,18 @@ def _make_download_info(pattern, selectors, version, assets):
return info

return None

@staticmethod
def _expand_asset_variables(asset_templates):
output = []
for pattern, spec in JSONApiClient._expand_asset_variables(asset_templates):
if len(spec["python_versions"]) == 1:
output.append((pattern, spec))
continue

for py_ver in spec["python_versions"]:
new_spec = spec.copy()
new_spec["python_versions"] = [py_ver]
output.append((pattern, new_spec))

return output
4 changes: 2 additions & 2 deletions package_control/distinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def generate_wheel(python_version, plat_specific):
specific to a platform and optionally architecture
"""

if python_version is not None and python_version not in {"3.3", "3.8"}:
if python_version is not None and python_version not in sys_path.python_versions():
raise ValueError("Invalid python_version %s" % repr(python_version))

version_tag = "py3"
Expand All @@ -171,7 +171,7 @@ def generate_wheel(python_version, plat_specific):
arch = os.uname()[4]
if python_version == "3.3":
arch_tag = "macosx_10_7_%s" % arch
elif python_version == "3.8":
else:
arch_tag = "macosx_10_9_%s" % arch
elif sys.platform == "linux":
arch_tag = "linux_%s" % os.uname()[4]
Expand Down
14 changes: 12 additions & 2 deletions package_control/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
from . import distinfo
from .clear_directory import delete_directory

BUILTIN_LIBRARIES = {"3.3": {}, "3.8": {"enum", "pathlib", "typing"}}
BUILTIN_LIBRARIES = {
"3.3": {},
"3.8": {"enum", "pathlib", "typing"},
"3.13": {"enum", "pathlib", "typing"},
}
"""3rd-party libraries, which are part of stdlib as of certain python version"""

DEPENDENCY_NAME_MAP = {
Expand Down Expand Up @@ -270,9 +274,15 @@ def convert_dependency(dependency_path, python_version, name, version, descripti

# include st4 dependencies on ST4, only
if int(sublime.version()) >= 4000:
# platform / arch specific releases must exactly match requested python version
# as they are expected to contain compiled libraries
install_rel_paths.append(("st4_arch", "st4_py{}_{}_{}".format(py, plat, arch)))
install_rel_paths.append(("st4_plat", "st4_py{}_{}".format(py, plat)))
install_rel_paths.append(("st4_py", "st4_py{}".format(py)))
# pure python releases releases for python 3.13+
if python_version == "3.13":
install_rel_paths.append(("st4_py", "st4_py313".format()))
# pure python releases for python 3.8+
install_rel_paths.append(("st4_py", "st4_py38".format()))
install_rel_paths.append(("st4", "st4"))

# platform/arch specific st3 dependencies are most likely only compatible with python 3.3
Expand Down
2 changes: 1 addition & 1 deletion package_control/package_disabler.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# Relative import does not work here due to hard loading events
# into global package_control (see bootstrap.py)!
from package_control import events
except ImportError:
except Exception:
# use relative import, if bootstrapping has not yet been completed
from . import events

Expand Down
43 changes: 11 additions & 32 deletions package_control/package_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,20 +261,24 @@ def get_python_version(self, package_name):
:return:
A unicode string of "3.3" or "3.8"
"""
supported_python_versions = sys_path.python_versions()

if self.settings["disable_plugin_host_3.3"]:
return "3.8"
# package runs on latest available python version
if len(supported_python_versions) == 1 or package_name.lower() == "user":
return supported_python_versions[-1]

python_version = read_package_file(package_name, ".python-version")
if python_version:
python_version = python_version.strip()
if python_version in sys_path.lib_paths():
# if requested version is supported, use it
if python_version in supported_python_versions:
return python_version

if package_name.lower() == "user" and self.settings['version'] > 4000:
return "3.8"
# otherwise, use latest python version
return supported_python_versions[-1]

return "3.3"
# package runs on earliest available python
return supported_python_versions[0]

def get_version(self, package_name):
"""
Expand Down Expand Up @@ -1555,8 +1559,8 @@ def install_package(self, package_name, unattended=False):
except (KeyError):
unpack = False

supported_python_versions = sys_path.python_versions()
python_version = "3.3"
supported_python_versions = set(sys_path.lib_paths().keys())

try:
python_version_file = common_folder + '.python-version'
Expand Down Expand Up @@ -1589,31 +1593,6 @@ def install_package(self, package_name, unattended=False):
except (FileNotFoundError):
pass

library_names = release.get('libraries')
if not library_names:
# If libraries were not in the channel, try the package
try:
lib_info_json = package_zip.read(common_folder + 'dependencies.json')
lib_info = json.loads(lib_info_json.decode('utf-8'))
except (KeyError):
lib_info = {}
except (ValueError):
console_write(
'''
Failed to parse the dependencies.json for "%s"
''',
package_name
)
return False

library_names = self.select_libraries(lib_info)

if library_names:
self.install_libraries(
library.names_to_libraries(library_names, python_version),
fail_early=False
)

if package_name != old_package_name:
self.rename_package(old_package_name, package_name)

Expand Down
11 changes: 11 additions & 0 deletions package_control/package_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,9 @@ def remove_packages(self, packages, progress=None, package_kind=''):
elif result is None:
deferred.add(package)

required_libraries = self.manager.find_required_libraries()
self.manager.cleanup_libraries(required_libraries=required_libraries)

if num_packages == 1:
message = 'Package {} successfully removed'.format(list(packages)[0])
elif num_packages == num_success:
Expand Down Expand Up @@ -395,6 +398,10 @@ def run_install_tasks(self, tasks, progress=None, unattended=False, package_kind
elif result is None:
package_names.remove(task.package_name)

required_libraries = self.manager.find_required_libraries()
self.manager.install_libraries(libraries=required_libraries, fail_early=False)
self.manager.cleanup_libraries(required_libraries=required_libraries)

if num_packages == num_success:
if package_kind or num_packages > 1:
message = 'All {}packages successfully installed'.format(package_kind)
Expand Down Expand Up @@ -488,6 +495,10 @@ def run_upgrade_tasks(self, tasks, progress=None, unattended=False):
if package != task.available_name:
disable_packages[self.INSTALL].remove(task.available_name)

required_libraries = self.manager.find_required_libraries()
self.manager.install_libraries(libraries=required_libraries, fail_early=False)
self.manager.cleanup_libraries(required_libraries=required_libraries)

if num_packages == num_success:
if num_packages > 1:
message = 'All packages successfully upgraded'
Expand Down
Loading
Loading