class SingleCommandSFTP(object): """ SingleCommandSFTP is a simple abstraction of the the Paramiko SFTP client. http://www.lag.net/paramiko/docs/paramiko.SFTPClient-class.html e.g. sftp_client = SingleCommandSFTP(host = "hostname", username = "username", password = "password") sftp_client.remove(path = '/mnt/a/b/file.txt') It is intended mainly for single operations as the connection is opened and closed for each command. For multiple commands it will be inefficient. """ def __init__(self, host, username, password, port = 22): self.host = host self.username = username self.password = password self.port = port def __open(self): self.transport = paramiko.Transport((self.host, self.port)) self.transport.connect(username = self.username, password = self.password) self.sftp_client = paramiko.SFTPClient.from_transport(self.transport) def __close(self): self.sftp_client.close() self.transport.close() def __run_command(self, command, **params): """ Call to a paramiko.SFTPClient.'command' instance method. """ log.info('sftp_client.{command}({parameters})'.format(command = command, parameters = params)) self.__open() method = getattr(self.sftp_client,command) method(**params) self.__close() # instance methods using __run_command for all instance methods of SFTPClient # are added once, dynamically, below. # SFTPClient Reference can be found at http://www.lag.net/paramiko/docs/paramiko.SFTPClient-class.html def instance_method_code_string(object,attr): argspec = inspect.getargspec(getattr(object,attr)) parameters = [arg for arg in argspec.args] if argspec.defaults: for index in range(len(argspec.defaults)): try: parameters[-1-index]+='="' + argspec.defaults[-1-index] + '"' except: parameters[-1-index]+='=%s' %argspec.defaults[-1-index] if argspec.varargs: parameters.append['*'+argspec.varargs] if argspec.keywords: parameters.append['*'+argspec.keywords] code_string = "def {name}({parameters}):\n".format(name = attr, parameters = ','.join(parameters)) code_string +=" self._SingleCommandSFTP__run_command(command='{name}',{parameters}"\ .format(name = attr, parameters = ','.join('{p}={p}'.format(p=p) for p in argspec.args[1:])) if argspec.varargs: code_string +='*'+argspec.varargs if argspec.keywords: code_string +='**'+argspec.keywords code_string +=")\n" return code_string def add_SFTPClient_equivalent_instance_methods_to_SingleCommandSFTP(): instance_method_names = [attr for attr in dir(paramiko.SFTPClient) if getattr(paramiko.SFTPClient,attr).__class__==paramiko.SFTPClient.__init__.__class__ and attr[0]!='_'] for instance_method_name in instance_method_names: code_string = instance_method_code_string(object=paramiko.SFTPClient, attr=instance_method_name) exec(code_string) setattr(SingleCommandSFTP,instance_method_name, eval(instance_method_name)) if 'instance_methods_added' not in dir(): instance_methods_added = True add_SFTPClient_equivalent_instance_methods_to_SingleCommandSFTP()getattr, setattr, eval, exec, getargspec. I won't have bloody clue what this does in two weeks' time!
Thursday, 1 May 2014
Dynamically adding instance methods.
I've been playing with some code today. It works, but may well be too clever for its own good. It may be slightly insane. There may be much better ways to do this.
I'd written a little class (SingleCommandSFTP) to make some other code a little shorter and neater. With one command, it was no problem, but when it expanded to three, the duplication of calls to __open and __close irked a little, so I added __run_command. The pragmatic thing to do was to stop. But unfortunately, I was having fun, and wondered about dynamically adding instance methods to call __run_command for all the available commands.
I ended up with this:
Subscribe to:
Posts (Atom)