Dealiing with instance in class when parameters changed - Python - python

I'm learning how to program and handle Python classes and have found this problem.
I have this files (example.py) and inside this file I'm importing two classes.
example.py
..
from class_folder.automobile import Class_Automobile
from class_folder.catalog import Class_Catalog
class_automobile = Class_Automobile()
class_catalog = Class_Catalog()
..
Inside automobile.py have this code:
from pdo_mysql import Pdo_Mysql
class Class_Automobile(object):
"""
Description
"""
__pdo_mysql_intern = None
def __init__(self):
self.__pdo_mysql_intern = Pdo_Mysql('host', 'user', 'password', 'db')
## End def init
def Validate_Make(self, make_search):
query_make = 'make_text = %s '
result = self.__pdo_mysql_intern.select('make', query_make, 'id_make', make=make_search)
return result
## End function Validate Make
And inside the catalog.py file has this information:
from pdo_mysql import Pdo_Mysql
class Class_Catalog(object):
"""
Description
"""
__pdo_mysql_intern = None
def __init__(self):
self.__pdo_mysql_intern = Pdo_Mysql('host', 'user', 'password', 'db2')
## End def init
And finally the pdo_mysql.py file has this:
import MySQLdb, sys
class Pdo_Mysql(object):
"""
Description
"""
__instance = None
__host = None
__user = None
__password = None
__database = None
__session = None
__connection = None
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super(Pdo_Mysql, cls).__new__(cls,*args,**kwargs)
return cls.__instance
## End __new__
def __init__(self, host='localhost', user='root', password='', database=''):
self.__host = host
self.__user = user
self.__password = password
self.__database = database
## __init__
def _open(self):
try:
cnx = MySQLdb.connect(self.__host, self.__user, self.__password, self.__database)
self.__connection = cnx
self.__session = cnx.cursor()
except MySQLdb.Error as e:
print "Error %d: %s" % (e.args[0],e.args[1])
## End function open
def _close(self):
self.__session.close()
self.__connection.close()
## End function close
def select(self, table, where=None, *args, **kwargs):
result = None
query = 'SELECT '
keys = args
values = tuple(kwargs.values())
l = len(keys) - 1
for i, key in enumerate(keys):
query += "`"+key+"`"
if i < l:
query += ","
## End for keys
query += 'FROM %s' % table
if where:
query += " WHERE %s" % where
## End if where
self._open()
self.__session.execute(query, values)
result = [item[0] for item in self.__session.fetchall()]
self._close()
return result
## End function select
When I run the example.py I have this problem, somehow when I call this
validate_information = class_automobile.Validate_Make(make)
I obtain this response
File "/Library/Python/2.7/site-packages/MySQLdb/cursors.py", line 205, in execute
self.errorhandler(self, exc, value)
File "/Library/Python/2.7/site-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorclass, errorvalue
_mysql_exceptions.ProgrammingError: (1146, "Table 'db2.make' doesn't exist")
This problem it is because the db from Class_Catalog is mixing when I'm calling the Class_Automobile.
It is solved when I put the Class_Catalog before the Class_Automobile but I want to know how to solve properlly.
Update:
Thanks to Adam Smith found the problem. I just had to change this file pdo_mysql.py:
class Pdo_Mysql(object):
# all your existing definitions
def __new__(cls, *args, **kwargs):
**if not cls.__instance or not cls.__database:**
cls.__instance = super(Pdo_Mysql, cls).__new__(cls,*args,**kwargs)
return cls.__instance

Here is what's happening. When you call class_automobile = Class_Automobile() a new Class_Automobile instance is created and the __init__ method is called on that instance. Inside __init__ you're calling Pdo_Mysql('host', 'user', 'password', 'db'). Pdo_Mysql.__new__ is invoked with Pdo_Mysql as the cls variable and ('host', 'user', 'password', 'db') as the args variable. Because this is the first time a Pdo_Mysql object has been requested the class variable __instance is None so your super call is run and the result is set on the Pdo_Mysql.__instance variable. This is a class level assignment. You then return this instance. If you look at the Python docs you'll see that :
If __new__() returns an instance of cls, then the new instance’s __init__() method will be invoked
So now you've got the instance of Pdo_Mysql from the super __new__ call and you return it. The __init__ method will be called on it with the args and kwargs sent to __new__. In __init__ you set the various connection attributes.
All is fine at this point. If you were to make calls to your class_automobile instance they would work as you expected. Let's look at what happens when you make your next object, the instance of Class_Catalog.
It begins in the same way as Class_Automobile until we get to the self. __pdo_mysql_intern = Pdo_Mysql call. This time when Pdo_Mysql.__new__ is invoked the cls.__instance variable is set to an instance of Pdo_Mysql so you don't make a new one. BUT you still return this instance from __new__ so the __init__ method is still run on this instance but with new args and kwargs. The instance variables are set to the new values. So now the instances class_automobile and class_catalog have a __pdo_mysql_intern variable pointing to the same instance and that instance has __host, __user, __password, and __database set to whatever the last arguments were used to initialize an instance.
Now on to one possible solution...
It appears you're trying to do some kind of connection sharing so you don't open a new connection for each object that connects to a database. However you're only allowing one instance of Pdo_Mysql to ever exist. This means you cannot connect to more than one database and only as a single user.
You need a pool of connections (Pdo_Mysql objects) to choose from so that when you want to connect to another db or as a different user you can do so. In your Pdo_Mysql class you could change the __instance = None variable to __instances = {}. Use the args as the key to this dictionary and change the __new__ method to look up the instance using the args. Note that this ignores the kwargs, I leave it to you to figure out how to include them (hint, you cannot use mutable objects as keys in dictionaries). You'll also want to add a __initialized variable to the Pdo_Mysql class. Default it to False and set it to True in __init__. This was you can make sure you only run __init__ on each instance once.
Going a step further I would suggest you not use __new__ to create singletons but instead create a helper method in pdo_mysql.py called get_conenction or something. Have it take the same 4 arguments and it can check it's pool of instances for any that match and either return an existing connection or make a new one.

Related

modify a function of a class from another class

In pymodbus library in server.sync, SocketServer.BaseRequestHandler is used, and defines as follow:
class ModbusBaseRequestHandler(socketserver.BaseRequestHandler):
""" Implements the modbus server protocol
This uses the socketserver.BaseRequestHandler to implement
the client handler.
"""
running = False
framer = None
def setup(self):
""" Callback for when a client connects
"""
_logger.debug("Client Connected [%s:%s]" % self.client_address)
self.running = True
self.framer = self.server.framer(self.server.decoder, client=None)
self.server.threads.append(self)
def finish(self):
""" Callback for when a client disconnects
"""
_logger.debug("Client Disconnected [%s:%s]" % self.client_address)
self.server.threads.remove(self)
def execute(self, request):
""" The callback to call with the resulting message
:param request: The decoded request message
"""
try:
context = self.server.context[request.unit_id]
response = request.execute(context)
except NoSuchSlaveException as ex:
_logger.debug("requested slave does not exist: %s" % request.unit_id )
if self.server.ignore_missing_slaves:
return # the client will simply timeout waiting for a response
response = request.doException(merror.GatewayNoResponse)
except Exception as ex:
_logger.debug("Datastore unable to fulfill request: %s; %s", ex, traceback.format_exc() )
response = request.doException(merror.SlaveFailure)
response.transaction_id = request.transaction_id
response.unit_id = request.unit_id
self.send(response)
# ----------------------------------------------------------------------- #
# Base class implementations
# ----------------------------------------------------------------------- #
def handle(self):
""" Callback when we receive any data
"""
raise NotImplementedException("Method not implemented by derived class")
def send(self, message):
""" Send a request (string) to the network
:param message: The unencoded modbus response
"""
raise NotImplementedException("Method not implemented by derived class")
setup() is called when a client is connected to the server, and finish() is called when a client is disconnected. I want to manipulate these methods (setup() and finish()) in another class in another file which use the library (pymodbus) and add some code to setup and finish functions. I do not intend to modify the library, since it may cause strange behavior in specific situation.
---Edited ----
To clarify, I want setup function in ModbusBaseRequestHandler class to work as before and remain untouched, but add sth else to it, but this modification should be done in my code not in the library.
The simplest, and usually best, thing to do is to not manipulate the methods of ModbusBaseRequestHandler, but instead inherit from it and override those methods in your subclass, then just use the subclass wherever you would have used the base class:
class SoupedUpModbusBaseRequestHandler(ModbusBaseRequestHandler):
def setup(self):
# do different stuff
# call super().setup() if you want
# or call socketserver.BaseRequestHandler.setup() to skip over it
# or call neither
Notice that a class statement is just a normal statement, and can go anywhere any other statement can, even in the middle of a method. So, even if you need to dynamically create the subclass because you won't know what you want setup to do until runtime, that's not a problem.
If you actually need to monkeypatch the class, that isn't very hard—although it is easy to screw things up if you aren't careful.
def setup(self):
# do different stuff
ModbusBaseRequestHandler.setup = setup
If you want to be able to call the normal implementation, you have to stash it somewhere:
_setup = ModbusBaseRequestHandler.setup
def setup(self):
# do different stuff
# call _setup whenever you want
ModbusBaseRequestHandler.setup = setup
If you want to make sure you copy over the name, docstring, etc., you can use `wraps:
#functools.wraps(ModbusBaseRequestHandler.setup)
def setup(self):
# do different stuff
ModbusBaseRequestHandler.setup = setup
Again, you can do this anywhere in your code, even in the middle of a method.
If you need to monkeypatch one instance of ModbusBaseRequestHandler while leaving any other instances untouched, you can even do that. You just have to manually bind the method:
def setup(self):
# do different stuff
myModbusBaseRequestHandler.setup = setup.__get__(myModbusBaseRequestHandler)
If you want to call the original method, or wraps it, or do this in the middle of some other method, etc., it's otherwise basically the same as the last version.
It can be done by Interceptor
from functools import wraps
def iterceptor(func):
print('this is executed at function definition time (def my_func)')
#wraps(func)
def wrapper(*args, **kwargs):
print('this is executed before function call')
result = func(*args, **kwargs)
print('this is executed after function call')
return result
return wrapper
#iterceptor
def my_func(n):
print('this is my_func')
print('n =', n)
my_func(4)
more explanation can be found here

Calling Class methods

I have 3 classes defined this way:
class Device:
Some method
class SSH:
def connect(self,type):
# code
def execute(self,cmd):
# code
class Netconf:
def connect(self,type):
# code
def execute(self,cmd):
# code
Note SSH and Netconf classes have same method names but they do things differently.
I have an instance of class Device and would like to access methods like this-
d = Device()
d.connect('cli') # <-- This should call SSH method and subsequently
# d.execute(cmd) should call execute method from SSH class
# too.
d.connect('netconf') # <-- This should call Netconf method and subsequently
# d.execute(cmd) should call execute method from
# Netconf class too.
The question is - how do I make it happen? I want to be able to use methods of SSH/Netconf class on Device class instance 'd' based on the input.
You can do this by storing the type of device connected in a private Device attribute and then forwarding most method calls to it by adding a custom __getattr__() method. This is a little tricky in the connect() method because that's were the target device is defined (as opposed to in the Device.__init__() initializer).
I also changed the variable you had named type to kind to avoid colliding with the built-in module of the same name.
class Device(object):
def connect(self, kind):
if kind == 'cli':
target = self._target = SSH()
elif kind == 'netconf':
target = self._target = Netconf()
else:
raise ValueError('Unknown device {!r}'.format(kind))
return target.connect(kind)
def __getattr__(self, name):
return getattr(self._target, name)
class SSH(object):
def connect(self, kind):
print('SSH.connect called with kind {!r}'.format(kind))
def execute(self, cmd):
print('SSH.execute called with cmd {!r}'.format(cmd))
class Netconf(object):
def connect(self, kind):
print('Netconf.connect called with kind {!r}'.format(kind))
def execute(self, cmd):
print('Netconf.execute called with cmd {!r}'.format(cmd))
d = Device()
d.connect('cli')
d.execute('cmd1')
d.connect('netconf')
d.execute('cmd2')
Output:
SSH.connect called with kind 'cli'
SSH.execute called with cmd 'cmd1'
Netconf.connect called with kind 'netconf'
Netconf.execute called with cmd 'cmd2'
You should implement the Strategy Pattern. The connect() method should instantiate the appropriate class (detach()ing from the previous if required) and store it, and then other methods should delegate to the stored object.

__str__ method showing None value for a class local variable

I'm having an issue where a class variable is set correctly and displayed correctly inside __init__() when using print, however, trying to display that same variable inside the __str__ method displays None. Three other variables generated the exact same way do not exhibit this behavior.
Here is my code:
class RestRequest(urllib2.Request):
def __init__(self, host, path, data=""):
self.host = host
self.data = data
self.path = path
print "RestRequest::init() path: %s" % self.path
print "RestRequest::init() data: %s" % self.data
print "RestRequest::init() host: %s" % self.host
urllib2.Request.__init__(self, self.path)
def __str__(self):
return "RestRequest::__str__() host='%s', path='%s', data='%s'" % (self.host, self.path, self.data)
The print statements inside __init__() display the correct values. The value of host inside __str__ is None. The path, method and data variables display correctly inside __str__.
Any ideas?
You're running into a naming clash with attributes on the super class. You'll need to change the names of your attributes -- or set them after urllib2.Request.__init__ (assuming your values are compatible with what the superclass wants).
urllib2.Request sets self.host = None in __init__. To prevent unexpected things happening, call the superclass __init__ earlier in the subclass __init__:
class RestRequest(urllib2.Request):
def __init__(self, host, path, data=""):
urllib2.Request.__init__(self, path) # sets self.host = None
self.host = host # resets to correct value
self.path = path
self.data = data

With statement in python is returning None object even though __init__ method works

For a DB class with the following init method:
class DB:
def __init__(self, dbprops):
self.dbprops = dbprops
self.conn = self.get_connection(self.dbprops)
debug("self.conn is %s" %self.conn)
def __enter__(self):
pass
def __exit__(self, exc_type, exc_val, exc_tb):
if not self.conn is None:
self.close()
And for a client method invoking it as follows:
with DB(self.dbprops) as db:
if not db:
raise Exception("Db is None inside with")
return db.get_cmdline_sql()
The output shows the debug message - thus the init method was successfully called:
File "./classifier_wf.py", line 28, in get_cmdline_mysql
raise Exception("Db is None inside with")
Exception: Db is None inside with
Update: fixed the enter method to return a DB object . But need help on how to invoke it:
def __enter__(self, dbprops):
return DB(dbprops)
Invoking it with a single parameter does not work apparently:
with DB(dbprops) as db:
TypeError: __enter__() takes exactly 2 arguments (1 given)
Now I do not follow because the "self" is supposed to be filled in automatically..
The context manager protocol is handled by the __enter__() and __exit__() methods; the former must return the value to assign.

Instantiate MySQL DB class

I would like to write a class that would contain all my MySQL operations.
Right now, I cant even get the class instantiated.
Traceback (most recent call last):
File "./compare.py", line 71, in <module>
main()
File "./compare.py", line 67, in main
db = Table.mysqlconnect()
TypeError: unbound method mysqlconnect() must be called with Table instance as first argument (got nothing instead)
code:
import MySQLdb
class Table(object):
""" Using Databases """
def __init__(self, db, name ):
self.db = db
self.name = name
self.cur = self.db.cursor()
def mysqlconnect():
conn = MySQLdb.connect (host = "mysql.blah.com",
user = "user",
passwd = "password",
db = "database")
cursor = conn.cursor ()
cursor.execute ("SELECT VERSION()")
row = cursor.fetchone ()
print "server version:", row[0]
cursor.close ()
conn.close ()
def main():
db = Table.mysqlconnect()
pass
if __name__ == '__main__':
main()
You should read those docs, but what you're looking for is:
db = Table()
db.mysqlconnect()
Short explanation: mysqlconnect is a instance method on your Table class.
Long explanation: Table is an abstract concept right now -- you've told the Python interpreter about it and what it should do, but you haven't actually made one yet. It's like the blueprint, if you will, for your class. Before you use it, or use any method defined as part of it* you'll need to actually "build it" first.
This is what you do when you do: db = Table() This tells the Python interpreter that I've got this variable called db now and I want it to be an instance of Table(). Now that you've got your instance, you can call the instance method (since the instance method only works on an instance) and get your result.
*There are things called class methods that you can use without instantiating the class first, but you'll see that when you read the documentation.
The error is exactly right:
TypeError: unbound method mysqlconnect() must be called with Table instance as first argument (got nothing instead)
In other words, you are using a class name (Table) and not an instance of the class Table.
Read these examples, they do exactly what you want. If you want to read up on classes and objects, the Python manual chapter on this is excellent.
Also, mysqlconnect needs to use self to access the class attributes. For example:
import MySQLdb
class Table:
def __init__(self, host, user, passwd, name)
self.db = MySQLdb.connect (host = host,
user = user,
passwd = passwd,
db = name)
self.cursor = self.db.cursor()
def mysqlconnect(self):
self.cursor.execute ("SELECT VERSION()")
row = cursor.fetchone()
print "server version:", row[0]
self.cursor.close ()
self.db.close ()
# Main must be outside the table class
def main():
tableInstance = Table("mysql.blah.com", "user", "password", "database")
tableInstance.mysqlconnect()
if __name__ == '__main__':
main()

Categories

Resources