Nat*_*nes 2 python django subprocess gulp
我最近将我的项目升级到 Django 2.0,并且开始出现错误。首先,django_gulp每当我启动 django runserver 时,我都会启动一个 gulp 进程。我正在使用项目的runserver_plus分支django_gulp。下面是相关的代码片段从django_gulp项目中,它使subprocess.Popen呼叫。此调用在 Django 1.11.x 中正常运行。
from __future__ import print_function
import atexit
import os
import psutil
import subprocess
import sys
import traceback
from signal import SIGTERM
from concurrent.futures import ThreadPoolExecutor
from django.core.management.base import CommandError
from django.conf import settings
from django_extensions.management.commands.runserver_plus import Command \
as DjangoExtensionsRunserverCommand
from env_tools import load_env
class Command(DjangoExtensionsRunserverCommand):
"""
Subclass the RunserverCommand from Staticfiles to set up our gulp
environment.
"""
def __init__(self, *args, **kwargs):
self.cleanup_closing = False
self.gulp_process = None
super(Command, self).__init__(*args, **kwargs)
@staticmethod
def gulp_exited_cb(future):
if future.exception():
print(traceback.format_exc())
children = psutil.Process().children(recursive=True)
for child in children:
print('>>> Killing pid {}'.format(child.pid))
child.send_signal(SIGTERM)
print('>>> Exiting')
# It would be nice to be able to raise a CommandError or use
# sys.kill here but neither of those stop the runserver instance
# since we're in a thread. This method is used in django as well.
os._exit(1)
def handle(self, *args, **options):
try:
env = load_env()
except IOError:
env = {}
# XXX: In Django 1.8 this changes to:
# if 'PORT' in env and not options.get('addrport'):
# options['addrport'] = env['PORT']
if 'PORT' in env and not args:
args = (env['PORT'],)
# We're subclassing runserver, which spawns threads for its
# autoreloader with RUN_MAIN set to true, we have to check for
# this to avoid running gulp twice.
if not os.getenv('RUN_MAIN', False):
pool = ThreadPoolExecutor(max_workers=1)
gulp_thread = pool.submit(self.start_gulp)
gulp_thread.add_done_callback(self.gulp_exited_cb)
return super(Command, self).handle(*args, **options)
def kill_gulp_process(self):
if self.gulp_process.returncode is not None:
return
self.cleanup_closing = True
self.stdout.write('>>> Closing gulp process')
self.gulp_process.terminate()
def start_gulp(self):
self.stdout.write('>>> Starting gulp')
gulp_command = getattr(settings, 'GULP_DEVELOP_COMMAND', 'gulp')
self.gulp_process = subprocess.Popen(
[gulp_command],
shell=True,
stdin=subprocess.PIPE,
stdout=self.stdout,
stderr=self.stderr)
if self.gulp_process.poll() is not None:
raise CommandError('gulp failed to start')
self.stdout.write('>>> gulp process on pid {0}'
.format(self.gulp_process.pid))
atexit.register(self.kill_gulp_process)
self.gulp_process.wait()
if self.gulp_process.returncode != 0 and not self.cleanup_closing:
raise CommandError('gulp exited unexpectedly')
Run Code Online (Sandbox Code Playgroud)
请注意,self.stdout和self.stderr,的参数subprocess.Popen,都是引用django.core.management.base.OutputWrapper。到目前为止,我只能说 Django 1.11 的OutputWrapper类继承自object,而 Django 2.0 的OutputWrapper类继承自TextIOBase.
这是我得到的错误:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.6/subprocess.py", line 667, in __init__
errread, errwrite) = self._get_handles(stdin, stdout, stderr)
File "/usr/local/lib/python3.6/subprocess.py", line 1184, in _get_handles
c2pwrite = stdout.fileno()
io.UnsupportedOperation: fileno
Run Code Online (Sandbox Code Playgroud)
如果这是一个django_gulp问题,我将在该存储库中创建一个问题和/或 PR 来解决这个问题。但是,就目前而言,我想让它在我自己的项目中工作。
我还应该提到我在 docker-compose 环境中运行它,所以可能是因为它导致了错误。我还没有在非 Docker 环境中测试过这个。
根据这个答案,子进程代码似乎假设流有一个文件描述符,在这种情况下没有。
您看到此错误的原因是类文件对象是 Python 抽象。操作系统和其他进程不知道这个抽象,他们只知道文件描述符。因此,您必须将有效的文件描述符传递给 Popen。
您可以访问由OutputWrapperfrom包装的流_out:
self.gulp_process = subprocess.Popen(
#...
stdout=self.stdout._out,
stderr=self.stderr._out)
Run Code Online (Sandbox Code Playgroud)
或者,您可以只传递标准输出和错误的标准文件号:
self.gulp_process = subprocess.Popen(
#...
stdout=1,
stderr=2)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1199 次 |
| 最近记录: |