# Buggy code
def ProcessSources(
    self,
    session_identifier,
    source_path_specs,
    storage_writer,
    enable_sigsegv_handler=False,
    filter_find_specs=None,
    filter_object=None,
    hasher_names_string=None,
    mount_path=None,
    number_of_worker_processes=0,
    parser_filter_expression=None,
    preferred_year=None,
    process_archive_files=False,
    status_update_callback=None,
    show_memory_usage=False,
    temporary_directory=None,
    text_prepend=None,
    yara_rules_string=None,
):
    """Processes the sources and extract event objects.

    Args:
      session_identifier (str): identifier of the session.
      source_path_specs (list[dfvfs.PathSpec]): path specifications of
          the sources to process.
      storage_writer (StorageWriter): storage writer for a session storage.
      enable_sigsegv_handler (Optional[bool]): True if the SIGSEGV handler
          should be enabled.
      filter_find_specs (Optional[list[dfvfs.FindSpec]]): find specifications
          used in path specification extraction.
      filter_object (Optional[objectfilter.Filter]): filter object.
      hasher_names_string (Optional[str]): comma separated string of names
          of hashers to use during processing.
      mount_path (Optional[str]): mount path.
      number_of_worker_processes (Optional[int]): number of worker processes.
      parser_filter_expression (Optional[str]): parser filter expression,
          where None represents all parsers and plugins.
      preferred_year (Optional[int]): preferred year.
      process_archive_files (Optional[bool]): True if archive files should be
          scanned for file entries.
      show_memory_usage (Optional[bool]): True if memory information should be
          included in status updates.
      status_update_callback (Optional[function]): callback function for status
          updates.
      temporary_directory (Optional[str]): path of the directory for temporary
          files.
      text_prepend (Optional[str]): text to prepend to every event.
      yara_rules_string (Optional[str]): unparsed yara rule definitions.

    Returns:
      ProcessingStatus: processing status.
    """
    if number_of_worker_processes < 1:
        # One worker for each "available" CPU (minus other processes).
        # The number here is derived from the fact that the engine starts up:
        # * A main process.
        #
        # If we want to utilize all CPUs on the system we therefore need to start
        # up workers that amounts to the total number of CPUs - the other
        # processes.
        cpu_count = multiprocessing.cpu_count() - 1

        if cpu_count <= self._WORKER_PROCESSES_MINIMUM:
            cpu_count = self._WORKER_PROCESSES_MINIMUM

        elif cpu_count >= self._WORKER_PROCESSES_MAXIMUM:
            cpu_count = self._WORKER_PROCESSES_MAXIMUM

        number_of_worker_processes = cpu_count

    self._enable_sigsegv_handler = enable_sigsegv_handler
    self._number_of_worker_processes = number_of_worker_processes
    self._show_memory_usage = show_memory_usage

    # Keep track of certain values so we can spawn new extraction workers.
    self._filter_find_specs = filter_find_specs
    self._filter_object = filter_object
    self._hasher_names_string = hasher_names_string
    self._mount_path = mount_path
    self._parser_filter_expression = parser_filter_expression
    self._preferred_year = preferred_year
    self._process_archive_files = process_archive_files
    self._session_identifier = session_identifier
    self._status_update_callback = status_update_callback
    self._storage_writer = storage_writer
    self._temporary_directory = temporary_directory
    self._text_prepend = text_prepend
    self._yara_rules_string = yara_rules_string

    # Set up the storage writer before the worker processes.
    storage_writer.StartTaskStorage()

    # Set up the task queue.
    if not self._use_zeromq:
        self._task_queue = multi_process_queue.MultiProcessingQueue(
            maximum_number_of_queued_items=self._maximum_number_of_tasks
        )

    else:
        task_outbound_queue = zeromq_queue.ZeroMQBufferedReplyBindQueue(
            delay_open=True, name="Task queue", buffer_timeout_seconds=300
        )
        self._task_queue = task_outbound_queue

        # The ZeroMQ backed queue must be started first, so we can save its port.
        # TODO: raises: attribute-defined-outside-init
        # self._task_queue.name = u'Task queue'
        self._task_queue.Open()
        self._task_queue_port = self._task_queue.port

    for _ in range(0, number_of_worker_processes):
        extraction_process = self._StartExtractionWorkerProcess(storage_writer)
        self._StartMonitoringProcess(extraction_process.pid)

    self._StartProfiling()

    if self._serializers_profiler:
        storage_writer.SetSerializersProfiler(self._serializers_profiler)

    storage_writer.Open()

    # Start the status update thread after open of the storage writer
    # so we don't have to clean up the thread if the open fails.
    self._StartStatusUpdateThread()

    storage_writer.WriteSessionStart()

    try:
        storage_writer.WritePreprocessingInformation(self.knowledge_base)

        self._ProcessSources(
            source_path_specs, storage_writer, filter_find_specs=filter_find_specs
        )

    finally:
        storage_writer.WriteSessionCompletion(aborted=self._abort)

        storage_writer.Close()

        # Stop the status update thread after close of the storage writer
        # so we include the storage sync to disk in the status updates.
        self._StopStatusUpdateThread()

        if self._serializers_profiler:
            storage_writer.SetSerializersProfiler(None)

        self._StopProfiling()

    try:
        self._StopExtractionProcesses(abort=self._abort)

    except KeyboardInterrupt:
        self._AbortKill()

        # The abort can leave the main process unresponsive
        # due to incorrectly finalized IPC.
        self._KillProcess(os.getpid())

    self._task_queue.Close(abort=self._abort)

    if self._processing_status.error_path_specs:
        task_storage_abort = True
    else:
        task_storage_abort = self._abort

    storage_writer.StopTaskStorage(abort=task_storage_abort)

    if self._abort:
        logging.debug("Processing aborted.")
        self._processing_status.aborted = True
    else:
        logging.debug("Processing completed.")

    # Reset values.
    self._enable_sigsegv_handler = None
    self._number_of_worker_processes = None
    self._show_memory_usage = None

    self._filter_find_specs = None
    self._filter_object = None
    self._hasher_names_string = None
    self._mount_path = None
    self._parser_filter_expression = None
    self._preferred_year = None
    self._process_archive_files = None
    self._session_identifier = None
    self._status_update_callback = None
    self._storage_writer = None
    self._text_prepend = None

    return self._processing_status


# Traceback (exceptions.AttributeError)
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 "./plaso/engine/zeromq_queue.py", line 651, in _ZeroMQResponder
<type 'exceptions.AttributeError'>: 'NoneType' object has no attribute 'Empty'
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 "./plaso/engine/zeromq_queue.py", line 752, in _ZeroMQResponder
<type 'exceptions.AttributeError'>: 'NoneType' object has no attribute 'Empty'
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 "./plaso/engine/zeromq_queue.py", line 752, in _ZeroMQResponder
<type 'exceptions.AttributeError'>: 'NoneType' object has no attribute 'Empty'
/home/travis/build.sh: line 45:  4478 Segmentation fault      (core dumped) coverage run --source=plaso --omit="*_test*,*__init__*,*test_lib*" ./run_tests.py

# Fixed code
def ProcessSources(
    self,
    session_identifier,
    source_path_specs,
    storage_writer,
    enable_sigsegv_handler=False,
    filter_find_specs=None,
    filter_object=None,
    hasher_names_string=None,
    mount_path=None,
    number_of_worker_processes=0,
    parser_filter_expression=None,
    preferred_year=None,
    process_archive_files=False,
    status_update_callback=None,
    show_memory_usage=False,
    temporary_directory=None,
    text_prepend=None,
    yara_rules_string=None,
):
    """Processes the sources and extract event objects.

    Args:
      session_identifier (str): identifier of the session.
      source_path_specs (list[dfvfs.PathSpec]): path specifications of
          the sources to process.
      storage_writer (StorageWriter): storage writer for a session storage.
      enable_sigsegv_handler (Optional[bool]): True if the SIGSEGV handler
          should be enabled.
      filter_find_specs (Optional[list[dfvfs.FindSpec]]): find specifications
          used in path specification extraction.
      filter_object (Optional[objectfilter.Filter]): filter object.
      hasher_names_string (Optional[str]): comma separated string of names
          of hashers to use during processing.
      mount_path (Optional[str]): mount path.
      number_of_worker_processes (Optional[int]): number of worker processes.
      parser_filter_expression (Optional[str]): parser filter expression,
          where None represents all parsers and plugins.
      preferred_year (Optional[int]): preferred year.
      process_archive_files (Optional[bool]): True if archive files should be
          scanned for file entries.
      show_memory_usage (Optional[bool]): True if memory information should be
          included in status updates.
      status_update_callback (Optional[function]): callback function for status
          updates.
      temporary_directory (Optional[str]): path of the directory for temporary
          files.
      text_prepend (Optional[str]): text to prepend to every event.
      yara_rules_string (Optional[str]): unparsed yara rule definitions.

    Returns:
      ProcessingStatus: processing status.
    """
    if number_of_worker_processes < 1:
        # One worker for each "available" CPU (minus other processes).
        # The number here is derived from the fact that the engine starts up:
        # * A main process.
        #
        # If we want to utilize all CPUs on the system we therefore need to start
        # up workers that amounts to the total number of CPUs - the other
        # processes.
        cpu_count = multiprocessing.cpu_count() - 1

        if cpu_count <= self._WORKER_PROCESSES_MINIMUM:
            cpu_count = self._WORKER_PROCESSES_MINIMUM

        elif cpu_count >= self._WORKER_PROCESSES_MAXIMUM:
            cpu_count = self._WORKER_PROCESSES_MAXIMUM

        number_of_worker_processes = cpu_count

    self._enable_sigsegv_handler = enable_sigsegv_handler
    self._number_of_worker_processes = number_of_worker_processes
    self._show_memory_usage = show_memory_usage

    # Keep track of certain values so we can spawn new extraction workers.
    self._filter_find_specs = filter_find_specs
    self._filter_object = filter_object
    self._hasher_names_string = hasher_names_string
    self._mount_path = mount_path
    self._parser_filter_expression = parser_filter_expression
    self._preferred_year = preferred_year
    self._process_archive_files = process_archive_files
    self._session_identifier = session_identifier
    self._status_update_callback = status_update_callback
    self._storage_writer = storage_writer
    self._temporary_directory = temporary_directory
    self._text_prepend = text_prepend
    self._yara_rules_string = yara_rules_string

    # Set up the storage writer before the worker processes.
    storage_writer.StartTaskStorage()

    # Set up the task queue.
    if not self._use_zeromq:
        self._task_queue = multi_process_queue.MultiProcessingQueue(
            maximum_number_of_queued_items=self._maximum_number_of_tasks
        )

    else:
        task_outbound_queue = zeromq_queue.ZeroMQBufferedReplyBindQueue(
            delay_open=True,
            linger_seconds=0,
            maximum_items=1,
            name="main_task_queue",
            timeout_seconds=self._ZEROMQ_NO_WORKER_REQUEST_TIME_SECONDS,
        )
        self._task_queue = task_outbound_queue

        # The ZeroMQ backed queue must be started first, so we can save its port.
        # TODO: raises: attribute-defined-outside-init
        # self._task_queue.name = u'Task queue'
        self._task_queue.Open()
        self._task_queue_port = self._task_queue.port

    self._StartProfiling()

    if self._serializers_profiler:
        storage_writer.SetSerializersProfiler(self._serializers_profiler)

    storage_writer.Open()

    # Start the status update thread after open of the storage writer
    # so we don't have to clean up the thread if the open fails.
    self._StartStatusUpdateThread()

    storage_writer.WriteSessionStart()

    try:
        storage_writer.WritePreprocessingInformation(self.knowledge_base)

        for _ in range(number_of_worker_processes):
            extraction_process = self._StartExtractionWorkerProcess(storage_writer)
            self._StartMonitoringProcess(extraction_process.pid)

        self._ProcessSources(
            source_path_specs, storage_writer, filter_find_specs=filter_find_specs
        )

    finally:
        storage_writer.WriteSessionCompletion(aborted=self._abort)

        storage_writer.Close()

        # Stop the status update thread after close of the storage writer
        # so we include the storage sync to disk in the status updates.
        self._StopStatusUpdateThread()

        if self._serializers_profiler:
            storage_writer.SetSerializersProfiler(None)

        self._StopProfiling()

    try:
        self._StopExtractionProcesses(abort=self._abort)

    except KeyboardInterrupt:
        self._AbortKill()

        # The abort can leave the main process unresponsive
        # due to incorrectly finalized IPC.
        self._KillProcess(os.getpid())

    # The task queue should be closed by _StopExtractionProcesses, this
    # close is a failsafe, primarily due to MultiProcessingQueue's
    # blocking behaviour.
    self._task_queue.Close(abort=True)

    if self._processing_status.error_path_specs:
        task_storage_abort = True
    else:
        task_storage_abort = self._abort

    storage_writer.StopTaskStorage(abort=task_storage_abort)

    if self._abort:
        logging.debug("Processing aborted.")
        self._processing_status.aborted = True
    else:
        logging.debug("Processing completed.")

    # Reset values.
    self._enable_sigsegv_handler = None
    self._number_of_worker_processes = None
    self._show_memory_usage = None

    self._filter_find_specs = None
    self._filter_object = None
    self._hasher_names_string = None
    self._mount_path = None
    self._parser_filter_expression = None
    self._preferred_year = None
    self._process_archive_files = None
    self._session_identifier = None
    self._status_update_callback = None
    self._storage_writer = None
    self._text_prepend = None

    return self._processing_status