Repository: pypa/pip

Issue:
Keyring support broken on Windows when keyring.exe is found on PATH after #11589
### Description

#11589 assumes all OSes have `\n` as the line seperator, that is incorrect for WIndows. Python already has `os.linesep` which should be used instead.

I included the fix in #11029, which currently is part of the [23.0 milestone](https://github.com/pypa/pip/milestone/62).

See https://github.com/pypa/pip/pull/11399#issuecomment-1349983741 for additional context.

### Expected behavior

`os.linesep` should be used so the password used by Pip won't end with `\r` on Windows.

### pip version

unreleased

### Python version

all

### OS

Windows

### How to Reproduce

1. Configure python to use a private repository and be sure to include a username in the url credential section
2. install keyring somewhere and make sure keyring.exe can only be found via PATH lookup and not by importing it as a package
3. make sure pip can find the password for the configured url and username combination only via keyring.exe on the PATH
4. try to pip install something
5. it will fail because Pip does not strip `\r` from the password

### Output

Looking in indexes: <url>
WARNING: 401 Error, Credentials not correct for <url>

### Code of Conduct

- [X] I agree to follow the [PSF Code of Conduct](https://www.python.org/psf/conduct/).


Discussion:
cc @judahrand
#11698 includes a fix and integration test.

Resolving patch:
diff --git a/src/pip/_internal/network/auth.py b/src/pip/_internal/network/auth.py
--- a/src/pip/_internal/network/auth.py
+++ b/src/pip/_internal/network/auth.py
@@ -128,7 +128,7 @@ def _get_password(self, service_name: str, username: str) -> Optional[str]:
         )
         if res.returncode:
             return None
-        return res.stdout.decode("utf-8").strip("\n")
+        return res.stdout.decode("utf-8").strip(os.linesep)
 
     def _set_password(self, service_name: str, username: str, password: str) -> None:
         """Mirror the implementation of keyring.set_password using cli"""
@@ -136,7 +136,7 @@ def _set_password(self, service_name: str, username: str, password: str) -> None
             return None
 
         cmd = [self.keyring, "set", service_name, username]
-        input_ = password.encode("utf-8") + b"\n"
+        input_ = (password + os.linesep).encode("utf-8")
         env = os.environ.copy()
         env["PYTHONIOENCODING"] = "utf-8"
         res = subprocess.run(cmd, input=input_, env=env)


Test patch:
diff --git a/tests/unit/test_network_auth.py b/tests/unit/test_network_auth.py
--- a/tests/unit/test_network_auth.py
+++ b/tests/unit/test_network_auth.py
@@ -1,4 +1,5 @@
 import functools
+import os
 import sys
 from typing import Any, Dict, Iterable, List, Optional, Tuple
 
@@ -360,7 +361,7 @@ def __call__(
                 self.returncode = 1
             else:
                 # Passwords are returned encoded with a newline appended
-                self.stdout = password.encode("utf-8") + b"\n"
+                self.stdout = (password + os.linesep).encode("utf-8")
 
         if cmd[1] == "set":
             assert stdin is None
@@ -369,7 +370,7 @@ def __call__(
             assert input is not None
 
             # Input from stdin is encoded
-            self.set_password(cmd[2], cmd[3], input.decode("utf-8").strip("\n"))
+            self.set_password(cmd[2], cmd[3], input.decode("utf-8").strip(os.linesep))
 
         return self