diff --git a/bootstrap-obvious-ci-and-miniconda.py b/bootstrap-obvious-ci-and-miniconda.py index 617f5fc2a..e6768762e 100644 --- a/bootstrap-obvious-ci-and-miniconda.py +++ b/bootstrap-obvious-ci-and-miniconda.py @@ -7,10 +7,9 @@ """ import argparse -import os +from pathlib import Path import platform import subprocess -import sys try: from urllib.request import urlretrieve @@ -85,22 +84,22 @@ def main( else: raise ValueError("Unsupported operating system.") - if not os.path.exists(basename): + if not Path(basename).exists(): print("Downloading from {}".format(URL)) urlretrieve(URL, basename) else: print("Using cached version of {}".format(URL)) # Install with powershell. - if os.path.exists(target_dir): + if Path(target_dir).exists(): raise IOError("Installation directory already exists") subprocess.check_call(cmd) - if not os.path.isdir(target_dir): + if not Path(target_dir).is_dir(): raise RuntimeError("Failed to install miniconda :(") if install_obvci: - conda_path = os.path.join(target_dir, bin_dir, "conda") + conda_path = Path(target_dir, bin_dir, "conda") subprocess.check_call( [ conda_path, diff --git a/conda_smithy/azure_ci_utils.py b/conda_smithy/azure_ci_utils.py index a10a3d6f5..fb5035308 100644 --- a/conda_smithy/azure_ci_utils.py +++ b/conda_smithy/azure_ci_utils.py @@ -1,4 +1,5 @@ import os +from pathlib import Path import typing import warnings @@ -41,7 +42,7 @@ def __init__( try: with open( - os.path.expanduser("~/.conda-smithy/azure.token"), "r" + Path("~/.conda-smithy/azure.token").expanduser(), "r" ) as fh: self.token = fh.read().strip() if not self.token: diff --git a/conda_smithy/ci_register.py b/conda_smithy/ci_register.py index b6a2117ed..56155f488 100755 --- a/conda_smithy/ci_register.py +++ b/conda_smithy/ci_register.py @@ -1,5 +1,6 @@ #!/usr/bin/env python import os +from pathlib import Path import requests import time import sys @@ -14,7 +15,7 @@ # https://circleci.com/api/v1/project/:username/:project/envvar?circle-token=:token try: - with open(os.path.expanduser("~/.conda-smithy/circle.token"), "r") as fh: + with open(Path("~/.conda-smithy/circle.token").expanduser(), "r") as fh: circle_token = fh.read().strip() if not circle_token: raise ValueError() @@ -25,7 +26,7 @@ ) try: - with open(os.path.expanduser("~/.conda-smithy/appveyor.token"), "r") as fh: + with open(Path("~/.conda-smithy/appveyor.token").expanduser(), "r") as fh: appveyor_token = fh.read().strip() if not appveyor_token: raise ValueError() @@ -36,7 +37,7 @@ ) try: - with open(os.path.expanduser("~/.conda-smithy/drone.token"), "r") as fh: + with open(Path("~/.conda-smithy/drone.token").expanduser(), "r") as fh: drone_token = fh.read().strip() if not drone_token: raise ValueError() @@ -51,7 +52,7 @@ except KeyError: try: with open( - os.path.expanduser("~/.conda-smithy/anaconda.token"), "r" + Path("~/.conda-smithy/anaconda.token").expanduser(), "r" ) as fh: anaconda_token = fh.read().strip() if not anaconda_token: @@ -91,7 +92,7 @@ def travis_headers(): "Content-Type": "application/json", "Travis-API-Version": "3", } - travis_token = os.path.expanduser("~/.conda-smithy/travis.token") + travis_token = Path("~/.conda-smithy/travis.token").expanduser() try: with open(travis_token, "r") as fh: token = fh.read().strip() diff --git a/conda_smithy/ci_skeleton.py b/conda_smithy/ci_skeleton.py index a51efd0a6..e4bde9f40 100644 --- a/conda_smithy/ci_skeleton.py +++ b/conda_smithy/ci_skeleton.py @@ -6,7 +6,7 @@ added to conda-forge's queue. """ -import os +from pathlib import Path import sys from .configure_feedstock import make_jinja_env @@ -14,13 +14,12 @@ def _render_template(template_file, env, forge_dir, config): """Renders the template""" - template = env.get_template( - os.path.basename(template_file) + ".ci-skel.tmpl" - ) - target_fname = os.path.join(forge_dir, template_file) - print("Generating " + target_fname, file=sys.stderr) + template_file_name = Path(template_file).name + template = env.get_template(template_file_name + ".ci-skel.tmpl") + target_fname = Path(forge_dir, template_file) + print("Generating ", target_fname, file=sys.stderr) new_file_contents = template.render(**config) - os.makedirs(os.path.dirname(target_fname), exist_ok=True) + target_fname.parent.mkdir(parents=True, exist_ok=True) with open(target_fname, "w") as fh: fh.write(new_file_contents) @@ -37,18 +36,16 @@ def _insert_into_gitignore( ): """Places gitignore contents into gitignore.""" # get current contents - fname = os.path.join(feedstock_directory, ".gitignore") - print("Updating " + fname) - if os.path.isfile(fname): + fname = Path(feedstock_directory, ".gitignore") + print("Updating ", fname.name) + if fname.is_file(): with open(fname, "r") as f: s = f.read() before, _, s = s.partition(prefix) _, _, after = s.partition(suffix) else: before = after = "" - dname = os.path.dirname(fname) - if dname: - os.makedirs(dname, exist_ok=True) + fname.parent.mkdir(parents=True, exist_ok=True) new = prefix + GITIGNORE_ADDITIONAL + suffix # write out the file with open(fname, "w") as f: @@ -60,7 +57,7 @@ def generate( package_name="pkg", feedstock_directory=".", recipe_directory="recipe" ): """Generates the CI skeleton.""" - forge_dir = os.path.abspath(feedstock_directory) + forge_dir = Path(feedstock_directory).resolve() env = make_jinja_env(forge_dir) config = dict( package_name=package_name, @@ -69,8 +66,7 @@ def generate( ) # render templates _render_template("conda-forge.yml", env, forge_dir, config) - _render_template( - os.path.join(recipe_directory, "meta.yaml"), env, forge_dir, config - ) + recipe_file_name = str(Path(recipe_directory, "meta.yaml")) + _render_template(recipe_file_name, env, forge_dir, config) # update files which may exist with other content _insert_into_gitignore(feedstock_directory=feedstock_directory) diff --git a/conda_smithy/cli.py b/conda_smithy/cli.py index d18b964f2..aa4d99570 100644 --- a/conda_smithy/cli.py +++ b/conda_smithy/cli.py @@ -1,5 +1,5 @@ -import os import logging +from pathlib import Path import subprocess import sys import time @@ -28,20 +28,21 @@ def default_feedstock_config_path(feedstock_directory): - return os.path.join(feedstock_directory, "conda-forge.yml") + return str(Path(feedstock_directory, "conda-forge.yml")) def generate_feedstock_content(target_directory, source_recipe_dir): - target_directory = os.path.abspath(target_directory) + target_directory = Path(target_directory).resolve() recipe_dir = "recipe" - target_recipe_dir = os.path.join(target_directory, recipe_dir) + target_recipe_dir = Path(target_directory, recipe_dir) + target_recipe_dir.mkdir(parents=True, exist_ok=True) - if not os.path.exists(target_recipe_dir): - os.makedirs(target_recipe_dir) # If there is a source recipe, copy it now to the right dir if source_recipe_dir: try: - configure_feedstock.copytree(source_recipe_dir, target_recipe_dir) + configure_feedstock.copytree( + source_recipe_dir, str(target_recipe_dir) + ) except Exception as e: import sys @@ -50,17 +51,15 @@ def generate_feedstock_content(target_directory, source_recipe_dir): ).with_traceback(sys.exc_info()[2]) forge_yml = default_feedstock_config_path(target_directory) - if not os.path.exists(forge_yml): + if not Path(forge_yml).exists(): with feedstock_io.write_file(forge_yml) as fh: fh.write("{}") # merge in the existing configuration in the source recipe directory - forge_yml_recipe = os.path.join(source_recipe_dir, "conda-forge.yml") + forge_yml_recipe = Path(source_recipe_dir, "conda-forge.yml") yaml = YAML() - if os.path.exists(forge_yml_recipe): - feedstock_io.remove_file( - os.path.join(target_recipe_dir, "conda-forge.yml") - ) + if forge_yml_recipe.exists(): + feedstock_io.remove_file(target_recipe_dir.joinpath("conda-forge.yml")) try: with open(forge_yml_recipe, "r") as fp: _cfg = yaml.load(fp.read()) @@ -114,7 +113,7 @@ def __init__(self, parser): def __call__(self, args): # check some error conditions - if args.recipe_directory and not os.path.isdir(args.recipe_directory): + if args.recipe_directory and not Path(args.recipe_directory).is_dir(): raise IOError( "The source recipe directory should be the directory of the " "conda-recipe you want to build a feedstock for. Got {}".format( @@ -135,7 +134,7 @@ def __call__(self, args): __version__ ) - os.makedirs(feedstock_directory) + Path(feedstock_directory).mkdir(parents=True) subprocess.check_call(["git", "init"], cwd=feedstock_directory) generate_feedstock_content(feedstock_directory, args.recipe_directory) subprocess.check_call( @@ -224,7 +223,7 @@ def __init__(self, parser): scp = self.subcommand_parser scp.add_argument( "--feedstock_directory", - default=feedstock_io.get_repo_root(os.getcwd()) or os.getcwd(), + default=feedstock_io.get_repo_root(Path.cwd()) or Path.cwd(), help="The directory of the feedstock git repository.", ) scp.add_argument( @@ -473,7 +472,7 @@ def __init__(self, parser): scp = self.subcommand_parser scp.add_argument( "--feedstock_directory", - default=feedstock_io.get_repo_root(os.getcwd()) or os.getcwd(), + default=feedstock_io.get_repo_root(Path.cwd()) or Path.cwd(), help="The directory of the feedstock git repository.", ) scp.add_argument( @@ -502,7 +501,7 @@ def __call__(self, args): from conda_smithy import azure_ci_utils owner = args.user or args.organization - repo = os.path.basename(os.path.abspath(args.feedstock_directory)) + repo = Path(args.feedstock_directory).resolve().name config = azure_ci_utils.AzureConfig( org_or_user=owner, project_name=args.project_name @@ -537,7 +536,7 @@ def __init__(self, parser): scp = self.subcommand_parser scp.add_argument( "--feedstock_directory", - default=feedstock_io.get_repo_root(os.getcwd()) or os.getcwd(), + default=feedstock_io.get_repo_root(Path.cwd()) or Path.cwd(), help="The directory of the feedstock git repository.", ) scp.add_argument( @@ -608,13 +607,13 @@ def __init__(self, parser): ) scp = self.subcommand_parser scp.add_argument("--conda-forge", action="store_true") - scp.add_argument("recipe_directory", default=[os.getcwd()], nargs="*") + scp.add_argument("recipe_directory", default=[Path.cwd()], nargs="*") def __call__(self, args): all_good = True for recipe in args.recipe_directory: lints, hints = lint_recipe.main( - os.path.join(recipe), + Path(recipe), conda_forge=args.conda_forge, return_hints=True, ) @@ -682,7 +681,7 @@ def __init__(self, parser): scp = self.subcommand_parser scp.add_argument( "--feedstock-directory", - default=os.getcwd(), + default=Path.cwd(), help="The directory of the feedstock git repository.", dest="feedstock_directory", ) @@ -755,7 +754,7 @@ def __init__(self, parser): scp = self.subcommand_parser scp.add_argument( "--feedstock_directory", - default=feedstock_io.get_repo_root(os.getcwd()) or os.getcwd(), + default=feedstock_io.get_repo_root(Path.cwd()) or Path.cwd(), help="The directory of the feedstock git repository.", ) scp.add_argument( @@ -780,7 +779,7 @@ def __call__(self, args): ) owner = args.user or args.organization - repo = os.path.basename(os.path.abspath(args.feedstock_directory)) + repo = Path(args.feedstock_directory).resolve().name if not args.unique_token_per_provider: generate_and_write_feedstock_token(owner, repo) @@ -831,7 +830,7 @@ def __init__(self, parser): scp = self.subcommand_parser scp.add_argument( "--feedstock_directory", - default=feedstock_io.get_repo_root(os.getcwd()) or os.getcwd(), + default=feedstock_io.get_repo_root(Path.cwd()) or Path.cwd(), help="The directory of the feedstock git repository.", ) scp.add_argument( @@ -897,7 +896,7 @@ def __call__(self, args): drone_endpoints = [drone_default_endpoint] owner = args.user or args.organization - repo = os.path.basename(os.path.abspath(args.feedstock_directory)) + repo = Path(args.feedstock_directory).resolve().name if args.token_repo is None: token_repo = ( @@ -978,7 +977,7 @@ def __init__(self, parser): scp = self.subcommand_parser scp.add_argument( "--feedstock_directory", - default=feedstock_io.get_repo_root(os.getcwd()) or os.getcwd(), + default=feedstock_io.get_repo_root(Path.cwd()) or Path.cwd(), help="The directory of the feedstock git repository.", ) scp.add_argument( @@ -1031,7 +1030,7 @@ def __call__(self, args): from conda_smithy.anaconda_token_rotation import rotate_anaconda_token owner = args.user or args.organization - repo = os.path.basename(os.path.abspath(args.feedstock_directory)) + repo = Path(args.feedstock_directory).resolve().name if args.feedstock_config is None: args.feedstock_config = default_feedstock_config_path( diff --git a/conda_smithy/configure_feedstock.py b/conda_smithy/configure_feedstock.py index b0f46cd92..7246363b5 100644 --- a/conda_smithy/configure_feedstock.py +++ b/conda_smithy/configure_feedstock.py @@ -64,7 +64,7 @@ from . import __version__ -conda_forge_content = os.path.abspath(os.path.dirname(__file__)) +conda_forge_content = Path(__file__).parent.resolve() logger = logging.getLogger(__name__) @@ -129,15 +129,15 @@ def copytree(src, dst, ignore=(), root_dst=None): are added to the repo""" if root_dst is None: root_dst = dst - for item in os.listdir(src): - s = os.path.join(src, item) - d = os.path.join(dst, item) - rel = os.path.relpath(d, root_dst) - if _ignore_match(ignore, rel): + for item in Path(src).iterdir(): + s = Path(src, item.name) + d = Path(dst, item.name) + rel = d.relative_to(root_dst) + if _ignore_match(ignore, str(rel)): continue - elif os.path.isdir(s): - if not os.path.exists(d): - os.makedirs(d) + elif s.is_dir(): + if not d.exists(): + d.mkdir(parents=True) copytree(s, d, ignore, root_dst=root_dst) else: copy_file(s, d) @@ -548,9 +548,9 @@ def _collapse_subpackage_variants( # determine if MACOSX_DEPLOYMENT_TARGET appears in recipe-local CBC; # all metas in list_of_metas come from same recipe, so path is identical - cbc_path = os.path.join(list_of_metas[0].path, "conda_build_config.yaml") + cbc_path = Path(list_of_metas[0].path, "conda_build_config.yaml") has_macdt = False - if os.path.exists(cbc_path): + if cbc_path.exists(): with open(cbc_path, "r") as f: lines = f.readlines() if any(re.match(r"^\s*MACOSX_DEPLOYMENT_TARGET:", x) for x in lines): @@ -762,10 +762,10 @@ def dump_subspace_config_files( # Shorten file name length to avoid hitting maximum filename limits. config_name = short_config_name - out_folder = os.path.join(root_path, ".ci_support") - out_path = os.path.join(out_folder, config_name) + ".yaml" - if not os.path.isdir(out_folder): - os.makedirs(out_folder) + out_folder = Path(root_path, ".ci_support") + out_path = Path(out_folder, config_name + ".yaml") + if not out_folder.is_dir(): + out_folder.mkdir(parents=True) config = finalize_config(config, platform, arch, forge_config) logger.debug( @@ -798,11 +798,11 @@ def _get_fast_finish_script( fast_finish_script = "" tooling_branch = forge_config["github"]["tooling_branch_name"] - cfbs_fpath = os.path.join( + cfbs_fpath = Path( forge_dir, forge_config["recipe_dir"], "ff_ci_pr_build.py" ) if provider_name == "appveyor": - if os.path.exists(cfbs_fpath): + if cfbs_fpath.exists(): fast_finish_script = "{recipe_dir}\\ff_ci_pr_build".format( recipe_dir=forge_config["recipe_dir"] ) @@ -823,7 +823,7 @@ def _get_fast_finish_script( else: # If the recipe supplies its own ff_ci_pr_build.py script, # we use it instead of the global one. - if os.path.exists(cfbs_fpath): + if cfbs_fpath.exists(): get_fast_finish_script += ( "cat {recipe_dir}/ff_ci_pr_build.py".format( recipe_dir=forge_config["recipe_dir"] @@ -996,7 +996,7 @@ def _render_ci_provider( # detect if `compiler('cuda')` is used in meta.yaml, # and set appropriate environment variable with open( - os.path.join(forge_dir, forge_config["recipe_dir"], "meta.yaml") + Path(forge_dir, forge_config["recipe_dir"], "meta.yaml") ) as f: meta_lines = f.readlines() # looking for `compiler('cuda')` with both quote variants; @@ -1018,7 +1018,7 @@ def _render_ci_provider( combined_variant_spec, _, ) = conda_build.variants.get_package_combined_spec( - os.path.join(forge_dir, forge_config["recipe_dir"]), config=config + Path(forge_dir, forge_config["recipe_dir"]), config=config ) migrated_combined_variant_spec = migrate_combined_spec( @@ -1064,19 +1064,19 @@ def _render_ci_provider( # Thus we move it out of the way. # TODO: upstream this as a flag in conda-build try: - _recipe_cbc = os.path.join( + _recipe_cbc = Path( forge_dir, forge_config["recipe_dir"], "conda_build_config.yaml", ) - if os.path.exists(_recipe_cbc): - os.rename(_recipe_cbc, _recipe_cbc + ".conda.smithy.bak") + if _recipe_cbc.exists(): + _recipe_cbc.rename(f"{_recipe_cbc}.conda.smithy.bak") channel_sources = migrated_combined_variant_spec.get( "channel_sources", [""] )[0].split(",") metas = _conda_build_api_render_for_smithy( - os.path.join(forge_dir, forge_config["recipe_dir"]), + Path(forge_dir, forge_config["recipe_dir"]), platform=platform, arch=arch, ignore_system_variants=True, @@ -1087,8 +1087,8 @@ def _render_ci_provider( channel_urls=channel_sources, ) finally: - if os.path.exists(_recipe_cbc + ".conda.smithy.bak"): - os.rename(_recipe_cbc + ".conda.smithy.bak", _recipe_cbc) + if Path(f"{_recipe_cbc}.conda.smithy.bak").exists(): + Path(f"{_recipe_cbc}.conda.smithy.bak").rename(_recipe_cbc) # render returns some download & reparsing info that we don't care about metas = [m for m, _, _ in metas] @@ -1175,19 +1175,18 @@ def _render_ci_provider( # If the recipe has its own conda_forge_ci_setup package, then # install that - if os.path.exists( - os.path.join( + if ( + Path( forge_dir, forge_config["recipe_dir"], "conda_forge_ci_setup", "__init__.py", - ) - ) and os.path.exists( - os.path.join( + ).exists() + and Path( forge_dir, forge_config["recipe_dir"], "setup.py", - ) + ).exists() ): forge_config["local_ci_setup"] = True else: @@ -1238,26 +1237,26 @@ def _get_build_setup_line(forge_dir, platform, forge_config): # If the recipe supplies its own run_conda_forge_build_setup script_linux, # we use it instead of the global one. if platform == "linux": - cfbs_fpath = os.path.join( + cfbs_fpath = Path( forge_dir, forge_config["recipe_dir"], "run_conda_forge_build_setup_linux", ) elif platform == "win": - cfbs_fpath = os.path.join( + cfbs_fpath = Path( forge_dir, forge_config["recipe_dir"], "run_conda_forge_build_setup_win.bat", ) else: - cfbs_fpath = os.path.join( + cfbs_fpath = Path( forge_dir, forge_config["recipe_dir"], "run_conda_forge_build_setup_osx", ) build_setup = "" - if os.path.exists(cfbs_fpath): + if cfbs_fpath.exists(): if platform == "linux": build_setup += textwrap.dedent( """\ @@ -1328,20 +1327,18 @@ def _circle_specific_setup(jinja_env, forge_config, forge_dir, platform): ) # Fix permission of other shell files. - target_fnames = [ - os.path.join(forge_dir, ".circleci", "checkout_merge_commit.sh") - ] + target_fnames = [Path(forge_dir, ".circleci", "checkout_merge_commit.sh")] for target_fname in target_fnames: set_exe_file(target_fname, True) def generate_yum_requirements(forge_config, forge_dir): # If there is a "yum_requirements.txt" file in the recipe, we honour it. - yum_requirements_fpath = os.path.join( + yum_requirements_fpath = Path( forge_dir, forge_config["recipe_dir"], "yum_requirements.txt" ) yum_build_setup = "" - if os.path.exists(yum_requirements_fpath): + if yum_requirements_fpath.exists(): with open(yum_requirements_fpath) as fh: requirements = [ line.strip() @@ -1418,7 +1415,7 @@ def _get_platforms_of_provider(provider, forge_config): def render_circle(jinja_env, forge_config, forge_dir, return_metadata=False): - target_path = os.path.join(forge_dir, ".circleci", "config.yml") + target_path = Path(forge_dir, ".circleci", "config.yml") template_filename = "circle.yml.tmpl" fast_finish_text = textwrap.dedent( """\ @@ -1428,8 +1425,8 @@ def render_circle(jinja_env, forge_config, forge_dir, return_metadata=False): ) extra_platform_files = { "common": [ - os.path.join(forge_dir, ".circleci", "checkout_merge_commit.sh"), - os.path.join(forge_dir, ".circleci", "fast_finish_ci_pr_build.sh"), + Path(forge_dir, ".circleci", "checkout_merge_commit.sh"), + Path(forge_dir, ".circleci", "fast_finish_ci_pr_build.sh"), ], } @@ -1487,13 +1484,12 @@ def _render_template_exe_files( forge_config, jinja_env, template_files, forge_dir ): for template_file in template_files: - template = jinja_env.get_template( - os.path.basename(template_file) + ".tmpl" - ) - target_fname = os.path.join(forge_dir, template_file) + template = jinja_env.get_template(Path(template_file).name + ".tmpl") + target_fname = str(Path(forge_dir, template_file)) new_file_contents = template.render(**forge_config) - if target_fname in get_common_scripts(forge_dir) and os.path.exists( - target_fname + if ( + target_fname in get_common_scripts(forge_dir) + and Path(target_fname).exists() ): with open(target_fname, "r") as fh: old_file_contents = fh.read() @@ -1525,7 +1521,7 @@ def _render_template_exe_files( def render_travis(jinja_env, forge_config, forge_dir, return_metadata=False): - target_path = os.path.join(forge_dir, ".travis.yml") + target_path = Path(forge_dir, ".travis.yml") template_filename = "travis.yml.tmpl" fast_finish_text = "" @@ -1568,7 +1564,7 @@ def _appveyor_specific_setup(jinja_env, forge_config, forge_dir, platform): def render_appveyor(jinja_env, forge_config, forge_dir, return_metadata=False): - target_path = os.path.join(forge_dir, ".appveyor.yml") + target_path = Path(forge_dir, ".appveyor.yml") fast_finish_text = textwrap.dedent( """\ {get_fast_finish_script} @@ -1727,9 +1723,7 @@ def _github_actions_specific_setup( def render_github_actions( jinja_env, forge_config, forge_dir, return_metadata=False ): - target_path = os.path.join( - forge_dir, ".github", "workflows", "conda-build.yml" - ) + target_path = Path(forge_dir, ".github", "workflows", "conda-build.yml") template_filename = "github-actions.yml.tmpl" fast_finish_text = "" @@ -1846,7 +1840,7 @@ def _azure_specific_setup(jinja_env, forge_config, forge_dir, platform): def render_azure(jinja_env, forge_config, forge_dir, return_metadata=False): - target_path = os.path.join(forge_dir, "azure-pipelines.yml") + target_path = Path(forge_dir, "azure-pipelines.yml") template_filename = "azure-pipelines.yml.tmpl" fast_finish_text = "" @@ -1859,7 +1853,7 @@ def render_azure(jinja_env, forge_config, forge_dir, return_metadata=False): logger.debug("azure platforms retreived") - remove_file_or_dir(os.path.join(forge_dir, ".azure-pipelines")) + remove_file_or_dir(Path(forge_dir, ".azure-pipelines")) return _render_ci_provider( "azure", jinja_env=jinja_env, @@ -1903,7 +1897,7 @@ def _drone_specific_setup(jinja_env, forge_config, forge_dir, platform): def render_drone(jinja_env, forge_config, forge_dir, return_metadata=False): - target_path = os.path.join(forge_dir, ".drone.yml") + target_path = Path(forge_dir, ".drone.yml") template_filename = "drone.yml.tmpl" fast_finish_text = "" @@ -1937,7 +1931,7 @@ def render_drone(jinja_env, forge_config, forge_dir, return_metadata=False): def render_woodpecker( jinja_env, forge_config, forge_dir, return_metadata=False ): - target_path = os.path.join(forge_dir, ".woodpecker.yml") + target_path = Path(forge_dir, ".woodpecker.yml") template_filename = "woodpecker.yml.tmpl" fast_finish_text = "" @@ -2014,7 +2008,7 @@ def render_README(jinja_env, forge_config, forge_dir, render_info=None): if len(metas) == 0: try: metas = conda_build.api.render( - os.path.join(forge_dir, forge_config["recipe_dir"]), + str(Path(forge_dir, forge_config["recipe_dir"])), exclusive_config_file=forge_config["exclusive_config_file"], permit_undefined_jinja=True, finalize=False, @@ -2032,15 +2026,15 @@ def render_README(jinja_env, forge_config, forge_dir, render_info=None): package_name = get_feedstock_name_from_meta(metas[0]) package_about = get_feedstock_about_from_meta(metas[0]) - ci_support_path = os.path.join(forge_dir, ".ci_support") + ci_support_path = Path(forge_dir, ".ci_support") variants = [] channel_targets = [] - if os.path.exists(ci_support_path): - for filename in os.listdir(ci_support_path): - if filename.endswith(".yaml"): - variant_name, _ = os.path.splitext(filename) + if ci_support_path.exists(): + for filename in ci_support_path.iterdir(): + if filename.suffix == ".yaml": + variant_name = filename.stem variants.append(variant_name) - with open(os.path.join(ci_support_path, filename)) as fh: + with open(ci_support_path.joinpath(filename)) as fh: data = yaml.safe_load(fh) channel_targets.append( data.get("channel_targets", ["conda-forge main"])[0] @@ -2064,7 +2058,7 @@ def render_README(jinja_env, forge_config, forge_dir, render_info=None): subpackages_about.append((name, about)) template = jinja_env.get_template("README.md.tmpl") - target_fname = os.path.join(forge_dir, "README.md") + target_fname = Path(forge_dir, "README.md") forge_config["noarch_python"] = all(meta.noarch for meta in metas) forge_config["package_about"] = subpackages_about forge_config["package_name"] = package_name @@ -2103,7 +2097,7 @@ def render_README(jinja_env, forge_config, forge_dir, render_info=None): with write_file(target_fname) as fh: fh.write(template.render(**forge_config)) - code_owners_file = os.path.join(forge_dir, ".github", "CODEOWNERS") + code_owners_file = Path(forge_dir, ".github", "CODEOWNERS") if len(forge_config["maintainers"]) > 0: with write_file(code_owners_file) as fh: line = "*" @@ -2130,17 +2124,17 @@ def render_github_actions_services(jinja_env, forge_config, forge_dir): skip_files = _get_skip_files(forge_config) for template_file in ["automerge.yml", "webservices.yml"]: template = jinja_env.get_template(template_file + ".tmpl") - rel_target_fname = os.path.join(".github", "workflows", template_file) + rel_target_fname = Path(".github", "workflows", template_file) if _ignore_match(skip_files, rel_target_fname): continue - target_fname = os.path.join(forge_dir, rel_target_fname) + target_fname = Path(forge_dir, rel_target_fname) new_file_contents = template.render(**forge_config) with write_file(target_fname) as fh: fh.write(new_file_contents) def copy_feedstock_content(forge_config, forge_dir): - feedstock_content = os.path.join(conda_forge_content, "feedstock_content") + feedstock_content = Path(conda_forge_content, "feedstock_content") skip_files = _get_skip_files(forge_config) copytree(feedstock_content, forge_dir, skip_files) @@ -2163,9 +2157,9 @@ def _read_forge_config(forge_dir, forge_yml=None): default_config = yaml.safe_load(fh.read()) if forge_yml is None: - forge_yml = os.path.join(forge_dir, "conda-forge.yml") + forge_yml = Path(forge_dir, "conda-forge.yml") - if not os.path.exists(forge_yml): + if not Path(forge_yml).exists(): raise RuntimeError( f"Could not find config file {forge_yml}." " Either you are not rerendering inside the feedstock root (likely)" @@ -2183,7 +2177,7 @@ def _read_forge_config(forge_dir, forge_yml=None): for err in chain(validate_lints, validate_hints): logger.warning( "%s: %s = %s -> %s", - os.path.relpath(forge_yml, forge_dir), + Path(forge_yml).relative_to(forge_dir), err.json_path, err.instance, err.message, @@ -2196,10 +2190,11 @@ def _read_forge_config(forge_dir, forge_yml=None): # check for conda-smithy 2.x matrix which we can't auto-migrate # to conda_build_config - if file_config.get("matrix") and not os.path.exists( - os.path.join( + if ( + file_config.get("matrix") + and not Path( forge_dir, config["recipe_dir"], "conda_build_config.yaml" - ) + ).exists() ): raise ValueError( "Cannot rerender with matrix in conda-forge.yml." @@ -2239,24 +2234,24 @@ def _legacy_compatibility_checks(config: dict, forge_dir): # remove those now. old_files = [ "disabled_appveyor.yml", - os.path.join("ci_support", "upload_or_check_non_existence.py"), + str(Path("ci_support", "upload_or_check_non_existence.py")), "circle.yml", "appveyor.yml", - os.path.join("ci_support", "checkout_merge_commit.sh"), - os.path.join("ci_support", "fast_finish_ci_pr_build.sh"), - os.path.join("ci_support", "run_docker_build.sh"), + str(Path("ci_support", "checkout_merge_commit.sh")), + str(Path("ci_support", "fast_finish_ci_pr_build.sh")), + str(Path("ci_support", "run_docker_build.sh")), "LICENSE", "__pycache__", - os.path.join(".github", "CONTRIBUTING.md"), - os.path.join(".github", "ISSUE_TEMPLATE.md"), - os.path.join(".github", "PULL_REQUEST_TEMPLATE.md"), - os.path.join(".github", "workflows", "main.yml"), + str(Path(".github", "CONTRIBUTING.md")), + str(Path(".github", "ISSUE_TEMPLATE.md")), + str(Path(".github", "PULL_REQUEST_TEMPLATE.md")), + str(Path(".github", "workflows", "main.yml")), ] for old_file in old_files: if old_file.replace(os.sep, "/") in config["skip_render"]: continue - remove_file_or_dir(os.path.join(forge_dir, old_file)) + remove_file_or_dir(Path(forge_dir, old_file)) # Older conda-smithy versions supported this with only one # entry. To avoid breakage, we are converting single elements @@ -2380,9 +2375,9 @@ def _load_forge_config(forge_dir, exclusive_config_file, forge_yml=None): os.environ["CF_MIN_R_VER"] = config["min_r_ver"] os.environ["CF_MAX_R_VER"] = config["max_r_ver"] - config["package"] = os.path.basename(forge_dir) + config["package"] = Path(forge_dir).name if not config["github"]["repo_name"]: - feedstock_name = os.path.basename(forge_dir) + feedstock_name = Path(forge_dir).name if not feedstock_name.endswith("-feedstock"): feedstock_name += "-feedstock" config["github"]["repo_name"] = feedstock_name @@ -2432,7 +2427,7 @@ def commit_changes(forge_file_directory, commit, cs_ver, cfp_ver, cb_ver): ) logger.info(msg) - is_git_repo = os.path.exists(os.path.join(forge_file_directory, ".git")) + is_git_repo = Path(forge_file_directory, ".git").exists() if is_git_repo: has_staged_changes = subprocess.call( ["git", "diff", "--cached", "--quiet", "--exit-code"], @@ -2466,7 +2461,7 @@ def get_cfp_file_path(temporary_directory): "Could not determine proper conda package extension for " "pinning package '%s'!" % pkg.url ) - dest = os.path.join( + dest = Path( temporary_directory, f"conda-forge-pinning-{ pkg.version }{ext}" ) @@ -2485,14 +2480,12 @@ def get_cfp_file_path(temporary_directory): cmd += ["x", "--dest", temporary_directory, dest] subprocess.check_call(cmd) - logger.debug(os.listdir(temporary_directory)) + logger.debug(list(Path(temporary_directory).iterdir())) - cf_pinning_file = os.path.join( - temporary_directory, "conda_build_config.yaml" - ) + cf_pinning_file = Path(temporary_directory, "conda_build_config.yaml") cf_pinning_ver = pkg.version - assert os.path.exists(cf_pinning_file) + assert cf_pinning_file.exists() return cf_pinning_file, cf_pinning_ver @@ -2545,8 +2538,8 @@ def get_cached_cfp_file_path(temporary_directory): def clear_variants(forge_dir): "Remove all variant files placed in the .ci_support path" - if os.path.isdir(os.path.join(forge_dir, ".ci_support")): - configs = glob.glob(os.path.join(forge_dir, ".ci_support", "*.yaml")) + if Path(forge_dir, ".ci_support").is_dir(): + configs = list(Path(forge_dir, ".ci_support").glob("*.yaml")) for config in configs: remove_file(config) @@ -2559,7 +2552,7 @@ def get_common_scripts(forge_dir): "create_conda_build_artifacts.bat", "create_conda_build_artifacts.sh", ]: - yield os.path.join(forge_dir, ".scripts", old_file) + yield Path(forge_dir, ".scripts", old_file) def clear_scripts(forge_dir): @@ -2578,19 +2571,17 @@ def clear_scripts(forge_dir): "create_conda_build_artifacts.bat", "create_conda_build_artifacts.sh", ]: - remove_file(os.path.join(forge_dir, folder, old_file)) + remove_file(Path(forge_dir, folder, old_file)) def make_jinja_env(feedstock_directory): """Creates a Jinja environment usable for rendering templates""" - forge_dir = os.path.abspath(feedstock_directory) - tmplt_dir = os.path.join(conda_forge_content, "templates") + forge_dir = Path(feedstock_directory).resolve() + tmplt_dir = Path(conda_forge_content, "templates") # Load templates from the feedstock in preference to the smithy's templates. env = SandboxedEnvironment( extensions=["jinja2.ext.do"], - loader=FileSystemLoader( - [os.path.join(forge_dir, "templates"), tmplt_dir] - ), + loader=FileSystemLoader([Path(forge_dir, "templates"), tmplt_dir]), ) return env @@ -2601,7 +2592,7 @@ def get_migrations_in_dir(migrations_root): from the timestamp to a tuple of (filename, migration_number) """ res = {} - for fn in glob.glob(os.path.join(migrations_root, "*.yaml")): + for fn in Path(migrations_root).glob("*.yaml"): with open(fn, "r") as f: contents = f.read() migration_yaml = ( @@ -2618,7 +2609,7 @@ def get_migrations_in_dir(migrations_root): .lower() == "true" ) - res[ts] = (fn, migration_number, use_local) + res[ts] = (str(fn), migration_number, use_local) return res @@ -2643,17 +2634,17 @@ def set_migration_fns(forge_dir, forge_config): feedstock, the filename of the migration in the feedstock is used. """ exclusive_config_file = forge_config["exclusive_config_file"] - cfp_migrations_dir = os.path.join( - os.path.dirname(exclusive_config_file), + cfp_migrations_dir = Path( + Path(exclusive_config_file).parent, "share", "conda-forge", "migrations", ) - migrations_root = os.path.join(forge_dir, ".ci_support", "migrations") + migrations_root = Path(forge_dir, ".ci_support", "migrations") migrations_in_feedstock = get_migrations_in_dir(migrations_root) - if not os.path.exists(cfp_migrations_dir): + if not cfp_migrations_dir.exists(): migration_fns = [fn for fn, _, _ in migrations_in_feedstock.values()] forge_config["migration_fns"] = migration_fns return @@ -2671,14 +2662,14 @@ def set_migration_fns(forge_dir, forge_config): new_fn, new_num, _ = migrations_in_cfp[ts] if num == new_num: logger.info( - f"{os.path.basename(fn)} from feedstock is ignored and upstream version is used" + f"{Path(fn).name} from feedstock is ignored and upstream version is used" ) result.append(new_fn) else: result.append(fn) else: # Delete this as this migration is over. - logger.info(f"{os.path.basename(fn)} is closed now. Removing") + logger.info(f"{Path(fn).name} is closed now. Removing") remove_file(fn) forge_config["migration_fns"] = result return @@ -2702,11 +2693,11 @@ def main( if check: return True - forge_dir = os.path.abspath(forge_file_directory) + forge_dir = Path(forge_file_directory).resolve() if exclusive_config_file is not None: - exclusive_config_file = os.path.join(forge_dir, exclusive_config_file) - if not os.path.exists(exclusive_config_file): + exclusive_config_file = Path(forge_dir, exclusive_config_file) + if not exclusive_config_file.exists(): raise RuntimeError("Given exclusive-config-file not found.") cf_pinning_ver = None @@ -2717,15 +2708,15 @@ def main( config = _load_forge_config(forge_dir, exclusive_config_file, forge_yml) - config["feedstock_name"] = os.path.basename(forge_dir) + config["feedstock_name"] = forge_dir.name env = make_jinja_env(forge_dir) logger.debug("env rendered") copy_feedstock_content(config, forge_dir) - if os.path.exists(os.path.join(forge_dir, "build-locally.py")): - set_exe_file(os.path.join(forge_dir, "build-locally.py")) + if Path(forge_dir, "build-locally.py").exists(): + set_exe_file(str(Path(forge_dir, "build-locally.py"))) clear_variants(forge_dir) clear_scripts(forge_dir) diff --git a/conda_smithy/feedstock_content/build-locally.py b/conda_smithy/feedstock_content/build-locally.py index e0d408d07..a1c87cdff 100755 --- a/conda_smithy/feedstock_content/build-locally.py +++ b/conda_smithy/feedstock_content/build-locally.py @@ -4,7 +4,7 @@ # locally. # import os -import glob +from pathlib import Path import subprocess from argparse import ArgumentParser import platform @@ -19,8 +19,8 @@ def setup_environment(ns): if ns.output_id: os.environ["BUILD_OUTPUT_ID"] = ns.output_id if "MINIFORGE_HOME" not in os.environ: - os.environ["MINIFORGE_HOME"] = os.path.join( - os.path.dirname(__file__), "miniforge3" + os.environ["MINIFORGE_HOME"] = str( + Path(__file__).parent.joinpath("miniforge3") ) @@ -35,9 +35,7 @@ def run_osx_build(ns): def verify_config(ns): - valid_configs = { - os.path.basename(f)[:-5] for f in glob.glob(".ci_support/*.yaml") - } + valid_configs = {f.stem for f in Path(".ci_support").glob("*.yaml")} print(f"valid configs are {valid_configs}") if ns.config in valid_configs: print("Using " + ns.config + " configuration") @@ -95,11 +93,9 @@ def main(args=None): elif ns.config.startswith("osx"): run_osx_build(ns) finally: - recipe_license_file = os.path.join( - "recipe", "recipe-scripts-license.txt" - ) - if os.path.exists(recipe_license_file): - os.remove(recipe_license_file) + recipe_license_file = Path("recipe", "recipe-scripts-license.txt") + if recipe_license_file.exists(): + recipe_license_file.unlink() if __name__ == "__main__": diff --git a/conda_smithy/feedstock_io.py b/conda_smithy/feedstock_io.py index f7ad6f90a..e0b1d6b4a 100644 --- a/conda_smithy/feedstock_io.py +++ b/conda_smithy/feedstock_io.py @@ -1,6 +1,7 @@ from contextlib import contextmanager import io import os +from pathlib import Path import shutil import stat @@ -43,14 +44,13 @@ def set_exe_file(filename, set_exe=True): mode |= IXALL else: mode -= mode & IXALL - os.chmod(filename, mode) + Path(filename).chmod(mode) @contextmanager def write_file(filename): - dirname = os.path.dirname(filename) - if dirname and not os.path.exists(dirname): - os.makedirs(dirname) + dirname = Path(filename).parent + dirname.mkdir(parents=True, exist_ok=True) with io.open(filename, "w", encoding="utf-8", newline="\n") as fh: yield fh @@ -66,7 +66,7 @@ def touch_file(filename): def remove_file_or_dir(filename): - if not os.path.isdir(filename): + if not Path(filename).is_dir(): return remove_file(filename) repo = get_repo(filename) @@ -82,11 +82,12 @@ def remove_file(filename): if repo: repo.index.remove([filename]) - os.remove(filename) + Path(filename).unlink() - dirname = os.path.dirname(filename) - if dirname and not os.listdir(dirname): - os.removedirs(dirname) + dirname = Path(filename).parent + while dirname.exists() and not any(dirname.iterdir()): + dirname.rmdir() + dirname = dirname.parent def copy_file(src, dst): diff --git a/conda_smithy/feedstock_tokens.py b/conda_smithy/feedstock_tokens.py index 66e1c0042..298f1eff4 100644 --- a/conda_smithy/feedstock_tokens.py +++ b/conda_smithy/feedstock_tokens.py @@ -25,6 +25,7 @@ import tempfile import os +from pathlib import Path import json import time import secrets @@ -59,18 +60,11 @@ def feedstock_token_local_path(user, project, provider=None): token is stored. """ if provider is None: - pth = os.path.join( - "~", - ".conda-smithy", - "%s_%s.token" % (user, project), - ) + pth = Path("~", ".conda-smithy", f"{user}_{project}.token") else: - pth = os.path.join( - "~", - ".conda-smithy", - "%s_%s_%s.token" % (user, project, provider), - ) - return os.path.expanduser(pth) + pth = Path("~", ".conda-smithy", f"{user}_{project}_{provider}.token") + + return pth.expanduser() def generate_and_write_feedstock_token(user, project, provider=None): @@ -87,7 +81,7 @@ def generate_and_write_feedstock_token(user, project, provider=None): try: token = secrets.token_hex(32) pth = feedstock_token_local_path(user, project, provider=provider) - if os.path.exists(pth): + if pth.exists(): failed = True err_msg = ( "Token for %s/%s on provider%s is already written locally!" @@ -98,8 +92,7 @@ def generate_and_write_feedstock_token(user, project, provider=None): ) ) raise FeedstockTokenError(err_msg) - - os.makedirs(os.path.dirname(pth), exist_ok=True) + pth.parent.mkdir(parents=True, exist_ok=True) with open(pth, "w") as fp: fp.write(token) @@ -140,7 +133,7 @@ def read_feedstock_token(user, project, provider=None): user, project, provider=provider ) - if not os.path.exists(user_token_pth): + if not user_token_pth.exists(): err_msg = "No token found in '%s'" % user_token_pth else: with open(user_token_pth, "r") as fp: @@ -178,13 +171,9 @@ def feedstock_token_exists(user, project, token_repo, provider=None): .replace("${GH_TOKEN}", github_token) ) git.Repo.clone_from(_token_repo, tmpdir, depth=1) - token_file = os.path.join( - tmpdir, - "tokens", - project + ".json", - ) + token_file = Path(tmpdir, "tokens", f"{project}.json") - if os.path.exists(token_file): + if token_file.exists(): with open(token_file, "r") as fp: token_data = json.load(fp) @@ -252,13 +241,9 @@ def is_valid_feedstock_token( .replace("${GH_TOKEN}", github_token) ) git.Repo.clone_from(_token_repo, tmpdir, depth=1) - token_file = os.path.join( - tmpdir, - "tokens", - project + ".json", - ) + token_file = Path(tmpdir, "tokens", f"{project}.json") - if os.path.exists(token_file): + if token_file.exists(): with open(token_file, "r") as fp: token_data = json.load(fp) @@ -343,14 +328,10 @@ def register_feedstock_token(user, project, token_repo, provider=None): .replace("${GH_TOKEN}", github_token) ) repo = git.Repo.clone_from(_token_repo, tmpdir, depth=1) - token_file = os.path.join( - tmpdir, - "tokens", - project + ".json", - ) + token_file = str(Path(tmpdir, "tokens", f"{project}.json")) # append the token if needed - if os.path.exists(token_file): + if Path(token_file).exists(): with open(token_file, "r") as fp: token_data = json.load(fp) if "tokens" not in token_data: diff --git a/conda_smithy/feedstocks.py b/conda_smithy/feedstocks.py index 45e71ff75..08257680a 100644 --- a/conda_smithy/feedstocks.py +++ b/conda_smithy/feedstocks.py @@ -1,7 +1,7 @@ import argparse -import glob import multiprocessing import os +from pathlib import Path import git from git import Repo, GitCommandError @@ -34,11 +34,11 @@ def cloned_feedstocks(feedstocks_directory): print(feedstock.directory) # The absolute path to the repo """ - pattern = os.path.abspath( - os.path.join(feedstocks_directory, "*-feedstock") - ) - for feedstock_dir in sorted(glob.glob(pattern)): - feedstock_basename = os.path.basename(feedstock_dir) + feedstocks_path = Path(feedstocks_directory).resolve() + pattern = "*-feedstock" + + for feedstock_dir in sorted(feedstocks_path.glob(pattern)): + feedstock_basename = Path(feedstock_dir).name feedstock_package = feedstock_basename.rsplit("-feedstock", 1)[0] feedstock = argparse.Namespace( name=feedstock_basename, @@ -86,8 +86,8 @@ def feedstocks_list_handle_args(args): def clone_feedstock(feedstock_gh_repo, feedstocks_dir): repo = feedstock_gh_repo - clone_directory = os.path.join(feedstocks_dir, repo.name) - if not os.path.exists(clone_directory): + clone_directory = Path(feedstocks_dir, repo.name) + if not Path(clone_directory).exists(): print("Cloning {}".format(repo.name)) clone = Repo.clone_from(repo.clone_url, clone_directory) clone.delete_remote("origin") @@ -116,7 +116,7 @@ def feedstocks_clone_all_handle_args(args): def feedstocks_list_cloned_handle_args(args): for feedstock in cloned_feedstocks(args.feedstocks_directory): - print(os.path.basename(feedstock.directory)) + print(Path(feedstock.directory).name) def feedstocks_apply_cloned_handle_args(args): @@ -255,9 +255,7 @@ def feedstocks_yaml( try: if use_local: with open( - os.path.join( - feedstock.directory, "recipe", "meta.yaml" - ), + Path(feedstock.directory, "recipe", "meta.yaml"), "r", ) as fh: content = "".join(fh.readlines()) diff --git a/conda_smithy/github.py b/conda_smithy/github.py index c8c381208..3fe249483 100644 --- a/conda_smithy/github.py +++ b/conda_smithy/github.py @@ -1,4 +1,4 @@ -import os +from pathlib import Path from random import choice from git import Repo @@ -16,7 +16,7 @@ def gh_token(): try: with open( - os.path.expanduser("~/.conda-smithy/github.token"), "r" + Path("~/.conda-smithy/github.token").expanduser(), "r" ) as fh: token = fh.read().strip() if not token: @@ -67,9 +67,9 @@ def has_in_members(team, member): def get_cached_team(org, team_name, description=""): - cached_file = os.path.expanduser( + cached_file = Path( "~/.conda-smithy/{}-{}-team".format(org.login, team_name) - ) + ).expanduser() try: with open(cached_file, "r") as fh: team_id = int(fh.read().strip()) diff --git a/conda_smithy/lint_recipe.py b/conda_smithy/lint_recipe.py index a158b610d..6b942a36f 100644 --- a/conda_smithy/lint_recipe.py +++ b/conda_smithy/lint_recipe.py @@ -15,6 +15,7 @@ import shutil import subprocess import sys +from pathlib import Path from glob import glob from inspect import cleandoc from textwrap import indent @@ -136,13 +137,9 @@ def find_local_config_file(recipe_dir, filename): # 2. staged-recipes with custom conda-forge.yaml in recipe # 3. staged-recipes found_filesname = ( - glob(os.path.join(recipe_dir, filename)) - or glob( - os.path.join(recipe_dir, "..", filename), - ) - or glob( - os.path.join(recipe_dir, "..", "..", filename), - ) + list(Path(recipe_dir).glob(filename)) + or list(Path(recipe_dir).parent.glob(filename)) + or list(Path(recipe_dir).parent.parent.glob(filename)) ) return found_filesname[0] if found_filesname else None @@ -151,13 +148,9 @@ def find_local_config_file(recipe_dir, filename): def lintify_forge_yaml(recipe_dir=None) -> (list, list): if recipe_dir: forge_yaml_filename = ( - glob(os.path.join(recipe_dir, "..", "conda-forge.yml")) - or glob( - os.path.join(recipe_dir, "conda-forge.yml"), - ) - or glob( - os.path.join(recipe_dir, "..", "..", "conda-forge.yml"), - ) + list(Path(recipe_dir).glob("conda-forge.yml")) + or list(Path(recipe_dir).parent.glob("conda-forge.yml")) + or list(Path(recipe_dir).parent.parent.glob("conda-forge.yml")) ) if forge_yaml_filename: with open(forge_yaml_filename[0], "r") as fh: @@ -180,7 +173,7 @@ def lintify_meta_yaml( # If the recipe_dir exists (no guarantee within this function) , we can # find the meta.yaml within it. - meta_fname = os.path.join(recipe_dir or "", "meta.yaml") + meta_fname = Path((recipe_dir or ""), "meta.yaml") sources_section = get_section(meta, "source", lints) build_section = get_section(meta, "build", lints) @@ -191,7 +184,7 @@ def lintify_meta_yaml( package_section = get_section(meta, "package", lints) outputs_section = get_section(meta, "outputs", lints) - recipe_dirname = os.path.basename(recipe_dir) if recipe_dir else "recipe" + recipe_dirname = Path(recipe_dir).name if recipe_dir else "recipe" is_staged_recipes = recipe_dirname != "recipe" # 0: Top level keys should be expected @@ -231,8 +224,7 @@ def lintify_meta_yaml( # 4: The recipe should have some tests. if not any(key in TEST_KEYS for key in test_section): a_test_file_exists = recipe_dir is not None and any( - os.path.exists(os.path.join(recipe_dir, test_file)) - for test_file in TEST_FILES + (Path(recipe_dir, test_file)).exists() for test_file in TEST_FILES ) if not a_test_file_exists: has_outputs_test = False @@ -261,7 +253,7 @@ def lintify_meta_yaml( lints.append("The recipe license cannot be unknown.") # 6: Selectors should be in a tidy form. - if recipe_dir is not None and os.path.exists(meta_fname): + if recipe_dir is not None and Path(meta_fname).exists(): bad_selectors, bad_lines = [], [] pyXY_selectors_lint, pyXY_lines_lint = [], [] pyXY_selectors_hint, pyXY_lines_hint = [], [] @@ -348,7 +340,7 @@ def lintify_meta_yaml( ) # 11: There should be one empty line at the end of the file. - if recipe_dir is not None and os.path.exists(meta_fname): + if recipe_dir is not None and Path(meta_fname).exists(): with io.open(meta_fname, "r") as f: lines = f.read().split("\n") # Count the number of empty lines from the end of the file @@ -463,7 +455,7 @@ def lintify_meta_yaml( forge_yaml = {} # 18: noarch doesn't work with selectors for runtime dependencies - if noarch_value is not None and os.path.exists(meta_fname): + if noarch_value is not None and Path(meta_fname).exists(): noarch_platforms = len(forge_yaml.get("noarch_platforms", [])) > 1 with io.open(meta_fname, "rt") as fh: in_runreqs = False @@ -507,7 +499,7 @@ def lintify_meta_yaml( ) # 20: Jinja2 variable definitions should be nice. - if recipe_dir is not None and os.path.exists(meta_fname): + if recipe_dir is not None and Path(meta_fname).exists(): bad_jinja = [] bad_lines = [] # Good Jinja2 variable definitions look like "{% set .+ = .+ %}" @@ -621,7 +613,7 @@ def lintify_meta_yaml( ) # 24: jinja2 variable references should be {{var}} - if recipe_dir is not None and os.path.exists(meta_fname): + if recipe_dir is not None and Path(meta_fname).exists(): bad_vars = [] bad_lines = [] with io.open(meta_fname, "rt") as fh: @@ -701,7 +693,7 @@ def check_pins_build_and_requirements(top_level): # ... so raw meta.yaml and regex it is... pure_python_wheel_re = re.compile(r".*[:-]\s+(http.*-none-any\.whl)\s+.*") wheel_re = re.compile(r".*[:-]\s+(http.*\.whl)\s+.*") - if recipe_dir is not None and os.path.exists(meta_fname): + if recipe_dir is not None and Path(meta_fname).exists(): with open(meta_fname, "rt") as f: for line in f: if match := pure_python_wheel_re.search(line): @@ -781,7 +773,7 @@ def check_pins_build_and_requirements(top_level): shellcheck_enabled = False shell_scripts = [] if recipe_dir: - shell_scripts = glob(os.path.join(recipe_dir, "*.sh")) + shell_scripts = list(Path(recipe_dir).glob("*.sh")) forge_yaml = find_local_config_file(recipe_dir, "conda-forge.yml") if shell_scripts and forge_yaml: with open(forge_yaml, "r") as fh: @@ -861,14 +853,11 @@ def check_pins_build_and_requirements(top_level): if not licenseref_regex.match(license): filtered_licenses.append(license) - with open( - os.path.join(os.path.dirname(__file__), "licenses.txt"), "r" - ) as f: + file_dir = Path(__file__).parent + with open(Path(file_dir, "licenses.txt"), "r") as f: expected_licenses = f.readlines() expected_licenses = set([l.strip() for l in expected_licenses]) - with open( - os.path.join(os.path.dirname(__file__), "license_exceptions.txt"), "r" - ) as f: + with open(Path(file_dir, "license_exceptions.txt"), "r") as f: expected_exceptions = f.readlines() expected_exceptions = set([l.strip() for l in expected_exceptions]) if set(filtered_licenses) - expected_licenses: @@ -1101,7 +1090,7 @@ def run_conda_forge_specific(meta, recipe_dir, lints, hints): # Fetch list of recipe maintainers maintainers = extra_section.get("recipe-maintainers", []) - recipe_dirname = os.path.basename(recipe_dir) if recipe_dir else "recipe" + recipe_dirname = Path(recipe_dir).name if recipe_dir else "recipe" recipe_name = package_section.get("name", "").strip() is_staged_recipes = recipe_dirname != "recipe" @@ -1182,7 +1171,7 @@ def run_conda_forge_specific(meta, recipe_dir, lints, hints): ) # 3: if the recipe dir is inside the example dir - if recipe_dir is not None and "recipes/example/" in recipe_dir: + if recipe_dir is not None and "recipes/example/" in str(recipe_dir): lints.append( "Please move the recipe out of the example dir and " "into its own dir." @@ -1190,11 +1179,11 @@ def run_conda_forge_specific(meta, recipe_dir, lints, hints): # 4: Do not delete example recipe if is_staged_recipes and recipe_dir is not None: - example_meta_fname = os.path.abspath( - os.path.join(recipe_dir, "..", "example", "meta.yaml") + example_meta_fname = ( + Path(recipe_dir).parent.joinpath("example", "meta.yaml").resolve() ) - if not os.path.exists(example_meta_fname): + if not example_meta_fname.exists(): msg = ( "Please do not delete the example recipe found in " "`recipes/example/meta.yaml`." @@ -1264,8 +1253,8 @@ def run_conda_forge_specific(meta, recipe_dir, lints, hints): # 7: Ensure that the recipe has some .ci_support files if not is_staged_recipes and recipe_dir is not None: - ci_support_files = glob( - os.path.join(recipe_dir, "..", ".ci_support", "*.yaml") + ci_support_files = list( + Path(recipe_dir).parent.joinpath(".ci_support").glob("*.yaml") ) if not ci_support_files: lints.append( @@ -1356,9 +1345,9 @@ def _format_validation_msg(error: "jsonschema.ValidationError"): def main(recipe_dir, conda_forge=False, return_hints=False): - recipe_dir = os.path.abspath(recipe_dir) - recipe_meta = os.path.join(recipe_dir, "meta.yaml") - if not os.path.exists(recipe_dir): + recipe_dir = Path(recipe_dir).resolve() + recipe_meta = recipe_dir.joinpath("meta.yaml") + if not recipe_dir.exists(): raise IOError("Feedstock has no recipe/meta.yaml.") with io.open(recipe_meta, "rt") as fh: diff --git a/conda_smithy/utils.py b/conda_smithy/utils.py index cfbd28c58..a24f41250 100644 --- a/conda_smithy/utils.py +++ b/conda_smithy/utils.py @@ -5,7 +5,7 @@ import jinja2.sandbox import datetime import time -import os + from pathlib import Path from collections import defaultdict from contextlib import contextmanager @@ -30,9 +30,10 @@ def get_feedstock_about_from_meta(meta) -> dict: # - if a subpackage has about, it's used as is # therefore we need to parse the yaml again just to get the about section... if "parent_recipe" in meta.meta["extra"]: - recipe_meta = os.path.join( + recipe_meta = Path( meta.meta["extra"]["parent_recipe"]["path"], "meta.yaml" ) + with io.open(recipe_meta, "rt") as fh: content = render_meta_yaml("".join(fh)) meta = get_yaml().load(content) @@ -123,7 +124,7 @@ def update_conda_forge_config(forge_yaml): >>> with update_conda_forge_config(somepath) as cfg: ... cfg['foo'] = 'bar' """ - if os.path.exists(forge_yaml): + if Path(forge_yaml).exists(): with open(forge_yaml, "r") as fh: code = get_yaml().load(fh) else: diff --git a/news/1937-pathlib-refactor.rst b/news/1937-pathlib-refactor.rst new file mode 100644 index 000000000..0a2ab7a5b --- /dev/null +++ b/news/1937-pathlib-refactor.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* Use pathlib library everywhere and remove usage of os.path module and glob library + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/rever.xsh b/rever.xsh index c2c3682d3..0c4562a4a 100644 --- a/rever.xsh +++ b/rever.xsh @@ -1,4 +1,4 @@ -import os +from pathlib import Path $PROJECT = $GITHUB_REPO = 'conda-smithy' $GITHUB_ORG = 'conda-forge' @@ -10,7 +10,7 @@ $CHANGELOG_FILENAME = 'CHANGELOG.rst' $CHANGELOG_TEMPLATE = 'TEMPLATE.rst' def sdist_asset(): - fname = os.path.join('dist', 'conda_smithy-' + $VERSION + '.tar.gz') + fname = str(Path('dist') / ('conda_smithy-' + $VERSION + '.tar.gz')) print('Creating sdist tarball ' + fname) ![python -m build --sdist] return fname diff --git a/tests/conftest.py b/tests/conftest.py index 9dbc620c9..38717624b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,6 @@ import collections import os +from pathlib import Path from textwrap import dedent import pytest @@ -28,19 +29,19 @@ def testing_workdir(tmpdir, request): :param request: py.test fixture-related, will be injected (see pytest docs) """ - saved_path = os.getcwd() + saved_path = Path.cwd() tmpdir.chdir() # temporary folder for profiling output, if any tmpdir.mkdir("prof") def return_to_saved_path(): - if os.path.isdir(os.path.join(saved_path, "prof")): + if saved_path.joinpath("prof").is_dir(): profdir = tmpdir.join("prof") files = profdir.listdir("*.prof") if profdir.isdir() else [] for f in files: - copy_into(str(f), os.path.join(saved_path, "prof", f.basename)) + copy_into(str(f), str(saved_path.joinpath("prof", f.basename))) os.chdir(saved_path) request.addfinalizer(return_to_saved_path) @@ -56,13 +57,13 @@ def recipe_dirname(): @pytest.fixture(scope="function") def config_yaml(testing_workdir, recipe_dirname): config = {"python": ["2.7", "3.5"], "r_base": ["3.3.2", "3.4.2"]} - os.makedirs(os.path.join(testing_workdir, recipe_dirname)) - with open(os.path.join(testing_workdir, "config.yaml"), "w") as f: + Path(testing_workdir, recipe_dirname).mkdir(parents=True) + with open(Path(testing_workdir, "config.yaml"), "w") as f: f.write("docker:\n") f.write(" fallback_image:\n") f.write(" - centos:6\n") with open( - os.path.join(testing_workdir, recipe_dirname, "default_config.yaml"), + Path(testing_workdir, recipe_dirname, "default_config.yaml"), "w", ) as f: yaml.dump(config, f, default_flow_style=False) @@ -84,23 +85,23 @@ def config_yaml(testing_workdir, recipe_dirname): ) ) # dummy file that needs to be present for circle ci. This is created by the init function - os.makedirs(os.path.join(testing_workdir, ".circleci")) + Path(testing_workdir, ".circleci").mkdir(parents=True) with open( - os.path.join(testing_workdir, ".circleci", "checkout_merge_commit.sh"), + Path(testing_workdir, ".circleci", "checkout_merge_commit.sh"), "w", ) as f: f.write("echo dummy file") with open( - os.path.join(testing_workdir, recipe_dirname, "short_config.yaml"), "w" + Path(testing_workdir, recipe_dirname, "short_config.yaml"), "w" ) as f: config = {"python": ["2.7"]} yaml.dump(config, f, default_flow_style=False) with open( - os.path.join(testing_workdir, recipe_dirname, "long_config.yaml"), "w" + Path(testing_workdir, recipe_dirname, "long_config.yaml"), "w" ) as f: config = {"python": ["2.7", "3.5", "3.6"]} yaml.dump(config, f, default_flow_style=False) - with open(os.path.join(testing_workdir, "conda-forge.yml"), "w") as f: + with open(Path(testing_workdir, "conda-forge.yml"), "w") as f: config = { "upload_on_branch": "foo-branch", "recipe_dir": recipe_dirname, @@ -111,9 +112,7 @@ def config_yaml(testing_workdir, recipe_dirname): @pytest.fixture(scope="function") def noarch_recipe(config_yaml, recipe_dirname, request): - with open( - os.path.join(config_yaml, recipe_dirname, "meta.yaml"), "w" - ) as fh: + with open(Path(config_yaml, recipe_dirname, "meta.yaml"), "w") as fh: fh.write( """ package: @@ -132,8 +131,8 @@ def noarch_recipe(config_yaml, recipe_dirname, request): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, recipe_dirname, "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, recipe_dirname, "default_config.yaml") ), ), ) @@ -141,7 +140,7 @@ def noarch_recipe(config_yaml, recipe_dirname, request): @pytest.fixture(scope="function") def r_recipe(config_yaml, request): - with open(os.path.join(config_yaml, "recipe", "meta.yaml"), "w") as fh: + with open(Path(config_yaml, "recipe", "meta.yaml"), "w") as fh: fh.write( """ package: @@ -160,8 +159,8 @@ def r_recipe(config_yaml, request): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "default_config.yaml") ), ), ) @@ -169,7 +168,7 @@ def r_recipe(config_yaml, request): @pytest.fixture(scope="function") def py_recipe(config_yaml, request): - with open(os.path.join(config_yaml, "recipe", "meta.yaml"), "w") as fh: + with open(Path(config_yaml, "recipe", "meta.yaml"), "w") as fh: fh.write( """ package: @@ -190,8 +189,8 @@ def py_recipe(config_yaml, request): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "default_config.yaml") ), ), ) @@ -199,7 +198,7 @@ def py_recipe(config_yaml, request): @pytest.fixture(scope="function") def stdlib_recipe(config_yaml, request): - with open(os.path.join(config_yaml, "recipe", "meta.yaml"), "w") as fh: + with open(Path(config_yaml, "recipe", "meta.yaml"), "w") as fh: fh.write( """ package: @@ -215,9 +214,7 @@ def stdlib_recipe(config_yaml, request): home: home """ ) - with open( - os.path.join(config_yaml, "recipe", "stdlib_config.yaml"), "w" - ) as f: + with open(Path(config_yaml, "recipe", "stdlib_config.yaml"), "w") as f: f.write( """\ c_stdlib: @@ -235,8 +232,8 @@ def stdlib_recipe(config_yaml, request): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "stdlib_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "stdlib_config.yaml") ), ), ) @@ -245,9 +242,7 @@ def stdlib_recipe(config_yaml, request): @pytest.fixture(scope="function") def stdlib_deployment_target_recipe(config_yaml, stdlib_recipe): # append to existing stdlib_config.yaml from stdlib_recipe - with open( - os.path.join(config_yaml, "recipe", "stdlib_config.yaml"), "a" - ) as f: + with open(Path(config_yaml, "recipe", "stdlib_config.yaml"), "a") as f: f.write( """\ MACOSX_DEPLOYMENT_TARGET: # [osx] @@ -262,8 +257,8 @@ def stdlib_deployment_target_recipe(config_yaml, stdlib_recipe): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "stdlib_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "stdlib_config.yaml") ), ), ) @@ -271,7 +266,7 @@ def stdlib_deployment_target_recipe(config_yaml, stdlib_recipe): @pytest.fixture(scope="function") def upload_on_branch_recipe(config_yaml, request): - with open(os.path.join(config_yaml, "recipe", "meta.yaml"), "w") as fh: + with open(Path(config_yaml, "recipe", "meta.yaml"), "w") as fh: fh.write( """ package: @@ -292,7 +287,7 @@ def upload_on_branch_recipe(config_yaml, request): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join(config_yaml, "conda-forge.yml"), + exclusive_config_file=str(Path(config_yaml, "conda-forge.yml")), ), ) @@ -300,7 +295,7 @@ def upload_on_branch_recipe(config_yaml, request): @pytest.fixture(scope="function") def recipe_migration_cfep9(config_yaml, request): # write a migrator - with open(os.path.join(config_yaml, "recipe", "meta.yaml"), "w") as fh: + with open(Path(config_yaml, "recipe", "meta.yaml"), "w") as fh: fh.write( """ package: @@ -317,11 +312,11 @@ def recipe_migration_cfep9(config_yaml, request): """ ) - os.makedirs( - os.path.join(config_yaml, ".ci_support", "migrations"), exist_ok=True + Path(config_yaml, ".ci_support", "migrations").mkdir( + parents=True, exist_ok=True ) with open( - os.path.join(config_yaml, ".ci_support", "migrations", "zlib.yaml"), + Path(config_yaml, ".ci_support", "migrations", "zlib.yaml"), "w", ) as fh: fh.write( @@ -336,8 +331,8 @@ def recipe_migration_cfep9(config_yaml, request): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "default_config.yaml") ), ), ) @@ -347,13 +342,11 @@ def recipe_migration_cfep9(config_yaml, request): def recipe_migration_cfep9_downgrade(config_yaml, recipe_migration_cfep9): # write a downgrade migrator that lives next to the current migrator. # Only this, more recent migrator should apply. - os.makedirs( - os.path.join(config_yaml, ".ci_support", "migrations"), exist_ok=True + Path(config_yaml, ".ci_support", "migrations").mkdir( + parents=True, exist_ok=True ) with open( - os.path.join( - config_yaml, ".ci_support", "migrations", "zlib-downgrade.yaml" - ), + Path(config_yaml, ".ci_support", "migrations", "zlib-downgrade.yaml"), "w", ) as fh: fh.write( @@ -368,8 +361,8 @@ def recipe_migration_cfep9_downgrade(config_yaml, recipe_migration_cfep9): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "default_config.yaml") ), ), ) @@ -377,13 +370,11 @@ def recipe_migration_cfep9_downgrade(config_yaml, recipe_migration_cfep9): @pytest.fixture(scope="function") def recipe_migration_win_compiled(config_yaml, py_recipe): - os.makedirs( - os.path.join(config_yaml, ".ci_support", "migrations"), exist_ok=True + Path(config_yaml, ".ci_support", "migrations").mkdir( + parents=True, exist_ok=True ) with open( - os.path.join( - config_yaml, ".ci_support", "migrations", "vc-migrate.yaml" - ), + Path(config_yaml, ".ci_support", "migrations", "vc-migrate.yaml"), "w", ) as fh: fh.write( @@ -411,8 +402,8 @@ def recipe_migration_win_compiled(config_yaml, py_recipe): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "default_config.yaml") ), ), ) @@ -420,7 +411,7 @@ def recipe_migration_win_compiled(config_yaml, py_recipe): @pytest.fixture(scope="function") def skipped_recipe(config_yaml, request): - with open(os.path.join(config_yaml, "recipe", "meta.yaml"), "w") as fh: + with open(Path(config_yaml, "recipe", "meta.yaml"), "w") as fh: fh.write( """ package: @@ -444,8 +435,8 @@ def skipped_recipe(config_yaml, request): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "default_config.yaml") ), ), ) @@ -453,7 +444,7 @@ def skipped_recipe(config_yaml, request): @pytest.fixture(scope="function") def python_skipped_recipe(config_yaml, request): - with open(os.path.join(config_yaml, "recipe", "meta.yaml"), "w") as fh: + with open(Path(config_yaml, "recipe", "meta.yaml"), "w") as fh: fh.write( """ package: @@ -474,8 +465,8 @@ def python_skipped_recipe(config_yaml, request): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "default_config.yaml") ), ), ) @@ -483,7 +474,7 @@ def python_skipped_recipe(config_yaml, request): @pytest.fixture(scope="function") def linux_skipped_recipe(config_yaml, request): - with open(os.path.join(config_yaml, "recipe", "meta.yaml"), "w") as fh: + with open(Path(config_yaml, "recipe", "meta.yaml"), "w") as fh: fh.write( """ package: @@ -502,8 +493,8 @@ def linux_skipped_recipe(config_yaml, request): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "default_config.yaml") ), ), ) @@ -511,7 +502,7 @@ def linux_skipped_recipe(config_yaml, request): @pytest.fixture(scope="function") def render_skipped_recipe(config_yaml, request): - with open(os.path.join(config_yaml, "recipe", "meta.yaml"), "w") as fh: + with open(Path(config_yaml, "recipe", "meta.yaml"), "w") as fh: fh.write( """ package: @@ -526,7 +517,7 @@ def render_skipped_recipe(config_yaml, request): - python """ ) - with open(os.path.join(config_yaml, "conda-forge.yml"), "a+") as fh: + with open(Path(config_yaml, "conda-forge.yml"), "a+") as fh: fh.write( """ skip_render: @@ -541,8 +532,8 @@ def render_skipped_recipe(config_yaml, request): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "default_config.yaml") ), ), ) @@ -550,7 +541,7 @@ def render_skipped_recipe(config_yaml, request): @pytest.fixture(scope="function") def choco_recipe(config_yaml, request): - with open(os.path.join(config_yaml, "recipe", "meta.yaml"), "w") as fh: + with open(Path(config_yaml, "recipe", "meta.yaml"), "w") as fh: fh.write( """ package: @@ -569,7 +560,7 @@ def choco_recipe(config_yaml, request): home: home """ ) - with open(os.path.join(config_yaml, "conda-forge.yml"), "a+") as fh: + with open(Path(config_yaml, "conda-forge.yml"), "a+") as fh: fh.write( """ choco: @@ -581,8 +572,8 @@ def choco_recipe(config_yaml, request): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "default_config.yaml") ), ), ) @@ -590,7 +581,7 @@ def choco_recipe(config_yaml, request): @pytest.fixture(scope="function") def cuda_enabled_recipe(config_yaml, request): - with open(os.path.join(config_yaml, "recipe", "meta.yaml"), "w") as fh: + with open(Path(config_yaml, "recipe", "meta.yaml"), "w") as fh: fh.write( """ package: @@ -614,8 +605,8 @@ def cuda_enabled_recipe(config_yaml, request): str(config_yaml), _load_forge_config( config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "default_config.yaml") ), ), ) @@ -623,7 +614,7 @@ def cuda_enabled_recipe(config_yaml, request): @pytest.fixture(scope="function") def jinja_env(request): - tmplt_dir = os.path.join(conda_forge_content, "templates") + tmplt_dir = str(Path(conda_forge_content, "templates")) # Load templates from the feedstock in preference to the smithy's templates. return SandboxedEnvironment( extensions=["jinja2.ext.do"], loader=FileSystemLoader([tmplt_dir]) diff --git a/tests/test_cli.py b/tests/test_cli.py index a4d09d3e0..334dfbcea 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,6 +1,6 @@ import argparse import collections -import os +from pathlib import Path import subprocess from textwrap import dedent @@ -10,7 +10,7 @@ from conda_smithy import cli -_thisdir = os.path.abspath(os.path.dirname(__file__)) +_thisdir = Path(__file__).resolve().parent InitArgs = collections.namedtuple( "ArgsObject", @@ -41,13 +41,13 @@ def test_init(py_recipe): recipe = py_recipe.recipe # expected args object has args = InitArgs( - recipe_directory=os.path.join(recipe, "recipe"), - feedstock_directory=os.path.join(recipe, "{package.name}-feedstock"), - temporary_directory=os.path.join(recipe, "temp"), + recipe_directory=str(Path(recipe, "recipe")), + feedstock_directory=str(Path(recipe, "{package.name}-feedstock")), + temporary_directory=str(Path(recipe, "temp")), ) init_obj(args) - destination = os.path.join(recipe, "py-test-feedstock") - assert os.path.isdir(destination) + destination = Path(recipe, "py-test-feedstock") + assert destination.is_dir() def test_init_with_custom_config(py_recipe): @@ -60,7 +60,7 @@ def test_init_with_custom_config(py_recipe): recipe = py_recipe.recipe # expected args object has - with open(os.path.join(recipe, "recipe", "conda-forge.yml"), "w") as fp: + with open(Path(recipe, "recipe", "conda-forge.yml"), "w") as fp: fp.write( dedent( """\ @@ -72,15 +72,15 @@ def test_init_with_custom_config(py_recipe): ) args = InitArgs( - recipe_directory=os.path.join(recipe, "recipe"), - feedstock_directory=os.path.join(recipe, "{package.name}-feedstock"), - temporary_directory=os.path.join(recipe, "temp"), + recipe_directory=str(Path(recipe, "recipe")), + feedstock_directory=str(Path(recipe, "{package.name}-feedstock")), + temporary_directory=str(Path(recipe, "temp")), ) init_obj(args) - destination = os.path.join(recipe, "py-test-feedstock") - assert os.path.isdir(destination) + destination = Path(recipe, "py-test-feedstock") + assert destination.is_dir() data = yaml.safe_load( - open(os.path.join(destination, "conda-forge.yml"), "r").read() + open(destination.joinpath("conda-forge.yml"), "r").read() ) assert data.get("bot") != None assert data["bot"]["automerge"] == True @@ -92,38 +92,34 @@ def test_init_multiple_output_matrix(testing_workdir): subparser = parser.add_subparsers() init_obj = cli.Init(subparser) regen_obj = cli.Regenerate(subparser) - recipe = os.path.join(_thisdir, "recipes", "multiple_outputs") - feedstock_dir = os.path.join( - testing_workdir, "multiple-outputs-test-feedstock" - ) + recipe = Path(_thisdir, "recipes", "multiple_outputs") + feedstock_dir = Path(testing_workdir, "multiple-outputs-test-feedstock") args = InitArgs( - recipe_directory=recipe, - feedstock_directory=feedstock_dir, - temporary_directory=os.path.join(recipe, "temp"), + recipe_directory=str(recipe), + feedstock_directory=str(feedstock_dir), + temporary_directory=str(recipe.joinpath("temp")), ) init_obj(args) # Ignore conda-forge-pinning for this test, as the test relies on conda-forge-pinning # not being present args = RegenerateArgs( - feedstock_directory=feedstock_dir, + feedstock_directory=str(feedstock_dir), feedstock_config=None, commit=False, no_check_uptodate=True, exclusive_config_file="recipe/conda_build_config.yaml", check=False, - temporary_directory=os.path.join(recipe, "temp"), + temporary_directory=str(recipe.joinpath("temp")), ) regen_obj(args) - matrix_dir = os.path.join(feedstock_dir, ".ci_support") + matrix_dir = feedstock_dir.joinpath(".ci_support") # the matrix should be consolidated among all outputs, as well as the top-level # reqs. Only the top-level reqs should have indedependent config files, # though - loops within outputs are contained in those top-level configs. - matrix_dir_len = len(os.listdir(matrix_dir)) + matrix_dir_len = len(list(matrix_dir.iterdir())) assert matrix_dir_len == 13 - linux_libpng16 = os.path.join( - matrix_dir, "linux_64_libpng1.6libpq9.5.yaml" - ) - assert os.path.isfile(linux_libpng16) + linux_libpng16 = Path(matrix_dir, "linux_64_libpng1.6libpq9.5.yaml") + assert linux_libpng16.is_file() with open(linux_libpng16) as f: config = yaml.safe_load(f) assert config["libpng"] == ["1.6"] @@ -148,31 +144,29 @@ def test_render_readme_with_multiple_outputs(testing_workdir, dirname): subparser = parser.add_subparsers() init_obj = cli.Init(subparser) regen_obj = cli.Regenerate(subparser) - _thisdir = os.path.abspath(os.path.dirname(__file__)) - recipe = os.path.join(_thisdir, "recipes", dirname) - feedstock_dir = os.path.join( - testing_workdir, "multiple-outputs-test-feedstock" - ) + _thisdir = Path(__file__).resolve().parent + recipe = Path(_thisdir).joinpath("recipes", dirname) + feedstock_dir = Path(testing_workdir, "multiple-outputs-test-feedstock") args = InitArgs( - recipe_directory=recipe, - feedstock_directory=feedstock_dir, - temporary_directory=os.path.join(recipe, "temp"), + recipe_directory=str(recipe), + feedstock_directory=str(feedstock_dir), + temporary_directory=str(recipe.joinpath("temp")), ) init_obj(args) # Ignore conda-forge-pinning for this test, as the test relies on conda-forge-pinning # not being present args = RegenerateArgs( - feedstock_directory=feedstock_dir, + feedstock_directory=str(feedstock_dir), feedstock_config=None, commit=False, no_check_uptodate=True, exclusive_config_file="recipe/conda_build_config.yaml", check=False, - temporary_directory=os.path.join(recipe, "temp"), + temporary_directory=str(recipe.joinpath("temp")), ) regen_obj(args) - readme_path = os.path.join(feedstock_dir, "README.md") - assert os.path.exists(readme_path) + readme_path = feedstock_dir.joinpath("README.md") + assert readme_path.exists() with open(readme_path, "r") as readme_file: readme = readme_file.read() if dirname == "multiple_outputs": @@ -205,40 +199,36 @@ def test_init_cuda_docker_images(testing_workdir): subparser = parser.add_subparsers() init_obj = cli.Init(subparser) regen_obj = cli.Regenerate(subparser) - recipe = os.path.join(_thisdir, "recipes", "cuda_docker_images") - feedstock_dir = os.path.join( - testing_workdir, "cuda_docker_images-feedstock" - ) + recipe = Path(_thisdir, "recipes", "cuda_docker_images") + feedstock_dir = Path(testing_workdir, "cuda_docker_images-feedstock") args = InitArgs( - recipe_directory=recipe, - feedstock_directory=feedstock_dir, - temporary_directory=os.path.join(recipe, "temp"), + recipe_directory=str(recipe), + feedstock_directory=str(feedstock_dir), + temporary_directory=str(recipe.joinpath("temp")), ) init_obj(args) # Ignore conda-forge-pinning for this test, as the test relies on # conda-forge-pinning not being present args = RegenerateArgs( - feedstock_directory=feedstock_dir, + feedstock_directory=str(feedstock_dir), feedstock_config=None, commit=False, no_check_uptodate=True, exclusive_config_file="recipe/conda_build_config.yaml", check=False, - temporary_directory=os.path.join(recipe, "temp"), + temporary_directory=str(recipe.joinpath("temp")), ) regen_obj(args) - matrix_dir = os.path.join(feedstock_dir, ".ci_support") + matrix_dir = feedstock_dir.joinpath(".ci_support") # the matrix should be consolidated among all outputs, as well as the # top-level reqs. Only the top-level reqs should have indedependent config # files, though - loops within outputs are contained in those top-level # configs. - matrix_dir_len = len(os.listdir(matrix_dir)) + matrix_dir_len = len(list(matrix_dir.iterdir())) assert matrix_dir_len == 7 # 6 docker images plus the README for v in [None, "9.2", "10.0", "10.1", "10.2", "11.0"]: - fn = os.path.join( - matrix_dir, f"linux_64_cuda_compiler_version{v}.yaml" - ) - assert os.path.isfile(fn) + fn = Path(matrix_dir, f"linux_64_cuda_compiler_version{v}.yaml") + assert fn.is_file() with open(fn) as fh: config = yaml.safe_load(fh) assert config["cuda_compiler"] == ["nvcc"] @@ -259,37 +249,35 @@ def test_init_multiple_docker_images(testing_workdir): subparser = parser.add_subparsers() init_obj = cli.Init(subparser) regen_obj = cli.Regenerate(subparser) - recipe = os.path.join(_thisdir, "recipes", "multiple_docker_images") - feedstock_dir = os.path.join( - testing_workdir, "multiple_docker_images-feedstock" - ) + recipe = Path(_thisdir, "recipes", "multiple_docker_images") + feedstock_dir = Path(testing_workdir, "multiple_docker_images-feedstock") args = InitArgs( - recipe_directory=recipe, - feedstock_directory=feedstock_dir, - temporary_directory=os.path.join(recipe, "temp"), + recipe_directory=str(recipe), + feedstock_directory=str(feedstock_dir), + temporary_directory=str(recipe.joinpath("temp")), ) init_obj(args) # Ignore conda-forge-pinning for this test, as the test relies on # conda-forge-pinning not being present args = RegenerateArgs( - feedstock_directory=feedstock_dir, + feedstock_directory=str(feedstock_dir), feedstock_config=None, commit=False, no_check_uptodate=True, exclusive_config_file="recipe/conda_build_config.yaml", check=False, - temporary_directory=os.path.join(recipe, "temp"), + temporary_directory=str(recipe.joinpath("temp")), ) regen_obj(args) - matrix_dir = os.path.join(feedstock_dir, ".ci_support") + matrix_dir = feedstock_dir.joinpath(".ci_support") # the matrix should be consolidated among all outputs, as well as the # top-level reqs. Only the top-level reqs should have indedependent config # files, though - loops within outputs are contained in those top-level # configs. - matrix_dir_len = len(os.listdir(matrix_dir)) + matrix_dir_len = len(list(matrix_dir.iterdir())) assert matrix_dir_len == 2 - fn = os.path.join(matrix_dir, "linux_64_.yaml") - assert os.path.isfile(fn) + fn = matrix_dir.joinpath("linux_64_.yaml") + assert fn.is_file() with open(fn) as fh: config = yaml.safe_load(fh) assert config["docker_image"] == ["pickme_a"] @@ -301,33 +289,31 @@ def test_regenerate(py_recipe, testing_workdir): subparser = parser.add_subparsers() regen_obj = cli.Regenerate(subparser) recipe = py_recipe.recipe - feedstock_dir = os.path.join(_thisdir, "recipes", "click-test-feedstock") - dest_dir = os.path.join(testing_workdir, "click-test-feedstock") + feedstock_dir = Path(_thisdir, "recipes", "click-test-feedstock") + dest_dir = Path(testing_workdir, "click-test-feedstock") shutil.copytree(feedstock_dir, dest_dir) subprocess.call("git init".split(), cwd=dest_dir) subprocess.call("git add *".split(), cwd=dest_dir) subprocess.call('git commit -m "init"'.split(), cwd=dest_dir) - matrix_folder = os.path.join(dest_dir, ".ci_support") + matrix_folder = dest_dir.joinpath(".ci_support") # original rendering was with py27, 36, no target_platform - assert len(os.listdir(matrix_folder)) == 7 + assert len(list(Path(matrix_folder).iterdir())) == 7 # reduce the python matrix and make sure the matrix files reflect the change args = RegenerateArgs( - feedstock_directory=dest_dir, + feedstock_directory=str(dest_dir), feedstock_config=None, commit=False, no_check_uptodate=True, - exclusive_config_file=os.path.join( - recipe, "recipe", "short_config.yaml" - ), + exclusive_config_file=str(Path(recipe, "recipe", "short_config.yaml")), check=False, - temporary_directory=os.path.join(dest_dir, "temp"), + temporary_directory=str(dest_dir.joinpath("temp")), ) regen_obj(args) # one py ver, no target_platform (tests that older configs don't stick around) - assert len(os.listdir(matrix_folder)) == 4 + assert len(list(Path(matrix_folder).iterdir())) == 4 def test_render_variant_mismatches(testing_workdir): @@ -335,38 +321,35 @@ def test_render_variant_mismatches(testing_workdir): subparser = parser.add_subparsers() init_obj = cli.Init(subparser) regen_obj = cli.Regenerate(subparser) - _thisdir = os.path.abspath(os.path.dirname(__file__)) - recipe = os.path.join(_thisdir, "recipes", "variant_mismatches") - feedstock_dir = os.path.join( - testing_workdir, "test-variant-mismatches-feedstock" - ) + _thisdir = Path(__file__).resolve().parent + recipe = Path(_thisdir, "recipes", "variant_mismatches") + feedstock_dir = Path(testing_workdir, "test-variant-mismatches-feedstock") args = InitArgs( - recipe_directory=recipe, - feedstock_directory=feedstock_dir, - temporary_directory=os.path.join(recipe, "temp"), + recipe_directory=str(recipe), + feedstock_directory=str(feedstock_dir), + temporary_directory=str(recipe.joinpath("temp")), ) init_obj(args) # Ignore conda-forge-pinning for this test, as the test relies on conda-forge-pinning # not being present args = RegenerateArgs( - feedstock_directory=feedstock_dir, + feedstock_directory=str(feedstock_dir), feedstock_config=None, commit=False, no_check_uptodate=True, exclusive_config_file="recipe/conda_build_config.yaml", check=False, - temporary_directory=os.path.join(recipe, "temp"), + temporary_directory=str(recipe.joinpath("temp")), ) regen_obj(args) - matrix_dir = os.path.join(feedstock_dir, ".ci_support") - cfgs = os.listdir(matrix_dir) - assert len(cfgs) == 3 # readme + 2 configs + matrix_dir = feedstock_dir.joinpath(".ci_support") + assert len(list(matrix_dir.iterdir())) == 3 # readme + 2 configs - for _cfg in cfgs: - if _cfg == "README": + for _cfg in matrix_dir.iterdir(): + if _cfg.name == "README": continue - cfg = os.path.join(matrix_dir, _cfg) + cfg = matrix_dir.joinpath(_cfg.name) with open(cfg, "r") as f: data = yaml.safe_load(f) assert data["a"] == data["b"] diff --git a/tests/test_configure_feedstock.py b/tests/test_configure_feedstock.py index 4d81864b1..b18fb41e2 100644 --- a/tests/test_configure_feedstock.py +++ b/tests/test_configure_feedstock.py @@ -1,6 +1,7 @@ import copy import logging import os +from pathlib import Path import re import shutil import textwrap @@ -21,7 +22,7 @@ def test_noarch_skips_appveyor(noarch_recipe, jinja_env): ) # this configuration should be skipped assert not noarch_recipe.config["appveyor"]["enabled"] - assert not os.path.isdir(os.path.join(noarch_recipe.recipe, ".ci_support")) + assert not Path(noarch_recipe.recipe, ".ci_support").is_dir() def test_noarch_skips_travis(noarch_recipe, jinja_env): @@ -32,7 +33,7 @@ def test_noarch_skips_travis(noarch_recipe, jinja_env): ) # this configuration should be skipped assert not noarch_recipe.config["travis"]["enabled"] - assert not os.path.isdir(os.path.join(noarch_recipe.recipe, ".ci_support")) + assert not Path(noarch_recipe.recipe, ".ci_support").is_dir() @pytest.mark.legacy_circle @@ -47,10 +48,10 @@ def test_noarch_runs_on_circle(noarch_recipe, jinja_env): # this configuration should be run assert noarch_recipe.config["circle"]["enabled"] - matrix_dir = os.path.join(noarch_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(noarch_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() # single matrix entry - readme is generated later in main function - assert len(os.listdir(matrix_dir)) == 1 + assert len(list(matrix_dir.iterdir())) == 1 @pytest.mark.parametrize("recipe_dirname", ["recipe", "custom_recipe_dir"]) @@ -62,10 +63,10 @@ def test_noarch_runs_on_azure(noarch_recipe, jinja_env): ) # this configuration should be run assert noarch_recipe.config["azure"]["enabled"] - matrix_dir = os.path.join(noarch_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(noarch_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() # single matrix entry - readme is generated later in main function - assert len(os.listdir(matrix_dir)) == 1 + assert len(list(matrix_dir.iterdir())) == 1 def test_r_skips_appveyor(r_recipe, jinja_env): @@ -77,7 +78,7 @@ def test_r_skips_appveyor(r_recipe, jinja_env): ) # this configuration should be skipped assert not r_recipe.config["appveyor"]["enabled"] - assert not os.path.isdir(os.path.join(r_recipe.recipe, ".ci_support")) + assert not Path(r_recipe.recipe, ".ci_support").is_dir() @pytest.mark.legacy_travis @@ -91,10 +92,10 @@ def test_r_matrix_travis(r_recipe, jinja_env): ) # this configuration should be run assert r_recipe.config["travis"]["enabled"] - matrix_dir = os.path.join(r_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(r_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() # single matrix entry - readme is generated later in main function - assert len(os.listdir(matrix_dir)) == 2 + assert len(list(matrix_dir.iterdir())) == 2 @pytest.mark.legacy_circle @@ -108,10 +109,10 @@ def test_r_matrix_on_circle(r_recipe, jinja_env): ) # this configuration should be run assert r_recipe.config["circle"]["enabled"] - matrix_dir = os.path.join(r_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(r_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() # single matrix entry - readme is generated later in main function - assert len(os.listdir(matrix_dir)) == 2 + assert len(list(matrix_dir.iterdir())) == 2 def test_r_matrix_azure(r_recipe, jinja_env): @@ -122,10 +123,10 @@ def test_r_matrix_azure(r_recipe, jinja_env): ) # this configuration should be run assert r_recipe.config["azure"]["enabled"] - matrix_dir = os.path.join(r_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(r_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() # single matrix entry - readme is generated later in main function - assert len(os.listdir(matrix_dir)) == 4 + assert len(list(matrix_dir.iterdir())) == 4 def test_py_matrix_appveyor(py_recipe, jinja_env): @@ -137,11 +138,11 @@ def test_py_matrix_appveyor(py_recipe, jinja_env): ) # this configuration should be skipped assert py_recipe.config["appveyor"]["enabled"] - matrix_dir = os.path.join(py_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(py_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() # 2 python versions. Recipe uses c_compiler, but this is a zipped key # and shouldn't add extra configurations - assert len(os.listdir(matrix_dir)) == 2 + assert len(list(matrix_dir.iterdir())) == 2 @pytest.mark.legacy_travis @@ -155,10 +156,10 @@ def test_py_matrix_travis(py_recipe, jinja_env): ) # this configuration should be run assert py_recipe.config["travis"]["enabled"] - matrix_dir = os.path.join(py_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(py_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() # two matrix enties - one per py ver - assert len(os.listdir(matrix_dir)) == 2 + assert len(list(matrix_dir.iterdir())) == 2 @pytest.mark.legacy_circle @@ -172,10 +173,10 @@ def test_py_matrix_on_circle(py_recipe, jinja_env): ) # this configuration should be run assert py_recipe.config["circle"]["enabled"] - matrix_dir = os.path.join(py_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(py_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() # single matrix entry - readme is generated later in main function - assert len(os.listdir(matrix_dir)) == 2 + assert len(list(matrix_dir.iterdir())) == 2 def test_py_matrix_on_github(py_recipe, jinja_env): @@ -188,15 +189,13 @@ def test_py_matrix_on_github(py_recipe, jinja_env): ) # this configuration should be run assert py_recipe.config["github_actions"]["enabled"] - matrix_dir = os.path.join(py_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(py_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() # single matrix entry - readme is generated later in main function - assert len(os.listdir(matrix_dir)) == 2 - assert os.path.exists( - os.path.join( - py_recipe.recipe, ".github", "workflows", "conda-build.yml" - ) - ) + assert len(list(matrix_dir.iterdir())) == 2 + assert Path( + py_recipe.recipe, ".github", "workflows", "conda-build.yml" + ).exists() def test_py_matrix_on_azure(py_recipe, jinja_env): @@ -207,10 +206,10 @@ def test_py_matrix_on_azure(py_recipe, jinja_env): ) # this configuration should be run assert py_recipe.config["azure"]["enabled"] - matrix_dir = os.path.join(py_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(py_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() # single matrix entry - readme is generated later in main function - assert len(os.listdir(matrix_dir)) == 6 + assert len(list(matrix_dir.iterdir())) == 6 def test_stdlib_on_azure(stdlib_recipe, jinja_env): @@ -221,16 +220,16 @@ def test_stdlib_on_azure(stdlib_recipe, jinja_env): ) # this configuration should be run assert stdlib_recipe.config["azure"]["enabled"] - matrix_dir = os.path.join(stdlib_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(stdlib_recipe.recipe, ".ci_support") + assert Path(matrix_dir).is_dir() # find stdlib-config in generated yaml files (plus version, on unix) - with open(os.path.join(matrix_dir, "linux_64_.yaml")) as f: + with open(matrix_dir.joinpath("linux_64_.yaml")) as f: linux_lines = f.readlines() linux_content = "".join(linux_lines) # multiline pattern to ensure we don't match other stuff accidentally assert re.match(r"(?s).*c_stdlib:\s*- sysroot", linux_content) assert re.match(r"(?s).*c_stdlib_version:\s*- ['\"]?2\.\d+", linux_content) - with open(os.path.join(matrix_dir, "osx_64_.yaml")) as f: + with open(matrix_dir.joinpath("osx_64_.yaml")) as f: osx_lines = f.readlines() osx_content = "".join(osx_lines) assert re.match( @@ -241,7 +240,7 @@ def test_stdlib_on_azure(stdlib_recipe, jinja_env): assert re.match( r"(?s).*MACOSX_DEPLOYMENT_TARGET:\s*- ['\"]?10\.9", osx_content ) - with open(os.path.join(matrix_dir, "win_64_.yaml")) as f: + with open(matrix_dir.joinpath("win_64_.yaml")) as f: win_lines = f.readlines() win_content = "".join(win_lines) assert re.match(r"(?s).*c_stdlib:\s*- vs", win_content) @@ -259,11 +258,9 @@ def test_stdlib_deployment_target( ) # this configuration should be run assert stdlib_deployment_target_recipe.config["azure"]["enabled"] - matrix_dir = os.path.join( - stdlib_deployment_target_recipe.recipe, ".ci_support" - ) - assert os.path.isdir(matrix_dir) - with open(os.path.join(matrix_dir, "osx_64_.yaml")) as f: + matrix_dir = Path(stdlib_deployment_target_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() + with open(matrix_dir.joinpath("osx_64_.yaml")) as f: lines = f.readlines() content = "".join(lines) # ensure both MACOSX_DEPLOYMENT_TARGET and c_stdlib_version match @@ -287,7 +284,7 @@ def test_upload_on_branch_azure(upload_on_branch_recipe, jinja_env): assert upload_on_branch_recipe.config["upload_on_branch"] == "foo-branch" # Check that the parameter is in the generated file. with open( - os.path.join( + Path( upload_on_branch_recipe.recipe, ".azure-pipelines", "azure-pipelines-osx.yml", @@ -304,7 +301,7 @@ def test_upload_on_branch_azure(upload_on_branch_recipe, jinja_env): ) with open( - os.path.join( + Path( upload_on_branch_recipe.recipe, ".azure-pipelines", "azure-pipelines-win.yml", @@ -318,7 +315,7 @@ def test_upload_on_branch_azure(upload_on_branch_recipe, jinja_env): ) assert win_build_step["env"]["UPLOAD_ON_BRANCH"] == "foo-branch" with open( - os.path.join( + Path( upload_on_branch_recipe.recipe, ".scripts", "run_win_build.bat", @@ -328,7 +325,7 @@ def test_upload_on_branch_azure(upload_on_branch_recipe, jinja_env): assert "BUILD_SOURCEBRANCHNAME" in build_script_win with open( - os.path.join( + Path( upload_on_branch_recipe.recipe, ".azure-pipelines", "azure-pipelines-linux.yml", @@ -357,9 +354,7 @@ def test_upload_on_branch_appveyor(upload_on_branch_recipe, jinja_env): assert upload_on_branch_recipe.config["upload_on_branch"] == "foo-branch" # Check that the parameter is in the generated file. - with open( - os.path.join(upload_on_branch_recipe.recipe, ".appveyor.yml") - ) as fp: + with open(Path(upload_on_branch_recipe.recipe, ".appveyor.yml")) as fp: content = yaml.safe_load(fp) assert "%APPVEYOR_REPO_BRANCH%" in content["deploy_script"][0] assert "UPLOAD_ON_BRANCH=foo-branch" in content["deploy_script"][-2] @@ -367,7 +362,7 @@ def test_upload_on_branch_appveyor(upload_on_branch_recipe, jinja_env): def test_circle_with_yum_reqs(py_recipe, jinja_env): with open( - os.path.join(py_recipe.recipe, "recipe", "yum_requirements.txt"), "w" + Path(py_recipe.recipe, "recipe", "yum_requirements.txt"), "w" ) as f: f.write("nano\n") configure_feedstock.render_circle( @@ -382,7 +377,7 @@ def test_circle_with_empty_yum_reqs_raises(py_recipe, jinja_env): py_recipe.config["provider"]["linux"] = "circle" with open( - os.path.join(py_recipe.recipe, "recipe", "yum_requirements.txt"), "w" + Path(py_recipe.recipe, "recipe", "yum_requirements.txt"), "w" ) as f: f.write("# effectively empty") with pytest.raises(ValueError): @@ -395,7 +390,7 @@ def test_circle_with_empty_yum_reqs_raises(py_recipe, jinja_env): def test_azure_with_empty_yum_reqs_raises(py_recipe, jinja_env): with open( - os.path.join(py_recipe.recipe, "recipe", "yum_requirements.txt"), "w" + Path(py_recipe.recipe, "recipe", "yum_requirements.txt"), "w" ) as f: f.write("# effectively empty") with pytest.raises(ValueError): @@ -414,24 +409,22 @@ def test_circle_osx(py_recipe, jinja_env): py_recipe.config["provider"]["linux"] = "circle" forge_dir = py_recipe.recipe - travis_yml_file = os.path.join(forge_dir, ".travis.yml") - circle_osx_file = os.path.join(forge_dir, ".scripts", "run_osx_build.sh") - circle_linux_file = os.path.join( - forge_dir, ".scripts", "run_docker_build.sh" - ) - circle_config_file = os.path.join(forge_dir, ".circleci", "config.yml") + travis_yml_file = Path(forge_dir, ".travis.yml") + circle_osx_file = Path(forge_dir, ".scripts", "run_osx_build.sh") + circle_linux_file = Path(forge_dir, ".scripts", "run_docker_build.sh") + circle_config_file = Path(forge_dir, ".circleci", "config.yml") configure_feedstock.clear_scripts(forge_dir) configure_feedstock.render_circle( jinja_env=jinja_env, forge_config=py_recipe.config, forge_dir=forge_dir ) - assert not os.path.exists(circle_osx_file) - assert os.path.exists(circle_linux_file) - assert os.path.exists(circle_config_file) + assert not circle_osx_file.exists() + assert circle_linux_file.exists() + assert circle_config_file.exists() configure_feedstock.render_travis( jinja_env=jinja_env, forge_config=py_recipe.config, forge_dir=forge_dir ) - assert os.path.exists(travis_yml_file) + assert travis_yml_file.exists() configure_feedstock.clear_scripts(forge_dir) config = copy.deepcopy(py_recipe.config) @@ -439,13 +432,13 @@ def test_circle_osx(py_recipe, jinja_env): configure_feedstock.render_circle( jinja_env=jinja_env, forge_config=config, forge_dir=forge_dir ) - assert os.path.exists(circle_osx_file) - assert os.path.exists(circle_linux_file) - assert os.path.exists(circle_config_file) + assert circle_osx_file.exists() + assert circle_linux_file.exists() + assert circle_config_file.exists() configure_feedstock.render_travis( jinja_env=jinja_env, forge_config=config, forge_dir=forge_dir ) - assert not os.path.exists(travis_yml_file) + assert not travis_yml_file.exists() configure_feedstock.clear_scripts(forge_dir) config = copy.deepcopy(py_recipe.config) @@ -454,18 +447,16 @@ def test_circle_osx(py_recipe, jinja_env): configure_feedstock.render_circle( jinja_env=jinja_env, forge_config=config, forge_dir=forge_dir ) - assert os.path.exists(circle_osx_file) - assert not os.path.exists(circle_linux_file) - assert os.path.exists(circle_config_file) + assert circle_osx_file.exists() + assert not circle_linux_file.exists() + assert circle_config_file.exists() def test_circle_skipped(linux_skipped_recipe, jinja_env): forge_dir = linux_skipped_recipe.recipe - circle_osx_file = os.path.join(forge_dir, ".scripts", "run_osx_build.sh") - circle_linux_file = os.path.join( - forge_dir, ".scripts", "run_docker_build.sh" - ) - circle_config_file = os.path.join(forge_dir, ".circleci", "config.yml") + circle_osx_file = Path(forge_dir, ".scripts", "run_osx_build.sh") + circle_linux_file = Path(forge_dir, ".scripts", "run_docker_build.sh") + circle_config_file = Path(forge_dir, ".circleci", "config.yml") config = copy.deepcopy(linux_skipped_recipe.config) configure_feedstock.copy_feedstock_content(config, forge_dir) @@ -474,9 +465,9 @@ def test_circle_skipped(linux_skipped_recipe, jinja_env): forge_config=linux_skipped_recipe.config, forge_dir=forge_dir, ) - assert not os.path.exists(circle_osx_file) - assert not os.path.exists(circle_linux_file) - assert os.path.exists(circle_config_file) + assert not circle_osx_file.exists() + assert not circle_linux_file.exists() + assert circle_config_file.exists() config["provider"]["osx"] = "circle" @@ -484,9 +475,9 @@ def test_circle_skipped(linux_skipped_recipe, jinja_env): configure_feedstock.render_circle( jinja_env=jinja_env, forge_config=config, forge_dir=forge_dir ) - assert os.path.exists(circle_osx_file) - assert not os.path.exists(circle_linux_file) - assert os.path.exists(circle_config_file) + assert circle_osx_file.exists() + assert not circle_linux_file.exists() + assert circle_config_file.exists() def test_render_with_all_skipped_generates_readme(skipped_recipe, jinja_env): @@ -495,8 +486,8 @@ def test_render_with_all_skipped_generates_readme(skipped_recipe, jinja_env): forge_config=skipped_recipe.config, forge_dir=skipped_recipe.recipe, ) - readme_path = os.path.join(skipped_recipe.recipe, "README.md") - assert os.path.exists(readme_path) + readme_path = Path(skipped_recipe.recipe, "README.md") + assert readme_path.exists() with open(readme_path, "rb") as readme_file: content = readme_file.read() assert b"skip-test-meta" in content @@ -505,8 +496,8 @@ def test_render_with_all_skipped_generates_readme(skipped_recipe, jinja_env): def test_render_windows_with_skipped_python(python_skipped_recipe, jinja_env): config = python_skipped_recipe.config config["provider"]["win"] = "appveyor" - config["exclusive_config_file"] = os.path.join( - python_skipped_recipe.recipe, "recipe", "long_config.yaml" + config["exclusive_config_file"] = str( + Path(python_skipped_recipe.recipe, "recipe", "long_config.yaml") ) configure_feedstock.render_appveyor( jinja_env=jinja_env, @@ -516,9 +507,9 @@ def test_render_windows_with_skipped_python(python_skipped_recipe, jinja_env): # this configuration should be skipped assert python_skipped_recipe.config["appveyor"]["enabled"] - matrix_dir = os.path.join(python_skipped_recipe.recipe, ".ci_support") + matrix_dir = Path(python_skipped_recipe.recipe, ".ci_support") # matrix has 2.7, 3.5, 3.6, but 3.6 is skipped. Should be 2 entries. - assert len(os.listdir(matrix_dir)) == 2 + assert len(list(matrix_dir.iterdir())) == 2 def test_readme_has_terminating_newline(noarch_recipe, jinja_env): @@ -527,8 +518,8 @@ def test_readme_has_terminating_newline(noarch_recipe, jinja_env): forge_config=noarch_recipe.config, forge_dir=noarch_recipe.recipe, ) - readme_path = os.path.join(noarch_recipe.recipe, "README.md") - assert os.path.exists(readme_path) + readme_path = Path(noarch_recipe.recipe, "README.md") + assert readme_path.exists() with open(readme_path, "rb") as readme_file: readme_file.seek(-1, os.SEEK_END) assert readme_file.read() == b"\n" @@ -541,17 +532,14 @@ def test_secrets(py_recipe, jinja_env): forge_dir=py_recipe.recipe, ) - run_docker_build = os.path.join( - py_recipe.recipe, ".scripts", "run_docker_build.sh" - ) + recipe_path = Path(py_recipe.recipe) + run_docker_build = recipe_path.joinpath(".scripts", "run_docker_build.sh") with open(run_docker_build, "rb") as run_docker_build_file: content = run_docker_build_file.read() assert b"-e BINSTAR_TOKEN" in content - for config_yaml in os.listdir( - os.path.join(py_recipe.recipe, ".azure-pipelines") - ): - if config_yaml.endswith(".yaml"): + for config_yaml in recipe_path.joinpath(".azure-pipelines").iterdir(): + if config_yaml.suffix == ".yaml": with open(config_yaml) as fo: config = yaml.safe_load(fo) if "jobs" in config: @@ -571,7 +559,7 @@ def test_secrets(py_recipe, jinja_env): forge_dir=py_recipe.recipe, ) - with open(os.path.join(py_recipe.recipe, ".drone.yml")) as fo: + with open(recipe_path.joinpath(".drone.yml")) as fo: config = list(yaml.safe_load_all(fo))[-1] assert any( step.get("environment", {}) @@ -590,7 +578,7 @@ def test_migrator_recipe(recipe_migration_cfep9, jinja_env): ) with open( - os.path.join( + Path( recipe_migration_cfep9.recipe, ".ci_support", "linux_64_python2.7.yaml", @@ -602,11 +590,11 @@ def test_migrator_recipe(recipe_migration_cfep9, jinja_env): def test_migrator_cfp_override(recipe_migration_cfep9, jinja_env): cfp_file = recipe_migration_cfep9.config["exclusive_config_file"] - cfp_migration_dir = os.path.join( - os.path.dirname(cfp_file), "share", "conda-forge", "migrations" + cfp_migration_dir = Path(cfp_file).parent.joinpath( + "share", "conda-forge", "migrations" ) - os.makedirs(cfp_migration_dir, exist_ok=True) - with open(os.path.join(cfp_migration_dir, "zlib2.yaml"), "w") as f: + cfp_migration_dir.mkdir(parents=True, exist_ok=True) + with open(cfp_migration_dir.joinpath("zlib2.yaml"), "w") as f: f.write( textwrap.dedent( """ @@ -623,7 +611,7 @@ def test_migrator_cfp_override(recipe_migration_cfep9, jinja_env): ) with open( - os.path.join( + Path( recipe_migration_cfep9.recipe, ".ci_support", "linux_64_python2.7.yaml", @@ -635,31 +623,20 @@ def test_migrator_cfp_override(recipe_migration_cfep9, jinja_env): def test_migrator_delete_old(recipe_migration_cfep9, jinja_env): cfp_file = recipe_migration_cfep9.config["exclusive_config_file"] - cfp_migration_dir = os.path.join( - os.path.dirname(cfp_file), "share", "conda-forge", "migrations" + cfp_migration_dir = Path(cfp_file).parent.joinpath( + "share", "conda-forge", "migrations" ) - assert os.path.exists( - os.path.join( - recipe_migration_cfep9.recipe, - ".ci_support", - "migrations", - "zlib.yaml", - ) + recipe_path = Path( + recipe_migration_cfep9.recipe, ".ci_support", "migrations", "zlib.yaml" ) - os.makedirs(cfp_migration_dir, exist_ok=True) + assert recipe_path.exists() + cfp_migration_dir.mkdir(parents=True, exist_ok=True) configure_feedstock.render_azure( jinja_env=jinja_env, forge_config=recipe_migration_cfep9.config, forge_dir=recipe_migration_cfep9.recipe, ) - assert not os.path.exists( - os.path.join( - recipe_migration_cfep9.recipe, - ".ci_support", - "migrations", - "zlib.yaml", - ) - ) + assert not recipe_path.exists() def test_migrator_downgrade_recipe( @@ -673,21 +650,15 @@ def test_migrator_downgrade_recipe( forge_config=recipe_migration_cfep9_downgrade.config, forge_dir=recipe_migration_cfep9_downgrade.recipe, ) - assert ( - len( - os.listdir( - os.path.join( - recipe_migration_cfep9_downgrade.recipe, - ".ci_support", - "migrations", - ) - ) - ) - == 2 + migrations_dir = Path( + recipe_migration_cfep9_downgrade.recipe, + ".ci_support", + "migrations", ) + assert len(list(migrations_dir.iterdir())) == 2 with open( - os.path.join( + Path( recipe_migration_cfep9_downgrade.recipe, ".ci_support", "linux_64_python2.7.yaml", @@ -708,22 +679,14 @@ def test_migrator_compiler_version_recipe( forge_config=recipe_migration_win_compiled.config, forge_dir=recipe_migration_win_compiled.recipe, ) - assert ( - len( - os.listdir( - os.path.join( - recipe_migration_win_compiled.recipe, - ".ci_support", - "migrations", - ) - ) - ) - == 1 - ) - rendered_variants = os.listdir( - os.path.join(recipe_migration_win_compiled.recipe, ".ci_support") + migrations_dir = Path( + recipe_migration_win_compiled.recipe, ".ci_support", "migrations" ) + assert len(list(migrations_dir.iterdir())) == 1 + + dir = Path(recipe_migration_win_compiled.recipe, ".ci_support") + rendered_variants = [item.name for item in dir.iterdir()] assert "win_64_c_compilervs2008python2.7.yaml" in rendered_variants assert "win_64_c_compilervs2017python3.5.yaml" in rendered_variants @@ -746,8 +709,8 @@ def test_files_skip_render(render_skipped_recipe, jinja_env): ".github/workflows/webservices.yml", ] for f in skipped_files: - fpath = os.path.join(render_skipped_recipe.recipe, f) - assert not os.path.exists(fpath) + fpath = Path(render_skipped_recipe.recipe, f) + assert not fpath.exists() def test_choco_install(choco_recipe, jinja_env): @@ -756,12 +719,10 @@ def test_choco_install(choco_recipe, jinja_env): forge_config=choco_recipe.config, forge_dir=choco_recipe.recipe, ) - azure_file = os.path.join( - os.path.join( - choco_recipe.recipe, ".azure-pipelines", "azure-pipelines-win.yml" - ) + azure_file = Path( + choco_recipe.recipe, ".azure-pipelines", "azure-pipelines-win.yml" ) - assert os.path.isfile(azure_file) + assert azure_file.is_file() with open(azure_file) as f: contents = f.read() exp = """ @@ -782,12 +743,9 @@ def test_webservices_action_exists(py_recipe, jinja_env): forge_config=py_recipe.config, forge_dir=py_recipe.recipe, ) - assert os.path.exists( - os.path.join(py_recipe.recipe, ".github/workflows/webservices.yml") - ) - with open( - os.path.join(py_recipe.recipe, ".github/workflows/webservices.yml") - ) as f: + filepath = Path(py_recipe.recipe, ".github/workflows/webservices.yml") + assert filepath.exists() + with open(filepath) as f: action_config = yaml.safe_load(f) assert "jobs" in action_config assert "webservices" in action_config["jobs"] @@ -799,32 +757,30 @@ def test_automerge_action_exists(py_recipe, jinja_env): forge_config=py_recipe.config, forge_dir=py_recipe.recipe, ) - assert os.path.exists( - os.path.join(py_recipe.recipe, ".github/workflows/automerge.yml") - ) - with open( - os.path.join(py_recipe.recipe, ".github/workflows/automerge.yml") - ) as f: + filepath = Path(py_recipe.recipe, ".github/workflows/automerge.yml") + assert filepath.exists() + with open(filepath) as f: action_config = yaml.safe_load(f) assert "jobs" in action_config assert "automerge-action" in action_config["jobs"] def test_conda_forge_yaml_empty(config_yaml): + config_yaml_path = Path(config_yaml) load_forge_config = lambda: configure_feedstock._load_forge_config( # noqa config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + config_yaml_path.joinpath("recipe", "default_config.yaml") ), ) assert load_forge_config()["recipe_dir"] == "recipe" - os.unlink(os.path.join(config_yaml, "conda-forge.yml")) + Path.unlink(config_yaml_path.joinpath("conda-forge.yml")) with pytest.raises(RuntimeError): load_forge_config() - with open(os.path.join(config_yaml, "conda-forge.yml"), "w"): + with open(config_yaml_path.joinpath("conda-forge.yml"), "w"): pass assert load_forge_config()["recipe_dir"] == "recipe" @@ -832,12 +788,12 @@ def test_conda_forge_yaml_empty(config_yaml): def test_noarch_platforms_bad_yaml(config_yaml, caplog): load_forge_config = lambda: configure_feedstock._load_forge_config( # noqa config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + Path(config_yaml, "recipe", "default_config.yaml") ), ) - with open(os.path.join(config_yaml, "conda-forge.yml"), "a+") as fp: + with open(Path(config_yaml, "conda-forge.yml"), "a+") as fp: fp.write("noarch_platforms: [eniac, zx80]") with caplog.at_level(logging.WARNING): @@ -847,23 +803,24 @@ def test_noarch_platforms_bad_yaml(config_yaml, caplog): def test_forge_yml_alt_path(config_yaml): + config_yaml_path = Path(config_yaml) load_forge_config = ( lambda forge_yml: configure_feedstock._load_forge_config( # noqa config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + config_yaml_path.joinpath("recipe", "default_config.yaml") ), forge_yml=forge_yml, ) ) - forge_yml = os.path.join(config_yaml, "conda-forge.yml") - forge_yml_alt = os.path.join( - config_yaml, ".config", "feedstock-config.yml" + forge_yml = config_yaml_path.joinpath("conda-forge.yml") + forge_yml_alt = config_yaml_path.joinpath( + ".config", "feedstock-config.yml" ) - os.mkdir(os.path.dirname(forge_yml_alt)) - os.rename(forge_yml, forge_yml_alt) + forge_yml_alt.parent.mkdir() + forge_yml.rename(forge_yml_alt) with pytest.raises(RuntimeError): load_forge_config(None) @@ -890,10 +847,10 @@ def test_cos7_env_render(py_recipe, jinja_env): # this configuration should be run assert forge_config["azure"]["enabled"] - matrix_dir = os.path.join(py_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(py_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() # single matrix entry - readme is generated later in main function - assert len(os.listdir(matrix_dir)) == 6 + assert len(list(matrix_dir.iterdir())) == 6 finally: if has_env: @@ -921,10 +878,10 @@ def test_cuda_enabled_render(cuda_enabled_recipe, jinja_env): # this configuration should be run assert forge_config["azure"]["enabled"] - matrix_dir = os.path.join(cuda_enabled_recipe.recipe, ".ci_support") - assert os.path.isdir(matrix_dir) + matrix_dir = Path(cuda_enabled_recipe.recipe, ".ci_support") + assert matrix_dir.is_dir() # single matrix entry - readme is generated later in main function - assert len(os.listdir(matrix_dir)) == 6 + assert len(list(matrix_dir.iterdir())) == 6 finally: if has_env: @@ -935,10 +892,11 @@ def test_cuda_enabled_render(cuda_enabled_recipe, jinja_env): def test_conda_build_tools(config_yaml, caplog): + config_yaml_path = Path(config_yaml) load_forge_config = lambda: configure_feedstock._load_forge_config( # noqa config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + config_yaml_path.joinpath("recipe", "default_config.yaml") ), ) @@ -949,21 +907,21 @@ def test_conda_build_tools(config_yaml, caplog): assert cfg["conda_build_tool"] == "conda-build" # current default # legacy compatibility config - with open(os.path.join(config_yaml, "conda-forge.yml")) as fp: + with open(config_yaml_path.joinpath("conda-forge.yml")) as fp: unmodified = fp.read() - with open(os.path.join(config_yaml, "conda-forge.yml"), "a+") as fp: + with open(config_yaml_path.joinpath("conda-forge.yml"), "a+") as fp: fp.write("build_with_mambabuild: true") with pytest.deprecated_call(match="build_with_mambabuild is deprecated"): assert load_forge_config()["conda_build_tool"] == "mambabuild" - with open(os.path.join(config_yaml, "conda-forge.yml"), "w") as fp: + with open(config_yaml_path.joinpath("conda-forge.yml"), "w") as fp: fp.write(unmodified) fp.write("build_with_mambabuild: false") with pytest.deprecated_call(match="build_with_mambabuild is deprecated"): assert load_forge_config()["conda_build_tool"] == "conda-build" - with open(os.path.join(config_yaml, "conda-forge.yml"), "w") as fp: + with open(config_yaml_path.joinpath("conda-forge.yml"), "w") as fp: fp.write(unmodified) fp.write("conda_build_tool: does-not-exist") @@ -973,17 +931,18 @@ def test_conda_build_tools(config_yaml, caplog): def test_remote_ci_setup(config_yaml): + config_yaml_path = Path(config_yaml) load_forge_config = lambda: configure_feedstock._load_forge_config( # noqa config_yaml, - exclusive_config_file=os.path.join( - config_yaml, "recipe", "default_config.yaml" + exclusive_config_file=str( + config_yaml_path.joinpath("recipe", "default_config.yaml") ), ) cfg = load_forge_config() - with open(os.path.join(config_yaml, "conda-forge.yml")) as fp: + with open(config_yaml_path.joinpath("conda-forge.yml")) as fp: unmodified = fp.read() - with open(os.path.join(config_yaml, "conda-forge.yml"), "a+") as fp: + with open(config_yaml_path.joinpath("conda-forge.yml"), "a+") as fp: fp.write( "remote_ci_setup: ['conda-forge-ci-setup=3', 'py-lief<0.12']\n" ) @@ -1000,7 +959,7 @@ def test_remote_ci_setup(config_yaml): "py-lief", ] - with open(os.path.join(config_yaml, "conda-forge.yml"), "w") as fp: + with open(config_yaml_path.joinpath("conda-forge.yml"), "w") as fp: fp.write(unmodified + "\n") fp.write( "remote_ci_setup: ['conda-forge-ci-setup=3', 'py-lief<0.12']\n" @@ -1953,9 +1912,9 @@ def test_get_used_key_values_by_input_order( def test_conda_build_api_render_for_smithy(testing_workdir): import conda_build.api - _thisdir = os.path.abspath(os.path.dirname(__file__)) - recipe = os.path.join(_thisdir, "recipes", "multiple_outputs") - dest_recipe = os.path.join(testing_workdir, "recipe") + _thisdir = Path(__file__).resolve().parent + recipe = _thisdir.joinpath("recipes", "multiple_outputs") + dest_recipe = str(Path(testing_workdir, "recipe")) shutil.copytree(recipe, dest_recipe) all_top_level_builds = { ("1.5", "9.5"), diff --git a/tests/test_feedstock_io.py b/tests/test_feedstock_io.py index 4665fbc1e..30cd2b5fe 100644 --- a/tests/test_feedstock_io.py +++ b/tests/test_feedstock_io.py @@ -2,6 +2,7 @@ import io import operator as op import os +from pathlib import Path import random import stat import string @@ -16,14 +17,14 @@ def keep_dir(dirname): - keep_filename = os.path.join(dirname, ".keep") + keep_filename = Path(dirname, ".keep") with io.open(keep_filename, "w", encoding="utf-8", newline="\n") as fh: fh.write("") def parameterize(): for pathfunc in [ - lambda pth, tmp_dir: os.path.relpath(pth, tmp_dir), + lambda pth, tmp_dir: str(Path(pth).relative_to(tmp_dir)), lambda pth, tmp_dir: pth, ]: for get_repo in [ @@ -34,7 +35,7 @@ def parameterize(): tmp_dir = tempfile.mkdtemp() keep_dir(tmp_dir) - old_dir = os.getcwd() + old_dir = Path.cwd() os.chdir(tmp_dir) yield ( @@ -49,13 +50,13 @@ def parameterize(): class TestFeedstockIO(unittest.TestCase): def setUp(self): - self.old_dir = os.getcwd() + self.old_dir = Path.cwd() self.tmp_dir = tempfile.mkdtemp() os.chdir(self.tmp_dir) with io.open( - os.path.abspath(".keep"), "w", encoding="utf-8", newline="\n" + Path(".keep").resolve(), "w", encoding="utf-8", newline="\n" ) as fh: fh.write("") @@ -67,15 +68,14 @@ def test_repo(self): self.assertIsInstance( fio.get_repo(pathfunc(tmp_dir)), git.Repo ) - possible_repo_subdir = os.path.join( - tmp_dir, - "".join( - "%s%s" - % (x, os.path.sep if random.random() > 0.5 else "") - for x in string.ascii_lowercase - ), + + random_path = "".join( + x + ("/" if random.random() > 0.5 else "") + for x in string.ascii_lowercase ) - os.makedirs(possible_repo_subdir) + + possible_repo_subdir = Path(tmp_dir, random_path) + possible_repo_subdir.mkdir(parents=True) assert fio.get_repo_root(possible_repo_subdir) == tmp_dir def test_set_exe_file(self): @@ -86,20 +86,22 @@ def test_set_exe_file(self): for set_exe in [True, False]: for tmp_dir, repo, pathfunc in parameterize(): filename = "test.txt" - filename = os.path.join(tmp_dir, filename) + filepath = Path(tmp_dir, filename) with io.open( - filename, "w", encoding="utf-8", newline="\n" + filepath, "w", encoding="utf-8", newline="\n" ) as fh: fh.write("") if repo is not None: - repo.index.add([filename]) + repo.index.add([str(filepath)]) - fio.set_exe_file(pathfunc(filename), set_exe) + fio.set_exe_file(pathfunc(str(filepath)), set_exe) - file_mode = os.stat(filename).st_mode + file_mode = filepath.stat().st_mode self.assertEqual(file_mode & set_mode, int(set_exe) * set_mode) if repo is not None: - blob = next(repo.index.iter_blobs(BlobFilter(filename)))[1] + blob = next( + repo.index.iter_blobs(BlobFilter(str(filepath))) + )[1] self.assertEqual( blob.mode & set_mode, int(set_exe) * set_mode ) @@ -107,14 +109,14 @@ def test_set_exe_file(self): def test_write_file(self): for tmp_dir, repo, pathfunc in parameterize(): for filename in ["test.txt", "dir1/dir2/test.txt"]: - filename = os.path.join(tmp_dir, filename) + filename = Path(tmp_dir, filename) write_text = "text" - with fio.write_file(pathfunc(filename)) as fh: + with fio.write_file(pathfunc(str(filename))) as fh: fh.write(write_text) if repo is not None: - repo.index.add([filename]) + repo.index.add([str(filename)]) read_text = "" with io.open(filename, "r", encoding="utf-8") as fh: @@ -123,7 +125,9 @@ def test_write_file(self): self.assertEqual(write_text, read_text) if repo is not None: - blob = next(repo.index.iter_blobs(BlobFilter(filename)))[1] + blob = next( + repo.index.iter_blobs(BlobFilter(str(filename))) + )[1] read_text = blob.data_stream[3].read().decode("utf-8") self.assertEqual(write_text, read_text) @@ -131,7 +135,7 @@ def test_write_file(self): def test_touch_file(self): for tmp_dir, repo, pathfunc in parameterize(): for filename in ["test.txt", "dir1/dir2/test.txt"]: - filename = os.path.join(tmp_dir, filename) + filename = Path(tmp_dir, filename) fio.touch_file(pathfunc(filename)) @@ -142,7 +146,9 @@ def test_touch_file(self): self.assertEqual("", read_text) if repo is not None: - blob = next(repo.index.iter_blobs(BlobFilter(filename)))[1] + blob = next( + repo.index.iter_blobs(BlobFilter(str(filename))) + )[1] read_text = blob.data_stream[3].read().decode("utf-8") self.assertEqual("", read_text) @@ -150,11 +156,10 @@ def test_touch_file(self): def test_remove_file(self): for tmp_dir, repo, pathfunc in parameterize(): for filename in ["test.txt", "dir1/dir2/test.txt"]: - dirname = os.path.dirname(filename) - if dirname and not os.path.exists(dirname): - os.makedirs(dirname) + dirname = Path(filename).parent + Path(dirname).mkdir(parents=True, exist_ok=True) - filename = os.path.join(tmp_dir, filename) + filename = str(Path(tmp_dir, filename)) with io.open( filename, "w", encoding="utf-8", newline="\n" @@ -163,10 +168,10 @@ def test_remove_file(self): if repo is not None: repo.index.add([filename]) - self.assertTrue(os.path.exists(filename)) - if dirname: - self.assertTrue(os.path.exists(dirname)) - self.assertTrue(os.path.exists(os.path.dirname(dirname))) + self.assertTrue(Path(filename).exists()) + if dirname != Path("."): + self.assertTrue(dirname.exists()) + self.assertTrue(dirname.parent.exists()) if repo is not None: self.assertTrue( list(repo.index.iter_blobs(BlobFilter(filename))) @@ -174,10 +179,10 @@ def test_remove_file(self): fio.remove_file(pathfunc(filename)) - self.assertFalse(os.path.exists(filename)) - if dirname: - self.assertFalse(os.path.exists(dirname)) - self.assertFalse(os.path.exists(os.path.dirname(dirname))) + self.assertFalse(Path(filename).exists()) + if dirname != Path("."): + self.assertFalse(dirname.exists()) + self.assertFalse(dirname.parent.exists()) if repo is not None: self.assertFalse( list(repo.index.iter_blobs(BlobFilter(filename))) @@ -188,27 +193,27 @@ def test_copy_file(self): filename1 = "test1.txt" filename2 = "test2.txt" - filename1 = os.path.join(tmp_dir, filename1) - filename2 = os.path.join(tmp_dir, filename2) + filename1 = Path(tmp_dir, filename1) + filename2 = Path(tmp_dir, filename2) write_text = "text" with io.open(filename1, "w", encoding="utf-8", newline="\n") as fh: fh.write(write_text) - self.assertTrue(os.path.exists(filename1)) - self.assertFalse(os.path.exists(filename2)) + self.assertTrue(filename1.exists()) + self.assertFalse(filename2.exists()) if repo is not None: self.assertFalse( - list(repo.index.iter_blobs(BlobFilter(filename2))) + list(repo.index.iter_blobs(BlobFilter(str(filename2)))) ) fio.copy_file(pathfunc(filename1), pathfunc(filename2)) - self.assertTrue(os.path.exists(filename1)) - self.assertTrue(os.path.exists(filename2)) + self.assertTrue(filename1.exists()) + self.assertTrue(filename2.exists()) if repo is not None: self.assertTrue( - list(repo.index.iter_blobs(BlobFilter(filename2))) + list(repo.index.iter_blobs(BlobFilter(str(filename2)))) ) read_text = "" @@ -218,7 +223,9 @@ def test_copy_file(self): self.assertEqual(write_text, read_text) if repo is not None: - blob = next(repo.index.iter_blobs(BlobFilter(filename2)))[1] + blob = next(repo.index.iter_blobs(BlobFilter(str(filename2))))[ + 1 + ] read_text = blob.data_stream[3].read().decode("utf-8") self.assertEqual(write_text, read_text) diff --git a/tests/test_feedstock_tokens.py b/tests/test_feedstock_tokens.py index 645cacf47..f3dacc57c 100644 --- a/tests/test_feedstock_tokens.py +++ b/tests/test_feedstock_tokens.py @@ -1,5 +1,6 @@ import os import json +from pathlib import Path from unittest import mock import time @@ -76,15 +77,15 @@ def test_feedstock_tokens_roundtrip( project, provider=ci, ) - token_json_pth = os.path.join(tmpdir, "tokens", "%s.json" % project) - os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True) + token_json_pth = Path(tmpdir, "tokens", f"{project}.json") + Path(tmpdir, "tokens").mkdir(parents=True, exist_ok=True) try: generate_and_write_feedstock_token(user, project, provider=ci) - assert os.path.exists(pth) + assert Path(pth).exists() register_feedstock_token(user, project, repo, provider=ci) - assert os.path.exists(token_json_pth) + assert token_json_pth.exists() with open(token_json_pth) as fp: token_data = json.load(fp) @@ -102,10 +103,10 @@ def test_feedstock_tokens_roundtrip( user, project, feedstock_token, repo, provider=ci ) finally: - if os.path.exists(pth): - os.remove(pth) - if os.path.exists(token_json_pth): - os.remove(token_json_pth) + if Path(pth).exists(): + Path(pth).unlink() + if token_json_pth.exists(): + token_json_pth.unlink() assert retval is (retval_ci and retval_time) @@ -180,8 +181,8 @@ def test_is_valid_feedstock_token_badtoken( user = "conda-forge" feedstock_token = "akdjhfl" - token_pth = os.path.join(tmpdir, "tokens", "%s.json" % project) - os.makedirs(os.path.dirname(token_pth), exist_ok=True) + token_pth = Path(tmpdir, "tokens", f"{project}.json") + Path(token_pth.parent).mkdir(parents=True, exist_ok=True) with open(token_pth, "w") as fp: td = {"salt": b"adf".hex(), "hashed_token": b"fgh".hex()} if provider is not None: @@ -202,17 +203,17 @@ def test_generate_and_write_feedstock_token(ci): repo = "foo" if ci: - pth = os.path.expanduser("~/.conda-smithy/bar_foo_%s.token" % ci) - opth = os.path.expanduser("~/.conda-smithy/bar_foo.token") + pth = Path(f"~/.conda-smithy/bar_foo_{ci}.token").expanduser() + opth = Path("~/.conda-smithy/bar_foo.token").expanduser() else: - pth = os.path.expanduser("~/.conda-smithy/bar_foo.token") - opth = os.path.expanduser("~/.conda-smithy/bar_foo_azure.token") + pth = Path("~/.conda-smithy/bar_foo.token").expanduser() + opth = Path("~/.conda-smithy/bar_foo_azure.token").expanduser() try: generate_and_write_feedstock_token(user, repo, provider=ci) - assert not os.path.exists(opth) - assert os.path.exists(pth) + assert not Path(opth).exists() + assert Path(pth).exists() if ci is not None: generate_and_write_feedstock_token(user, repo, provider=None) @@ -223,10 +224,10 @@ def test_generate_and_write_feedstock_token(ci): with pytest.raises(FeedstockTokenError): generate_and_write_feedstock_token(user, repo, provider=ci) finally: - if os.path.exists(pth): - os.remove(pth) - if os.path.exists(opth): - os.remove(opth) + if Path(pth).exists(): + Path(pth).unlink() + if Path(opth).exists(): + Path(opth).unlink() @pytest.mark.parametrize("ci", [None, "azure"]) @@ -234,9 +235,9 @@ def test_read_feedstock_token(ci): user = "bar" repo = "foo" if ci: - pth = os.path.expanduser("~/.conda-smithy/bar_foo_%s.token" % ci) + pth = Path(f"~/.conda-smithy/bar_foo_{ci}.token").expanduser() else: - pth = os.path.expanduser("~/.conda-smithy/bar_foo.token") + pth = Path("~/.conda-smithy/bar_foo.token").expanduser() # no token token, err = read_feedstock_token(user, repo, provider=ci) @@ -245,13 +246,13 @@ def test_read_feedstock_token(ci): # empty try: - os.system("touch " + pth) + os.system("touch " + str(pth)) token, err = read_feedstock_token(user, repo, provider=ci) assert "Empty token found in" in err assert token is None finally: - if os.path.exists(pth): - os.remove(pth) + if Path(pth).exists(): + Path(pth).unlink() # token ok try: @@ -267,8 +268,8 @@ def test_read_feedstock_token(ci): assert "No token found in" in err assert token is None finally: - if os.path.exists(pth): - os.remove(pth) + if Path(pth).exists(): + Path(pth).unlink() @pytest.mark.parametrize( @@ -324,11 +325,9 @@ def test_feedstock_token_exists( ) user = "foo" - os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True) + Path(tmpdir, "tokens").mkdir(parents=True, exist_ok=True) if file_exists: - with open( - os.path.join(tmpdir, "tokens", "%s.json" % project), "w" - ) as fp: + with open(Path(tmpdir, "tokens", f"{project}.json"), "w") as fp: data = {"tokens": [{}]} if provider is not None: data["tokens"][0]["provider"] = provider @@ -365,8 +364,8 @@ def test_feedstock_token_raises( git_mock.Repo.clone_from.side_effect = ValueError("blarg") user = "foo" - os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True) - with open(os.path.join(tmpdir, "tokens", "%s.json" % project), "w") as fp: + Path(tmpdir, "tokens").mkdir(parents=True, exist_ok=True) + with open(Path(tmpdir, "tokens", f"{project}.json"), "w") as fp: fp.write("{}") with pytest.raises(FeedstockTokenError) as e: @@ -409,13 +408,13 @@ def test_register_feedstock_token_works( user = "foo" project = "bar" - os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True) + Path(tmpdir, "tokens").mkdir(parents=True, exist_ok=True) pth = feedstock_token_local_path( user, project, provider=ci, ) - token_json_pth = os.path.join(tmpdir, "tokens", "%s.json" % project) + token_json_pth = Path(tmpdir, "tokens", f"{project}.json") try: generate_and_write_feedstock_token(user, project, provider=ci) @@ -423,8 +422,8 @@ def test_register_feedstock_token_works( register_feedstock_token(user, project, repo, provider=ci) finally: - if os.path.exists(pth): - os.remove(pth) + if Path(pth).exists(): + Path(pth).unlink() git_mock.Repo.clone_from.assert_called_once_with( "abc123", @@ -433,7 +432,7 @@ def test_register_feedstock_token_works( ) repo = git_mock.Repo.clone_from.return_value - repo.index.add.assert_called_once_with(token_json_pth) + repo.index.add.assert_called_once_with(str(token_json_pth)) repo.index.commit.assert_called_once_with( "[ci skip] [skip ci] [cf admin skip] ***NO_CI*** added token for %s/%s on provider%s" % (user, project, "" if ci is None else " " + ci) @@ -481,20 +480,20 @@ def test_register_feedstock_token_notoken( user = "foo" project = "bar" - os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True) + Path(tmpdir, "tokens").mkdir(parents=True, exist_ok=True) pth = feedstock_token_local_path( user, project, provider=ci, ) - token_json_pth = os.path.join(tmpdir, "tokens", "bar.json") + token_json_pth = Path(tmpdir, "tokens", "bar.json") try: with pytest.raises(FeedstockTokenError) as e: register_feedstock_token(user, project, repo, provider=ci) finally: - if os.path.exists(pth): - os.remove(pth) + if Path(pth).exists(): + Path(pth).unlink() git_mock.Repo.clone_from.assert_not_called() @@ -504,7 +503,7 @@ def test_register_feedstock_token_notoken( repo.remote.return_value.pull.assert_not_called() repo.remote.return_value.push.assert_not_called() - assert not os.path.exists(token_json_pth) + assert not token_json_pth.exists() assert "No token found in" in str(e.value) @@ -537,13 +536,13 @@ def test_register_feedstock_token_append( user = "foo" project = "bar" - os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True) + Path(tmpdir, "tokens").mkdir(parents=True, exist_ok=True) pth = feedstock_token_local_path( user, project, provider=ci, ) - token_json_pth = os.path.join(tmpdir, "tokens", "bar.json") + token_json_pth = Path(tmpdir, "tokens", "bar.json") with open(token_json_pth, "w") as fp: fp.write('{"tokens": [1]}') @@ -551,8 +550,8 @@ def test_register_feedstock_token_append( generate_and_write_feedstock_token(user, project, provider=ci) register_feedstock_token(user, project, repo, provider=ci) finally: - if os.path.exists(pth): - os.remove(pth) + if Path(pth).exists(): + Path(pth).unlink() git_mock.Repo.clone_from.assert_called_once_with( "abc123", @@ -561,7 +560,7 @@ def test_register_feedstock_token_append( ) repo = git_mock.Repo.clone_from.return_value - repo.index.add.assert_called_once_with(token_json_pth) + repo.index.add.assert_called_once_with(str(token_json_pth)) repo.index.commit.assert_called_once_with( "[ci skip] [skip ci] [cf admin skip] ***NO_CI*** added token for %s/%s on provider%s" % (user, project, "" if ci is None else " " + ci) @@ -715,8 +714,8 @@ def test_register_feedstock_token_with_providers( finally: for provider in providers: pth = feedstock_token_local_path(user, project, provider=provider) - if os.path.exists(pth): - os.remove(pth) + if Path(pth).exists(): + Path(pth).unlink() @pytest.mark.parametrize("unique_token_per_provider", [False, True]) @@ -833,5 +832,5 @@ def test_register_feedstock_token_with_providers_error( finally: for _provider in providers: pth = feedstock_token_local_path(user, project, provider=_provider) - if os.path.exists(pth): - os.remove(pth) + if Path(pth).exists(): + Path(pth).unlink() diff --git a/tests/test_lint_recipe.py b/tests/test_lint_recipe.py index 6ae6a94a4..6339e925d 100644 --- a/tests/test_lint_recipe.py +++ b/tests/test_lint_recipe.py @@ -4,6 +4,7 @@ from contextlib import contextmanager import io import os +from pathlib import Path import shutil import subprocess import tempfile @@ -16,7 +17,7 @@ import conda_smithy.lint_recipe as linter -_thisdir = os.path.abspath(os.path.dirname(__file__)) +_thisdir = Path(__file__).resolve().parent def is_gh_token_set(): @@ -38,7 +39,7 @@ def test_stdlib_hint(comp_lang): expected_message = "This recipe is using a compiler" with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( f""" package: @@ -58,7 +59,7 @@ def test_sysroot_hint(): expected_message = "You're setting a requirement on sysroot" with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( """ package: @@ -78,7 +79,7 @@ def test_osx_hint(where): expected_message = "You're setting a constraint on the `__osx` virtual" with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( f""" package: @@ -147,7 +148,7 @@ def test_osx_noarch_hint(where): avoid_message = "You're setting a constraint on the `__osx` virtual" with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( f""" package: @@ -217,9 +218,9 @@ def test_cbc_osx_hints( std_selector, with_linux, reverse_arch, macdt, v_std, sdk, exp_hint ): with tmp_directory() as rdir: - with open(os.path.join(rdir, "meta.yaml"), "w") as fh: + with open(Path(rdir, "meta.yaml"), "w") as fh: fh.write("package:\n name: foo") - with open(os.path.join(rdir, "conda_build_config.yaml"), "a") as fh: + with open(Path(rdir, "conda_build_config.yaml"), "w") as fh: if macdt is not None: fh.write( f"""\ @@ -256,7 +257,7 @@ def test_cbc_osx_hints( # run the linter _, hints = linter.main(rdir, return_hints=True) # show CBC/hints for debugging - with open(os.path.join(rdir, "conda_build_config.yaml"), "r") as fh: + with open(Path(rdir, "conda_build_config.yaml"), "r") as fh: print("".join(fh.readlines())) print(hints) # validate against expectations @@ -467,7 +468,7 @@ def test_test_section_with_recipe(self): lints, hints = linter.lintify_meta_yaml({}, recipe_dir) self.assertIn(expected_message, lints) - with io.open(os.path.join(recipe_dir, "run_test.py"), "w") as fh: + with io.open(Path(recipe_dir, "run_test.py"), "w") as fh: fh.write("# foo") lints, hints = linter.lintify_meta_yaml({}, recipe_dir) self.assertNotIn(expected_message, lints) @@ -480,7 +481,7 @@ def test_jinja2_vars(self): ) with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( """ package: @@ -511,7 +512,7 @@ def test_selectors(self): with tmp_directory() as recipe_dir: def assert_selector(selector, is_good=True): - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( """ package: @@ -552,7 +553,7 @@ def assert_pyXY_selector(meta_string, is_good=False, kind="lint"): expected_start = "Old-style Python selectors (py27, py34, py35, py36) are deprecated" else: expected_start = "Old-style Python selectors (py27, py35, etc) are only available" - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write(meta_string) lints, hints = linter.main(recipe_dir, return_hints=True) if is_good: @@ -662,7 +663,7 @@ def test_noarch_selectors(self): with tmp_directory() as recipe_dir: def assert_noarch_selector(meta_string, is_good=False): - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write(meta_string) lints = linter.main(recipe_dir) if is_good: @@ -810,7 +811,7 @@ def test_suggest_noarch(self): with tmp_directory() as recipe_dir: def assert_noarch_hint(meta_string, is_good=False): - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write(meta_string) lints, hints = linter.main(recipe_dir, return_hints=True) if is_good: @@ -881,7 +882,7 @@ def test_jinja_os_environ(self): # Test that we can use os.environ in a recipe. We don't care about # the results here. with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( """ {% set version = os.environ.get('WIBBLE') %} @@ -896,14 +897,14 @@ def test_jinja_load_file_regex(self): # Test that we can use load_file_regex in a recipe. We don't care about # the results here. with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "sha256"), "w") as fh: + with io.open(Path(recipe_dir, "sha256"), "w") as fh: fh.write( """ d0e46ea5fca7d4c077245fe0b4195a828d9d4d69be8a0bd46233b2c12abd2098 iwftc_osx.zip 8ce4dc535b21484f65027be56263d8b0d9f58e57532614e1a8f6881f3b8fe260 iwftc_win.zip """ ) - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( """ {% set sha256_osx = load_file_regex(load_file="sha256", @@ -922,7 +923,7 @@ def test_jinja_load_file_data(self): # renders conda-build functions to just function stubs to pass the linting. # TODO: add *args and **kwargs for functions used to parse the file. with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( """ {% set data = load_file_data("IDONTNEED", from_recipe_dir=True, recipe_dir=".") %} @@ -939,7 +940,7 @@ def test_jinja_load_setup_py_data(self): # renders conda-build functions to just function stubs to pass the linting. # TODO: add *args and **kwargs for functions used to parse the file. with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( """ {% set data = load_setup_py_data("IDONTNEED", from_recipe_dir=True, recipe_dir=".") %} @@ -956,7 +957,7 @@ def test_jinja_load_str_data(self): # renders conda-build functions to just function stubs to pass the linting. # TODO: add *args and **kwargs for functions used to parse the data. with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( """ {% set data = load_str_data("IDONTNEED", "json") %} @@ -970,7 +971,7 @@ def test_jinja_load_str_data(self): def test_jinja_os_sep(self): # Test that we can use os.sep in a recipe. with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( """ package: @@ -986,7 +987,7 @@ def test_target_platform(self): # Test that we can use target_platform in a recipe. We don't care about # the results here. with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( """ package: @@ -1226,7 +1227,7 @@ def test_end_empty_line(self): bad_contents + [valid_content], [0, 0, 0, 2, 2, 2, 3, 3, 3, 1] ): with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as f: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as f: f.write(content) lints, hints = linter.lintify_meta_yaml( {}, recipe_dir=recipe_dir @@ -1251,7 +1252,7 @@ def test_end_empty_line(self): def test_cb3_jinja2_functions(self): lints = linter.main( - os.path.join(_thisdir, "recipes", "cb3_jinja2_functions", "recipe") + Path(_thisdir, "recipes", "cb3_jinja2_functions", "recipe") ) assert not lints @@ -1506,20 +1507,18 @@ def test_examples(self): self.assertNotIn(msg, lints) def test_multiple_sources(self): - lints = linter.main( - os.path.join(_thisdir, "recipes", "multiple_sources") - ) + lints = linter.main(Path(_thisdir, "recipes", "multiple_sources")) assert not lints def test_noarch_platforms(self): lints = linter.main( - os.path.join(_thisdir, "recipes", "noarch_platforms", "recipe") + Path(_thisdir, "recipes", "noarch_platforms", "recipe") ) assert not lints def test_noarch_selector_variants(self): lints = linter.main( - os.path.join(_thisdir, "recipes", "noarch_selector_variants") + Path(_thisdir, "recipes", "noarch_selector_variants") ) assert not lints @@ -1636,7 +1635,7 @@ def test_r_base_requirements(self): ) def test_build_sh_with_shellcheck_findings(self): lints, hints = linter.main( - os.path.join(_thisdir, "recipes", "build_script_with_findings"), + Path(_thisdir, "recipes", "build_script_with_findings"), return_hints=True, ) assert any( @@ -1675,7 +1674,7 @@ def test_mpl_base_hint_outputs(self): class TestCLI_recipe_lint(unittest.TestCase): def test_cli_fail(self): with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( textwrap.dedent( """ @@ -1695,7 +1694,7 @@ def test_cli_fail(self): def test_cli_success(self): with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( textwrap.dedent( """ @@ -1726,7 +1725,7 @@ def test_cli_success(self): def test_cli_environ(self): with tmp_directory() as recipe_dir: - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( textwrap.dedent( """ @@ -1763,7 +1762,7 @@ def test_unicode(self): """ with tmp_directory() as recipe_dir: with io.open( - os.path.join(recipe_dir, "meta.yaml"), "wt", encoding="utf-8" + Path(recipe_dir, "meta.yaml"), "wt", encoding="utf-8" ) as fh: fh.write( """ @@ -1793,7 +1792,7 @@ def test_jinja_variable_def(self): with tmp_directory() as recipe_dir: def assert_jinja(jinja_var, is_good=True): - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( """ {{% set name = "conda-smithy" %}} @@ -1830,13 +1829,13 @@ def test_lint_no_builds(): expected_message = "The feedstock has no `.ci_support` files and " with tmp_directory() as feedstock_dir: - ci_support_dir = os.path.join(feedstock_dir, ".ci_support") - os.makedirs(ci_support_dir, exist_ok=True) - with io.open(os.path.join(ci_support_dir, "README"), "w") as fh: + ci_support_dir = Path(feedstock_dir, ".ci_support") + ci_support_dir.mkdir(parents=True, exist_ok=True) + with io.open(Path(ci_support_dir, "README"), "w") as fh: fh.write("blah") - recipe_dir = os.path.join(feedstock_dir, "recipe") - os.makedirs(recipe_dir, exist_ok=True) - with io.open(os.path.join(recipe_dir, "meta.yaml"), "w") as fh: + recipe_dir = Path(feedstock_dir, "recipe") + recipe_dir.mkdir(parents=True, exist_ok=True) + with io.open(Path(recipe_dir, "meta.yaml"), "w") as fh: fh.write( """ package: @@ -1847,7 +1846,7 @@ def test_lint_no_builds(): lints = linter.main(recipe_dir, conda_forge=True) assert any(lint.startswith(expected_message) for lint in lints) - with io.open(os.path.join(ci_support_dir, "blah.yaml"), "w") as fh: + with io.open(Path(ci_support_dir, "blah.yaml"), "w") as fh: fh.write("blah") lints = linter.main(recipe_dir, conda_forge=True)