Python – (Python) Passing a threading.Thread object through a function

classinheritancemultithreadingpython

I have a timer function:

# This is a class that schedules tasks. It will call it's ring() function
# when the timer starts, and call it's running() function when within the
# time limit, and call it's over() function when the time is up.

# This class uses SYSTEM time.

import time, threading
import settings

from object import Object

class Timer(Object, threading.Thread):
    # INIT -------------------------------------------------------------
    # Init vars
    #
    # If autotick is True (default) the timer will run in a seperate
    # process. Other wise it will need to be updated automatically by
    # calling tick()
    def __init__(self, autotick=False):
        # Call inherited __init__ first.
        threading.Thread.__init__(self)
        Object.__init__(self)

        # Now our vars
        self.startTimeString = "" # The time when the timer starts as a string
        self.endTimeString = "" # The time when the timer stops as a string
        self.timeFormat = "" # The string to use as the format for the string       
        self.set = False # The timer starts deactivated
        self.process = autotick # Wether or not to run in a seperate process.
        self.rung = False # Has the timer rang yet?


    # ACTIVATE --------------------------------------------------------------
    # Sets the timer
    def activate(self, startTime, endTime, format):
        # Set the timer.
        self.startTimeString = startTime
        self.endTimeString = endTime
        self.timeFormat = format

        # Conver the strings to time using format
        try:
            self.startTime = time.strptime(startTime, self.timeFormat)
            self.endTime = time.strptime(endTime, self.timeFormat)
        except ValueError:
            # Error
            print ("Error: Cannot convert time according to format")
            return False

        # Try and convert the time to seconds
        try:
            self.startTimeSecs = time.mktime(self.startTime)
            self.endTimeSecs = time.mktime(self.endTime)
        except OverflowError, ValueError:
            # Error
            print ("Error: Cannot convert time to seconds")
            return False

        # The timer is now set
        self.set = True

        # If self.process is true, we need to start calling tick in a
        # seperate process.
        if self.process:
            self.deamon = True # We don't want python to hang if a timer
                                # is still running at exit.
            self.start()

    # RING -------------------------------------------------------------
    # This function is called when the timer starts.
    def ring(self):
        pass

    # RUNNING ----------------------------------------------------------
    # Called when the the time is whithin the time limits.
    def running(self):
        pass

    # OVER -------------------------------------------------------------
    # Called when the time is up
    def over(self):
        pass

    # TICK -------------------------------------------------------------
    # Call this every loop (or in a seperate process)
    def tick(self): 
        print time.time(), self.startTimeSecs, self.endTimeSecs, time.strftime("%A %H:%M", time.localtime(self.startTimeSecs))

        # Check the time
        if time.mktime(time.localtime()) > self.startTimeSecs and time.mktime(time.localtime()) < self.endTimeSecs and not self.rung:
            # The time has come =)
            # Call ring()
            self.ring()

            # Now set self.rung to True
            self.rung = True

        # If the time is up..
        elif time.mktime(time.localtime()) > self.endTimeSecs and self.rung:
            self.over()

            # Unset the timer
            self.set = False
            self.rung = False

        # If we are inbetween the starttime and endtime.
        elif time.mktime(time.localtime()) > self.startTimeSecs and time.mktime(time.localtime()) < self.endTimeSecs and self.rung:
            self.running()

        # If any of those aren't true, then the timer hasn't started yet
        else:
            # Check if the endTime has already passed
            if time.mktime(time.localtime()) > self.endTimeSecs:

                # The time has already passed.
                self.set = False


    # THREADING STUFF --------------------------------------------------
    # This is run by Threads start() method.
    def run(self):
        while self.set == True:
            # Tick
            self.tick()

            # Sleep for a bit to save CPU
            time.sleep(settings.TIMER_SLEEP)

And I am added schedule blocks to a scheduler:

# LOAD -------------------------------------------------------------
# Loads schedule from a file (schedule_settings.py).
def load(self):
    # Add blocks
    for block in schedule_settings.BLOCKS:
        # Calculate the day
        start_day = str(getDate(block[1].split()[0]))
        end_day = str(getDate(block[2].split()[0]))

        self.scheduler.add(start_day + " " + block[1].split()[1], end_day + " " + block[2].split()[1], "%j %H:%M", block[0])

    for block in self.scheduler.blocks:
        block.timer.tick()

    print len(self.scheduler.blocks)

    # Start the scheduler (if it isn't already)
    if not self.scheduler.running:
        self.scheduler.start()

The add function looks like this:

# ADD --------------------------------------------------------------
# Add a scheduled time
# 
# block should be a Block instance, describing what to do at this time.
def add(self, startTime, endTime, format, block):
    # Add this block
    newBlock = block

    # Start a timer for this block
    newBlock.timer = Timer()

    # Figure out the time
    year = time.strftime("%Y")

    # Add the block timer
    newBlock.timer.activate(year + " " + startTime, year + " " + endTime, "%Y " + format)

    # Add this block to the list
    self.blocks.append(newBlock)

    return

Basically with my program you can make a week's schedule and play your videos as if it were a TV channel. A block is a period of time where the channel will play certain episodes, or certain series.

My problem is that the blocks get completley messed up after using the add function. Some get duplicated, they're in the wrong order, etc. However before the add function they are completely fine. If I use a small amount of blocks (2 or 3) it seems to work fine, though.

For example if my schedule_settings.py (set's up a weeks schedule) looks like this:

# -*- coding: utf-8 -*-
# This file contains settings for a week's schedule
from block import Block
from series import Series

# MAIN BLOCK (All old episodes)
mainBlock = Block()
mainBlock.picker = 'random'
mainBlock.name = "Main Block"
mainBlock.auto(".")
mainBlock.old_episodes = True

# ONE PIECE
onepieceBlock = Block()
onepieceBlock.picker = 'latest'
onepieceBlock.name = "One Piece"
onepieceBlock.series = [
        Series(auto="One Piece"),
]

# NEWISH STUFF
newishBlock = Block()
newishBlock.picker = 'random'
newishBlock.auto(".")
newishBlock.name = "NewishBlock"
newishBlock.exclude_series = [
        #Series(auto="One Piece"),
        #Series(auto="Nyan Koi!"),
]

# NEW STUFF
newBlock = Block()
newBlock.picker = 'latest'
newBlock.name = "New Stuff"
newBlock.series = [
        Series(auto="Nyan Koi!"),
]

# ACTIVE BLOCKS
BLOCKS = (
        # MONDAY
        (mainBlock, "Monday 08:00", "Monday 22:20"),
        (onepieceBlock, "Monday 22:20", "Monday 22:30"),
        (newishBlock, "Monday 22:30", "Monday 23:00"),

        # TUESDAY
        (mainBlock, "Tuesday 08:00", "Tuesday 18:00"),
        (newBlock, "Tuesday 18:00", "Tuesday 18:30"),
        (newishBlock, "Tuesday 18:30", "Tuesday 22:00"),

        # WEDNESDAY
        (mainBlock, "Wednesday 08:00", "Wednesday 18:00"),
        (newBlock, "Wednesday 18:00", "Wednesday 18:30"),
        (newishBlock, "Wednesday 18:30", "Wednesday 22:00"),

        # THURSDAY
        (mainBlock, "Thursday 08:00", "Thursday 18:00"),
        (newBlock, "Thursday 18:00", "Thursday 18:30"),
        (newishBlock, "Thursday 18:30", "Thursday 22:00"),

        # FRIDAY
        (mainBlock, "Friday 08:00", "Friday 18:00"),
        (newBlock, "Friday 18:00", "Friday 18:30"),

        # WEEKEND
        (newishBlock, "Saturday 08:00", "Saturday 23:00"),

        (newishBlock, "Sunday 08:00", "Sunday 23:00"),

)

Before adding to the scheduler, The list produced looks fine, but after adding, then printing it out, I get:

1254810368.0 1255071600.0 1255107600.0 Friday 08:00
1254810368.0 1254777600.0 1254778200.0 Monday 22:20
1254810368.0 1255244400.0 1255298400.0 Sunday 08:00
1254810368.0 1255071600.0 1255107600.0 Friday 08:00
1254810368.0 1255107600.0 1255109400.0 Friday 18:00
1254810368.0 1255244400.0 1255298400.0 Sunday 08:00
1254810368.0 1255071600.0 1255107600.0 Friday 08:00
1254810368.0 1255107600.0 1255109400.0 Friday 18:00
1254810368.0 1255244400.0 1255298400.0 Sunday 08:00
1254810368.0 1255071600.0 1255107600.0 Friday 08:00
1254810368.0 1255107600.0 1255109400.0 Friday 18:00
1254810368.0 1255244400.0 1255298400.0 Sunday 08:00
1254810368.0 1255071600.0 1255107600.0 Friday 08:00
1254810368.0 1255107600.0 1255109400.0 Friday 18:00
1254810368.0 1255244400.0 1255298400.0 Sunday 08:00
1254810368.0 1255244400.0 1255298400.0 Sunday 08:00

I'm assuming this has somthing to do with the subclassing of threading.Thread I have done in my Timer class. Does passing it through a function and adding it to a list mess this up somehow?

(Edit) Sorry if that wasn't very concise, I was in rush and forgot to post the most important code =( Using the timer to tick manually was just some debug code, normally I would have auto=True in the timer class.

You can find all my code at: http://github.com/bombpersons/MYOT

Best Answer

You show us tons of code but not the key parts -- the Scheduler part (and what's that peculiar upper-case-O Object class...?). Anyway, to answer your question, passing instances of Thread subclasses through functions, adding them to lists, etc etc, is perfectly fine (though other things you're doing may not be -- e.g. you may not realize that, just because tick is a method of a Thread subclass, calling it from another thread does NOT mean it will execute in its own thread... rather, when called, it will execute in the calling thread).

May I suggest using the sched module in Python standard library to implement scheduling functionality, rather than running your own...?