# Grimoire Logging
[](https://github.com/wyrdbound/grimoire-logging/actions)
[](https://www.python.org/downloads/)
[](LICENSE)
[](https://github.com/astral-sh/ruff)
**Flexible logging utilities with dependency injection support for the Grimoire engine.**
Grimoire Logging provides a clean, thread-safe logging system with dependency injection capabilities. It allows applications to easily switch between different logging implementations without changing their logging code, making it ideal for libraries, applications, and testing scenarios.
## โจ Features
- **๐ Dependency Injection**: Inject custom logger implementations at runtime
- **๐งต Thread-Safe**: All operations are thread-safe and can be used in concurrent environments
- **๐ Fallback Support**: Automatically falls back to Python's standard logging when no custom logger is injected
- **๐ฆ Zero Dependencies**: Core functionality requires no external dependencies
- **๐ฏ Protocol-Based**: Clean interface definition using Python protocols
- **๐ง Easy Integration**: Simple API that works with existing codebases
- **๐งช Test-Friendly**: Easy to inject mock loggers for testing
## ๐ Quick Start
### Installation
```bash
pip install grimoire-logging
```
### Basic Usage
```python
from grimoire_logging import get_logger, inject_logger
# Get a logger - falls back to standard Python logging by default
logger = get_logger(__name__)
logger.info("Hello, world!")
# Create a custom logger implementation
class CustomLogger:
def info(self, msg: str, *args, **kwargs) -> None:
print(f"๐ {msg}")
def debug(self, msg: str, *args, **kwargs) -> None:
print(f"๐ {msg}")
def warning(self, msg: str, *args, **kwargs) -> None:
print(f"โ ๏ธ {msg}")
def error(self, msg: str, *args, **kwargs) -> None:
print(f"โ {msg}")
def critical(self, msg: str, *args, **kwargs) -> None:
print(f"๐ {msg}")
# Inject your custom logger - all existing loggers will now use it
inject_logger(CustomLogger())
logger.info("Now using custom logger!") # Output: ๐ Now using custom logger!
```
### Thread-Safe Logger Management
```python
import threading
from grimoire_logging import get_logger, inject_logger
def worker_function(worker_id):
logger = get_logger(f"worker_{worker_id}")
logger.info(f"Worker {worker_id} starting")
# Do work...
logger.info(f"Worker {worker_id} finished")
# Inject a logger that will be used by all threads
inject_logger(CustomLogger())
# Start multiple threads - all will use the same injected logger
threads = []
for i in range(5):
thread = threading.Thread(target=worker_function, args=(i,))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
```
### Integration with Standard Logging
```python
import logging
from grimoire_logging import get_logger, inject_logger
# Set up standard logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# Create an adapter to integrate with standard logging
class StandardLoggingAdapter:
def __init__(self, logger_name: str = "grimoire"):
self.logger = logging.getLogger(logger_name)
def debug(self, msg: str, *args, **kwargs) -> None:
self.logger.debug(msg, *args, **kwargs)
def info(self, msg: str, *args, **kwargs) -> None:
self.logger.info(msg, *args, **kwargs)
def warning(self, msg: str, *args, **kwargs) -> None:
self.logger.warning(msg, *args, **kwargs)
def error(self, msg: str, *args, **kwargs) -> None:
self.logger.error(msg, *args, **kwargs)
def critical(self, msg: str, *args, **kwargs) -> None:
self.logger.critical(msg, *args, **kwargs)
# Use standard logging as the backend
inject_logger(StandardLoggingAdapter())
logger = get_logger("myapp")
logger.info("This will use standard Python logging with proper formatting")
```
### Structured Logging
```python
import json
from datetime import datetime
from grimoire_logging import get_logger, inject_logger
class JSONLogger:
def _log(self, level: str, message: str):
log_entry = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"level": level,
"message": message
}
print(json.dumps(log_entry))
def debug(self, msg: str, *args, **kwargs) -> None:
self._log("DEBUG", msg)
def info(self, msg: str, *args, **kwargs) -> None:
self._log("INFO", msg)
def warning(self, msg: str, *args, **kwargs) -> None:
self._log("WARNING", msg)
def error(self, msg: str, *args, **kwargs) -> None:
self._log("ERROR", msg)
def critical(self, msg: str, *args, **kwargs) -> None:
self._log("CRITICAL", msg)
inject_logger(JSONLogger())
logger = get_logger("app")
logger.info("Application started")
# Output: {"timestamp": "2025-01-01T12:00:00Z", "level": "INFO", "message": "Application started"}
```
## ๐ Core Concepts
### Logger Protocol
Custom loggers must implement the `LoggerProtocol` interface:
```python
from typing import Protocol
class LoggerProtocol(Protocol):
def debug(self, msg: str, *args, **kwargs) -> None: ...
def info(self, msg: str, *args, **kwargs) -> None: ...
def warning(self, msg: str, *args, **kwargs) -> None: ...
def error(self, msg: str, *args, **kwargs) -> None: ...
def critical(self, msg: str, *args, **kwargs) -> None: ...
```
### Dependency Injection
The library uses a global injection mechanism that affects all logger instances:
```python
from grimoire_logging import inject_logger, clear_logger_injection
# Inject a custom logger
inject_logger(my_custom_logger)
# All loggers now use the custom implementation
logger1 = get_logger("module1")
logger2 = get_logger("module2")
# Both use my_custom_logger
# Clear injection to return to standard logging
clear_logger_injection()
```
### Thread Safety
All operations are thread-safe and can be called from multiple threads:
```python
import threading
from grimoire_logging import get_logger, inject_logger
# Safe to call from any thread
inject_logger(custom_logger)
logger = get_logger(__name__)
logger.info("Thread-safe logging")
```
## ๐ง API Reference
### Main Functions
#### `get_logger(name: str) -> LoggerProtocol`
Get a logger instance for the given name.
**Parameters:**
- `name`: Logger name (typically `__name__`)
**Returns:**
- Logger proxy that conforms to `LoggerProtocol`
**Example:**
```python
logger = get_logger(__name__)
logger.info("Hello, world!")
```
#### `inject_logger(logger: Optional[LoggerProtocol]) -> None`
Inject a custom logger implementation.
**Parameters:**
- `logger`: Logger implementation or `None` to revert to standard logging
**Example:**
```python
inject_logger(CustomLogger())
# or
inject_logger(None) # Clear injection
```
#### `clear_logger_injection() -> None`
Clear any injected logger and revert to default.
Equivalent to `inject_logger(None)`.
**Example:**
```python
clear_logger_injection()
```
### Protocol
#### `LoggerProtocol`
Protocol defining the interface for custom loggers.
**Methods:**
- `debug(msg: str, *args, **kwargs) -> None`
- `info(msg: str, *args, **kwargs) -> None`
- `warning(msg: str, *args, **kwargs) -> None`
- `error(msg: str, *args, **kwargs) -> None`
- `critical(msg: str, *args, **kwargs) -> None`
## ๐ Examples
See the [`examples/`](examples/) directory for comprehensive examples:
- [`basic_usage.py`](examples/basic_usage.py) - Core functionality and basic patterns
- [`advanced_usage.py`](examples/advanced_usage.py) - Structured logging, filtering, and concurrent usage
- [`integration.py`](examples/integration.py) - Integration with standard logging and frameworks
Run examples:
```bash
python examples/basic_usage.py
python examples/advanced_usage.py
python examples/integration.py
```
## ๐งช Development
### Setup
```bash
git clone https://github.com/wyrdbound/grimoire-logging.git
cd grimoire-logging
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -e ".[dev]"
```
### Running Tests
```bash
# Run all tests
pytest
# Run with coverage
pytest --cov=grimoire_logging
# Run specific test file
pytest tests/test_logging.py
```
### Code Quality
```bash
# Linting and formatting
ruff check .
ruff format .
# Type checking
mypy src/
```
## ๐ Requirements
- Python 3.8+
- No runtime dependencies
## ๐ License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
Copyright (c) 2025 The Wyrd One
## ๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate and follow the existing code style.
If you have questions about the project, please contact: wyrdbound@proton.me
## ๐ฏ Use Cases
Grimoire Logging is particularly well-suited for:
- **๐ฒ Game Development**: Flexible logging for game engines and RPG systems
- **๐ Library Development**: Allow users to control logging behavior
- **๐งช Testing**: Easy injection of mock loggers for testing
- **๐ Plugin Systems**: Different logging implementations for different environments
- **๐ Web Applications**: Request-scoped logging and structured output
- **๐ง Configuration Management**: Runtime logging configuration changes
## ๐๏ธ Architecture
The library is built around several key components:
- **LoggerProxy**: Thread-safe proxy that delegates to injected or fallback loggers
- **Protocol Design**: Clean interface definition using Python protocols
- **Thread Safety**: Re-entrant locks ensure safe concurrent access
- **Minimal Dependencies**: Zero runtime dependencies for maximum compatibility
## ๐ Performance
- **Memory Efficient**: Logger instances are cached and reused
- **Thread Safe**: Designed for high-concurrency environments
- **Low Overhead**: Minimal impact when using standard logging fallback
- **Scalable**: Efficient delegation pattern supports many logger instances
## ๐ Comparison with Other Solutions
| Feature | Grimoire Logging | Python logging | loguru | structlog |
| -------------------- | ---------------- | -------------- | ------ | --------- |
| Dependency Injection | โ
| โ | โ | Partial |
| Zero Dependencies | โ
| โ
| โ | โ |
| Thread Safety | โ
| โ
| โ
| โ
|
| Protocol Based | โ
| โ | โ | Partial |
| Easy Testing | โ
| Partial | โ
| โ
|
| Fallback Support | โ
| N/A | โ | โ |
Grimoire Logging is designed specifically for scenarios where you need clean dependency injection and the ability to completely swap out logging implementations at runtime, making it ideal for libraries and applications that need maximum flexibility.
Raw data
{
"_id": null,
"home_page": null,
"name": "grimoire-logging",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "The Wyrd One <wyrdbound@proton.me>",
"keywords": "dependency-injection, grimoire, logging, rpg, tabletop, thread-safe",
"author": null,
"author_email": "The Wyrd One <wyrdbound@proton.me>",
"download_url": "https://files.pythonhosted.org/packages/29/0a/09c6ec95b801f28b7f8eb735f8fa8076ebd5f8dbf9d6eca685cb45e6ea4f/grimoire_logging-0.1.0.tar.gz",
"platform": null,
"description": "# Grimoire Logging\n\n[](https://github.com/wyrdbound/grimoire-logging/actions)\n[](https://www.python.org/downloads/)\n[](LICENSE)\n[](https://github.com/astral-sh/ruff)\n\n**Flexible logging utilities with dependency injection support for the Grimoire engine.**\n\nGrimoire Logging provides a clean, thread-safe logging system with dependency injection capabilities. It allows applications to easily switch between different logging implementations without changing their logging code, making it ideal for libraries, applications, and testing scenarios.\n\n## \u2728 Features\n\n- **\ud83d\udd04 Dependency Injection**: Inject custom logger implementations at runtime\n- **\ud83e\uddf5 Thread-Safe**: All operations are thread-safe and can be used in concurrent environments\n- **\ud83d\udd04 Fallback Support**: Automatically falls back to Python's standard logging when no custom logger is injected\n- **\ud83d\udce6 Zero Dependencies**: Core functionality requires no external dependencies\n- **\ud83c\udfaf Protocol-Based**: Clean interface definition using Python protocols\n- **\ud83d\udd27 Easy Integration**: Simple API that works with existing codebases\n- **\ud83e\uddea Test-Friendly**: Easy to inject mock loggers for testing\n\n## \ud83d\ude80 Quick Start\n\n### Installation\n\n```bash\npip install grimoire-logging\n```\n\n### Basic Usage\n\n```python\nfrom grimoire_logging import get_logger, inject_logger\n\n# Get a logger - falls back to standard Python logging by default\nlogger = get_logger(__name__)\nlogger.info(\"Hello, world!\")\n\n# Create a custom logger implementation\nclass CustomLogger:\n def info(self, msg: str, *args, **kwargs) -> None:\n print(f\"\ud83d\udcdd {msg}\")\n\n def debug(self, msg: str, *args, **kwargs) -> None:\n print(f\"\ud83d\udc1b {msg}\")\n\n def warning(self, msg: str, *args, **kwargs) -> None:\n print(f\"\u26a0\ufe0f {msg}\")\n\n def error(self, msg: str, *args, **kwargs) -> None:\n print(f\"\u274c {msg}\")\n\n def critical(self, msg: str, *args, **kwargs) -> None:\n print(f\"\ud83d\udc80 {msg}\")\n\n# Inject your custom logger - all existing loggers will now use it\ninject_logger(CustomLogger())\nlogger.info(\"Now using custom logger!\") # Output: \ud83d\udcdd Now using custom logger!\n```\n\n### Thread-Safe Logger Management\n\n```python\nimport threading\nfrom grimoire_logging import get_logger, inject_logger\n\ndef worker_function(worker_id):\n logger = get_logger(f\"worker_{worker_id}\")\n logger.info(f\"Worker {worker_id} starting\")\n # Do work...\n logger.info(f\"Worker {worker_id} finished\")\n\n# Inject a logger that will be used by all threads\ninject_logger(CustomLogger())\n\n# Start multiple threads - all will use the same injected logger\nthreads = []\nfor i in range(5):\n thread = threading.Thread(target=worker_function, args=(i,))\n threads.append(thread)\n thread.start()\n\nfor thread in threads:\n thread.join()\n```\n\n### Integration with Standard Logging\n\n```python\nimport logging\nfrom grimoire_logging import get_logger, inject_logger\n\n# Set up standard logging\nlogging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')\n\n# Create an adapter to integrate with standard logging\nclass StandardLoggingAdapter:\n def __init__(self, logger_name: str = \"grimoire\"):\n self.logger = logging.getLogger(logger_name)\n\n def debug(self, msg: str, *args, **kwargs) -> None:\n self.logger.debug(msg, *args, **kwargs)\n\n def info(self, msg: str, *args, **kwargs) -> None:\n self.logger.info(msg, *args, **kwargs)\n\n def warning(self, msg: str, *args, **kwargs) -> None:\n self.logger.warning(msg, *args, **kwargs)\n\n def error(self, msg: str, *args, **kwargs) -> None:\n self.logger.error(msg, *args, **kwargs)\n\n def critical(self, msg: str, *args, **kwargs) -> None:\n self.logger.critical(msg, *args, **kwargs)\n\n# Use standard logging as the backend\ninject_logger(StandardLoggingAdapter())\n\nlogger = get_logger(\"myapp\")\nlogger.info(\"This will use standard Python logging with proper formatting\")\n```\n\n### Structured Logging\n\n```python\nimport json\nfrom datetime import datetime\nfrom grimoire_logging import get_logger, inject_logger\n\nclass JSONLogger:\n def _log(self, level: str, message: str):\n log_entry = {\n \"timestamp\": datetime.utcnow().isoformat() + \"Z\",\n \"level\": level,\n \"message\": message\n }\n print(json.dumps(log_entry))\n\n def debug(self, msg: str, *args, **kwargs) -> None:\n self._log(\"DEBUG\", msg)\n\n def info(self, msg: str, *args, **kwargs) -> None:\n self._log(\"INFO\", msg)\n\n def warning(self, msg: str, *args, **kwargs) -> None:\n self._log(\"WARNING\", msg)\n\n def error(self, msg: str, *args, **kwargs) -> None:\n self._log(\"ERROR\", msg)\n\n def critical(self, msg: str, *args, **kwargs) -> None:\n self._log(\"CRITICAL\", msg)\n\ninject_logger(JSONLogger())\nlogger = get_logger(\"app\")\nlogger.info(\"Application started\")\n# Output: {\"timestamp\": \"2025-01-01T12:00:00Z\", \"level\": \"INFO\", \"message\": \"Application started\"}\n```\n\n## \ud83d\udcda Core Concepts\n\n### Logger Protocol\n\nCustom loggers must implement the `LoggerProtocol` interface:\n\n```python\nfrom typing import Protocol\n\nclass LoggerProtocol(Protocol):\n def debug(self, msg: str, *args, **kwargs) -> None: ...\n def info(self, msg: str, *args, **kwargs) -> None: ...\n def warning(self, msg: str, *args, **kwargs) -> None: ...\n def error(self, msg: str, *args, **kwargs) -> None: ...\n def critical(self, msg: str, *args, **kwargs) -> None: ...\n```\n\n### Dependency Injection\n\nThe library uses a global injection mechanism that affects all logger instances:\n\n```python\nfrom grimoire_logging import inject_logger, clear_logger_injection\n\n# Inject a custom logger\ninject_logger(my_custom_logger)\n\n# All loggers now use the custom implementation\nlogger1 = get_logger(\"module1\")\nlogger2 = get_logger(\"module2\")\n# Both use my_custom_logger\n\n# Clear injection to return to standard logging\nclear_logger_injection()\n```\n\n### Thread Safety\n\nAll operations are thread-safe and can be called from multiple threads:\n\n```python\nimport threading\nfrom grimoire_logging import get_logger, inject_logger\n\n# Safe to call from any thread\ninject_logger(custom_logger)\nlogger = get_logger(__name__)\nlogger.info(\"Thread-safe logging\")\n```\n\n## \ud83d\udd27 API Reference\n\n### Main Functions\n\n#### `get_logger(name: str) -> LoggerProtocol`\n\nGet a logger instance for the given name.\n\n**Parameters:**\n\n- `name`: Logger name (typically `__name__`)\n\n**Returns:**\n\n- Logger proxy that conforms to `LoggerProtocol`\n\n**Example:**\n\n```python\nlogger = get_logger(__name__)\nlogger.info(\"Hello, world!\")\n```\n\n#### `inject_logger(logger: Optional[LoggerProtocol]) -> None`\n\nInject a custom logger implementation.\n\n**Parameters:**\n\n- `logger`: Logger implementation or `None` to revert to standard logging\n\n**Example:**\n\n```python\ninject_logger(CustomLogger())\n# or\ninject_logger(None) # Clear injection\n```\n\n#### `clear_logger_injection() -> None`\n\nClear any injected logger and revert to default.\n\nEquivalent to `inject_logger(None)`.\n\n**Example:**\n\n```python\nclear_logger_injection()\n```\n\n### Protocol\n\n#### `LoggerProtocol`\n\nProtocol defining the interface for custom loggers.\n\n**Methods:**\n\n- `debug(msg: str, *args, **kwargs) -> None`\n- `info(msg: str, *args, **kwargs) -> None`\n- `warning(msg: str, *args, **kwargs) -> None`\n- `error(msg: str, *args, **kwargs) -> None`\n- `critical(msg: str, *args, **kwargs) -> None`\n\n## \ud83d\udcd6 Examples\n\nSee the [`examples/`](examples/) directory for comprehensive examples:\n\n- [`basic_usage.py`](examples/basic_usage.py) - Core functionality and basic patterns\n- [`advanced_usage.py`](examples/advanced_usage.py) - Structured logging, filtering, and concurrent usage\n- [`integration.py`](examples/integration.py) - Integration with standard logging and frameworks\n\nRun examples:\n\n```bash\npython examples/basic_usage.py\npython examples/advanced_usage.py\npython examples/integration.py\n```\n\n## \ud83e\uddea Development\n\n### Setup\n\n```bash\ngit clone https://github.com/wyrdbound/grimoire-logging.git\ncd grimoire-logging\npython -m venv .venv\nsource .venv/bin/activate # On Windows: .venv\\Scripts\\activate\npip install -e \".[dev]\"\n```\n\n### Running Tests\n\n```bash\n# Run all tests\npytest\n\n# Run with coverage\npytest --cov=grimoire_logging\n\n# Run specific test file\npytest tests/test_logging.py\n```\n\n### Code Quality\n\n```bash\n# Linting and formatting\nruff check .\nruff format .\n\n# Type checking\nmypy src/\n```\n\n## \ud83d\udccb Requirements\n\n- Python 3.8+\n- No runtime dependencies\n\n## \ud83d\udcc4 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\nCopyright (c) 2025 The Wyrd One\n\n## \ud83e\udd1d Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.\n\nPlease make sure to update tests as appropriate and follow the existing code style.\n\nIf you have questions about the project, please contact: wyrdbound@proton.me\n\n## \ud83c\udfaf Use Cases\n\nGrimoire Logging is particularly well-suited for:\n\n- **\ud83c\udfb2 Game Development**: Flexible logging for game engines and RPG systems\n- **\ud83d\udcda Library Development**: Allow users to control logging behavior\n- **\ud83e\uddea Testing**: Easy injection of mock loggers for testing\n- **\ud83d\udd0c Plugin Systems**: Different logging implementations for different environments\n- **\ud83c\udf10 Web Applications**: Request-scoped logging and structured output\n- **\ud83d\udd27 Configuration Management**: Runtime logging configuration changes\n\n## \ud83c\udfd7\ufe0f Architecture\n\nThe library is built around several key components:\n\n- **LoggerProxy**: Thread-safe proxy that delegates to injected or fallback loggers\n- **Protocol Design**: Clean interface definition using Python protocols\n- **Thread Safety**: Re-entrant locks ensure safe concurrent access\n- **Minimal Dependencies**: Zero runtime dependencies for maximum compatibility\n\n## \ud83d\udcc8 Performance\n\n- **Memory Efficient**: Logger instances are cached and reused\n- **Thread Safe**: Designed for high-concurrency environments\n- **Low Overhead**: Minimal impact when using standard logging fallback\n- **Scalable**: Efficient delegation pattern supports many logger instances\n\n## \ud83d\udd0d Comparison with Other Solutions\n\n| Feature | Grimoire Logging | Python logging | loguru | structlog |\n| -------------------- | ---------------- | -------------- | ------ | --------- |\n| Dependency Injection | \u2705 | \u274c | \u274c | Partial |\n| Zero Dependencies | \u2705 | \u2705 | \u274c | \u274c |\n| Thread Safety | \u2705 | \u2705 | \u2705 | \u2705 |\n| Protocol Based | \u2705 | \u274c | \u274c | Partial |\n| Easy Testing | \u2705 | Partial | \u2705 | \u2705 |\n| Fallback Support | \u2705 | N/A | \u274c | \u274c |\n\nGrimoire Logging is designed specifically for scenarios where you need clean dependency injection and the ability to completely swap out logging implementations at runtime, making it ideal for libraries and applications that need maximum flexibility.\n",
"bugtrack_url": null,
"license": null,
"summary": "Flexible logging utilities with dependency injection support for use in GRIMOIRE",
"version": "0.1.0",
"project_urls": {
"Changelog": "https://github.com/wyrdbound/grimoire-logging/releases",
"Documentation": "https://github.com/wyrdbound/grimoire-logging#readme",
"Homepage": "https://github.com/wyrdbound/grimoire-logging",
"Issues": "https://github.com/wyrdbound/grimoire-logging/issues",
"Repository": "https://github.com/wyrdbound/grimoire-logging.git"
},
"split_keywords": [
"dependency-injection",
" grimoire",
" logging",
" rpg",
" tabletop",
" thread-safe"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "e3cf7f0415e2b8379a51677a7e15cbfd06152ddf9742c14b0842c3a0767f1391",
"md5": "6cf4645716b6a2ba54d1a60826552e9d",
"sha256": "829a36b4efdd077dba3b2d2814f1e825f26c395ede9a97acf457df222db568bf"
},
"downloads": -1,
"filename": "grimoire_logging-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "6cf4645716b6a2ba54d1a60826552e9d",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 8388,
"upload_time": "2025-09-11T10:14:31",
"upload_time_iso_8601": "2025-09-11T10:14:31.213977Z",
"url": "https://files.pythonhosted.org/packages/e3/cf/7f0415e2b8379a51677a7e15cbfd06152ddf9742c14b0842c3a0767f1391/grimoire_logging-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "290a09c6ec95b801f28b7f8eb735f8fa8076ebd5f8dbf9d6eca685cb45e6ea4f",
"md5": "5ed45c56553e132e93625c9e2677b6b8",
"sha256": "ac74c48bdcebf7910729e910aec106ae2628e6ddec85f42cc53194475c90360d"
},
"downloads": -1,
"filename": "grimoire_logging-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "5ed45c56553e132e93625c9e2677b6b8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 19780,
"upload_time": "2025-09-11T10:14:32",
"upload_time_iso_8601": "2025-09-11T10:14:32.553894Z",
"url": "https://files.pythonhosted.org/packages/29/0a/09c6ec95b801f28b7f8eb735f8fa8076ebd5f8dbf9d6eca685cb45e6ea4f/grimoire_logging-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-11 10:14:32",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "wyrdbound",
"github_project": "grimoire-logging",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "grimoire-logging"
}