Name | atckit JSON |
Version |
2.0.1
JSON |
| download |
home_page | None |
Summary | AccidentallyTheCables Utility Kit |
upload_time | 2025-07-11 02:19:51 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.11 |
license | GPLv3 |
keywords |
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# ATCKit
AccidentallyTheCable's Utility Kit
- [ATCKit](#atckit)
- [About](#about)
- [How does it work?](#how-does-it-work)
- [Usage](#usage)
- [FunctionSubscriber (atckit.subscriber)](#functionsubscriber-atckitsubscriber)
- [Core Functions (atckit)](#core-functions-atckit)
- [`create_object_logger`](#create_object_logger)
- [`create_static_logger`](#create_static_logger)
- [`deltatime_str`](#deltatime_str)
- [`deep_sort`](#deep_sort)
- [File Utils (atckit.files)](#file-utils-atckitfiles)
- [`dump_sstr`](#dump_sstr)
- [`load_sstr`](#load_sstr)
- [`load_sfile`](#load_sfile)
- [`scan_dir`](#scan_dir)
- [`find_config_file`](#find_config_file)
- [`add_config_search_path`](#add_config_search_path)
- [`remove_config_search_path`](#remove_config_search_path)
- [`add_config_search_file_ext`](#add_config_search_file_ext)
- [`remove_config_search_file_ext`](#remove_config_search_file_ext)
- [Signals (atckit.signals)](#signals-atckitsignals)
- [`check_pid`](#check_pid)
- [`register_pid`](#register_pid)
- [`register_signals`](#register_signals)
- [Service (atckit.service)](#service-atckitservice)
- [Version (atckit.version)](#version-atckitversion)
- [Version Search Strings](#version-search-strings)
- [`version_locator`](#version_locator)
- [`version_search_merge`](#version_search_merge)
## About
This 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.
> **WARNING**: Version 2.0 is a breaking change from 1.x versions
> 2.0 Removes the static class and moves things around. Please check the docs below for where things are now
### How does it work?
Do the needfuls.... *do the needful dance*
Literally, import whatever you need to use..
## Usage
### FunctionSubscriber (atckit.subscriber)
A 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 swear
```
This class uses the `typing.Callable` type for function storage. You can extend the `FunctionSubscriber` class 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)
```
## Core Functions (atckit)
### `create_object_logger`
Create `logging.Logger` instance for object specifically
### `create_static_logger`
Create `logging.Logger` instance of a specified name
### `deltatime_str`
Create `datetime.timedelta` from short formatted time string. Format: `0Y0M0w0d0h0m0s0ms`
### `deep_sort`
Sort a Dictionary recursively, including through lists of dicts
## File Utils (atckit.files)
Classes and functions located in the `files` module
### `dump_sstr`
Dump Structured Data (dict) to str of specified format. Accepts JSON, YAML, TOML
### `load_sstr`
Load Structured Data from String. Accepts JSON, YAML, TOML
### `load_sfile`
Load Structured Data from File, automatically determining data by file extension. Accepts JSON, YAML, TOML
### `scan_dir`
Search a specified Path, and execute a callback function on discovered files.
- Allows exclusion of Files/Dirs via regex pattern matching
### `find_config_file`
Look for config file in 'well defined' paths. Searches for `<service>/<config>.[toml,json,yaml]` in `~/.local/` and `/etc/` (in that order)
### `add_config_search_path`
Add Search Path for [`find_config_file`](#find_config_file)
### `remove_config_search_path`
Remove Search Path for [`find_config_file`](#find_config_file)
### `add_config_search_file_ext`
Add file extension for [`find_config_file`](#find_config_file)
### `remove_config_search_file_ext`
Remove file extension for [`find_config_file`](#find_config_file)
## Signals (atckit.signals)
Signal Handling functions located in `signals`
### `check_pid`
Check if a process ID exists (via kill 0)
### `register_pid`
Register (Write) process ID in specified directory as `<service>.pid`
### `register_signals`
Register Shutdown / Restart Handlers
- Check for Shutdown via UtilFuncs.shutdown (bool)
- Check for Restart via UtilFuncs.restart (bool)
## Service (atckit.service)
A Service / Daemon Class. Responds to signals properly, including HUP to restart threads
HUP 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 `.services` [`FunctionSubscriber`](#FunctionSubscriber). These functions should be loopable, or be capable of starting again each time the function completes.
Create a class, which extends `Service`, such as `MyService`.
- 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: Utilizes [`UtilFuncs.find_config_file()`](#find_config_file) and [`UtilFuncs.load_sfile()`](#load_sfile), will attempt to load `<service_name>/<service_name>.[toml,yaml,json]` from 'well known' paths, configuaration available in `MyService._config`. Additional locations can be added with [`UtilFuncs.add_config_search_path()`](#add_config_search_path)
- Subscribe / Create Thread: `MyService.services += <function>`
- Unsubscribe / Remove Thread: `MyService.services -= <function>`
- Shutdown: Set `MyService.shutdown` (bool), Utilizes `Utilfuncs.shutdown`
- Restart: Set `MyService.restart` (bool), Utilizes `Utilfuncs.restart`
- Run Check: Check `MyService.should_run` to see if thread needs to stop
- Run: Call `MyService.run()`
- Stop: Call `MyService.stop()`
- Signal Handlers: Utilizes [`Utilfuncs.register_signals()`](#register_signals)
- Process ID storage: Set `pid_dir` in Configuration File
Example 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 Shutdown
```
## Version (atckit.version)
A 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 0
### Version Search Strings
To 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_locator`
Given 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.0
- Not Version 2.0.2
- Any Version Older than 4.0.0, including 4.0.0
- Example 2 matching:
- Any Version Older than 3.0.0, including 3.0.0
- Any Version Newer than 0.9.0, not including 0.9.0
#### `version_search_merge`
Combine 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`
Raw data
{
"_id": null,
"home_page": null,
"name": "atckit",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.11",
"maintainer_email": null,
"keywords": null,
"author": null,
"author_email": "AccidentallyTheCable <cableninja@cableninja.net>",
"download_url": "https://files.pythonhosted.org/packages/d8/ab/48febe366249b98a95332e4030f042f3737c0e7b9bb59052a4b5f40a70c1/atckit-2.0.1.tar.gz",
"platform": null,
"description": "# ATCKit\n\nAccidentallyTheCable's Utility Kit\n\n- [ATCKit](#atckit)\n - [About](#about)\n - [How does it work?](#how-does-it-work)\n - [Usage](#usage)\n - [FunctionSubscriber (atckit.subscriber)](#functionsubscriber-atckitsubscriber)\n - [Core Functions (atckit)](#core-functions-atckit)\n - [`create_object_logger`](#create_object_logger)\n - [`create_static_logger`](#create_static_logger)\n - [`deltatime_str`](#deltatime_str)\n - [`deep_sort`](#deep_sort)\n - [File Utils (atckit.files)](#file-utils-atckitfiles)\n - [`dump_sstr`](#dump_sstr)\n - [`load_sstr`](#load_sstr)\n - [`load_sfile`](#load_sfile)\n - [`scan_dir`](#scan_dir)\n - [`find_config_file`](#find_config_file)\n - [`add_config_search_path`](#add_config_search_path)\n - [`remove_config_search_path`](#remove_config_search_path)\n - [`add_config_search_file_ext`](#add_config_search_file_ext)\n - [`remove_config_search_file_ext`](#remove_config_search_file_ext)\n - [Signals (atckit.signals)](#signals-atckitsignals)\n - [`check_pid`](#check_pid)\n - [`register_pid`](#register_pid)\n - [`register_signals`](#register_signals)\n - [Service (atckit.service)](#service-atckitservice)\n - [Version (atckit.version)](#version-atckitversion)\n - [Version Search Strings](#version-search-strings)\n - [`version_locator`](#version_locator)\n - [`version_search_merge`](#version_search_merge)\n\n## About\n\nThis 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.\n\n> **WARNING**: Version 2.0 is a breaking change from 1.x versions\n> 2.0 Removes the static class and moves things around. Please check the docs below for where things are now\n\n### How does it work?\n\nDo the needfuls.... *do the needful dance*\n\nLiterally, import whatever you need to use..\n\n## Usage\n\n### FunctionSubscriber (atckit.subscriber)\n\nA Class container for Function callback subscription via `+=` or `-=`. Functions can be retrieved in order of addition.\n\n```\nsubscriber = FunctionSubscriber()\n\ndef a():\n print(\"I am a teapot\")\n\ndef b():\n print(\"I am definitely totally not also a teapot, I swear\")\n\nsubscriber += a\nsubscriber += b\n\nfor cb in subscriber.functions:\n cb()\n\n>> I am a teapot\n>> I am definitely totally not also a teapot, I swear\n```\n\nThis class uses the `typing.Callable` type for function storage. You can extend the `FunctionSubscriber` class to define the\ncallback function parameters, etc.\n\n```\nclass MySubscriber(FunctionSubscriber):\n \"\"\"My Function Subscriber\n Callback: (bool) -> None\n \"\"\"\n\n _functions:list[Callable[[bool],None]]\n\n def __iadd__(self,fn:Callable[[bool],None]) -> Self:\n \"\"\"Inline Add. Subscribe Function\n @param method \\c fn Method to Subscribe\n \"\"\"\n return super().__iadd__(fn)\n\n def __isub__(self,fn:Callable[[bool],None]) -> Self:\n \"\"\"Inline Subtract. Unsubscribe Function\n @param method \\c fn Method to Unsubscribe\n \"\"\"\n return super().__isub__(fn)\n```\n\n## Core Functions (atckit)\n\n### `create_object_logger`\n\n Create `logging.Logger` instance for object specifically\n\n### `create_static_logger`\n\n Create `logging.Logger` instance of a specified name\n\n### `deltatime_str`\n\n Create `datetime.timedelta` from short formatted time string. Format: `0Y0M0w0d0h0m0s0ms`\n\n### `deep_sort`\n \n Sort a Dictionary recursively, including through lists of dicts\n\n## File Utils (atckit.files)\n\nClasses and functions located in the `files` module\n\n### `dump_sstr`\n \n Dump Structured Data (dict) to str of specified format. Accepts JSON, YAML, TOML\n\n### `load_sstr`\n\n Load Structured Data from String. Accepts JSON, YAML, TOML\n\n### `load_sfile`\n \n Load Structured Data from File, automatically determining data by file extension. Accepts JSON, YAML, TOML\n### `scan_dir`\n \n Search a specified Path, and execute a callback function on discovered files.\n - Allows exclusion of Files/Dirs via regex pattern matching\n\n### `find_config_file`\n \n Look for config file in 'well defined' paths. Searches for `<service>/<config>.[toml,json,yaml]` in `~/.local/` and `/etc/` (in that order)\n\n### `add_config_search_path`\n \n Add Search Path for [`find_config_file`](#find_config_file)\n\n### `remove_config_search_path`\n \n Remove Search Path for [`find_config_file`](#find_config_file)\n\n### `add_config_search_file_ext`\n \n Add file extension for [`find_config_file`](#find_config_file)\n\n### `remove_config_search_file_ext`\n \n Remove file extension for [`find_config_file`](#find_config_file)\n\n## Signals (atckit.signals)\n\nSignal Handling functions located in `signals`\n\n### `check_pid`\n \n Check if a process ID exists (via kill 0)\n### `register_pid`\n \n Register (Write) process ID in specified directory as `<service>.pid`\n### `register_signals`\n \n Register Shutdown / Restart Handlers\n - Check for Shutdown via UtilFuncs.shutdown (bool)\n - Check for Restart via UtilFuncs.restart (bool)\n\n## Service (atckit.service)\n\nA Service / Daemon Class. Responds to signals properly, including HUP to restart threads\n\nHUP 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.\n\nEntrypoint functions for services are defined under the `.services` [`FunctionSubscriber`](#FunctionSubscriber). These functions should be loopable, or be capable of starting again each time the function completes.\n\nCreate a class, which extends `Service`, such as `MyService`.\n\n - Set Service Name: `MyService._SERVICE_NAME = \"myservice\"`\n - Set Shutdown Time Limit: `MyService._SERVICE_SHUTDOWN_LIMIT = 300` (default shown)\n - Set Thread Check Interval: `MyService._SERVICE_CHECK_TIME = 0.5` (default shown)\n - Configuration Loading: Utilizes [`UtilFuncs.find_config_file()`](#find_config_file) and [`UtilFuncs.load_sfile()`](#load_sfile), will attempt to load `<service_name>/<service_name>.[toml,yaml,json]` from 'well known' paths, configuaration available in `MyService._config`. Additional locations can be added with [`UtilFuncs.add_config_search_path()`](#add_config_search_path)\n - Subscribe / Create Thread: `MyService.services += <function>`\n - Unsubscribe / Remove Thread: `MyService.services -= <function>`\n - Shutdown: Set `MyService.shutdown` (bool), Utilizes `Utilfuncs.shutdown`\n - Restart: Set `MyService.restart` (bool), Utilizes `Utilfuncs.restart`\n - Run Check: Check `MyService.should_run` to see if thread needs to stop\n - Run: Call `MyService.run()`\n - Stop: Call `MyService.stop()`\n - Signal Handlers: Utilizes [`Utilfuncs.register_signals()`](#register_signals)\n - Process ID storage: Set `pid_dir` in Configuration File\n\nExample Service Functions:\n\n```\nimport logging\nfrom time import sleep\n\nfrom atckit.service import Service\n\nclass MyService(Service):\n def __init__(self) -> None:\n super().__init__()\n self.services += self._testloopA # Add Thread to Service\n self.services += self._testloopB # Add another Thread\n\n def _testloopA(self) -> None:\n \"\"\"Test Function, Continuous loop\n @retval None Nothing\n \"\"\"\n while self.should_run:\n self.logger.info(\"Loop test\")\n sleep(1)\n\n def _testloopB(self) -> None:\n \"\"\"Test Function, One Shot, restarting at minimum every `MyService._SERVICE_CHECK_TIME` seconds\n @retval None Nothing\n \"\"\"\n self.logger.info(\"Test Looop\")\n sleep(1)\n\nif __name__ == \"__main__\":\n logging.basicConfig(level=logging.DEBUG) # Logging Configuration\n service:MyService = MyService() # Initialize Service\n service.run() # Stop with ABRT/INT/TERM CTRL+C\n service.stop() # Cleanup / Wait for Shutdown\n```\n\n## Version (atckit.version)\n\nA Class for version manipulation.\n\nA Version can be created from:\n - Semantic String (`\"1.0.0\"`)\n - List of Strings or Ints of a version (`[\"1\",\"0\",\"0\"]` or `[1,0,0]`)\n - Tuple of Strings or Ints of a version (`(\"1\",\"0\",\"0\")` or `(1,0,0)`)\n\nVersions are comparable (`>`,`<`,`>=`,`<=`,`==`,`!=`)\nVersions are addable and subtractable (`a -= b`, `a += b`)\n - During subtraction, if a part goes negative, it will be set to 0\n\n### Version Search Strings\n\nTo 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.\n\nVersion Search Strings are 1 or more entries in a specially formatted string: `<comparator>:<version>,...`\n\nSupported comparators: `>`,`<`,`>=`,`<=`,`==`,`!=`\n\nExample Searches:\n\n - \">=:1.0.0,!=:2.0.2,<=:4.0.0\"\n - \"<=:3.0.0,>:0.9.0\"\n\n#### `version_locator`\n\nGiven a list of versions, locate a version which matches a given search string.\n\n - Example 1 matching:\n - Any Version Newer than 1.0.0, including 1.0.0\n - Not Version 2.0.2\n - Any Version Older than 4.0.0, including 4.0.0\n - Example 2 matching:\n - Any Version Older than 3.0.0, including 3.0.0\n - Any Version Newer than 0.9.0, not including 0.9.0\n\n#### `version_search_merge`\n\nCombine 2 Version Search Strings, creating a single string, which satisfies all searches in each string.\n\nGiven the examples above, merging these two searches, would result in the following compatible search: `>=:1.0.0,<=:3.0.0,!=:2.0.2`\n",
"bugtrack_url": null,
"license": "GPLv3",
"summary": "AccidentallyTheCables Utility Kit",
"version": "2.0.1",
"project_urls": {
"Bug Tracker": "https://gitlab.com/accidentallythecable-public/python-modules/python-atckit/issues",
"Homepage": "https://gitlab.com/accidentallythecable-public/python-modules/python-atckit/"
},
"split_keywords": [],
"urls": [
{
"comment_text": "atckit, Version: 2.0.1",
"digests": {
"blake2b_256": "8fbd94cb20177fcd6c7e15be01b1ca504619cd9d5e15d8bab2463a4bdc93bf25",
"md5": "46d76237d0bf7b3499aa3cadbaea6076",
"sha256": "b66c07fa71463f772a5fd19df23387de38085e6f73d20ec3c5df7bb95131679c"
},
"downloads": -1,
"filename": "atckit-2.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "46d76237d0bf7b3499aa3cadbaea6076",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11",
"size": 28356,
"upload_time": "2025-07-11T02:19:50",
"upload_time_iso_8601": "2025-07-11T02:19:50.427786Z",
"url": "https://files.pythonhosted.org/packages/8f/bd/94cb20177fcd6c7e15be01b1ca504619cd9d5e15d8bab2463a4bdc93bf25/atckit-2.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "atckit, Version: 2.0.1",
"digests": {
"blake2b_256": "d8ab48febe366249b98a95332e4030f042f3737c0e7b9bb59052a4b5f40a70c1",
"md5": "0f6d67cb194ceeb9893646bc3545c9b4",
"sha256": "85c87581523dc42819e85e4ea719d4c8cd6703aeebe3344df8926b89a2ef9d4b"
},
"downloads": -1,
"filename": "atckit-2.0.1.tar.gz",
"has_sig": false,
"md5_digest": "0f6d67cb194ceeb9893646bc3545c9b4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.11",
"size": 92183,
"upload_time": "2025-07-11T02:19:51",
"upload_time_iso_8601": "2025-07-11T02:19:51.749345Z",
"url": "https://files.pythonhosted.org/packages/d8/ab/48febe366249b98a95332e4030f042f3737c0e7b9bb59052a4b5f40a70c1/atckit-2.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-11 02:19:51",
"github": false,
"gitlab": true,
"bitbucket": false,
"codeberg": false,
"gitlab_user": "accidentallythecable-public",
"gitlab_project": "python-modules",
"lcname": "atckit"
}