10000 gh-135788: Support NETRC environment variable in `netrc` by berthin · Pull Request #135802 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-135788: Support NETRC environment variable in netrc #135802

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Wrap lines to 80 chars
  • Loading branch information
Berthin Torres authored and Berthin Torres committed Jun 22, 2025
commit 45d4f4ccda71e02f3ee7d77a5626e6ae68f0b09d
91 changes: 43 additions & 48 deletions Lib/test/test_netrc.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import netrc, os, unittest, sys, textwrap, tempfile
import netrc, os, unittest, sys, textwrap

from test import support
from contextlib import ExitStack
Expand All @@ -11,42 +11,41 @@

class NetrcEnvironment:
"""
Context manager for setting up an isolated environment to test `.netrc` file handling.
Context manager for setting up an isolated environment to test `.netrc` file
handling.

This class configures a temporary directory for the `.netrc` file and environment variables, providing
a controlled setup to simulate different scenarios.
This class configures a temporary directory for the `.netrc` file and
environment variables, providing a controlled setup to simulate different
scenarios.
"""

def __enter__(self) -> 'NetrcEnvironment':
def __enter__(self):
"""
Enters the managed environment.
Enter the managed environment.
"""
self.stack = ExitStack()
self.environ = self.stack.enter_context(support.os_helper.EnvironmentVarGuard())
self.tmpdir = self.stack.enter_context(tempfile.TemporaryDirectory())
self.environ = self.stack.enter_context(
support.os_helper.EnvironmentVarGuard(),
)
self.tmpdir = self.stack.enter_context(support.os_helper.temp_dir())
return self

def __exit__(self, *ignore_exc) -> None:
def __exit__(self, *ignore_exc):
"""
Exits the managed environment and performs cleanup. This method closes the `ExitStack`,
which automatically cleans up the temporary directory and environment.
Exit the managed environment and performs cleanup. This method closes
the `ExitStack`, which automatically cleans up the temporary directory
and environment.
"""
self.stack.close()

def generate_netrc(self, content, filename=".netrc", mode=0o600, encoding="utf-8") -> str:
"""
Creates a `.netrc` file in the temporary directory with the given content and permissions.

Args:
content (str): The content to write into the `.netrc` file.
filename (str, optional): The name of the file to write. Defaults to ".netrc".
mode (int, optional): File permission bits to set after writing. Defaults to `0o600`. Mode
is set only if the platform supports `chmod`.
encoding (str, optional): The encoding used to write the file. Defaults to "utf-8".

Returns:
str: The full path to the generated `.netrc` file.
"""
def generate_netrc(
self,
content,
filename=".netrc",
mode=0o600,
encoding="utf-8",
):
"""Create and return the path to a temporary `.netrc` file."""
write_mode = "w"
if sys.platform != "cygwin":
write_mode += "t"
Expand All @@ -62,19 +61,14 @@ def generate_netrc(self, content, filename=".netrc", mode=0o600, encoding="utf-8


class NetrcBuilder:
"""
Utility class to construct and load `netrc.netrc` instances using different configuration scenarios.

This class provides static methods to simulate different ways the `netrc` module can locate and load
a `.netrc` file.

These methods are useful for testing or mocking `.netrc` behavior in different system environments.
"""Utility class to construct and load `netrc.netrc` instances using
different configuration scenarios.
"""

@staticmethod
def use_default_netrc_in_home(*args, **kwargs) -> netrc.netrc:
"""
Loads an instance of netrc using the default `.netrc` file from the user's home directory.
def use_default_netrc_in_home(*args, **kwargs):
"""Load an instance of netrc using the default `.netrc` file from the
user's home directory.
"""
with NetrcEnvironment() as helper:
helper.environ.unset("NETRC")
Expand All @@ -84,9 +78,9 @@ def use_default_netrc_in_home(*args, **kwargs) -> netrc.netrc:
return netrc.netrc()

@staticmethod
def use_netrc_envvar(*args, **kwargs) -> netrc.netrc:
"""
Loads an instance of the netrc using the `.netrc` file specified by the `NETRC` environment variable.
def use_netrc_envvar(*args, **kwargs):
"""Load an instance of the netrc using the `.netrc` file specified by
the `NETRC` environment variable.
"""
with NetrcEnvironment() as helper:
netrc_file = helper.generate_netrc(*args, **kwargs)
Expand All @@ -95,24 +89,23 @@ def use_netrc_envvar(*args, **kwargs) -> netrc.netrc:
return netrc.netrc()

@staticmethod
def use_file_argument(*args, **kwargs) -> netrc.netrc:
"""
Loads an instance of `.netrc` file using the file as argument.
def use_file_argument(*args, **kwargs):
"""Load an instance of `.netrc` file using the file as argument.
"""
with NetrcEnvironment() as helper:
# Just to stress a bit more the test scenario, the NETRC envvar will contain
# rubish information which shouldn't be used
# Just to stress a bit more the test scenario, the NETRC envvar
# will contain rubish information which shouldn't be used
helper.environ.set("NETRC", "not-a-file.netrc")

netrc_file = helper.generate_netrc(*args, **kwargs)
return netrc.netrc(netrc_file)

@staticmethod
def get_all_scenarios():
"""
Returns all `.netrc` loading scenarios as callables.
"""Return all `.netrc` loading scenarios as callables.

This method is useful for iterating through all supported ways the `.netrc` file can be located.
This method is useful for iterating through all supported ways the
`.netrc` file can be located.
"""
return (NetrcBuilder.use_default_netrc_in_home,
NetrcBuilder.use_netrc_envvar,
Expand All @@ -128,7 +121,8 @@ def test_toplevel_non_ordered_tokens(self, make_nrc):
machine host.domain.com password pass1 login log1 account acct1
default login log2 password pass2 account acct2
""")
self.assertEqual(nrc.hosts['host.domain.com'], ('log1', 'acct1', 'pass1'))
self.assertEqual(nrc.hosts['host.domain.com'],
('log1', 'acct1', 'pass1'))
self.assertEqual(nrc.hosts['default'], ('log2', 'acct2', 'pass2'))

@support.subTests('make_nrc', ALL_NETRC_FILE_SCENARIOS)
Expand All @@ -137,7 +131,8 @@ def test_toplevel_tokens(self, make_nrc):
machine host.domain.com login log1 password pass1 account acct1
default login log2 password pass2 account acct2
""")
self.assertEqual(nrc.hosts['host.domain.com'], ('log1', 'acct1', 'pass1'))
self.assertEqual(nrc.hosts['host.domain.com'],
('log1', 'acct1', 'pass1'))
self.assertEqual(nrc.hosts['default'], ('log2', 'acct2', 'pass2'))

@support.subTests('make_nrc', ALL_NETRC_FILE_SCENARIOS)
Expand Down
0