2025, Nov 01 21:31
Python फ़ाइल को REPL सत्र की तरह चलाने का व्यावहारिक तरीका
जानें कैसे Python फ़ाइल को REPL सत्र जैसा चलाएँ: code.InteractiveConsole से लाइन‑दर‑लाइन रन, >>>/… प्रॉम्प्ट्स और stdout कैप्चर के साथ भरोसेमंद ट्रांस्क्रिप्ट
एक Python फ़ाइल चलाना अक्सर असली REPL सत्र जैसा नहीं लगता। स्क्रिप्ट मोड में इंटरप्रेटर प्रॉम्प्ट्स छोड़ देता है, अभिव्यक्तियों के परिणाम वापस नहीं दिखाता, और वह इंटरैक्टिव प्रवाह गायब हो जाता है जिस पर कई डेवलपर और टूल निर्भर रहते हैं। कभी‑कभी आपको उल्टा चाहिए होता है: फ़ाइल को Python में दें और वही आउटपुट पाएँ जो हाथ से चलाए गए इंटरैक्टिव सत्र जैसा लगे—>>> और ... प्रॉम्प्ट्स सहित, साथ ही अभिव्यक्ति के इको भी।
ऐसा उदाहरण इनपुट जो REPL सत्र की तरह बर्ताव करे
मान लीजिए एक साधारण Python फ़ाइल है जिसे आप ऐसे चलाना चाहते हैं जैसे इंटरैक्टिव शेल में लाइन‑दर‑लाइन टाइप किया गया हो:
n = 5
n + 8
if n < 4:
print("123")
else:
print("xyz")
exit()असल में होता क्या है, और यह मुश्किल क्यों है
इंटरप्रेटर के दो अलग‑अलग मोड होते हैं। इंटरैक्टिव/REPL मोड में वह प्रॉम्प्ट प्रिंट करता है, नंगी अभिव्यक्तियों का मान निकालकर दिखाता है, और बहु‑पंक्ति स्टेटमेंट्स को कंटिन्यूएशन प्रॉम्प्ट्स के साथ संभालता है। स्क्रिप्ट मोड में वह बिना प्रॉम्प्ट्स और बिना एक्सप्रेशन वैल्यू इको किए फ़ाइल चला देता है। साधारण कमांड‑लाइन विकल्प किसी स्क्रिप्ट को असली REPL ट्रांस्क्रिप्ट में नहीं बदलते।
इस इंटरैक्टिव व्यवहार की नकल प्रोग्राम से सीधे की जा सकती है। code मॉड्यूल एक InteractiveConsole देता है जो कोड को लाइन‑दर‑लाइन लेता है और REPL की तरह मूल्यांकन करता है। अपनी फ़ाइल को उसी कंसोल में खिलाकर, खुद प्रॉम्प्ट्स प्रिंट करके, और stdout कैप्चर करके, आप वैसा ही ट्रांस्क्रिप्ट बना सकते हैं जैसा अपेक्षित है। अगर आप सोच रहे हैं कि doctest कुछ ऐसा करता है या नहीं, तो वह example.source को exec(compile(example.source)) से चलाता है, जो पूर्ण REPL ट्रांस्क्रिप्ट जनरेटर नहीं है।
समाधान: code.InteractiveConsole चलाएँ और stdout कैप्चर करें
तरीका यह है: फ़ाइल पढ़ें, हर पंक्ति InteractiveConsole में भेजें, अपने स्तर पर >>> और ... प्रॉम्प्ट्स दिखाएँ, और इंटरप्रेटर जो भी प्रिंट करे उसे कैप्चर करें। नीचे दिया स्निपेट ... से शुरू होने वाली पंक्तियों को भी समझता है, इसलिए यदि आप बहु‑पंक्ति ब्लॉक्स के लिए इंटरैक्टिव इनपुट फ़ॉर्मैटिंग की नकल करना चाहें, तो स्रोत में कंटिन्यूएशन मार्कर डाल सकते हैं।
import code
import sys
import io
from contextlib import redirect_stdout
def emulate_repl_session(path_to_file):
# फ़ाइल को मेमोरी में पढ़ें
with open(path_to_file, 'r') as fh:
rows = fh.readlines()
# लाइन एंडिंग सामान्य करें और खाली पंक्तियों का व्यवहार पूर्वानुमेय रखें
rows = [r.rstrip('\n') for r in rows]
# REPL बैनर (छापना वैकल्पिक है, चाहें तो छोड़ दें)
print(f"Python {sys.version} on {sys.platform}")
print('Type "help", "copyright", "credits" or "license" for more information.')
# एक इंटरैक्टिव कंसोल बनाएँ
interp = code.InteractiveConsole()
# मल्टी‑लाइन इनपुट के लिए बफ़र और स्थिति
chunk = []
awaiting = False
for row in rows:
# सामान्य REPL टाइपिंग से मेल खाने के लिए खाली पंक्तियों को नज़रअंदाज़ करें
if not row.strip():
continue
# '...' से शुरू होने वाली कंटिन्यूएशन पंक्तियाँ
if row.strip().startswith('...'):
chunk.append(row.replace('...', '', 1).lstrip())
continue
# अगर हम एक मल्टी‑लाइन ब्लॉक इकट्ठा कर रहे थे, तो उसे फ्लश कर के चलाएँ
if awaiting:
suite = '\n'.join(chunk)
print(f'>>> {chunk[0]}')
for frag in chunk[1:]:
print(f'... {frag}')
with io.StringIO() as stream, redirect_stdout(stream):
cont = interp.push(suite)
emitted = stream.getvalue()
if emitted:
print(emitted, end='')
chunk = []
awaiting = cont
if row.strip() == 'exit()':
break
if not awaiting:
continue
# स्पष्ट सत्र समाप्ति को संभालें
if row.strip() == 'exit()':
print('>>> exit()')
break
# प्रॉम्प्ट और इनपुट पंक्ति प्रिंट करें
print(f'>>> {row}')
# अगर पंक्ति किसी suite को खोलती है, तो ब्लॉक इकट्ठा करना शुरू करें
if row.rstrip().endswith(':'):
chunk.append(row)
awaiting = True
continue
# एक पंक्ति चलाएँ और जो भी प्रिंट हो उसे कैप्चर करें
with io.StringIO() as stream, redirect_stdout(stream):
cont = interp.push(row)
emitted = stream.getvalue()
if emitted:
print(emitted, end='')
awaiting = cont
if __name__ == '__main__':
if len(sys.argv) != 2:
print('Usage: python repl_emulator.py <script_path>')
sys.exit(1)
emulate_repl_session(sys.argv[1])यह क्यों काम करता है
InteractiveConsole कोड के टुकड़ों को उसी तरह चलाती है जैसे REPL, जिसमें वे मूल्यांकन नियम भी शामिल हैं जो नंगी अभिव्यक्तियों का आउटपुट दिखाते हैं। आप प्रॉम्प्ट्स नियंत्रित करते हैं और हर टुकड़ा push के ज़रिए भेजते हैं, तो उपयोगकर्ता‑मुखी ट्रांस्क्रिप्ट दोबारा बन जाता है। stdout को contextlib.redirect_stdout से कैप्चर किया जाता है और फिर ज्यों‑का‑त्यों प्रिंट किया जाता है, इसलिए इंटरप्रेटर जो भी निकालता है, वह ठीक वहीं दिखता है जहाँ एक लाइव सत्र में इंसान उम्मीद करेगा।
यह जानना क्यों उपयोगी है
दोहराए जा सकने वाले REPL ट्रांस्क्रिप्ट ऑटोमेशन, डेमो या वेरिफ़िकेशन को बहुत सरल बना देते हैं। लाइव टर्मिनल से कॉपी‑पेस्ट करने के बजाय, आप एक सादा .py फ़ाइल रखें और जब भी व्यवहार साफ़‑सुथरे और एक‑सा रूप में दिखाना हो, वही इंटरैक्टिव‑दिखने वाला आउटपुट फिर से बना लें। अगर आपके इनपुट में पहले से ... से शुरू होने वाली कंटिन्यूएशन पंक्तियाँ हैं, तो प्रवाह स्वाभाविक और पठनीय रहता है, फिर भी निर्धार्य (deterministic) बना रहता है।
समापन
जब आपको किसी स्क्रिप्ट का निष्पादन असली इंटरैक्टिव सत्र जैसा दिखाना हो, तो code.InteractiveConsole और उसके चारों ओर एक छोटा‑सा हार्नेस अपना लें। फ़ाइल को पंक्ति‑दर‑पंक्ति भेजें, प्रॉम्प्ट्स प्रिंट करें, stdout कैप्चर करें, और बाकी इंटरप्रेटर पर छोड़ दें। स्रोत हल्का रखें, पहले से >>> मार्कर डालने की बजाय सादी Python स्टेटमेंट्स को प्राथमिकता दें, और ... का इस्तेमाल तभी करें जब आप जानबूझकर मल्टी‑लाइन एंट्री की नकल कर रहे हों। इस तरह आपको REPL शैली का भरोसेमंद ट्रांस्क्रिप्ट मिलता है—बिना कमांड‑लाइन फ़्लैग्स से जूझे या मूल्यांकन नियमों को फिर से गढ़े।
यह लेख StackOverflow के प्रश्न पर आधारित है, जिसे Thomas Weise ने पूछा, तथा Tushar Neupaney के उत्तर पर।