Python argparse accept variable argument (even flags) - python

I have the following
p = ThrowingArgumentParser()
p.add_argument('action', type=str, choices=actions)
p.add_argument('args', nargs='*')
This is part of a multi level application. In level one, the command I care about is of the format command other-things-that-will-be-parsed-by-the-sub-module (for example get user john). So I'd except to get action = "get" and args = ["user", "john"].
So far so good. However, if I include a flag, all hell breaks loose (get user john --detailed). This will return a None. But I want to get the same as before: action = "get" and args = ["user", "john", "--detailed"].
Why is this failing?

I think you can use nargs=argparse.REMAINDER. Please see https://docs.python.org/3/library/argparse.html#nargs.

Related

How to replace email domain misspellings

I manage a booking system where people enter their emails to book appointments. Unfortunately, many of the email addresses have typos, especially in the top-level domain. I'd like to implement a function to identify when the domain should be .com, which should be all cases where it starts with ".c".
I wrote the function and tested the individual lines, which seem to work. But when I run the function on a set of emails, it only returns the email with no ".c", meaning it only returns 'my_name_is#orange.org'. The ideal output would be:
['me.you#perp.com',
'appleorange#msn.edu.com',
'laughable#gmail.com',
'notfunny#pointdexter.com',
'very.latin...word#word.nutherwork.com',
'very.latin..word#word.nutherwork.com',
'my_name_is#orange.org']
Any help would be appreciated!
emails = ['me.you#perp.comp',
'appleorange#msn.edu.cot',
'laughable#gmail.copl',
'notfunny#pointdexter.com',
'very.latin...word#word.nutherwork.com',
'very.latin..word#word.nutherwork.cnm',
'my_name_is#orange.org']
def domain(emails):
for email in emails:
parse_mail = email.split('.')
parse_mail = parse_mail[-1]
if parse_mail[0] == "c":
email = email.replace(parse_mail,"com")
pass
return email
print(domain(emails))
It returns the email without "c" because that's the last one in the array.
Your routine "domain" only returns the last email in the array. If you put the org address higher up, it'll return an email with a "c".
I expect what you're looking for is:
def domain(emails):
for i in range(len(emails)):
parse_mail = emails[i].split('.')
if parse_mail[-1][0] == "c":
parse_mail = emails[i].split('.')[:-1]
parse_mail.append("com")
correct = '.'.join(parse_mail)
emails[i] = correct
pass
return emails
print(domain(emails))
But as Sembei Norimaki, mentioned - this will also auto-correct (erroneously) other extensions beginning with a 'c'.

Flask reqparse.RequestParser ValueError with parse_args()

Good day everyone,
I am having a really strange issue with Flask's RequestParser.parse_args(). Below is a snippet of my __init__ method of the relevant class:
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
self.arg_parser = reqparse.RequestParser()
self.arg_parser.add_argument('optional_arg_from_url', type=bool, default=False)
Notice I only add 1 argument to self.arg_parser. Then, in the post method, I want to retrieve optional_arg_from_url with arg_parser.parse_args() as follows:
def post(self):
# Authorization in headers
token = request.headers.get('Authorization').split()[1]
# Parse args
args = self.arg_parser.parse_args()
optional_arg_from_url = args['optional_arg_from_url']
Now, an example of the request url containing the optional arg would look as follows:
http://localhost:8080/path/to/endpoint/?optional_arg_from_url=True
The error that I am getting is ValueError: not enough values to unpack: expected 2, which is raised when args = self.arg_parser.parse_args() is called. I don't understand why 2 values are expected, since I only add one argument to the parser. Also, why would a value error be raised when I try to parse the args? Shouldn't it just parse all the args regardless?
Another interesting thing, is that the corresponding unit tests are working, regardless of whether the optional arg is included in the url or not. The code ALSO works if I do not include the optional arg in the url (which means the arg defaults to False) and gets parsed correspondingly. It is just when I try to overwrite the arg's value to True within the request url when a value error is raised.
I also made sure that optional_arg_from_url is spelled exactly the same everywhere, so that is not the issue.
Any assistance is appreciated.

Building URLs with concatinate

Trying to build a URL using the variables I've already setup and then a known part of the URL on the back end. The code will throw the following error:
TypeError: cannot concatenate 'str' and 'NoneType' objects
block_explorer_url = "https://blockexplorer.com/api/addrs/"
#?from=0&to=50
parser = argparse.ArgumentParser(description='Collect and visualize Bitcoin transactions and any related hidden services.')
parser.add_argument("--graph",help="Output filename of the graph file. Example: bitcoin.gexf",default="bitcoingraph.gexf")
parser.add_argument("--address", help="A bitcoin address to begin the search on.",)
args = parser.parse_args()
bitcoin_address = args.address
graph_file = args.graph
#
# Retrieve all bitcoin transactions for a Bitcoin address
#
def get_all_transactions(bitcoin_address):
transactions = []
from_number = 0
to_number = 50
block_explorer_url_full = block_explorer_url + bitcoin_address + "/txs?from=%d&to=%d" % (from_number,to_number)
Logically, having the variables there and then adding on the rest of the URL as a string makes since to me. Where am I going astray?
The problem is that when bitcoin_address is None (not provided by user), your program still tries to concatenate it to a str, which definitely won't work.
To solve that, you could add some code that checks the result of parse_args and raises an error when that happens, such as this:
if args.address is None:
raise ValueError('A bitcoin address must be provided.')
Separately, while your approach to string formatting is generally correct, you should consider moving away from C-style formatting and towards the format method, for example:
At the start of your script:
base_url = 'https://blockexplorer.com/api/addrs/{address}/txs?from={from}&to={to}'
And later in the function:
full_url = base_url.format(address=bitcoin_address,
from=from_number,
to=to_number)

'for loop' for updating argparser argument

My code searches youtube video link for every line in file using 'for loop', and youtube api.
File line looks like this 'video_id name'. I am passing this video_id value to my function with 'argparser function' that fails. After first iteration code fails when trying to launch this args = argparser.parse_args()
Is there a way to maybe clear argparser? I googled for that, but it seems like there is no solution for my particular constantly changing value of argparser argument. This link Disable/Remove argument in argparse suggests creating parent parser, but I don't understand how it could help my problem.
def ytSearchLaunch(video_id):
argparser.add_argument("--q", help="Search term", default=video_id)
argparser.add_argument("--max-results", help="Max results", default=25)
args = argparser.parse_args()
youtube_search(args)
This is whole code.
def youtube_search(options):
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
developerKey=DEVELOPER_KEY)
search_response = youtube.search().list(
q=options.q,
type="video",
part="id,snippet",
maxResults=options.max_results
).execute()
def ytSearchLaunch(video_id):
argparser.add_argument("--q", help="Search term", default=video_id)
argparser.add_argument("--max-results", help="Max results", default=25)
args = argparser.parse_args()
youtube_search(args)
def checkDateFunction():
fname = 'file'
f = open(fname,'r')
for l in f:
video_id = l.split()[0]
ytSearchLaunch(video_id)
Your ytSearchLaunch function is taking an argparser object that was created by other code, adding a couple of arguments to it, and then parsing the command line values (which you can also see in sys.argv). It is then passing the resulting args namespace object to youtube_search.
It would help us if you showed how argparser was created, or at least show the help or usage that it produces. It would also be nice to see the args object after parsing.
If I read this right you want to parse the inputs multiple times, each time with a different -q default value. But you can't add the same argument to argparser several times. You can do it once, but on the repeat calls you'll have to modify an existing argument.
Here's a possible fix:
def ytSearchLaunch(video_id, added_actions=[]):
if len(added_actions)==0: # first time
a1 = argparser.add_argument("--q", help="Search term", default=video_id)
a2 = argparser.add_argument("--max-results", help="Max results", default=25)
added_actions.append(a1)
added_actions.append(a2)
else: # repeats
# change defaults to the -q argument without adding a new one
added_actions[0].default = video_id
args = argparser.parse_args()
youtube_search(args)

Designing argparse with many optional sub-arguments

I am currently writing a script for my library, and i ended up getting stuck on how to design argparse that will have a lot of options, and sub arguments.
Currently i am designing the search function, which has the following options, some required and some not:
search_session_id - Required
user_session_id - Required
discover_fields - Optional
start_time - Optional
end_time - Optional
summary_fields - Optional
field_summary - Optional
local_search - Optional
My problem is then the following:
How can i make the argparse and the if statements, if all optionals needs to work together, but also work if only one of them are defined?
If i need to check every single combination, i would end up with something like this:
#!/usr/bin/env python3
"""Script to generate searches on the ArcSight Logger"""
import arcsightrest
import argparse
parser = argparse.ArgumentParser(description='Script used to send search '
'queries to ArcSight Logger API')
parser.add_argument('-t', '--target',
help='IP Address of the Loggger', required=True)
parser.add_argument('-u', '--username',
help='Username to access the logger', required=True)
parser.add_argument('-p', '--password',
help='Password to access the logger', required=True)
parser.add_argument('-ussl', '--unsecuressl', action='store_true',
help='Disable ssl warnings', )
parser.add_argument('-w', '--wait', action='store_true',
help='Wait for query to finish', )
parser.add_argument('-q', '--query',
help='Query to be used in the search')
parser.add_argument('-st', '--starttime',
help='From which time the query should look')
parser.add_argument('-et', '--endtime',
help='To which time the query should look')
parser.add_argument('-e', '--event',
help='Events based input search id')
parser.add_argument('-s', '--status',
help='Status of running search')
args = (parser.parse_args())
"""
Sets the target Logger Server
"""
arcsightrest.ArcsightLogger.TARGET = args.target
"""
Gets login token from the Logger API
"""
arc = arcsightrest.ArcsightLogger(args.username, args.password,
args.unsecuressl)
"""
Checks if query is used, and starts a search
"""
if args.query:
if args.starttime:
search_id, response = arc.search(args.query, start_time=args.starttime,
end_time=args.endtime)
search_id, response = arc.search(args.query)
if args.starttime and args.discover_fields:
search_id, response = arc.search(args.query, start_time=args.starttime,
end_time=args.endtime,
discover_fields=args.discover_fields)
print('The search id is {}'.format(search_id))
if response:
print('The search has successfully started')
As you can see, i can continue with no end, to make if statements that have every single combination of optional arguments. There must be an easier way to design this? If i was to just parse it in as kwargs, they would not be sent in the correct format, or i would require the person using the script to write things like end_time=SOMETIME, instead of just --endtime TIME. Now this might seem to be a small price to pay, but if i need to add every function with all their parameters into the script, then this will be come a lot longer and more tedious.
You could collect all the optional keyword arguments passed to arc.search to a dict and then unpack it when you call the function:
import argparse
parser = argparse.ArgumentParser(description='Script used to send search '
'queries to ArcSight Logger API')
parser.add_argument('-t', '--target',
help='IP Address of the Loggger', required=True)
parser.add_argument('-u', '--username',
help='Username to access the logger', required=True)
parser.add_argument('-p', '--password',
help='Password to access the logger', required=True)
parser.add_argument('-q', '--query',
help='Query to be used in the search')
parser.add_argument('-st', '--starttime',
help='From which time the query should look')
parser.add_argument('-et', '--endtime',
help='To which time the query should look')
args = (parser.parse_args())
# Mock search
def search(query, start_time=None, end_time=None, discover_fields=None):
return 'Id', ', '.join(str(x) for x in [start_time, end_time, discover_fields])
"""
Checks if query is used, and starts a search
"""
if args.query:
# {name used in argparse: search parameter name}
query_args = {
'starttime': 'start_time',
'endtime': 'end_time',
'discover_fields': 'discover_fields'
}
d = vars(args)
real_args = {v: d[k] for k, v in query_args.items() if k in d}
search_id, response = search(args.query, **real_args)
print('The search id is {}'.format(search_id))
print('Response is {}'.format(response))
Output:
>python test.py -t foo -u user -p pass -q
query -st start -et end
The search id is Id
Response is start, end, None
Since some of the argument names used by parser are different than the ones passed to search the names need to remapped. vars is used to create a dict from Namespace object returned by parse_args(). Then dictionary comprehension iterates over the mapped argument names, picks ones which were given user and creates a new dictionary with key names that arc.search understands. Finally **real_args unpacks the dictionary named parameters within the function call.

Categories

Resources