forked from wbond/package_control
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split the source into individual files for easier maintenance. Added …
…get_cache(), set_cache(), console_write() and show_error() in the process.
- Loading branch information
Showing
64 changed files
with
5,250 additions
and
4,810 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
import sublime | ||
import threading | ||
import re | ||
import os | ||
import datetime | ||
import time | ||
|
||
from .console_write import console_write | ||
from .package_installer import PackageInstaller | ||
from .package_renamer import PackageRenamer | ||
|
||
|
||
class AutomaticUpgrader(threading.Thread): | ||
""" | ||
Automatically checks for updated packages and installs them. controlled | ||
by the `auto_upgrade`, `auto_upgrade_ignore`, `auto_upgrade_frequency` and | ||
`auto_upgrade_last_run` settings. | ||
""" | ||
|
||
def __init__(self, found_packages): | ||
""" | ||
:param found_packages: | ||
A list of package names for the packages that were found to be | ||
installed on the machine. | ||
""" | ||
|
||
self.installer = PackageInstaller() | ||
self.manager = self.installer.manager | ||
|
||
self.load_settings() | ||
|
||
self.package_renamer = PackageRenamer() | ||
self.package_renamer.load_settings() | ||
|
||
self.auto_upgrade = self.settings.get('auto_upgrade') | ||
self.auto_upgrade_ignore = self.settings.get('auto_upgrade_ignore') | ||
|
||
self.next_run = int(time.time()) | ||
self.last_run = None | ||
last_run_file = os.path.join(sublime.packages_path(), 'User', | ||
'Package Control.last-run') | ||
|
||
if os.path.isfile(last_run_file): | ||
with open(last_run_file) as fobj: | ||
try: | ||
self.last_run = int(fobj.read()) | ||
except ValueError: | ||
pass | ||
|
||
frequency = self.settings.get('auto_upgrade_frequency') | ||
if frequency: | ||
if self.last_run: | ||
self.next_run = int(self.last_run) + (frequency * 60 * 60) | ||
else: | ||
self.next_run = time.time() | ||
|
||
# Detect if a package is missing that should be installed | ||
self.missing_packages = list(set(self.installed_packages) - | ||
set(found_packages)) | ||
|
||
if self.auto_upgrade and self.next_run <= time.time(): | ||
with open(last_run_file, 'w') as fobj: | ||
fobj.write(str(int(time.time()))) | ||
|
||
threading.Thread.__init__(self) | ||
|
||
def load_settings(self): | ||
""" | ||
Loads the list of installed packages from the | ||
Package Control.sublime-settings file | ||
""" | ||
|
||
self.settings_file = 'Package Control.sublime-settings' | ||
self.settings = sublime.load_settings(self.settings_file) | ||
self.installed_packages = self.settings.get('installed_packages', []) | ||
self.should_install_missing = self.settings.get('install_missing') | ||
if not isinstance(self.installed_packages, list): | ||
self.installed_packages = [] | ||
|
||
def run(self): | ||
self.install_missing() | ||
|
||
if self.next_run > time.time(): | ||
self.print_skip() | ||
return | ||
|
||
self.upgrade_packages() | ||
|
||
def install_missing(self): | ||
""" | ||
Installs all packages that were listed in the list of | ||
`installed_packages` from Package Control.sublime-settings but were not | ||
found on the filesystem and passed as `found_packages`. | ||
""" | ||
|
||
if not self.missing_packages or not self.should_install_missing: | ||
return | ||
|
||
console_write(u'Installing %s missing packages' % len(self.missing_packages), True) | ||
for package in self.missing_packages: | ||
if self.installer.manager.install_package(package): | ||
console_write(u'Installed missing package %s' % package, True) | ||
|
||
def print_skip(self): | ||
""" | ||
Prints a notice in the console if the automatic upgrade is skipped | ||
due to already having been run in the last `auto_upgrade_frequency` | ||
hours. | ||
""" | ||
|
||
last_run = datetime.datetime.fromtimestamp(self.last_run) | ||
next_run = datetime.datetime.fromtimestamp(self.next_run) | ||
date_format = '%Y-%m-%d %H:%M:%S' | ||
message_string = u'Skipping automatic upgrade, last run at %s, next run at %s or after' % ( | ||
last_run.strftime(date_format), next_run.strftime(date_format)) | ||
console_write(message_string, True) | ||
|
||
def upgrade_packages(self): | ||
""" | ||
Upgrades all packages that are not currently upgraded to the lastest | ||
version. Also renames any installed packages to their new names. | ||
""" | ||
|
||
if not self.auto_upgrade: | ||
return | ||
|
||
self.package_renamer.rename_packages(self.installer) | ||
|
||
packages = self.installer.make_package_list(['install', | ||
'reinstall', 'downgrade', 'overwrite', 'none'], | ||
ignore_packages=self.auto_upgrade_ignore) | ||
|
||
# If Package Control is being upgraded, just do that and restart | ||
for package in packages: | ||
if package[0] != 'Package Control': | ||
continue | ||
|
||
def reset_last_run(): | ||
settings = sublime.load_settings(self.settings_file) | ||
settings.set('auto_upgrade_last_run', None) | ||
sublime.save_settings(self.settings_file) | ||
sublime.set_timeout(reset_last_run, 1) | ||
packages = [package] | ||
break | ||
|
||
if not packages: | ||
console_write(u'No updated packages', True) | ||
return | ||
|
||
console_write(u'Installing %s upgrades' % len(packages), True) | ||
for package in packages: | ||
self.installer.manager.install_package(package[0]) | ||
version = re.sub('^.*?(v[\d\.]+).*?$', '\\1', package[2]) | ||
if version == package[2] and version.find('pull with') != -1: | ||
vcs = re.sub('^pull with (\w+).*?$', '\\1', version) | ||
version = 'latest %s commit' % vcs | ||
message_string = u'Upgraded %s to %s' % (package[0], version) | ||
console_write(message_string, True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import time | ||
|
||
|
||
# A cache of channel and repository info to allow users to install multiple | ||
# packages without having to wait for the metadata to be downloaded more | ||
# than once. The keys are managed locally by the utilizing code. | ||
_channel_repository_cache = {} | ||
|
||
|
||
def set_cache(key, data, ttl=300): | ||
""" | ||
Sets an in-memory cache value | ||
:param key: | ||
The string key | ||
:param data: | ||
The data to cache | ||
:param ttl: | ||
The integer number of second to cache the data for | ||
""" | ||
|
||
_channel_repository_cache[key] = { | ||
'data': data, | ||
'expires': time.time() + ttl | ||
} | ||
|
||
|
||
def get_cache(key, default=None): | ||
""" | ||
Gets an in-memory cache value | ||
:param key: | ||
The string key | ||
:param default: | ||
The value to return if the key has not been set, or the ttl expired | ||
:return: | ||
The cached value, or default | ||
""" | ||
|
||
struct = _channel_repository_cache.get(key, {}) | ||
expires = struct.get('expires') | ||
if expires and expires > time.time(): | ||
return struct.get('data') | ||
return default |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import os | ||
|
||
|
||
def clear_directory(directory, ignore_paths=None): | ||
""" | ||
Tries to delete all files and folders from a directory | ||
:param directory: | ||
The string directory path | ||
:param ignore_paths: | ||
An array of paths to ignore while deleting files | ||
:return: | ||
If all of the files and folders were successfully deleted | ||
""" | ||
|
||
was_exception = False | ||
for root, dirs, files in os.walk(directory, topdown=False): | ||
paths = [os.path.join(root, f) for f in files] | ||
paths.extend([os.path.join(root, d) for d in dirs]) | ||
|
||
for path in paths: | ||
try: | ||
# Don't delete the metadata file, that way we have it | ||
# when the reinstall happens, and the appropriate | ||
# usage info can be sent back to the server | ||
if ignore_paths and path in ignore_paths: | ||
continue | ||
if os.path.isdir(path): | ||
os.rmdir(path) | ||
else: | ||
os.remove(path) | ||
except (OSError, IOError): | ||
was_exception = True | ||
|
||
return not was_exception |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import os | ||
import subprocess | ||
import re | ||
|
||
|
||
def create_cmd(args, basename_binary=False): | ||
""" | ||
Takes an array of strings to be passed to subprocess.Popen and creates | ||
a string that can be pasted into a terminal | ||
:param args: | ||
The array containing the binary name/path and all arguments | ||
:param basename_binary: | ||
If only the basename of the binary should be disabled instead of the full path | ||
:return: | ||
The command string | ||
""" | ||
|
||
if basename_binary: | ||
args[0] = os.path.basename(args[0]) | ||
|
||
if os.name == 'nt': | ||
return subprocess.list2cmdline(args) | ||
else: | ||
escaped_args = [] | ||
for arg in args: | ||
if re.search('^[a-zA-Z0-9/_^\\-\\.:=]+$', arg) == None: | ||
arg = u"'" + arg.replace(u"'", u"'\\''") + u"'" | ||
escaped_args.append(arg) | ||
return u' '.join(escaped_args) |
Empty file.
36 changes: 36 additions & 0 deletions
36
package_control/commands/add_repository_channel_command.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import sublime | ||
import sublime_plugin | ||
|
||
|
||
class AddRepositoryChannelCommand(sublime_plugin.WindowCommand): | ||
""" | ||
A command to add a new channel (list of repositories) to the user's machine | ||
""" | ||
|
||
def run(self): | ||
self.window.show_input_panel('Channel JSON URL', '', | ||
self.on_done, self.on_change, self.on_cancel) | ||
|
||
def on_done(self, input): | ||
""" | ||
Input panel handler - adds the provided URL as a channel | ||
:param input: | ||
A string of the URL to the new channel | ||
""" | ||
|
||
settings = sublime.load_settings('Package Control.sublime-settings') | ||
repository_channels = settings.get('repository_channels', []) | ||
if not repository_channels: | ||
repository_channels = [] | ||
repository_channels.append(input) | ||
settings.set('repository_channels', repository_channels) | ||
sublime.save_settings('Package Control.sublime-settings') | ||
sublime.status_message(('Channel %s successfully ' + | ||
'added') % input) | ||
|
||
def on_change(self, input): | ||
pass | ||
|
||
def on_cancel(self): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import sublime | ||
import sublime_plugin | ||
|
||
|
||
class AddRepositoryCommand(sublime_plugin.WindowCommand): | ||
""" | ||
A command to add a new repository to the user's machine | ||
""" | ||
|
||
def run(self): | ||
self.window.show_input_panel('GitHub or BitBucket Web URL, or Custom' + | ||
' JSON Repository URL', '', self.on_done, | ||
self.on_change, self.on_cancel) | ||
|
||
def on_done(self, input): | ||
""" | ||
Input panel handler - adds the provided URL as a repository | ||
:param input: | ||
A string of the URL to the new repository | ||
""" | ||
|
||
settings = sublime.load_settings('Package Control.sublime-settings') | ||
repositories = settings.get('repositories', []) | ||
if not repositories: | ||
repositories = [] | ||
repositories.append(input) | ||
settings.set('repositories', repositories) | ||
sublime.save_settings('Package Control.sublime-settings') | ||
sublime.status_message('Repository %s successfully added' % input) | ||
|
||
def on_change(self, input): | ||
pass | ||
|
||
def on_cancel(self): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import sublime_plugin | ||
|
||
from ..package_creator import PackageCreator | ||
|
||
|
||
class CreateBinaryPackageCommand(sublime_plugin.WindowCommand, PackageCreator): | ||
""" | ||
Command to create a binary .sublime-package file. Binary packages in | ||
general exclude the .py source files and instead include the .pyc files. | ||
Actual included and excluded files are controlled by settings. | ||
""" | ||
|
||
def run(self): | ||
self.show_panel() | ||
|
||
def on_done(self, picked): | ||
""" | ||
Quick panel user selection handler - processes the user package | ||
selection and create the package file | ||
:param picked: | ||
An integer of the 0-based package name index from the presented | ||
list. -1 means the user cancelled. | ||
""" | ||
|
||
if picked == -1: | ||
return | ||
package_name = self.packages[picked] | ||
package_destination = self.get_package_destination() | ||
|
||
if self.manager.create_package(package_name, package_destination, | ||
binary_package=True): | ||
self.window.run_command('open_dir', {"dir": | ||
package_destination, "file": package_name + | ||
'.sublime-package'}) |
Oops, something went wrong.