Why is the input string read by OptParser? - python

I am currently creating a launcher (launcher.py) in Python 2.7 which can be used from GUI and terminal input. The launcher combines already eisting Software, that has its own GUI and its own terminal mode.
I added an OptionParser (option.py) to start the embedded tools (app.py) directly from the launcher.
App.py executes and is generating correct results, BUT terminal always throws following error:
Usage: launcher.py [options] arg1 arg2
launcher.py: error: no such option -i
here is the option.py file: (is imported as module to launcher.py)
def execute():
tapp_object = tapp.TALP()
input_file = sys.argv[sys.argv.index('-input') + 1]
tapp_object.run(gui=False, filename=input_file)
class Options(OptionParser):
usage = "Usage: %prog arg1 "
parser = OptionParser(usage=usage)
def tapp_batch(option, opt, value, parser):
p = mp.Process(target=execute)
p.start()
p.join()
parser.add_option('--tapp',
'--tAPPLICATION',
action="callback",
callback=tapp_batch,
help='''start APP from batch mode using:
-launcher.py --tapp -input filename.yml
add filename.yml file containing''' )
i tried to do an dirty fix, calling a 'pass' function for flag -i but then it throws the same error for -n -->
Usage: launcher.py [options] arg1 arg2
launcher.py: error: no such option -n
reading full -input string. Why ? and how do I fix it. Please help.

Related

How to call an equivalent command to strace on mac -- ideally from python?

I inherited this code and in it calls strace from within python. In particular it calls:
def strace_build(executable: str,
regex: str,
workdir: Optional[str],
command: List[str],
strace_logdir=None) -> List[str]:
''' trace calls of executable during access to files that match regex
in workdir while executing the command and returns the list of pycoq_context
file names
In the simplest case strace runs the specified command until it
exits. It intercepts and records the system calls which are
called by a process and the signals which are received by a
process. The name of each system call, its arguments and its
return value are printed on standard error or to the file
specified with the -o option.
https://stackoverflow.com/questions/73724074/how-to-call-an-equivalent-command-to-strace-on-mac-ideally-from-python
'''
print('---- Calling strace_build ----')
def _strace_build(executable, regex, workdir, command, logdir):
logfname = os.path.join(logdir, 'strace.log')
logging.info(f"pycoq: tracing {executable} accesing {regex} while "
f"executing {command} from {workdir} with "
f"curdir {os.getcwd()}")
print(f"pycoq: tracing {executable} accesing {regex} while "
f"executing {command} from {workdir} with "
f"curdir {os.getcwd()}")
with subprocess.Popen(['strace', '-e', 'trace=execve',
'-v', '-ff', '-s', '100000000',
'-xx', '-ttt',
'-o', logfname] + command,
cwd=workdir,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE) as proc:
for line in iter(proc.stdout.readline, ''):
logging.debug(f"strace stdout: {line}")
print(f"strace stdout: {line=}")
logging.info(f"strace stderr: {proc.stderr.read()}"
"waiting strace to finish...")
proc.wait()
logging.info('strace finished')
res: list[str] = parse_strace_logdir(logdir, executable, regex)
print('---- Done with strace_build ----')
return res
if strace_logdir is None:
with tempfile.TemporaryDirectory() as _logdir:
return _strace_build(executable, regex, workdir, command, _logdir)
else:
os.makedirs(strace_logdir, exist_ok=True)
strace_logdir_cur = tempfile.mkdtemp(dir=strace_logdir)
return _strace_build(executable, regex, workdir, command, strace_logdir_cur)
but because it calls strace it only works on linux. I want it to work on my mac -- ideally if possible in the most pythonic way possible. I believe what it does is strace a terminal command that is called from within python.
What would be an equivalent way to call this command on mac using the same flags so that it works (ideally exactly) the same?
Not sure if this matters but I am using an m1 mac.
some output when the above function is used:
--done with make attempt--
---- Calling strace_build ----
pycoq: tracing /home/bot/.opam/ocaml-variants.4.07.1+flambda_coq-serapi.8.11.0+0.11.1/bin/coqc accesing .*\.v$ while executing ['opam', 'reinstall', '--yes', '--switch', 'ocaml-variants.4.07.1+flambda_coq-serapi.8.11.0+0.11.1', '--keep-build-dir', 'debug_proj'] from None with curdir /home/bot
strace stdout: line='\n'
strace stdout: line='<><> Synchronising pinned packages ><><><><><><><><><><><><><><><><><><><><><><>\n'
strace stdout: line='[debug_proj.~dev] no changes from file:///home/bot/iit-term-synthesis/coq_projects/debug_proj\n'
strace stdout: line='\n'
strace stdout: line='debug_proj is not installed. Install it? [Y/n] y\n'
strace stdout: line='Sorry, no solution found: there seems to be a problem with your request.\n'
strace stdout: line='\n'
strace stdout: line='No solution found, exiting\n'
---- Done with strace_build ----
...
---- Calling strace_build ----
pycoq: tracing /home/bot/.opam/ocaml-variants.4.07.1+flambda_coq-serapi.8.11.0+0.11.1/bin/coqc accesing .*\.v$ while executing ['make', '-C', '/home/bot/iit-term-synthesis/coq_projects/debug_proj'] from None with curdir /home/bot
strace stdout: line="make: Entering directory '/home/bot/iit-term-synthesis/coq_projects/debug_proj'\n"
strace stdout: line='coq_makefile -f _CoqProject -o CoqMakefile\n'
strace stdout: line='make --no-print-directory -f CoqMakefile \n'
strace stdout: line='COQDEP VFILES\n'
strace stdout: line='COQC debug_0_plus_n_eq_n.v\n'
strace stdout: line='COQC debug1_n_plus_1_greater_than_n.v\n'
strace stdout: line='COQC debug2_n_plus_0_eq_n.v\n'
strace stdout: line="make: Leaving directory '/home/bot/iit-term-synthesis/coq_projects/debug_proj'\n"
---- Done with strace_build ----
def strace_build_mac_m1(executable: str,
regex: str,
workdir: Optional[str],
command: List[str],
strace_logdir=None) -> List[str]:
''' trace calls of executable during access to files that match regex
in workdir while executing the command and returns the list of pycoq_context
file names
In the simplest case strace runs the specified command until it
exits. It intercepts and records the system calls which are
called by a process and the signals which are received by a
process. The name of each system call, its arguments and its
return value are printed on standard error or to the file
specified with the -o option.
https://stackoverflow.com/questions/73724074/how-to-call-an-equivalent-command-to-strace-on-mac-ideally-from-python
plan:
- get the command we are running
- pip push my pycoq with no name changes so code doesn't break
- pull the rest of the repos needed, I don't think anything else since lf is here
- harcode test
- actually, look at commands...we need to provide for reproducibility a way to install opam and all this stuff
without docker but in the mac since we are trying to do a mac install. Argh...
COMMANDS:
pycoq: tracing /home/bot/.opam/ocaml-variants.4.07.1+flambda_coq-serapi.8.11.0+0.11.1/bin/coqc accesing .*\.v$ while executing ['opam', 'reinstall', '--yes', '--switch', 'ocaml-variants.4.07.1+flambda_coq-serapi.8.11.0+0.11.1', '--keep-build-dir', 'lf'] from None with curdir /home/bot
executable='/home/bot/.opam/ocaml-variants.4.07.1+flambda_coq-serapi.8.11.0+0.11.1/bin/coqc'
regex='.*\\.v$'
workdir=None
command=['opam', 'reinstall', '--yes', '--switch', 'ocaml-variants.4.07.1+flambda_coq-serapi.8.11.0+0.11.1', '--keep-build-dir', 'lf']
curdir: os.getcwd()='/home/bot'
'''
print('---- Calling strace_build_mac_m1 ----')
def _strace_build(executable, regex, workdir, command, logdir):
logfname = os.path.join(logdir, 'strace.log')
logging.info(f"pycoq: tracing {executable} accesing {regex} while "
f"executing {command} from {workdir} with "
f"curdir {os.getcwd()}")
print(f"pycoq: tracing {executable} accesing {regex} while "
f"executing {command} from {workdir} with "
f"curdir {os.getcwd()}")
print(f'{executable=}')
print(f'{regex=}')
print(f'{workdir=}')
print(f'{command=}')
print(f'curdir: {os.getcwd()=}')
with subprocess.Popen(['dtruss', '-e', 'trace=execve',
'-v', '-ff', '-s', '100000000',
'-xx', '-ttt',
'-o', logfname] + command,
cwd=workdir,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE) as proc:
for line in iter(proc.stdout.readline, ''):
logging.debug(f"strace stdout: {line}")
print(f"strace stdout: {line=}")
logging.info(f"strace stderr: {proc.stderr.read()}"
"waiting strace to finish...")
proc.wait()
logging.info('strace finished')
res: list[str] = parse_strace_logdir(logdir, executable, regex)
print('---- Done with strace_build_mac_m1 ----')
return res
if strace_logdir is None:
with tempfile.TemporaryDirectory() as _logdir:
return _strace_build(executable, regex, workdir, command, _logdir)
else:
os.makedirs(strace_logdir, exist_ok=True)
strace_logdir_cur = tempfile.mkdtemp(dir=strace_logdir)
return _strace_build(executable, regex, workdir, command, strace_logdir_cur)
# -
def code_for_mac_m1():
coq_package = 'lf'
coq_package_pin = '~/pycoq/pycoq/test/lf'
coq_package_pin = os.path.expanduser(coq_package_pin)
print(f'coq_package: {coq_package=}')
print(f'coq_package_pin: {coq_package_pin=}')
### pycoq: tracing /home/bot/.opam/ocaml-variants.4.07.1+flambda_coq-serapi.8.11.0+0.11.1/bin/coqc accesing .*\.v$ while executing ['opam', 'reinstall', '--yes', '--switch', 'ocaml-variants.4.07.1+flambda_coq-serapi.8.11.0+0.11.1', '--keep-build-dir', 'lf'] from None with curdir /home/bot
# executable='/home/bot/.opam/ocaml-variants.4.07.1+flambda_coq-serapi.8.11.0+0.11.1/bin/coqc'
# regex='.*\\.v$'
# workdir=None
# command=['opam', 'reinstall', '--yes', '--switch', 'ocaml-variants.4.07.1+flambda_coq-serapi.8.11.0+0.11.1', '--keep-build-dir', 'lf']
# curdir: os.getcwd()='/home/bot'
# - get the filename in split
# path2filenames: list[str] = pycoq.opam.opam_strace_build(coq_proj, coq_proj_pin)
path2filenames_raw: list[str] = strace_build_mac_m1()
path2filenames_raw.sort()
print(f'\n====----> Populate coq pkg/proj data with files: {path2filenames_raw=}')
if __name__ == '__main__':
code_for_mac_m1()
print('Done!\n\a')
why doesn't this meet the SO guidelines? I am not asking for a software request clearly. From the discussion it's much harder to run in python + having the mac setup as needed + the tool with the equivalent flags to strace for mac.
google for replacement (analogue) of functionality of strace for Mac OS and modify the parser that parses output of strace for that “mac-strace”
The strace functionality is used to inspect the Coq building system to record all options and arguments and environment for coqc in which each individual .v file has be processed by coqc during the build process. The Coq build system is complicated, and i didn’t parse but resolved to just observing by strace of what the actual Coq does and strace simply records all options and arguments (like -R etc) of what the actual Coq does so that pycoq could call coqc with exactly the same options and arguments
related:
cross, unix reddit: https://www.reddit.com/r/unix/comments/y2ghlk/how_to_call_an_equivalent_command_to_strace_on/
cross, apple devs reddit: https://www.reddit.com/r/appledevelopers/comments/y35oeu/how_to_call_an_equivalent_command_to_strace_on/
The short and simple answer is that the security model of MacOS is inherently different. The standard tool for tracing system calls (and much, much more) on BSD-based systems is dtruss, but it's quite different from strace; for starters, it requires you to be root to even start exploring what's possible.
Since it's not clear if that's a route you want to take, I will just briefly link to a few resources for further information.
https://etcnotes.com/posts/system-call/ - very brief, Medium-style quick intro
Mac OSX: Using dtruss? has some details and amplifications (I see you already commented there)
http://dtrace.org/blogs/brendan/2011/10/10/top-10-dtrace-scripts-for-mac-os-x/ is a more detailed intro; the author has many more posts on the topic on their personal blog
https://mohit.io/blog/fs_usage-trace-file-system-calls-on-mac-os-x/ alleges that they have a tool which is similar to strace but the name suggests that it's specific to file system calls; I have not explored this further. There's a related command sc_usage for which I was however unable to find any further information by quick Duck Duck Going.
So, in very brief, it is unlikely that you can easily extend this code to be portable between Linux and MacOS.
Perhaps a plausible alternative would be to extend the software you are attempting to strace to instead provide some sort of internal debugging mode to emit an event or print a message each time it dispatches an "interesting" system call. Depending on what's "interesting", this can be anywhere from moderately trivial to extremely challenging.
Another possible avenue to explore would be a wrapper for the system calls you are interested in. You could simply print out the arguments, then proceed to call the proper system call. See e.g. DYLD_LIBRARY_PATH & DYLD_INSERT_LIBRARIES not working (The equivalent facility on Linux is called LD_PRELOAD.)
I have no experience in this, but I'll try my best.
You can use the DTrace.
Here are some commands from Oracle docs - DTrace command line:
dtrace [-CeFGhHlqSvVwZ]
[-b bufsz] [-c command] [-D name[=value]] [-I pathname] [-L pathname]
[-o pathname] [-p PID] [-s source_pathname]
[-U name] [-x option[=value]][-X[a|c|s|t]]
[-P provider[[predicate]action]]
[-m [[provider:]module[[predicate]action]]]
[-f [[provider:]module:]function[[predicate]action]]
[-n [[[provider:]module:]function:]name[[predicate]action]]
[-i probe-id[[predicate]action]]
Now apparently this only operates in the D-language, but there are many githubs online that can handle this with python.
There is this particular Github by #paulross, where the preparation of creating a dtrace version of Python and a virtual environment in the ~/venvs directory (or wherever you prefer):
cd ~/tmp
curl -o Python-3.7.0.tgz https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tgz
tar -xzf Python-3.7.0.tgz
cd Python-3.7.0
./configure --with-dtrace
make
python.exe -m venv ~/venvs/dtrace
Unfortunately I cannot provide the exact code using dtrace that is used with strace due to my inexperience in this.
Here's an enormously long and comprehensive book on DTrace
Please check the following two scripts based on this book. I believe these two combined will give you a similar result as the original code.
I fixed the script to be compatible to new version. It runs on my M1. Not sure if I am getting the expected output. Please read the book for more information.
#openproc.py ( Fixed to newer_version)
#!/usr/bin/env python
import re
import sys
# ----------------------------
# openproc.py - Collect data from opentrace.py and merge :entry and :return →
# Open trace file or use stdin
try:
inf = file(sys.argv[1], 'r')
except OSError as ose:
print(ose)
print('''openproc.py [filename]''')
sys.exit(1)
except IndexError:
inf = sys.stdin
# Convert time to human time
def human_time(ns):
ns = float(ns)
for unit in ['ns', 'us', 'ms']:
if abs(ns) == 0:
print('ERROR %d' % ret)
else:
print('OPEN %s %d %s => %s [%s]' % (users.get(uid, str(uid)),
pid, state[pid][1], status,
human_time(tm - state[pid][0])))
del state[pid]
#opentrace.py (Fixed syntax)
#!/usr/bin/env python
import sys, os, subprocess, platform
from optparse import OptionParser
# ----------------------------
# opentrace.py - Trace open syscalls via SystemTap or DTrace
# supports filtering per UID or PID
optparser = OptionParser()
optparser.add_option('-S', '--stap', action='store_true',
dest='systemtap', help='Run SystemTap')
optparser.add_option('-D', '--dtrace', action='store_true',
dest='dtrace', help='Run DTrace')
optparser.add_option('-p', '--pid', action='store', type='int',
dest='pid', default='-1', metavar='PID',
help='Trace process with specified PID')
optparser.add_option('-u', '--uid', action='store', type='int',
dest='uid', default='-1', metavar='UID',
help='Filter traced processes by UID')
optparser.add_option('-c', '--command', action='store', type='string',
dest='command', metavar='CMD',
help='Run specified command CMD and trace it')
(opts, args) = optparser.parse_args()
if opts.pid >= 0 and opts.command is not None:
optparser.error('-p and -c are mutually exclusive')
if (opts.pid >= 0 or opts.command is not None) and opts.uid >= 0:
optparser.error('-p or -c are mutually exclusive with -u')
if opts.systemtap and opts.dtrace:
optparser.error('-S and -D are mutually exclusive')
if not opts.systemtap and not opts.dtrace:
# Try to guess based on operating system
opts.systemtap = sys.platform == 'linux2'
opts.dtrace = sys.platform == 'sunos5'
if not opts.systemtap and not opts.dtrace:
optparser.error('DTrace or SystemTap are non-standard for your platform,please specify -S or -D option')
def run_tracer(entry, ret, cond_proc, cond_user, cond_default,
env_bin_var, env_bin_path,
opt_pid, opt_command, args, fmt_probe):
cmdargs = [os.getenv(env_bin_var, env_bin_path)]
if opts.pid >= 0:
cmdargs.extend([opt_pid, str(opts.pid)])
entry['cond'] = ret['cond'] = cond_proc
elif opts.command is not None:
cmdargs.extend([opt_command, opts.command])
entry['cond'] = ret['cond'] = cond_proc
elif opts.uid >= 0:
entry['cond'] = ret['cond'] = cond_user % opts.uid
else:
entry['cond'] = ret['cond'] = cond_default
cmdargs.extend(args)
proc = subprocess.Popen(cmdargs, stdin=subprocess.PIPE)
proc.stdin.write(fmt_probe % entry)
proc.stdin.write(fmt_probe % ret)
proc.stdin.close()
proc.wait()
if opts.systemtap:
entry = {'name': 'syscall.open',
'dump': '''printf("=> uid: %d pid: %d open: %s %d\\n",
uid(), pid(), filename, gettimeofday_ns());'''}
ret = {'name': 'syscall.open.return',
'dump': '''printf(" uid: %%d pid: %%d open: %%s %%lld\\n",
uid, pid, copyinstr(%s), (long long) timestamp); ''' % fn_arg}

Option Parser does not match argument

I have a simple python (v2.7) script (test.py)
#!/usr/bin/python
import sys
from optparse import OptionParser
def main():
parser = OptionParser()
parser.add_option("--files", dest="files",
metavar="FILES", default=None,
help="A file pattern matching ottcall logs.")
(options, args) = parser.parse_args()
print "FILES_PATTERN %s" % options.files
if not options.files:
parser.error("Files_pattern option is mandatory - Abort execution.")
return 0
if __name__ == "__main__":
sys.exit(main())
User must provide a file pattern or a filename
Run script in command line if option is missing returns error:
python test.py
FILES_PATTERN None
Usage: test.py [options]
test.py: error: Files_pattern option is mandatory - Abort execution.
If option files is missing some letters (--fil instead of --files):
python test.py --fil "a_pattern_for_files"
FILES_PATTERN a_pattern_for_files
I think I should have an error like the following
python test.py --fl "a_pattern_for_files"
Usage: test.py [options]
test.py: error: no such option: --fl
Why don't I get an error from OptionParser when I use --fil instead of the correct argument --files ?
Not only I do not get an error but variable files stores the value: a_pattern_for_files (which is printed).
I am expecting argument files to have value: None (default) unless in command line --files exists
optparse allows abbreviated forms of long options. --fil is a prefix of --files and not a prefix of any other long options the program supports, so --fil is treated as equivalent to --files.
This is barely mentioned in the docs, and there is no option to turn it off. argparse has an option to turn it off, but only in Python 3.5+.

Python unittest does not take passed parameters

I have following code:
class WebuiSeleniumTest(unittest.TestCase):
def setup_parser(self):
parser = argparse.ArgumentParser(description='Automation Testing!')
parser.add_argument('-p', '--platform', help='Platform for desired_caps', default='Mac OS X 10.9')
parser.add_argument('-b', '--browser-name', help='Browser Name for desired_caps', default='chrome')
parser.add_argument('-v', '--version', default='')
return parser.parse_args()
def test_parser(self):
args = self.setup_parser()
print args
if __name__ == "__main__":
unittest.main()
When i try to run this in terminal with the command "python myfile.py -b firefox" I get AttributeError: 'module' object has no attribute 'firefox' and the help output is generated.
When I isolate it and run it without the if __name__ == "__main__" it works fine. Why does it try to apply my passed argument on unittest ? I need it as a String in my code.
Calling your script with python myfile.py -b firefox does indeed go to unittest, and not to your argument parser.
Unittest tries to parse the arguments you gave, e.g. if you call your script like this:
python myfile.py --help
You see the valid options:
Usage: myfile.py [options] [test] [...]
Options:
-h, --help Show this message
-v, --verbose Verbose output
-q, --quiet Minimal output
-f, --failfast Stop on first failure
-c, --catch Catch control-C and display results
-b, --buffer Buffer stdout and stderr during test runs
Examples:
parse.py - run default set of tests
parse.py MyTestSuite - run suite 'MyTestSuite'
parse.py MyTestCase.testSomething - run MyTestCase.testSomething
parse.py MyTestCase - run all 'test*' test methods
in MyTestCase
Looking at the help output -b would buffer (I guess suppress?) stdout/stderr. The argument firefox is taken as the name of the test to run in your module. And there is no function method existing, it outputs this error:
AttributeError: 'module' object has no attribute 'firefox'
Now, what you probably want to do is to call test_parser, and if you do that with python myfile.py WebuiSeleniumTest.test_parser then you cannot pass any additional arguments. And that's probably your question in the end. There is this question which gives some possible solutions for testing argparse as unit test.

python argparse don't show correct help message

I have a python script using argparse. After typing in python script_name.py -h on the command line, it shows help message for another command but the code still works. The script can recognize options defined in it and run well. It looks like the script is packaged by something. I put argparse in a function and everything works well at the beginning. I just can't find out what causes the help message changed.
Here is the code:
#!/usr/bin/env python
import os
import sys
import json
import logging
import argparse
import handlers
HZZ_DIR = os.path.dirname(os.path.abspath(__file__))
ROOT_DIR = os.path.dirname(os.path.dirname(HZZ_DIR))
logger = logging.getLogger('hzz_logger')
logger.setLevel(logging.DEBUG)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
logger.addHandler(console)
def parse_args():
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('job', choices=['ws','lm','np'],
help="ws: workspace; lm: limit; np: npranking")
arg_parser.add_argument('-a', '--action', nargs=1,
help="for Limit and NPranking: get/plot (limit/pull)")
arg_parser.add_argument('-b', '--blinded', action='store_true',
help="for Limit: true -- do expected only, false -- do observed as well.")
arg_parser.add_argument('-v', '--version', nargs=1, type=int,
help="input version")
arg_parser.add_argument('-t', '--tag', nargs=1,
help='workspace tag')
arg_parser.add_argument('-m', '--mass', nargs='+', type=int,
help='signal mass(es)')
arg_parser.add_argument('-c', '--config', nargs=1,
help='configure file')
arg_parser.add_argument('-u', '--update', action='store_true',
help="update default settings")
args = arg_parser.parse_args()
return args
def load_settings(args):
pass
def run_job(settings):
pass
def execute():
args = parse_args()
settings = load_settings(args)
run_job(settings)
if __name__ == '__main__':
execute()
The help message is pasted here, which is actually the help message a command not directly used in this code. The options for this command can also be recognized...
$ python hzz_handler.py -h
Usage: python [-l] [-b] [-n] [-q] [dir] [[file:]data.root] [file1.C ... fileN.C]
Options:
-b : run in batch mode without graphics
-x : exit on exception
-n : do not execute logon and logoff macros as specified in .rootrc
-q : exit after processing command line macro files
-l : do not show splash screen
dir : if dir is a valid directory cd to it before executing
-? : print usage
-h : print usage
--help : print usage
-config : print ./configure options
-memstat : run with memory usage monitoring
Wow, another anti-Pythonic ROOT mystery! Your question and the comments are really helpful. Why did not anybody post the answer with ROOT.PyConfig.IgnoreCommandLineOptions = True?
Here is a primitive work-around:
import argparse
# notice! ROOT takes over argv and prints its own help message when called from command line!
# instead I want the help message for my script
# therefore, check first if you are running from the command line
# and setup the argparser before ROOT cuts in
if __name__ == '__main__':
parser = argparse.ArgumentParser(
formatter_class = argparse.RawDescriptionHelpFormatter,
description = "my script",
epilog = "Example:\n$ python my_script.py -h"
)
parser.add_argument("param", type=str, help="a parameter")
parser.add_argument("-d", "--debug", action='store_true', help="DEBUG level of logging")
args = parser.parse_args()
if args.debug:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
logging.debug("parsed args: %s" % repr(args))
import ROOT
...
if __name__ == '__main__':
<do something with args>
In sort the answer is that always calls import ROOT after the argparse. Then ROOT won't take over the argparse and prints the required message that we want.

Python: command line argument not passing the same way as hard coded lines

I'm working on some Selenium scripts to test sites across different devices, browsers, and platforms. I can get the scripts to work using the same code except for two lines where I define command executor URL and the browser capabilities.
I'm trying to get build a single script where I can define these lines using command line arguments.
Here's my code:
from selenium import webdriver
import time
import sys
import getopt
def main(argv):
#define desired browser capabilities
desktopCapabilities = {'browserName': 'chrome'} #change browserName to: 'firefox' 'chrome' 'safari' 'internet explorer'
iosCapabilities = {'platformName': 'iOS' ,'platformVersion': '8.1' ,'deviceName': 'iPad Air','browserName': 'Safari'}
androidCapabilities = {'chromeOptions': {'androidPackage': 'com.android.chrome'}}
# Establish the command executor URL
desktopExecutor = 'http://127.0.0.1:4444/wd/hub'
iosExecutor = 'http://127.0.0.1:4723/wd/hub'
androidExecutor = 'http://127.0.0.1:9515'
cmdExecutor = desktopExecutor
browserCapabilities = desktopCapabilities
try:
opts, args = getopt.getopt(argv,"he:c:",["executor=","capabilities="])
except getopt.GetoptError:
print 'test.py -e <executor> -c <capabilities>'
sys.exit(2)
for opt, arg in opts:
if opt == '-h':
print 'test.py -e <executor> -c <capabilities>'
sys.exit()
elif opt in ("-e", "--executor"):
cmdExecutor = arg
elif opt in ("-c", "--capabilities"):
browserCapabilities = arg
print 'Command executor is:', cmdExecutor
print 'Desired capabilities are:', browserCapabilities
driver = webdriver.Remote(command_executor=cmdExecutor, desired_capabilities=browserCapabilities)
driver.get("http://google.com")
time.sleep(5)
driver.quit()
if __name__ == "__main__":
main(sys.argv[1:])
This code runs as expected if I don't add any arguments via the command line. It also works if I run it with:
python test.py -e 'http://127.0.0.1:4444/wd/hub'
It breaks if I run it using the following command because -c is not passed as a dictionary:
python test.py -e 'http://127.0.0.1:4444/wd/hub' -c {'browserName': 'firefox'}
How can I get this to run this with:
python test.py -e iosExecutor -c iosCapabilities
Here's the output I get when I run the command mentioned above:
python my_script.py -e iosExecutor --capabilities iosCapabilities
Command executor is: iosExecutor
Desired capabilities are: iosCapabilities
Traceback (most recent call last):
File "my_script.py", line 38, in <module>
main(sys.argv[1:])
File "my_script.py", line 33, in main
driver = webdriver.Remote(command_executor=cmdExecutor, desired_capabilities=browserCapabilities)
File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 62, in __init__
raise WebDriverException("Desired Capabilities must be a dictionary")
selenium.common.exceptions.WebDriverException: Message: Desired Capabilities must be a dictionary
Basically it is running as if I passed line 33 like this:
driver = webdriver.Remote(command_executor="iosExecutor", desired_capabilities="iosCapabilities")
It also works if I hard code lines 15 and 16 with "iosExecutor" and "iosCapabilites" so this tells me it's how I'm passing info from the CLI.
Any advice would be great. I'm quite new to this (programming) so I'm guessing there may be a better way to do this, but Google hasn't cleared it up for me.
Using argparse would make things much easier. For capabilities you can use json.loads, or ast.literal_eval as a type, as, for example, was done here:
Accepting a dictionary as an argument with argparse and python
type=dict in argparse.add_argument()
As for executor, either pass a url in as a string, or define a user-friendly mapping, like:
EXECUTORS = {
'desktop': 'http://127.0.0.1:4444/wd/hub',
'ios': 'http://127.0.0.1:4723/wd/hub',
'android': 'http://127.0.0.1:9515'
}
Here's how the code would look like in the end:
import ast
import time
import argparse
from selenium import webdriver
EXECUTORS = {
'desktop': 'http://127.0.0.1:4444/wd/hub',
'ios': 'http://127.0.0.1:4723/wd/hub',
'android': 'http://127.0.0.1:9515'
}
parser = argparse.ArgumentParser(description='My program.')
parser.add_argument('-c', '--capabilities', type=ast.literal_eval)
parser.add_argument('-e', '--executor', type=str, choices=EXECUTORS)
args = parser.parse_args()
driver = webdriver.Remote(command_executor=EXECUTORS[args.executor], desired_capabilities=args.capabilities)
driver.get("http://google.com")
time.sleep(5)
driver.quit()
Sample script runs:
$ python test.py -e android -c "{'chromeOptions': {'androidPackage': 'com.android.chrome'}}"
$ python test.py -e ios -c "{'platformName': 'iOS' ,'platformVersion': '8.1' ,'deviceName': 'iPad Air','browserName': 'Safari'}"
$ python test.py -e desktop -c "{'browserName': 'chrome'}"
And, as a bonus, you get a built-in help magically made by argparse:
$ python test.py --help
usage: test.py [-h] [-c CAPABILITIES] [-e {android,ios,desktop}]
My program.
optional arguments:
-h, --help show this help message and exit
-c CAPABILITIES, --capabilities CAPABILITIES
-e {android,ios,desktop}, --executor {android,ios,desktop}

Categories

Resources