PEP 686: Python 3.15'te UTF-8 modu varsayılan olarak etkinleşiyor
(peps.python.org)- Python 3.15, UTF-8 modunu varsayılan olarak açarak dosyalar, standart giriş/çıkış ve pipe'lar için varsayılan kodlamayı UTF-8'e getiriyor
- UTF-8; kaynak dosyaları, JSON·TOML·YAML, başlıca editörler, web verileri ve Node.js·Go·Rust·Java gibi ortamlarda fiilî standart kodlama olarak kullanıldığı için birlikte çalışabilirlik artıyor
- Mevcut varsayılan kodlama platforma göre değiştiğinden, Unix geliştiricileri
encoding="utf-8"ifadesini atladığında Windows gibi ortamlarda uyuşmazlık kaynaklı hatalar oluşabiliyor - Gerekirse
PYTHONUTF8=0veya-X utf8=0ile kapatılabiliyor; uyumluluk denetimi içinEncodingWarning,encoding="utf-8",encoding="locale",locale.getencoding()kullanılabiliyor - Varsayılan kodlamaya bağımlı programlar özellikle Windows'ta
UnicodeError, mojibake ve sessiz veri bozulması yaşayabileceği için önceden kontrol edilmesi gerekiyor
Python 3.15'te değişen varsayılan kodlama
- PEP 686, PEP 540'daki UTF-8 modunu varsayılan olarak etkinleştiren bir değişiklik
- Varsayılan kodlama gerektiren dosyalar, stdio ve pipe'larda Python artık tutarlı şekilde UTF-8 kullanıyor
- Bu özellik Python 3.15 ile varsayılan olacak; kullanıcılar ise şu yollarla devre dışı bırakabilecek
PYTHONUTF8=0-X utf8=0
Neden varsayılan olarak UTF-8?
- UTF-8 birçok ortamda standart metin kodlaması haline geldi
- Python kaynak dosyalarının varsayılan kodlaması UTF-8
- JSON, TOML ve YAML UTF-8 kullanıyor
- Visual Studio Code ve Windows Notepad dahil çoğu metin editörü varsayılan olarak UTF-8 kullanıyor
- İnternetteki web sitelerinin ve metin verilerinin büyük kısmı UTF-8 kullanıyor
- Node.js, Go, Rust ve Java dahil birçok popüler programlama dili varsayılan olarak UTF-8 kullanıyor
- Python'un varsayılan kodlamasının UTF-8'e geçmesi, diğer araçlar·diller·veri biçimleriyle birlikte çalışabilirliği artırıyor
- Birçok Unix ortamı Python geliştiricisi, varsayılan kodlamanın platforma bağımlı olduğunu unutup JSON·TOML·Markdown·Python kaynak dosyaları gibi UTF-8 metinleri okurken
encoding="utf-8"yazmıyor - Platformlar arasındaki varsayılan kodlama farkı, bu tür kodların başka ortamlarda bozulmasına yol açan bir hata kaynağı oluyor
locale API'si ve encoding="locale" düzeltmesi
- UTF-8 modu
locale.getpreferredencoding(False)sonucunu etkilediği için, UTF-8 modundan bağımsız olarak locale kodlamasını döndüren bir API'ye ihtiyaç vardı locale.getencoding()bu amaçla eklendi; locale kodlamasını döndürürken UTF-8 modunu yok sayıyor- Bu API Python 3.11'de eklendi
warn_default_encodingseçeneği verildiğindelocale.getpreferredencoding(),open()gibi PEP 597'dekiEncodingWarninguyarısını üretiyor- PEP 597,
TextIOWrapperiçinencoding="locale"seçeneğini ekleyerek locale kodlamasının açıkça belirtilmesini sağladı - Önceden UTF-8 modunda
encoding="locale"belirtilse bileTextIOWrapper"UTF-8"kullanıyordu- Bu durum PEP 597'nin gerekçesiyle uyumlu değildi
- Çünkü Python'un varsayılan metin kodlaması değiştiğinde UTF-8 modunun varsayılan hale geleceği senaryo öngörülmemişti
- Bu tutarsızlık Python 3.11'de düzeltildi; artık UTF-8 modunda da
encoding="locale"geçirilirse locale kodlaması kullanılıyor
Geriye dönük uyumluluk ve geçiş süreci
- Çoğu Unix sistemi UTF-8 locale kullanıyor ve Python locale C veya POSIX olduğunda zaten UTF-8 modunu etkinleştirdiği için değişikliğin etkisi ağırlıklı olarak Windows kullanıcılarında görülecek
- Varsayılan kodlamaya bağımlı Python programları şu sorunları yaşayabilir
UnicodeError- mojibake
- sessiz veri bozulması
- Geriye dönük uyumluluk sorunlarını düzeltmek için önerilen süreç şöyle
- UTF-8 modunu devre dışı bırakın
- PEP 597'deki
EncodingWarningile UTF-8 modundan etkilenen yerleri bulunencodingseçeneği atlandıysaencoding="utf-8"veyaencoding="locale"kullanmayı değerlendirinlocale.getpreferredencoding()kullanıldıysa"utf-8"veyalocale.getencoding()kullanmayı değerlendirin
- Uygulamayı UTF-8 modunda test edin
Ruby·Java örnekleri ve reddedilen alternatifler
- Ruby, Ruby 3.0 ile 2020'de Windows'taki varsayılan
external_encodingdeğerini UTF-8 olarak değiştirdi - Java, JDK 18 ile 2022'de varsayılan metin kodlamasını UTF-8 olarak değiştirdi
- Ruby ve Java geriye dönük uyumluluk için seçenekler sunuyor, ancak Python'daki
EncodingWarninggibi bir varsayılan kodlama kullanım uyarısı sunmuyor - Varsayılan kodlama kullanımını tamamen kaldırma fikri reddedildi
- Çünkü birçok durumda varsayılan kodlama yalnızca ASCII metin okumak ve yazmak için kullanılıyor
- Yalnızca Unix'te çalışan, çapraz platform olmayan uygulamalar için böyle bir uyarı faydalı olmayabilir
- Her yerde
encodingzorunlu kılınırsa kullanıcı yükü artar ve çok sayıdaDeprecationWarning, kullanıcıların uyarıları görmezden gelmesine yol açabilir - PEP 387, geriye dönük uyumluluğu bozan değişikliklerde uyarı eklenmesini ister; ancak bunun mutlaka
DeprecationWarningolması gerekmez
subprocessmodülündeki pipe'lar için varsayılan kodlama olarakPYTHONIOENCODINGkullanma önerisi de reddedildi- Bu yaklaşım, UTF-8 modunda bile
subprocess.Popen(text=True)için eski kodlamanın kullanılmasına izin verirdi - Ancak bu, “varsayılan kodlama” kavramını daha karmaşık hale getiriyor ve kendi başına da geriye dönük uyumluluğu bozan bir değişiklik sayılıyor
- Kullanıcılar
text=Truekullanımınıencoding="utf-8"veyaencoding="locale"ile değiştirene kadar UTF-8 modunu kapatabilir
- Bu yaklaşım, UTF-8 modunda bile
Kullanıcı eğitimi açısından
- Yeni kullanıcıların ilk yıl içinde metin kodlamasını öğrenme ihtiyacı azalacak
- Kodlama bilgisini yalnızca UTF-8 olmayan metin dosyalarıyla çalışmaları gerektiğinde edinmeleri yeterli olacak
- Mevcut kullanıcıların ise geriye dönük uyumluluk sürecine göre etkilenen noktaları kontrol etmesi gerekiyor
1 yorum
Hacker News yorumları
Varsayılan metin dosyası kodlamasının platforma göre değişmesi her zaman can sıkıcıydı; bu değişiklik sevindirici
Dosya sistemi kodlamasına dokunmaya çalışmamaları da iyi. O ayrı bir mesele ve kendi başına baş ağrısı
Windows’un
TextOutAgibi ANSI işlevlerinin UTF-8 kod sayfasını kullanmasını basitçe seçmenin bir yolunu uzun süre sunmamış olması büyük bir hataydı. Bunun manifest dosyasıyla mümkün hale gelmesi Windows 10 geliştirmelerinin ortalarına doğru oldu; böyle bir özellik NT4 ya da Windows 98 zamanında gelmeliydiYalnızca platforma değil, kullanıcının tercih ettiği yerele de bağlıydı; C standart kütüphanesi de aynı şekilde davranır. Örneğin Unix/Linux’ta Batı Avrupa dilleri için
iso-8859-1yaygındı; euro’nun gelmesinden sonra ise€simgesini içereniso-8859-15e geçmek sıklaştı. UTF-8’in sorunsuz çalışmaya başlaması 2000’lerin sonlarına denk gelir; Debian, Etch sürümünde varsayılanı UTF-8 yaptıŞirket dizüstümde yerel testlerin hepsi iyi geçti, ama Linux ana makineye dağıtınca alt uygulama CRLF istediği için veriyi tüketemedi. Bazen hatırlamak zorunda kaldığınız küçük ve aptal sorunlardan biri. Yine de yeni yazılmış bir yazılımın neden belirli bir satır sonlandırıcı istediği de geçerli bir soru
Oynak sistem varsayılanlarına yaslanmamak iyi bir şey
Bu değerler bir noktada varsaydığımdan farklı dönme eğiliminde. Birkaç yıl önce Ubuntu ve
init.dbetikleriyle uğraşırken, Java’yı başlatan betik root olarak çalışıyordu; Docker öncesi olduğu için bu daha da belirgindi ve normal kullanıcılar için düzgün UTF-8 varsayılanlarını ayarlamayan bir shell içinde yürütülüyordu. Sonuçta Java’nın işletim sistemi varsayılanını kullanan kötü API kullanımını ortaya çıkardıGünümüzde çoğu yerde kodlamayı açıkça belirtebileceğiniz alternatif API’ler var ve statik kod denetleyicileri yanlış olanı kullanınca uyarıyor. Ama tek bir yer bile atlanırsa içerik bozulmaya başlıyor. Artık UTF-8 dışı bir kodlama kullanımı büyük olasılıkla çoğu zaman kasıtsızdır; kasıtlıysa da işletim sisteminin tuhaf dolaylı ayarlarına yaslanmak yerine açıkça belirtilmelidir. Bu yüzden iyi bir değişiklik ve bundan bozulan koda basit bir düzeltme eklenmesi daha iyi
touchişlevinin ürettiği.gitignoredosyasını kullanıyordum; ne yaparsam yapayım Git bunu dikkate almıyorduKontrol edince oluşturulan metin dosyasının UTF-16 olduğunu ve fiilen yok sayıldığını gördüm. Dersimi alıp sistem varsayılanını UTF-8’e çevirdim, ama şimdi sadece metin düzenleyiciye güveniyorum
printf("%f", 4.2)ortama göre sihirli biçimde farklı bir dize yazdırıyorsa, çözdüğünden fazla sorun çıkarır. Yerel ayara bağlı davranış istendiğinde, yerel bilgiyi ya da ilgili parçaları işleve açıkça geçirmek gerekirSon birkaç on yılda giderek daha doğru çıkan bir sezgisel kural var: Bir yerde
charsetayarı varsa ve UTF-8 değilse yanlıştırPython 2 karakter kümesinden bağımsız olduğu için hep çalışırdı, ama Python 3’teki iyileştirme yalnızca basit bir iyileştirme değildi. Python 3 betiği ile Python 2 betiğini ayırmanın yolu şu: içinde
utf-8dizgesi varsa Python 3’tür; yalnızcaC.UTF-8yerelinde çalışıyorsa Python 3’tür. Bu değişiklik Python 3’ü “onarıyor” gibi anlaşıldığı için memnuniyetle karşılıyorumBunun Python 3’ten beri varsayılan olduğunu sanıyordum
u""önekinin gereksiz hale geldiği dizeleri düşünmüşsünüzAz önce Python 2.7’de
"éķů"yazdım; ilgili karakterlerin UTF-8 baytlarını yazdırdı, bu yüzdenuönekinin tam olarak ne yaptığından emin değilim. Ama Python 2’den 3’e geçişteki büyük değişikliklerden biri, dizelerin bir kodlamaya sahip olması ve bayt dizelerinin kodlamasız bayt dizileri haline gelmesiydi. Bu değişiklik esas olarak Windows gibi varsayılan kodlaması UTF-8 olmayan ortamlardaopen('filename', mode='r')kullanırkenopen('filename', mode='r', encoding='UTF-8')yazmayı açıkça belirtme gerekliliğiyle ilgili görünüyorPath("filenames use their own encoding").write_text("file content encoding uses yet another encoding")örneğinde olduğu gibi, dize literali, dosya adı ve dosya içeriği kodlamaları birbirinden farklıdır. Karşılık gelen kodlamalartokenize.openiçin UTF-8,os.fsencodeiçinsys.getfilesystemencoding(),openiçinlocale.getpreferredencoding()dir“Node.js, Go, Rust ve Java dahil diğer popüler programlama dilleri de varsayılan olarak UTF-8 kullanır” denmiş; Java’nın UTF-16’dan UTF-8’e geçtiğini kaçırmışım
Stringsınıfının içinde UTF-16 ve latin-1 kodlamaları hâlâ kullanılıyor; JVM de eskisi gibi değiştirilmiş UTF-8 kodlamasını kullanıyor.Stringsınıfı başlangıçta yalnızca UTF-16 kullanıyordu, ancak Java 9’dan beri mümkün olduğunda karakter başına 1 baytlık latin-1 kodlamasını da kullanıyorJava, okuma/yazma kodlamasının varsayılanı olarak hiç UTF-16 kullanmadı
CPython’in iç kodlaması artık UTF-8 mi?
Python dizgeleri indisle erişilebilir, ama rastgele erişim yeterince nadir olduğundan gerektiğinde tembel indeksleme yapmak sorun olmayacak gibi. Yalnızca bir karakter ileri ya da geri gitmek gerekiyorsa indekse ihtiyaç yok; dolayısıyla iç gösterimin UTF-8 olması da gayet mümkün
str’yi temsil eden şeyPyUnicodenesnesidirUTF-8 baytları istendiğinde, gerekirse bir
bytesnesnesi oluşturulur,PyUnicode’un parçası olarak önbelleğe alınır vePyUnicodeserbest bırakıldığında onunla birlikte serbest bırakılır. Bunun dışında, dizgeyi oluşturan kod noktaları rastgele erişim mümkün olsun diye basit bir dizide saklanır. Her kod noktasının boyutu 1, 2 veya 4 bayt olabilir;PyUnicodeoluşturulurken en büyük kod noktası değeri belirtildiğinde bu değer 127, 255, 65535 veya 1.114.111 değerlerinden birine yuvarlanır ve 1/2/4 bayt kullanılıp kullanılmayacağı belirlenirEn büyük kod noktası değeri 127 ise bu dizi gösterimi doğrudan UTF-8 olarak kullanılabilir. Yani sorunun yanıtı şu: Tüm kod noktaları 127 veya altında olan pek çok dizge UTF-8 olarak saklanır. Ancak bir dizge üzerinde gezinirken bunu kod noktası biriminde yapmamak gerekir. Kullanıcının algıladığı karakter, yani grafem kümesi, bir veya daha fazla kod noktasından oluşur. Örneğin aksanlı bir e,
ekod noktasının ardından gelen bir birleştirici aksan kod noktasından oluşabilir; anka kuşu emojisi ise kuş emojisi, sıfır genişlikli birleştirici ve ateş emojisinden oluşur. Yüz milyonlarca kişinin kullandığı bazı yazı sistemleri de ünsüzlere ünlüyü gösteren birleştirici işaretlerin eklenmesine benzer bir şekilde çalışır. Bu- -5 kod noktasıdır ve çeşitli dillerin bunun “uzunluğunu” nasıl raporladığını ele alan iyi bir yazı var: https://hsivonen.fi/string-length/. Bu kısmı ele alan Unicode TR29’u Python C uzantısı olarak yeni uygulamış biri olarak söylüyorumNeden
utf-8-sigolmadığını merak ediyorum. İsteğe bağlı BOM’u işliyor; daha geçen hafta bu yüzden bir betiği düzeltmem gerektiÖnerilmiyor da; günümüzde BOM yüzünden hata vermek de makul bir davranış bence
UTF-8’den söz açılmışken, Linux framebuffer’ın çoktan doğru düzgün UTF-8 desteğine sahip olması gerekirdi
256/512 gliflik olanlardan değil, gerçek destekten bahsediyorum. GNU Hurd’ün bile yaklaşık 2007’den beri UTF-8 destekleyen daha iyi bir terminal konsolu vardı; şimdi 2024’teyiz
Güzel. Şimdi geriye yalnızca JS’nin UTF-8’e geçmesi kaldı
Tabii JS iyileştirilemez. Çünkü başka hiçbir programlama dilinden farklı olarak 1995’te yazılmış kodlarla uyumlu olmak zorunda
Dizgelerin iç temsili ayrı bir konu; JavaScript gibi Python da içeride “sadece UTF-8” kullanmıyor
“Unix kullanan pek çok Python geliştiricisi, varsayılan kodlamanın platforma bağlı olduğunu unutuyor ve UTF-8 ile kodlanmış metin dosyalarını okurken
encoding="utf-8"belirtmeyi atlıyor” kısmında, bu unutulmaktan ziyade yeterince bilinmiyor olabilirAçıkçası Python’ın açıkça farklı bir şey istenmediği sürece her yerde yalnızca UTF-8 kullandığını sanıyordum
bytes.decodevestr.encode, en azından Python 3’ten beri varsayılan olarak UTF-8 kullanıyor. Buna karşılık dosya adlarını çözerken varsayılan kodlamasys.getfilesystemencoding()kullanır; Windows ve macOS’te bu da UTF-8’dir, ama Linux’ta yerel ayara, daha somut olarakCODESET’e bağlıdır. Son olarakopendoğrudanlocale.getencoding()kullanır