python eval and string indexing - python

Let's say I have a string
string = '1234567890'
and I want a slice of that string defined by another string
slice = '5:8'
This is easy to do with
>>>string[5:8]
'678'
However the slice is passed in through a file and changes on user input. Is their a way of doing something such as
>>>string[eval(slice)]
'678'
When I do this I get
5:8
^
SyntaxError: invalid syntax
I have a function that accounts for all four cases, I was just wondering if their was a more elegant way of doing this.
Thanks for your answers.

You are getting the syntax error since 5:8 isn't a valid Python statement on its own; eval expects normal Python code, not just fragments.
If you really want to use eval, you can say:
string = '1234567890'
sliceInput = '5:8'
result = eval('string[' + sliceInput + ']')
However this is not at all secure if you're allowing user input. A safer way would be:
string = '1234567890'
sliceInput = '5:8'
sliceParts = sliceInput.split(':')
if len(sliceParts) != 2:
# Invalid input -- either no ':' or too many
else:
try:
start, end = [ int(x) for x in sliceParts ]
except ValueError:
# Invalid input, not a number
else:
result = string[start : end]
Note that slice() is a built-in Python function, so it isn't considered good practice to use it as a variable name.

How about:
string = '1234567890'
slice = '5:8'
sliceP = slice.split(':')
string[int(sliceP[0]):int(sliceP[1])]

The slice syntax isn't permitted outside of brackets, so it will break if you try to eval it on its own. If you really want to eval input from a file, you can construct the complete call as a string, then eval it:
eval("string[" + slice + "]")
The usual caveats about eval apply: a malicious user can get your program to execute arbitrary code this way, so you might be better off trying to parse out the bounds rather than evaling them.

Related

Why does f-string literal not work here, whereas %()s formatting does?

I am trying to format my validator message with the min/max values in the actual validator.
Here's my Flask Form:
class MyForm(FlaskForm):
example = IntegerField(label=('Integer 0-10'),
validators=[InputRequired(), NumberRange(min=0, max=10, message="must be between %(min)s and %(max)s!")])
Using message="must be between %(min)s and %(max)s!" gives me the expected output:
must be between 0 and 10!
Whereas using message=f"must be between {min} and {max}!" gives me the output:
must be between <built-in function min> and <built-in function max>!
How can I use f-string formatting for my validator message? Is this something related to f-string evaluating at run-time? I don't fully understand the concept behind it, I just know it's the preferred way to string format.
The f-string literal is evaluated immediately, before being passed to IntegerField.
>>> foo = 3
>>> print(f'{foo}')
3
The other string contains literal %(...) substrings which are
used later with the % operator.
>>> print("%(foo)s")
%(foo)s
>>> print("%(foo)s" % {'foo': 3})
3
"must be between %(min)s and %(max)s!" is a string literal that Flask will later perform a search-and-replace on, while f"must be between {min} and {max}!" is a simpler and more efficient way to say "must be between " + str(min) + " and " + str(max) + "!". That evaluates to the string you described.
You must declare such variables, like
min = 1
max = 2
print(f"must be between {min} and {max}!")
But please consider to use somewhat different variable names to not shadow builtin functions.
Ok, I see it now, you wanted to use that as a kind of string template.

How to say in Pythonese - do something unless it causes an error (without resorting to multilevel try/execpt blocks)

This is a little difficult to explain, so let's hope I'm expressing the problem coherently:
Say I have this list:
my_list = ["a string", 45, 0.5]
The critical point to understand in order to see where the question comes from is that my_list is generated by another function; I don't know ahead of time anything about my_list, specifically its length and the datatype of any of its members.
Next, say that every time <my_list> is generated, there is a number of predetermined operations I want to perform on it. For example, I want to:
my_text = my_list[1]+"hello"
some_var = my_list[10]
mini_list = my_list[0].split('s')[1]
my_sum = my_list[7]+2
etc. The important point here is that it's a large number of operations.
Obviously, some of these operations would succeed with any given my_list and some would fail and, importantly, those which fail will do so with an unpredictable Error type; but I need to run all of them on every generation of my_list.
One obvious solution would be to use try/except on each of these operations:
try:
my_text = my_list[1]+"hello"
except:
my_text = "None"
try:
some_var = my_list[10]
except:
some_var = "couldn't do it"
etc.
But with a large number of operations, this gets very cumbersome. I looked into the various questions about multiple try/excepts, but unless I'm missing something, they don't address this.
Based on someone's suggestion (sorry, lost the link), I tried to create a function with a built-in try/except, create another list of these operations, and send each operation to the function. Something along the lines of
def careful(op):
try:
return op
else:
return "None"
And use it with, for example, the first operation:
my_text = careful(my_list[1]+"hello")
The problem is python seems to evaluate the careful() argument before it's sent out to the function and the error is generated before it can be caught...
So I guess I'm looking for a form of a ternary operator that can do something like:
my text = my_list[1]+"hello" if (this doesn't cause any type of error) else "None"
But, if one exist, I couldn't find it...
Any ideas would be welcome and sorry for the long post.
Maybe something like this?
def careful(op, default):
ret = default
try:
ret = computation()
else:
pass
return ret
If you must do this, consider keeping a collection of the operations as strings and calling exec on them in a loop
actions = [
'my_text = my_list[1]+"hello"',
'some_var = my_list[10]',
'mini_list = my_list[0].split("s")[1]',
'my_sum = my_list[7]+2',
]
If you make this collection a dict, you may also assign a default
Note that if an action default (or part of an action string) is meant to be a string, it must be quoted twice. Consider using block-quotes for this if you already have complex escaping, like returning a raw strings or a string representing a regular expression
{
"foo = bar": r"""r'[\w]+baz.*'"""
}
complete example:
>>> actions_defaults = {
... 'my_text = my_list[1]+"hello"': '"None"',
... 'some_var = my_list[10]': '"couldn\'t do it"',
... 'mini_list = my_list[0].split("s")[1]': '"None"',
... 'my_sum = my_list[7]+2': '"None"',
... }
>>>
>>> for action, default in actions_defaults.items():
... try:
... exec(action)
... except Exception: # consider logging error
... exec("{} = {}".format(action.split("=")[0], default))
...
>>> my_text
'None'
>>> some_var
"couldn't do it"
Other notes
this is pretty evil
declaring your vars before running to be their default values is probably better/clearer (sufficient to pass in the except block, as the assignment will fail)
you may run into weird scoping and need to access some vars via locals()
This sounds like an XY Problem
If you can make changes to the source logic, returning a dict may be a much better solution. Then you can determine if a key exists before doing some action, and potentially also look up the action which should be taken if the key exists in another dict.

Is it possible to reuse an f-string as it is possible with a string and format?

I would like to create an f-string that could be used multiple times as the following code with format:
TEXT_AMOUNT = 'text {amount}'
def f1(beginning):
return beginning + TEXT_AMOUNT.format(amount=10)
def f2(ending):
return TEXT_AMOUNT.format(amount=100) + ending
How can I achieve the same functionality with using an f-string? I tried:
TEXT_AMOUNT = f'text {amount}'
def f1(beginning):
amount = 100
return beginning + TEXT_AMOUNT
def f2(ending):
amount = 10
return TEXT_AMOUNT + ending
However, I get the following error:
NameError: name 'amount' is not defined
You can store it as lambda function:
TEXT_AMOUNT = lambda amount: f'text {amount}'
print(TEXT_AMOUNT(10))
out: 'text 10'
You can't.
An f-string isn't a kind of string, it's a kind of string literal, which is evaluated immediately. You can't store an f-string in a variable to be evaluated later, or accept one from a user, etc.1 This is the only reason that they're safe.
So, what if you do want to use a format multiple times (or one taken from a user, etc.)? You use str.format.
Occasionally, you need to capture all of the locals and globals the same way an f-string does, but to do it explicitly. Because this is a rare case (and potentially a security hole), it's intentionally a bit ugly:
TEXT_AMOUNT = 'text {amount}'
def f1(beginning):
amount = 100
return beginning + TEXT_AMOUNT.format(**locals(), **globals())
This makes you think about what you're writing—you don't really want globals here, right? So leave it off. And it also signals the reader—if you're pulling in locals, they'll want to see that the string really is a constant in your source that isn't doing anything dangerous.
1. Well, you could use an f-string inside a string that you pass to eval… but that's a terrible idea.
But you can put f-string in a function and reuse it that way
def TEXT_AMOUNT(amount):
return f'text {amount}'
def f1(beginning):
return beginning + " " + TEXT_AMOUNT(amount=10)
def f2(ending):
return TEXT_AMOUNT(amount=100) + " " + ending
print(f1("first"))
print(f2("last"))
first text 10
text 100 last

How to get the value of the last assigned variable in iPython?

I am a total iPython newbie, but I was wondering if there is a way to get the value of the last assigned variable:
In [1]: long_variable_name = 333
In [2]: <some command/shortcut that returns 333>
In R we have .Last.value:
> long_variable_name = 333
> .Last.value
[1] 333
There's a shortcut for the last returned object, _.
In [1]: 1 + 3
Out[1]: 4
In [2]: _
Out[2]: 4
You can use IPython's In and Out variables which contain the commands/statements entered and the the corresponding output (if any) of those statements.
So, a naive approach would be to use those variables as the basis of defining a %last magic method.
However, since not all statements necessarily generate output, In and Out are not synchronous.
So, the approach I came up with was to parse In, and look for the occurrences of = and parse those lines for the output:
def last_assignment_value(self, parameter_s=''):
ops = set('()')
has_assign = [i for i,inpt in enumerate(In) if '=' in inpt] #find all line indices that have `=`
has_assign.sort(reverse=True) #reverse sort, because the most recent assign will be at the end
for idx in has_assign:
inpt_line_tokens = [token for token in In[idx].split(' ') if token.strip() != ''] #
indices = [inpt_line_tokens.index(token) for token in inpt_line_tokens if '=' in token and not any((c in ops) for c in token)]
#Since assignment is an operator that occurs in the middle of two terms
#a valid assignment occurs at index 1 (the 2nd term)
if 1 in indices:
return ' '.join(inpt_line_tokens[2:]) #this simply returns on the first match with the above criteria
And, lastly to make that your own custom command in IPython:
get_ipython().define_magic('last', last_assignment_value)
And, now you can call:
%last
And this will output the term assigned as a string (which may not be what you want).
However, there is a caveat to this: in that if you had entered incorrect input that involved assignment; e.g.: (a = 2), this method will pick it up. And, if your assignment involved variables: e.g. a = name, this method will return name and the not the value of name.
Given that limitation, you can then use the parser module to try and evaluate the expression like this (which can be appended to last_assignment_value in the last if statement):
import parser
def eval_expression(src):
try:
st = parser.expr(src)
code = st.compile('obj.py')
return eval(code)
except SyntaxError:
print 'Warning: there is a Syntax Error with the RHS of the last assignment! "%s"' % src
return None
However, given the possible evils of eval, I've left that inclusion up to you.
But, to be perfectly honest, a truly wholesome method would involve a parsing of the statement to verify the validity of the found input, as well as the input before it and more.
REFERENCES:
https://gist.github.com/fperez/2396341

Defining dynamic functions to a string

I have a small python script which i use everyday......it basically reads a file and for each line i basically apply different string functions like strip(), replace() etc....im constanstly editing the file and commenting to change the functions. Depending on the file I'm dealing with, I use different functions. For example I got a file where for each line, i need to use line.replace(' ','') and line.strip()...
What's the best way to make all of these as part of my script? So I can just say assign numbers to each functions and just say apply function 1 and 4 for each line.
First of all, many string functions – including strip and replace – are deprecated. The following answer uses string methods instead. (Instead of string.strip(" Hello "), I use the equivalent of " Hello ".strip().)
Here's some code that will simplify the job for you. The following code assumes that whatever methods you call on your string, that method will return another string.
class O(object):
c = str.capitalize
r = str.replace
s = str.strip
def process_line(line, *ops):
i = iter(ops)
while True:
try:
op = i.next()
args = i.next()
except StopIteration:
break
line = op(line, *args)
return line
The O class exists so that your highly abbreviated method names don't pollute your namespace. When you want to add more string methods, you add them to O in the same format as those given.
The process_line function is where all the interesting things happen. First, here is a description of the argument format:
The first argument is the string to be processed.
The remaining arguments must be given in pairs.
The first argument of the pair is a string method. Use the shortened method names here.
The second argument of the pair is a list representing the arguments to that particular string method.
The process_line function returns the string that emerges after all these operations have performed.
Here is some example code showing how you would use the above code in your own scripts. I've separated the arguments of process_line across multiple lines to show the grouping of the arguments. Of course, if you're just hacking away and using this code in day-to-day scripts, you can compress all the arguments onto one line; this actually makes it a little easier to read.
f = open("parrot_sketch.txt")
for line in f:
p = process_line(
line,
O.r, ["He's resting...", "This is an ex-parrot!"],
O.c, [],
O.s, []
)
print p
Of course, if you very specifically wanted to use numerals, you could name your functions O.f1, O.f2, O.f3… but I'm assuming that wasn't the spirit of your question.
If you insist on numbers, you can't do much better than a dict (as gimel suggests) or list of functions (with indices zero and up). With names, though, you don't necessarily need an auxiliary data structure (such as gimel's suggested dict), since you can simply use getattr to retrieve the method to call from the object itself or its type. E.g.:
def all_lines(somefile, methods):
"""Apply a sequence of methods to all lines of some file and yield the results.
Args:
somefile: an open file or other iterable yielding lines
methods: a string that's a whitespace-separated sequence of method names.
(note that the methods must be callable without arguments beyond the
str to which they're being applied)
"""
tobecalled = [getattr(str, name) for name in methods.split()]
for line in somefile:
for tocall in tobecalled: line = tocall(line)
yield line
It is possible to map string operations to numbers:
>>> import string
>>> ops = {1:string.split, 2:string.replace}
>>> my = "a,b,c"
>>> ops[1](",", my)
[',']
>>> ops[1](my, ",")
['a', 'b', 'c']
>>> ops[2](my, ",", "-")
'a-b-c'
>>>
But maybe string descriptions of the operations will be more readable.
>>> ops2={"split":string.split, "replace":string.replace}
>>> ops2["split"](my, ",")
['a', 'b', 'c']
>>>
Note:
Instead of using the string module, you can use the str type for the same effect.
>>> ops={1:str.split, 2:str.replace}
To map names (or numbers) to different string operations, I'd do something like
OPERATIONS = dict(
strip = str.strip,
lower = str.lower,
removespaces = lambda s: s.replace(' ', ''),
maketitle = lamdba s: s.title().center(80, '-'),
# etc
)
def process(myfile, ops):
for line in myfile:
for op in ops:
line = OPERATIONS[op](line)
yield line
which you use like this
for line in process(afile, ['strip', 'removespaces']):
...

Categories