装饰\委托一个File对象来添加功能 [英] Decorate \ delegate a File object to add functionality

查看:106
本文介绍了装饰\委托一个File对象来添加功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在编写一个小的Python脚本,它使用子进程模块和辅助函数执行一些shell命令:


$ b $
$ b $$$$$$$$$$$$$$$
start = datetime.datetime.now()
sys.stderr.write('% - 65s'%description)
s = sp.Popen(command,shell = True,stderr = sp.PIPE,stdout = sp.PIPE)
out,err = s.communicate()
end = datetime.datetime.now()
duration = end-start
status ='Done'if s.returncode == 0 else'Failed'
print'%s(%d seconds)'%(status,duration.seconds)

以下行读取标准输出和错误:

  s = sp.Popen(command,shell = True,stderr = sp.PIPE,stdout = sp.PIPE)
out,err = s.communicate()
pre>

如您所见,stdout和stderr不被使用。假设我想以格式化的方式将输出和错误消息写入日志文件,例如:

  [STDOUT: 2011-01-17 14:53:55]< message> 
[STDERR:2011-01-17 14:53:56]< message>

我的问题是,最聪明的方式是做什么?我想到了三个选项:


  1. 继承文件对象并覆盖写入方法

  2. 使用实现的代理类写入

  3. 连接到 PIPE 本身以某种方式。

更新:参考测试脚本



我使用此脚本检查结果,保存为 test.py

 #!/ usr / bin / python 
import sys

sys.stdout.write('OUT\\ \\ n')
sys.stdout.flush()
sys.stderr.write('ERR\\\
')
sys.stderr.flush()

任何想法?

解决方案

1和2是合理的解决方案,但是覆盖write()是不够的。



问题是Popen需要文件句柄来附加到进程,所以Python文件对象不起作用,它们必须是操作系统级别。要解决这个问题,你必须拥有一个具有os级别文件句柄的Python对象。我可以想到解决的唯一方法是使用管道,所以你有一个os级别的文件句柄来写。但是,然后你需要另一个线程,并轮询该管道以便读取,以便它可以记录。 (所以这是更严格的执行2,因为它代表日志记录)。



说完了:

  import io 
import logging
import os
import select
import subprocess
import time
import threading

LOG_FILENAME ='output.log'
logging.basicConfig(filename = LOG_FILENAME,level = logging.DEBUG)

class StreamLogger(io.IOBase):
def __init __(self,level):
self.level = level
self.pipe = os.pipe()
self.thread = threading.Thread(target = self._flusher)
self.thread.start()

def _flusher(self):
self._run = True
buf = b'
while self。 _run:
for select.select中的fh([self.pipe [0]],[],[],0)[0]:
buf + = os.read(fh,1024)
而b'\\\
'在buf中:
data,buf = buf.split(b'\\\
',1)
self.write(data.decode())
time.sleep(1)
self._run =无

def write(self,data):
return log.log(self.level,data)

def fileno(self):
return self.pipe [1]

def close(self):
如果self._run:
self._run = False
而self._run不是无:
time.sleep(1)
os.close(self.pipe [0])
os.close(self.pipe [1])$ ​​b $ b

类启动一个os级别的管道,Popen可以将stdin / out / error附加到子进程。它还启动一个线程,轮询另一端一秒钟进行日志记录,然后记录日志模块。



可能这个类应该实现更多的东西是完整的,但它在这种情况下也是有效的。



示例代码:

 code> with StreamLogger(logging.INFO)as out:
with StreamLogger(logging.ERROR)as err:
subprocess.Popen(ls,stdout = out,stderr = err,shell = true)

output.log最终如此:

  INFO:root:output.log 
INFO:root:streamlogger.py
INFO:root:和
INFO:root:so
INFO:root:on

使用Python 2.6,2.7和3.1进行测试。 >

我认为1和3的任何实现都需要使用类似的技术。这有点涉及,但除非您可以使Popen命令正确登录本身,否则我没有更好的想法)。


I've been writing a small Python script that executes some shell commands using the subprocess module and a helper function:

import subprocess as sp
def run(command, description):
    """Runs a command in a formatted manner. Returns its return code."""
    start=datetime.datetime.now()
    sys.stderr.write('%-65s' % description)
    s=sp.Popen(command, shell=True, stderr=sp.PIPE, stdout=sp.PIPE)
    out,err=s.communicate()
    end=datetime.datetime.now()
    duration=end-start
    status='Done' if s.returncode==0 else 'Failed'
    print '%s (%d seconds)' % (status, duration.seconds)

The following lines reads the standard output and error:

    s=sp.Popen(command, shell=True, stderr=sp.PIPE, stdout=sp.PIPE)
    out,err=s.communicate()

As you can see, stdout and stderr are not used. Suppose that I want to write the output and error messages to a log file, in a formatted way, e.g.:

[STDOUT: 2011-01-17 14:53:55] <message>
[STDERR: 2011-01-17 14:53:56] <message>

My question is, what's the most Pythonic way to do it? I thought of three options:

  1. Inherit the file object and override the write method.
  2. Use a Delegate class which implements write.
  3. Connect to the PIPE itself in some way.

UPDATE : reference test script

I'm checking the results with this script, saved as test.py:

#!/usr/bin/python
import sys

sys.stdout.write('OUT\n')
sys.stdout.flush()
sys.stderr.write('ERR\n')
sys.stderr.flush()

Any ideas?

解决方案

1 and 2 are reasonable solutions, but overriding write() won't be enough.

The problem is that Popen needs file handles to attach to the process, so Python file objects doesn't work, they have to be OS level. To solve that you have to have a Python object that has a os level file handle. The only way I can think of solving that is to use pipes, so you have an os level file handle to write to. But then you need another thread that sits and polls that pipe for things to read in so it can log it. (So this is more strictly an implementation of 2, as it delegates to logging).

Said and done:

import io
import logging
import os
import select
import subprocess
import time
import threading

LOG_FILENAME = 'output.log'
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)

class StreamLogger(io.IOBase):
    def __init__(self, level):
        self.level = level
        self.pipe = os.pipe()
        self.thread = threading.Thread(target=self._flusher)
        self.thread.start()

    def _flusher(self):
        self._run = True
        buf = b''
        while self._run:
            for fh in select.select([self.pipe[0]], [], [], 0)[0]:
                buf += os.read(fh, 1024)
                while b'\n' in buf:
                    data, buf = buf.split(b'\n', 1)
                    self.write(data.decode())
            time.sleep(1)
        self._run = None

    def write(self, data):
        return logging.log(self.level, data)

    def fileno(self):
        return self.pipe[1]

    def close(self):
        if self._run:
            self._run = False
            while self._run is not None:
                time.sleep(1)
            os.close(self.pipe[0])
            os.close(self.pipe[1])

So that class starts a os level pipe that Popen can attach the stdin/out/error to for the subprocess. It also starts a thread that polls the other end of that pipe once a second for things to log, which it then logs with the logging module.

Possibly this class should implement more things for completeness, but it works in this case anyway.

Example code:

with StreamLogger(logging.INFO) as out:
    with StreamLogger(logging.ERROR) as err:
        subprocess.Popen("ls", stdout=out, stderr=err, shell=True)

output.log ends up like so:

INFO:root:output.log
INFO:root:streamlogger.py
INFO:root:and
INFO:root:so
INFO:root:on

Tested with Python 2.6, 2.7 and 3.1.

I would think any implementation of 1 and 3 would need to use similar techniques. It is a bit involved, but unless you can make the Popen command log correctly itself, I don't have a better idea).

这篇关于装饰\委托一个File对象来添加功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆