예제 #1
0
파일: repy.py 프로젝트: mh6100/repy_v2
def execute_namespace_until_completion(thisnamespace, thiscontext):

    # I'll use this to detect when the program is idle so I know when to quit...
    idlethreadcount = threading.activeCount()

    # add my thread to the set of threads that are used...
    event_id = idhelper.getuniqueid()
    try:
        nanny.tattle_add_item('events', event_id)
    except Exception as e:
        tracebackrepy.handle_internalerror("Failed to acquire event for '" + \
                  "initialize' event.\n(Exception was: %s)" % e.message, 140)

    try:
        thisnamespace.evaluate(thiscontext)
    except SystemExit:
        raise
    except:
        # I think it makes sense to exit if their code throws an exception...
        tracebackrepy.handle_exception()
        harshexit.harshexit(6)
    finally:
        nanny.tattle_remove_item('events', event_id)

    # I've changed to the threading library, so this should increase if there are
    # pending events
    while threading.activeCount() > idlethreadcount:
        # do accounting here?
        time.sleep(0.25)

    # Once there are no more events, return...
    return
예제 #2
0
def execute_namespace_until_completion(thisnamespace, thiscontext):

    # I'll use this to detect when the program is idle so I know when to quit...
    idlethreadcount = threading.activeCount()

    # add my thread to the set of threads that are used...
    event_id = idhelper.getuniqueid()
    try:
        nanny.tattle_add_item('events', event_id)
    except Exception, e:
        tracebackrepy.handle_internalerror("Failed to acquire event for '" + \
                  "initialize' event.\n(Exception was: %s)" % e.message, 140)
예제 #3
0
def createthread(function):
    """
  <Purpose>
    Creates a new thread of execution.

  <Arguments>
    function:
      The function to invoke on entering the new thread.

  <Exceptions>
    RepyArgumentError is raised if the function is not callable.
    ResourceExhaustedError is raised if there are no available events.

  <Side Effects>
    Launches a new thread.

  <Resource Consumption>
    Consumes an event.

  <Returns>
    None
  """
    # Check if the function is callable
    if not safe_callable(function):
        raise RepyArgumentError("Provided function is not callable!")

    # Generate a unique handle and see if there are resources available
    eventhandle = EVENT_PREFIX + idhelper.getuniqueid()
    nanny.tattle_add_item('events', eventhandle)

    # Wrap the provided function
    def wrapped_func():
        try:
            function()
        except:
            # Exit if they throw an uncaught exception
            tracebackrepy.handle_exception()
            harshexit.harshexit(30)
        finally:
            # Remove the event before I exit
            nanny.tattle_remove_item('events', eventhandle)

    # Create a thread object
    tobj = threading.Thread(target=wrapped_func,
                            name=idhelper.get_new_thread_name(EVENT_PREFIX))

    # Check if we get an exception trying to create a new thread
    try:
        tobj.start()
    except thread.error:
        # Set exit code 56, which stands for a Threading Error
        # The Node manager will detect this and handle it
        harshexit.harshexit(56)
예제 #4
0
    def __init__(self, filename, create):
        """
      This is an internal initializer.   See emulated_open for details.
    """
        # Initialize the fields, otherwise __del__ gets confused
        # when we throw an exception. This was not a problem when the
        # logic was in emulated_open, since we would never throw an
        # exception

        self.filename = filename
        self.fobj = None
        self.seek_lock = threading.Lock()
        self.filesize = 0

        # raise an RepyArgumentError if the filename isn't valid
        _assert_is_allowed_filename(filename)

        # Check the  type of create
        if type(create) is not bool:
            raise RepyArgumentError(
                "Create argument type is invalid! Must be a Boolean!")

        OPEN_FILES_LOCK.acquire()
        try:
            # I am not checking whether this file is already opened, I will allow two fd's
            # on the same file. This behaviour is normal with python in windows and linux.

            # Here is where we try to allocate a "file" resource from the
            # nanny system.   We will restore this below if there is an exception
            # This may raise a ResourceExhautedError
            nanny.tattle_add_item('filesopened', self.filename)

            # When a file is opened in "r+", it will only succeed if the file already exists
            # this will work when create is set to False, I catch the exception and raise RepyError
            # this avoids the need to explicitly check with the create flag.
            # Store a file handle.
            try:
                self.fobj = safe_open(self.filename,
                                      "w+b" if create else "r+b")
            except IOError:
                raise FileNotFoundError('Cannot openfile non-existent file "' +
                                        filename + '" without creating it!')

        # I am not sure what will cause this exception, I will leave it for now, this won't have
        # any performance impact.
        except RepyException:
            # Restore the file handle we tattled
            nanny.tattle_remove_item('filesopened', self.filename)
            raise

        finally:
            OPEN_FILES_LOCK.release()
예제 #5
0
def createthread(function):
  """
  <Purpose>
    Creates a new thread of execution.

  <Arguments>
    function:
      The function to invoke on entering the new thread.

  <Exceptions>
    RepyArgumentError is raised if the function is not callable.
    ResourceExhaustedError is raised if there are no available events.

  <Side Effects>
    Launches a new thread.

  <Resource Consumption>
    Consumes an event.

  <Returns>
    None
  """
  # Check if the function is callable
  if not safe_callable(function):
    raise RepyArgumentError("Provided function is not callable!")

  # Generate a unique handle and see if there are resources available
  eventhandle = EVENT_PREFIX + idhelper.getuniqueid()
  nanny.tattle_add_item('events', eventhandle)

  # Wrap the provided function
  def wrapped_func():
    try:
      function()
    except:
      # Exit if they throw an uncaught exception
      tracebackrepy.handle_exception()
      harshexit.harshexit(30)
    finally: 
      # Remove the event before I exit
      nanny.tattle_remove_item('events',eventhandle)

  # Create a thread object
  tobj = threading.Thread(target=wrapped_func, name=idhelper.get_new_thread_name(EVENT_PREFIX))

  # Check if we get an exception trying to create a new thread
  try:
    tobj.start()
  except thread.error:
    # Set exit code 56, which stands for a Threading Error
    # The Node manager will detect this and handle it
    harshexit.harshexit(56)
예제 #6
0
def execute_namespace_until_completion(thisnamespace, thiscontext):

  # I'll use this to detect when the program is idle so I know when to quit...
  idlethreadcount =  threading.activeCount()

 
  # add my thread to the set of threads that are used...
  event_id = idhelper.getuniqueid()
  try:
    nanny.tattle_add_item('events', event_id)
  except Exception, e:
    tracebackrepy.handle_internalerror("Failed to acquire event for '" + \
              "initialize' event.\n(Exception was: %s)" % e.message, 140)
예제 #7
0
파일: emulfile.py 프로젝트: Ashmita89/attic
  def __init__(self, filename, create):
    """
      This is an internal initializer.   See emulated_open for details.
    """
    # Initialize the fields, otherwise __del__ gets confused
    # when we throw an exception. This was not a problem when the
    # logic was in emulated_open, since we would never throw an
    # exception

    self.filename = filename
    self.fobj = None
    self.seek_lock = threading.Lock()
    self.filesize = 0

    # raise an RepyArgumentError if the filename isn't valid
    _assert_is_allowed_filename(filename)

    # Check the  type of create
    if type(create) is not bool:
      raise RepyArgumentError("Create argument type is invalid! Must be a Boolean!")

    OPEN_FILES_LOCK.acquire()
    try:
      # I am not checking whether this file is already opened, I will allow two fd's
      # on the same file. This behaviour is normal with python in windows and linux.

      # Here is where we try to allocate a "file" resource from the
      # nanny system.   We will restore this below if there is an exception
      # This may raise a ResourceExhautedError
      nanny.tattle_add_item('filesopened', self.filename)

      # When a file is opened in "r+", it will only succeed if the file already exists
      # this will work when create is set to False, I catch the exception and raise RepyError
      # this avoids the need to explicitly check with the create flag.
      # Store a file handle.
      try:
        self.fobj = safe_open(self.filename, "w+b" if create else "r+b")
      except IOError:
        raise FileNotFoundError('Cannot openfile non-existent file "'+filename+'" without creating it!')

    # I am not sure what will cause this exception, I will leave it for now, this won't have
    # any performance impact.
    except RepyException:
      # Restore the file handle we tattled
      nanny.tattle_remove_item('filesopened', self.filename)
      raise

    finally:
      OPEN_FILES_LOCK.release()
예제 #8
0
def settimer(waittime, function, args):
    """
   <Purpose>
      Allow the current event to set an event to be performed in the future.
      This does not guarantee the event will be triggered at that time, only
      that it will be triggered after that time.

   <Arguments>
      waittime:
         The minimum amount of time to wait before delivering the event
      function:
         The function to call
      args:
         The arguments to pass to the function.   This should be a tuple or 
         list

   <Exceptions>
      None.

   <Side Effects>
      None.

   <Returns>
      A timer handle, for use with canceltimer
  """
    restrictions.assertisallowed('settimer', waittime)

    eventhandle = generate_eventhandle()

    nanny.tattle_add_item('events', eventhandle)

    tobj = threading.Timer(waittime, functionwrapper,
                           [function] + [eventhandle] + [args])

    # Set the name of the thread
    tobj.setName(idhelper.get_new_thread_name(EVENT_PREFIX))

    timerinfo[eventhandle] = {'timer': tobj}

    # Check if we get an exception trying to create a new thread
    try:
        # start the timer
        tobj.start()
    except thread.error, exp:
        # Set exit code 56, which stands for a Threading Error
        # The Node manager will detect this and handle it
        harshexit.harshexit(56)
예제 #9
0
def settimer(waittime, function, args):
  """
   <Purpose>
      Allow the current event to set an event to be performed in the future.
      This does not guarantee the event will be triggered at that time, only
      that it will be triggered after that time.

   <Arguments>
      waittime:
         The minimum amount of time to wait before delivering the event
      function:
         The function to call
      args:
         The arguments to pass to the function.   This should be a tuple or 
         list

   <Exceptions>
      None.

   <Side Effects>
      None.

   <Returns>
      A timer handle, for use with canceltimer
  """
  restrictions.assertisallowed('settimer',waittime)
  
  eventhandle = generate_eventhandle()

  nanny.tattle_add_item('events',eventhandle)

  tobj = threading.Timer(waittime,functionwrapper,[function] + [eventhandle] + [args])

  # Set the name of the thread
  tobj.setName(idhelper.get_new_thread_name(EVENT_PREFIX))

  timerinfo[eventhandle] = {'timer':tobj}
  
  # Check if we get an exception trying to create a new thread
  try:
    # start the timer
    tobj.start()
  except thread.error, exp:
    # Set exit code 56, which stands for a Threading Error
    # The Node manager will detect this and handle it
    harshexit.harshexit(56)
예제 #10
0
  def __init__(self, filename, create):
    """
      This is an internal initializer.   See emulated_open for details.
    """
    # Initialize the fields, otherwise __del__ gets confused
    # when we throw an exception. This was not a problem when the
    # logic was in emulated_open, since we would never throw an
    # exception
    self.filename = filename
    self.abs_filename = None
    self.fobj = None
    self.seek_lock = threading.Lock()
    self.filesize = 0

    # raise an RepyArgumentError if the filename isn't valid
    _assert_is_allowed_filename(filename)

    # Check the  type of create
    if type(create) is not bool:
      raise RepyArgumentError("Create argument type is invalid! Must be a Boolean!")

    OPEN_FILES_LOCK.acquire()
    try:
      # Check if the file is in use
      if filename in OPEN_FILES:
        raise FileInUseError('Cannot open file "'+filename+'" because it is already open!')

      # Get the absolute file name
      self.abs_filename = os.path.abspath(os.path.join(repy_constants.REPY_CURRENT_DIR, filename))
      

      # Here is where we try to allocate a "file" resource from the
      # nanny system.   We will restore this below if there is an exception
      # This may raise a ResourceExhautedError
      nanny.tattle_add_item('filesopened', self.abs_filename)

      
      # charge for checking if the file exists.
      nanny.tattle_quantity('fileread', 4096)
      exists = os.path.isfile(self.abs_filename)

      # if there isn't a file already...
      if not exists:
        # if we shouldn't create it, it's an error
        if not create:
          raise FileNotFoundError('Cannot openfile non-existent file "'+filename+'" without creating it!')

        # okay, we should create it...
        nanny.tattle_quantity('filewrite', 4096)
        safe_open(self.abs_filename, "w").close() # Forces file creation

      # Store a file handle
      # Always open in mode r+b, this avoids Windows text-mode
      # quirks, and allows reading and writing
      self.fobj = safe_open(self.abs_filename, "r+b")

      # Add the filename to the open files
      OPEN_FILES.add(filename)

      # Get the file's size
      self.filesize = os.path.getsize(self.abs_filename)

    except RepyException:
      # Restore the file handle we tattled
      nanny.tattle_remove_item('filesopened', self.abs_filename)
      raise

    finally:
      OPEN_FILES_LOCK.release()
예제 #11
0
  # If we are in "simple execution" mode, execute and exit
  if simpleexec:
    main_namespace.evaluate(usercontext)
    sys.exit(0)


  # I'll use this to detect when the program is idle so I know when to quit...
  idlethreadcount =  threading.activeCount()

  # call the initialize function
  usercontext['callfunc'] = 'initialize'
  usercontext['callargs'] = args[:]

  initialize_id = idhelper.getuniqueid()
  try:
    nanny.tattle_add_item('events', initialize_id)
  except Exception, e:
    tracebackrepy.handle_internalerror("Failed to aquire event for '" + \
        "initialize' event.\n(Exception was: %s)" % e.message, 140)

  try:
    main_namespace.evaluate(usercontext)
  except SystemExit:
    raise
  except:
    # I think it makes sense to exit if their code throws an exception...
    tracebackrepy.handle_exception()
    harshexit.harshexit(6)
  finally:
    nanny.tattle_remove_item('events', initialize_id)
예제 #12
0
    def __init__(self, filename, mode="r", create=False):
        """
     <Purpose>
        Allows the user program to open a file safely.   This function is not
        meant to resemble the builtin "open".

     <Arguments>
        filename:
           The file that should be operated on
        mode:
           The mode:
              "r":  Open the file for reading.
              "rw": Open the file for reading and writing.

              These are the only valid modes accepted by this version of
              open(). Note: files are always opened in "binary" mode.
        create:
           If True, create the file if it doesn't exist already.

     <Exceptions>
        As with open, this may raise a number of errors. Additionally:

        ValueError is raised if this is passed an invalid mode.

     <Side Effects>
        Opens a file on disk, using a file descriptor.

     <Returns>
        A file-like object 
    """

        # Only allow 'r' and 'rw'.

        actual_mode = None
        if mode == "r":
            actual_mode = "rb"
        elif mode == "rw":
            actual_mode = "r+b"

        if actual_mode is None:
            raise ValueError("Valid modes for opening a file in repy are 'r' and 'rw'.")

        restrictions.assertisallowed("file.__init__", filename, actual_mode)

        # Here we are checking that we only open a given file once in 'write' mode
        # so that file access is more uniform across platforms. (On Microsoft
        # Windows, for example, writing to the same file from two different file-
        # handles throws an error because of the way Windows (by default) locks
        # files opened for writing.)
        fileinfolock.acquire()

        try:
            # Check the entire fileinfo dictionary for the same file already being
            # open.
            for fileinfokey in fileinfo.keys():

                # If the filename matches this one, raise an exception.
                if os.path.abspath(fileinfo[fileinfokey]["filename"]) == os.path.abspath(filename):
                    raise ValueError("A file is only allowed to have one open filehandle.")

            _assert_is_allowed_filename(filename)

            # If the file doesn't exist and the create flag was passed, create the
            # file first.
            if create and not os.path.exists(filename):
                # Create a file by opening it in write mode and then closing it.
                restrictions.assertisallowed("file.__init__", filename, "wb")

                # Allocate a resource.
                try:
                    nanny.tattle_add_item("filesopened", self.filehandle)
                except Exception:
                    # Ok, maybe we can free up a file by garbage collecting.
                    gc.collect()
                    nanny.tattle_add_item("filesopened", self.filehandle)

                # Create the file, and then free up the resource.
                created_file = myfile(filename, "wb")
                created_file.close()
                nanny.tattle_remove_item("filesopened", self.filehandle)

            self.filehandle = idhelper.getuniqueid()

            # Here is where we try to allocate a "file" resource from the
            # nanny system. If that fails, we garbage collect and try again
            # (this forces __del__() methods to be called on objects with
            # no references, which is how we automatically free up
            # file resources).
            try:
                nanny.tattle_add_item("filesopened", self.filehandle)
            except Exception:
                # Ok, maybe we can free up a file by garbage collecting.
                gc.collect()
                nanny.tattle_add_item("filesopened", self.filehandle)

            fileinfo[self.filehandle] = {
                "filename": filename,
                "mode": actual_mode,
                "fobj": myfile(filename, actual_mode),
            }
            self.name = filename
            self.mode = mode

        finally:
            fileinfolock.release()
예제 #13
0
  def __init__(self, filename, mode="r", create=False):
    """
     <Purpose>
        Allows the user program to open a file safely.   This function is not
        meant to resemble the builtin "open".

     <Arguments>
        filename:
           The file that should be operated on
        mode:
           The mode:
              "r":  Open the file for reading.
              "rw": Open the file for reading and writing.

              These are the only valid modes accepted by this version of
              open(). Note: files are always opened in "binary" mode.
        create:
           If True, create the file if it doesn't exist already.

     <Exceptions>
        As with open, this may raise a number of errors. Additionally:

        ValueError is raised if this is passed an invalid mode.

     <Side Effects>
        Opens a file on disk, using a file descriptor.

     <Returns>
        A file-like object 
    """

    # Only allow 'r' and 'rw'.

    actual_mode = None
    if mode == "r":
      actual_mode = "rb"
    elif mode == "rw":
      actual_mode = "r+b"

    if actual_mode is None:
      raise ValueError("Valid modes for opening a file in repy are 'r' and 'rw'.")
     
    restrictions.assertisallowed('file.__init__', filename, actual_mode)

    # Here we are checking that we only open a given file once in 'write' mode
    # so that file access is more uniform across platforms. (On Microsoft
    # Windows, for example, writing to the same file from two different file-
    # handles throws an error because of the way Windows (by default) locks
    # files opened for writing.)
    fileinfolock.acquire()

    try:
      # Check the entire fileinfo dictionary for the same file already being
      # open.
      for fileinfokey in fileinfo.keys():

        # If the filename matches this one, raise an exception.
        if os.path.abspath(fileinfo[fileinfokey]['filename']) == \
            os.path.abspath(filename):
          raise ValueError(\
              "A file is only allowed to have one open filehandle.")

      _assert_is_allowed_filename(filename)

      # If the file doesn't exist and the create flag was passed, create the
      # file first.
      if create and not os.path.exists(filename):
        # Create a file by opening it in write mode and then closing it.
        restrictions.assertisallowed('file.__init__', filename, 'wb')

        # Allocate a resource.
        try:
          nanny.tattle_add_item('filesopened', self.filehandle)
        except Exception:
          # Ok, maybe we can free up a file by garbage collecting.
          gc.collect()
          nanny.tattle_add_item('filesopened', self.filehandle)

        # Create the file, and then free up the resource.
        created_file = myfile(filename, 'wb')
        created_file.close()
        nanny.tattle_remove_item('filesopened', self.filehandle)

      self.filehandle = idhelper.getuniqueid()

      # Here is where we try to allocate a "file" resource from the
      # nanny system. If that fails, we garbage collect and try again
      # (this forces __del__() methods to be called on objects with
      # no references, which is how we automatically free up
      # file resources).
      try:
        nanny.tattle_add_item('filesopened', self.filehandle)
      except Exception:
        # Ok, maybe we can free up a file by garbage collecting.
        gc.collect()
        nanny.tattle_add_item('filesopened', self.filehandle)

      fileinfo[self.filehandle] = {'filename':filename, \
          'mode':actual_mode, 'fobj':myfile(filename, actual_mode)}
      self.name = filename
      self.mode = mode

    finally:
      fileinfolock.release()
예제 #14
0
    def __init__(self, filename, create):
        """
      This is an internal initializer.   See emulated_open for details.
    """
        # Initialize the fields, otherwise __del__ gets confused
        # when we throw an exception. This was not a problem when the
        # logic was in emulated_open, since we would never throw an
        # exception
        self.filename = filename
        self.abs_filename = None
        self.fobj = None
        self.seek_lock = threading.Lock()
        self.filesize = 0

        # raise an RepyArgumentError if the filename isn't valid
        _assert_is_allowed_filename(filename)

        # Check the  type of create
        if type(create) is not bool:
            raise RepyArgumentError(
                "Create argument type is invalid! Must be a Boolean!")

        OPEN_FILES_LOCK.acquire()
        try:
            # Check if the file is in use
            if filename in OPEN_FILES:
                raise FileInUseError('Cannot open file "' + filename +
                                     '" because it is already open!')

            # Get the absolute file name
            self.abs_filename = os.path.abspath(
                os.path.join(repy_constants.REPY_CURRENT_DIR, filename))

            # Here is where we try to allocate a "file" resource from the
            # nanny system.   We will restore this below if there is an exception
            # This may raise a ResourceExhautedError
            nanny.tattle_add_item('filesopened', self.abs_filename)

            # charge for checking if the file exists.
            nanny.tattle_quantity('fileread', 4096)
            exists = os.path.isfile(self.abs_filename)

            # if there isn't a file already...
            if not exists:
                # if we shouldn't create it, it's an error
                if not create:
                    raise FileNotFoundError(
                        'Cannot openfile non-existent file "' + filename +
                        '" without creating it!')

                # okay, we should create it...
                nanny.tattle_quantity('filewrite', 4096)
                safe_open(self.abs_filename,
                          "w").close()  # Forces file creation

            # Store a file handle
            # Always open in mode r+b, this avoids Windows text-mode
            # quirks, and allows reading and writing
            self.fobj = safe_open(self.abs_filename, "r+b")

            # Add the filename to the open files
            OPEN_FILES.add(filename)

            # Get the file's size
            self.filesize = os.path.getsize(self.abs_filename)

        except RepyException:
            # Restore the file handle we tattled
            nanny.tattle_remove_item('filesopened', self.abs_filename)
            raise

        finally:
            OPEN_FILES_LOCK.release()
예제 #15
0
파일: repy.py 프로젝트: stredger/viewpoints
  # If we are in "simple execution" mode, execute and exit
  if simpleexec:
    main_namespace.evaluate(usercontext)
    sys.exit(0)


  # I'll use this to detect when the program is idle so I know when to quit...
  idlethreadcount =  threading.activeCount()

  # call the initialize function
  usercontext['callfunc'] = 'initialize'
  usercontext['callargs'] = args[:]

  initialize_id = idhelper.getuniqueid()
  try:
    nanny.tattle_add_item('events', initialize_id)
  except Exception, e:
    tracebackrepy.handle_internalerror("Failed to aquire event for '" + \
        "initialize' event.\n(Exception was: %s)" % e.message, 140)

  try:
    main_namespace.evaluate(usercontext)
  except SystemExit:
    raise
  except:
    # I think it makes sense to exit if their code throws an exception...
    tracebackrepy.handle_exception()
    harshexit.harshexit(6)
  finally:
    nanny.tattle_remove_item('events', initialize_id)
예제 #16
0
  # If we are in "simple execution" mode, execute and exit
  if simpleexec:
    main_namespace.evaluate(usercontext)
    sys.exit(0)


  # I'll use this to detect when the program is idle so I know when to quit...
  idlethreadcount =  threading.activeCount()

  # call the initialize function
  usercontext['callfunc'] = 'initialize'
  usercontext['callargs'] = args[:]
 
  event_id = idhelper.getuniqueid()
  try:
    nanny.tattle_add_item('events', event_id)
  except Exception, e:
    tracebackrepy.handle_internalerror("Failed to aquire event for '" + \
              "initialize' event.\n(Exception was: %s)" % e.message, 140)
 
  try:
    if profile:
      p = cProfile.Profile()
      p.runctx('main_namespace.evaluate(usercontext)', globals(), locals(),)
      p = pstats.Stats(p)
      # p.sort_stats('cumulative')
      p.print_stats()
    else:
      main_namespace.evaluate(usercontext)
  except SystemExit:
    raise
예제 #17
0
        print "Static-code analysis failed with error: " + str(e)
        harshexit.harshexit(5)

    # Let the code string get GC'ed
    usercode = None

    # I'll use this to detect when the program is idle so I know when to quit...
    idlethreadcount = threading.activeCount()

    # call the initialize function
    usercontext['callfunc'] = 'initialize'
    usercontext['callargs'] = args[:]

    event_id = idhelper.getuniqueid()
    try:
        nanny.tattle_add_item('events', event_id)
    except Exception, e:
        tracebackrepy.handle_internalerror("Failed to aquire event for '" + \
                  "initialize' event.\n(Exception was: %s)" % e.message, 140)

    return main_namespace.code, main_namespace.get_safe_context(usercontext)


def finalize():
    global idlethreadcount, event_id

    nanny.tattle_remove_item('events', event_id)
    # I've changed to the threading library, so this should increase if there are
    # pending events

    while threading.activeCount() > idlethreadcount: