PEP 686: Python 3.15 में UTF-8 मोड डिफ़ॉल्ट रूप से सक्षम
(peps.python.org)- 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 समस्याएँ ठीक करने के लिए सुझाई गई प्रक्रिया यह है
- UTF-8 मोड बंद करें
- PEP 597 के
EncodingWarningसे उन जगहों की पहचान करें जहाँ UTF-8 मोड असर डालता है- जहाँ
encodingविकल्प छोड़ा गया है, वहाँencoding="utf-8"याencoding="locale"के उपयोग पर विचार करें - जहाँ
locale.getpreferredencoding()का उपयोग है, वहाँ"utf-8"याlocale.getencoding()के उपयोग पर विचार करें
- जहाँ
- 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 मोड बंद रख सकते हैं
- इस तरीके से UTF-8 मोड में भी
उपयोगकर्ता शिक्षा के दृष्टिकोण से
- नए उपयोगकर्ताओं को शुरुआती एक साल में text encoding सीखने की ज़रूरत कम होगी
- encoding के बारे में तब सीखना होगा जब non-UTF-8 text files संभालनी हों
- मौजूदा उपयोगकर्ताओं को backward compatibility प्रक्रिया के अनुसार प्रभावित हिस्सों की जाँच करनी चाहिए
1 टिप्पणियां
Hacker News की राय
डिफ़ॉल्ट text file encoding का platform के हिसाब से बदलना हमेशा झुंझलाहट भरा था, इसलिए यह बदलाव स्वागतयोग्य है
अच्छा है कि उन्होंने filesystem encoding तक छेड़ने की कोशिश नहीं की। वह अलग समस्या है, और अपने आप में सिरदर्द है
Windows ने लंबे समय तक
TextOutAजैसे ANSI functions को UTF-8 code page इस्तेमाल करने के लिए आसानी से चुनने का तरीका नहीं दिया, यह बड़ी गलती थी। manifest file से यह संभव होना Windows 10 development के बीच के दौर में हुआ, जबकि ऐसी सुविधा NT4 या Windows 98 के समय ही आ जानी चाहिए थीयह सिर्फ 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 में बदल दियाcompany laptop पर local tests सब ठीक थे, लेकिन Linux host पर deploy करने पर downstream application consume नहीं कर सका क्योंकि उसे CRLF चाहिए था। यह उन छोटी और मूर्खतापूर्ण बातों में से एक है जिन्हें कभी-कभी याद रखना पड़ता है। हालांकि यह भी वाजिब सवाल है कि नया लिखा गया software किसी खास line terminator की मांग क्यों करता है
अस्थिर system defaults पर निर्भर न रहना अच्छी बात है
ऐसे values किसी समय मेरी धारणा से अलग लौटने लगते हैं। कुछ साल पहले Ubuntu और
init.dscripts के साथ काम करते समय, 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 लगना ही बेहतर है
touchfunction से बनी.gitignoreइस्तेमाल कर रहा था, लेकिन Git किसी भी तरह उसे respect नहीं कर रहा थाजांचने पर पता चला कि generated text file UTF-16 थी, इसलिए व्यवहार में ignore हो रही थी। सबक मिला और system default को UTF-8 में बदला, लेकिन अब मैं बस text editor पर निर्भर रहता हूं
अगर
printf("%f", 4.2)environment के हिसाब से जादुई तरीके से अलग string output करे, तो यह समाधान से ज्यादा समस्याएं पैदा करता है। जब locale-dependent behavior चाहिए हो, तो function को local information या संबंधित हिस्से explicitly pass करने चाहिएपिछले कई दशकों में एक heuristic धीरे-धीरे और सही साबित होती गई है: अगर कहीं
charsetsetting है और वह UTF-8 नहीं है, तो वह गलत हैPython 2 character sets से बेपरवाह था, इसलिए हमेशा चलता था, लेकिन Python 3 का सुधार सिर्फ साधारण सुधार नहीं था। Python 3 script और Python 2 script में फर्क करने का तरीका यह है: अगर string
utf-8शामिल है तो वह Python 3 है, और अगर वह सिर्फC.UTF-8locale में चलती है तो वह Python 3 है। यह बदलाव Python 3 को “ठीक” करने जैसा समझ आता है, इसलिए स्वागतयोग्य हैमुझे लगा था कि Python 3 से ही यह default है
u""prefix की जरूरत खत्म हो गई strings की बात याद आ रही होगीअभी Python 2.7 में
"éķů"input करके देखा तो उसने उन characters के UTF-8 bytes output किए, इसलिएuprefix ने ठीक-ठीक क्या किया यह पक्का नहीं, लेकिन 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 लिखने की जरूरत वाले मुद्दे से जुड़ा लगता है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 पर जाना छूट गया
Stringclass के अंदर UTF-16 और latin-1 encodings अब भी इस्तेमाल होती हैं, और JVM पहले की तरह modified UTF-8 encoding इस्तेमाल करता है।Stringclass मूल रूप से सिर्फ UTF-16 इस्तेमाल करती थी, लेकिन Java 9 से जब संभव हो तो प्रति character 1-byte latin-1 encoding भी इस्तेमाल करती हैJava ने read/write encoding के default के रूप में कभी UTF-16 इस्तेमाल नहीं किया
क्या CPython की internal encoding अब UTF-8 है?
Python strings को subscript से index किया जा सकता है, लेकिन random access इतना कम इस्तेमाल होता है कि ज़रूरत पड़ने पर lazy indexing भी ठीक लगती है। अगर सिर्फ एक step आगे या पीछे जाना हो तो index की ज़रूरत नहीं, इसलिए internal representation को UTF-8 रखना भी पूरी तरह संभव है
strको represent करने वाला objectPyUnicodeहैजब UTF-8 bytes मांगे जाते हैं, तो
bytesobject ज़रूरत पड़ने पर बनाया जाता है औरPyUnicodeके हिस्से के रूप में cache होता है;PyUnicoderelease होने पर वह भी साथ 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,
ecode 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 ठीक करनी पड़ी थीयह recommended भी नहीं है, और आजकल BOM पर fail होना भी मुझे reasonable behavior लगता है
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 रहना पड़ता है
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 ही इस्तेमाल करता है
bytes.decodeऔरstr.encodeकम-से-कम Python 3 से UTF-8 को default के रूप में इस्तेमाल करते आए हैं। वहीं filenames decode करते समय default encodingsys.getfilesystemencoding()का इस्तेमाल करती है, और Windows व macOS पर यह भी UTF-8 है, लेकिन Linux पर यह locale, खास तौर परCODESET, पर निर्भर करती है। आखिर में,openसीधेlocale.getencoding()का इस्तेमाल करता है