Last active
July 7, 2022 17:41
-
-
Save DiamondDemon669/1cf245fcce42c1d4057e1b52058d12f7 to your computer and use it in GitHub Desktop.
Pyspeed: an asynchronous CLI tool made to speed up python by avoiding running the script running more than once using sockets and/or files
This file contains hidden or 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
import asyncio, socket, os, shlex | |
from watchdog.events import FileSystemEventHandler | |
from watchdog.observers import Observer | |
from watchdog.observers.polling import PollingObserver | |
from multiprocessing import Process | |
class socket_handler: | |
def __init__(self): | |
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
async def run_server(self, address, port, printrun, handle): | |
server = self.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, *([''] + shlex.split(str(request).replace('\n', '')))) + '\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): | |
try: | |
asyncio.run(self.run_server(address, port, printrun, self.handle_client)) | |
except KeyboardInterrupt: | |
self.server.shutdown(socket.SHUT_RDWR) | |
self.server.close() | |
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, *([''] + shlex.split(x.replace('\n', ''))))) | |
self.modified = True | |
else: | |
self.modified = False | |
def handle_data(self, file, *argv): | |
return ''.join(argv) | |
class pyspeed_handler(file_handler, socket_handler): | |
def __init__(self, handle_path=''): | |
socket_handler.__init__(self) | |
file_handler.__init__(self, handle_path) | |
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 parametersTo 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):
Linux/MacOS:
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. theargv
parameter has the same rules as the socket handler. to run the listener, call the run method with thehandle_file
parameterThis class requires watchdog dependency
you still need to know the timing of the code for input/output
Windows:
Linux/MacOS:
Combined handler
The combined handler opens a socket and listens for modifications on a file. it sends socket data to
handle_request
, file data tohandle_data
and both tohandle_all
. the source object forhandle_request
will be a client object, a file object forhandle_data
and either forhandle_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.Runs both handlers, so any of the above I/O commands will work
API reference
class socket_handler
async def run_server
Method behind
socket_handler.run
. Do not run this, use the run methodasync def handle_client
Method behind
socket_handler.handle_request
, listens for connectionsdef handle_request(self, client, *argv)
Method called upon connection by
socket_handler.handle_client
. accepts the socket client and a list of arguments, UNIX-like.the return value will be sent back
Override this method
def run(self, address, port, printrun=False)
call this method to run the server.
printrun
is purely for debugging purposesclass file_handler(watchdog.events.FileSystemEventHandler)
def run(self, handle_path=None, printrun=False)
Call this method to run the server.
handle_path
argument is a path to the file the server will listen on andprintrun
is for debugging purposes. if no handle path is provided, it will use the one provided on initializationdef clear_handle(self)
Do not run this method in
handle_data
method, or you will be stuck in recursive loop. Clears the handle filedef generate_handle(self, program_name, handle_path=None)
Generates a handle file for you. nothing special.
def on_modified(self, event)
Called when handle file is modified. Calls
handle_data
methoddef handle_data(self, file, *argv)
Called by
on_modified
method. accepts file object and list of arguments.the return value will be written to the file
Override this method