Skip to main content

Python binding of the afrim.

Project description

afrim-py

Python bindings for the afrim ime engine.

Python Rust License Changelog

Repository

Built with 🦀🐍 by @esubaalew

About

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.

🔋 Features Included

  • 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 rhai feature is enabled)
  • String Similarity - Fuzzy matching with strsim feature

Installation

afrim-py is available on pypi.

pip install afrim-py

Usage

Basic Example

from 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}")

Configuration

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)

Advanced Usage with Command Processing

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())

Development

Build requirements

  • Rust 1.70+

  • Cargo

  • Python 3.8+ and and maturin

  • uv (optional)

Build from source

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 python

Using uv

# Clone the repository
git clone https://github.com/fodydev/afrim-py.git
cd afrim-py

# Prerelease build
uv build --prerelease

# Release build
uv build

Testing

The project includes tests that represent a real user scenario:

# Run all tests
python -m pytest tests/ -v

Contributing

Contributions 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.

Acknowledgments

  • afrim-js - Web bindings that inspired this project

License

Licensed under the MIT LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

afrim_py-0.1.0.tar.gz (32.2 kB view details)

Uploaded Source

File details

Details for the file afrim_py-0.1.0.tar.gz.

File metadata

  • Download URL: afrim_py-0.1.0.tar.gz
  • Upload date:
  • Size: 32.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":null,"id":"forky","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for afrim_py-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e0adb85d661feb056a0fbf3fbd79a969c43bac8bd5fa2ca1bb1225173128e1cc
MD5 20023731b9721ea46b1ebaeb3071beb7
BLAKE2b-256 7dfa623368f23e08596e753b1c708096cff57c2aadc11de768b4d2a95dc11041

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page