PEP 686: Mode UTF-8 Diaktifkan Secara Default di Python 3.15
(peps.python.org)- Python 3.15 menyalakan mode UTF-8 secara default sehingga encoding bawaan untuk file, input/output standar, dan pipe menjadi UTF-8
- UTF-8 sudah digunakan sebagai encoding standar de facto di source file, JSON·TOML·YAML, editor utama, data web, serta Node.js·Go·Rust·Java, sehingga interoperabilitas meningkat
- Encoding default sebelumnya berbeda menurut platform, sehingga bila pengembang Unix menghilangkan
encoding="utf-8", dapat muncul bug ketidakcocokan di Windows dan lingkungan lain - Jika perlu, mode ini bisa dimatikan dengan
PYTHONUTF8=0atau-X utf8=0; untuk pemeriksaan kompatibilitas digunakanEncodingWarning,encoding="utf-8",encoding="locale", danlocale.getencoding() - Program yang bergantung pada encoding default dapat mengalami
UnicodeError, mojibake, atau kerusakan data diam-diam, terutama di Windows, sehingga perlu diperiksa lebih dulu
Encoding default yang berubah di Python 3.15
- PEP 686 adalah perubahan yang mengaktifkan mode UTF-8 dari PEP 540 sebagai nilai default
- Untuk file, stdio, dan pipe yang memerlukan encoding default, Python akan secara konsisten menggunakan UTF-8
- Mulai Python 3.15, ini akan aktif secara default, dan pengguna dapat menonaktifkannya dengan cara berikut
PYTHONUTF8=0-X utf8=0
Mengapa UTF-8 dijadikan default
- UTF-8 telah menjadi semacam encoding teks standar di berbagai lingkungan
- Encoding default untuk source file Python adalah UTF-8
- JSON, TOML, YAML menggunakan UTF-8
- Sebagian besar editor teks, termasuk Visual Studio Code dan Windows Notepad, menggunakan UTF-8 sebagai default
- Sebagian besar situs web dan data teks di internet menggunakan UTF-8
- Banyak bahasa pemrograman populer, termasuk Node.js, Go, Rust, dan Java, menggunakan UTF-8 sebagai default
- Jika encoding default Python berubah menjadi UTF-8, interoperabilitas dengan alat, bahasa, dan format data lain akan membaik
- Banyak pengembang Python di lingkungan Unix lupa bahwa encoding default bergantung pada platform, lalu menghilangkan
encoding="utf-8"saat membaca teks UTF-8 seperti file JSON·TOML·Markdown·source file Python - Perbedaan encoding default antarplatform menjadi penyebab bug yang membuat kode seperti itu rusak di lingkungan lain
Perubahan pada API locale dan encoding="locale"
- Karena mode UTF-8 memengaruhi
locale.getpreferredencoding(False), dibutuhkan API untuk mendapatkan encoding locale tanpa terpengaruh mode UTF-8 locale.getencoding()ditambahkan untuk tujuan ini, dan mengembalikan encoding locale sambil mengabaikan mode UTF-8- API ini ditambahkan di Python 3.11
- Jika opsi
warn_default_encodingditentukan,locale.getpreferredencoding()akan memunculkanEncodingWarningdari PEP 597, sepertiopen() - PEP 597 menambahkan opsi
encoding="locale"keTextIOWrapperagar encoding locale bisa ditentukan secara eksplisit - Sebelumnya, bahkan jika
encoding="locale"ditentukan dalam mode UTF-8,TextIOWrappertetap menggunakan"UTF-8"- Ini tidak sejalan dengan motivasi PEP 597
- Alasannya karena saat encoding teks default Python berubah, situasi di mana mode UTF-8 menjadi default belum diperkirakan
- Ketidaksesuaian ini diperbaiki di Python 3.11, dan kini jika
encoding="locale"diberikan, encoding locale akan digunakan bahkan dalam mode UTF-8
Kompatibilitas mundur dan prosedur migrasi
- Sebagian besar sistem Unix menggunakan locale UTF-8, dan Python mengaktifkan mode UTF-8 saat locale adalah C atau POSIX, sehingga dampak perubahan ini terutama terkonsentrasi pada pengguna Windows
- Program Python yang bergantung pada encoding default dapat mengalami masalah berikut
UnicodeError- mojibake
- kerusakan data diam-diam
- Prosedur yang direkomendasikan untuk memperbaiki masalah kompatibilitas mundur adalah sebagai berikut
- Nonaktifkan mode UTF-8
- Gunakan
EncodingWarningdari PEP 597 untuk menemukan lokasi yang terdampak oleh mode UTF-8- Jika opsi
encodingdihilangkan, pertimbangkan penggunaanencoding="utf-8"atauencoding="locale" - Jika
locale.getpreferredencoding()digunakan, pertimbangkan penggunaan"utf-8"ataulocale.getencoding()
- Jika opsi
- Uji aplikasi dalam mode UTF-8
Preseden Ruby·Java dan alternatif yang ditolak
- Ruby mengubah
external_encodingdefault di Windows menjadi UTF-8 pada Ruby 3.0 tahun 2020 melalui perubahan ini - Java mengubah encoding teks default menjadi UTF-8 pada JDK 18 tahun 2022 melalui perubahan ini
- Ruby dan Java sama-sama menyediakan opsi untuk kompatibilitas mundur, tetapi tidak menyediakan peringatan penggunaan encoding default seperti
EncodingWarningdi Python - Opsi untuk menghapus penggunaan encoding default sepenuhnya ditolak
- Encoding default sering dipakai untuk membaca dan menulis teks ASCII saja
- Untuk aplikasi non-cross-platform yang hanya berjalan di Unix, peringatan seperti itu tidak terlalu berguna
- Jika
encodingdipaksa di semua tempat, beban pengguna akan besar, dan terlalu banyakDeprecationWarningdapat membuat pengguna mengabaikan peringatan - PEP 387 memang meminta penambahan peringatan untuk perubahan yang merusak kompatibilitas mundur, tetapi tidak selalu mewajibkan
DeprecationWarning
- Opsi untuk memakai
PYTHONIOENCODINGsebagai encoding default pipe di modulsubprocessjuga ditolak- Pendekatan ini memungkinkan
subprocess.Popen(text=True)memakai encoding lama bahkan dalam mode UTF-8 - Namun itu membuat “encoding default” menjadi lebih rumit, dan pendekatan itu sendiri juga merupakan perubahan yang merusak kompatibilitas mundur
- Pengguna dapat mematikan mode UTF-8 sampai mereka mengganti
text=Truemenjadiencoding="utf-8"atauencoding="locale"
- Pendekatan ini memungkinkan
Dari sudut pandang edukasi pengguna
- Pengguna baru jadi lebih jarang perlu mempelajari encoding teks selama tahun pertama
- Mereka cukup mempelajari encoding saat perlu menangani file teks non-UTF-8
- Pengguna lama perlu memeriksa titik-titik yang terdampak sesuai prosedur kompatibilitas mundur
1 komentar
Opini Hacker News
Selalu menyebalkan bahwa encoding file teks default berbeda tergantung platform, jadi perubahan ini menyenangkan
Bagus juga bahwa mereka tidak mencoba mengutak-atik encoding filesystem. Itu masalah terpisah, dan sama-sama merepotkan
Kesalahan besar bahwa Windows begitu lama tidak menyediakan cara sederhana untuk memilih agar fungsi ANSI seperti
TextOutAmemakai code page UTF-8. Ini baru bisa dilakukan lewat file manifest sekitar pertengahan pengembangan Windows 10, padahal fitur seperti ini seharusnya sudah ada sejak era NT4 atau Windows 98Bukan hanya platform, tetapi juga bergantung pada locale pilihan pengguna, dan pustaka standar C juga bekerja dengan cara yang sama. Misalnya di Unix/Linux, untuk bahasa Eropa Barat
iso-8859-1umum dipakai, dan setelah euro diperkenalkan, orang sering beralih keiso-8859-15yang memiliki simbol€. UTF-8 mulai bekerja tanpa masalah sekitar akhir 2000-an, dan Debian mengubah default-nya ke UTF-8 pada rilis EtchDi laptop kantor, semua pengujian lokal berjalan baik, tetapi ketika dideploy ke host Linux, aplikasi turunan tidak bisa mengonsumsinya karena membutuhkan CRLF. Ini salah satu masalah kecil dan bodoh yang sesekali harus diingat. Meski begitu, pertanyaan yang valid juga mengapa software yang baru ditulis membutuhkan terminator baris tertentu
Tidak bergantung pada default sistem yang tidak stabil adalah hal yang baik
Nilai-nilai seperti ini cenderung suatu saat kembali berbeda dari asumsi saya. Beberapa tahun lalu saat menangani Ubuntu dan skrip
init.d, skrip yang menjalankan Java dijalankan sebagai root—dan karena itu masih era sebelum Docker, kondisinya lebih terasa—di shell yang tidak menetapkan default UTF-8 normal untuk pengguna biasa. Akibatnya, penggunaan API Java yang buruk, yang memakai default OS, jadi terlihatSekarang sebagian besar API punya varian yang memungkinkan encoding dinyatakan secara eksplisit, dan static code checker juga memberi peringatan jika memakai yang salah. Namun jika satu tempat saja terlewat, konten mulai rusak. Kini penggunaan encoding selain UTF-8 kemungkinan besar tidak disengaja; kalau memang disengaja, harus dinyatakan eksplisit alih-alih bergantung pada konfigurasi tidak langsung OS yang aneh. Jadi ini perubahan yang baik, dan kode yang rusak karenanya lebih baik diberi perbaikan sederhana
.gitignoreyang dibuat oleh fungsitouchalias di PowerShell, tetapi Git sama sekali tidak menghormatinyaSetelah dicek, file teks yang dibuat ternyata UTF-16, sehingga pada dasarnya diabaikan. Saya mendapat pelajaran dan mengubah default sistem ke UTF-8, tetapi sekarang saya hanya mengandalkan editor teks
Jika
printf("%f", 4.2)secara ajaib mencetak string berbeda tergantung lingkungan, masalah yang ditimbulkan lebih banyak daripada yang diselesaikan. Jika menginginkan perilaku yang bergantung pada locale, informasi locale atau bagian terkait harus diteruskan secara eksplisit ke fungsiAda heuristik yang makin lama makin tepat selama beberapa dekade terakhir: jika di suatu tempat ada pengaturan
charsetdan bukan UTF-8, maka itu salahPython 2 tidak bergantung pada charset sehingga selalu berjalan, tetapi perbaikan di Python 3 bukan sekadar perbaikan sederhana. Cara membedakan skrip Python 3 dan skrip Python 2 adalah begini: jika berisi string
utf-8, itu Python 3; jika hanya berjalan pada localeC.UTF-8, itu Python 3. Perubahan ini saya sambut baik karena terasa seperti “memperbaiki” Python 3Saya kira itu sudah menjadi default sejak Python 3
u""Saya baru saja mengetik
"éķů"di Python 2.7 dan ia mencetak byte UTF-8 untuk karakter tersebut, jadi saya tidak sepenuhnya yakin apa tepatnya fungsi prefiksu. Namun salah satu perubahan besar saat beralih dari Python 2 ke 3 adalah string memiliki encoding, sedangkan byte string menjadi urutan byte tanpa encoding. Perubahan ini tampaknya terutama terkait masalah saat memakaiopen('filename', mode='r')di lingkungan seperti Windows yang encoding default-nya bukan UTF-8, sehingga sebelumnya perlu menuliskanopen('filename', mode='r', encoding='UTF-8')secara eksplisitSeperti
Path("filenames use their own encoding").write_text("file content encoding uses yet another encoding"), encoding untuk literal string, nama file, dan isi file masing-masing berbeda. Encoding yang bersesuaian adalah UTF-8 padatokenize.open,sys.getfilesystemencoding()padaos.fsencode, danlocale.getpreferredencoding()padaopenKatanya “bahasa pemrograman populer lain termasuk Node.js, Go, Rust, dan Java juga memakai UTF-8 sebagai default”; saya rupanya melewatkan bahwa Java berpindah dari UTF-16 ke UTF-8
Di dalam kelas
String, encoding UTF-16 dan latin-1 masih digunakan, dan JVM tetap memakai encoding modified UTF-8 seperti sebelumnya. KelasStringawalnya hanya memakai UTF-16, tetapi sejak Java 9 juga memakai encoding latin-1 1 byte per karakter jika memungkinkanJava tidak pernah memakai UTF-16 sebagai default untuk encoding baca/tulis
Apakah encoding internal CPython sekarang UTF-8?
String Python bisa diindeks dengan subskrip, tetapi akses acak cukup jarang sehingga sepertinya tidak masalah jika pengindeksan dilakukan secara lazy saat diperlukan. Kalau hanya maju satu posisi atau mundur satu posisi, indeks tidak diperlukan, jadi representasi internal dengan UTF-8 juga sepenuhnya memungkinkan
stradalah objekPyUnicodeKetika byte UTF-8 diminta, objek
bytesdibuat jika diperlukan dan di-cache sebagai bagian dariPyUnicode, lalu dibebaskan bersama saatPyUnicodedibebaskan. Secara terpisah, code point yang menyusun string disimpan dalam array sederhana agar akses acak dimungkinkan. Ukuran tiap code point bisa 1, 2, atau 4 byte; saat membuatPyUnicode, jika nilai code point maksimum ditentukan, nilainya dibulatkan naik ke salah satu dari 127, 255, 65535, atau 1.114.111, yang menentukan apakah akan memakai 1/2/4 byteJika nilai code point maksimum adalah 127, representasi array itu bisa langsung dipakai sebagai UTF-8. Jadi jawaban atas pertanyaannya adalah: banyak string yang semua code point-nya 127 atau kurang memang disimpan sebagai UTF-8. Namun, saat mengiterasi string, jangan melakukannya per code point. Karakter yang dikenali pengguna, yaitu klaster grafem, terdiri dari satu atau lebih code point. Misalnya e beraksen bisa berupa code point
ediikuti code point aksen penggabung, dan emoji phoenix terdiri dari emoji burung, zero-width joiner, dan emoji api. Beberapa sistem tulisan yang dipakai ratusan juta orang juga mirip, dengan tanda penggabung untuk vokal yang dilekatkan pada konsonan.- -ini terdiri dari 5 code point, dan ada tulisan bagus yang membahas bagaimana berbagai bahasa melaporkan “panjang”-nya: https://hsivonen.fi/string-length/. Ini berdasarkan pengalaman saya baru saja mengimplementasikan Unicode TR29 yang menangani bagian ini sebagai ekstensi C PythonSaya penasaran kenapa bukan
utf-8-sig. Itu menangani BOM opsional, dan baru minggu lalu saya harus memperbaiki skrip karena hal ituItu juga tidak direkomendasikan, dan menurut saya perilaku yang gagal karena BOM saat ini pun masuk akal
Kalau bicara UTF-8, framebuffer Linux seharusnya sudah lama punya dukungan UTF-8 yang benar
Maksudnya dukungan sungguhan, bukan yang hanya 256/512 glyph. Bahkan GNU Hurd saja sudah punya konsol terminal yang lebih baik dengan dukungan UTF-8 sejak sekitar 2007, sementara sekarang sudah 2024
Bagus. Sekarang yang tersisa hanya JS beralih ke UTF-8
Tentu saja JS tidak bisa diperbaiki. Karena tidak seperti bahasa pemrograman lain mana pun, ia harus kompatibel dengan kode yang ditulis pada 1995
Representasi internal string adalah persoalan terpisah, dan seperti JavaScript, Python juga tidak memakai “UTF-8 begitu saja” secara internal
Dalam “Banyak developer Python yang memakai Unix lupa bahwa encoding default bergantung pada platform, dan menghilangkan
encoding="utf-8"saat membaca file teks yang dienkode UTF-8”, mungkin ini bukan karena lupa, melainkan karena hal itu tidak cukup dikenalSejujurnya saya mengira Python hanya memakai UTF-8 di mana-mana kecuali diminta lain secara eksplisit
bytes.decodedanstr.encodesudah memakai UTF-8 sebagai default setidaknya sejak Python 3. Sebaliknya, encoding default saat mendekode nama file memakaisys.getfilesystemencoding(), dan di Windows serta macOS ini juga UTF-8, tetapi di Linux bergantung pada locale, tepatnyaCODESET. Terakhir,openmemakailocale.getencoding()secara langsung