3 पॉइंट द्वारा GN⁺ 2024-04-28 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Python 3.15 में UTF-8 मोड डिफ़ॉल्ट रूप से चालू रहेगा, जिससे फ़ाइलों, standard input/output और pipes की डिफ़ॉल्ट encoding UTF-8 होगी
  • UTF-8 पहले से source files, JSON·TOML·YAML, प्रमुख editors, web data, और Node.js·Go·Rust·Java आदि में व्यावहारिक मानक encoding के रूप में इस्तेमाल हो रहा है, इसलिए interoperability बढ़ेगी
  • मौजूदा डिफ़ॉल्ट encoding प्लेटफ़ॉर्म के अनुसार बदलती है, इसलिए अगर Unix डेवलपर encoding="utf-8" छोड़ देते हैं तो Windows जैसे वातावरण में mismatch bugs हो सकते हैं
  • ज़रूरत होने पर इसे PYTHONUTF8=0 या -X utf8=0 से बंद किया जा सकता है, और compatibility जाँच के लिए EncodingWarning, encoding="utf-8", encoding="locale", locale.getencoding() का उपयोग किया जाता है
  • डिफ़ॉल्ट encoding पर निर्भर प्रोग्राम, खासकर Windows पर, UnicodeError, mojibake, और silent data corruption का सामना कर सकते हैं, इसलिए पहले से जाँच ज़रूरी है

Python 3.15 में डिफ़ॉल्ट encoding में बदलाव

  • PEP 686 में PEP 540 के UTF-8 मोड को डिफ़ॉल्ट रूप से सक्षम करने का बदलाव प्रस्तावित है
  • जहाँ फ़ाइलों, stdio और pipes के लिए डिफ़ॉल्ट encoding की ज़रूरत होती है, वहाँ Python लगातार UTF-8 का उपयोग करेगा
  • यह Python 3.15 से डिफ़ॉल्ट रूप से सक्षम होगा, और उपयोगकर्ता इसे इन तरीकों से बंद कर सकते हैं
    • PYTHONUTF8=0
    • -X utf8=0

UTF-8 को डिफ़ॉल्ट बनाने की वजह

  • UTF-8 कई वातावरणों में मानक text encoding के रूप में स्थापित हो चुका है
    • Python source files की डिफ़ॉल्ट encoding UTF-8 है
    • JSON, TOML, YAML UTF-8 का उपयोग करते हैं
    • Visual Studio Code और Windows Notepad सहित अधिकांश text editors डिफ़ॉल्ट रूप से UTF-8 इस्तेमाल करते हैं
    • इंटरनेट की अधिकांश websites और text data UTF-8 का उपयोग करते हैं
    • Node.js, Go, Rust, Java सहित कई लोकप्रिय programming languages डिफ़ॉल्ट रूप से UTF-8 इस्तेमाल करती हैं
  • Python की डिफ़ॉल्ट encoding UTF-8 होने से दूसरे tools, languages और data formats के साथ interoperability बेहतर होगी
  • कई Unix वातावरणों के Python डेवलपर यह भूल जाते हैं कि डिफ़ॉल्ट encoding प्लेटफ़ॉर्म-निर्भर होती है, और JSON·TOML·Markdown·Python source files जैसे UTF-8 text पढ़ते समय encoding="utf-8" छोड़ देते हैं
  • प्लेटफ़ॉर्म के अनुसार डिफ़ॉल्ट encoding में अंतर ऐसे कोड के दूसरे वातावरण में टूटने का bug source बनता है

locale API और encoding="locale" में सुधार

  • UTF-8 मोड locale.getpreferredencoding(False) को प्रभावित करता है, इसलिए UTF-8 मोड से अलग locale encoding पाने के लिए एक API की ज़रूरत थी
  • locale.getencoding() इसी उद्देश्य से जोड़ा गया, और यह UTF-8 मोड को नज़रअंदाज़ करते हुए locale encoding लौटाता है
    • यह API Python 3.11 में जोड़ा गया था
  • अगर warn_default_encoding विकल्प दिया गया हो, तो locale.getpreferredencoding() भी open() की तरह PEP 597 का EncodingWarning उत्पन्न करता है
  • PEP 597 ने TextIOWrapper में encoding="locale" विकल्प जोड़ा ताकि locale encoding को स्पष्ट रूप से निर्दिष्ट किया जा सके
  • पहले UTF-8 मोड में encoding="locale" देने पर भी TextIOWrapper "UTF-8" का उपयोग करता था
    • यह PEP 597 के उद्देश्य के अनुरूप नहीं था
    • क्योंकि Python की डिफ़ॉल्ट text encoding बदलने पर UTF-8 मोड के डिफ़ॉल्ट बनने की स्थिति का अनुमान नहीं लगाया गया था
  • यह असंगति Python 3.11 में ठीक की गई, और अब UTF-8 मोड में भी encoding="locale" देने पर locale encoding का उपयोग होता है

backward compatibility और migration प्रक्रिया

  • अधिकांश Unix systems UTF-8 locale का उपयोग करते हैं, और Python locale के C या POSIX होने पर UTF-8 मोड सक्रिय करता है, इसलिए इस बदलाव का असर मुख्य रूप से Windows उपयोगकर्ताओं पर पड़ेगा
  • डिफ़ॉल्ट encoding पर निर्भर Python प्रोग्रामों में ये समस्याएँ आ सकती हैं
    • UnicodeError
    • mojibake
    • silent data corruption
  • backward compatibility समस्याएँ ठीक करने के लिए सुझाई गई प्रक्रिया यह है
    1. UTF-8 मोड बंद करें
    2. PEP 597 के EncodingWarning से उन जगहों की पहचान करें जहाँ UTF-8 मोड असर डालता है
      • जहाँ encoding विकल्प छोड़ा गया है, वहाँ encoding="utf-8" या encoding="locale" के उपयोग पर विचार करें
      • जहाँ locale.getpreferredencoding() का उपयोग है, वहाँ "utf-8" या locale.getencoding() के उपयोग पर विचार करें
    3. UTF-8 मोड में application को test करें

Ruby·Java के पूर्व उदाहरण और अस्वीकार किए गए विकल्प

  • Ruby ने Ruby 3.0, 2020 में Windows की डिफ़ॉल्ट external_encoding को UTF-8 में बदला
  • Java ने JDK 18, 2022 में डिफ़ॉल्ट text encoding को UTF-8 में बदला
  • Ruby और Java दोनों backward compatibility के लिए विकल्प देते हैं, लेकिन Python के EncodingWarning जैसी डिफ़ॉल्ट encoding उपयोग चेतावनी नहीं देते
  • डिफ़ॉल्ट encoding के उपयोग को पूरी तरह समाप्त करने का प्रस्ताव अस्वीकार किया गया
    • कई मामलों में डिफ़ॉल्ट encoding सिर्फ ASCII text पढ़ने-लिखने के लिए इस्तेमाल होती है
    • केवल Unix पर चलने वाले non-cross-platform applications में ऐसी चेतावनी उपयोगी नहीं होगी
    • हर जगह encoding अनिवार्य करने से उपयोगकर्ता पर बोझ बढ़ेगा, और बहुत सारे DeprecationWarning उपयोगकर्ताओं को चेतावनियाँ नज़रअंदाज़ करने की आदत डाल सकते हैं
    • PEP 387 backward compatibility तोड़ने वाले बदलावों में चेतावनी जोड़ने की अपेक्षा करता है, लेकिन अनिवार्य रूप से DeprecationWarning की माँग नहीं करता
  • subprocess मॉड्यूल के pipes की डिफ़ॉल्ट encoding के लिए PYTHONIOENCODING इस्तेमाल करने का विकल्प भी अस्वीकार किया गया
    • इस तरीके से UTF-8 मोड में भी subprocess.Popen(text=True) के लिए legacy encoding इस्तेमाल की जा सकती थी
    • लेकिन इससे “डिफ़ॉल्ट encoding” और जटिल हो जाती, और यह तरीका भी अपने आप में backward compatibility तोड़ने वाला बदलाव था
    • उपयोगकर्ता text=True को encoding="utf-8" या encoding="locale" में बदलने तक UTF-8 मोड बंद रख सकते हैं

उपयोगकर्ता शिक्षा के दृष्टिकोण से

  • नए उपयोगकर्ताओं को शुरुआती एक साल में text encoding सीखने की ज़रूरत कम होगी
  • encoding के बारे में तब सीखना होगा जब non-UTF-8 text files संभालनी हों
  • मौजूदा उपयोगकर्ताओं को backward compatibility प्रक्रिया के अनुसार प्रभावित हिस्सों की जाँच करनी चाहिए

1 टिप्पणियां

 
GN⁺ 2024-04-28
Hacker News की राय
  • डिफ़ॉल्ट text file encoding का platform के हिसाब से बदलना हमेशा झुंझलाहट भरा था, इसलिए यह बदलाव स्वागतयोग्य है
    अच्छा है कि उन्होंने filesystem encoding तक छेड़ने की कोशिश नहीं की। वह अलग समस्या है, और अपने आप में सिरदर्द है

    • Windows का system default code page सिर्फ platform पर नहीं, बल्कि system locale पर भी निर्भर करता है
      Windows ने लंबे समय तक TextOutA जैसे ANSI functions को UTF-8 code page इस्तेमाल करने के लिए आसानी से चुनने का तरीका नहीं दिया, यह बड़ी गलती थी। manifest file से यह संभव होना Windows 10 development के बीच के दौर में हुआ, जबकि ऐसी सुविधा NT4 या Windows 98 के समय ही आ जानी चाहिए थी
    • ऐतिहासिक रूप से यह समझ में आता था। ज़्यादातर software local-only था, और text files के भी local encoding में होने की उम्मीद की जाती थी
      यह सिर्फ platform पर नहीं, बल्कि user के preferred locale पर निर्भर करता था, और C standard library भी इसी तरह काम करती है। उदाहरण के लिए Unix/Linux में पश्चिमी यूरोपीय भाषाओं के लिए iso-8859-1 आम था, और euro आने के बाद symbol वाले iso-8859-15 पर जाना अक्सर होने लगा। UTF-8 ने बिना दिक्कत काम करना 2000s के आखिर के आसपास शुरू किया, और Debian ने Etch release में default को UTF-8 में बदल दिया
    • कुछ दिन पहले मैं line endings को implicitly बदलने की वजह से फँस गया था
      company laptop पर local tests सब ठीक थे, लेकिन Linux host पर deploy करने पर downstream application consume नहीं कर सका क्योंकि उसे CRLF चाहिए था। यह उन छोटी और मूर्खतापूर्ण बातों में से एक है जिन्हें कभी-कभी याद रखना पड़ता है। हालांकि यह भी वाजिब सवाल है कि नया लिखा गया software किसी खास line terminator की मांग क्यों करता है
    • Windows पर जब भी कोई code लिखना शुरू करता है, वह इस समस्या से कई बार टकराता है
  • अस्थिर system defaults पर निर्भर न रहना अच्छी बात है
    ऐसे values किसी समय मेरी धारणा से अलग लौटने लगते हैं। कुछ साल पहले Ubuntu और init.d scripts के साथ काम करते समय, Java launch करने वाली script root के रूप में चल रही थी, और Docker से पहले का दौर होने के कारण यह और भी ज्यादा था; वह ऐसे shell में चलती थी जो normal user के लिए सही UTF-8 defaults set नहीं करता था। नतीजतन OS defaults इस्तेमाल करने वाले Java के खराब API use का पता चला
    आजकल अधिकतर encodings explicitly set करने वाले variant APIs मौजूद हैं, और static code checkers भी गलत वाला इस्तेमाल करने पर warning देते हैं। लेकिन एक जगह भी छूट जाए तो content बिगड़ना शुरू हो जाता है। अब non-UTF-8 encoding का use अधिकतर cases में बहुत संभव है कि अनजाने में हो, और अगर जानबूझकर है तो OS की अजीब indirect setting पर निर्भर रहने के बजाय उसे explicitly बताना चाहिए। इसलिए यह अच्छा बदलाव है, और इससे टूटने वाले code में simple fix लगना ही बेहतर है

    • मैं PowerShell में alias के रूप में बनाए गए touch function से बनी .gitignore इस्तेमाल कर रहा था, लेकिन Git किसी भी तरह उसे respect नहीं कर रहा था
      जांचने पर पता चला कि generated text file UTF-16 थी, इसलिए व्यवहार में ignore हो रही थी। सबक मिला और system default को UTF-8 में बदला, लेकिन अब मैं बस text editor पर निर्भर रहता हूं
    • global locale सिर्फ encoding के लिए नहीं, कुल मिलाकर ही गलती था
      अगर printf("%f", 4.2) environment के हिसाब से जादुई तरीके से अलग string output करे, तो यह समाधान से ज्यादा समस्याएं पैदा करता है। जब locale-dependent behavior चाहिए हो, तो function को local information या संबंधित हिस्से explicitly pass करने चाहिए
  • पिछले कई दशकों में एक heuristic धीरे-धीरे और सही साबित होती गई है: अगर कहीं charset setting है और वह UTF-8 नहीं है, तो वह गलत है
    Python 2 character sets से बेपरवाह था, इसलिए हमेशा चलता था, लेकिन Python 3 का सुधार सिर्फ साधारण सुधार नहीं था। Python 3 script और Python 2 script में फर्क करने का तरीका यह है: अगर string utf-8 शामिल है तो वह Python 3 है, और अगर वह सिर्फ C.UTF-8 locale में चलती है तो वह Python 3 है। यह बदलाव Python 3 को “ठीक” करने जैसा समझ आता है, इसलिए स्वागतयोग्य है

  • मुझे लगा था कि Python 3 से ही यह default है

    • शायद Python 3 में u"" prefix की जरूरत खत्म हो गई strings की बात याद आ रही होगी
      अभी Python 2.7 में "éķů" input करके देखा तो उसने उन characters के UTF-8 bytes output किए, इसलिए u prefix ने ठीक-ठीक क्या किया यह पक्का नहीं, लेकिन Python 2 से 3 में जाते समय बड़े बदलावों में से एक यह था कि strings के पास encoding होती है, और byte strings encoding-less byte sequences बन गईं। यह बदलाव मुख्य रूप से Windows जैसे environments में, जहां default encoding UTF-8 नहीं है, open('filename', mode='r') इस्तेमाल करते समय open('filename', mode='r', encoding='UTF-8') explicitly लिखने की जरूरत वाले मुद्दे से जुड़ा लगता है
    • Python 3 में Python source code default रूप से UTF-8 है। लेकिन file में save करते समय इस्तेमाल होने वाली character encoding के बारे में यह कुछ नहीं कहता, और default locale-dependent है
      Path("filenames use their own encoding").write_text("file content encoding uses yet another encoding") की तरह string literals, filenames, और file content encodings अलग-अलग हैं। इनके corresponding encodings हैं tokenize.open का UTF-8, os.fsencode का sys.getfilesystemencoding(), और open का locale.getpreferredencoding()
  • “Node.js, Go, Rust, Java सहित दूसरी popular programming languages भी UTF-8 को default के रूप में इस्तेमाल करती हैं” — लगता है Java का UTF-16 से UTF-8 पर जाना छूट गया

    • Java में bytes को string में convert करते समय default encoding मूल रूप से platform-dependent थी, और अब UTF-8 है
      String class के अंदर UTF-16 और latin-1 encodings अब भी इस्तेमाल होती हैं, और JVM पहले की तरह modified UTF-8 encoding इस्तेमाल करता है। String class मूल रूप से सिर्फ UTF-16 इस्तेमाल करती थी, लेकिन Java 9 से जब संभव हो तो प्रति character 1-byte latin-1 encoding भी इस्तेमाल करती है
    • लगता है internal string representation और read/write encoding को मिलाकर कहा जा रहा है
      Java ने read/write encoding के default के रूप में कभी UTF-16 इस्तेमाल नहीं किया
    • लगता है यह Java 18 में 2 साल पहले बदला था
  • क्या CPython की internal encoding अब UTF-8 है?
    Python strings को subscript से index किया जा सकता है, लेकिन random access इतना कम इस्तेमाल होता है कि ज़रूरत पड़ने पर lazy indexing भी ठीक लगती है। अगर सिर्फ एक step आगे या पीछे जाना हो तो index की ज़रूरत नहीं, इसलिए internal representation को UTF-8 रखना भी पूरी तरह संभव है

    • str को represent करने वाला object PyUnicode है
      जब UTF-8 bytes मांगे जाते हैं, तो bytes object ज़रूरत पड़ने पर बनाया जाता है और PyUnicode के हिस्से के रूप में cache होता है; PyUnicode release होने पर वह भी साथ release हो जाता है। अलग से, string बनाने वाले code points random access के लिए एक simple array में store होते हैं। हर code point का size 1, 2, या 4 bytes हो सकता है, और PyUnicode बनाते समय maximum code point value specify करने पर उसे 127, 255, 65535, 1,114,111 में से किसी एक तक round up किया जाता है, जिससे तय होता है कि 1/2/4 bytes इस्तेमाल होंगे
      अगर maximum code point value 127 है, तो उस array representation को सीधे UTF-8 की तरह इस्तेमाल किया जा सकता है। इसलिए सवाल का जवाब यह है कि बहुत-सी strings जिनमें सभी code points 127 या उससे कम हैं, UTF-8 के रूप में store होती हैं। हालांकि string को iterate करते समय code point unit में नहीं करना चाहिए। user द्वारा पहचाना जाने वाला character, यानी grapheme cluster, एक या अधिक code points से मिलकर बना होता है। उदाहरण के लिए accent वाला e, e code point के बाद combining accent code point से बन सकता है, और phoenix emoji bird emoji, zero-width joiner, और fire emoji से बना होता है। करोड़ों लोगों द्वारा इस्तेमाल की जाने वाली कुछ writing systems भी इसी तरह consonant में vowel दर्शाने वाला combining mark जोड़ती हैं। यह - - 5 code points है, और कई languages इसकी “length” कैसे report करती हैं, इस पर एक अच्छा लेख है: https://hsivonen.fi/string-length/. यह बात Unicode TR29 को अभी-अभी Python C extension के रूप में implement करने के अनुभव से आई है
  • हैरानी है कि utf-8-sig क्यों नहीं। यह optional BOM handle कर देता है, और पिछले हफ्ते भी उसी वजह से मुझे script ठीक करनी पड़ी थी

    • अब किसी भी चीज़ को UTF-8 में BOM नहीं डालना चाहिए
      यह recommended भी नहीं है, और आजकल BOM पर fail होना भी मुझे reasonable behavior लगता है
    • Python को हर input/output के आगे चुपचाप invisible BOM लगाने के लिए बदलना अच्छा idea नहीं है
  • UTF-8 की बात हो रही है तो Linux framebuffer में बहुत पहले proper UTF-8 support आ जाना चाहिए था
    256/512 glyphs वाला नहीं, सचमुच का support। GNU Hurd तक में करीब 2007 से UTF-8 support वाला बेहतर terminal console था, और अब 2024 है

  • अच्छा है। अब बस JS का UTF-8 पर switch करना बाकी है
    बेशक JS improve नहीं हो सकता। किसी भी दूसरी programming language के उलट, उसे 1995 में लिखे code के साथ compatible रहना पड़ता है

    • यह बात Python में file को “text के रूप में” खोलने पर default में कौन-सी encoding इस्तेमाल हो, उससे जुड़ी है
      strings की internal representation अलग मुद्दा है, और JavaScript की तरह Python भी internally “बस UTF-8” इस्तेमाल नहीं करता
  • “Unix इस्तेमाल करने वाले कई Python developers यह भूल जाते हैं कि default encoding platform-dependent है, और UTF-8 encoded text file पढ़ते समय encoding="utf-8" छोड़ देते हैं” में, यह भूलने से ज़्यादा शायद पर्याप्त रूप से जाना-पहचाना न होना हो सकता है
    सच कहूं तो मुझे लगा था कि Python, जब तक explicitly अलग से न कहा जाए, हर जगह सिर्फ UTF-8 ही इस्तेमाल करता है

    • असल में यह situation पर निर्भर करता है
      bytes.decode और str.encode कम-से-कम Python 3 से UTF-8 को default के रूप में इस्तेमाल करते आए हैं। वहीं filenames decode करते समय default encoding sys.getfilesystemencoding() का इस्तेमाल करती है, और Windows व macOS पर यह भी UTF-8 है, लेकिन Linux पर यह locale, खास तौर पर CODESET, पर निर्भर करती है। आखिर में, open सीधे locale.getencoding() का इस्तेमाल करता है