Python का प्रीप्रोसेसर
(pydong.org)- Python पहली दो लाइनों की source encoding magic comment और custom codec का उपयोग करके, फ़ाइल की सामग्री को execution से पहले बदल सकता है या उसे पूरी तरह अलग code से replace कर सकता है
- custom codec को
.pthpath configuration file मेंimportexecute करके interpreter initialization के दौरान register किया जा सकता है, औरcodecs.registerसे search function जोड़ा जाता है - codec implementation के लिए
decode(data: bytes) -> tuple[str, int]और incremental decoder की जरूरत होती है; exception handling न हो तो असली कारण की जगह सिर्फSyntaxError: encoding problem: your_codecदिख सकता है ++/--increment/decrement operator, curly braces आधारित Python,cppyyके जरिए C/C++ execution, और TOML को JSON Schema से validate करने का तरीका—ये सब उसी entry point से implement किए जा सकते हैं- मज़ाकिया examples से आगे बढ़कर, इसका उपयोग
pythonql,future-typing,future-fstrings,future-annotationsजैसे Python extensions और backports में भी हो सकता है;magic_codecrepetitive काम कम करता है
source encoding को preprocessing entry point के रूप में इस्तेमाल करना
- PEP-0263 के अनुसार Python file की पहली दो lines में से किसी एक में source code encoding specify की जा सकती है
- उदाहरण:
# coding=utf8,# -*- coding: utf8 -*-,# vim: set fileencoding=utf8 :
- उदाहरण:
- magic line को regex
^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)से match होना चाहिए- codec name को
[-_.a-zA-Z0-9]+से match होना चाहिए
- codec name को
- custom codec सिर्फ source को decode करने तक सीमित नहीं रहता; वह source string को बदलकर Python interpreter को पास कर सकता है
.pth file से codec register करना
- अगर Python interpreter
-Soption के बिना start होता है, तो initialization के दौरानsitepackage load होता है site-packagesके अंदर.pthpath configuration file खाली lines और#से शुरू होने वाली lines को छोड़कर बाकी content को module search path में जोड़ती है- Python documentation के अनुसार, जिन lines में
importके बाद space या tab आता है वे execute होती हैं- उदाहरण:
packagename.pthमेंimport packagename.register_codecडालने पर initialization के दौरान वह module import हो जाता है
- उदाहरण:
- imported module
codecs.registercall करके codec search function register कर सकता है- import सिर्फ एक बार execute होता है, इसलिए search function भी सिर्फ एक बार register होता है
custom codec implementation
- custom codec के लिए दो चीजें चाहिए
decode(data: bytes) -> tuple[str, int]- incremental decoder class
decodefunction वास्तविक UTF-8 decodingcodecs.utf_8_decodeसे कर सकता है और result string को preprocessing function को दे सकता है- अगर codec के अंदर की exceptions catch नहीं होतीं, तो सामान्य traceback की जगह सिर्फ
SyntaxError: encoding problem: your_codecoutput हो सकता है- preprocessing function में आई exception को
traceback.print_exc()से सीधे print करके फिर से raise करना बेहतर है
- preprocessing function में आई exception को
- incremental decoder पूरी file को buffer में जमा रख सकता है और आखिरी
decodecall में सिर्फ एक बार preprocess कर सकता है- example implementation
codecs.BufferedIncrementalDecoderको inherit करता है औरdecode(self, data, final=False)में सिर्फfinalहोने पर process करता है
- example implementation
- preprocessing result को original file content का इस्तेमाल करना जरूरी नहीं है; पूरी तरह arbitrary Python code भी return किया जा सकता है
- हालांकि पहली line को magic line माना जाता है इसलिए वह delete होती है, और result valid Python होना चाहिए
Python syntax extension examples
-
++और--increment/decrement operators- Python में unary increment/decrement operator नहीं है
x++,x--syntactically valid नहीं हैं++x,--xsyntactically valid तो हैं, लेकिन वे क्रमशःx.__pos__().__pos__(),x.__neg__().__neg__()calls बन जाते हैं- preprocessor token stream बदलकर उन्हें increment/decrement operator जैसा behave करा सकता है
x++→(x, x := x + 1)[0]x--→(x, x := x - 1)[0]++x→(x, x := x + 1)[1]--x→(x, x := x - 1)[1]
- यह transformation Python के assignment expression, यानी walrus operator का उपयोग करता है
- सिर्फ simple token replacement से
x++ - -yजैसे expression में fail हो सकता है;x++ - (-y)की तरह parentheses से ambiguity घटाई जा सकती है - incdec.py regex से replace करता है, लेकिन string literal के अंदर replacement से बचने की कोशिश के बावजूद fragile हो सकता है
- token stream को सीधे modify करने वाला implementation magic.incdec में है
-
curly braces आधारित Python
from __future__ import bracesSyntaxError: not a chanceraise करता है- preprocessor token stream modify करके curly braces scope को indentation-based Python में बदल सकता है
- implementation flow इस प्रकार है
tokenize.generate_tokensसे tokens generate करता है- input string को
io.StringIOकेreadlineके जरिए line-by-line provide करता है - existing
INDENT,DEDENTtokens remove करता है {मिलने पर indentation level बढ़ाता है और:output करता है}मिलने पर indentation level घटाता हैNLके बाद current indentation level के अनुसारINDENTtoken जोड़ता है
- Python के dictionary literal से conflict घटाने के लिए, सिर्फ
{के बाद newline होने पर indentation level adjust किया जा सकता है, और सिर्फ}से पहले newline होने पर closing scope की तरह treat किया जा सकता है - multi-line dictionary में backslash का उपयोग करने पर curly braces के अंदर newline token नहीं बनता, इसलिए उसका उपयोग किया जा सकता है
दूसरी languages को Python से execute करना
-
C और C++
- shell scripts, CMake scripts, PHP, Ruby जैसी languages जिनमें
#से comments लिखे जाते हैं, उनमें shebang के साथ encoding magic line डालना आसान है - C और C++ में comments
/* ... */या// ...होते हैं, लेकिन preprocessor directives#से शुरू होते हैं, इसलिए उन्हें encoding regex से match कराया जा सकता है - example magic line C/C++ source में भी valid है और Python के encoding pattern से भी match होती है
#define CODEC "coding:magic.cpp"
- cppyy का उपयोग करके Python में C/C++ code interpret किया जा सकता है और bindings generate किए जा सकते हैं
cppyyinternallyclingका उपयोग करता है
- preprocessing result roughly नीचे दिए Python code जैसा बनता है
import cppyycppyy.cppdef("<input source file content>")from cppyy.gbl import main__name__ == "__main__"होने पर C/C++ काmain()call
- example implementation magic.cpp में है
- shell scripts, CMake scripts, PHP, Ruby जैसी languages जिनमें
TOML validation tool के रूप में उपयोग करना
- TOML में comments
#से शुरू होते हैं, इसलिए# coding: magic.tomlजैसी encoding magic line डाली जा सकती है - preprocessing result को Python validation script में बदलने पर Python interpreter को TOML validation tool की तरह इस्तेमाल किया जा सकता है
- validation example नीचे दिए modules का उपयोग करता है
tomllibसे TOML file पढ़ता हैjsonसे JSON Schema file पढ़ता है- jsonschema से validate करता है
- execution example:
python tests/toml/data_valid.toml -s tests/toml/schema.json- valid होने पर
Successfully validated.output करता है
- गलत TOML example में
scoresarray के अंदर string'20'number नहीं है—यह validation error output होता है - implementation example magic.toml में है
वास्तविक उपयोग और magic_codec
- custom codec और
.pthfile को साथ इस्तेमाल करने पर Python interpreter का behavior काफी बदला जा सकता है - ज्यादातर examples मज़े के लिए हैं, लेकिन real-world use cases भी हैं
- pythonql: Python के लिए query language extension
- future-typing: Python 3.6+ में generic type hints और
|union syntax का backport - future-fstrings
- future-annotations
- अगर आप
site-packagesको सीधे modify नहीं करना चाहते या.pthfiles और repetitive code खुद नहीं लिखना चाहते, तो magic_codec इस्तेमाल कर सकते हैं magic_codecextension कोmagic_prefix वाले Python package के रूप में बनाया जा सकता है- file codec को
magic_fooset करने परmagic_foopackage load होता है - यह check करता है कि उस package में
preprocessfunction है या नहीं
- file codec को
- expected
preprocesssignature इस प्रकार हैdef preprocess(data: str) -> str:
- extension example example/ में है
1 टिप्पणियां
Hacker News की राय
from __future__ import bracesचलाने परSyntaxError: not a chanceवाला यह मज़ेदार error message CPython में 2001 से hardcoded थाhttps://github.com/python/cpython/commit/ad3d3f2f3f19833f59f...
इसे लिखने वाले Jeremy Hylton अब Google में AI search quality के Principal Engineer हैं; 24 सालों में उनका करियर प्रतिबंधित syntax को मज़ाकिया ढंग से याद करने से लेकर ऐसे universal query system तक आ गया जिसे किसी dedicated syntax की ज़रूरत नहीं—यह काफी प्रभावशाली है
break rust;से Rust compiler में internal compiler error आने वाली बात याद आ गई। सोचता हूँ दूसरी languages में ऐसे कितने Easter eggs होंगेमुझे लगता है यह गलतफहमी है कि playful, अनौपचारिक hobby hacking और असली professional development अलग-अलग दुनिया हैं
https://news.ycombinator.com/item?id=41314393
मुझे लगता था import hook के साथ शरारत करना नौकरी से निकाले जाने का सबसे creative तरीका है, लेकिन वह मेरी मासूम सोच थी। अफसोस है कि codec regex की वजह से
μtf8जैसी चीज़ से ठीक से trolling नहीं कर पाऊँगा, तो अब शायद import hook, preprocessor औरsys.settraceका इस्तेमाल करके हर function को उसके ठीक पहले call हुए function से monkey-patch करना और हर 17 मिनट में stdout और stderr को आपस में बदलना ही रास्ता बचेगाPython ने preprocessor hooks को जानबूझकर expose नहीं किया, इसके अच्छे कारण हैं, और मुझे लगता है कि sensible adults को इससे दूर रहना चाहिए
लेकिन दूसरी तरफ, मैं sensible adults के साथ उलझना भी नहीं चाहता। इससे सचमुच बहुत मज़ेदार चीज़ें की जा सकती हैं
public/privatevisibility specifiers नहीं हैं और शायद हर तरह का metaprogramming magic भी खुला छोड़ रखा है। अगर “sensible adults” की चिंता होती तो ये design decisions थोड़े अजीब लगते :)यह सुविधाजनक और वाकई उपयोगी लगता है। बेवकूफी भरे import hacks करते समय मैं आम तौर पर module import करके
astmodule से code rewrite करता था, फिरexecकरता था और बीच मेंexit()डाल देता था; preprocessor हो तो यह बहुत ज्यादा usable होगाजब तक सभी
dictorder guarantee नहीं करते थे, तब तक मैंastrewriting का इस्तेमाल मुख्यतः list literals को ordered dict calls में बदलने के लिए करता था, और यह सच में useful थामुझे Python की flexibility पसंद है। मैंने जो सबसे cursed काम किया, वह strings को in-place बदलना था, और आखिर में
mmapतक abuse करके खुद को बदलने वाली script लिख डाली। अब लगता है producer के रूप में Lisp interpreter लिखना पड़ेगाctypesजैसी किसी चीज़ से memory location पर directly लिखा था?अब तक मिला सबसे अच्छा use case JSX से inspired pyxl है: https://github.com/dropbox/pyxl
ऐसा code लिखा जा सकता है
# coding: pyxlprint Hello World!सोचता हूँ क्या इसका इस्तेमाल Python 2 से 3 में transition को बेहतर संभालने के लिए किया जा सकता था। जैसे
# coding: six.python2Python 2 code को valid Python 3 code में ढाल दे, या# coding: six.python3Python 3 code को Python 2 पर चलने लायक बदल देb"..."याu"..."prefixes जोड़ना या हटाना भी संभव रहा होगाdictkey के रूप में इस्तेमाल करने पर वे same item की ओर इशारा करते थे, लेकिन Python 3 में same ASCII content वालेbytesऔरstrएक हीdictमें अलग-अलग items की ओर इशारा करते हैंऔर भी tricky changes हैं।
.keys()और.values()जैसी कई built-in सुविधाएँ Python 2 में list return करती हैं, लेकिन Python 3 में iterator return करती हैं।sixutilities या दूसरे workarounds से code को safely translate करने की कोशिश करें तो code बहुत verbose हो जाता है, क्योंकि ज्यादातर चीज़ें एक बार ही use होती हैं, लेकिन कभी-कभी दो बार भी use होती हैंअगर import के समय code rewrite करने वाला tool हो, तो मेरी राय में transformed code को commit करके धीरे-धीरे clean up करना बेहतर है। मुश्किल हिस्सा
strबनामbytesजैसे behavior changes हैं जो दूर-दूर के code तक असर डालते हैंइस coding hook strategy से बनी dependencies को
pip freezeयाuvपकड़ते हैं क्या?अगर नहीं, तो मज़ेदार समय आने वाला है :). अगर किसी ने ऐसी चीज़ डाल रखी है, तो लगभग तय है कि और भी जाल होंगे; ऐसे dragons से लड़ने के बजाय library दोबारा लिखना शायद आसान होगा
pseudocode Python बनाकर उसे LLM से decode करवाया जाए तो काफी मज़ेदार होगा। जाहिर है भयानक होगा, लेकिन मज़ा तो आएगा