Skip to content

Commit

Permalink
Persist processes in disk and reload them on plugin_load
Browse files Browse the repository at this point in the history
  • Loading branch information
nicosantangelo committed Sep 5, 2016
1 parent d230c62 commit 70fbe4e
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 25 deletions.
1 change: 1 addition & 0 deletions .sublime-gulp.cache
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
59 changes: 53 additions & 6 deletions caches.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,29 @@
import codecs
import os

is_sublime_text_3 = int(sublime.version()) >= 3000

if is_sublime_text_3:
from .configuration import Configuration
else:
from configuration import Configuration


class ProcessCache():
_procs = []
_persist_processes = True
last_command = None

@classmethod
def copy(cls):
def set_persist_processes(cls, value):
cls._persist_processes = value

@classmethod
def get_persisted(cls):
return cls.cache_file().read() or []

@classmethod
def get(cls):
return cls._procs[:]

@classmethod
Expand All @@ -21,13 +37,18 @@ def remove_dead(process):

@classmethod
def add(cls, process):
cls._procs.append(process)
cls.last_command = process.last_command
if process not in cls._procs:
cls._procs.append(process)

process = process.to_json()
cls.cache_file().update(lambda procs: procs + [process] if process not in procs else procs)

@classmethod
def remove(cls, process):
if process in cls._procs:
cls._procs.remove(process)
cls.cache_file().update(lambda procs: [proc for proc in procs if proc['pid'] != process.pid])

@classmethod
def kill_all(cls):
Expand All @@ -36,7 +57,7 @@ def kill_all(cls):

@classmethod
def each(cls, fn):
for process in cls.copy():
for process in cls.get():
fn(process)

@classmethod
Expand All @@ -46,14 +67,40 @@ def empty(cls):
@classmethod
def clear(cls):
del cls._procs[:]
cls.cache_file().write([])

@classmethod
def cache_file(cls):
if cls._persist_processes:
return CacheFile(Configuration.PACKAGE_PATH)
else:
return Cache()


class CacheFile():
cache_file_name = ".sublime-gulp.cache"
class Cache():
def exists(self):
pass

def remove(self):
pass

def open(self, mode="r"):
pass

def read(self):
pass

def write(self, data):
pass

def update(self, fn):
pass


class CacheFile(Cache):
def __init__(self, working_dir):
self.working_dir = working_dir
self.cache_path = os.path.join(self.working_dir, CacheFile.cache_file_name)
self.cache_path = os.path.join(self.working_dir, Configuration.CACHE_FILE_NAME)

def exists(self):
return os.path.exists(self.cache_path)
Expand Down
32 changes: 22 additions & 10 deletions cross_platform_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import os
from threading import Thread

if int(sublime.version()) >= 3000:
is_sublime_text_3 = int(sublime.version()) >= 3000

if is_sublime_text_3:
from .dir_context import Dir
from .cross_platform_codecs import CrossPlatformCodecs
from .caches import ProcessCache, CacheFile
Expand All @@ -15,16 +17,19 @@


class CrossPlatformProcess():
def __init__(self, settings):
self.working_dir = settings.working_dir
self.nonblocking = settings.nonblocking
self.path = Env.get_path(settings.exec_args)
self.last_command = ""
def __init__(self, working_dir="", nonblocking=False, exec_args={}, last_command="", pid=None):
self.working_dir = working_dir
self.nonblocking = nonblocking
self.path = Env.get_path(exec_args)
self.pid = pid
self.last_command = last_command
self.process = None
self.failed = False

def run(self, command):
with Dir.cd(self.working_dir):
self.process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.path, shell=True, preexec_fn=self._preexec_val())
self.pid = self.process.pid

self.last_command = command.rstrip()
ProcessCache.add(self)
Expand All @@ -35,6 +40,7 @@ def run_sync(self, command):

with Dir.cd(self.working_dir):
self.process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.path, shell=True)
self.pid = self.process.pid
(stdout, stderr) = self.process.communicate()
self.failed = self.process.returncode == 127 or stderr

Expand Down Expand Up @@ -77,20 +83,26 @@ def terminate(self):
ProcessCache.remove(self)

def is_alive(self):
return self.process.poll() is None
return (self.process is None and self.pid is not None) or self.process.poll() is None

def returncode(self):
return self.process.returncode

def kill(self):
pid = self.process.pid
if sublime.platform() == "windows":
kill_process = subprocess.Popen(['C:\\Windows\\system32\\taskkill.exe', '/F', '/T', '/PID', str(pid)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
kill_process = subprocess.Popen(['C:\\Windows\\system32\\taskkill.exe', '/F', '/T', '/PID', str(self.pid)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
kill_process.communicate()
else:
os.killpg(pid, signal.SIGTERM)
os.killpg(self.pid, signal.SIGTERM)
ProcessCache.remove(self)

def to_json(self):
return {
'workding_dir': self.working_dir,
'command': self.last_command,
'pid': self.pid,
}


class Env():
@classmethod
Expand Down
34 changes: 25 additions & 9 deletions gulp.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,8 @@ def fetch_json(self):
raise Exception("Have you renamed a folder?.\nSometimes Sublime doesn't update the project path, try removing the folder from the project and adding it again.")

def write_to_cache(self):
package_path = os.path.join(sublime.packages_path(), self.package_name)

process = CrossPlatformProcess(self)
(stdout, stderr) = process.run_sync(r'node "%s/write_tasks_to_cache.js"' % package_path)
process = CrossPlatformProcess(self.working_dir, self.nonblocking, self.exec_args)
(stdout, stderr) = process.run_sync(r'node "%s/write_tasks_to_cache.js"' % Configuration.PACKAGE_PATH)

if process.failed:
try:
Expand All @@ -147,7 +145,7 @@ def write_to_cache(self):
return self.fetch_json()

def write_to_cache_without_js(self):
process = CrossPlatformProcess(self)
process = CrossPlatformProcess(self.working_dir, self.nonblocking, self.exec_args)
(stdout, stderr) = process.run_sync(r'gulp -v')

if process.failed or not GulpVersion(stdout).supports_tasks_simple():
Expand Down Expand Up @@ -199,7 +197,7 @@ def construct_gulp_task(self):
return r"gulp %s %s" % (self.task_name, self.task_flag)

def run_process(self, task):
process = CrossPlatformProcess(self)
process = CrossPlatformProcess(self.working_dir, self.nonblocking, self.exec_args)
process.run(task)
stdout, stderr = process.communicate(self.append_to_output_view_in_main_thread)
self.defer_sync(lambda: self.finish(stdout, stderr))
Expand Down Expand Up @@ -250,14 +248,18 @@ def work(self):
if ProcessCache.empty():
self.status_message("There are no running tasks")
else:
self.procs = ProcessCache.copy()
quick_panel_list = [[process.last_command, process.working_dir] for process in self.procs]
self.procs = ProcessCache.get()
quick_panel_list = [[process.last_command, process.working_dir, 'Pid: %d' % process.pid] for process in self.procs]
self.show_quick_panel(quick_panel_list, self.kill_process, font=0)

def kill_process(self, index=-1):
if index >= 0 and index < len(self.procs):
process = self.procs[index]
process.kill()
try:
process.kill()
except ProcessLookupError as e:
print('Process %d seems to be dead already' % process.pid)

self.show_output_panel('')
self.append_to_output_view("\n%s killed! # %s\n" % (process.last_command, process.working_dir))

Expand Down Expand Up @@ -350,3 +352,17 @@ def run(self):
self.window.run_command("gulp_kill")
finally:
self.window.run_command("exit")


def plugin_loaded():
def load_process_cache():
for process in ProcessCache.get_persisted():
ProcessCache.add(
CrossPlatformProcess(process['workding_dir'], last_command=process['command'], pid=process['pid'])
)

sublime.set_timeout(load_process_cache, 2000)


if not is_sublime_text_3:
plugin_loaded()

0 comments on commit 70fbe4e

Please sign in to comment.