diff --git a/.github/ISSUE_TEMPLATE/2_documentation.yml b/.github/ISSUE_TEMPLATE/2_documentation.yml new file mode 100644 index 0000000000..68ae890de9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2_documentation.yml @@ -0,0 +1,37 @@ +--- +name: Documentation +description: Create a documentation related issue. +labels: + - type::documentation +body: + - type: markdown + attributes: + value: | + > [!NOTE] + > Documentation requests that are incomplete or missing information may be closed as inactionable. + + Since there are already a lot of open issues, please also take a moment to search existing ones to see if your bug has already been reported. If you find something related, please upvote that issue and provide additional details as necessary. + + 💐 Thank you for helping to make conda better. We would be unable to improve conda without our community! + - type: checkboxes + id: checks + attributes: + label: Checklist + description: Please confirm and check all of the following options. + options: + - label: I added a descriptive title + required: true + - label: I searched open reports and couldn't find a duplicate + required: true + - type: textarea + id: what + attributes: + label: What happened? + description: Mention here any typos, broken links, or missing, incomplete, or outdated information, etc. that you have noticed in the conda docs or CLI help. + validations: + required: true + - type: textarea + id: context + attributes: + label: Additional Context + description: Include any additional information (or screenshots) that you think would be valuable. diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 371b874431..6284ac0c42 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -39,7 +39,7 @@ jobs: with: path: https://raw.githubusercontent.com/conda/infra/main/.github/messages.yml - - uses: actions/stale@v8 + - uses: actions/stale@v9 id: stale with: # Only issues with these labels are checked whether they are stale diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5fcaf26458..80caa4cdea 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -92,7 +92,6 @@ jobs: CONDA_VERSION: ${{ contains('canary,release', matrix.conda-version) && 'conda' || format('conda={0}', matrix.conda-version) }} REPLAY_NAME: Linux-${{ matrix.conda-version }}-Py${{ matrix.python-version }} REPLAY_DIR: ${{ github.workspace }}/pytest-replay - ALLURE_DIR: ${{ github.workspace }}/allure-results PYTEST_MARKER: ${{ matrix.test-type == 'serial' && 'serial' || 'not serial' }} PYTEST_NUMPROCESSES: ${{ matrix.test-type == 'serial' && 0 || 'auto' }} @@ -145,7 +144,6 @@ jobs: --cov-report xml \ --replay-record-dir="${{ env.REPLAY_DIR }}" \ --replay-base-name="${{ env.REPLAY_NAME }}" \ - --alluredir="${{ env.ALLURE_DIR }}" \ -m "${{ env.PYTEST_MARKER }}" \ ./tests @@ -153,17 +151,6 @@ jobs: with: flags: ${{ matrix.test-type }},${{ matrix.python-version }},linux-64 - - name: Tar Allure Results - if: '!cancelled()' - run: tar -zcf "${{ env.ALLURE_DIR }}.tar.gz" "${{ env.ALLURE_DIR }}" - - - name: Upload Allure Results - if: '!cancelled()' - uses: actions/upload-artifact@v3 - with: - name: allure-Linux-${{ matrix.conda-version }}-Py${{ matrix.python-version }}-${{ matrix.test-type }} - path: allure-results.tar.gz - - name: Upload Pytest Replay if: '!cancelled()' uses: actions/upload-artifact@v3 @@ -196,7 +183,6 @@ jobs: CONDA_CHANNEL_LABEL: ${{ matrix.conda-version == 'canary' && 'conda-canary/label/dev' || 'defaults' }} REPLAY_NAME: Win-${{ matrix.conda-version }}-Py${{ matrix.python-version }} REPLAY_DIR: ${{ github.workspace }}\pytest-replay - ALLURE_DIR: ${{ github.workspace }}\allure-results PYTEST_MARKER: ${{ matrix.test-type == 'serial' && 'serial' || 'not serial and not slow' }} PYTEST_NUMPROCESSES: ${{ matrix.test-type == 'serial' && 0 || 'auto' }} @@ -232,10 +218,6 @@ jobs: --file .\tests\requirements.txt ^ --file .\tests\requirements-windows.txt ^ ${{ env.CONDA_CHANNEL_LABEL }}::conda || exit 1 - :: TEMPORARY - if "${{ matrix.python-version }}" == "3.8" CALL conda install "https://anaconda.org/conda-forge/menuinst/2.0.0/download/win-64/menuinst-2.0.0-py38hd3f51b4_1.conda" || exit 1 - if "${{ matrix.python-version }}" == "3.11" CALL conda install "https://anaconda.org/conda-forge/menuinst/2.0.0/download/win-64/menuinst-2.0.0-py311h12c1d0e_1.conda" || exit 1 - :: /TEMPORARY CALL pip install -e . --no-deps || exit 1 - name: Show info @@ -256,7 +238,6 @@ jobs: --cov-report xml ` --replay-record-dir="${{ env.REPLAY_DIR }}" ` --replay-base-name="${{ env.REPLAY_NAME }}" ` - --alluredir="${{ env.ALLURE_DIR }}" ` -m "${{ env.PYTEST_MARKER }}" ` .\tests @@ -264,20 +245,6 @@ jobs: with: flags: ${{ matrix.test-type }},${{ matrix.python-version }},win-64 - - name: Tar Allure Results - if: '!cancelled()' - run: tar -zcf "${{ env.ALLURE_DIR }}.tar.gz" "${{ env.ALLURE_DIR }}" - # windows-2019/powershell ships with GNU tar 1.28 which struggles with Windows paths - # window-2019/cmd ships with bsdtar 3.5.2 which doesn't have this problem - shell: cmd - - - name: Upload Allure Results - if: '!cancelled()' - uses: actions/upload-artifact@v3 - with: - name: allure-Win-${{ matrix.conda-version }}-Py${{ matrix.python-version }}-${{ matrix.test-type }} - path: allure-results.tar.gz - - name: Upload Pytest Replay if: '!cancelled()' uses: actions/upload-artifact@v3 @@ -313,7 +280,6 @@ jobs: CONDA_CHANNEL_LABEL: ${{ matrix.conda-version == 'canary' && 'conda-canary/label/dev' || 'defaults' }} REPLAY_NAME: macOS-${{ matrix.conda-version }}-Py${{ matrix.python-version }} REPLAY_DIR: ${{ github.workspace }}/pytest-replay - ALLURE_DIR: ${{ github.workspace }}/allure-results PYTEST_MARKER: ${{ matrix.test-type == 'serial' && 'serial' || 'not serial' }} PYTEST_NUMPROCESSES: ${{ matrix.test-type == 'serial' && 0 || 'auto' }} @@ -367,7 +333,6 @@ jobs: --cov-report xml \ --replay-record-dir="${{ env.REPLAY_DIR }}" \ --replay-base-name="${{ env.REPLAY_NAME }}" \ - --alluredir="${{ env.ALLURE_DIR }}" \ -m "${{ env.PYTEST_MARKER }}" \ ./tests @@ -375,17 +340,6 @@ jobs: with: flags: ${{ matrix.test-type }},${{ matrix.python-version }},osx-64 - - name: Tar Allure Results - if: '!cancelled()' - run: tar -zcf "${{ env.ALLURE_DIR }}.tar.gz" "${{ env.ALLURE_DIR }}" - - - name: Upload Allure Results - if: '!cancelled()' - uses: actions/upload-artifact@v3 - with: - name: allure-macOS-${{ matrix.conda-version }}-Py${{ matrix.python-version }}-${{ matrix.test-type }} - path: allure-results.tar.gz - - name: Upload Pytest Replay if: '!cancelled()' uses: actions/upload-artifact@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aa5a565768..030c783909 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -54,7 +54,7 @@ repos: # auto format Python codes within docstrings - id: blacken-docs - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.6 + rev: v0.1.9 hooks: # lint & attempt to correct failures (e.g. pyupgrade) - id: ruff diff --git a/conda_build/api.py b/conda_build/api.py index 522aa1b08d..8f55488708 100644 --- a/conda_build/api.py +++ b/conda_build/api.py @@ -10,21 +10,27 @@ """ from __future__ import annotations -import sys as _sys - # imports are done locally to keep the api clean and limited strictly # to conda-build's functionality. +import os +import sys from os.path import dirname, expanduser, join from pathlib import Path # make the Config class available in the api namespace -from conda_build.config import DEFAULT_PREFIX_LENGTH as _prefix_length -from conda_build.config import Config, get_channel_urls, get_or_merge_config -from conda_build.utils import ensure_list as _ensure_list -from conda_build.utils import expand_globs as _expand_globs -from conda_build.utils import get_logger as _get_logger - +from .config import DEFAULT_PREFIX_LENGTH as _prefix_length +from .config import Config, get_channel_urls, get_or_merge_config from .deprecations import deprecated +from .utils import ( + CONDA_PACKAGE_EXTENSIONS, + LoggingContext, + ensure_list, + expand_globs, + find_recipe, + get_logger, + get_skip_message, + on_win, +) def render( @@ -42,9 +48,9 @@ def render( Returns a list of (metadata, needs_download, needs_reparse in env) tuples""" from collections import OrderedDict - from conda_build.conda_interface import NoPackagesFoundError - from conda_build.exceptions import DependencyNeedsBuildingError - from conda_build.render import finalize_metadata, render_recipe + from .conda_interface import NoPackagesFoundError + from .exceptions import DependencyNeedsBuildingError + from .render import finalize_metadata, render_recipe config = get_or_merge_config(config, **kwargs) @@ -104,7 +110,7 @@ def render( def output_yaml(metadata, file_path=None, suppress_outputs=False): """Save a rendered recipe in its final form to the path given by file_path""" - from conda_build.render import output_yaml + from .render import output_yaml return output_yaml(metadata, file_path, suppress_outputs=suppress_outputs) @@ -121,8 +127,7 @@ def get_output_file_paths( Both split packages (recipes with more than one output) and build matrices, created with variants, contribute to the list of file paths here. """ - from conda_build.render import bldpkg_path - from conda_build.utils import get_skip_message + from .render import bldpkg_path config = get_or_merge_config(config, **kwargs) @@ -176,7 +181,7 @@ def get_output_file_path( Both split packages (recipes with more than one output) and build matrices, created with variants, contribute to the list of file paths here. """ - log = _get_logger(__name__) + log = get_logger(__name__) log.warn( "deprecation warning: this function has been renamed to get_output_file_paths, " "to reflect that potentially multiple paths are returned. This function will be " @@ -222,10 +227,7 @@ def build( If recipe paths are provided, renders recipe before building. Tests built packages by default. notest=True to skip test.""" - import os - - from conda_build.build import build_tree - from conda_build.utils import find_recipe + from .build import build_tree assert post in (None, True, False), ( "post must be boolean or None. Remember, you must pass " @@ -233,9 +235,9 @@ def build( ) recipes = [] - for recipe in _ensure_list(recipe_paths_or_metadata): + for recipe in ensure_list(recipe_paths_or_metadata): if isinstance(recipe, str): - for recipe in _expand_globs(recipe, os.getcwd()): + for recipe in expand_globs(recipe, os.getcwd()): try: recipe = find_recipe(recipe) except OSError: @@ -275,7 +277,7 @@ def test( For a recipe folder, it renders the recipe enough to know what package to download, and obtains it from your currently configuured channels.""" - from conda_build.build import test + from .build import test if hasattr(recipedir_or_package_or_metadata, "config"): config = recipedir_or_package_or_metadata.config @@ -335,7 +337,7 @@ def skeletonize( # only relevant ones below config = get_or_merge_config(config, **kwargs) config.compute_build_id("skeleton") - packages = _ensure_list(packages) + packages = ensure_list(packages) # This is a little bit of black magic. The idea is that for any keyword argument that # we inspect from the given module's skeletonize function, we should hoist the argument @@ -370,7 +372,7 @@ def skeletonize( def develop( recipe_dir, - prefix=_sys.prefix, + prefix=sys.prefix, no_pth_file=False, build_ext=False, clean=False, @@ -381,7 +383,7 @@ def develop( This works by creating a conda.pth file in site-packages.""" from .develop import execute - recipe_dir = _ensure_list(recipe_dir) + recipe_dir = ensure_list(recipe_dir) return execute(recipe_dir, prefix, no_pth_file, build_ext, clean, uninstall) @@ -400,7 +402,7 @@ def convert( portable, such as pure python, or header-only C/C++ libraries.""" from .convert import conda_convert - platforms = _ensure_list(platforms) + platforms = ensure_list(platforms) if package_file.endswith("tar.bz2"): return conda_convert( package_file, @@ -431,7 +433,7 @@ def test_installable(channel="defaults"): def inspect_linkages( packages, - prefix=_sys.prefix, + prefix=sys.prefix, untracked=False, all_packages=False, show_files=False, @@ -440,7 +442,7 @@ def inspect_linkages( ): from .inspect_pkg import inspect_linkages - packages = _ensure_list(packages) + packages = ensure_list(packages) return inspect_linkages( packages, prefix=prefix, @@ -452,18 +454,18 @@ def inspect_linkages( ) -def inspect_objects(packages, prefix=_sys.prefix, groupby="filename"): +def inspect_objects(packages, prefix=sys.prefix, groupby="filename"): from .inspect_pkg import inspect_objects - packages = _ensure_list(packages) + packages = ensure_list(packages) return inspect_objects(packages, prefix=prefix, groupby=groupby) def inspect_prefix_length(packages, min_prefix_length=_prefix_length): - from conda_build.tarcheck import check_prefix_lengths + from .tarcheck import check_prefix_lengths config = Config(prefix_length=min_prefix_length) - packages = _ensure_list(packages) + packages = ensure_list(packages) prefix_lengths = check_prefix_lengths(packages, config) if prefix_lengths: print( @@ -538,14 +540,11 @@ def update_index( current_index_versions=None, **kwargs, ): - import os - import yaml - from conda_build.index import update_index as legacy_update_index - from conda_build.utils import ensure_list + from .index import update_index as legacy_update_index - dir_paths = [os.path.abspath(path) for path in _ensure_list(dir_paths)] + dir_paths = [os.path.abspath(path) for path in ensure_list(dir_paths)] if isinstance(current_index_versions, str): with open(current_index_versions) as f: @@ -580,14 +579,11 @@ def debug( your package's build or test phase. """ import logging - import os import time from fnmatch import fnmatch - from conda_build.build import build as run_build - from conda_build.build import test as run_test - from conda_build.utils import CONDA_PACKAGE_EXTENSIONS, LoggingContext, on_win - + from .build import build as run_build + from .build import test as run_test from .metadata import MetaData is_package = False @@ -702,15 +698,11 @@ def debug( os.symlink(link_target, debug_source_loc) except PermissionError as e: raise Exception( - "You do not have the necessary permissions to create symlinks in {}\nerror: {}".format( - dn, str(e) - ) + f"You do not have the necessary permissions to create symlinks in {dn}\nerror: {str(e)}" ) except Exception as e: raise Exception( - "Unknown error creating symlinks in {}\nerror: {}".format( - dn, str(e) - ) + f"Unknown error creating symlinks in {dn}\nerror: {str(e)}" ) ext = ".bat" if on_win else ".sh" diff --git a/conda_build/bdist_conda.py b/conda_build/bdist_conda.py index 6e4a5335b9..b10e4758c4 100644 --- a/conda_build/bdist_conda.py +++ b/conda_build/bdist_conda.py @@ -1,10 +1,5 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause -""" -bdist_conda - -""" - import sys import time from collections import defaultdict @@ -13,12 +8,12 @@ from setuptools.dist import Distribution from setuptools.errors import BaseError, OptionError -from conda_build import api -from conda_build.build import handle_anaconda_upload -from conda_build.conda_interface import StringIO, configparser, spec_from_line -from conda_build.config import Config -from conda_build.metadata import MetaData -from conda_build.skeletons import pypi +from . import api +from .build import handle_anaconda_upload +from .conda_interface import StringIO, configparser, spec_from_line +from .config import Config +from .metadata import MetaData +from .skeletons import pypi # TODO: Add support for all the options that conda build has diff --git a/conda_build/build.py b/conda_build/build.py index c007286474..fe0b5fe5a4 100644 --- a/conda_build/build.py +++ b/conda_build/build.py @@ -3,12 +3,6 @@ """ Module that does most of the heavy lifting for the ``conda build`` command. """ - -# this is to compensate for a requests idna encoding error. Conda is a better place to fix, -# eventually -# exception is raises: "LookupError: unknown encoding: idna" -# http://stackoverflow.com/a/13057751/1170370 -import encodings.idna # NOQA import fnmatch import json import os @@ -29,39 +23,8 @@ from bs4 import UnicodeDammit from conda import __version__ as conda_version -import conda_build.noarch_python as noarch_python -import conda_build.os_utils.external as external -from conda_build import __version__ as conda_build_version -from conda_build import environ, source, tarcheck, utils -from conda_build.config import Config -from conda_build.create_test import create_all_test_files -from conda_build.exceptions import CondaBuildException, DependencyNeedsBuildingError -from conda_build.index import _delegated_update_index, get_build_index -from conda_build.metadata import FIELDS, MetaData -from conda_build.post import ( - fix_permissions, - get_build_metadata, - post_build, - post_process, -) -from conda_build.render import ( - add_upstream_pins, - bldpkg_path, - distribute_variants, - execute_download_actions, - expand_outputs, - output_yaml, - render_recipe, - reparse, - try_download, -) -from conda_build.variants import ( - dict_of_lists_to_list_of_dicts, - get_package_variants, - set_language_env_vars, -) - -# used to get version +from . import __version__ as conda_build_version +from . import environ, noarch_python, source, tarcheck, utils from .conda_interface import ( CondaError, EntityEncoder, @@ -80,18 +43,50 @@ root_dir, url_path, ) +from .config import Config +from .create_test import create_all_test_files +from .exceptions import CondaBuildException, DependencyNeedsBuildingError +from .index import _delegated_update_index, get_build_index +from .metadata import FIELDS, MetaData +from .os_utils import external +from .post import ( + fix_permissions, + get_build_metadata, + post_build, + post_process, +) +from .render import ( + add_upstream_pins, + bldpkg_path, + distribute_variants, + execute_download_actions, + expand_outputs, + output_yaml, + render_recipe, + reparse, + try_download, +) from .utils import ( CONDA_PACKAGE_EXTENSION_V1, CONDA_PACKAGE_EXTENSION_V2, CONDA_PACKAGE_EXTENSIONS, env_var, glob, + on_linux, + on_mac, + on_win, shutil_move_more_retrying, tmp_chdir, + write_bat_activation_text, +) +from .variants import ( + dict_of_lists_to_list_of_dicts, + get_package_variants, + set_language_env_vars, ) -if sys.platform == "win32": - import conda_build.windows as windows +if on_win: + from . import windows if "bsd" in sys.platform: shell_path = "/bin/sh" @@ -181,7 +176,7 @@ def create_post_scripts(m: MetaData): def prefix_replacement_excluded(path): if path.endswith((".pyc", ".pyo")) or not isfile(path): return True - if sys.platform != "darwin" and islink(path): + if not on_mac and islink(path): # OSX does not allow hard-linking symbolic links, so we cannot # skip symbolic links (as we can on Linux) return True @@ -746,9 +741,7 @@ def perform_replacements(matches, prefix, verbose=False, diff=None): if match["type"] == "binary": if len(original) < len(new_string): print( - "ERROR :: Cannot replace {} with {} in binary file {}".format( - original, new_string, filename - ) + f"ERROR :: Cannot replace {original} with {new_string} in binary file {filename}" ) new_string = new_string.ljust(len(original), b"\0") assert len(new_string) == len(original) @@ -1041,9 +1034,7 @@ def copy_test_source_files(m, destination): except OSError as e: log = utils.get_logger(__name__) log.warn( - "Failed to copy {} into test files. Error was: {}".format( - f, str(e) - ) + f"Failed to copy {f} into test files. Error was: {str(e)}" ) for ext in ".pyc", ".pyo": for f in utils.get_ext_files(destination, ext): @@ -1313,9 +1304,9 @@ def record_prefix_files(m, files_with_prefix): and detect_binary_files_with_prefix ): print( - "File {} force-identified as 'binary', " + f"File {fn} force-identified as 'binary', " "But it is 'binary' anyway, suggest removing it from " - "`build/binary_has_prefix_files`".format(fn) + "`build/binary_has_prefix_files`" ) if fn in binary_has_prefix_files: binary_has_prefix_files.remove(fn) @@ -1328,9 +1319,9 @@ def record_prefix_files(m, files_with_prefix): mode = "text" elif fn in text_has_prefix_files and not len_text_has_prefix_files: print( - "File {} force-identified as 'text', " + f"File {fn} force-identified as 'text', " "But it is 'text' anyway, suggest removing it from " - "`build/has_prefix_files`".format(fn) + "`build/has_prefix_files`" ) if fn in text_has_prefix_files: text_has_prefix_files.remove(fn) @@ -1472,13 +1463,10 @@ def write_info_json(m: MetaData): fo.write( "# This file as created when building:\n" "#\n" - "# {}.tar.bz2 (on '{}')\n" + f"# {m.dist()}.tar.bz2 (on '{m.config.build_subdir}')\n" "#\n" "# It can be used to create the runtime environment of this package using:\n" - "# $ conda create --name --file ".format( - m.dist(), - m.config.build_subdir, - ) + "# $ conda create --name --file " ) for dist in sorted(runtime_deps + [" ".join(m.dist().rsplit("-", 2))]): fo.write("%s\n" % "=".join(dist.split())) @@ -1756,11 +1744,11 @@ def post_process_files(m: MetaData, initial_prefix_files): if len(missing): log = utils.get_logger(__name__) log.warning( - "The install/build script(s) for {} deleted the following " - "files (from dependencies) from the prefix:\n{}\n" + f"The install/build script(s) for {package_name} deleted the following " + f"files (from dependencies) from the prefix:\n{missing}\n" "This will cause the post-link checks to mis-report. Please " "try not to delete and files (DSOs in particular) from the " - "prefix".format(package_name, missing) + "prefix" ) get_build_metadata(m) create_post_scripts(m) @@ -1793,7 +1781,7 @@ def post_process_files(m: MetaData, initial_prefix_files): if m.noarch == 'python' and m.config.subdir == 'win-32': # Delete any PIP-created .exe launchers and fix entry_points.txt # .. but we need to provide scripts instead here. - from conda_build.post import caseless_sepless_fnmatch + from .post import caseless_sepless_fnmatch exes = caseless_sepless_fnmatch(new_files, 'Scripts/*.exe') for ff in exes: os.unlink(os.path.join(m.config.host_prefix, ff)) @@ -1806,11 +1794,9 @@ def post_process_files(m: MetaData, initial_prefix_files): tuple(f for f in new_files if m.config.meta_dir in join(host_prefix, f)), ) sys.exit( - "Error: Untracked file(s) {} found in conda-meta directory. This error usually comes " + f"Error: Untracked file(s) {meta_files} found in conda-meta directory. This error usually comes " "from using conda in the build script. Avoid doing this, as it can lead to packages " - "that include their dependencies.".format( - meta_files, - ) + "that include their dependencies." ) post_build(m, new_files, build_python=python) @@ -1919,9 +1905,12 @@ def bundle_conda(output, metadata: MetaData, env, stats, **kw): val = var.split("=", 1)[1] var = var.split("=", 1)[0] elif var not in os.environ: - raise ValueError( - f"env var '{var}' specified in script_env, but is not set." + warnings.warn( + "The environment variable '%s' specified in script_env is undefined." + % var, + UserWarning, ) + val = "" else: val = os.environ[var] env_output[var] = val @@ -1973,11 +1962,11 @@ def bundle_conda(output, metadata: MetaData, env, stats, **kw): for dep, env_var_name in dangerous_double_deps.items(): if all(dep in pkgs_list for pkgs_list in (build_pkgs, host_pkgs)): raise CondaBuildException( - "Empty package; {0} present in build and host deps. " - "You probably picked up the build environment's {0} " + f"Empty package; {dep} present in build and host deps. " + f"You probably picked up the build environment's {dep} " " executable. You need to alter your recipe to " - " use the {1} env var in your recipe to " - "run that executable.".format(dep, env_var_name) + f" use the {env_var_name} env var in your recipe to " + "run that executable." ) elif dep in build_pkgs and metadata.uses_new_style_compiler_activation: link = ( @@ -1985,10 +1974,10 @@ def bundle_conda(output, metadata: MetaData, env, stats, **kw): "define-metadata.html#host" ) raise CondaBuildException( - "Empty package; {0} dep present in build but not " - "host requirements. You need to move your {0} dep " - "to the host requirements section. See {1} for more " - "info.".format(dep, link) + f"Empty package; {dep} dep present in build but not " + f"host requirements. You need to move your {dep} dep " + f"to the host requirements section. See {link} for more " + "info." ) initial_files = set(utils.prefix_files(metadata.config.host_prefix)) @@ -2078,9 +2067,7 @@ def bundle_conda(output, metadata: MetaData, env, stats, **kw): except KeyError as e: log.warn( "Package doesn't have necessary files. It might be too old to inspect." - "Legacy noarch packages are known to fail. Full message was {}".format( - e - ) + f"Legacy noarch packages are known to fail. Full message was {e}" ) try: crossed_subdir = metadata.config.target_subdir @@ -2239,7 +2226,7 @@ def _write_sh_activation_text(file_handle, m): stack = "--stack" if m.is_cross else "" file_handle.write(f'conda activate {stack} "{build_prefix_path}"\n') - from conda_build.os_utils.external import find_executable + from .os_utils.external import find_executable ccache = find_executable("ccache", m.config.build_prefix, False) if ccache: @@ -2300,16 +2287,14 @@ def _write_activation_text(script_path, m): data = fh.read() fh.seek(0) if os.path.splitext(script_path)[1].lower() == ".bat": - if m.config.build_subdir.startswith("win"): - from conda_build.utils import write_bat_activation_text write_bat_activation_text(fh, m) elif os.path.splitext(script_path)[1].lower() == ".sh": _write_sh_activation_text(fh, m) else: log = utils.get_logger(__name__) log.warn( - "not adding activation to {} - I don't know how to do so for " - "this file type".format(script_path) + f"not adding activation to {script_path} - I don't know how to do so for " + "this file type" ) fh.write(data) @@ -2488,11 +2473,9 @@ def build( print( "Packages for ", m.path or m.name(), - "with variant {} " + f"with variant {m.get_hash_contents()} " "are already built and available from your configured channels " - "(including local) or are otherwise specified to be skipped.".format( - m.get_hash_contents() - ), + "(including local) or are otherwise specified to be skipped.", ) return default_return @@ -2757,10 +2740,8 @@ def build( if test_script: if not os.path.isfile(os.path.join(m.path, test_script)): raise ValueError( - "test script specified as {} does not exist. Please " - "check for typos or create the file and try again.".format( - test_script - ) + f"test script specified as {test_script} does not exist. Please " + "check for typos or create the file and try again." ) utils.copy_into( os.path.join(m.path, test_script), @@ -2953,8 +2934,8 @@ def guess_interpreter(script_filename): break else: raise NotImplementedError( - "Don't know how to run {} file. Please specify " - "script_interpreter for {} output".format(file_ext, script_filename) + f"Don't know how to run {file_ext} file. Please specify " + f"script_interpreter for {script_filename} output" ) return interpreter_command @@ -3240,7 +3221,7 @@ def _write_test_run_script( if py_files: test_python = metadata.config.test_python # use pythonw for import tests when osx_is_app is set - if metadata.get_value("build/osx_is_app") and sys.platform == "darwin": + if metadata.get_value("build/osx_is_app") and on_mac: test_python = test_python + "w" tf.write( '"{python}" -s "{test_file}"\n'.format( @@ -3295,11 +3276,7 @@ def _write_test_run_script( ) elif os.path.splitext(shell_file)[1] == ".sh": # TODO: Run the test/commands here instead of in run_test.py - tf.write( - '"{shell_path}" {trace}-e "{test_file}"\n'.format( - shell_path=shell_path, test_file=shell_file, trace=trace - ) - ) + tf.write(f'"{shell_path}" {trace}-e "{shell_file}"\n') def write_test_scripts( @@ -3562,7 +3539,7 @@ def test( env["CONDA_PATH_BACKUP"] = os.environ["CONDA_PATH_BACKUP"] if config.test_run_post: - from conda_build.utils import get_installed_packages + from .utils import get_installed_packages installed = get_installed_packages(metadata.config.test_prefix) files = installed[metadata.meta["package"]["name"]]["files"] @@ -3656,9 +3633,7 @@ def tests_failed(package_or_metadata, move_broken, broken_dir, config): try: shutil.move(pkg, dest) log.warn( - "Tests failed for {} - moving package to {}".format( - os.path.basename(pkg), broken_dir - ) + f"Tests failed for {os.path.basename(pkg)} - moving package to {broken_dir}" ) except OSError: pass @@ -3669,17 +3644,15 @@ def tests_failed(package_or_metadata, move_broken, broken_dir, config): def check_external(): - if sys.platform.startswith("linux"): + if on_linux: patchelf = external.find_executable("patchelf") if patchelf is None: sys.exit( "Error:\n" - " Did not find 'patchelf' in: {}\n" + f" Did not find 'patchelf' in: {os.pathsep.join(external.dir_paths)}\n" " 'patchelf' is necessary for building conda packages on Linux with\n" " relocatable ELF libraries. You can install patchelf using conda install\n" - " patchelf.\n".format( - os.pathsep.join(external.dir_paths), - ) + " patchelf.\n" ) @@ -3859,8 +3832,8 @@ def build_tree( DependencyNeedsBuildingError, ) as e: log.warn( - "Skipping downstream test for spec {}; was " - "unsatisfiable. Error was {}".format(dep, e) + f"Skipping downstream test for spec {dep}; was " + f"unsatisfiable. Error was {e}" ) continue # make sure to download that package to the local cache if not there @@ -3933,9 +3906,7 @@ def build_tree( if pkg in to_build_recursive: cfg.clean(remove_folders=False) raise RuntimeError( - "Can't build {} due to environment creation error:\n".format( - recipe - ) + f"Can't build {recipe} due to environment creation error:\n" + str(e.message) + "\n" + extra_help @@ -3976,11 +3947,9 @@ def build_tree( MatchSpec(matchspec), dep_meta[0], metadata ): print( - ( - "Missing dependency {0}, but found" - + " recipe directory, so building " - + "{0} first" - ).format(pkg) + f"Missing dependency {pkg}, but found " + f"recipe directory, so building " + f"{pkg} first" ) add_recipes.append(recipe_dir) available = True @@ -4012,7 +3981,7 @@ def build_tree( handle_pypi_upload(wheels, config=config) # Print the variant information for each package because it is very opaque and never printed. - from conda_build.inspect_pkg import get_hash_input + from .inspect_pkg import get_hash_input hash_inputs = get_hash_input(tarballs) print( @@ -4058,7 +4027,7 @@ def build_tree( def handle_anaconda_upload(paths, config): - from conda_build.os_utils.external import find_executable + from .os_utils.external import find_executable paths = utils.ensure_list(paths) @@ -4089,7 +4058,7 @@ def handle_anaconda_upload(paths, config): no_upload_message += ( "\n" "# To have conda build upload to anaconda.org automatically, use\n" - "# {}conda config --set anaconda_upload yes\n".format(prompter) + f"# {prompter}conda config --set anaconda_upload yes\n" ) no_upload_message += f"anaconda upload{joiner}" + joiner.join(paths) @@ -4102,7 +4071,7 @@ def handle_anaconda_upload(paths, config): sys.exit( "Error: cannot locate anaconda command (required for upload)\n" "# Try:\n" - "# {}conda install anaconda-client".format(prompter) + f"# {prompter}conda install anaconda-client" ) cmd = [ anaconda, diff --git a/conda_build/cli/main_build.py b/conda_build/cli/main_build.py index 5b5bb67ef6..e66ff0e11b 100644 --- a/conda_build/cli/main_build.py +++ b/conda_build/cli/main_build.py @@ -596,8 +596,8 @@ def main(): sys.exit(1) except filelock.Timeout as e: print( - "File lock on {} could not be obtained. You might need to try fewer builds at once." - " Otherwise, run conda clean --lock".format(e.lock_file) + f"File lock on {e.lock_file} could not be obtained. You might need to try fewer builds at once." + " Otherwise, run conda clean --lock" ) sys.exit(1) return diff --git a/conda_build/cli/main_inspect.py b/conda_build/cli/main_inspect.py index aa38ce51f1..7f9a30c847 100644 --- a/conda_build/cli/main_inspect.py +++ b/conda_build/cli/main_inspect.py @@ -134,8 +134,10 @@ def parse_args(args): "--test-installable", "-t", action="store_true", - help="""Test every package in the channel to see if it is installable - by conda.""", + help=( + "DEPRECATED. This is the default (and only) behavior. " + "Test every package in the channel to see if it is installable by conda." + ), ) channels.add_argument( "channel", @@ -184,13 +186,9 @@ def execute(args): if not args.subcommand: parser.print_help() - exit() - + sys.exit(0) elif args.subcommand == "channels": - if not args.test_installable: - parser.error("At least one option (--test-installable) is required.") - else: - print(api.test_installable(args.channel)) + print(api.test_installable(args.channel)) elif args.subcommand == "linkages": print( api.inspect_linkages( @@ -219,7 +217,7 @@ def execute(args): elif args.subcommand == "hash-inputs": pprint(api.inspect_hash_inputs(args.packages)) else: - raise ValueError(f"Unrecognized subcommand: {args.subcommand}.") + parser.error(f"Unrecognized subcommand: {args.subcommand}.") @deprecated("3.26.0", "24.1.0", addendum="Use `conda inspect` instead.") diff --git a/conda_build/cli/main_render.py b/conda_build/cli/main_render.py index 9026fb1b57..cdd831021b 100644 --- a/conda_build/cli/main_render.py +++ b/conda_build/cli/main_render.py @@ -15,7 +15,6 @@ from ..utils import LoggingContext from ..variants import get_package_variants, set_language_env_vars -on_win = sys.platform == "win32" log = logging.getLogger(__name__) @@ -194,7 +193,7 @@ def execute(args, print_results=True): config = get_or_merge_config(None, **args.__dict__) variants = get_package_variants(args.recipe, config, variants=args.variants) - from conda_build.build import get_all_replacements + from ..build import get_all_replacements get_all_replacements(variants) set_language_env_vars(variants) @@ -217,7 +216,7 @@ def execute(args, print_results=True): if args.file and len(metadata_tuples) > 1: log.warning( "Multiple variants rendered. " - "Only one will be written to the file you specified ({}).".format(args.file) + f"Only one will be written to the file you specified ({args.file})." ) if print_results: diff --git a/conda_build/cli/validators.py b/conda_build/cli/validators.py index b1c1144662..e21304e074 100644 --- a/conda_build/cli/validators.py +++ b/conda_build/cli/validators.py @@ -5,8 +5,7 @@ import os from argparse import ArgumentError -from conda_build import utils -from conda_build.utils import CONDA_PACKAGE_EXTENSIONS +from ..utils import CONDA_PACKAGE_EXTENSIONS, is_conda_pkg CONDA_PKG_OR_RECIPE_ERROR_MESSAGE = ( "\nUnable to parse provided recipe directory or package file.\n\n" @@ -21,7 +20,7 @@ def validate_is_conda_pkg_or_recipe_dir(arg_val: str) -> str: """ if os.path.isdir(arg_val): return arg_val - elif utils.is_conda_pkg(arg_val): + elif is_conda_pkg(arg_val): return arg_val else: raise ArgumentError(None, CONDA_PKG_OR_RECIPE_ERROR_MESSAGE) diff --git a/conda_build/conda_interface.py b/conda_build/conda_interface.py index 10bd300ed3..78eeb03f95 100644 --- a/conda_build/conda_interface.py +++ b/conda_build/conda_interface.py @@ -26,11 +26,11 @@ UnsatisfiableError, ) from conda.exports import ( # noqa: F401 - ArgumentParser, # noqa: F401 + ArgumentParser, Channel, Completer, - CondaSession, # noqa: F401 - EntityEncoder, # noqa: F401 + CondaSession, + EntityEncoder, FileMode, InstalledPackages, MatchSpec, @@ -41,15 +41,15 @@ TemporaryDirectory, TmpDownload, Unsatisfiable, - VersionOrder, # noqa: F401 - _toposort, # noqa: F401 + VersionOrder, + _toposort, add_parser_channels, add_parser_prefix, display_actions, download, execute_actions, execute_plan, - get_index, # noqa: F401 + get_index, handle_proxy_407, hashsum_file, human_bytes, @@ -164,7 +164,7 @@ def get_installed_version(prefix, pkgs): Primarily used by conda-forge, but may be useful in general for checking when a package needs to be updated """ - from conda_build.utils import ensure_list + from .utils import ensure_list pkgs = ensure_list(pkgs) linked_pkgs = linked(prefix) diff --git a/conda_build/config.py b/conda_build/config.py index 67f0e9f6a0..89c158e52d 100644 --- a/conda_build/config.py +++ b/conda_build/config.py @@ -10,7 +10,6 @@ import os import re import shutil -import sys import time from collections import namedtuple from os.path import abspath, expanduser, expandvars, join @@ -26,10 +25,15 @@ url_path, ) from .deprecations import deprecated -from .utils import get_build_folders, get_conda_operation_locks, get_logger, rm_rf +from .utils import ( + get_build_folders, + get_conda_operation_locks, + get_logger, + on_win, + rm_rf, +) from .variants import get_default_variant -on_win = sys.platform == "win32" invocation_time = "" @@ -551,9 +555,7 @@ def CONDA_R(self, value): self.variant["r_base"] = value def _get_python(self, prefix, platform): - if platform.startswith("win") or ( - platform == "noarch" and sys.platform == "win32" - ): + if platform.startswith("win") or (platform == "noarch" and on_win): if os.path.isfile(os.path.join(prefix, "python_d.exe")): res = join(prefix, "python_d.exe") else: @@ -580,9 +582,7 @@ def _get_lua(self, prefix, platform): return res def _get_r(self, prefix, platform): - if platform.startswith("win") or ( - platform == "noarch" and sys.platform == "win32" - ): + if platform.startswith("win") or (platform == "noarch" and on_win): res = join(prefix, "Scripts", "R.exe") # MRO test: if not os.path.exists(res): diff --git a/conda_build/convert.py b/conda_build/convert.py index 5c283cb98d..c2882d1508 100644 --- a/conda_build/convert.py +++ b/conda_build/convert.py @@ -14,7 +14,7 @@ import tempfile from pathlib import Path -from conda_build.utils import filter_info_files, walk +from .utils import filter_info_files, walk def retrieve_c_extensions(file_path, show_imports=False): @@ -113,9 +113,7 @@ def retrieve_python_version(file_path): ) build_version = re.sub(r"\A.*py\d\d.*\Z", "python", index["build"]) - return "{}{}.{}".format( - build_version, build_version_number[0], build_version_number[1] - ) + return f"{build_version}{build_version_number[0]}.{build_version_number[1]}" def extract_temporary_directory(file_path): @@ -200,11 +198,7 @@ def update_index_file(temp_dir, target_platform, dependencies, verbose): if verbose: print("Updating platform from {} to {}".format(index["platform"], platform)) print("Updating subdir from {} to {}".format(index["subdir"], target_platform)) - print( - "Updating architecture from {} to {}".format( - source_architecture, architecture - ) - ) + print(f"Updating architecture from {source_architecture} to {architecture}") index["platform"] = platform index["subdir"] = target_platform @@ -719,9 +713,7 @@ def convert_from_unix_to_windows( ) prefixes.add( - "/opt/anaconda1anaconda2anaconda3 text Scripts/{}-script.py\n".format( - retrieve_executable_name(script) - ) + f"/opt/anaconda1anaconda2anaconda3 text Scripts/{retrieve_executable_name(script)}-script.py\n" ) new_bin_path = os.path.join(temp_dir, "Scripts") @@ -766,9 +758,7 @@ def convert_from_windows_to_unix( remove_executable(directory, script) prefixes.add( - "/opt/anaconda1anaconda2anaconda3 text bin/{}\n".format( - retrieve_executable_name(script) - ) + f"/opt/anaconda1anaconda2anaconda3 text bin/{retrieve_executable_name(script)}\n" ) new_bin_path = os.path.join(temp_dir, "bin") @@ -824,8 +814,8 @@ def conda_convert( if len(retrieve_c_extensions(file_path)) > 0 and not force: sys.exit( - "WARNING: Package {} contains C extensions; skipping conversion. " - "Use -f to force conversion.".format(os.path.basename(file_path)) + f"WARNING: Package {os.path.basename(file_path)} contains C extensions; skipping conversion. " + "Use -f to force conversion." ) conversion_platform, source_platform, architecture = retrieve_package_platform( @@ -853,16 +843,14 @@ def conda_convert( for platform in platforms: if platform == source_platform_architecture: print( - "Source platform '{}' and target platform '{}' are identical. " - "Skipping conversion.".format(source_platform_architecture, platform) + f"Source platform '{source_platform_architecture}' and target platform '{platform}' are identical. " + "Skipping conversion." ) continue if not quiet: print( - "Converting {} from {} to {}".format( - os.path.basename(file_path), source_platform_architecture, platform - ) + f"Converting {os.path.basename(file_path)} from {source_platform_architecture} to {platform}" ) if platform.startswith(("osx", "linux")) and conversion_platform == "unix": diff --git a/conda_build/create_test.py b/conda_build/create_test.py index 35511ef503..334645dcac 100644 --- a/conda_build/create_test.py +++ b/conda_build/create_test.py @@ -229,16 +229,14 @@ def create_pl_files(m: MetaData, test_dir: os.PathLike) -> bool: # Don't try to print version for complex imports if " " not in name: print( - ( - "if (defined {0}->VERSION) {{\n" - + "\tmy $given_version = {0}->VERSION;\n" - + "\t$given_version =~ s/0+$//;\n" - + "\tdie('Expected version ' . $expected_version . ' but" - + " found ' . $given_version) unless ($expected_version " - + "eq $given_version);\n" - + "\tprint('\tusing version ' . {0}->VERSION . '\n');\n" - + "\n}}" - ).format(name), + f"if (defined {name}->VERSION) {{\n" + f"\tmy $given_version = {name}->VERSION;\n" + f"\t$given_version =~ s/0+$//;\n" + f"\tdie('Expected version ' . $expected_version . ' but" + f" found ' . $given_version) unless ($expected_version " + f"eq $given_version);\n" + f"\tprint('\tusing version ' . {name}->VERSION . '\n');\n" + f"\n}}", file=fo, ) return tf if (tf_exists or imports) else False diff --git a/conda_build/deprecations.py b/conda_build/deprecations.py index 4d09205da5..372f700876 100644 --- a/conda_build/deprecations.py +++ b/conda_build/deprecations.py @@ -41,7 +41,7 @@ def __call__( *, addendum: str | None = None, stack: int = 0, - ) -> Callable[(Callable), Callable]: + ) -> Callable[[Callable], Callable]: """Deprecation decorator for functions, methods, & classes. :param deprecate_in: Version in which code will be marked as deprecated. @@ -83,7 +83,7 @@ def argument( rename: str | None = None, addendum: str | None = None, stack: int = 0, - ) -> Callable[(Callable), Callable]: + ) -> Callable[[Callable], Callable]: """Deprecation decorator for keyword arguments. :param deprecate_in: Version in which code will be marked as deprecated. @@ -132,7 +132,7 @@ def action( self, deprecate_in: str, remove_in: str, - action: Action, + action: type[Action], *, addendum: str | None = None, stack: int = 0, @@ -262,7 +262,10 @@ def topic( """ # detect function name and generate message category, message = self._generate_message( - deprecate_in, remove_in, topic, addendum + deprecate_in, + remove_in, + topic, + addendum, ) # alert developer that it's time to remove something @@ -283,12 +286,20 @@ def _get_module(self, stack: int) -> tuple[ModuleType, str]: try: frame = sys._getframe(2 + stack) module = inspect.getmodule(frame) - return (module, module.__name__) - except (IndexError, AttributeError): - raise DeprecatedError("unable to determine the calling module") from None + if module is not None: + return (module, module.__name__) + except IndexError: + # IndexError: 2 + stack is out of range + pass + + raise DeprecatedError("unable to determine the calling module") def _generate_message( - self, deprecate_in: str, remove_in: str, prefix: str, addendum: str + self, + deprecate_in: str, + remove_in: str, + prefix: str, + addendum: str | None, ) -> tuple[type[Warning] | None, str]: """Deprecation decorator for functions, methods, & classes. @@ -301,6 +312,7 @@ def _generate_message( deprecate_version = parse(deprecate_in) remove_version = parse(remove_in) + category: type[Warning] | None if self._version < deprecate_version: category = PendingDeprecationWarning warning = f"is pending deprecation and will be removed in {remove_in}." diff --git a/conda_build/develop.py b/conda_build/develop.py index 5e9c892e36..5b83185fdc 100644 --- a/conda_build/develop.py +++ b/conda_build/develop.py @@ -4,9 +4,9 @@ import sys from os.path import abspath, exists, expanduser, isdir, join -from conda_build.os_utils.external import find_executable -from conda_build.post import mk_relative_osx -from conda_build.utils import check_call_env, get_site_packages, rec_glob +from .os_utils.external import find_executable +from .post import mk_relative_osx +from .utils import check_call_env, get_site_packages, on_mac, rec_glob def relink_sharedobjects(pkg_path, build_prefix): @@ -24,7 +24,7 @@ def relink_sharedobjects(pkg_path, build_prefix): # find binaries in package dir and make them relocatable bin_files = rec_glob(pkg_path, [".so"]) for b_file in bin_files: - if sys.platform == "darwin": + if on_mac: mk_relative_osx(b_file, build_prefix) else: print("Nothing to do on Linux or Windows.") diff --git a/conda_build/environ.py b/conda_build/environ.py index c165bdeba2..319b4e7bf4 100644 --- a/conda_build/environ.py +++ b/conda_build/environ.py @@ -14,14 +14,7 @@ from glob import glob from os.path import join, normpath -from conda_build import utils -from conda_build.exceptions import BuildLockError, DependencyNeedsBuildingError -from conda_build.features import feature_list -from conda_build.index import get_build_index -from conda_build.os_utils import external -from conda_build.utils import ensure_list, env_var, prepend_bin_path -from conda_build.variants import get_default_variant - +from . import utils from .conda_interface import ( CondaError, LinkError, @@ -42,7 +35,19 @@ root_dir, ) from .deprecations import deprecated +from .exceptions import BuildLockError, DependencyNeedsBuildingError +from .features import feature_list +from .index import get_build_index from .metadata import MetaData +from .os_utils import external +from .utils import ( + ensure_list, + env_var, + on_mac, + on_win, + prepend_bin_path, +) +from .variants import get_default_variant # these are things that we provide env vars for more explicitly. This list disables the # pass-through of variant values to env vars for these keys. @@ -149,7 +154,7 @@ def verify_git_repo( stderr=stderr, ) except subprocess.CalledProcessError: - if sys.platform == "win32" and cache_dir.startswith("/"): + if on_win and cache_dir.startswith("/"): cache_dir = utils.convert_unix_path_to_win(cache_dir) remote_details = utils.check_output_env( [git_exe, "--git-dir", cache_dir, "remote", "-v"], @@ -161,7 +166,7 @@ def verify_git_repo( # on windows, remote URL comes back to us as cygwin or msys format. Python doesn't # know how to normalize it. Need to convert it to a windows path. - if sys.platform == "win32" and remote_url.startswith("/"): + if on_win and remote_url.startswith("/"): remote_url = utils.convert_unix_path_to_win(git_url) if os.path.exists(remote_url): @@ -491,15 +496,17 @@ def meta_vars(meta: MetaData, skip_build_id=False): value = os.getenv(var_name) if value is None: warnings.warn( - "The environment variable '%s' is undefined." % var_name, UserWarning + "The environment variable '%s' specified in script_env is undefined." + % var_name, + UserWarning, ) else: d[var_name] = value warnings.warn( - "The environment variable '%s' is being passed through with value '%s'. " + f"The environment variable '{var_name}' is being passed through with value " + f"'{'' if meta.config.suppress_variables else value}'. " "If you are splitting build and test phases with --no-test, please ensure " - "that this value is also set similarly at test time." - % (var_name, "" if meta.config.suppress_variables else value), + "that this value is also set similarly at test time.", UserWarning, ) @@ -518,7 +525,7 @@ def meta_vars(meta: MetaData, skip_build_id=False): git_url = meta.get_value("source/0/git_url") if os.path.exists(git_url): - if sys.platform == "win32": + if on_win: git_url = utils.convert_unix_path_to_win(git_url) # If git_url is a relative path instead of a url, convert it to an abspath git_url = normpath(join(meta.path, git_url)) @@ -558,7 +565,7 @@ def meta_vars(meta: MetaData, skip_build_id=False): @lru_cache(maxsize=None) def get_cpu_count(): - if sys.platform == "darwin": + if on_mac: # multiprocessing.cpu_count() is not reliable on OSX # See issue #645 on github.com/conda/conda-build out, _ = subprocess.Popen( @@ -754,7 +761,7 @@ def os_vars(m, prefix): if not m.config.activate: d = prepend_bin_path(d, m.config.host_prefix) - if sys.platform == "win32": + if on_win: windows_vars(m, get_default, prefix) else: unix_vars(m, get_default, prefix) @@ -1224,8 +1231,8 @@ def clean_pkg_cache(dist, config): locks = get_pkg_dirs_locks([config.bldpkgs_dir] + pkgs_dirs, config) with utils.try_acquire_locks(locks, timeout=config.timeout): rmplan = [ - "RM_EXTRACTED {0} local::{0}".format(dist), - "RM_FETCHED {0} local::{0}".format(dist), + f"RM_EXTRACTED {dist} local::{dist}", + f"RM_FETCHED {dist} local::{dist}", ] execute_plan(rmplan) diff --git a/conda_build/exceptions.py b/conda_build/exceptions.py index 857141fb4f..f38706786a 100644 --- a/conda_build/exceptions.py +++ b/conda_build/exceptions.py @@ -96,9 +96,7 @@ def __str__(self): @property def message(self): - return "Unsatisfiable dependencies for platform {}: {}".format( - self.subdir, set(self.matchspecs) - ) + return f"Unsatisfiable dependencies for platform {self.subdir}: {set(self.matchspecs)}" class RecipeError(CondaBuildException): diff --git a/conda_build/features.py b/conda_build/features.py index 4b506cbc80..414b15333f 100644 --- a/conda_build/features.py +++ b/conda_build/features.py @@ -16,7 +16,7 @@ if key in env_vars: if value not in ("0", "1"): sys.exit( - "Error: did not expect environment variable '%s' " - "being set to '%s' (not '0' or '1')" % (key, value) + f"Error: did not expect environment variable '{key}' " + f"being set to '{value}' (not '0' or '1')" ) feature_list.append((key[8:].lower(), bool(int(value)))) diff --git a/conda_build/index.py b/conda_build/index.py index 5f296a164d..8ac164dccf 100644 --- a/conda_build/index.py +++ b/conda_build/index.py @@ -48,8 +48,7 @@ from yaml.reader import ReaderError from yaml.scanner import ScannerError -from conda_build import conda_interface, utils - +from . import conda_interface, utils from .conda_interface import ( CondaError, CondaHTTPError, @@ -67,10 +66,10 @@ CONDA_PACKAGE_EXTENSION_V1, CONDA_PACKAGE_EXTENSION_V2, CONDA_PACKAGE_EXTENSIONS, - FileNotFoundError, JSONDecodeError, get_logger, glob, + on_win, ) log = get_logger(__name__) @@ -123,9 +122,7 @@ def map(self, func, *iterables): MAX_THREADS_DEFAULT = ( os.cpu_count() if (hasattr(os, "cpu_count") and os.cpu_count() > 1) else 1 ) -if ( - sys.platform == "win32" -): # see https://github.com/python/cpython/commit/8ea0fd85bc67438f679491fae29dfe0a3961900a +if on_win: # see https://github.com/python/cpython/commit/8ea0fd85bc67438f679491fae29dfe0a3961900a MAX_THREADS_DEFAULT = min(48, MAX_THREADS_DEFAULT) LOCK_TIMEOUT_SECS = 3 * 3600 LOCKFILE_NAME = ".lock" @@ -1725,10 +1722,8 @@ def _create_patch_instructions(self, subdir, repodata, patch_generator=None): else: if patch_generator: raise ValueError( - "Specified metadata patch file '{}' does not exist. Please try an absolute " - "path, or examine your relative path carefully with respect to your cwd.".format( - patch_generator - ) + f"Specified metadata patch file '{patch_generator}' does not exist. Please try an absolute " + "path, or examine your relative path carefully with respect to your cwd." ) return {} diff --git a/conda_build/inspect_pkg.py b/conda_build/inspect_pkg.py index 87bc372766..d5650681f6 100644 --- a/conda_build/inspect_pkg.py +++ b/conda_build/inspect_pkg.py @@ -4,46 +4,47 @@ import json import os -import re import sys -import tempfile from collections import defaultdict from functools import lru_cache from itertools import groupby from operator import itemgetter from os.path import abspath, basename, dirname, exists, join from pathlib import Path +from tempfile import TemporaryDirectory from typing import Iterable, Literal +from conda.api import Solver +from conda.core.index import get_index from conda.core.prefix_data import PrefixData from conda.models.dist import Dist from conda.models.records import PrefixRecord from conda.resolve import MatchSpec -from conda_build.conda_interface import ( - display_actions, - get_index, - install_actions, +from . import conda_interface +from .conda_interface import ( linked_data, specs_from_args, ) -from conda_build.os_utils.ldd import ( +from .deprecations import deprecated +from .os_utils.ldd import ( get_linkages, get_package_obj_files, get_untracked_obj_files, ) -from conda_build.os_utils.liefldd import codefile_class, machofile -from conda_build.os_utils.macho import get_rpaths, human_filetype -from conda_build.utils import ( +from .os_utils.liefldd import codefile_class, machofile +from .os_utils.macho import get_rpaths, human_filetype +from .utils import ( comma_join, ensure_list, get_logger, + on_linux, + on_mac, + on_win, package_has_file, - rm_rf, ) -from .deprecations import deprecated -from .utils import on_mac, on_win +log = get_logger(__name__) @deprecated("3.28.0", "24.1.0") @@ -131,23 +132,21 @@ def __str__(self): untracked_package = _untracked_package() +@deprecated.argument("24.1.0", "24.3.0", "platform", rename="subdir") +@deprecated.argument("24.1.0", "24.3.0", "prepend") +@deprecated.argument("24.1.0", "24.3.0", "minimal_hint") def check_install( - packages, platform=None, channel_urls=(), prepend=True, minimal_hint=False -): - prefix = tempfile.mkdtemp("conda") - try: - specs = specs_from_args(packages) - index = get_index( - channel_urls=channel_urls, prepend=prepend, platform=platform, prefix=prefix - ) - actions = install_actions( - prefix, index, specs, pinned=False, minimal_hint=minimal_hint - ) - display_actions(actions, index) - return actions - finally: - rm_rf(prefix) - return None + packages: Iterable[str], + subdir: str | None = None, + channel_urls: Iterable[str] = (), +) -> None: + with TemporaryDirectory() as prefix: + Solver( + prefix, + channel_urls, + [subdir or conda_interface.subdir], + specs_from_args(packages), + ).solve_for_transaction(ignore_pinned=True).print_transaction_summary() def print_linkages( @@ -187,9 +186,9 @@ def print_linkages( def replace_path(binary, path, prefix): - if sys.platform.startswith("linux"): + if on_linux: return abspath(path) - elif sys.platform.startswith("darwin"): + elif on_mac: if path == basename(binary): return abspath(join(prefix, binary)) if "@rpath" in path: @@ -211,61 +210,39 @@ def replace_path(binary, path, prefix): return "not found" -def test_installable(channel="defaults"): +def test_installable(channel: str = "defaults") -> bool: success = True - log = get_logger(__name__) - has_py = re.compile(r"py(\d)(\d)") - for platform in ["osx-64", "linux-32", "linux-64", "win-32", "win-64"]: - log.info("######## Testing platform %s ########", platform) - channels = [channel] - index = get_index(channel_urls=channels, prepend=False, platform=platform) - for _, rec in index.items(): - # If we give channels at the command line, only look at - # packages from those channels (not defaults). - if channel != "defaults" and rec.get("schannel", "defaults") == "defaults": - continue - name = rec["name"] + for subdir in ["osx-64", "linux-32", "linux-64", "win-32", "win-64"]: + log.info("######## Testing subdir %s ########", subdir) + for prec in get_index(channel_urls=[channel], prepend=False, platform=subdir): + name = prec["name"] if name in {"conda", "conda-build"}: # conda can only be installed in the root environment continue - if name.endswith("@"): + elif name.endswith("@"): # this is a 'virtual' feature record that conda adds to the index for the solver # and should be ignored here continue - # Don't fail just because the package is a different version of Python - # than the default. We should probably check depends rather than the - # build string. - build = rec["build"] - match = has_py.search(build) - assert match if "py" in build else True, build - if match: - additional_packages = [f"python={match.group(1)}.{match.group(2)}"] - else: - additional_packages = [] - version = rec["version"] + version = prec["version"] log.info("Testing %s=%s", name, version) try: - install_steps = check_install( - [name + "=" + version] + additional_packages, - channel_urls=channels, + check_install( + [f"{name}={version}"], + channel_urls=[channel], prepend=False, - platform=platform, + subdir=subdir, ) - success &= bool(install_steps) - except KeyboardInterrupt: - raise - # sys.exit raises an exception that doesn't subclass from Exception - except BaseException as e: + except Exception as err: success = False log.error( - "FAIL: %s %s on %s with %s (%s)", + "[%s/%s::%s=%s] %s", + channel, + subdir, name, version, - platform, - additional_packages, - e, + repr(err), ) return success diff --git a/conda_build/jinja_context.py b/conda_build/jinja_context.py index eaadc3a100..6933f631ad 100644 --- a/conda_build/jinja_context.py +++ b/conda_build/jinja_context.py @@ -214,7 +214,7 @@ def load_setup_py_data( else: raise CondaBuildException( "Could not render recipe - need modules " - 'installed in root env. Import error was "{}"'.format(e) + f'installed in root env. Import error was "{e}"' ) # cleanup: we must leave the source tree empty unless the source code is already present rm_rf(os.path.join(m.config.work_dir, "_load_setup_py_data.py")) @@ -349,11 +349,11 @@ def pin_compatible( compatibility = apply_pin_expressions(version, min_pin, max_pin) if not compatibility and not permit_undefined_jinja and not bypass_env_check: - check = re.compile(r"pin_compatible\s*\(\s*[" '"]{}[' '"]'.format(package_name)) + check = re.compile(rf'pin_compatible\s*\(\s*["]{package_name}["]') if check.search(m.extract_requirements_text()): raise RuntimeError( - "Could not get compatibility information for {} package. " - "Is it one of your host dependencies?".format(package_name) + f"Could not get compatibility information for {package_name} package. " + "Is it one of your host dependencies?" ) return ( " ".join((package_name, compatibility)) @@ -409,10 +409,7 @@ def pin_subpackage_against_outputs( ] ) else: - pin = "{} {}".format( - sp_m.name(), - apply_pin_expressions(sp_m.version(), min_pin, max_pin), - ) + pin = f"{sp_m.name()} {apply_pin_expressions(sp_m.version(), min_pin, max_pin)}" else: pin = matching_package_keys[0][0] return pin @@ -463,9 +460,9 @@ def pin_subpackage( pin = subpackage_name if not permit_undefined_jinja and not allow_no_other_outputs: raise ValueError( - "Didn't find subpackage version info for '{}', which is used in a" + f"Didn't find subpackage version info for '{subpackage_name}', which is used in a" " pin_subpackage expression. Is it actually a subpackage? If not, " - "you want pin_compatible instead.".format(subpackage_name) + "you want pin_compatible instead." ) return pin diff --git a/conda_build/license_family.py b/conda_build/license_family.py index 2833974066..976cc1b33a 100644 --- a/conda_build/license_family.py +++ b/conda_build/license_family.py @@ -3,8 +3,8 @@ import re import string -from conda_build import exceptions -from conda_build.utils import comma_join +from . import exceptions +from .utils import comma_join allowed_license_families = """ AGPL @@ -109,7 +109,7 @@ def ensure_valid_license_family(meta): if remove_special_characters(normalize(license_family)) not in allowed_families: raise RuntimeError( exceptions.indent( - "about/license_family '%s' not allowed. Allowed families are %s." - % (license_family, comma_join(sorted(allowed_license_families))) + f"about/license_family '{license_family}' not allowed. " + f"Allowed families are {comma_join(sorted(allowed_license_families))}." ) ) diff --git a/conda_build/metadata.py b/conda_build/metadata.py index f25e57f280..906ce0b628 100644 --- a/conda_build/metadata.py +++ b/conda_build/metadata.py @@ -17,11 +17,13 @@ from bs4 import UnicodeDammit -from conda_build import exceptions, utils, variants -from conda_build.config import Config, get_or_merge_config -from conda_build.features import feature_list -from conda_build.license_family import ensure_valid_license_family -from conda_build.utils import ( +from . import exceptions, utils, variants +from .conda_interface import MatchSpec, envs_dirs, md5_file +from .config import Config, get_or_merge_config +from .deprecations import deprecated +from .features import feature_list +from .license_family import ensure_valid_license_family +from .utils import ( DEFAULT_SUBDIRS, HashableDict, ensure_list, @@ -29,11 +31,9 @@ find_recipe, get_installed_packages, insert_variant_versions, + on_win, ) -from .conda_interface import MatchSpec, envs_dirs, md5_file -from .deprecations import deprecated - try: import yaml except ImportError: @@ -75,8 +75,6 @@ def remove_constructor(cls, tag): StringifyNumbersLoader.remove_constructor("tag:yaml.org,2002:float") StringifyNumbersLoader.remove_constructor("tag:yaml.org,2002:int") -on_win = sys.platform == "win32" - # arches that don't follow exact names in the subdir need to be mapped here ARCH_MAP = {"32": "x86", "64": "x86_64"} @@ -347,9 +345,7 @@ def _trim_None_strings(meta_dict): meta_dict[key] = keep else: log.debug( - "found unrecognized data type in dictionary: {}, type: {}".format( - value, type(value) - ) + f"found unrecognized data type in dictionary: {value}, type: {type(value)}" ) return meta_dict @@ -466,14 +462,14 @@ def parse(data, config, path=None): or (hasattr(res[field], "__iter__") and not isinstance(res[field], str)) ): raise RuntimeError( - "The %s field should be a dict or list of dicts, not " - "%s in file %s." % (field, res[field].__class__.__name__, path) + f"The {field} field should be a dict or list of dicts, not " + f"{res[field].__class__.__name__} in file {path}." ) else: if not isinstance(res[field], dict): raise RuntimeError( - "The %s field should be a dict, not %s in file %s." - % (field, res[field].__class__.__name__, path) + f"The {field} field should be a dict, not " + f"{res[field].__class__.__name__} in file {path}." ) ensure_valid_fields(res) @@ -973,7 +969,7 @@ def finalize_outputs_pass( log = utils.get_logger(__name__) log.warn( "Could not finalize metadata due to missing dependencies: " - "{}".format(e.packages) + f"{e.packages}" ) outputs[ ( @@ -1302,8 +1298,8 @@ def parse_until_resolved( bypass_env_check=bypass_env_check, ) sys.exit( - "Undefined Jinja2 variables remain ({}). Please enable " - "source downloading and try again.".format(self.undefined_jinja_vars) + f"Undefined Jinja2 variables remain ({self.undefined_jinja_vars}). Please enable " + "source downloading and try again." ) # always parse again at the end, too. @@ -1562,20 +1558,18 @@ def ms_depends(self, typ="run"): for c in "=!@#$%^&*:;\"'\\|<>?/": if c in ms.name: sys.exit( - "Error: bad character '%s' in package name " - "dependency '%s'" % (c, ms.name) + f"Error: bad character '{c}' in package name " + f"dependency '{ms.name}'" ) parts = spec.split() if len(parts) >= 2: if parts[1] in {">", ">=", "=", "==", "!=", "<", "<="}: msg = ( - "Error: bad character '%s' in package version " - "dependency '%s'" % (parts[1], ms.name) + f"Error: bad character '{parts[1]}' in package version " + f"dependency '{ms.name}'" ) if len(parts) >= 3: - msg += "\nPerhaps you meant '{} {}{}'".format( - ms.name, parts[1], parts[2] - ) + msg += f"\nPerhaps you meant '{ms.name} {parts[1]}{parts[2]}'" sys.exit(msg) specs[spec] = ms return list(specs.values()) @@ -1780,7 +1774,7 @@ def has_prefix_files(self): ret = ensure_list(self.get_value("build/has_prefix_files", [])) if not isinstance(ret, list): raise RuntimeError("build/has_prefix_files should be a list of paths") - if sys.platform == "win32": + if on_win: if any("\\" in i for i in ret): raise RuntimeError( "build/has_prefix_files paths must use / " @@ -1795,7 +1789,7 @@ def ignore_prefix_files(self): "build/ignore_prefix_files should be boolean or a list of paths " "(optionally globs)" ) - if sys.platform == "win32": + if on_win: if isinstance(ret, list) and any("\\" in i for i in ret): raise RuntimeError( "build/ignore_prefix_files paths must use / " @@ -1827,7 +1821,7 @@ def binary_relocation(self): "build/binary_relocation should be boolean or a list of paths " "(optionally globs)" ) - if sys.platform == "win32": + if on_win: if isinstance(ret, list) and any("\\" in i for i in ret): raise RuntimeError( "build/binary_relocation paths must use / " @@ -1846,7 +1840,7 @@ def binary_has_prefix_files(self): raise RuntimeError( "build/binary_has_prefix_files should be a list of paths" ) - if sys.platform == "win32": + if on_win: if any("\\" in i for i in ret): raise RuntimeError( "build/binary_has_prefix_files paths must use / " @@ -1886,7 +1880,7 @@ def _get_contents( with open(self.meta_path) as fd: return fd.read() - from conda_build.jinja_context import ( + from .jinja_context import ( FilteredLoader, UndefinedNeverFail, context_processor, @@ -1968,9 +1962,7 @@ def _get_contents( if "'None' has not attribute" in str(ex): ex = "Failed to run jinja context function" sys.exit( - "Error: Failed to render jinja template in {}:\n{}".format( - self.meta_path, str(ex) - ) + f"Error: Failed to render jinja template in {self.meta_path}:\n{str(ex)}" ) finally: if "CONDA_BUILD_STATE" in os.environ: @@ -2102,7 +2094,7 @@ def get_recipe_text( self.name(), getattr(self, "type", None) ) else: - from conda_build.render import output_yaml + from .render import output_yaml recipe_text = output_yaml(self) recipe_text = _filter_recipe_text(recipe_text, extract_pattern) @@ -2496,7 +2488,7 @@ def get_output_metadata_set( permit_unsatisfiable_variants=False, bypass_env_check=False, ): - from conda_build.source import provide + from .source import provide out_metadata_map = {} if self.final: @@ -2902,8 +2894,8 @@ def _get_used_vars_output_script(self): else: log = utils.get_logger(__name__) log.warn( - "Not detecting used variables in output script {}; conda-build only knows " - "how to search .sh and .bat files right now.".format(script) + f"Not detecting used variables in output script {script}; conda-build only knows " + "how to search .sh and .bat files right now." ) return used_vars diff --git a/conda_build/metapackage.py b/conda_build/metapackage.py index 0566836030..5c7b57c7b5 100644 --- a/conda_build/metapackage.py +++ b/conda_build/metapackage.py @@ -2,8 +2,8 @@ # SPDX-License-Identifier: BSD-3-Clause from collections import defaultdict -from conda_build.config import Config -from conda_build.metadata import MetaData +from .config import Config +from .metadata import MetaData def create_metapackage( @@ -19,7 +19,7 @@ def create_metapackage( config=None, ): # local import to avoid circular import, we provide create_metapackage in api - from conda_build.api import build + from .api import build if not config: config = Config() diff --git a/conda_build/noarch_python.py b/conda_build/noarch_python.py index 30efb3d45d..daaf163490 100644 --- a/conda_build/noarch_python.py +++ b/conda_build/noarch_python.py @@ -8,14 +8,25 @@ import sys from os.path import basename, dirname, isdir, isfile, join -ISWIN = sys.platform.startswith("win") +from .deprecations import deprecated +from .utils import on_win +deprecated.constant( + "24.1", + "24.3", + "ISWIN", + on_win, + addendum="Use `conda_build.utils.on_win` instead.", +) + +@deprecated("24.1", "24.3", addendum="Use `os.makedirs(exist_ok=True)` instead.") def _force_dir(dirname): if not isdir(dirname): os.makedirs(dirname) +@deprecated("24.1", "24.3") def _error_exit(exit_message): sys.exit("[noarch_python] %s" % exit_message) @@ -26,7 +37,7 @@ def rewrite_script(fn, prefix): noarch pacakges""" # Load and check the source file for not being a binary - src = join(prefix, "Scripts" if ISWIN else "bin", fn) + src = join(prefix, "Scripts" if on_win else "bin", fn) encoding = locale.getpreferredencoding() # if default locale is ascii, allow UTF-8 (a reasonably modern ASCII extension) if encoding == "ANSI_X3.4-1968": @@ -35,17 +46,17 @@ def rewrite_script(fn, prefix): try: data = fi.read() except UnicodeDecodeError: # file is binary - _error_exit("Noarch package contains binary script: %s" % fn) + sys.exit("[noarch_python] Noarch package contains binary script: %s" % fn) src_mode = os.stat(src).st_mode os.unlink(src) # Get rid of '-script.py' suffix on Windows - if ISWIN and fn.endswith("-script.py"): + if on_win and fn.endswith("-script.py"): fn = fn[:-10] # Rewrite the file to the python-scripts directory dst_dir = join(prefix, "python-scripts") - _force_dir(dst_dir) + os.makedirs(dst_dir, exist_ok=True) dst = join(dst_dir, fn) with open(dst, "w") as fo: fo.write(data) @@ -69,12 +80,12 @@ def handle_file(f, d, prefix): elif "site-packages" in f: nsp = join(prefix, "site-packages") - _force_dir(nsp) + os.makedirs(nsp, exist_ok=True) g = f[f.find("site-packages") :] dst = join(prefix, g) dst_dir = dirname(dst) - _force_dir(dst_dir) + os.makedirs(dst_dir, exist_ok=True) shutil.move(path, dst) d["site-packages"].append(g[14:]) @@ -103,7 +114,7 @@ def populate_files(m, files, prefix, entry_point_scripts=None): handle_file(f, d, prefix) # Windows path conversion - if ISWIN: + if on_win: for fns in (d["site-packages"], d["Examples"]): for i, fn in enumerate(fns): fns[i] = fn.replace("\\", "/") @@ -119,10 +130,10 @@ def populate_files(m, files, prefix, entry_point_scripts=None): def transform(m, files, prefix): bin_dir = join(prefix, "bin") - _force_dir(bin_dir) + os.makedirs(bin_dir, exist_ok=True) scripts_dir = join(prefix, "Scripts") - _force_dir(scripts_dir) + os.makedirs(scripts_dir, exist_ok=True) name = m.name() diff --git a/conda_build/os_utils/external.py b/conda_build/os_utils/external.py index 215f395f00..8b84833c00 100644 --- a/conda_build/os_utils/external.py +++ b/conda_build/os_utils/external.py @@ -2,11 +2,11 @@ # SPDX-License-Identifier: BSD-3-Clause import os import stat -import sys from glob import glob from os.path import expanduser, isfile, join -from conda_build.conda_interface import root_dir +from ..conda_interface import root_dir +from ..utils import on_win def find_executable(executable, prefix=None, all_matches=False): @@ -14,7 +14,7 @@ def find_executable(executable, prefix=None, all_matches=False): # in other code global dir_paths result = None - if sys.platform == "win32": + if on_win: dir_paths = [ join(root_dir, "Scripts"), join(root_dir, "Library\\mingw-w64\\bin"), @@ -36,7 +36,7 @@ def find_executable(executable, prefix=None, all_matches=False): dir_paths.insert(0, join(prefix, "bin")) dir_paths.extend(os.environ["PATH"].split(os.pathsep)) - if sys.platform == "win32": + if on_win: exts = (".exe", ".bat", "") else: exts = ("",) @@ -47,7 +47,7 @@ def find_executable(executable, prefix=None, all_matches=False): path = expanduser(join(dir_path, executable + ext)) if isfile(path): st = os.stat(path) - if sys.platform == "win32" or st.st_mode & stat.S_IEXEC: + if on_win or st.st_mode & stat.S_IEXEC: if all_matches: all_matches_found.append(path) else: diff --git a/conda_build/os_utils/ldd.py b/conda_build/os_utils/ldd.py index ed68a461aa..70267d08f4 100644 --- a/conda_build/os_utils/ldd.py +++ b/conda_build/os_utils/ldd.py @@ -12,12 +12,11 @@ from conda.models.records import PrefixRecord -from conda_build.conda_interface import untracked -from conda_build.os_utils.macho import otool -from conda_build.os_utils.pyldd import codefile_class, inspect_linkages, machofile - +from ..conda_interface import untracked from ..deprecations import deprecated from ..utils import on_linux, on_mac +from .macho import otool +from .pyldd import codefile_class, inspect_linkages, machofile LDD_RE = re.compile(r"\s*(.*?)\s*=>\s*(.*?)\s*\(.*\)") LDD_NOT_FOUND_RE = re.compile(r"\s*(.*?)\s*=>\s*not found") diff --git a/conda_build/os_utils/liefldd.py b/conda_build/os_utils/liefldd.py index 9d638d055c..8898f45473 100644 --- a/conda_build/os_utils/liefldd.py +++ b/conda_build/os_utils/liefldd.py @@ -6,7 +6,6 @@ import json import os import struct -import sys import threading from collections.abc import Hashable from fnmatch import fnmatch @@ -15,6 +14,7 @@ from subprocess import PIPE, Popen from ..deprecations import deprecated +from ..utils import on_mac, on_win, rec_glob from .external import find_executable # lief cannot handle files it doesn't know about gracefully @@ -925,12 +925,12 @@ def get_static_lib_exports_nope(file): def get_static_lib_exports_nm(filename): nm_exe = find_executable("nm") - if sys.platform == "win32" and not nm_exe: + if on_win and not nm_exe: nm_exe = "C:\\msys64\\mingw64\\bin\\nm.exe" if not nm_exe or not os.path.exists(nm_exe): return None flags = "-Pg" - if sys.platform == "darwin": + if on_mac: flags = "-PgUj" try: out, _ = Popen( @@ -973,8 +973,6 @@ def get_static_lib_exports_dumpbin(filename): ] results = [] for p in programs: - from conda_build.utils import rec_glob - dumpbin = rec_glob(os.path.join(pfx86, p), ("dumpbin.exe",)) for result in dumpbin: try: @@ -986,7 +984,7 @@ def get_static_lib_exports_dumpbin(filename): results.append((result, version)) except: pass - from conda_build.conda_interface import VersionOrder + from ..conda_interface import VersionOrder results = sorted(results, key=lambda x: VersionOrder(x[1])) dumpbin_exe = results[-1][0] @@ -1044,7 +1042,7 @@ def get_exports(filename, arch="native", enable_static=False): os.path.exists(filename) and (filename.endswith(".a") or filename.endswith(".lib")) and is_archive(filename) - ) and sys.platform != "win32": + ) and not on_win: # syms = os.system('nm -g {}'.filename) # on macOS at least: # -PgUj is: @@ -1052,11 +1050,11 @@ def get_exports(filename, arch="native", enable_static=False): # g: global (exported) only # U: not undefined # j: name only - if debug_static_archives or sys.platform == "win32": + if debug_static_archives or on_win: exports = get_static_lib_exports_externally(filename) # Now, our own implementation which does not require nm and can # handle .lib files. - if sys.platform == "win32": + if on_win: # Sorry, LIEF does not handle COFF (only PECOFF) and object files are COFF. exports2 = exports else: diff --git a/conda_build/os_utils/macho.py b/conda_build/os_utils/macho.py index eb13669049..950ebd6d57 100644 --- a/conda_build/os_utils/macho.py +++ b/conda_build/os_utils/macho.py @@ -7,8 +7,9 @@ from itertools import islice from subprocess import PIPE, STDOUT, CalledProcessError, Popen, check_output -from conda_build import utils -from conda_build.os_utils.external import find_preferably_prefixed_executable +from .. import utils +from ..utils import on_mac +from .external import find_preferably_prefixed_executable NO_EXT = ( ".py", @@ -76,7 +77,7 @@ def human_filetype(path, build_prefix): if not lines[0].startswith((path, "Mach header")): raise ValueError( "Expected `otool -h` output to start with" - " Mach header or {}, got:\n{}".format(path, output) + f" Mach header or {path}, got:\n{output}" ) assert lines[0].startswith((path, "Mach header")), path @@ -183,8 +184,8 @@ def find_apple_cctools_executable(name, build_prefix, nofail=False): except Exception as e: log = utils.get_logger(__name__) log.error( - "ERROR :: Found `{}` but is is an Apple Xcode stub executable\n" - "and it returned an error:\n{}".format(tool, e.output) + f"ERROR :: Found `{tool}` but is is an Apple Xcode stub executable\n" + f"and it returned an error:\n{e.output}" ) raise e tool = tool_xcr @@ -356,6 +357,6 @@ def install_name_change(path, build_prefix, cb_func, dylibs, verbose=False): if __name__ == "__main__": - if sys.platform == "darwin": + if on_mac: for path in "/bin/ls", "/etc/locate.rc": print(path, is_macho(path)) diff --git a/conda_build/os_utils/pyldd.py b/conda_build/os_utils/pyldd.py index d65e0cbc3b..7f1eb81a8b 100644 --- a/conda_build/os_utils/pyldd.py +++ b/conda_build/os_utils/pyldd.py @@ -11,9 +11,8 @@ import sys from pathlib import Path -from conda_build.utils import ensure_list, get_logger - from ..deprecations import deprecated +from ..utils import ensure_list, get_logger, on_linux, on_mac, on_win logging.basicConfig(level=logging.INFO) @@ -1095,7 +1094,7 @@ def _trim_sysroot(sysroot): def _get_arch_if_native(arch): if arch == "native": - if sys.platform == "win32": + if on_win: arch = "x86_64" if sys.maxsize > 2**32 else "i686" else: _, _, _, _, arch = os.uname() @@ -1248,9 +1247,7 @@ def otool(*args): args.filename, resolve_filenames=False, recurse=False, arch=args.arch_type ) print( - "Shared libs used (non-recursively) by {} are:\n{}".format( - args.filename, shared_libs - ) + f"Shared libs used (non-recursively) by {args.filename} are:\n{shared_libs}" ) return 0 return 1 @@ -1280,11 +1277,7 @@ def ldd(*args): shared_libs = inspect_linkages( args.filename, resolve_filenames=False, recurse=True ) - print( - "Shared libs used (recursively) by {} are:\n{}".format( - args.filename, shared_libs - ) - ) + print(f"Shared libs used (recursively) by {args.filename} are:\n{shared_libs}") return 0 return 1 @@ -1311,7 +1304,7 @@ def main_maybe_test(): tool = sys.argv[2] if tool != "otool" and tool != "ldd": - if sys.platform == "darwin": + if on_mac: tool = "otool" else: tool = "ldd" @@ -1333,14 +1326,14 @@ def main_maybe_test(): resolve_filenames=False, recurse=False, ) - if sys.platform == "darwin": + if on_mac: test_that = functools.partial(inspect_linkages_otool) SOEXT = "dylib" elif tool == "ldd": test_this = functools.partial( inspect_linkages, sysroot=sysroot, resolve_filenames=True, recurse=True ) - if sys.platform.startswith("linux"): + if on_linux: test_that = functools.partial(inspect_linkages_ldd) SOEXT = "so" # Find a load of dylibs or elfs and compare @@ -1363,11 +1356,9 @@ def main_maybe_test(): else: that = this print("\n".join(this)) - assert set(this) == set( - that - ), "py-ldd result incorrect for {}, this:\n{}\nvs that:\n{}".format( - codefile, set(this), set(that) - ) + assert ( + set(this) == set(that) + ), f"py-ldd result incorrect for {codefile}, this:\n{set(this)}\nvs that:\n{set(that)}" else: return main(sys.argv) diff --git a/conda_build/post.py b/conda_build/post.py index 18e2723531..558ae50bc9 100644 --- a/conda_build/post.py +++ b/conda_build/post.py @@ -37,17 +37,19 @@ from conda.core.prefix_data import PrefixData from conda.models.records import PrefixRecord -from conda_build import utils -from conda_build.conda_interface import ( +from . import utils +from .conda_interface import ( TemporaryDirectory, lchmod, md5_file, walk_prefix, ) -from conda_build.exceptions import OverDependingError, OverLinkingError, RunPathError -from conda_build.inspect_pkg import which_package -from conda_build.os_utils import external, macho -from conda_build.os_utils.liefldd import ( +from .deprecations import deprecated +from .exceptions import OverDependingError, OverLinkingError, RunPathError +from .inspect_pkg import which_package +from .metadata import MetaData +from .os_utils import external, macho +from .os_utils.liefldd import ( get_exports_memoized, get_linkages_memoized, get_rpaths_raw, @@ -55,16 +57,14 @@ have_lief, set_rpath, ) -from conda_build.os_utils.pyldd import ( +from .os_utils.pyldd import ( DLLfile, EXEfile, codefile_class, elffile, machofile, ) - -from .deprecations import deprecated -from .metadata import MetaData +from .utils import linked_data_no_multichannels, on_mac, on_win, prefix_files filetypes_for_platform = { "win": (DLLfile, EXEfile), @@ -127,7 +127,7 @@ def fix_shebang(f, prefix, build_python, osx_is_app=False): py_exec = "#!" + ( "/bin/bash " + prefix + "/bin/pythonw" - if sys.platform == "darwin" and osx_is_app + if on_mac and osx_is_app else prefix + "/bin/" + basename(build_python) ) if bytes_ and hasattr(py_exec, "encode"): @@ -198,13 +198,11 @@ def remove_easy_install_pth(files, prefix, config, preserve_egg_dir=False): except OSError as e: fn = basename(str(e).split()[-1]) raise OSError( - "Tried to merge folder {egg_path} into {sp_dir}, but {fn}" + f"Tried to merge folder {egg_path} into {sp_dir}, but {fn}" " exists in both locations. Please either add " "build/preserve_egg_dir: True to meta.yaml, or manually " "remove the file during your install process to avoid " - "this conflict.".format( - egg_path=egg_path, sp_dir=sp_dir, fn=fn - ) + "this conflict." ) else: shutil.move(join(egg_path, fn), join(sp_dir, fn)) @@ -276,7 +274,7 @@ def compile_missing_pyc(files, cwd, python_exe, skip_compile_pyc=()): unskipped_files = set(files) - skipped_files for fn in unskipped_files: # omit files in Library/bin, Scripts, and the root prefix - they are not generally imported - if sys.platform == "win32": + if on_win: if any( [ fn.lower().startswith(start) @@ -300,7 +298,7 @@ def compile_missing_pyc(files, cwd, python_exe, skip_compile_pyc=()): else: print("compiling .pyc files...") # We avoid command lines longer than 8190 - if sys.platform == "win32": + if on_win: limit = 8190 else: limit = 32760 @@ -400,8 +398,8 @@ def find_lib(link, prefix, files, path=None): else: file_names[link].sort() print( - "Found multiple instances of %s (%s). " - "Choosing the first one." % (link, file_names[link]) + f"Found multiple instances of {link} ({file_names[link]}). " + "Choosing the first one." ) return file_names[link][0] print("Don't know how to find %s, skipping" % link) @@ -594,15 +592,11 @@ def mk_relative_linux(f, prefix, rpaths=("lib",), method=None): except CalledProcessError: if method == "patchelf": print( - "ERROR :: `patchelf --print-rpath` failed for {}, but patchelf was specified".format( - elf - ) + f"ERROR :: `patchelf --print-rpath` failed for {elf}, but patchelf was specified" ) elif method != "LIEF": print( - "WARNING :: `patchelf --print-rpath` failed for {}, will proceed with LIEF (was {})".format( - elf, method - ) + f"WARNING :: `patchelf --print-rpath` failed for {elf}, will proceed with LIEF (was {method})" ) method = "LIEF" else: @@ -612,9 +606,7 @@ def mk_relative_linux(f, prefix, rpaths=("lib",), method=None): existing2, _, _ = get_rpaths_raw(elf) if existing_pe and existing_pe != existing2: print( - "WARNING :: get_rpaths_raw()={} and patchelf={} disagree for {} :: ".format( - existing2, existing_pe, elf - ) + f"WARNING :: get_rpaths_raw()={existing2} and patchelf={existing_pe} disagree for {elf} :: " ) # Use LIEF if method is LIEF to get the initial value? if method == "LIEF": @@ -786,8 +778,6 @@ def library_nature( addendum="Query `conda.core.prefix_data.PrefixData` instead.", ) def dists_from_names(names: Iterable[str], prefix: str | os.PathLike | Path): - from conda_build.utils import linked_data_no_multichannels - names = utils.ensure_list(names) return [prec for prec in linked_data_no_multichannels(prefix) if prec.name in names] @@ -1123,16 +1113,14 @@ def _lookup_in_sysroots_and_whitelist( if len(pkgs): _print_msg( errors, - "{}: {} found in CDT/compiler package {}".format( - info_prelude, n_dso_p, pkgs[0] - ), + f"{info_prelude}: {n_dso_p} found in CDT/compiler package {pkgs[0]}", verbose=verbose, ) else: _print_msg( errors, - "{}: {} not found in any CDT/compiler package," - " nor the whitelist?!".format(msg_prelude, n_dso_p), + f"{msg_prelude}: {n_dso_p} not found in any CDT/compiler package," + " nor the whitelist?!", verbose=verbose, ) if not in_sysroots: @@ -1154,8 +1142,8 @@ def _lookup_in_sysroots_and_whitelist( if not in_whitelist and not in_sysroots: _print_msg( errors, - "{}: {} not found in packages, sysroot(s) nor the missing_dso_whitelist.\n" - ".. is this binary repackaging?".format(msg_prelude, needed_dso), + f"{msg_prelude}: {needed_dso} not found in packages, sysroot(s) nor the missing_dso_whitelist.\n" + ".. is this binary repackaging?", verbose=verbose, ) @@ -1253,9 +1241,7 @@ def _show_linking_messages( for sysroot, sr_files in sysroots.items(): _print_msg( errors, - " INFO: sysroot: '{}' files: '{}'".format( - sysroot, sorted(list(sr_files), reverse=True)[1:5] - ), + f" INFO: sysroot: '{sysroot}' files: '{sorted(list(sr_files), reverse=True)[1:5]}'", verbose=verbose, ) for f in files: @@ -1309,9 +1295,7 @@ def _show_linking_messages( elif needed_dso.startswith("$PATH"): _print_msg( errors, - "{}: {} found in build prefix; should never happen".format( - err_prelude, needed_dso - ), + f"{err_prelude}: {needed_dso} found in build prefix; should never happen", verbose=verbose, ) else: @@ -1440,11 +1424,11 @@ def check_overlinking_impl( # .. and in that sysroot there are 3 suddirs in which we may search for DSOs. sysroots = ["/usr/lib", "/opt/X11", "/System/Library/Frameworks"] whitelist = DEFAULT_MAC_WHITELIST - build_is_host = True if sys.platform == "darwin" else False + build_is_host = True if on_mac else False elif subdir.startswith("win"): sysroots = ["C:/Windows"] whitelist = DEFAULT_WIN_WHITELIST - build_is_host = True if sys.platform == "win-32" else False + build_is_host = True if on_win else False whitelist += missing_dso_whitelist or [] @@ -1452,8 +1436,6 @@ def check_overlinking_impl( # the first sysroot is more important than others. sysroots_files = dict() for sysroot in sysroots: - from conda_build.utils import prefix_files - srs = sysroot if sysroot.endswith("/") else sysroot + "/" sysroot_files = prefix_files(sysroot) sysroot_files = [p.replace("\\", "/") for p in sysroot_files] @@ -1590,19 +1572,15 @@ def check_overlinking_impl( if found_interpreted_and_interpreter: _print_msg( errors, - "{}: Interpreted package '{}' is interpreted by '{}'".format( - info_prelude, pkg_vendored_dist.name, lib.name - ), + f"{info_prelude}: Interpreted package '{pkg_vendored_dist.name}' is interpreted by '{lib.name}'", verbose=verbose, ) elif package_nature[lib] != "non-library": _print_msg( errors, - "{}: {} package {} in requirements/run but it is not used " + f"{msg_prelude}: {package_nature[lib]} package {lib} in requirements/run but it is not used " "(i.e. it is overdepending or perhaps statically linked? " - "If that is what you want then add it to `build/ignore_run_exports`)".format( - msg_prelude, package_nature[lib], lib - ), + "If that is what you want then add it to `build/ignore_run_exports`)", verbose=verbose, ) if len(errors): @@ -1852,8 +1830,8 @@ def check_symlinks(files, prefix, croot): # Symlinks to absolute paths on the system (like /usr) are fine. if real_link_path.startswith(croot): msgs.append( - "%s is a symlink to a path that may not " - "exist after the build is completed (%s)" % (f, link_path) + f"{f} is a symlink to a path that may not " + f"exist after the build is completed ({link_path})" ) if msgs: diff --git a/conda_build/render.py b/conda_build/render.py index 1e8ddae08a..c97f3bbe9f 100644 --- a/conda_build/render.py +++ b/conda_build/render.py @@ -26,17 +26,7 @@ import yaml -import conda_build.index -import conda_build.source as source -from conda_build import environ, exceptions, utils -from conda_build.exceptions import DependencyNeedsBuildingError -from conda_build.metadata import MetaData, combine_top_level_metadata_with_output -from conda_build.variants import ( - filter_by_key_value, - get_package_variants, - list_of_dicts_to_dict_of_lists, -) - +from . import environ, exceptions, source, utils from .conda_interface import ( ProgressiveFetchExtract, TemporaryDirectory, @@ -45,9 +35,15 @@ pkgs_dirs, specs_from_url, ) +from .exceptions import DependencyNeedsBuildingError +from .index import get_build_index +from .metadata import MetaData, combine_top_level_metadata_with_output from .utils import CONDA_PACKAGE_EXTENSION_V1, CONDA_PACKAGE_EXTENSION_V2 - -# from conda_build.jinja_context import pin_subpackage_against_outputs +from .variants import ( + filter_by_key_value, + get_package_variants, + list_of_dicts_to_dict_of_lists, +) def odict_representer(dumper, data): @@ -329,7 +325,7 @@ def _read_specs_from_package(pkg_loc, pkg_dist): def execute_download_actions(m, actions, env, package_subset=None, require_files=False): subdir = getattr(m.config, f"{env}_subdir") - index, _, _ = conda_build.index.get_build_index( + index, _, _ = get_build_index( subdir=subdir, bldpkgs_dir=m.config.bldpkgs_dir, output_folder=m.config.output_folder, @@ -741,8 +737,8 @@ def finalize_metadata( m.final = False log = utils.get_logger(__name__) log.warn( - "Returning non-final recipe for {}; one or more dependencies " - "was unsatisfiable:".format(m.dist()) + f"Returning non-final recipe for {m.dist()}; one or more dependencies " + "was unsatisfiable:" ) if build_unsat: log.warn(f"Build: {build_unsat}") @@ -851,7 +847,7 @@ def distribute_variants( top_loop = metadata.get_reduced_variant_set(used_variables) for variant in top_loop: - from conda_build.build import get_all_replacements + from .build import get_all_replacements get_all_replacements(variant) mv = metadata.copy() @@ -921,7 +917,7 @@ def expand_outputs(metadata_tuples): expanded_outputs = OrderedDict() for _m, download, reparse in metadata_tuples: - from conda_build.build import get_all_replacements + from .build import get_all_replacements get_all_replacements(_m.config) from copy import deepcopy diff --git a/conda_build/skeletons/cpan.py b/conda_build/skeletons/cpan.py index 7cd48e08ef..e1c061bf73 100644 --- a/conda_build/skeletons/cpan.py +++ b/conda_build/skeletons/cpan.py @@ -3,8 +3,6 @@ """ Tools for converting CPAN packages to conda recipes. """ - - import codecs import gzip import hashlib @@ -21,20 +19,21 @@ import requests -from conda_build import environ -from conda_build.conda_interface import ( +from .. import environ +from ..conda_interface import ( CondaError, CondaHTTPError, MatchSpec, Resolve, + TemporaryDirectory, TmpDownload, download, get_index, ) -from conda_build.config import get_or_merge_config -from conda_build.utils import check_call_env, on_win -from conda_build.variants import get_default_variant -from conda_build.version import _parse as parse_version +from ..config import Config, get_or_merge_config +from ..utils import check_call_env, on_linux, on_win +from ..variants import get_default_variant +from ..version import _parse as parse_version CPAN_META = """\ {{% set name = "{packagename}" %}} @@ -205,7 +204,7 @@ def __enter__(self): def get_build_dependencies_from_src_archive(package_url, sha256, src_cache): import tarfile - from conda_build import source + from .. import source cached_path, _ = source.download_to_cache( src_cache, "", {"url": package_url, "sha256": sha256} @@ -334,14 +333,11 @@ def load_or_pickle(filename_prefix, base_folder, data_partial, key): def install_perl_get_core_modules(version): try: - from conda_build.conda_interface import TemporaryDirectory - from conda_build.config import Config - config = Config() - if sys.platform.startswith("win"): + if on_win: subdirs = ("win-64", "Library", "bin", "perl.exe") - elif sys.platform.startswith("linux"): + elif on_linux: subdirs = ("linux-64", "bin", "perl") else: subdirs = ("osx-64", "bin", "perl") @@ -361,10 +357,8 @@ def install_perl_get_core_modules(version): "my @modules = grep {Module::CoreList::is_core($_)} Module::CoreList->find_modules(qr/.*/); " 'print join "\n", @modules;', ] - from subprocess import check_output - all_core_modules = ( - check_output(args, shell=False) + subprocess.check_output(args, shell=False) .decode("utf-8") .replace("\r\n", "\n") .split("\n") @@ -456,19 +450,15 @@ def skeletonize( ) if package == "perl": print( - ( - "WARNING: {0} is a Perl core module that is not developed " - + "outside of Perl, so we are skipping creating a recipe " - + "for it." - ).format(orig_package) + f"WARNING: {orig_package} is a Perl core module that is not developed " + f"outside of Perl, so we are skipping creating a recipe " + f"for it." ) continue elif package not in {orig_package, orig_package.replace("::", "-")}: print( - ( - "WARNING: {0} was part of the {1} distribution, so we are " - + "making a recipe for {1} instead." - ).format(orig_package, package) + f"WARNING: {orig_package} was part of the {package} distribution, so we are " + f"making a recipe for {package} instead." ) latest_release_data = get_release_info( @@ -823,13 +813,10 @@ def deps_for_package( ) except InvalidReleaseError: print( - ( - "WARNING: The version of %s listed as a " - + "dependency for %s, %s, is not available on MetaCPAN, " - + "so we are just assuming the latest version is " - + "okay." - ) - % (orig_dist, package, str(dep_version)) + f"WARNING: The version of {orig_dist} listed as a " + f"dependency for {package}, {dep_version}, is not available on MetaCPAN, " + f"so we are just assuming the latest version is " + f"okay." ) dep_version = parse_version("0") @@ -968,10 +955,8 @@ def release_module_dict_direct(cpan_url, cache_dir, module): print(f"INFO :: OK, found 'dependency' in module {module}") if not rel_dict or "dependency" not in rel_dict: print( - "WARNING :: No dependencies found for module {} in distribution {}\n" - "WARNING :: Please check {} and {}".format( - module, distribution, url_module, url_release - ) + f"WARNING :: No dependencies found for module {module} in distribution {distribution}\n" + f"WARNING :: Please check {url_module} and {url_release}" ) return rel_dict @@ -1035,11 +1020,8 @@ def core_module_dict_old(cpan_url, module): # If there was an error, report it except CondaHTTPError as e: sys.exit( - ( - "Error: Could not find module or distribution named" - " %s on MetaCPAN. Error was: %s" - ) - % (module, e.message) + f"Error: Could not find module or distribution named" + f" {module} on MetaCPAN. Error was: {e.message}" ) else: mod_dict = {"distribution": "perl"} @@ -1106,12 +1088,10 @@ def get_release_info(cpan_url, cache_dir, core_modules, package, version): core_version = metacpan_api_is_core_version(cpan_url, package) if core_version is not None and (version is None or (version == core_version)): print( - ( - "WARNING: {0} is not available on MetaCPAN, but it's a " - + "core module, so we do not actually need the source file, " - + "and are omitting the URL and MD5 from the recipe " - + "entirely." - ).format(orig_package) + f"WARNING: {orig_package} is not available on MetaCPAN, but it's a " + f"core module, so we do not actually need the source file, " + f"and are omitting the URL and MD5 from the recipe " + f"entirely." ) rel_dict = { "version": str(core_version), diff --git a/conda_build/skeletons/cran.py b/conda_build/skeletons/cran.py index d942013d65..e1a4406252 100755 --- a/conda_build/skeletons/cran.py +++ b/conda_build/skeletons/cran.py @@ -41,14 +41,13 @@ from conda.common.io import dashlist -from conda_build import source -from conda_build.conda_interface import TemporaryDirectory, cc_conda_build -from conda_build.config import get_or_merge_config -from conda_build.license_family import allowed_license_families, guess_license_family -from conda_build.utils import ensure_list, rm_rf -from conda_build.variants import DEFAULT_VARIANTS, get_package_variants - +from .. import source +from ..conda_interface import TemporaryDirectory, cc_conda_build +from ..config import get_or_merge_config +from ..license_family import allowed_license_families, guess_license_family from ..metadata import MetaData +from ..utils import ensure_list, rm_rf +from ..variants import DEFAULT_VARIANTS, get_package_variants SOURCE_META = """\ {archive_keys} @@ -790,9 +789,7 @@ def package_to_inputs_dict( commp = commonprefix((package, output_dir)) if commp != output_dir: raise RuntimeError( - "package {} specified with abs path outside of output-dir {}".format( - package, output_dir - ) + f"package {package} specified with abs path outside of output-dir {output_dir}" ) location = package existing_location = existing_recipe_dir( @@ -995,8 +992,8 @@ def skeletonize( stderr = stderr.decode("utf-8") if p.returncode: sys.exit( - "Error: 'git checkout %s' failed (%s).\nInvalid tag?" - % (new_git_tag, stderr.strip()) + f"Error: 'git checkout {new_git_tag}' failed ({stderr.strip()}).\n" + "Invalid tag?" ) if stdout: print(stdout, file=sys.stdout) @@ -1014,9 +1011,8 @@ def skeletonize( DESCRIPTION = sub_description_name else: sys.exit( - "%s does not appear to be a valid R package " - "(no DESCRIPTION file in %s, %s)" - % (location, sub_description_pkg, sub_description_name) + f"{location} does not appear to be a valid R package " + f"(no DESCRIPTION file in {sub_description_pkg}, {sub_description_name})" ) cran_package = get_archive_metadata(DESCRIPTION) @@ -1098,9 +1094,7 @@ def skeletonize( build_number = m.build_number() build_number += 1 if update_policy == "merge-incr-build-num" else 0 if add_maintainer: - new_maintainer = "{indent}{add_maintainer}".format( - indent=INDENT, add_maintainer=add_maintainer - ) + new_maintainer = f"{INDENT}{add_maintainer}" if new_maintainer not in extra_recipe_maintainers: if not len(extra_recipe_maintainers): # We hit this case when there is no existing recipe. @@ -1198,9 +1192,7 @@ def skeletonize( ) except: print( - "logic error, file {} should exist, we found it in a dir listing earlier.".format( - package_url - ) + f"logic error, file {package_url} should exist, we found it in a dir listing earlier." ) sys.exit(1) if description_path is None or archive_type == "source": @@ -1387,11 +1379,7 @@ def skeletonize( for s in list(chain(imports, depends, links)): match = VERSION_DEPENDENCY_REGEX.match(s) if not match: - sys.exit( - "Could not parse version from dependency of {}: {}".format( - package, s - ) - ) + sys.exit(f"Could not parse version from dependency of {package}: {s}") name = match.group("name") if name in seen: continue @@ -1406,7 +1394,7 @@ def skeletonize( if archs: sys.exit( "Don't know how to handle archs from dependency of " - "package %s: %s" % (package, s) + f"package {package}: {s}" ) dep_dict[name] = f"{relop}{ver}" @@ -1476,44 +1464,28 @@ def skeletonize( if dep_type == "build": if need_c: deps.append( - "{indent}{{{{ compiler('c') }}}} {sel}".format( - indent=INDENT, sel=sel_src_not_win - ) + f"{INDENT}{{{{ compiler('c') }}}} {sel_src_not_win}" ) deps.append( - "{indent}{{{{ compiler('m2w64_c') }}}} {sel}".format( - indent=INDENT, sel=sel_src_and_win - ) + f"{INDENT}{{{{ compiler('m2w64_c') }}}} {sel_src_and_win}" ) if need_cxx: deps.append( - "{indent}{{{{ compiler('cxx') }}}} {sel}".format( - indent=INDENT, sel=sel_src_not_win - ) + f"{INDENT}{{{{ compiler('cxx') }}}} {sel_src_not_win}" ) deps.append( - "{indent}{{{{ compiler('m2w64_cxx') }}}} {sel}".format( - indent=INDENT, sel=sel_src_and_win - ) + f"{INDENT}{{{{ compiler('m2w64_cxx') }}}} {sel_src_and_win}" ) if need_f: deps.append( - "{indent}{{{{ compiler('fortran') }}}} {sel}".format( - indent=INDENT, sel=sel_src_not_win - ) + f"{INDENT}{{{{ compiler('fortran') }}}} {sel_src_not_win}" ) deps.append( - "{indent}{{{{ compiler('m2w64_fortran') }}}}{sel}".format( - indent=INDENT, sel=sel_src_and_win - ) + f"{INDENT}{{{{ compiler('m2w64_fortran') }}}}{sel_src_and_win}" ) if use_rtools_win: need_c = need_cxx = need_f = need_autotools = need_make = False - deps.append( - "{indent}rtools {sel}".format( - indent=INDENT, sel=sel_src_and_win - ) - ) + deps.append(f"{INDENT}rtools {sel_src_and_win}") # extsoft is legacy. R packages will download rwinlib subprojects # as necessary according to Jeroen Ooms. (may need to disable that # for non-MRO builds or maybe switch to Jeroen's toolchain?) @@ -1521,69 +1493,41 @@ def skeletonize( # indent=INDENT, sel=sel_src_and_win)) if need_autotools or need_make or need_git: deps.append( - "{indent}{{{{ posix }}}}filesystem {sel}".format( - indent=INDENT, sel=sel_src_and_win - ) + f"{INDENT}{{{{ posix }}}}filesystem {sel_src_and_win}" ) if need_git: deps.append(f"{INDENT}{{{{ posix }}}}git") if need_autotools: deps.append( - "{indent}{{{{ posix }}}}sed {sel}".format( - indent=INDENT, sel=sel_src_and_win - ) + f"{INDENT}{{{{ posix }}}}sed {sel_src_and_win}" ) deps.append( - "{indent}{{{{ posix }}}}grep {sel}".format( - indent=INDENT, sel=sel_src_and_win - ) + f"{INDENT}{{{{ posix }}}}grep {sel_src_and_win}" ) + deps.append(f"{INDENT}{{{{ posix }}}}autoconf {sel_src}") deps.append( - "{indent}{{{{ posix }}}}autoconf {sel}".format( - indent=INDENT, sel=sel_src - ) + f"{INDENT}{{{{ posix }}}}automake {sel_src_not_win}" ) deps.append( - "{indent}{{{{ posix }}}}automake {sel}".format( - indent=INDENT, sel=sel_src_not_win - ) - ) - deps.append( - "{indent}{{{{ posix }}}}automake-wrapper{sel}".format( - indent=INDENT, sel=sel_src_and_win - ) + f"{INDENT}{{{{ posix }}}}automake-wrapper{sel_src_and_win}" ) deps.append(f"{INDENT}{{{{ posix }}}}pkg-config") if need_make: - deps.append( - "{indent}{{{{ posix }}}}make {sel}".format( - indent=INDENT, sel=sel_src - ) - ) + deps.append(f"{INDENT}{{{{ posix }}}}make {sel_src}") if not need_autotools: deps.append( - "{indent}{{{{ posix }}}}sed {sel}".format( - indent=INDENT, sel=sel_src_and_win - ) + f"{INDENT}{{{{ posix }}}}sed {sel_src_and_win}" ) deps.append( - "{indent}{{{{ posix }}}}coreutils {sel}".format( - indent=INDENT, sel=sel_src_and_win - ) - ) - deps.append( - "{indent}{{{{ posix }}}}zip {sel}".format( - indent=INDENT, sel=sel_src_and_win + f"{INDENT}{{{{ posix }}}}coreutils {sel_src_and_win}" ) - ) + deps.append(f"{INDENT}{{{{ posix }}}}zip {sel_src_and_win}") if add_cross_r_base: deps.append(f"{INDENT}cross-r-base {{{{ r_base }}}} {sel_cross}") elif dep_type == "run": if need_c or need_cxx or need_f: deps.append( - "{indent}{{{{native}}}}gcc-libs {sel}".format( - indent=INDENT, sel=sel_src_and_win - ) + f"{INDENT}{{{{native}}}}gcc-libs {sel_src_and_win}" ) if dep_type == "host" or dep_type == "run": @@ -1605,13 +1549,7 @@ def skeletonize( conda_name = "r-" + name.lower() if dep_dict[name]: - deps.append( - "{indent}{name} {version}".format( - name=conda_name, - version=dep_dict[name], - indent=INDENT, - ) - ) + deps.append(f"{INDENT}{conda_name} {dep_dict[name]}") else: deps.append(f"{INDENT}{conda_name}") if recursive: diff --git a/conda_build/skeletons/pypi.py b/conda_build/skeletons/pypi.py index b1194e6a8b..fe69e09d23 100644 --- a/conda_build/skeletons/pypi.py +++ b/conda_build/skeletons/pypi.py @@ -3,8 +3,6 @@ """ Tools for converting PyPI packages to conda recipes. """ - - import keyword import logging import os @@ -23,7 +21,7 @@ import yaml from requests.packages.urllib3.util.url import parse_url -from conda_build.conda_interface import ( +from ..conda_interface import ( StringIO, configparser, default_python, @@ -34,20 +32,21 @@ normalized_version, spec_from_line, ) -from conda_build.config import Config -from conda_build.environ import create_env -from conda_build.license_family import allowed_license_families, guess_license_family -from conda_build.metadata import MetaData -from conda_build.render import FIELDS as EXPECTED_SECTION_ORDER -from conda_build.source import apply_patch -from conda_build.utils import ( +from ..config import Config +from ..environ import create_env +from ..license_family import allowed_license_families, guess_license_family +from ..metadata import MetaData +from ..render import FIELDS as EXPECTED_SECTION_ORDER +from ..source import apply_patch +from ..utils import ( check_call_env, decompressible_exts, ensure_list, + on_win, rm_rf, tar_xf, ) -from conda_build.version import _parse as parse_version +from ..version import _parse as parse_version pypi_example = """ Examples: @@ -336,8 +335,7 @@ def skeletonize( if version: if version not in versions: sys.exit( - "Error: Version %s of %s is not available on PyPI." - % (version, package) + f"Error: Version {version} of {package} is not available on PyPI." ) d["version"] = version else: @@ -1283,9 +1281,9 @@ def get_pkginfo( download(pypiurl, join(config.src_cache, filename)) if hashsum_file(download_path, hash_type) != hash_value: raise RuntimeError( - " Download of {} failed" - " checksum type {} expected value {}. Please" - " try again.".format(package, hash_type, hash_value) + f" Download of {package} failed" + f" checksum type {hash_type} expected value {hash_value}. Please" + " try again." ) else: print("Using cached download") @@ -1366,7 +1364,7 @@ def run_setuppy(src_dir, temp_dir, python_version, extra_specs, config, setup_op ) stdlib_dir = join( config.host_prefix, - "Lib" if sys.platform == "win32" else "lib/python%s" % python_version, + "Lib" if on_win else "lib/python%s" % python_version, ) patch = join(temp_dir, "pypi-distutils.patch") @@ -1385,7 +1383,7 @@ def run_setuppy(src_dir, temp_dir, python_version, extra_specs, config, setup_op stdlib_dir, "distutils", "__pycache__", - "core.cpython-%s%s.pyc" % sys.version_info[:2], + f"core.cpython-{sys.version_info[0]}{sys.version_info[1]}.pyc", ) ) rm_rf( @@ -1393,7 +1391,7 @@ def run_setuppy(src_dir, temp_dir, python_version, extra_specs, config, setup_op stdlib_dir, "distutils", "__pycache__", - "core.cpython-%s%s.pyo" % sys.version_info[:2], + f"core.cpython-{sys.version_info[0]}{sys.version_info[1]}.pyo", ) ) else: diff --git a/conda_build/skeletons/rpm.py b/conda_build/skeletons/rpm.py index 76f2e5ea86..f0abb8c747 100644 --- a/conda_build/skeletons/rpm.py +++ b/conda_build/skeletons/rpm.py @@ -1,32 +1,21 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause import argparse -from copy import copy - -from conda_build.license_family import guess_license_family -from conda_build.source import download_to_cache - -try: - import cPickle as pickle -except: - import pickle as pickle - import gzip import hashlib +import pickle import re +from copy import copy from os import chmod, makedirs from os.path import basename, dirname, exists, join, splitext from textwrap import wrap +from urllib.request import urlopen from xml.etree import ElementTree as ET +from ..license_family import guess_license_family +from ..source import download_to_cache from .cran import yaml_quote_string -try: - from urllib.request import urlopen -except ImportError: - from urllib2 import urlopen - - # This is used in two places default_architecture = "x86_64" default_distro = "centos6" @@ -337,9 +326,7 @@ def get_repo_dict(repomd_url, data_type, dict_massager, cdt, src_cache): ) assert ( csum == cached_csum - ), "Checksum for {} does not match value in {}".format( - xmlgz_file, repomd_url - ) + ), f"Checksum for {xmlgz_file} does not match value in {repomd_url}" with gzip.open(cached_path, "rb") as gz: xml_content = gz.read() xml_csum = cdt["checksummer"]() @@ -547,9 +534,7 @@ def write_conda_recipes( depends.append(copy_provides) else: print( - "WARNING: Additional dependency of {}, {} not found".format( - package, missing_dep - ) + f"WARNING: Additional dependency of {package}, {missing_dep} not found" ) for depend in depends: dep_entry, dep_name, dep_arch = find_repo_entry_and_arch( @@ -798,9 +783,7 @@ def distro(distro_name): "--distro", type=distro, default=default_distro, - help="Distro to use. Applies to all packages, valid values are: {}".format( - valid_distros() - ), + help=f"Distro to use. Applies to all packages, valid values are: {valid_distros()}", ) rpm.add_argument( diff --git a/conda_build/source.py b/conda_build/source.py index 85e64c8292..0db306fb75 100644 --- a/conda_build/source.py +++ b/conda_build/source.py @@ -13,14 +13,23 @@ from pathlib import Path from subprocess import CalledProcessError from typing import Iterable - -from conda_build.conda_interface import CondaHTTPError, url_path -from conda_build.os_utils import external -from conda_build.utils import ( +from urllib.parse import urljoin + +from .conda_interface import ( + CondaHTTPError, + TemporaryDirectory, + download, + hashsum_file, + url_path, +) +from .exceptions import MissingDependency +from .os_utils import external +from .utils import ( LoggingContext, check_call_env, check_output_env, convert_path_for_cygwin_or_msys2, + convert_unix_path_to_win, copy_into, decompressible_exts, ensure_list, @@ -31,17 +40,7 @@ tar_xf, ) -from .conda_interface import TemporaryDirectory, download, hashsum_file -from .exceptions import MissingDependency - log = get_logger(__name__) -if on_win: - from conda_build.utils import convert_unix_path_to_win - -if sys.version_info[0] == 3: - from urllib.parse import urljoin -else: - from urlparse import urljoin git_submod_re = re.compile(r"(?:.+)\.(.+)\.(?:.+)\s(.+)") ext_re = re.compile(r"(.*?)(\.(?:tar\.)?[^.]+)$") @@ -74,8 +73,8 @@ def download_to_cache(cache_folder, recipe_path, source_dict, verbose=False): break else: log.warn( - "No hash (md5, sha1, sha256) provided for {}. Source download forced. " - "Add hash to recipe to use source cache.".format(unhashed_fn) + f"No hash (md5, sha1, sha256) provided for {unhashed_fn}. Source download forced. " + "Add hash to recipe to use source cache." ) path = join(cache_folder, fn) if isfile(path): @@ -122,9 +121,7 @@ def download_to_cache(cache_folder, recipe_path, source_dict, verbose=False): if expected_hash != hashed: rm_rf(path) raise RuntimeError( - "{} mismatch: '{}' != '{}'".format( - tp.upper(), hashed, expected_hash - ) + f"{tp.upper()} mismatch: '{hashed}' != '{expected_hash}'" ) break @@ -253,8 +250,7 @@ def git_mirror_checkout_recursive( if not mirror_dir.startswith(git_cache + os.sep): sys.exit( - "Error: Attempting to mirror to %s which is outside of GIT_CACHE %s" - % (mirror_dir, git_cache) + f"Error: Attempting to mirror to {mirror_dir} which is outside of GIT_CACHE {git_cache}" ) # This is necessary for Cygwin git and m2-git, although it is fixed in newer MSYS2. @@ -302,7 +298,7 @@ def git_mirror_checkout_recursive( except CalledProcessError: msg = ( "Failed to update local git cache. " - "Deleting local cached repo: {} ".format(mirror_dir) + f"Deleting local cached repo: {mirror_dir} " ) print(msg) @@ -323,7 +319,7 @@ def git_mirror_checkout_recursive( except CalledProcessError: # on windows, remote URL comes back to us as cygwin or msys format. Python doesn't # know how to normalize it. Need to convert it to a windows path. - if sys.platform == "win32" and git_url.startswith("/"): + if on_win and git_url.startswith("/"): git_url = convert_unix_path_to_win(git_url) if os.path.exists(git_url): @@ -438,7 +434,7 @@ def git_source(source_dict, git_cache, src_dir, recipe_path=None, verbose=True): if git_url.startswith("."): # It's a relative path from the conda recipe git_url = abspath(normpath(os.path.join(recipe_path, git_url))) - if sys.platform == "win32": + if on_win: git_dn = git_url.replace(":", "_") else: git_dn = git_url[1:] diff --git a/conda_build/tarcheck.py b/conda_build/tarcheck.py index 3fc363986e..3a98559187 100644 --- a/conda_build/tarcheck.py +++ b/conda_build/tarcheck.py @@ -4,7 +4,7 @@ import tarfile from os.path import basename, normpath -from conda_build.utils import codec, filter_info_files +from .utils import codec, filter_info_files def dist_fn(fn): @@ -61,9 +61,7 @@ def index_json(self): for varname in "name", "version": if info[varname] != getattr(self, varname): raise Exception( - "{}: {!r} != {!r}".format( - varname, info[varname], getattr(self, varname) - ) + f"{varname}: {info[varname]!r} != {getattr(self, varname)!r}" ) assert isinstance(info["build_number"], int) diff --git a/conda_build/utils.py b/conda_build/utils.py index 3961e38bd6..fc70e9660d 100644 --- a/conda_build/utils.py +++ b/conda_build/utils.py @@ -18,10 +18,15 @@ import tarfile import tempfile import time +import urllib.parse as urlparse +import urllib.request as urllib from collections import OrderedDict, defaultdict from functools import lru_cache +from glob import glob from itertools import filterfalse +from json.decoder import JSONDecodeError from locale import getpreferredencoding +from os import walk from os.path import ( abspath, dirname, @@ -38,58 +43,28 @@ from threading import Thread from typing import Iterable -import libarchive - -from .deprecations import deprecated - -try: - from json.decoder import JSONDecodeError -except ImportError: - JSONDecodeError = ValueError - import conda_package_handling.api import filelock +import libarchive import yaml - -try: - from conda.base.constants import ( - CONDA_PACKAGE_EXTENSION_V1, - CONDA_PACKAGE_EXTENSION_V2, - CONDA_PACKAGE_EXTENSIONS, - ) -except Exception: - from conda.base.constants import ( - CONDA_TARBALL_EXTENSION as CONDA_PACKAGE_EXTENSION_V1, - ) - - CONDA_PACKAGE_EXTENSION_V2 = ".conda" - CONDA_PACKAGE_EXTENSIONS = (CONDA_PACKAGE_EXTENSION_V2, CONDA_PACKAGE_EXTENSION_V1) - -import urllib.parse as urlparse -import urllib.request as urllib -from contextlib import ExitStack # noqa: F401 -from glob import glob - -from conda.api import PackageCacheData # noqa -from conda.base.constants import KNOWN_SUBDIRS +from conda.base.constants import ( + CONDA_PACKAGE_EXTENSION_V1, # noqa: F401 + CONDA_PACKAGE_EXTENSION_V2, # noqa: F401 + CONDA_PACKAGE_EXTENSIONS, + KNOWN_SUBDIRS, +) from conda.core.prefix_data import PrefixData from conda.models.dist import Dist from conda.models.records import PrefixRecord -# NOQA because it is not used in this file. -from conda_build.conda_interface import rm_rf as _rm_rf # noqa -from conda_build.exceptions import BuildLockError # noqa -from conda_build.os_utils import external # noqa - -from .conda_interface import ( # noqa +from .conda_interface import ( CondaHTTPError, - Dist, # noqa MatchSpec, - StringIO, # noqa + StringIO, TemporaryDirectory, VersionOrder, - cc_conda_build, # noqa - context, # noqa + cc_conda_build, + context, download, get_conda_channel, hashsum_file, @@ -99,9 +74,9 @@ unix_path_to_win, win_path_to_unix, ) - -PermissionError = PermissionError # NOQA -FileNotFoundError = FileNotFoundError +from .conda_interface import rm_rf as _rm_rf +from .deprecations import deprecated +from .exceptions import BuildLockError on_win = sys.platform == "win32" on_mac = sys.platform == "darwin" @@ -138,11 +113,6 @@ # filenames accepted as recipe meta files VALID_METAS = ("meta.yaml", "meta.yml", "conda.yaml", "conda.yml") -try: - from os import scandir, walk # NOQA -except ImportError: - from scandir import walk - @lru_cache(maxsize=None) def stat_file(path): @@ -230,7 +200,7 @@ def _setup_rewrite_pipe(env): r_fd, w_fd = os.pipe() r = os.fdopen(r_fd, "rt") - if sys.platform == "win32": + if on_win: replacement_t = "%{}%" else: replacement_t = "${}" @@ -714,8 +684,8 @@ def merge_tree( assert not dst.startswith(src), ( "Can't merge/copy source into subdirectory of itself. " "Please create separate spaces for these things.\n" - " src: {}\n" - " dst: {}".format(src, dst) + f" src: {src}\n" + f" dst: {dst}" ) new_files = copytree(src, dst, symlinks=symlinks, dry_run=True) @@ -833,10 +803,12 @@ def relative(f, d="lib"): def _tar_xf_fallback(tarball, dir_path, mode="r:*"): + from .os_utils.external import find_executable + if tarball.lower().endswith(".tar.z"): - uncompress = external.find_executable("uncompress") + uncompress = find_executable("uncompress") if not uncompress: - uncompress = external.find_executable("gunzip") + uncompress = find_executable("gunzip") if not uncompress: sys.exit( """\ @@ -863,8 +835,6 @@ def _tar_xf_fallback(tarball, dir_path, mode="r:*"): def tar_xf_file(tarball, entries): - from conda_build.utils import ensure_list - entries = ensure_list(entries) if not os.path.isabs(tarball): tarball = os.path.join(os.getcwd(), tarball) @@ -992,7 +962,9 @@ def rec_glob(path, patterns, ignores=None): def convert_unix_path_to_win(path): - if external.find_executable("cygpath"): + from .os_utils.external import find_executable + + if find_executable("cygpath"): cmd = f"cygpath -w {path}" path = subprocess.getoutput(cmd) @@ -1002,7 +974,9 @@ def convert_unix_path_to_win(path): def convert_win_path_to_unix(path): - if external.find_executable("cygpath"): + from .os_utils.external import find_executable + + if find_executable("cygpath"): cmd = f"cygpath -u {path}" path = subprocess.getoutput(cmd) @@ -1018,7 +992,7 @@ def path2url(path): def get_stdlib_dir(prefix, py_ver): - if sys.platform == "win32": + if on_win: lib_dir = os.path.join(prefix, "Lib") else: lib_dir = os.path.join(prefix, "lib") @@ -1042,7 +1016,7 @@ def get_build_folders(croot): def prepend_bin_path(env, prefix, prepend_prefix=False): env["PATH"] = join(prefix, "bin") + os.pathsep + env["PATH"] - if sys.platform == "win32": + if on_win: env["PATH"] = ( join(prefix, "Library", "mingw-w64", "bin") + os.pathsep @@ -1093,7 +1067,7 @@ def path_prepended(prefix, prepend_prefix=True): os.environ["PATH"] = old_path -bin_dirname = "Scripts" if sys.platform == "win32" else "bin" +bin_dirname = "Scripts" if on_win else "bin" entry_pat = re.compile(r"\s*([\w\-\.]+)\s*=\s*([\w.]+):([\w.]+)\s*$") @@ -1152,7 +1126,7 @@ def get_ext_files(start_path, pattern): def convert_path_for_cygwin_or_msys2(exe, path): "If exe is a Cygwin or MSYS2 executable then filters it through `cygpath -u`" - if sys.platform != "win32": + if not on_win: return path if exe not in _posix_exes_cache: with open(exe, "rb") as exe_file: @@ -1361,8 +1335,8 @@ def find_recipe(path): if len(metas) == 1: get_logger(__name__).warn( "Multiple meta files found. " - "The %s file in the base directory (%s) " - "will be used." % (metas[0], path) + f"The {metas[0]} file in the base directory ({path}) " + "will be used." ) return os.path.join(path, metas[0]) @@ -1801,9 +1775,7 @@ def merge_or_update_dict( and raise_on_clobber ): log.debug( - "clobbering key {} (original value {}) with value {}".format( - key, base_value, value - ) + f"clobbering key {key} (original value {base_value}) with value {value}" ) if value is None and key in base: del base[key] @@ -1957,14 +1929,12 @@ def ensure_valid_spec(spec, warn=False): if match.group(1) not in ("python", "vc") and warn: log = get_logger(__name__) log.warn( - "Adding .* to spec '{}' to ensure satisfiability. Please " + f"Adding .* to spec '{spec}' to ensure satisfiability. Please " "consider putting {{{{ var_name }}}}.* or some relational " "operator (>/=/<=) on this spec in meta.yaml, or if req is " "also a build req, using {{{{ pin_compatible() }}}} jinja2 " "function instead. See " - "https://conda.io/docs/user-guide/tasks/build-packages/variants.html#pinning-at-the-variant-level".format( # NOQA - spec - ) + "https://conda.io/docs/user-guide/tasks/build-packages/variants.html#pinning-at-the-variant-level" ) spec = spec_needing_star_re.sub(r"\1 \2.*", spec) return spec @@ -2055,6 +2025,8 @@ def sha256_checksum(filename, buffersize=65536): def write_bat_activation_text(file_handle, m): + from .os_utils.external import find_executable + file_handle.write(f'call "{root_script_dir}\\..\\condabin\\conda_hook.bat"\n') if m.is_cross: # HACK: we need both build and host envs "active" - i.e. on PATH, @@ -2087,7 +2059,6 @@ def write_bat_activation_text(file_handle, m): file_handle.write( f'call "{root_script_dir}\\..\\condabin\\conda.bat" activate --stack "{m.config.build_prefix}"\n' ) - from conda_build.os_utils.external import find_executable ccache = find_executable("ccache", m.config.build_prefix, False) if ccache: @@ -2127,10 +2098,7 @@ def write_bat_activation_text(file_handle, m): file_handle.write(f"mklink gcc-ranlib{ext} {ccache}\n") file_handle.write("popd\n") file_handle.write( - "set PATH={dirname_ccache_ln};{dirname_ccache};%PATH%\n".format( - dirname_ccache_ln=dirname_ccache_ln_bin, - dirname_ccache=os.path.dirname(ccache), - ) + f"set PATH={dirname_ccache_ln_bin};{os.path.dirname(ccache)};%PATH%\n" ) elif method == "native": pass @@ -2190,17 +2158,15 @@ def shutil_move_more_retrying(src, dest, debug_name): shutil.move(src, dest) if attempts_left != 5: log.warning( - "shutil.move({}={}, dest={}) succeeded on attempt number {}".format( - debug_name, src, dest, 6 - attempts_left - ) + f"shutil.move({debug_name}={src}, dest={dest}) succeeded on attempt number {6 - attempts_left}" ) attempts_left = -1 except: attempts_left = attempts_left - 1 if attempts_left > 0: log.warning( - "Failed to rename {} directory, check with strace, struss or procmon. " - "Will sleep for 3 seconds and try again!".format(debug_name) + f"Failed to rename {debug_name} directory, check with strace, struss or procmon. " + "Will sleep for 3 seconds and try again!" ) import time diff --git a/conda_build/variants.py b/conda_build/variants.py index 7e9dfc7ff0..8cf2c007cc 100644 --- a/conda_build/variants.py +++ b/conda_build/variants.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: BSD-3-Clause """This file handles the parsing of feature specifications from files, ending up with a configuration matrix""" - import os.path import re import sys @@ -13,9 +12,9 @@ import yaml -from conda_build.conda_interface import cc_conda_build, subdir -from conda_build.utils import ensure_list, get_logger, islist, on_win, trim_empty_keys -from conda_build.version import _parse as parse_version +from .conda_interface import cc_conda_build, subdir +from .utils import ensure_list, get_logger, islist, on_win, trim_empty_keys +from .version import _parse as parse_version DEFAULT_VARIANTS = { "python": f"{sys.version_info.major}.{sys.version_info.minor}", @@ -125,7 +124,7 @@ def get_default_variant(config): def parse_config_file(path, config): - from conda_build.metadata import get_selectors, select_lines + from .metadata import get_selectors, select_lines with open(path) as f: contents = f.read() @@ -161,8 +160,8 @@ def validate_spec(src, spec): # check for duplicate keys unique = set() errors.extend( - " zip_key entry {} in group {} is a duplicate, keys can only occur " - "in one group".format(k, zg) + f" zip_key entry {k} in group {zg} is a duplicate, keys can only occur " + "in one group" # include error if key has already been seen, otherwise add to unique keys if k in unique else unique.add(k) @@ -498,13 +497,8 @@ def filter_by_key_value(variants, key, values, source_name): else: log = get_logger(__name__) log.debug( - "Filtering variant with key {key} not matching target value(s) " - "({tgt_vals}) from {source_name}, actual {actual_val}".format( - key=key, - tgt_vals=values, - source_name=source_name, - actual_val=variant.get(key), - ) + f"Filtering variant with key {key} not matching target value(s) " + f"({values}) from {source_name}, actual {variant.get(key)}" ) return reduced_variants @@ -646,7 +640,7 @@ def get_package_combined_spec(recipedir_or_metadata, config=None, variants=None) if hasattr(recipedir_or_metadata, "config"): config = recipedir_or_metadata.config if not config: - from conda_build.config import Config + from .config import Config config = Config() files = find_config_files(recipedir_or_metadata, config) diff --git a/conda_build/windows.py b/conda_build/windows.py index 84da4a0f0d..ba53abf80a 100644 --- a/conda_build/windows.py +++ b/conda_build/windows.py @@ -18,15 +18,15 @@ # Allow some imports to work for cross or CONDA_SUBDIR usage. pass -from conda_build import environ -from conda_build.utils import ( +from . import environ +from .utils import ( check_call_env, copy_into, get_logger, path_prepended, write_bat_activation_text, ) -from conda_build.variants import get_default_variant, set_language_env_vars +from .variants import get_default_variant, set_language_env_vars VS_VERSION_STRING = { "8.0": "Visual Studio 8 2005", @@ -203,9 +203,7 @@ def build_vcvarsall_cmd(cmd, arch=arch_selector): # If the WindowsSDKDir environment variable has not been successfully # set then try activating VS2010 msvc_env_lines.append( - 'if not "%WindowsSDKDir%" == "{}" ( {} )'.format( - WIN_SDK_71_PATH, build_vcvarsall_cmd(vcvarsall_vs_path) - ) + f'if not "%WindowsSDKDir%" == "{WIN_SDK_71_PATH}" ( {build_vcvarsall_cmd(vcvarsall_vs_path)} )' ) # sdk is not installed. Fall back to only trying VS 2010 except KeyError: diff --git a/docs/requirements.txt b/docs/requirements.txt index b6f1b46b9c..81d30818d9 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -conda-sphinx-theme==0.1.1 +conda-sphinx-theme==0.2.0 linkify-it-py==2.0.2 myst-parser==2.0.0 Pillow==10.0.1 diff --git a/docs/source/conf.py b/docs/source/conf.py index 0aaacec6f0..8680d7451f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -119,6 +119,7 @@ # Navbar icon links "navbar_start": ["navbar-logo"], "use_edit_page_button": True, + "goatcounter_url": "https://docs-conda-io.goatcounter.com/count", "icon_links": [ { "name": "GitHub", @@ -146,6 +147,7 @@ "github_repo": "conda-build", "github_version": "main", "doc_path": "docs/source", + "goatcounter_dashboard_url": "https://docs-conda-io.goatcounter.com", } html_short_title = "conda-build" diff --git a/news/5033-update-conda-inspect-channels b/news/5033-update-conda-inspect-channels new file mode 100644 index 0000000000..13fba0b6b4 --- /dev/null +++ b/news/5033-update-conda-inspect-channels @@ -0,0 +1,22 @@ +### Enhancements + +* Update `conda inspect channels` to use updated solver/transaction logic. (#5033) + +### Bug fixes + +* + +### Deprecations + +* Mark `conda inspect channels --test-installable` as pending deprecation. (#5033) +* Mark `conda_build.inspect_pkg.check_install(package)` as pending deprecation in favor of `conda_build.inspect_pkg.check_install(subdir)`. (#5033) +* Mark `conda_build.inspect_pkg.check_install(prepend)` as pending deprecation. (#5033) +* Mark `conda_build.inspect_pkg.check_install(minimal_hint)` as pending deprecation. (#5033) + +### Docs + +* + +### Other + +* diff --git a/news/5093-add-goat-counter b/news/5093-add-goat-counter new file mode 100644 index 0000000000..37b3a9b3b9 --- /dev/null +++ b/news/5093-add-goat-counter @@ -0,0 +1,19 @@ +### Enhancements + +* + +### Bug fixes + +* + +### Deprecations + +* + +### Docs + +* Add goat counter (https://www.goatcounter.com/) as an analytics tool. (#5093) + +### Other + +* diff --git a/news/5105-script-env-warn b/news/5105-script-env-warn new file mode 100644 index 0000000000..5b7d66bad5 --- /dev/null +++ b/news/5105-script-env-warn @@ -0,0 +1,20 @@ +### Enhancements + +* Relax script_env error in outputs when variable referenced in script_env is not defined. + This unifies current behavior with the top-level build. (#5105) + +### Bug fixes + +* + +### Deprecations + +* + +### Docs + +* + +### Other + +* diff --git a/news/allure-removal b/news/allure-removal new file mode 100644 index 0000000000..3a2df35fba --- /dev/null +++ b/news/allure-removal @@ -0,0 +1,19 @@ +### Enhancements + +* + +### Bug fixes + +* + +### Deprecations + +* + +### Docs + +* + +### Other + +* Remove unused Allure test report collection. diff --git a/pyproject.toml b/pyproject.toml index 825ae5627f..8b55ee4168 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ dependencies = [ "jinja2", "jsonschema >=4.19", "libarchive-c", - "menuinst", + "menuinst >=2", "packaging", "pkginfo", "psutil", @@ -97,7 +97,10 @@ pycodestyle = {max-line-length = 120} # E, W = pycodestyle errors and warnings # F = pyflakes # I = isort -select = ["E", "W", "F", "I"] +# UP = pyupgrade +# ISC = flake8-implicit-str-concat +# see also https://docs.astral.sh/ruff/rules/ +select = ["E", "W", "F", "I", "UP", "ISC"] # E402 module level import not at top of file # E722 do not use bare 'except' # E731 do not assign a lambda expression, use a def diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 1fd9801ca5..54c792b9c9 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -38,7 +38,7 @@ requirements: - jinja2 - jsonschema >=4.19 - m2-patch >=2.6 # [win] - - menuinst + - menuinst >=2 - packaging - patch >=2.6 # [not win] - patchelf # [linux] diff --git a/tests/cli/test_main_render.py b/tests/cli/test_main_render.py index 10ed9f803e..59fff7901c 100644 --- a/tests/cli/test_main_render.py +++ b/tests/cli/test_main_render.py @@ -37,7 +37,7 @@ def test_render_add_channel(): required_package_details = required_package_string.split(" ") assert len(required_package_details) > 1, ( "Expected version number on successful " - "rendering, but got only {}".format(required_package_details) + f"rendering, but got only {required_package_details}" ) assert ( required_package_details[1] == "1.0" diff --git a/tests/requirements.txt b/tests/requirements.txt index d50ae7fec9..a7140e8673 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -2,7 +2,6 @@ anaconda-client beautifulsoup4 chardet conda >=22.11.0 -conda-forge::allure-pytest conda-index conda-package-handling >=1.3 conda-verify @@ -12,7 +11,7 @@ filelock git jinja2 jsonschema >=4.19 -menuinst +menuinst >=2 numpy packaging perl diff --git a/tests/test-recipes/split-packages/_build_script_missing_var/meta.yaml b/tests/test-recipes/split-packages/_build_script_missing_var/meta.yaml new file mode 100644 index 0000000000..d1c2bfbe57 --- /dev/null +++ b/tests/test-recipes/split-packages/_build_script_missing_var/meta.yaml @@ -0,0 +1,9 @@ +package: + name: test_build_script_in_output + version: 1.0 + +outputs: + - name: test_1 + build: + script_env: + - TEST_FN_DOESNT_EXIST diff --git a/tests/test_api_build.py b/tests/test_api_build.py index f7e6864412..856cc4fa1c 100644 --- a/tests/test_api_build.py +++ b/tests/test_api_build.py @@ -12,6 +12,7 @@ import tarfile import uuid from collections import OrderedDict +from contextlib import nullcontext from glob import glob from pathlib import Path from shutil import which @@ -28,8 +29,10 @@ import conda_build from conda_build import __version__, api, exceptions from conda_build.conda_interface import ( + CONDA_VERSION, CondaError, LinkError, + VersionOrder, cc_conda_build, context, reset_context, @@ -45,7 +48,6 @@ from conda_build.os_utils.external import find_executable from conda_build.render import finalize_metadata from conda_build.utils import ( - FileNotFoundError, check_call_env, check_output_env, convert_path_for_cygwin_or_msys2, @@ -397,12 +399,12 @@ def dummy_executable(folder, exename): with open(dummyfile, "w") as f: f.write( prefix - + """ - echo ******* You have reached the dummy {}. It is likely there is a bug in + + f""" + echo ******* You have reached the dummy {exename}. It is likely there is a bug in echo ******* conda that makes it not add the _build/bin directory onto the echo ******* PATH before running the source checkout tool exit -1 - """.format(exename) + """ ) if sys.platform != "win32": import stat @@ -611,10 +613,9 @@ def test_numpy_setup_py_data(testing_config): subprocess.check_call(["conda", "install", "-y", "cython"]) m = api.render(recipe_path, config=testing_config, numpy="1.16")[0][0] _hash = m.hash_dependencies() - assert os.path.basename( - api.get_output_file_path(m)[0] - ) == "load_setup_py_test-0.1.0-np116py{}{}{}_0.tar.bz2".format( - sys.version_info.major, sys.version_info.minor, _hash + assert ( + os.path.basename(api.get_output_file_path(m)[0]) + == f"load_setup_py_test-0.1.0-np116py{sys.version_info.major}{sys.version_info.minor}{_hash}_0.tar.bz2" ) @@ -813,9 +814,9 @@ def test_disable_pip(testing_metadata): with pytest.raises(subprocess.CalledProcessError): api.build(testing_metadata) - testing_metadata.meta["build"]["script"] = ( - 'python -c "import setuptools; ' 'print(setuptools.__version__)"' - ) + testing_metadata.meta["build"][ + "script" + ] = 'python -c "import setuptools; print(setuptools.__version__)"' with pytest.raises(subprocess.CalledProcessError): api.build(testing_metadata) @@ -1902,3 +1903,43 @@ def test_activated_prefixes_in_actual_path(testing_metadata): if path in expected_paths ] assert actual_paths == expected_paths + + +@pytest.mark.parametrize("add_pip_as_python_dependency", [False, True]) +def test_add_pip_as_python_dependency_from_condarc_file( + testing_metadata, testing_workdir, add_pip_as_python_dependency, monkeypatch +): + """ + Test whether settings from .condarc files are heeded. + ref: https://github.com/conda/conda-libmamba-solver/issues/393 + """ + if VersionOrder(CONDA_VERSION) <= VersionOrder("23.10.0"): + if not add_pip_as_python_dependency and context.solver == "libmamba": + pytest.xfail( + "conda.plan.install_actions from conda<=23.10.0 ignores .condarc files." + ) + from conda.base.context import context_stack + + # ContextStack's pop/replace methods don't call self.apply. + context_stack.apply() + + # TODO: SubdirData._cache_ clearing might not be needed for future conda versions. + # See https://github.com/conda/conda/pull/13365 for proposed changes. + from conda.core.subdir_data import SubdirData + + # SubdirData's cache doesn't distinguish on add_pip_as_python_dependency. + SubdirData._cache_.clear() + + testing_metadata.meta["build"]["script"] = ['python -c "import pip"'] + testing_metadata.meta["requirements"]["host"] = ["python"] + del testing_metadata.meta["test"] + if add_pip_as_python_dependency: + check_build_fails = nullcontext() + else: + check_build_fails = pytest.raises(subprocess.CalledProcessError) + + conda_rc = Path(testing_workdir, ".condarc") + conda_rc.write_text(f"add_pip_as_python_dependency: {add_pip_as_python_dependency}") + with env_var("CONDARC", conda_rc, reset_context): + with check_build_fails: + api.build(testing_metadata) diff --git a/tests/test_api_convert.py b/tests/test_api_convert.py index bc17db6ffe..7da9ede2d3 100644 --- a/tests/test_api_convert.py +++ b/tests/test_api_convert.py @@ -55,9 +55,7 @@ def test_show_imports(base_platform, package, capfd): if platform == source_platform: platforms.remove(platform) - f = "http://repo.anaconda.com/pkgs/free/{}-64/{}-py36_0.tar.bz2".format( - base_platform, package_name - ) + f = f"http://repo.anaconda.com/pkgs/free/{base_platform}-64/{package_name}-py36_0.tar.bz2" fn = f"{package_name}-py36_0.tar.bz2" download(f, fn) @@ -78,9 +76,7 @@ def test_show_imports(base_platform, package, capfd): def test_no_imports_found(base_platform, package, capfd): package_name, example_file = package - f = "http://repo.anaconda.com/pkgs/free/{}-64/{}-py36_0.tar.bz2".format( - base_platform, package_name - ) + f = f"http://repo.anaconda.com/pkgs/free/{base_platform}-64/{package_name}-py36_0.tar.bz2" fn = f"{package_name}-py36_0.tar.bz2" download(f, fn) @@ -96,9 +92,7 @@ def test_no_imports_found(base_platform, package, capfd): def test_no_platform(base_platform, package): package_name, example_file = package - f = "http://repo.anaconda.com/pkgs/free/{}-64/{}-py36_0.tar.bz2".format( - base_platform, package_name - ) + f = f"http://repo.anaconda.com/pkgs/free/{base_platform}-64/{package_name}-py36_0.tar.bz2" fn = f"{package_name}-py36_0.tar.bz2" download(f, fn) @@ -122,9 +116,7 @@ def test_c_extension_error(base_platform, package): if platform == source_platform: platforms.remove(platform) - f = "http://repo.anaconda.com/pkgs/free/{}-64/{}-py36_0.tar.bz2".format( - base_platform, package_name - ) + f = f"http://repo.anaconda.com/pkgs/free/{base_platform}-64/{package_name}-py36_0.tar.bz2" fn = f"{package_name}-py36_0.tar.bz2" download(f, fn) @@ -133,8 +125,8 @@ def test_c_extension_error(base_platform, package): api.convert(fn, platforms=platform) assert ( - "WARNING: Package {} contains C extensions; skipping conversion. " - "Use -f to force conversion.".format(fn) + f"WARNING: Package {fn} contains C extensions; skipping conversion. " + "Use -f to force conversion." ) in str(e.value) @@ -150,9 +142,7 @@ def test_c_extension_conversion(base_platform, package): if platform == source_platform: platforms.remove(platform) - f = "http://repo.anaconda.com/pkgs/free/{}-64/{}-py36_0.tar.bz2".format( - base_platform, package_name - ) + f = f"http://repo.anaconda.com/pkgs/free/{base_platform}-64/{package_name}-py36_0.tar.bz2" fn = f"{package_name}-py36_0.tar.bz2" download(f, fn) @@ -170,9 +160,7 @@ def test_c_extension_conversion(base_platform, package): def test_convert_platform_to_others(base_platform, package): package_name, example_file = package subdir = f"{base_platform}-64" - f = "http://repo.anaconda.com/pkgs/free/{}/{}-py27_0.tar.bz2".format( - subdir, package_name - ) + f = f"http://repo.anaconda.com/pkgs/free/{subdir}/{package_name}-py27_0.tar.bz2" fn = f"{package_name}-py27_0.tar.bz2" download(f, fn) expected_paths_json = package_has_file(fn, "info/paths.json") @@ -254,9 +242,7 @@ def test_convert_from_unix_to_win_creates_entry_points(testing_config, request): def test_convert_dependencies(base_platform, package): package_name, example_file = package subdir = f"{base_platform}-64" - f = "http://repo.anaconda.com/pkgs/free/{}/{}-np112py36_0.tar.bz2".format( - subdir, package_name - ) + f = f"http://repo.anaconda.com/pkgs/free/{subdir}/{package_name}-np112py36_0.tar.bz2" fn = f"{package_name}-np112py36_0.tar.bz2" download(f, fn) @@ -291,9 +277,7 @@ def test_convert_dependencies(base_platform, package): def test_convert_no_dependencies(base_platform, package): package_name, example_file = package subdir = f"{base_platform}-64" - f = "http://repo.anaconda.com/pkgs/free/{}/{}-np112py36_0.tar.bz2".format( - subdir, package_name - ) + f = f"http://repo.anaconda.com/pkgs/free/{subdir}/{package_name}-np112py36_0.tar.bz2" fn = f"{package_name}-np112py36_0.tar.bz2" download(f, fn) @@ -324,9 +308,7 @@ def test_skip_conversion(base_platform, package, capfd): package_name, example_file = package source_plat_arch = f"{base_platform}-64" - f = "http://repo.anaconda.com/pkgs/free/{}-64/{}-np112py36_0.tar.bz2".format( - base_platform, package_name - ) + f = f"http://repo.anaconda.com/pkgs/free/{base_platform}-64/{package_name}-np112py36_0.tar.bz2" fn = f"{package_name}-np112py36_0.tar.bz2" download(f, fn) @@ -337,8 +319,8 @@ def test_skip_conversion(base_platform, package, capfd): output, error = capfd.readouterr() skip_message = ( - "Source platform '{}' and target platform '{}' are identical. " - "Skipping conversion.\n".format(source_plat_arch, source_plat_arch) + f"Source platform '{source_plat_arch}' and target platform '{source_plat_arch}' are identical. " + "Skipping conversion.\n" ) package = os.path.join(source_plat_arch, fn) @@ -361,9 +343,7 @@ def test_renaming_executables(base_platform, package): """ package_name, example_file = package subdir = f"{base_platform}-64" - f = "http://repo.anaconda.com/pkgs/free/{}/{}-py27_0.tar.bz2".format( - subdir, package_name - ) + f = f"http://repo.anaconda.com/pkgs/free/{subdir}/{package_name}-py27_0.tar.bz2" fn = f"{package_name}-py27_0.tar.bz2" download(f, fn) expected_paths_json = package_has_file(fn, "info/paths.json") diff --git a/tests/test_api_render.py b/tests/test_api_render.py index 1451fbbbe0..878617e78d 100644 --- a/tests/test_api_render.py +++ b/tests/test_api_render.py @@ -99,9 +99,7 @@ def test_get_output_file_path_jinja2(testing_config): assert build_path == os.path.join( testing_config.croot, testing_config.host_subdir, - "conda-build-test-source-git-jinja2-1.20.2-py{}{}_0_g262d444.tar.bz2".format( - python, _hash - ), + f"conda-build-test-source-git-jinja2-1.20.2-py{python}{_hash}_0_g262d444.tar.bz2", ) diff --git a/tests/test_jinja_context.py b/tests/test_jinja_context.py index 3a82ed0227..8393bc6ba5 100644 --- a/tests/test_jinja_context.py +++ b/tests/test_jinja_context.py @@ -126,7 +126,7 @@ def test_load_setup_py_data_from_setup_cfg(testing_metadata, tmp_path: Path): setup_py = tmp_path / "setup.py" setup_cfg = tmp_path / "setup.cfg" setup_py.write_text( - "from setuptools import setup\n" 'setup(name="name_from_setup_py")\n' + 'from setuptools import setup\nsetup(name="name_from_setup_py")\n' ) setup_cfg.write_text( "[metadata]\n" diff --git a/tests/test_subpackages.py b/tests/test_subpackages.py index 2d63042cb5..ca6004eefd 100644 --- a/tests/test_subpackages.py +++ b/tests/test_subpackages.py @@ -116,9 +116,7 @@ def test_intradependencies(testing_config): outputs2_set = {os.path.basename(p) for p in outputs2} assert ( outputs1_set == outputs2_set - ), "pkgs differ :: get_output_file_paths()={} but build()={}".format( - outputs1_set, outputs2_set - ) + ), f"pkgs differ :: get_output_file_paths()={outputs1_set} but build()={outputs2_set}" def test_git_in_output_version(testing_config, conda_build_test_recipe_envvar: str): @@ -335,6 +333,16 @@ def test_build_script_and_script_env(testing_config): api.build(recipe, config=testing_config) +@pytest.mark.sanity +def test_build_script_and_script_env_warn_empty_script_env(testing_config): + recipe = os.path.join(subpackage_dir, "_build_script_missing_var") + with pytest.warns( + UserWarning, + match="The environment variable 'TEST_FN_DOESNT_EXIST' specified in script_env is undefined", + ): + api.build(recipe, config=testing_config) + + @pytest.mark.sanity @pytest.mark.skipif(sys.platform != "darwin", reason="only implemented for mac") def test_strong_run_exports_from_build_applies_to_host(testing_config): diff --git a/tests/test_utils.py b/tests/test_utils.py index baa5bf5a34..727859501a 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -274,7 +274,7 @@ def test_logger_config_from_file(testing_workdir, capfd, mocker): test_file = os.path.join(testing_workdir, "build_log_config.yaml") with open(test_file, "w") as f: f.write( - """ + f""" version: 1 formatters: simple: @@ -286,14 +286,14 @@ def test_logger_config_from_file(testing_workdir, capfd, mocker): formatter: simple stream: ext://sys.stdout loggers: - {}: + {__name__}: level: WARN handlers: [console] propagate: no root: level: DEBUG handlers: [console] -""".format(__name__) +""" ) cc_conda_build = mocker.patch.object(utils, "cc_conda_build") cc_conda_build.get.return_value = test_file diff --git a/tests/utils.py b/tests/utils.py index 4819d3c76b..d7bd5b479d 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -53,8 +53,9 @@ def get_valid_recipes(*parts: Path | str) -> Generator[Path, None, None]: def add_mangling(filename): - filename = os.path.splitext(filename)[0] + ".cpython-{}{}.py".format( - sys.version_info.major, sys.version_info.minor + filename = ( + os.path.splitext(filename)[0] + + f".cpython-{sys.version_info.major}{sys.version_info.minor}.py" ) filename = os.path.join( os.path.dirname(filename), "__pycache__", os.path.basename(filename)