├── .gitignore ├── CHANGES ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── README.rst ├── docs ├── README ├── build │ ├── doctrees │ │ ├── filelist.doctree │ │ ├── filemanagement.doctree │ │ ├── index.doctree │ │ ├── oauth.doctree │ │ ├── pydrive.doctree │ │ └── quickstart.doctree │ └── html │ │ ├── .buildinfo │ │ ├── _sources │ │ ├── filelist.txt │ │ ├── filemanagement.txt │ │ ├── index.txt │ │ ├── oauth.txt │ │ ├── pydrive.txt │ │ └── quickstart.txt │ │ ├── _static │ │ ├── ajax-loader.gif │ │ ├── basic.css │ │ ├── comment-bright.png │ │ ├── comment-close.png │ │ ├── comment.png │ │ ├── css │ │ │ ├── badge_only.css │ │ │ └── theme.css │ │ ├── doctools.js │ │ ├── down-pressed.png │ │ ├── down.png │ │ ├── file.png │ │ ├── fonts │ │ │ ├── Inconsolata-Bold.ttf │ │ │ ├── Inconsolata-Regular.ttf │ │ │ ├── Lato-Bold.ttf │ │ │ ├── Lato-Regular.ttf │ │ │ ├── RobotoSlab-Bold.ttf │ │ │ ├── RobotoSlab-Regular.ttf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ └── fontawesome-webfont.woff │ │ ├── jquery-1.11.1.js │ │ ├── jquery.js │ │ ├── js │ │ │ ├── modernizr.min.js │ │ │ └── theme.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── underscore-1.3.1.js │ │ ├── underscore.js │ │ ├── up-pressed.png │ │ ├── up.png │ │ └── websupport.js │ │ ├── filelist.html │ │ ├── filemanagement.html │ │ ├── genindex.html │ │ ├── index.html │ │ ├── oauth.html │ │ ├── objects.inv │ │ ├── py-modindex.html │ │ ├── pydrive.html │ │ ├── quickstart.html │ │ ├── search.html │ │ └── searchindex.js ├── conf.py ├── filelist.rst ├── filemanagement.rst ├── index.rst ├── oauth.rst ├── pydrive.rst └── quickstart.rst ├── examples ├── strip_bom_example.py └── using_folders.py ├── pydrive ├── __init__.py ├── apiattr.py ├── auth.py ├── drive.py ├── files.py ├── settings.py └── test │ ├── README.rst │ ├── __init__.py │ ├── settings │ ├── default.yaml │ ├── test1.yaml │ ├── test2.yaml │ ├── test3.yaml │ ├── test4.yaml │ ├── test5.yaml │ └── test6.yaml │ ├── test_apiattr.py │ ├── test_drive.py │ ├── test_file.py │ ├── test_filelist.py │ ├── test_oauth.py │ └── test_util.py ├── setup.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *~ 3 | pydrive/test/configs/* 4 | pydrive/test/credentials/* 5 | 6 | *.egg-info 7 | dist 8 | build/ 9 | .cache 10 | 11 | client_secrets.json 12 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | v1.0.0, Aug 16, 2013 -- Initial release. 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Contributing guidelines 2 | ======================= 3 | 4 | How to become a contributor and submit your own code 5 | ---------------------------------------------------- 6 | 7 | Contributor License Agreements 8 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 9 | 10 | We'd love to accept your patches! Before we can take them, we have to jump a couple of legal hurdles. 11 | 12 | Please fill out either the individual or corporate Contributor License Agreement (CLA). 13 | 14 | * If you are an individual writing original source code and you're sure you 15 | own the intellectual property, then you'll need to sign an `individual CLA `_. 16 | * If you work for a company that wants to allow you to contribute your 17 | work, then you'll need to sign a `corporate CLA `_. 18 | 19 | Follow either of the two links above to access the appropriate CLA and instructions for how to sign and return it. Once we receive it, we'll be able to accept your pull requests. 20 | 21 | ***NOTE***: Only original source code from you and other people that have signed the CLA can be accepted into the main repository. 22 | 23 | Contributing code 24 | ~~~~~~~~~~~~~~~~~ 25 | 26 | If you have improvements to PyDrive, send us your pull requests! For those 27 | just getting started, Github has a `howto `_. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | 6 | Unless required by applicable law or agreed to in writing, software 7 | distributed under the License is distributed on an "AS IS" BASIS, 8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | See the License for the specific language governing permissions and 10 | limitations under the License. 11 | 12 | Apache License 13 | Version 2.0, January 2004 14 | http://www.apache.org/licenses/ 15 | 16 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 17 | 18 | 1. Definitions. 19 | 20 | "License" shall mean the terms and conditions for use, reproduction, 21 | and distribution as defined by Sections 1 through 9 of this document. 22 | 23 | "Licensor" shall mean the copyright owner or entity authorized by 24 | the copyright owner that is granting the License. 25 | 26 | "Legal Entity" shall mean the union of the acting entity and all 27 | other entities that control, are controlled by, or are under common 28 | control with that entity. For the purposes of this definition, 29 | "control" means (i) the power, direct or indirect, to cause the 30 | direction or management of such entity, whether by contract or 31 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 32 | outstanding shares, or (iii) beneficial ownership of such entity. 33 | 34 | "You" (or "Your") shall mean an individual or Legal Entity 35 | exercising permissions granted by this License. 36 | 37 | "Source" form shall mean the preferred form for making modifications, 38 | including but not limited to software source code, documentation 39 | source, and configuration files. 40 | 41 | "Object" form shall mean any form resulting from mechanical 42 | transformation or translation of a Source form, including but 43 | not limited to compiled object code, generated documentation, 44 | and conversions to other media types. 45 | 46 | "Work" shall mean the work of authorship, whether in Source or 47 | Object form, made available under the License, as indicated by a 48 | copyright notice that is included in or attached to the work 49 | (an example is provided in the Appendix below). 50 | 51 | "Derivative Works" shall mean any work, whether in Source or Object 52 | form, that is based on (or derived from) the Work and for which the 53 | editorial revisions, annotations, elaborations, or other modifications 54 | represent, as a whole, an original work of authorship. For the purposes 55 | of this License, Derivative Works shall not include works that remain 56 | separable from, or merely link (or bind by name) to the interfaces of, 57 | the Work and Derivative Works thereof. 58 | 59 | "Contribution" shall mean any work of authorship, including 60 | the original version of the Work and any modifications or additions 61 | to that Work or Derivative Works thereof, that is intentionally 62 | submitted to Licensor for inclusion in the Work by the copyright owner 63 | or by an individual or Legal Entity authorized to submit on behalf of 64 | the copyright owner. For the purposes of this definition, "submitted" 65 | means any form of electronic, verbal, or written communication sent 66 | to the Licensor or its representatives, including but not limited to 67 | communication on electronic mailing lists, source code control systems, 68 | and issue tracking systems that are managed by, or on behalf of, the 69 | Licensor for the purpose of discussing and improving the Work, but 70 | excluding communication that is conspicuously marked or otherwise 71 | designated in writing by the copyright owner as "Not a Contribution." 72 | 73 | "Contributor" shall mean Licensor and any individual or Legal Entity 74 | on behalf of whom a Contribution has been received by Licensor and 75 | subsequently incorporated within the Work. 76 | 77 | 2. Grant of Copyright License. Subject to the terms and conditions of 78 | this License, each Contributor hereby grants to You a perpetual, 79 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 80 | copyright license to reproduce, prepare Derivative Works of, 81 | publicly display, publicly perform, sublicense, and distribute the 82 | Work and such Derivative Works in Source or Object form. 83 | 84 | 3. Grant of Patent License. Subject to the terms and conditions of 85 | this License, each Contributor hereby grants to You a perpetual, 86 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 87 | (except as stated in this section) patent license to make, have made, 88 | use, offer to sell, sell, import, and otherwise transfer the Work, 89 | where such license applies only to those patent claims licensable 90 | by such Contributor that are necessarily infringed by their 91 | Contribution(s) alone or by combination of their Contribution(s) 92 | with the Work to which such Contribution(s) was submitted. If You 93 | institute patent litigation against any entity (including a 94 | cross-claim or counterclaim in a lawsuit) alleging that the Work 95 | or a Contribution incorporated within the Work constitutes direct 96 | or contributory patent infringement, then any patent licenses 97 | granted to You under this License for that Work shall terminate 98 | as of the date such litigation is filed. 99 | 100 | 4. Redistribution. You may reproduce and distribute copies of the 101 | Work or Derivative Works thereof in any medium, with or without 102 | modifications, and in Source or Object form, provided that You 103 | meet the following conditions: 104 | 105 | (a) You must give any other recipients of the Work or 106 | Derivative Works a copy of this License; and 107 | 108 | (b) You must cause any modified files to carry prominent notices 109 | stating that You changed the files; and 110 | 111 | (c) You must retain, in the Source form of any Derivative Works 112 | that You distribute, all copyright, patent, trademark, and 113 | attribution notices from the Source form of the Work, 114 | excluding those notices that do not pertain to any part of 115 | the Derivative Works; and 116 | 117 | (d) If the Work includes a "NOTICE" text file as part of its 118 | distribution, then any Derivative Works that You distribute must 119 | include a readable copy of the attribution notices contained 120 | within such NOTICE file, excluding those notices that do not 121 | pertain to any part of the Derivative Works, in at least one 122 | of the following places: within a NOTICE text file distributed 123 | as part of the Derivative Works; within the Source form or 124 | documentation, if provided along with the Derivative Works; or, 125 | within a display generated by the Derivative Works, if and 126 | wherever such third-party notices normally appear. The contents 127 | of the NOTICE file are for informational purposes only and 128 | do not modify the License. You may add Your own attribution 129 | notices within Derivative Works that You distribute, alongside 130 | or as an addendum to the NOTICE text from the Work, provided 131 | that such additional attribution notices cannot be construed 132 | as modifying the License. 133 | 134 | You may add Your own copyright statement to Your modifications and 135 | may provide additional or different license terms and conditions 136 | for use, reproduction, or distribution of Your modifications, or 137 | for any such Derivative Works as a whole, provided Your use, 138 | reproduction, and distribution of the Work otherwise complies with 139 | the conditions stated in this License. 140 | 141 | 5. Submission of Contributions. Unless You explicitly state otherwise, 142 | any Contribution intentionally submitted for inclusion in the Work 143 | by You to the Licensor shall be under the terms and conditions of 144 | this License, without any additional terms or conditions. 145 | Notwithstanding the above, nothing herein shall supersede or modify 146 | the terms of any separate license agreement you may have executed 147 | with Licensor regarding such Contributions. 148 | 149 | 6. Trademarks. This License does not grant permission to use the trade 150 | names, trademarks, service marks, or product names of the Licensor, 151 | except as required for reasonable and customary use in describing the 152 | origin of the Work and reproducing the content of the NOTICE file. 153 | 154 | 7. Disclaimer of Warranty. Unless required by applicable law or 155 | agreed to in writing, Licensor provides the Work (and each 156 | Contributor provides its Contributions) on an "AS IS" BASIS, 157 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 158 | implied, including, without limitation, any warranties or conditions 159 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 160 | PARTICULAR PURPOSE. You are solely responsible for determining the 161 | appropriateness of using or redistributing the Work and assume any 162 | risks associated with Your exercise of permissions under this License. 163 | 164 | 8. Limitation of Liability. In no event and under no legal theory, 165 | whether in tort (including negligence), contract, or otherwise, 166 | unless required by applicable law (such as deliberate and grossly 167 | negligent acts) or agreed to in writing, shall any Contributor be 168 | liable to You for damages, including any direct, indirect, special, 169 | incidental, or consequential damages of any character arising as a 170 | result of this License or out of the use or inability to use the 171 | Work (including but not limited to damages for loss of goodwill, 172 | work stoppage, computer failure or malfunction, or any and all 173 | other commercial damages or losses), even if such Contributor 174 | has been advised of the possibility of such damages. 175 | 176 | 9. Accepting Warranty or Additional Liability. While redistributing 177 | the Work or Derivative Works thereof, You may choose to offer, 178 | and charge a fee for, acceptance of support, warranty, indemnity, 179 | or other liability obligations and/or rights consistent with this 180 | License. However, in accepting such obligations, You may act only 181 | on Your own behalf and on Your sole responsibility, not on behalf 182 | of any other Contributor, and only if You agree to indemnify, 183 | defend, and hold each Contributor harmless for any liability 184 | incurred by, or claims asserted against, such Contributor by reason 185 | of your accepting any such warranty or additional liability. 186 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS 2 | include CHANGES 3 | include LICENSE 4 | include MANIFEST.in 5 | include README.rst 6 | recursive-include docs * 7 | recursive-include pydrive/test * 8 | recursive-exclude * *.py[co] 9 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Deprecated 2 | ---------- 3 | 4 | This project is deprecated and no longer maintained. No further changes will be made. 5 | 6 | In one of the PyDrive `issues `_, we learned about the `PyDrive2 `_ fork of PyDrive. Forks are permitted under PyDrive's `license `_, and we hope that such forks will be useful for the needs of PyDrive users. The PyDrive team makes no endorsement or support promises of any particular fork, but we're excited to see the open source license being a vehicle for new project development. 7 | 8 | 9 | PyDrive 10 | ------- 11 | 12 | *PyDrive* is a wrapper library of 13 | `google-api-python-client `_ 14 | that simplifies many common Google Drive API tasks. 15 | 16 | Project Info 17 | ------------ 18 | 19 | - Homepage: `https://pypi.python.org/pypi/PyDrive `_ 20 | - Documentation: `Official documentation on GitHub pages `_ 21 | - GitHub: `https://github.com/googleworkspace/PyDrive `_ 22 | 23 | Features of PyDrive 24 | ------------------- 25 | 26 | - Simplifies OAuth2.0 into just few lines with flexible settings. 27 | - Wraps `Google Drive API `_ into 28 | classes of each resource to make your program more object-oriented. 29 | - Helps common operations else than API calls, such as content fetching 30 | and pagination control. 31 | 32 | How to install 33 | -------------- 34 | 35 | You can install PyDrive with regular ``pip`` command. 36 | 37 | :: 38 | 39 | $ pip install PyDrive 40 | 41 | To install the current development version from GitHub, use: 42 | 43 | :: 44 | 45 | $ pip install git+https://github.com/googleworkspace/PyDrive.git#egg=PyDrive 46 | 47 | OAuth made easy 48 | --------------- 49 | 50 | Download *client\_secrets.json* from Google API Console and OAuth2.0 is 51 | done in two lines. You can customize behavior of OAuth2 in one settings 52 | file *settings.yaml*. 53 | 54 | .. code:: python 55 | 56 | 57 | from pydrive.auth import GoogleAuth 58 | from pydrive.drive import GoogleDrive 59 | 60 | gauth = GoogleAuth() 61 | gauth.LocalWebserverAuth() 62 | 63 | drive = GoogleDrive(gauth) 64 | 65 | File management made easy 66 | ------------------------- 67 | 68 | Upload/update the file with one method. PyDrive will do it in the most 69 | efficient way. 70 | 71 | .. code:: python 72 | 73 | file1 = drive.CreateFile({'title': 'Hello.txt'}) 74 | file1.SetContentString('Hello') 75 | file1.Upload() # Files.insert() 76 | 77 | file1['title'] = 'HelloWorld.txt' # Change title of the file 78 | file1.Upload() # Files.patch() 79 | 80 | content = file1.GetContentString() # 'Hello' 81 | file1.SetContentString(content+' World!') # 'Hello World!' 82 | file1.Upload() # Files.update() 83 | 84 | file2 = drive.CreateFile() 85 | file2.SetContentFile('hello.png') 86 | file2.Upload() 87 | print('Created file %s with mimeType %s' % (file2['title'], 88 | file2['mimeType'])) 89 | # Created file hello.png with mimeType image/png 90 | 91 | file3 = drive.CreateFile({'id': file2['id']}) 92 | print('Downloading file %s from Google Drive' % file3['title']) # 'hello.png' 93 | file3.GetContentFile('world.png') # Save Drive file as a local file 94 | 95 | # or download Google Docs files in an export format provided. 96 | # downloading a docs document as an html file: 97 | docsfile.GetContentFile('test.html', mimetype='text/html') 98 | 99 | File listing pagination made easy 100 | --------------------------------- 101 | 102 | *PyDrive* handles file listing pagination for you. 103 | 104 | .. code:: python 105 | 106 | # Auto-iterate through all files that matches this query 107 | file_list = drive.ListFile({'q': "'root' in parents"}).GetList() 108 | for file1 in file_list: 109 | print('title: {}, id: {}'.format(file1['title'], file1['id'])) 110 | 111 | # Paginate file lists by specifying number of max results 112 | for file_list in drive.ListFile({'maxResults': 10}): 113 | print('Received {} files from Files.list()'.format(len(file_list))) # <= 10 114 | for file1 in file_list: 115 | print('title: {}, id: {}'.format(file1['title'], file1['id'])) 116 | 117 | Concurrent access made easy 118 | --------------------------- 119 | 120 | All calls made are thread-safe. The underlying implementation in the 121 | google-api-client library 122 | `is not thread-safe `_, 123 | which means that every request has to re-authenticate an http object. You 124 | can avoid this overhead by 125 | creating your own http object for each thread and re-use it for every call. 126 | 127 | This can be done as follows: 128 | 129 | .. code:: python 130 | 131 | # Create httplib.Http() object. 132 | http = drive.auth.Get_Http_Object() 133 | 134 | # Create file object to upload. 135 | file_obj = drive.CreateFile() 136 | file_obj['title'] = "file name" 137 | 138 | # Upload the file and pass the http object into the call to Upload. 139 | file_obj.Upload(param={"http": http}) 140 | 141 | You can specify the http-object in every access method which takes a *param* 142 | parameter. 143 | 144 | Note: This is not an official Google product. 145 | -------------------------------------------------------------------------------- /docs/README: -------------------------------------------------------------------------------- 1 | This document outlines how to rebuild the documentation. 2 | 3 | General setup: 4 | - Install Sphinx, `pip install sphinx` or `apt-get install python-sphinx` 5 | - Install the theme used for the docs: `pip install sphinx_rtd_theme` 6 | - Run `python setup.py build_sphinx --source-dir=docs/ --build-dir=docs/build --all-files` 7 | 8 | If code files were added, the easiest way to reflect code changes in the 9 | documentation by referencing the file from within pydrive.rst. 10 | 11 | If a non-code related file was added (it has to have the ".rst" ending), 12 | then add the file name to the list of names under "Table of Contents" 13 | in index.rst. Make sure to add the file name excluding the ".rst" file ending. -------------------------------------------------------------------------------- /docs/build/doctrees/filelist.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/doctrees/filelist.doctree -------------------------------------------------------------------------------- /docs/build/doctrees/filemanagement.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/doctrees/filemanagement.doctree -------------------------------------------------------------------------------- /docs/build/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/doctrees/index.doctree -------------------------------------------------------------------------------- /docs/build/doctrees/oauth.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/doctrees/oauth.doctree -------------------------------------------------------------------------------- /docs/build/doctrees/pydrive.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/doctrees/pydrive.doctree -------------------------------------------------------------------------------- /docs/build/doctrees/quickstart.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/doctrees/quickstart.doctree -------------------------------------------------------------------------------- /docs/build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 4 | tags: 5 | -------------------------------------------------------------------------------- /docs/build/html/_sources/filelist.txt: -------------------------------------------------------------------------------- 1 | File listing made easy 2 | ============================= 3 | 4 | *PyDrive* handles paginations and parses response as list of `GoogleDriveFile`_. 5 | 6 | Get all files which matches the query 7 | ------------------------------------- 8 | 9 | Create `GoogleDriveFileList`_ instance with `parameters of Files.list()`_ as ``dict``. Call `GetList()`_ and you will get all files that matches your query as a list of `GoogleDriveFile`_. 10 | 11 | .. code-block:: python 12 | 13 | from pydrive.drive import GoogleDrive 14 | 15 | drive = GoogleDrive(gauth) # Create GoogleDrive instance with authenticated GoogleAuth instance 16 | 17 | # Auto-iterate through all files in the root folder. 18 | file_list = drive.ListFile({'q': "'root' in parents and trashed=false"}).GetList() 19 | for file1 in file_list: 20 | print('title: %s, id: %s' % (file1['title'], file1['id'])) 21 | 22 | You can update metadata or content of these `GoogleDriveFile`_ instances if you need it. 23 | 24 | Paginate and iterate through files 25 | ---------------------------------- 26 | 27 | *PyDrive* provides Pythonic way of paginating and iterating through list of files. All you have to do is to limit number of results with ``maxResults`` parameter and build ``for`` loop retrieving file list from API each iteration. 28 | 29 | Sample code continues from above: 30 | 31 | .. code-block:: python 32 | 33 | # Paginate file lists by specifying number of max results 34 | for file_list in drive.ListFile({'q': 'trashed=true', 'maxResults': 10}): 35 | print('Received %s files from Files.list()' % len(file_list)) # <= 10 36 | for file1 in file_list: 37 | print('title: %s, id: %s' % (file1['title'], file1['id'])) 38 | 39 | 40 | .. _`GoogleDriveFile`: ./pydrive.html#pydrive.files.GoogleDriveFile 41 | .. _`GoogleDriveFileList`: ./pydrive.html#pydrive.files.GoogleDriveFileList 42 | .. _`parameters of Files.list()`: https://developers.google.com/drive/v2/reference/files/list#request 43 | .. _`GetList()`: ./pydrive.html#pydrive.apiattr.ApiResourceList.GetList 44 | -------------------------------------------------------------------------------- /docs/build/html/_sources/filemanagement.txt: -------------------------------------------------------------------------------- 1 | File management made easy 2 | ========================= 3 | 4 | There are many methods to create and update file metadata and contents. 5 | With *PyDrive*, you don't have to care about any of these different API methods. 6 | Manipulate file metadata and contents from `GoogleDriveFile`_ object and call 7 | `Upload()`_. *PyDrive* will make the optimal API call for you. 8 | 9 | Upload a new file 10 | ----------------- 11 | 12 | Here is a sample code to upload a file. ``gauth`` is an authenticated `GoogleAuth`_ object. 13 | 14 | .. code-block:: python 15 | 16 | from pydrive.drive import GoogleDrive 17 | 18 | # Create GoogleDrive instance with authenticated GoogleAuth instance. 19 | drive = GoogleDrive(gauth) 20 | 21 | # Create GoogleDriveFile instance with title 'Hello.txt'. 22 | file1 = drive.CreateFile({'title': 'Hello.txt'}) 23 | file1.Upload() # Upload the file. 24 | print('title: %s, id: %s' % (file1['title'], file1['id'])) 25 | # title: Hello.txt, id: {{FILE_ID}} 26 | 27 | Now, you will have a file 'Hello.txt' uploaded to your Google Drive. You can open it from web interface to check its content, 'Hello World!'. 28 | 29 | Note that `CreateFile()`_ will create `GoogleDriveFile`_ instance but not actually upload a file to Google Drive. You can initialize `GoogleDriveFile`_ object by itself. However, it is not recommended to do so in order to keep authentication consistent. 30 | 31 | Delete, Trash and un-Trash files 32 | -------------------------------- 33 | You may want to delete, trash, or un-trash a file. To do this use ``Delete()``, 34 | ``Trash()`` or ``UnTrash()`` on a GoogleDriveFile object. 35 | 36 | *Note:* ``Trash()`` *moves a file into the trash and can be recovered,* 37 | ``Delete()`` *deletes the file permanently and immediately.* 38 | 39 | .. code-block:: python 40 | 41 | # Create GoogleDriveFile instance and upload it. 42 | file1 = drive.CreateFile() 43 | file1.Upload() 44 | 45 | file1.Trash() # Move file to trash. 46 | file1.UnTrash() # Move file out of trash. 47 | file1.Delete() # Permanently delete the file. 48 | 49 | Update file metadata 50 | -------------------- 51 | 52 | You can manipulate file metadata from a `GoogleDriveFile`_ object just as you manipulate a ``dict``. 53 | The format of file metadata can be found in the Google Drive API documentation: `Files resource`_. 54 | 55 | Sample code continues from `Upload a new file`_: 56 | 57 | .. code-block:: python 58 | 59 | file1['title'] = 'HelloWorld.txt' # Change title of the file. 60 | file1.Upload() # Update metadata. 61 | print('title: %s' % file1['title']) # title: HelloWorld.txt. 62 | 63 | Now, the title of your file has changed to 'HelloWorld.txt'. 64 | 65 | Download file metadata from file ID 66 | ----------------------------------- 67 | 68 | You might want to get file metadata from file ID. In that case, just initialize 69 | `GoogleDriveFile`_ with file ID and access metadata from `GoogleDriveFile`_ 70 | just as you access ``dict``. 71 | 72 | Sample code continues from above: 73 | 74 | .. code-block:: python 75 | 76 | # Create GoogleDriveFile instance with file id of file1. 77 | file2 = drive.CreateFile({'id': file1['id']}) 78 | print('title: %s, mimeType: %s' % (file2['title'], file2['mimeType'])) 79 | # title: HelloWorld.txt, mimeType: text/plain 80 | 81 | Handling special metadata 82 | ------------------------- 83 | 84 | Not all metadata can be set with the methods described above. 85 | PyDrive gives you access to the metadata of an object through 86 | ``file_object.FetchMetadata()``. This function has two optional parameters: 87 | ``fields`` and ``fetch_all``. 88 | 89 | .. code-block:: python 90 | 91 | file1 = drive.CreateFile({'id': ''}) 92 | 93 | # Fetches all basic metadata fields, including file size, last modified etc. 94 | file1.FetchMetadata() 95 | 96 | # Fetches all metadata available. 97 | file1.FetchMetadata(fetch_all=True) 98 | 99 | # Fetches the 'permissions' metadata field. 100 | file1.FetchMetadata(fields='permissions') 101 | # You can update a list of specific fields like this: 102 | file1.FetchMetadata(fields='permissions,labels,mimeType') 103 | 104 | For more information on available metadata fields have a look at the 105 | `official documentation`_. 106 | 107 | Insert permissions 108 | __________________ 109 | Insert, retrieving or deleting permissions is illustrated by making a file 110 | readable to all who have a link to the file. 111 | 112 | .. code-block:: python 113 | 114 | file1 = drive.CreateFile() 115 | file1.Upload() 116 | 117 | # Insert the permission. 118 | permission = file1.InsertPermission({ 119 | 'type': 'anyone', 120 | 'value': 'anyone', 121 | 'role': 'reader'}) 122 | 123 | print(file1['alternateLink']) # Display the sharable link. 124 | 125 | Note: ``InsertPermission()`` calls ``GetPermissions()`` after successfully 126 | inserting the permission. 127 | 128 | You can find more information on the permitted fields of a permission 129 | `here `_. 130 | This file is now shared and anyone with the link can view it. But what if you 131 | want to check whether a file is already shared? 132 | 133 | List permissions 134 | ________________ 135 | 136 | Permissions can be fetched using the ``GetPermissions()`` function of a 137 | ``GoogleDriveFile``, and can be used like so: 138 | 139 | .. code-block:: python 140 | 141 | # Create a new file 142 | file1 = drive.CreateFile() 143 | # Fetch permissions. 144 | permissions = file1.GetPermissions() 145 | print(permissions) 146 | 147 | # The permissions are also available as file1['permissions']: 148 | print(file1['permissions']) 149 | 150 | For the more advanced user: ``GetPermissions()`` is a shorthand for: 151 | 152 | .. code-block:: python 153 | 154 | # Fetch Metadata, including the permissions field. 155 | file1.FetchMetadata(fields='permissions') 156 | 157 | # The permissions array is now available for further use. 158 | print(file1['permissions']) 159 | 160 | Remove a Permission 161 | ___________________ 162 | *PyDrive* allows you to remove a specific permission using the 163 | ``DeletePermission(permission_id)`` function. This function allows you to delete 164 | one permission at a time by providing the permission's ID. 165 | 166 | .. code-block:: python 167 | 168 | file1 = drive.CreateFile({'id': ''}) 169 | permissions = file1.GetPermissions() # Download file permissions. 170 | 171 | permission_id = permissions[1]['id'] # Get a permission ID. 172 | 173 | file1.DeletePermission(permission_id) # Delete the permission. 174 | 175 | Upload and update file content 176 | ------------------------------ 177 | 178 | Managing file content is as easy as managing file metadata. You can set file 179 | content with either `SetContentFile(filename)`_ or `SetContentString(content)`_ 180 | and call `Upload()`_ just as you did to upload or update file metadata. 181 | 182 | Sample code continues from `Download file metadata from file ID`_: 183 | 184 | .. code-block:: python 185 | 186 | file4 = drive.CreateFile({'title':'appdata.json', 'mimeType':'application/json'}) 187 | file4.SetContentString('{"firstname": "John", "lastname": "Smith"}') 188 | file4.Upload() # Upload file. 189 | file4.SetContentString('{"firstname": "Claudio", "lastname": "Afshar"}') 190 | file4.Upload() # Update content of the file. 191 | 192 | file5 = drive.CreateFile() 193 | # Read file and set it as a content of this instance. 194 | file5.SetContentFile('cat.png') 195 | file5.Upload() # Upload the file. 196 | print('title: %s, mimeType: %s' % (file5['title'], file5['mimeType'])) 197 | # title: cat.png, mimeType: image/png 198 | 199 | **Advanced Users:** If you call SetContentFile and GetContentFile you can can 200 | define which character encoding is to be used by using the optional 201 | parameter `encoding`. 202 | 203 | If you, for example, are retrieving a file which is stored on your Google 204 | Drive which is encoded with ISO-8859-1, then you can get the content string 205 | like so: 206 | 207 | .. code-block:: python 208 | 209 | content_string = file4.GetContentString(encoding='ISO-8859-1') 210 | 211 | Download file content 212 | --------------------- 213 | 214 | Just as you uploaded file content, you can download it using 215 | `GetContentFile(filename)`_ or `GetContentString()`_. 216 | 217 | Sample code continues from above: 218 | 219 | .. code-block:: python 220 | 221 | # Initialize GoogleDriveFile instance with file id. 222 | file6 = drive.CreateFile({'id': file5['id']}) 223 | file6.GetContentFile('catlove.png') # Download file as 'catlove.png'. 224 | 225 | # Initialize GoogleDriveFile instance with file id. 226 | file7 = drive.CreateFile({'id': file4['id']}) 227 | content = file7.GetContentString() 228 | # content: '{"firstname": "Claudio", "lastname": "Afshar"}' 229 | 230 | file7.SetContentString(content.replace('lastname', 'familyname')) 231 | file7.Upload() 232 | # Uploaded content: '{"firstname": "Claudio", "familyname": "Afshar"}' 233 | 234 | **Advanced users**: Google Drive is `known`_ to add BOM (Byte Order Marks) to 235 | the beginning of some files, such as Google Documents downloaded as text files. 236 | In some cases confuses parsers and leads to corrupt files. 237 | PyDrive can remove the BOM from the beginning of a file when it 238 | is downloaded. Just set the `remove_bom` parameter in `GetContentString()` or 239 | `GetContentFile()` - see `examples/strip_bom_example.py` in the GitHub 240 | repository for an example. 241 | 242 | 243 | .. _`GoogleDriveFile`: ./pydrive.html#pydrive.files.GoogleDriveFile 244 | .. _`Upload()`: ./pydrive.html#pydrive.files.GoogleDriveFile.Upload 245 | .. _`GoogleAuth`: ./pydrive.html#pydrive.auth.GoogleAuth 246 | .. _`CreateFile()`: ./pydrive.html#pydrive.drive.GoogleDrive.CreateFile 247 | .. _`Files resource`: https://developers.google.com/drive/v2/reference/files#resource-representations 248 | .. _`SetContentFile(filename)`: ./pydrive.html#pydrive.files.GoogleDriveFile.SetContentFile 249 | .. _`SetContentString(content)`: ./pydrive.html#pydrive.files.GoogleDriveFile.SetContentString 250 | .. _`GetContentFile(filename)`: ./pydrive.html#pydrive.files.GoogleDriveFile.GetContentFile 251 | .. _`GetContentString()`: ./pydrive.html#pydrive.files.GoogleDriveFile.GetContentString 252 | .. _`official documentation`: https://developers.google.com/drive/v2/reference/files#resource-representations 253 | .. _`known`: https://productforums.google.com/forum/#!topic/docs/BJLimQDGtjQ -------------------------------------------------------------------------------- /docs/build/html/_sources/index.txt: -------------------------------------------------------------------------------- 1 | .. PyDrive documentation master file, created by 2 | sphinx-quickstart on Sun Jun 12 23:01:40 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to PyDrive's documentation! 7 | =================================== 8 | 9 | PyDrive is a wrapper library of `google-api-python-client`_ that simplifies many common Google Drive API tasks. 10 | 11 | Project Info 12 | ============ 13 | 14 | - Homepage: `https://pypi.python.org/pypi/PyDrive `_ 15 | - Documentation: `Official documentation on GitHub pages `_ 16 | - Github: `https://github.com/googledrive/PyDrive `_ 17 | 18 | How to install 19 | ============== 20 | 21 | You can install PyDrive with regular ``pip`` command. 22 | 23 | :: 24 | 25 | $ pip install PyDrive 26 | 27 | To install the current development version from GitHub, use: 28 | 29 | :: 30 | 31 | $ pip install git+https://github.com/googledrive/PyDrive.git#egg=PyDrive 32 | 33 | Table of Contents 34 | ================= 35 | 36 | .. toctree:: 37 | :maxdepth: 2 38 | 39 | quickstart 40 | oauth 41 | filemanagement 42 | filelist 43 | pydrive 44 | 45 | 46 | .. _`google-api-python-client`: https://github.com/google/google-api-python-client 47 | 48 | Indices and tables 49 | ================== 50 | 51 | * :ref:`genindex` 52 | * :ref:`search` 53 | 54 | -------------------------------------------------------------------------------- /docs/build/html/_sources/oauth.txt: -------------------------------------------------------------------------------- 1 | OAuth made easy 2 | =============== 3 | 4 | Authentication in two lines 5 | --------------------------- 6 | 7 | OAuth2.0 is complex and difficult to start with. To make it more simple, 8 | *PyDrive* makes all authentication into just two lines. 9 | 10 | .. code-block:: python 11 | 12 | from pydrive.auth import GoogleAuth 13 | 14 | gauth = GoogleAuth() 15 | # Create local webserver and auto handles authentication. 16 | gauth.LocalWebserverAuth() 17 | 18 | # Or use the CommandLineAuth(), which provides you with a link to paste 19 | # into your browser. The site it leads to then provides you with an 20 | # authentication token which you paste into the command line. 21 | # Commented out as it is an alternative to the LocalWebserverAuth() above, 22 | # and someone will just copy-paste the entire thing into their editor. 23 | 24 | # gauth.CommandLineAuth() 25 | 26 | To make this code work, you need to download the application configurations file 27 | from APIs Console. Take a look at quickstart_ for detailed instructions. 28 | 29 | `LocalWebserverAuth()`_ is a built-in method of `GoogleAuth`_ which sets up 30 | local webserver to automatically receive authentication code from user and 31 | authorizes by itself. You can also use `CommandLineAuth()`_ which manually 32 | takes code from user at command line. 33 | 34 | .. _quickstart: ./quickstart.html#authentication 35 | .. _`LocalWebserverAuth()`: ./pydrive.html#pydrive.auth.GoogleAuth.LocalWebserverAuth 36 | .. _`GoogleAuth`: ./pydrive.html#pydrive.auth.GoogleAuth 37 | .. _`CommandLineAuth()`: ./pydrive.html#pydrive.auth.GoogleAuth.CommandLineAuth 38 | 39 | Automatic and custom authentication with *settings.yaml* 40 | -------------------------------------------------------- 41 | 42 | Read this section if you need a custom authentication flow, **such as silent 43 | authentication on a remote machine**. For an example of such a setup have a look 44 | at `Sample settings.yaml`_. 45 | 46 | OAuth is complicated and it requires a lot of settings. By default, 47 | when you don't provide any settings, *PyDrive* will automatically set default 48 | values which works for most of the cases. Here are some default settings. 49 | 50 | - Read client configuration from file *client_secrets.json* 51 | - OAuth scope: :code:`https://www.googleapis.com/auth/drive` 52 | - Don't save credentials 53 | - Don't retrieve refresh token 54 | 55 | However, you might want to customize these settings while maintaining two lines 56 | of clean code. If that is the case, you can make *settings.yaml* file in your 57 | working directory and *PyDrive* will read it to customize authentication 58 | behavior. 59 | 60 | These are all the possible fields of a *settings.yaml* file: 61 | 62 | .. code-block:: python 63 | 64 | client_config_backend: {{str}} 65 | client_config_file: {{str}} 66 | client_config: 67 | client_id: {{str}} 68 | client_secret: {{str}} 69 | auth_uri: {{str}} 70 | token_uri: {{str}} 71 | redirect_uri: {{str}} 72 | revoke_uri: {{str}} 73 | 74 | save_credentials: {{bool}} 75 | save_credentials_backend: {{str}} 76 | save_credentials_file: {{str}} 77 | 78 | get_refresh_token: {{bool}} 79 | 80 | oauth_scope: {{list of str}} 81 | 82 | Fields explained: 83 | 84 | :client_config_backend (str): From where to read client configuration(API application settings such as client_id and client_secrets) from. Valid values are 'file' and 'settings'. **Default**: 'file'. **Required**: No. 85 | :client_config_file (str): When *client_config_backend* is 'file', path to the file containing client configuration. **Default**: 'client_secrets.json'. **Required**: No. 86 | :client_config (dict): Place holding dictionary for client configuration when *client_config_backend* is 'settings'. **Required**: Yes, only if *client_config_backend* is 'settings' 87 | :client_config['client_id'] (str): Client ID of the application. **Required**: Yes, only if *client_config_backend* is 'settings' 88 | :client_config['client_secret'] (str): Client secret of the application. **Required**: Yes, only if *client_config_backend* is 'settings' 89 | :client_config['auth_uri'] (str): The authorization server endpoint URI. **Default**: 'https://accounts.google.com/o/oauth2/auth'. **Required**: No. 90 | :client_config['token_uri'] (str): The token server endpoint URI. **Default**: 'https://accounts.google.com/o/oauth2/token'. **Required**: No. 91 | :client_config['redirect_uri'] (str): Redirection endpoint URI. **Default**: 'urn:ietf:wg:oauth:2.0:oob'. **Required**: No. 92 | :client_config['revoke_uri'] (str): Revoke endpoint URI. **Default**: None. **Required**: No. 93 | :save_credentials (bool): True if you want to save credentials. **Default**: False. **Required**: No. 94 | :save_credentials_backend (str): Backend to save credentials to. 'file' is the only valid value for now. **Default**: 'file'. **Required**: No. 95 | :save_credentials_file (str): Destination of credentials file. **Required**: Yes, only if *save_credentials_backend* is 'file'. 96 | :get_refresh_token (bool): True if you want to retrieve refresh token along with access token. **Default**: False. **Required**: No. 97 | :oauth_scope (list of str): OAuth scope to authenticate. **Default**: ['https://www.googleapis.com/auth/drive']. **Required**: No. 98 | 99 | Sample *settings.yaml* 100 | ______________________ 101 | 102 | :: 103 | 104 | client_config_backend: settings 105 | client_config: 106 | client_id: 9637341109347.apps.googleusercontent.com 107 | client_secret: psDskOoWr1P602PXRTHi 108 | 109 | save_credentials: True 110 | save_credentials_backend: file 111 | save_credentials_file: credentials.json 112 | 113 | get_refresh_token: True 114 | 115 | oauth_scope: 116 | - https://www.googleapis.com/auth/drive.file 117 | - https://www.googleapis.com/auth/drive.install 118 | 119 | Building your own authentication flow 120 | ------------------------------------- 121 | 122 | You might want to build your own authentication flow. For example, you might 123 | want to integrate your existing website with Drive API. In that case, you can 124 | customize authentication flow as follwing: 125 | 126 | 1. Get authentication Url from `GetAuthUrl()`_. 127 | 2. Ask users to visit the authentication Url and grant access to your application. Retrieve authentication code manually by user or automatically by building your own oauth2callback. 128 | 3. Call `Auth(code)`_ with the authentication code you retrieved from step 2. 129 | 130 | Your *settings.yaml* will work for your customized authentication flow, too. 131 | 132 | Here is a sample code for your customized authentication flow 133 | 134 | .. code-block:: python 135 | 136 | from pydrive.auth import GoogleAuth 137 | 138 | gauth = GoogleAuth() 139 | auth_url = gauth.GetAuthUrl() # Create authentication url user needs to visit 140 | code = AskUserToVisitLinkAndGiveCode(auth_url) # Your customized authentication flow 141 | gauth.Auth(code) # Authorize and build service from the code 142 | 143 | .. _`GetAuthUrl()`: ./pydrive.html#pydrive.auth.GoogleAuth.GetAuthUrl 144 | .. _`Auth(code)`: ./pydrive.html#pydrive.auth.GoogleAuth.Auth 145 | -------------------------------------------------------------------------------- /docs/build/html/_sources/pydrive.txt: -------------------------------------------------------------------------------- 1 | pydrive package 2 | =============== 3 | 4 | pydrive.apiattr module 5 | ---------------------- 6 | 7 | .. automodule:: pydrive.apiattr 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | pydrive.auth module 13 | ------------------- 14 | 15 | .. automodule:: pydrive.auth 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | pydrive.drive module 21 | -------------------- 22 | 23 | .. automodule:: pydrive.drive 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | pydrive.files module 29 | -------------------- 30 | 31 | .. automodule:: pydrive.files 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | 36 | pydrive.settings module 37 | ----------------------- 38 | 39 | .. automodule:: pydrive.settings 40 | :members: 41 | :undoc-members: 42 | :show-inheritance: 43 | -------------------------------------------------------------------------------- /docs/build/html/_sources/quickstart.txt: -------------------------------------------------------------------------------- 1 | Quickstart 2 | ============================= 3 | 4 | Authentication 5 | -------------- 6 | Drive API requires OAuth2.0 for authentication. *PyDrive* makes your life much easier by handling complex authentication steps for you. 7 | 8 | 1. Go to `APIs Console`_ and make your own project. 9 | 2. Search for 'Google Drive API', select the entry, and click 'Enable'. 10 | 3. Select 'Credentials' from the left menu, click 'Create Credentials', select 'OAuth client ID'. 11 | 4. Now, the product name and consent screen need to be set -> click 'Configure consent screen' and follow the instructions. Once finished: 12 | 13 | a. Select 'Application type' to be *Web application*. 14 | b. Enter an appropriate name. 15 | c. Input *http://localhost:8080* for 'Authorized JavaScript origins'. 16 | d. Input *http://localhost:8080/* for 'Authorized redirect URIs'. 17 | e. Click 'Create'. 18 | 19 | 5. Click 'Download JSON' on the right side of Client ID to download **client_secret_.json**. 20 | 21 | The downloaded file has all authentication information of your application. 22 | **Rename the file to "client_secrets.json" and place it in your working directory.** 23 | 24 | Create *quickstart.py* file and copy and paste the following code. 25 | 26 | .. code-block:: python 27 | 28 | from pydrive.auth import GoogleAuth 29 | 30 | gauth = GoogleAuth() 31 | gauth.LocalWebserverAuth() # Creates local webserver and auto handles authentication. 32 | 33 | Run this code with *python quickstart.py* and you will see a web browser asking you for authentication. Click *Accept* and you are done with authentication. For more details, take a look at documentation: `OAuth made easy`_ 34 | 35 | .. _`APIs Console`: https://console.developers.google.com/iam-admin/projects 36 | .. _`OAuth made easy`: ./oauth.html 37 | 38 | Creating and updating file 39 | -------------------------- 40 | 41 | There are many methods to create and update file metadata and contents. With *PyDrive*, all you have to know is `Upload()`_ method which makes optimal API call for you. Add the following code to your *quickstart.py* and run it. 42 | 43 | .. code-block:: python 44 | 45 | from pydrive.drive import GoogleDrive 46 | 47 | drive = GoogleDrive(gauth) 48 | 49 | file1 = drive.CreateFile({'title': 'Hello.txt'}) # Create GoogleDriveFile instance with title 'Hello.txt'. 50 | file1.SetContentString('Hello World!') # Set content of the file from given string. 51 | file1.Upload() 52 | 53 | This code will create a new file with title *Hello.txt* and its content *Hello World!*. You can see and open this file from `Google Drive`_ if you want. For more details, take a look at documentation: `File management made easy`_ 54 | 55 | .. _`Upload()`: ./pydrive.html#pydrive.files.GoogleDriveFile.Upload 56 | .. _`Google Drive`: https://drive.google.com 57 | .. _`File management made easy`: ./filemanagement.html 58 | 59 | Getting list of files 60 | --------------------- 61 | 62 | *PyDrive* handles paginations and parses response as list of `GoogleDriveFile`_. Let's get title and id of all the files in the root folder of Google Drive. Again, add the following code to *quickstart.py* and execute it. 63 | 64 | .. code-block:: python 65 | 66 | # Auto-iterate through all files that matches this query 67 | file_list = drive.ListFile({'q': "'root' in parents and trashed=false"}).GetList() 68 | for file1 in file_list: 69 | print('title: %s, id: %s' % (file1['title'], file1['id'])) 70 | 71 | You will see title and id of all the files and folders in root folder of your Google Drive. For more details, take a look at documentation: `File listing made easy`_ 72 | 73 | .. _`GoogleDriveFile`: ./pydrive.html#pydrive.files.GoogleDriveFile 74 | .. _`File listing made easy`: ./filelist.html 75 | -------------------------------------------------------------------------------- /docs/build/html/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/ajax-loader.gif -------------------------------------------------------------------------------- /docs/build/html/_static/basic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * basic.css 3 | * ~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- basic theme. 6 | * 7 | * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /* -- main layout ----------------------------------------------------------- */ 13 | 14 | div.clearer { 15 | clear: both; 16 | } 17 | 18 | /* -- relbar ---------------------------------------------------------------- */ 19 | 20 | div.related { 21 | width: 100%; 22 | font-size: 90%; 23 | } 24 | 25 | div.related h3 { 26 | display: none; 27 | } 28 | 29 | div.related ul { 30 | margin: 0; 31 | padding: 0 0 0 10px; 32 | list-style: none; 33 | } 34 | 35 | div.related li { 36 | display: inline; 37 | } 38 | 39 | div.related li.right { 40 | float: right; 41 | margin-right: 5px; 42 | } 43 | 44 | /* -- sidebar --------------------------------------------------------------- */ 45 | 46 | div.sphinxsidebarwrapper { 47 | padding: 10px 5px 0 10px; 48 | } 49 | 50 | div.sphinxsidebar { 51 | float: left; 52 | width: 230px; 53 | margin-left: -100%; 54 | font-size: 90%; 55 | word-wrap: break-word; 56 | overflow-wrap : break-word; 57 | } 58 | 59 | div.sphinxsidebar ul { 60 | list-style: none; 61 | } 62 | 63 | div.sphinxsidebar ul ul, 64 | div.sphinxsidebar ul.want-points { 65 | margin-left: 20px; 66 | list-style: square; 67 | } 68 | 69 | div.sphinxsidebar ul ul { 70 | margin-top: 0; 71 | margin-bottom: 0; 72 | } 73 | 74 | div.sphinxsidebar form { 75 | margin-top: 10px; 76 | } 77 | 78 | div.sphinxsidebar input { 79 | border: 1px solid #98dbcc; 80 | font-family: sans-serif; 81 | font-size: 1em; 82 | } 83 | 84 | div.sphinxsidebar #searchbox input[type="text"] { 85 | width: 170px; 86 | } 87 | 88 | img { 89 | border: 0; 90 | max-width: 100%; 91 | } 92 | 93 | /* -- search page ----------------------------------------------------------- */ 94 | 95 | ul.search { 96 | margin: 10px 0 0 20px; 97 | padding: 0; 98 | } 99 | 100 | ul.search li { 101 | padding: 5px 0 5px 20px; 102 | background-image: url(file.png); 103 | background-repeat: no-repeat; 104 | background-position: 0 7px; 105 | } 106 | 107 | ul.search li a { 108 | font-weight: bold; 109 | } 110 | 111 | ul.search li div.context { 112 | color: #888; 113 | margin: 2px 0 0 30px; 114 | text-align: left; 115 | } 116 | 117 | ul.keywordmatches li.goodmatch a { 118 | font-weight: bold; 119 | } 120 | 121 | /* -- index page ------------------------------------------------------------ */ 122 | 123 | table.contentstable { 124 | width: 90%; 125 | } 126 | 127 | table.contentstable p.biglink { 128 | line-height: 150%; 129 | } 130 | 131 | a.biglink { 132 | font-size: 1.3em; 133 | } 134 | 135 | span.linkdescr { 136 | font-style: italic; 137 | padding-top: 5px; 138 | font-size: 90%; 139 | } 140 | 141 | /* -- general index --------------------------------------------------------- */ 142 | 143 | table.indextable { 144 | width: 100%; 145 | } 146 | 147 | table.indextable td { 148 | text-align: left; 149 | vertical-align: top; 150 | } 151 | 152 | table.indextable dl, table.indextable dd { 153 | margin-top: 0; 154 | margin-bottom: 0; 155 | } 156 | 157 | table.indextable tr.pcap { 158 | height: 10px; 159 | } 160 | 161 | table.indextable tr.cap { 162 | margin-top: 10px; 163 | background-color: #f2f2f2; 164 | } 165 | 166 | img.toggler { 167 | margin-right: 3px; 168 | margin-top: 3px; 169 | cursor: pointer; 170 | } 171 | 172 | div.modindex-jumpbox { 173 | border-top: 1px solid #ddd; 174 | border-bottom: 1px solid #ddd; 175 | margin: 1em 0 1em 0; 176 | padding: 0.4em; 177 | } 178 | 179 | div.genindex-jumpbox { 180 | border-top: 1px solid #ddd; 181 | border-bottom: 1px solid #ddd; 182 | margin: 1em 0 1em 0; 183 | padding: 0.4em; 184 | } 185 | 186 | /* -- general body styles --------------------------------------------------- */ 187 | 188 | div.body p, div.body dd, div.body li, div.body blockquote { 189 | -moz-hyphens: auto; 190 | -ms-hyphens: auto; 191 | -webkit-hyphens: auto; 192 | hyphens: auto; 193 | } 194 | 195 | a.headerlink { 196 | visibility: hidden; 197 | } 198 | 199 | h1:hover > a.headerlink, 200 | h2:hover > a.headerlink, 201 | h3:hover > a.headerlink, 202 | h4:hover > a.headerlink, 203 | h5:hover > a.headerlink, 204 | h6:hover > a.headerlink, 205 | dt:hover > a.headerlink, 206 | caption:hover > a.headerlink, 207 | p.caption:hover > a.headerlink, 208 | div.code-block-caption:hover > a.headerlink { 209 | visibility: visible; 210 | } 211 | 212 | div.body p.caption { 213 | text-align: inherit; 214 | } 215 | 216 | div.body td { 217 | text-align: left; 218 | } 219 | 220 | .field-list ul { 221 | padding-left: 1em; 222 | } 223 | 224 | .first { 225 | margin-top: 0 !important; 226 | } 227 | 228 | p.rubric { 229 | margin-top: 30px; 230 | font-weight: bold; 231 | } 232 | 233 | img.align-left, .figure.align-left, object.align-left { 234 | clear: left; 235 | float: left; 236 | margin-right: 1em; 237 | } 238 | 239 | img.align-right, .figure.align-right, object.align-right { 240 | clear: right; 241 | float: right; 242 | margin-left: 1em; 243 | } 244 | 245 | img.align-center, .figure.align-center, object.align-center { 246 | display: block; 247 | margin-left: auto; 248 | margin-right: auto; 249 | } 250 | 251 | .align-left { 252 | text-align: left; 253 | } 254 | 255 | .align-center { 256 | text-align: center; 257 | } 258 | 259 | .align-right { 260 | text-align: right; 261 | } 262 | 263 | /* -- sidebars -------------------------------------------------------------- */ 264 | 265 | div.sidebar { 266 | margin: 0 0 0.5em 1em; 267 | border: 1px solid #ddb; 268 | padding: 7px 7px 0 7px; 269 | background-color: #ffe; 270 | width: 40%; 271 | float: right; 272 | } 273 | 274 | p.sidebar-title { 275 | font-weight: bold; 276 | } 277 | 278 | /* -- topics ---------------------------------------------------------------- */ 279 | 280 | div.topic { 281 | border: 1px solid #ccc; 282 | padding: 7px 7px 0 7px; 283 | margin: 10px 0 10px 0; 284 | } 285 | 286 | p.topic-title { 287 | font-size: 1.1em; 288 | font-weight: bold; 289 | margin-top: 10px; 290 | } 291 | 292 | /* -- admonitions ----------------------------------------------------------- */ 293 | 294 | div.admonition { 295 | margin-top: 10px; 296 | margin-bottom: 10px; 297 | padding: 7px; 298 | } 299 | 300 | div.admonition dt { 301 | font-weight: bold; 302 | } 303 | 304 | div.admonition dl { 305 | margin-bottom: 0; 306 | } 307 | 308 | p.admonition-title { 309 | margin: 0px 10px 5px 0px; 310 | font-weight: bold; 311 | } 312 | 313 | div.body p.centered { 314 | text-align: center; 315 | margin-top: 25px; 316 | } 317 | 318 | /* -- tables ---------------------------------------------------------------- */ 319 | 320 | table.docutils { 321 | border: 0; 322 | border-collapse: collapse; 323 | } 324 | 325 | table caption span.caption-number { 326 | font-style: italic; 327 | } 328 | 329 | table caption span.caption-text { 330 | } 331 | 332 | table.docutils td, table.docutils th { 333 | padding: 1px 8px 1px 5px; 334 | border-top: 0; 335 | border-left: 0; 336 | border-right: 0; 337 | border-bottom: 1px solid #aaa; 338 | } 339 | 340 | table.field-list td, table.field-list th { 341 | border: 0 !important; 342 | } 343 | 344 | table.footnote td, table.footnote th { 345 | border: 0 !important; 346 | } 347 | 348 | th { 349 | text-align: left; 350 | padding-right: 5px; 351 | } 352 | 353 | table.citation { 354 | border-left: solid 1px gray; 355 | margin-left: 1px; 356 | } 357 | 358 | table.citation td { 359 | border-bottom: none; 360 | } 361 | 362 | /* -- figures --------------------------------------------------------------- */ 363 | 364 | div.figure { 365 | margin: 0.5em; 366 | padding: 0.5em; 367 | } 368 | 369 | div.figure p.caption { 370 | padding: 0.3em; 371 | } 372 | 373 | div.figure p.caption span.caption-number { 374 | font-style: italic; 375 | } 376 | 377 | div.figure p.caption span.caption-text { 378 | } 379 | 380 | 381 | /* -- other body styles ----------------------------------------------------- */ 382 | 383 | ol.arabic { 384 | list-style: decimal; 385 | } 386 | 387 | ol.loweralpha { 388 | list-style: lower-alpha; 389 | } 390 | 391 | ol.upperalpha { 392 | list-style: upper-alpha; 393 | } 394 | 395 | ol.lowerroman { 396 | list-style: lower-roman; 397 | } 398 | 399 | ol.upperroman { 400 | list-style: upper-roman; 401 | } 402 | 403 | dl { 404 | margin-bottom: 15px; 405 | } 406 | 407 | dd p { 408 | margin-top: 0px; 409 | } 410 | 411 | dd ul, dd table { 412 | margin-bottom: 10px; 413 | } 414 | 415 | dd { 416 | margin-top: 3px; 417 | margin-bottom: 10px; 418 | margin-left: 30px; 419 | } 420 | 421 | dt:target, .highlighted { 422 | background-color: #fbe54e; 423 | } 424 | 425 | dl.glossary dt { 426 | font-weight: bold; 427 | font-size: 1.1em; 428 | } 429 | 430 | .field-list ul { 431 | margin: 0; 432 | padding-left: 1em; 433 | } 434 | 435 | .field-list p { 436 | margin: 0; 437 | } 438 | 439 | .optional { 440 | font-size: 1.3em; 441 | } 442 | 443 | .sig-paren { 444 | font-size: larger; 445 | } 446 | 447 | .versionmodified { 448 | font-style: italic; 449 | } 450 | 451 | .system-message { 452 | background-color: #fda; 453 | padding: 5px; 454 | border: 3px solid red; 455 | } 456 | 457 | .footnote:target { 458 | background-color: #ffa; 459 | } 460 | 461 | .line-block { 462 | display: block; 463 | margin-top: 1em; 464 | margin-bottom: 1em; 465 | } 466 | 467 | .line-block .line-block { 468 | margin-top: 0; 469 | margin-bottom: 0; 470 | margin-left: 1.5em; 471 | } 472 | 473 | .guilabel, .menuselection { 474 | font-family: sans-serif; 475 | } 476 | 477 | .accelerator { 478 | text-decoration: underline; 479 | } 480 | 481 | .classifier { 482 | font-style: oblique; 483 | } 484 | 485 | abbr, acronym { 486 | border-bottom: dotted 1px; 487 | cursor: help; 488 | } 489 | 490 | /* -- code displays --------------------------------------------------------- */ 491 | 492 | pre { 493 | overflow: auto; 494 | overflow-y: hidden; /* fixes display issues on Chrome browsers */ 495 | } 496 | 497 | span.pre { 498 | -moz-hyphens: none; 499 | -ms-hyphens: none; 500 | -webkit-hyphens: none; 501 | hyphens: none; 502 | } 503 | 504 | td.linenos pre { 505 | padding: 5px 0px; 506 | border: 0; 507 | background-color: transparent; 508 | color: #aaa; 509 | } 510 | 511 | table.highlighttable { 512 | margin-left: 0.5em; 513 | } 514 | 515 | table.highlighttable td { 516 | padding: 0 0.5em 0 0.5em; 517 | } 518 | 519 | div.code-block-caption { 520 | padding: 2px 5px; 521 | font-size: small; 522 | } 523 | 524 | div.code-block-caption code { 525 | background-color: transparent; 526 | } 527 | 528 | div.code-block-caption + div > div.highlight > pre { 529 | margin-top: 0; 530 | } 531 | 532 | div.code-block-caption span.caption-number { 533 | padding: 0.1em 0.3em; 534 | font-style: italic; 535 | } 536 | 537 | div.code-block-caption span.caption-text { 538 | } 539 | 540 | div.literal-block-wrapper { 541 | padding: 1em 1em 0; 542 | } 543 | 544 | div.literal-block-wrapper div.highlight { 545 | margin: 0; 546 | } 547 | 548 | code.descname { 549 | background-color: transparent; 550 | font-weight: bold; 551 | font-size: 1.2em; 552 | } 553 | 554 | code.descclassname { 555 | background-color: transparent; 556 | } 557 | 558 | code.xref, a code { 559 | background-color: transparent; 560 | font-weight: bold; 561 | } 562 | 563 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { 564 | background-color: transparent; 565 | } 566 | 567 | .viewcode-link { 568 | float: right; 569 | } 570 | 571 | .viewcode-back { 572 | float: right; 573 | font-family: sans-serif; 574 | } 575 | 576 | div.viewcode-block:target { 577 | margin: -1px -10px; 578 | padding: 0 10px; 579 | } 580 | 581 | /* -- math display ---------------------------------------------------------- */ 582 | 583 | img.math { 584 | vertical-align: middle; 585 | } 586 | 587 | div.body div.math p { 588 | text-align: center; 589 | } 590 | 591 | span.eqno { 592 | float: right; 593 | } 594 | 595 | /* -- printout stylesheet --------------------------------------------------- */ 596 | 597 | @media print { 598 | div.document, 599 | div.documentwrapper, 600 | div.bodywrapper { 601 | margin: 0 !important; 602 | width: 100%; 603 | } 604 | 605 | div.sphinxsidebar, 606 | div.related, 607 | div.footer, 608 | #top-link { 609 | display: none; 610 | } 611 | } -------------------------------------------------------------------------------- /docs/build/html/_static/comment-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/comment-bright.png -------------------------------------------------------------------------------- /docs/build/html/_static/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/comment-close.png -------------------------------------------------------------------------------- /docs/build/html/_static/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/comment.png -------------------------------------------------------------------------------- /docs/build/html/_static/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}} 2 | /*# sourceMappingURL=badge_only.css.map */ 3 | -------------------------------------------------------------------------------- /docs/build/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for all documentation. 6 | * 7 | * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | /** 18 | * make the code below compatible with browsers without 19 | * an installed firebug like debugger 20 | if (!window.console || !console.firebug) { 21 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", 22 | "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", 23 | "profile", "profileEnd"]; 24 | window.console = {}; 25 | for (var i = 0; i < names.length; ++i) 26 | window.console[names[i]] = function() {}; 27 | } 28 | */ 29 | 30 | /** 31 | * small helper function to urldecode strings 32 | */ 33 | jQuery.urldecode = function(x) { 34 | return decodeURIComponent(x).replace(/\+/g, ' '); 35 | }; 36 | 37 | /** 38 | * small helper function to urlencode strings 39 | */ 40 | jQuery.urlencode = encodeURIComponent; 41 | 42 | /** 43 | * This function returns the parsed url parameters of the 44 | * current request. Multiple values per key are supported, 45 | * it will always return arrays of strings for the value parts. 46 | */ 47 | jQuery.getQueryParameters = function(s) { 48 | if (typeof s == 'undefined') 49 | s = document.location.search; 50 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 51 | var result = {}; 52 | for (var i = 0; i < parts.length; i++) { 53 | var tmp = parts[i].split('=', 2); 54 | var key = jQuery.urldecode(tmp[0]); 55 | var value = jQuery.urldecode(tmp[1]); 56 | if (key in result) 57 | result[key].push(value); 58 | else 59 | result[key] = [value]; 60 | } 61 | return result; 62 | }; 63 | 64 | /** 65 | * highlight a given string on a jquery object by wrapping it in 66 | * span elements with the given class name. 67 | */ 68 | jQuery.fn.highlightText = function(text, className) { 69 | function highlight(node) { 70 | if (node.nodeType == 3) { 71 | var val = node.nodeValue; 72 | var pos = val.toLowerCase().indexOf(text); 73 | if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { 74 | var span = document.createElement("span"); 75 | span.className = className; 76 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 77 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 78 | document.createTextNode(val.substr(pos + text.length)), 79 | node.nextSibling)); 80 | node.nodeValue = val.substr(0, pos); 81 | } 82 | } 83 | else if (!jQuery(node).is("button, select, textarea")) { 84 | jQuery.each(node.childNodes, function() { 85 | highlight(this); 86 | }); 87 | } 88 | } 89 | return this.each(function() { 90 | highlight(this); 91 | }); 92 | }; 93 | 94 | /* 95 | * backward compatibility for jQuery.browser 96 | * This will be supported until firefox bug is fixed. 97 | */ 98 | if (!jQuery.browser) { 99 | jQuery.uaMatch = function(ua) { 100 | ua = ua.toLowerCase(); 101 | 102 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 103 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 104 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 105 | /(msie) ([\w.]+)/.exec(ua) || 106 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 107 | []; 108 | 109 | return { 110 | browser: match[ 1 ] || "", 111 | version: match[ 2 ] || "0" 112 | }; 113 | }; 114 | jQuery.browser = {}; 115 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 116 | } 117 | 118 | /** 119 | * Small JavaScript module for the documentation. 120 | */ 121 | var Documentation = { 122 | 123 | init : function() { 124 | this.fixFirefoxAnchorBug(); 125 | this.highlightSearchWords(); 126 | this.initIndexTable(); 127 | 128 | }, 129 | 130 | /** 131 | * i18n support 132 | */ 133 | TRANSLATIONS : {}, 134 | PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, 135 | LOCALE : 'unknown', 136 | 137 | // gettext and ngettext don't access this so that the functions 138 | // can safely bound to a different name (_ = Documentation.gettext) 139 | gettext : function(string) { 140 | var translated = Documentation.TRANSLATIONS[string]; 141 | if (typeof translated == 'undefined') 142 | return string; 143 | return (typeof translated == 'string') ? translated : translated[0]; 144 | }, 145 | 146 | ngettext : function(singular, plural, n) { 147 | var translated = Documentation.TRANSLATIONS[singular]; 148 | if (typeof translated == 'undefined') 149 | return (n == 1) ? singular : plural; 150 | return translated[Documentation.PLURALEXPR(n)]; 151 | }, 152 | 153 | addTranslations : function(catalog) { 154 | for (var key in catalog.messages) 155 | this.TRANSLATIONS[key] = catalog.messages[key]; 156 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); 157 | this.LOCALE = catalog.locale; 158 | }, 159 | 160 | /** 161 | * add context elements like header anchor links 162 | */ 163 | addContextElements : function() { 164 | $('div[id] > :header:first').each(function() { 165 | $('\u00B6'). 166 | attr('href', '#' + this.id). 167 | attr('title', _('Permalink to this headline')). 168 | appendTo(this); 169 | }); 170 | $('dt[id]').each(function() { 171 | $('\u00B6'). 172 | attr('href', '#' + this.id). 173 | attr('title', _('Permalink to this definition')). 174 | appendTo(this); 175 | }); 176 | }, 177 | 178 | /** 179 | * workaround a firefox stupidity 180 | * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 181 | */ 182 | fixFirefoxAnchorBug : function() { 183 | if (document.location.hash) 184 | window.setTimeout(function() { 185 | document.location.href += ''; 186 | }, 10); 187 | }, 188 | 189 | /** 190 | * highlight the search words provided in the url in the text 191 | */ 192 | highlightSearchWords : function() { 193 | var params = $.getQueryParameters(); 194 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; 195 | if (terms.length) { 196 | var body = $('div.body'); 197 | if (!body.length) { 198 | body = $('body'); 199 | } 200 | window.setTimeout(function() { 201 | $.each(terms, function() { 202 | body.highlightText(this.toLowerCase(), 'highlighted'); 203 | }); 204 | }, 10); 205 | $('') 207 | .appendTo($('#searchbox')); 208 | } 209 | }, 210 | 211 | /** 212 | * init the domain index toggle buttons 213 | */ 214 | initIndexTable : function() { 215 | var togglers = $('img.toggler').click(function() { 216 | var src = $(this).attr('src'); 217 | var idnum = $(this).attr('id').substr(7); 218 | $('tr.cg-' + idnum).toggle(); 219 | if (src.substr(-9) == 'minus.png') 220 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); 221 | else 222 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); 223 | }).css('display', ''); 224 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { 225 | togglers.click(); 226 | } 227 | }, 228 | 229 | /** 230 | * helper function to hide the search marks again 231 | */ 232 | hideSearchWords : function() { 233 | $('#searchbox .highlight-link').fadeOut(300); 234 | $('span.highlighted').removeClass('highlighted'); 235 | }, 236 | 237 | /** 238 | * make the url absolute 239 | */ 240 | makeURL : function(relativeURL) { 241 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; 242 | }, 243 | 244 | /** 245 | * get the current relative url 246 | */ 247 | getCurrentURL : function() { 248 | var path = document.location.pathname; 249 | var parts = path.split(/\//); 250 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { 251 | if (this == '..') 252 | parts.pop(); 253 | }); 254 | var url = parts.join('/'); 255 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1); 256 | }, 257 | 258 | initOnKeyListeners: function() { 259 | $(document).keyup(function(event) { 260 | var activeElementType = document.activeElement.tagName; 261 | // don't navigate when in search box or textarea 262 | if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { 263 | switch (event.keyCode) { 264 | case 37: // left 265 | var prevHref = $('link[rel="prev"]').prop('href'); 266 | if (prevHref) { 267 | window.location.href = prevHref; 268 | return false; 269 | } 270 | case 39: // right 271 | var nextHref = $('link[rel="next"]').prop('href'); 272 | if (nextHref) { 273 | window.location.href = nextHref; 274 | return false; 275 | } 276 | } 277 | } 278 | }); 279 | } 280 | }; 281 | 282 | // quick alias for translations 283 | _ = Documentation.gettext; 284 | 285 | $(document).ready(function() { 286 | Documentation.init(); 287 | }); -------------------------------------------------------------------------------- /docs/build/html/_static/down-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/down-pressed.png -------------------------------------------------------------------------------- /docs/build/html/_static/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/down.png -------------------------------------------------------------------------------- /docs/build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/file.png -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/Inconsolata-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/fonts/Inconsolata-Bold.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/Inconsolata-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/fonts/Inconsolata-Regular.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/Lato-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/fonts/Lato-Bold.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/Lato-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/fonts/Lato-Regular.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/RobotoSlab-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/fonts/RobotoSlab-Bold.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/RobotoSlab-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/fonts/RobotoSlab-Regular.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/build/html/_static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/build/html/_static/js/theme.js: -------------------------------------------------------------------------------- 1 | require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o"); 77 | 78 | // Add expand links to all parents of nested ul 79 | $('.wy-menu-vertical ul').not('.simple').siblings('a').each(function () { 80 | var link = $(this); 81 | expand = $(''); 82 | expand.on('click', function (ev) { 83 | self.toggleCurrent(link); 84 | ev.stopPropagation(); 85 | return false; 86 | }); 87 | link.prepend(expand); 88 | }); 89 | }; 90 | 91 | nav.reset = function () { 92 | // Get anchor from URL and open up nested nav 93 | var anchor = encodeURI(window.location.hash); 94 | if (anchor) { 95 | try { 96 | var link = $('.wy-menu-vertical') 97 | .find('[href="' + anchor + '"]'); 98 | $('.wy-menu-vertical li.toctree-l1 li.current') 99 | .removeClass('current'); 100 | link.closest('li.toctree-l2').addClass('current'); 101 | link.closest('li.toctree-l3').addClass('current'); 102 | link.closest('li.toctree-l4').addClass('current'); 103 | } 104 | catch (err) { 105 | console.log("Error expanding nav for anchor", err); 106 | } 107 | } 108 | }; 109 | 110 | nav.onScroll = function () { 111 | this.winScroll = false; 112 | var newWinPosition = this.win.scrollTop(), 113 | winBottom = newWinPosition + this.winHeight, 114 | navPosition = this.navBar.scrollTop(), 115 | newNavPosition = navPosition + (newWinPosition - this.winPosition); 116 | if (newWinPosition < 0 || winBottom > this.docHeight) { 117 | return; 118 | } 119 | this.navBar.scrollTop(newNavPosition); 120 | this.winPosition = newWinPosition; 121 | }; 122 | 123 | nav.onResize = function () { 124 | this.winResize = false; 125 | this.winHeight = this.win.height(); 126 | this.docHeight = $(document).height(); 127 | }; 128 | 129 | nav.hashChange = function () { 130 | this.linkScroll = true; 131 | this.win.one('hashchange', function () { 132 | this.linkScroll = false; 133 | }); 134 | }; 135 | 136 | nav.toggleCurrent = function (elem) { 137 | var parent_li = elem.closest('li'); 138 | parent_li.siblings('li.current').removeClass('current'); 139 | parent_li.siblings().find('li.current').removeClass('current'); 140 | parent_li.find('> ul li.current').removeClass('current'); 141 | parent_li.toggleClass('current'); 142 | } 143 | 144 | return nav; 145 | }; 146 | 147 | module.exports.ThemeNav = ThemeNav(); 148 | 149 | if (typeof(window) != 'undefined') { 150 | window.SphinxRtdTheme = { StickyNav: module.exports.ThemeNav }; 151 | } 152 | 153 | },{"jquery":"jquery"}]},{},["sphinx-rtd-theme"]); 154 | -------------------------------------------------------------------------------- /docs/build/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/minus.png -------------------------------------------------------------------------------- /docs/build/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/plus.png -------------------------------------------------------------------------------- /docs/build/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ 8 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 9 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 10 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ 11 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 12 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 13 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 14 | .highlight .ge { font-style: italic } /* Generic.Emph */ 15 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 16 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 17 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 18 | .highlight .go { color: #333333 } /* Generic.Output */ 19 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 20 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 21 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 22 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 23 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 24 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 25 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 26 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 27 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 28 | .highlight .kt { color: #902000 } /* Keyword.Type */ 29 | .highlight .m { color: #208050 } /* Literal.Number */ 30 | .highlight .s { color: #4070a0 } /* Literal.String */ 31 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 32 | .highlight .nb { color: #007020 } /* Name.Builtin */ 33 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 34 | .highlight .no { color: #60add5 } /* Name.Constant */ 35 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 36 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 37 | .highlight .ne { color: #007020 } /* Name.Exception */ 38 | .highlight .nf { color: #06287e } /* Name.Function */ 39 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 40 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 41 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 42 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 43 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 44 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 45 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */ 46 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 47 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 48 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 49 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 50 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 51 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 52 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 53 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 54 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 55 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 56 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 57 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 58 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 59 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 60 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 61 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 62 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 63 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 64 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 65 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/build/html/_static/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.3.1 2 | // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Underscore is freely distributable under the MIT license. 4 | // Portions of Underscore are inspired or borrowed from Prototype, 5 | // Oliver Steele's Functional, and John Resig's Micro-Templating. 6 | // For all details and documentation: 7 | // http://documentcloud.github.com/underscore 8 | (function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== 9 | c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c, 10 | h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each= 11 | b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e2;a== 12 | null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect= 13 | function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e= 14 | e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck= 15 | function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a, 17 | c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}}; 24 | b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments, 25 | 1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)}; 26 | b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"}; 27 | b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a), 28 | function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+ 29 | u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]= 30 | function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain= 31 | true;return this};m.prototype.value=function(){return this._wrapped}}).call(this); 32 | -------------------------------------------------------------------------------- /docs/build/html/_static/up-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/up-pressed.png -------------------------------------------------------------------------------- /docs/build/html/_static/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/_static/up.png -------------------------------------------------------------------------------- /docs/build/html/filelist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | File listing made easy — PyDrive 1.3.1 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 | 50 | 104 | 105 |
106 | 107 | 108 | 112 | 113 | 114 | 115 |
116 |
117 | 118 | 119 | 120 | 121 | 122 | 123 |
124 |
    125 |
  • Docs »
  • 126 | 127 |
  • File listing made easy
  • 128 |
  • 129 | 130 | 131 | View page source 132 | 133 | 134 |
  • 135 |
136 |
137 |
138 |
139 |
140 | 141 |
142 |

File listing made easy

143 |

PyDrive handles paginations and parses response as list of GoogleDriveFile.

144 |
145 |

Get all files which matches the query

146 |

Create GoogleDriveFileList instance with parameters of Files.list() as dict. Call GetList() and you will get all files that matches your query as a list of GoogleDriveFile.

147 |
from pydrive.drive import GoogleDrive
148 | 
149 | drive = GoogleDrive(gauth) # Create GoogleDrive instance with authenticated GoogleAuth instance
150 | 
151 | # Auto-iterate through all files in the root folder.
152 | file_list = drive.ListFile({'q': "'root' in parents and trashed=false"}).GetList()
153 | for file1 in file_list:
154 |   print('title: %s, id: %s' % (file1['title'], file1['id']))
155 | 
156 |
157 |

You can update metadata or content of these GoogleDriveFile instances if you need it.

158 |
159 |
160 |

Paginate and iterate through files

161 |

PyDrive provides Pythonic way of paginating and iterating through list of files. All you have to do is to limit number of results with maxResults parameter and build for loop retrieving file list from API each iteration.

162 |

Sample code continues from above:

163 |
# Paginate file lists by specifying number of max results
164 | for file_list in drive.ListFile({'q': 'trashed=true', 'maxResults': 10}):
165 |   print('Received %s files from Files.list()' % len(file_list)) # <= 10
166 |   for file1 in file_list:
167 |       print('title: %s, id: %s' % (file1['title'], file1['id']))
168 | 
169 |
170 |
171 |
172 | 173 | 174 |
175 |
176 |
177 | 178 | 186 | 187 | 188 |
189 | 190 |
191 |

192 | © Copyright 2016, JunYoung Gwak, Scott Blevins, Robin Nabel, Google Inc.. 193 | 194 |

195 |
196 | Built with Sphinx using a theme provided by Read the Docs. 197 | 198 |
199 | 200 |
201 |
202 | 203 |
204 | 205 |
206 | 207 | 208 | 209 | 210 | 211 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 238 | 239 | 240 | 241 | -------------------------------------------------------------------------------- /docs/build/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Welcome to PyDrive’s documentation! — PyDrive 1.3.1 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 | 48 | 49 | 99 | 100 |
101 | 102 | 103 | 107 | 108 | 109 | 110 |
111 |
112 | 113 | 114 | 115 | 116 | 117 | 118 |
119 |
    120 |
  • Docs »
  • 121 | 122 |
  • Welcome to PyDrive’s documentation!
  • 123 |
  • 124 | 125 | 126 | View page source 127 | 128 | 129 |
  • 130 |
131 |
132 |
133 |
134 |
135 | 136 |
137 |

Welcome to PyDrive’s documentation!

138 |

PyDrive is a wrapper library of google-api-python-client that simplifies many common Google Drive API tasks.

139 |
140 |
141 |

Project Info

142 | 147 |
148 |
149 |

How to install

150 |

You can install PyDrive with regular pip command.

151 |
$ pip install PyDrive
152 | 
153 |
154 |

To install the current development version from GitHub, use:

155 |
$ pip install git+https://github.com/googledrive/PyDrive.git#egg=PyDrive
156 | 
157 |
158 |
159 | 201 |
202 |

Indices and tables

203 | 207 |
208 | 209 | 210 |
211 |
212 |
213 | 214 | 220 | 221 | 222 |
223 | 224 |
225 |

226 | © Copyright 2016, JunYoung Gwak, Scott Blevins, Robin Nabel, Google Inc.. 227 | 228 |

229 |
230 | Built with Sphinx using a theme provided by Read the Docs. 231 | 232 |
233 | 234 |
235 |
236 | 237 |
238 | 239 |
240 | 241 | 242 | 243 | 244 | 245 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 272 | 273 | 274 | 275 | -------------------------------------------------------------------------------- /docs/build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/docs/build/html/objects.inv -------------------------------------------------------------------------------- /docs/build/html/py-modindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Python Module Index — PyDrive 1.3.1 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
49 | 50 | 51 | 101 | 102 |
103 | 104 | 105 | 109 | 110 | 111 | 112 |
113 |
114 | 115 | 116 | 117 | 118 | 119 | 120 |
121 |
    122 |
  • Docs »
  • 123 | 124 |
  • 125 |
  • 126 | 127 | 128 | 129 |
  • 130 |
131 |
132 |
133 |
134 |
135 | 136 | 137 |

Python Module Index

138 | 139 |
140 | p 141 |
142 | 143 | 144 | 145 | 147 | 148 | 150 | 153 | 154 | 155 | 158 | 159 | 160 | 163 | 164 | 165 | 168 | 169 | 170 | 173 | 174 | 175 | 178 |
 
146 | p
151 | pydrive 152 |
    156 | pydrive.apiattr 157 |
    161 | pydrive.auth 162 |
    166 | pydrive.drive 167 |
    171 | pydrive.files 172 |
    176 | pydrive.settings 177 |
179 | 180 | 181 |
182 |
183 |
184 | 185 | 186 |
187 | 188 |
189 |

190 | © Copyright 2016, JunYoung Gwak, Scott Blevins, Robin Nabel, Google Inc.. 191 | 192 |

193 |
194 | Built with Sphinx using a theme provided by Read the Docs. 195 | 196 |
197 | 198 |
199 |
200 | 201 |
202 | 203 |
204 | 205 | 206 | 207 | 208 | 209 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 236 | 237 | 238 | 239 | -------------------------------------------------------------------------------- /docs/build/html/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Search — PyDrive 1.3.1 documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 | 47 | 48 | 98 | 99 |
100 | 101 | 102 | 106 | 107 | 108 | 109 |
110 |
111 | 112 | 113 | 114 | 115 | 116 | 117 |
118 |
    119 |
  • Docs »
  • 120 | 121 |
  • 122 |
  • 123 | 124 |
  • 125 |
126 |
127 |
128 |
129 |
130 | 131 | 139 | 140 | 141 |
142 | 143 |
144 | 145 |
146 |
147 |
148 | 149 | 150 |
151 | 152 |
153 |

154 | © Copyright 2016, JunYoung Gwak, Scott Blevins, Robin Nabel, Google Inc.. 155 | 156 |

157 |
158 | Built with Sphinx using a theme provided by Read the Docs. 159 | 160 |
161 | 162 |
163 |
164 | 165 |
166 | 167 |
168 | 169 | 170 | 171 | 172 | 173 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 201 | 202 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /docs/build/html/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({envversion:49,filenames:["filelist","filemanagement","index","oauth","pydrive","quickstart"],objects:{"pydrive.apiattr":{ApiAttribute:[4,1,1,""],ApiAttributeMixin:[4,1,1,""],ApiResource:[4,1,1,""],ApiResourceList:[4,1,1,""]},"pydrive.apiattr.ApiResource":{GetChanges:[4,2,1,""],UpdateMetadata:[4,2,1,""],auth:[4,3,1,""],update:[4,2,1,""]},"pydrive.apiattr.ApiResourceList":{GetList:[4,2,1,""],Reset:[4,2,1,""],metadata:[4,3,1,""]},"pydrive.auth":{AuthError:[4,4,1,""],AuthenticationError:[4,4,1,""],AuthenticationRejected:[4,4,1,""],CheckAuth:[4,5,1,""],CheckServiceAuth:[4,5,1,""],GoogleAuth:[4,1,1,""],InvalidCredentialsError:[4,4,1,""],LoadAuth:[4,5,1,""],RefreshError:[4,4,1,""]},"pydrive.auth.GoogleAuth":{Auth:[4,2,1,""],Authenticate:[4,2,1,""],Authorize:[4,2,1,""],CLIENT_CONFIGS_LIST:[4,3,1,""],CommandLineAuth:[4,2,1,""],DEFAULT_SETTINGS:[4,3,1,""],GetAuthUrl:[4,2,1,""],GetFlow:[4,2,1,""],Get_Http_Object:[4,2,1,""],LoadClientConfig:[4,2,1,""],LoadClientConfigFile:[4,2,1,""],LoadClientConfigSettings:[4,2,1,""],LoadCredentials:[4,2,1,""],LoadCredentialsFile:[4,2,1,""],LoadServiceConfigSettings:[4,2,1,""],LocalWebserverAuth:[4,2,1,""],Refresh:[4,2,1,""],SERVICE_CONFIGS_LIST:[4,3,1,""],SaveCredentials:[4,2,1,""],SaveCredentialsFile:[4,2,1,""],ServiceAuth:[4,2,1,""],access_token_expired:[4,3,1,""],auth_method:[4,3,1,""],client_config:[4,3,1,""],credentials:[4,3,1,""],flow:[4,3,1,""],http:[4,3,1,""],service:[4,3,1,""],settings:[4,3,1,""]},"pydrive.drive":{GoogleDrive:[4,1,1,""]},"pydrive.drive.GoogleDrive":{CreateFile:[4,2,1,""],GetAbout:[4,2,1,""],ListFile:[4,2,1,""]},"pydrive.files":{ApiRequestError:[4,4,1,""],FileNotDownloadableError:[4,4,1,""],FileNotUploadedError:[4,4,1,""],GoogleDriveFile:[4,1,1,""],GoogleDriveFileList:[4,1,1,""],LoadMetadata:[4,5,1,""]},"pydrive.files.GoogleDriveFile":{Delete:[4,2,1,""],DeletePermission:[4,2,1,""],FetchContent:[4,2,1,""],FetchMetadata:[4,2,1,""],GetContentFile:[4,2,1,""],GetContentString:[4,2,1,""],GetPermissions:[4,2,1,""],InsertPermission:[4,2,1,""],SetContentFile:[4,2,1,""],SetContentString:[4,2,1,""],Trash:[4,2,1,""],UnTrash:[4,2,1,""],Upload:[4,2,1,""],content:[4,3,1,""],metadata:[4,3,1,""],uploaded:[4,3,1,""]},"pydrive.settings":{InvalidConfigError:[4,4,1,""],LoadSettingsFile:[4,5,1,""],SettingsError:[4,4,1,""],ValidateSettings:[4,5,1,""]},pydrive:{apiattr:[4,0,0,"-"],auth:[4,0,0,"-"],drive:[4,0,0,"-"],files:[4,0,0,"-"],settings:[4,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","attribute","Python attribute"],"4":["py","exception","Python exception"],"5":["py","function","Python function"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:attribute","4":"py:exception","5":"py:function"},terms:{"abstract":4,"byte":[1,4],"case":[1,3],"class":4,"default":[3,4],"function":[1,4],"import":[0,1,3,5],"long":5,"return":4,"super":4,"true":[0,1,3,4],"try":4,"while":[3,4],about:[1,4],abov:[0,1,3],accept:5,access:[1,3,4],access_token:4,access_token_expir:4,accord:4,account:[3,4],actual:1,add:[1,5],addit:4,addition:4,advanc:1,afshar:1,after:[1,4],again:5,allow:1,along:3,alreadi:1,also:[1,3],altern:3,alternatelink:1,ani:[1,3,4],anyon:1,api:[0,1,2,3,4,5],apiattr:[],apiattribut:4,apiattributemixin:4,apirequesterror:4,apiresourc:4,apiresourcelist:4,app:3,appdata:1,applic:[1,3,4,5],applicat:5,appropri:5,arg:4,arrai:1,ask:[3,5],askusertovisitlinkandgivecod:3,auth:[],auth_method:4,auth_uri:[3,4],auth_url:3,authent:[],authenticationerror:4,authenticationreject:4,autherror:4,author:[3,4,5],auto:[0,3,5],automat:[],avail:[1,4],back:4,backend:[3,4],base:4,basic:1,begin:1,behav:4,behavior:3,bom:[1,4],bool:[3,4],browser:[3,5],build:[],built:3,bytesio:4,call:[0,1,3,4,5],can:[0,1,2,3,4,5],care:1,cat:1,catlov:1,chang:[1,4],charact:1,check:[1,4],checkauth:4,checkserviceauth:4,choos:4,claudio:1,clean:[3,4],click:5,client:[2,3,4,5],client_config:[3,4],client_config_backend:[3,4],client_config_fil:[3,4],client_configs_list:4,client_id:[3,4],client_pkcs12_file_path:4,client_secret:[3,4,5],client_secret_:5,client_service_email:4,client_user_email:4,close:4,code:[0,1,3,4,5],com:[2,3,4],comma:4,command:[2,3,4],commandlin:4,commandlineauth:[3,4],comment:3,common:[2,4],complex:[3,5],complic:3,config:4,configur:[3,4,5],confus:1,consent:5,consist:1,consol:[3,4,5],contain:[3,4],content:[],content_str:1,continu:[0,1],copi:[3,4,5],correspond:4,corrupt:1,creat:[],createfil:[1,4,5],credenti:[3,4,5],credentials_fil:4,current:[2,4],custom:[],data:4,decod:4,decor:4,decorate:4,default_settings:4,defin:1,deletepermiss:[1,4],describ:1,descriptor:4,destin:[3,4],detail:[3,4,5],develop:2,dict:[0,1,3,4],dictionari:[3,4],did:1,differ:1,difficult:3,directori:[3,5],displai:1,document:[],doesn:4,don:[1,3,4],done:5,download_url:4,drive:[],each:[0,4],easier:5,editor:3,effici:4,egg:2,either:1,element:4,email:4,empti:4,enabl:5,encod:[1,4],endpoint:3,enter:5,entir:3,entri:[4,5],equival:4,error:4,etc:[1,4],everi:4,exampl:[1,3],except:4,exception:4,execut:5,exist:[3,4],expir:4,explain:3,fals:[0,3,4,5],familynam:1,fetch:[1,4],fetch_al:[1,4],fetchcont:4,fetchmetadata:[1,4],field:[1,3,4],file1:[0,1,5],file2:1,file4:1,file5:1,file6:1,file7:1,file_id:1,file_list:[0,5],file_object:1,filelist:4,filenam:[1,4],filenotdownloadableerror:4,filenotuploadederror:4,find:1,finish:5,firstnam:1,flow:[],folder:[0,5],follow:5,follw:3,format:[1,4],found:1,from:[],further:1,gauth:[0,1,3,5],gener:4,get_http_object:4,get_refresh_token:3,getabout:4,getauthurl:[3,4],getchang:4,getcontentfil:[1,4],getcontentstr:[1,4],getflow:4,getlist:[0,4,5],getpermiss:[1,4],git:2,github:[1,2],give:1,given:[4,5],global:4,googl:[1,2,3,4,5],googleapi:[3,4],googleauth:[0,1,3,4,5],googledr:[0,1,2,4,5],googledrivefil:[0,1,4,5],googledrivefilelist:[0,4],googleusercont:3,grant:[3,4],guid:4,handl:[],hard:4,have:[0,1,3,4,5],hello:[1,5],helloworld:1,here:[1,3],hold:3,homepag:2,host:4,host_nam:4,howev:[1,3],http:[2,3,4,5],http_timeout:4,httplib2:4,ietf:3,illustr:1,imag:1,immedi:1,includ:[1,4],index:2,inform:[1,4,5],inherit:4,initi:[1,4],initial:1,input:5,insertpermiss:[1,4],instanc:[0,1,4,5],instruct:[3,5],integr:3,interfac:1,invalidconfigerror:4,invalidcredentialserror:4,ioerror:4,iso:1,iterat:4,itself:[1,3],javascript:5,john:1,json:[1,3,4,5],just:[1,3],keep:1,kei:4,know:5,known:[1,4],kwarg:4,label:[1,4],last:1,lastnam:1,lead:[1,3],left:5,len:0,let:5,librari:[2,4],life:5,like:[1,4],limit:0,line:[],link:[1,3],listfil:[0,4,5],load:4,loadauth:4,loadclientconfig:4,loadclientconfigfil:4,loadclientconfigset:4,loadcredenti:4,loadcredentialsfil:4,loadmetadata:4,loadserviceconfigset:4,loadsettingsfil:4,local:[3,4,5],localhost:5,localwebserverauth:[3,4,5],look:[1,3,5],loop:0,lot:3,machin:3,mai:1,main:4,maintain:3,make:[1,3,4,5],mani:[1,2,5],manipul:1,manual:3,mark:[1,4],max:0,maxresult:[0,4],menu:5,metadata:[],method:[1,3,4,5],might:[1,3],mimetyp:[1,4],mind:4,mixin:4,modifi:[1,4],modul:[],more:[1,3,5],most:[3,4],move:[1,4],much:5,name:[4,5],necessari:4,need:[0,3,5],new_permiss:4,none:[3,4],note:1,now:[1,3,5],number:[0,4],oauth2:[3,4,5],oauth2callback:3,oauth2client:4,oauth:[],oauth_scop:[3,4],object:[1,4],offici:[1,4],official:2,onc:4,once:5,onli:3,oob:3,open:[1,4,5],optim:[1,5],option:1,order:[1,4],org:2,origin:5,otherwis:4,out:[1,3,4],overwritten:4,own:[],p12:4,packag:[],page:2,pagetoken:4,param:4,paramet:[0,1,4],parent:[0,5],pars:[0,5],parser:1,past:[3,5],path:[3,4],perform:4,perman:1,permission_id:[1,4],permit:1,pip:2,place:[3,5],plain:[1,4],pleas:4,png:1,port:4,port_numb:4,possibl:3,print:[0,1,4,5],privat:4,product:5,provid:[0,1,3,4],psdskoowr1p602pxrthi:3,pydriv:[],pypi:2,python:[0,2,4,5],quickstart:[],quota:4,rais:4,read:[1,3,4],readabl:1,reader:1,realli:5,receiv:[0,3],recommend:1,recov:1,redirect:[3,5],redirect_uri:[3,4],refresh:[3,4],refresherror:4,regular:2,reject:4,relat:4,remot:3,remove_bom:[1,4],renam:5,replac:1,repositori:1,request:4,requir:[3,4,5],reset:4,resourc:[1,4],respons:[0,5],result:0,retriev:[0,1,3,4],revok:3,revoke_uri:[3,4],right:5,role:1,root:[0,5],rtype:4,run:5,runtimeerror:4,safeti:4,sampl:[],save:[3,4],save_credenti:[3,4],save_credentials_backend:3,save_credentials_fil:3,savecredenti:4,savecredentialsfil:4,scope:3,screen:5,search:[2,5],secret:3,section:3,see:[1,4,5],select:5,self:4,sent:4,separ:4,server:[3,4],servic:[3,4],service_configs_list:4,serviceauth:4,set:[],setcontentfil:[1,4],setcontentstr:[1,4,5],settings_fil:4,settingserror:4,setup:3,sharabl:1,share:1,shorthand:1,side:5,silent:3,simpl:3,simplifi:2,site:3,six:4,size:1,smith:1,some:[1,3],someon:3,specif:[1,4],specifi:[0,4],standalon:4,start:3,step:[3,5],store:1,str:[3,4],string:[1,4,5],strip:4,strip_bom_exampl:1,succe:4,successfulli:1,take:[3,5],target:4,task:2,text:[1,4],them:4,thi:[1,3,4,5],thing:3,thread:4,time:1,titl:[0,1,4,5],token:[3,4],token_uri:[3,4],too:3,trash:[],tri:4,two:[],txt:[1,5],type:[1,4,5],untrash:[1,4],updat:[],updatemetadata:4,uri:[3,5],url:[3,4],urn:3,usag:4,user:[1,3,4],utf:4,valid:[3,4],validateset:4,valu:[1,3,4],variabl:4,version:2,view:1,visit:[3,4],wai:0,want:[1,3,4,5],web:[1,4,5],webserv:[3,5],websit:3,what:1,when:[1,3,4],where:[3,4],whether:[1,4],who:1,work:[3,5],world:[1,5],would:4,wrapper:[2,4],write:4,www:[3,4],yaml:[],you:[0,1,2,3,4,5],your:[]},titles:["File listing made easy","File management made easy","Welcome to PyDrive’s documentation!","OAuth made easy","pydrive package","Quickstart"],titleterms:{"new":1,all:0,apiattr:4,auth:4,authent:[3,5],automat:3,build:3,content:[1,2],creat:5,custom:3,delet:1,document:2,download:1,drive:4,easi:[0,1,3],file:[0,1,4,5],flow:3,from:1,get:[0,5],handl:1,how:2,indice:2,info:2,insert:1,instal:2,iter:0,line:3,list:[0,1,5],made:[0,1,3],manag:1,match:0,metadata:1,modul:4,oauth:3,own:3,packag:4,pagin:0,permiss:1,project:2,pydriv:[2,4],queri:0,quickstart:5,remov:1,sampl:3,set:[3,4],special:1,tabl:2,through:0,trash:1,two:3,updat:[1,5],update:1,upload:1,welcom:2,which:0,yaml:3,your:3}}) -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # PyDrive documentation build configuration file, created by 4 | # sphinx-quickstart on Sun Jun 12 23:01:40 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | sys.path.insert(0, os.path.abspath('../')) 22 | #exclude_patterns = ['_build', '**tests**', '**spi**'] 23 | exclude_dirnames = ["test"] 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | #needs_sphinx = '1.0' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [ 33 | 'sphinx.ext.autodoc', 34 | ] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ['_templates'] 38 | 39 | # The suffix(es) of source filenames. 40 | # You can specify multiple suffix as a list of string: 41 | # source_suffix = ['.rst', '.md'] 42 | source_suffix = '.rst' 43 | 44 | # The encoding of source files. 45 | #source_encoding = 'utf-8-sig' 46 | 47 | # The master toctree document. 48 | master_doc = 'index' 49 | 50 | # General information about the project. 51 | project = u'PyDrive' 52 | copyright = u'2016, JunYoung Gwak, Scott Blevins, Robin Nabel, Google Inc.' 53 | author = u'JunYoung Gwak, Scott Blevins, Robin Nabel' 54 | 55 | # The version info for the project you're documenting, acts as replacement for 56 | # |version| and |release|, also used in various other places throughout the 57 | # built documents. 58 | # 59 | # The short X.Y version. 60 | version = u'1.3.1' 61 | # The full version, including alpha/beta/rc tags. 62 | release = u'1.3.1' 63 | 64 | # The language for content autogenerated by Sphinx. Refer to documentation 65 | # for a list of supported languages. 66 | # 67 | # This is also used if you do content translation via gettext catalogs. 68 | # Usually you set "language" from the command line for these cases. 69 | language = None 70 | 71 | # There are two options for replacing |today|: either, you set today to some 72 | # non-false value, then it is used: 73 | #today = '' 74 | # Else, today_fmt is used as the format for a strftime call. 75 | #today_fmt = '%B %d, %Y' 76 | 77 | # List of patterns, relative to source directory, that match files and 78 | # directories to ignore when looking for source files. 79 | exclude_patterns = ['_build', 80 | 'pydrive/test/*', 81 | 'test/*', 82 | 'pydrive/test', 83 | '../pydrive/test'] 84 | 85 | # The reST default role (used for this markup: `text`) to use for all 86 | # documents. 87 | #default_role = None 88 | 89 | # If true, '()' will be appended to :func: etc. cross-reference text. 90 | #add_function_parentheses = True 91 | 92 | # If true, the current module name will be prepended to all description 93 | # unit titles (such as .. function::). 94 | #add_module_names = True 95 | 96 | # If true, sectionauthor and moduleauthor directives will be shown in the 97 | # output. They are ignored by default. 98 | #show_authors = False 99 | 100 | # The name of the Pygments (syntax highlighting) style to use. 101 | pygments_style = 'sphinx' 102 | 103 | # A list of ignored prefixes for module index sorting. 104 | #modindex_common_prefix = [] 105 | 106 | # If true, keep warnings as "system message" paragraphs in the built documents. 107 | #keep_warnings = False 108 | 109 | # If true, `todo` and `todoList` produce output, else they produce nothing. 110 | todo_include_todos = False 111 | 112 | 113 | # -- Options for HTML output ---------------------------------------------- 114 | 115 | # The theme to use for HTML and HTML Help pages. See the documentation for 116 | # a list of builtin themes. 117 | html_theme = 'sphinx_rtd_theme' 118 | 119 | # Theme options are theme-specific and customize the look and feel of a theme 120 | # further. For a list of options available for each theme, see the 121 | # documentation. 122 | #html_theme_options = {} 123 | 124 | # Add any paths that contain custom themes here, relative to this directory. 125 | #html_theme_path = [] 126 | 127 | # The name for this set of Sphinx documents. If None, it defaults to 128 | # " v documentation". 129 | #html_title = None 130 | 131 | # A shorter title for the navigation bar. Default is the same as html_title. 132 | #html_short_title = None 133 | 134 | # The name of an image file (relative to this directory) to place at the top 135 | # of the sidebar. 136 | #html_logo = None 137 | 138 | # The name of an image file (relative to this directory) to use as a favicon of 139 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 140 | # pixels large. 141 | #html_favicon = None 142 | 143 | # Add any paths that contain custom static files (such as style sheets) here, 144 | # relative to this directory. They are copied after the builtin static files, 145 | # so a file named "default.css" will overwrite the builtin "default.css". 146 | html_static_path = ['_static'] 147 | 148 | # Add any extra paths that contain custom files (such as robots.txt or 149 | # .htaccess) here, relative to this directory. These files are copied 150 | # directly to the root of the documentation. 151 | #html_extra_path = [] 152 | 153 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 154 | # using the given strftime format. 155 | #html_last_updated_fmt = '%b %d, %Y' 156 | 157 | # If true, SmartyPants will be used to convert quotes and dashes to 158 | # typographically correct entities. 159 | #html_use_smartypants = True 160 | 161 | # Custom sidebar templates, maps document names to template names. 162 | #html_sidebars = {} 163 | 164 | # Additional templates that should be rendered to pages, maps page names to 165 | # template names. 166 | #html_additional_pages = {} 167 | 168 | # If false, no module index is generated. 169 | #html_domain_indices = True 170 | 171 | # If false, no index is generated. 172 | #html_use_index = True 173 | 174 | # If true, the index is split into individual pages for each letter. 175 | #html_split_index = False 176 | 177 | # If true, links to the reST sources are added to the pages. 178 | #html_show_sourcelink = True 179 | 180 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 181 | #html_show_sphinx = True 182 | 183 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 184 | #html_show_copyright = True 185 | 186 | # If true, an OpenSearch description file will be output, and all pages will 187 | # contain a tag referring to it. The value of this option must be the 188 | # base URL from which the finished HTML is served. 189 | #html_use_opensearch = '' 190 | 191 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 192 | #html_file_suffix = None 193 | 194 | # Language to be used for generating the HTML full-text search index. 195 | # Sphinx supports the following languages: 196 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 197 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 198 | #html_search_language = 'en' 199 | 200 | # A dictionary with options for the search language support, empty by default. 201 | # Now only 'ja' uses this config value 202 | #html_search_options = {'type': 'default'} 203 | 204 | # The name of a javascript file (relative to the configuration directory) that 205 | # implements a search results scorer. If empty, the default will be used. 206 | #html_search_scorer = 'scorer.js' 207 | 208 | # Output file base name for HTML help builder. 209 | htmlhelp_basename = 'PyDrivedoc' 210 | 211 | # -- Options for LaTeX output --------------------------------------------- 212 | 213 | latex_elements = { 214 | # The paper size ('letterpaper' or 'a4paper'). 215 | #'papersize': 'letterpaper', 216 | 217 | # The font size ('10pt', '11pt' or '12pt'). 218 | #'pointsize': '10pt', 219 | 220 | # Additional stuff for the LaTeX preamble. 221 | #'preamble': '', 222 | 223 | # Latex figure (float) alignment 224 | #'figure_align': 'htbp', 225 | } 226 | 227 | # Grouping the document tree into LaTeX files. List of tuples 228 | # (source start file, target name, title, 229 | # author, documentclass [howto, manual, or own class]). 230 | latex_documents = [ 231 | (master_doc, 'PyDrive.tex', u'PyDrive Documentation', 232 | u'JunYoung Gwak, Scott Blevins, Robin Nabel', 'manual'), 233 | ] 234 | 235 | # The name of an image file (relative to this directory) to place at the top of 236 | # the title page. 237 | #latex_logo = None 238 | 239 | # For "manual" documents, if this is true, then toplevel headings are parts, 240 | # not chapters. 241 | #latex_use_parts = False 242 | 243 | # If true, show page references after internal links. 244 | #latex_show_pagerefs = False 245 | 246 | # If true, show URL addresses after external links. 247 | #latex_show_urls = False 248 | 249 | # Documents to append as an appendix to all manuals. 250 | #latex_appendices = [] 251 | 252 | # If false, no module index is generated. 253 | #latex_domain_indices = True 254 | 255 | 256 | # -- Options for manual page output --------------------------------------- 257 | 258 | # One entry per manual page. List of tuples 259 | # (source start file, name, description, authors, manual section). 260 | man_pages = [ 261 | (master_doc, 'pydrive', u'PyDrive Documentation', 262 | [author], 1) 263 | ] 264 | 265 | # If true, show URL addresses after external links. 266 | #man_show_urls = False 267 | 268 | 269 | # -- Options for Texinfo output ------------------------------------------- 270 | 271 | # Grouping the document tree into Texinfo files. List of tuples 272 | # (source start file, target name, title, author, 273 | # dir menu entry, description, category) 274 | texinfo_documents = [ 275 | (master_doc, 'PyDrive', u'PyDrive Documentation', 276 | author, 'PyDrive', 'One line description of project.', 277 | 'Miscellaneous'), 278 | ] 279 | 280 | # Documents to append as an appendix to all manuals. 281 | #texinfo_appendices = [] 282 | 283 | # If false, no module index is generated. 284 | #texinfo_domain_indices = True 285 | 286 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 287 | #texinfo_show_urls = 'footnote' 288 | 289 | # If true, do not generate a @detailmenu in the "Top" node's menu. 290 | #texinfo_no_detailmenu = False 291 | -------------------------------------------------------------------------------- /docs/filelist.rst: -------------------------------------------------------------------------------- 1 | File listing made easy 2 | ============================= 3 | 4 | *PyDrive* handles paginations and parses response as list of `GoogleDriveFile`_. 5 | 6 | Get all files which matches the query 7 | ------------------------------------- 8 | 9 | Create `GoogleDriveFileList`_ instance with `parameters of Files.list()`_ as ``dict``. Call `GetList()`_ and you will get all files that matches your query as a list of `GoogleDriveFile`_. 10 | 11 | .. code-block:: python 12 | 13 | from pydrive.drive import GoogleDrive 14 | 15 | drive = GoogleDrive(gauth) # Create GoogleDrive instance with authenticated GoogleAuth instance 16 | 17 | # Auto-iterate through all files in the root folder. 18 | file_list = drive.ListFile({'q': "'root' in parents and trashed=false"}).GetList() 19 | for file1 in file_list: 20 | print('title: %s, id: %s' % (file1['title'], file1['id'])) 21 | 22 | You can update metadata or content of these `GoogleDriveFile`_ instances if you need it. 23 | 24 | Paginate and iterate through files 25 | ---------------------------------- 26 | 27 | *PyDrive* provides Pythonic way of paginating and iterating through list of files. All you have to do is to limit number of results with ``maxResults`` parameter and build ``for`` loop retrieving file list from API each iteration. 28 | 29 | Sample code continues from above: 30 | 31 | .. code-block:: python 32 | 33 | # Paginate file lists by specifying number of max results 34 | for file_list in drive.ListFile({'q': 'trashed=true', 'maxResults': 10}): 35 | print('Received %s files from Files.list()' % len(file_list)) # <= 10 36 | for file1 in file_list: 37 | print('title: %s, id: %s' % (file1['title'], file1['id'])) 38 | 39 | 40 | .. _`GoogleDriveFile`: ./pydrive.html#pydrive.files.GoogleDriveFile 41 | .. _`GoogleDriveFileList`: ./pydrive.html#pydrive.files.GoogleDriveFileList 42 | .. _`parameters of Files.list()`: https://developers.google.com/drive/v2/reference/files/list#request 43 | .. _`GetList()`: ./pydrive.html#pydrive.apiattr.ApiResourceList.GetList 44 | -------------------------------------------------------------------------------- /docs/filemanagement.rst: -------------------------------------------------------------------------------- 1 | File management made easy 2 | ========================= 3 | 4 | There are many methods to create and update file metadata and contents. 5 | With *PyDrive*, you don't have to care about any of these different API methods. 6 | Manipulate file metadata and contents from `GoogleDriveFile`_ object and call 7 | `Upload()`_. *PyDrive* will make the optimal API call for you. 8 | 9 | Upload a new file 10 | ----------------- 11 | 12 | Here is a sample code to upload a file. ``gauth`` is an authenticated `GoogleAuth`_ object. 13 | 14 | .. code-block:: python 15 | 16 | from pydrive.drive import GoogleDrive 17 | 18 | # Create GoogleDrive instance with authenticated GoogleAuth instance. 19 | drive = GoogleDrive(gauth) 20 | 21 | # Create GoogleDriveFile instance with title 'Hello.txt'. 22 | file1 = drive.CreateFile({'title': 'Hello.txt'}) 23 | file1.Upload() # Upload the file. 24 | print('title: %s, id: %s' % (file1['title'], file1['id'])) 25 | # title: Hello.txt, id: {{FILE_ID}} 26 | 27 | Now, you will have a file 'Hello.txt' uploaded to your Google Drive. You can open it from web interface to check its content, 'Hello World!'. 28 | 29 | Note that `CreateFile()`_ will create `GoogleDriveFile`_ instance but not actually upload a file to Google Drive. You can initialize `GoogleDriveFile`_ object by itself. However, it is not recommended to do so in order to keep authentication consistent. 30 | 31 | Delete, Trash and un-Trash files 32 | -------------------------------- 33 | You may want to delete, trash, or un-trash a file. To do this use ``Delete()``, 34 | ``Trash()`` or ``UnTrash()`` on a GoogleDriveFile object. 35 | 36 | *Note:* ``Trash()`` *moves a file into the trash and can be recovered,* 37 | ``Delete()`` *deletes the file permanently and immediately.* 38 | 39 | .. code-block:: python 40 | 41 | # Create GoogleDriveFile instance and upload it. 42 | file1 = drive.CreateFile() 43 | file1.Upload() 44 | 45 | file1.Trash() # Move file to trash. 46 | file1.UnTrash() # Move file out of trash. 47 | file1.Delete() # Permanently delete the file. 48 | 49 | Update file metadata 50 | -------------------- 51 | 52 | You can manipulate file metadata from a `GoogleDriveFile`_ object just as you manipulate a ``dict``. 53 | The format of file metadata can be found in the Google Drive API documentation: `Files resource`_. 54 | 55 | Sample code continues from `Upload a new file`_: 56 | 57 | .. code-block:: python 58 | 59 | file1['title'] = 'HelloWorld.txt' # Change title of the file. 60 | file1.Upload() # Update metadata. 61 | print('title: %s' % file1['title']) # title: HelloWorld.txt. 62 | 63 | Now, the title of your file has changed to 'HelloWorld.txt'. 64 | 65 | Download file metadata from file ID 66 | ----------------------------------- 67 | 68 | You might want to get file metadata from file ID. In that case, just initialize 69 | `GoogleDriveFile`_ with file ID and access metadata from `GoogleDriveFile`_ 70 | just as you access ``dict``. 71 | 72 | Sample code continues from above: 73 | 74 | .. code-block:: python 75 | 76 | # Create GoogleDriveFile instance with file id of file1. 77 | file2 = drive.CreateFile({'id': file1['id']}) 78 | print('title: %s, mimeType: %s' % (file2['title'], file2['mimeType'])) 79 | # title: HelloWorld.txt, mimeType: text/plain 80 | 81 | Handling special metadata 82 | ------------------------- 83 | 84 | Not all metadata can be set with the methods described above. 85 | PyDrive gives you access to the metadata of an object through 86 | ``file_object.FetchMetadata()``. This function has two optional parameters: 87 | ``fields`` and ``fetch_all``. 88 | 89 | .. code-block:: python 90 | 91 | file1 = drive.CreateFile({'id': ''}) 92 | 93 | # Fetches all basic metadata fields, including file size, last modified etc. 94 | file1.FetchMetadata() 95 | 96 | # Fetches all metadata available. 97 | file1.FetchMetadata(fetch_all=True) 98 | 99 | # Fetches the 'permissions' metadata field. 100 | file1.FetchMetadata(fields='permissions') 101 | # You can update a list of specific fields like this: 102 | file1.FetchMetadata(fields='permissions,labels,mimeType') 103 | 104 | For more information on available metadata fields have a look at the 105 | `official documentation`_. 106 | 107 | Insert permissions 108 | __________________ 109 | Insert, retrieving or deleting permissions is illustrated by making a file 110 | readable to all who have a link to the file. 111 | 112 | .. code-block:: python 113 | 114 | file1 = drive.CreateFile() 115 | file1.Upload() 116 | 117 | # Insert the permission. 118 | permission = file1.InsertPermission({ 119 | 'type': 'anyone', 120 | 'value': 'anyone', 121 | 'role': 'reader'}) 122 | 123 | print(file1['alternateLink']) # Display the sharable link. 124 | 125 | Note: ``InsertPermission()`` calls ``GetPermissions()`` after successfully 126 | inserting the permission. 127 | 128 | You can find more information on the permitted fields of a permission 129 | `here `_. 130 | This file is now shared and anyone with the link can view it. But what if you 131 | want to check whether a file is already shared? 132 | 133 | List permissions 134 | ________________ 135 | 136 | Permissions can be fetched using the ``GetPermissions()`` function of a 137 | ``GoogleDriveFile``, and can be used like so: 138 | 139 | .. code-block:: python 140 | 141 | # Create a new file 142 | file1 = drive.CreateFile() 143 | # Fetch permissions. 144 | permissions = file1.GetPermissions() 145 | print(permissions) 146 | 147 | # The permissions are also available as file1['permissions']: 148 | print(file1['permissions']) 149 | 150 | For the more advanced user: ``GetPermissions()`` is a shorthand for: 151 | 152 | .. code-block:: python 153 | 154 | # Fetch Metadata, including the permissions field. 155 | file1.FetchMetadata(fields='permissions') 156 | 157 | # The permissions array is now available for further use. 158 | print(file1['permissions']) 159 | 160 | Remove a Permission 161 | ___________________ 162 | *PyDrive* allows you to remove a specific permission using the 163 | ``DeletePermission(permission_id)`` function. This function allows you to delete 164 | one permission at a time by providing the permission's ID. 165 | 166 | .. code-block:: python 167 | 168 | file1 = drive.CreateFile({'id': ''}) 169 | permissions = file1.GetPermissions() # Download file permissions. 170 | 171 | permission_id = permissions[1]['id'] # Get a permission ID. 172 | 173 | file1.DeletePermission(permission_id) # Delete the permission. 174 | 175 | Upload and update file content 176 | ------------------------------ 177 | 178 | Managing file content is as easy as managing file metadata. You can set file 179 | content with either `SetContentFile(filename)`_ or `SetContentString(content)`_ 180 | and call `Upload()`_ just as you did to upload or update file metadata. 181 | 182 | Sample code continues from `Download file metadata from file ID`_: 183 | 184 | .. code-block:: python 185 | 186 | file4 = drive.CreateFile({'title':'appdata.json', 'mimeType':'application/json'}) 187 | file4.SetContentString('{"firstname": "John", "lastname": "Smith"}') 188 | file4.Upload() # Upload file. 189 | file4.SetContentString('{"firstname": "Claudio", "lastname": "Afshar"}') 190 | file4.Upload() # Update content of the file. 191 | 192 | file5 = drive.CreateFile() 193 | # Read file and set it as a content of this instance. 194 | file5.SetContentFile('cat.png') 195 | file5.Upload() # Upload the file. 196 | print('title: %s, mimeType: %s' % (file5['title'], file5['mimeType'])) 197 | # title: cat.png, mimeType: image/png 198 | 199 | **Advanced Users:** If you call SetContentFile and GetContentFile you can can 200 | define which character encoding is to be used by using the optional 201 | parameter `encoding`. 202 | 203 | If you, for example, are retrieving a file which is stored on your Google 204 | Drive which is encoded with ISO-8859-1, then you can get the content string 205 | like so: 206 | 207 | .. code-block:: python 208 | 209 | content_string = file4.GetContentString(encoding='ISO-8859-1') 210 | 211 | Download file content 212 | --------------------- 213 | 214 | Just as you uploaded file content, you can download it using 215 | `GetContentFile(filename)`_ or `GetContentString()`_. 216 | 217 | Sample code continues from above: 218 | 219 | .. code-block:: python 220 | 221 | # Initialize GoogleDriveFile instance with file id. 222 | file6 = drive.CreateFile({'id': file5['id']}) 223 | file6.GetContentFile('catlove.png') # Download file as 'catlove.png'. 224 | 225 | # Initialize GoogleDriveFile instance with file id. 226 | file7 = drive.CreateFile({'id': file4['id']}) 227 | content = file7.GetContentString() 228 | # content: '{"firstname": "Claudio", "lastname": "Afshar"}' 229 | 230 | file7.SetContentString(content.replace('lastname', 'familyname')) 231 | file7.Upload() 232 | # Uploaded content: '{"firstname": "Claudio", "familyname": "Afshar"}' 233 | 234 | **Advanced users**: Google Drive is `known`_ to add BOM (Byte Order Marks) to 235 | the beginning of some files, such as Google Documents downloaded as text files. 236 | In some cases confuses parsers and leads to corrupt files. 237 | PyDrive can remove the BOM from the beginning of a file when it 238 | is downloaded. Just set the `remove_bom` parameter in `GetContentString()` or 239 | `GetContentFile()` - see `examples/strip_bom_example.py` in the GitHub 240 | repository for an example. 241 | 242 | 243 | .. _`GoogleDriveFile`: ./pydrive.html#pydrive.files.GoogleDriveFile 244 | .. _`Upload()`: ./pydrive.html#pydrive.files.GoogleDriveFile.Upload 245 | .. _`GoogleAuth`: ./pydrive.html#pydrive.auth.GoogleAuth 246 | .. _`CreateFile()`: ./pydrive.html#pydrive.drive.GoogleDrive.CreateFile 247 | .. _`Files resource`: https://developers.google.com/drive/v2/reference/files#resource-representations 248 | .. _`SetContentFile(filename)`: ./pydrive.html#pydrive.files.GoogleDriveFile.SetContentFile 249 | .. _`SetContentString(content)`: ./pydrive.html#pydrive.files.GoogleDriveFile.SetContentString 250 | .. _`GetContentFile(filename)`: ./pydrive.html#pydrive.files.GoogleDriveFile.GetContentFile 251 | .. _`GetContentString()`: ./pydrive.html#pydrive.files.GoogleDriveFile.GetContentString 252 | .. _`official documentation`: https://developers.google.com/drive/v2/reference/files#resource-representations 253 | .. _`known`: https://productforums.google.com/forum/#!topic/docs/BJLimQDGtjQ -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. PyDrive documentation master file, created by 2 | sphinx-quickstart on Sun Jun 12 23:01:40 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to PyDrive's documentation! 7 | =================================== 8 | 9 | PyDrive is a wrapper library of `google-api-python-client`_ that simplifies many common Google Drive API tasks. 10 | 11 | Project Info 12 | ============ 13 | 14 | - Homepage: `https://pypi.python.org/pypi/PyDrive `_ 15 | - Documentation: `Official documentation on GitHub pages `_ 16 | - Github: `https://github.com/googleworkspace/PyDrive `_ 17 | 18 | How to install 19 | ============== 20 | 21 | You can install PyDrive with regular ``pip`` command. 22 | 23 | :: 24 | 25 | $ pip install PyDrive 26 | 27 | To install the current development version from GitHub, use: 28 | 29 | :: 30 | 31 | $ pip install git+https://github.com/googleworkspace/PyDrive.git#egg=PyDrive 32 | 33 | Table of Contents 34 | ================= 35 | 36 | .. toctree:: 37 | :maxdepth: 2 38 | 39 | quickstart 40 | oauth 41 | filemanagement 42 | filelist 43 | pydrive 44 | 45 | 46 | .. _`google-api-python-client`: https://github.com/google/google-api-python-client 47 | 48 | Indices and tables 49 | ================== 50 | 51 | * :ref:`genindex` 52 | * :ref:`search` 53 | 54 | -------------------------------------------------------------------------------- /docs/oauth.rst: -------------------------------------------------------------------------------- 1 | OAuth made easy 2 | =============== 3 | 4 | Authentication in two lines 5 | --------------------------- 6 | 7 | OAuth2.0 is complex and difficult to start with. To make it more simple, 8 | *PyDrive* makes all authentication into just two lines. 9 | 10 | .. code-block:: python 11 | 12 | from pydrive.auth import GoogleAuth 13 | 14 | gauth = GoogleAuth() 15 | # Create local webserver and auto handles authentication. 16 | gauth.LocalWebserverAuth() 17 | 18 | # Or use the CommandLineAuth(), which provides you with a link to paste 19 | # into your browser. The site it leads to then provides you with an 20 | # authentication token which you paste into the command line. 21 | # Commented out as it is an alternative to the LocalWebserverAuth() above, 22 | # and someone will just copy-paste the entire thing into their editor. 23 | 24 | # gauth.CommandLineAuth() 25 | 26 | To make this code work, you need to download the application configurations file 27 | from APIs Console. Take a look at quickstart_ for detailed instructions. 28 | 29 | `LocalWebserverAuth()`_ is a built-in method of `GoogleAuth`_ which sets up 30 | local webserver to automatically receive authentication code from user and 31 | authorizes by itself. You can also use `CommandLineAuth()`_ which manually 32 | takes code from user at command line. 33 | 34 | .. _quickstart: ./quickstart.html#authentication 35 | .. _`LocalWebserverAuth()`: ./pydrive.html#pydrive.auth.GoogleAuth.LocalWebserverAuth 36 | .. _`GoogleAuth`: ./pydrive.html#pydrive.auth.GoogleAuth 37 | .. _`CommandLineAuth()`: ./pydrive.html#pydrive.auth.GoogleAuth.CommandLineAuth 38 | 39 | Automatic and custom authentication with *settings.yaml* 40 | -------------------------------------------------------- 41 | 42 | Read this section if you need a custom authentication flow, **such as silent 43 | authentication on a remote machine**. For an example of such a setup have a look 44 | at `Sample settings.yaml`_. 45 | 46 | OAuth is complicated and it requires a lot of settings. By default, 47 | when you don't provide any settings, *PyDrive* will automatically set default 48 | values which works for most of the cases. Here are some default settings. 49 | 50 | - Read client configuration from file *client_secrets.json* 51 | - OAuth scope: :code:`https://www.googleapis.com/auth/drive` 52 | - Don't save credentials 53 | - Don't retrieve refresh token 54 | 55 | However, you might want to customize these settings while maintaining two lines 56 | of clean code. If that is the case, you can make *settings.yaml* file in your 57 | working directory and *PyDrive* will read it to customize authentication 58 | behavior. 59 | 60 | These are all the possible fields of a *settings.yaml* file: 61 | 62 | .. code-block:: python 63 | 64 | client_config_backend: {{str}} 65 | client_config_file: {{str}} 66 | client_config: 67 | client_id: {{str}} 68 | client_secret: {{str}} 69 | auth_uri: {{str}} 70 | token_uri: {{str}} 71 | redirect_uri: {{str}} 72 | revoke_uri: {{str}} 73 | 74 | save_credentials: {{bool}} 75 | save_credentials_backend: {{str}} 76 | save_credentials_file: {{str}} 77 | 78 | get_refresh_token: {{bool}} 79 | 80 | oauth_scope: {{list of str}} 81 | 82 | Fields explained: 83 | 84 | :client_config_backend (str): From where to read client configuration(API application settings such as client_id and client_secrets) from. Valid values are 'file' and 'settings'. **Default**: 'file'. **Required**: No. 85 | :client_config_file (str): When *client_config_backend* is 'file', path to the file containing client configuration. **Default**: 'client_secrets.json'. **Required**: No. 86 | :client_config (dict): Place holding dictionary for client configuration when *client_config_backend* is 'settings'. **Required**: Yes, only if *client_config_backend* is 'settings' 87 | :client_config['client_id'] (str): Client ID of the application. **Required**: Yes, only if *client_config_backend* is 'settings' 88 | :client_config['client_secret'] (str): Client secret of the application. **Required**: Yes, only if *client_config_backend* is 'settings' 89 | :client_config['auth_uri'] (str): The authorization server endpoint URI. **Default**: 'https://accounts.google.com/o/oauth2/auth'. **Required**: No. 90 | :client_config['token_uri'] (str): The token server endpoint URI. **Default**: 'https://accounts.google.com/o/oauth2/token'. **Required**: No. 91 | :client_config['redirect_uri'] (str): Redirection endpoint URI. **Default**: 'urn:ietf:wg:oauth:2.0:oob'. **Required**: No. 92 | :client_config['revoke_uri'] (str): Revoke endpoint URI. **Default**: None. **Required**: No. 93 | :save_credentials (bool): True if you want to save credentials. **Default**: False. **Required**: No. 94 | :save_credentials_backend (str): Backend to save credentials to. 'file' is the only valid value for now. **Default**: 'file'. **Required**: No. 95 | :save_credentials_file (str): Destination of credentials file. **Required**: Yes, only if *save_credentials_backend* is 'file'. 96 | :get_refresh_token (bool): True if you want to retrieve refresh token along with access token. **Default**: False. **Required**: No. 97 | :oauth_scope (list of str): OAuth scope to authenticate. **Default**: ['https://www.googleapis.com/auth/drive']. **Required**: No. 98 | 99 | Sample *settings.yaml* 100 | ______________________ 101 | 102 | :: 103 | 104 | client_config_backend: settings 105 | client_config: 106 | client_id: 9637341109347.apps.googleusercontent.com 107 | client_secret: psDskOoWr1P602PXRTHi 108 | 109 | save_credentials: True 110 | save_credentials_backend: file 111 | save_credentials_file: credentials.json 112 | 113 | get_refresh_token: True 114 | 115 | oauth_scope: 116 | - https://www.googleapis.com/auth/drive.file 117 | - https://www.googleapis.com/auth/drive.install 118 | - https://www.googleapis.com/auth/drive.metadata 119 | 120 | Building your own authentication flow 121 | ------------------------------------- 122 | 123 | You might want to build your own authentication flow. For example, you might 124 | want to integrate your existing website with Drive API. In that case, you can 125 | customize authentication flow as follwing: 126 | 127 | 1. Get authentication Url from `GetAuthUrl()`_. 128 | 2. Ask users to visit the authentication Url and grant access to your application. Retrieve authentication code manually by user or automatically by building your own oauth2callback. 129 | 3. Call `Auth(code)`_ with the authentication code you retrieved from step 2. 130 | 131 | Your *settings.yaml* will work for your customized authentication flow, too. 132 | 133 | Here is a sample code for your customized authentication flow 134 | 135 | .. code-block:: python 136 | 137 | from pydrive.auth import GoogleAuth 138 | 139 | gauth = GoogleAuth() 140 | auth_url = gauth.GetAuthUrl() # Create authentication url user needs to visit 141 | code = AskUserToVisitLinkAndGiveCode(auth_url) # Your customized authentication flow 142 | gauth.Auth(code) # Authorize and build service from the code 143 | 144 | .. _`GetAuthUrl()`: ./pydrive.html#pydrive.auth.GoogleAuth.GetAuthUrl 145 | .. _`Auth(code)`: ./pydrive.html#pydrive.auth.GoogleAuth.Auth 146 | -------------------------------------------------------------------------------- /docs/pydrive.rst: -------------------------------------------------------------------------------- 1 | pydrive package 2 | =============== 3 | 4 | pydrive.apiattr module 5 | ---------------------- 6 | 7 | .. automodule:: pydrive.apiattr 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | pydrive.auth module 13 | ------------------- 14 | 15 | .. automodule:: pydrive.auth 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | pydrive.drive module 21 | -------------------- 22 | 23 | .. automodule:: pydrive.drive 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | pydrive.files module 29 | -------------------- 30 | 31 | .. automodule:: pydrive.files 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | 36 | pydrive.settings module 37 | ----------------------- 38 | 39 | .. automodule:: pydrive.settings 40 | :members: 41 | :undoc-members: 42 | :show-inheritance: 43 | -------------------------------------------------------------------------------- /docs/quickstart.rst: -------------------------------------------------------------------------------- 1 | Quickstart 2 | ============================= 3 | 4 | Authentication 5 | -------------- 6 | Drive API requires OAuth2.0 for authentication. *PyDrive* makes your life much easier by handling complex authentication steps for you. 7 | 8 | 1. Go to `APIs Console`_ and make your own project. 9 | 2. Search for 'Google Drive API', select the entry, and click 'Enable'. 10 | 3. Select 'Credentials' from the left menu, click 'Create Credentials', select 'OAuth client ID'. 11 | 4. Now, the product name and consent screen need to be set -> click 'Configure consent screen' and follow the instructions. Once finished: 12 | 13 | a. Select 'Application type' to be *Web application*. 14 | b. Enter an appropriate name. 15 | c. Input *http://localhost:8080* for 'Authorized JavaScript origins'. 16 | d. Input *http://localhost:8080/* for 'Authorized redirect URIs'. 17 | e. Click 'Create'. 18 | 19 | 5. Click 'Download JSON' on the right side of Client ID to download **client_secret_.json**. 20 | 21 | The downloaded file has all authentication information of your application. 22 | **Rename the file to "client_secrets.json" and place it in your working directory.** 23 | 24 | Create *quickstart.py* file and copy and paste the following code. 25 | 26 | .. code-block:: python 27 | 28 | from pydrive.auth import GoogleAuth 29 | 30 | gauth = GoogleAuth() 31 | gauth.LocalWebserverAuth() # Creates local webserver and auto handles authentication. 32 | 33 | Run this code with *python quickstart.py* and you will see a web browser asking you for authentication. Click *Accept* and you are done with authentication. For more details, take a look at documentation: `OAuth made easy`_ 34 | 35 | .. _`APIs Console`: https://console.developers.google.com/iam-admin/projects 36 | .. _`OAuth made easy`: ./oauth.html 37 | 38 | Creating and updating file 39 | -------------------------- 40 | 41 | There are many methods to create and update file metadata and contents. With *PyDrive*, all you have to know is 42 | `Upload()`_ method which makes optimal API call for you. Add the following code to your *quickstart.py* and run it. 43 | 44 | .. code-block:: python 45 | 46 | from pydrive.drive import GoogleDrive 47 | 48 | drive = GoogleDrive(gauth) 49 | 50 | file1 = drive.CreateFile({'title': 'Hello.txt'}) # Create GoogleDriveFile instance with title 'Hello.txt'. 51 | file1.SetContentString('Hello World!') # Set content of the file from given string. 52 | file1.Upload() 53 | 54 | This code will create a new file with title *Hello.txt* and its content *Hello World!*. You can see and open this 55 | file from `Google Drive`_ if you want. For more details, take a look at documentation: `File management made easy`_ 56 | 57 | .. _`Upload()`: ./pydrive.html#pydrive.files.GoogleDriveFile.Upload 58 | .. _`Google Drive`: https://drive.google.com 59 | .. _`File management made easy`: ./filemanagement.html 60 | 61 | Getting list of files 62 | --------------------- 63 | 64 | *PyDrive* handles paginations and parses response as list of `GoogleDriveFile`_. Let's get title and id of all the files in the root folder of Google Drive. Again, add the following code to *quickstart.py* and execute it. 65 | 66 | .. code-block:: python 67 | 68 | # Auto-iterate through all files that matches this query 69 | file_list = drive.ListFile({'q': "'root' in parents and trashed=false"}).GetList() 70 | for file1 in file_list: 71 | print('title: %s, id: %s' % (file1['title'], file1['id'])) 72 | 73 | Creating a Folder 74 | ----------------- 75 | 76 | GoogleDrive treats everything as a file and assigns different mimetypes for different file formats. A folder is thus 77 | also a file with a special mimetype. The code below allows you to add a subfolder to an existing folder. 78 | .. code-block:: python 79 | def create_folder(parent_folder_id, subfolder_name): 80 | newFolder = drive.CreateFile({'title': subfolder_name, "parents": [{"kind": "drive#fileLink", "id": \ 81 | parent_folder_id}],"mimeType": "application/vnd.google-apps.folder"}) 82 | newFolder.Upload() 83 | return newFolder 84 | 85 | 86 | Get Id of the title 87 | ------------------- 88 | 89 | get_id_of_title is a function that return id of the given title's file in the directory. 90 | .. code-block:: python 91 | def get_id_of_title(title,parent_directory_id): 92 | foldered_list=drive.ListFile({'q': "'"+parent_directory_id+"' in parents and trashed=false"}).GetList() 93 | for file in foldered_list: 94 | if(file['title']==title): 95 | return file['id'] 96 | return None 97 | 98 | folder browser 99 | -------------- 100 | This return a json output of the data in the directory with some important attributes like size, title, parent_id etc 101 | .. code-block:: python 102 | browsed=[] 103 | def folder_browser(folder_list,parent_id): 104 | for element in folder_list: 105 | if type(element) is dict: 106 | print (element['title']) 107 | else: 108 | print (element) 109 | print("Enter Name of Folder You Want to Use\nEnter '/' to use current folder\nEnter ':' to create New Folder and 110 | use that" ) 111 | inp=input() 112 | if inp=='/': 113 | return parent_id 114 | elif inp==':': 115 | print("Enter Name of Folder You Want to Create") 116 | inp=input() 117 | newfolder=create_folder(parent_id,inp) 118 | if not os.path.exists(HOME_DIRECTORY+ROOT_FOLDER_NAME+os.path.sep+USERNAME): 119 | os.makedirs(HOME_DIRECTORY+ROOT_FOLDER_NAME+os.path.sep+USERNAME) 120 | return newfolder['id'] 121 | 122 | else: 123 | folder_selected=inp 124 | for element in folder_list: 125 | if type(element) is dict: 126 | if element["title"]==folder_selected: 127 | struc=element["list"] 128 | browsed.append(folder_selected) 129 | print("Inside "+folder_selected) 130 | return folder_browser(struc,element['id']) 131 | 132 | here folder_list is the list of folders that is present 133 | 134 | You will see title and id of all the files and folders in root folder of your Google Drive. For more details, take a look at documentation: `File listing made easy`_ 135 | 136 | .. _`GoogleDriveFile`: ./pydrive.html#pydrive.files.GoogleDriveFile 137 | .. _`File listing made easy`: ./filelist.html 138 | -------------------------------------------------------------------------------- /examples/strip_bom_example.py: -------------------------------------------------------------------------------- 1 | from pydrive.auth import GoogleAuth 2 | from pydrive.drive import GoogleDrive 3 | 4 | # Authenticate the client. 5 | gauth = GoogleAuth() 6 | gauth.LocalWebserverAuth() 7 | drive = GoogleDrive(gauth) 8 | 9 | # Create a file, set content, and upload. 10 | file1 = drive.CreateFile() 11 | original_file_content = 'Generic, non-exhaustive\n ASCII test string.' 12 | file1.SetContentString(original_file_content) 13 | # {'convert': True} triggers conversion to a Google Drive document. 14 | file1.Upload({'convert': True}) 15 | 16 | 17 | # Download the file. 18 | file2 = drive.CreateFile({'id': file1['id']}) 19 | 20 | # Print content before download. 21 | print('Original text:') 22 | print(bytes(original_file_content.encode('unicode-escape'))) 23 | print('Number of chars: %d' % len(original_file_content)) 24 | print('') 25 | # Original text: 26 | # Generic, non-exhaustive\n ASCII test string. 27 | # Number of chars: 43 28 | 29 | 30 | # Download document as text file WITH the BOM and print the contents. 31 | content_with_bom = file2.GetContentString(mimetype='text/plain') 32 | print('Content with BOM:') 33 | print(bytes(content_with_bom.encode('unicode-escape'))) 34 | print('Number of chars: %d' % len(content_with_bom)) 35 | print('') 36 | # Content with BOM: 37 | # \ufeffGeneric, non-exhaustive\r\n ASCII test string. 38 | # Number of chars: 45 39 | 40 | 41 | # Download document as text file WITHOUT the BOM and print the contents. 42 | content_without_bom = file2.GetContentString(mimetype='text/plain', 43 | remove_bom=True) 44 | print('Content without BOM:') 45 | print(bytes(content_without_bom.encode('unicode-escape'))) 46 | print('Number of chars: %d' % len(content_without_bom)) 47 | print('') 48 | # Content without BOM: 49 | # Generic, non-exhaustive\r\n ASCII test string. 50 | # Number of chars: 44 51 | 52 | # *NOTE*: When downloading a Google Drive document as text file, line-endings 53 | # are converted to the Windows-style: \r\n. 54 | 55 | 56 | # Delete the file as necessary. 57 | file1.Delete() 58 | -------------------------------------------------------------------------------- /examples/using_folders.py: -------------------------------------------------------------------------------- 1 | from pydrive.auth import GoogleAuth 2 | from pydrive.drive import GoogleDrive 3 | 4 | gauth = GoogleAuth() 5 | gauth.LocalWebserverAuth() 6 | 7 | drive = GoogleDrive(gauth) 8 | 9 | # Create folder. 10 | folder_metadata = { 11 | 'title' : '', 12 | # The mimetype defines this new file as a folder, so don't change this. 13 | 'mimeType' : 'application/vnd.google-apps.folder' 14 | } 15 | folder = drive.CreateFile(folder_metadata) 16 | folder.Upload() 17 | 18 | # Get folder info and print to screen. 19 | folder_title = folder['title'] 20 | folder_id = folder['id'] 21 | print('title: %s, id: %s' % (folder_title, folder_id)) 22 | 23 | # Upload file to folder. 24 | f = drive.CreateFile({"parents": [{"kind": "drive#fileLink", "id": folder_id}]}) 25 | 26 | # Make sure to add the path to the file to upload below. 27 | f.SetContentFile('') 28 | f.Upload() 29 | -------------------------------------------------------------------------------- /pydrive/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/pydrive/__init__.py -------------------------------------------------------------------------------- /pydrive/apiattr.py: -------------------------------------------------------------------------------- 1 | from six import Iterator, iteritems 2 | 3 | class ApiAttribute(object): 4 | """A data descriptor that sets and returns values.""" 5 | 6 | def __init__(self, name): 7 | """Create an instance of ApiAttribute. 8 | 9 | :param name: name of this attribute. 10 | :type name: str. 11 | """ 12 | self.name = name 13 | 14 | def __get__(self, obj, type=None): 15 | """Accesses value of this attribute.""" 16 | return obj.attr.get(self.name) 17 | 18 | def __set__(self, obj, value): 19 | """Write value of this attribute.""" 20 | obj.attr[self.name] = value 21 | if obj.dirty.get(self.name) is not None: 22 | obj.dirty[self.name] = True 23 | 24 | def __del__(self, obj=None): 25 | """Delete value of this attribute.""" 26 | if not obj: 27 | return 28 | 29 | del obj.attr[self.name] 30 | if obj.dirty.get(self.name) is not None: 31 | del obj.dirty[self.name] 32 | 33 | class ApiAttributeMixin(object): 34 | """Mixin to initialize required global variables to use ApiAttribute.""" 35 | 36 | def __init__(self): 37 | self.attr = {} 38 | self.dirty = {} 39 | self.http = None # Any element may make requests and will require this 40 | # field. 41 | 42 | 43 | class ApiResource(dict): 44 | """Super class of all api resources. 45 | 46 | Inherits and behaves as a python dictionary to handle api resources. 47 | Save clean copy of metadata in self.metadata as a dictionary. 48 | Provides changed metadata elements to efficiently update api resources. 49 | """ 50 | auth = ApiAttribute('auth') 51 | 52 | def __init__(self, *args, **kwargs): 53 | """Create an instance of ApiResource.""" 54 | super(ApiResource, self).__init__() 55 | self.update(*args, **kwargs) 56 | self.metadata = dict(self) 57 | 58 | def __getitem__(self, key): 59 | """Overwritten method of dictionary. 60 | 61 | :param key: key of the query. 62 | :type key: str. 63 | :returns: value of the query. 64 | """ 65 | return dict.__getitem__(self, key) 66 | 67 | def __setitem__(self, key, val): 68 | """Overwritten method of dictionary. 69 | 70 | :param key: key of the query. 71 | :type key: str. 72 | :param val: value of the query. 73 | """ 74 | dict.__setitem__(self, key, val) 75 | 76 | def __repr__(self): 77 | """Overwritten method of dictionary.""" 78 | dict_representation = dict.__repr__(self) 79 | return '%s(%s)' % (type(self).__name__, dict_representation) 80 | 81 | def update(self, *args, **kwargs): 82 | """Overwritten method of dictionary.""" 83 | for k, v in iteritems(dict(*args, **kwargs)): 84 | self[k] = v 85 | 86 | def UpdateMetadata(self, metadata=None): 87 | """Update metadata and mark all of them to be clean.""" 88 | if metadata: 89 | self.update(metadata) 90 | self.metadata = dict(self) 91 | 92 | def GetChanges(self): 93 | """Returns changed metadata elements to update api resources efficiently. 94 | 95 | :returns: dict -- changed metadata elements. 96 | """ 97 | dirty = {} 98 | for key in self: 99 | if self.metadata.get(key) is None: 100 | dirty[key] = self[key] 101 | elif self.metadata[key] != self[key]: 102 | dirty[key] = self[key] 103 | return dirty 104 | 105 | 106 | class ApiResourceList(ApiAttributeMixin, ApiResource, Iterator): 107 | """Abstract class of all api list resources. 108 | 109 | Inherits ApiResource and builds iterator to list any API resource. 110 | """ 111 | metadata = ApiAttribute('metadata') 112 | 113 | def __init__(self, auth=None, metadata=None): 114 | """Create an instance of ApiResourceList. 115 | 116 | :param auth: authorized GoogleAuth instance. 117 | :type auth: GoogleAuth. 118 | :param metadata: parameter to send to list command. 119 | :type metadata: dict. 120 | """ 121 | ApiAttributeMixin.__init__(self) 122 | ApiResource.__init__(self) 123 | self.auth = auth 124 | self.UpdateMetadata() 125 | if metadata: 126 | self.update(metadata) 127 | 128 | def __iter__(self): 129 | """Returns iterator object. 130 | 131 | :returns: ApiResourceList -- self 132 | """ 133 | return self 134 | 135 | def __next__(self): 136 | """Make API call to list resources and return them. 137 | 138 | Auto updates 'pageToken' every time it makes API call and 139 | raises StopIteration when it reached the end of iteration. 140 | 141 | :returns: list -- list of API resources. 142 | :raises: StopIteration 143 | """ 144 | if 'pageToken' in self and self['pageToken'] is None: 145 | raise StopIteration 146 | result = self._GetList() 147 | self['pageToken'] = self.metadata.get('nextPageToken') 148 | return result 149 | 150 | def GetList(self): 151 | """Get list of API resources. 152 | 153 | If 'maxResults' is not specified, it will automatically iterate through 154 | every resources available. Otherwise, it will make API call once and 155 | update 'pageToken'. 156 | 157 | :returns: list -- list of API resources. 158 | """ 159 | if self.get('maxResults') is None: 160 | self['maxResults'] = 1000 161 | result = [] 162 | for x in self: 163 | result.extend(x) 164 | del self['maxResults'] 165 | return result 166 | else: 167 | return next(self) 168 | 169 | def _GetList(self): 170 | """Helper function which actually makes API call. 171 | 172 | Should be overwritten. 173 | 174 | :raises: NotImplementedError 175 | """ 176 | raise NotImplementedError 177 | 178 | def Reset(self): 179 | """Resets current iteration""" 180 | if 'pageToken' in self: 181 | del self['pageToken'] 182 | -------------------------------------------------------------------------------- /pydrive/drive.py: -------------------------------------------------------------------------------- 1 | from .apiattr import ApiAttributeMixin 2 | from .files import GoogleDriveFile 3 | from .files import GoogleDriveFileList 4 | from .auth import LoadAuth 5 | 6 | 7 | class GoogleDrive(ApiAttributeMixin, object): 8 | """Main Google Drive class.""" 9 | 10 | def __init__(self, auth=None): 11 | """Create an instance of GoogleDrive. 12 | 13 | :param auth: authorized GoogleAuth instance. 14 | :type auth: pydrive.auth.GoogleAuth. 15 | """ 16 | ApiAttributeMixin.__init__(self) 17 | self.auth = auth 18 | 19 | def CreateFile(self, metadata=None): 20 | """Create an instance of GoogleDriveFile with auth of this instance. 21 | 22 | This method would not upload a file to GoogleDrive. 23 | 24 | :param metadata: file resource to initialize GoogleDriveFile with. 25 | :type metadata: dict. 26 | :returns: pydrive.files.GoogleDriveFile -- initialized with auth of this instance. 27 | """ 28 | return GoogleDriveFile(auth=self.auth, metadata=metadata) 29 | 30 | def ListFile(self, param=None): 31 | """Create an instance of GoogleDriveFileList with auth of this instance. 32 | 33 | This method will not fetch from Files.List(). 34 | 35 | :param param: parameter to be sent to Files.List(). 36 | :type param: dict. 37 | :returns: pydrive.files.GoogleDriveFileList -- initialized with auth of this instance. 38 | """ 39 | return GoogleDriveFileList(auth=self.auth, param=param) 40 | 41 | @LoadAuth 42 | def GetAbout(self): 43 | """Return information about the Google Drive of the auth instance. 44 | 45 | :returns: A dictionary of Google Drive information like user, usage, quota etc. 46 | """ 47 | return self.auth.service.about().get().execute(http=self.http) 48 | -------------------------------------------------------------------------------- /pydrive/settings.py: -------------------------------------------------------------------------------- 1 | from yaml import load 2 | from yaml import YAMLError 3 | try: 4 | from yaml import CLoader as Loader 5 | except ImportError: 6 | from yaml import Loader 7 | 8 | SETTINGS_FILE = 'settings.yaml' 9 | SETTINGS_STRUCT = { 10 | 'client_config_backend': { 11 | 'type': str, 12 | 'required': True, 13 | 'default': 'file', 14 | 'dependency': [ 15 | { 16 | 'value': 'file', 17 | 'attribute': ['client_config_file'] 18 | }, 19 | { 20 | 'value': 'settings', 21 | 'attribute': ['client_config'] 22 | }, 23 | { 24 | 'value': 'service', 25 | 'attribute': ['service_config'] 26 | } 27 | ] 28 | }, 29 | 'save_credentials': { 30 | 'type': bool, 31 | 'required': True, 32 | 'default': False, 33 | 'dependency': [ 34 | { 35 | 'value': True, 36 | 'attribute': ['save_credentials_backend'] 37 | } 38 | ] 39 | }, 40 | 'get_refresh_token': { 41 | 'type': bool, 42 | 'required': False, 43 | 'default': False 44 | }, 45 | 'client_config_file': { 46 | 'type': str, 47 | 'required': False, 48 | 'default': 'client_secrets.json' 49 | }, 50 | 'save_credentials_backend': { 51 | 'type': str, 52 | 'required': False, 53 | 'dependency': [ 54 | { 55 | 'value': 'file', 56 | 'attribute': ['save_credentials_file'] 57 | } 58 | ] 59 | }, 60 | 'client_config': { 61 | 'type': dict, 62 | 'required': False, 63 | 'struct': { 64 | 'client_id': { 65 | 'type': str, 66 | 'required': True 67 | }, 68 | 'client_secret': { 69 | 'type': str, 70 | 'required': True 71 | }, 72 | 'auth_uri': { 73 | 'type': str, 74 | 'required': True, 75 | 'default': 'https://accounts.google.com/o/oauth2/auth' 76 | }, 77 | 'token_uri': { 78 | 'type': str, 79 | 'required': True, 80 | 'default': 'https://accounts.google.com/o/oauth2/token' 81 | }, 82 | 'redirect_uri': { 83 | 'type': str, 84 | 'required': True, 85 | 'default': 'urn:ietf:wg:oauth:2.0:oob' 86 | }, 87 | 'revoke_uri': { 88 | 'type': str, 89 | 'required': True, 90 | 'default': None 91 | } 92 | } 93 | }, 94 | 'service_config': { 95 | 'type': dict, 96 | 'required': False, 97 | 'struct': { 98 | 'client_user_email': { 99 | 'type': str, 100 | 'required': True, 101 | 'default': None 102 | }, 103 | 'client_service_email': { 104 | 'type': str, 105 | 'required': True 106 | }, 107 | 'client_pkcs12_file_path': { 108 | 'type': str, 109 | 'required': True 110 | } 111 | } 112 | }, 113 | 'oauth_scope': { 114 | 'type': list, 115 | 'required': True, 116 | 'struct': str, 117 | 'default': ['https://www.googleapis.com/auth/drive'] 118 | }, 119 | 'save_credentials_file': { 120 | 'type': str, 121 | 'required': False, 122 | } 123 | } 124 | 125 | 126 | class SettingsError(IOError): 127 | """Error while loading/saving settings""" 128 | 129 | 130 | class InvalidConfigError(IOError): 131 | """Error trying to read client configuration.""" 132 | 133 | 134 | def LoadSettingsFile(filename=SETTINGS_FILE): 135 | """Loads settings file in yaml format given file name. 136 | 137 | :param filename: path for settings file. 'settings.yaml' by default. 138 | :type filename: str. 139 | :raises: SettingsError 140 | """ 141 | try: 142 | stream = open(filename, 'r') 143 | data = load(stream, Loader=Loader) 144 | except (YAMLError, IOError) as e: 145 | raise SettingsError(e) 146 | return data 147 | 148 | 149 | def ValidateSettings(data): 150 | """Validates if current settings is valid. 151 | 152 | :param data: dictionary containing all settings. 153 | :type data: dict. 154 | :raises: InvalidConfigError 155 | """ 156 | _ValidateSettingsStruct(data, SETTINGS_STRUCT) 157 | 158 | 159 | def _ValidateSettingsStruct(data, struct): 160 | """Validates if provided data fits provided structure. 161 | 162 | :param data: dictionary containing settings. 163 | :type data: dict. 164 | :param struct: dictionary containing structure information of settings. 165 | :type struct: dict. 166 | :raises: InvalidConfigError 167 | """ 168 | # Validate required elements of the setting. 169 | for key in struct: 170 | if struct[key]['required']: 171 | _ValidateSettingsElement(data, struct, key) 172 | 173 | 174 | def _ValidateSettingsElement(data, struct, key): 175 | """Validates if provided element of settings data fits provided structure. 176 | 177 | :param data: dictionary containing settings. 178 | :type data: dict. 179 | :param struct: dictionary containing structure information of settings. 180 | :type struct: dict. 181 | :param key: key of the settings element to validate. 182 | :type key: str. 183 | :raises: InvalidConfigError 184 | """ 185 | # Check if data exists. If not, check if default value exists. 186 | value = data.get(key) 187 | data_type = struct[key]['type'] 188 | if value is None: 189 | try: 190 | default = struct[key]['default'] 191 | except KeyError: 192 | raise InvalidConfigError('Missing required setting %s' % key) 193 | else: 194 | data[key] = default 195 | # If data exists, Check type of the data 196 | elif type(value) is not data_type: 197 | raise InvalidConfigError('Setting %s should be type %s' % (key, data_type)) 198 | # If type of this data is dict, check if structure of the data is valid. 199 | if data_type is dict: 200 | _ValidateSettingsStruct(data[key], struct[key]['struct']) 201 | # If type of this data is list, check if all values in the list is valid. 202 | elif data_type is list: 203 | for element in data[key]: 204 | if type(element) is not struct[key]['struct']: 205 | raise InvalidConfigError('Setting %s should be list of %s' % 206 | (key, struct[key]['struct'])) 207 | # Check dependency of this attribute. 208 | dependencies = struct[key].get('dependency') 209 | if dependencies: 210 | for dependency in dependencies: 211 | if value == dependency['value']: 212 | for reqkey in dependency['attribute']: 213 | _ValidateSettingsElement(data, struct, reqkey) 214 | -------------------------------------------------------------------------------- /pydrive/test/README.rst: -------------------------------------------------------------------------------- 1 | Instructions 2 | ------------ 3 | 4 | - Install timeout-decorator and futures (used for parallel upload and 5 | download testing). 6 | - Create *configs* and *credentials* folder in working directory. 7 | - Put *client_secrets.json* file from API console into *configs* folder. 8 | - Put *clients_secrets_local.json* from API console, for native device in 9 | *configs* folder. 10 | - Put different *a.png* and *b.png* file in working directory. 11 | - Replace {{ }} sections in *settings/test2.yaml* with the relevant sections 12 | from your config file. 13 | - For ServiceAuth test, place PKCS12 file in working directory. 14 | -------------------------------------------------------------------------------- /pydrive/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/PyDrive/42022f9a1c48f435438fce74ad4032ec9f34cfd1/pydrive/test/__init__.py -------------------------------------------------------------------------------- /pydrive/test/settings/default.yaml: -------------------------------------------------------------------------------- 1 | client_config_backend: 'file' 2 | client_config_file: 'configs/client_secrets.json' 3 | 4 | save_credentials: True 5 | save_credentials_backend: 'file' 6 | save_credentials_file: 'credentials/1.dat' 7 | 8 | oauth_scope: 9 | - 'https://www.googleapis.com/auth/drive' 10 | -------------------------------------------------------------------------------- /pydrive/test/settings/test1.yaml: -------------------------------------------------------------------------------- 1 | client_config_backend: 'file' 2 | client_config_file: 'configs/client_secrets.json' 3 | 4 | save_credentials: True 5 | save_credentials_backend: 'file' 6 | save_credentials_file: 'credentials/1.dat' 7 | 8 | oauth_scope: 9 | - 'https://www.googleapis.com/auth/drive' 10 | -------------------------------------------------------------------------------- /pydrive/test/settings/test2.yaml: -------------------------------------------------------------------------------- 1 | client_config_backend: 'settings' 2 | client_config: 3 | client_id: "{{YOUR_CLIENT_ID}}" 4 | client_secret: "{{YOUR_CLIENT_SECRET}}" 5 | 6 | save_credentials: True 7 | save_credentials_backend: 'file' 8 | save_credentials_file: 'credentials/2.dat' 9 | 10 | oauth_scope: 11 | - "https://www.googleapis.com/auth/drive" 12 | -------------------------------------------------------------------------------- /pydrive/test/settings/test3.yaml: -------------------------------------------------------------------------------- 1 | client_config_backend: 'file' 2 | client_config_file: 'configs/client_secrets.json' 3 | 4 | save_credentials: False 5 | 6 | oauth_scope: 7 | - 'https://www.googleapis.com/auth/drive' 8 | -------------------------------------------------------------------------------- /pydrive/test/settings/test4.yaml: -------------------------------------------------------------------------------- 1 | client_config_backend: 'file' 2 | client_config_file: 'configs/client_secrets_local.json' 3 | 4 | save_credentials: True 5 | save_credentials_backend: 'file' 6 | save_credentials_file: 'credentials/1.dat' 7 | 8 | oauth_scope: 9 | - "https://www.googleapis.com/auth/drive" 10 | -------------------------------------------------------------------------------- /pydrive/test/settings/test5.yaml: -------------------------------------------------------------------------------- 1 | client_config_backend: 'file' 2 | client_config_file: 'configs/client_secrets.json' 3 | -------------------------------------------------------------------------------- /pydrive/test/settings/test6.yaml: -------------------------------------------------------------------------------- 1 | client_config_backend: 'service' 2 | service_config: 3 | client_user_email: "{{YOUR_USER_EMAIL}}" 4 | client_service_email: "{{YOUR_SERVICE_EMAIL}}" 5 | client_pkcs12_file_path: "{{YOUR_PKCS12_FILE_PATH}}" 6 | 7 | save_credentials: True 8 | save_credentials_backend: 'file' 9 | save_credentials_file: 'credentials/5.dat' 10 | 11 | oauth_scope: 12 | - "https://www.googleapis.com/auth/drive" 13 | -------------------------------------------------------------------------------- /pydrive/test/test_apiattr.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from pydrive.auth import GoogleAuth 4 | from pydrive.drive import GoogleDrive 5 | 6 | class ApiAttributeTest(unittest.TestCase): 7 | """Test ApiAttr functions. 8 | """ 9 | ga = GoogleAuth('settings/test1.yaml') 10 | ga.LocalWebserverAuth() 11 | first_file = 'a.png' 12 | second_file = 'b.png' 13 | 14 | def test_UpdateMetadataNotInfinitelyNesting(self): 15 | # Verify 'metadata' field present. 16 | self.assertTrue(self.file1.metadata is not None) 17 | self.file1.UpdateMetadata() 18 | self.file1.UpdateMetadata() 19 | 20 | # Verify 'metadata' field still present. 21 | self.assertTrue(self.file1.metadata is not None) 22 | # Ensure no 'metadata' field in 'metadata' (i.e. nested). 23 | self.assertTrue('metadata' not in self.file1.metadata) 24 | 25 | def setUp(self): 26 | self.drive = GoogleDrive(self.ga) 27 | self.file1 = self.drive.CreateFile() 28 | self.file1.Upload() 29 | 30 | def tearDown(self): 31 | self.file1.Delete() 32 | 33 | if __name__ == '__main__': 34 | unittest.main() -------------------------------------------------------------------------------- /pydrive/test/test_drive.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import unittest 3 | 4 | from pydrive.auth import GoogleAuth 5 | from pydrive.drive import GoogleDrive 6 | 7 | 8 | class GoogleDriveTest(unittest.TestCase): 9 | """Tests basic operations on meta-data information of the linked Google Drive account. 10 | """ 11 | 12 | ga = GoogleAuth('settings/test1.yaml') 13 | ga.LocalWebserverAuth() 14 | 15 | def test_01_About_Request(self): 16 | drive = GoogleDrive(self.ga) 17 | 18 | about_object = drive.GetAbout() 19 | self.assertTrue(about_object is not None, "About object not loading.") 20 | 21 | 22 | if __name__ == '__main__': 23 | unittest.main() 24 | -------------------------------------------------------------------------------- /pydrive/test/test_filelist.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import sys 4 | import unittest 5 | 6 | from pydrive.auth import GoogleAuth 7 | from pydrive.drive import GoogleDrive 8 | from pydrive.test import test_util 9 | 10 | 11 | class GoogleDriveFileListTest(unittest.TestCase): 12 | """Tests operations of files.GoogleDriveFileList class. 13 | Equivalent to Files.list in Google Drive API. 14 | """ 15 | ga = GoogleAuth('settings/test1.yaml') 16 | ga.LocalWebserverAuth() 17 | drive = GoogleDrive(ga) 18 | 19 | def test_01_Files_List_GetList(self): 20 | drive = GoogleDrive(self.ga) 21 | flist = drive.ListFile({'q': "title = '%s' and trashed = false" 22 | % self.title}) 23 | files = flist.GetList() # Auto iterate every file 24 | for file1 in self.file_list: 25 | found = False 26 | for file2 in files: 27 | if file1['id'] == file2['id']: 28 | found = True 29 | self.assertEqual(found, True) 30 | 31 | def test_02_Files_List_ForLoop(self): 32 | drive = GoogleDrive(self.ga) 33 | flist = drive.ListFile({'q': "title = '%s' and trashed = false"%self.title, 34 | 'maxResults': 2}) 35 | files = [] 36 | for x in flist: # Build iterator to access files simply with for loop 37 | self.assertTrue(len(x) <= 2) 38 | files.extend(x) 39 | for file1 in self.file_list: 40 | found = False 41 | for file2 in files: 42 | if file1['id'] == file2['id']: 43 | found = True 44 | self.assertEqual(found, True) 45 | 46 | def test_03_Files_List_GetList_Iterate(self): 47 | drive = GoogleDrive(self.ga) 48 | flist = drive.ListFile({'q': "title = '%s' and trashed = false"%self.title, 49 | 'maxResults': 2}) 50 | files = [] 51 | while True: 52 | try: 53 | x = flist.GetList() 54 | self.assertTrue(len(x) <= 2) 55 | files.extend(x) 56 | except StopIteration: 57 | break 58 | for file1 in self.file_list: 59 | found = False 60 | for file2 in files: 61 | if file1['id'] == file2['id']: 62 | found = True 63 | self.assertEqual(found, True) 64 | 65 | def test_File_List_Folders(self): 66 | drive = GoogleDrive(self.ga) 67 | folder1 = drive.CreateFile( 68 | {'mimeType': 'application/vnd.google-apps.folder', 69 | 'title': self.title}) 70 | folder1.Upload() 71 | self.file_list.append(folder1) 72 | 73 | flist = drive.ListFile({'q': "title = '%s' and trashed = false" 74 | % self.title}) 75 | count = 0 76 | for _flist in flist: 77 | for file1 in _flist: 78 | self.assertFileInFileList(file1) 79 | count += 1 80 | 81 | self.assertTrue(count == 11) 82 | 83 | # setUp and tearDown methods. 84 | # =========================== 85 | def setUp(self): 86 | title = test_util.CreateRandomFileName() 87 | file_list = [] 88 | for x in range(0, 10): 89 | file1 = self.drive.CreateFile() 90 | file1['title'] = title 91 | file1.Upload() 92 | file_list.append(file1) 93 | 94 | self.title = title 95 | self.file_list = file_list 96 | 97 | def tearDown(self): 98 | # Deleting uploaded files. 99 | for file1 in self.file_list: 100 | file1.Delete() 101 | 102 | def assertFileInFileList(self, file_object): 103 | found = False 104 | for file1 in self.file_list: 105 | if file_object['id'] == file1['id']: 106 | found = True 107 | self.assertEqual(found, True) 108 | 109 | def DeleteOldFile(self, file_name): 110 | try: 111 | os.remove(file_name) 112 | except OSError: 113 | pass 114 | 115 | if __name__ == '__main__': 116 | unittest.main() 117 | -------------------------------------------------------------------------------- /pydrive/test/test_oauth.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | import time 4 | 5 | from pydrive.auth import GoogleAuth 6 | 7 | class GoogleAuthTest(unittest.TestCase): 8 | """Tests basic OAuth2 operations of auth.GoogleAuth.""" 9 | 10 | def test_01_LocalWebserverAuthWithClientConfigFromFile(self): 11 | # Delete old credentials file 12 | self.DeleteOldCredentialsFile('credentials/1.dat') 13 | # Test if authentication works with config read from file 14 | ga = GoogleAuth('settings/test1.yaml') 15 | ga.LocalWebserverAuth() 16 | self.assertEqual(ga.access_token_expired, False) 17 | # Test if correct credentials file is created 18 | self.CheckCredentialsFile('credentials/1.dat') 19 | time.sleep(1) 20 | 21 | def test_02_LocalWebserverAuthWithClientConfigFromSettings(self): 22 | # Delete old credentials file 23 | self.DeleteOldCredentialsFile('credentials/2.dat') 24 | # Test if authentication works with config read from settings 25 | ga = GoogleAuth('settings/test2.yaml') 26 | ga.LocalWebserverAuth() 27 | self.assertEqual(ga.access_token_expired, False) 28 | # Test if correct credentials file is created 29 | self.CheckCredentialsFile('credentials/2.dat') 30 | time.sleep(1) 31 | 32 | def test_03_LocalWebServerAuthWithNoCredentialsSaving(self): 33 | # Delete old credentials file 34 | self.DeleteOldCredentialsFile('credentials/4.dat') 35 | # Provide wrong credentials file 36 | ga = GoogleAuth('settings/test3.yaml') 37 | ga.LocalWebserverAuth() 38 | self.assertEqual(ga.access_token_expired, False) 39 | # Test if correct credentials file is created 40 | self.CheckCredentialsFile('credentials/4.dat', no_file=True) 41 | time.sleep(1) 42 | 43 | def test_04_CommandLineAuthWithClientConfigFromFile(self): 44 | # Delete old credentials file 45 | self.DeleteOldCredentialsFile('credentials/1.dat') 46 | # Test if authentication works with config read from file 47 | ga = GoogleAuth('settings/test4.yaml') 48 | ga.CommandLineAuth() 49 | self.assertEqual(ga.access_token_expired, False) 50 | # Test if correct credentials file is created 51 | self.CheckCredentialsFile('credentials/1.dat') 52 | time.sleep(1) 53 | 54 | def test_05_ConfigFromSettingsWithoutOauthScope(self): 55 | # Test if authentication works without oauth_scope 56 | ga = GoogleAuth('settings/test5.yaml') 57 | ga.LocalWebserverAuth() 58 | self.assertEqual(ga.access_token_expired, False) 59 | time.sleep(1) 60 | 61 | def DeleteOldCredentialsFile(self, credentials): 62 | try: 63 | os.remove(credentials) 64 | except OSError: 65 | pass 66 | 67 | def CheckCredentialsFile(self, credentials, no_file=False): 68 | ga = GoogleAuth('settings/default.yaml') 69 | ga.LoadCredentialsFile(credentials) 70 | self.assertEqual(ga.access_token_expired, no_file) 71 | 72 | if __name__ == '__main__': 73 | unittest.main() 74 | -------------------------------------------------------------------------------- /pydrive/test/test_util.py: -------------------------------------------------------------------------------- 1 | import random 2 | import re 3 | 4 | newline_pattern = re.compile(r'[\r\n]') 5 | 6 | 7 | def CreateRandomFileName(): 8 | hash = random.getrandbits(128) 9 | return "%032x" % hash 10 | 11 | 12 | def StripNewlines(string): 13 | return newline_pattern.sub("", string) -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='PyDrive', 5 | version='1.3.1', 6 | author='JunYoung Gwak', 7 | author_email='jgwak@dreamylab.com', 8 | maintainer='Robin Nabel', 9 | maintainer_email='rnabel@ucdavis.edu', 10 | packages=['pydrive', 'pydrive.test'], 11 | url='https://github.com/googleworkspace/PyDrive', 12 | license='LICENSE', 13 | description='Google Drive API made easy.', 14 | long_description=open('README.rst').read(), 15 | install_requires=[ 16 | "google-api-python-client >= 1.2", 17 | "oauth2client >= 4.0.0", 18 | "PyYAML >= 3.0", 19 | ], 20 | ) 21 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py26, py27, py33, py34 3 | 4 | [testenv] 5 | changedir = {toxinidir}/pydrive/test 6 | deps = 7 | pytest 8 | httplib2 9 | PyYAML 10 | git+https://github.com/google/google-api-python-client.git 11 | commands = 12 | py.test -v -s 13 | --------------------------------------------------------------------------------