Print any error from the script to a file - stderr - python

I have a python program that accepts three command line arguments -f (file1) -n (file2) -o (file3).
The program is structured as follows:
ffile=''
nfile=''
ofile=''
try:
myopts, args = getopt.getopt(sys.argv[1:],"f:n:o:h:")
for x,y in myopts:
if x == '-f':
ffile=y
elif x == '-n':
nfile=y
elif x == '-o':
ofile=y
<do something here with the files>
except:
sys.stderr("err.txt",w)
sys.exit(2)
What I am trying to do is:
If any of the arguments (-f,-n) are missing, then print a corresponding error.
Print the error message - whatever it might be - to a file "err.txt".
However, the err.txt file is empty. I am not sure what is going on. I have seen many questions where stderr is written to a file, but it does not seem to answer my question.

If you are open to changing your approach, I would recommend not trying to re-invent the wheel, and checking out some of the existing python libraries to accomplish this functionality.
There is a library called argparse that does well at parsing out command line arguments and returning appropriate error messages, maybe you would find it helpful.
import argparse
p = argparse.ArgumentParser(description='My Program', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
p.add_argument("-f", type=str, help="-f file", required=True)
p.add_argument("-n", type=str, help="-n file", required=True)
p.add_argument("-o", type=str, help="-o file", default=None)
args = p.parse_args() # will throw error if required -f and -n args not passed
In terms of error logging, you could try using the python logging library. This library can be configured to log information to stdout, text files, etc. and is likely flexible enough for your requirements.

Related

argparse error with parsing required arguments

I have a script saved as workspace.py
import argparse
import os
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('title', type=str, help="Will be displayed as the title")
parser.add_argument('-f', '--folder', help='Point to the folder you want to read from (defaults to current folder in command prompt)', type=str, default=os.getcwd())
args = parser.parse_args()
print(args)
someFunction(args.folder, args.title)
Which I call from terminal with:
workspace.py myTitle
Resulting in the error
workspace.py: error: the following arguments are required: title
I have no idea why this is happening because I supply "myTitle" in the terminal. If I specify a default= for the title argument it works perfectly with that value. The part that is throwing me is it doesn't even get to the print(args) so I cannot see what the program thinks is what, but instead fails at args = parser.parse_args()
I tried to even redo the exact example at: https://docs.python.org/2/howto/argparse.html#introducing-positional-arguments (copied below)
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo", help="echo the string you use here")
args = parser.parse_args()
print args.echo
Running
workspace.py hello
Results in (after adding parenthesis to the print for 3.X)
workspace.py: error: the following arguments are required: echo
Is there something I'm missing? Why does it not just print "hello"? Is there some Python 3 specific syntax I'm missing or something?
I've gotten it to work if I run python workspace.py someString instead of workspace.py someString. I do not understand why this version works, since command prompt obviously recognizes it as Python and runs it correctly until args = parser.parse_args(). There were no errors like 'workspace.py' is not recognized as an internal or external command, operable program or batch file. There was no problem importing modules either. Consider the below command prompt session if you are running into a similar error. Maybe you will simply have to include python in your commands like I have to...
C:\Users\rparkhurst\PycharmProjects\Workspace>workspace.py MyTitle
usage: workspace.py [-h] [-f FOLDER] title
workspace.py: error: the following arguments are required: title
C:\Users\rparkhurst\PycharmProjects\Workspace>python workspace.py MyTitle
Namespace(folder='C:\\Users\\rparkhurst\\PycharmProjects\\Workspace', title='MyTitle')

Displaying help through command line argument

I have a python program that I am running through command line arguments. I have used sys module.
Below is my test.py Python file where I am taking all the args:
if len(sys.argv) > 1:
files = sys.argv
get_input(files)
The get_input method is in another Python file where I have the options defined.
options = {
'--case1': case1,
'--case2': case2,
}
def get_input(arguments):
for file in arguments[1:]:
if file in options:
options[file]()
else:
invalid_input(file)
To run:
python test.py --case1 --case2
My intentions are that I want to show the user all the commands in case they want to read the docs for that.
They should be able to read all the commands like they usually are in all the package for reading help, python test.py --help . With this they should be able to look into all the commands they can run.
How do I do this?
One of the best quality a Python developer can be proud of is to use built-in libraries instead of custom ones. So let's use argparse:
import argparse
# define your command line arguments
parser = argparse.ArgumentParser(description='My application description')
parser.add_argument('--case1', help='It does something', action='store_true')
parser.add_argument('--case2', help='It does something else, I guess', action='store_true')
# parse command line arguments
args = parser.parse_args()
# Accessing arguments values
print('case1 ', args.case1)
print('case2 ', args.case2)
You can now use your cmd arguments like python myscript.py --case1
This comes with a default --help argument you can now use like: python myscript.py --help which will output:
usage: myscript.py [-h] [--case1] [--case2]
My application description
optional arguments:
-h, --help show this help message and exit
--case1 It does something
--case2 It does something else, I guess
Hi you can use option parser and add your options and related help information.
It has by default help option which shows all the available options which you have added.
The detailed document is here. And below is the example.
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-f", "--file", dest="filename",
help="write report to FILE", metavar="FILE")
parser.add_option("-q", "--quiet",
action="store_false", dest="verbose", default=True,
help="don't print status messages to stdout")
(options, args) = parser.parse_args()

python script envoke -h or --help if no options are chosen

Trying to make my script more generic so I added some flags. My problem is the help only works if you type -h , obviously. I want to envoke -h when no flags are selected.
For example:
python 0_log_cleaner.py
Traceback (most recent call last):
File "0_log_cleaner.py", line 51, in <module>
getFiles(options.path,options.org_phrase,options.new_phrase,options.org_AN,options.new_AN,options.dst_path)
File "0_log_cleaner.py", line 37, in getFiles
for filename in os.listdir(path):
TypeError: coercing to Unicode: need string or buffer, NoneType found
but if I add -h I get:
python 0_log_cleaner.py -h
Usage: Example:
python 0_log_cleaner.py --sp original_logs/ --dp clean_logs/ --od CNAME --nd New_CNAME --oan 10208 --nan NewAN
Options:
-h, --help show this help message and exit
--sp=PATH Path to the source logs ie original_logs/
--dp=DST_PATH Path to where sanitized logs will be written to ie
clean_logs
--od=ORG_PHRASE original domain name ie www.clientName.com, use the command
-od clientName
--nd=NEW_PHRASE domain name to replace -od. ie -od clientName -nd domain
makes all log that use to be www.clientName.com into
www.domain.com
--oan=ORG_AN original AN number
--nan=NEW_AN AN number to replace original. ie -oan 12345 -nan AAAA1
replaces all instances of the AN number 12345 with AAAA1
EDIT 3 ANSWER
sample of my code to produce ^
import argparse
import sys
usage = "Description of function"
parser = argparse.ArgumentParser(description=usage)
parser.add_argument("--sp", dest="path", help='Path to the source logs ie logs/')
...
...(additional add arugments)
args = parser.parse_args()
def getFiles(path,org_phrase,new_phrase,org_AN,new_AN,dst_path):
if not len(sys.argv) > 1:
parser.print_help()
else:
run your logic
borrowed from here : Argparse: Check if any arguments have been passed
Here's how the final code looks like:
import argparse
import sys
usage = "Description of function"
parser = argparse.ArgumentParser(description=usage)
parser.add_argument("--sp", dest="path", help='Path to the source logs ie logs/')
...
...(additional add arugments)
args = parser.parse_args()
def getFiles(path,org_phrase,new_phrase,org_AN,new_AN,dst_path):
if not len(sys.argv) > 1:
parser.print_help()
else:
run your logic
If someone is still interested in a (very simple) solution:
parser = argparse.ArgumentParser()
parser.add_argument("jfile", type=str, help="Give the JSON file name.")
parser.add_argument("--output", type=str, help="Type in the final excel files name.")
try:
args = parser.parse_args()
return args
except:
parser.print_help()
My professor wanted the script to force the -h / --help page even when there are too few arguments. Instead of going like "python SCRIPT.py -h".
So what I did here was like: "Try to parse the arguments. And if it works, give them back to the main methode. Otherwise, if you fail (except), print the help(). Okay? Nice". ;)
Without knowing the method you are parsing with, I will assume the following (comment me if I am wrong or edit your question with some code on how you handle your parsing):
You are parsing everything and putting it in a variable. let parsed be that variable.
You are checking parsed for the existence of any of your option flags.
You probably not checking for the non-existence of arguments:
parsed = '' <- empty string
# or if you are using a list:
# parsed = []
if parsed: <- if parsed is not empty ("" or []) returns true
Do your stuff here, because you have options now
else: <- Differently options were not provided
Invoke the same method that you invoke when the option is -h
Also as #dhke suggests, consider using argparse if you are not using it already!
EDIT #1:
Translated for your specific case:
args = parser.parse_args() <-- ending line of your provided code
if not args:
parser.print_help()
else:
Do your stuff

Python code to redirect stdin and stdout to a shell script

I am trying to write a program that reads from a file and writes to a new file using the command line. I am using the CommandLine class and using ArgParse to accept different args as well as the file names for input and output. I am trying to redirect stdin and stdout to these files. However, I keep getting an error that I put at the bottom of the code below. Am I inputting the arguments to the command line incorrectly, or is something else going on? All of my files are in the same folder.
class CommandLine() :
'''
Handle the command line, usage and help requests.
CommandLine uses argparse, now standard in 2.7 and beyond.
it implements a standard command line argument parser with various argument options,
a standard usage and help, and an error termination mechanism do-usage_and_die.
attributes:
all arguments received from the commandline using .add_argument will be
avalable within the .args attribute of object instantiated from CommandLine.
For example, if myCommandLine is an object of the class, and requiredbool was
set as an option using add_argument, then myCommandLine.args.requiredbool will
name that option.
'''
def __init__(self, inOpts=None):
'''
CommandLine constructor.
Implements a parser to interpret the command line argv string using argparse.
'''
import argparse
self.parser = argparse.ArgumentParser(description = 'Program prolog - a brief description of what this thing does',
epilog = 'Program epilog - some other stuff you feel compelled to say',
add_help = True, #default is True
prefix_chars = '-',
usage = '%(prog)s [options] -option1[default] <input >output'
)
self.parser.add_argument('inFile', action = 'store', help='input file name')
self.parser.add_argument('outFile', action = 'store', help='output file name')
self.parser.add_argument('-lG', '--longestGene', action = 'store', nargs='?', const=True, default=False, help='longest Gene in an ORF')
self.parser.add_argument('-mG', '--minGene', type=int, choices= (0, 100, 200, 300, 500, 1000), action = 'store', help='minimum Gene length')
self.parser.add_argument('-s', '--start', action = 'append', nargs='?', help='start Codons') #allows multiple list options
self.parser.add_argument('-st', '--stop', action = 'append', nargs='?', help='stop Codons') #allows multiple list options
self.parser.add_argument('-v', '--version', action='version', version='%(prog)s 0.1')
if inOpts is None :
self.args = self.parser.parse_args()
else :
self.args = self.parser.parse_args(inOpts)
C:\Users\Zach\Documents\UCSC\BME 160\Lab 5>python findORFsCmdLine.py -- minGene=3
00 --longestGene --start=ATG --stop=TAG --stop=TGA --stop=TAA <tass2.fa >result.
txt
usage: findORFsCmdLine.py [options] -option1[default] <input >output
findORFsCmdLine.py: error: the following arguments are required: inFile, outFile
The error tells you that you did not provide inFile and outFile. This seems confusing because you did indeed provide <tass2.fa and >result.txt on the command line.
On the command line < and > have special meaning. <tass2.fa is handled by bash (or whichever shell you use) and it opens the file tass2.fa and sends the contents to your program over stdin. It then removes that from the list of command line arguments because bash has already taken care of it.
A similar thing happens with >result.txt where any output from your file that normally goes to stdout such as print calls will be written to that file. Again bash handles this so your program never gets the argument from the command line.
To make sure the filenames go to your program you need to remove the < and > symbols.
In addition to that, the way you are call add_argument for inFile and outFile is not correct. Using action='store' simply stores the value of the argument under the given name. This is the default action so doing that is redundant.
You need to tell add_argument that these are file types. Then you can use these as files and read and write as you desire.
parser.add_argument('inFile', type=argparse.FileType('r'))
parser.add_argument('outFile', type=argparse.FileType('w'))
If you wish to allow these arguments to be optional and use stdin and stdout automatically if they do not supply a filename then you can do this:
parser.add_argument('inFile', type=argparse.FileType('r'),
nargs='?', default=sys.stdin)
parser.add_argument('outFile', type=argparse.FileType('w'),
nargs='?', default=sys.stdout)
You say you are writing a program that reads from a file and writes to a new file using the command line and go on to say that you are trying to redirect these files to stdin and stdout.
If it is really your intention to redirect the input and output files using the shell with the > and < operators, then you do not need to call add_argument with outFile at all. Just let the shell handle it. Remove that line from your program and use print or similar to send your content to stdout and that file will be created. You will still need the call add_argument('inFile', ....

Call my Chat script from other Python script

This question is similar to other python from within python script calling but none of those are working for me.
I have a chat script which uses Python XMPP to send a chat message. The syntax is as so:
python chat.py -c "recipient#example.com" -u "sender#example.com" -p "secret" -m "message"
script:
#!/usr/bin/python
import sys
import argparse
import xmpp
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument('-c', dest='contact', required=True)
parser.add_argument('-u', dest='username', required=True)
parser.add_argument('-p', dest='password', required=True)
parser.add_argument('-m', dest='message', required=False, default='')
args = parser.parse_args(argv)
if (args.message == '') and not sys.stdin.isatty():
for line in sys.stdin:
args.message = args.message + line
jid = xmpp.protocol.JID(args.username)
jabber = xmpp.Client(jid.getDomain(), debug=[])
jabber.connect(server=(jid.getDomain(), 5222) )
jabber.auth(jid.getNode(), args.password)
jabber.send(xmpp.Message(args.contact, args.message.strip()))
if __name__ == "__main__":
main(sys.argv[1:])
as you can see, it takes 4 arguments.
Now I have another python script which is listening to sensors. I am trying to get it to send chat messages when it detects sensor readings so from within listen.py I am doing this:
...
import chat
...
chat.main('-c "chatto#server.com" -u "chatfrom#server.com" -p "password" -m "Yo Yo Yo Wassup"')
....
I have also tried subprocess.call but perhaps have not gotten the syntax correct. both python scripts are in the same directory. So for those of you looking for a specific question, How can I call the chat.py from within listen.py while providing the four required args?
parse_args() is going to expect a list of strings. For simple ones you can just split() on spaces, but in your case you have a complex argument with internal spaces so that won't work.
Don't include quotes in the strings. Those are used to allow an argument to have internal spaces when defined from the shell, but they aren't sent into argv.
chat.main(['-c', 'chatto#server.com', '-u', 'chatfrom#server.com', '-p', 'password', '-m', 'Yo Yo Yo Wassup'])
See more here: https://docs.python.org/2/library/argparse.html#beyond-sys-argv

Categories