prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Tracing Popen monkey patch breaks environment
  In `0.10.0` the new tracing functionality wipes the environment for subprocesses.
  
  As calls to `_get_argument` modify input (surely an anti-pattern) the subprocess call becomes `POpen(..., env={sentry trace values only})` which wipes the environment from the parent process.
  
  https://github.com/getsentry/sentry-python/blob/ca0ba7f6c417d9ce7ee157149ddddce5add893a9/sentry_sdk/integrations/stdlib.py#L143
  
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `sentry_sdk/integrations/stdlib.py`
  Content:
  ```
  1 import os
  2 import subprocess
  3 import sys
  4 import platform
  5 
  6 from sentry_sdk.hub import Hub
  7 from sentry_sdk.integrations import Integration
  8 from sentry_sdk.scope import add_global_event_processor
  9 from sentry_sdk.tracing import EnvironHeaders, record_http_request
  10 
  11 try:
  12     from httplib import HTTPConnection  # type: ignore
  13 except ImportError:
  14     from http.client import HTTPConnection
  15 
  16 _RUNTIME_CONTEXT = {
  17     "name": platform.python_implementation(),
  18     "version": "%s.%s.%s" % (sys.version_info[:3]),
  19     "build": sys.version,
  20 }
  21 
  22 
  23 class StdlibIntegration(Integration):
  24     identifier = "stdlib"
  25 
  26     @staticmethod
  27     def setup_once():
  28         # type: () -> None
  29         _install_httplib()
  30         _install_subprocess()
  31 
  32         @add_global_event_processor
  33         def add_python_runtime_context(event, hint):
  34             if Hub.current.get_integration(StdlibIntegration) is not None:
  35                 contexts = event.setdefault("contexts", {})
  36                 if isinstance(contexts, dict) and "runtime" not in contexts:
  37                     contexts["runtime"] = _RUNTIME_CONTEXT
  38 
  39             return event
  40 
  41 
  42 def _install_httplib():
  43     # type: () -> None
  44     real_putrequest = HTTPConnection.putrequest
  45     real_getresponse = HTTPConnection.getresponse
  46 
  47     def putrequest(self, method, url, *args, **kwargs):
  48         hub = Hub.current
  49         if hub.get_integration(StdlibIntegration) is None:
  50             return real_putrequest(self, method, url, *args, **kwargs)
  51 
  52         host = self.host
  53         port = self.port
  54         default_port = self.default_port
  55 
  56         real_url = url
  57         if not real_url.startswith(("http://", "https://")):
  58             real_url = "%s://%s%s%s" % (
  59                 default_port == 443 and "https" or "http",
  60                 host,
  61                 port != default_port and ":%s" % port or "",
  62                 url,
  63             )
  64 
  65         recorder = record_http_request(hub, real_url, method)
  66         data_dict = recorder.__enter__()
  67 
  68         try:
  69             rv = real_putrequest(self, method, url, *args, **kwargs)
  70 
  71             for key, value in hub.iter_trace_propagation_headers():
  72                 self.putheader(key, value)
  73         except Exception:
  74             recorder.__exit__(*sys.exc_info())
  75             raise
  76 
  77         self._sentrysdk_recorder = recorder
  78         self._sentrysdk_data_dict = data_dict
  79 
  80         return rv
  81 
  82     def getresponse(self, *args, **kwargs):
  83         recorder = getattr(self, "_sentrysdk_recorder", None)
  84 
  85         if recorder is None:
  86             return real_getresponse(self, *args, **kwargs)
  87 
  88         data_dict = getattr(self, "_sentrysdk_data_dict", None)
  89 
  90         try:
  91             rv = real_getresponse(self, *args, **kwargs)
  92 
  93             if data_dict is not None:
  94                 data_dict["httplib_response"] = rv
  95                 data_dict["status_code"] = rv.status
  96                 data_dict["reason"] = rv.reason
  97         except TypeError:
  98             # python-requests provokes a typeerror to discover py3 vs py2 differences
  99             #
  100             # > TypeError("getresponse() got an unexpected keyword argument 'buffering'")
  101             raise
  102         except Exception:
  103             recorder.__exit__(*sys.exc_info())
  104             raise
  105         else:
  106             recorder.__exit__(None, None, None)
  107 
  108         return rv
  109 
  110     HTTPConnection.putrequest = putrequest
  111     HTTPConnection.getresponse = getresponse
  112 
  113 
  114 def _get_argument(args, kwargs, name, position, setdefault=None):
  115     if name in kwargs:
  116         rv = kwargs[name]
  117         if rv is None and setdefault is not None:
  118             rv = kwargs[name] = setdefault
  119     elif position < len(args):
  120         rv = args[position]
  121         if rv is None and setdefault is not None:
  122             rv = args[position] = setdefault
  123     else:
  124         rv = kwargs[name] = setdefault
  125 
  126     return rv
  127 
  128 
  129 def _install_subprocess():
  130     old_popen_init = subprocess.Popen.__init__
  131 
  132     def sentry_patched_popen_init(self, *a, **kw):
  133         hub = Hub.current
  134         if hub.get_integration(StdlibIntegration) is None:
  135             return old_popen_init(self, *a, **kw)
  136 
  137         # do not setdefault! args is required by Popen, doing setdefault would
  138         # make invalid calls valid
  139         args = _get_argument(a, kw, "args", 0) or []
  140         cwd = _get_argument(a, kw, "cwd", 10)
  141 
  142         for k, v in hub.iter_trace_propagation_headers():
  143             env = _get_argument(a, kw, "env", 11, {})
  144             env["SUBPROCESS_" + k.upper().replace("-", "_")] = v
  145 
  146         with hub.span(op="subprocess", description=" ".join(map(str, args))) as span:
  147             span.set_tag("subprocess.cwd", cwd)
  148 
  149             return old_popen_init(self, *a, **kw)
  150 
  151     subprocess.Popen.__init__ = sentry_patched_popen_init  # type: ignore
  152 
  153 
  154 def get_subprocess_traceparent_headers():
  155     return EnvironHeaders(os.environ, prefix="SUBPROCESS_")
  156
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/sentry_sdk/integrations/stdlib.py b/sentry_sdk/integrations/stdlib.py
  --- a/sentry_sdk/integrations/stdlib.py
  +++ b/sentry_sdk/integrations/stdlib.py
  @@ -111,17 +111,28 @@
       HTTPConnection.getresponse = getresponse
   
   
  -def _get_argument(args, kwargs, name, position, setdefault=None):
  +def _init_argument(args, kwargs, name, position, setdefault_callback=None):
  +    """
  +    given (*args, **kwargs) of a function call, retrieve (and optionally set a
  +    default for) an argument by either name or position.
  +
  +    This is useful for wrapping functions with complex type signatures and
  +    extracting a few arguments without needing to redefine that function's
  +    entire type signature.
  +    """
  +
       if name in kwargs:
           rv = kwargs[name]
  -        if rv is None and setdefault is not None:
  -            rv = kwargs[name] = setdefault
  +        if rv is None and setdefault_callback is not None:
  +            rv = kwargs[name] = setdefault_callback()
       elif position < len(args):
           rv = args[position]
  -        if rv is None and setdefault is not None:
  -            rv = args[position] = setdefault
  +        if rv is None and setdefault_callback is not None:
  +            rv = args[position] = setdefault_callback()
       else:
  -        rv = kwargs[name] = setdefault
  +        rv = setdefault_callback and setdefault_callback()
  +        if rv is not None:
  +            kwargs[name] = rv
   
       return rv
   
  @@ -136,11 +147,14 @@
   
           # do not setdefault! args is required by Popen, doing setdefault would
           # make invalid calls valid
  -        args = _get_argument(a, kw, "args", 0) or []
  -        cwd = _get_argument(a, kw, "cwd", 10)
  +        args = _init_argument(a, kw, "args", 0) or []
  +        cwd = _init_argument(a, kw, "cwd", 10)
  +
  +        env = None
   
           for k, v in hub.iter_trace_propagation_headers():
  -            env = _get_argument(a, kw, "env", 11, {})
  +            if env is None:
  +                env = _init_argument(a, kw, "env", 11, lambda: dict(os.environ))
               env["SUBPROCESS_" + k.upper().replace("-", "_")] = v
   
           with hub.span(op="subprocess", description=" ".join(map(str, args))) as span:
  
verification_info: {"golden_diff": "diff --git a/sentry_sdk/integrations/stdlib.py b/sentry_sdk/integrations/stdlib.py\n--- a/sentry_sdk/integrations/stdlib.py\n+++ b/sentry_sdk/integrations/stdlib.py\n@@ -111,17 +111,28 @@\n     HTTPConnection.getresponse = getresponse\n \n \n-def _get_argument(args, kwargs, name, position, setdefault=None):\n+def _init_argument(args, kwargs, name, position, setdefault_callback=None):\n+    \"\"\"\n+    given (*args, **kwargs) of a function call, retrieve (and optionally set a\n+    default for) an argument by either name or position.\n+\n+    This is useful for wrapping functions with complex type signatures and\n+    extracting a few arguments without needing to redefine that function's\n+    entire type signature.\n+    \"\"\"\n+\n     if name in kwargs:\n         rv = kwargs[name]\n-        if rv is None and setdefault is not None:\n-            rv = kwargs[name] = setdefault\n+        if rv is None and setdefault_callback is not None:\n+            rv = kwargs[name] = setdefault_callback()\n     elif position < len(args):\n         rv = args[position]\n-        if rv is None and setdefault is not None:\n-            rv = args[position] = setdefault\n+        if rv is None and setdefault_callback is not None:\n+            rv = args[position] = setdefault_callback()\n     else:\n-        rv = kwargs[name] = setdefault\n+        rv = setdefault_callback and setdefault_callback()\n+        if rv is not None:\n+            kwargs[name] = rv\n \n     return rv\n \n@@ -136,11 +147,14 @@\n \n         # do not setdefault! args is required by Popen, doing setdefault would\n         # make invalid calls valid\n-        args = _get_argument(a, kw, \"args\", 0) or []\n-        cwd = _get_argument(a, kw, \"cwd\", 10)\n+        args = _init_argument(a, kw, \"args\", 0) or []\n+        cwd = _init_argument(a, kw, \"cwd\", 10)\n+\n+        env = None\n \n         for k, v in hub.iter_trace_propagation_headers():\n-            env = _get_argument(a, kw, \"env\", 11, {})\n+            if env is None:\n+                env = _init_argument(a, kw, \"env\", 11, lambda: dict(os.environ))\n             env[\"SUBPROCESS_\" + k.upper().replace(\"-\", \"_\")] = v\n \n         with hub.span(op=\"subprocess\", description=\" \".join(map(str, args))) as span:\n", "issue": "Tracing Popen monkey patch breaks environment\nIn `0.10.0` the new tracing functionality wipes the environment for subprocesses.\r\n\r\nAs calls to `_get_argument` modify input (surely an anti-pattern) the subprocess call becomes `POpen(..., env={sentry trace values only})` which wipes the environment from the parent process.\r\n\r\nhttps://github.com/getsentry/sentry-python/blob/ca0ba7f6c417d9ce7ee157149ddddce5add893a9/sentry_sdk/integrations/stdlib.py#L143\r\n\r\n\n", "before_files": [{"content": "import os\nimport subprocess\nimport sys\nimport platform\n\nfrom sentry_sdk.hub import Hub\nfrom sentry_sdk.integrations import Integration\nfrom sentry_sdk.scope import add_global_event_processor\nfrom sentry_sdk.tracing import EnvironHeaders, record_http_request\n\ntry:\n    from httplib import HTTPConnection  # type: ignore\nexcept ImportError:\n    from http.client import HTTPConnection\n\n_RUNTIME_CONTEXT = {\n    \"name\": platform.python_implementation(),\n    \"version\": \"%s.%s.%s\" % (sys.version_info[:3]),\n    \"build\": sys.version,\n}\n\n\nclass StdlibIntegration(Integration):\n    identifier = \"stdlib\"\n\n    @staticmethod\n    def setup_once():\n        # type: () -> None\n        _install_httplib()\n        _install_subprocess()\n\n        @add_global_event_processor\n        def add_python_runtime_context(event, hint):\n            if Hub.current.get_integration(StdlibIntegration) is not None:\n                contexts = event.setdefault(\"contexts\", {})\n                if isinstance(contexts, dict) and \"runtime\" not in contexts:\n                    contexts[\"runtime\"] = _RUNTIME_CONTEXT\n\n            return event\n\n\ndef _install_httplib():\n    # type: () -> None\n    real_putrequest = HTTPConnection.putrequest\n    real_getresponse = HTTPConnection.getresponse\n\n    def putrequest(self, method, url, *args, **kwargs):\n        hub = Hub.current\n        if hub.get_integration(StdlibIntegration) is None:\n            return real_putrequest(self, method, url, *args, **kwargs)\n\n        host = self.host\n        port = self.port\n        default_port = self.default_port\n\n        real_url = url\n        if not real_url.startswith((\"http://\", \"https://\")):\n            real_url = \"%s://%s%s%s\" % (\n                default_port == 443 and \"https\" or \"http\",\n                host,\n                port != default_port and \":%s\" % port or \"\",\n                url,\n            )\n\n        recorder = record_http_request(hub, real_url, method)\n        data_dict = recorder.__enter__()\n\n        try:\n            rv = real_putrequest(self, method, url, *args, **kwargs)\n\n            for key, value in hub.iter_trace_propagation_headers():\n                self.putheader(key, value)\n        except Exception:\n            recorder.__exit__(*sys.exc_info())\n            raise\n\n        self._sentrysdk_recorder = recorder\n        self._sentrysdk_data_dict = data_dict\n\n        return rv\n\n    def getresponse(self, *args, **kwargs):\n        recorder = getattr(self, \"_sentrysdk_recorder\", None)\n\n        if recorder is None:\n            return real_getresponse(self, *args, **kwargs)\n\n        data_dict = getattr(self, \"_sentrysdk_data_dict\", None)\n\n        try:\n            rv = real_getresponse(self, *args, **kwargs)\n\n            if data_dict is not None:\n                data_dict[\"httplib_response\"] = rv\n                data_dict[\"status_code\"] = rv.status\n                data_dict[\"reason\"] = rv.reason\n        except TypeError:\n            # python-requests provokes a typeerror to discover py3 vs py2 differences\n            #\n            # > TypeError(\"getresponse() got an unexpected keyword argument 'buffering'\")\n            raise\n        except Exception:\n            recorder.__exit__(*sys.exc_info())\n            raise\n        else:\n            recorder.__exit__(None, None, None)\n\n        return rv\n\n    HTTPConnection.putrequest = putrequest\n    HTTPConnection.getresponse = getresponse\n\n\ndef _get_argument(args, kwargs, name, position, setdefault=None):\n    if name in kwargs:\n        rv = kwargs[name]\n        if rv is None and setdefault is not None:\n            rv = kwargs[name] = setdefault\n    elif position < len(args):\n        rv = args[position]\n        if rv is None and setdefault is not None:\n            rv = args[position] = setdefault\n    else:\n        rv = kwargs[name] = setdefault\n\n    return rv\n\n\ndef _install_subprocess():\n    old_popen_init = subprocess.Popen.__init__\n\n    def sentry_patched_popen_init(self, *a, **kw):\n        hub = Hub.current\n        if hub.get_integration(StdlibIntegration) is None:\n            return old_popen_init(self, *a, **kw)\n\n        # do not setdefault! args is required by Popen, doing setdefault would\n        # make invalid calls valid\n        args = _get_argument(a, kw, \"args\", 0) or []\n        cwd = _get_argument(a, kw, \"cwd\", 10)\n\n        for k, v in hub.iter_trace_propagation_headers():\n            env = _get_argument(a, kw, \"env\", 11, {})\n            env[\"SUBPROCESS_\" + k.upper().replace(\"-\", \"_\")] = v\n\n        with hub.span(op=\"subprocess\", description=\" \".join(map(str, args))) as span:\n            span.set_tag(\"subprocess.cwd\", cwd)\n\n            return old_popen_init(self, *a, **kw)\n\n    subprocess.Popen.__init__ = sentry_patched_popen_init  # type: ignore\n\n\ndef get_subprocess_traceparent_headers():\n    return EnvironHeaders(os.environ, prefix=\"SUBPROCESS_\")\n", "path": "sentry_sdk/integrations/stdlib.py"}], "after_files": [{"content": "import os\nimport subprocess\nimport sys\nimport platform\n\nfrom sentry_sdk.hub import Hub\nfrom sentry_sdk.integrations import Integration\nfrom sentry_sdk.scope import add_global_event_processor\nfrom sentry_sdk.tracing import EnvironHeaders, record_http_request\n\ntry:\n    from httplib import HTTPConnection  # type: ignore\nexcept ImportError:\n    from http.client import HTTPConnection\n\n_RUNTIME_CONTEXT = {\n    \"name\": platform.python_implementation(),\n    \"version\": \"%s.%s.%s\" % (sys.version_info[:3]),\n    \"build\": sys.version,\n}\n\n\nclass StdlibIntegration(Integration):\n    identifier = \"stdlib\"\n\n    @staticmethod\n    def setup_once():\n        # type: () -> None\n        _install_httplib()\n        _install_subprocess()\n\n        @add_global_event_processor\n        def add_python_runtime_context(event, hint):\n            if Hub.current.get_integration(StdlibIntegration) is not None:\n                contexts = event.setdefault(\"contexts\", {})\n                if isinstance(contexts, dict) and \"runtime\" not in contexts:\n                    contexts[\"runtime\"] = _RUNTIME_CONTEXT\n\n            return event\n\n\ndef _install_httplib():\n    # type: () -> None\n    real_putrequest = HTTPConnection.putrequest\n    real_getresponse = HTTPConnection.getresponse\n\n    def putrequest(self, method, url, *args, **kwargs):\n        hub = Hub.current\n        if hub.get_integration(StdlibIntegration) is None:\n            return real_putrequest(self, method, url, *args, **kwargs)\n\n        host = self.host\n        port = self.port\n        default_port = self.default_port\n\n        real_url = url\n        if not real_url.startswith((\"http://\", \"https://\")):\n            real_url = \"%s://%s%s%s\" % (\n                default_port == 443 and \"https\" or \"http\",\n                host,\n                port != default_port and \":%s\" % port or \"\",\n                url,\n            )\n\n        recorder = record_http_request(hub, real_url, method)\n        data_dict = recorder.__enter__()\n\n        try:\n            rv = real_putrequest(self, method, url, *args, **kwargs)\n\n            for key, value in hub.iter_trace_propagation_headers():\n                self.putheader(key, value)\n        except Exception:\n            recorder.__exit__(*sys.exc_info())\n            raise\n\n        self._sentrysdk_recorder = recorder\n        self._sentrysdk_data_dict = data_dict\n\n        return rv\n\n    def getresponse(self, *args, **kwargs):\n        recorder = getattr(self, \"_sentrysdk_recorder\", None)\n\n        if recorder is None:\n            return real_getresponse(self, *args, **kwargs)\n\n        data_dict = getattr(self, \"_sentrysdk_data_dict\", None)\n\n        try:\n            rv = real_getresponse(self, *args, **kwargs)\n\n            if data_dict is not None:\n                data_dict[\"httplib_response\"] = rv\n                data_dict[\"status_code\"] = rv.status\n                data_dict[\"reason\"] = rv.reason\n        except TypeError:\n            # python-requests provokes a typeerror to discover py3 vs py2 differences\n            #\n            # > TypeError(\"getresponse() got an unexpected keyword argument 'buffering'\")\n            raise\n        except Exception:\n            recorder.__exit__(*sys.exc_info())\n            raise\n        else:\n            recorder.__exit__(None, None, None)\n\n        return rv\n\n    HTTPConnection.putrequest = putrequest\n    HTTPConnection.getresponse = getresponse\n\n\ndef _init_argument(args, kwargs, name, position, setdefault_callback=None):\n    \"\"\"\n    given (*args, **kwargs) of a function call, retrieve (and optionally set a\n    default for) an argument by either name or position.\n\n    This is useful for wrapping functions with complex type signatures and\n    extracting a few arguments without needing to redefine that function's\n    entire type signature.\n    \"\"\"\n\n    if name in kwargs:\n        rv = kwargs[name]\n        if rv is None and setdefault_callback is not None:\n            rv = kwargs[name] = setdefault_callback()\n    elif position < len(args):\n        rv = args[position]\n        if rv is None and setdefault_callback is not None:\n            rv = args[position] = setdefault_callback()\n    else:\n        rv = setdefault_callback and setdefault_callback()\n        if rv is not None:\n            kwargs[name] = rv\n\n    return rv\n\n\ndef _install_subprocess():\n    old_popen_init = subprocess.Popen.__init__\n\n    def sentry_patched_popen_init(self, *a, **kw):\n        hub = Hub.current\n        if hub.get_integration(StdlibIntegration) is None:\n            return old_popen_init(self, *a, **kw)\n\n        # do not setdefault! args is required by Popen, doing setdefault would\n        # make invalid calls valid\n        args = _init_argument(a, kw, \"args\", 0) or []\n        cwd = _init_argument(a, kw, \"cwd\", 10)\n\n        env = None\n\n        for k, v in hub.iter_trace_propagation_headers():\n            if env is None:\n                env = _init_argument(a, kw, \"env\", 11, lambda: dict(os.environ))\n            env[\"SUBPROCESS_\" + k.upper().replace(\"-\", \"_\")] = v\n\n        with hub.span(op=\"subprocess\", description=\" \".join(map(str, args))) as span:\n            span.set_tag(\"subprocess.cwd\", cwd)\n\n            return old_popen_init(self, *a, **kw)\n\n    subprocess.Popen.__init__ = sentry_patched_popen_init  # type: ignore\n\n\ndef get_subprocess_traceparent_headers():\n    return EnvironHeaders(os.environ, prefix=\"SUBPROCESS_\")\n", "path": "sentry_sdk/integrations/stdlib.py"}]}