Repository: iterative/dvc

Issue:
dvc.api.open: Will ignore remote option if a default remote is set in config.
# Bug Report

## dvc.api.open: Will ignore remote option if a default remote is set in config.

## Description

In some cases, when passing the remote parameter. The remote specification will get lost during the `dvc pull` execution and will be overwritten by the default remote from the dvc config.

### Reproduce

1. Create new git Repo
2. Init dvc
3. Add two different non local remotes (perferably s3)
4. Make one of the remotes the default
5. Push the same data to both remotes
6. Push git Repo
7. From somewhere outside a dvc project run:
```python
with dvc.api.open(path="myfile", remote='non-default-remote') 
```
8. Track the actually used remote with a Debugger. Or make sure both remotes require different Authentification and just provide the non-defaut-remote authentification during the api call. You will run into Permission errors.

### Expected

If the remote argument is passed this remote should allways be used, when opening files.

### Environment information

**Output of `dvc doctor`:**

```console
DVC version: 2.9.3 (pip)
---------------------------------
Platform: Python 3.8.12 on Linux-5.10.47-linuxkit-x86_64-with-glibc2.2.5
Supports:
        hdfs (fsspec = 2022.1.0, pyarrow = 1.0.1),
        webhdfs (fsspec = 2022.1.0),
        http (aiohttp = 3.8.1, aiohttp-retry = 2.4.6),
        https (aiohttp = 3.8.1, aiohttp-retry = 2.4.6),
        s3 (s3fs = 2022.1.0, boto3 = 1.20.24)
root@112f43f4f86b:/workspaces/centerline-identification-job# 
```



Discussion:
Initial discussion:
https://discord.com/channels/485586884165107732/485596304961962003/933674011982983219

@PietrassykFP has been setting up the credentials via environmental variables, so if the remote gets resolved properly, it should not raise issues. I created a local test aiming to verify if remote gets resolved properly  and it seem to be working fine:

```
def test_open_from_proper_remote(tmp_dir, erepo_dir, make_tmp_dir):
    default_remote = make_tmp_dir("default_storage")
    other_remote = make_tmp_dir("other_storage")

    with erepo_dir.chdir():
        with erepo_dir.dvc.config.edit() as conf:
            conf["remote"]["default"] = {"url": str(default_remote)}
            conf["core"]["remote"] = "default"
            conf["remote"]["other"] = {"url": str(other_remote)}

        erepo_dir.dvc_gen({"foo": "foo content"}, commit="create file")
        erepo_dir.dvc.push(remote="other")

        (erepo_dir / "foo").unlink()
        shutil.rmtree((erepo_dir / ".dvc" / "cache"))

    assert not (
        default_remote / "6d" / "bda444875c24ec1bbdb433456be11f"
    ).is_file()
    assert (other_remote / "6d" / "bda444875c24ec1bbdb433456be11f").is_file()

    with api.open("foo", repo=os.fspath(erepo_dir)) as fd:
        result = fd.read()

    assert result == "foo content"
```

It would be good to reproduce the problem and create a test, though that needs some more investigation.

Resolving patch:
diff --git a/dvc/api/data.py b/dvc/api/data.py
--- a/dvc/api/data.py
+++ b/dvc/api/data.py
@@ -1,6 +1,6 @@
 from contextlib import _GeneratorContextManager as GCM
 from contextlib import contextmanager
-from typing import Optional
+from typing import Any, Dict, Optional
 
 from funcy import reraise
 
@@ -214,7 +214,11 @@ def open(  # noqa, pylint: disable=redefined-builtin
 
 
 def _open(path, repo=None, rev=None, remote=None, mode="r", encoding=None):
-    with Repo.open(repo, rev=rev, subrepos=True, uninitialized=True) as _repo:
+    repo_kwargs: Dict[str, Any] = {"subrepos": True, "uninitialized": True}
+    if remote:
+        repo_kwargs["config"] = {"core": {"remote": remote}}
+
+    with Repo.open(repo, rev=rev, **repo_kwargs) as _repo:
         with _wrap_exceptions(_repo, path):
             import os
             from typing import TYPE_CHECKING, Union
@@ -235,12 +239,6 @@ def _open(path, repo=None, rev=None, remote=None, mode="r", encoding=None):
                 fs_path = fs.from_os_path(path)
 
             try:
-                if remote:
-                    remote_odb = _repo.cloud.get_remote_odb(name=remote)
-                    oid = fs.info(fs_path)["dvc_info"]["md5"]
-                    fs = remote_odb.fs
-                    fs_path = remote_odb.oid_to_path(oid)
-
                 with fs.open(
                     fs_path,
                     mode=mode,
diff --git a/dvc/external_repo.py b/dvc/external_repo.py
--- a/dvc/external_repo.py
+++ b/dvc/external_repo.py
@@ -43,6 +43,7 @@ def external_repo(
 
     config = _get_remote_config(url) if os.path.isdir(url) else {}
     config.update(cache_config)
+    config.update(kwargs.pop("config", None) or {})
 
     main_root = "/"
     if for_write:


Test patch:
diff --git a/tests/func/api/test_data.py b/tests/func/api/test_data.py
--- a/tests/func/api/test_data.py
+++ b/tests/func/api/test_data.py
@@ -193,10 +193,6 @@ def test_get_url_subrepos(tmp_dir, scm, local_cloud):
     assert api.get_url("subrepo/bar") == expected_url
 
 
-@pytest.mark.xfail(
-    raises=PathMissingError,
-    reason="https://github.com/iterative/dvc/issues/7341",
-)
 def test_open_from_remote(tmp_dir, erepo_dir, cloud, local_cloud):
     erepo_dir.add_remote(config=cloud.config, name="other")
     erepo_dir.add_remote(config=local_cloud.config, default=True)