Repository: xonsh/xonsh

Issue:
Captured object shows unwanted stderr
Hi! 
Captured object shows unwanted stderr i.e. before object repr:

```python
cd /tmp
echo @("""
import sys
print('out', file=sys.stdout)
print('err', file=sys.stderr)
""") > ttt.py

r=!(python ttt.py)  # No stdout, no stderr - good

!(python ttt.py)  # stderr before object repr - bad
# err                          
# CommandPipeline(
#   stdin=<_io.BytesIO object at 0x7f4c48b3c900>,
#   stdout=<_io.BytesIO object at 0x7f4c48b3cdb0>,
#   stderr=<_io.BytesIO object at 0x7f4c48b3c680>,
#   pid=6314,
#   returncode=0,
#   args=['python', 'ttt.py'],
#   alias=None,
#   stdin_redirect=['<stdin>', 'r'],
#   stdout_redirect=[10, 'wb'],
#   stderr_redirect=[12, 'w'],
#   timestamps=[1608146243.7313583, 1608146243.761544],
#   executed_cmd=['python', 'ttt.py'],
#   input='',
#   output='out\n',
#   errors='err\n'
# )

# Prepared by xontrib-hist-format
```


## For community
⬇️  **Please click the 👍 reaction instead of leaving a `+1` or 👍  comment**


Discussion:
anki-code: And the question: what if we set this to False? Personally I'm waiting no any output when use captured object.
codecov-io: # [Codecov](https://codecov.io/gh/xonsh/xonsh/pull/4221?src=pr&el=h1) Report
> Merging [#4221](https://codecov.io/gh/xonsh/xonsh/pull/4221?src=pr&el=desc) (f634687) into [main](https://codecov.io/gh/xonsh/xonsh/commit/5a792a6eec4889cbbc50619da6b26f7471d7ce5d?el=desc) (5a792a6) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head f634687 differs from pull request most recent head 3cd6a7d. Consider uploading reports for the commit 3cd6a7d to get more accurate results
[![Impacted file tree graph](https://codecov.io/gh/xonsh/xonsh/pull/4221/graphs/tree.svg?width=650&height=150&src=pr&token=GXLt8EOk8Z)](https://codecov.io/gh/xonsh/xonsh/pull/4221?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##             main    #4221   +/-   ##
=======================================
  Coverage   52.88%   52.89%           
=======================================
  Files         129      129           
  Lines       20006    20009    +3     
  Branches     3604     3605    +1     
=======================================
+ Hits        10581    10583    +2     
  Misses       8548     8548           
- Partials      877      878    +1     
```


| [Impacted Files](https://codecov.io/gh/xonsh/xonsh/pull/4221?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [xonsh/environ.py](https://codecov.io/gh/xonsh/xonsh/pull/4221/diff?src=pr&el=tree#diff-eG9uc2gvZW52aXJvbi5weQ==) | `72.67% <100.00%> (+0.03%)` | :arrow_up: |
| [xonsh/procs/pipelines.py](https://codecov.io/gh/xonsh/xonsh/pull/4221/diff?src=pr&el=tree#diff-eG9uc2gvcHJvY3MvcGlwZWxpbmVzLnB5) | `75.04% <100.00%> (-0.10%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/xonsh/xonsh/pull/4221?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/xonsh/xonsh/pull/4221?src=pr&el=footer). Last update [5a792a6...3cd6a7d](https://codecov.io/gh/xonsh/xonsh/pull/4221?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

daniel-shimon: Yeah I agree. Capture stderr without printing by default. 

Patch:
diff --git a/news/subproc_captured_print_stderr.rst b/news/subproc_captured_print_stderr.rst
new file mode 100644
index 00000000..0227acf6
--- /dev/null
+++ b/news/subproc_captured_print_stderr.rst
@@ -0,0 +1,23 @@
+**Added:**
+
+* Added XONSH_SUBPROC_CAPTURED_PRINT_STDERR environment variable to hide unwanted printing the stderr when using captured object.
+
+**Changed:**
+
+* <news item>
+
+**Deprecated:**
+
+* <news item>
+
+**Removed:**
+
+* <news item>
+
+**Fixed:**
+
+* <news item>
+
+**Security:**
+
+* <news item>
diff --git a/xonsh/environ.py b/xonsh/environ.py
index 3e499128..f1fa33b3 100644
--- a/xonsh/environ.py
+++ b/xonsh/environ.py
@@ -857,6 +857,10 @@ class GeneralSetting(Xettings):
         "should cause an end to execution. This is less useful at a terminal. "
         "The error that is raised is a ``subprocess.CalledProcessError``.",
     )
+    XONSH_SUBPROC_CAPTURED_PRINT_STDERR = Var.with_default(
+        True,
+        "If ``True`` the stderr from captured subproc will be printed automatically.",
+    )
     TERM = Var.no_default(
         "str",
         "TERM is sometimes set by the terminal emulator. This is used (when "
diff --git a/xonsh/procs/pipelines.py b/xonsh/procs/pipelines.py
index cfa5ad5d..351252d2 100644
--- a/xonsh/procs/pipelines.py
+++ b/xonsh/procs/pipelines.py
@@ -396,12 +396,16 @@ class CommandPipeline:
         if self.stderr_postfix:
             b += self.stderr_postfix
         stderr_has_buffer = hasattr(sys.stderr, "buffer")
-        # write bytes to std stream
-        if stderr_has_buffer:
-            sys.stderr.buffer.write(b)
-        else:
-            sys.stderr.write(b.decode(encoding=enc, errors=err))
-        sys.stderr.flush()
+        show_stderr = self.captured != "object" or env.get(
+            "XONSH_SUBPROC_CAPTURED_PRINT_STDERR", True
+        )
+        if show_stderr:
+            # write bytes to std stream
+            if stderr_has_buffer:
+                sys.stderr.buffer.write(b)
+            else:
+                sys.stderr.write(b.decode(encoding=enc, errors=err))
+            sys.stderr.flush()
         # save the raw bytes
         self._raw_error = b
         # do some munging of the line before we save it to the attr


Test patch:
diff --git a/tests/test_integrations.py b/tests/test_integrations.py
index de96da1c..e9b05e07 100644
--- a/tests/test_integrations.py
+++ b/tests/test_integrations.py
@@ -174,6 +174,22 @@ print(x.returncode)
         "hallo on err\n1\n",
         0,
     ),
+    # test captured streaming alias without stderr
+    (
+        """
+def _test_stream(args, stdin, stdout, stderr):
+    print('hallo on err', file=stderr)
+    print('hallo on out', file=stdout)
+    return 1
+
+aliases['test-stream'] = _test_stream
+with __xonsh__.env.swap(XONSH_SUBPROC_CAPTURED_PRINT_STDERR=False):
+    x = !(test-stream)
+    print(x.returncode)
+""",
+        "1\n",
+        0,
+    ),
     # test piping aliases
     (
         """