Initialising Python properties - python

I'm writing a Python class to manage a Postgres database connection using pyscopg2.
I'd like the class to set up a connection to the database upon initialisation (I feel like this might be a terrible idea, but I can't think of a good reason why). I'm trying to make this work with a property, which I've never used before. In other words, I want the getter to be called from within the __init__ method.
My class looks something like this:
class MyDatabase:
connection_string = "host='<host_ip>' db_name='<db_name>'"
def __init__(self):
# *
self._connection = connection
#property
def connection(self):
# Check for an existing connection
if self._connection:
self._connection.close()
self._connection = psycopg2.connect(connection_string)
return self._connection
...
In this version, the check for an existing connection throws AttributeError: Elefriends instance has no attribute '_connection', which makes sense. I can get around this by simply adding a line that says self._connection = None at the place I've marked with # *, but this feels clunky. Is this the price I pay for the convenience? Am I just being fussy? Or is there a better way?
Thanks!

Instead of the if ... statement, you could use:
try:
self._connection.close()
except AttributeError:
pass
self._connection = psycopg2.connect(connection_string)
return self._connection

Related

Python multi layer dot notation

I'm trying to get a set of python classes built to simplify the sending and receiving of information over a socket connection.
I've had success using a getter and setter via the #property.
I would like the code to be in a dot notation such as:
class dc_load_fake_socket:
#staticmethod
def sendall(msg):
print(msg)
#staticmethod
def recv(size):
return 'Dummy Info'
class test_equipment:
def __init__(self, TCP_IP, TCP_PORT=2101, BUFFER_SIZE=1024):
self.TCP_IP = str(TCP_IP)
self.TCP_PORT = TCP_PORT
self.BUFFER_SIZE = BUFFER_SIZE
self.dc_load_socket = dc_load_fake_socket
def sendall(self, message):
message = f'{message}\n'
self.dc_load_socket.sendall(message.encode())
def recv(self):
return self.dc_load_socket.recv(self.BUFFER_SIZE)
def query(self, message):
self.sendall(message)
return self.recv().strip('\n')
#property
def voltage(self):
return self.query("MEASure:VOLTage?")
dcl = test_equipment('192.168.0.2')
print(dcl.voltage)
While this works, the issue is the fact that this isn't the only 'subsystem' that uses voltage.
Ideally I would like it to act like this:
dcl.measure.voltage
dcl.fetch.voltage
dcl.spec.voltage
dcl.spec.voltage = 1.5
I've looked at using inner classes but I'm not able to use the recv(), sendall() and query() of the main class.
Originally this was done in a python file with only functions. But I ran into an issue were I actually needed two of them. I started turning this into a class as a way to make it easier to maintain, and at the same time had to update from python 2.7 to 3
I'm not the most experienced in python and any help with this would be extremely appreciated.

How to correctly set class attributes from methods that need to be persistent?

I am building a class that has a number of methods to request different data from the same website. The catch is the session_id needs to stay persistent across all instance methods.
I have written some mock code below and was wondering if this is the correct way to go about it or have I got it all wrong?
import random
class classTest():
def __init__(self):
self.session_id = None
def set_session_id(self):
if self.session_id == None:
self.session_id = random.randrange(0,1000)
return self.session_id
def use_session_id(self):
return self.set_session_id()
def use_session_id_again(self):
return self.set_session_id()
In the above example I mocked the session_id with a random number and when I test the two different methods I get persistent results, which means it is working great... but is this the correct way to achieve this? Can anyone advise on a better / proper way to do this?
Tre preferred way is to set session_id only once in __init__ method so after initialization it stays persistent during the whole lifetime of the object. And just not change it after initialization.
Something like this:
import random
class classTest():
def __init__(self):
self.session_id = random.randrange(0,1000)
def use_session_id(self):
print(self.session_id)
def use_session_id_again(self):
print(self.session_id)
test = classTest() # session_id is set here
test.use_session_id() # will print session id
test.use_session_id_again() # will print the same session id

How to mock MongoClient for python unit test?

I have following piece of code to UT, which makes me in trouble:
def initialize():
try :
self.client = MongoClient("127.0.0.1", 27017)
self.conn = self.client["DB_NAME"]
except Exception:
print "Except in initialize!"
return False
return True
I write following test case to cover the above function, hope to get return value "True":
def mock_mongodb_mongoclient_init(self, para1, para2):
pass
def mock_mongodb_mongoclient_getitem(self, name):
return {"DB_NAME":"Something"}
def test_mongodb_initialize_true(self):
self.patcher1 = patch('pymongo.MongoClient.__init__', new=self.mock_mongodb_mongoclient_init)
self.patcher2 = patch('pymongo.MongoClient.__getitem__', new=self.mock_mongodb_mongoclient_getitem)
self.patcher1.start()
self.patcher2.start()
self.assertEqual(initialize(), True)
self.patcher1.stop()
self.patcher2.stop()
But this never works! It always report "Exception in initialize!"! and return "False".
How could I UT this MongoClient and makes the function return "True"?
Since you are unit testing and not trying to actually connect to Mongo in any way, I think you should just care that the client API has been called. So I would suggest the following -
from unittest import mock
#mock.patch("pymongo.MongoClient")
def test_mongodb_initialize(self, mock_pymongo):
MyMongo.initialize()
self.assertTrue(mock_pymongo.called)
(Forgive me if my syntax is off, I use pytest rather than unittest.)
For simply pass the initialize(), we can mock the pymongo.MongoClient as following:
import unittest
import pymongo
from mock import patch
class MyMongo(object):
def initialize(self):
try :
self.client = pymongo.MongoClient("127.0.0.1", 27017)
self.conn = self.client["DB_NAME"]
except Exception:
print "Except in initialize!"
return False
return True
class TestMyMongo(unittest.TestCase):
def test_mongodb_initialize_true(self):
with patch('pymongo.MongoClient') as mock_mongo:
self.mymongo = MyMongo()
self.assertEqual(self.mymongo.initialize(), True)
However I'm not sure if you're trying to mock the MongoClient or just the MongoClient.init part?
MongoClient is not designed to be mocked this way. It must initialize its attributes in __init__ in order to function, so if you skip calling __init__, all further operations will throw various exceptions. In your specific case, MongoClient needs access to the __slave_okay attribute, but it isn't set.
Either set up an actual MongoDB server and test against it, or mock all of PyMongo with a fake library. Simply overriding a handful of methods in PyMongo is not going to work with reasonable effort.

Close an sqlite3 database on exit, no matter what

I'm currently writing a script that uses sqlite3. I recently ran into a problem with the database being in use by another program due to my code exiting early with an error.
With similar problems, one usually uses:
conn = sqlite3.connect(...)
try:
#Do stuff
finally:
conn.close()
But this won't work in my case. In a nutshell, this is my code:
import sqlite3
class Thingamadoodle:
def __init__(self, ...):
self.conn = sqlite3.connect(...)
...
#Methods and stuff
def __del__(self):
self.conn.close()
poop = Thingamadoodle(...)
poop.do_stuff(...)
poop.throw_irritating_exception_that_you_cant_track_down(irritatingness=11)
After the program exits without closing the connection, I get errors when I try to modify the database.
Is there a way to safely close the connection, even on an unclean exit?
To be honest, i don't understand the question much, but why not just wrap the poop.do_stuff() in a try/except block?
try:
poop.do_stuff()
except:
poop.__del__()
finally:
poop.__del__()
Or to be a bit cleaner, use a context manager:
class Thingamadoodle:
def __init__(self, ...):
...
#Methods and stuff
def __enter__(self):
self.conn = sqlite3.connect(...)
return self
def __exit__(self, errorType, errorValue, errorTrace):
self.conn.close()
And just execute it as:
with Thingmadoodle(args) as poop:
#do things
After all the code is done, or after an exception happened in the statement, __exit__ will be executed, and you can safely close it.
Hope this helps!

Asserting an object passed into a mocked method in the python mock

Say I have a class called Client that creates an object of the Request class and passes it to a method of a Connection object:
class Client(object):
def __init__(self, connection):
self._conn = connection
def sendText(plaintext):
self._conn.send(Request(0, plaintext))
And I want to assert the object passed into the Connection.send method to check its properties. I start with creating a mocked Connection class:
conn = Mock()
client = Client(conn)
client.sendText('some message')
And then I want something like:
conn.send.assert_called_with(
(Request,
{'type': 0, 'text': 'some message'})
)
Where 'type' and 'text' are properties of Request. Is there a way to do this in python's mock? All I found in the documentation were simple data examples.
I could have done it with mock.patch decorator by replacing the original 'send' method with a method which asserts the object's fields:
def patchedSend(self, req):
assert req.Type == 0
with mock.patch.object(Connection, 'send', TestClient.patchedSend):
...
but in this case I would have to define a separete mocked function for every method check and I couldn't check (without additional coding) if the function has been called at all.
You can get the last arguments to the mock with
request, = conn.send.call_args
and then assert properties about that. If you want facilities to express more sophisticated assertions about things, you can install PyHamcrest.
Note: Don't use assert in unit tests. Use assertion methods like assertEqual or assertTrue. Assertion methods can't be accidentally turned off, and they can give more useful messages than assert statements.
Well, I think that the easiest and better way of doing it, in this specific case, is to make a function to create requests and then mock it.
For instance, it'd be something like it:
class Client(object):
def __init__(self, connection):
self._conn = connection
def _create_request(self, plain_text):
return Request(0, plain_text)
def send_text(self, plaintext):
self._conn.send(self._create_request(plain_text))
And then, in the test, you could mock _create_request to return some specific value and then assert that send_text was called with it.
You could also get the parameters by call_args, as suggested, but I think it looks better this way.

Categories

Resources