# PyDrive2: a maintained fork that makes Google Drive look like files

> PyDrive2 is the Google Drive API Python wrapper that the DVC project maintains as a fork of the original PyDrive, turning the API V2 resources into objects you can create, upload and download with a handful of methods. It reduces OAuth2 to two lines, hides pagination, adds an fsspec filesystem, and pins its crypto dependencies to work around an upstream break, which is the kind of detail that tells you what a maintained fork really does.

**iterative/PyDrive2** — Google Drive API Python wrapper library. Maintained fork of PyDrive.

- Repository: https://github.com/iterative/PyDrive2
- Website: https://docs.iterative.ai/PyDrive2
- Stars: 668 · Forks: 75
- Language: Python
- License: NOASSERTION
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/iterative-pydrive2

## A fork, and the reason it exists

PyDrive2 is a wrapper library of google-api-python-client that simplifies many common Google Drive API V2 tasks, and it is an actively maintained fork of the original PyDrive, by the authors and maintainers of the Git for Data project, DVC.

The relationship matters for evaluation. The original PyDrive was the reference implementation, and the fork inherits its API shape, so the difference is not a redesign but continued maintenance of a design. For a team already using PyDrive, PyDrive2 is a dependency swap rather than a rewrite, and the import paths change from pydrive to pydrive2.

The reason a fork exists at all is usually one of three things: the original stops moving, the dependency tree rots, or the original's scope is narrower than the fork's users. All three are visible in this repository's metadata. The dependency list pins cryptography below version 44 and pyOpenSSL between 19.1.0 and 24.2.1, with a comment linking the issue that explains the pin. That is not a stylistic choice; it is a workaround for a break in the dependency chain, and it is the sort of thing a maintained fork exists to carry.

The project metadata also shows the fork's shape. The build uses setuptools with setuptools_scm for the version, which means the version comes from the git tags rather than being written into a file. The package is on PyPI, on conda-forge, and has a CI badge, so the distribution channels are the standard ones for a library other projects depend on.

The licence is recorded in the pyproject as Apache License 2.0, though the repository's own licence field is not a standard identifier, so the LICENSE file in the tree is the authoritative text.

## OAuth2 in two lines, and the settings file beside it

The authentication story is the first thing the README demonstrates, and it is deliberately short. Download client_secrets.json from the Google API Console and OAuth2 is done in two lines, and you can customise the behaviour of OAuth2 in one settings file, settings.yaml.

```python
    from pydrive2.auth import GoogleAuth
    from pydrive2.drive import GoogleDrive

    gauth = GoogleAuth()
    gauth.LocalWebserverAuth()

    drive = GoogleDrive(gauth)
```

The LocalWebserverAuth call is what launches a local web server to complete the consent flow, which is the standard approach for a desktop or local script. The GoogleAuth object then holds the credentials, and GoogleDrive takes it and hands back the object you work with.

The settings.yaml file is the escape hatch, and it is worth understanding what it is for. The library ships a default settings file that defines where credentials live, what the OAuth scope is, and where the saved token is stored. Editing it lets you point at a different client_secrets.json, change the scope, or choose a different save location, without touching your code. That is a meaningful difference from the raw google-api-python-client, where the same settings are arguments to every call site.

One practical note about the flow: this is a local-server authorisation, so it needs a browser and a free port on the machine running the script. For a headless server, the service account path or a pre-obtained refresh token is the way to go, and the fsspec variant below shows a different credential shape for exactly that reason.

## Files as objects, and the upload path

The file management section is where the library earns its name. The API V2 surface is wrapped into classes of each resource so your program can be more object-oriented, and the README's example is short enough to be read at a glance.

```python
    file1 = drive.CreateFile({'title': 'Hello.txt'})
    file1.SetContentString('Hello')
    file1.Upload() # Files.insert()

    file1['title'] = 'HelloWorld.txt'  # Change title of the file
    file1.Upload() # Files.patch()

    content = file1.GetContentString()  # 'Hello'
    file1.SetContentString(content+' World!')  # 'Hello World!'
    file1.Upload() # Files.update()
```

The comments on the right are the important part, because they name the underlying API call for each method. CreateFile plus Upload is Files.insert, changing the title and uploading again is Files.patch, and setting new content on an existing object is Files.update. So the library is not hiding which API operation it performs, which matters when you read the Google documentation or debug a permission problem.

The same object is reused across the three operations. You create the metadata, set the content, and upload; then you mutate the same object and upload again to rename; then you set new content and upload again. The object holds the file id after the first upload, which is why the later calls target the same remote file.

Setting content comes in two flavours, SetContentString for text and SetContentFile for a local path, and downloading has the same pair, GetContentString and GetContentFile. The local file variant preserves the mime type, and the README shows that a file uploaded this way comes back with mimeType image/png for a PNG.

The line about efficiency in the README, that PyDrive2 will do it in the most efficient way, refers to this: the library picks the right API operation for the change you made, rather than always doing a full re-upload.

## Downloads, exports, and the difference between a file and a document

The download path deserves a note because Google Drive does not treat everything as a file. The README shows the ordinary case, where you create a file object from an existing id and save it locally:

```python
    file3 = drive.CreateFile({'id': file2['id']})
    print('Downloading file %s from Google Drive' % file3['title']) # 'hello.png'
    file3.GetContentFile('world.png')  # Save Drive file as a local file
```

The more interesting case is the one after it. You can download Google Docs files in an export format provided, and the example is downloading a docs document as an html file with GetContentFile given a mimetype of text/html.

That single argument is the whole difference between a binary blob and a document. A Google Doc is not a file on Drive in the ordinary sense; it is a structured document, and to get it as something you can process you export it, and the export format is chosen by the mimetype you pass. The same mechanism gives you PDF or plain text exports.

The CreateFile call with an id is also how you get a handle on a file you did not create, which is the pattern for working with files discovered through a listing or picked from a search.

So the download surface is: give the object an id, choose a destination, and optionally choose an export format. It reads as a small API, and it is the part of the library you will use most if your use case is moving data in and out of Drive rather than administering it.

## Pagination you do not have to think about

File listing is the other thing the library absorbs, and the README calls it out as a headline feature. PyDrive2 handles file listing pagination for you, and the example shows the two ways to consume a list.

```python
    # Auto-iterate through all files that matches this query
    file_list = drive.ListFile({'q': "'root' in parents"}).GetList()
    for file1 in file_list:
        print('title: {}, id: {}'.format(file1['title'], file1['id']))

    # Paginate file lists by specifying number of max results
    for file_list in drive.ListFile({'maxResults': 10}):
        print('Received {} files from Files.list()'.format(len(file_list))) # <= 10
        for file1 in file_list:
```

GetList() returns everything in one go, which is convenient and, on a large drive, a good way to exhaust memory and to blow through the API quota without noticing. The second form iterates the underlying paginated response directly, so each chunk is bounded by maxResults and you can process a drive of any size in a constant amount of memory.

The distinction is the kind of thing that separates a convenience wrapper from a careful one. The lazy form is the one you want in production, and it is one keyword away.

The q parameter is a Google Drive query string, and 'root' in parents means the files directly in the root of the drive. That query language is Google's, not PyDrive2's, and it is the most powerful part of the listing API because you can filter server-side rather than fetching everything and filtering in Python.

## The fsspec filesystem, and the thread-safety claim

PyDrive2 also provides a filesystem implementation for fsspec, a compatible GDriveFileSystem, and the README says it provides an easy way to work with your files through fsspec. The optional extra has to be installed explicitly:

```python
    from pydrive2.fs import GDriveFileSystem

    # replace `root` with ID of a drive or directory and give service account access to it
    fs = GDriveFileSystem("root", client_id=my_id, client_secret=my_secret)

    for root, dnames, fnames in fs.walk("root"):
```

The extra pulls in fsspec itself along with tqdm, funcy and appdirs, so the fsspec support does not weigh down an install that does not need it.

```bash
    $ pip install PyDrive2[fsspec]
```

The comment on the constructor is the part to read. You replace root with the ID of a drive or directory, and you give a service account access to it. That is a different credential model from the OAuth flow earlier in the README, and it makes sense: a filesystem is the kind of thing a batch job or a pipeline wants, and a service account with access to a shared drive is the natural fit for that, while the local web server flow is the natural fit for a person at a laptop.

The walk method returning roots, directory names and file names is the standard fsspec shape, which means GDriveFileSystem drops into anything built on that interface.

The other capability claim in the README is short and worth repeating exactly: all API functions are made to be thread-safe. For a library that is often used inside a data pipeline doing many transfers, that is a load-bearing property rather than a nice-to-have, and it is unusual for a wrapper of this kind to make it.

## Conclusion

Adopt PyDrive2 if you need to talk to Google Drive from Python and want the resource-object style of the original PyDrive with the fork's ongoing fixes, especially if you already use fsspec or DVC and want GDriveFileSystem alongside it. Do not adopt it for a new project without checking the API generation, because the wrapper targets Google Drive API V2, which is the older surface, and the underlying google-api-python-client dependency sets the practical ceiling. Before you commit, read the changelog, note that the last tagged release was 1.21.3 on 2024-11-29 while the last push was on 2026-08-05, and plan around the pinned cryptography and pyOpenSSL versions if your environment already manages those.

## FAQ

### What is PyDrive2 and who maintains it?

It is a wrapper library of google-api-python-client that simplifies many common Google Drive API V2 tasks, and it is an actively maintained fork of the original PyDrive, by the authors and maintainers of the Git for Data project, DVC. The homepage is docs.iterative.ai/PyDrive2.

### How do I install PyDrive2 and authenticate against Google Drive?

Install it with pip install PyDrive2, or from source with pip install git+https://github.com/iterative/PyDrive2.git#egg=PyDrive2. Download client_secrets.json from the Google API Console, then create a GoogleAuth object, call LocalWebserverAuth on it and pass the result to GoogleDrive, with behaviour customised through a settings.yaml file.

### How do I upload and download files with PyDrive2?

Create a file object with drive.CreateFile, set its content with SetContentString or SetContentFile, and call Upload. Downloading uses GetContentString or GetContentFile, and for Google Docs files you can pass a mimetype such as text/html to get the document exported in that format.

### How does PyDrive2 handle file listing pagination?

It handles pagination for you. Calling GetList() materialises every matching file, while iterating drive.ListFile directly, for example with a maxResults of 10, walks the paginated results in bounded chunks so a large drive does not have to fit in memory.

### Does PyDrive2 provide an fsspec filesystem?

Yes, it provides a compatible GDriveFileSystem, installed with the optional extra, pip install PyDrive2[fsspec]. You construct it with the ID of a drive or directory plus client_id and client_secret for a service account that has access to it, and then use it through the standard fsspec interface including walk.

## Sources

- [Issues](https://github.com/iterative/PyDrive2/issues)
- [iterative/PyDrive2 on GitHub](https://github.com/iterative/PyDrive2)
- [Project website](https://docs.iterative.ai/PyDrive2)
- [README](https://github.com/iterative/PyDrive2/blob/main/README.md)
- [Releases](https://github.com/iterative/PyDrive2/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/iterative-pydrive2
