if else statement for input arguments of a function in python - python

I run my bash script my_file.sh in a python file as follows:
import subprocess
def rest_api():
params = {
'query': 'indepedence day',
'formats': '["NEWSPAPER"]',
}
subprocess.call(['bash',
'my_file.sh',
f'QUERY={params.get("query")}',
f'DOC_TYPE={params.get("formats")}',
f'LANGUAGE={params.get("lang")}', # returns None!
])
if __name__ == '__main__':
rest_api()
Several of my input arguments in subprocess.call do not normally exist in a dictionary params={} (here I provided f'LANGUAGE={params.get("lang")}' as one example). I handle such unavailability in my_file.sh to initialize with something, for instance:
if [ -z "$LANGUAGE" ]; then LANGUAGE="${LANGUAGE:-[]}"; fi
What I want is to apply some sort of if else statement in subprocess.call function with this logic:
if params.get("lang") is None, do not even send it as an input to bash file, e.g., treat it as I never provided such input for my_file.sh.
Therefore, I tried to rewrote my code like this:
subprocess.call(['bash',
'my_file.sh',
f'QUERY={params.get("query")}',
f'DOC_TYPE={params.get("formats")}',
if params.get("lang"): f'LANGUAGE={params.get("lang")}', # syntax Error
])
which is wrong I get the following invalid syntax error:
Traceback (most recent call last):
File "nationalbiblioteket_logs.py", line 13, in <module>
from url_scraping import *
File "/home/xenial/WS_Farid/DARIAH-FI/url_scraping.py", line 17, in <module>
from utils import *
File "/home/xenial/WS_Farid/DARIAH-FI/utils.py", line 53
if params.get("lang"): f'LANGUAGE={params.get("lang")}',
^
SyntaxError: invalid syntax
Do I have a wrong understanding of applying if else statement for the input arguments of a python function or is there an easier or cleaner way doing it?
Cheers,

You can specify the default when calling .get(), so use an empty string.
f'LANGUAGE={params.get("lang", "")}'

If you don't want the LANGUAGE= argument at all when no value is provided, you need to build the list dynamically.
cmd = ['bash',
'my_file.sh',
f'QUERY={params.get("query")}',
f'DOC_TYPE={params.get("formats")}']
if (lang := params.get("lang")) is not None:
cmd += [f'LANGUAGE={lang}']
subprocess.call(cmd)

Related

Calling Python from PHP using shell_exec() in XAMPP

I'm trying to make this work on XAMPP. It works fine on a Linux server/virtual machine.
I need to send an associative array to a python script, and I do it with:
<?php
echo "Test simple call/answer process<br>";
$age = array("Peter"=>"35", "Ben"=>"37", "Joe"=>"43");
$param = base64_encode(json_encode($age));
$command = escapeshellcmd('python python_array_answer.py $param');
$output = shell_exec($command);
$obj = json_decode($output);
print_r($obj);
?>
The Python script is:
#! /Users/.../AppData/Local/Programs/Python/Python37/python.exe
import sys
import json
import base64
hotel_data = json.loads(base64.b64decode(sys.argv[1]))
print(json.dumps(hotel_data))
I get a NULL array back, with this error:
Traceback (most recent call last):
File "python_array_answer.py", line 6, in <module>
hotel_data = json.loads(base64.b64decode(sys.argv[1]))
File "C:\Users\mghiglia\AppData\Local\Programs\Python\Python37\lib\base64.py", line 87, in b64decode
return binascii.a2b_base64(s)
binascii.Error: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4
Any ideas? thank.
Assuming the rest of the code is okay, you're using single quotes in this line
$command = escapeshellcmd('python python_array_answer.py $param');
where you should either use double quotes, so php known to replace $param with the value of variable $param, or concatenate the two like this...
$command = escapeshellcmd('python python_array_answer.py '.$param);

lldb python equivalent to gdb.GdbError()

With gdb python scripting, I can have a helper function throw an error for an unexpected condition:
def isFOOEnabled( ):
sb = gdb.parse_and_eval( "foo" )
if sb == 0x0:
raise gdb.GdbError( "Fatal error." )
return sb
I can catch the expression not evaluated error in lldb python like so:
def isFOOEnabled( ):
sb = lldb.frame.EvaluateExpression( "foo" )
if sb.GetValue() is None:
return 0
return sb
but I'd also like to force the script to abort like in my gdb version.
For exposition, here is an example of a call to gdb.GdbError:
(gdb) info zregisters
Traceback (most recent call last):
File "/home/pjoot/gdb/zinfo.py", line 157, in invoke
isFOOEnabled( )
File "/home/pjoot/gdb/apis/common.py", line 24, in isFOOEnabled
sb = gdb.parse_and_eval( "sysblk" )
gdb.error: No symbol "sysblk" in current context.
Error occurred in Python command: No symbol "sysblk" in current context.
(gdb)
after which you are back to the (gdb) shell, with a python stacktrace so that you know where things went wrong. Looks like the string parameter that gdb.GdbError takes is actually lost.
Is there a python lldb.* helper function to do that like gdb.GdbError()?

Initialize Objects with classes parsed with argparser

I wrote a Machine Learning script which I want to control from the command line. I already parsed all the options like for example --optimize 400, to perform 400 iterations over a RandomizedSearchCV grid.
However, I'm struggeling with one thing: I want to choose my estimator, for example GradientBoostingRegressor() or Lasso(), from the command line. I tried two things:
def cli()
p = arg.ArgumentParser(description="Perform ML regression.")
p.add_argument("-e","--estimator",default=Lasso(), help="Choose between Lasso() and GradientBoostingRegressor()")
return p.parse_args()
args = cli()
estimator = args.estimator
But when I try to open the program with:
python script.py -e GradientBoostingRegressor()
I get errors, because of the "()", and also without the (), because it gets interpreted as a string.
Another thing I tried is:
def cli()
p = arg.ArgumentParser(description="Perform ML regression.")
group = p.add_mutually_exclusive_group()
group.add_argument("-SVR", nargs='?', default = SVR(),
help="Choose Suppor Vector Regression")
group.add_argument("-GBR", nargs='?', default = GradientBoostingRegressor())
return p.parse_args()
args = cli()
But now I dont know how to access the estimator and also it seems like when I call the programm like this:
python script.py -SVR
the namespace "args" holds SVR=None and GBR=GradientBoostingRegressor(default_GBR_options), which is exactly the opposite to what I want.
Ideally I could choose between -SVR and -GBR in the command line and the parser would pass it just like my other options and I could initialize an object like this:
estimator = args.estimator
I hope anybody has some information on how to do that.
Thank you very much!
Arguments are always just strings. You need to parse the string to get a function which you can call.
import argparse
def Lasso():
print("Lasso!")
def GradientBoostingRegressor():
print("GradientBoostingRegressor!")
class GetEstimator(argparse.Action):
estimators = {
"Lasso": Lasso,
"GBR": GradientBoostingRegressor,
}
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, self.estimators[values])
p = argparse.ArgumentParser()
p.add_argument( "-e", "--estimator", action=GetEstimator, default=Lasso, choices=GetEstimaor.estimators.keys())
args = p.parse_args()
args.estimator()
(This replaces a previous answer that used the type parameter to map a string argument to a function. I misunderstood how type and choices interact.)
While #chepner's use of type is a nice use of argparse, the approach can be difficult to get right and understand.
As written it raises an error in the add_argument method:
Traceback (most recent call last):
File "stack50799294.py", line 18, in <module>
p.add_argument("-e", "--estimator", type=estimators.get, default=Lasso, choices=estimators.keys())
File "/usr/lib/python3.6/argparse.py", line 1346, in add_argument
type_func = self._registry_get('type', action.type, action.type)
File "/usr/lib/python3.6/argparse.py", line 1288, in _registry_get
return self._registries[registry_name].get(value, default)
TypeError: unhashable type: 'dict'
It's testing that the type parameter is either an item in the registry, or that it's a valid function. I'm not sure why it's raising this error.
def mytype(astr):
return estimators.get(astr)
works better in type=mytype. But there's further level of difficulty - choices is the keys(), strings. But mytype returns a class, producing an error like:
0942:~/mypy$ python3 stack50799294.py -e GBR
usage: stack50799294.py [-h] [-e {Lasso,GBR}]
stack50799294.py: error: argument -e/--estimator: invalid choice: <class '__main__.GradientBoostingRegressor'> (choose from 'Lasso', 'GBR')
To avoid those difficulties, I'd suggest separating the argument to class mapping. This should be easier to understand and to expand:
import argparse
class Lasso():pass
class GradientBoostingRegressor():pass
# Map an easy-to-type string to each function
estimators = {
'Lasso': Lasso,
'GBR': GradientBoostingRegressor
}
p = argparse.ArgumentParser(description="Perform ML regression.")
p.add_argument("-e", "--estimator", default='Lasso', choices=estimators.keys())
args = p.parse_args()
print(args)
estimator = estimators.get(args.estimator, None)
if estimator is not None:
print(type(estimator()))
samples:
0946:~/mypy$ python3 stack50799294.py -e GBR
Namespace(estimator='GBR')
<class '__main__.GradientBoostingRegressor'>
0946:~/mypy$ python3 stack50799294.py
Namespace(estimator='Lasso')
<class '__main__.Lasso'>
0946:~/mypy$ python3 stack50799294.py -e Lasso
Namespace(estimator='Lasso')
<class '__main__.Lasso'>
0946:~/mypy$ python3 stack50799294.py -e lasso
usage: stack50799294.py [-h] [-e {Lasso,GBR}]
stack50799294.py: error: argument -e/--estimator: invalid choice: 'lasso' (choose from 'Lasso', 'GBR')
const parameter
You could use store_const to choose between 2 classes, a default and a const:
parser.add_argument('-e', action='store_const', default=Lasso(), const=GradientBoostingRegressor())
but that can't be generalized to more. `nargs='?' provides a 3 way choice - default, const, and user provided. But there's still the problem of converting the commandline string to a class object.
Subparsers, https://docs.python.org/3/library/argparse.html#sub-commands, shows how set_defaults can be used to set functions or classes. But to use this you have to define a subparser for each choice.
In general it's better to start with the simple argparse approach, accepting strings and string choices, and doing the mapping after. Using more argparse features can come later.
get error
#chepner's error has something to do with how Python views the d.get method. Even though it looks like a method, it's somehow seeing the dict rather than the method:
In [444]: d = {}
In [445]: d.get(d.get)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-445-c6d679ba4e8d> in <module>()
----> 1 d.get(d.get)
TypeError: unhashable type: 'dict'
In [446]: def foo(astr):
...: return d.get(astr)
...:
...:
In [447]: d.get(foo)
That could be viewed as a basic python bug, or a argparse bug, but a user defined function or lambda is an easy work around.
Maybe you could separate input from functionality.
First collect the input from the user, then according to the input run the functionality that the user requested.
For example, if the user ran:
python script.py -e GradientBoostingRegressor
you would do:
if args.estimator == "GradientBoostingRegressor":
do stuff...

Weird invalid syntax in python

I'm trying to run a python script in the Mac Terminal with python test.py.
The problem is that I getting this error:
Traceback (most recent call last):
File "test.py", line 3, in <module>
import agent as a
File "/home/test/script/agent.py", line 5
budget: None
^
SyntaxError: invalid syntax
why this line budget: Noneis giving a SyntaxError?
the original code:
class Agent:
budget: None
budget_left: None
(....)
When I run the script in PyCharm works just fine.
It's giving a syntax error because, well, that is invalid syntax. You don't define attributes like that; you need to use assignment.
class Agent:
budget = None
budget_left = None
Note, though, defining these as class attributes probably isn't what you want.
Just put '=' for the variables and you are done.
budget = None
Read this for more on the topic:
https://www.digitalocean.com/community/tutorials/understanding-class-and-instance-variables-in-python-3

Python error: 'bool' not iterable

I'm trying to run a code:
import os
from os import listdir
for f in sorted(os.listdir("/path")):
if f in f.startswith("20"):
for f in sorted(os.listdir(f)):
if f.endswith(".txt"):
pass
else:
try:
os.system("/path/script.py %s" % f)
except:
pass
I have received this error:
Traceback (most recent call last):
File "files_correct_phase.py", line 5, in <module>
if f in f.startswith("20"):
TypeError: argument of type 'bool' is not iterable
code here
I ran it inside the python prompt and it worked fine after line 5, but when I run it as
python python_script.py
in the command line, it gives me this error. I would be grateful for any advice and/or help.
(Python version 2.7.6)
if f in f.startswith("20"):
is not valid. startswith returns a bool the in keyword trys to check for containment inside your bool. That only works for iterables (which bool is not). You probably want:
if f.startswith("20"):

Categories