I am trying to write multiple files for specific tasks and in the end I would like to be able to use those functionalities in those files by importing them. But in the mean time, I also want to make other files runnable on their own. Therefore, for each file I have added options to them. But when I tried to run main where I imported all of the files, I saw that option parser of a file, overrides the option parser of the main. Code below is showing a similar case.
import optparse
def get_user_input():
parse_object = optparse.OptionParser()
parse_object.add_option("-t","--test",dest="test",help="test")
(user_input,arguments) = parse_object.parse_args()
return user_input
def test_func1():
print("hello")
def test11():
user_input = get_user_input()
test11()
This is the test1.py.
import optparse
import test1
def get_input():
sample = "[sudo] python3 main.py -[option] [argument]"
parse_object = optparse.OptionParser(usage=sample)
parse_object.add_option("-d","--discovery",dest="network_discovery",help="dd.")
parse_object.add_option("-p","--all-ports",dest = "all_ports",help = "pp")
parse_object.add_option("-i","--ip",dest="ip",help="ii.")
parse_object.add_option("-l","--list",dest="ip_list",help="ll")
parse_object.add_option("-o","--output",dest="save_output",help="oo")
parse_object.add_option("-c","--icmp",dest="icmp_scan",help="icmp")
(user_input,arguments) = parse_object.parse_args()
return user_input
user_input = get_input()
And this is the main.py.
I have realized that when I do not test11() in test1.py it does not override which simply means that when I don't call get_user_input(). But when I run main.py with help function the output is as below.
Usage: main.py [options]
Options:
-h, --help show this help message and exit
-t TEST, --test=TEST test
But the expected output is as follows:
Usage: [sudo] python3 main.py -[option] [argument]
Options:
-h, --help show this help message and exit
-d NETWORK_DISCOVERY, --discovery=NETWORK_DISCOVERY
dd.
-p ALL_PORTS, --all-ports=ALL_PORTS
pp
-i IP, --ip=IP ii.
-l IP_LIST, --list=IP_LIST
ll
-o SAVE_OUTPUT, --output=SAVE_OUTPUT
oo
-c ICMP_SCAN, --icmp=ICMP_SCAN
icmp
As I mentioned, this is just an example case. In the real project, I have six files which can be run individually and one main function to run all of them in a specific way and all files have option parser.
So far, I have tried test1.get_user_input = get_input, from test1 import test_func1 to see if I could exclude that call. This is the last stage of the project. So, If you could help me out it will be much appreciated.
Related
I have a Python function that I want to run from the context-menu. I am using the Windows Registry to call the function when a file is right clicked, and I want the function to receive the file selected.
The context menu item looks something like the Random Commands option.
The function I wish to run is foo1() in the file test1:
# in script test1.py
def foo1():
import sys
print(sys.argv)
I tried approaching the problem using the python -c switch to directly call the function:
python -c "import test1; test1.foo1()" %1
However, the value of sys.argv after selecting the file and executing is ['-c'], which doesn't include the file selected.
I also tried using an argument parser to dynamically import and run the function:
# in file registry_parser.py
import argparse
import os
import sys
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-n', '--func_name')
parser.add_argument('-f', '--file_name')
parser.add_argument('-p', '--file_path')
args = vars(parser.parse_args())
sys.path.insert(0, args['file_path'])
exec(f"import {args['file_name']}")
exec(f"{args['file_name']}.{args['func_name']}(sys.argv)")
And the function I would call from the registry is (paths simplified):
registry_parser.py --func_name foo1 --file_name test1 --file_path "[PATH TO registry_parser.py]" %1
However, I either get an error registry_parser.py: error: unrecognized arguments: %1 or the "%1" is parsed quite literally and ends up as a value in sys.argv.
Any help is appreciated.
So on all the forums I asked I never received a correct answer, but I found this Reddit Post that has an example to a solution. Hope this helps someone else.
I tried to pass multiple arguments to my python script (opsgit) using the click module like this:
import click
#click.command()
#click.argument('arguments', nargs=-1)
def cli(arguments):
"""CLI for git"""
cmd = create_command(arguments)
_execute_command(cmd)
When I execute this command line:
$ opsgit git checkout -b pvt_test
I get this error:
Usage: opsgit git [OPTIONS] [ARGUMENTS]...
Try "opsgit git --help" for help.
Error: no such option: -b
Can anyone let me know how to solve this one?
You are missing the ignore_unkown_options flag. Here is your example with the flag added. Check out the docs for more info on how to use nargs.
import click
#click.command(context_settings=dict(
ignore_unknown_options=True,
))
#click.argument('arguments', nargs=-1)
def cli(arguments):
"""CLI for git"""
cmd = click.create_command(arguments)
_execute_command(cmd)
I need to run python test script for different environments (different urls). And I need to define which variable use from command line. In future this parameter will be used in Jenkins job.
script.py:
class TestLogin(unittest.TestCase):
#allure.step
def test_LoginValidation(self):
devURL = "http://url1/admin/login/"
stagingURL = "http://url2/admin/login/"
prodURL = "https://url3/admin/login"
driver.maximize_window()
driver.implicitly_wait(10)
driver.get(url)
lp = LoginPage(driver)
lp.login("login", "password")
time.sleep(2)
driver.quit()
In command line I need to write
python script.py stagingURL
In a result in method test_LoginValidation in driver.get(url) will be used url which I defined in command line.
You can use argparse to do this:
import argparse
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Description')
parser.add_argument('--dev',
dest='dev',
action='store_true',
help="Help message")
parser.add_argument('--stage',
dest='stage',
action='store_true',
help="Help message")
parser.add_argument('--prod',
dest='prod',
action='store_true',
help="Help message")
parser.set_defaults(dev=True,
stage=False,
action=False)
args = parser.parse_args()
url = None
if args.dev:
url = "http://url1/admin/login/"
if args.stage:
url = "http://url2/admin/login/"
if args.prod:
url = "https://url3/admin/login"
# do something with the url
This is one way to do it. You are creating some arg parameters --dev, --stage, --prod and by default --dev is set to true. You can also have no default (just set dev=False).
So next time you can run:
python program.py --dev
python program.py --stage
python program.py --prod
You might want to handle the case where more than one flag is passed.
You can also do it this way:
import argparse
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Description')
parser.add_argument("--env",
choices={"dev", "stage", "prod"},
help="Some help message.")
args = parser.parse_args()
url = None
if args.env == "dev":
url = "http://url1/admin/login/"
elif args.env == "stage":
url = "http://url2/admin/login/"
elif args.env == "prod":
url = "https://url3/admin/login"
else:
print("Please specify the environment using --env flag.")
if url is not None:
print(url)
Example:
$ python3 test2.py
Please specify the environment using --env flag.
$ python3 test2.py --env prod
https://url3/admin/login
$ python3 test2.py --env stage
http://url2/admin/login/
$ python3 test2.py --env dev
http://url1/admin/login/
$ python3 test2.py --env wrong
usage: test2.py [-h] [--env {stage,dev,prod}]
test2.py: error: argument --env: invalid choice: 'wrong' (choose from 'stage', 'dev', 'prod')
You can read more about argparse here.
I can recommend click package for creating CLI. It's really simple, well documented, has a lot of options and in my opinion much easier to use than argparse.
A dummy example:
import click
#click.command()
#click.option(
'--count',
default=1,
help='Number of greetings.'
)
#click.option(
'--name',
prompt='Your name',
help='The person to greet.'
)
def hello(**options):
"""Simple program that greets NAME for a total of COUNT times."""
for x in range(options['count']):
click.echo('Hello %s!' % options['name'])
if __name__ == '__main__':
hello()
And what it looks like when run:
$ python hello.py --count=3
Your name: John
Hello John!
Hello John!
Hello John!
It automatically generates nicely formatted help pages:
$ python hello.py --help
Usage: hello.py [OPTIONS]
Simple program that greets NAME for a total of COUNT times.
Options:
--count INTEGER Number of greetings.
--name TEXT The person to greet.
--help Show this message and exit.
You can get the library directly from PyPI:
pip install click
If you want to create CLI just to parametrize unit test you may consider using #pytest.mark.parametrize which allows one to define multiple sets of arguments and fixtures at the test function or class.
An example:
import pytest
class TestLogin(object):
#pytest.mark.parametrize("url", [
"http://url1/admin/login/",
"http://url2/admin/login/",
"https://url3/admin/login",
])
def test_LoginValidation(self, url):
driver.maximize_window()
driver.implicitly_wait(10)
driver.get(url)
lp = LoginPage(driver)
lp.login("login", "password")
time.sleep(2)
driver.quit()
What you're looking for is argparse. That should allow you to do exactly what you're looking for, for example:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('url', help = 'The URL to use for ...', type = str)
This sets up the url as a required argument to be passed to the function, and sets its type to str (this is the default behavior, but being explicit is good).
You can then extract the arguments using:
args = parser.parse_args()
specified_url = args.url
From here you can proceed as you normally would. If you wish to make the argument optional but with a default value, that is also possible using argparse.
Using the environment variables works but is much harder to debug, especially if you expect this script to be run by another piece of software argparse is much more reliable.
It's almost certainly easier to do this in Jenkins than it is to do it in Python. Additionally it seems to make sense that your devops pipeline controls the location of dev, staging, and release URIs (at least as much as it is sensible to do that).
def targetUrl = ''
switch (env.TARGET) {
case 'dev':
targetUrl = "http://url1/admin/login/"
break
// etc
}
sh "python script.py ${targetUrl}"
then have the python script look at sys.argv[1] (which is the first argument passed to it) and use that URL directly.
I have a python option parsers that parses an optional --list-something option.
I also want the --list-something option to have an optional argument (an option)
Using the argument default="simple" does not work here, otherwise simple will always
be the default, not only when --list-something was given.
from optparse import OptionParser, OptionGroup
parser = OptionParser()
options = OptionGroup(parser, "options")
options.add_option("--list-something",
type="choice",
choices=["simple", "detailed"],
help="show list of available things"
)
parser.add_option_group(options)
opts, args = parser.parse_args()
print opts, args
The above code is producing this:
[jens#ca60c173 ~]$ python main.py --list-something simple
{'list_something': 'simple'} []
[jens#ca60c173 ~]$ python main.py --list-something
Usage: main.py [options]
main.py: error: --list-something option requires an argument
[jens#ca60c173 ~]$ python main.py
{'list_something': None} []
But I want this to hapen:
[jens#ca60c173 ~]$ python main.py --list-something simple
{'list_something': 'simple'} []
[jens#ca60c173 ~]$ python main.py --list-something
{'list_something': 'simple'} []
[jens#ca60c173 ~]$ python main.py
{'list_something': None} []
I would like something that works out of the box in python 2.4 up till 3.0 (3.0 not included)
Since argparse is only introduced in python 2.7 this is not something I could use.
Optparse does not have any options for doing this easily. Instead you'll have to create a custom callback for your option. The callback is triggered when your option is parsed, at which point, you can check the remaining args to see if the user put an argument for the option.
Check out the custom callback section of the docs, in particular Callback example 6: variable arguments.
There is no default for the Optparser in python.
However, you can use the follwing -
# show help as default
if len(sys.argv) == 1:
os.system(sys.argv[0] + " -h")
exit()
This will run the same script with the -h option, and exit.
please notice - you will need to import the sys module.
I copied this script from internet but idon't know how to use it. i am newbiw to python so please help. When i execute it using
./test.py then i can only see
usage: py4sa [option]
A unix toolbox
options:
--version show program's version number and exit
-h, --help show this help message and exit
-i, --ip gets current IP Address
-u, --usage gets disk usage of homedir
-v, --verbose prints verbosely
when i type py4sa then it says bash command not found
The full script is
#!/usr/bin/env python
import subprocess
import optparse
import re
#Create variables out of shell commands
#Note triple quotes can embed Bash
#You could add another bash command here
#HOLDING_SPOT="""fake_command"""
#Determines Home Directory Usage in Gigs
HOMEDIR_USAGE = """
du -sh $HOME | cut -f1
"""
#Determines IP Address
IPADDR = """
/sbin/ifconfig -a | awk '/(cast)/ { print $2 }' | cut -d':' -f2 | head -1
"""
#This function takes Bash commands and returns them
def runBash(cmd):
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
out = p.stdout.read().strip()
return out #This is the stdout from the shell command
VERBOSE=False
def report(output,cmdtype="UNIX COMMAND:"):
#Notice the global statement allows input from outside of function
if VERBOSE:
print "%s: %s" % (cmdtype, output)
else:
print output
#Function to control option parsing in Python
def controller():
global VERBOSE
#Create instance of OptionParser Module, included in Standard Library
p = optparse.OptionParser(description='A unix toolbox',
prog='py4sa',
version='py4sa 0.1',
usage= '%prog [option]')
p.add_option('--ip','-i', action="store_true", help='gets current IP Address')
p.add_option('--usage', '-u', action="store_true", help='gets disk usage of homedir')
p.add_option('--verbose', '-v',
action = 'store_true',
help='prints verbosely',
default=False)
#Option Handling passes correct parameter to runBash
options, arguments = p.parse_args()
if options.verbose:
VERBOSE=True
if options.ip:
value = runBash(IPADDR)
report(value,"IPADDR")
elif options.usage:
value = runBash(HOMEDIR_USAGE)
report(value, "HOMEDIR_USAGE")
else:
p.print_help()
#Runs all the functions
def main():
controller()
#This idiom means the below code only runs when executed from command line
if __name__ == '__main__':
main()
It seems to me you have stored the script under another name: test.py rather than py4sa. So typing ./test.py, like you did, is correct for you. The program requires arguments, however, so you have to enter one of the options listed under 'usage'.
Normally 'py4sa [OPTIONS]' would mean that OPTIONS is optional, but looking at the code we can see that it isn't:
if options.verbose:
# ...
if options.ip:
# ...
elif options.usage:
# ...
else:
# Here's a "catch all" in case no options are supplied.
# It will show the help text you get:
p.print_help()
Note that the program probably would not be recognized by bash even if you renamed it to py4sa, as the current directory is often not in bash's PATH. It says 'usage: py4sa (..)' because that's hard-coded into the program.
The script is called "test.py". Either invoke it as such, or rename it to "py4sa".
you run a Python script using the interpreter, so
$ python py4sa