Repository: snowflakedb/snowflake-connector-python

Issue:
SNOW-576084: Setting environment variable SF_PARTNER doesn't send overridden `application` value in requests
Please answer these questions before submitting your issue. Thanks!

1. What version of Python are you using?

```
Python 3.9.11 (main, Apr  5 2022, 09:45:25) 
[Clang 13.1.6 (clang-1316.0.21.2)]
```

2. What operating system and processor architecture are you using?

```
macOS-12.3.1-arm64-arm-64bit
```

3. What are the component versions in the environment (`pip freeze`)?

Testing locally
```
-e git+ssh://git@github.com/sfc-gh-zblackwood/snowflake-connector-python.git@0a3ad2de0fda656e8d5871c3eabf9ef6d618e190#egg=snowflake_connector_python
```

4. What did you do?

Here is the issue written as a test (note that the first assert succeeds, and the last fails)

```
@pytest.mark.skipolddriver
@patch("snowflake.connector.network.SnowflakeRestful._post_request")
def test_partner_env_var(mockSnowflakeRestfulPostRequest, capsys):
    PARTNER_NAME = "Amanda"

    def mock_post_request(url, headers, json_body, **kwargs):
        global mock_cnt
        print(json_body)
        ret = None
        if mock_cnt == 0:
            # return from /v1/login-request
            ret = {
                "success": True,
                "message": None,
                "data": {
                    "token": "TOKEN",
                    "masterToken": "MASTER_TOKEN",
                    "idToken": None,
                    "parameters": [
                        {"name": "SERVICE_NAME", "value": "FAKE_SERVICE_NAME"}
                    ],
                },
            }
        return ret

    # POST requests mock
    mockSnowflakeRestfulPostRequest.side_effect = mock_post_request

    global mock_cnt
    mock_cnt = 0

    with patch.dict(os.environ, {ENV_VAR_PARTNER: PARTNER_NAME}):
        # connection
        con = snowflake.connector.connect(
            user="user",
            account="account",
            password="testpassword",
            database="TESTDB",
            warehouse="TESTWH",
        )
        assert con.application == PARTNER_NAME

    # Check that the json body of the request that is made contains the new
    # APPLICATION name, instead of the default value
    captured = capsys.readouterr()
    assert f'"APPLICATION": "{PARTNER_NAME}"' in captured.out
```

5. What did you expect to see?

  I expected that setting ENV_VAR_PARTNER environment variable would result in APPLICATION being overwritten in the requests, but it was not.

6. Can you set logging to DEBUG and collect the logs?

   ```
   import logging
   import os

   for logger_name in ['snowflake.sqlalchemy', 'snowflake.connector', 'botocore']:
       logger = logging.getLogger(logger_name)
       logger.setLevel(logging.DEBUG)
       ch = logging.StreamHandler()
       ch.setLevel(logging.DEBUG)
       ch.setFormatter(logging.Formatter('%(asctime)s - %(threadName)s %(filename)s:%(lineno)d - %(funcName)s() - %(levelname)s - %(message)s'))
       logger.addHandler(ch)
   ```

<!--
If you need urgent assistance reach out to support for escalated issue processing https://community.snowflake.com/s/article/How-To-Submit-a-Support-Case-in-Snowflake-Lodge
-->


Comments:

Patch:
diff --git a/src/snowflake/connector/connection.py b/src/snowflake/connector/connection.py
index 6387cdb0..f6f07031 100644
--- a/src/snowflake/connector/connection.py
+++ b/src/snowflake/connector/connection.py
@@ -268,18 +268,13 @@ class SnowflakeConnection:
 
         self.heartbeat_thread = None
 
+        if "application" not in kwargs and ENV_VAR_PARTNER in os.environ.keys():
+            kwargs["application"] = os.environ[ENV_VAR_PARTNER]
+
         self.converter = None
         self.__set_error_attributes()
         self.connect(**kwargs)
         self._telemetry = TelemetryClient(self._rest)
-        # Some configuration files need to be updated here to make them testable
-        # E.g.: if DEFAULT_CONFIGURATION pulled in env variables these would be not testable
-        if (
-            self.application
-            == DEFAULT_CONFIGURATION["application"][0]  # still default value
-            and ENV_VAR_PARTNER in os.environ.keys()  # is defined as an env variable
-        ):
-            self._application = os.environ[ENV_VAR_PARTNER]
 
     def __del__(self):  # pragma: no cover
         try:


Test patch:
diff --git a/test/unit/test_connection.py b/test/unit/test_connection.py
index 792529a0..6be22ce0 100644
--- a/test/unit/test_connection.py
+++ b/test/unit/test_connection.py
@@ -4,6 +4,7 @@
 #
 from __future__ import annotations
 
+import json
 import os
 from unittest.mock import patch
 
@@ -136,12 +137,38 @@ def test_is_still_running():
 
 
 @pytest.mark.skipolddriver
-def test_partner_env_var():
-    with patch.dict(os.environ, {ENV_VAR_PARTNER: "Amanda"}):
-        with patch("snowflake.connector.network.SnowflakeRestful.fetch"):
-            with snowflake.connector.connect(
-                user="user",
-                account="account",
-                password="password123",
-            ) as conn:
-                assert conn.application == "Amanda"
+@patch("snowflake.connector.network.SnowflakeRestful._post_request")
+def test_partner_env_var(mockSnowflakeRestfulPostRequest):
+    PARTNER_NAME = "Amanda"
+
+    request_body = {}
+
+    def mock_post_request(url, headers, json_body, **kwargs):
+        nonlocal request_body
+        request_body = json.loads(json_body)
+        return {
+            "success": True,
+            "message": None,
+            "data": {
+                "token": "TOKEN",
+                "masterToken": "MASTER_TOKEN",
+                "idToken": None,
+                "parameters": [{"name": "SERVICE_NAME", "value": "FAKE_SERVICE_NAME"}],
+            },
+        }
+
+    # POST requests mock
+    mockSnowflakeRestfulPostRequest.side_effect = mock_post_request
+
+    with patch.dict(os.environ, {ENV_VAR_PARTNER: PARTNER_NAME}):
+        # connection
+        con = snowflake.connector.connect(
+            user="user",
+            account="account",
+            password="testpassword",
+            database="TESTDB",
+            warehouse="TESTWH",
+        )
+        assert con.application == PARTNER_NAME
+
+    assert request_body["data"]["CLIENT_ENVIRONMENT"]["APPLICATION"] == PARTNER_NAME