Skip to main content

mazini

PyPI CI

Arabic verb conjugator for Python. Give it a root and a pattern (وزن) and it returns every form of the verb: the past, the three non-past moods, the imperative, active and passive, in all thirteen persons, plus the participles and the مصدر — 119 slots, fully vocalised, with the attested alternative spellings where Arabic has them.

It is a pure-Python port of Arabic Wiktionary's conjugation engine, وحدة:ar-verb (itself descended from Module:ar-verb on English Wiktionary), and it is verified two ways:

  • Against the book. Every one of the 15,549 printed forms of the 462 paradigms in أنطوان الدحداح, معجم تصريف الأفعال العربية — the reference work on the subject, transcribed and published as the ar-conjugation dataset — is among the forms the engine generates.

  • Against the original. For 7,851 inputs (every root × pattern combination of a 154-root grid, plain, passive and assimilated, plus every book verb) the output is byte-identical to the Lua module's: 741,188 forms, footnotes and metadata fields, and the same 201 rejected inputs.

  • Pure Python, zero dependencies, no data files. Python 3.11+. Typed.

Named after أبو عثمان المازني (Abū ʿUthmān al-Māzinī, d. 863), whose كتاب التصريف is the first standalone treatise on Arabic morphology.

A JavaScript/TypeScript port with the same API and the same tests, and a live demo: mazini-js — forzagreen.github.io/mazini-js.

Install

pip install mazini

Usage

from mazini import conjugate

v = conjugate("كتب", "فعَل يفعُل")
v.slots["past_3ms"][0].form  # "كَتَبَ"
v.slots["ind_3ms"][0].form  # "يَكْتُبُ"
v.slots["imp_2fs"][0].form  # "اُكْتُبِي"
v.slots["ap"][0].form  # "كَاتِب"
v.verb_type  # "فعل ثلاثي مُجرَّد صحيح سالم"

# A pattern can be given by its Arabic name or its form code; both take the same options.
conjugate("قول", "أفعل").slots["past_3ms"][0].form  # "أَقَالَ"
conjugate("علم", "V").slots["vn"][0].form  # "تَعَلُّم"
conjugate("كتب", "I", vowels=("a", "u"))  # same as "فعَل يفعُل"

# Two switches, the same ones {{تصريف}} takes on the wiki:
conjugate("أخذ", "افتعل", reduced=True).slots["past_3ms"][0].form  # "اِتَّخَذَ" (assimilated affix)
conjugate("علم", "فعِل يفعَل", passive=True)  # a full personal passive for a verb the engine would default to impersonal

The result's slots maps a slot name (past_3ms, ind_pass_2fp, juss_1p, imp_2fp, ap, pp, vn, …) to the list of forms for that slot, each Form(form, footnotes); a slot the verb lacks is absent. Alongside it: lemma, verb_form, wazn, radicals, weakness, passive (the passive type), irregular, morph_patterns, has_active / has_passive, uncertain_slots (slots shown as "?", i.e. a form-I مصدر or participle the root alone cannot predict), verb_type and classification.

Roots may be written كتب, ك ت ب or ك_ت_ب; hamza seats fold to ء and ى to ي. Bad input raises MaziniError, whose .code is stable (bad_root, unknown_wazn, reduced_not_applicable, …).

from mazini import WAZNS, SLOTS, classify_triliteral_verb, loose

WAZNS  # the 22 patterns: name, form code, vowels, مجرد/مزيد class
classify_triliteral_verb("ق", "و", "ل")  # "معتل أجوف واوي"
loose("فَعَلَا") == loose("فَعَلا")  # the comparison the book tests use

Command line:

mazini كتب "فعَل يفعُل"            # the table
mazini أخذ افتعل --reduced --json  # everything, as JSON
mazini --list-wazn

What it does not do (yet)

It conjugates from a root and a pattern, which is what every caller on Arabic Wiktionary does. The Lua module can also take a written, vocalised verb and infer its root (infer_radicals), accept per-slot overrides, and render the wikitext table; none of that is ported. The subjunctive, jussive, participles and مصدر are generated but are outside the book's tables, so they are verified only against the Lua module, not against print.

Development

uv sync
uv run pytest -q          # hamza, book, golden, loose, api
uv run ruff check
python3 tools/gen_golden.py   # regenerate tests/fixtures/ from ../ar-wiktionary-modules (needs luajit)

tests/fixtures/ is generated upstream by ar-wiktionary-modules' tools/port_fixtures.py: golden.jsonl.gz (the module's output for 7,851 inputs), hamza.jsonl.gz (every hamza-seating call it made), loose.jsonl.gz (the normaliser's parity set), book/ (the 462 paradigms as CSV) and MANIFEST.json (the upstream commit). The source is ported file for file from mazini-js, which was ported from the Lua; the three keep the same function names and the Lua line references.

Licence

© 2026 Wael Tellat. Released under CC BY 4.0; see LICENSE.

Metadata

Release files for mazini 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mazini 0.1.0
File Size Uploaded
mazini-0.1.0.tar.gz 2.3 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for mazini 0.1.0
File Interpreter ABI Platform
mazini-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 2.4 MB

Release files / mazini-0.1.0.tar.gz

Download URL mazini-0.1.0.tar.gz
Size 2.3 MB
Tags Source
SHA-256 checksum
How to use checksums
422a773e3db485bd142977ce2514099fac84579c9ecb43839d8ff58bc6886653
BLAKE2b-256 checksum
How to use checksums
389a291ae84ad939ccd082322ddd7dee23123239ad1677abffe38377f8f44ba0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / mazini-0.1.0-py3-none-any.whl

Download URL mazini-0.1.0-py3-none-any.whl
Size 49.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
083475bdd7d393ba5bb1bd6095f6bab8466bed4de9efbefcbfafcc0734e1c8ff
BLAKE2b-256 checksum
How to use checksums
2b0ff2108bea2aa7a990f8ce63649538d48a52de59ec8e23c73f58c89e4ae8e2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page