رفتن به محتوا

fa-console

  • مخزن: https://github.com/Padandish/fa-console (بستهٔ PyPI با نام fa-console نسخهٔ 2.2.0) · کامیت خوانده‌شده: 007d75a (۲۰۲۶-۰۹-۰۹) · مجوز: MIT · زبان: Python ≥3.8، یک فایل واحد fa_console.py (۱٬۳۳۱ خط) · سکو: ویندوز (conhost کلاسیک + ترمینال VS Code)؛ در جاهای دیگر بی‌اثر است مگر به‌اجبار فعال شود
  • دسته: تبدیل خروجی (پوشش‌دهندهٔ درون‌فرایندی sys.stdout) + کتابخانه

اصلاح‌کننده‌ای برای برنامه‌های پایتون روی ویندوز که هنگام import اجرا می‌شود. دو کار انجام می‌دهد:

  • رمزگذاری: صفحهٔ کد (code page) کنسول را روی UTF-8 می‌گذارد، جریان‌های استاندارد را دوباره پیکربندی می‌کند، PYTHONUTF8/PYTHONIOENCODING را تثبیت می‌کند و یک قلم TrueType برای کنسول تنظیم می‌کند.
  • نمایش: روی sys.stdout/sys.stderr یک VisualStream نصب می‌کند که هر نوشتن را با شکل‌دهی (shaping) مبتنی بر فرم‌های نمایشی (presentation forms) به ترتیب دیداری (visual order) تبدیل می‌کند.

همچنین یک ویرایشگر خط با پژواک زنده (fa_input) و یک یکسان‌ساز نویسه‌های هم‌شکل فارسی/عربی (fa_normalize) همراه دارد.

چگونه کار می‌کند (تأییدشده در کد)

Section titled “چگونه کار می‌کند (تأییدشده در کد)”
  • راه‌اندازی خودکار هنگام import: setup_console() هنگام import ماژول اجرا می‌شود (fa_console.py:1305-1308). مقدار os.environ["PYTHONIOENCODING"]="utf-8" و PYTHONUTF8="1" را می‌نویسد (:1087-1088). روی ویندوز SetConsoleOutputCP/SetConsoleCP(65001) (:432-459) و SetCurrentConsoleFontEx("Courier New") (:462-527، _DEFAULT_FONT در :235) را فرا می‌خواند. سپس روی هر سه جریان stream.reconfigure(encoding="utf-8", errors="replace") را صدا می‌زند (:549-583).
  • چه زمانی تبدیل دیداری روشن است (:1110-1127): FA_CONSOLE_NO_VISUAL / FA_CONSOLE_FORCE_VISUAL تصمیم را تغییر می‌دهند. در غیر این صورت فقط روی ویندوز، وقتی stdout.isatty() برقرار است و ترمینال «مدرن» نیست، روشن می‌شود. _modern_terminal() یعنی WT_SESSION تنظیم شده باشد، یا هر TERM_PROGRAM به‌جز vscode (:586-602). لوله‌ها و فایل‌ها منطقی می‌مانند (قرارداد :101-104، :1121).
  • دو پیشانه (backend) (_VisualTransformer، :633-851):
    • اختیاری: arabic_reshaper.ArabicReshaper({"language":"Farsi"}) + bidi.algorithm.get_display (:658-679، به‌صورت extras در pyproject.toml). این UAX #9 واقعی از طریق python-bidi است، اما همچنان ترتیب دیداری و فرم‌های نمایشی تولید می‌کند.
    • درونی (بدون وابستگی): shape() (:710-776) حروف را از طریق _DUAL_FORMS / _RIGHT_FORMS / _LAM_ALEF به فرم‌های نمایشی U+FB50–FDFF / U+FE70–FEFF نگاشت می‌کند (:258-322). حروف فارسی پ چ ژ ک گ ی ۀ، هر دو صورت ي/ی و ك/ک، و چند حرف اردو را پوشش می‌دهد. نیم‌فاصله (ZWNJ) اتصال را می‌شکند و از خروجی حذف می‌شود (:728-730). نشانه‌های شفاف فقط U+064B–065F و U+0670 هستند (:334). باگ لام‌الف: پیش‌نگری از روی اعراب رد می‌شود (:750-751)، اما کد سپس i += 2 انجام می‌دهد (:764)، پس در «لَا» الف و فتحه به‌اشتباه حذف می‌شوند.
    • جابه‌جایی درونی (reorder_line، :780-821): UAX #9 نیست. اگر خطی هر حرف راست‌به‌چپی داشته باشد، کل خط وارونه می‌شود. فقط بیشینه‌بخش‌هایی از نویسه‌های غیر راست‌به‌چپ isalnum، ارقام فارسی/عربی، ٫/٬ و ./, میان ارقام سرپا نگه داشته می‌شوند. شش کروشهٔ ASCII قرینه می‌شوند. جهت پاراگراف تشخیص داده نمی‌شود: یک جملهٔ انگلیسی با یک واژهٔ فارسی کاملاً وارونه می‌شود. عبارت‌های لاتین چندواژه‌ای ترتیب واژه‌هایشان برعکس می‌شود، چون فاصله یک توکن LTR به حساب نمی‌آید.
  • دانه‌بندی: VisualStream.write هر فراخوانی write() را جداگانه تبدیل می‌کند (:901-903) و آن را روی \n می‌شکند (:844-851). print(a, b) برای مقدارها، جداکننده‌ها و پایان، نوشتن‌های جداگانه انجام می‌دهد، پس یک خط دیداری ممکن است به‌صورت چند تکهٔ جدا جابه‌جا شود.
  • ANSI/CSI: اصلاً مدیریت نمی‌شود (در کل ماژول هیچ \x1bی نیست). در جابه‌جایی درونی، ESC (<0x20) یک مرز است، اما [، ارقام و حرف پایانی نویسه‌های معمولی‌اند. بنابراین \x1b[31m در یک خط راست‌به‌چپ به‌صورت 31m] و پس از آن ESC بیرون می‌آید: خراب، و [ هم قرینه شده است. get_display در python-bidi هم به همان اندازه از ANSI بی‌خبر است. مستندات به کاربران می‌گویند colorama را پس از این ماژول import کنند (:123-125، README.md:204).
  • ورودی: _ConhostLineEditor (:965-1052) کلیدها را با msvcrt.getwch() می‌خواند، یک بافر منطقی (logical buffer) نگه می‌دارد و با هر کلید \r + visual(prompt+buffer) را دوباره می‌کشد. کلیدهای جهت‌نما، کلیدهای F و Tab بلعیده می‌شوند (مکان‌نما جابه‌جا نمی‌شود)؛ برای پرکردن فاصله از len() به‌عنوان پهنا استفاده می‌شود (:1012). متن منطقی را برمی‌گرداند، و در صورت انتخاب آن را با fa_normalize یکسان‌سازی می‌کند (:1154-1196).
  • یکسان‌سازی (normalization): fa_normalize ي→ی و ك→ک را یکی می‌کند و می‌تواند ارقام ASCII، عربی-هندی و فارسی را یکسان کند؛ این کار با str.translate و بدون تغییر طول انجام می‌شود (:362-401، :1199-1245).
  • آزمون‌ها: tests/test_fa_console.py لیگاتورها، نیم‌فاصله، جداکننده‌های رقم، قرینه‌سازی، دروازه‌گذاری با متغیرهای محیطی و یکسان‌سازی را پوشش می‌دهد. هیچ آزمونی برای دنباله‌های گریز یا پاراگراف‌های دوجهته (mixed-direction) وجود ندارد.
لایه وضعیت شاهد
۱ رمزگذاری و یکسان‌سازی ✅ صفحهٔ کد UTF-8 + پیکربندی دوبارهٔ جریان‌ها + تثبیت متغیرهای محیطی (:432-459, 549-583, 1087)؛ یکسان‌سازی ي/ك←ی/ک و ارقام (:362-401, 1199-1245). NFC/NFKC و یکسان‌سازی نیم‌فاصله ندارد
۲ ورودی ◐ ویرایشگر خط با بافر منطقی و پژواک دیداری زنده (:965-1052)؛ یکسان‌سازی گونه‌های چیدمان صفحه‌کلید؛ جابه‌جایی مکان‌نما و آگاهی از IME ندارد
۳ پهنا ❌ len() به‌عنوان تعداد ستون‌ها به کار رفته (:1012-1016)؛ حذف نیم‌فاصله از نمایش تعداد خانه‌ها را تغییر می‌دهد
۴ شکل‌دهی ◐ جایگزینی با فرم‌های نمایشی (جدول‌های درونی یا arabic-reshaper)
۵ دوجهتی ◐ پیشانهٔ python-bidi = UAX #9؛ پیشانهٔ درونی = روش حدسی وارونه‌سازی کل خط (:780-821)
۶ مکان‌نما، انتخاب و کپی ◐ داده‌های برنامه منطقی می‌مانند (خوب)، اما صفحهٔ کنسول فرم‌های نمایشی را با ترتیب دیداری نگه می‌دارد، پس کپی از پنجرهٔ کنسول خراب است
۷ قلم‌ها ◐ قلم conhost را برای پوشش گلیف‌های عربی به‌زور روی Courier New می‌گذارد (:462-527)
۸ مالتی‌پلکسرها ❌ در نظر گرفته نشده
۹ برنامه‌ها و چارچوب‌های TUI ❌ فقط print() خطی؛ ANSI را خراب می‌کند؛ از صفحهٔ جایگزین (alternate screen) و خروجی مکان‌نما-محور آگاه نیست
  • «Windows Terminal عربی را به‌صورت بومی شکل‌دهی و جابه‌جا می‌کند» (fa_console.py:111-113، README.md:185, 196). این ادعایی بیرونی است و کد فقط با دیدن WT_SESSION کنار می‌کشد. آزمون‌های خود ما باید رفتار دوجهتی WT را تأیید کنند، نه اینکه آن را مسلم بگیرند.
  • اینکه هر TERM_PROGRAM غیر از vscode (mintty و غیره) را قادر بداند که «عربی را به‌صورت بومی و قابل‌اعتماد شکل‌دهی و جابه‌جا کند» (:586-602) یک فرض است، نه چیزی که تشخیص داده شود.
  • «جابه‌جایی دوجهتی» برای موتور درونی (README.md:22, 46) یک روش حدسی وارونه‌سازی است، نه الگوریتم دوجهتی. فقط پیشانهٔ اختیاری «UAX #9 کامل» است (README.md:48-49).
  • سرآیند docstring نسخه را 2.1.0 می‌گوید (:10)؛ __version__ برابر 2.2.0 است (:158).

آنچه می‌آموزیم یا برمی‌داریم

Section titled “آنچه می‌آموزیم یا برمی‌داریم”
  • لایهٔ ۱ روی ویندوز اینجا به‌شکلی تمیز حل شده است: SetConsoleCP/OutputCP(65001)، reconfigure(encoding="utf-8")، PYTHONUTF8، و قراردادی که در صورت شکست، کار را متوقف نمی‌کند (fail-open) همراه با یک ConsoleSetupReport ساختاریافته و عیب‌یابی از طریق get_console_info(). مرجع خوبی برای بخش ویندوز در راهنمای ما است.
  • سیاست یکسان‌سازی اصولی است: فقط نویسه‌های واقعاً هم‌شکل (ي/ی، ك/ک) یکی شوند و هرگز ة/ه یکی نشود؛ یکسان‌سازی ارقام اختیاری باشد؛ طول ثابت بماند. ارزش دارد در توصیه‌های لایهٔ ۱ ما برداشته شود.
  • «ناوردای دادهٔ منطقی» (:99-104) قرارداد درستی است: مقادیر برنامه منطقی می‌مانند، تبدیل فقط هنگام نمایش انجام می‌شود، و وقتی خروجی TTY نیست دست‌نخورده می‌ماند. مرزبندی درستی است، هرچند نیمهٔ نمایشی آن از تکنیک‌هایی استفاده می‌کند که ما رد می‌کنیم.
  • راه‌های گریز مبتنی بر متغیر محیطی (FA_CONSOLE_FORCE_VISUAL / FA_CONSOLE_NO_VISUAL) الگویی ساده و مؤثر برای مذاکرهٔ «چه کسی مسئول دوجهتی است» هستند، تا زمانی که ترمینال‌ها توانایی خود را اعلام کنند.

محدودیت‌ها و الگوهای نادرست

Section titled “محدودیت‌ها و الگوهای نادرست”
  • جایگزینی با فرم‌های نمایشی و ارسال متن با ترتیب دیداری به ترمینال. اصول ما هر دو را رد می‌کند؛ کپی و جست‌وجو در کنسول متن وارونه و ازپیش‌شکل‌داده‌شده برمی‌گرداند.
  • «دوجهتی» درونی، وارونه‌سازی خط با جزیره‌هایی از توکن‌های LTR است. جهت پاراگراف برای خط‌هایی که غالباً LTR هستند نادرست است، و عبارت‌های لاتین ترتیب واژه‌هایشان برعکس می‌شود.
  • دنباله‌های گریز ANSI (ANSI escape sequences) را در خط‌های راست‌به‌چپ خراب می‌کند، پس با خروجی رنگی CLI و برنامه‌های TUI ناسازگار است.
  • تبدیل به‌ازای هر write() یعنی مرزهای جابه‌جایی به نحوهٔ تکه‌تکه‌کردن خروجی توسط فراخواننده بستگی دارد.
  • اثرات جانبی سراسری هنگام import: sys.stdout/sys.stderr و os.environ را تغییر می‌دهد و قلم کنسول کاربر را عوض می‌کند.
  • تشخیص توانایی با بو کشیدن متغیرهای محیطی (WT_SESSION، TERM_PROGRAM) به‌جای پرس‌وجو از خود ترمینال.

یک پروژهٔ مهدی بانشی · mbaneshi.ir