Skip to content

Commit

Permalink
Merge pull request #3609 from pypa/distutils-d82d926
Browse files Browse the repository at this point in the history
Merge with distutils@d82d926
  • Loading branch information
jaraco committed Sep 24, 2022
2 parents ba3995e + 517587c commit e0dac17
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 130 deletions.
1 change: 1 addition & 0 deletions changelog.d/3609.change.rst
@@ -0,0 +1 @@
Merge with pypa/distutils@d82d926 including support for DIST_EXTRA_CONFIG in pypa/distutils#177.
3 changes: 2 additions & 1 deletion docs/deprecated/distutils/configfile.rst
Expand Up @@ -36,7 +36,8 @@ consequences:
:file:`setup.py`

* installers can override anything in :file:`setup.cfg` using the command-line
options to :file:`setup.py`
options to :file:`setup.py` or by pointing :envvar:`DIST_EXTRA_CONFIG`
to another configuration file

The basic syntax of the configuration file is simple:

Expand Down
57 changes: 26 additions & 31 deletions setuptools/_distutils/dist.py
Expand Up @@ -7,6 +7,8 @@
import sys
import os
import re
import pathlib
import contextlib
from email import message_from_file

try:
Expand Down Expand Up @@ -322,47 +324,40 @@ def find_config_files(self):
should be parsed. The filenames returned are guaranteed to exist
(modulo nasty race conditions).
There are three possible config files: distutils.cfg in the
Distutils installation directory (ie. where the top-level
Distutils __inst__.py file lives), a file in the user's home
directory named .pydistutils.cfg on Unix and pydistutils.cfg
on Windows/Mac; and setup.cfg in the current directory.
The file in the user's home directory can be disabled with the
--no-user-cfg option.
There are multiple possible config files:
- distutils.cfg in the Distutils installation directory (i.e.
where the top-level Distutils __inst__.py file lives)
- a file in the user's home directory named .pydistutils.cfg
on Unix and pydistutils.cfg on Windows/Mac; may be disabled
with the ``--no-user-cfg`` option
- setup.cfg in the current directory
- a file named by an environment variable
"""
files = []
check_environ()
files = [str(path) for path in self._gen_paths() if path.is_file()]

# Where to look for the system-wide Distutils config file
sys_dir = os.path.dirname(sys.modules['distutils'].__file__)
if DEBUG:
self.announce("using config files: %s" % ', '.join(files))

# Look for the system config file
sys_file = os.path.join(sys_dir, "distutils.cfg")
if os.path.isfile(sys_file):
files.append(sys_file)
return files

# What to call the per-user config file
if os.name == 'posix':
user_filename = ".pydistutils.cfg"
else:
user_filename = "pydistutils.cfg"
def _gen_paths(self):
# The system-wide Distutils config file
sys_dir = pathlib.Path(sys.modules['distutils'].__file__).parent
yield sys_dir / "distutils.cfg"

# And look for the user config file
# The per-user config file
prefix = '.' * (os.name == 'posix')
filename = prefix + 'pydistutils.cfg'
if self.want_user_cfg:
user_file = os.path.join(os.path.expanduser('~'), user_filename)
if os.path.isfile(user_file):
files.append(user_file)
yield pathlib.Path('~').expanduser() / filename

# All platforms support local setup.cfg
local_file = "setup.cfg"
if os.path.isfile(local_file):
files.append(local_file)
yield pathlib.Path('setup.cfg')

if DEBUG:
self.announce("using config files: %s" % ', '.join(files))

return files
# Additional config indicated in the environment
with contextlib.suppress(TypeError):
yield pathlib.Path(os.getenv("DIST_EXTRA_CONFIG"))

def parse_config_files(self, filenames=None): # noqa: C901
from configparser import ConfigParser
Expand Down
141 changes: 56 additions & 85 deletions setuptools/_distutils/tests/test_dist.py
Expand Up @@ -8,6 +8,7 @@
import unittest.mock as mock

import pytest
import jaraco.path

from distutils.dist import Distribution, fix_help_options
from distutils.cmd import Command
Expand All @@ -18,6 +19,9 @@
from distutils import log


pydistutils_cfg = '.' * (os.name == 'posix') + 'pydistutils.cfg'


class test_dist(Command):
"""Sample distutils extension command."""

Expand Down Expand Up @@ -97,26 +101,26 @@ def test_venv_install_options(self, request):

fakepath = '/somedir'

with open(TESTFN, "w") as f:
print(
(
"[install]\n"
"install-base = {0}\n"
"install-platbase = {0}\n"
"install-lib = {0}\n"
"install-platlib = {0}\n"
"install-purelib = {0}\n"
"install-headers = {0}\n"
"install-scripts = {0}\n"
"install-data = {0}\n"
"prefix = {0}\n"
"exec-prefix = {0}\n"
"home = {0}\n"
"user = {0}\n"
"root = {0}"
).format(fakepath),
file=f,
)
jaraco.path.build(
{
TESTFN: f"""
[install]
install-base = {fakepath}
install-platbase = {fakepath}
install-lib = {fakepath}
install-platlib = {fakepath}
install-purelib = {fakepath}
install-headers = {fakepath}
install-scripts = {fakepath}
install-data = {fakepath}
prefix = {fakepath}
exec-prefix = {fakepath}
home = {fakepath}
user = {fakepath}
root = {fakepath}
""",
}
)

# Base case: Not in a Virtual Environment
with mock.patch.multiple(sys, prefix='/a', base_prefix='/a'):
Expand Down Expand Up @@ -157,12 +161,14 @@ def test_venv_install_options(self, request):
def test_command_packages_configfile(self, request, clear_argv):
sys.argv.append("build")
request.addfinalizer(functools.partial(os.unlink, TESTFN))
f = open(TESTFN, "w")
try:
print("[global]", file=f)
print("command_packages = foo.bar, splat", file=f)
finally:
f.close()
jaraco.path.build(
{
TESTFN: """
[global]
command_packages = foo.bar, splat
""",
}
)

d = self.create_distribution([TESTFN])
assert d.get_command_packages() == ["distutils.command", "foo.bar", "splat"]
Expand Down Expand Up @@ -240,38 +246,23 @@ def test_announce(self):
with pytest.raises(ValueError):
dist.announce(args, kwargs)

def test_find_config_files_disable(self):
def test_find_config_files_disable(self, temp_home):
# Ticket #1180: Allow user to disable their home config file.
temp_home = self.mkdtemp()
if os.name == 'posix':
user_filename = os.path.join(temp_home, ".pydistutils.cfg")
else:
user_filename = os.path.join(temp_home, "pydistutils.cfg")

with open(user_filename, 'w') as f:
f.write('[distutils]\n')

def _expander(path):
return temp_home
jaraco.path.build({pydistutils_cfg: '[distutils]\n'}, temp_home)

old_expander = os.path.expanduser
os.path.expanduser = _expander
try:
d = Distribution()
all_files = d.find_config_files()
d = Distribution()
all_files = d.find_config_files()

d = Distribution(attrs={'script_args': ['--no-user-cfg']})
files = d.find_config_files()
finally:
os.path.expanduser = old_expander
d = Distribution(attrs={'script_args': ['--no-user-cfg']})
files = d.find_config_files()

# make sure --no-user-cfg disables the user cfg file
assert len(all_files) - 1 == len(files)


@pytest.mark.usefixtures('save_env')
@pytest.mark.usefixtures('save_argv')
class MetadataTestCase(support.TempdirManager):
class TestMetadata(support.TempdirManager):
def format_metadata(self, dist):
sio = io.StringIO()
dist.metadata.write_pkg_file(sio)
Expand Down Expand Up @@ -456,51 +447,31 @@ def test_long_description(self):
meta = meta.replace('\n' + 8 * ' ', '\n')
assert long_desc in meta

def test_custom_pydistutils(self):
# fixes #2166
# make sure pydistutils.cfg is found
if os.name == 'posix':
user_filename = ".pydistutils.cfg"
else:
user_filename = "pydistutils.cfg"

temp_dir = self.mkdtemp()
user_filename = os.path.join(temp_dir, user_filename)
f = open(user_filename, 'w')
try:
f.write('.')
finally:
f.close()

try:
dist = Distribution()

# linux-style
if sys.platform in ('linux', 'darwin'):
os.environ['HOME'] = temp_dir
files = dist.find_config_files()
assert user_filename in files

# win32-style
if sys.platform == 'win32':
# home drive should be found
os.environ['USERPROFILE'] = temp_dir
files = dist.find_config_files()
assert user_filename in files, '{!r} not found in {!r}'.format(
user_filename, files
)
finally:
os.remove(user_filename)
def test_custom_pydistutils(self, temp_home):
"""
pydistutils.cfg is found
"""
jaraco.path.build({pydistutils_cfg: ''}, temp_home)
config_path = temp_home / pydistutils_cfg

assert str(config_path) in Distribution().find_config_files()

def test_extra_pydistutils(self, monkeypatch, tmp_path):
jaraco.path.build({'overrides.cfg': ''}, tmp_path)
filename = tmp_path / 'overrides.cfg'
monkeypatch.setenv('DIST_EXTRA_CONFIG', str(filename))
assert str(filename) in Distribution().find_config_files()

def test_fix_help_options(self):
help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
fancy_options = fix_help_options(help_tuples)
assert fancy_options[0] == ('a', 'b', 'c')
assert fancy_options[1] == (1, 2, 3)

def test_show_help(self):
def test_show_help(self, request):
# smoke test, just makes sure some help is displayed
self.addCleanup(log.set_threshold, log._global_log.threshold)
reset_log = functools.partial(log.set_threshold, log._global_log.threshold)
request.addfinalizer(reset_log)
dist = Distribution()
sys.argv = []
dist.help = 1
Expand Down
7 changes: 3 additions & 4 deletions setuptools/_distutils/tests/test_util.py
Expand Up @@ -136,17 +136,16 @@ def _join(*path):
# XXX platforms to be covered: mac

def test_check_environ(self):
util._environ_checked = 0
util.check_environ.cache_clear()
os.environ.pop('HOME', None)

check_environ()

assert os.environ['PLAT'] == get_platform()
assert util._environ_checked == 1

@pytest.mark.skipif("os.name != 'posix'")
def test_check_environ_getpwuid(self):
util._environ_checked = 0
util.check_environ.cache_clear()
os.environ.pop('HOME', None)

import pwd
Expand All @@ -159,7 +158,7 @@ def test_check_environ_getpwuid(self):
check_environ()
assert os.environ['HOME'] == '/home/distutils'

util._environ_checked = 0
util.check_environ.cache_clear()
os.environ.pop('HOME', None)

# bpo-10496: Catch pwd.getpwuid() error
Expand Down
12 changes: 3 additions & 9 deletions setuptools/_distutils/util.py
Expand Up @@ -11,6 +11,8 @@
import subprocess
import sys
import sysconfig
import functools

from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError
from distutils.dep_util import newer
from distutils.spawn import spawn
Expand Down Expand Up @@ -170,9 +172,7 @@ def change_root(new_root, pathname):
raise DistutilsPlatformError(f"nothing known about platform '{os.name}'")


_environ_checked = 0


@functools.lru_cache()
def check_environ():
"""Ensure that 'os.environ' has all the environment variables we
guarantee that users can use in config files, command-line options,
Expand All @@ -181,10 +181,6 @@ def check_environ():
PLAT - description of the current platform, including hardware
and OS (see 'get_platform()')
"""
global _environ_checked
if _environ_checked:
return

if os.name == 'posix' and 'HOME' not in os.environ:
try:
import pwd
Expand All @@ -198,8 +194,6 @@ def check_environ():
if 'PLAT' not in os.environ:
os.environ['PLAT'] = get_platform()

_environ_checked = 1


def subst_vars(s, local_vars):
"""
Expand Down

0 comments on commit e0dac17

Please sign in to comment.