afrim-py provides Python bindings for the powerful afrim input method engine, enabling developers to build sophisticated input method applications in Python. This project brings the capabilities of the Rust-based afrim engine to the Python ecosystem through PyO3 bindings.
- Preprocessor - Advanced key sequence processing and input transformation
- Translator - Dictionary-based text translation with multiple candidates
- TOML Support - Easy configuration through TOML files
- Unicode Support - Full support for international characters
- Rhai Scripting - Dynamic translation scripts (when
rhaifeature is enabled) - String Similarity - Fuzzy matching with
strsimfeature
afrim-py is available on pypi.
pip install afrim-pyfrom afrim_py import Preprocessor, Translator, Config
# Configure the preprocessor with key mappings
preprocessor_data = {
"a1": "Γ ",
"e1": "Γ©",
"u1": "ΓΉ",
"hello": "hi"
}
# Configure the translator with dictionary
translator_dict = {
"hello": ["hi", "hey", "greetings"],
"world": ["earth", "globe", "planet"],
"python": ["snake", "programming language"]
}
# Create instances
preprocessor = Preprocessor(preprocessor_data, buffer_size=64)
translator = Translator(translator_dict, auto_commit=True)
# Process keyboard events
changed = preprocessor.process("h", "keydown")
changed = preprocessor.process("e", "keydown")
changed = preprocessor.process("l", "keydown")
changed = preprocessor.process("l", "keydown")
changed = preprocessor.process("o", "keydown")
# Get the processed input
current_input = preprocessor.get_input() # "hello"
# Translate the input
translations = translator.translate(current_input)
print(translations)
# [{'texts': ['hi', 'hey', 'greetings'], 'code': 'hello', 'remaining_code': '', 'can_commit': True}]
# Process commands from the queue
while True:
command = preprocessor.pop_queue()
if command == "NOP":
break
print(f"Command: {command}")from afrim_py import Config
import json
# Configuration file `config.toml`
'''
[core]
buffer_size = 64
auto_capitalize = false
auto_commit = false
page_size = 10
[data]
a1 = "Γ "
e2 = "Γ©"
[translators]
datetime = { path = "./scripts/datetime.toml" }
[translation]
hi = 'hello'
'''
config = Config('config.toml')
# Use the configuration
preprocessor_data = config.extract_data()
preprocessor = Preprocessor(preprocessor_data, 64)
translator_dict = config.extract_translation()
translator = Translator(translator_dict, True)import asyncio
from afrim_py import Preprocessor, Translator, Config
class InputMethodEngine:
def __init__(self, config_file: str):
config = Config(config_file)
self.preprocessor = Preprocessor(config.extract_data(), 64)
self.translator = Translator(config.extract_translation(), True)
self.running = False
async def process_commands(self):
"""Process commands from the preprocessor queue"""
while self.running:
command = self.preprocessor.pop_queue()
if command == "NOP":
await asyncio.sleep(0.01) # Small delay
continue
# Handle different command types
if isinstance(command, dict):
if "Insert" in command:
text = command["Insert"]["text"]
print(f"Insert: {text}")
elif "Delete" in command:
count = command["Delete"]["count"]
print(f"Delete: {count} characters")
else:
print(f"Command: {command}")
def handle_key_event(self, key, state="keydown"):
"""Handle keyboard events"""
changed = self.preprocessor.process(key, state)
if changed:
current_input = self.preprocessor.get_input()
if current_input:
translations = self.translator.translate(current_input)
return translations
return []
def commit_text(self, text):
"""Commit selected text"""
self.preprocessor.commit(text)
async def start(self):
"""Start the input method engine"""
self.running = True
await self.process_commands()
def stop(self):
"""Stop the input method engine"""
self.running = False
# Usage
async def main():
ime = InputMethodEngine(
preprocessor_data={"A": "α", "Aa": "α", "C": "α"},
translator_dict={"Atarah": ["αα£α«"], "Adiel": ["αα²αα"]}
)
# Simulate key events
translations = ime.handle_key_event("A")
translations = ime.handle_key_event("a")
translations = ime.handle_key_event("C")
print("Translations:", translations)
# Commit text
if translations:
ime.commit_text(translations[0]["texts"][0])
# Run the example
# asyncio.run(main())To simplify the development, we recommend to use uv.
Using maturin
# Clone the repository
git clone https://github.com/fodydev/afrim-py.git
cd afrim-py
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Development build
maturin develop
# Release build
maturin build --release
# Build wheel
maturin build --interpreter pythonUsing uv
# Clone the repository
git clone https://github.com/fodydev/afrim-py.git
cd afrim-py
# Prerelease build
uv build --prerelease
# Release build
uv buildThe project includes tests that represent a real user scenario:
# Run all tests
python -m pytest tests/ -vContributions 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.
- afrim-js - Web bindings that inspired this project
Licensed under the MIT LICENSE.