Skip to content

Instantly share code, notes, and snippets.

@DiamondDemon669
Last active May 29, 2022
Embed
What would you like to do?
Pyspeed: an asynchronous CLI tool made to speed up python by avoiding running the script running more than once using sockets and/or files
import asyncio, socket, os
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
from watchdog.observers.polling import PollingObserver
from multiprocessing import Process
class socket_handler:
async def run_server(self, address, port, printrun, handle):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server = server
server.bind((address, port))
server.listen(8)
if printrun:
print("Running!")
server.setblocking(False)
loop = asyncio.get_event_loop()
while True:
client, _ = await loop.sock_accept(server)
loop.create_task(handle(client))
async def handle_client(self, client):
loop = asyncio.get_event_loop()
request = None
try:
while True:
request = (await loop.sock_recv(client, 255)).decode('utf8')
response = self.handle_request(client, *([''] + str(request).replace('\n', '').split(' '))) + '\n'
await loop.sock_sendall(client, response.encode('utf8'))
client.close()
except (BrokenPipeError, ConnectionAbortedError):
pass
def handle_request(self, client, *argv):
return ''.join(argv)
def run(self, address="localhost", port=50200, printrun=False):
asyncio.run(self.run_server(address, port, printrun, self.handle_client))
class file_handler(FileSystemEventHandler):
def __init__(self, handle_path=''):
self.observer = Observer()
self.handle_path = handle_path
self.modified = False
if isinstance(self.observer, PollingObserver):
raise TypeError("Cannot use polling observer")
def run(self, handle_path=None, printrun=False):
if not handle_path:
handle_path = self.handle_path
self.handle_path = handle_path
self.observer.schedule(self, path=handle_path, recursive=False)
self.observer.start()
if printrun:
print("Running!")
try:
while True:
pass
except KeyboardInterrupt:
self.observer.stop()
self.observer.join()
def clear_handle(self):
with open(self.handle_path, "w+") as file:
self.modified = True
file.write('')
return True
def generate_handle(self, program_name, handle_path=None):
if not handle_path:
handle_path = self.handle_path
with open(os.path.join(handle_path, program_name), 'r+') as handlefile:
self.handle_path = os.path.join(handle_path, program_name)
return handlefile
def on_modified(self, event):
if os.path.isdir(event.src_path): return
if not self.modified:
with open(event.src_path, 'r+') as file:
lines = file.readlines().copy()
file.seek(0)
for x in lines:
file.write(self.handle_data(file, *([''] + x.replace('\n', '').split(' '))))
self.modified = True
else:
self.modified = False
def handle_data(self, file, *argv):
return ''.join(argv)
class pyspeed_handler(file_handler, socket_handler):
def run(self, address="localhost", port=50200, handle_path=None, printrun=False):
socketproc = Process(target=socket_handler.run, args=(self, address, port, printrun))
fileproc = Process(target=file_handler.run, args=(self, handle_path, printrun))
socketproc.start()
fileproc.start()
try:
while True:
pass
except KeyboardInterrupt:
socketproc.terminate()
fileproc.terminate()
socketproc.join()
fileproc.join()
def handle_request(self, client, *argv):
return self.handle_all(self, client, *argv)
def handle_data(self, file, *argv):
return self.handle_all(self, file, *argv)
def handle_all(self, ctx, *argv):
return ''.join(argv)
@DiamondDemon669
Copy link
Author

DiamondDemon669 commented Apr 24, 2022

Docs

Usage

Socket handler usage

The socket handler opens a local socket to send data to the script. to parse data, override the handle_request method. the return value will be sent back to the client. all handlers will take a source object and all the data split up by spaces in *argv. for the socket object, the source is the client object. argv[0] is a blank string. to run the server, call the run method with the address and port parameters

import pyspeed

class myhandler(pyspeed.socket_handler):
    def handle_request(self, client, *argv):
        return f"hi, {argv[1]}"

handler = myhandler()
handler.run(address="localhost", port=50200)

To send input to the script, run the script in the background then use this function
however you need to know how long it will take for the code to run
Windows (requires cygwin tools timeout and nc):

@echo off
echo %* | timeout 0.1 nc -w1 127.0.0.1 50200

Linux/MacOS:

sayhi(){ printf "$@" | timeout 0.1 nc 127.0.0.1 50200; }

File handler usage

The file handler watches a file for changes and sends any written data to the handle_data method. the return value will be written to the file and the source object is a file object of the handle file. the argv parameter has the same rules as the socket handler. to run the listener, call the run method with the handle_file parameter

import pyspeed

class myhandler(pyspeed.file_handler):
    def handle_data(self, file, *argv):
        return f"hi, {argv[1]}"

handler = myhandler()
handler.run("/tmp/test")

This class requires watchdog dependency
you still need to know the timing of the code for input/output
Windows:

@echo off
echo %* > C:\TEMP\test
timeout /t 1
type C:\TEMP\test

Linux/MacOS:

sayhi(){ printf "$@" > /tmp/test; sleep 1; cat /tmp/test; }

Combined handler

The combined handler opens a socket and listens for modifications on a file. it sends socket data to handle_request, file data to handle_data and both to handle_all. the source object for handle_request will be a client object, a file object for handle_data and either for handle_all. the return value will be either sent back to the socket or written to the file depending on the method. to run the server, call the run method and pass the address, port and handle_path which will be passed to the handlers.

import pyspeed

class myhandler(pyspeed.pyspeed_handler):
    def handle_all(self, ctx, *argv):
        return f"hi, {argv[1]}"

handler = myhandler()
handler.run(address="localhost", port=50200, handle_path="/tmp/test")

Runs both handlers, so any of the above I/O commands will work

API reference

TODO

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