Raising errors in the context of python ternary operators - python

So let's say that I have all the necessary variables defined and I want to throw an error while assigning a variable with the conditional all in one line:
isTestData,isTrainingData,testData,trainingData=True,False,str,int
def whoops():
raise
a = testData() if isTestData else TrainingData() if isTrainingData else whoops()
I'm wondering if there is some builtin function or syntax with raise that I'm not getting... Is defining whoops necessary to get this behavior with a one liner?

The basic syntax of ternary operator(one liner) in python is:
<expr1> if <conditional_expr> else <expr2>
Which requires all the entities enclosed in <> to be an expression.
Note:
Expressions can only contain identifiers, operators and literals.
Statements are everything that can make a line/lines of python code.
Its important to note that all the expressions are statements but
not vice-versa.
In your case, you can't use raise directly in ternary operator(one liner) because it makes a statement.

Related

Is it legal to use return statement in exec built-in function? [duplicate]

I'm storing code snippets inside the Postgres DB. When I need the code, I find it inside the DB and use exec() function. The code snippet is a body of extract function.
Unfortunately it returns SyntaxError: 'return' outside function
Method
def extract(self,response):
exec(self.custom_code)
Code snippet (repr(code_snippet))
u"return response.xpath('/text()')"
I suppose that it should behave like this:
def extract(self,response):
return response.xpath('/text()')
What I should do?
This is just one line snippet and I need to execute multiline snippets.
EDIT:
I'm using Django with PostgreSQL and I realised that it strips spaces at the beginning of the line - indentation. I don't know if it has to do something with the problem.
EDIT2:
Tried eval instead of exec. Now it raises:
File "/home/milano/PycharmProjects/Stilio_project/stilio/engine/models.py", line 107, in extract
eval(self.custom_code)
File "<string>", line 1
return response.xpath('/text()')
^
SyntaxError: invalid syntax
Per the exec docs:
Be aware that the return and yield statements may not be used outside of function definitions even within the context of code passed to the exec statement.
So exec is explicitly off-limits. And that wording is global, not specific to exec; on checking, while eval using code compile-ed in 'single' mode has the same error; you can't dynamically insert return statements like this.
If you absolutely must allow executing arbitrary code, I strongly recommend limiting it to expressions, not statements, and implicitly returning the result of said expressions. So instead of storing u"return response.xpath('/text()')", you'd store u"response.xpath('/text()')", and your code that performs dynamic invocation would change to:
def extract(self,response):
return eval(self.custom_code)

How to evaluate a variable as an f-string?

I would like to have a mechanism which evaluates an f-string where the contents to be evaluated are provided inside a variable. For example,
x=7
s='{x+x}'
fstr_eval(s)
For the usage case I have in mind, the string s may arise from user input (where the user is trusted with eval).
While using eval in production is generally very bad practice, there are notable exceptions. For instance, the user may be a Python developer, working on a local machine, who would like to use full Python syntax to develop SQL queries.
Note on duplication: There are similar questions here and here. The first question was asked in the limited context of templates. The second question, although very similar to this one, has been marked as a duplicate. Because the context of this question is significantly different from the first, I decided to ask this third question based on the automatically-generated advice following the second question:
This question has been asked before and already has an answer. If
those answers do not fully address your question, please ask a new
question.
Even with a trusted user, using eval should only be a very last resort.
If you are willing to sacrifice flexibility of your syntax for a bit more security and control, then you could use str.format and provide it your whole scope.
This will disallow evaluation of expressions, but single variables will be formated into the output.
Code
x = 3
y = 'foo'
s = input('> ')
print(s.format(**vars()))
Example
> {x} and {y}
3 and foo
Here is my attempt at more robust evaluation of f-strings, inspired by kadee's elegant answer to a similar question.
I would however like to avoid some basic pitfalls of the eval approach. For instance, eval(f"f'{template}'") fails whenever the template contains an apostrophe, e.g. the string's evaluation becomes f'the string's evaluation' which evaluates with a syntax error. The first improvement is to use triple-apostrophes:
eval(f"f'''{template}'''")
Now it is (mostly) safe to use apostrophes in the template, as long as they are not triple-apostrophes. (Triple-quotes are however fine.) A notable exception is an apostrophe at the end of the string: whatcha doin' becomes f'''whatcha doin'''' which evaluates with a syntax error at the fourth consecutive apostrophe. The following code avoids this particular issue by stripping apostrophes at the end of the string and putting them back after evaluation.
import builtins
def fstr_eval(_s: str, raw_string=False, eval=builtins.eval):
r"""str: Evaluate a string as an f-string literal.
Args:
_s (str): The string to evaluate.
raw_string (bool, optional): Evaluate as a raw literal
(don't escape \). Defaults to False.
eval (callable, optional): Evaluation function. Defaults
to Python's builtin eval.
Raises:
ValueError: Triple-apostrophes ''' are forbidden.
"""
# Prefix all local variables with _ to reduce collisions in case
# eval is called in the local namespace.
_TA = "'''" # triple-apostrophes constant, for readability
if _TA in _s:
raise ValueError("Triple-apostrophes ''' are forbidden. " + \
'Consider using """ instead.')
# Strip apostrophes from the end of _s and store them in _ra.
# There are at most two since triple-apostrophes are forbidden.
if _s.endswith("''"):
_ra = "''"
_s = _s[:-2]
elif _s.endswith("'"):
_ra = "'"
_s = _s[:-1]
else:
_ra = ""
# Now the last character of s (if it exists) is guaranteed
# not to be an apostrophe.
_prefix = 'rf' if raw_string else 'f'
return eval(_prefix + _TA + _s + _TA) + _ra
Without specifying an evaluation function, this function's local variables are accessible, so
print(fstr_eval(r"raw_string: {raw_string}\neval: {eval}\n_s: {_s}"))
prints
raw_string: False
eval: <built-in function eval>
_s: raw_string: {raw_string}\neval: {eval}\n_s: {_s}
While the prefix _ reduces the likelihood of unintentional collisions, the issue can be avoided by passing an appropriate evaluation function. For instance, one could pass the current global namespace by means of lambda:
fstr_eval('{_s}', eval=lambda expr: eval(expr))#NameError: name '_s' is not defined
or more generally by passing suitable globals and locals arguments to eval, for instance
fstr_eval('{x+x}', eval=lambda expr: eval(expr, {}, {'x': 7})) # 14
I have also included a mechanism to select whether or not \ should be treated as an escape character via the "raw string literal" mechanism. For example,
print(fstr_eval(r'x\ny'))
yields
x
y
while
print(fstr_eval(r'x\ny', raw_string=True))
yields
x\ny
There are likely other pitfalls which I have not noticed, but for many purposes I think this will suffice.

python one-line if statement calling function if true

I'm using argparse.
def help():
parser.print_help()
sys.exit(0)
help() if (args.lock and args.unlock)
This gives me a syntax error. What is wrong with my if statement?
You are using a conditional expression: true_result if condition else false_result. A conditional expression requires an else part, because it has to produce a value; i.e. when the condition is false, there has to be an expression to produce the result in that case.
Don't use a conditional expression when all you want is a proper if statement:
if args.lock and args.unlock: help()

Python: How does multiple assignments in a single line work?

I know that assignment is a statement in Python, i.e., it doesn't evaluate to a value unlike an expression. How does the following line of code work in Python, then? Please explain what happens internally in the Python interpreter (lexing, parsing, formation of abstract syntax tree).
# this works
spam = eggs = 'ham'
# this doesn't work. Throws SyntaxError
spam = (eggs = 'ham')
why the first line above works while the second doesn't?
It's not about operator precedence. It's a designated syntax. It cannot be "reconcilliated" by adding parenthesis.
Now for the full answer (as #Rob's comments already indicate) see here and here.

What effect has a statement like 'var and do_something_with(var)' in Python?

While looking for some answers in a package source code (colander to be specific) I stumbled upon a string that I cannot comprehend. Also my PyCharm frowns on it with 'statement seems to have no effect'.
Here's the code abstract:
...
for path in e.paths():
keyparts = []
msgs = []
for exc in path:
exc.msg and msgs.extend(exc.messages()) # <-- what is that?
keyname = exc._keyname()
keyname and keyparts.append(keyname) # <-- and that
errors['.'.join(keyparts)] = '; '.join(interpolate(msgs))
return errors
...
It seems to be extremely pythonic and I want to master it!
UPD. So, as I see it's not pythonic at all - readability is harmed for the sake of shorthand.
If keyname evaluates to False, the and statement will return false immediately and not evaluate the second part. Otherwise, it will evaluate the second part (not that the return value matters in this case). So it's basically equivalent to:
if keyname:
keyparts.append(keyname)
I'm not sure that it's very pythonic though, since the the version I just suggested seem much more readable (to me personally, at least).
and and or are short-circuiting logical operators; which means as soon as Python knows what the answer must be, it stops evaluating any remaining clauses.
In the snippet you posted and is being used to guard the .extend() and .append() functions -- presumably the author does not want to post, for example, None into the lists.
I typically use this feature in if statements:
if name and name[0] in ('Mr', 'Mrs', 'Ms'):
...
name is a possibly empty list -- if it is empty, name[0] will fail with an IndexError, so I guard it with name and -- if name is empty, name[0] (and the if block) do not execute and the error is avoided.
This is a very Pythonic feature.
Since in python the first expression in an and statement is evaluated before the second and the interpreter breaks out of evaluating the and statement if the first expression is False,
keyname and keyparts.append(keyname)
is equivalent to:
if keyname:
keyparts.append(keyname)

Categories