Skip to content

Instantly share code, notes, and snippets.

@joonty
Created October 7, 2013 15:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joonty/6869594 to your computer and use it in GitHub Desktop.
Save joonty/6869594 to your computer and use it in GitHub Desktop.
Background thread based socket server for vim
import sys
import socket
import select
import Queue
import threading
import time
import vim
import os
class LogError(Exception):
pass
class Logger:
""" Abstract class for all logger implementations.
Concrete classes will log messages using various methods,
e.g. write to a file.
"""
(ERROR,INFO,DEBUG) = (0,1,2)
TYPES = ("ERROR","Info","Debug")
debug_level = ERROR
def __init__(self,debug_level):
pass
def log(self, string, level):
""" Log a message """
pass
def shutdown(self):
""" Action to perform when closing the logger """
pass
def time(self):
""" Get a nicely formatted time string """
return time.strftime("%a %d %Y %H:%M:%S", \\
time.localtime())
def format(self,string,level):
display_level = self.TYPES[level]
""" Format the error message in a standard way """
return "- [%s] {%s} %s" %(display_level, self.time(), str(string))
class FileLogger(Logger):
""" Log messages to a window.
The window object is passed in on construction, but
only created if a message is written.
"""
def __init__(self,debug_level,filename):
self.filename = os.path.expanduser(filename)
self.f = None
self.debug_level = int(debug_level)
def __open(self):
try:
self.f = open(self.filename,'w')
except IOError as e:
raise LogError("Invalid file name '%s' for log file: %s" \\
%(self.filename,str(e)))
except:
raise LogError("Error using file '%s' as a log file: %s" \\
%(self.filename,sys.exc_info()[0]))
def shutdown(self):
if self.f is not None:
self.f.close()
def log(self, string, level):
if level > self.debug_level:
return
if self.f is None:
self.__open()
self.f.write(\\
self.format(string,level)+"\\n")
self.f.flush()
class QueueServer(threading.Thread):
def __init__(self, host, port, message_queue, lock):
self.__message_queue = message_queue
self.__host = host
self.__port = port
self.__logger = FileLogger(Logger.DEBUG, "queueserver.log")
self.__lock = lock
threading.Thread.__init__(self)
def log(self, message):
self.__logger.log(message, Logger.DEBUG)
def run(self):
try:
self.log("Started")
self.log("Listening on port %s" % self.__port)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(0)
s.bind((self.__host, self.__port))
s.listen(5)
while 1:
try:
self.peek_for_exit()
client, address = s.accept()
self.log("Found client, %s" % str(address))
self.client_read(client)
while 1:
self.log("Waiting for message")
message = self.__message_queue.get()
self.log("Received message: %s" % message)
self.check_exit(message)
self.client_write(client, message)
if not self.client_read(client):
self.log("Closing client connection")
client.close()
break
except socket.error, e:
if e.errno == 11:
pass
else:
if client:
client.close()
raise e
except Exception, e:
self.log("Error: %s" % str(sys.exc_info()))
self.log("Stopping server")
raise e
def client_write(self, client, msg):
self.log("Writing message: %s" % msg)
client.send(msg)
def client_read(self, client):
self.log("Receiving")
data = client.recv(1024)
if data == "":
self.log("Client connection closed")
return False
else:
self.log("Received data: %s" % data)
self.__lock.acquire()
self.log("Acquired lock")
vim.current.buffer.append(data.strip().split("\\n"))
self.__lock.release()
self.log("Written to buffer")
return True
def peek_for_exit(self):
try:
self.check_exit(self.__message_queue.get_nowait())
except Queue.Empty:
pass
def check_exit(self, message):
if message == "exit":
raise Exception("Exiting")
class BackgroundSocketServer:
def __init__(self):
self.__message_queue = Queue.Queue(0)
self.__lock = threading.Lock()
self.__thread = None
def __del__(self):
self.stop()
def start(self):
if self.__thread:
print self.__thread.is_alive()
if self.__thread and self.__thread.is_alive():
print "Already running!"
else:
self.__thread = QueueServer('127.0.0.1', 9000, self.__message_queue,
self.__lock)
self.__thread.start()
print "Started queue server thread"
def send_message(self, message):
self.__message_queue.put(message)
def stop(self):
if self.__thread and self.__thread.is_alive():
print "Sending exit message"
self.__message_queue.put_nowait("exit")
print "Joining threads"
self.__thread.join()
print "Stopped"
else:
print "Already stopped!"
@joonty
Copy link
Author

joonty commented Oct 7, 2013

From vim:

pyfile path/to/threaded-vim-server.py
python server = BackgroundSocketServer()

Then you can start the server with:

:python server.start()

And send a message to the socket with:

:python server.send_message("Hello, sir")

Finally, stop it with

:python server.stop()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment