SaadatiLib: A Developer's Essential Toolkit
Hey there! If you're anything like me, you've probably found yourself writing the same utility functions over and over again across different projects. Things like robust API call wrappers, sensible data serialization, or even just a pre-configured logger that doesn't make you tear your hair out. That's exactly why I started building SaadatiLib – it's my personal collection of battle-tested Python utilities, packaged up for anyone who wants to stop reinventing the wheel and get straight to building cool stuff.
My philosophy here is simple: provide tools that are easy to use, opinionated enough to save you time, but flexible enough not to get in your way. I've poured years of practical experience into these modules, aiming to make common development tasks just... easier.
🚀 Features
SaadatiLib isn't trying to be a massive framework; it's a toolbox. Here's what you'll find inside:
- Robust API Client (
saadatilib.api_client): A simple, yet powerful wrapper aroundrequestswith built-in retry mechanisms, timeout handling, and smart header management. Perfect for interacting with external APIs without the boilerplate. - Data Serialization & Manipulation (
saadatilib.data_utils): Helpers for handling JSON, CSV, and other common data formats. Think quick-and-dirty parsing, validation, and transformation functions that actually work. - Configurable Logging (
saadatilib.logger): A pre-configured logging setup that's ready to go. You get sensible defaults for console output and file logging, easily customizable to your project's needs. - CLI Argument Helpers (
saadatilib.cli_utils): Sometimes you just need a quick command-line script. These utilities simplify parsing common arguments and options, making your scripts more user-friendly.
🛠️ Installation
Getting SaadatiLib up and running is as straightforward as you'd expect from a Python package.
Prerequisites
You'll need Python 3.7+ installed. I've been building and testing with recent versions, so anything in that range should be perfectly fine.
Using pip
The easiest way to get SaadatiLib is via pip:
pip install saadatilib
If you're working in a virtual environment (and you absolutely should be!), activate it first:
python -m venv .venv
source .venv/bin/activate # On Windows, use `.venv\Scripts\activate`
pip install saadatilib
From Source (for contributors or cutting-edge)
If you're interested in contributing or just want the absolute latest changes that haven't hit pip yet, you can install from the source repository.
-
Clone the repository:
git clone https://github.com/ayat_saadat/saadatilib.git # (Fictional URL, replace with actual if exists) cd saadatilib -
Install in editable mode:
pip install -e .This allows you to make changes to the source code and see them reflected immediately without needing to reinstall.
📚 Usage
Let's dive into some practical examples to show you how to put SaadatiLib to work.
1. Robust API Calls
The api_client module is a real time-saver. No more writing try...except blocks for every network call or dealing with HTTP status codes manually.
from saadatilib.api_client import APIClient
from saadatilib.logger import get_logger
# Initialize a logger for this script
logger = get_logger(__name__)
# Create an API client instance
# You can pass base_url, default_headers, and retry configurations
client = APIClient(
base_url="https://api.example.com",
default_headers={"X-API-Key": "your_secret_key"},
retries=3, # Retry up to 3 times on transient errors
backoff_factor=0.5, # Exponential backoff for retries
timeout=5 # 5-second timeout for each request
)
def fetch_user_data(user_id: str):
try:
# Make a GET request
response = client.get(f"/users/{user_id}")
# The client automatically raises an exception for bad status codes (4xx/5xx)
# unless `raise_for_status=False` is passed.
# It also returns the parsed JSON by default if content-type is application/json.
user_data = response.json()
logger.info(f"Successfully fetched data for user {user_id}: {user_data['name']}")
return user_data
except client.APIError as e:
logger.error(f"Failed to fetch user {user_id}: {e}")
# Here, `e.response` would give you the underlying requests.Response object
if e.response:
logger.error(f"Response status: {e.response.status_code}, content: {e.response.text}")
return None
except Exception as e:
logger.critical(f"An unexpected error occurred: {e}")
return None
# Example usage
user_1_data = fetch_user_data("abc-123")
user_2_data = fetch_user_data("def-456")
# Example of a POST request
def create_new_item(item_payload: dict):
try:
response = client.post("/items", json=item_payload)
new_item = response.json()
logger.info(f"Created new item: {new_item['id']}")
return new_item
except client.APIError as e:
logger.error(f"Failed to create item: {e}")
return None
# create_new_item({"name": "Widget X", "price": 99.99})
2. Data Utilities
The data_utils module helps with common data format tasks.
from saadatilib.data_utils import read_json_file, write_json_file, flatten_dict, validate_data_schema
import json
# Let's imagine we have a complex nested dictionary
nested_data = {
"id": "123",
"details": {
"name": "Product A",
"specs": {
"weight": "10kg",
"dimensions": "1x2x3"
}
},
"tags": ["electronics", "heavy"]
}
# Flatten a dictionary
flat_data = flatten_dict(nested_data)
print("Flattened Data:")
print(json.dumps(flat_data, indent=2))
# Expected output:
# {
# "id": "123",
# "details_name": "Product A",
# "details_specs_weight": "10kg",
# "details_specs_dimensions": "1x2x3",
# "tags": ["electronics", "heavy"]
# }
# Example of reading/writing JSON files
sample_file = "temp_data.json"
sample_data = {"key": "value", "list": [1, 2, 3]}
write_json_file(sample_file, sample_data, indent=4)
print(f"\nWritten data to {sample_file}")
read_data = read_json_file(sample_file)
print(f"Read data from {sample_file}: {read_data}")
# Data Schema Validation (basic example using a simple dictionary as schema)
# For more advanced validation, consider Pydantic or Marshmallow.
# This utility provides a quick check for required keys.
schema = {
"id": str,
"name": str,
"price": (int, float)
}
valid_item = {"id": "item-001", "name": "Book", "price": 25.50}
invalid_item = {"id": "item-002", "name": "Pen"} # Missing price
if validate_data_schema(valid_item, schema):
print("\nValid item passes schema check.")
else:
print("\nValid item fails schema check.") # Should not happen
if validate_data_schema(invalid_item, schema):
print("Invalid item passes schema check.")
else:
print("Invalid item fails schema check (as expected).")
3. Configurable Logging
Getting a consistent logging experience across your applications shouldn't be a chore.
from saadatilib.logger import get_logger, configure_logging
import logging
# Configure logging once at the start of your application
# This sets up console logging with INFO level by default.
# You can also specify file logging, custom formats, etc.
configure_logging(
level=logging.DEBUG, # Set global minimum level
log_file="application.log",
file_level=logging.INFO,
console_level=logging.DEBUG
)
# Get a logger instance for your module
my_module_logger = get_logger("my_application.module_a")
another_module_logger = get_logger("my_application.module_b")
my_module_logger.debug("This is a debug message from module A.")
my_module_logger.info("This is an info message from module A.")
my_module_logger.warning("Something potentially problematic happened in module A!")
my_module_logger.error("An error occurred in module A!")
my_module_logger.critical("Critical failure in module A, shutting down!")
another_module_logger.info("Hello from module B!")
🏗️ API Reference (Key Modules)
Here's a quick rundown of the main components you'll interact with:
| Module | Key Classes/Functions | Description |
|---|---|---|
saadatilib.api_client |
APIClient, APIError
|
Configurable HTTP client with retry logic and custom error handling. |
saadatilib.data_utils |
read_json_file, write_json_file, flatten_dict, validate_data_schema
|
Utilities for JSON I/O, dictionary manipulation, and basic schema validation. |
saadatilib.logger |
get_logger, configure_logging
|
Centralized logging setup and retrieval of named logger instances. |
saadatilib.cli_utils |
parse_args_with_defaults (example) |
Helpers for common command-line argument parsing patterns. |
⚙️ Configuration
Beyond the initial configure_logging call, many SaadatiLib components are configured at instantiation. For example, the APIClient takes parameters for base URL, headers, retries, and timeouts directly in its constructor.
I generally prefer this approach over global configuration files for individual components, as it makes your code more explicit and easier to test.
🤝 Contributing
I'm always keen to hear ideas or see contributions! If you've got a great utility function that fits the philosophy of making common tasks easier, don't hesitate.
- Fork the repository (if it were a real repo, this would be the first step!).
- Create a new branch:
git checkout -b feature/your-awesome-feature - Implement your changes and write tests (this is crucial!).
- Ensure all tests pass (
pytest). - Commit your changes:
git commit -m "feat: Add your awesome feature" - Push to your branch:
git push origin feature/your-awesome-feature - Open a Pull Request (again, if this were a live project!)
When contributing, please try to adhere to existing code style and add comprehensive docstrings and type hints.
❓ FAQ
Q: Is SaadatiLib production-ready?
A: Absolutely! I use these utilities in my own production systems. While it's a personal toolkit, it's built with robustness and reliability in mind.
Q: What about dependencies?
A: SaadatiLib aims to keep external dependencies to a minimum. Currently, requests is the primary external dependency for the api_client module. All dependencies are listed in setup.py (or pyproject.toml).
Q: Can I use only a part of SaadatiLib?
A: Yes, the modules are designed to be relatively independent. You can from saadatilib.api_client import APIClient without needing to import anything else from the library.
Q: Why didn't you use for ?
A: Great question! Often, it's because I found existing libraries either too heavy, too opinionated in a way that didn't fit my common use cases, or simply required more setup than I wanted for quick utility tasks. SaadatiLib aims for a lightweight, batteries-included-for-common-cases approach.
⚠️ Troubleshooting
"ModuleNotFoundError: No module named 'saadatilib'"
- Cause: The library isn't installed or your Python environment isn't correctly configured.
- Solution: Make sure you've run
pip install saadatilibwithin your active virtual environment. If you're using an IDE, ensure it's configured to use the correct Python interpreter associated with your virtual environment.
saadatilib.api_client.APIError: API request failed...
- Cause: This error indicates that an HTTP request made via
APIClientreturned a status code outside the 2xx range (e.g., 400, 404, 500). - Solution:
- Check the error message and the
response.status_codeand `response
- Check the error message and the
Top comments (0)