Repository: iterative/dvc

Issue:
add support of team drives for GDrive remote
Google TeamDrives are common for sharing data in academia and education as they provide unlimited storage for free. Right now dvc supports only personal googledrive and it is not clear how to configure a remote to a folder inside of the teamdrive to which you have write access to.

I believe that teamdrive support will be very valuable for dvc (at least we use team drives a lot in our lab). Teamdrives are already supported by rclone and other gdrive libs (see https://github.com/gsuitedevs/PyDrive/issues/149 )


Discussion:
Discord context: https://discordapp.com/channels/485586884165107732/485596304961962003/661010338879111179

Resolving patch:
diff --git a/dvc/remote/gdrive.py b/dvc/remote/gdrive.py
--- a/dvc/remote/gdrive.py
+++ b/dvc/remote/gdrive.py
@@ -2,6 +2,7 @@
 import posixpath
 import logging
 import threading
+import re
 
 from funcy import retry, compose, decorator, wrap_with
 from funcy.py3 import cat
@@ -68,6 +69,16 @@ def __init__(self, repo, config):
         super().__init__(repo, config)
         self.no_traverse = False
         self.path_info = self.path_cls(config[Config.SECTION_REMOTE_URL])
+
+        bucket = re.search(
+            "{}://(.*)".format(self.scheme),
+            config[Config.SECTION_REMOTE_URL],
+            re.IGNORECASE,
+        )
+        self.bucket = (
+            bucket.group(1).split("/")[0] if bucket else self.path_info.bucket
+        )
+
         self.config = config
         self.init_drive()
 
@@ -144,7 +155,7 @@ def cache_root_dirs(self):
         cached_dirs = {}
         cached_ids = {}
         for dir1 in self.gdrive_list_item(
-            "'{}' in parents and trashed=false".format(self.root_id)
+            "'{}' in parents and trashed=false".format(self.remote_root_id)
         ):
             remote_path = posixpath.join(self.path_info.path, dir1["title"])
             cached_dirs.setdefault(remote_path, []).append(dir1["id"])
@@ -227,7 +238,9 @@ def drive(self):
 
             self._gdrive = GoogleDrive(gauth)
 
-            self.root_id = self.get_remote_id(self.path_info, create=True)
+            self.remote_root_id = self.get_remote_id(
+                self.path_info, create=True
+            )
             self._cached_dirs, self._cached_ids = self.cache_root_dirs()
 
         return self._gdrive
@@ -261,7 +274,7 @@ def get_remote_item(self, name, parents_ids):
         return next(iter(item_list), None)
 
     def resolve_remote_item_from_path(self, path_parts, create):
-        parents_ids = ["root"]
+        parents_ids = [self.bucket]
         current_path = ""
         for path_part in path_parts:
             current_path = posixpath.join(current_path, path_part)
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -84,7 +84,10 @@ def run(self):
 # google-api-python-client is internal dependency of pydrive. After merge of
 # https://github.com/gsuitedevs/PyDrive/pull/180 into pydrive's master,
 # usage of google-api-python-client can be removed from DVC.
-gdrive = ["pydrive==1.3.1", "google-api-python-client>=1.2"]
+gdrive = [
+    "pydrive @ git+https://github.com/gsuitedevs/PyDrive@master#egg=pydrive",
+    "google-api-python-client>=1.2",
+]
 s3 = ["boto3>=1.9.201"]
 azure = ["azure-storage-blob==2.1.0"]
 oss = ["oss2==2.6.1"]


Test patch:
diff --git a/tests/unit/remote/test_gdrive.py b/tests/unit/remote/test_gdrive.py
--- a/tests/unit/remote/test_gdrive.py
+++ b/tests/unit/remote/test_gdrive.py
@@ -1,5 +1,3 @@
-from unittest import TestCase
-
 import pytest
 import os
 
@@ -18,7 +16,7 @@ class Repo(object):
     tmp_dir = ""
 
 
-class TestRemoteGDrive(TestCase):
+class TestRemoteGDrive(object):
     CONFIG = {
         "url": "gdrive://root/data",
         "gdrive_client_id": "client",