mirror of
https://github.com/sstent/sublime-text-3.git
synced 2026-02-01 10:01:42 +00:00
backing up sublime settings
This commit is contained in:
7
Packages/SublimeREPL/repls/__init__.py
Normal file
7
Packages/SublimeREPL/repls/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from .repl import *
|
||||
from .subprocess_repl import *
|
||||
from .sublimehaskell_repl import *
|
||||
from .telnet_repl import *
|
||||
from .sublimepython_repl import *
|
||||
from .powershell_repl import *
|
||||
# from .execnet_repl import * # disabled for now
|
||||
60
Packages/SublimeREPL/repls/autocomplete_server.py
Normal file
60
Packages/SublimeREPL/repls/autocomplete_server.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
import socket
|
||||
import threading
|
||||
import sublime
|
||||
|
||||
def read_netstring(s):
|
||||
size = 0
|
||||
while True:
|
||||
byte = s.recv(1)
|
||||
if byte == b':':
|
||||
break
|
||||
size = size * 10 + int(byte)
|
||||
msg = b""
|
||||
while size != 0:
|
||||
msg += s.recv(size)
|
||||
size -= len(msg)
|
||||
byte = s.recv(1)
|
||||
assert byte == b','
|
||||
return msg.decode("utf-8")
|
||||
|
||||
def send_netstring(s, msg):
|
||||
payload = "".join([str(len(msg)), ':', msg, ',']).encode("utf-8")
|
||||
s.sendall(payload)
|
||||
|
||||
|
||||
class AutocompleteServer(object):
|
||||
def __init__(self, repl, server_ip="127.0.0.1"):
|
||||
self._repl = repl
|
||||
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self._cli_sock = None
|
||||
self._server_ip = server_ip
|
||||
|
||||
def start(self):
|
||||
self._sock.bind((self._server_ip, 0))
|
||||
threading.Thread(target=self._wait).start()
|
||||
|
||||
def _wait(self):
|
||||
self._sock.listen(1)
|
||||
s, address = self._sock.accept()
|
||||
self._cli_sock = s
|
||||
|
||||
def port(self):
|
||||
return self._sock.getsockname()[1]
|
||||
|
||||
def connected(self):
|
||||
return bool(self._cli_sock)
|
||||
|
||||
def complete(self, whole_line, pos_in_line, prefix, whole_prefix, locations):
|
||||
req = json.dumps({"text": "", "line": whole_line, "cursor_pos": pos_in_line})
|
||||
send_netstring(self._cli_sock, req)
|
||||
self._cli_sock.settimeout(4)
|
||||
msg = read_netstring(self._cli_sock)
|
||||
self._cli_sock.settimeout(None)
|
||||
res = json.loads(msg)
|
||||
if not res:
|
||||
return []
|
||||
return [(x, x) for x in res[1]]
|
||||
111
Packages/SublimeREPL/repls/execnet_repl.py
Normal file
111
Packages/SublimeREPL/repls/execnet_repl.py
Normal file
@@ -0,0 +1,111 @@
|
||||
# encoding: utf-8
|
||||
from . import repl
|
||||
from queue import Queue
|
||||
import sys
|
||||
import execnet
|
||||
|
||||
REMOTE_CODE = """
|
||||
from __future__ import with_statement
|
||||
from __future__ import division
|
||||
from __future__ import absolute_import
|
||||
#if '{activate_file}':
|
||||
# execfile('{activate_file}', dict(__file__='{activate_file}'))
|
||||
|
||||
import code
|
||||
import sys
|
||||
import time
|
||||
import contextlib
|
||||
import threading
|
||||
try:
|
||||
from Queue import Queue
|
||||
except ImportError:
|
||||
from queue import Queue # py3
|
||||
|
||||
class ChannelOut(object):
|
||||
def write(self, data):
|
||||
channel.send(data)
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
@contextlib.contextmanager
|
||||
def redirect_stdio():
|
||||
orig = (sys.stdout, sys.stderr)
|
||||
sys.stdout = sys.stderr = ChannelOut()
|
||||
yield
|
||||
(sys.stdout, sys.stderr) = orig
|
||||
|
||||
class InterceptingConsole(code.InteractiveConsole):
|
||||
PS1 = "{ps1}"
|
||||
PS2 = "... "
|
||||
def __init__(self):
|
||||
code.InteractiveConsole.__init__(self)
|
||||
self.input = Queue()
|
||||
self.output = channel
|
||||
self.output.send(self.PS1)
|
||||
|
||||
def write(self, data):
|
||||
self.output.send(data)
|
||||
|
||||
def push(self, line):
|
||||
with redirect_stdio():
|
||||
more = code.InteractiveConsole.push(self, line)
|
||||
self.output.send(self.PS2 if more else self.PS1)
|
||||
return more
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
line = self.input.get()
|
||||
if line is None:
|
||||
break
|
||||
self.push(line)
|
||||
|
||||
|
||||
ic = InterceptingConsole()
|
||||
_thread = threading.Thread(target=ic.run)
|
||||
_thread.start()
|
||||
|
||||
channel.setcallback(ic.input.put, endmarker=None)
|
||||
|
||||
while not channel.isclosed():
|
||||
time.sleep(1.0)
|
||||
"""
|
||||
|
||||
|
||||
class ExecnetRepl(repl.Repl):
|
||||
TYPE = "execnet_repl"
|
||||
|
||||
def __init__(self, encoding, connection_string=None, activate_file="", ps1=">>> "):
|
||||
super(ExecnetRepl, self).__init__(encoding, "python", "\n", False)
|
||||
self._connections_string = connection_string
|
||||
self._ps1 = ps1
|
||||
self._gw = execnet.makegateway(connection_string)
|
||||
remote_code = REMOTE_CODE.format(ps1=ps1, activate_file=activate_file)
|
||||
self._channel = self._gw.remote_exec(remote_code)
|
||||
self.output = Queue()
|
||||
self._channel.setcallback(self.output.put, endmarker=None)
|
||||
self._alive = True
|
||||
self._killed = False
|
||||
|
||||
def name(self):
|
||||
return "execnet " + self._ps1.split()[0]
|
||||
|
||||
def is_alive(self):
|
||||
return self._alive
|
||||
|
||||
def write_bytes(self, bytes):
|
||||
if self._channel.isclosed():
|
||||
self._alive = False
|
||||
else:
|
||||
self._channel.send(bytes)
|
||||
|
||||
def read_bytes(self):
|
||||
bytes = self.output.get()
|
||||
if bytes is None:
|
||||
self._gw.exit()
|
||||
else:
|
||||
return bytes
|
||||
|
||||
def kill(self):
|
||||
self._killed = True
|
||||
self._channel.close()
|
||||
self._gw.exit()
|
||||
3
Packages/SublimeREPL/repls/killableprocess/__init__.py
Normal file
3
Packages/SublimeREPL/repls/killableprocess/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .killableprocess import Popen, mswindows
|
||||
if mswindows:
|
||||
from .winprocess import STARTUPINFO, STARTF_USESHOWWINDOW
|
||||
325
Packages/SublimeREPL/repls/killableprocess/killableprocess.py
Normal file
325
Packages/SublimeREPL/repls/killableprocess/killableprocess.py
Normal file
@@ -0,0 +1,325 @@
|
||||
# killableprocess - subprocesses which can be reliably killed
|
||||
#
|
||||
# Parts of this module are copied from the subprocess.py file contained
|
||||
# in the Python distribution.
|
||||
#
|
||||
# Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
|
||||
#
|
||||
# Additions and modifications written by Benjamin Smedberg
|
||||
# <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation
|
||||
# <http://www.mozilla.org/>
|
||||
#
|
||||
# More Modifications
|
||||
# Copyright (c) 2006-2007 by Mike Taylor <bear@code-bear.com>
|
||||
# Copyright (c) 2007-2008 by Mikeal Rogers <mikeal@mozilla.com>
|
||||
#
|
||||
# By obtaining, using, and/or copying this software and/or its
|
||||
# associated documentation, you agree that you have read, understood,
|
||||
# and will comply with the following terms and conditions:
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software and
|
||||
# its associated documentation for any purpose and without fee is
|
||||
# hereby granted, provided that the above copyright notice appears in
|
||||
# all copies, and that both that copyright notice and this permission
|
||||
# notice appear in supporting documentation, and that the name of the
|
||||
# author not be used in advertising or publicity pertaining to
|
||||
# distribution of the software without specific, written prior
|
||||
# permission.
|
||||
#
|
||||
# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
||||
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
"""killableprocess - Subprocesses which can be reliably killed
|
||||
|
||||
This module is a subclass of the builtin "subprocess" module. It allows
|
||||
processes that launch subprocesses to be reliably killed on Windows (via the Popen.kill() method.
|
||||
|
||||
It also adds a timeout argument to Wait() for a limited period of time before
|
||||
forcefully killing the process.
|
||||
|
||||
Note: On Windows, this module requires Windows 2000 or higher (no support for
|
||||
Windows 95, 98, or NT 4.0). It also requires ctypes, which is bundled with
|
||||
Python 2.5+ or available from http://python.net/crew/theller/ctypes/
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import datetime
|
||||
import types
|
||||
|
||||
try:
|
||||
from subprocess import CalledProcessError
|
||||
except ImportError:
|
||||
# Python 2.4 doesn't implement CalledProcessError
|
||||
class CalledProcessError(Exception):
|
||||
"""This exception is raised when a process run by check_call() returns
|
||||
a non-zero exit status. The exit status will be stored in the
|
||||
returncode attribute."""
|
||||
def __init__(self, returncode, cmd):
|
||||
self.returncode = returncode
|
||||
self.cmd = cmd
|
||||
def __str__(self):
|
||||
return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
|
||||
|
||||
mswindows = (sys.platform == "win32")
|
||||
py2 = (sys.version_info[0] == 2)
|
||||
|
||||
if mswindows:
|
||||
from . import winprocess
|
||||
else:
|
||||
import signal
|
||||
|
||||
def call(*args, **kwargs):
|
||||
waitargs = {}
|
||||
if "timeout" in kwargs:
|
||||
waitargs["timeout"] = kwargs.pop("timeout")
|
||||
|
||||
return Popen(*args, **kwargs).wait(**waitargs)
|
||||
|
||||
def check_call(*args, **kwargs):
|
||||
"""Call a program with an optional timeout. If the program has a non-zero
|
||||
exit status, raises a CalledProcessError."""
|
||||
|
||||
retcode = call(*args, **kwargs)
|
||||
if retcode:
|
||||
cmd = kwargs.get("args")
|
||||
if cmd is None:
|
||||
cmd = args[0]
|
||||
raise CalledProcessError(retcode, cmd)
|
||||
|
||||
if not mswindows:
|
||||
def DoNothing(*args):
|
||||
pass
|
||||
|
||||
class Popen(subprocess.Popen):
|
||||
kill_called = False
|
||||
if mswindows:
|
||||
if py2:
|
||||
def _execute_child(self, args, executable, preexec_fn, close_fds,
|
||||
cwd, env, universal_newlines, startupinfo,
|
||||
creationflags, shell,
|
||||
p2cread, p2cwrite,
|
||||
c2pread, c2pwrite,
|
||||
errread, errwrite):
|
||||
return self._execute_child_compat(args, executable, preexec_fn, close_fds,
|
||||
cwd, env, universal_newlines, startupinfo,
|
||||
creationflags, shell,
|
||||
p2cread, p2cwrite,
|
||||
c2pread, c2pwrite,
|
||||
errread, errwrite)
|
||||
else:
|
||||
def _execute_child(self, args, executable, preexec_fn, close_fds,
|
||||
pass_fds,
|
||||
cwd, env,
|
||||
startupinfo,
|
||||
creationflags, shell,
|
||||
p2cread, p2cwrite,
|
||||
c2pread, c2pwrite,
|
||||
errread, errwrite,
|
||||
unused_restore_signals, unused_start_new_session):
|
||||
return self._execute_child_compat(args, executable, preexec_fn, close_fds,
|
||||
cwd, env, True, startupinfo,
|
||||
creationflags, shell,
|
||||
p2cread, p2cwrite,
|
||||
c2pread, c2pwrite,
|
||||
errread, errwrite)
|
||||
|
||||
|
||||
if mswindows:
|
||||
def _execute_child_compat(self, args, executable, preexec_fn, close_fds,
|
||||
cwd, env, universal_newlines, startupinfo,
|
||||
creationflags, shell,
|
||||
p2cread, p2cwrite,
|
||||
c2pread, c2pwrite,
|
||||
errread, errwrite):
|
||||
if not isinstance(args, str):
|
||||
args = subprocess.list2cmdline(args)
|
||||
|
||||
# Always or in the create new process group
|
||||
creationflags |= winprocess.CREATE_NEW_PROCESS_GROUP
|
||||
|
||||
if startupinfo is None:
|
||||
startupinfo = winprocess.STARTUPINFO()
|
||||
|
||||
if None not in (p2cread, c2pwrite, errwrite):
|
||||
startupinfo.dwFlags |= winprocess.STARTF_USESTDHANDLES
|
||||
|
||||
startupinfo.hStdInput = int(p2cread)
|
||||
startupinfo.hStdOutput = int(c2pwrite)
|
||||
startupinfo.hStdError = int(errwrite)
|
||||
if shell:
|
||||
startupinfo.dwFlags |= winprocess.STARTF_USESHOWWINDOW
|
||||
startupinfo.wShowWindow = winprocess.SW_HIDE
|
||||
comspec = os.environ.get("COMSPEC", "cmd.exe")
|
||||
args = comspec + " /c " + args
|
||||
|
||||
# determine if we can create create a job
|
||||
canCreateJob = winprocess.CanCreateJobObject()
|
||||
|
||||
# set process creation flags
|
||||
creationflags |= winprocess.CREATE_SUSPENDED
|
||||
creationflags |= winprocess.CREATE_UNICODE_ENVIRONMENT
|
||||
if canCreateJob:
|
||||
creationflags |= winprocess.CREATE_BREAKAWAY_FROM_JOB
|
||||
|
||||
# create the process
|
||||
hp, ht, pid, tid = winprocess.CreateProcess(
|
||||
executable, args,
|
||||
None, None, # No special security
|
||||
1, # Must inherit handles!
|
||||
creationflags,
|
||||
winprocess.EnvironmentBlock(env),
|
||||
cwd, startupinfo)
|
||||
self._child_created = True
|
||||
self._handle = int(hp)
|
||||
self._thread = ht
|
||||
self.pid = pid
|
||||
self.tid = tid
|
||||
|
||||
if canCreateJob:
|
||||
# We create a new job for this process, so that we can kill
|
||||
# the process and any sub-processes
|
||||
self._job = winprocess.CreateJobObject()
|
||||
winprocess.AssignProcessToJobObject(self._job, int(hp))
|
||||
else:
|
||||
self._job = None
|
||||
|
||||
winprocess.ResumeThread(int(ht))
|
||||
ht.Close()
|
||||
|
||||
if p2cread is not None and p2cread != -1:
|
||||
p2cread.Close()
|
||||
if c2pwrite is not None and c2pwrite != -1:
|
||||
c2pwrite.Close()
|
||||
if errwrite is not None and errwrite != -1:
|
||||
errwrite.Close()
|
||||
time.sleep(.1)
|
||||
|
||||
def kill(self, group=True):
|
||||
"""Kill the process. If group=True, all sub-processes will also be killed."""
|
||||
self.kill_called = True
|
||||
if mswindows:
|
||||
if group and self._job:
|
||||
winprocess.TerminateJobObject(self._job, 127)
|
||||
else:
|
||||
try:
|
||||
winprocess.TerminateProcess(self._handle, 127)
|
||||
except:
|
||||
# TODO: better error handling here
|
||||
pass
|
||||
self.returncode = 127
|
||||
else:
|
||||
if group:
|
||||
try:
|
||||
os.killpg(self.pid, signal.SIGKILL)
|
||||
except: pass
|
||||
else:
|
||||
os.kill(self.pid, signal.SIGKILL)
|
||||
super(Popen, self).kill()
|
||||
self.returncode = -9
|
||||
|
||||
def wait(self, timeout=None, group=True):
|
||||
"""Wait for the process to terminate. Returns returncode attribute.
|
||||
If timeout seconds are reached and the process has not terminated,
|
||||
it will be forcefully killed. If timeout is -1, wait will not
|
||||
time out."""
|
||||
|
||||
if timeout is not None:
|
||||
# timeout is now in milliseconds
|
||||
timeout = timeout * 1000
|
||||
|
||||
if self.returncode is not None:
|
||||
return self.returncode
|
||||
|
||||
starttime = datetime.datetime.now()
|
||||
|
||||
if mswindows:
|
||||
if timeout is None:
|
||||
timeout = -1
|
||||
rc = winprocess.WaitForSingleObject(self._handle, timeout)
|
||||
|
||||
if rc != winprocess.WAIT_TIMEOUT:
|
||||
def check():
|
||||
now = datetime.datetime.now()
|
||||
diff = now - starttime
|
||||
if (diff.seconds * 1000 * 1000 + diff.microseconds) < (timeout * 1000):
|
||||
if self._job:
|
||||
if (winprocess.QueryInformationJobObject(self._job, 8)['BasicInfo']['ActiveProcesses'] > 0):
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
while check():
|
||||
time.sleep(.5)
|
||||
|
||||
now = datetime.datetime.now()
|
||||
diff = now - starttime
|
||||
if (diff.seconds * 1000 * 1000 + diff.microseconds) > (timeout * 1000):
|
||||
self.kill(group)
|
||||
else:
|
||||
self.returncode = winprocess.GetExitCodeProcess(self._handle)
|
||||
else:
|
||||
if (sys.platform == 'linux2') or (sys.platform in ('sunos5', 'solaris')):
|
||||
def group_wait(timeout):
|
||||
try:
|
||||
os.waitpid(self.pid, 0)
|
||||
except OSError as e:
|
||||
pass # If wait has already been called on this pid, bad things happen
|
||||
return self.returncode
|
||||
elif sys.platform == 'darwin':
|
||||
def group_wait(timeout):
|
||||
try:
|
||||
count = 0
|
||||
if timeout is None and self.kill_called:
|
||||
timeout = 10 # Have to set some kind of timeout or else this could go on forever
|
||||
if timeout is None:
|
||||
while 1:
|
||||
os.killpg(self.pid, signal.SIG_DFL)
|
||||
while ((count * 2) <= timeout):
|
||||
os.killpg(self.pid, signal.SIG_DFL)
|
||||
# count is increased by 500ms for every 0.5s of sleep
|
||||
time.sleep(.5); count += 500
|
||||
except OSError:
|
||||
return self.returncode
|
||||
|
||||
if timeout is None:
|
||||
if group is True:
|
||||
return group_wait(timeout)
|
||||
else:
|
||||
subprocess.Popen.wait(self)
|
||||
return self.returncode
|
||||
|
||||
returncode = False
|
||||
|
||||
now = datetime.datetime.now()
|
||||
diff = now - starttime
|
||||
while (diff.seconds * 1000 * 1000 + diff.microseconds) < (timeout * 1000) and ( returncode is False ):
|
||||
if group is True:
|
||||
return group_wait(timeout)
|
||||
else:
|
||||
if subprocess.poll() is not None:
|
||||
returncode = self.returncode
|
||||
time.sleep(.5)
|
||||
now = datetime.datetime.now()
|
||||
diff = now - starttime
|
||||
return self.returncode
|
||||
|
||||
return self.returncode
|
||||
# We get random maxint errors from subprocesses __del__
|
||||
__del__ = lambda self: None
|
||||
|
||||
def setpgid_preexec_fn():
|
||||
os.setpgid(0, 0)
|
||||
|
||||
def runCommand(cmd, **kwargs):
|
||||
if sys.platform != "win32":
|
||||
return Popen(cmd, preexec_fn=setpgid_preexec_fn, **kwargs)
|
||||
else:
|
||||
return Popen(cmd, **kwargs)
|
||||
157
Packages/SublimeREPL/repls/killableprocess/qijo.py
Normal file
157
Packages/SublimeREPL/repls/killableprocess/qijo.py
Normal file
@@ -0,0 +1,157 @@
|
||||
from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE, addressof, c_size_t, c_ulong
|
||||
from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LARGE_INTEGER
|
||||
|
||||
LPVOID = c_void_p
|
||||
LPDWORD = POINTER(DWORD)
|
||||
SIZE_T = c_size_t
|
||||
ULONG_PTR = POINTER(c_ulong)
|
||||
|
||||
# A ULONGLONG is a 64-bit unsigned integer.
|
||||
# Thus there are 8 bytes in a ULONGLONG.
|
||||
# XXX why not import c_ulonglong ?
|
||||
ULONGLONG = BYTE * 8
|
||||
|
||||
class IO_COUNTERS(Structure):
|
||||
# The IO_COUNTERS struct is 6 ULONGLONGs.
|
||||
# TODO: Replace with non-dummy fields.
|
||||
_fields_ = [('dummy', ULONGLONG * 6)]
|
||||
|
||||
class JOBOBJECT_BASIC_ACCOUNTING_INFORMATION(Structure):
|
||||
_fields_ = [('TotalUserTime', LARGE_INTEGER),
|
||||
('TotalKernelTime', LARGE_INTEGER),
|
||||
('ThisPeriodTotalUserTime', LARGE_INTEGER),
|
||||
('ThisPeriodTotalKernelTime', LARGE_INTEGER),
|
||||
('TotalPageFaultCount', DWORD),
|
||||
('TotalProcesses', DWORD),
|
||||
('ActiveProcesses', DWORD),
|
||||
('TotalTerminatedProcesses', DWORD)]
|
||||
|
||||
class JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION(Structure):
|
||||
_fields_ = [('BasicInfo', JOBOBJECT_BASIC_ACCOUNTING_INFORMATION),
|
||||
('IoInfo', IO_COUNTERS)]
|
||||
|
||||
# see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx
|
||||
class JOBOBJECT_BASIC_LIMIT_INFORMATION(Structure):
|
||||
_fields_ = [('PerProcessUserTimeLimit', LARGE_INTEGER),
|
||||
('PerJobUserTimeLimit', LARGE_INTEGER),
|
||||
('LimitFlags', DWORD),
|
||||
('MinimumWorkingSetSize', SIZE_T),
|
||||
('MaximumWorkingSetSize', SIZE_T),
|
||||
('ActiveProcessLimit', DWORD),
|
||||
('Affinity', ULONG_PTR),
|
||||
('PriorityClass', DWORD),
|
||||
('SchedulingClass', DWORD)
|
||||
]
|
||||
|
||||
# see http://msdn.microsoft.com/en-us/library/ms684156%28VS.85%29.aspx
|
||||
class JOBOBJECT_EXTENDED_LIMIT_INFORMATION(Structure):
|
||||
_fields_ = [('BasicLimitInformation', JOBOBJECT_BASIC_LIMIT_INFORMATION),
|
||||
('IoInfo', IO_COUNTERS),
|
||||
('ProcessMemoryLimit', SIZE_T),
|
||||
('JobMemoryLimit', SIZE_T),
|
||||
('PeakProcessMemoryUsed', SIZE_T),
|
||||
('PeakJobMemoryUsed', SIZE_T)]
|
||||
|
||||
# XXX Magical numbers like 8 should be documented
|
||||
JobObjectBasicAndIoAccountingInformation = 8
|
||||
|
||||
# ...like magical number 9 comes from
|
||||
# http://community.flexerasoftware.com/archive/index.php?t-181670.html
|
||||
# I wish I had a more canonical source
|
||||
JobObjectExtendedLimitInformation = 9
|
||||
|
||||
class JobObjectInfo(object):
|
||||
mapping = { 'JobObjectBasicAndIoAccountingInformation': 8,
|
||||
'JobObjectExtendedLimitInformation': 9
|
||||
}
|
||||
structures = { 8: JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION,
|
||||
9: JOBOBJECT_EXTENDED_LIMIT_INFORMATION
|
||||
}
|
||||
def __init__(self, _class):
|
||||
if isinstance(_class, str):
|
||||
assert _class in self.mapping, 'Class should be one of %s; you gave %s' % (self.mapping, _class)
|
||||
_class = self.mapping[_class]
|
||||
assert _class in self.structures, 'Class should be one of %s; you gave %s' % (self.structures, _class)
|
||||
self.code = _class
|
||||
self.info = self.structures[_class]()
|
||||
|
||||
|
||||
QueryInformationJobObjectProto = WINFUNCTYPE(
|
||||
BOOL, # Return type
|
||||
HANDLE, # hJob
|
||||
DWORD, # JobObjectInfoClass
|
||||
LPVOID, # lpJobObjectInfo
|
||||
DWORD, # cbJobObjectInfoLength
|
||||
LPDWORD # lpReturnLength
|
||||
)
|
||||
|
||||
QueryInformationJobObjectFlags = (
|
||||
(1, 'hJob'),
|
||||
(1, 'JobObjectInfoClass'),
|
||||
(1, 'lpJobObjectInfo'),
|
||||
(1, 'cbJobObjectInfoLength'),
|
||||
(1, 'lpReturnLength', None)
|
||||
)
|
||||
|
||||
_QueryInformationJobObject = QueryInformationJobObjectProto(
|
||||
('QueryInformationJobObject', windll.kernel32),
|
||||
QueryInformationJobObjectFlags
|
||||
)
|
||||
|
||||
class SubscriptableReadOnlyStruct(object):
|
||||
def __init__(self, struct):
|
||||
self._struct = struct
|
||||
|
||||
def _delegate(self, name):
|
||||
result = getattr(self._struct, name)
|
||||
if isinstance(result, Structure):
|
||||
return SubscriptableReadOnlyStruct(result)
|
||||
return result
|
||||
|
||||
def __getitem__(self, name):
|
||||
match = [fname for fname, ftype in self._struct._fields_
|
||||
if fname == name]
|
||||
if match:
|
||||
return self._delegate(name)
|
||||
raise KeyError(name)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return self._delegate(name)
|
||||
|
||||
def QueryInformationJobObject(hJob, JobObjectInfoClass):
|
||||
jobinfo = JobObjectInfo(JobObjectInfoClass)
|
||||
result = _QueryInformationJobObject(
|
||||
hJob=hJob,
|
||||
JobObjectInfoClass=jobinfo.code,
|
||||
lpJobObjectInfo=addressof(jobinfo.info),
|
||||
cbJobObjectInfoLength=sizeof(jobinfo.info)
|
||||
)
|
||||
if not result:
|
||||
raise WinError()
|
||||
return SubscriptableReadOnlyStruct(jobinfo.info)
|
||||
|
||||
def test_qijo():
|
||||
from .killableprocess import Popen
|
||||
|
||||
popen = Popen('c:\\windows\\notepad.exe')
|
||||
|
||||
try:
|
||||
result = QueryInformationJobObject(0, 8)
|
||||
raise AssertionError('throw should occur')
|
||||
except WindowsError as e:
|
||||
pass
|
||||
|
||||
try:
|
||||
result = QueryInformationJobObject(0, 1)
|
||||
raise AssertionError('throw should occur')
|
||||
except NotImplementedError as e:
|
||||
pass
|
||||
|
||||
result = QueryInformationJobObject(popen._job, 8)
|
||||
if result['BasicInfo']['ActiveProcesses'] != 1:
|
||||
raise AssertionError('expected ActiveProcesses to be 1')
|
||||
popen.kill()
|
||||
|
||||
result = QueryInformationJobObject(popen._job, 8)
|
||||
if result.BasicInfo.ActiveProcesses != 0:
|
||||
raise AssertionError('expected ActiveProcesses to be 0')
|
||||
370
Packages/SublimeREPL/repls/killableprocess/winprocess.py
Normal file
370
Packages/SublimeREPL/repls/killableprocess/winprocess.py
Normal file
@@ -0,0 +1,370 @@
|
||||
# A module to expose various thread/process/job related structures and
|
||||
# methods from kernel32
|
||||
#
|
||||
# The MIT License
|
||||
#
|
||||
# Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
|
||||
#
|
||||
# Additions and modifications written by Benjamin Smedberg
|
||||
# <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation
|
||||
# <http://www.mozilla.org/>
|
||||
#
|
||||
# More Modifications
|
||||
# Copyright (c) 2006-2007 by Mike Taylor <bear@code-bear.com>
|
||||
# Copyright (c) 2007-2008 by Mikeal Rogers <mikeal@mozilla.com>
|
||||
#
|
||||
# By obtaining, using, and/or copying this software and/or its
|
||||
# associated documentation, you agree that you have read, understood,
|
||||
# and will comply with the following terms and conditions:
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software and
|
||||
# its associated documentation for any purpose and without fee is
|
||||
# hereby granted, provided that the above copyright notice appears in
|
||||
# all copies, and that both that copyright notice and this permission
|
||||
# notice appear in supporting documentation, and that the name of the
|
||||
# author not be used in advertising or publicity pertaining to
|
||||
# distribution of the software without specific, written prior
|
||||
# permission.
|
||||
#
|
||||
# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
||||
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE
|
||||
from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LPCWSTR, LPWSTR, UINT, WORD
|
||||
from .qijo import QueryInformationJobObject
|
||||
|
||||
LPVOID = c_void_p
|
||||
LPBYTE = POINTER(BYTE)
|
||||
LPDWORD = POINTER(DWORD)
|
||||
LPBOOL = POINTER(BOOL)
|
||||
|
||||
def ErrCheckBool(result, func, args):
|
||||
"""errcheck function for Windows functions that return a BOOL True
|
||||
on success"""
|
||||
if not result:
|
||||
raise WinError()
|
||||
return args
|
||||
|
||||
|
||||
# AutoHANDLE
|
||||
|
||||
class AutoHANDLE(HANDLE):
|
||||
"""Subclass of HANDLE which will call CloseHandle() on deletion."""
|
||||
|
||||
CloseHandleProto = WINFUNCTYPE(BOOL, HANDLE)
|
||||
CloseHandle = CloseHandleProto(("CloseHandle", windll.kernel32))
|
||||
CloseHandle.errcheck = ErrCheckBool
|
||||
|
||||
def Close(self):
|
||||
if self.value and self.value != HANDLE(-1).value:
|
||||
self.CloseHandle(self)
|
||||
self.value = 0
|
||||
|
||||
def __del__(self):
|
||||
self.Close()
|
||||
|
||||
def __int__(self):
|
||||
return self.value
|
||||
|
||||
def ErrCheckHandle(result, func, args):
|
||||
"""errcheck function for Windows functions that return a HANDLE."""
|
||||
if not result:
|
||||
raise WinError()
|
||||
return AutoHANDLE(result)
|
||||
|
||||
# PROCESS_INFORMATION structure
|
||||
|
||||
class PROCESS_INFORMATION(Structure):
|
||||
_fields_ = [("hProcess", HANDLE),
|
||||
("hThread", HANDLE),
|
||||
("dwProcessID", DWORD),
|
||||
("dwThreadID", DWORD)]
|
||||
|
||||
def __init__(self):
|
||||
Structure.__init__(self)
|
||||
|
||||
self.cb = sizeof(self)
|
||||
|
||||
LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)
|
||||
|
||||
# STARTUPINFO structure
|
||||
|
||||
class STARTUPINFO(Structure):
|
||||
_fields_ = [("cb", DWORD),
|
||||
("lpReserved", LPWSTR),
|
||||
("lpDesktop", LPWSTR),
|
||||
("lpTitle", LPWSTR),
|
||||
("dwX", DWORD),
|
||||
("dwY", DWORD),
|
||||
("dwXSize", DWORD),
|
||||
("dwYSize", DWORD),
|
||||
("dwXCountChars", DWORD),
|
||||
("dwYCountChars", DWORD),
|
||||
("dwFillAttribute", DWORD),
|
||||
("dwFlags", DWORD),
|
||||
("wShowWindow", WORD),
|
||||
("cbReserved2", WORD),
|
||||
("lpReserved2", LPBYTE),
|
||||
("hStdInput", HANDLE),
|
||||
("hStdOutput", HANDLE),
|
||||
("hStdError", HANDLE)
|
||||
]
|
||||
LPSTARTUPINFO = POINTER(STARTUPINFO)
|
||||
|
||||
SW_HIDE = 0
|
||||
|
||||
STARTF_USESHOWWINDOW = 0x01
|
||||
STARTF_USESIZE = 0x02
|
||||
STARTF_USEPOSITION = 0x04
|
||||
STARTF_USECOUNTCHARS = 0x08
|
||||
STARTF_USEFILLATTRIBUTE = 0x10
|
||||
STARTF_RUNFULLSCREEN = 0x20
|
||||
STARTF_FORCEONFEEDBACK = 0x40
|
||||
STARTF_FORCEOFFFEEDBACK = 0x80
|
||||
STARTF_USESTDHANDLES = 0x100
|
||||
|
||||
# EnvironmentBlock
|
||||
|
||||
class EnvironmentBlock:
|
||||
"""An object which can be passed as the lpEnv parameter of CreateProcess.
|
||||
It is initialized with a dictionary."""
|
||||
|
||||
def __init__(self, dict):
|
||||
if not dict:
|
||||
self._as_parameter_ = None
|
||||
else:
|
||||
values = ["%s=%s" % (key, value)
|
||||
for (key, value) in dict.items()]
|
||||
values.append("")
|
||||
self._as_parameter_ = LPCWSTR("\0".join(values))
|
||||
|
||||
# CreateProcess()
|
||||
|
||||
CreateProcessProto = WINFUNCTYPE(BOOL, # Return type
|
||||
LPCWSTR, # lpApplicationName
|
||||
LPWSTR, # lpCommandLine
|
||||
LPVOID, # lpProcessAttributes
|
||||
LPVOID, # lpThreadAttributes
|
||||
BOOL, # bInheritHandles
|
||||
DWORD, # dwCreationFlags
|
||||
LPVOID, # lpEnvironment
|
||||
LPCWSTR, # lpCurrentDirectory
|
||||
LPSTARTUPINFO, # lpStartupInfo
|
||||
LPPROCESS_INFORMATION # lpProcessInformation
|
||||
)
|
||||
|
||||
CreateProcessFlags = ((1, "lpApplicationName", None),
|
||||
(1, "lpCommandLine"),
|
||||
(1, "lpProcessAttributes", None),
|
||||
(1, "lpThreadAttributes", None),
|
||||
(1, "bInheritHandles", True),
|
||||
(1, "dwCreationFlags", 0),
|
||||
(1, "lpEnvironment", None),
|
||||
(1, "lpCurrentDirectory", None),
|
||||
(1, "lpStartupInfo"),
|
||||
(2, "lpProcessInformation"))
|
||||
|
||||
def ErrCheckCreateProcess(result, func, args):
|
||||
ErrCheckBool(result, func, args)
|
||||
# return a tuple (hProcess, hThread, dwProcessID, dwThreadID)
|
||||
pi = args[9]
|
||||
return AutoHANDLE(pi.hProcess), AutoHANDLE(pi.hThread), pi.dwProcessID, pi.dwThreadID
|
||||
|
||||
CreateProcess = CreateProcessProto(("CreateProcessW", windll.kernel32),
|
||||
CreateProcessFlags)
|
||||
CreateProcess.errcheck = ErrCheckCreateProcess
|
||||
|
||||
# flags for CreateProcess
|
||||
CREATE_BREAKAWAY_FROM_JOB = 0x01000000
|
||||
CREATE_DEFAULT_ERROR_MODE = 0x04000000
|
||||
CREATE_NEW_CONSOLE = 0x00000010
|
||||
CREATE_NEW_PROCESS_GROUP = 0x00000200
|
||||
CREATE_NO_WINDOW = 0x08000000
|
||||
CREATE_SUSPENDED = 0x00000004
|
||||
CREATE_UNICODE_ENVIRONMENT = 0x00000400
|
||||
|
||||
# flags for job limit information
|
||||
# see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx
|
||||
JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800
|
||||
JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000
|
||||
|
||||
# XXX these flags should be documented
|
||||
DEBUG_ONLY_THIS_PROCESS = 0x00000002
|
||||
DEBUG_PROCESS = 0x00000001
|
||||
DETACHED_PROCESS = 0x00000008
|
||||
|
||||
# CreateJobObject()
|
||||
|
||||
CreateJobObjectProto = WINFUNCTYPE(HANDLE, # Return type
|
||||
LPVOID, # lpJobAttributes
|
||||
LPCWSTR # lpName
|
||||
)
|
||||
|
||||
CreateJobObjectFlags = ((1, "lpJobAttributes", None),
|
||||
(1, "lpName", None))
|
||||
|
||||
CreateJobObject = CreateJobObjectProto(("CreateJobObjectW", windll.kernel32),
|
||||
CreateJobObjectFlags)
|
||||
CreateJobObject.errcheck = ErrCheckHandle
|
||||
|
||||
# AssignProcessToJobObject()
|
||||
|
||||
AssignProcessToJobObjectProto = WINFUNCTYPE(BOOL, # Return type
|
||||
HANDLE, # hJob
|
||||
HANDLE # hProcess
|
||||
)
|
||||
AssignProcessToJobObjectFlags = ((1, "hJob"),
|
||||
(1, "hProcess"))
|
||||
AssignProcessToJobObject = AssignProcessToJobObjectProto(
|
||||
("AssignProcessToJobObject", windll.kernel32),
|
||||
AssignProcessToJobObjectFlags)
|
||||
AssignProcessToJobObject.errcheck = ErrCheckBool
|
||||
|
||||
# GetCurrentProcess()
|
||||
# because os.getPid() is way too easy
|
||||
GetCurrentProcessProto = WINFUNCTYPE(HANDLE # Return type
|
||||
)
|
||||
GetCurrentProcessFlags = ()
|
||||
GetCurrentProcess = GetCurrentProcessProto(
|
||||
("GetCurrentProcess", windll.kernel32),
|
||||
GetCurrentProcessFlags)
|
||||
GetCurrentProcess.errcheck = ErrCheckHandle
|
||||
|
||||
# IsProcessInJob()
|
||||
try:
|
||||
IsProcessInJobProto = WINFUNCTYPE(BOOL, # Return type
|
||||
HANDLE, # Process Handle
|
||||
HANDLE, # Job Handle
|
||||
LPBOOL # Result
|
||||
)
|
||||
IsProcessInJobFlags = ((1, "ProcessHandle"),
|
||||
(1, "JobHandle", HANDLE(0)),
|
||||
(2, "Result"))
|
||||
IsProcessInJob = IsProcessInJobProto(
|
||||
("IsProcessInJob", windll.kernel32),
|
||||
IsProcessInJobFlags)
|
||||
IsProcessInJob.errcheck = ErrCheckBool
|
||||
except AttributeError:
|
||||
# windows 2k doesn't have this API
|
||||
def IsProcessInJob(process):
|
||||
return False
|
||||
|
||||
|
||||
# ResumeThread()
|
||||
|
||||
def ErrCheckResumeThread(result, func, args):
|
||||
if result == -1:
|
||||
raise WinError()
|
||||
|
||||
return args
|
||||
|
||||
ResumeThreadProto = WINFUNCTYPE(DWORD, # Return type
|
||||
HANDLE # hThread
|
||||
)
|
||||
ResumeThreadFlags = ((1, "hThread"),)
|
||||
ResumeThread = ResumeThreadProto(("ResumeThread", windll.kernel32),
|
||||
ResumeThreadFlags)
|
||||
ResumeThread.errcheck = ErrCheckResumeThread
|
||||
|
||||
# TerminateProcess()
|
||||
|
||||
TerminateProcessProto = WINFUNCTYPE(BOOL, # Return type
|
||||
HANDLE, # hProcess
|
||||
UINT # uExitCode
|
||||
)
|
||||
TerminateProcessFlags = ((1, "hProcess"),
|
||||
(1, "uExitCode", 127))
|
||||
TerminateProcess = TerminateProcessProto(
|
||||
("TerminateProcess", windll.kernel32),
|
||||
TerminateProcessFlags)
|
||||
TerminateProcess.errcheck = ErrCheckBool
|
||||
|
||||
# TerminateJobObject()
|
||||
|
||||
TerminateJobObjectProto = WINFUNCTYPE(BOOL, # Return type
|
||||
HANDLE, # hJob
|
||||
UINT # uExitCode
|
||||
)
|
||||
TerminateJobObjectFlags = ((1, "hJob"),
|
||||
(1, "uExitCode", 127))
|
||||
TerminateJobObject = TerminateJobObjectProto(
|
||||
("TerminateJobObject", windll.kernel32),
|
||||
TerminateJobObjectFlags)
|
||||
TerminateJobObject.errcheck = ErrCheckBool
|
||||
|
||||
# WaitForSingleObject()
|
||||
|
||||
WaitForSingleObjectProto = WINFUNCTYPE(DWORD, # Return type
|
||||
HANDLE, # hHandle
|
||||
DWORD, # dwMilliseconds
|
||||
)
|
||||
WaitForSingleObjectFlags = ((1, "hHandle"),
|
||||
(1, "dwMilliseconds", -1))
|
||||
WaitForSingleObject = WaitForSingleObjectProto(
|
||||
("WaitForSingleObject", windll.kernel32),
|
||||
WaitForSingleObjectFlags)
|
||||
|
||||
INFINITE = -1
|
||||
WAIT_TIMEOUT = 0x0102
|
||||
WAIT_OBJECT_0 = 0x0
|
||||
WAIT_ABANDONED = 0x0080
|
||||
|
||||
# GetExitCodeProcess()
|
||||
|
||||
GetExitCodeProcessProto = WINFUNCTYPE(BOOL, # Return type
|
||||
HANDLE, # hProcess
|
||||
LPDWORD, # lpExitCode
|
||||
)
|
||||
GetExitCodeProcessFlags = ((1, "hProcess"),
|
||||
(2, "lpExitCode"))
|
||||
GetExitCodeProcess = GetExitCodeProcessProto(
|
||||
("GetExitCodeProcess", windll.kernel32),
|
||||
GetExitCodeProcessFlags)
|
||||
GetExitCodeProcess.errcheck = ErrCheckBool
|
||||
|
||||
def CanCreateJobObject():
|
||||
currentProc = GetCurrentProcess()
|
||||
if IsProcessInJob(currentProc):
|
||||
jobinfo = QueryInformationJobObject(HANDLE(0), 'JobObjectExtendedLimitInformation')
|
||||
limitflags = jobinfo['BasicLimitInformation']['LimitFlags']
|
||||
return bool(limitflags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) or bool(limitflags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)
|
||||
else:
|
||||
return True
|
||||
|
||||
### testing functions
|
||||
|
||||
def parent():
|
||||
print('Starting parent')
|
||||
currentProc = GetCurrentProcess()
|
||||
if IsProcessInJob(currentProc):
|
||||
print("You should not be in a job object to test")
|
||||
sys.exit(1)
|
||||
assert CanCreateJobObject()
|
||||
print('File: %s' % __file__)
|
||||
command = [sys.executable, __file__, '-child']
|
||||
print('Running command: %s' % command)
|
||||
process = Popen(command)
|
||||
process.kill()
|
||||
code = process.returncode
|
||||
print('Child code: %s' % code)
|
||||
assert code == 127
|
||||
|
||||
def child():
|
||||
print('Starting child')
|
||||
currentProc = GetCurrentProcess()
|
||||
injob = IsProcessInJob(currentProc)
|
||||
print("Is in a job?: %s" % injob)
|
||||
can_create = CanCreateJobObject()
|
||||
print('Can create job?: %s' % can_create)
|
||||
process = Popen('c:\\windows\\notepad.exe')
|
||||
assert process._job
|
||||
jobinfo = QueryInformationJobObject(process._job, 'JobObjectExtendedLimitInformation')
|
||||
print('Job info: %s' % jobinfo)
|
||||
limitflags = jobinfo['BasicLimitInformation']['LimitFlags']
|
||||
print('LimitFlags: %s' % limitflags)
|
||||
process.kill()
|
||||
74
Packages/SublimeREPL/repls/powershell_repl.py
Normal file
74
Packages/SublimeREPL/repls/powershell_repl.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2011, Wojciech Bederski (wuub.net)
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for details.
|
||||
import os
|
||||
import re
|
||||
from . import subprocess_repl
|
||||
|
||||
# PowerShell in interactive mode shows no prompt, so we must hold it by hand.
|
||||
# Every command prepended with other command, which will output only one character ('.')
|
||||
# When user command leads to no output (for example, 'cd ..'), we get only this character,
|
||||
# and then we send command to show prompt explicitly.
|
||||
# No output at all means, that PowerShell needs more input (multiline mode).
|
||||
# In this case we proceeds sending user input without modifications.
|
||||
|
||||
class PowershellRepl(subprocess_repl.SubprocessRepl):
|
||||
TYPE = "powershell"
|
||||
PREPENDER = b"."
|
||||
|
||||
def __init__(self, encoding, **kwds):
|
||||
if not encoding:
|
||||
# Detect encoding
|
||||
chcp = os.popen('chcp')
|
||||
chcp_encoding = re.match(r'[^\d]+(\d+)', chcp.read())
|
||||
if not chcp_encoding:
|
||||
raise LookupError("Can't detect encoding from chcp")
|
||||
encoding = "cp" + chcp_encoding.groups()[0]
|
||||
print(encoding)
|
||||
|
||||
super(PowershellRepl, self).__init__(encoding, **kwds)
|
||||
|
||||
# Using this to detect whether PowerShell returns some output or it needs more input
|
||||
# PowerShell in interactive mode doesn't show prompt, so we must hold it by hand
|
||||
# It's a hack and, for example, we can send 'Write-Host "" -NoNewLine' with no output, but in outhr cases it may work well
|
||||
self.got_output = True
|
||||
self.multiline = False
|
||||
|
||||
self.prompt()
|
||||
|
||||
def read_bytes(self):
|
||||
# this is windows specific problem, that you cannot tell if there
|
||||
# are more bytes ready, so we read only 1 at a times
|
||||
|
||||
result = super(PowershellRepl, self).read_bytes()
|
||||
|
||||
# Consumes output (it must be equal to PREPENDER)
|
||||
if result and not self.got_output:
|
||||
self.got_output = True
|
||||
self.multiline = False
|
||||
self.prompt()
|
||||
# Don't return PREPENDER, read another input
|
||||
return self.read_bytes()
|
||||
|
||||
return result
|
||||
|
||||
def write_bytes(self, bytes):
|
||||
# Drop flag on new input
|
||||
self.got_output = False
|
||||
if not self.multiline:
|
||||
# Turn multiline mode on, it will be turned off, when PowerShell returns some output
|
||||
self.multiline = True
|
||||
self.prepend()
|
||||
self.do_write(bytes)
|
||||
|
||||
def do_write(self, bytes):
|
||||
super(PowershellRepl, self).write_bytes(bytes)
|
||||
|
||||
def prompt(self):
|
||||
""" Sends command to get prompt """
|
||||
self.do_write(b'Write-Host ("PS " + (gl).Path + "> ") -NoNewline\n')
|
||||
|
||||
def prepend(self):
|
||||
""" Command to prepend every output with special mark to detect multiline mode """
|
||||
self.do_write(b'Write-Host "' + PowershellRepl.PREPENDER + b'" -NoNewLine; ')
|
||||
100
Packages/SublimeREPL/repls/repl.py
Normal file
100
Packages/SublimeREPL/repls/repl.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2011, Wojciech Bederski (wuub.net)
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for details.
|
||||
|
||||
from uuid import uuid4
|
||||
from codecs import getincrementaldecoder, getencoder
|
||||
|
||||
|
||||
class NoReplError(LookupError):
|
||||
"""Looking for Repl subclass failed"""
|
||||
pass
|
||||
|
||||
class Repl(object):
|
||||
"""Class that represents a process that is being executed.
|
||||
For example this can be python, bash or a telnet session"""
|
||||
|
||||
TYPE = "<base>"
|
||||
|
||||
@classmethod
|
||||
def subclass(cls, type):
|
||||
"""Returns subclass of Repl of given type eq. SubprocessRepl"""
|
||||
todo = [cls]
|
||||
seen = set()
|
||||
while True:
|
||||
if not todo:
|
||||
raise NoReplError
|
||||
cur = todo.pop()
|
||||
if cur in seen:
|
||||
continue
|
||||
if cur.TYPE == type:
|
||||
return cur
|
||||
todo.extend(cur.__subclasses__())
|
||||
|
||||
def __init__(self, encoding, external_id=None, cmd_postfix="\n", suppress_echo=False, additional_scopes=None):
|
||||
self.id = uuid4().hex
|
||||
self._encoding = encoding
|
||||
self.decoder = getincrementaldecoder(self._encoding)()
|
||||
self.encoder = getencoder(encoding)
|
||||
self.external_id = external_id
|
||||
self.cmd_postfix = cmd_postfix
|
||||
self.suppress_echo = suppress_echo
|
||||
self.additional_scopes = additional_scopes or []
|
||||
|
||||
def autocomplete_available(self):
|
||||
return False
|
||||
|
||||
def autocomplete_completions(self, whole_line, pos_in_line, prefix, whole_prefix, locations):
|
||||
raise NotImplementedError
|
||||
|
||||
def allow_restarts(self):
|
||||
"""Override if for some reason restart logic should not be
|
||||
used for this REPL"""
|
||||
return True
|
||||
|
||||
def close(self):
|
||||
if self.is_alive():
|
||||
self.kill()
|
||||
|
||||
def name(self):
|
||||
"""Returns name of this repl that should be used as a filename"""
|
||||
return NotImplementedError
|
||||
|
||||
def is_alive(self):
|
||||
""" Returns true if the undelying process is stil working"""
|
||||
raise NotImplementedError
|
||||
|
||||
def write_bytes(self, bytes):
|
||||
raise NotImplementedError
|
||||
|
||||
def read_bytes(self):
|
||||
"""Reads at lest one byte of Repl output. Returns None if output died.
|
||||
Can block!!!"""
|
||||
raise NotImplementedError
|
||||
|
||||
def kill(self):
|
||||
"""Kills the underlying repl"""
|
||||
raise NotImplementedError
|
||||
|
||||
def write(self, command):
|
||||
"""Encodes and evaluates a given command"""
|
||||
(bytes, how_many) = self.encoder(command)
|
||||
return self.write_bytes(bytes)
|
||||
|
||||
def reset_decoder(self):
|
||||
self.decoder = getincrementaldecoder(self._encoding)()
|
||||
|
||||
def read(self):
|
||||
"""Reads at least one decoded char of output"""
|
||||
while True:
|
||||
bs = self.read_bytes()
|
||||
if not bs:
|
||||
return None
|
||||
try:
|
||||
output = self.decoder.decode(bs)
|
||||
except Exception as e:
|
||||
output = "■"
|
||||
self.reset_decoder()
|
||||
if output:
|
||||
return output
|
||||
93
Packages/SublimeREPL/repls/sublimehaskell_repl.py
Normal file
93
Packages/SublimeREPL/repls/sublimehaskell_repl.py
Normal file
@@ -0,0 +1,93 @@
|
||||
import re
|
||||
import os
|
||||
import sublime
|
||||
|
||||
from .subprocess_repl import SubprocessRepl
|
||||
|
||||
def get_settings():
|
||||
return sublime.load_settings("SublimeHaskell.sublime-settings")
|
||||
|
||||
def get_setting(key, default=None):
|
||||
"This should be used only from main thread"
|
||||
# Get setting
|
||||
return get_settings().get(key, default)
|
||||
|
||||
def ghci_package_db():
|
||||
dev = get_setting('use_cabal_dev')
|
||||
box = get_setting('cabal_dev_sandbox')
|
||||
if dev and box:
|
||||
package_conf = (filter(lambda x: re.match('packages-(.*)\.conf', x), os.listdir(box)) + [None])[0]
|
||||
if package_conf:
|
||||
return os.path.join(box, package_conf)
|
||||
return None
|
||||
|
||||
def ghci_append_package_db(cmd):
|
||||
package_conf = ghci_package_db()
|
||||
if package_conf:
|
||||
cmd.extend(['-package-db', package_conf])
|
||||
return cmd
|
||||
|
||||
def ghci_get_min_whitespace_prefix(lines):
|
||||
line_spaces = [len(line) - len(line.lstrip()) for line in lines]
|
||||
if not line_spaces:
|
||||
return 0
|
||||
min_spaces = min(line_spaces)
|
||||
return min_spaces
|
||||
|
||||
def ghci_inject_let(lines):
|
||||
fixed_lines = [line for line in lines if not line.isspace()]
|
||||
|
||||
letprefix = "let "
|
||||
spaceprefix = " "
|
||||
|
||||
# matches eg. "func x y z ="
|
||||
# must start lowercase at start of line
|
||||
# remaining chars must be upper or lowercase letters, numbers, _ or '
|
||||
if fixed_lines and (not fixed_lines[0].startswith("let ")) and re.search("\A([a-z](\w|['_])*[ ]).*[=][ ]", lines[0]):
|
||||
fixed_lines[0] = letprefix + fixed_lines[0]
|
||||
fixed_lines[1:] = [spaceprefix + line for line in fixed_lines[1:]]
|
||||
|
||||
return fixed_lines
|
||||
|
||||
def ghci_remove_whitespace(lines):
|
||||
# remove lines that are completely whitespace
|
||||
lines = [line for line in lines if not line.isspace()]
|
||||
|
||||
# remove extra whitespace for more flexible block execution
|
||||
min_spaces = ghci_get_min_whitespace_prefix(lines)
|
||||
|
||||
# remove the minimum number of spaces over all lines from each
|
||||
fixed_lines = [line[min_spaces:] for line in lines]
|
||||
return fixed_lines
|
||||
|
||||
def ghci_wrap_multiline_syntax(lines):
|
||||
# wrap in mutli-line syntax if more than one line
|
||||
if len(lines) <= 1:
|
||||
return lines
|
||||
fixed_lines = [":{" + os.linesep] + lines + [os.linesep + ":}" + os.linesep]
|
||||
return fixed_lines
|
||||
|
||||
class SublimeHaskellRepl(SubprocessRepl):
|
||||
TYPE = "sublime_haskell"
|
||||
|
||||
def __init__(self, encoding, cmd=None, **kwds):
|
||||
super(SublimeHaskellRepl, self).__init__(encoding, cmd=ghci_append_package_db(cmd), **kwds)
|
||||
|
||||
def write(self, command):
|
||||
setting_multiline = get_setting('format_multiline', True)
|
||||
setting_trimwhitespace = get_setting('format_trim_whitespace', True)
|
||||
setting_injectlet = get_setting('format_inject_let', True)
|
||||
|
||||
new_cmd = ""
|
||||
if command.isspace() or (not setting_multiline and not setting_trimwhitespace):
|
||||
new_cmd = command
|
||||
else:
|
||||
lines = command.splitlines(True)
|
||||
if setting_trimwhitespace:
|
||||
lines = ghci_remove_whitespace(lines)
|
||||
if setting_injectlet:
|
||||
lines = ghci_inject_let(lines)
|
||||
if setting_multiline:
|
||||
lines = ghci_wrap_multiline_syntax(lines)
|
||||
new_cmd = "".join(lines)
|
||||
return super(SublimeHaskellRepl, self).write(new_cmd)
|
||||
98
Packages/SublimeREPL/repls/sublimepython_repl.py
Normal file
98
Packages/SublimeREPL/repls/sublimepython_repl.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# encoding: utf-8
|
||||
import code
|
||||
import contextlib
|
||||
from .repl import Repl
|
||||
try:
|
||||
from queue import Queue
|
||||
except ImportError:
|
||||
from Queue import Queue
|
||||
import sys
|
||||
import threading
|
||||
import sublime
|
||||
|
||||
|
||||
class QueueOut(object):
|
||||
def __init__(self, queue):
|
||||
self.queue = queue
|
||||
|
||||
def write(self, data):
|
||||
self.queue.put(data)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def redirect_stdio(queue):
|
||||
orig = (sys.stdout, sys.stderr)
|
||||
sys.stdout = sys.stderr = QueueOut(queue)
|
||||
yield
|
||||
(sys.stdout, sys.stderr) = orig
|
||||
|
||||
|
||||
class SublimeLocals(dict):
|
||||
def __init__(self, *args, **kwds):
|
||||
import pydoc
|
||||
super(SublimeLocals, self).__init__(*args, **kwds)
|
||||
self['sublime'] = sublime
|
||||
self['__name__'] = "__main__"
|
||||
self['view'] = None
|
||||
self['window'] = None
|
||||
self['help'] = pydoc.help
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == 'window':
|
||||
return sublime.active_window()
|
||||
if key == 'view':
|
||||
return sublime.active_window().active_view()
|
||||
return super(SublimeLocals, self).__getitem__(key)
|
||||
|
||||
|
||||
class InterceptingConsole(code.InteractiveConsole):
|
||||
PS1 = ">>> "
|
||||
PS2 = "... "
|
||||
|
||||
def __init__(self, encoding):
|
||||
code.InteractiveConsole.__init__(self, locals=SublimeLocals())
|
||||
self.input = Queue()
|
||||
self.output = Queue()
|
||||
self.output.put(self.PS1)
|
||||
self._encoding = encoding
|
||||
|
||||
def write(self, data):
|
||||
self.output.put(data)
|
||||
|
||||
def push(self, line):
|
||||
with redirect_stdio(self.output):
|
||||
more = code.InteractiveConsole.push(self, line.decode(self._encoding))
|
||||
self.output.put(self.PS2 if more else self.PS1)
|
||||
return more
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
line = self.input.get()
|
||||
if line is None:
|
||||
break
|
||||
self.push(line)
|
||||
|
||||
|
||||
class SublimePythonRepl(Repl):
|
||||
TYPE = "sublime_python"
|
||||
|
||||
def __init__(self, encoding):
|
||||
super(SublimePythonRepl, self).__init__(encoding, "python", "\n", False)
|
||||
self._console = InterceptingConsole(encoding)
|
||||
self._thread = threading.Thread(target=self._console.run)
|
||||
self._thread.start()
|
||||
|
||||
def name(self):
|
||||
return "sublime"
|
||||
|
||||
def is_alive(self):
|
||||
return True
|
||||
|
||||
def write_bytes(self, bytes):
|
||||
self._console.input.put(bytes)
|
||||
|
||||
def read_bytes(self):
|
||||
return self._console.output.get().encode(self._encoding)
|
||||
|
||||
def kill(self):
|
||||
self._console.input.put(None)
|
||||
231
Packages/SublimeREPL/repls/subprocess_repl.py
Normal file
231
Packages/SublimeREPL/repls/subprocess_repl.py
Normal file
@@ -0,0 +1,231 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2011, Wojciech Bederski (wuub.net)
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for details.
|
||||
from __future__ import absolute_import, unicode_literals, print_function, division
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
from .repl import Repl
|
||||
import signal
|
||||
from sublime import load_settings
|
||||
from .autocomplete_server import AutocompleteServer
|
||||
from .killableprocess import Popen
|
||||
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if os.name == 'posix':
|
||||
POSIX = True
|
||||
import fcntl
|
||||
import select
|
||||
else:
|
||||
POSIX = False
|
||||
|
||||
|
||||
class Unsupported(Exception):
|
||||
def __init__(self, msgs):
|
||||
super(Unsupported, self).__init__()
|
||||
self.msgs = msgs
|
||||
|
||||
def __repr__(self):
|
||||
return "\n".join(self.msgs)
|
||||
|
||||
|
||||
def win_find_executable(executable, env):
|
||||
"""Explicetely looks for executable in env["PATH"]"""
|
||||
if os.path.dirname(executable):
|
||||
return executable # executable is already absolute filepath
|
||||
path = env.get("PATH", "")
|
||||
pathext = env.get("PATHEXT") or ".EXE"
|
||||
dirs = path.split(os.path.pathsep)
|
||||
(base, ext) = os.path.splitext(executable)
|
||||
if ext:
|
||||
extensions = [ext]
|
||||
else:
|
||||
extensions = pathext.split(os.path.pathsep)
|
||||
for directory in dirs:
|
||||
for extension in extensions:
|
||||
filepath = os.path.join(directory, base + extension)
|
||||
if os.path.exists(filepath):
|
||||
return filepath
|
||||
return None
|
||||
|
||||
|
||||
class SubprocessRepl(Repl):
|
||||
TYPE = "subprocess"
|
||||
|
||||
def __init__(self, encoding, cmd=None, env=None, cwd=None, extend_env=None, soft_quit="", autocomplete_server=False, **kwds):
|
||||
super(SubprocessRepl, self).__init__(encoding, **kwds)
|
||||
settings = load_settings('SublimeREPL.sublime-settings')
|
||||
|
||||
if cmd[0] == "[unsupported]":
|
||||
raise Unsupported(cmd[1:])
|
||||
|
||||
self._autocomplete_server = None
|
||||
if autocomplete_server:
|
||||
self._autocomplete_server = AutocompleteServer(self, settings.get("autocomplete_server_ip"))
|
||||
self._autocomplete_server.start()
|
||||
|
||||
env = self.env(env, extend_env, settings)
|
||||
env[b"SUBLIMEREPL_AC_PORT"] = str(self.autocomplete_server_port()).encode("utf-8")
|
||||
env[b"SUBLIMEREPL_AC_IP"] = settings.get("autocomplete_server_ip").encode("utf-8")
|
||||
|
||||
if PY3:
|
||||
strings_env = {}
|
||||
for k, v in env.items():
|
||||
strings_env[k.decode("utf-8")] = v.decode("utf-8")
|
||||
env = strings_env
|
||||
|
||||
self._cmd = self.cmd(cmd, env)
|
||||
self._soft_quit = soft_quit
|
||||
self._killed = False
|
||||
self.popen = Popen(
|
||||
self._cmd,
|
||||
startupinfo=self.startupinfo(settings),
|
||||
creationflags=self.creationflags(settings),
|
||||
bufsize=1,
|
||||
cwd=self.cwd(cwd, settings),
|
||||
env=env,
|
||||
stderr=subprocess.STDOUT,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
|
||||
if POSIX:
|
||||
flags = fcntl.fcntl(self.popen.stdout, fcntl.F_GETFL)
|
||||
fcntl.fcntl(self.popen.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
|
||||
|
||||
def autocomplete_server_port(self):
|
||||
if not self._autocomplete_server:
|
||||
return None
|
||||
return self._autocomplete_server.port()
|
||||
|
||||
def autocomplete_available(self):
|
||||
if not self._autocomplete_server:
|
||||
return False
|
||||
return self._autocomplete_server.connected()
|
||||
|
||||
def autocomplete_completions(self, whole_line, pos_in_line, prefix, whole_prefix, locations):
|
||||
return self._autocomplete_server.complete(
|
||||
whole_line=whole_line,
|
||||
pos_in_line=pos_in_line,
|
||||
prefix=prefix,
|
||||
whole_prefix=whole_prefix,
|
||||
locations=locations,
|
||||
)
|
||||
|
||||
def cmd(self, cmd, env):
|
||||
"""On Linux and OSX just returns cmd, on windows it has to find
|
||||
executable in env because of this: http://bugs.python.org/issue8557"""
|
||||
if os.name != "nt":
|
||||
return cmd
|
||||
if isinstance(cmd, str):
|
||||
_cmd = [cmd]
|
||||
else:
|
||||
_cmd = cmd
|
||||
executable = win_find_executable(_cmd[0], env)
|
||||
if executable:
|
||||
_cmd[0] = executable
|
||||
return _cmd
|
||||
|
||||
def cwd(self, cwd, settings):
|
||||
if cwd and os.path.exists(cwd):
|
||||
return cwd
|
||||
return None
|
||||
|
||||
def env(self, env, extend_env, settings):
|
||||
updated_env = env if env else os.environ.copy()
|
||||
default_extend_env = settings.get("default_extend_env")
|
||||
if default_extend_env:
|
||||
updated_env.update(self.interpolate_extend_env(updated_env, default_extend_env))
|
||||
if extend_env:
|
||||
updated_env.update(self.interpolate_extend_env(updated_env, extend_env))
|
||||
bytes_env = {}
|
||||
for k, v in list(updated_env.items()):
|
||||
try:
|
||||
enc_k = self.encoder(str(k))[0]
|
||||
enc_v = self.encoder(str(v))[0]
|
||||
except UnicodeDecodeError:
|
||||
continue #f*** it, we'll do it live
|
||||
else:
|
||||
bytes_env[enc_k] = enc_v
|
||||
return bytes_env
|
||||
|
||||
def interpolate_extend_env(self, env, extend_env):
|
||||
"""Interpolates (subst) values in extend_env.
|
||||
Mostly for path manipulation"""
|
||||
new_env = {}
|
||||
for key, val in list(extend_env.items()):
|
||||
new_env[key] = str(val).format(**env)
|
||||
return new_env
|
||||
|
||||
def startupinfo(self, settings):
|
||||
startupinfo = None
|
||||
if os.name == 'nt':
|
||||
from .killableprocess import STARTUPINFO, STARTF_USESHOWWINDOW
|
||||
startupinfo = STARTUPINFO()
|
||||
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
|
||||
startupinfo.wShowWindow |= 1 # SW_SHOWNORMAL
|
||||
return startupinfo
|
||||
|
||||
def creationflags(self, settings):
|
||||
creationflags = 0
|
||||
if os.name == "nt":
|
||||
creationflags = 0x8000000 # CREATE_NO_WINDOW
|
||||
return creationflags
|
||||
|
||||
def name(self):
|
||||
if self.external_id:
|
||||
return self.external_id
|
||||
if isinstance(self._cmd, str):
|
||||
return self._cmd
|
||||
return " ".join([str(x) for x in self._cmd])
|
||||
|
||||
def is_alive(self):
|
||||
return self.popen.poll() is None
|
||||
|
||||
def read_bytes(self):
|
||||
out = self.popen.stdout
|
||||
if POSIX:
|
||||
while True:
|
||||
i, _, _ = select.select([out], [], [])
|
||||
if i:
|
||||
return out.read(4096)
|
||||
else:
|
||||
# this is windows specific problem, that you cannot tell if there
|
||||
# are more bytes ready, so we read only 1 at a times
|
||||
|
||||
while True:
|
||||
byte = self.popen.stdout.read(1)
|
||||
if byte == b'\r':
|
||||
# f'in HACK, for \r\n -> \n translation on windows
|
||||
# I tried universal_endlines but it was pain and misery! :'(
|
||||
continue
|
||||
return byte
|
||||
|
||||
|
||||
|
||||
def write_bytes(self, bytes):
|
||||
si = self.popen.stdin
|
||||
si.write(bytes)
|
||||
si.flush()
|
||||
|
||||
def kill(self):
|
||||
self._killed = True
|
||||
self.write(self._soft_quit)
|
||||
self.popen.kill()
|
||||
|
||||
def available_signals(self):
|
||||
signals = {}
|
||||
for k, v in list(signal.__dict__.items()):
|
||||
if not k.startswith("SIG"):
|
||||
continue
|
||||
signals[k] = v
|
||||
return signals
|
||||
|
||||
def send_signal(self, sig):
|
||||
if sig == signal.SIGTERM:
|
||||
self._killed = True
|
||||
if self.is_alive():
|
||||
self.popen.send_signal(sig)
|
||||
|
||||
43
Packages/SublimeREPL/repls/telnet_repl.py
Normal file
43
Packages/SublimeREPL/repls/telnet_repl.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2011, Wojciech Bederski (wuub.net)
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for details.
|
||||
|
||||
import telnetlib
|
||||
from . import repl
|
||||
|
||||
class TelnetRepl(repl.Repl):
|
||||
TYPE = "telnet"
|
||||
|
||||
def __init__(self, encoding, host="localhost", port=23, **kwds):
|
||||
"""Create new TelnetRepl with the following initial values:
|
||||
encoding: one of python accepted encoding used to encode commands and decode responses
|
||||
external_id: external, persisten name of this repl used to find it later
|
||||
host: telnet host to connect to
|
||||
port: telnet port to connect to
|
||||
cmd_postfix: some REPLS require you to end a command with a postfix to begin execution,
|
||||
think ';' or '.', you can force repl to add it automatically"""
|
||||
super(TelnetRepl, self).__init__(encoding, **kwds)
|
||||
self._telnet = telnetlib.Telnet()
|
||||
#convert to int for user's sake, we don't care if it's an float or string
|
||||
# as long as it can be turned into an INT
|
||||
self._telnet.open(host, int(port))
|
||||
self._alive = True
|
||||
self._killed = False
|
||||
|
||||
def name(self):
|
||||
return "%s:%s" % (self._telnet.host, self._telnet.port)
|
||||
|
||||
def is_alive(self):
|
||||
return self._alive
|
||||
|
||||
def read_bytes(self):
|
||||
return self._telnet.read_some()
|
||||
|
||||
def write_bytes(self, bytes):
|
||||
self._telnet.write(bytes)
|
||||
|
||||
def kill(self):
|
||||
self._killed = True
|
||||
self._telnet.close()
|
||||
self._alive = False
|
||||
Reference in New Issue
Block a user