I am trying to test some software using unittest with Python 2.2.2 (#37, Oct
14 2002, 17:02:34) [MSC 32 bit (Intel)] on win32 and I am having some
problems with exceptions:
The simple test script below yields:
E
======================================================================
ERROR: test_duffer (__main__.dufferTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\tedfrje\traders_toolkit\test_duffer.py", line 12, in test_duffer
self.assertRaises(ValueError(), duffer())
File "C:\tedfrje\traders_toolkit\test_duffer.py", line 7, in duffer
raise ValueError()
ValueError
----------------------------------------------------------------------
Ran 1 tests in 0.010s
FAILED (errors=1)
In my opinion, the script should PASS because I specified that a
ValueError() was the PASS criteria.
What am I doing wrong?
Test Script source:
"""
Try some unittest functions
"""
import unittest
def duffer():
raise ValueError()
class dufferTest(unittest.TestCase):
def test_duffer(self):
self.assertRaises(ValueError(), duffer())
def suite():
duffer_suite = unittest.makeSuite(dufferTest,'test')
return unittest.TestSuite((duffer_suite,))
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
# done.
When you raise an exception, you raise the exception CLASS, not an instance
of the exception:
def duffer():
- raise ValueError()
+ raise ValueError
class dufferTest(unittest.TestCase):
def test_duffer(self):
- self.assertRaises(ValueError(), duffer())
+ self.assertRaises(ValueError, duffer())
My guess is that somewhere in the bowels of assertRaises there's a test like
this:
if (<ExceptionClass in 1st argument> is <ExceptionClass received by
executing the 2nd argument>):
This test will fail, because in your case it's comparing two different
instances: (ValueError() is ValueError()) will *always* be false, whereas
(ValueError is ValueError) will *always* be true, because its the same
class, and the same entity in memory.
If you've gotten into the habit of raising exception instances rather than
classes, break it right now, or you'll be plagued with problems like this.
(Just a little time-saving tip: instead of setting up a suite, for simple
unittest files you can do unittest.main(), which will run every test in the
file whose name conforms to some lexical rules I do not now remember....)
--
Francis Avila
Frithiof> What am I doing wrong?
Try using just "ValueError", not "ValueError()" in a number of places.
Skip
> I am trying to test some software using unittest with Python 2.2.2 (#37, Oct
> 14 2002, 17:02:34) [MSC 32 bit (Intel)] on win32 and I am having some
> problems with exceptions:
>
> The simple test script below yields:
>
> E
> ======================================================================
> ERROR: test_duffer (__main__.dufferTest)
> ----------------------------------------------------------------------
> Traceback (most recent call last):
> File "C:\tedfrje\traders_toolkit\test_duffer.py", line 12, in test_duffer
> self.assertRaises(ValueError(), duffer())
> File "C:\tedfrje\traders_toolkit\test_duffer.py", line 7, in duffer
> raise ValueError()
> ValueError
>
> ----------------------------------------------------------------------
> Ran 1 tests in 0.010s
>
> FAILED (errors=1)
>
> In my opinion, the script should PASS because I specified that a
> ValueError() was the PASS criteria.
>
> What am I doing wrong?
assertRaises takes an exception *class* and a callable object
(usually a function or method).
you're passing in an exception instance and the *result* of your
duffer function.
getting rid of the extra parens fixes the problem:
self.assertRaises(ValueError, duffer)
</F>
if I remember correctly, it's even to go one further:
self.assertRaises(ValueError, differ)
as the second argument needs to be a callable, leaving
the possibility to pass arguments like:
self.assertRaises(ValueError, differ, arg1, arg2)
Karl
> Traceback (most recent call last):
> File "C:\tedfrje\traders_toolkit\test_duffer.py", line 12, in test_duffer
> self.assertRaises(ValueError(), duffer())
> File "C:\tedfrje\traders_toolkit\test_duffer.py", line 7, in duffer
> raise ValueError()
> ValueError
You probably need to change this line:
self.assertRaises(ValueError(), duffer())
to this:
self.assertRaises(ValueError, duffer)
> FAILED (errors=1)
>
> In my opinion, the script should PASS because I specified that a
> ValueError() was the PASS criteria.
In this case, the ValueError was not raised by "duffer" as intended.
Instead it was raised because you passed duffer() to assertRaises,
which expects a callable object.
Ah yes, this is correct. I was too focused on the ValueError problem....
--
Francis Avila
> "Frithiof Andreas Jensen" <frithio...@removethis.ted.ericsson.dk> wrote
> in message news:b9r15s$1v$1...@newstree.wise.edt.ericsson.se...
...
> > 14 2002, 17:02:34) [MSC 32 bit (Intel)] on win32 and I am having some
> > problems with exceptions:
> [...]
> > In my opinion, the script should PASS because I specified that a
> > ValueError() was the PASS criteria.
> >
> > What am I doing wrong?
...
> > def duffer():
> > raise ValueError()
> >
> >
> > class dufferTest(unittest.TestCase):
> > def test_duffer(self):
> > self.assertRaises(ValueError(), duffer())
...
> When you raise an exception, you raise the exception CLASS, not an instance
> of the exception:
>
> def duffer():
> - raise ValueError()
> + raise ValueError
>
>
> class dufferTest(unittest.TestCase):
> def test_duffer(self):
> - self.assertRaises(ValueError(), duffer())
> + self.assertRaises(ValueError, duffer())
>
>
> My guess is that somewhere in the bowels of assertRaises there's a test like
> this:
>
> if (<ExceptionClass in 1st argument> is <ExceptionClass received by
> executing the 2nd argument>):
>
> This test will fail, because in your case it's comparing two different
> instances: (ValueError() is ValueError()) will *always* be false, whereas
> (ValueError is ValueError) will *always* be true, because its the same
> class, and the same entity in memory.
>
>
> If you've gotten into the habit of raising exception instances rather than
> classes, break it right now, or you'll be plagued with problems like this.
Umm, since when is raising a instance deprecated? See:
http://www.python.org/dev/doc/devel/ref/raise.html#raise
In any case it shouldn't matter, e.g.:
% python
Python 2.2 (#1, Mar 12 2002, 10:45:35)
[GCC 3.0.4] on linux2
>>> class c: pass
...
>>> try:
... raise c
... except c,x:
... print 'x=',x
...
x= <__main__.c instance at 0x8171ca4>
>>> try:
... raise c()
... except c,x:
... print 'x=',x
...
x= <__main__.c instance at 0x8173a74>
In both cases the except clause gets an instance. It's just a different
way of initiating the exception.
--
I cannot think why the whole bed of the ocean is
not one solid mass of oysters, so prolific they seem. Ah,
I am wandering! Strange how the brain controls the brain!
-- Sherlock Holmes in "The Dying Detective"
> When you raise an exception, you raise the exception CLASS, not an instance
> of the exception:
"raise" takes either an instance or a class; in the latter case, it creates
the instance for you. in modern Python, the actual exception (exc_value)
is *always* an instance of a class:
http://www.python.org/doc/current/ref/raise.html
> def duffer():
> - raise ValueError()
> + raise ValueError
>
>
> class dufferTest(unittest.TestCase):
> def test_duffer(self):
> - self.assertRaises(ValueError(), duffer())
> + self.assertRaises(ValueError, duffer())
>
> My guess is that somewhere in the bowels of assertRaises there's a test like
> this:
>
> if (<ExceptionClass in 1st argument> is <ExceptionClass received by
> executing the 2nd argument>):
>
> This test will fail, because in your case it's comparing two different
> instances: (ValueError() is ValueError()) will *always* be false, whereas
> (ValueError is ValueError) will *always* be true, because its the same
> class, and the same entity in memory.
the exception machinery uses "isinstance", not "is". for details, see:
http://www.python.org/doc/current/ref/try.html
> (Just a little time-saving tip: instead of setting up a suite, for simple
> unittest files you can do unittest.main(), which will run every test in the
> file whose name conforms to some lexical rules I do not now remember....)
to save even more time, run modified code at least once before you
ship the product ;-)
</F>
Bad advice, I believe. When you raise an exception class, it is
converted to an exception instance which is tested against the classes
listed in except statements with isinstance(). It has been suggested
on PyDev that raising instances directly might someday (Python 3.0) be
required. IE, when raise string is disallowed, so might be raise
class.
TJR
And here I am writing a PEP based on a similar recent discussion
here in c.l.py.
I applied Google to mail.python.org, but was unable to find the
suggestion to which you refer. Can you narrow my search space?
--
Steven Taschuk stas...@telusplanet.net
Every public frenzy produces legislation purporting to address it.
(Kinsley's Law)
All I can find is
http://mail.python.org/pipermail/python-dev/2003-January/031907.html
[Python-Dev] Raising string exceptions
which only speaks of deprecating string exceptions, so I will have to
withdraw my claim about raising classes and py-dev.
Perhaps I mixed what I read with what I thought (and still think)
should also be done. IE, I find it a bit confusing that one can
create an exception instance either directly with
exceptionclass(initarg) or indirectly with exceptionclass, initarg,
which latter gets translated to the former. Two nearly identical ways
to do the same thing, one the standard way (elsewhere in the language)
and one unique to the particular context, seems rather useless. One
should know that the exception object one gets with 'except
exceptionclass, obj:' is an instance of exceptionclass and not
exceptionclass itself. This is much easier to remember if one raises
the instance directly instead of indirectly. My opinion, anyway,
based on once having been confused on this issue.
Terry J. Reedy
But that seems completely backwards to me. I thought raising instances was
depreciated (ok, "discouraged") when we switched over to exception classes
instead of strings. And so now we raise classes instead of (for now only
conceptually) instances of string objects. It would seem instead that
raising *instances* (including all the now-builtin types) would be
depreciated, and only classes could be raised, which are implicitly
instantiated before being tossed up. Is the suggestion really that every
raise statement be
raise Exception(arg)
instead of
raise Exception, arg
as we have now? I *never* see anyone raise a class instance explicitly, and
won't except statements *require* a class rather than a instance?
In any case, I was completely wrong about what the OP's problem was: the
entire issue was in the arguments to assertRaises, as Matt Brubrek
explained. Still, I thought I recalled getting burned by trying to raise an
instance instead of a class? Maybe I am mis-remembering, or it was some
other problem I didn't notice.
(What I mean by "raising an x" is that x is the expression in the raise
statement, not that x is what is actually being associated with the
traceback. I'm getting the impression that I'm not using this phrase the
same way everyone else is....)
--
Francis Avila
I entirely agree.
(And your reasons are in my draft PEP. Stay tuned!)
--
Steven Taschuk 7\ 7'Z {&~ .
stas...@telusplanet.net Y r --/hG-
(__/ )_ 1^1`
This is how one creates a class instance everywhere else in Python,
and optionally here.
> instead of
>
> raise Exception, arg
This is the only place in Python where a class instance is created
with this syntax.
So yes, I would like to see this exceptional and unnecessary duplicate
syntax considered for deprecation (along with string exceptions).
> won't except statements *require* a class rather than a instance?
Except statements 'receive' a class instance which is checked against
the class-or-tuple with isinstance(instance, class-or-tuple). Again,
the exception object bound to the name you give after the ',' is the
instance, not its class. I find it easier to remember this if I
directly create the instance in the first place. The time saving of
typing ', ' instead of '(' and ')' is not, to me, worth the confusion.
> In any case, I was completely wrong about what the OP's problem was:
the
Yeah, this is off-topic, but I think he got enough on-topic advice to
move forward, at least to his next problem.
Terry J. Reedy
"Terry Reedy" <tjr...@udel.edu> wrote in message
news:AMadnT1A-6b...@comcast.com...
>
> "Francis Avila" <franci...@yahoo.com> wrote in message
> news:vc2snua...@corp.supernews.com...
> >
> > instantiated before being tossed up. Is the suggestion really
that
> every
> > raise statement be
> >
> > raise Exception(arg)
>
> This is how one creates a class instance everywhere else in Python,
> and optionally here.
>
> > instead of
> >
> > raise Exception, arg
>
> This is the only place in Python where a class instance is created
> with this syntax.
> So yes, I would like to see this exceptional and unnecessary
duplicate
> syntax considered for deprecation (along with string exceptions).
This syntax is a holdover from string exceptions for things like:
MyExcept = 'MyException'
...
raise MyExcept, 'This loop is taking too long...'
It is needed now for backward compatibility, but when string
exceptions go, it will no longer be *needed*
> > won't except statements *require* a class rather than a instance?
>
> Except statements 'receive' a class instance which is checked
against
> the class-or-tuple with isinstance(instance, class-or-tuple).
Again,
> the exception object bound to the name you give after the ',' is the
> instance, not its class. I find it easier to remember this if I
> directly create the instance in the first place. The time saving of
> typing ', ' instead of '(' and ')' is not, to me, worth the
confusion.
You were more right. Apparently, from reading RefMan 7.4, the test is
issubclass(class or instance.__class__, class_or_tuple).
The manual is not completely consistent: 4.2 says "Exceptions are
identified by string objects or class instances" whereas 6.9 says "If
the first object is an instance, the type of the exception is the
class of the instance". 7.4 then says "An object is compatible with
an exception if it is either the object that identifies the
exception..." If it were really true that a class instance can
'identify' an exception, then 'except instance' should work, but it
does not, and so 4.2 seems to be wrong.
To me, the root problem is trying to explain two different exception
systems with one set of words. An exception string indentifier and an
exception string value are both the same type: strings. But an
exception class and an exception class instance are different types.
Calling a class and its instance an 'identifier' and a 'value' is
something of a stretch. It certainly is a confusing overload of terms
that have other well-defined meanings in Python.
Terry J. Reedy
Thinking a bit more, I am sure there is another possible deprecation
that *has* been discussed -- that of exception classes that do not
inheret from Exception or an existing subclass thereof.
TJR
Thanks for the help!
Just to be sure ;-)
Is it legal, moral and commendable to raise exceptions like: 'raise
ValueError('some explanation text')?
and in unittest one would test them with:
assertRaises(ValueError, function, (tuple-of-parameters) )
This seems to work, at least?
For the dubious: Ref Manual, 6.9 The raise statement
"Otherwise, raise evaluates its first expression, which must yield a
string, class, or instance object. .... If the first expression is an
instance object, the second expression must be None." [or omitted,
unless third expression is present]
try:
raise Exception("Test of instance raising")
except Exception, haha:
print haha, type(haha)
#prints
Test of instance raising <type 'instance'>
Terry J. Reedy
Terry J. Reedy
I'm not sure what you are demonstrating, other than that it's legal ;-) I.e.,
you would get the same haha instance if you raised Exception with a comma and the string.
Anyway, some additional things to notice:
>>> class Classic:
... def __init__(self, *args): self.args = args
... def __str__(self): return '<[Classic %r]>' % `self.args`
...
>>> class NewStyle(object):
... def __init__(self, *args): self.args = args
... def __str__(self): return '<[NewStyle %r]>' % `self.args`
...
>>> for x in [Classic, Classic(1), Classic(1,2,3), NewStyle, NewStyle(1,2,3)]:
... try:
... raise x
... except Classic, e:
... print '%s! : %s\n args: %r' % (e.__class__.__name__, e, e.args)
... except Exception, e:
... print '%s! : %s\n args: %r' % (e.__class__.__name__, e, e.args)
... except:
... print sys.exc_info()
...
Classic! : <[Classic '()']>
args: ()
Classic! : <[Classic '(1,)']>
args: (1,)
Classic! : <[Classic '(1, 2, 3)']>
args: (1, 2, 3)
TypeError! : exceptions must be strings, classes, or instances, not type
args: ('exceptions must be strings, classes, or instances, not type',)
TypeError! : exceptions must be strings, classes, or instances, not NewStyle
args: ('exceptions must be strings, classes, or instances, not NewStyle',)
Regards,
Bengt Richter