[bumpversion]
-current_version = 65.4.0
+current_version = 65.4.1
commit = True
tag = True
push:
tags:
- '*'
+ pull_request:
+ paths:
+ - .github/workflows/ci-sage.yml
workflow_dispatch:
# Allow to run manually
DIST_PREREQ: python3
# Name of this project in the Sage distribution
SPKG: setuptools
- # Sage distribution packages to build
- TARGETS_PRE: build/make/Makefile
- TARGETS: setuptools pyzmq
- TARGETS_OPTIONAL: build/make/Makefile
- # Standard setting: Test the current beta release of Sage:
- SAGE_REPO: sagemath/sage
- SAGE_REF: develop
- # Test with the branch from https://trac.sagemath.org/ticket/33288
- # This may provide hotfixes for the CI that have not been merged into
- # the sage develop branch yet.
- SAGE_TRAC_GIT: https://github.com/sagemath/sagetrac-mirror.git
- SAGE_TICKET: 33288
- REMOVE_PATCHES: "*"
jobs:
path: upstream
name: upstream
- docker:
- runs-on: ubuntu-latest
+ linux:
+ # https://github.com/sagemath/sage/blob/develop/.github/workflows/docker.yml
+ # Use branch of ticket https://trac.sagemath.org/ticket/33288
+ uses: sagemath/sagetrac-mirror/.github/workflows/docker.yml@u/mkoeppe/setuptools_ci_target
+ with:
+ # Sage distribution packages to build
+ targets: setuptools pyzmq
+ # Standard setting: Test the current beta release of Sage:
+ sage_repo: sagemath/sage
+ sage_ref: develop
+ upstream_artifact: upstream
+ sage_trac_git: https://github.com/sagemath/sagetrac-mirror.git
+ # Test with the branch from https://trac.sagemath.org/ticket/33288
+ # This may provide hotfixes for the CI that have not been merged into
+ # the sage develop branch yet.
+ sage_trac_ticket: 33288
+ # We prefix the image name with the SPKG name ("setuptools-") to avoid the error
+ # 'Package "sage-docker-..." is already associated with another repository.'
+ docker_push_repository: ghcr.io/${{ github.repository }}/setuptools-
needs: [dist]
- strategy:
- fail-fast: false
- max-parallel: 32
- matrix:
- tox_system_factor: [ubuntu-trusty-toolchain-gcc_9, ubuntu-xenial-toolchain-gcc_9, ubuntu-bionic, ubuntu-focal, ubuntu-hirsute, ubuntu-impish, ubuntu-jammy, ubuntu-kinetic, debian-stretch, debian-buster, debian-bullseye, debian-bookworm, debian-sid, linuxmint-19, linuxmint-19.3, linuxmint-20.1, linuxmint-20.2, linuxmint-20.3, linuxmint-21, fedora-26, fedora-27, fedora-28, fedora-29, fedora-30, fedora-31, fedora-32, fedora-33, fedora-34, fedora-35, fedora-36, fedora-37, centos-7-devtoolset-gcc_11, centos-stream-8, gentoo-python3.9, gentoo-python3.10, archlinux-latest, opensuse-15.3, opensuse-tumbleweed, ubuntu-bionic-i386, manylinux-2_24-i686, debian-buster-i386, centos-7-i386-devtoolset-gcc_11]
- tox_packages_factor: [minimal, standard]
- env:
- TOX_ENV: docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }}
- LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }}
- DOCKER_TARGETS: configured with-targets with-targets-optional
- steps:
- - name: Check out SageMath
- uses: actions/checkout@v2
- with:
- repository: ${{ env.SAGE_REPO }}
- ref: ${{ env.SAGE_REF }}
- fetch-depth: 2000
- if: env.SAGE_REPO != ''
- - name: Check out git-trac-command
- uses: actions/checkout@v2
- with:
- repository: sagemath/git-trac-command
- path: git-trac-command
- if: env.SAGE_TRAC_GIT != ''
- - name: Check out SageMath from trac.sagemath.org
- shell: bash {0}
- run: |
- git config --global user.email "ci-sage@example.com"
- git config --global user.name "ci-sage workflow"
- if [ ! -d .git ]; then git init; fi; git remote add trac ${{ env.SAGE_TRAC_GIT }} && x=1 && while [ $x -le 5 ]; do x=$(( $x + 1 )); sleep $(( $RANDOM % 60 + 1 )); if git-trac-command/git-trac fetch $SAGE_TICKET; then git merge FETCH_HEAD || echo "(ignored)"; exit 0; fi; sleep 40; done; exit 1
- if: env.SAGE_TRAC_GIT != ''
- - uses: actions/download-artifact@v2
- with:
- path: upstream
- name: upstream
- - name: Install test prerequisites
- run: |
- sudo DEBIAN_FRONTEND=noninteractive apt-get update
- sudo DEBIAN_FRONTEND=noninteractive apt-get install tox python3-setuptools
- - name: Update Sage packages from upstream artifact
- run: |
- (export PATH=$(pwd)/build/bin:$PATH; (cd upstream && bash -x update-pkgs.sh) && sed -i.bak '/upstream/d' .dockerignore && echo "/:toolchain:/i ADD upstream upstream" | sed -i.bak -f - build/bin/write-dockerfile.sh && git diff)
- - name: Configure and build Sage distribution within a Docker container
- run: |
- set -o pipefail; EXTRA_DOCKER_BUILD_ARGS="--build-arg USE_MAKEFLAGS=\"-k V=0 SAGE_NUM_THREADS=3\"" tox -e $TOX_ENV -- $TARGETS 2>&1 | sed "/^configure: notice:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: warning:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: error:/s|^|::error file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;"
- - name: Copy logs from the Docker image or build container
- run: |
- mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"
- cp -r .tox/$TOX_ENV/Dockerfile .tox/$TOX_ENV/log "artifacts/$LOGS_ARTIFACT_NAME"
- if [ -f .tox/$TOX_ENV/Dockertags ]; then CONTAINERS=$(docker create $(tail -1 .tox/$TOX_ENV/Dockertags) /bin/bash || true); fi
- if [ -n "$CONTAINERS" ]; then for CONTAINER in $CONTAINERS; do for ARTIFACT in /sage/logs; do docker cp $CONTAINER:$ARTIFACT artifacts/$LOGS_ARTIFACT_NAME && HAVE_LOG=1; done; if [ -n "$HAVE_LOG" ]; then break; fi; done; fi
- if: always()
- - uses: actions/upload-artifact@v2
- with:
- path: artifacts
- name: ${{ env.LOGS_ARTIFACT_NAME }}
- if: always()
- - name: Print out logs for immediate inspection
- # and markup the output with GitHub Actions logging commands
- run: |
- .github/workflows/scan-logs.sh "artifacts/$LOGS_ARTIFACT_NAME"
- if: always()
distutils:
- local
python:
- - 3.7-dev
- - 3.10-dev
+ - "3.7"
+ - "3.10"
# disabled due to #3365
- # - 3.11-dev
- - pypy-3.7
+ # - "3.11"
+ # Workaround for actions/setup-python#508
+ dev:
+ - -dev
platform:
- ubuntu-latest
- macos-latest
- windows-latest
include:
+ - python: pypy3.9
+ platform: ubuntu-latest
- platform: ubuntu-latest
python: "3.10"
distutils: stdlib
- name: Setup Python
uses: actions/setup-python@v4
with:
- python-version: ${{ matrix.python }}
+ python-version: ${{ matrix.python }}${{ matrix.dev }}
- uses: actions/cache@v3
id: cache
with:
- name: Setup Python
uses: actions/setup-python@v4
with:
- python-version: "3.11-dev"
+ python-version: 3.11-dev
- name: Install tox
run: |
python -m pip install tox
+v65.4.1
+-------
+
+
+Misc
+^^^^
+* #3613: Fixed encoding errors in ``expand.StaticModule`` when system default encoding doesn't match expectations for source files.
+* #3617: Merge with pypa/distutils@6852b20 including fix for pypa/distutils#181.
+
+
v65.4.0
-------
-extensions = ['sphinx.ext.autodoc', 'jaraco.packaging.sphinx', 'rst.linker']
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'jaraco.packaging.sphinx',
+]
master_doc = "index"
+# Link dates and other references in the changelog
+extensions += ['rst.linker']
link_files = {
'../CHANGES.rst': dict(
using=dict(
),
}
-# Be strict about any broken references:
+# Be strict about any broken references
nitpicky = True
# Include Python intersphinx mapping to prevent failures
'python': ('https://docs.python.org/3', None),
}
+# Preserve authored syntax for defaults
+autodoc_preserve_defaults = True
+
intersphinx_mapping.update({
'pip': ('https://pip.pypa.io/en/latest', None),
'build': ('https://pypa-build.readthedocs.io/en/latest', None),
[metadata]
name = setuptools
-version = 65.4.0
+version = 65.4.1
author = Python Packaging Authority
author_email = distutils-sig@python.org
description = Easily download, build, install, upgrade, and uninstall Python packages
docs =
# upstream
- sphinx
+ sphinx >= 3.5
jaraco.packaging >= 9
rst.linker >= 1.9
jaraco.tidelift >= 1.4
- a file named by an environment variable
"""
check_environ()
- files = [str(path) for path in self._gen_paths() if path.is_file()]
+ files = [str(path) for path in self._gen_paths() if os.path.isfile(path)]
if DEBUG:
self.announce("using config files: %s" % ', '.join(files))
import re
import sys
import sysconfig
+import pathlib
from .errors import DistutilsPlatformError
from . import py39compat
project_base = os.getcwd()
-# python_build: (Boolean) if true, we're either building Python or
-# building an extension with an un-installed Python, so we use
-# different (hard-wired) directories.
def _is_python_source_dir(d):
- for fn in ("Setup", "Setup.local"):
- if os.path.isfile(os.path.join(d, "Modules", fn)):
- return True
- return False
+ """
+ Return True if the target directory appears to point to an
+ un-installed Python.
+ """
+ modules = pathlib.Path(d).joinpath('Modules')
+ return any(modules.joinpath(fn).is_file() for fn in ('Setup', 'Setup.local'))
_sys_home = getattr(sys, '_home', None)
--- /dev/null
+import os
+import sys
+import platform
+
+
+def subprocess_args_compat(*args):
+ return list(map(os.fspath, args))
+
+
+def subprocess_args_passthrough(*args):
+ return list(args)
+
+
+subprocess_args = (
+ subprocess_args_compat
+ if platform.system() == "Windows" and sys.version_info < (3, 8)
+ else subprocess_args_passthrough
+)
try:
from test.support.os_helper import (
- change_cwd,
rmtree,
EnvironmentVarGuard,
- TESTFN,
unlink,
skip_unless_symlink,
temp_dir,
- create_empty_file,
- temp_cwd,
)
except (ModuleNotFoundError, ImportError):
from test.support import (
- change_cwd,
rmtree,
EnvironmentVarGuard,
- TESTFN,
unlink,
skip_unless_symlink,
temp_dir,
- create_empty_file,
- temp_cwd,
)
import pathlib
import pytest
+import path
from distutils import archive_util
from distutils.archive_util import (
from test.support import patch
from .unix_compat import require_unix_id, require_uid_0, grp, pwd, UID_0_SUPPORT
-from .py38compat import change_cwd
from .py38compat import check_warnings
base_name = os.path.join(tmpdir2, target_name)
# working with relative paths to avoid tar warnings
- with change_cwd(tmpdir):
+ with path.Path(tmpdir):
make_tarball(splitdrive(base_name)[1], 'dist', **kwargs)
# check if the compressed tarball was created
# creating something to tar
tmpdir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
- with change_cwd(tmpdir):
+ with path.Path(tmpdir):
make_zipfile(base_name, 'dist')
# check if the compressed tarball was created
# create something to tar and compress
tmpdir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
- with change_cwd(tmpdir):
+ with path.Path(tmpdir):
make_zipfile(base_name, 'dist')
tarball = base_name + '.zip'
import tempfile
import importlib
import shutil
+import re
+
+import path
+import pytest
from distutils.core import Distribution
from distutils.command.build_ext import build_ext
)
from test import support
-from . import py38compat as os_helper
from . import py38compat as import_helper
-import pytest
-import re
@pytest.fixture()
# bpo-30132: On Windows, a .pdb file may be created in the current
# working directory. Create a temporary working directory to cleanup
# everything at the end of the test.
- with os_helper.change_cwd(self.tmp_dir):
+ with path.Path(self.tmp_dir):
yield
site.USER_BASE = orig_user_base
import pytest
-from . import py38compat as os_helper
from distutils.dist import Distribution
# setup script that uses __file__
monkeypatch.setattr(sys, 'stdout', sys.stdout)
+@pytest.fixture
+def temp_file(tmp_path):
+ return tmp_path / 'file'
+
+
@pytest.mark.usefixtures('save_env')
@pytest.mark.usefixtures('save_argv')
-@pytest.mark.usefixtures('cleanup_testfn')
class TestCore:
- def write_setup(self, text, path=os_helper.TESTFN):
- f = open(path, "w")
- try:
- f.write(text)
- finally:
- f.close()
- return path
-
- def test_run_setup_provides_file(self):
+ def test_run_setup_provides_file(self, temp_file):
# Make sure the script can use __file__; if that's missing, the test
# setup.py script will raise NameError.
- distutils.core.run_setup(self.write_setup(setup_using___file__))
+ temp_file.write_text(setup_using___file__)
+ distutils.core.run_setup(temp_file)
- def test_run_setup_preserves_sys_argv(self):
+ def test_run_setup_preserves_sys_argv(self, temp_file):
# Make sure run_setup does not clobber sys.argv
argv_copy = sys.argv.copy()
- distutils.core.run_setup(self.write_setup(setup_does_nothing))
+ temp_file.write_text(setup_does_nothing)
+ distutils.core.run_setup(temp_file)
assert sys.argv == argv_copy
- def test_run_setup_defines_subclass(self):
+ def test_run_setup_defines_subclass(self, temp_file):
# Make sure the script can use __file__; if that's missing, the test
# setup.py script will raise NameError.
- dist = distutils.core.run_setup(self.write_setup(setup_defines_subclass))
+ temp_file.write_text(setup_defines_subclass)
+ dist = distutils.core.run_setup(temp_file)
install = dist.get_command_obj('install')
assert 'cmd' in install.sub_commands
- def test_run_setup_uses_current_dir(self):
- # This tests that the setup script is run with the current directory
- # as its own current directory; this was temporarily broken by a
- # previous patch when TESTFN did not use the current directory.
+ def test_run_setup_uses_current_dir(self, tmp_path):
+ """
+ Test that the setup script is run with the current directory
+ as its own current directory.
+ """
sys.stdout = io.StringIO()
cwd = os.getcwd()
# Create a directory and write the setup.py file there:
- os.mkdir(os_helper.TESTFN)
- setup_py = os.path.join(os_helper.TESTFN, "setup.py")
- distutils.core.run_setup(self.write_setup(setup_prints_cwd, path=setup_py))
+ setup_py = tmp_path / 'setup.py'
+ setup_py.write_text(setup_prints_cwd)
+ distutils.core.run_setup(setup_py)
output = sys.stdout.getvalue()
if output.endswith("\n"):
output = output[:-1]
assert cwd == output
- def test_run_setup_within_if_main(self):
- dist = distutils.core.run_setup(
- self.write_setup(setup_within_if_main), stop_after="config"
- )
+ def test_run_setup_within_if_main(self, temp_file):
+ temp_file.write_text(setup_within_if_main)
+ dist = distutils.core.run_setup(temp_file, stop_after="config")
assert isinstance(dist, Distribution)
assert dist.get_name() == "setup_within_if_main"
- def test_run_commands(self):
+ def test_run_commands(self, temp_file):
sys.argv = ['setup.py', 'build']
- dist = distutils.core.run_setup(
- self.write_setup(setup_within_if_main), stop_after="commandline"
- )
+ temp_file.write_text(setup_within_if_main)
+ dist = distutils.core.run_setup(temp_file, stop_after="commandline")
assert 'build' not in dist.have_run
distutils.core.run_commands(dist)
assert 'build' in dist.have_run
from distutils.cmd import Command
from test.support import captured_stdout, captured_stderr
-from .py38compat import TESTFN
from distutils.tests import support
from distutils import log
'distutils' not in Distribution.parse_config_files.__module__,
reason='Cannot test when virtualenv has monkey-patched Distribution',
)
- def test_venv_install_options(self, request):
+ def test_venv_install_options(self, tmp_path):
sys.argv.append("install")
- request.addfinalizer(functools.partial(os.unlink, TESTFN))
+ file = str(tmp_path / 'file')
fakepath = '/somedir'
jaraco.path.build(
{
- TESTFN: f"""
+ file: f"""
[install]
install-base = {fakepath}
install-platbase = {fakepath}
# Base case: Not in a Virtual Environment
with mock.patch.multiple(sys, prefix='/a', base_prefix='/a'):
- d = self.create_distribution([TESTFN])
+ d = self.create_distribution([file])
- option_tuple = (TESTFN, fakepath)
+ option_tuple = (file, fakepath)
result_dict = {
'install_base': option_tuple,
# Test case: In a Virtual Environment
with mock.patch.multiple(sys, prefix='/a', base_prefix='/b'):
- d = self.create_distribution([TESTFN])
+ d = self.create_distribution([file])
for key in result_dict.keys():
assert key not in d.command_options.get('install', {})
- def test_command_packages_configfile(self, request, clear_argv):
+ def test_command_packages_configfile(self, tmp_path, clear_argv):
sys.argv.append("build")
- request.addfinalizer(functools.partial(os.unlink, TESTFN))
+ file = str(tmp_path / "file")
jaraco.path.build(
{
- TESTFN: """
+ file: """
[global]
command_packages = foo.bar, splat
""",
}
)
- d = self.create_distribution([TESTFN])
+ d = self.create_distribution([file])
assert d.get_command_packages() == ["distutils.command", "foo.bar", "splat"]
# ensure command line overrides config:
sys.argv[1:] = ["--command-packages", "spork", "build"]
- d = self.create_distribution([TESTFN])
+ d = self.create_distribution([file])
assert d.get_command_packages() == ["distutils.command", "spork"]
# Setting --command-packages to '' should cause the default to
# be used even if a config file specified something else:
sys.argv[1:] = ["--command-packages", "", "build"]
- d = self.create_distribution([TESTFN])
+ d = self.create_distribution([file])
assert d.get_command_packages() == ["distutils.command"]
def test_empty_options(self, request):
# make sure --no-user-cfg disables the user cfg file
assert len(all_files) - 1 == len(files)
+ @pytest.mark.skipif(
+ 'platform.system() == "Windows"',
+ reason='Windows does not honor chmod 000',
+ )
+ def test_find_config_files_permission_error(self, fake_home):
+ """
+ Finding config files should not fail when directory is inaccessible.
+ """
+ fake_home.joinpath(pydistutils_cfg).write_text('')
+ fake_home.chmod(0o000)
+ Distribution().find_config_files()
+
@pytest.mark.usefixtures('save_env')
@pytest.mark.usefixtures('save_argv')
from distutils import filelist
from test.support import captured_stdout
-from distutils.tests import support
-from . import py38compat as os_helper
import pytest
+import jaraco.path
+
+from distutils.tests import support
+from . import py38compat as os_helper
MANIFEST_IN = """\
class TestFindAll:
@os_helper.skip_unless_symlink
- def test_missing_symlink(self):
- with os_helper.temp_cwd():
- os.symlink('foo', 'bar')
- assert filelist.findall() == []
+ def test_missing_symlink(self, temp_cwd):
+ os.symlink('foo', 'bar')
+ assert filelist.findall() == []
- def test_basic_discovery(self):
+ def test_basic_discovery(self, temp_cwd):
"""
When findall is called with no parameters or with
'.' as the parameter, the dot should be omitted from
the results.
"""
- with os_helper.temp_cwd():
- os.mkdir('foo')
- file1 = os.path.join('foo', 'file1.txt')
- os_helper.create_empty_file(file1)
- os.mkdir('bar')
- file2 = os.path.join('bar', 'file2.txt')
- os_helper.create_empty_file(file2)
- expected = [file2, file1]
- assert sorted(filelist.findall()) == expected
-
- def test_non_local_discovery(self):
+ jaraco.path.build({'foo': {'file1.txt': ''}, 'bar': {'file2.txt': ''}})
+ file1 = os.path.join('foo', 'file1.txt')
+ file2 = os.path.join('bar', 'file2.txt')
+ expected = [file2, file1]
+ assert sorted(filelist.findall()) == expected
+
+ def test_non_local_discovery(self, tmp_path):
"""
When findall is called with another path, the full
path name should be returned.
"""
- with os_helper.temp_dir() as temp_dir:
- file1 = os.path.join(temp_dir, 'file1.txt')
- os_helper.create_empty_file(file1)
- expected = [file1]
- assert filelist.findall(temp_dir) == expected
+ filename = tmp_path / 'file1.txt'
+ filename.write_text('')
+ expected = [str(filename)]
+ assert filelist.findall(tmp_path) == expected
@os_helper.skip_unless_symlink
- def test_symlink_loop(self):
- with os_helper.temp_dir() as temp_dir:
- link = os.path.join(temp_dir, 'link-to-parent')
- content = os.path.join(temp_dir, 'somefile')
- os_helper.create_empty_file(content)
- os.symlink('.', link)
- files = filelist.findall(temp_dir)
- assert len(files) == 1
+ def test_symlink_loop(self, tmp_path):
+ tmp_path.joinpath('link-to-parent').symlink_to('.')
+ tmp_path.joinpath('somefile').write_text('')
+ files = filelist.findall(tmp_path)
+ assert len(files) == 1
from test.support import unix_shell
+import path
+
from . import py38compat as os_helper
from distutils.spawn import find_executable
os.chmod(exe, 0o777)
spawn([exe]) # should work without any error
- def test_find_executable(self):
- with os_helper.temp_dir() as tmp_dir:
- # use TESTFN to get a pseudo-unique filename
- program_noeext = os_helper.TESTFN
- # Give the temporary program an ".exe" suffix for all.
- # It's needed on Windows and not harmful on other platforms.
- program = program_noeext + ".exe"
-
- filename = os.path.join(tmp_dir, program)
- with open(filename, "wb"):
- pass
- os.chmod(filename, stat.S_IXUSR)
-
- # test path parameter
- rv = find_executable(program, path=tmp_dir)
+ def test_find_executable(self, tmp_path):
+ program_noeext = 'program'
+ # Give the temporary program an ".exe" suffix for all.
+ # It's needed on Windows and not harmful on other platforms.
+ program = program_noeext + ".exe"
+
+ program_path = tmp_path / program
+ program_path.write_text("")
+ program_path.chmod(stat.S_IXUSR)
+ filename = str(program_path)
+ tmp_dir = path.Path(tmp_path)
+
+ # test path parameter
+ rv = find_executable(program, path=tmp_dir)
+ assert rv == filename
+
+ if sys.platform == 'win32':
+ # test without ".exe" extension
+ rv = find_executable(program_noeext, path=tmp_dir)
assert rv == filename
- if sys.platform == 'win32':
- # test without ".exe" extension
- rv = find_executable(program_noeext, path=tmp_dir)
- assert rv == filename
-
- # test find in the current directory
- with os_helper.change_cwd(tmp_dir):
+ # test find in the current directory
+ with tmp_dir:
+ rv = find_executable(program)
+ assert rv == program
+
+ # test non-existent program
+ dont_exist_program = "dontexist_" + program
+ rv = find_executable(dont_exist_program, path=tmp_dir)
+ assert rv is None
+
+ # PATH='': no match, except in the current directory
+ with os_helper.EnvironmentVarGuard() as env:
+ env['PATH'] = ''
+ with mock.patch(
+ 'distutils.spawn.os.confstr', return_value=tmp_dir, create=True
+ ), mock.patch('distutils.spawn.os.defpath', tmp_dir):
rv = find_executable(program)
- assert rv == program
-
- # test non-existent program
- dont_exist_program = "dontexist_" + program
- rv = find_executable(dont_exist_program, path=tmp_dir)
- assert rv is None
-
- # PATH='': no match, except in the current directory
- with os_helper.EnvironmentVarGuard() as env:
- env['PATH'] = ''
- with mock.patch(
- 'distutils.spawn.os.confstr', return_value=tmp_dir, create=True
- ), mock.patch('distutils.spawn.os.defpath', tmp_dir):
- rv = find_executable(program)
- assert rv is None
-
- # look in current directory
- with os_helper.change_cwd(tmp_dir):
- rv = find_executable(program)
- assert rv == program
-
- # PATH=':': explicitly looks in the current directory
- with os_helper.EnvironmentVarGuard() as env:
- env['PATH'] = os.pathsep
- with mock.patch(
- 'distutils.spawn.os.confstr', return_value='', create=True
- ), mock.patch('distutils.spawn.os.defpath', ''):
+ assert rv is None
+
+ # look in current directory
+ with tmp_dir:
rv = find_executable(program)
- assert rv is None
+ assert rv == program
+
+ # PATH=':': explicitly looks in the current directory
+ with os_helper.EnvironmentVarGuard() as env:
+ env['PATH'] = os.pathsep
+ with mock.patch(
+ 'distutils.spawn.os.confstr', return_value='', create=True
+ ), mock.patch('distutils.spawn.os.defpath', ''):
+ rv = find_executable(program)
+ assert rv is None
- # look in current directory
- with os_helper.change_cwd(tmp_dir):
- rv = find_executable(program)
- assert rv == program
+ # look in current directory
+ with tmp_dir:
+ rv = find_executable(program)
+ assert rv == program
- # missing PATH: test os.confstr("CS_PATH") and os.defpath
- with os_helper.EnvironmentVarGuard() as env:
- env.pop('PATH', None)
+ # missing PATH: test os.confstr("CS_PATH") and os.defpath
+ with os_helper.EnvironmentVarGuard() as env:
+ env.pop('PATH', None)
- # without confstr
- with mock.patch(
- 'distutils.spawn.os.confstr', side_effect=ValueError, create=True
- ), mock.patch('distutils.spawn.os.defpath', tmp_dir):
- rv = find_executable(program)
- assert rv == filename
+ # without confstr
+ with mock.patch(
+ 'distutils.spawn.os.confstr', side_effect=ValueError, create=True
+ ), mock.patch('distutils.spawn.os.defpath', tmp_dir):
+ rv = find_executable(program)
+ assert rv == filename
- # with confstr
- with mock.patch(
- 'distutils.spawn.os.confstr', return_value=tmp_dir, create=True
- ), mock.patch('distutils.spawn.os.defpath', ''):
- rv = find_executable(program)
- assert rv == filename
+ # with confstr
+ with mock.patch(
+ 'distutils.spawn.os.confstr', return_value=tmp_dir, create=True
+ ), mock.patch('distutils.spawn.os.defpath', ''):
+ rv = find_executable(program)
+ assert rv == filename
def test_spawn_missing_exe(self):
with pytest.raises(DistutilsExecError) as ctx:
"""Tests for distutils.sysconfig."""
import contextlib
import os
-import shutil
import subprocess
import sys
-import textwrap
+import pathlib
import pytest
import jaraco.envs
+import path
+from jaraco.text import trim
import distutils
from distutils import sysconfig
from distutils.unixccompiler import UnixCCompiler
from test.support import swap_item
-from .py38compat import TESTFN
+from . import py37compat
@pytest.mark.usefixtures('save_env')
-@pytest.mark.usefixtures('cleanup_testfn')
class TestSysconfig:
- def cleanup_testfn(self):
- if os.path.isfile(TESTFN):
- os.remove(TESTFN)
- elif os.path.isdir(TESTFN):
- shutil.rmtree(TESTFN)
-
def test_get_config_h_filename(self):
config_h = sysconfig.get_config_h_filename()
- assert os.path.isfile(config_h), config_h
+ assert os.path.isfile(config_h)
@pytest.mark.skipif("platform.system() == 'Windows'")
@pytest.mark.skipif("sys.implementation.name != 'cpython'")
def test_get_makefile_filename(self):
makefile = sysconfig.get_makefile_filename()
- assert os.path.isfile(makefile), makefile
+ assert os.path.isfile(makefile)
- def test_get_python_lib(self):
- # XXX doesn't work on Linux when Python was never installed before
- # self.assertTrue(os.path.isdir(lib_dir), lib_dir)
- # test for pythonxx.lib?
- assert sysconfig.get_python_lib() != sysconfig.get_python_lib(prefix=TESTFN)
+ def test_get_python_lib(self, tmp_path):
+ assert sysconfig.get_python_lib() != sysconfig.get_python_lib(prefix=tmp_path)
def test_get_config_vars(self):
cvars = sysconfig.get_config_vars()
assert cvars
@pytest.mark.skipif('sysconfig.IS_PYPY')
- @pytest.mark.xfail(reason="broken")
- def test_srcdir(self):
- # See Issues #15322, #15364.
- srcdir = sysconfig.get_config_var('srcdir')
+ @pytest.mark.skipif('sysconfig.python_build')
+ @pytest.mark.xfail('platform.system() == "Windows"')
+ def test_srcdir_simple(self):
+ # See #15364.
+ srcdir = pathlib.Path(sysconfig.get_config_var('srcdir'))
- assert os.path.isabs(srcdir), srcdir
- assert os.path.isdir(srcdir), srcdir
+ assert srcdir.absolute()
+ assert srcdir.is_dir()
- if sysconfig.python_build:
- # The python executable has not been installed so srcdir
- # should be a full source checkout.
- Python_h = os.path.join(srcdir, 'Include', 'Python.h')
- assert os.path.exists(Python_h), Python_h
- assert sysconfig._is_python_source_dir(srcdir)
- elif os.name == 'posix':
- assert os.path.dirname(sysconfig.get_makefile_filename()) == srcdir
+ makefile = pathlib.Path(sysconfig.get_makefile_filename())
+ assert makefile.parent.samefile(srcdir)
+
+ @pytest.mark.skipif('sysconfig.IS_PYPY')
+ @pytest.mark.skipif('not sysconfig.python_build')
+ def test_srcdir_python_build(self):
+ # See #15364.
+ srcdir = pathlib.Path(sysconfig.get_config_var('srcdir'))
+
+ # The python executable has not been installed so srcdir
+ # should be a full source checkout.
+ Python_h = srcdir.joinpath('Include', 'Python.h')
+ assert Python_h.is_file()
+ assert sysconfig._is_python_source_dir(srcdir)
+ assert sysconfig._is_python_source_dir(str(srcdir))
def test_srcdir_independent_of_cwd(self):
- # srcdir should be independent of the current working directory
- # See Issues #15322, #15364.
+ """
+ srcdir should be independent of the current working directory
+ """
+ # See #15364.
srcdir = sysconfig.get_config_var('srcdir')
- cwd = os.getcwd()
- try:
- os.chdir('..')
+ with path.Path('..'):
srcdir2 = sysconfig.get_config_var('srcdir')
- finally:
- os.chdir(cwd)
assert srcdir == srcdir2
def customize_compiler(self):
assert comp.shared_lib_extension == 'sc_shutil_suffix'
assert 'ranlib' not in comp.exes
- def test_parse_makefile_base(self):
- self.makefile = TESTFN
- fd = open(self.makefile, 'w')
- try:
- fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n')
- fd.write('VAR=$OTHER\nOTHER=foo')
- finally:
- fd.close()
- d = sysconfig.parse_makefile(self.makefile)
+ def test_parse_makefile_base(self, tmp_path):
+ makefile = tmp_path / 'Makefile'
+ makefile.write_text(
+ trim(
+ """
+ CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'
+ VAR=$OTHER
+ OTHER=foo
+ """
+ )
+ )
+ d = sysconfig.parse_makefile(makefile)
assert d == {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", 'OTHER': 'foo'}
- def test_parse_makefile_literal_dollar(self):
- self.makefile = TESTFN
- fd = open(self.makefile, 'w')
- try:
- fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n')
- fd.write('VAR=$OTHER\nOTHER=foo')
- finally:
- fd.close()
- d = sysconfig.parse_makefile(self.makefile)
+ def test_parse_makefile_literal_dollar(self, tmp_path):
+ makefile = tmp_path / 'Makefile'
+ makefile.write_text(
+ trim(
+ """
+ CONFIG_ARGS= '--arg1=optarg1' 'ENV=\\$$LIB'
+ VAR=$OTHER
+ OTHER=foo
+ """
+ )
+ )
+ d = sysconfig.parse_makefile(makefile)
assert d == {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", 'OTHER': 'foo'}
def test_sysconfig_module(self):
with pytest.warns(DeprecationWarning):
sysconfig.get_config_var('SO')
- def test_customize_compiler_before_get_config_vars(self):
+ def test_customize_compiler_before_get_config_vars(self, tmp_path):
# Issue #21923: test that a Distribution compiler
# instance can be called without an explicit call to
# get_config_vars().
- with open(TESTFN, 'w') as f:
- f.writelines(
- textwrap.dedent(
- '''\
+ file = tmp_path / 'file'
+ file.write_text(
+ trim(
+ """
from distutils.core import Distribution
config = Distribution().get_command_obj('config')
# try_compile may pass or it may fail if no compiler
# is found but it should not raise an exception.
rc = config.try_compile('int x;')
- '''
- )
+ """
)
+ )
p = subprocess.Popen(
- [str(sys.executable), TESTFN],
+ py37compat.subprocess_args(sys.executable, file),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True,
'\\PCbuild\\'.casefold() not in sys.executable.casefold(),
reason='Need sys.executable to be in a source tree',
)
- def test_win_build_venv_from_source_tree(self):
+ def test_win_build_venv_from_source_tree(self, tmp_path):
"""Ensure distutils.sysconfig detects venvs from source tree builds."""
env = jaraco.envs.VEnv()
env.create_opts = env.clean_opts
- env.root = TESTFN
+ env.root = tmp_path
env.ensure_env()
cmd = [
env.exe(),
import importlib
import io
import os
+import pathlib
import sys
import warnings
from glob import iglob
"""Proxy to a module object that avoids executing arbitrary code."""
def __init__(self, name: str, spec: ModuleSpec):
- with open(spec.origin) as strm: # type: ignore
- src = strm.read()
- module = ast.parse(src)
+ module = ast.parse(pathlib.Path(spec.origin).read_bytes())
vars(self).update(locals())
del self.self
class TestReadAttr:
+ @pytest.mark.parametrize(
+ "example",
+ [
+ # No cookie means UTF-8:
+ b"__version__ = '\xc3\xa9'\nraise SystemExit(1)\n",
+ # If a cookie is present, honor it:
+ b"# -*- coding: utf-8 -*-\n__version__ = '\xc3\xa9'\nraise SystemExit(1)\n",
+ b"# -*- coding: latin1 -*-\n__version__ = '\xe9'\nraise SystemExit(1)\n",
+ ]
+ )
+ def test_read_attr_encoding_cookie(self, example, tmp_path):
+ (tmp_path / "mod.py").write_bytes(example)
+ assert expand.read_attr('mod.__version__', root_dir=tmp_path) == 'é'
+
def test_read_attr(self, tmp_path, monkeypatch):
files = {
"pkg/__init__.py": "",