Repository: pallets/click

Issue:
click.echo() raises UnsupportedOperation on Windows when using pytest capsys
### Actual behavior

When using `click.echo()` in a test function that uses the pytest `capsys` fixture, it raises `UnsupportedOperation` on Windows. It runs fine on Linux and macOS.

Here is a complete test module `tests/unit/test_error_click.py` that reproduces the issue:
```
import click
import pytest

def myfunc():
    """Function to be tested"""
    click.echo('bla')

def test_myfunc(capsys):
    myfunc()
    stdout, stderr = capsys.readouterr()
    assert stdout == 'bla\n'
```

Here is the failure on Windows (using Python 2.7):
```
$ pytest tests
. . .
_________________________________ test_myfunc _________________________________
capsys = <_pytest.capture.CaptureFixture object at 0x04865830>
    def test_myfunc(capsys):
>       myfunc()
tests\unit\test_error_click.py:15: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests\unit\test_error_click.py:11: in myfunc
    click.echo('bla')
.tox\win64_py27_32\lib\site-packages\click\utils.py:230: in echo
    file = _default_text_stdout()
.tox\win64_py27_32\lib\site-packages\click\_compat.py:760: in func
    rv = wrapper_func()
.tox\win64_py27_32\lib\site-packages\click\_compat.py:256: in get_text_stdout
    rv = _get_windows_console_stream(sys.stdout, encoding, errors)
.tox\win64_py27_32\lib\site-packages\click\_winconsole.py:356: in _get_windows_console_stream
    and _is_console(f)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
f = <_pytest.compat.CaptureIO object at 0x049156F0>
    def _is_console(f):
        if not hasattr(f, "fileno"):
            return False
    
        try:
>           fileno = f.fileno()
E           UnsupportedOperation: fileno
.tox\win64_py27_32\lib\site-packages\click\_winconsole.py:343: UnsupportedOperation
```

This is from this Appveyor run on Python 2.7: https://ci.appveyor.com/project/KSchopmeyer/pywbemtools/builds/33566529/job/j22nqi2rq10f7uk2#L1463

It also happens on Python 3.8: https://ci.appveyor.com/project/KSchopmeyer/pywbemtools/builds/33566529/job/0mo646j7ck1yidhs#L1473

### Expected behavior

This should succeed on Windows as it does on Linux and macOS.

### Possible solutions

I think `UnsupportedOperation` should be tolerated in the call to `f.fileno()` in `_winconsole._is_console()`. This seems to be done in other people's code calling fileno(), too:  https://www.programcreek.com/python/example/17474/io.UnsupportedOperation

### Environment

* Python version: 2.7, 3.8
* Click version: 7.1.2


Discussion:

Resolving patch:
diff --git a/src/click/_winconsole.py b/src/click/_winconsole.py
--- a/src/click/_winconsole.py
+++ b/src/click/_winconsole.py
@@ -284,7 +284,7 @@ def _is_console(f):
 
     try:
         fileno = f.fileno()
-    except OSError:
+    except (OSError, io.UnsupportedOperation):
         return False
 
     handle = msvcrt.get_osfhandle(fileno)


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -261,6 +261,12 @@ def emulate_input(text):
     assert err == "Pause to stderr\n"
 
 
+def test_echo_with_capsys(capsys):
+    click.echo("Capture me.")
+    out, err = capsys.readouterr()
+    assert out == "Capture me.\n"
+
+
 def test_open_file(runner):
     @click.command()
     @click.argument("filename")