Heisenberg strikes again!

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • neeson

    Heisenberg strikes again!

    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_co mpleter(comp)
    readline.parse_ and_bind("tab: complete")

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

    #2
    Re: Heisenberg strikes again!

    neeson wrote:[color=blue]
    > 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[/color]

    What is supposed to happen when state != 0? 'matches' will not exist
    at that point.
    [color=blue]
    > return commands[state]
    >
    > readline.set_co mpleter(comp)
    > readline.parse_ and_bind("tab: complete")
    >
    > while 1:
    > try:
    > line = raw_input("> ")
    > except EOFError:
    > print "\n",
    > sys.exit()
    > print ": %s" % line[/color]


    Comment

    • David C. Fox

      #3
      Re: Heisenberg strikes again!

      neeson wrote:
      [color=blue]
      > 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.[/color]

      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).
      [color=blue]
      > Any elightenment would be appreciated...
      >
      > Heath[/color]

      [color=blue]
      >
      > import readline
      > import sys
      >
      > commands = ["one", "two", "three", "four"]
      > matches = []
      >
      > def comp(text, state):
      > if state == 0:
      > matches = [][/color]

      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.

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

      [color=blue]
      > throwaway = matches[0] # <--- Comment out this line[/color]

      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)
      [color=blue]
      > return commands[state]
      >
      > readline.set_co mpleter(comp)
      > readline.parse_ and_bind("tab: complete")[/color]

      David

      Comment

      • John J. Lee

        #4
        Re: Heisenberg strikes again!

        "David C. Fox" <davidcfox@post .harvard.edu> writes:
        [...][color=blue]
        > Apparently, readline treats an exception in the completer as
        > equivalent to returning None. In fact, you seem to be relying on this[/color]
        [...]

        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(te xt, state):
        try:
        # put everything here
        ...
        except:
        print_exc()


        John

        Comment

        • neeson

          #5
          Re: Heisenberg strikes again!

          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" <davidcfox@post .harvard.edu> wrote in message news:<nRM7b.311 656$Oz4.100417@ rwcrnsc54>...[color=blue]
          > neeson wrote:
          >[color=green]
          > > 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.[/color]
          >
          > 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).
          >[color=green]
          > > Any elightenment would be appreciated...
          > >
          > > Heath[/color]
          >
          >[color=green]
          > >
          > > import readline
          > > import sys
          > >
          > > commands = ["one", "two", "three", "four"]
          > > matches = []
          > >
          > > def comp(text, state):
          > > if state == 0:
          > > matches = [][/color]
          >
          > 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.
          >
          >[color=green]
          > > n = len(text)
          > > for cmd in commands:
          > > if cmd[:n] == text:
          > > matches.append( cmd)[/color]
          >
          >[color=green]
          > > throwaway = matches[0] # <--- Comment out this line[/color]
          >
          > 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)
          >[color=green]
          > > return commands[state]
          > >
          > > readline.set_co mpleter(comp)
          > > readline.parse_ and_bind("tab: complete")[/color]
          >
          > David[/color]

          Comment

          • Duncan Booth

            #6
            Re: Heisenberg strikes again!

            "David C. Fox" <davidcfox@post .harvard.edu> wrote in
            news:nRM7b.3116 56$Oz4.100417@r wcrnsc54:
            [color=blue][color=green]
            >> throwaway = matches[0] # <--- Comment out this line[/color]
            >
            > 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.[/color]

            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.

            --
            Duncan Booth duncan@rcp.co.u k
            int month(char *p){return(1248 64/((p[0]+p[1]-p[2]&0x1f)+1)%12 )["\5\x8\3"
            "\6\7\xb\1\x9\x a\2\0\4"];} // Who said my code was obscure?

            Comment

            Working...