I am trying to overload new and this is my attempt:-
class String(object):
def __new__(cls, *args, **kargs):
print "in __new__"
return super(String, cls).__new__(cls)
def __init__(self):
print "Initiating instance of String"
raise Exception
def __del__(self):
print "Deleting instance of String"
I read at many places that actually __new__ create the instance and __init__ is just to initialize the instance. I am deliberately throwing exception in __init__ to let it fail. Here call to new returns the instance but init fails, so I am expecting an instance which won't have any attributes. But result surprised me like anything -
st = String()
in __new__
Initiating instance of String
Traceback (most recent call last):
File "<pyshell#89>", line 1, in <module>
st = String()
File "<pyshell#88>", line 7, in __init__
raise Exception
As expected it failed in __init__, Next I tried to print newly created instance 'st' and result surprised me, it deleted instance before printing.
>>> print st
**Deleting instance of String**
Traceback (most recent call last):
File "<pyshell#90>", line 1, in <module>
print st
NameError: name 'st' is not defined
Please help me to understand this strange behavior.
Note - I know when we should overload __new__ and when we shouldn't.
Exception was raised before Python managed to assign the object in memory to the the variable name. Therefore the reference counter of the object in memory is 0, so it got 'garbage collected', and you got name is not defined exception.
Update for the comment:
Python runs GC when it tries to allocate memory for a new object, if the threshold is filled. In your case it's probably the threshold for generation 0 that was reached, because it's where that new 'failed' String object should have been. And the event itself that triggered GC was memory allocation to execute print.
Related
I am playing with pyelliptic, but I can't use the method raw_get_ecdh_key defined in the file ecc.py.
Here's my example code:
>>> import pyelliptic
>>> pubkey1="0453eb8248f42b00d057bc1adc724c4c841ec75851d6cd86f56f9bae5223219c7d3c7aff832d2383dfec167327ef38e9bf066690a8f94c055b336a607cebc2dddf".decode('hex')
>>> pubkey2="04678b95e902b04817ba258ecd3dbb2150d83850848a3b8523c11d51b6a9f89b93ea82db74ba82ba44fadb050b35eae8b96f3738e88c7c117227303a528c8df985".decode('hex')
>>> pyelliptic.ECC.raw_get_ecdh_key(pubkey1, pubkey2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method raw_get_ecdh_key() must be called with ECC instance as first argument (got str instance instead)
I searched and found many questions on the subject here. I learned that I need to call ECC() instead of ECC, but it doesn't work any better:
>>> pyelliptic.ECC().raw_get_ecdh_key(pubkey1, pubkey2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/dist-packages/pyelliptic/ecc.py", line 324, in raw_get_ecdh_key
OpenSSL.EC_KEY_free(own_key)
UnboundLocalError: local variable 'own_key' referenced before assignment
I can't figure what's wrong. I checked and the variable own_key should be referenced.
It's a very obvious bug in pyelliptic - if you look at the code, the part where own_key is defined (ecc.py line 301) is wrapped in huge try/finally block. If an exception is raised (and not managed) before line 301 (which is obviously what happened to you), then the execution flow jumps to the finally block, which to tries to reference own_key which at this point has not yet been defined.
You can fix by cloning the git repo and editing the raw_get_ecdh_key function's code so 1. the relevant local variables are defined at the beginning of the function before the try block (setting them to None) and 2. in the finally block these variables are tested against None before trying to use them to free resources, ie (untested):
def raw_get_ecdh_key(self, pubkey_x, pubkey_y):
other_key = None
other_pub_key_x = None
other_pub_key_y = None
other_pub_key = None
own_key = None
own_priv_key = None
try:
# (snip code)
finally:
if other_key is not None:
OpenSSL.EC_KEY_free(other_key)
if other_pub_key_x is not None:
OpenSSL.BN_free(other_pub_key_x)
if other_pub_key_y is not None:
OpenSSL.BN_free(other_pub_key_y)
if other_pub_key is not None:
OpenSSL.EC_POINT_free(other_pub_key)
if own_key is not None:
OpenSSL.EC_KEY_free(own_key)
if own_priv_key is not None:
OpenSSL.BN_free(own_priv_key)
And then run the the unittests (eventually adding some tests for this case) and submit a pull request to the author.
Note that you'll have applied this (untested) fix you should have another error - the one that sent you in the finally block at first - but at least you should then have more infos about why the call failed.
Not sure how possible this is, but here goes:
I'm trying to write an object with some slightly more subtle behavior - which may or may not be a good idea, I haven't determined that yet.
I have this method:
def __getattr__(self, attr):
try:
return self.props[attr].value
except KeyError:
pass #to hide the keyerror exception
msg = "'{}' object has no attribute '{}'"
raise AttributeError(msg.format(self.__dict__['type'], attr))
Now, when I create an instance of this like so:
t = Thing()
t.foo
I get a stacktrace containing my function:
Traceback (most recent call last):
File "attrfun.py", line 23, in <module>
t.foo
File "attrfun.py", line 15, in __getattr__
raise AttributeError(msg.format(self._type, attr))
AttributeError: 'Thing' object has no attribute 'foo'
I don't want that - I want the stack trace to read:
Traceback (most recent call last):
File "attrfun.py", line 23, in <module>
t.foo
AttributeError: 'Thing' object has no attribute 'foo'
Is this possible with a minimal amount of effort, or is there kind of a lot required? I found this answer which indicates that something looks to be possible, though perhaps involved. If there's an easier way, I'd love to hear it! Otherwise I'll just put that idea on the shelf for now.
You cannot tamper with traceback objects (and that's a good thing). You can only control how you process one that you've already got.
The only exceptions are: you can
substitute an exception with another or re-raise it with raise e (i.e make the traceback point to the re-raise statement's location)
raise an exception with an explicit traceback object
remove outer frame(s) from a traceback object by accessing its tb_next property (this reflects a traceback object's onion-like structure)
For your purpose, the way to go appears to be the 1st option: re-raise an exception from a handler one level above your function.
And, I'll say this again, this is harmful for yourself or whoever will be using your module as it deletes valuable diagnostic information. If you're dead set on making your module proprietary with whatever rationale, it's more productive for that goal to make it a C extension.
The traceback object is created during stack unwinding, not directly when you raise the exception, so you can not alter it right in your function. What you could do instead (though it's probably a bad idea) is to alter the top level exception hook so that it hides your function from the traceback.
Suppose you have this code:
class MagicGetattr:
def __getattr__(self, item):
raise AttributeError(f"{item} not found")
orig_excepthook = sys.excepthook
def excepthook(type, value, traceback):
iter_tb = traceback
while iter_tb.tb_next is not None:
if iter_tb.tb_next.tb_frame.f_code is MagicGetattr.__getattr__.__code__:
iter_tb.tb_next = None
break
iter_tb = iter_tb.tb_next
orig_excepthook(type, value, traceback)
sys.excepthook = excepthook
# The next line will raise an error
MagicGetattr().foobar
You will get the following output:
Traceback (most recent call last):
File "test.py", line 49, in <module>
MagicGetattr().foobar
AttributeError: foobar not found
Note that this ignores the __cause__ and __context__ members of the exception, which you would probably want to visit too if you were to implement this in real life.
You can get the current frame and any other level using the inspect module. For instance, here is what I use when I'd like to know where I'm in my code :
from inspect import currentframe
def get_c_frame(level = 0) :
"""
Return caller's frame
"""
return currentframe(level)
...
def locate_error(level = 0) :
"""
Return a string containing the filename, function name and line
number where this function was called.
Output is : ('file name' - 'function name' - 'line number')
"""
fi = get_c_frame(level = level + 2)
return '({} - {} - {})'.format(__file__,
fi.f_code,
fi.f_lineno)
Is it fine to raise an exception in __init__ in python? I have this piece of code:
class VersionManager(object):
def __init__(self, path):
self._path = path
if not os.path.exists(path): os.mkdir(path)
myfunction(path)
The second line can potentially result in an exception. In that case the object will not be init'ed properly. Is there a better way to handle situations where code in __init__ might throw an exception?
EDIT
Added a call to a function after os.mkdir
Added a check to see if directory exists
It is perfectly fine to raise an exception in __init__. You would then wrap the object initiation/creation call with try/except and react to the exception.
One potential odd result though is that __del__ is run anyway:
class Demo(object):
def __init__(self, value):
self.value=value
if value==2:
raise ValueError
def __del__(self):
print '__del__', self.value
d=Demo(1) # successfully create an object here
d=22 # new int object labeled 'd'; old 'd' goes out of scope
# '__del__ 1' is printed once a new name is put on old 'd'
# since the object is deleted with no references
Now try with the value 2 that we are testing for:
Demo(2)
Traceback (most recent call last):
File "Untitled 3.py", line 11, in <module>
Demo(2)
File "Untitled 3.py", line 5, in __init__
raise ValueError
ValueError
__del__ 2 # But note that `__del__` is still run.
The creation of the object with value 2 raises a ValueError exception and show that __del__ is still run to clean up the object.
Keep in mind that if you raise an exception during __init__ your object will not get a name. (It will, however, be created and destroyed. Since __del__ is paired with __new__ it still gets called)
ie, just like this does not create x:
>>> x=1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>> x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
Potential sneakier:
>>> x='Old X'
>>> x=1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> x
'Old X'
Same thing if you catch an exception of __init__:
try:
o=Demo(2)
except ValueError:
print o # name error -- 'o' never gets bound to the object...
# Worst still -- 'o' is its OLD value!
So don't try to refer to the incomplete object o -- it's gone out of scope by the time you get to except. And the name o is either nothing (i.e., NameError if you try to use it) or its old value.
So wrapping up (thanks to Steve Jessop for the User Defined Exception idea), you can wrap the creation of the object and catch the exception. Just figure out how to react appropriately to the OS error you are looking at.
So:
class ForbiddenTwoException(Exception):
pass
class Demo(object):
def __init__(self, value):
self.value=value
print 'trying to create with val:', value
if value==2:
raise ForbiddenTwoException
def __del__(self):
print '__del__', self.value
try:
o=Demo(2)
except ForbiddenTwoException:
print 'Doh! Cant create Demo with a "2"! Forbidden!!!'
# with your example - react to being unusable to create a directory...
Prints:
trying to create with val: 2
Doh! Cant create Demo with a "2"! Forbidden!!!
__del__ 2
You can wrap the call, as jramirez suggested:
try:
ver = VersionManager(path)
except:
raise
Or you can use a context manager:
class VersionManager(object):
def __init__(self):
#not-so-harmful code
self.path = path
def __enter__(self):
try:
self.path = path
os.mkdir(path)
self.myfunction(path)
except Exception as e:
print e
print "The directory making has failed, the function hasn't been executed."
return self
def __exit__(self, exc_type, exc_value, traceback):
print(exc_type, exc_value, traceback)
And to run it:
with VersionManager(my_path) as myVersionManager:
#do things you want with myVersionManager
This way, you'll catch errors inside the with statement as well.
You can use try/except when initializing the object.
try:
ver = VersionManager(my_path)
except Exception as e:
# raise e or handle error
print e
My favourite is to simply output errors to console and march on:
import sys, os, traceback
class Myclass
def __init__(self, path):
self._path = path
"""Risky Code"""
try:
os.mkdir(path)
except:
traceback.print_exc(file = sys.stdout)
This way an exception will print out more like a warning rather than a real exception.
I'm trying to understand a problem I'm having with python 2.7 right now.
Here is my code from the file test.py:
class temp:
def __init__(self):
self = dict()
self[1] = 'bla'
Then, on the terminal, I enter:
from test import temp
a=temp
if I enter a I get this:
>>> a
<test.temp instance at 0x10e3387e8>
And if I try to read a[1], I get this:
>>> a[1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: temp instance has no attribute '__getitem__'
Why does this happen?
First, the code you posted cannot yield the error you noted. You have not instantiated the class; a is merely another name for temp. So your actual error message will be:
TypeError: 'classobj' object has no attribute '__getitem__'
Even if you instantiate it (a = temp()) it still won't do what you seem to expect. Assigning self = dict() merely changes the value of the variable self within your __init__() method; it does not do anything to the instance. When the __init__() method ends, this variable goes away, since you did not store it anywhere else.
It seems as if you might want to subclass dict instead:
class temp(dict):
def __init__(self):
self[1] = 'bla'
Can I dynamically add attributes to instances of a new-style class (one that derives from object)?
Details:
I'm working with an instance of sqlite3.Connection. Simply extending the class isn't an option because I don't get the instance by calling a constructor; I get it by calling sqlite3.connect().
Building a wrapper doesn't save me much of the bulk for the code I'm writing.
Python 2.7.1
Edit
Right answers all. But I still am not reaching my goal; instances of sqlite3.Connection bar my attempts to set attributes in the following ways (as do instances of object itself). I always get an AttributeError:
> conn = sqlite3.connect([filepath])
> conn.a = 'foo'
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
conn.a = 'foo'
AttributeError: 'object' object has no attribute 'a'
> conn.__setattr__('a','foo')
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
conn.__setattr__('a','foo')
AttributeError: 'object' object has no attribute 'a'
Help?
Yes, unless the class is using __slots__ or preventing attribute writing by overriding __setattr__, or an internal Python class, or a Python class implemented natively (usually in C).
You can always try setting an attribute. Except for seriously weird __setattr__ implementations, assigning an attribute to an instance of a class of one of the types mentioned above should raise an AttributeError.
In these cases, you'll have to use a wrapper, like this:
class AttrWrapper(object):
def __init__(self, wrapped):
self._wrapped = wrapped
def __getattr__(self, n):
return getattr(self._wrapped, n)
conn = AttrWrapper(sqlite3.connect(filepath))
Simple experimentation:
In []: class Tst(object): pass
..:
In []: t= Tst()
In []: t.attr= 'is this valid?'
In []: t.attr
Out[]: 'is this valid?'
So, indeed it seems to be possible to do that.
Update:
But from the documentation: SQLite is a C library that ..., so it seems that you really need to wrap it.
conn.a = 'foo',
or any dynamic assignment is valid, if conn is
<type 'classobj'>.
Things like:
c=object()
c.e=1
will raise an Attribute error. On the otherhand: Python allows you to do fantastic Metaclass programming:
>>>from new import classobj
>>>Foo2 = classobj('Foo2',(Foo,),{'bar':lambda self:'bar'})
>>>Foo2().bar()
>>>'bar'
>>>Foo2().say_foo()
>>>foo