We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
dvc version: show external cache and remotes being used
This would have made my life easier when investigating https://github.com/iterative/dvc/pull/4570.

Another question to ask is that `dvc version` can only have a limited amount of information. Should there be `dvc version --json`?



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `dvc/tree/__init__.py`
Content:
```
1 import posixpath
2 from urllib.parse import urlparse
3 
4 from .azure import AzureTree
5 from .gdrive import GDriveTree
6 from .gs import GSTree
7 from .hdfs import HDFSTree
8 from .http import HTTPTree
9 from .https import HTTPSTree
10 from .local import LocalTree
11 from .oss import OSSTree
12 from .s3 import S3Tree
13 from .ssh import SSHTree
14 from .webdav import WebDAVTree
15 from .webdavs import WebDAVSTree
16 from .webhdfs import WebHDFSTree
17 
18 TREES = [
19     AzureTree,
20     GDriveTree,
21     GSTree,
22     HDFSTree,
23     HTTPTree,
24     HTTPSTree,
25     S3Tree,
26     SSHTree,
27     OSSTree,
28     WebDAVTree,
29     WebDAVSTree,
30     WebHDFSTree
31     # NOTE: LocalTree is the default
32 ]
33 
34 
35 def _get_tree(remote_conf):
36     for tree_cls in TREES:
37         if tree_cls.supported(remote_conf):
38             return tree_cls
39     return LocalTree
40 
41 
42 def _get_conf(repo, **kwargs):
43     name = kwargs.get("name")
44     if name:
45         remote_conf = repo.config["remote"][name.lower()]
46     else:
47         remote_conf = kwargs
48     return _resolve_remote_refs(repo, remote_conf)
49 
50 
51 def _resolve_remote_refs(repo, remote_conf):
52     # Support for cross referenced remotes.
53     # This will merge the settings, shadowing base ref with remote_conf.
54     # For example, having:
55     #
56     #       dvc remote add server ssh://localhost
57     #       dvc remote modify server user root
58     #       dvc remote modify server ask_password true
59     #
60     #       dvc remote add images remote://server/tmp/pictures
61     #       dvc remote modify images user alice
62     #       dvc remote modify images ask_password false
63     #       dvc remote modify images password asdf1234
64     #
65     # Results on a config dictionary like:
66     #
67     #       {
68     #           "url": "ssh://localhost/tmp/pictures",
69     #           "user": "alice",
70     #           "password": "asdf1234",
71     #           "ask_password": False,
72     #       }
73     parsed = urlparse(remote_conf["url"])
74     if parsed.scheme != "remote":
75         return remote_conf
76 
77     base = _get_conf(repo, name=parsed.netloc)
78     url = posixpath.join(base["url"], parsed.path.lstrip("/"))
79     return {**base, **remote_conf, "url": url}
80 
81 
82 def get_cloud_tree(repo, **kwargs):
83     from dvc.config import SCHEMA, ConfigError, Invalid
84 
85     remote_conf = _get_conf(repo, **kwargs)
86     try:
87         remote_conf = SCHEMA["remote"][str](remote_conf)
88     except Invalid as exc:
89         raise ConfigError(str(exc)) from None
90     return _get_tree(remote_conf)(repo, remote_conf)
91
```

Path: `dvc/info.py`
Content:
```
1 import itertools
2 import os
3 import pathlib
4 import platform
5 import uuid
6 
7 from dvc.exceptions import DvcException, NotDvcRepoError
8 from dvc.repo import Repo
9 from dvc.scm.base import SCMError
10 from dvc.system import System
11 from dvc.tree import TREES
12 from dvc.utils import error_link
13 from dvc.utils.pkg import PKG
14 from dvc.version import __version__
15 
16 try:
17     import psutil
18 except ImportError:
19     psutil = None
20 
21 if PKG is None:
22     package = ""
23 else:
24     package = f"({PKG})"
25 
26 
27 def get_dvc_info():
28     info = [
29         f"DVC version: {__version__} {package}",
30         "---------------------------------",
31         f"Platform: Python {platform.python_version()} on "
32         f"{platform.platform()}",
33         f"Supports: {_get_supported_remotes()}",
34     ]
35 
36     try:
37         repo = Repo()
38 
39         # cache_dir might not exist yet (e.g. after `dvc init`), and we
40         # can't auto-create it, as it might cause issues if the user
41         # later decides to enable shared cache mode with
42         # `dvc config cache.shared group`.
43         if os.path.exists(repo.cache.local.cache_dir):
44             info.append(
45                 "Cache types: {}".format(_get_linktype_support_info(repo))
46             )
47             if psutil:
48                 fs_type = get_fs_type(repo.cache.local.cache_dir)
49                 info.append(f"Cache directory: {fs_type}")
50         else:
51             info.append("Cache types: " + error_link("no-dvc-cache"))
52 
53     except NotDvcRepoError:
54         pass
55     except SCMError:
56         info.append("Repo: dvc, git (broken)")
57     else:
58         root_directory = repo.root_dir
59         if psutil:
60             fs_root = get_fs_type(os.path.abspath(root_directory))
61             info.append(f"Workspace directory: {fs_root}")
62         info.append("Repo: {}".format(_get_dvc_repo_info(repo)))
63     return "\n".join(info)
64 
65 
66 def _get_linktype_support_info(repo):
67 
68     links = {
69         "reflink": (System.reflink, None),
70         "hardlink": (System.hardlink, System.is_hardlink),
71         "symlink": (System.symlink, System.is_symlink),
72     }
73 
74     fname = "." + str(uuid.uuid4())
75     src = os.path.join(repo.cache.local.cache_dir, fname)
76     open(src, "w").close()
77     dst = os.path.join(repo.root_dir, fname)
78 
79     cache = []
80 
81     for name, (link, is_link) in links.items():
82         try:
83             link(src, dst)
84             status = "supported"
85             if is_link and not is_link(dst):
86                 status = "broken"
87             os.unlink(dst)
88         except DvcException:
89             status = "not supported"
90 
91         if status == "supported":
92             cache.append(name)
93     os.remove(src)
94 
95     return ", ".join(cache)
96 
97 
98 def _get_supported_remotes():
99 
100     supported_remotes = []
101     for tree_cls in TREES:
102         if not tree_cls.get_missing_deps():
103             supported_remotes.append(tree_cls.scheme)
104 
105     if len(supported_remotes) == len(TREES):
106         return "All remotes"
107 
108     if len(supported_remotes) == 1:
109         return supported_remotes
110 
111     return ", ".join(supported_remotes)
112 
113 
114 def get_fs_type(path):
115 
116     partition = {
117         pathlib.Path(part.mountpoint): (part.fstype + " on " + part.device)
118         for part in psutil.disk_partitions(all=True)
119     }
120 
121     path = pathlib.Path(path)
122 
123     for parent in itertools.chain([path], path.parents):
124         if parent in partition:
125             return partition[parent]
126     return ("unknown", "none")
127 
128 
129 def _get_dvc_repo_info(self):
130     if self.config.get("core", {}).get("no_scm", False):
131         return "dvc (no_scm)"
132 
133     if self.root_dir != self.scm.root_dir:
134         return "dvc (subdir), git"
135 
136     return "dvc, git"
137
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/dvc/info.py b/dvc/info.py
--- a/dvc/info.py
+++ b/dvc/info.py
@@ -8,7 +8,7 @@
 from dvc.repo import Repo
 from dvc.scm.base import SCMError
 from dvc.system import System
-from dvc.tree import TREES
+from dvc.tree import TREES, get_tree_cls, get_tree_config
 from dvc.utils import error_link
 from dvc.utils.pkg import PKG
 from dvc.version import __version__
@@ -50,6 +50,10 @@
         else:
             info.append("Cache types: " + error_link("no-dvc-cache"))
 
+        info.append(f"Caches: {_get_caches(repo.cache)}")
+
+        info.append(f"Remotes: {_get_remotes(repo.config)}")
+
     except NotDvcRepoError:
         pass
     except SCMError:
@@ -63,6 +67,26 @@
     return "\n".join(info)
 
 
+def _get_caches(cache):
+    caches = (
+        cache_type
+        for cache_type, cache_instance in cache.by_scheme()
+        if cache_instance
+    )
+
+    # Caches will be always non-empty including the local cache
+    return ", ".join(caches)
+
+
+def _get_remotes(config):
+    schemes = (
+        get_tree_cls(get_tree_config(config, name=remote)).scheme
+        for remote in config["remote"]
+    )
+
+    return ", ".join(schemes) or "None"
+
+
 def _get_linktype_support_info(repo):
 
     links = {
diff --git a/dvc/tree/__init__.py b/dvc/tree/__init__.py
--- a/dvc/tree/__init__.py
+++ b/dvc/tree/__init__.py
@@ -32,23 +32,23 @@
 ]
 
 
-def _get_tree(remote_conf):
+def get_tree_cls(remote_conf):
     for tree_cls in TREES:
         if tree_cls.supported(remote_conf):
             return tree_cls
     return LocalTree
 
 
-def _get_conf(repo, **kwargs):
+def get_tree_config(config, **kwargs):
     name = kwargs.get("name")
     if name:
-        remote_conf = repo.config["remote"][name.lower()]
+        remote_conf = config["remote"][name.lower()]
     else:
         remote_conf = kwargs
-    return _resolve_remote_refs(repo, remote_conf)
+    return _resolve_remote_refs(config, remote_conf)
 
 
-def _resolve_remote_refs(repo, remote_conf):
+def _resolve_remote_refs(config, remote_conf):
     # Support for cross referenced remotes.
     # This will merge the settings, shadowing base ref with remote_conf.
     # For example, having:
@@ -74,7 +74,7 @@
     if parsed.scheme != "remote":
         return remote_conf
 
-    base = _get_conf(repo, name=parsed.netloc)
+    base = get_tree_config(config, name=parsed.netloc)
     url = posixpath.join(base["url"], parsed.path.lstrip("/"))
     return {**base, **remote_conf, "url": url}
 
@@ -82,9 +82,9 @@
 def get_cloud_tree(repo, **kwargs):
     from dvc.config import SCHEMA, ConfigError, Invalid
 
-    remote_conf = _get_conf(repo, **kwargs)
+    remote_conf = get_tree_config(repo.config, **kwargs)
     try:
         remote_conf = SCHEMA["remote"][str](remote_conf)
     except Invalid as exc:
         raise ConfigError(str(exc)) from None
-    return _get_tree(remote_conf)(repo, remote_conf)
+    return get_tree_cls(remote_conf)(repo, remote_conf)

{"golden_diff": "diff --git a/dvc/info.py b/dvc/info.py\n--- a/dvc/info.py\n+++ b/dvc/info.py\n@@ -8,7 +8,7 @@\n from dvc.repo import Repo\n from dvc.scm.base import SCMError\n from dvc.system import System\n-from dvc.tree import TREES\n+from dvc.tree import TREES, get_tree_cls, get_tree_config\n from dvc.utils import error_link\n from dvc.utils.pkg import PKG\n from dvc.version import __version__\n@@ -50,6 +50,10 @@\n         else:\n             info.append(\"Cache types: \" + error_link(\"no-dvc-cache\"))\n \n+        info.append(f\"Caches: {_get_caches(repo.cache)}\")\n+\n+        info.append(f\"Remotes: {_get_remotes(repo.config)}\")\n+\n     except NotDvcRepoError:\n         pass\n     except SCMError:\n@@ -63,6 +67,26 @@\n     return \"\\n\".join(info)\n \n \n+def _get_caches(cache):\n+    caches = (\n+        cache_type\n+        for cache_type, cache_instance in cache.by_scheme()\n+        if cache_instance\n+    )\n+\n+    # Caches will be always non-empty including the local cache\n+    return \", \".join(caches)\n+\n+\n+def _get_remotes(config):\n+    schemes = (\n+        get_tree_cls(get_tree_config(config, name=remote)).scheme\n+        for remote in config[\"remote\"]\n+    )\n+\n+    return \", \".join(schemes) or \"None\"\n+\n+\n def _get_linktype_support_info(repo):\n \n     links = {\ndiff --git a/dvc/tree/__init__.py b/dvc/tree/__init__.py\n--- a/dvc/tree/__init__.py\n+++ b/dvc/tree/__init__.py\n@@ -32,23 +32,23 @@\n ]\n \n \n-def _get_tree(remote_conf):\n+def get_tree_cls(remote_conf):\n     for tree_cls in TREES:\n         if tree_cls.supported(remote_conf):\n             return tree_cls\n     return LocalTree\n \n \n-def _get_conf(repo, **kwargs):\n+def get_tree_config(config, **kwargs):\n     name = kwargs.get(\"name\")\n     if name:\n-        remote_conf = repo.config[\"remote\"][name.lower()]\n+        remote_conf = config[\"remote\"][name.lower()]\n     else:\n         remote_conf = kwargs\n-    return _resolve_remote_refs(repo, remote_conf)\n+    return _resolve_remote_refs(config, remote_conf)\n \n \n-def _resolve_remote_refs(repo, remote_conf):\n+def _resolve_remote_refs(config, remote_conf):\n     # Support for cross referenced remotes.\n     # This will merge the settings, shadowing base ref with remote_conf.\n     # For example, having:\n@@ -74,7 +74,7 @@\n     if parsed.scheme != \"remote\":\n         return remote_conf\n \n-    base = _get_conf(repo, name=parsed.netloc)\n+    base = get_tree_config(config, name=parsed.netloc)\n     url = posixpath.join(base[\"url\"], parsed.path.lstrip(\"/\"))\n     return {**base, **remote_conf, \"url\": url}\n \n@@ -82,9 +82,9 @@\n def get_cloud_tree(repo, **kwargs):\n     from dvc.config import SCHEMA, ConfigError, Invalid\n \n-    remote_conf = _get_conf(repo, **kwargs)\n+    remote_conf = get_tree_config(repo.config, **kwargs)\n     try:\n         remote_conf = SCHEMA[\"remote\"][str](remote_conf)\n     except Invalid as exc:\n         raise ConfigError(str(exc)) from None\n-    return _get_tree(remote_conf)(repo, remote_conf)\n+    return get_tree_cls(remote_conf)(repo, remote_conf)\n", "issue": "dvc version: show external cache and remotes being used\nThis would have made my life easier when investigating https://github.com/iterative/dvc/pull/4570.\r\n\r\nAnother question to ask is that `dvc version` can only have a limited amount of information. Should there be `dvc version --json`?\r\n\r\n\n", "before_files": [{"content": "import posixpath\nfrom urllib.parse import urlparse\n\nfrom .azure import AzureTree\nfrom .gdrive import GDriveTree\nfrom .gs import GSTree\nfrom .hdfs import HDFSTree\nfrom .http import HTTPTree\nfrom .https import HTTPSTree\nfrom .local import LocalTree\nfrom .oss import OSSTree\nfrom .s3 import S3Tree\nfrom .ssh import SSHTree\nfrom .webdav import WebDAVTree\nfrom .webdavs import WebDAVSTree\nfrom .webhdfs import WebHDFSTree\n\nTREES = [\n    AzureTree,\n    GDriveTree,\n    GSTree,\n    HDFSTree,\n    HTTPTree,\n    HTTPSTree,\n    S3Tree,\n    SSHTree,\n    OSSTree,\n    WebDAVTree,\n    WebDAVSTree,\n    WebHDFSTree\n    # NOTE: LocalTree is the default\n]\n\n\ndef _get_tree(remote_conf):\n    for tree_cls in TREES:\n        if tree_cls.supported(remote_conf):\n            return tree_cls\n    return LocalTree\n\n\ndef _get_conf(repo, **kwargs):\n    name = kwargs.get(\"name\")\n    if name:\n        remote_conf = repo.config[\"remote\"][name.lower()]\n    else:\n        remote_conf = kwargs\n    return _resolve_remote_refs(repo, remote_conf)\n\n\ndef _resolve_remote_refs(repo, remote_conf):\n    # Support for cross referenced remotes.\n    # This will merge the settings, shadowing base ref with remote_conf.\n    # For example, having:\n    #\n    #       dvc remote add server ssh://localhost\n    #       dvc remote modify server user root\n    #       dvc remote modify server ask_password true\n    #\n    #       dvc remote add images remote://server/tmp/pictures\n    #       dvc remote modify images user alice\n    #       dvc remote modify images ask_password false\n    #       dvc remote modify images password asdf1234\n    #\n    # Results on a config dictionary like:\n    #\n    #       {\n    #           \"url\": \"ssh://localhost/tmp/pictures\",\n    #           \"user\": \"alice\",\n    #           \"password\": \"asdf1234\",\n    #           \"ask_password\": False,\n    #       }\n    parsed = urlparse(remote_conf[\"url\"])\n    if parsed.scheme != \"remote\":\n        return remote_conf\n\n    base = _get_conf(repo, name=parsed.netloc)\n    url = posixpath.join(base[\"url\"], parsed.path.lstrip(\"/\"))\n    return {**base, **remote_conf, \"url\": url}\n\n\ndef get_cloud_tree(repo, **kwargs):\n    from dvc.config import SCHEMA, ConfigError, Invalid\n\n    remote_conf = _get_conf(repo, **kwargs)\n    try:\n        remote_conf = SCHEMA[\"remote\"][str](remote_conf)\n    except Invalid as exc:\n        raise ConfigError(str(exc)) from None\n    return _get_tree(remote_conf)(repo, remote_conf)\n", "path": "dvc/tree/__init__.py"}, {"content": "import itertools\nimport os\nimport pathlib\nimport platform\nimport uuid\n\nfrom dvc.exceptions import DvcException, NotDvcRepoError\nfrom dvc.repo import Repo\nfrom dvc.scm.base import SCMError\nfrom dvc.system import System\nfrom dvc.tree import TREES\nfrom dvc.utils import error_link\nfrom dvc.utils.pkg import PKG\nfrom dvc.version import __version__\n\ntry:\n    import psutil\nexcept ImportError:\n    psutil = None\n\nif PKG is None:\n    package = \"\"\nelse:\n    package = f\"({PKG})\"\n\n\ndef get_dvc_info():\n    info = [\n        f\"DVC version: {__version__} {package}\",\n        \"---------------------------------\",\n        f\"Platform: Python {platform.python_version()} on \"\n        f\"{platform.platform()}\",\n        f\"Supports: {_get_supported_remotes()}\",\n    ]\n\n    try:\n        repo = Repo()\n\n        # cache_dir might not exist yet (e.g. after `dvc init`), and we\n        # can't auto-create it, as it might cause issues if the user\n        # later decides to enable shared cache mode with\n        # `dvc config cache.shared group`.\n        if os.path.exists(repo.cache.local.cache_dir):\n            info.append(\n                \"Cache types: {}\".format(_get_linktype_support_info(repo))\n            )\n            if psutil:\n                fs_type = get_fs_type(repo.cache.local.cache_dir)\n                info.append(f\"Cache directory: {fs_type}\")\n        else:\n            info.append(\"Cache types: \" + error_link(\"no-dvc-cache\"))\n\n    except NotDvcRepoError:\n        pass\n    except SCMError:\n        info.append(\"Repo: dvc, git (broken)\")\n    else:\n        root_directory = repo.root_dir\n        if psutil:\n            fs_root = get_fs_type(os.path.abspath(root_directory))\n            info.append(f\"Workspace directory: {fs_root}\")\n        info.append(\"Repo: {}\".format(_get_dvc_repo_info(repo)))\n    return \"\\n\".join(info)\n\n\ndef _get_linktype_support_info(repo):\n\n    links = {\n        \"reflink\": (System.reflink, None),\n        \"hardlink\": (System.hardlink, System.is_hardlink),\n        \"symlink\": (System.symlink, System.is_symlink),\n    }\n\n    fname = \".\" + str(uuid.uuid4())\n    src = os.path.join(repo.cache.local.cache_dir, fname)\n    open(src, \"w\").close()\n    dst = os.path.join(repo.root_dir, fname)\n\n    cache = []\n\n    for name, (link, is_link) in links.items():\n        try:\n            link(src, dst)\n            status = \"supported\"\n            if is_link and not is_link(dst):\n                status = \"broken\"\n            os.unlink(dst)\n        except DvcException:\n            status = \"not supported\"\n\n        if status == \"supported\":\n            cache.append(name)\n    os.remove(src)\n\n    return \", \".join(cache)\n\n\ndef _get_supported_remotes():\n\n    supported_remotes = []\n    for tree_cls in TREES:\n        if not tree_cls.get_missing_deps():\n            supported_remotes.append(tree_cls.scheme)\n\n    if len(supported_remotes) == len(TREES):\n        return \"All remotes\"\n\n    if len(supported_remotes) == 1:\n        return supported_remotes\n\n    return \", \".join(supported_remotes)\n\n\ndef get_fs_type(path):\n\n    partition = {\n        pathlib.Path(part.mountpoint): (part.fstype + \" on \" + part.device)\n        for part in psutil.disk_partitions(all=True)\n    }\n\n    path = pathlib.Path(path)\n\n    for parent in itertools.chain([path], path.parents):\n        if parent in partition:\n            return partition[parent]\n    return (\"unknown\", \"none\")\n\n\ndef _get_dvc_repo_info(self):\n    if self.config.get(\"core\", {}).get(\"no_scm\", False):\n        return \"dvc (no_scm)\"\n\n    if self.root_dir != self.scm.root_dir:\n        return \"dvc (subdir), git\"\n\n    return \"dvc, git\"\n", "path": "dvc/info.py"}], "after_files": [{"content": "import posixpath\nfrom urllib.parse import urlparse\n\nfrom .azure import AzureTree\nfrom .gdrive import GDriveTree\nfrom .gs import GSTree\nfrom .hdfs import HDFSTree\nfrom .http import HTTPTree\nfrom .https import HTTPSTree\nfrom .local import LocalTree\nfrom .oss import OSSTree\nfrom .s3 import S3Tree\nfrom .ssh import SSHTree\nfrom .webdav import WebDAVTree\nfrom .webdavs import WebDAVSTree\nfrom .webhdfs import WebHDFSTree\n\nTREES = [\n    AzureTree,\n    GDriveTree,\n    GSTree,\n    HDFSTree,\n    HTTPTree,\n    HTTPSTree,\n    S3Tree,\n    SSHTree,\n    OSSTree,\n    WebDAVTree,\n    WebDAVSTree,\n    WebHDFSTree\n    # NOTE: LocalTree is the default\n]\n\n\ndef get_tree_cls(remote_conf):\n    for tree_cls in TREES:\n        if tree_cls.supported(remote_conf):\n            return tree_cls\n    return LocalTree\n\n\ndef get_tree_config(config, **kwargs):\n    name = kwargs.get(\"name\")\n    if name:\n        remote_conf = config[\"remote\"][name.lower()]\n    else:\n        remote_conf = kwargs\n    return _resolve_remote_refs(config, remote_conf)\n\n\ndef _resolve_remote_refs(config, remote_conf):\n    # Support for cross referenced remotes.\n    # This will merge the settings, shadowing base ref with remote_conf.\n    # For example, having:\n    #\n    #       dvc remote add server ssh://localhost\n    #       dvc remote modify server user root\n    #       dvc remote modify server ask_password true\n    #\n    #       dvc remote add images remote://server/tmp/pictures\n    #       dvc remote modify images user alice\n    #       dvc remote modify images ask_password false\n    #       dvc remote modify images password asdf1234\n    #\n    # Results on a config dictionary like:\n    #\n    #       {\n    #           \"url\": \"ssh://localhost/tmp/pictures\",\n    #           \"user\": \"alice\",\n    #           \"password\": \"asdf1234\",\n    #           \"ask_password\": False,\n    #       }\n    parsed = urlparse(remote_conf[\"url\"])\n    if parsed.scheme != \"remote\":\n        return remote_conf\n\n    base = get_tree_config(config, name=parsed.netloc)\n    url = posixpath.join(base[\"url\"], parsed.path.lstrip(\"/\"))\n    return {**base, **remote_conf, \"url\": url}\n\n\ndef get_cloud_tree(repo, **kwargs):\n    from dvc.config import SCHEMA, ConfigError, Invalid\n\n    remote_conf = get_tree_config(repo.config, **kwargs)\n    try:\n        remote_conf = SCHEMA[\"remote\"][str](remote_conf)\n    except Invalid as exc:\n        raise ConfigError(str(exc)) from None\n    return get_tree_cls(remote_conf)(repo, remote_conf)\n", "path": "dvc/tree/__init__.py"}, {"content": "import itertools\nimport os\nimport pathlib\nimport platform\nimport uuid\n\nfrom dvc.exceptions import DvcException, NotDvcRepoError\nfrom dvc.repo import Repo\nfrom dvc.scm.base import SCMError\nfrom dvc.system import System\nfrom dvc.tree import TREES, get_tree_cls, get_tree_config\nfrom dvc.utils import error_link\nfrom dvc.utils.pkg import PKG\nfrom dvc.version import __version__\n\ntry:\n    import psutil\nexcept ImportError:\n    psutil = None\n\nif PKG is None:\n    package = \"\"\nelse:\n    package = f\"({PKG})\"\n\n\ndef get_dvc_info():\n    info = [\n        f\"DVC version: {__version__} {package}\",\n        \"---------------------------------\",\n        f\"Platform: Python {platform.python_version()} on \"\n        f\"{platform.platform()}\",\n        f\"Supports: {_get_supported_remotes()}\",\n    ]\n\n    try:\n        repo = Repo()\n\n        # cache_dir might not exist yet (e.g. after `dvc init`), and we\n        # can't auto-create it, as it might cause issues if the user\n        # later decides to enable shared cache mode with\n        # `dvc config cache.shared group`.\n        if os.path.exists(repo.cache.local.cache_dir):\n            info.append(\n                \"Cache types: {}\".format(_get_linktype_support_info(repo))\n            )\n            if psutil:\n                fs_type = get_fs_type(repo.cache.local.cache_dir)\n                info.append(f\"Cache directory: {fs_type}\")\n        else:\n            info.append(\"Cache types: \" + error_link(\"no-dvc-cache\"))\n\n        info.append(f\"Caches: {_get_caches(repo.cache)}\")\n\n        info.append(f\"Remotes: {_get_remotes(repo.config)}\")\n\n    except NotDvcRepoError:\n        pass\n    except SCMError:\n        info.append(\"Repo: dvc, git (broken)\")\n    else:\n        root_directory = repo.root_dir\n        if psutil:\n            fs_root = get_fs_type(os.path.abspath(root_directory))\n            info.append(f\"Workspace directory: {fs_root}\")\n        info.append(\"Repo: {}\".format(_get_dvc_repo_info(repo)))\n    return \"\\n\".join(info)\n\n\ndef _get_caches(cache):\n    caches = (\n        cache_type\n        for cache_type, cache_instance in cache.by_scheme()\n        if cache_instance\n    )\n\n    # Caches will be always non-empty including the local cache\n    return \", \".join(caches)\n\n\ndef _get_remotes(config):\n    schemes = (\n        get_tree_cls(get_tree_config(config, name=remote)).scheme\n        for remote in config[\"remote\"]\n    )\n\n    return \", \".join(schemes) or \"None\"\n\n\ndef _get_linktype_support_info(repo):\n\n    links = {\n        \"reflink\": (System.reflink, None),\n        \"hardlink\": (System.hardlink, System.is_hardlink),\n        \"symlink\": (System.symlink, System.is_symlink),\n    }\n\n    fname = \".\" + str(uuid.uuid4())\n    src = os.path.join(repo.cache.local.cache_dir, fname)\n    open(src, \"w\").close()\n    dst = os.path.join(repo.root_dir, fname)\n\n    cache = []\n\n    for name, (link, is_link) in links.items():\n        try:\n            link(src, dst)\n            status = \"supported\"\n            if is_link and not is_link(dst):\n                status = \"broken\"\n            os.unlink(dst)\n        except DvcException:\n            status = \"not supported\"\n\n        if status == \"supported\":\n            cache.append(name)\n    os.remove(src)\n\n    return \", \".join(cache)\n\n\ndef _get_supported_remotes():\n\n    supported_remotes = []\n    for tree_cls in TREES:\n        if not tree_cls.get_missing_deps():\n            supported_remotes.append(tree_cls.scheme)\n\n    if len(supported_remotes) == len(TREES):\n        return \"All remotes\"\n\n    if len(supported_remotes) == 1:\n        return supported_remotes\n\n    return \", \".join(supported_remotes)\n\n\ndef get_fs_type(path):\n\n    partition = {\n        pathlib.Path(part.mountpoint): (part.fstype + \" on \" + part.device)\n        for part in psutil.disk_partitions(all=True)\n    }\n\n    path = pathlib.Path(path)\n\n    for parent in itertools.chain([path], path.parents):\n        if parent in partition:\n            return partition[parent]\n    return (\"unknown\", \"none\")\n\n\ndef _get_dvc_repo_info(self):\n    if self.config.get(\"core\", {}).get(\"no_scm\", False):\n        return \"dvc (no_scm)\"\n\n    if self.root_dir != self.scm.root_dir:\n        return \"dvc (subdir), git\"\n\n    return \"dvc, git\"\n", "path": "dvc/info.py"}]}