From 7bb87aa0c8ea6f57c49992ade830786b8c1a552b Mon Sep 17 00:00:00 2001 From: Yannik Tausch Date: Sun, 28 Jul 2024 16:12:40 +0200 Subject: [PATCH 1/5] refactor: replace get_repo with new git backend logic squash! refactor: replace get_repo with new git backend logic !squash rename default branch --- conda_forge_tick/auto_tick.py | 118 +++++++++++++++++------ conda_forge_tick/contexts.py | 4 + conda_forge_tick/git_utils.py | 94 ++++++++----------- conda_forge_tick/migrators_types.py | 1 + tests/fake_lazy_json.py | 15 +++ tests/test_auto_tick.py | 141 +++++++++++++++++++++++++++- tests/test_contexts.py | 6 ++ tests/test_git_utils.py | 71 +++++++++----- 8 files changed, 341 insertions(+), 109 deletions(-) create mode 100644 tests/fake_lazy_json.py diff --git a/conda_forge_tick/auto_tick.py b/conda_forge_tick/auto_tick.py index 6bc85306d..00cb17e3a 100644 --- a/conda_forge_tick/auto_tick.py +++ b/conda_forge_tick/auto_tick.py @@ -15,6 +15,7 @@ import github import github3 +import github3.repos import networkx as nx import tqdm from conda.models.version import VersionOrder @@ -28,8 +29,11 @@ from conda_forge_tick.deploy import deploy from conda_forge_tick.feedstock_parser import BOOTSTRAP_MAPPINGS from conda_forge_tick.git_utils import ( + DryRunBackend, + GitPlatformBackend, + RepositoryNotFoundError, comment_on_pr, - get_repo, + github3_client, github_backend, is_github_api_limit_reached, push_repo, @@ -140,9 +144,55 @@ def _get_pre_pr_migrator_attempts(attrs, migrator_name, *, is_version): return pri.get("pre_pr_migrator_attempts", {}).get(migrator_name, 0) +def _prepare_feedstock_repository( + backend: GitPlatformBackend, + context: ClonedFeedstockContext, + branch: str, + base_branch: str, +) -> bool: + """ + Prepare a feedstock repository for migration by forking and cloning it. The local clone will be present in + context.local_clone_dir. + + Any errors are written to the pr_info attribute of the feedstock context and logged. + + :param backend: The GitPlatformBackend instance to use. + :param context: The current context + :param branch: The branch to create in the forked repository. + :param base_branch: The base branch to branch from. + :return: True if the repository was successfully prepared, False otherwise. + """ + try: + backend.fork(context.git_repo_owner, context.git_repo_name) + except RepositoryNotFoundError: + logger.warning( + f"Could not fork {context.git_repo_owner}/{context.git_repo_name}: Not Found" + ) + + error_message = f"{context.feedstock_name}: Git repository not found." + logger.critical( + f"Failed to migrate {context.feedstock_name}, {error_message}", + ) + + with context.attrs["pr_info"] as pri: + pri["bad"] = error_message + + return False + + backend.clone_fork_and_branch( + upstream_owner=context.git_repo_owner, + repo_name=context.git_repo_name, + target_dir=context.local_clone_dir, + new_branch=branch, + base_branch=base_branch, + ) + return True + + def run_with_tmpdir( context: FeedstockContext, migrator: Migrator, + git_backend: GitPlatformBackend, rerender: bool = True, base_branch: str = "main", dry_run: bool = False, @@ -161,6 +211,7 @@ def run_with_tmpdir( return run( context=cloned_context, migrator=migrator, + git_backend=git_backend, rerender=rerender, base_branch=base_branch, dry_run=dry_run, @@ -171,6 +222,7 @@ def run_with_tmpdir( def run( context: ClonedFeedstockContext, migrator: Migrator, + git_backend: GitPlatformBackend, rerender: bool = True, base_branch: str = "main", dry_run: bool = False, @@ -184,6 +236,8 @@ def run( The current feedstock context, already containing information about a temporary directory for the feedstock. migrator: Migrator instance The migrator to run on the feedstock + git_backend: GitPlatformBackend + The git backend to use. Use the DryRunBackend for testing. rerender : bool Whether to rerender base_branch : str, optional @@ -202,9 +256,6 @@ def run( # sometimes we get weird directory issues so make sure we reset os.chdir(BOT_HOME_DIR) - # get the repo - branch_name = migrator.remote_branch(context) + "_h" + uuid4().hex[0:6] - migrator_name = get_migrator_name(migrator) is_version_migration = isinstance(migrator, Version) _increment_pre_pr_migrator_attempt( @@ -213,20 +264,25 @@ def run( is_version=is_version_migration, ) - # TODO: run this in parallel - repo = get_repo(context=context, branch=branch_name, base_branch=base_branch) - - feedstock_dir = str(context.local_clone_dir) - if not feedstock_dir or not repo: - logger.critical( - "Failed to migrate %s, %s", - context.feedstock_name, - context.attrs.get("pr_info", {}).get("bad"), - ) + branch_name = migrator.remote_branch(context) + "_h" + uuid4().hex[0:6] + if not _prepare_feedstock_repository( + git_backend, + context, + branch_name, + base_branch, + ): + # something went wrong during forking or cloning return False, False # need to use an absolute path here - feedstock_dir = os.path.abspath(feedstock_dir) + feedstock_dir = str(context.local_clone_dir.resolve()) + + # This is needed because we want to migrate to the new backend step-by-step + repo: github3.repos.Repository | None = github3_client().repository( + context.git_repo_owner, context.git_repo_name + ) + + assert repo is not None migration_run_data = run_migration( migrator=migrator, @@ -566,7 +622,8 @@ def _run_migrator_on_feedstock_branch( attrs, base_branch, migrator, - fctx, + fctx: FeedstockContext, + git_backend: GitPlatformBackend, dry_run, mctx, migrator_name, @@ -581,6 +638,7 @@ def _run_migrator_on_feedstock_branch( migrator_uid, pr_json = run_with_tmpdir( context=fctx, migrator=migrator, + git_backend=git_backend, rerender=migrator.rerender, hash_type=attrs.get("hash_type", "sha256"), base_branch=base_branch, @@ -743,7 +801,9 @@ def _is_migrator_done(_mg_start, good_prs, time_per, pr_limit): return False -def _run_migrator(migrator, mctx, temp, time_per, dry_run): +def _run_migrator( + migrator, mctx, temp, time_per, dry_run, git_backend: GitPlatformBackend +): _mg_start = time.time() migrator_name = get_migrator_name(migrator) @@ -861,14 +921,15 @@ def _run_migrator(migrator, mctx, temp, time_per, dry_run): ) ): good_prs, break_loop = _run_migrator_on_feedstock_branch( - attrs, - base_branch, - migrator, - fctx, - dry_run, - mctx, - migrator_name, - good_prs, + attrs=attrs, + base_branch=base_branch, + migrator=migrator, + fctx=fctx, + git_backend=git_backend, + dry_run=dry_run, + mctx=mctx, + migrator_name=migrator_name, + good_prs=good_prs, ) if break_loop: break @@ -1117,14 +1178,11 @@ def main(ctx: CliContext) -> None: ), flush=True, ) + git_backend = github_backend() if not ctx.dry_run else DryRunBackend() for mg_ind, migrator in enumerate(migrators): good_prs = _run_migrator( - migrator, - mctx, - temp, - time_per_migrator[mg_ind], - ctx.dry_run, + migrator, mctx, temp, time_per_migrator[mg_ind], ctx.dry_run, git_backend ) if good_prs > 0: pass diff --git a/conda_forge_tick/contexts.py b/conda_forge_tick/contexts.py index 13f715b65..9175ab076 100644 --- a/conda_forge_tick/contexts.py +++ b/conda_forge_tick/contexts.py @@ -46,6 +46,10 @@ def __post_init__(self): DEFAULT_BRANCHES.get(self.feedstock_name, "main"), ) + @property + def git_repo_owner(self) -> str: + return "conda-forge" + @property def git_repo_name(self) -> str: return f"{self.feedstock_name}-feedstock" diff --git a/conda_forge_tick/git_utils.py b/conda_forge_tick/git_utils.py index 2d58ae97d..44518bef4 100644 --- a/conda_forge_tick/git_utils.py +++ b/conda_forge_tick/git_utils.py @@ -11,7 +11,7 @@ from datetime import datetime from functools import cached_property from pathlib import Path -from typing import Dict, Literal, Optional, Union +from typing import Dict, Optional, Union import backoff import github @@ -28,7 +28,7 @@ # and pull all the needed info from the various source classes) from conda_forge_tick.lazy_json_backends import LazyJson -from .contexts import ClonedFeedstockContext, FeedstockContext +from .contexts import FeedstockContext from .executors import lock_git_operation from .os_utils import pushd from .utils import get_bot_run_url, run_command_hiding_token @@ -404,8 +404,8 @@ def does_repository_exist(self, owner: str, repo_name: str) -> bool: """ pass - @staticmethod def get_remote_url( + self, owner: str, repo_name: str, connection_mode: GitConnectionMode = GitConnectionMode.HTTPS, @@ -416,6 +416,8 @@ def get_remote_url( :param repo_name: The name of the repository. :param connection_mode: The connection mode to use. :raises ValueError: If the connection mode is not supported. + :raises RepositoryNotFoundError: If the repository does not exist. This is only raised if the backend relies on + the repository existing to generate the URL. """ # Currently we don't need any abstraction for other platforms than GitHub, so we don't build such abstractions. match connection_mode: @@ -641,6 +643,12 @@ class DryRunBackend(GitPlatformBackend): def __init__(self): super().__init__(GitCli()) self._repos: set[str] = set() + self._repos: dict[str, str] = {} + """ + _repos maps from repository name to the owner of the upstream repository. + If a remote URL of a fork is requested with get_remote_url, _USER (the virtual current user) is + replaced by the owner of the upstream repository. This allows cloning the forked repository. + """ def get_api_requests_left(self) -> Bound: return Bound.INFINITY @@ -654,15 +662,40 @@ def does_repository_exist(self, owner: str, repo_name: str) -> bool: self.get_remote_url(owner, repo_name, GitConnectionMode.HTTPS) ) + def get_remote_url( + self, + owner: str, + repo_name: str, + connection_mode: GitConnectionMode = GitConnectionMode.HTTPS, + ) -> str: + if owner != self._USER: + return super().get_remote_url(owner, repo_name, connection_mode) + # redirect to the upstream repository + try: + upstream_owner = self._repos[repo_name] + except KeyError: + raise RepositoryNotFoundError( + f"Repository {owner}/{repo_name} appears to be a virtual fork but does not exist. Note that dry-run " + "forks are persistent only for the duration of the backend instance." + ) + + return super().get_remote_url(upstream_owner, repo_name, connection_mode) + @lock_git_operation() def fork(self, owner: str, repo_name: str): if repo_name in self._repos: - raise ValueError(f"Fork of {repo_name} already exists.") + logger.debug(f"Fork of {repo_name} already exists. Doing nothing.") + return + + if not self.does_repository_exist(owner, repo_name): + raise RepositoryNotFoundError( + f"Cannot fork non-existing repository {owner}/{repo_name}." + ) logger.debug( f"Dry Run: Creating fork of {owner}/{repo_name} for user {self._USER}." ) - self._repos.add(repo_name) + self._repos[repo_name] = owner def _sync_default_branch(self, upstream_owner: str, upstream_repo: str): logger.debug( @@ -720,57 +753,6 @@ def feedstock_repo(fctx: FeedstockContext) -> str: return fctx.feedstock_name + "-feedstock" -@lock_git_operation() -def get_repo( - context: ClonedFeedstockContext, - branch: str, - base_branch: str = "main", -) -> github3.repos.Repository | Literal[False]: - """Get the feedstock repo - - Parameters - ---------- - context : ClonedFeedstockContext - Feedstock context used for constructing feedstock urls, etc. - branch : str - The branch to be made. - base_branch : str, optional - The base branch from which to make the new branch. - - Returns - ------- - repo : github3 repository - The github3 repository object. - """ - backend = github_backend() - feedstock_repo_name = feedstock_repo(context) - - try: - backend.fork("conda-forge", feedstock_repo_name) - except RepositoryNotFoundError: - logger.warning(f"Could not fork conda-forge/{feedstock_repo_name}") - with context.attrs["pr_info"] as pri: - pri["bad"] = f"{context.feedstock_name}: Git repository not found.\n" - return False, False - - backend.clone_fork_and_branch( - upstream_owner="conda-forge", - repo_name=feedstock_repo_name, - target_dir=context.local_clone_dir, - new_branch=branch, - base_branch=base_branch, - ) - - # This is needed because we want to migrate to the new backend step-by-step - repo: github3.repos.Repository | None = github3_client().repository( - "conda-forge", feedstock_repo_name - ) - - assert repo is not None - - return repo - - @lock_git_operation() def delete_branch(pr_json: LazyJson, dry_run: bool = False) -> None: ref = pr_json["head"]["ref"] diff --git a/conda_forge_tick/migrators_types.py b/conda_forge_tick/migrators_types.py index efbe8a4a8..8f83d6ee3 100644 --- a/conda_forge_tick/migrators_types.py +++ b/conda_forge_tick/migrators_types.py @@ -128,6 +128,7 @@ class AttrsTypedDict_(TypedDict, total=False): raw_meta_yaml: str req: Set[str] platforms: List[str] + pr_info: typing.Any requirements: RequirementsTypedDict source: SourceTypedDict test: TestTypedDict diff --git a/tests/fake_lazy_json.py b/tests/fake_lazy_json.py new file mode 100644 index 000000000..2880d8522 --- /dev/null +++ b/tests/fake_lazy_json.py @@ -0,0 +1,15 @@ +from types import TracebackType +from typing import Self + + +class FakeLazyJson(dict): + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + pass diff --git a/tests/test_auto_tick.py b/tests/test_auto_tick.py index 4e9600d4e..b087cad91 100644 --- a/tests/test_auto_tick.py +++ b/tests/test_auto_tick.py @@ -1,14 +1,148 @@ +import copy +import logging +import tempfile +from pathlib import Path from unittest import mock -from unittest.mock import ANY, MagicMock +from unittest.mock import ANY, MagicMock, create_autospec import pytest -from conda_forge_tick.auto_tick import run_with_tmpdir +from conda_forge_tick.auto_tick import _prepare_feedstock_repository, run_with_tmpdir from conda_forge_tick.contexts import ClonedFeedstockContext, FeedstockContext +from conda_forge_tick.git_utils import ( + DryRunBackend, + GitPlatformBackend, + RepositoryNotFoundError, +) +from tests.fake_lazy_json import FakeLazyJson demo_attrs = {"conda-forge.yml": {"provider": {"default_branch": "main"}}} +def test_prepare_feedstock_repository_success(): + backend = create_autospec(GitPlatformBackend) + + with tempfile.TemporaryDirectory() as tmpdir_str: + tmpdir = Path(tmpdir_str) + cloned_context = ClonedFeedstockContext( + feedstock_name="pytest", + attrs=demo_attrs, + default_branch="main", + local_clone_dir=tmpdir, + ) + + assert ( + _prepare_feedstock_repository( + backend, cloned_context, "new_branch", "base_branch" + ) + is True + ) + + backend.fork.assert_called_once_with("conda-forge", "pytest-feedstock") + + backend.clone_fork_and_branch.assert_called_once_with( + upstream_owner="conda-forge", + repo_name="pytest-feedstock", + target_dir=tmpdir, + new_branch="new_branch", + base_branch="base_branch", + ) + + +def test_prepare_feedstock_repository_repository_not_found(caplog): + backend = create_autospec(GitPlatformBackend) + + caplog.set_level(logging.WARNING) + + with tempfile.TemporaryDirectory() as tmpdir_str: + tmpdir = Path(tmpdir_str) + + demo_attrs_copy = copy.deepcopy(demo_attrs) + + attrs = FakeLazyJson() + pr_info = FakeLazyJson() + + with attrs: + for key, value in demo_attrs_copy.items(): + attrs[key] = value + attrs["pr_info"] = pr_info + + cloned_context = ClonedFeedstockContext( + feedstock_name="pytest", + attrs=attrs, # type: ignore + default_branch="main", + local_clone_dir=tmpdir, + ) + + backend.fork.side_effect = RepositoryNotFoundError( + "Repository not found - MAGIC WORDS" + ) + + assert ( + _prepare_feedstock_repository( + backend, cloned_context, "new_branch", "base_branch" + ) + is False + ) + + backend.fork.assert_called_once_with("conda-forge", "pytest-feedstock") + + backend.clone_fork_and_branch.assert_not_called() + + assert "Not Found" in caplog.text + assert "pytest: Git repository not found." in attrs["pr_info"]["bad"] + + +def test_prepare_feedstock_repository_complete_dry_run(): + """ + This test really clones the repository using the DryRunBackend. + """ + + backend = DryRunBackend() + + context = FeedstockContext( + feedstock_name="pytest", + attrs=demo_attrs, + ) + + with context.reserve_clone_directory() as cloned_context: + assert ( + _prepare_feedstock_repository(backend, cloned_context, "new_branch", "main") + is True + ) + + assert cloned_context.local_clone_dir.joinpath("conda-forge.yml").exists() + + # new_branch should be checked out + assert ( + "new_branch" + in backend.cli._run_git_command( + ["status"], + cloned_context.local_clone_dir, + capture_text=True, + ).stdout + ) + + +def test_prepare_feedstock_repository_complete_fail(): + """ + This test really clones the repository using the DryRunBackend. + """ + + backend = DryRunBackend() + + context = FeedstockContext( + feedstock_name="this-repo-does-not-exist", + attrs=MagicMock(), + ) + + with context.reserve_clone_directory() as cloned_context: + assert ( + _prepare_feedstock_repository(backend, cloned_context, "new_branch", "main") + is False + ) + + @pytest.mark.parametrize("dry_run", [True, False]) @pytest.mark.parametrize("base_branch", ["main", "master"]) @pytest.mark.parametrize("rerender", [True, False]) @@ -22,6 +156,7 @@ def test_run_with_tmpdir( ) migrator = MagicMock() + git_backend = DryRunBackend() kwargs = { "these": "are", @@ -31,6 +166,7 @@ def test_run_with_tmpdir( run_with_tmpdir( context=context, migrator=migrator, + git_backend=git_backend, rerender=rerender, base_branch=base_branch, dry_run=dry_run, @@ -40,6 +176,7 @@ def test_run_with_tmpdir( run_mock.assert_called_once_with( context=ANY, migrator=migrator, + git_backend=git_backend, rerender=rerender, base_branch=base_branch, dry_run=dry_run, diff --git a/tests/test_contexts.py b/tests/test_contexts.py index 473f94a44..82956ea57 100644 --- a/tests/test_contexts.py +++ b/tests/test_contexts.py @@ -46,6 +46,11 @@ def test_feedstock_context_default_branch_set(): assert context.default_branch == "feature" +def test_feedstock_context_git_repo_owner(): + context = FeedstockContext("TEST-FEEDSTOCK-NAME", demo_attrs) + assert context.git_repo_owner == "conda-forge" + + def test_feedstock_context_git_repo_name(): context = FeedstockContext("TEST-FEEDSTOCK-NAME", demo_attrs) assert context.git_repo_name == "TEST-FEEDSTOCK-NAME-feedstock" @@ -68,6 +73,7 @@ def test_feedstock_context_reserve_clone_directory( if default_branch else "main" ) + assert cloned_context.git_repo_owner == "conda-forge" assert cloned_context.git_repo_name == "pytest-feedstock" assert cloned_context.local_clone_dir.exists() diff --git a/tests/test_git_utils.py b/tests/test_git_utils.py index c6d4f5e79..754624cdf 100644 --- a/tests/test_git_utils.py +++ b/tests/test_git_utils.py @@ -1,3 +1,4 @@ +import logging import subprocess import tempfile from pathlib import Path @@ -589,15 +590,6 @@ def test_git_cli_clone_fork_and_branch_non_existing_remote_existing_target_dir(c assert "trying to reset hard" in caplog.text -def test_git_platform_backend_get_remote_url_https(): - owner = "OWNER" - repo = "REPO" - - url = GitPlatformBackend.get_remote_url(owner, repo, GitConnectionMode.HTTPS) - - assert url == f"https://github.com/{owner}/{repo}.git" - - def test_github_backend_from_token(): token = "TOKEN" @@ -626,6 +618,16 @@ def test_github_backend_does_repository_exist(does_exist: bool): github3_client.repository.assert_called_once_with("OWNER", "REPO") +def test_github_backend_get_remote_url_https(): + owner = "OWNER" + repo = "REPO" + backend = GitHubBackend(MagicMock(), MagicMock()) + + url = backend.get_remote_url(owner, repo, GitConnectionMode.HTTPS) + + assert url == f"https://github.com/{owner}/{repo}.git" + + @mock.patch("time.sleep", return_value=None) @mock.patch( "conda_forge_tick.git_utils.GitHubBackend.user", new_callable=mock.PropertyMock @@ -852,9 +854,11 @@ def test_dry_run_backend_get_api_requests_left(): def test_dry_run_backend_does_repository_exist_own_repo(): backend = DryRunBackend() - assert not backend.does_repository_exist("auto-tick-bot-dry-run", "REPO") - backend.fork("UPSTREAM_OWNER", "REPO") - assert backend.does_repository_exist("auto-tick-bot-dry-run", "REPO") + assert not backend.does_repository_exist( + "auto-tick-bot-dry-run", "pytest-feedstock" + ) + backend.fork("conda-forge", "pytest-feedstock") + assert backend.does_repository_exist("auto-tick-bot-dry-run", "pytest-feedstock") def test_dry_run_backend_does_repository_exist_other_repo(): @@ -866,26 +870,51 @@ def test_dry_run_backend_does_repository_exist_other_repo(): ) +def test_dry_run_backend_get_remote_url_non_fork(): + backend = DryRunBackend() + + url = backend.get_remote_url("OWNER", "REPO", GitConnectionMode.HTTPS) + + assert url == "https://github.com/OWNER/REPO.git" + + +def test_dry_run_backend_get_remote_url_non_existing_fork(): + backend = DryRunBackend() + + with pytest.raises(RepositoryNotFoundError, match="does not exist"): + backend.get_remote_url(backend.user, "REPO", GitConnectionMode.HTTPS) + + +def test_dry_run_backend_get_remote_url_existing_fork(): + backend = DryRunBackend() + + backend.fork("conda-forge", "pytest-feedstock") + + url = backend.get_remote_url( + backend.user, "pytest-feedstock", GitConnectionMode.HTTPS + ) + + # note that the URL does not indicate anymore that it is a fork + assert url == "https://github.com/conda-forge/pytest-feedstock.git" + + def test_dry_run_backend_fork(caplog): - caplog.set_level("DEBUG") + caplog.set_level(logging.DEBUG) backend = DryRunBackend() - backend.fork("UPSTREAM_OWNER", "REPO") + backend.fork("conda-forge", "pytest-feedstock") assert ( - "Dry Run: Creating fork of UPSTREAM_OWNER/REPO for user auto-tick-bot-dry-run" + "Dry Run: Creating fork of conda-forge/pytest-feedstock for user auto-tick-bot-dry-run" in caplog.text ) - with pytest.raises(ValueError, match="Fork of REPO already exists"): - backend.fork("UPSTREAM_OWNER", "REPO") - - with pytest.raises(ValueError, match="Fork of REPO already exists"): - backend.fork("OTHER_OWNER", "REPO") + with pytest.raises(RepositoryNotFoundError): + backend.fork("conda-forge", "this-repository-does-not-exist") def test_dry_run_backend_sync_default_branch(caplog): - caplog.set_level("DEBUG") + caplog.set_level(logging.DEBUG) backend = DryRunBackend() From 31115a163a77429aa6494bbf008b88725b51a866 Mon Sep 17 00:00:00 2001 From: Yannik Tausch Date: Sun, 28 Jul 2024 16:31:33 +0200 Subject: [PATCH 2/5] move FakeLazyJson to conftest --- tests/conftest.py | 20 ++++++++++++++++++++ tests/fake_lazy_json.py | 15 --------------- tests/test_auto_tick.py | 7 +++---- 3 files changed, 23 insertions(+), 19 deletions(-) delete mode 100644 tests/fake_lazy_json.py diff --git a/tests/conftest.py b/tests/conftest.py index 149a92547..5ca6b7c0f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,6 @@ import os +from types import TracebackType +from typing import Self import pytest @@ -82,3 +84,21 @@ def use_containers(): os.environ.pop("CF_TICK_IN_CONTAINER", None) else: os.environ["CF_TICK_IN_CONTAINER"] = old_in_container + + +class FakeLazyJson(dict): + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + pass + + +@pytest.fixture +def fake_lazy_json(): + return FakeLazyJson() diff --git a/tests/fake_lazy_json.py b/tests/fake_lazy_json.py deleted file mode 100644 index 2880d8522..000000000 --- a/tests/fake_lazy_json.py +++ /dev/null @@ -1,15 +0,0 @@ -from types import TracebackType -from typing import Self - - -class FakeLazyJson(dict): - def __enter__(self) -> Self: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - pass diff --git a/tests/test_auto_tick.py b/tests/test_auto_tick.py index b087cad91..0ad662135 100644 --- a/tests/test_auto_tick.py +++ b/tests/test_auto_tick.py @@ -14,7 +14,6 @@ GitPlatformBackend, RepositoryNotFoundError, ) -from tests.fake_lazy_json import FakeLazyJson demo_attrs = {"conda-forge.yml": {"provider": {"default_branch": "main"}}} @@ -49,7 +48,7 @@ def test_prepare_feedstock_repository_success(): ) -def test_prepare_feedstock_repository_repository_not_found(caplog): +def test_prepare_feedstock_repository_repository_not_found(caplog, fake_lazy_json): backend = create_autospec(GitPlatformBackend) caplog.set_level(logging.WARNING) @@ -59,8 +58,8 @@ def test_prepare_feedstock_repository_repository_not_found(caplog): demo_attrs_copy = copy.deepcopy(demo_attrs) - attrs = FakeLazyJson() - pr_info = FakeLazyJson() + attrs = copy.deepcopy(fake_lazy_json) + pr_info = copy.deepcopy(fake_lazy_json) with attrs: for key, value in demo_attrs_copy.items(): From 7f67c49039336a124c7812dc4542dfc441e3cf07 Mon Sep 17 00:00:00 2001 From: Yannik Tausch Date: Tue, 3 Sep 2024 14:15:11 +0200 Subject: [PATCH 3/5] avoid deepcopy FakeLazyJson --- tests/conftest.py | 5 ----- tests/test_auto_tick.py | 7 ++++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 5ca6b7c0f..63c68256f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -97,8 +97,3 @@ def __exit__( exc_tb: TracebackType | None, ) -> None: pass - - -@pytest.fixture -def fake_lazy_json(): - return FakeLazyJson() diff --git a/tests/test_auto_tick.py b/tests/test_auto_tick.py index 0ad662135..74a9f4989 100644 --- a/tests/test_auto_tick.py +++ b/tests/test_auto_tick.py @@ -14,6 +14,7 @@ GitPlatformBackend, RepositoryNotFoundError, ) +from tests.conftest import FakeLazyJson demo_attrs = {"conda-forge.yml": {"provider": {"default_branch": "main"}}} @@ -48,7 +49,7 @@ def test_prepare_feedstock_repository_success(): ) -def test_prepare_feedstock_repository_repository_not_found(caplog, fake_lazy_json): +def test_prepare_feedstock_repository_repository_not_found(caplog): backend = create_autospec(GitPlatformBackend) caplog.set_level(logging.WARNING) @@ -58,8 +59,8 @@ def test_prepare_feedstock_repository_repository_not_found(caplog, fake_lazy_jso demo_attrs_copy = copy.deepcopy(demo_attrs) - attrs = copy.deepcopy(fake_lazy_json) - pr_info = copy.deepcopy(fake_lazy_json) + attrs = FakeLazyJson() + pr_info = FakeLazyJson() with attrs: for key, value in demo_attrs_copy.items(): From 20c117d22abe6919344476005321f752e62e1d26 Mon Sep 17 00:00:00 2001 From: "Matthew R. Becker" Date: Tue, 3 Sep 2024 12:13:55 -0500 Subject: [PATCH 4/5] Update tests/test_auto_tick.py --- tests/test_auto_tick.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_auto_tick.py b/tests/test_auto_tick.py index 74a9f4989..d4f97c0c0 100644 --- a/tests/test_auto_tick.py +++ b/tests/test_auto_tick.py @@ -14,7 +14,7 @@ GitPlatformBackend, RepositoryNotFoundError, ) -from tests.conftest import FakeLazyJson +from conftest import FakeLazyJson demo_attrs = {"conda-forge.yml": {"provider": {"default_branch": "main"}}} From a5a82079e9048e17d1684fe2fd1ff2c9c856faf6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:16:39 +0000 Subject: [PATCH 5/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_auto_tick.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_auto_tick.py b/tests/test_auto_tick.py index d4f97c0c0..f6dac25d8 100644 --- a/tests/test_auto_tick.py +++ b/tests/test_auto_tick.py @@ -6,6 +6,7 @@ from unittest.mock import ANY, MagicMock, create_autospec import pytest +from conftest import FakeLazyJson from conda_forge_tick.auto_tick import _prepare_feedstock_repository, run_with_tmpdir from conda_forge_tick.contexts import ClonedFeedstockContext, FeedstockContext @@ -14,7 +15,6 @@ GitPlatformBackend, RepositoryNotFoundError, ) -from conftest import FakeLazyJson demo_attrs = {"conda-forge.yml": {"provider": {"default_branch": "main"}}}