title: Why is this Popen call returning a "io.UnsupportedOperation: fileno" error in Django 2.0?
body: <p>I recently upgraded my project to Django 2.0, and an error started cropping up. First off, I use <code>django_gulp</code> to start a gulp process whenever I start the django runserver. I'm using the <a href="https://github.com/beaugunderson/django-gulp/tree/runserver-plus" rel="nofollow noreferrer"><code>runserver_plus</code> branch of the <code>django_gulp</code> project</a>. Here's <a href="https://github.com/beaugunderson/django-gulp/blob/runserver-plus/django_gulp/management/commands/runserver_plus.py#L87" rel="nofollow noreferrer">the relevant code snippet</a> from the <code>django_gulp</code> project, where it makes the <code>subprocess.Popen</code> call. This call was functioning correctly in Django 1.11.x.</p>
  
  <pre><code>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('&gt;&gt;&gt; Killing pid {}'.format(child.pid))
  
                  child.send_signal(SIGTERM)
  
              print('&gt;&gt;&gt; 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('&gt;&gt;&gt; Closing gulp process')
  
          self.gulp_process.terminate()
  
      def start_gulp(self):
          self.stdout.write('&gt;&gt;&gt; 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('&gt;&gt;&gt; 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')
  </code></pre>
  
  <p>Notice that <code>self.stdout</code> and <code>self.stderr</code>, the arguments to <code>subprocess.Popen</code>, are references to <a href="https://github.com/django/django/blob/stable/2.0.x/django/core/management/base.py#L75" rel="nofollow noreferrer"><code>django.core.management.base.OutputWrapper</code></a>. All I can say so far is that Django 1.11's <code>OutputWrapper</code> class inherited from <code>object</code>, and Django 2.0's <code>OutputWrapper</code> class inherits from <code>TextIOBase</code>. </p>
  
  <p>Here's the error I'm getting:</p>
  
  <pre><code>Traceback (most recent call last):
    File "&lt;stdin&gt;", line 1, in &lt;module&gt;
    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
  </code></pre>
  
  <p>If this is a <code>django_gulp</code> issue, I'll create an issue and/or PR within that repo that fixes this. But, for now, I'd like to get this working within my own project.</p>
  
  <p>I should also mention that I am running this in a docker-compose environment, so maybe something with that is causing the error. I have not tested this on a non-Docker environment yet.</p>
  
  <h2>Edit</h2>
  
  <p>According to <a href="https://stackoverflow.com/a/42791095/690573">this answer</a>, it appears the subprocess code may be assuming that the stream has a file descriptor, where in this case there isn't one.</p>