I'm writing a package, and doing my testing like a good little programmer, but here's what happens:
class TestOne(unittest.TestCase):
def setUp(self):
self.finder = Finder()
def test_default_search_parts(self):
self.assertEqual(self.finder.search_parts, [])
class TestTwo(unittest.TestCase):
def setUp(self):
self.finder = Finder()
def test_add_letter(self):
self.finder.add('a')
self.assertNotEqual(self.finder.search_parts, [])
in this case, test_default_search_parts fails with AssertionError: ['a'] != [], and test_add_letter passes. I don't know what's going on here. It gets really weird when I rewrite test_default_search_parts:
def test_default_search_parts(self):
f = Finder()
self.assertEqual(f.search_parts, [])
the same failure occurs. What am I doing wrong here with initializing my instances?
Oh, and I'm using nose to run them, if that matters.
As #samplebias mentioned, shared state, in this case with class-level attributes, can cause problems. Here is a possible situation you have:
import unittest
# bad implementation of Finder, class-level attribute
class Finder(object):
search_parts = []
def add(self, letter):
self.search_parts.append(letter)
# using 'Zne' here makes sure this test is run second
class TestZne(unittest.TestCase):
def setUp(self):
print 'I am run next'
self.finder = Finder()
def test_default_search_parts(self):
self.assertEqual(self.finder.search_parts, [])
class TestTwo(unittest.TestCase):
def setUp(self):
print 'I am run first'
self.finder = Finder()
def test_add_letter(self):
self.finder.add('a')
self.assertNotEqual(self.finder.search_parts, [])
unittest.main()
Outputs
Traceback (most recent call last):
File "test.py", line 18, in test_default_search_parts
self.assertEqual(self.finder.search_parts, [])
AssertionError: Lists differ: ['a'] != []
The problem being that all Finder instances share this class-level attribute search_parts, and add_letter is being run before the default search test.
To resolve, use something like:
class Finder(object):
def __init__(self):
self.search_parts = []
This will ensure it is an instance attribute only.
Related
I have extended the Parent class with the Override class. I have have overridden the method() method to fix a bug that occurs in the Parent class. I fixed the said bug and this fix has been tested in the Override class.
I use the Override class through an External class. By testing the External class to see if the previous bug is fixed, I discovered that it is not and that the traceback does not go through the Override class.
class Parent():
def method(self, param):
# Bugged
do_stuff()
class Override(Parent):
def method(self, param):
# Fixed (tested)
param = fix_param(param)
super(Parent, self).method(param)
class External():
def processing():
# Same bug as in `Parent`
param = get_param()
obj = Override()
obj.method(param)
It seems to me that the External class uses the Parent.method() method instead of the Override.method() method. Any clue on how to fix it or on where this problem comes from?
I'm a beginner and have not been confronted inheritance a lot so, please, forgive my ignorance and my lack of experience.
EDIT
Test that fails in External :
import os
import collections
import envtpl
from acquisition.configargparse_confparser import StepConfigFileParser
from configparser_extended import ExtendedConfigParser
from unittest import TestCase
from acquisition.utils import set_custom_environment
class ConfigFileParserTestCase(TestCase):
def test_parse_extended(self):
# x = StepConfigFileParser("test_plugin_name", "test")
plugin_name = "test_plugin_name"
step_name = "test"
set_custom_environment(plugin_name, step_name)
config = os.environ.get('MFCONFIG', 'GENERIC')
filename = os.path.dirname(os.path.realpath(__file__)) + "/test.ini"
with open(filename, 'r') as stream:
config_parser = ExtendedConfigParser(
config=config, inheritance='im', interpolation=None)
content = stream.read()
config_parser.read_string(content) # Fails here
section = "step_%s" % step_name
res = collections.OrderedDict()
for key in config_parser.options(section):
if not key.startswith('arg_'):
continue
res[key.replace('arg_', '', 1)] = envtpl.render_string(
config_parser.get(section, key))
self.assertEqual(res, {"venom": "snake", "revolver": "ocelot"})
Overridden method :
read_string() in https://github.com/thefab/configparser_extended/blob/master/configparser_extended/ecp.py line 573
Parent method :
read_string() from configparser
(https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.read_string)
test.ini
[step_test]
arg_venom=snake
arg_revolver=ocelot
liquid=snake
Error :
ERROR: test_parse_extended (tests.test_confparser.ConfigFileParserTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/florian/metwork/mfdata/src/acquisition/tests/test_confparser.py", line 39, in test_parse_extended
config_parser.read_string(content)
File "/opt/metwork-mfext/opt/python2/lib/python2.7/site-packages/backports/configparser/__init__.py", line 728, in read_string
sfile = io.StringIO(string)
TypeError: initial_value must be unicode or None, not str
Your code is not a complete working example, but it should do as you suggest.
Here is an example you can run to prove the point:
class Parent():
def method(self):
print('parent')
class Child(Parent):
def method(self):
print('child')
class Other():
def call_method(self):
c = Child()
c.method()
o = Other()
o.call_method()
That prints 'child', proving the Child class has overridden method(self).
class OurAtt():
def __init__(self):
self.Log = False
def setLog(self):
self.Log = True
def clearLog(self):
self.Log = False
class OurClass(object):
def __init__(self):
self.__OurAtt = OurAtt()
#property
def OurAtt(self):
return self.__OurAtt
#OurAtt.setter
def OurAtt(self, val):
raise Exception("can't modify the attribute" )
x = OurClass()
x.OurAtt.setLog()
print x.OurAtt.Log # print True
x.OurAtt.Log = False
print x.OurAtt.Log # sets to False Aim set this through function call x.OurAtt.setLog() I want to restrict the access, something like private variable.
Final aim is Log should be the attribute of OurAttr and should be protected by getter and setters or properties. Its like nesting of properties. and hierarchy should be maintained like object.OurAttr.Log
I researched and got the following link.
Python: multiple properties, one setter/getter
But It is not hitting my aim.
I am actually new to getter, setter and properties. Thanks in advance
I believe you are over-complicating the issue. If you want to prevent access to the attributes of OurAtt, the #property decorator should be used withing OurAtt. Instances of the OurAtt class will implement this protected-access behavior always, including when they are members of OurClass. You don't need to do anything with the #property decorator in OurClass unless you want to prevent modifying members of that class.
This, I think, does what you are trying to accomplish. It runs under 2.7 - if you are using an earlier version your mileage may vary.
class OurAttr(object):
def __init__(self):
self._log = False
#property
def log(self):
return self._log
#log.setter
def log(self, value):
raise AttributeError("Cannot set 'log' attribute directly.")
#log.deleter
def log(self):
raise AttributeError("Cannot delete 'log' attribute directly.")
def setLog(self):
self._log = True
print "Log is", self._log
def clearLog(self):
self._log = False
print "Log is", self._log
class OurClass(object):
def __init__(self):
self.OurAttr = OurAttr()
oc = OurClass()
oc.OurAttr.setLog()
oc.OurAttr.clearLog()
oc.OurAttr.log = False # Raises exception
Output is:
$ python2.7 test.py
Log is True
Log is False
Traceback (most recent call last):
File "test.py", line 33, in <module>
oc.OurAttr.log = False
File "test.py", line 11, in log
raise AttributeError("Cannot set 'log' attribute directly.")
AttributeError: Cannot set 'log' attribute directly.
So I have a class that extends two classes deep, here is it's definition and __init__():
class ProspectEventSocketProtocol(ChannelEventSocketProtocol):
def __init__(self, *args, **kwargs):
super(ProspectEventSocketProtocol, self).__init__(*args, **kwargs)
self.channel_info = None
self.rep_uuid = None
self.manual_dial = None
self.datetime_setup = timezone.now()
self.datetime_answered = None
self.defer_until_answered = defer.Deferred()
self.defer_until_originated = defer.Deferred()
self.defer_until_finished = defer.Deferred()
The definition and __init__() for the ChannelEventSocketProtocol is here:
class ChannelEventSocketProtocol(Freeswitch):
def __init__(self, *args, **kwargs):
self.channel_driver = None
self.uuid = kwargs.pop('uuid', str(uuid4()))
self._call_driver = kwargs.pop('call_driver', None)
super(ChannelEventSocketProtocol, self).__init__(*args, **kwargs)
And the definition and __init__() for the Freeswitch class is here:
class Freeswitch(client.EventSocketProtocol, TwistedLoggingMixin):
def __init__(self, *args, **kwargs):
self.jobs = {}
self.defer_until_authenticated = defer.Deferred() # This is the problem
client.EventSocketProtocol.__init__(self, *args, **kwargs)
TwistedLoggingMixin.__init__(self)
Even though I know that this is running and the defer_until_authenticated is being set as well as it's callback and errback, when I call this:
live_call = yield self._create_client_dial_live_call(client_dial.cid, client_dial.campaign)
pchannel = yield self.realm.get_or_create_channel_driver(live_call.uuid, 'prospect')
# ...
client_dial.prospect_channel = pchannel
yield pchannel.freeswitch_protocol.defer_until_authenticated # This is the problem here!
I get the error:
type object 'ProspectEventSocketProtocol' has no attribute 'defer_until_authenticated'
I have no idea why I can't get the attribute again. I know it is being set, but I have no idea where it goes... or what happens to it. I've searched the error and I have no idea what is happening in this spot.
Just for reference, here are the _create_client_dial_live_call() and get_or_create_channel_driver() functions:
def _create_client_dial_live_call():
# ...
p, created = Prospect.objects.get_or_create_client_dial_prospect(campaign, cid_num)
# ...
live_call = LiveCall(prospect=p, campaign=campaign.slug)
live_call.channel_vars_dict = chan_vars
live_call.save()
# ...
def get_or_create_channel_driver()
# The code is kind of confusing with even more context,
# it basically either gets the existing ProspectChannel
# object or creates a new one and then returns it.
Something somewhere is forgetting to instantiate a class.
The error is not telling you that an instance of the class ProspectEventSocketProtocol has no attribute defer_until_authenticated. It's telling you that the class ProspectEventSocketProtocol itself has no such attribute.
In other words, you are quite probably writing something like
pchannel.freeswitch_protocol = ProspectEventSocketProtocol
when you want
pchannel.freeswitch_protocol = ProspectEventSocketProtocol(...)
instead.
Here's a quick demo script that reproduces the error message you are seeing:
#!/usr/bin/env python3
class Test(object):
def __init__(self):
self.arg = "1234"
correct = Test()
print(correct.arg)
wrong = Test
print(wrong.arg)
When I run it, I get the following output:
1234
Traceback (most recent call last):
File "./type_object_error.py", line 12, in <module>
print(wrong.arg)
AttributeError: type object 'Test' has no attribute 'arg'
How to get a function inside if/else inside an __init__ :
class Foo(object):
def __init__(self, q, **keywords):
if a == "":
print "No empty strings"
else:
def on_g(self, response):
if response.error:
print "Check your internet settings"
else:
self.Bar()
http_client.fetch("http://www.google.com/", self.on_g)
because the program dont read the on_g() if i put an empty string!
If i use the on_g() outside in parallel with __init__() i need a declared variable, for example:
class Foo(object):
def __init__(self, q, **keywords):
if a == "":
print "No empty strings"
else:
self.on_g()
def on_g(self):
print 'hello there'
will return hello there
Your bug is in
http_client.fetch("http://www.google.com/", self.on_g)
which should be
http_client.fetch("http://www.google.com/", on_g)
since you defined a function, not a method.
self (the instance you're creating through __init__ ) doesn't have a on_g method.
Functions for the class-es need to be defined at the class level (as shown on your second chunk of code). They are evaluated when the class is first... erm... "looked-up"? "evaluated"?
That's why your second piece of code works. How come you can call self.on_g within the __init__ when the actual definition of the on_g method seems to come later in the code? It's an odd behavior (at a first glance) for an interpreter, right? Well... That's because when you run self.on_g(), the whole Foo class has already been evaluated and on_g has been added to the class (not to the instance!: To the class)
class Foo(object):
def __init__(self, q, **keywords):
[ . . . ]
else:
self.on_g() # I can use self.on_g() eventhough is defined... _
# |
# |
def on_g(self): # <------------ LATER ---------------------------|
print 'hello there'
Whereas if you define your method within the __init__, the interpreter will yell at you:
class Test(object):
def __init__(self):
def test(self):
print "Hello"
self.test()
a = Test()
Throws:
Traceback (most recent call last):
File "./test.py", line 10, in <module>
a = Test()
File "./test.py", line 8, in __init__
self.test()
AttributeError: 'Test' object has no attribute 'test'
Even if you think Oh, maybe the class doesn't have the test method because it's still within the __init__, and it will have it once the initialization is completed... Meeeck... Wrong:
class Test(object):
def __init__(self):
def test(self):
print "Hello"
a = Test()
a.test()
Same AttributeError.
If you still want to add on_g to the class at runtime (very bad idea, IMHO) you can do the interpreter's job by doing this:
class Test(object):
def __init__(self):
def test(self):
print "Hello"
self.__class__.test = test
self.test()
a = Test()
a.test()
... which correctly prints:
Hello
Hello
Now, the two most straightforward things to do I can think of are:
You move the def on_g(self) to the class level (as you showed in your second code snippet)
You call your http_client.fetch with on_g as a function local to the __init__'s scope (being picky with the language: on_g now is a function, not a method, since is not bound to an object anymore).
def __init__(self, q, **keywords):
if a == "":
print "No empty strings"
else:
def on_g(response):
if response.error:
print "Check your internet settings"
else:
self.Bar()
http_client.fetch("http://www.google.com/", on_g)
So I've looked at similar questions, and I've found some solutions to this, but I can't quite figure out how to do this.
What I'm trying to do is add a method to a class from a string. I can do this with the setattr() method, but that won't let me use self as an attribute in the extra method. Here's an example: (and I apologize for the variable names, I always use yolo when I'm mocking up an idea)
class what:
def __init__(self):
s = 'def yolo(self):\n\tself.extra = "Hello"\n\tprint self.extra'
exec(s)
setattr(self,"yolo",yolo)
what().yolo()
returns this:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: yolo() takes exactly 1 argument (0 given)
and if s = 'def yolo():\n\tself.extra = "Hello"\n\tprint self.extra'
then I get this result:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 2, in yolo
NameError: global name 'self' is not defined
This essentially means that I cannot dynamically create methods for classes, which I know is bad practice and unpythonic, because the methods would be unable to access the variables that the rest of the class has access to.
I appreciate any help.
You have to bind your function to the class instance to turn it into a method. It can be done by wrapping it in types.MethodType:
import types
class what:
def __init__(self):
s = 'def yolo(self):\n\tself.extra = "Hello"\n\tprint self.extra'
exec(s)
self.yolo = types.MethodType(yolo, self)
what().yolo()
On a side note, why do you even need exec in this case? You can just as well write
import types
class what:
def __init__(self):
def yolo(self):
self.extra = "Hello"
print self.extra
self.yolo = types.MethodType(yolo, self)
what().yolo()
Edit: for the sake of completeness, one might prefer a solution through the descriptor protocol:
class what:
def __init__(self):
def yolo(self):
self.extra = "Hello"
print self.extra
self.yolo = yolo.__get__(self)
what().yolo()
Another way, seems more elegant to me:
class what:
pass
ld = {}
exec("""
def yolo(self):
self.extra = "Hello"
print(self.extra)
""", None, ld)
# print('locals got: {}'.format(ld))
for name, value in ld.items():
setattr(what, name, value)
what().yolo()