Heisenberg strikes again!

N

neeson

Alright, I'm fairly new to Python, and this one has got me stumped.
I've just started to write a cli program that'll use readline, and
I've attached the relevant bits.

Here's the mystery: If I so much as /look/ at the list 'matches',
readline stops working. Run the program, hit tab at the prompt,
you'll get 'one', which is incorrect, as there are in fact four
possible completions. Now comment out the line that I've marked
(which, incidentally, does nothing). /Now/ it works.

Is there some very strange side effect to accessing a list element
that I'm unaware of? I've tried it in two different versions of
Python.

Any elightenment would be appreciated...

Heath

ps In terms of being useful, this program doesn't make any sense. I'm
not trying to get it to work, I'm looking to understand why the
commented line affects the rest of the code.


#!/usr/bin/python

import readline
import sys

commands = ["one", "two", "three", "four"]
matches = []

def comp(text, state):
if state == 0:
matches = []
n = len(text)
for cmd in commands:
if cmd[:n] == text:
matches.append(cmd)
throwaway = matches[0] # <--- Comment out this line
return commands[state]

readline.set_completer(comp)
readline.parse_and_bind("tab: complete")

while 1:
try:
line = raw_input("> ")
except EOFError:
print "\n",
sys.exit()
print ": %s" % line
 
R

rzed

neeson said:
Alright, I'm fairly new to Python, and this one has got me stumped.
I've just started to write a cli program that'll use readline, and
I've attached the relevant bits.

Here's the mystery: If I so much as /look/ at the list 'matches',
readline stops working. Run the program, hit tab at the prompt,
you'll get 'one', which is incorrect, as there are in fact four
possible completions. Now comment out the line that I've marked
(which, incidentally, does nothing). /Now/ it works.

Is there some very strange side effect to accessing a list element
that I'm unaware of? I've tried it in two different versions of
Python.

Any elightenment would be appreciated...

Heath

ps In terms of being useful, this program doesn't make any sense.
I'm not trying to get it to work, I'm looking to understand why the
commented line affects the rest of the code.


#!/usr/bin/python

import readline
import sys

commands = ["one", "two", "three", "four"]
matches = []

def comp(text, state):
if state == 0:
matches = []
n = len(text)
for cmd in commands:
if cmd[:n] == text:
matches.append(cmd)
throwaway = matches[0] # <--- Comment out this line

What is supposed to happen when state != 0? 'matches' will not exist
at that point.
return commands[state]

readline.set_completer(comp)
readline.parse_and_bind("tab: complete")

while 1:
try:
line = raw_input("> ")
except EOFError:
print "\n",
sys.exit()
print ": %s" % line
 
D

David C. Fox

neeson said:
Alright, I'm fairly new to Python, and this one has got me stumped.
I've just started to write a cli program that'll use readline, and
I've attached the relevant bits.

Here's the mystery: If I so much as /look/ at the list 'matches',
readline stops working. Run the program, hit tab at the prompt,
you'll get 'one', which is incorrect, as there are in fact four
possible completions. Now comment out the line that I've marked
(which, incidentally, does nothing). /Now/ it works.

Is there some very strange side effect to accessing a list element
that I'm unaware of? I've tried it in two different versions of
Python.

No, but there is a side effect of accessing the first element of an
empty list: namely, Python raises an IndexError exception (i.e. index
out of range).
Any elightenment would be appreciated...

Heath


import readline
import sys

commands = ["one", "two", "three", "four"]
matches = []

def comp(text, state):
if state == 0:
matches = []

Because you haven't used

global matches

the next statement creates a new list called matches as a variable local
to comp. All your matching commands are added to this variable, which
disappears when comp returns.

n = len(text)
for cmd in commands:
if cmd[:n] == text:
matches.append(cmd)

throwaway = matches[0] # <--- Comment out this line

When comp is called with state == 0, matches is still referring to the
local list which is not empty (unless there were no matches to the
text), so this line does indeed do nothing. However, when comp is
called with state != 0, this line refers to the global matches list,
which is empty, so it raises an IndexError, and the following line is
not reached.

Apparently, readline treats an exception in the completer as equivalent
to returning None. In fact, you seem to be relying on this behavior
implicitly in the line below, because you are not checking whether state
< len(commands)
return commands[state]

readline.set_completer(comp)
readline.parse_and_bind("tab: complete")

David
 
J

John J. Lee

David C. Fox said:
Apparently, readline treats an exception in the completer as
equivalent to returning None. In fact, you seem to be relying on this
[...]

That's pretty unpleasant. Naked except:s (with no specific exception)
can cause about the weirdest bugs it's possible to get in Python. I
think it's worth filing an SF documentation bug (or a patch, better)
about that: set_completer should mention it.

While debugging your completer function, try putting the whole content
of the function in a big try: except:, and use the traceback module:

from traceback import print_exc

def my_completer(text, state):
try:
# put everything here
...
except:
print_exc()


John
 
N

neeson

Thank you! That answers my questions precisely. :) You can see my
c/c++/java biases showing. Expecting global variables to carry into
functions and not expecting silent exceptions to alter my execution
paths. Valuable lessons!

Heath


David C. Fox said:
neeson said:
Alright, I'm fairly new to Python, and this one has got me stumped.
I've just started to write a cli program that'll use readline, and
I've attached the relevant bits.

Here's the mystery: If I so much as /look/ at the list 'matches',
readline stops working. Run the program, hit tab at the prompt,
you'll get 'one', which is incorrect, as there are in fact four
possible completions. Now comment out the line that I've marked
(which, incidentally, does nothing). /Now/ it works.

Is there some very strange side effect to accessing a list element
that I'm unaware of? I've tried it in two different versions of
Python.

No, but there is a side effect of accessing the first element of an
empty list: namely, Python raises an IndexError exception (i.e. index
out of range).
Any elightenment would be appreciated...

Heath


import readline
import sys

commands = ["one", "two", "three", "four"]
matches = []

def comp(text, state):
if state == 0:
matches = []

Because you haven't used

global matches

the next statement creates a new list called matches as a variable local
to comp. All your matching commands are added to this variable, which
disappears when comp returns.

n = len(text)
for cmd in commands:
if cmd[:n] == text:
matches.append(cmd)

throwaway = matches[0] # <--- Comment out this line

When comp is called with state == 0, matches is still referring to the
local list which is not empty (unless there were no matches to the
text), so this line does indeed do nothing. However, when comp is
called with state != 0, this line refers to the global matches list,
which is empty, so it raises an IndexError, and the following line is
not reached.

Apparently, readline treats an exception in the completer as equivalent
to returning None. In fact, you seem to be relying on this behavior
implicitly in the line below, because you are not checking whether state
< len(commands)
return commands[state]

readline.set_completer(comp)
readline.parse_and_bind("tab: complete")

David
 
D

Duncan Booth

throwaway = matches[0] # <--- Comment out this line

When comp is called with state == 0, matches is still referring to the
local list which is not empty (unless there were no matches to the
text), so this line does indeed do nothing. However, when comp is
called with state != 0, this line refers to the global matches list,
which is empty, so it raises an IndexError, and the following line is
not reached.

Not quite true there. Whether or not state is 0, this line always tries to
access the local variable 'matches'. If you assign to a variable anywhere
in a function (and don't declare it global), then all accesses in the
function are to the local variable, not the global.

Of course the effect is basically the same, the function throws an
exception either way, its just a different exception.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top