2025, Oct 17 17:31
pytest के तहत TMP बदलने पर tempfile नहीं बदलता: कारण और समाधान
Python में pytest चलाते समय TMP बदलने पर भी tempfile.gettempdir() क्यों नहीं बदलता? कैशिंग, TMPDIR/TEMP की प्राथमिकता और समाधान: प्रोसेस स्टार्ट पर env सेट करें.
जब रनटाइम पर TMP में बदलाव करने वाला कोड कमांड लाइन से ठीक चलता है, लेकिन pytest के तहत विफल हो जाता है, तो लगता है कि टेस्ट रनर वातावरण के साथ “छेड़छाड़” कर रहा है। असलियत इससे सरल और थोड़ी सूक्ष्म है: tempfile पहली बार उपयोग पर अस्थायी डायरेक्टरी को कैश कर लेता है, और आम तौर पर टेस्ट, आपकी टेस्ट फ़ंक्शन से पहले ही उसे ट्रिगर कर देते हैं।
न्यूनतम पुनरुत्पादन
यह स्निपेट मानता है कि TMP सेट करने से तुरंत tempfile.gettempdir() का रिटर्न बदल जाएगा:
import os
import tempfile
def check_tmp_under_test():
    os.environ["TMP"] = "/home/fedora"
    assert tempfile.gettempdir() == "/home/fedora"
    print(tempfile.gettempdir())
check_tmp_under_test()
इसे सीधे चलाएँ—आप /home/fedora देखेंगे। pytest से चलाएँ, तो यह /tmp प्रिंट कर सकता है या assertion पर त्रुटि दे सकता है। वही मशीन, वही Python, लेकिन नतीजा अलग।
वास्तव में क्या हो रहा है
मुख्य बात है tempfile मॉड्यूल के भीतर प्रोसेस-स्तरीय कैशिंग। प्रोसेस में tempfile.gettempdir() की पहली कॉल यह तय करती है कि कौन-सी डायरेक्टरी इस्तेमाल होगी, और वही मान प्रोसेस की पूरी अवधि के लिए कैश हो जाता है। उसके बाद os.environ बदलने से कैश्ड परिणाम पर असर नहीं पड़ता।
tempfile.gettempdir() की पहली कॉल के बाद पढ़ा गया मान कैश हो जाता है — यानी प्रोसेस में पहली कॉल के बाद आप os.environ बदलें भी, तो पहले लौटाया गया मान ही वापस मिलता रहेगा।
आप इसे एक इंटरैक्टिव सत्र में देख सकते हैं:
import tempfile, os
print(tempfile.gettempdir())  # उदाहरण: '/tmp'
os.environ["TMP"] = "/tmp/xis"
print(tempfile.gettempdir())  # अभी भी '/tmp', क्योंकि यह कैश कर लिया गया था
pytest के तहत, tempfile.gettempdir() की कॉल अक्सर आपकी टेस्ट फ़ंक्शन के चलने से पहले हो जाती है। यह टेस्ट रनर, किसी प्लगइन, या किसी भी ऐसे कोड से ट्रिगर हो सकती है जो अस्थायी फ़ाइल या डायरेक्टरी बनाता है—क्योंकि वे सभी रास्ते इसी कैश्ड मान का उपयोग करते हैं। एक बार कैश हो जाने पर, टेस्ट के भीतर TMP में बाद के बदलाव प्रभावी नहीं होते।
एक और बात ध्यान देने योग्य है। tempfile मॉड्यूल कई environment variables देखता है, और कुछ की प्राथमिकता दूसरों से अधिक होती है। TMPDIR और TEMP, TMP से ऊपर माने जाते हैं। अगर टेस्ट प्रोसेस में TMPDIR या TEMP सेट है, तो TMP बदलने से कुछ फर्क नहीं पड़ेगा।
आंतरिक रूप से, tempfile._candidate_tempdir_list() जैसे निजी हेल्पर मौजूद हैं जो हर कॉल पर मौजूदा वातावरण को दर्शाते हैं, लेकिन tempfile.gettempdir() और tempfile._gettempdir() दोनों प्रोसेस में पहली कॉल के बाद डायनेमिक रहना बंद कर देते हैं। प्रोसेस के बीच में उस कैश को रीसेट करने का कोई समर्थित तरीका नहीं है।
समाधान
टेस्ट प्रोसेस शुरू होने से पहले वातावरण को निर्धारक बना लें। pytest लॉन्च करते समय OS environment में इच्छित अस्थायी डायरेक्टरी सेट करें—टेस्ट फ़ंक्शन के अंदर नहीं। इससे प्रोसेस में tempfile की पहली कॉल सही मान देखेगी, और कैश्ड परिणाम आपकी अपेक्षा से मेल खाएगा। साथ ही, यह भी सुनिश्चित करें कि TMPDIR या TEMP जैसे उच्च-प्राथमिकता वाले वेरिएबल किसी विरोधाभासी मान पर सेट न हों।
export TMP=/home/fedora
uv run pytest pytest.py
प्रोसेस स्टार्ट पर TMP सेट होने के साथ, tempfile.gettempdir() पहली बार में /home/fedora को कैश कर लेगा, और सीधे रन तथा pytest—दोनों में आपकी assertions मेल खाएँगी।
यह क्यों मायने रखता है
टेस्ट लंबे समय तक चलने वाले प्रोसेस में रहते हैं, जो आपके कोड से ज़्यादा काम करते हैं। रनर्स, प्लगइन्स और यूटिलिटीज अक्सर शुरुआत में ही tempfile को छू लेते हैं, और कैशिंग के कारण वही शुरुआती स्पर्श आगे आने वाली हर चीज़ के लिए temp डायरेक्टरी तय कर देता है। टेस्ट के बीच में temp पाथ के लिए os.environ बदलने पर भरोसा करना नाज़ुक है और गैर-निर्धारक विफलताओं की ओर ले जाता है।
निष्कर्ष
यदि आपको एक विशेष अस्थायी डायरेक्टरी चाहिए, तो Python प्रोसेस शुरू होने से पहले TMP सेट करें। याद रखें कि TMPDIR और TEMP, TMP को ओवरराइड करते हैं, इसलिए जाँच लें कि टेस्ट वातावरण में वे किसी और मान पर सेट न हों। यह उम्मीद न करें कि टेस्ट के दौरान os.environ["TMP"] बदलने से tempfile.gettempdir() अपडेट हो जाएगा; एक बार कैश होने के बाद, यह मान प्रोसेस की पूरी अवधि तक स्थिर रहता है। यदि आप बिना कैश को प्रभावित किए इस क्षण tempfile किन विकल्पों पर विचार करेगा यह देखना चाहते हैं, तो निजी यूटिलिटीज के जरिए डायनेमिक कैंडिडेट सूची देख सकते हैं, मगर कैश्ड मान को रीसेट करने का कोई समर्थित तरीका नहीं है—इसलिए प्रोसेस स्टार्ट पर वातावरण नियंत्रित करना ही भरोसेमंद रास्ता है।
यह लेख StackOverflow पर इस प्रश्न (लेखक: Paul Dejean) और jsbueno के उत्तर पर आधारित है।