From 83ab4377deebc712666416fae42c15ad1c3838ac Mon Sep 17 00:00:00 2001 From: Dan Mahn Date: Wed, 2 Mar 2011 09:06:18 -0500 Subject: Taking initial py3k work from Dan Mahn. --- tftpy/TftpClient.py | 12 +++--- tftpy/TftpPacketFactory.py | 6 +-- tftpy/TftpPacketTypes.py | 50 ++++++++++++------------ tftpy/TftpServer.py | 39 +++++++++---------- tftpy/TftpShared.py | 8 +++- tftpy/TftpStates.py | 94 +++++++++++++++++++++++----------------------- tftpy/__init__.py | 16 ++++---- 7 files changed, 114 insertions(+), 111 deletions(-) diff --git a/tftpy/TftpClient.py b/tftpy/TftpClient.py index 5fbe733..de565da 100644 --- a/tftpy/TftpClient.py +++ b/tftpy/TftpClient.py @@ -3,9 +3,9 @@ instance of the client, and then use its upload or download method. Logging is performed via a standard logging object set in TftpShared.""" import time, types -from TftpShared import * -from TftpPacketFactory import * -from TftpStates import TftpContextClientDownload, TftpContextClientUpload +from .TftpShared import * +from .TftpPacketFactory import * +from .TftpStates import TftpContextClientDownload, TftpContextClientUpload class TftpClient(TftpSession): """This class is an implementation of a tftp client. Once instantiated, a @@ -21,11 +21,11 @@ class TftpClient(TftpSession): self.options = options # FIXME: If the blksize is DEF_BLKSIZE, we should just skip sending # it. - if self.options.has_key('blksize'): + if 'blksize' in self.options: size = self.options['blksize'] - tftpassert(types.IntType == type(size), "blksize must be an int") + tftpassert(int == type(size), "blksize must be an int") if size < MIN_BLKSIZE or size > MAX_BLKSIZE: - raise TftpException, "Invalid blksize: %d" % size + raise TftpException("Invalid blksize: %d" % size) else: self.options['blksize'] = DEF_BLKSIZE diff --git a/tftpy/TftpPacketFactory.py b/tftpy/TftpPacketFactory.py index d689185..b47bcd3 100644 --- a/tftpy/TftpPacketFactory.py +++ b/tftpy/TftpPacketFactory.py @@ -2,8 +2,8 @@ buffer, and return the appropriate TftpPacket object to represent it, via the parse() method.""" -from TftpShared import * -from TftpPacketTypes import * +from .TftpShared import * +from .TftpPacketTypes import * class TftpPacketFactory(object): """This class generates TftpPacket objects. It is responsible for parsing @@ -33,7 +33,7 @@ class TftpPacketFactory(object): def __create(self, opcode): """This method returns the appropriate class object corresponding to the passed opcode.""" - tftpassert(self.classes.has_key(opcode), + tftpassert(opcode in self.classes, "Unsupported opcode: %d" % opcode) packet = self.classes[opcode]() diff --git a/tftpy/TftpPacketTypes.py b/tftpy/TftpPacketTypes.py index 14cbd90..acc8108 100644 --- a/tftpy/TftpPacketTypes.py +++ b/tftpy/TftpPacketTypes.py @@ -2,7 +2,7 @@ corresponding encode and decode methods for them.""" import struct -from TftpShared import * +from .TftpShared import * class TftpSession(object): """This class is the base class for the tftp client and server. Any shared @@ -59,17 +59,17 @@ class TftpPacketWithOptions(object): length = 0 for c in buffer: #log.debug("iterating this byte: " + repr(c)) - if ord(c) == 0: + if c == 0: log.debug("found a null at length %d" % length) if length > 0: format += "%dsx" % length length = -1 else: - raise TftpException, "Invalid options in buffer" + raise TftpException("Invalid options in buffer") length += 1 log.debug("about to unpack, format is: %s" % format) - mystruct = struct.unpack(format, buffer) + mystruct = list(x.decode('latin-1') for x in struct.unpack(format, buffer)) tftpassert(len(mystruct) % 2 == 0, "packet with odd number of option/value pairs") @@ -94,7 +94,7 @@ class TftpPacket(object): order suitable for sending over the wire. This is an abstract method.""" - raise NotImplementedError, "Abstract method" + raise NotImplementedError("Abstract method") def decode(self): """The decode method of a TftpPacket takes a buffer off of the wire in @@ -104,7 +104,7 @@ class TftpPacket(object): datagram. This is an abstract method.""" - raise NotImplementedError, "Abstract method" + raise NotImplementedError("Abstract method") class TftpPacketInitial(TftpPacket, TftpPacketWithOptions): """This class is a common parent class for the RRQ and WRQ packets, as @@ -133,18 +133,18 @@ class TftpPacketInitial(TftpPacket, TftpPacketWithOptions): if self.mode == "octet": format += "5sx" else: - raise AssertionError, "Unsupported mode: %s" % mode + raise AssertionError("Unsupported mode: %s" % mode) # Add options. options_list = [] - if self.options.keys() > 0: + if len(list(self.options.keys())) > 0: log.debug("there are options to encode") for key in self.options: # Populate the option name format += "%dsx" % len(key) - options_list.append(key) + options_list.append(key.encode('latin-1')) # Populate the option value format += "%dsx" % len(str(self.options[key])) - options_list.append(str(self.options[key])) + options_list.append(str(self.options[key]).encode('latin-1')) log.debug("format is %s" % format) log.debug("options_list is %s" % options_list) @@ -152,8 +152,8 @@ class TftpPacketInitial(TftpPacket, TftpPacketWithOptions): self.buffer = struct.pack(format, self.opcode, - self.filename, - self.mode, + self.filename.encode('latin-1'), + self.mode.encode('latin-1'), *options_list) log.debug("buffer is " + repr(self.buffer)) @@ -170,7 +170,7 @@ class TftpPacketInitial(TftpPacket, TftpPacketWithOptions): subbuf = self.buffer[2:] for c in subbuf: log.debug("iterating this byte: " + repr(c)) - if ord(c) == 0: + if c == 0: nulls += 1 log.debug("found a null at length %d, now have %d" % (length, nulls)) @@ -188,13 +188,13 @@ class TftpPacketInitial(TftpPacket, TftpPacketWithOptions): shortbuf = subbuf[:tlength+1] log.debug("about to unpack buffer with format: %s" % format) log.debug("unpacking buffer: " + repr(shortbuf)) - mystruct = struct.unpack(format, shortbuf) + mystruct = list(x.decode('latin-1') for x in struct.unpack(format, shortbuf)) tftpassert(len(mystruct) == 2, "malformed packet") + log.debug("setting filename to %s" % mystruct[0]) + log.debug("setting mode to %s" % mystruct[1]) self.filename = mystruct[0] - self.mode = mystruct[1].lower() # force lc - bug 17 - log.debug("set filename to %s" % self.filename) - log.debug("set mode to %s" % self.mode) + self.mode = mystruct[1] self.options = self.decode_options(subbuf[tlength+1:]) return self @@ -370,7 +370,7 @@ class TftpPacketERR(TftpPacket): self.buffer = struct.pack(format, self.opcode, self.errorcode, - self.errmsgs[self.errorcode]) + self.errmsgs[self.errorcode].encode('latin-1')) return self def decode(self): @@ -419,8 +419,8 @@ class TftpPacketOACK(TftpPacket, TftpPacketWithOptions): log.debug("value is %s" % self.options[key]) format += "%dsx" % len(key) format += "%dsx" % len(self.options[key]) - options_list.append(key) - options_list.append(self.options[key]) + options_list.append(key.encode('latin-1')) + options_list.append(self.options[key].encode('latin-1')) self.buffer = struct.pack(format, self.opcode, *options_list) return self @@ -435,13 +435,13 @@ class TftpPacketOACK(TftpPacket, TftpPacketWithOptions): the options so that the session can update itself to the negotiated options.""" for name in self.options: - if options.has_key(name): + if name in options: if name == 'blksize': # We can accept anything between the min and max values. - size = self.options[name] - if size >= MIN_BLKSIZE and size <= MAX_BLKSIZE: + size = int(self.options[name]) + if (size >= MIN_BLKSIZE) and (size <= MAX_BLKSIZE): log.debug("negotiated blksize of %d bytes" % size) - options[blksize] = size + options['blksize'] = size else: - raise TftpException, "Unsupported option: %s" % name + raise TftpException("Unsupported option: %s" % name) return True diff --git a/tftpy/TftpServer.py b/tftpy/TftpServer.py index 9e64d83..591d7a9 100644 --- a/tftpy/TftpServer.py +++ b/tftpy/TftpServer.py @@ -5,10 +5,10 @@ TftpShared.""" import socket, os, re, time, random import select -from TftpShared import * -from TftpPacketTypes import * -from TftpPacketFactory import * -from TftpStates import * +from .TftpShared import * +from .TftpPacketTypes import * +from .TftpPacketFactory import * +from .TftpStates import * class TftpServer(TftpSession): """This class implements a tftp server object. Run the listen() method to @@ -18,13 +18,14 @@ class TftpServer(TftpSession): read from during downloads. This permits the serving of dynamic content.""" - def __init__(self, tftproot='/tftpboot', dyn_file_func=None): + def __init__(self, tftproot='/tftpboot', dyn_file_func=None, write_mode=TftpServerWriteMode.Overwrite): self.listenip = None self.listenport = None self.sock = None # FIXME: What about multiple roots? self.root = os.path.abspath(tftproot) self.dyn_file_func = dyn_file_func + self.write_mode = write_mode # A dict of sessions, where each session is keyed by a string like # ip:tid for the remote end. self.sessions = {} @@ -32,19 +33,19 @@ class TftpServer(TftpSession): if os.path.exists(self.root): log.debug("tftproot %s does exist" % self.root) if not os.path.isdir(self.root): - raise TftpException, "The tftproot must be a directory." + raise TftpException("The tftproot must be a directory.") else: log.debug("tftproot %s is a directory" % self.root) if os.access(self.root, os.R_OK): log.debug("tftproot %s is readable" % self.root) else: - raise TftpException, "The tftproot must be readable" + raise TftpException("The tftproot must be readable") if os.access(self.root, os.W_OK): log.debug("tftproot %s is writable" % self.root) else: log.warning("The tftproot %s is not writable" % self.root) else: - raise TftpException, "The tftproot does not exist." + raise TftpException("The tftproot does not exist.") def listen(self, listenip="", @@ -64,7 +65,7 @@ class TftpServer(TftpSession): # FIXME - sockets should be non-blocking self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind((listenip, listenport)) - except socket.error, err: + except socket.error as err: # Reraise it for now. raise @@ -99,25 +100,21 @@ class TftpServer(TftpSession): # which should safely work through NAT. key = "%s:%s" % (raddress, rport) - if not self.sessions.has_key(key): + if key not in self.sessions: log.debug("Creating new server context for " "session key = %s" % key) self.sessions[key] = TftpContextServer(raddress, rport, timeout, self.root, - self.dyn_file_func) - try: - self.sessions[key].start(buffer) - except TftpException, err: - deletion_list.append(key) - log.error("Fatal exception thrown from " - "session %s: %s" % (key, str(err))) + self.dyn_file_func, + self.write_mode) + self.sessions[key].start(buffer) else: log.warn("received traffic on main socket for " "existing session??") log.info("Currently handling these sessions:") - for session_key, session in self.sessions.items(): + for session_key, session in list(self.sessions.items()): log.info(" %s" % session) else: @@ -131,7 +128,7 @@ class TftpServer(TftpSession): if self.sessions[key].state == None: log.info("Successful transfer.") deletion_list.append(key) - except TftpException, err: + except TftpException as err: deletion_list.append(key) log.error("Fatal exception thrown from " "session %s: %s" @@ -149,7 +146,7 @@ class TftpServer(TftpSession): for key in self.sessions: try: self.sessions[key].checkTimeout(now) - except TftpException, err: + except TftpException as err: log.error(str(err)) deletion_list.append(key) @@ -157,7 +154,7 @@ class TftpServer(TftpSession): for key in deletion_list: log.info('') log.info("Session %s complete" % key) - if self.sessions.has_key(key): + if key in self.sessions: log.debug("Gathering up metrics from session before deleting") self.sessions[key].end() metrics = self.sessions[key].metrics diff --git a/tftpy/TftpShared.py b/tftpy/TftpShared.py index 69ade90..167beb1 100644 --- a/tftpy/TftpShared.py +++ b/tftpy/TftpShared.py @@ -23,7 +23,7 @@ def tftpassert(condition, msg): with the message passed. This just makes the code throughout cleaner by refactoring.""" if not condition: - raise TftpException, msg + raise TftpException(msg) def setLogLevel(level): """This function is a utility function for setting the internal log level. @@ -49,3 +49,9 @@ class TftpException(Exception): """This class is the parent class of all exceptions regarding the handling of the TFTP protocol.""" pass + +class TftpServerWriteMode(object): + """This class defines the server write modes.""" + Overwrite = 0 + WriteNew = 1 + DenyWrite = 2 diff --git a/tftpy/TftpStates.py b/tftpy/TftpStates.py index 142029c..5362fa1 100644 --- a/tftpy/TftpStates.py +++ b/tftpy/TftpStates.py @@ -9,9 +9,9 @@ the next packet in the transfer, and returns a state object until the transfer is complete, at which point it returns None. That is, unless there is a fatal error, in which case a TftpException is returned instead.""" -from TftpShared import * -from TftpPacketTypes import * -from TftpPacketFactory import * +from .TftpShared import * +from .TftpPacketTypes import * +from .TftpPacketFactory import * import socket, time, os, sys ############################################################################### @@ -53,7 +53,7 @@ class TftpMetrics(object): def add_dup(self, blocknumber): """This method adds a dup for a block number to the metrics.""" log.debug("Recording a dup for block %d" % blocknumber) - if self.dups.has_key(blocknumber): + if blocknumber in self.dups: self.dups[blocknumber] += 1 else: self.dups[blocknumber] = 1 @@ -67,7 +67,7 @@ class TftpMetrics(object): class TftpContext(object): """The base class of the contexts.""" - def __init__(self, host, port, timeout, dyn_file_func=None): + def __init__(self, host, port, timeout, dyn_file_func=None, write_mode=TftpServerWriteMode.Overwrite): """Constructor for the base context, setting shared instance variables.""" self.file_to_transfer = None @@ -94,6 +94,7 @@ class TftpContext(object): # The last DAT packet we sent, if applicable, to make resending easy. self.last_dat_pkt = None self.dyn_file_func = dyn_file_func + self.write_mode = write_mode def __del__(self): """Simple destructor to try to call housekeeping in the end method if @@ -105,17 +106,17 @@ class TftpContext(object): """Compare current time with last_update time, and raise an exception if we're over SOCK_TIMEOUT time.""" if now - self.last_update > SOCK_TIMEOUT: - raise TftpException, "Timeout waiting for traffic" + raise TftpException("Timeout waiting for traffic") def start(self): - raise NotImplementedError, "Abstract method" + raise NotImplementedError("Abstract method") def end(self): """Perform session cleanup, since the end method should always be called explicitely by the calling code, this works better than the destructor.""" log.debug("in TftpContext.end") - if self.fileobj is not None and not self.fileobj.closed: + if not self.fileobj.closed: log.debug("self.fileobj is open - closing") self.fileobj.close() @@ -132,7 +133,7 @@ class TftpContext(object): host = property(gethost, sethost) def setNextBlock(self, block): - if block >= 2 ** 16: + if block > 2 ** 16: log.debug("Block number rollover to 0 again") block = 0 self.__eblock = block @@ -151,12 +152,12 @@ class TftpContext(object): log.debug("In cycle, receive attempt %d" % i) try: (buffer, (raddress, rport)) = self.sock.recvfrom(MAX_BLKSIZE) - except socket.timeout, err: + except socket.timeout as err: log.warn("Timeout waiting for traffic, retrying...") continue break else: - raise TftpException, "Hit max timeouts, giving up." + raise TftpException("Hit max timeouts, giving up.") # Ok, we've received a packet. Log it. log.debug("Received %d bytes from %s:%s" @@ -190,18 +191,20 @@ class TftpContext(object): class TftpContextServer(TftpContext): """The context for the server.""" - def __init__(self, host, port, timeout, root, dyn_file_func=None): + def __init__(self, host, port, timeout, root, dyn_file_func=None, write_mode=TftpServerWriteMode.Overwrite): TftpContext.__init__(self, host, port, timeout, - dyn_file_func + dyn_file_func, + write_mode ) # At this point we have no idea if this is a download or an upload. We # need to let the start state determine that. self.state = TftpStateServerStart(self) self.root = root self.dyn_file_func = dyn_file_func + self.write_mode = write_mode def __str__(self): return "%s:%s %s" % (self.host, self.port, self.state) @@ -372,12 +375,12 @@ class TftpState(object): def handle(self, pkt, raddress, rport): """An abstract method for handling a packet. It is expected to return a TftpState object, either itself or a new state.""" - raise NotImplementedError, "Abstract method" + raise NotImplementedError("Abstract method") def handleOACK(self, pkt): """This method handles an OACK from the server, syncing any accepted options.""" - if pkt.options.keys() > 0: + if len(list(pkt.options.keys())) > 0: if pkt.match_options(self.context.options): log.info("Successful negotiation of options") # Set options to OACK options @@ -386,9 +389,9 @@ class TftpState(object): log.info(" %s = %s" % (key, self.context.options[key])) else: log.error("Failed to negotiate options") - raise TftpException, "Failed to negotiate options" + raise TftpException("Failed to negotiate options") else: - raise TftpException, "No options found in OACK" + raise TftpException("No options found in OACK") def returnSupportedOptions(self, options): """This method takes a requested options list from a client, and @@ -441,8 +444,7 @@ class TftpState(object): # FIXME - only octet mode is supported at this time. if pkt.mode != 'octet': self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, \ - "Only octet transfers are supported at this time." + raise TftpException("Only octet transfers are supported at this time.") # test host/port of client end if self.context.host != raddress or self.context.port != rport: @@ -462,7 +464,7 @@ class TftpState(object): # FIXME: Should we allow subdirectories? if pkt.filename.find(os.sep) >= 0: self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "%s found in filename, not permitted" % os.sep + raise TftpException("%s found in filename, not permitted" % os.sep) self.context.file_to_transfer = pkt.filename @@ -560,7 +562,7 @@ class TftpState(object): if pkt.blocknumber == 0: log.warn("There is no block zero!") self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "There is no block zero!" + raise TftpException("There is no block zero!") log.warn("Dropping duplicate block %d" % pkt.blocknumber) self.context.metrics.add_dup(pkt.blocknumber) log.debug("ACKing block %d again, just in case" % pkt.blocknumber) @@ -571,7 +573,7 @@ class TftpState(object): msg = "Whoa! Received future block %d but expected %d" \ % (pkt.blocknumber, self.context.next_block) log.error(msg) - raise TftpException, msg + raise TftpException(msg) # Default is to ack return TftpStateExpectDAT(self.context) @@ -593,15 +595,9 @@ class TftpStateServerRecvRRQ(TftpState): log.debug("No such file %s but using dyn_file_func" % path) self.context.fileobj = \ self.context.dyn_file_func(self.context.file_to_transfer) - - if self.context.fileobj is None: - log.debug("dyn_file_func returned 'None', treating as " - "FileNotFound") - self.sendError(TftpErrors.FileNotFound) - raise TftpException, "File not found: %s" % path else: self.sendError(TftpErrors.FileNotFound) - raise TftpException, "File not found: %s" % path + raise TftpException("File not found: %s" % path) # Options negotiation. if sendoack: @@ -629,6 +625,12 @@ class TftpStateServerRecvWRQ(TftpState): log.debug("In TftpStateServerRecvWRQ.handle") sendoack = self.serverInitial(pkt, raddress, rport) path = self.context.root + os.sep + self.context.file_to_transfer + if (self.context.write_mode == TftpServerWriteMode.WriteNew) and os.path.exists(path): + self.sendError(TftpErrors.FileAlreadyExists) + raise TftpException("File already exists: %s" % path) + elif self.context.write_mode == TftpServerWriteMode.DenyWrite: + self.sendError(TftpErrors.DiskFull) + raise TftpException("Configured for read only operation: %s" % path) log.info("Opening file %s for writing" % path) if os.path.exists(path): # FIXME: correct behavior? @@ -674,8 +676,7 @@ class TftpStateServerStart(TftpState): rport) else: self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, \ - "Invalid packet to begin up/download: %s" % pkt + raise TftpException("Invalid packet to begin up/download: %s" % pkt) class TftpStateExpectACK(TftpState): """This class represents the state of the transfer when a DAT was just @@ -708,8 +709,7 @@ class TftpStateExpectACK(TftpState): return self elif isinstance(pkt, TftpPacketERR): log.error("Received ERR packet from peer: %s" % str(pkt)) - raise TftpException, \ - "Received ERR packet from peer: %s" % str(pkt) + raise TftpException("Received ERR packet from peer: %s" % str(pkt)) else: log.warn("Discarding unsupported packet: %s" % str(pkt)) return self @@ -725,19 +725,19 @@ class TftpStateExpectDAT(TftpState): elif isinstance(pkt, TftpPacketACK): # Umm, we ACK, you don't. self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "Received ACK from peer when expecting DAT" + raise TftpException("Received ACK from peer when expecting DAT") elif isinstance(pkt, TftpPacketWRQ): self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "Received WRQ from peer when expecting DAT" + raise TftpException("Received WRQ from peer when expecting DAT") elif isinstance(pkt, TftpPacketERR): self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "Received ERR from peer: " + str(pkt) + raise TftpException("Received ERR from peer: " + str(pkt)) else: self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "Received unknown packet type from peer: " + str(pkt) + raise TftpException("Received unknown packet type from peer: " + str(pkt)) class TftpStateSentWRQ(TftpState): """Just sent an WRQ packet for an upload.""" @@ -753,7 +753,7 @@ class TftpStateSentWRQ(TftpState): log.info("Received OACK from server") try: self.handleOACK(pkt) - except TftpException, err: + except TftpException as err: log.error("Failed to negotiate options") self.sendError(TftpErrors.FailedNegotiation) raise @@ -780,19 +780,19 @@ class TftpStateSentWRQ(TftpState): elif isinstance(pkt, TftpPacketERR): self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "Received ERR from server: " + str(pkt) + raise TftpException("Received ERR from server: " + str(pkt)) elif isinstance(pkt, TftpPacketRRQ): self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "Received RRQ from server while in upload" + raise TftpException("Received RRQ from server while in upload") elif isinstance(pkt, TftpPacketDAT): self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "Received DAT from server while in upload" + raise TftpException("Received DAT from server while in upload") else: self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "Received unknown packet type from server: " + str(pkt) + raise TftpException("Received unknown packet type from server: " + str(pkt)) # By default, no state change. return self @@ -810,7 +810,7 @@ class TftpStateSentRRQ(TftpState): log.info("Received OACK from server") try: self.handleOACK(pkt) - except TftpException, err: + except TftpException as err: log.error("Failed to negotiate options: %s" % str(err)) self.sendError(TftpErrors.FailedNegotiation) raise @@ -835,19 +835,19 @@ class TftpStateSentRRQ(TftpState): elif isinstance(pkt, TftpPacketACK): # Umm, we ACK, the server doesn't. self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "Received ACK from server while in download" + raise TftpException("Received ACK from server while in download") elif isinstance(pkt, TftpPacketWRQ): self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "Received WRQ from server while in download" + raise TftpException("Received WRQ from server while in download") elif isinstance(pkt, TftpPacketERR): self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "Received ERR from server: " + str(pkt) + raise TftpException("Received ERR from server: " + str(pkt)) else: self.sendError(TftpErrors.IllegalTftpOp) - raise TftpException, "Received unknown packet type from server: " + str(pkt) + raise TftpException("Received unknown packet type from server: " + str(pkt)) # By default, no state change. return self diff --git a/tftpy/__init__.py b/tftpy/__init__.py index b1400df..9a13121 100644 --- a/tftpy/__init__.py +++ b/tftpy/__init__.py @@ -10,13 +10,13 @@ directly. The TftpClient and TftpServer classes can be reached through it. import sys -# Make sure that this is at least Python 2.3 +# Make sure that this is at least Python 3.1 verlist = sys.version_info -if not verlist[0] >= 2 or not verlist[1] >= 3: - raise AssertionError, "Requires at least Python 2.3" +if not verlist[0] >= 3 or not verlist[1] >= 1: + raise AssertionError("Requires at least Python 3.1") -from TftpShared import * -from TftpPacketTypes import * -from TftpPacketFactory import * -from TftpClient import * -from TftpServer import * +from .TftpShared import * +from .TftpPacketTypes import * +from .TftpPacketFactory import * +from .TftpClient import * +from .TftpServer import * -- cgit v1.2.1