How to "terminate" inside of init - python

I have a class that does some sanity checks inside __init__. If some of these fail, I want to stop processing __init__.
Example:
class MyClass(object):
def __init__(self, hostname, conn=None, user=None, passwd=None):
# SNIP (code to check if connection was supplied, if not user/pass)
if not conn.is_logged_in():
if not conn.Login():
raise LoginFailed("Failed to login to '%s'" % hostname)
return
# some code that relies on valid connection
If I want to cease processing after this first sanity check, how can this be done? The actual code I'm writing establishes a connection to a REST service, so my first sanity check is to test the provided connection, or, if provided a username and password instead, establish the connection in __init__().
Obviously if the connection fails, I don't want to execute the subsequent tasks that rely on the connection. Essentially I want the object creation to fail.
Any suggestions? Thanks.

On C++ we use while break.
In python that would be:
def __init__(self):
while True:
# Note that all the relevant code goes here. Inside the while
if not conn.is_logged_in():
if not conn.Login():
# raise LoginFailed("Failed to login to '%s'" % hostname)
break

You could use sys.exit(), although this does what your code already does.
You could also return from the init method to stop processing, and this won't raise a fatal error, but will stop the method.

Related

Checking FTP connection is valid using NOOP command

I'm having trouble with one of my scripts seemingly disconnecting from my FTP during long batches of jobs. To counter this, I've attempted to make a module as shown below:
def connect_ftp(ftp):
print "ftp1"
starttime = time.time()
retry = False
try:
ftp.voidcmd("NOOP")
print "ftp2"
except:
retry = True
print "ftp3"
print "ftp4"
while (retry):
try:
print "ftp5"
ftp.connect()
ftp.login('LOGIN', 'CENSORED')
print "ftp6"
retry = False
print "ftp7"
except IOError as e:
print "ftp8"
retry = True
sys.stdout.write("\rTime disconnected - "+str(time.time()-starttime))
sys.stdout.flush()
print "ftp9"
I call the function using only:
ftp = ftplib.FTP('CENSORED')
connect_ftp(ftp)
However, I've traced how the code runs using print lines, and on the first use of the module (before the FTP is even connected to) my script runs ftp.voidcmd("NOOP") and does not except it, so no attempt is made to connect to the FTP initially.
The output is:
ftp1
ftp2
ftp4
ftp success #this is ran after the module is called
I admit my code isn't the best or prettiest, and I haven't implemented anything yet to make sure I'm not reconnecting constantly if I keep failing to reconnect, but I can't work out why this isn't working for the life of me so I don't see a point in expanding the module yet. Is this even the best approach for connecting/reconnecting to an FTP?
Thank you in advance
This connects to the server:
ftp = ftplib.FTP('CENSORED')
So, naturally the NOOP command succeeds, as it does not need an authenticated connection.
Your connect_ftp is correct, except that you need to specify a hostname in your connect call.

Write a unit test with pytest to test a socket

I wrote a small server chat that does very basic things and I would like to write the tests around it. Unfortunately I quite lost regarding. I would need some help to get on the right tracks.
I have a class called Server() and it contains a method called bind_socket(). I would like to write unit test (preferably using pytest) to test the following method:
class Server(Threading.Thread):
""" Server side class
Instanciate a server in a thread.
"""
MAX_WAITING_CONNECTIONS = 10
def __init__(self, host='localhost', port=10000):
""" Constructor of the Server class.
Initialize the instance in a thread.
Args:
host (str): Host to which to connect (default=localhost)
port (int): Port on which to connect (default=10000)
"""
threading.Thread.__init__(self)
self.host = host
self.port = port
self.connections = []
self.running = True
def bind_socket(self, ip=socket.AF_INET, protocol=socket.SOCK_STREAM):
self.server_socket = socket.socket(ip, protocol)
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(self.MAX_WAITING_CONNECTIONS)
self.connections.append(self.server_socket)
I'm wondering what is the best way to write a test for this method as it doesn't return anything. Should I mock it and try to return the number of of call of socket(), bind(), listen() and append() or is it the wrong way to do proceed? I'm quite lost on that, I did many tests either with pytest and unittest, watch conferences and read articles and I still don't have anything working.
Some explanation and/or examples would be greatly appreciated.
Thanks a lot
For each line of bind_socket you should ask yourself the questions:
What if this line didn't exist
(for conditionals... I know you don't have any here) What if this condition was the other way around
Can this line raise exceptions.
You want your tests to cover all these eventualities.
For example, socket.bind can raise an exception if it's already bound, or socket.listen can raise an exception. Do you close the socket afterwards?

Best way to roll back actions when an exception is raised with Python

I have a code who looks like this :
# step 1 remove from switch
for server in server_list:
remove_server_from_switch(server)
logger.info("OK : Removed %s", server)
# step 2 remove port
for port in port_list:
remove_ports_from_switch(port)
logger.info("OK : Removed port %s", port)
# step 3 execute the other operations
for descr in pairs:
move_descr(descr)
# step 4 add server back to switch
for server in server_list:
add_server_to_switch(server)
logger.info("OK : server added %s", server)
# step 5 add back port
for port in port_list:
add_ports_to_switch(port)
logger.info("OK : Added port %s", port)
functions inside the for loop can raise exceptions or the user can interrupt the script with the Ctrl+C.
But I would like to enter in a roll-back mode by undo changes already done before if exceptions are raised during the execution.
I mean, if an exception is raised during the step 3, I have to roll-back steps 1 and 2 (by executing actions in step 4 and 5 ).
Or if a user try to stop the script with a Ctrl+C in the middle of for loop in the step 1, I would like to roll-back the action and add back the servers removed.
How can it be done in a good pythonic way with the use of exceptions, please ? :)
This is what context managers are for. Read up on the with statement for details, but the general idea is you need to write context manager classes where the __enter__ and __exit__ functions do the removal/re-addition of your servers/ports. Then your code structure becomes something like:
with RemoveServers(server_list):
with RemovePorts(port_list):
do_stuff
# exiting the with blocks will undo the actions
You should use a with construct.
As this link explains:
with expression as target_var:
do_something(target_var)
The context manager object results from evaluating the expression
after with. In other words, expression must return an object that
implements the context management protocol. This protocol consists of
two special methods:
.__enter__() is called by the with statement to enter the runtime context.
.__exit__() is called when the execution leaves the with code block.
Maybe something like this will work:
undo_dict = {remove_server_from_switch: add_server_to_switch,
remove_ports_from_switch: add_ports_to_switch,
add_server_to_switch: remove_server_from_switch,
add_ports_to_switch: remove_ports_from_switch}
def undo_action(action):
args = action[1:]
func = action[0]
undo_dict[func](*args)
try:
#keep track of all successfully executed actions
action_list = []
# step 1 remove from switch
for server in server_list:
remove_server_from_switch(server)
logger.info("OK : Removed %s", server)
action_list.append((remove_server_from_switch, server))
# step 2 remove port
for port in port_list:
remove_ports_from_switch(port)
logger.info("OK : Removed port %s", port)
action_list.append((remove_ports_from_switch, port))
# step 3 execute the other operations
for descr in pairs:
move_descr(descr)
# step 4 add server back to switch
for server in server_list:
add_server_to_switch(server)
logger.info("OK : server added %s", server)
action_list.append((add_server_to_switch, server))
# step 5 add back port
for port in port_list:
add_ports_to_switch(port)
logger.info("OK : Added port %s", port)
action_list.append((add_ports_to_switch, port))
except Exception:
for action in reverse(action_list):
undo_action(action)
logger.info("ERROR Recovery : undoing {func}({args})",func = action[0], args = action[1:])
finally:
del action_list
EDIT: As tzaman said below, the best thing to do in a situation like this is to wrap the entire thing into a context manager, and use the with statement. Then it doesn't matter whether or not there was an error encountered- all your actions are undone at the end of the with block.
Here's what it might look like:
class ActionManager():
def __init__(self, undo_dict):
self.action_list = []
self.undo_dict = undo_dict
def action_pop(self):
yield self.action_list.pop()
def action_add(self, *args):
self.action_list.append(args)
def undo_action(self, action):
args = action[1:]
func = action[0]
self.undo_dict[func](*args)
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
for action in self.action_stack:
undo_action(action)
logger.info("Action Manager Cleanup : undoing {func}({args})",func = action[0], args = action[1:])
Now you can just do this:
#same undo_dict as before
with ActionManager(undo_dict) as am:
# step 1 remove from switch
for server in server_list:
remove_server_from_switch(server)
logger.info("OK : Removed %s", server)
am.action_add(remove_server_from_switch, server)
# step 2 remove port
for port in port_list:
remove_ports_from_switch(port)
logger.info("OK : Removed port %s", port)
am.action_add(remove_ports_from_switch, port)
# step 3 execute the other operations
for descr in pairs:
move_descr(descr)
# steps 4 and 5 occur automatically
Another way to do it - and probably a lot better - would be to add the servers/ports in the __enter__ method. You could subclass the ActionManager above and add the port addition and removal logic inside of it.
The __enter__ method doesn't even have to return an instance of the ActionManager class - if it makes sense to do so, you could even write it so that with SwitchManager(servers,ports) returns your pairs object, and you could end up doing this:
with SwitchManager(servers, ports) as pairs:
for descr in pairs:
move_descr(descr)

Python SimpleXMLRPCServer: get user IP and simple authentication

I am trying to make a very simple XML RPC Server with Python that provides basic authentication + ability to obtain the connected user's IP. Let's take the example provided in http://docs.python.org/library/xmlrpclib.html :
import xmlrpclib
from SimpleXMLRPCServer import SimpleXMLRPCServer
def is_even(n):
return n%2 == 0
server = SimpleXMLRPCServer(("localhost", 8000))
server.register_function(is_even, "is_even")
server.serve_forever()
So now, the first idea behind this is to make the user supply credentials and process them before allowing him to use the functions. I need very simple authentication, for example just a code. Right now what I'm doing is to force the user to supply this code in the function call and test it with an if-statement.
The second one is to be able to get the user IP when he calls a function or either store it after he connects to the server.
Moreover, I already have an Apache Server running and it might be simpler to integrate this into it.
What do you think?
This is a related question that I found helpful:
IP address of client in Python SimpleXMLRPCServer?
What worked for me was to grab the client_address in an overridden finish_request method of the server, stash it in the server itself, and then access this in an overridden server _dispatch routine. You might be able to access the server itself from within the method, too, but I was just trying to add the IP address as an automatic first argument to all my method calls. The reason I used a dict was because I'm also going to add a session token and perhaps other metadata as well.
from xmlrpc.server import DocXMLRPCServer
from socketserver import BaseServer
class NewXMLRPCServer( DocXMLRPCServer):
def finish_request( self, request, client_address):
self.client_address = client_address
BaseServer.finish_request( self, request, client_address)
def _dispatch( self, method, params):
metadata = { 'client_address' : self.client_address[ 0] }
newParams = ( metadata, ) + params
return DocXMLRPCServer._dispatch( self, method, metadata)
Note this will BREAK introspection functions like system.listMethods() because that isn't expecting the extra argument. One idea would be to check the method name for "system." and just pass the regular params in that case.

Verifying Authentication to a Telnet Session in Python

I am trying to telnet into a Cisco Switch and run a couple of commands on it. I am able to check if the host doesn't exist, not sure how to check if the username or password is correct. This is what I got so far(This is part of my class)
def login(self):
if self.user_name and self.password:
try:
self.connection=telnetlib.Telnet(self.telnet_host)
try:
self.connection.read_until('sername:',1)
self.connection.write(self.user_name+'\r\n')
self.connection.read_until('assword:',1)
self.connection.write(self.password+'\r\n')
self.connection.read_until(self.prompt,1)
print "Connected"
self.loggedON=True
except EOFError:
print "Authentication to "+ self.telnet_host+" failed.\n"
return
except:
print "Can't connect to "+self.telnet_host+"\n"
return
else:
if not self.user_name:
self.user_name=raw_input("Username: ")
self.login()
else:
self.password=raw_input("Password: ")
self.login()
It will still say it is connected even if the wrong password or username.
You could also try Exscript:
from Exscript.util.start import quickstart
def do_something(conn):
conn.autoinit()
conn.execute('show version')
quickstart('telnet://localhost', do_something)
The quickstart() function asks the user for username and password (use start() if that is not what you want). Login failure (and other errors) are handeled automatically. You may also want to look at Exscript.util.start.
First of all, you shouldn't have a blanket try/except block like that. Catch exceptions more narrowly. Also, as others have commented, you might consider SNMP.
Having said that, if you push ahead with Telnet, you might as well just reuse someone else's code. I found this for example with a simple Google search.

Categories

Resources