Search code examples
pythonmultithreadingattributeerror

Python, getting 'NoneType' object has no attribute 'something' - error when exiting a multithreaded program


I have a multithreaded program and I'm having some problems with exiting it cleanly when using a keyboard interrupt. Is there a way I could exit the program cleanly even when there are multiple threads running?

Here is the code that gives the error when the program is shut down from main thread.

        def oneround(self):
          try:
                threadLocal.s.send(threadLocal.message)
                threadLocal.next_time += threadLocal.delay
                threadLocal.sch.enterabs(threadLocal.next_time, 0, self.oneround, ())
                if self.duration != 0 and time.time() > self.timeout:
                        print '\n' + 'Client socket closed after ' + str(self.duration) + ' seconds'
                        threadLocal.s.close()
                        os.kill(os.getpid(), signal.SIGINT)
          except IOError as (errno, strerror):
                if errno == 111:
                        print 'Connection failed'
                        threadLocal.s.close()

Here is the whole error message:

Exception in thread Thread-1 (most likely raised during interpreter shutdown):

Traceback (most recent call last):

File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner

File "/usr/lib/python2.7/threading.py", line 763, in run

File "trafgen6.py", line 592, in init

File "/usr/lib/python2.7/sched.py", line 117, in run

File "trafgen6.py", line 598, in oneround

type 'exceptions.AttributeError': 'NoneType' object has no attribute 's'

EDIT Here is the rest of the code for client-class

class Client(QThread):

        def __init__(self, host, port, rate, duration, parent = None):
          super(Client, self).__init__(parent)

          threadLocal.sch = sched.scheduler(time.time, time.sleep)
          threadLocal.next_time = time.time()
          threadLocal.sch.enterabs(threadLocal.next_time, 0, self.oneround, ())

          host = host
          port = port
          speed = rate
          self.duration = duration
          threadLocal.delay = 1.0 / (speed*128)
          n = bool(True)

          openfile = open('1024')
          threadLocal.message = openfile.read()

          threadLocal.s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
          threadLocal.s.setblocking(0)
          try:
                threadLocal.s.connect((host,port))
                threadLocal.s.send(threadLocal.message)
                time.sleep(0.1)
                foo =  threadLocal.s.recvfrom(1500)
                if foo != " ":
                        pass
          except IOError as (errno, strerror):
                if errno == -9:
                        n = bool(False)
                        print 'Invalid address'
                        os.kill(os.getpid(), signal.SIGINT)
                if errno == 11:
                        n = bool(False)
                        print 'Connection failed'
                        os.kill(os.getpid(), signal.SIGINT)
          if n == True:
                        self.timeout = time.time() + duration
                        threadLocal.sch.run()

EDIT 2 Here is the code for the main thread

if __name__ == '__main__':
        f = open('1024','w')
        for i in range(0, 962):
                f.write('@')
        f.close()

        def valid_interfaces():
                for i in ni.interfaces():
                        ip = ni.ifaddresses(i)[ni.AF_INET6][0]['addr']
                        if not ip.startswith('fe80') and not i.startswith('lo'):
                                list.insert(0,i)

        parser = argparse.ArgumentParser(description='Send Ipv6 traffic through your network')
        group1 = parser.add_mutually_exclusive_group()
        group2 = parser.add_mutually_exclusive_group()
        threadLocal = threading.local()
        list = []
        valid_interfaces()

        group1.add_argument('-g', '--gui',
                        action='store_true', dest='gui',
                        help='Start graphical user interface. Do not use other arguments with this option')
        group1.add_argument('-s', '--server',
                        action='store_true', dest='server',
                        help='Set the program to run in server mode')
        group1.add_argument('-c', '--client',
                        action='store_true', dest='client',
                        help='Set the program to run in client mode')
        group2.add_argument('-i', '--interface',
                        action='store', dest='interface', choices=list,
                        help='Specifies the interface for server')
        group2.add_argument('-d', '--destip',
                        action='store', dest='targetip',
                        help='Sets the destination IPv6 address eg.2001:dead:beef::1')
        parser.add_argument('-p', '--port',
                        action='store', dest='port', type=int,
                        help='Specifies the port used by server or client | Default 5001', default=5001)
        parser.add_argument('-t', '--time',
                        action='store', dest='time', type=int,
                        help='Specifies how long traffic will be sent| Default (0) is unlimited', default=0)
        parser.add_argument('-r', '--rate',
                        action='store', dest='rate', type=int,
                        help='Sets the rate at which traffic will be sent | Default 1Mb/s', default=1)
        args = parser.parse_args()

        if args.gui:
                app = QApplication(sys.argv)
                app.setStyle('cleanlooks')
                mainwindow = Application()
                mainwindow.show()
                sys.exit(app.exec_())
        if args.server and args.interface:
                thread1 = threading.Thread(target=Server, args=[args.interface, args.port])
                thread1.setDaemon(True)
                thread1.start()
                mode = 1
        if args.client and args.targetip:
                thread1 = threading.Thread(target=Client, args=[args.targetip, args.port, args.rate, args.time])
                thread1.setDaemon(True)
                thread1.start()
                mode = 2
        if threading.active_count() > 0:
                calculator(mode)

And here is the code for the main thread

class calculator(QThread):

        def __init__(self, mode, parent = None):
          super(calculator, self).__init__(parent)
          mb = 1048576.00
          try:
            while True:
              try:
                sent = {}
                recv = {}
                inwards = []
                out = []
                total = []
                n = 1
                for i in ni.interfaces():
                        counter = psutil.net_io_counters(pernic=True)[i]
                        sent.update({n:counter.bytes_sent})
                        recv.update({n:counter.bytes_recv})
                        n +=1
                time.sleep(1)
                n = 1
                for i in ni.interfaces():
                        counter = psutil.net_io_counters(pernic=True)[i]
                        totalsent = counter.bytes_sent - sent[n]
                        totalrecv = counter.bytes_recv - recv[n]
                        alldata = totalsent + totalrecv
                        self.totalout = (totalsent/mb) * 8
                        self.totalin = (totalrecv/mb) * 8
                        self.totalinout = (alldata/mb) * 8
                        inwards.insert((n-1), float("%.2f" % self.totalin))
                        out.insert((n-1), float("%.2f" % self.totalout))
                        total.insert((n-1), float("%.2f" % self.totalinout))
                        n += 1

                inwards = sum(inwards)
                out = sum(out)
                total = sum(total)
                sys.stdout.write('\r' + 'Outbound traffic ' + str(out) + 'Mb/s     ' + 'Inbound traffic ' + str$
                sys.stdout.flush()
              except KeyboardInterrupt:
                if mode == 1:
                        print '\n' + 'Server stopped'
                        raise
                elif mode == 2:
                        print '\n' + 'Client stopped'
                        raise
          except KeyboardInterrupt:
                sys.exit()

Solution

  • threading.local() creates an object that's specific to the currently running thread when the call is made. You only do this once on the main thread and then share it with all of the others. Once the interpreter starts shutting down and this goes out of scope, the other threads have a broken reference.

    threadLocal = threading.local() must be moved into your actual thread, i.e. the top of Client.__init__.