<commit_before>#!/usr/bin/python
# (c) 2018 <NAME>. MIT licensed, see https://opensource.org/licenses/MIT
# Part of Blender Driver, see https://github.com/sjjhsjjh/blender-driver
"""Python module for Blender Driver diagnostic application.
This application doesn't make anything happen in the 3D scene. It demonstrates
various things relating to use of Python in the Blender Game Engine (BGE).

-   When a controllers module gets loaded by BGE.
-   When the BGE Python instance can get execution cycles.
-   Use of threads and thread locks for synchronisation.
-   Keyboard events.

The command line options can switch off some things on which Blender Driver
relies, to demonstrate reliance.

This module can only be used from within the Blender Game Engine."""
# Exit if run other than as a module.
if __name__ == '__main__':
    print(__doc__)
    raise SystemExit(1)

# Standard library imports, in alphabetic order.
#
# Module for command line switches.
# https://docs.python.org/3.5/library/argparse.html
# The import isn't needed because this class uses the base class to get an
# object.
# import argparse
#
# Module for levelled logging messages.
# Tutorial is here: https://docs.python.org/3.5/howto/logging.html
# Reference is here: https://docs.python.org/3.5/library/logging.html
from logging import DEBUG, INFO, WARNING, ERROR, log, getLogger
#
# Module for the version of Python.
# https://docs.python.org/3.5/library/sys.html#sys.version
from sys import version as pythonVersion
#
# This module prints diagnostic information about threads.
# https://docs.python.org/3.5/library/threading.html#thread-objects
import threading
#
# Module for logging current time and sleep.
# https://docs.python.org/3.5/library/time.html
import time
#
# Local imports.
#
# Proper threaded application module.
import blender_driver.application.thread
#
# Tick time dumper.
from diagnostic.analysis import timing_analysis_dump

# Diagnostic print to show when it's imported, if all its own imports run OK.
print("".join(('Application module "', __name__, '" ')))

class Application(blender_driver.application.thread.Application):
        
    def diagnostic_remove_controllers(self, controllers):
        super().diagnostic_remove_controllers(controllers)
        if self.arguments.removeInitialiseController:
            controllers.initialise = None
        if self.arguments.removeTickController:
            controllers.tick = None

    # Override.
    def game_initialise(self):
        super().game_initialise()
        self._counter = 0
        self._tickTimes = []
        if not getLogger().isEnabledFor(DEBUG):
            log(WARNING, "Diagnostic application should be run verbose.")

        if self.arguments.mainLock:
            log(DEBUG, "acquiring main lock ...")
            self.mainLock.acquire()
            log(DEBUG, "acquired main lock.")
        log(DEBUG
            , "counter:{}.\nPython: {}\nArguments: {}\nGame scene objects: {}"
            , self._counter
            , " ".join(_.rstrip() for _ in pythonVersion.splitlines())
            , vars(self.arguments)
            , self.gameScene.objects)
        if self.arguments.removeTickController:
            print("Tick controller has been removed. Terminate BGE by"
                  " pressing Escape.")

        if self.arguments.mainLock:
            log(DEBUG, "releasing main lock.")
            self.mainLock.release()
        
        if self.arguments.thread:
            threading.Thread(
                target=self._dummy_action,
                args=(self.arguments.sleepInitialise, "initialise")
            ).start()
        else:
            self._dummy_action(self.arguments.sleepInitialise, "initialise")
        
        
    # Override.
    def game_tick(self):
        self._counter += 1
        log(DEBUG, {'counter': self._counter,
                    'Threads': threading.active_count(),
                    'sleepTick': self.arguments.sleepTick})

        if self.arguments.ticks > 0 and self._counter > self.arguments.ticks:
            log(INFO, "Terminating after ticks:{}. Maximum:{}. Threads:{}."
                , self._counter, self.arguments.ticks, threading.active_count())
            self.game_terminate()
            return
        
        if self.arguments.tickLock and self.arguments.thread:
            super().game_tick()
            # Above would set the _tickPerf. Other branches don't execute it so
            # set it "manually".
        elif self.arguments.thread:
            # Threads without locking.
            self._tickPerf = time.perf_counter() - self._gameInitialisePerf
            threading.Thread(target=self.game_tick_run).start()
        else:
            self._tickPerf = time.perf_counter() - self._gameInitialisePerf
            self.game_tick_run()
        # Case of tickLock but not threading is treated as not tickLock.

    # Override.
    def game_tick_run(self):
        self._tickTimes.append(self.tickPerf)
        self._dummy_action(
            self.arguments.sleepTick, " ".join(('tick', str(self._counter))))

    # Override.
    def tick_skipped(self):
        log(WARNING, "Tick skipped:{}. Total:{}."
            , self._counter, self.skippedTicks)

    # Override.
    def game_terminate(self):
        if self.arguments.terminateLock and self.arguments.thread:
            super().game_terminate()
        else:
            if self.arguments.terminateLock:
                self.game_terminate_lock()
            if self.arguments.thread:
                self.game_terminate_threads()
            #
            # Go up two class inheritance levels and call the method there.
            super(blender_driver.application.thread.Application, self
                  ).game_terminate()
       
        log(INFO, 'Ticks:{:d} interval:{:d}\n{}'
            , len(self._tickTimes), self.arguments.tickInterval
            , timing_analysis_dump(self._tickTimes))
    
    def _dummy_action(self, duration, name=None):
        if self.arguments.mainLock:
            log(DEBUG, '{} acquiring main lock at start ...', name)
            self.mainLock.acquire()

        log(DEBUG, '{} start of dummy.', name)
            
        if duration is None:
            log(DEBUG, '{} no sleep.', name)
        else:
            if int(duration) == duration:
                duration = int(duration)
                # This is a kind of N and a half times loop. Before the loop
                # starts, the mainLock is already acquired. Inside the loop it
                # gets released and then acquired. If the loop is broken out of,
                # or just runs out, the lock is in the acquired state.
                for sleep in range(duration):
                    if self.terminating():
                        log(DEBUG, '{} stopping.', name)<commit_msg>Changed to use tick time summary instead of logging skipped ticks.<commit_after>#!/usr/bin/python
# (c) 2018 <NAME>. MIT licensed, see https://opensource.org/licenses/MIT
# Part of Blender Driver, see https://github.com/sjjhsjjh/blender-driver
"""Python module for Blender Driver diagnostic application.
This application doesn't make anything happen in the 3D scene. It demonstrates
various things relating to use of Python in the Blender Game Engine (BGE).

-   When a controllers module gets loaded by BGE.
-   When the BGE Python instance can get execution cycles.
-   Use of threads and thread locks for synchronisation.
-   Keyboard events.

The command line options can switch off some things on which Blender Driver
relies, to demonstrate reliance.

This module can only be used from within the Blender Game Engine."""
# Exit if run other than as a module.
if __name__ == '__main__':
    print(__doc__)
    raise SystemExit(1)

# Standard library imports, in alphabetic order.
#
# Module for command line switches.
# https://docs.python.org/3.5/library/argparse.html
# The import isn't needed because this class uses the base class to get an
# object.
# import argparse
#
# Module for levelled logging messages.
# Tutorial is here: https://docs.python.org/3.5/howto/logging.html
# Reference is here: https://docs.python.org/3.5/library/logging.html
from logging import DEBUG, INFO, WARNING, ERROR, log, getLogger
#
# Module for the version of Python.
# https://docs.python.org/3.5/library/sys.html#sys.version
from sys import version as pythonVersion
#
# This module prints diagnostic information about threads.
# https://docs.python.org/3.5/library/threading.html#thread-objects
import threading
#
# Module for logging current time and sleep.
# https://docs.python.org/3.5/library/time.html
import time
#
# Local imports.
#
# Proper threaded application module.
import blender_driver.application.thread
#
# Tick time dumper.
from diagnostic.analysis import timing_summary, timing_analysis_dump

# Diagnostic print to show when it's imported, if all its own imports run OK.
print("".join(('Application module "', __name__, '" ')))

class Application(blender_driver.application.thread.Application):
        
    def diagnostic_remove_controllers(self, controllers):
        super().diagnostic_remove_controllers(controllers)
        if self.arguments.removeInitialiseController:
            controllers.initialise = None
        if self.arguments.removeTickController:
            controllers.tick = None

    # Override.
    def game_initialise(self):
        super().game_initialise()
        self._counter = 0
        self._tickTimes = []
        if not getLogger().isEnabledFor(DEBUG):
            log(WARNING, "Diagnostic application should be run verbose.")

        if self.arguments.mainLock:
            log(DEBUG, "acquiring main lock ...")
            self.mainLock.acquire()
            log(DEBUG, "acquired main lock.")
        log(DEBUG
            , "counter:{}.\nPython: {}\nArguments: {}\nGame scene objects: {}"
            , self._counter
            , " ".join(_.rstrip() for _ in pythonVersion.splitlines())
            , vars(self.arguments)
            , self.gameScene.objects)
        if self.arguments.removeTickController:
            print("Tick controller has been removed. Terminate BGE by"
                  " pressing Escape.")

        if self.arguments.mainLock:
            log(DEBUG, "releasing main lock.")
            self.mainLock.release()
        
        if self.arguments.thread:
            threading.Thread(
                target=self._dummy_action,
                args=(self.arguments.sleepInitialise, "initialise")
            ).start()
        else:
            self._dummy_action(self.arguments.sleepInitialise, "initialise")
        
        
    # Override.
    def game_tick(self):
        self._counter += 1
        log(DEBUG, {'counter': self._counter,
                    'Threads': threading.active_count(),
                    'sleepTick': self.arguments.sleepTick})

        if self.arguments.ticks > 0 and self._counter > self.arguments.ticks:
            log(INFO, "Terminating after ticks:{}. Maximum:{}. Threads:{}."
                , self._counter, self.arguments.ticks, threading.active_count())
            self.game_terminate()
            return
        
        if self.arguments.tickLock and self.arguments.thread:
            super().game_tick()
            # Above would set the _tickPerf. Other branches don't execute it so
            # set it "manually".
        elif self.arguments.thread:
            # Threads without locking.
            self._tickPerf = time.perf_counter() - self._gameInitialisePerf
            threading.Thread(target=self.game_tick_run).start()
        else:
            self._tickPerf = time.perf_counter() - self._gameInitialisePerf
            self.game_tick_run()
        # Case of tickLock but not threading is treated as not tickLock.

    # Override.
    def game_tick_run(self):
        self._tickTimes.append(self.tickPerf)
        self._dummy_action(
            self.arguments.sleepTick, " ".join(('tick', str(self._counter))))

    # Override.
    def tick_skipped(self):
        self._tickTimes.append(None)

    # Override.
    def game_terminate(self):
        if self.arguments.terminateLock and self.arguments.thread:
            super().game_terminate()
        else:
            if self.arguments.terminateLock:
                self.game_terminate_lock()
            if self.arguments.thread:
                self.game_terminate_threads()
            #
            # Go up two class inheritance levels and call the method there.
            super(blender_driver.application.thread.Application, self
                  ).game_terminate()
       
        log(INFO, 'Tick time analysis: interval:{:d} {}\n{}'
            , self.tickInterval, timing_summary(self._tickTimes)
            , timing_analysis_dump(self._tickTimes))
    
    def _dummy_action(self, duration, name=None):
        if self.arguments.mainLock:
            log(DEBUG, '{} acquiring main lock at start ...', name)
            self.mainLock.acquire()

        log(DEBUG, '{} start of dummy.', name)
            
        if duration is None:
            log(DEBUG, '{} no sleep.', name)
        else:
            if int(duration) == duration:
                duration = int(duration)
                # This is a kind of N and a half times loop. Before the loop
                # starts, the mainLock is already acquired. Inside the loop it
                # gets released and then acquired. If the loop is broken out of,
                # or just runs out, the lock is in the acquired state.
                for sleep in range(duration):
                    if self.terminating():
                        log(DEBUG, '{} stopping.', name)
{'fraction_non_alphanumeric': 0.09877539048177751, 'fraction_numerical': 0.003079856273373909, 'mean_word_length': 2.3271529641375945, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 16, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5418034', 'n_tokens_mistral': 3717, 'n_tokens_neox': 3594, 'n_words': 1068}