पायथन फ़ंक्शन कॉल से स्टडआउट आउटपुट कैसे कैप्चर करें?


112

मैं एक पायथन लाइब्रेरी का उपयोग कर रहा हूं जो किसी वस्तु के लिए कुछ करता है

do_something(my_object)

और इसे बदल देता है। ऐसा करते समय, यह कुछ आंकड़ों को प्रिंट करने के लिए प्रिंट करता है, और मैं इस जानकारी पर पकड़ बनाना चाहता हूं। do_something()संबंधित जानकारी वापस करने के लिए बदलने के लिए उचित समाधान होगा ,

out = do_something(my_object)

लेकिन यह do_something()इस मुद्दे को पाने के देवता से पहले एक समय होगा । वर्कअराउंड के रूप में, मैंने स्टडआउट को जो कुछ भी do_something()लिखा है उसे पार्स करने के बारे में सोचा ।

मैं कोड में दो बिंदुओं के बीच स्टडआउट आउटपुट कैसे ले सकता हूं, जैसे,

start_capturing()
do_something(my_object)
out = end_capturing()

?



जुड़े हुए प्रश्न में मेरा उत्तर यहाँ भी लागू होता है।
मार्टिन पीटर्स

मैंने एक बार ऐसा करने की कोशिश की और मुझे जो सबसे अच्छा जवाब मिला वह था: stackoverflow.com/a/3113913/1330293
elyase

@ हेलीसे जुड़ा हुआ जवाब एक सुंदर समाधान है
पाइकलर

जवाबों:


183

इस संदर्भ प्रबंधक का प्रयास करें:

from io import StringIO 
import sys

class Capturing(list):
    def __enter__(self):
        self._stdout = sys.stdout
        sys.stdout = self._stringio = StringIO()
        return self
    def __exit__(self, *args):
        self.extend(self._stringio.getvalue().splitlines())
        del self._stringio    # free up some memory
        sys.stdout = self._stdout

उपयोग:

with Capturing() as output:
    do_something(my_object)

output अब एक सूची है जिसमें फ़ंक्शन कॉल द्वारा मुद्रित लाइनें हैं।

उन्नत उपयोग:

यह स्पष्ट नहीं हो सकता है कि यह एक से अधिक बार किया जा सकता है और परिणाम संक्षिप्त हो सकते हैं:

with Capturing() as output:
    print('hello world')

print('displays on screen')

with Capturing(output) as output:  # note the constructor argument
    print('hello world2')

print('done')
print('output:', output)

आउटपुट:

displays on screen                     
done                                   
output: ['hello world', 'hello world2']

अद्यतन : वे पायथन 3.4 (साथ में ) में जोड़े redirect_stdout()गए । तो आप इसके साथ एक समान परिणाम प्राप्त करने के लिए उपयोग कर सकते हैं (हालांकि एक सूची के साथ-साथ एक संदर्भ प्रबंधक यकीनन अधिक सुविधाजनक है)।contextlibredirect_stderr()io.StringIOCapturing


धन्यवाद! और उन्नत अनुभाग जोड़ने के लिए धन्यवाद ... मैंने मूल रूप से सूची में कैप्चर किए गए पाठ को चिपकाने के लिए एक स्लाइस असाइनमेंट का उपयोग किया था, फिर मैंने खुद को सिर में बांधा और इसका इस्तेमाल .extend()किया, इसलिए इसे ध्यान से इस्तेमाल किया जा सकता था, जैसा आपने देखा। :-)
kindall

पुनश्च यदि इसका बार-बार उपयोग किया जा रहा है, तो मैं सुझाव दूंगा कि सदस्य द्वारा रखी गई कुछ यादों को जारी करने की विधि में कॉल के self._stringio.truncate(0)बाद जोड़ें । self.extend()__exit__()_stringio
मार्टीन्यू

25
शानदार जवाब, धन्यवाद। पायथन 3 के लिए, from io import StringIOसंदर्भ प्रबंधक में पहली पंक्ति के बजाय उपयोग करें ।
Wtower

1
क्या यह धागा सुरक्षित है? यदि do_something रन होते हैं तो कुछ अन्य थ्रेड / कॉल प्रिंट () का उपयोग करता है तो क्या होता है?
डोरिस्ट्रिन

1
यह उत्तर C साझा पुस्तकालयों से आउटपुट के लिए काम नहीं करेगा, इस उत्तर को इसके बजाय देखें।
क्रेयमिकेल

81

अजगर में = = 3.4 पर, रेफ़रलिब में redirect_stdoutडेकोरेटर होता है । इसका उपयोग आपके प्रश्न का उत्तर देने के लिए किया जा सकता है:

import io
from contextlib import redirect_stdout

f = io.StringIO()
with redirect_stdout(f):
    do_something(my_object)
out = f.getvalue()

से डॉक्स :

किसी अन्य फ़ाइल या फ़ाइल जैसी ऑब्जेक्ट के लिए sys.stdout को अस्थायी रूप से पुनर्निर्देशित करने के लिए संदर्भ प्रबंधक।

यह उपकरण मौजूदा फ़ंक्शंस या उन कक्षाओं में लचीलेपन को जोड़ता है जिनके आउटपुट को स्टडआउट के लिए हार्डवार्ड किया जाता है।

उदाहरण के लिए, सहायता का उत्पादन () सामान्य रूप से sys.stdout को भेजा जाता है। आप उस आउटपुट को एक io.StringIO ऑब्जेक्ट में आउटपुट को रीडायरेक्ट करके एक स्ट्रिंग में कैप्चर कर सकते हैं:

  f = io.StringIO() 
  with redirect_stdout(f):
      help(pow) 
  s = f.getvalue()

डिस्क पर एक फ़ाइल को मदद () की आउटपुट भेजने के लिए, आउटपुट को एक नियमित फ़ाइल पर पुनर्निर्देशित करें:

 with open('help.txt', 'w') as f:
     with redirect_stdout(f):
         help(pow)

Sys.stderr को मदद () का आउटपुट भेजने के लिए:

with redirect_stdout(sys.stderr):
    help(pow)

ध्यान दें कि sys.stdout पर वैश्विक दुष्प्रभाव का मतलब है कि यह संदर्भ प्रबंधक लाइब्रेरी कोड और अधिकांश थ्रेडेड एप्लिकेशन में उपयोग के लिए उपयुक्त नहीं है। उपप्रकारों के उत्पादन पर भी इसका कोई प्रभाव नहीं है। हालांकि, यह अभी भी कई उपयोगिता लिपियों के लिए एक उपयोगी दृष्टिकोण है।

यह संदर्भ प्रबंधक प्रतिवादी है।


जब कोशिश की f = io.StringIO() with redirect_stdout(f): logger = getLogger('test_logger') logger.debug('Test debug message') out = f.getvalue() self.assertEqual(out, 'DEBUG:test_logger:Test debug message')। यह मुझे एक त्रुटि देता है:AssertionError: '' != 'Test debug message'
Eziz Durdyyev

जिसका मतलब है कि मैंने कुछ गलत किया है या यह स्टडआउट लॉग नहीं पकड़ सका।
एजिज़ दुर्येव

@EzizDurdyyev, logger.debugडिफ़ॉल्ट रूप से stdout को नहीं लिखता है। यदि आप अपने लॉग कॉल को बदलते हैं तो आपको print()संदेश देखना चाहिए।
फॉरएवरविंटर

हाँ, मुझे पता है, लेकिन मैं इसे इस तरह लिखने के लिए लिखता हूँ stream_handler = logging.StreamHandler(sys.stdout):। और उस हैंडलर को मेरे लकड़हारे से जोड़ दें। इसलिए इसे लिखने के लिए लिखना चाहिए और redirect_stdoutइसे पकड़ना चाहिए, है ना?
एजिज़ दुर्यदेव

मुझे संदेह है कि समस्या आपके लॉगर को कॉन्फ़िगर करने के तरीके से है। मैं यह सत्यापित करूंगा कि यह रीडायरेक्ट_स्टडआउट के बिना प्रिंट करने के लिए प्रिंट करता है। यदि ऐसा होता है, तो संदर्भ प्रबंधक के बाहर निकलने तक बफर को फ्लश नहीं किया जा सकता है।
फॉरएवरविंटर

0

यहाँ फ़ाइल पाइप का उपयोग कर एक async समाधान है।

import threading
import sys
import os

class Capturing():
    def __init__(self):
        self._stdout = None
        self._stderr = None
        self._r = None
        self._w = None
        self._thread = None
        self._on_readline_cb = None

    def _handler(self):
        while not self._w.closed:
            try:
                while True:
                    line = self._r.readline()
                    if len(line) == 0: break
                    if self._on_readline_cb: self._on_readline_cb(line)
            except:
                break

    def print(self, s, end=""):
        print(s, file=self._stdout, end=end)

    def on_readline(self, callback):
        self._on_readline_cb = callback

    def start(self):
        self._stdout = sys.stdout
        self._stderr = sys.stderr
        r, w = os.pipe()
        r, w = os.fdopen(r, 'r'), os.fdopen(w, 'w', 1)
        self._r = r
        self._w = w
        sys.stdout = self._w
        sys.stderr = self._w
        self._thread = threading.Thread(target=self._handler)
        self._thread.start()

    def stop(self):
        self._w.close()
        if self._thread: self._thread.join()
        self._r.close()
        sys.stdout = self._stdout
        sys.stderr = self._stderr

उदाहरण का उपयोग:

from Capturing import *
import time

capturing = Capturing()

def on_read(line):
    # do something with the line
    capturing.print("got line: "+line)

capturing.on_readline(on_read)
capturing.start()
print("hello 1")
time.sleep(1)
print("hello 2")
time.sleep(1)
print("hello 3")
capturing.stop()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.