argparse like docker cli - python-3.x

Hello i am writing cmd tool,
i want to have behaviour like docker does:
docker container run --help
print the help for this particular command.
im stucked with code:
parser.add_argument("method", help=getHelp())
but the method can be anything like
add
remove
update
and how to later add a method in add like:
add ram
add cpu
i can add subparser for add but how to add later a subparser for ram ?
How can i achieve that with argparse in python?
Is it even possible?
Can somebody show me example of third deep command with its own arguments ?

import argparse
import pprint
import random
def get_comments(args):
return [{'post_id': args.post_id,
'comment_id': str(random.randrange(1, 1000)),
'comment': "< comment's body >"}
for _ in range(random.randrange(1, 10))]
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='command')
list_parser = subparsers.add_parser('list')
list_subparsers = list_parser.add_subparsers(dest='type')
comments_parser = list_subparsers.add_parser('comments')
comments_parser.add_argument('post_id')
comments_parser.set_defaults(func=get_comments)
accounts_parser = list_subparsers.add_parser('accounts')
show_parser = subparsers.add_parser('show')
args = parser.parse_args()
print(args)
print(args.command)
#result = args.func(args)
print(parser)
#pprint.pprint(args)

Related

Is there a way to pass options values during mod_wsgi server start up in Django app

I am using mod_wsgi to run my Django app. As there are a plethora of options to define when the server run command is fired, I was trying to create some kind of python script to pass the options and their pre-set values.
For example:
Instead of using:
$python3 manage.py runmodwsgi --processes 3 --threads 1
I am trying to use a python script and use it as:
$python3 runme.py
where runme.py is the script file I am trying to use to start the server.
What I have done so far?
Created the script file:
import os
from django.core.management import execute_from_command_line
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '<proj>.settings')
myargs = 'runmodwsgi'
list_of_args = ['', myargs]
execute_from_command_line(list_of_args)
As expected the server started with preset options, for example:
Now what I am trying to achieve is the pass values of certain options like:
--processes 3 --threads 1
and so on.
Is there a way I may pass the preset values (as I may be able to define in my script file runme.py), say something like adding to the list of arguments:
list_of_args = ['', myargs, addl_args]
I have been checking SO queries posted in addition to help available on python site, but could not get my head around to the problem.
I tried the following which is not very helpful though:
import os
import argparse # New import
from django.core.management import execute_from_command_line
# Addition of new code lines
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-p", "--processes", action="store_true")
group.add_argument("-t", "--threads", action="store_true")
parser.add_argument("pint", type=int, help="no. of processes")
parser.add_argument("tint", type=int, help="no. of threads")
args = parser.parse_args()
# End of new code
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '<proj>.settings')
myargs = 'runmodwsgi'
# At this line if I add ", args" (like shown below:])
list_of_args = ['', myargs, args]
execute_from_command_line(list_of_args)
and run the command, it comes up with error:
TypeError: 'Namespace' object does not support indexing
If I simply run: python3 runme.py, I get the following error:
usage: runme.py [-h] [-p | -t] pint tint
runme.py: error: the following arguments are required: pint, tint
Whereas, using
python3 runme.py 3 1
starts the server but the options integers "3" and "1" does not have any effect (as intended for no. of processes and threads).
If I use:
python3 runme.py --processes 3 --threads 1
I get the following error:
usage: runme.py [-h] [-p | -t] pint tint
runme.py: error: argument -t/--threads: not allowed with argument -p/--processes
Tried with a single arg like:
python3 runme.py --processes 3 1
The server starts at this time but the options value/s are not affected.
How do I define the option values and pass these preset values of options to the run command?
It looks like you're just misunderstanding/misusing argparse.
import os
import sys
import argparse
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '<proj>.settings')
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--processes", type=int, default=1)
parser.add_argument("-t", "--threads", type=int, default=1)
args = parser.parse_args()
from django.core.management import execute_from_command_line
command = [sys.argv[0], 'runmodwsgi', '--processes', str(args.processes), '--threads', str(args.threads)]
execute_from_command_line(command)
might be closer to what you want; it will default processes and threads both to 1.

Python3 argparse nargs="+" get number of arguments

I'm now googling for quite a while and I just don't find any solution to my problem.
I am using argparse to parse some command line arguments. I want to be able to parse an arbitrary number of arguments > 1 (therefore nargs="+") and I want to know afterwards how many arguments I have parsed. The arguments are all strings. But I get a problem when I just have one argument, because then the length of the list is the number of characters of the word and not 1 as in 1 argument. And I want to know how many arguments were parsed. Does anyone know how I could solve this problem?
examples:
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("--test", type=str, nargs="+", required=False, default="hi")
args = parser.parse_args()
test = args.test
print(test)
print(len(test))
So with python3 example.py --test hello hallo hey the output is:
['hello', 'hallo', 'hey']
3
But with python3 example.py --test servus the output is:
servus
6
What I already know is that I could do print([test]) but I only want to do that if I have 1 argument. Because if I have more than one arguments and use print([test]) I get a double array... So I just want to know the number of parsed arguments for "test".
I cannot imagine that I am the only one with such a problem, but I could not find anything in the internet. Is there a quick and clean solution?
You left off the test=args.test line.
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("--test", type=str, nargs="+", required=False, default="hi")
args = parser.parse_args()
print(args)
print(len(args.test))
test cases
0856:~/mypy$ python3 stack68020846.py --test one two three
Namespace(test=['one', 'two', 'three'])
3
0856:~/mypy$ python3 stack68020846.py --test one
Namespace(test=['one'])
1
0856:~/mypy$ python3 stack68020846.py
Namespace(test='hi')
2
change the default to default=[]
0856:~/mypy$ python3 stack68020846.py
Namespace(test=[])
0
Theres probably a better solution, but try this:
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("--test", type=str, nargs="+", required=False, default="hi")
args = parser.parse_args()
print("Number of arguments:", len(args._get_kwargs()[-1][-1]))
By the way, i figured this out by checking the content of args with print(dir(args))

Understanding argparse to get dynamic maps with Geo-Location of tweets

I have found this python code online (twitter_map_clustered.py) which (I think) help create a map using the geodata of different tweets.:
from argparse import ArgumentParser
import folium
from folium.plugins import MarkerCluster
import json
def get_parser():
parser = ArgumentParser()
parser.add_argument('--geojson')
parser.add_argument('--map')
return parser
def make_map(geojson_file, map_file):
tweet_map = folium.Map(Location=[50, 5], max_zoom=20)
marker_cluster = MarkerCluster().add_to(tweet_map)
geodata= json.load(open(geojson_file))
for tweet in geodata['features']:
tweet['geometry']['coordinates'].reverse()
marker = folium.Marker(tweet['geometry']['coordinates'], popup=tweet['properties']['text'])
marker.add_to(marker_cluster)
#Save to HTML map file
tweet_map.save(map_file)
if __name__ == '__main__':
parser = get_parser()
args = parser.parse_args()
make_map(args.geojson, args.map)
I managed to extract the geo information of different tweets and save it into a geo_data.json file. However, I have trouble understanding the code, specially the function def get_parser().
It seems that we need to add argument when running the file in the command prompt. The argument should be geo_data.json. However, it is also asking for a map ? parser.add_argument('--map')
Why is it the case? In the code, aren't we creating the map here?
#Save to HTML map file
tweet_map.save(map_file)
Can you please help me. How would you run the python script ? Is there anything important I am missing ?
As explained by argparse documentation, it simply asks for the name of the geojson file and a name that your code will use to save the map.
Therefore, you will run:
python twitter_map_clustered.py --geojson geo_data.json --map mymap.html
and you will get a map named mymap.html.

Calling functions from a script using argparse without using subprocess

I have been given an existing script (let's call it existing.py) that in its MVCE form has the following structure.
import argparse
FLAGS = None
def func():
print(FLAGS.abc)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'--abc',
type=str,
default='',
help='abc.'
)
FLAGS, unparsed = parser.parse_known_args()
func()
As this is part of tool that gets constantly updated, I cannot change existing.py. Normally, existing.py is invoked with commandline arguments.
python -m existing.py --abc "Ok"
which prints the output 'Ok'.
I wish to call the functions (not the whole script) in existing.py using another script. How can I feed in the FLAGS object that is used in the functions of the script? I do not wish to use subprocess will just run the script in its entirety.
I know that argparse creates the FLAGS as a Namespace dictionary and I can construct it in calling.py (see code below) but I cannot then push it back into the function that is imported from existing.py into calling.py. The following is the calling.py that I've tried.
from existing import func
import argparse
args = argparse.Namespace()
args.abc = 'Ok'
FLAGS = args
func()
which throws an error
AttributeError: 'NoneType' object has no attribute 'abc'
This is different from other StackOverflow questions as this question explicitly forbids subprocess and the existing script cannot be changed.
Import existing and use
existing.FLAGS = args
Now functions defined in the existing namespace should see the desired FLAGS object.

Argparse add option strings

That question will be strange in some point...
I'm trying to add the option strings in the parser after adding one argument.
For example
import argparse
p = argparse.ArgumentParser()
p.add_argument(dest = 'myvar')
p._actions[1].option_strings = ['-foo']
p.parse_args('-foo 1')
This example doesn't work, it says:
: error: the following arguments are required: -foo
Even though I'm supplying the argument...
Anyone know why this is occurring?
Is there some way to add the option strings after the add_argument method?
After looking at the source code of argparse, I found you also have to register the action by the option string it has:
import argparse
p = argparse.ArgumentParser()
p.add_argument(dest = 'myvar')
p._actions[1].option_strings = ['-foo']
# You also need to do this
p._option_string_actions['-foo'] = p._actions[1]
args = p.parse_args(['-foo', '100'])
print('myvar is', args.myvar) # myvar is 1
Note the change when calling parse_args.
I hope this help!!

Resources