start script with arguments sys.argv - python

Im trying to start this script with a argument.
When i start the script from a terminal i want to type "python test.py C:\Users\etc\etc\log.log count" to run func1 or error to run func3.
I have try to play around with sys.argv / sys.argv[2] but i cant get it to work.
import sys
def func1():
count = 0
with open(r'C:\Users\etc\etc\log.log') as logfile:
lines = logfile.readlines()
for error in lines:
count += error.count('[error]')
print('errors', count)
def func3():
import re
with open(r'C:\Users\etc\etc\log.log') as logfile:
for line in map(str.strip, logfile):
m = re.findall('\[.*?\]', line)
if len(m) > 1 and m[1] in ('[error]'):
offset = line.find(m[1]) + len(m[1]) + 1
print(m[0], line[offset:])
if __name__ == '__main__':
func1()
func3()

sys.argv is a list containing the arguments passed to the program. The first item is the file name, the rest are the arguments so to get the first argument you use sys.argv[1].
Because the first argument isn't necessarily provided, I've used a try/except block to catch the possible IndexError. You can handle this however you like.
You can then just use an if statement to decide which function to run.
import sys
def func1():
count = 0
with open(r'C:\Users\etc\etc\log.log') as logfile:
lines = logfile.readlines()
for error in lines:
count += error.count('[error]')
print('errors', count)
def func3():
import re
with open(r'C:\Users\etc\etc\log.log') as logfile:
for line in map(str.strip, logfile):
m = re.findall('\[.*?\]', line)
if len(m) > 1 and m[1] in ('[error]'):
offset = line.find(m[1]) + len(m[1]) + 1
print(m[0], line[offset:])
if __name__ == '__main__':
try:
arg = sys.argv[1]
except: pass
else:
if arg == "count":
func1()
elif arg == "error":
func3()

Related

One of methods doesn't work correctly when i call it

I need to make two checks in log files and display the result. Separately methods work correctly, but when I run all code method hit_unique_check always return "PASS: All hits are unique.". For two of three .log files this result is incorrect.
import os
class ReadFiles:
def __init__(self):
self.current_file = ""
self.shoot_from = "Shoot from"
self.hit_player = "Hit player"
def equally_check(self):
shoot_from_list = []
hit_player_list = []
for line in self.current_file:
if self.shoot_from in line:
shoot_from_list.append(line)
elif self.hit_player in line:
hit_player_list.append(line)
if len(shoot_from_list) == len(hit_player_list):
print(" PASS: Shoots and hits are equal.\n")
else:
print(" FAIL: Shoots and hits are NOT equal.\n")
def hit_unique_check(self):
unique_hit_list = []
duplicates = []
for line in self.current_file:
if self.hit_player in line:
unique_hit_list.append(line)
else:
continue
for i in unique_hit_list:
if unique_hit_list.count(i) > 1:
duplicates.append(i)
print(i)
else:
continue
if len(duplicates) < 1:
print(" PASS: All hits are unique.\n")
else:
print(" FAIL: This hits are duplicated.\n")
def run(self):
for file in os.listdir():
if file.endswith(".log"):
print(f"Log file - {file}")
self.current_file = open(f"{file}", 'rt')
print(self.current_file.readlines, f"")
self.equally_check()
self.hit_unique_check()
self.current_file.close()
if __name__ == "__main__":
run = ReadFiles()
run.run()
I run my python code, but result always the same: "PASS: All hits are unique.". For some files it must be "FAIL: This hits are duplicated.". I'm not sure that problem in the method hit_unique_check, and have no idea what to do.
Can you explain me, how I can make this method working correctly not only separately?
Consider this organization. Each function has one task, to evaluate and return its result. It's up to the caller to decide what to do with the result. Also note that I'm using counters instead of lists, since you don't really care what the lists contain. Also note the use of defaultdict, to avoid having to do repeated searches of your hit list.
import os
from collections import defaultdict
class ReadFiles:
def __init__(self):
self.shoot_from = "Shoot from"
self.hit_player = "Hit player"
def equally_check(self, lines):
shoot_from = 0
hit_player = 0
for line in lines:
if self.shoot_from in line:
shoot_from += 1
elif self.hit_player in line:
hit_player += 1
return shoot_from == hit_player
def hit_unique_check(self, lines):
unique_hit_list = defaultdict(int)
for line in lines:
if self.hit_player in line:
unique_hit_list[line] += 1
duplicates = 0
for k,v in unique_hit_list.items()
if v > 1:
duplicates += 1
print(k)
return not duplicates
def run(self):
for filename in os.listdir():
if filename.endswith(".log"):
print(f"Log file - {filename}")
lines = open(filename, 'rt').readlines()
print(lines)
if self.equally_check(lines):
print(" PASS: Shoots and hits are equal.\n")
else:
print(" FAIL: Shoots and hits are NOT equal.\n")
if self.hit_unique_check(lines):
print(" PASS: All hits are unique.\n")
else:
print(" FAIL: This hits are duplicated.\n")
if __name__ == "__main__":
run = ReadFiles()
run.run()
You could even replace the loop in hit_unique_check with a counter:
from collections import Counter
...
def hit_unique_check(self,lines):
unique_hit_list = Counter(lines)
for k,v in unique_hit_list,items():
...

Python - Simple While loop with IF statements

I am trying to make a script to check if an argument was entered. If an argument was entered it uses that number for the timer, if no argument was entered, then by default, the timer should equal 3.
This while loop will count down from the given number & print each value as it counts down. When it gets to the end it will output 'blast off!'
#!/usr/bin/env python3
import sys
timer = int(sys.argv[1])
while timer != 0:
if len(sys.argv[1]) == 0:
timer = 3
elif len(sys.argv[1]) == int(sys.argv[1]):
timer = int(sys.argv[1])
print (timer)
timer = timer - 1
print ('blast off!')
My homework checking script gives me an error of IndexError: list index out of range - related to the first timer = int(sys.argv[1])
I am not sure how exactly I am supposed to "convert an empty string into an integer"
Thank you
Check that you actually have an argument:
timer = int(sys.argv[1]) if len(sys.argv) >= 2 else 3
You are attempting to index into a position in the list that does not exist and getting an IndexError: list index out of range.
Lets call your file countdown.py.
When you run python3 countdown.py then sys.argv == ['countdown.py'].
If you ran python3 countdown.py 5 then sys.argv == ['countdown.py', 5]
Would recommend looking into the argparse library as this is the pythonic way to parse arguments from the command line.
For example your code would look like this:
#!/usr/bin/env python3
import argparse
def countdown(timer):
[print(tick) for tick in reversed(range(1, timer+1))]
print('blast off!')
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Countdown to blast off!')
parser.add_argument('timer', type=int, nargs='?', default=3)
args = parser.parse_args()
countdown(args.timer)
I'm guessing that your script is called without an argument which will fail because len(sys.argv) == 0.
If the user doesn't specify a value then set timer to whatever.
import sys
##################
if len(sys.argv) > 1:
timer = int(sys.argv[1])
else:
timer = 0
##################
while timer != 0:
if len(sys.argv[1]) == 0:
timer = 3
elif len(sys.argv[1]) == int(sys.argv[1]):
timer = int(sys.argv[1])
print (timer)
timer = timer - 1
print ('blast off!')
Or even better:
try:
timer = int(sys.argv[1])
except:
timer = 0

Executing python functions with multiple arguments in terminal

I have written a python function which takes multiple arguments and i want it to run from terminal but it's not working. what am I doing wrong?
counting.py script:
def count (a, b):
word = False
a = " " + a + " "
b = " " + b + " "
result = 0
for i in range (len (a)-1):
if a[i] == " " and a[i+1] != " ":
word = True
result += 1
else:
word = False
for i in range (len (b)-1):
if b[i] == " " and b[i+1] != " ":
word = True
result += 1
else:
word = False
return result
if __name__ == "__main__":
count (a, b)
terminal command:
python counting.py count "hello world" "let's check you out"
useing sys model,
add this code, the sys.argv first parameter is this file name
import sys
if __name__ == "__main__":
a = sys.argv[1]
b = sys.argv[2]
count(a,b)
terminal command:
python counting.py "hello word" "let's check you out"
ex:
import sys
def count(s1, s2):
print s1 + s2
print sys.argv
count(sys.argv[1], sys.argv[2])
out:
python zzzzzzz.py "hello" "word"
['zzzzzzz.py', 'hello', 'word']
helloword
a and b are the arguments of count. You cannot use them outside that scope. You could instead use sys.argv to access the commandline arguments:
from sys import argv
if __name__ == "__main__":
print(count (argv[1], argv[2]))
As suggested by others using sys:
from sys import argv
def count(a, b):
return len(a.split(" ")) + len(b.split(" "))
if __name__ == "__main__":
a = argv[1]
b = argv[2]
word_count = count(a, b)
print(word_count)
Or, you could use the built-in module argparse. In case you ever have a more complex script taking arguments from the console.
import argparse
def count(a, b):
return len(a.split(" ")) + len(b.split(" "))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Word Count")
parser.add_argument("-a", type=str, help="First Sentence")
parser.add_argument("-b", type=str, help="Second Sentence")
args = parser.parse_args()
word_count = count(args.a, args.b)
print(word_count)
Execute your script with python counting.py -a "hello world" -b "let's check you out".
And if you execute python counting.py -h, you'll get a nicely formatted help for the users:
usage: counting.py [-h] [-a A] [-b B]
Word Count
optional arguments:
-h, --help show this help message and exit
-a A First Sentence
-b B Second Sentence

return a variable value from a subprocess in python

I have a code workflow in which from a main script(level 0) I call another script through subprocess. This subprocess script (level 1) in turn calls another script as a subprocess. Now from this level 2 subprocess script I want to return the value of a variable upto the main script(level 0). I have tried Popen.communicate() but I am not able to return the value. My current code is as:
main_script.py
def func():
para = ['a','b']
result = subprocess.Popen([sys.executable,"first_subprocess.py"]+para,stdout=subprocess.PIPE)
result.wait()
return_code = result.returncode
out, err = sub_result.communicate()
if return_code == 1:
return return_code
else:
if out is not None:
print 'return value: ', out
if __name__ == '__main__':
func()
From above script is called first_subprocess.py which has:
def some_func():
# some other code and multiple print statements
para = ['a','b']
result = subprocess.Popen([sys.executable,"second_subprocess.py"]+para,stdout=subprocess.PIPE)
result.wait()
out, err = result.communicate()
return_code = sub_result.returncode
if return_code == 0:
return out
if __name__ == '__main__':
some_func()
The second_subprocess.py returns a value like:
def test():
# some other code and multiple print statements
val = 'time'
print 'returning value'
return val
if __name__ == '__main__':
test()
When I try above code I get all the print statements in the codes as an output but not the return value. Even if try to print the variable value in subprocess instead of doing a return it wont serve the purpose because there are multiple print statements.
How can I return the variable value in this case?
UPDATED VERSION:
After #Anthons suggestion I have modifed my first_subprocess.py script and main_script.py as follows:
first_subprocess.py:
def some_func():
try:
key = None
if not (key is None):
para = ['a','b']
result = subprocess.Popen([sys.executable,"second_subprocess.py"]+para,stdout=subprocess.PIPE)
sub_result.wait()
out, err = sub_result.communicate()
return_code = sub_result.returncode
if return_code == 0:
for line in out.splitlines():
if not line.startswith('>>>'):
continue
print line
else:
sys.exit(0)
except:
return 1
Main_script.py:
if out is not None:
for line in out.splitlines():
if not line.startswith('>>>'):
continue
value = line.split(':',1)[1].lstrip()
print 'return value:',value`
When I execute above I get UnboundLocalError: local variable 'value' referenced before assignment at the print value command. It seems if I do not execute the code in level 1 script and do a sys.exit() then out in main script is neither empty nor none but it has some undefined value and thus the value variable doesn't get initialized and throws error
If you just want to return an integer value you can use the exit value. This is not the same a returning from some_func(), you would have to do sys.exit(integer_val).
If you want to return a string like time you should print that ( or write to sys.stdout ) and then in the calling process (level 1) parse the output from the subprocess and print it to its own stdout for level 0 to see it.
In your case level two should do something like:
def test():
# some other code and multiple print statements
val = 'time'
print 'returning value:', val
if __name__ == '__main__':
test()
And at level 1 you would do:
def some_func():
# some other code and multiple print statements
para = ['a','b']
result = subprocess.Popen([sys.executable,"second_subprocess.py"]+para,stdout=subprocess.PIPE)
result.wait()
out, err = result.communicate()
return_code = sub_result.returncode
if return_code == 0:
print out
if __name__ == '__main__':
some_func()
With that main_script.py has something to read from the invocation of your level 1 script.
I normally use subprocess.check_output() for these kind of passing on info. That throws an exception if the called process has a non-zero exit status (i.e. on error). I can also recommend that if the subprocess writes more info than just the variable, you make the output lines easily parseable by returning something unique at the beginning of the line (so you still can use print statements for debugging the individual scripts and get the right value from the output):
Level 2:
def test():
# some other code and multiple print statements
print 'debug: Still going strong'
val = 'time'
print '>>>> returning value:', val
if __name__ == '__main__':
test()
Level 1:
...
out, err = result.communicate()
for line in out.splitlines():
if not line.startswith('>>>>'):
continue
print line
...
Level 0:
...
out, err = result.communicate()
for line in out.splitlines():
if not line.startswith('>>>>'):
continue
try:
value = line.split(':', 1)[1]
except IndexError:
print 'wrong input line', repr(line)
print 'return value: ', value
...
The following files work together. Save them under their indicated names
lvl2.py:
# lvl2
import sys
def test():
# some other code and multiple print statements
print >> sys.stderr, 'argv', sys.argv[1:]
print 'debug: Still going strong'
val = 'time'
print '>>>> returning value:', val
return 0
if __name__ == '__main__':
sys.exit(test())
lvl1.py:
# lvl1.py
import sys
import subprocess
def some_func():
para = ['a','b']
sub_result = subprocess.Popen(
[sys.executable, "lvl2.py" ] + para,
stdout=subprocess.PIPE)
sub_result.wait()
out, err = sub_result.communicate()
return_code = sub_result.returncode
if return_code == 0:
for line in out.splitlines():
if not line.startswith('>>>'):
continue
print line
else:
print >> sys.stderr, 'level 2 exited with' + return_code
sys.exit(0)
if __name__ == '__main__':
sys.exit(some_func())
lvl0.py:
# lvl0
import subprocess
import sys
def func():
para = ['a','b']
result = subprocess.Popen(
[sys.executable, "lvl1.py"] + para,
stdout=subprocess.PIPE)
result.wait()
return_code = result.returncode
out, err = result.communicate()
value = None
if return_code == 0:
for line in out.splitlines():
if not line.startswith('>>>'):
continue
value = line.split(':',1)[1].lstrip()
print
else:
print 'non-zero exit', return_code
print 'return value:', value
if __name__ == '__main__':
func()
Then run python lvl0.py to check the output to be
argv ['a', 'b']
return value: time
Now bring these under your revision control system and start making changes a few lines at a time, every time running python lvl0.py to check what you might have broken. Commit each revision so you can roll back to last "known good" state and slowly bring in the rest of your code.

Performing an action as python script closes

I was wondering if it was possible to perform an action at any given point in a basic python script, so say when it is close. I have the following code to find prime numbers (Just for fun)
number = 1
primelist = []
nonprime = []
while number < 1000:
number += 1
for i in range(number):
if i != 1 and i != number and i !=0:
if number%i == 0:
nonprime.append(number)
else:
primelist.append(number)
nonprimes = open("nonprimes.txt", "w")
for nonprime in set(primelist) & set(nonprime):
nonprimes.write(str(nonprime) + ", ")
nonprimes.close()
So basically i wanted to run the last part as the script is stopped. If this isn't possible is there a way where say i press "space" while the program is running and then it saves the list?
Cheers in advance :)
EDIT:
I've modified the code to include the atexit module as suggested, but it doesn't appear to be working. Here it is:
import time, atexit
class primes():
def __init__(self):
self.work(1)
def work(self, number):
number = 1
self.primelist = []
self.nonprime = []
while number < 20:
time.sleep(0.1)
print "Done"
number += 1
for i in range(number):
if i != 1 and i != number and i !=0:
if number%i == 0:
self.nonprime.append(number)
else:
self.primelist.append(number)
nonprimes = open("nonprimes.txt", "w")
for nonprime in set(self.primelist) & set(self.nonprime):
nonprimes.write(str(nonprime) + ", ")
nonprimes.close()
def exiting(self, primelist, nonprimelist):
primelist = self.primelist
nonprimelist = self.nonprime
nonprimes = open("nonprimes.txt", "w")
for nonprime in set(self.primelist) & set(self.nonprime):
nonprimes.write(str(nonprime) + ", ")
nonprimes.close()
atexit.register(exiting)
if __name__ == "__main__":
primes()
While I'm pretty certain the file object does cleanup and flushes the stuff to file when it is reclaimed. The best way to go about this is to use a with statement.
with open("nonprimes.txt", "w") as nonprimes:
for nonprime in set(primelist) & set(nonprime):
nonprimes.write(str(nonprime) + ", ")
The boiler plate code of closing the file and such is performed automatically when the statement ends.
Python has an atexit module that allows you to register code you want executed when a script exits:
import atexit, sys
def doSomethingAtExit():
print "Doing something on exit"
atexit.register(doSomethingAtExit)
if __name__ == "__main__":
sys.exit(1)
print "This won't get called"

Categories

Resources