python try...except in handling argument from user input - python

my python file would read two files from command line using argparse.
I want to use 'try...except...' to handle incorrect user input, like non-exist file and incorrect command:
for example, I use '-f' to open file, then '-g' is incorrect command.
my codes are:
> parser = argparse.ArgumentParser(description = "Handle with input
files")
parser.add_argument('-m', dest = "model", type=argparse.FileType('r'))
parser.add_argument('-t', dest = "test", type=argparse.FileType('r'))
parser.add_argument('-d', dest = "permute",type=argparse.FileType('r'))
args = parser.parse_args()
# Parse file names from command line
model_file = open(args.model.name)
test_file = open(args.test.name)
decoy_file = open(args.permute.name)
I try to implement 'try...except' in my code but failed. Here is what I did:
> parser = argparse.ArgumentParser(description = "Handle with input
files")
parser.add_argument('-m', dest = "model", type=argparse.FileType('r'))
parser.add_argument('-t', dest = "test", type=argparse.FileType('r'))
parser.add_argument('-d', dest = "permute",type=argparse.FileType('r'))
args = parser.parse_args()
# Parse file names from command line
try:
model_file = open(args.model.name)
test_file = open(args.test.name)
decoy_file = open(args.permute.name)
except IOError:
print('Wrong file name')

I would suggest adding something like this to make sure all arguments are there. Also argparse.FileType('r') will already open the file (meaning you can do something like args.model.readlines()).
if not all([args.model, args.test, args.permute]):
print("All Arguments are required")
exit(1)

Firstly the error happend at args = parser.parse_args() when it parse your command param.
As we can see from argparse's origin code:
...
def parse_args(self, args=None, namespace=None):
args, argv = self.parse_known_args(args, namespace)
if argv:
msg = _('unrecognized arguments: %s')
self.error(msg % ' '.join(argv))
return args
...
def error(self, message):
"""error(message: string)
Prints a usage message incorporating the message to stderr and
exits.
If you override this in a subclass, it should not return -- it
should either exit or raise an exception.
"""
self.print_usage(_sys.stderr)
args = {'prog': self.prog, 'message': message}
self.exit(2, _('%(prog)s: error: %(message)s\n') % args)
# exit here ,that's why can not catch the exception
So when you input incorrectly, like what you mentioned above, like non-exist file or incorrect command , it will call error function in the end, and exit the thread, that's why you cannot catch a exception by try .. except synax.
If you want to catch the exception, you can inherit the main class ArgumentParser, and rewrite the method error. you can take my codes as reference:
import argparse
import sys as _sys
from argparse import ArgumentParser
class ArgumentParserSub(ArgumentParser):
def error(self, message):
self.print_usage(_sys.stderr)
args = {'prog': self.prog, 'message': message}
raise Exception(('error: %(message)s\n') % args)
parser = ArgumentParserSub(description = "Handle with input files")
parser.add_argument('-m', dest = "model", type=argparse.FileType('r'))
parser.add_argument('-t', dest = "test", type=argparse.FileType('r'))
parser.add_argument('-d', dest = "permute",type=argparse.FileType('r'))
try:
args = parser.parse_args()
# Parse file names from command line
model_file = open(args.model.name)
test_file = open(args.test.name)
decoy_file = open(args.permute.name)
except Exception as e:
print(e)
print('Wrong file name')

If you don't want the parser to catch and exit bad files, open the files yourself. Just ask the parser for names, not open files.
parser = argparse.ArgumentParser(description = "Handle with input files")
parser.add_argument('-m', dest = "model") # no type
parser.add_argument('-t', dest = "test")
parser.add_argument('-d', dest = "permute")
args = parser.parse_args()
# print(args) # good idea when debugging
# Open file names from command line
try:
model_file = open(args.model)
test_file = open(args.test)
decoy_file = open(args.permute)
except IOError:
print('Wrong file name')

Related

How to change python program to write output into a file?

I used the "pdf2txt.py" program which came as part of the pdfminer package in GitHub to try convert pdf file to text.As per the instruction , I ran the program by typing "python pdf2txt.py somefile.pdf" in the Mac OS terminal.The output was correctly generated and printed in the terminal itself. Now my question is this, how do I direct this output to a text file.I only know the bare basics of python and I am not able to figure out which line in the program actually prints the output and what needs to be changed to direct the same into a .txt file?
import argparse
import logging
import six
import sys
import pdfminer.settings
pdfminer.settings.STRICT = False
import pdfminer.high_level
import pdfminer.layout
from pdfminer.image import ImageWriter
def extract_text(files=[], outfile='-',
_py2_no_more_posargs=None, # Bloody Python2 needs a shim
no_laparams=False, all_texts=None, detect_vertical=None, # LAParams
word_margin=None, char_margin=None, line_margin=None, boxes_flow=None, # LAParams
output_type='text', codec='utf-8', strip_control=False,
maxpages=0, page_numbers=None, password="", scale=1.0, rotation=0,
layoutmode='normal', output_dir=None, debug=False,
disable_caching=False, **other):
if _py2_no_more_posargs is not None:
raise ValueError("Too many positional arguments passed.")
if not files:
raise ValueError("Must provide files to work upon!")
# If any LAParams group arguments were passed, create an LAParams object and
# populate with given args. Otherwise, set it to None.
if not no_laparams:
laparams = pdfminer.layout.LAParams()
for param in ("all_texts", "detect_vertical", "word_margin", "char_margin", "line_margin", "boxes_flow"):
paramv = locals().get(param, None)
if paramv is not None:
setattr(laparams, param, paramv)
else:
laparams = None
imagewriter = None
if output_dir:
imagewriter = ImageWriter(output_dir)
if output_type == "text" and outfile != "-":
for override, alttype in ( (".htm", "html"),
(".html", "html"),
(".xml", "xml"),
(".tag", "tag") ):
if outfile.endswith(override):
output_type = alttype
if outfile == "-":
outfp = sys.stdout
if outfp.encoding is not None:
codec = 'utf-8'
else:
outfp = open(outfile, "wb")
for fname in files:
with open(fname, "rb") as fp:
pdfminer.high_level.extract_text_to_fp(fp, **locals())
return outfp
def maketheparser():
parser = argparse.ArgumentParser(description=__doc__, add_help=True)
parser.add_argument("files", type=str, default=None, nargs="+", help="File to process.")
parser.add_argument("-d", "--debug", default=False, action="store_true", help="Debug output.")
parser.add_argument("-p", "--pagenos", type=str, help="Comma-separated list of page numbers to parse. Included for legacy applications, use --page-numbers for more idiomatic argument entry.")
parser.add_argument("--page-numbers", type=int, default=None, nargs="+", help="Alternative to --pagenos with space-separated numbers; supercedes --pagenos where it is used.")
parser.add_argument("-m", "--maxpages", type=int, default=0, help="Maximum pages to parse")
parser.add_argument("-P", "--password", type=str, default="", help="Decryption password for PDF")
parser.add_argument("-o", "--outfile", type=str, default="-", help="Output file (default \"-\" is stdout)")
parser.add_argument("-t", "--output_type", type=str, default="text", help="Output type: text|html|xml|tag (default is text)")
parser.add_argument("-c", "--codec", type=str, default="utf-8", help="Text encoding")
parser.add_argument("-s", "--scale", type=float, default=1.0, help="Scale")
parser.add_argument("-A", "--all-texts", default=None, action="store_true", help="LAParams all texts")
parser.add_argument("-V", "--detect-vertical", default=None, action="store_true", help="LAParams detect vertical")
parser.add_argument("-W", "--word-margin", type=float, default=None, help="LAParams word margin")
parser.add_argument("-M", "--char-margin", type=float, default=None, help="LAParams char margin")
parser.add_argument("-L", "--line-margin", type=float, default=None, help="LAParams line margin")
parser.add_argument("-F", "--boxes-flow", type=float, default=None, help="LAParams boxes flow")
parser.add_argument("-Y", "--layoutmode", default="normal", type=str, help="HTML Layout Mode")
parser.add_argument("-n", "--no-laparams", default=False, action="store_true", help="Pass None as LAParams")
parser.add_argument("-R", "--rotation", default=0, type=int, help="Rotation")
parser.add_argument("-O", "--output-dir", default=None, help="Output directory for images")
parser.add_argument("-C", "--disable-caching", default=False, action="store_true", help="Disable caching")
parser.add_argument("-S", "--strip-control", default=False, action="store_true", help="Strip control in XML mode")
return parser
# main
def main(args=None):
P = maketheparser()
A = P.parse_args(args=args)
if A.page_numbers:
A.page_numbers = set([x-1 for x in A.page_numbers])
if A.pagenos:
A.page_numbers = set([int(x)-1 for x in A.pagenos.split(",")])
imagewriter = None
if A.output_dir:
imagewriter = ImageWriter(A.output_dir)
if six.PY2 and sys.stdin.encoding:
A.password = A.password.decode(sys.stdin.encoding)
if A.output_type == "text" and A.outfile != "-":
for override, alttype in ( (".htm", "html"),
(".html", "html"),
(".xml", "xml" ),
(".tag", "tag" ) ):
if A.outfile.endswith(override):
A.output_type = alttype
if A.outfile == "-":
outfp = sys.stdout
if outfp.encoding is not None:
# Why ignore outfp.encoding? :-/ stupid cathal?
A.codec = 'utf-8'
else:
outfp = open(A.outfile, "wb")
## Test Code
outfp = extract_text(**vars(A))
outfp.close()
return 0
if __name__ == '__main__': sys.exit(main())
Try
python pdf2txt.py somefile.pd > pdf_text.txt
Thank you everyone for the inputs.
I added a line
sys.stdout=open("somefile.txt","w")
right before the line
outfp=sys.stdout.
worked like a charm.

python 2.6 linecache.getline() and stdin. How does it work?

I have a script which runs through lines of input to find the occurrence of an ID string while keeping track of the linenumber.
Then it runs backwards up the input to trace parentID/childID relationships. The script accepts either a logfile using a '-f' flag as an argument or the contents of stdin from a pipe.
The logfile as input portion works just fine, but reading from stdin seems not to work.
For the sake of reasonable clarity I've included the portion of the script that this concerns, but don't expect to be able to run it. It's just to show you sorta whats going on (anyone who works in financial services around FIX protocol would recognize a few things):
import os
import sys
import linecache
from types import *
from ____ import FixMessage # custom message class that is used throughout
# Feel free to ignore all the getArgs and validation crap
def getArgs():
import argparse
parser = argparse.ArgumentParser(
description='Get amendment history.')
parser.add_argument('-f', '--file',
help="input logfile.'")
args = parser.parse_args()
return validateArgs(args)
def validateArgs(args):
try:
if sys.stdin.isatty():
if args.file:
assert os.path.isfile(args.file.strip('\n')), \
'File "{0}" does not exist'.format(args.file)
args.file = open(args.file, 'r')
else:
args.file = sys.stdin
assert args.file, \
"Please either include a file with '-f' or pipe some text in"
except AssertionError as err:
print err
exit(1)
return args
defGetMessageTrail(logfile, orderId):
# some input validation
if isinstance(logfile, StringType):
try: logfile = open(logfile, 'r')
except IOError as err: exit(1)
elif not isinstance(logfile, FileType):
raise TypeError(
'Expected FileType and got {0}'.format(type(logfile)))
linenum = 0
# This retrieves the message containing the orderID as well as the linenum
for line in logfile:
linenum += 1
if orderId in line:
# FixMessage is a custom class that is treated here like
# a dictionary with some metadata
# Missing dict keys return 'None'
# .isvalid is bool results of some text validation
# .direction is either incoming or outgoing
# thats all you really need to know
msg = FixMessage(line)
if msg.isvalid and msg.direction == 'Incoming':
yield msg
break
# If there is a message parentID, it would be in msg['41']
if msg['41']:
messages = findParentMessages(logfile, startline=linenum, msg['41'])
for msg in messages: yield msg
def findParentMessages(logfile, startline, targetId):
# Some more input validation
assert isinstance(logfile, FileType)
assert isinstance(startline, IntType)
assert isinstance(targetId, StringType)
# should just make a integer decrementing generator,
# but this is fine for the example
for linenum in range(startline)[::-1]:
# *** This is where the question lies... ***
# print(logfile.name) # returns "<stdin>"
line = linecache.getline(logfile.name, linenum)
if 'Incoming' in line and '11=' + targetId in line:
msg = FixMessage(line)
yield msg
if msg['41']: findParentMessages(logfile, linenum, msg['41'])
else: break
def main():
log = getArgs().file
trail = getMessageTrail(log, 'ORDER123')
if __name__ == '__main__': main()
The question is, how does linecache.getline work when it comes to reading stdin as a file? is it different than how it would work if given a regular filename?
linecache.getline() accepts a file name, not a file object. It is not designed to work that way as filename is passed to calls like open() and os.stat().
For reference: https://github.com/python/cpython/blob/2.6/Lib/linecache.py

Python Arguments, Required 1 Argument

I am working on a zip file password cracker, and I need a brute-force option. I have the basic code for it written out, but I am not sure of how to use an argument to make it work. How can I write an argument to make this work?
import optparse
import zipfile
import argparse
from threading import Thread
parser = argparse.ArgumentParser()
parser.add_argument("-b", "--brute")
def extract_zip(zFile, password):
try:
password_encoded = bytes(password.encode('utf-8'))
zFile.setpassword(password_encoded)
zFile.testzip()
print ("[+] Password Found: " + password + '\n')
except:
pass
def Main():
parser = optparse.OptionParser("useage &prog "+\
"-f <zipfile> -d <dictionary> / -b <brute force>")
parser.add_option('-f', dest='zname', type='string',\
help='specify zip file')
parser.add_option('-d', dest='dname', type='string',\
help='specify dictionary file')
parser.add_option('-b', dest='bname', type='string',\
help='specify brute force')
(options, arg) = parser.parse_args()
if (options.zname == None) | (options.dname == None) | (options.bname == None):
print (parser.usage)
exit(0)
else:
zname = options.zname
dname = options.dname
bname = options.bname
zFile = zipfile.ZipFile(zname)
passFile = open(dname)
brute = open(bname)
for line in passFile.readlines():
password = line.strip('\n')
t = Thread(target=extract_zip, args=(zFile, password))
t.start()
if args.brute:
characters = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
for length in range(1, int(args.brute)):
to_attempt = product(characters, repeat=length)
t = Thread(target=extract_zip, args=(zFile, password))
t.start()
if __name__ == '__main__':
Main()

Python script leaves command line stuck

I wrote a Python script to let me call previously written functions in other classes directly from the command line. It works fine -- once. But the script seems like it never finishes running, and I can't input anything into command prompt after running it without closing that command prompt window and opening a new one. All my code is below:
import yaml
import numpy as np
from robot_move import UR3
import argparse
import inspect
def moveReactors():
_, success = rob.waypointMove(store, waypoint)
if success:
success = rob.pickplacexyz(store,[0,0,0],disp)
if success:
success = rob.pickplace(store,goal)
return success
#setup variables from the config file
with open('robot_config.txt') as f:
config = yaml.load(f)
IP = config['IP']
store = np.array(config['reactorStorePose'])
disp = np.array(config['reactor1Disp'])
goal = np.array(config['reactorGoalPose'])
waypoint = np.array(config['reactorWaypointAngles'])
rob = UR3(IP)
# get the names of all the functions to be passed to the parser
selfFuncNames = [name for name in dir() if inspect.isfunction(globals().get(name))]
robFuncNames = [name for name in dir(rob) if inspect.ismethod(getattr(rob,name)) and not name[0] == '_']
#parse the command line input for functions to be called
parser = argparse.ArgumentParser(description="Command the robot")
for funcName in robFuncNames+selfFuncNames:
parser.add_argument('-'+funcName, nargs = '*')
called = {funcName:args for funcName, args in vars(parser.parse_args()).items() if not args is None}
# call each function with its arguments
for funcName in called:
if funcName in robFuncNames:
func = getattr(rob,funcName)
elif funcName in selfFuncNames:
func = globals().get(funcName)
#funcArgs, funcVarargs, funcKeywords, funcDefaults = inspect.getargspec(func)
args = called[funcName]
args = [globals().get(arg) if arg in globals() else arg for arg in args] #let user pass in the names of arguments in this scripts' namespace
try:
if len(args) > 0:
print func(*args)
else:
print func()
except Exception as exc:
raise Exception ('Command "' + funcName + '" failed: ' +str(exc) )
print ('Success!')

parse command line arguments not reading all arguments?

So, I came across the getopt module to parse command line args, although I can't make any sense of the docs. For whatever reason, I cannot figure out why this isn't seeing my --domain example.com argument..
$ ./httpdsetup.py -a -u zack --domain example.com
[('-a', ''), ('-u', '')]
I printed out what gets dumped into opts to see what it saw. The code below is just about an exact duplicate from the documentation site.
def main(argv):
import getopt
try:
opts, args = getopt.getopt(argv, "h:al:ud:v", ["user=", "apache", "lighttpd", "dir=", "domain=", "vhost="])
except getopt.GetoptError:
print_usage()
sys.exit(2)
username = ''
directory = ''
domain = ''
httpd = 'apache'
print(opts)
for opt, arg in opts:
if opt == '-h':
print_usage()
sys.exit()
elif opt in ('-u', '--username'):
username = arg
elif opt in ('-d', '--dir'):
directory = arg
elif opt in ('-v', '--domain', '--vhost'):
domain = arg
elif opt in ('-a', '--apache'):
httpd = 'apache'
elif opt in ('-l', '--lighttpd'):
httpd = 'lighttpd'
else:
print_usage()
sys.exit()
if httpd == 'apache':
create_apache_vhost(domain, directory, username)
elif httpd == 'lighttpd':
create_lighty_vhost(domain, directory, username)
if __name__ == '__main__':
main(sys.argv[1:])
I prefer argparse. Python documention here.
It's in Python >=2.7 and >=3.2, but not in Python 3.0 and 3.1. If it's missing in your install, just copy the single file from here to where your script is, or into your Python install.
Here's something close to your example with argparse:
#!/usr/bin/env python3
import sys
def create_apache_vhost(*args, **kwargs):
pass
def create_lighty_vhost(*args, **kwargs):
pass
def main(argv):
import argparse
parser = argparse.ArgumentParser(description="Some server",
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('--username', type=str)
parser.add_argument('-u', dest='username', type=str)
parser.add_argument('--apache', dest='httpd', action='store_const', const='apache')
parser.add_argument('-a', dest='httpd', action='store_const', const='apache')
parser.add_argument('--lighthttpd', dest='httpd', action='store_const', const='lighthttpd')
parser.add_argument('-l', dest='httpd', action='store_const', const='lighthttpd')
parser.add_argument('--domain', type=str)
parser.add_argument('--vhost', type=str)
parser.add_argument('-v', dest='domain', type=str)
parser.add_argument('--dir', dest='directory', type=str)
parser.add_argument('-d', dest='directory', type=str)
defaults = {
'httpd': 'apache',
}
parser.set_defaults(**defaults)
args = parser.parse_args(args=argv)
print(args)
if args.httpd == 'apache':
create_apache_vhost(args.domain, args.directory, args.username)
elif args.httpd == 'lighttpd':
create_lighty_vhost(args.domain, args.directory, args.username)
if __name__ == '__main__':
main(sys.argv[1:])

Categories