1 poin oleh GN⁺ 2024-08-23 | 1 komentar | Bagikan ke WhatsApp

Preprosesor Python

  • Anggapan bahwa Python tidak memiliki preprosesor sebenarnya tidak benar
  • Python memiliki preprosesor yang sangat kuat

Encoding kode sumber Python

  • Berkat PEP-0263, encoding kode sumber dapat didefinisikan
  • Encoding dapat diatur dengan menambahkan magic comment pada dua baris pertama
  • Contoh: # coding=utf8, # -*- coding: utf8 -*-, # vim: set fileencoding=utf8 :

File konfigurasi path (.pth)

  • Saat interpreter Python dijalankan tanpa opsi -S, paket site dimuat secara otomatis
  • Jalur pencarian modul dapat diperluas dengan menambahkan file .pth ke folder site-packages
  • Baris dalam file .pth yang dimulai dengan import akan dieksekusi
  • Ini memungkinkan eksekusi kode arbitrer saat inisialisasi interpreter Python

Mendefinisikan codec kustom

  • Ada dua kebutuhan yang harus dipenuhi oleh interpreter Python:
    • fungsi decode(data: bytes) -> tuple[str, int]
    • kelas decoder inkremental
  • Gunakan codecs.utf_8_decode untuk melakukan decoding sebenarnya, lalu teruskan string hasilnya ke preprosesor
  • Sebaiknya tangkap exception, tampilkan, lalu lemparkan kembali

Menyediakan decoder inkremental

  • Implementasikan decoder inkremental dengan mewarisi codecs.BufferedIncrementalDecoder
  • Kumpulkan data di buffer dan lakukan praproses seluruh file saat pemanggilan decode final

Ekstensi Python

  • Memperluas Python dengan menggunakan pustaka standar Python relatif mudah
  • Anda dapat memodifikasi stream token file dengan modul tokenize atau memodifikasi abstract syntax tree dengan modul ast
Increment dan decrement unary
  • Python tidak memiliki operator increment dan decrement unary
  • x++, x-- tidak valid
  • ++x, --x valid tetapi memiliki makna yang berbeda
  • Ekspresi increment dan decrement unary dapat diubah menjadi ekspresi Python
Contoh
  • File input incdec.py:
    # coding: magic.incdec
    i = 6
    assert i-- == 6
    assert i == 5
    assert ++i == 6
    assert --i == 5
    assert i++ == 5
    assert i == 6
    assert (++i, 'i++') == (7, 'i++')
    print("PASSED")
    
  • File hasil transformasi:
    i = 6
    assert ((i, i := i - 1)[0]) == 6
    assert i == 5
    assert ((i, i := i + 1)[1]) == 6
    assert ((i, i := i - 1)[1]) == 5
    assert ((i, i := i + 1)[0]) == 5
    assert i == 6
    assert (((i, i := i + 1)[1]), 'i++') == (7, 'i++')
    print("PASSED")
    

Python dengan kurung kurawal (Bython)

  • Cakupan dapat ditentukan dengan kurung kurawal alih-alih indentasi Python
  • Stream token dimodifikasi menggunakan tokenize.generate_tokens
Contoh
  • File input test.by:
    # coding: magic.braces
    def print_message(num_of_times) {
      for i in range(num_of_times) {
        print("braces ftw")
      }
      print({'x': 3})
    }
    x = {
      'foo': 42,
      'bar': 5
    }
    if __name__ == "__main__" {
      print_message(2)
      print({k: v for k, v in x.items()})
    }
    
  • File hasil transformasi:
    def print_message(num_of_times):
      for i in range(num_of_times):
        print("braces ftw")
      print({'x': 3})
    x = {
      'foo': 42,
      'bar': 5
    }
    if __name__ == "__main__":
      print_message(2)
      print({k: v for k, v in x.items()})
    

Menginterpretasikan bahasa lain

  • Interpreter Python dapat dibuat untuk menginterpretasikan bahasa lain
  • Contoh: C, C++, TOML
Contoh
  • File C++ test.cpp:
    #define CODEC "coding:magic.cpp"
    #include <cstdio>
    int main() {
      puts("Hello World");
    }
    
  • File hasil transformasi:
    import cppyy
    cppyy.cppdef(r"""
    #define CODEC "coding:magic.cpp"
    #include <cstdio>
    int main() {
      puts("Hello World");
    }
    """)
    from cppyy.gbl import main
    if __name__ == "__main__":
      main()
    

Validasi data

  • Data berformat TOML dapat divalidasi menggunakan skema JSON
  • jsonschema digunakan untuk melakukan validasi sebenarnya
Contoh
  • File skema schema.json:
    {
      "type": "object",
      "properties": {
        "name": {"type": "string"},
        "age": {"type": "number"},
        "scores": {
          "type": "array",
          "items": {"type": "number"}
        },
        "address": {"$ref": "#/$defs/address"}
      },
      "required": ["name"],
      "$defs": {
        "address": {
          "type": "object",
          "properties": {
            "street": {"type": "string"},
            "postcode": {"type": "number"}
          },
          "required": ["street"]
        }
      }
    }
    
  • File data valid data_valid.toml:
    # coding: magic.toml
    name = "John Doe"
    age = 42
    scores = [40, 20, 80, 90]
    [address]
    street = "Grove St. 4"
    postcode = 19201
    
  • File data tidak valid data_invalid.toml:
    # coding: magic.toml
    name = "John Doe"
    age = 42
    scores = [40, "20", 80, 90]
    [address]
    street = "Grove St. 4"
    postcode = 19201
    

Kesimpulan

  • Dengan menggunakan codec kustom dan file konfigurasi path, perilaku interpreter Python dapat diubah secara signifikan
  • Contohnya termasuk pythonql, future-typing, future-fstrings, future-annotations, dan lainnya
  • Anda dapat dengan mudah bereksperimen dengan preprosesor sendiri menggunakan magic_codec

Ringkasan GN⁺

  • Berbagai ekstensi bahasa dan validasi data dapat dilakukan dengan memanfaatkan preprosesor Python
  • Dijelaskan cara mengubah perilaku interpreter Python melalui codec kustom
  • Artikel ini menyediakan alat dan teknik yang berguna bagi pengembang Python
  • Proyek dengan fungsi serupa antara lain pythonql, future-typing, dan lainnya

1 komentar

 
GN⁺ 2024-08-23
Komentar Hacker News
  • pesan galat sintaks from __future__ import braces telah di-hardcode di cpython sejak 2001

    • ditulis oleh Jeremy Hylton, yang saat ini bekerja di Google sebagai staf engineer yang menangani kualitas pencarian AI
    • menakjubkan bahwa dalam 24 tahun, karier seseorang berkembang dari memperingati pelarangan sintaks tertentu menjadi mengerjakan sistem pencarian yang tidak memerlukan sintaks khusus
  • sempat memikirkan cara pemecatan yang kreatif dengan menggunakan import-hooks, tetapi sayang codec regex mencegah penggunaan sesuatu seperti "μtf8"

    • harus menggunakan import hooks, preprocessors, dan sys.settrace untuk me-monkey-patch semua fungsi menjadi fungsi yang dipanggil sebelumnya, serta menukar stdout dan stderr setiap 17 menit
  • ada alasan mengapa python tidak mengekspos hook preprocessor, dan saya rasa orang dewasa yang rasional sebaiknya menghindarinya

    • tetapi terlepas dari orang dewasa yang rasional, saya ingin mengejar kesenangan
  • preprocessor lebih praktis dan berguna

    • pernah meretas dengan menulis ulang kode menggunakan modul ast, menjalankannya dengan exec, lalu menyisipkan exit()
    • sebelum semua dict terurut, fitur penulisan ulang ast pernah digunakan secara berguna
  • saya suka fleksibilitas python

    • pekerjaan paling terkutuk yang pernah dilakukan adalah memodifikasi string di tempat, dengan menyalahgunakan mmap agar skrip dapat memodifikasi dirinya sendiri
    • sekarang jadi ingin menulis interpreter Lisp
  • contoh terbaik penggunaan pyxl terinspirasi dari jsx

    • dengan # coding: pyxl, kita bisa menulis kode HTML
  • bertanya-tanya apakah transisi dari Python 2 ke 3 bisa ditangani dengan lebih baik

    • # coding: six.python2 bisa membuat kode Python2 menjadi valid di Python3, dan # coding: six.python3 bisa menyesuaikan kode Python3 agar dapat dijalankan di Python2
  • senang ide ini disukai, akan ada lebih banyak lagi segera

  • sudah lama sekali sejak terakhir kali merasa takjub oleh ide yang benar-benar baru

  • jika ingin pembuatan kode inline di Python, Anda bisa memakai cog buatan Ned Batchelder

  • penasaran apakah dependensi yang diperkenalkan lewat strategi coding hook ini terdeteksi oleh pip freeze atau uv

    • jika tidak, silakan bersenang-senang. mungkin lebih mudah menulis ulang library-nya (jika ada jebakan seperti ini, kemungkinan ada jebakan lain juga)