Python'un Önişlemcisi
(pydong.org)- Python, ilk iki satırdaki kaynak kodlama sihirli yorumu ve kullanıcı tanımlı codec'ler sayesinde dosya içeriğini çalıştırmadan önce değiştirebilir veya tamamen farklı bir kodla değiştirebilir
- Kullanıcı tanımlı codec'ler,
.pthyol yapılandırma dosyası içindekiimportçalıştırmasıyla yorumlayıcı başlatılırken kaydedilebilir vecodecs.registerile bir arama işlevi eklenir - Codec uygulaması için
decode(data: bytes) -> tuple[str, int]ve bir artımlı decoder gerekir; istisnalar işlenmezse asıl neden yerine yalnızcaSyntaxError: encoding problem: your_codecgörünebilir - Aynı giriş noktasıyla
++/--artırım-azaltım operatörleri, süslü parantez tabanlı Python,cppyyüzerinden C/C++ çalıştırma ve TOML'ü JSON Schema ile doğrulama gibi şeyler uygulanabilir - Şaka amaçlı örneklerin ötesinde
pythonql,future-typing,future-fstrings,future-annotationsgibi Python genişletmeleri ve backport'larda da kullanılabilir;magic_codecise tekrar eden işleri azaltır
Kaynak kodlamasını önişlem giriş noktası olarak kullanmak
- PEP-0263 uyarınca bir Python dosyasının ilk iki satırından birinde kaynak kodu kodlaması belirtilebilir
- Örnek:
# coding=utf8,# -*- coding: utf8 -*-,# vim: set fileencoding=utf8 :
- Örnek:
- Sihirli satır,
^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)düzenli ifadesiyle eşleşmelidir- Codec adı da
[-_.a-zA-Z0-9]+ile eşleşmelidir
- Codec adı da
- Kullanıcı tanımlı bir codec, yalnızca kaynağı decode etmekle kalmaz; kaynak dizesini değiştirip ardından Python yorumlayıcısına iletebilir
Codec'i .pth dosyasıyla kaydetmek
- Python yorumlayıcısı
-Sseçeneği olmadan başlatılırsasitepaketi başlatma sırasında yüklenir site-packagesiçindeki.pthyol yapılandırma dosyaları, boş satırlar ve#ile başlayan satırlar dışındaki içerikleri modül arama yoluna ekler- Python belgelerine göre
importile başlayıp ardından boşluk ya da tab gelen satırlar çalıştırılır- Örneğin
packagename.pthiçineimport packagename.register_codeckoyarsanız, bu modül başlatma sırasında import edilir
- Örneğin
- Import edilen modül, bir codec arama işlevini kaydetmek için
codecs.registerçağırabilir- Import yalnızca bir kez çalıştığı için arama işlevi de yalnızca bir kez kaydedilir
Kullanıcı tanımlı codec uygulaması
- Kullanıcı tanımlı bir codec için iki şey gerekir
decode(data: bytes) -> tuple[str, int]- artımlı decoder sınıfı
decodeişlevi, gerçek UTF-8 decode işleminicodecs.utf_8_decodeile yapar ve ortaya çıkan dizeyi önişleme işlevine aktarabilir- Codec içindeki istisnalar yakalanmazsa, normal traceback yerine yalnızca
SyntaxError: encoding problem: your_codecyazabilir- Önişleme işlevinde oluşan istisnaları
traceback.print_exc()ile doğrudan yazdırıp sonra tekrar raise etmek daha iyidir
- Önişleme işlevinde oluşan istisnaları
- Artımlı decoder, tüm dosyayı ara bellekte toplayıp yalnızca son
decodeçağrısında bir kez önişleme yapabilir- Örnek uygulama,
codecs.BufferedIncrementalDecodersınıfını miras alır vedecode(self, data, final=False)içinde yalnızcafinalolduğunda işlem yapar
- Örnek uygulama,
- Önişleme sonucu mutlaka özgün dosya içeriğini kullanmak zorunda değildir; tamamen keyfi bir Python kodu da döndürebilir
- Ancak ilk satır sihirli satır olarak beklendiği için silinir ve sonuç geçerli bir Python olmalıdır
Python sözdizimini genişletme örnekleri
-
++ve--artırım-azaltım operatörleri- Python'da tekli artırım-azaltım operatörü yoktur
x++,x--sözdizimsel olarak geçerli değildir++x,--xise sözdizimsel olarak geçerlidir ama sırasıylax.__pos__().__pos__()vex.__neg__().__neg__()çağrısına dönüşür- Önişlemci, token akışını değiştirerek bunların artırım-azaltım operatörü gibi davranmasını sağlayabilir
x++→(x, x := x + 1)[0]x--→(x, x := x - 1)[0]++x→(x, x := x + 1)[1]--x→(x, x := x - 1)[1]
- Bu dönüşüm, Python'un atama ifadesi olan walrus operator'u kullanır
- Yalnızca basit token değiştirme,
x++ - -ygibi ifadelerde başarısız olabilir;x++ - (-y)gibi parantezler belirsizliği azaltabilir - incdec.py düzenli ifadeyle değiştirme yapar, ancak dizge sabitleri içindeki değişiklikleri önlemeye çalışsa da kırılgan olabilir
- Token akışını doğrudan değiştiren uygulama magic.incdec içinde yer alır
-
Süslü parantez tabanlı Python
from __future__ import bracesifadesiSyntaxError: not a chanceüretir- Önişlemci, token akışını değiştirerek süslü parantez kapsamını girintiye dayalı Python'a çevirebilir
- Uygulama akışı şöyledir
- Token'lar
tokenize.generate_tokensile üretilir - Girdi dizesi,
io.StringIOiçindekireadlineile satır satır verilir - Mevcut
INDENT,DEDENTtoken'ları kaldırılır {görüldüğünde girinti seviyesi artırılır ve:yazdırılır}görüldüğünde girinti seviyesi azaltılırNLsonrasında mevcut girinti seviyesine uygunINDENTtoken'ı eklenir
- Token'lar
- Python sözlük sabitleriyle çakışmayı azaltmak için, yalnızca
{sonrasında newline varsa girinti seviyesini ayarlayıp yalnızca}öncesinde newline varsa bunu kapanan kapsam olarak yorumlamak mümkündür - Çok satırlı sözlükler, ters eğik çizgi kullanılırsa süslü parantez içinde newline token'ı oluşmayacağı için kullanılabilir
Başka dilleri Python üzerinden çalıştırmak
-
C ve C++
- Kabuk betikleri, CMake betikleri, PHP ve Ruby gibi
#ile yorum yazan dillerde, shebang ile birlikte kodlama sihirli satırını eklemek kolaydır - C ve C++'ta yorumlar
/* ... */veya// ...şeklindedir, ancak önişlem direktifleri#ile başladığı için kodlama düzenli ifadesine uyabilir - Örnek sihirli satır, hem C/C++ kaynağında geçerlidir hem de Python'un kodlama desenine uyar
#define CODEC "coding:magic.cpp"
- cppyy kullanıldığında Python içinde C/C++ kodu yorumlanıp bağlamalar üretilebilir
cppyybunu dahili olarakclingile yapar
- Önişleme sonucu kabaca şu Python koduna dönüşür
import cppyycppyy.cppdef("<input source file content>")from cppyy.gbl import main__name__ == "__main__"olduğunda C/C++ içindekimain()çağrılır
- Örnek uygulama magic.cpp içinde bulunur
- Kabuk betikleri, CMake betikleri, PHP ve Ruby gibi
TOML doğrulama aracı olarak kullanmak
- TOML, yorumları
#ile başlattığı için# coding: magic.tomlgibi bir kodlama sihirli satırı eklenebilir - Önişleme sonucunu bir Python doğrulama betiğine dönüştürerek Python yorumlayıcısı TOML doğrulama aracı gibi kullanılabilir
- Doğrulama örneğinde şu modüller kullanılır
- TOML dosyasını okumak için
tomllib - JSON Schema dosyasını okumak için
json - Doğrulama için jsonschema
- TOML dosyasını okumak için
- Çalıştırma örneği:
python tests/toml/data_valid.toml -s tests/toml/schema.json- Geçerliyse
Successfully validated.yazdırılır
- Hatalı TOML örneğinde,
scoresdizisi içindeki'20'dizgesinin sayı olmadığına dair doğrulama hatası yazdırılır - Örnek uygulama magic.toml içinde bulunur
Gerçek kullanım ve magic_codec
- Kullanıcı tanımlı codec ile
.pthdosyalarını birlikte kullanmak, Python yorumlayıcısının davranışını büyük ölçüde değiştirebilir - Örneklerin çoğu eğlence amaçlı olsa da gerçek kullanım örnekleri de vardır
- pythonql: Python için sorgu dili genişletmesi
- future-typing: Python 3.6+ için jenerik tip ipuçları ve
|union sözdizimini backport eder - future-fstrings
- future-annotations
site-packagesiçine doğrudan dokunmak veya.pthdosyalarını ve tekrar eden kodları elle yazmak istemiyorsanız magic_codec kullanabilirsinizmagic_codecgenişletmeleri,magic_öneki taşıyan Python paketleri olarak oluşturulabilir- Dosya codec'ini
magic_fooolarak ayarlarsanızmagic_foopaketi yüklenir - Bu pakette
preprocessişlevinin olup olmadığı denetlenir
- Dosya codec'ini
- Beklenen
preprocessimzası şöyledirdef preprocess(data: str) -> str:
- Genişletme örnekleri example/ içinde yer alır
1 yorum
Hacker News yorumları
from __future__ import bracesçalıştırıldığındaSyntaxError: not a chanceşeklinde çıkan bu komik hata mesajı, 2001’den beri CPython içinde sabit kodlanmış durumdaymışhttps://github.com/python/cpython/commit/ad3d3f2f3f19833f59f...
Bunu yazan Jeremy Hylton şu anda Google’da yapay zeka arama kalitesinden sorumlu Principal Engineer olarak çalışıyor; 24 yıl içinde yasaklı bir sözdizimini şakacı biçimde anmaktan, özel bir sözdizimi gerektirmeyen evrensel bir sorgu sistemine uzanan bir kariyer çizgisi epey etkileyici
break rust;ifadesinin Rust derleyicisinde iç derleyici hatası üretmesini hatırlattı. Diğer dillerde buna benzer kaç tane easter egg var merak ediyorumŞakacı ve gayriresmî hobi amaçlı hacking’in gerçek profesyonel geliştirmeden ayrı bir dünya olduğu düşüncesinin bir yanılgı olduğunu düşünüyorum
https://news.ycombinator.com/item?id=41314393
Import hook’larıyla şakalaşmanın işten atılmanın en yaratıcı yolu olduğunu sanıyordum ama bu safça bir düşünceymiş. Codec regex’i yüzünden
μtf8gibi şeylerle düzgün trollük yapamayacak olmak üzücü; artık import hook, önişlemci vesys.settracekullanıp tüm fonksiyonları hemen önce çağrılan fonksiyona monkey patch’lemek ve her 17 dakikada bir stdout ile stderr’i yer değiştirmekten başka çare yok gibiPython’ın önişlemci hook’unu bilerek açıkta bırakmamasının iyi nedenleri var ve aklı başında yetişkinlerin uzak durması gerektiğini düşündüğüm bir tarafı da var
Ama öte yandan aklı başında yetişkinlerle işim olsun istemem. Gerçekten çok eğlenceli şeyler yapılabilir gibi
public/privategörünürlük belirteçleri yok ve muhtemelen her tür metaprogramlama büyüsünü de açık bırakıyor. “Aklı başında yetişkinler” konusunda endişe edilseydi bu tasarım kararları biraz tuhaf olurdu :)Bu kullanışlı ve gerçekten faydalı görünüyor. Saçma import hack’leri yaparken genelde modülü import edip
astmodülüyle kodu yeniden yazar,execeder ve arayaexit()koyardım; önişlemci olursa kullanımı çok daha iyi olurTüm
dict’ler sıralamayı garanti etmeden önce,astyeniden yazımıyla liste literal’lerini ordered dict çağrılarına çevirmek için kullanıyordum ve gerçekten işe yarıyorduPython’ın esnekliğini seviyorum. Yaptığım en lanetli şey string’leri yerinde değiştirmekti; sonunda
mmap’i bile kötüye kullanıp kendi kendini değiştiren script yazmaya kadar gittim. Şimdi producer olarak bir Lisp yorumlayıcısı yazmam gerekiyormuş gibi hissediyorumctypesgibi bir şeyle doğrudan bellek konumuna mı yazdın?Şimdiye kadar bulduğum en iyi kullanım alanı, JSX’ten ilham alan pyxl: https://github.com/dropbox/pyxl
Şöyle kod yazılabiliyor
# coding: pyxlprint Hello World!Bunun Python 2’den 3’e geçişi daha iyi yönetmek için kullanılıp kullanılamayacağını merak ediyorum. Örneğin
# coding: six.python2Python 2 kodunu geçerli Python 3 koduna uyarlasa ya da# coding: six.python3Python 3 kodunu Python 2’de çalışacak şekilde dönüştürseb"..."veyau"..."öneklerini eklemek ya da kaldırmak da mümkün olabilirdidictanahtarı olarak kullanıldığında aynı öğeyi işaret ederdi; Python 3’te ise aynı ASCII içeriğe sahipbytesvestr, aynıdictiçinde farklı öğeleri işaret ederDaha zor değişiklikler de var.
.keys()ve.values()gibi birçok yerleşik özellik Python 2’de liste döndürürken Python 3’te iterator döndürür.sixyardımcıları veya başka geçici çözümlerle kodu güvenli şekilde çevirmeye çalışınca kod çok laf kalabalığına dönüşüyor. Çünkü çoğu şey yalnızca bir kez kullanılıyor ama bazen iki kez kullanıldığı durumlar oluyorImport sırasında kodu yeniden yazabilen bir araç varsa, bence dönüştürülmüş kodu commit edip kademeli olarak temizlemek daha iyi. Zor kısım,
strilebytesgibi uzaktaki kodları bile etkileyen davranış değişiklikleriBu coding hook stratejisi ile oluşan bağımlılığı
pip freezeveyauvyakalıyor mu?Yakalamıyorsa eğlenceli zamanlar yaşanacak demektir :). Birisi böyle bir şey koyduysa, başka hangi tuzakların olacağı neredeyse garanti; o ejderhalarla savaşmaktansa kütüphaneyi yeniden yazmak daha kolay gibi
Sözde kod Python yapıp bunu LLM’in çözmesini sağlamak epey komik olurdu. Elbette korkunç olur ama eğlenceli de olur