A production-grade, highly performant, and pure Python software infrastructure for Persian text processing, validation, financial calculations, calendar conversions, graphical text reshaping, developer diagnostics, and sensitive data security.
ParsiKit (imported as parsikit) is a robust, zero-dependency Python library designed as a full-scale infrastructure for Persian language applications.
With the release of v3.3.0, ParsiKit evolves into a highly advanced developer utility, text-cleansing, and security-centric suite. Alongside its unified procedural API and rich Object-Oriented Domain Model, ParsiKit now introduces bومی (Persian-aware) formatting, visual alignment helpers, text diagnostics, smart classifiers, and sensitive data masking tools designed to make Persian software development reliable, clean, and secure.
- Thread-Safe Caching: Highly repetitive and CPU-bound operations (such as converting large digits to words or parsing database dates) are optimized with an internal thread-safe LRU-like cache.
- Global Configurations: Easily control default parameters like value-added tax rates, default currency units, or toggle caching on/off dynamically at runtime.
Never worry about managing generic ValueError or TypeError crashes. ParsiKit ships with a structured exception tree tailored to Iranian standards, making it perfect for Clean Architecture or DDD pipelines.
Wrap raw strings in rich domain models like PersianText, NationalCode, MobileNumber, FixedLine, BankCard, Sheba, or VehiclePlate. These models validate inputs upon instantiation, extract rich metadata (like province/city of issue, operators, or bank details), and serialize cleanly.
- Persian-Aware Visual Padding (
pformat&persian_fstring): Standard formatters misalign texts containing Zero-Width Non-Joiners (ZWNJs) or Arabic diacritics. ParsiKit calculates true visual string widths to output cleanly aligned tables and logs, supporting automatic Persian digit conversions. - Interactive Text Debugging: Inspect texts for missing ZWNJs, layout issues, or redundant spacings and get immediate corrective actions.
- SEO Romanization (
slugify): Transliterates mixed or Persian strings into SEO-friendly Finglish slugs, utilizing a built-in common word mapping dictionary. - Master Cleansing & Normalization (
clean_text/normalize_whitespace): Sanitize multi-line texts by eliminating hidden unicode characters, collapsing redundant whitespace, correcting ZWNJ boundaries, and resolving newline excesses. - Smart Data Detection (
detect): A unified classifier that parses strings to automatically identify if they represent an Iranian mobile number, national code, card number, Sheba code, email, URL, or IP address. - Sensitive Data Masking (
mask_*): Instantly scrub logs, API outputs, or admin panel displays by masking middle digits of mobiles, cards, emails, and national codes.
pip install parsikitRequires Python 3.10+
import parsikit
# Adjust VAT/Tax rate globally (Default: 0.10)
parsikit.config.default_tax_rate = 0.09
# Adjust global default currency (Default: "toman")
parsikit.config.default_currency = "rial"
# Toggle high-performance computation caching
parsikit.config.enable_cache = TrueAll specific exceptions inherit from parsikit.exceptions.ValidationError which inherits from ParsiKitError and Python's native ValueError:
import parsikit
try:
card = parsikit.BankCard("invalid-card-number")
except parsikit.InvalidCardNumberError as e:
print(f"Card processing failed: {e}")
except parsikit.ValidationError as e:
print(f"Generic ParsiKit validation error: {e}")You can declare ParsiKit models directly inside Pydantic schemas or FastAPI route handlers. ParsiKit natively handles data parsing, raises clean structured validation errors on failure, and serializes values cleanly to string:
from fastapi import FastAPI
from pydantic import BaseModel
import parsikit
app = FastAPI()
class UserRegisterSchema(BaseModel):
fullname: parsikit.PersianText
national_id: parsikit.NationalCode
phone: parsikit.MobileNumber
card: parsikit.BankCard
@app.post("/register")
def register_user(user: UserRegisterSchema):
# Inputs are already parsed, validated, and normalized!
print(user.fullname.standardize()) # Normalized chain
print(user.national_id.location) # {"province": "تهران", "city": "تهران مرکزی"}
print(user.phone.to_international()) # "+989123456789"
# Easily serialize everything to dictionary
return {
"status": "success",
"data": {
"national_id": user.national_id.to_dict(),
"card_bank": user.card.bank
}
}ParsiKit v3.3.0 ships with a highly optimized developer diagnostic suite for printing, inspecting, and managing logs containing Persian data.
Identify encoding errors, missing semi-spaces, or digit script issues instantly:
import parsikit
# Get detailed dictionary diagnostics
analysis = parsikit.inspect_text("ي كافيه ك کتاب ها ميباشد ۱۲۳")
print(analysis["has_arabic_chars"]) # True
print(analysis["has_zwnj_issues"]) # True
print(analysis["suggestions"])
# Output: [
# "Replace Arabic letters (ي, ك, ة) with standard Persian equivalents (ی, ک, ه).",
# "Add proper zero-width non-joiners (ZWNJs) around prefixes/suffixes.",
# ...
# ]
# Print a beautiful terminal-colored diagnostic report to stdout
parsikit.debug_text("ي كافيه ك کتاب ها ميباشد")Standard Python structures escape Persian characters inside containers. ParsiKit fixes this with native-repr representations and colored outputs:
import parsikit
data = {"کاربر": ["امیر", "رضا"], "وضعیت": True}
# Standard python repr: "{'\\u06a9\\u0627\\u0631\\u0628\\u0631': ['\\u0627\\u0645\\u06cc\\u0631', ...]}"
# ParsiKit clean representation:
print(parsikit.persian_repr(data))
# Output: {'کاربر': ['امیر', 'رضا'], 'وضعیت': True}
# Pretty print structured dicts/lists or ParsiKit models with ANSI colors
parsikit.pretty_print(data, title="User Information")Validate batches of raw inputs against any ParsiKit domain model and print/return a summary breakdown:
import parsikit
items = ["7730123452", "1111111111", "0010123451", "bad-code"]
report = parsikit.validate_batch(items, parsikit.NationalCode, silent=False)
# Prints a structured validation summary detailing valid/invalid elements and indexesConfigures Python's stream logger with clean formatting that prints unescaped Persian text:
import parsikit
logger = parsikit.setup_logging("DEBUG")
logger.info("ثبت نام کاربر با موفقیت انجام شد.")Standard string formatters align strings based on character length. However, zero-width characters (like ZWNJs) or diacritics occupy character indices while taking up zero visual width, resulting in crooked alignments. ParsiKit resolves this visually.
import parsikit
# "کتابها" contains a ZWNJ (character length is 8, but visually occupies 7 slots)
# Standard format '{:<10}' outputs a string with 2 trailing spaces (visual width of 9)
# pformat compensates for the ZWNJ, adding 3 trailing spaces to achieve a visual width of exactly 10 slots:
aligned = parsikit.pformat("{:<10} | تراز شد", "کتابها")
print(aligned) # "کتابها | تراز شد"Append :fa or :p to placeholders inside templates to automatically format numbers in Persian script:
import parsikit
# Auto convert numbers to Persian digits and apply standard spacing/grouping
print(parsikit.pformat("هزینه: {:>10,fa} ریال", 1500000))
# Output: "هزینه: ۱،۵۰۰،۰۰۰ ریال"Evaluates standard formatting templates while automatically extracting local/global variables from the caller's frame context:
import parsikit
name = "علی"
price = 12500
# Automatically parses 'name' and 'price' from the surrounding variables scope
formatted = parsikit.persian_fstring("کاربر: {name:<5} | قیمت: {price:fa} تومان")
print(formatted)
# Output: "کاربر: علی | قیمت: ۱۲۵۰۰ تومان"Easily convert mixed Persian scripts into clean components, URLs, and database-safe records.
Transliterates mixed or Persian strings into SEO-friendly, clean Finglish slugs:
import parsikit
# Auto maps popular Persian words (like "سلام" -> "salam", "دنیا" -> "donya", "آموزش" -> "amoozesh")
# and transliterates other characters alphabetically
print(parsikit.slugify("آموزش وردپرس به زبان ساده"))
# Output: "amoozesh-wordpress-beh-zaban-sadeh"
print(parsikit.slugify("سلام دنیا 💕"))
# Output: "salam-donya"Removes invisible non-printing unicode characters, collapses extra spaces, fixes ZWNJ boundaries, and converts Arabic characters and English digits:
import parsikit
dirty_text = "ي كافيه ك کتاب ها ميباشد ۱۱۲۳\n\n\n\nجدید "
print(parsikit.clean_text(dirty_text))
# Output:
# "ی کافیه ک کتابها میباشد ۱۱۲۳
#
# جدید"Normalizes whitespace structures, with an option to preserve single paragraph line breaks or flatten them into a single line:
import parsikit
raw_paragraphs = "خط اول\n\n\n\nخط دوم با فاصله\n\nخط سوم"
# Preserves paragraph spacing
print(parsikit.normalize_whitespace(raw_paragraphs, keep_paragraphs=True))
# Output:
# "خط اول
#
# خط دوم با فاصله
#
# خط سوم"
# Flattens all line breaks
print(parsikit.normalize_whitespace(raw_paragraphs, keep_paragraphs=False))
# Output: "خط اول خط دوم با فاصله خط سوم"Convert all numbers in a given raw block of text to Persian or English digits:
import parsikit
text = "شماره تماس ما: 09123456789"
print(parsikit.convert_numbers(text, to="persian"))
# Output: "شماره تماس ما: ۰۹۱۲۳۴۵۶۷۸۹"
print(parsikit.convert_numbers("کد امنیتی: ۱۲۵۰۰", to="english"))
# Output: "کد امنیتی: 12500"Implement smart validation pipelines and protect sensitive user logs or profiles from exposure.
Automatically parses any string input to categorize its semantic data type:
import parsikit
print(parsikit.detect("09123456789")) # "mobile_number"
print(parsikit.detect("7730123452")) # "national_code"
print(parsikit.detect("ali@example.com")) # "email"
print(parsikit.detect("192.168.1.1")) # "ip"
print(parsikit.detect("https://google.com")) # "url"
print(parsikit.detect("some general phrase")) # NonePerfect for admin panel fields, user profile previews, and secure database logging:
import parsikit
# Mask Mobile Numbers
print(parsikit.mask_mobile("09123456789"))
# Output: "0912***6789"
# Mask Bank Cards (preserves standard formatted chunks)
print(parsikit.mask_card("6037991122334455", mask_char="X"))
# Output: "6037-99XX-XXXX-4455"
# Mask National Codes (preserves formatted XXX-XXXXXX-X layout)
print(parsikit.mask_national_code("7730123452"))
# Output: "773-****45-2"
# Mask Email usernames proportionally
print(parsikit.mask_email("kamrani.exe@gmail.com"))
# Output: "ka******e@gmail.com"All standard tools from previous versions remain completely supported.
import parsikit
import datetime
# Gregorian to Jalali (Cached for performance)
jy, jm, jd = parsikit.gregorian_to_jalali(2026, 7, 5)
print(parsikit.format_jalali(jy, jm, jd, "YYYY-MM-DD"))
# Output: "1405-04-14"
# Check leap year (Precise astronomical cycle)
print(parsikit.is_jalali_leap(1403)) # True
# Relative Time Humanizer
posted_at = datetime.datetime.now() - datetime.timedelta(hours=3)
print(parsikit.humanize_relative_time(posted_at))
# Output: "۳ ساعت پیش"import parsikit
# Reshapes connected text for canvas engines with poor RTL support (OpenCV, Pygame, Pillow)
shaped = parsikit.reshape_for_graphics("عَلِیّ", reverse=False)
print(shaped) # "ﻋَﻠِﻲّ"import parsikit
from PySide6.QtWidgets import QLineEdit, QApplication
app = QApplication([])
card_input = QLineEdit()
# Auto-formats card digits reactive-style as "XXXX-XXXX-XXXX-XXXX" as the user types
parsikit.bind_persian_input(card_input, "card_number")
card_input.show()
app.exec()A comprehensive, bug-free unit test suite is included in the project root to guarantee full structural compliance:
python -m unittest test.py
# or
python test.pyAli Kamrani
- GitHub: @MRThugh
- Email: kamrani.exe@gmail.com
This project is licensed under the MIT License. Feel free to use, modify, and distribute it in your commercial or open-source projects.
ParsiKit — Making Persian software development cleaner and more professional. 🚀