Imported Upstream version 65.4.1 upstream/65.4.1
authorJinWang An <jinwang.an@samsung.com>
Mon, 27 Mar 2023 08:02:56 +0000 (17:02 +0900)
committerJinWang An <jinwang.an@samsung.com>
Mon, 27 Mar 2023 08:02:56 +0000 (17:02 +0900)
19 files changed:
.bumpversion.cfg
.github/workflows/ci-sage.yml
.github/workflows/main.yml
CHANGES.rst
docs/conf.py
setup.cfg
setuptools/_distutils/dist.py
setuptools/_distutils/sysconfig.py
setuptools/_distutils/tests/py37compat.py [new file with mode: 0644]
setuptools/_distutils/tests/py38compat.py
setuptools/_distutils/tests/test_archive_util.py
setuptools/_distutils/tests/test_build_ext.py
setuptools/_distutils/tests/test_core.py
setuptools/_distutils/tests/test_dist.py
setuptools/_distutils/tests/test_filelist.py
setuptools/_distutils/tests/test_spawn.py
setuptools/_distutils/tests/test_sysconfig.py
setuptools/config/expand.py
setuptools/tests/config/test_expand.py

index 66973fe71a7df7002bd0d6cfac8008b40a28961a..aeee9a833b5fa6c546f19cff3038bd4be1b3ac92 100644 (file)
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 65.4.0
+current_version = 65.4.1
 commit = True
 tag = True
 
index 2a91934d2f8912026792c3120967e85c64233a74..f3fe8513b92e2aafeae0a42d635be08472292f25 100644 (file)
@@ -37,6 +37,9 @@ on:
   push:
     tags:
       - '*'
+  pull_request:
+    paths:
+      - .github/workflows/ci-sage.yml
   workflow_dispatch:
     # Allow to run manually
 
@@ -45,19 +48,6 @@ env:
   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:
 
@@ -85,68 +75,23 @@ 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()
index b5d1a1e7a141294114956a06fb6b828246e3e377..7b4669aceeae8bce246ad28a17f7807e63fc6cdc 100644 (file)
@@ -16,16 +16,20 @@ jobs:
         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
@@ -38,7 +42,7 @@ jobs:
       - 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:
@@ -149,7 +153,7 @@ jobs:
       - 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
index 2c39cfd4c78f52d2e5b2b23fa9f884603a222f74..089ec83123a98aaa760ad52803de8fd9e7c737bc 100644 (file)
@@ -1,3 +1,13 @@
+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
 -------
 
index 2b60bf57b2d7b4b1175fdeb41cc59894b76bc5f0..ecd7aac4263cfbaeeedf0816299b590f86899add 100644 (file)
@@ -1,7 +1,15 @@
-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(
@@ -81,7 +89,7 @@ link_files = {
     ),
 }
 
-# Be strict about any broken references:
+# Be strict about any broken references
 nitpicky = True
 
 # Include Python intersphinx mapping to prevent failures
@@ -91,6 +99,9 @@ intersphinx_mapping = {
     '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),
index fe95dd67629103ce6066c42f3dcdcaf193ac83ce..436435a9c736ad05f87320a28d7611536f356d75 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [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
@@ -87,7 +87,7 @@ testing-integration =
 
 docs =
        # upstream
-       sphinx
+       sphinx >= 3.5
        jaraco.packaging >= 9
        rst.linker >= 1.9
        jaraco.tidelift >= 1.4
index 1dc25fe541fbd9948dec9828a0e7e4552135351f..917cd94a0c29985085f9332c5a73549c51bb8fb1 100644 (file)
@@ -334,7 +334,7 @@ Common commands: (see '--help-commands' for more)
         - 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))
index 3dd8185f79f82f661ef7e2ea4960a1cfd9d219c8..6a979f8c91fce3c8239b36ddb8764dc85dea41f2 100644 (file)
@@ -13,6 +13,7 @@ import os
 import re
 import sys
 import sysconfig
+import pathlib
 
 from .errors import DistutilsPlatformError
 from . import py39compat
@@ -40,14 +41,13 @@ else:
         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)
diff --git a/setuptools/_distutils/tests/py37compat.py b/setuptools/_distutils/tests/py37compat.py
new file mode 100644 (file)
index 0000000..e5d406a
--- /dev/null
@@ -0,0 +1,18 @@
+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
+)
index 35ddbb5bde6dfcc3b690a74109abf7f831258eb4..211d3a6c50a88d61c6f7b7bdd14b418af9c5bf1f 100644 (file)
@@ -18,27 +18,19 @@ except (ModuleNotFoundError, ImportError):
 
 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,
     )
 
 
index 72aa9d7c7bb04ef9372d1180dffb577e5e0a3081..d0f5b73481ae5ae92f1e546b1e4134b17ebcc8f9 100644 (file)
@@ -9,6 +9,7 @@ import operator
 import pathlib
 
 import pytest
+import path
 
 from distutils import archive_util
 from distutils.archive_util import (
@@ -23,7 +24,6 @@ from distutils.tests import support
 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
 
 
@@ -95,7 +95,7 @@ class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer):
         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
@@ -227,7 +227,7 @@ class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer):
         # 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
@@ -253,7 +253,7 @@ class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer):
         # 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'
index e60814ff64e191754315d75b94ad21933634fd8f..cf6e98985c97c3e0f561be2f74b15fccb97e3950 100644 (file)
@@ -8,6 +8,10 @@ import platform
 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
@@ -27,10 +31,7 @@ from distutils.errors import (
 )
 
 from test import support
-from . import py38compat as os_helper
 from . import py38compat as import_helper
-import pytest
-import re
 
 
 @pytest.fixture()
@@ -47,7 +48,7 @@ def user_site_dir(request):
     # 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
index 86b0040f60e191bd99ec37a5cc9355b0f78be839..5fe7e958f516cfe412362802e9ee16b7e786b304 100644 (file)
@@ -8,7 +8,6 @@ from test.support import captured_stdout
 
 import pytest
 
-from . import py38compat as os_helper
 from distutils.dist import Distribution
 
 # setup script that uses __file__
@@ -61,65 +60,63 @@ def save_stdout(monkeypatch):
     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
index a943832620bf5390c149bd8abead7498e3babb39..52e0b3ce26e7a0ad97559d64b2a2f07b27d46440 100644 (file)
@@ -14,7 +14,6 @@ from distutils.dist import Distribution, fix_help_options
 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
 
@@ -95,15 +94,15 @@ class TestDistributionBehavior(
         '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}
@@ -124,9 +123,9 @@ class TestDistributionBehavior(
 
         # 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,
@@ -153,35 +152,35 @@ class TestDistributionBehavior(
 
         # 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):
@@ -259,6 +258,18 @@ class TestDistributionBehavior(
         # 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')
index 26071820648604989d9097b1b1f1985f65bad717..7ff9d3e8660a0dd5c7b6a01cbb201fd1616af21c 100644 (file)
@@ -8,10 +8,12 @@ from distutils.filelist import glob_to_re, translate_pattern, FileList
 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 = """\
@@ -303,44 +305,35 @@ class TestFileList(support.LoggingSilencer):
 
 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
index d2a898ed3f61ed61fb78a20de33cebeaf9088fb4..5da499777a52b40c77181c711e59e6cdd5e3d1b0 100644 (file)
@@ -6,6 +6,8 @@ import unittest.mock as mock
 
 from test.support import unix_shell
 
+import path
+
 from . import py38compat as os_helper
 
 from distutils.spawn import find_executable
@@ -44,83 +46,82 @@ class TestSpawn(support.TempdirManager, support.LoggingSilencer):
         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:
index f1759839bb99d553532a16dd07c9e528f0ae68e0..66f92c2ae00e855deea818411334b99b077bc84e 100644 (file)
@@ -1,13 +1,14 @@
 """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
@@ -15,33 +16,23 @@ from distutils.ccompiler import get_default_compiler  # noqa: F401
 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()
@@ -49,33 +40,39 @@ class TestSysconfig:
         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):
@@ -169,26 +166,32 @@ class TestSysconfig:
         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):
@@ -231,24 +234,24 @@ class TestSysconfig:
         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,
@@ -278,11 +281,11 @@ class TestSysconfig:
         '\\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(),
index 384504d879f391c7c3edc40e3f708bd689e24ccd..38eb3db7d8bfa77dde6c0763e78a6b6683e1df04 100644 (file)
@@ -21,6 +21,7 @@ import ast
 import importlib
 import io
 import os
+import pathlib
 import sys
 import warnings
 from glob import iglob
@@ -62,9 +63,7 @@ class StaticModule:
     """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
 
index 523779a8ed208905e3baf8a524333790270e66c6..39f3b7c70fbefa1bba27267c758718f2b9336f46 100644 (file)
@@ -60,6 +60,20 @@ def test_read_files(tmp_path, monkeypatch):
 
 
 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": "",