readline python module does not work with aioconsole.ainput - python

The following code accepts input from terminal and echo it back:
import readline
import asyncio
import aioconsole
async def input_coro():
while True:
from_user = await aioconsole.ainput('> ')
from_user = from_user.strip()
print(from_user)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(input_coro())
However import readline has not effect here, meaning using up/down arrows to go through the command line history does not work.
How can I get the readline functionality with aioconsole.ainput ?

Related

Python, how to execute a line of code without it stopping the rest of the code from executing?

first of all, im a beginner.
Want i want to accomplish is that music plays while the script is executing.
What it does right now it plays the music, waits until the music is over and then executes the rest of the code. That is not what i want. Here my Code:
import os
import subprocess
import multiprocessing
import threading
from playsound import playsound
CurrentPath = os.path.dirname(os.path.normpath(__file__))
os.chdir(CurrentPath)
def music():
Music = "Music.mp4"
#subprocess.run(["ffplay", "-nodisp", "-autoexit", "-hide_banner", Music])
playsound("Music.mp4")
def other_things():
print("Hello World")
#musicp = multiprocessing.Process(target=music())
#restp = multiprocessing.Process(target=other_things())
musicp = threading.Thread(target=music())
restp = threading.Thread(target=other_things())
restp.start()
musicp.start()
LIke you can see i even tried multithreading but it still waits until the music is over before it goes to the rest of the code.
Don't call the functions in the target parameter of the Thread function - delete the brackets to reference the function, not its return value
musicp = threading.Thread(target=music) # instead of music()
restp = threading.Thread(target=other_things) # instead of other_things()

How to write a Python terminal application with a fixed input line?

I'm trying to write a terminal application to interact with an Arduino microcontroller via pyserial. The following features are important:
Print incoming messages to the command line.
Allow the user to enter output messages to the serial port. The input should be possible, while new incoming messages are printed.
In principle, this should be possible with cmd. But I'm struggling with printing incoming messages, when the user started typing.
For simplicity, I wrote the following test script emulating incoming messages every second. Outgoing messages are just echoed back to the command line with the prefix ">":
#!/usr/bin/env python3
from cmd import Cmd
from threading import Thread
import time
class Prompt(Cmd):
def default(self, inp):
print('>', inp)
stop = False
def echo():
while not stop:
print(time.time())
time.sleep(1)
thread = Thread(target=echo)
thread.daemon = True
thread.start()
try:
Prompt().cmdloop()
except KeyboardInterrupt:
stop = True
thread.join()
In Spyder IDE, the result is just perfect:
But in iterm2 (Mac OS) the output is pretty messed up:
Since I want to use this application from within Visual Studio Code, it should work outside Spyder. Do you have any idea how to get the same behaviour in iterm2 as in Spyder?
Things I already considered or tried out:
Use the curses library. This solves my problem of printing text to different regions. But I'm loosing endless scrolling, since curses defines its own fullscreen window.
Move the cursor using ansi escape sequences. It might be a possible solution, but I'm just not getting it to work. It always destroys the bottom line where the user is typing. I might need to adjust the scrolling region, which I still didn't manage to do.
Use a different interpreter. I already tried Python vs. iPython, without success. It might be a more subtle setting in Spyder's interpreter.
Yes! I found a solution: The Prompt Toolkit 3.0 in combination with asyncio lets you handle this very problem using patch_stdout, "a context manager that ensures that print statements within it won’t destroy the user interface".
Here is a minimum working example:
#!/usr/bin/env python3
from prompt_toolkit import PromptSession
from prompt_toolkit.patch_stdout import patch_stdout
import asyncio
import time
async def echo():
while True:
print(time.time())
await asyncio.sleep(1)
async def read():
session = PromptSession()
while True:
with patch_stdout():
line = await session.prompt_async("> ")
print(line.upper())
loop = asyncio.get_event_loop()
loop.create_task(echo())
loop.create_task(read())
loop.run_forever()
It's a while since I was interacting with an Arduino with my Mac. I used pyserial and it was 100% reliable. key is user read_until(). I've included my wrapper class for illustration. (Also has an emulation mode for when I didn't have a Arduino)
import serial # pip install PySerial
from serial.tools import list_ports
import pty, os # for creating virtual serial interface
from serial import Serial
from typing import Optional
class SerialInterface:
# define constants which control how class works
FULLEMULATION=0
SERIALEMULATION=1
URLEMULATION=2
FULLSOLUTION=3
# define private class level variables
__emulate:int = FULLEMULATION
__ser:Serial
__port:str = ""
def __init__(self, emulate:int=FULLEMULATION, port:str="") -> None:
self.__buffer:list = []
self.__emulate = emulate
self.__port = port
#self.listports()
# setup connection to COM/serial port
# emulation sets up a virtual port, but this has not been working
if emulate == self.FULLSOLUTION:
self.__ser = serial.Serial(port, 9600)
elif emulate == self.SERIALEMULATION:
master, slave = pty.openpty()
serialport = os.ttyname(slave)
self.__ser = serial.Serial(port=serialport, baudrate=9600, timeout=1)
elif emulate == self.URLEMULATION:
self.__ser = serial.serial_for_url("loop://")
# useful to show COM/serial ports on a computer
#staticmethod
def listports() -> list:
for p in serial.tools.list_ports.comports():
print(p, p.device)
serialport = p.device
return serial.tools.list_ports.comports()
def read_until(self, expected:bytes=b'\n', size:Optional[int]=None) -> bytes:
if self.__emulate == self.FULLEMULATION:
return self.__buffer.pop()
else:
return self.__ser.read_until(expected, size)
# note it is important to have \n on end of every write to allow data to be read item by item
def write(self, bytes:bytes=b'') -> None:
if self.__emulate == self.FULLEMULATION:
self.__buffer.append(bytes)
else:
self.__ser.write(bytes)
def dataAvail(self) -> bool:
if self.__emulate == self.FULLEMULATION:
return len(self.__buffer) > 0
else:
return self.__ser.inWaiting() > 0
def close(self) -> None:
self.__ser.close()
def mode(self) -> int:
return self.__emulate

Discord.py tasks.loop does not work and it returns no errors

I've been trying to make loops in Discord.py, but I keep failing. What makes it harder is that it returns no errors.
This is the code;
import discord
import datetime
import time
import threading
import os
import json
import random
import asyncio
from discord.ext import commands
from discord.ext.commands import has_permissions
from discord.ext.tasks import loop
import keep_alive
prefix = "s."
token = str(os.getenv("TOKEN"))
client = commands.Bot(command_prefix = prefix)
client.remove_command("help")
os.chdir(r".")
ownerId = [219567539049594880]
logChn = 705181132672729098
secondsUp = 0
#loop(seconds=1)
async def add_second():
secondsUp+=1
print("+1 Second")
The console does not print anything. No errors. Is there anything I'm missing?
In discord.py, you must call the .start() method on every loop that you create. In this case, add_seconds.start(). Also, try adding global secondsUp to the top of your function definition.

Python program as Windows Service

below is my code that I am trying to turn into a windows service. You'll see test.py as the call it makes and all this is a short script that writes into a log file (as a test).
The code is there to make it a windows service and it does that fine, but when I run it nothing writes into the log file. Help greatly appreciated. Below is the code:
import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import win32evtlogutil
import os, sys, string, time
class aservice(win32serviceutil.ServiceFramework):
_svc_name_ = "MyServiceShortName"
_svc_display_name_ = "A python test"
_svc_description_ = "Writing to a log"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
def SvcDoRun(self):
import servicemanager
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, ''))
self.timeout = 1000 #1 seconds
# This is how long the service will wait to run / refresh itself (see script below)
while 1:
# Wait for service stop signal, if I timeout, loop again
rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
# Check to see if self.hWaitStop happened
if rc == win32event.WAIT_OBJECT_0:
# Stop signal encountered
servicemanager.LogInfoMsg("SomeShortNameVersion - STOPPED!") #For Event Log
break
else:
#what to run
try:
file_path = "test.py"
execfile(file_path)
except:
pass
#end of what to run
def ctrlHandler(ctrlType):
return True
if __name__ == '__main__':
win32api.SetConsoleCtrlHandler(ctrlHandler, True)
win32serviceutil.HandleCommandLine(aservice)
Edit: Thought for the sake of it I'd include my test.py file's code, it has unnecessary imports but will get the job done if you run it alone.
import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import win32evtlogutil
import os
logfile = open("log.txt", "a") #open file to log restart timestamp
logfile.write("\nthat's good!!!!")
logfile.close()
Okay so I figured it out and would like to come back and post in case anyone else is dealing with this, all though this was a tad unique.
You have to specify your file path if you are within a windows service, duh... but it wasn't done and I pulled my hair out for no reason.
file_path = "test.py"
should have been
file_path = r"c:\users\...\test.py"
Be careful when using '\' for windows file paths. They must be escaped like '\\' or the string must be declared as raw string ('r'). Using the unix like slash '/' separators works also but may look odd for windows users.

How to use FIFO to communitace between python subprocesss

I try to use Python (2.6) subprocess module to communicated between two external programs. When I started to pump more data 'client' stopped to received everything. I thought it might be related to limited size of subprocess.PIPE object (http://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/). So I decided to try fifo, but it didn't worked as expected. How should I use fifo to communicated between two external program called from python script. Currently my script stops at (waiting for another end of fifo):
pipe_name = "stream-%s.fifo"%self.transponder
os.mkfifo(pipe_name)
self.stream = Popen(program1, stdout=open(pipe_name,'w'),
stderr=open("test.log",'w'))
I didn't manage to get mkfifo working. However I managed to have server/client communicate via os.pipe:
#!/usr/bin/env python
from subprocess import Popen
import os
from itertools import cycle
from time import sleep
from sys import stdout, executable
def client():
for letter in cycle('ABCDE'):
stdout.write(letter)
stdout.flush()
sleep(1)
def main(argv=None):
import sys
from argparse import ArgumentParser
argv = argv or sys.argv
parser = ArgumentParser()
parser.add_argument('--client', default=False, action='store_true')
args = parser.parse_args(argv[1:])
if args.client:
client()
r, w = os.pipe()
pipe = Popen([executable, __file__, '--client'], stdout=os.fdopen(w, 'w'))
try:
client_out = os.fdopen(r, 'r')
while True:
letter = client_out.read(1)
print(letter)
except KeyboardInterrupt:
pipe.kill()
if __name__ == '__main__':
main()

Categories

Resources