Queue in Python - python

I am an PHP developer, i've never used python before, now i'm implementing am SMS system on our project, it's already working, but as i dont like python i've just made a little script with some information i collected over the web and i call this script using an php, the python script is
#!/usr/bin/python
import urllib, sys
import serial
import time
for arg in sys.argv:
if arg == "-t":
recipient = sys.argv[2] # numero
elif arg == "-m":
message = sys.argv[4] # mensagem
phone = serial.Serial("/dev/serial0", 9600, timeout=5)
try:
time.sleep(0.2)
phone.write(b'ATZ\r')
time.sleep(0.12)
phone.write(b'AT+CMGF=1\r')
time.sleep(0.12)
phone.write(b'AT+CMGS="' + recipient.encode() + b'"\r')
time.sleep(0.12)
phone.write(message.encode() + b"\r")
time.sleep(0.12)
phone.write(bytes([26]))
time.sleep(0.12)
finally:
phone.close()
print('OK')
And i call it passing the vars on php like:
$recipient = "+5511anyphonenumber";
$message = "'Estou fazendo um teste'";
$command = "python3 teste2.py -t $recipient -m $message";
exec($command);
The only problem is that i need the script to run only once per 3 seconds, so if it is called once and is called again 2 seconds after, i need the second call to be queued until 3 seconds has been passed from the last call, is there any easy way to do this? Please take in count that i'm not familiar with python.

You can use a list (something like an array) to store any calls that come during the 3 seconds. As the calls come, append them to the list (use the append() method - <list>.append(<object>)
).
After, 3 seconds are over, execute the first element of the list: <list>[0] and remove it from the list:
<item> = <list>.pop(0)
(You can execute the <item>)
If you want to accept calls simultaneously during the three seconds while other processes are going, then, use the threading module to accept the calls while running the original process and to store these calls to execute after 3 seconds.

I take it the 3 seconds is just an arbitrary duration it takes for the program has finished doing its thing, not a hard limit?
You'll probably want to use a lock file of some sort to avoid concurrent executions. Take a look at fasteners (as suggested in the now-deprecated lockfile); it's designed to do things right (there are certain race-condition/atomicity corner cases you need to take care of if you decide to roll your own.)

Related

Trying to use the "schedule" module to automate opening a program is this possible? (Im trying to automatically log into my meeting

import schedule
import os
def start_file():
os.startfile("C:\python\Zoom.lnk")
schedule.every().day.at("09:00").do(start_file())
It just opens my zoom application without it being at the proper time and it gives me the error:
"File "C:\Users\Hate348\PycharmProjects\pythonProject\venv\lib\site-packages\schedule\__init__.py", line 440, in do
self.job_func = functools.partial(job_func, *args, **kwargs)
TypeError: the first argument must be callable"
First of all, start_file() should be start_file in the do() call:
import schedule
import os
def start_file():
os.startfile(r"C:\python\Zoom.lnk")
schedule.every().day.at("09:00").do(start_file)
Otherwise you call start_file() only once, and you ask the schedule library to call the result value of that call, which is None, every day at 9. Calling None doesn't make sense, hence the exception.
Second, there has to be a loop for the scheduled jobs to actually take place:
import time
...
while True:
schedule.run_pending()
time.sleep(1)
Otherwise the script will just stop and not do anything. It's a very simple library without any external dependencies; it doesn't use the operating system's task schedulers or anything like that, so the script has to keep running for it to work.
So, in total:
import os
import schedule
import time
def start_file():
os.startfile(r"C:\python\Zoom.lnk")
schedule.every().day.at("09:00").do(start_file)
while True:
schedule.run_pending()
time.sleep(1)
If you don't need the task to start at 9:00:00 sharp, but just around 9:00, you can also prolong the sleep to one minute and save a little bit of CPU:
while True:
schedule.run_pending()
time.sleep(60)
Also, I added r before the "C:\python\Zoom.lnk" string. Not because it doesn't work in this specific example (it does), but generally, without the r, the \ will be an escape character in the string, and with other characters after \, such as n, you will get unwanted results. With the r, \ characters are taken literally and never as escape characters. And that's what you want with backslashes in filenames, especially on Windows.

How to make a python script stopable from another script?

TL;DR: If you have a program that should run for an undetermined amount of time, how do you code something to stop it when the user decide it is time? (Without KeyboardInterrupt or killing the task)
--
I've recently posted this question: How to make my code stopable? (Not killing/interrupting)
The answers did address my question, but from a termination/interruption point of view, and that's not really what I wanted. (Although, my question didn't made that clear)
So, I'm rephrasing it.
I created a generic script for example purposes. So I have this class, that gathers data from a generic API and write the data into a csv. The code is started by typing python main.py on a terminal window.
import time,csv
import GenericAPI
class GenericDataCollector:
def __init__(self):
self.generic_api = GenericAPI()
self.loop_control = True
def collect_data(self):
while self.loop_control: #Can this var be changed from outside of the class? (Maybe one solution)
data = self.generic_api.fetch_data() #Returns a JSON with some data
self.write_on_csv(data)
time.sleep(1)
def write_on_csv(self, data):
with open('file.csv','wt') as f:
writer = csv.writer(f)
writer.writerow(data)
def run():
obj = GenericDataCollector()
obj.collect_data()
if __name__ == "__main__":
run()
The script is supposed to run forever OR until I command it to stop. I know I can just KeyboardInterrupt (Ctrl+C) or abruptly kill the task. That isn't what I'm looking for. I want a "soft" way to tell the script it's time to stop, not only because interruption can be unpredictable, but it's also a harsh way to stop.
If that script was running on a docker container (for example) you wouldn't be able to Ctrl+C unless you happen to be in the terminal/bash inside the docker.
Or another situation: If that script was made for a customer, I don't think it's ok to tell the customer, just use Ctrl+C/kill the task to stop it. Definitely counterintuitive, especially if it's a non tech person.
I'm looking for way to code another script (assuming that's a possible solution) that would change to False the attribute obj.loop_control, finishing the loop once it's completed. Something that could be run by typing on a (different) terminal python stop_script.py.
It doesn't, necessarily, needs to be this way. Other solutions are also acceptable, as long it doesn't involve KeyboardInterrupt or Killing tasks. If I could use a method inside the class, that would be great, as long I can call it from another terminal/script.
Is there a way to do this?
If you have a program that should run for an undetermined amount of time, how do you code something to stop it when the user decide it is time?
In general, there are two main ways of doing this (as far as I can see). The first one would be to make your script check some condition that can be modified from outside (like the existence or the content of some file/socket). Or as #Green Cloak Guy stated, using pipes which is one form of interprocess communication.
The second one would be to use the built in mechanism for interprocess communication called signals that exists in every OS where python runs. When the user presses Ctrl+C the terminal sends a specific signal to the process in the foreground. But you can send the same (or another) signal programmatically (i.e. from another script).
Reading the answers to your other question I would say that what is missing to address this one is a way to send the appropriate signal to your already running process. Essentially this can be done by using the os.kill() function. Note that although the function is called 'kill' it can send any signal (not only SIGKILL).
In order for this to work you need to have the process id of the running process. A commonly used way of knowing this is making your script save its process id when it launches into a file stored in a common location. To get the current process id you can use the os.getpid() function.
So summarizing I'd say that the steps to achieve what you want would be:
Modify your current script to store its process id (obtainable by using os.getpid()) into a file in a common location, for example /tmp/myscript.pid. Note that if you want your script to be protable you will need to address this in a way that works in non-unix like OSs like Windows.
Choose one signal (typically SIGINT or SIGSTOP or SIGTERM) and modify your script to register a custom handler using signal.signal() that addresses the graceful termination of your script.
Create another (note that it could be the same script with some command line paramater) script that reads the process id from the known file (aka /tmp/myscript.pid) and sends the chosen signal to that process using os.kill().
Note that an advantage of using signals to achieve this instead of an external way (files, pipes, etc.) is that the user can still press Ctrl+C (if you chose SIGINT) and that will produce the same behavior as the 'stop script' would.
What you're really looking for is any way to send a signal from one program to another, independent, program. One way to do this would be to use an inter-process pipe. Python has a module for this (which does, admittedly, seem to require a POSIX-compliant shell, but most major operating systems should provide that).
What you'll have to do is agree on a filepath beforehand between your running-program (let's say main.py) and your stopping-program (let's say stop.sh). Then you might make the main program run until someone inputs something to that pipe:
import pipes
...
t = pipes.Template()
# create a pipe in the first place
t.open("/tmp/pipefile", "w")
# create a lasting pipe to read from that
pipefile = t.open("/tmp/pipefile", "r")
...
And now, inside your program, change your loop condition to "as long as there's no input from this file - unless someone writes something to it, .read() will return an empty string:
while not pipefile.read():
# do stuff
To stop it, you put another file or script or something that will write to that file. This is easiest to do with a shell script:
#!/usr/bin/env sh
echo STOP >> /tmp/pipefile
which, if you're containerizing this, you could put in /usr/bin and name it stop, give it at least 0111 permissions, and tell your user "to stop the program, just do docker exec containername stop".
(using >> instead of > is important because we just want to append to the pipe, not to overwrite it).
Proof of concept on my python console:
>>> import pipes
>>> t = pipes.Template()
>>> t.open("/tmp/file1", "w")
<_io.TextIOWrapper name='/tmp/file1' mode='w' encoding='UTF-8'>
>>> pipefile = t.open("/tmp/file1", "r")
>>> i = 0
>>> while not pipefile.read():
... i += 1
...
At this point I go to a different terminal tab and do
$ echo "Stop" >> /tmp/file1
then I go back to my python tab, and the while loop is no longer executing, so I can check what happened to i while I was gone.
>>> print(i)
1704312

Chaining Python Scripts

I have two user defined python scripts. First takes a file and processes it, while the second script takes the output of first and runs an executable, and supplies the output of first script to program with additional formatting.
I need to run these scripts via another python script, which is my main executable script.
I searched a bit about this topic and;
I can use importlib to gather the content of scripts so that I can call them at appropriate times. This requires the scripts to be under my directory/or modification to path environment variable. So it is a bit ugly looking at best, not seem pythonish.
Built-in eval function. This requires the user to write a server-client like structure, cause the second script might have to run the said program more than one time while the first script still gives output.
I think I'm designing something wrong, but I cannot come up with a better approach.
A more detailed explenation(maybe gibberish)
I need to benchmark some programs, while doing so I have a standard form of data, and this data needs to be supplied to benchmark programs. The scripts are (due to nature of benchmark) special to each program, and needs to be bundled with benchmark definition, yet I need to create this program as a standalone configurable tester. I think, I have designed something wrong, and would love to hear the design approaches.
PS: I do not want to limit the user, and this is the reason why I choose to run python scripts.
I created a few test scripts to make sure this works.
The first one (count_01.py) sleeps for 100 seconds, then counts from 0 to 99 and sends it to count_01.output.
The second one (count_02.py) reads the output of first one (count_01.output) and adds 1 to each number and writes that to count_02.output.
The third script (chaining_programs.py) runs the first one and waits for it to finish before calling the second one.
# count_01.py --------------------
from time import sleep
sleep(100)
filename = "count_01.output"
file_write = open(filename,"w")
for i in range(100):
#print " i = " + str(i)
output_string = str(i)
file_write.write(output_string)
file_write.write("\n")
file_write.close()
# ---------------------------------
# count_02.py --------------------
file_in = "count_01.output"
file_out = "count_02.output"
file_read = open(file_in,"r")
file_write = open(file_out,"w")
for i in range(100):
line_in = file_read.next()
line_out = str(int(line_in) + 1)
file_write.write(line_out)
file_write.write("\n")
file_read.close()
file_write.close()
# ---------------------------------
# chaining_programs.py -------------------------------------------------------
import subprocess
import sys
#-----------------------------------------------------------------------------
path_python = 'C:\Python27\python.exe' # 'C:\\Python27\\python.exe'
#
# single slashes did not work
#program_to_run = 'C:\Users\aaaaa\workspace\Rich_Project_044_New_Snippets\source\count.py'
program_to_run_01 = 'C:\\Users\\aaaaa\\workspace\\Rich_Project_044_New_Snippets\\source\\count_01.py'
program_to_run_02 = 'C:\\Users\\aaaaa\\workspace\\Rich_Project_044_New_Snippets\\source\\count_02.py'
#-----------------------------------------------------------------------------
# waits
sys.pid = subprocess.call([path_python, program_to_run_01])
# does not wait
sys.pid = subprocess.Popen([path_python, program_to_run_02])
#-----------------------------------------------------------------------------

How to keep a While True loop running with raw_input() if inputs are seldom?

I'm currently working on a project where I need to send data via Serial persistently but need to occasionally change that data based in new inputs. My issue is that my current loop only functions exactly when a new input is offered by raw_input(). Nothing runs again until another raw_input() is received.
My current (very slimmed down) loop looks like this:
while True:
foo = raw_input()
print(foo)
I would like for the latest values to be printed (or passed to another function) constantly regardless of how often changes occur.
Any help is appreciated.
The select (or in Python 3.4+, selectors) module can allow you to solve this without threading, while still performing periodic updates.
Basically, you just write the normal loop but use select to determine if new input is available, and if so, grab it:
import select
while True:
# Polls for availability of data on stdin without blocking
if select.select((sys.stdin,), (), (), 0)[0]:
foo = raw_input()
print(foo)
As written, this would print far more than you probably want; you could either time.sleep after each print, or change the timeout argument to select.select to something other than 0; if you make it 1 for instance, then you'll update immediately when new data is available, otherwise, you'll wait a second before giving up and printing the old data again.
How will you type in your data at the same time while data is being printed?
However, you can use multithreading if you make sure your source of data doesn't interfere with your output of data.
import thread
def give_output():
while True:
pass # output stuff here
def get_input():
while True:
pass # get input here
thread.start_new_thread(give_output, ())
thread.start_new_thread(get_input, ())
Your source of data could be another program. You could connect them using a file or a socket.

Keyboard input between select() in Python

I write some codes to get the input from keyboard and also check something is alive or not:
import sys
from select import select
timeout = 10
while is_alive(): # is_alive is a method to check some stuffs, might take 5 secs
rlist, _, _ = select([sys.stdin], [], [], timeout)
if rlist:
s = sys.stdin.readline()
print repr(s)
handle(s) # handle is a method to handle and react according to input s
I found that when the keyboard input ends outside of the waiting in select() (usually it ends during the 5 secs of is_alive()), the if rlist: will get false.
I can understand why but I don't know how to solve it.
And there is still another question related to the situation mentioned above, sometimes readline() will return the last line of my input when some inputs are located across different select() waiting.
That means, if I enter 'abc\n' and unfortunately the '\n' located outside of wating in select() (that means, when I press Enter, the program are executing other parts, such as is_alive()), and then if I enter 'def\n' and this time the Enter pressed successfully located within select(), I'll see the s from readline() becomes 'def\n' and the first line is disappeared.
Is there any good solution to solve two issues above? I'm using FreeBSD 9.0.
As your code in is_alive() calls ssh, this will eat up the stdin.
Try starting ssh with the -n option or with a re-directed stdin.
The latter would work with
sp = subprocess.Popen(..., stdin=subprocess.PIPE)
sp.stdin.close()

Categories

Resources