Package: atckit
ATCKitAccidentallyTheCable's Utility KitATCKitAboutHow does it work?UsageFunctionSubscriberUtilFuncscreate_object_loggercreate_static_loggerdeltatime_strdump_sstrload_sstrload_sfilescan_dirdeep_sortcheck_pidregister_pidregister_signalsfind_config_fileadd_config_search_pathremove_config_search_pathadd_config_search_file_extremove_config_search_file_extServiceVersionVersion Search Stringsversion_locatorversion_search_mergeAboutThis is a small kit of classes, util functions, etc that I found myself rewriting or reusing frequently, and instead of copying everywhere, they are now here.How does it work?Do the needfuls....do the needful danceLiterally, import whatever you need to use..UsageFunctionSubscriberA Class container for Function callback subscription via+=or-=. Functions can be retrieved in order of addition.subscriber = FunctionSubscriber()

def a():
    print("I am a teapot")

def b():
    print("I am definitely totally not also a teapot, I swear")

subscriber += a
subscriber += b

for cb in subscriber.functions:
    cb()

>> I am a teapot
>> I am definitely totally not also a teapot, I swearThis class uses thetyping.Callabletype for function storage. You can extend theFunctionSubscriberclass to define the
callback function parameters, etc.class MySubscriber(FunctionSubscriber):
    """My Function Subscriber
    Callback: (bool) -> None
    """

    _functions:list[Callable[[bool],None]]

    def __iadd__(self,fn:Callable[[bool],None]) -> Self:
        """Inline Add. Subscribe Function
        @param method \c fn Method to Subscribe
        """
        return super().__iadd__(fn)

    def __isub__(self,fn:Callable[[bool],None]) -> Self:
        """Inline Subtract. Unsubscribe Function
        @param method \c fn Method to Unsubscribe
        """
        return super().__isub__(fn)UtilFuncsA Class containing various static methods:create_object_loggerCreatelogging.Loggerinstance for object specificallycreate_static_loggerCreatelogging.Loggerinstance of a specified namedeltatime_strCreatedatetime.timedeltafrom short formatted time string. Format:0Y0M0w0d0h0m0s0msdump_sstrDump Structured Data (dict) to str of specified format. Accepts JSON, YAML, TOMLload_sstrLoad Structured Data from String. Accepts JSON, YAML, TOMLload_sfileLoad Structured Data from File, automatically determining data by file extension. Accepts JSON, YAML, TOMLscan_dirSearch a specified Path, and execute a callback function on discovered files.Allows exclusion of Files/Dirs via regex pattern matchingdeep_sortSort a Dictionary recursively, including through lists of dictscheck_pidCheck if a process ID exists (via kill 0)register_pidRegister (Write) process ID in specified directory as<service>.pidregister_signalsRegister Shutdown / Restart HandlersCheck for Shutdown via UtilFuncs.shutdown (bool)Check for Restart via UtilFuncs.restart (bool)find_config_fileLook for config file in 'well defined' paths. Searches for<service>/<config>.[toml,json,yaml]in~/.local/and/etc/(in that order)add_config_search_pathAdd Search Path forfind_config_fileremove_config_search_pathRemove Search Path forfind_config_fileadd_config_search_file_extAdd file extension forfind_config_fileremove_config_search_file_extRemove file extension forfind_config_fileServiceA Service / Daemon Class. Responds to signals properly, including HUP to restart threadsHUP does not restart main thread. So if the main configuration file needs to be re-read, the service needs to be stopped and started completely.Entrypoint functions for services are defined under the.servicesFunctionSubscriber. These functions should be loopable, or be capable of starting again each time the function completes.Create a class, which extendsService, such asMyService.Set Service Name:MyService._SERVICE_NAME = "myservice"Set Shutdown Time Limit:MyService._SERVICE_SHUTDOWN_LIMIT = 300(default shown)Set Thread Check Interval:MyService._SERVICE_CHECK_TIME = 0.5(default shown)Configuration Loading: UtilizesUtilFuncs.find_config_file()andUtilFuncs.load_sfile(), will attempt to load<service_name>/<service_name>.[toml,yaml,json]from 'well known' paths, configuaration available inMyService._config. Additional locations can be added withUtilFuncs.add_config_search_path()Subscribe / Create Thread:MyService.services += <function>Unsubscribe / Remove Thread:MyService.services -= <function>Shutdown: SetMyService.shutdown(bool), UtilizesUtilfuncs.shutdownRestart: SetMyService.restart(bool), UtilizesUtilfuncs.restartRun Check: CheckMyService.should_runto see if thread needs to stopRun: CallMyService.run()Stop: CallMyService.stop()Signal Handlers: UtilizesUtilfuncs.register_signals()Process ID storage: Setpid_dirin Configuration FileExample Service Functions:import logging
from time import sleep

from atckit.service import Service

class MyService(Service):
    def __init__(self) -> None:
        super().__init__()
        self.services += self._testloopA # Add Thread to Service
        self.services += self._testloopB # Add another Thread

    def _testloopA(self) -> None:
        """Test Function, Continuous loop
        @retval None Nothing
        """
        while self.should_run:
            self.logger.info("Loop test")
            sleep(1)

    def _testloopB(self) -> None:
        """Test Function, One Shot, restarting at minimum every `MyService._SERVICE_CHECK_TIME` seconds
        @retval None Nothing
        """
        self.logger.info("Test Looop")
        sleep(1)

if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG) # Logging Configuration
    service:MyService = MyService() # Initialize Service
    service.run() # Stop with ABRT/INT/TERM CTRL+C
    service.stop() # Cleanup / Wait for ShutdownVersionA Class for version manipulation.A Version can be created from:Semantic String ("1.0.0")List of Strings or Ints of a version (["1","0","0"]or[1,0,0])Tuple of Strings or Ints of a version (("1","0","0")or(1,0,0))Versions are comparable (>,<,>=,<=,==,!=)
Versions are addable and subtractable (a -= b,a += b)During subtraction, if a part goes negative, it will be set to 0Version Search StringsTo make Version things even easier, 2 functions are also included in the Version module, which enables a list of matching versions to be created, from the search.Version Search Strings are 1 or more entries in a specially formatted string:<comparator>:<version>,...Supported comparators:>,<,>=,<=,==,!=Example Searches:">=:1.0.0,!=:2.0.2,<=:4.0.0""<=:3.0.0,>:0.9.0"version_locatorGiven a list of versions, locate a version which matches a given search string.Example 1 matching:Any Version Newer than 1.0.0, including 1.0.0Not Version 2.0.2Any Version Older than 4.0.0, including 4.0.0Example 2 matching:Any Version Older than 3.0.0, including 3.0.0Any Version Newer than 0.9.0, not including 0.9.0version_search_mergeCombine 2 Version Search Strings, creating a single string, which satisfies all searches in each string.Given the examples above, merging these two searches, would result in the following compatible search:>=:1.0.0,<=:3.0.0,!=:2.0.2