"a better input"

Andrew Dalke dalke at dalkescientific.com
Thu May 9 18:28:48 EDT 2002


Alex:
> >>> class X: pass
> ...
> >>> complex(X)
> Traceback (most recent call last):
>   File "<stdin>", line 1, in ?
> TypeError: complex() arg can't be converted to complex
> >>>
>
> X is not a complex literal, yet it doesn't give a ValueError.

Hmm...  As I recall the spec on which this was based was

Alexis Layton:
>> attempt to parse the string as a Python boolean, integer, real, or
>> complex* literal
>> otherwise return a the string

where the string came from raw_input() or some other input source.
Of course it could raise anything it wanted, including SystemExit
or KeyboardInterrupt or MemoryError.

> Now tell me again how to write a function that takes ANY object
> and just tell me if it's translatable into complex.  Are you SURE
> "except:" is not the right approach...?

There is a difference between "ANY object" and a string.  To me the
protocol for int/string/float/complex/long conversion is that it
takes a string (or other datatype) and either returns a value of that
type or raises a ValueError exception.  If it raises some other
exception then it doesn't meet the "can be tested to see if it supports
that type conversion" protocol.

That protocol is implicit, so I also wouldn't depend on it for
much.  Still, it's what I've used in my code and so far has worked
well enough for me and my clients.

>> There may be some exceptions those throw besides ValueError,

>If astr is a string, no.  But if you forget the parentheses on the
>call to raw_input, as I did (more bugs in my coding...), specific
>exception checking helps;-).

> > My worry about "except:" is that it ignores KeyboardInterrupt
> > (if done at just the right/wrong time) and gives false results

> Yeah, well, what's the vulnerability window?

Very small if done once.  A lot larger if done in a loop.  Here's
my test

def spam():
  infile = open("/boot/vmlinux-2.2.14-6.0")
  count = 0
  skipped_count = 0
  try:
    while 1:
      try:
        s = "1+2" + infile.read(1)
        if not s:
          break
        try:
          complex(s)
        except KeyboardInterrupt:
          print "Got one!"
          skipped_count += 1
        except:
          pass
      except KeyboardInterrupt:
        count += 1
  except KeyboardInterrupt:
    print "Caught outside the outside"
    print skipped_count, "skipped ^C of", count, float(skipped_count)/count
    raise
  print skipped_count, "skipped ^C of", count, float(skipped_count)/count

The raw output is at the end of this mail.  I found that about 1% of
the KeyboardInterrupts were masked

Alex:
> Sure.  But when you have NO idea about the argument, as in the
> first function, there's really no alternative.  You MIGHT catch
> KeyboardInterrupt earlier if you're the kind who wear braces, a
> belt, AND clean new undies at all times just in cases belt and
> braces both fail, though.

Hmm... I'm wearing neither belt nor braces right now.  No comment
on the undies :)

For a string I would only have ValueError.  For other objects, I would
be suspicious about the need for that sort of test in the first place
and suspicious about suppressing non-ValueError exceptions.  (I might
catch TypeError as well.)

But given that it should test any object and return True or False
depending on if the conversion was successful or not, then I agree,
there's no other way to handle that case other than a blanket "else:"
since any exception I can check can also be thrown by the __float__ /
__complex__ / __int__ ... methods.

                    Andrew
                    dalke at dalkescientific.com


  Here's the output of my test of the KeyboardInterrupt mask check code.

>>> import spam
>>> spam.spam()
^CCaught outside the outside
0 skipped ^C of 43 0.0
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "spam.py", line 6, in spam
    while 1:
KeyboardInterrupt
>>> spam.spam()
Got one!
Caught outside the outside
1 skipped ^C of 31 0.0322580645161
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "spam.py", line 6, in spam
    while 1:
KeyboardInterrupt
>>> spam.spam()
Caught outside the outside
0 skipped ^C of 56 0.0
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "spam.py", line 17, in spam
    pass
KeyboardInterrupt
>>>
KeyboardInterrupt
>>> spam.spam()
Caught outside the outside
0 skipped ^C of 14 0.0
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "spam.py", line 17, in spam
    pass
KeyboardInterrupt
>>>
KeyboardInterrupt
>>>
KeyboardInterrupt
>>> spam.spam()
Got one!
Got one!
Caught outside the outside
2 skipped ^C of 127 0.0157480314961
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "spam.py", line 17, in spam
    pass
KeyboardInterrupt
>>>







More information about the Python-list mailing list