-
-
-
-
diff --git a/docs/epy/crarr.png b/docs/epy/crarr.png
deleted file mode 100644
index 26b43c52433b71e72a9a478c52d446278335f0e4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 340
zcmeAS@N?(olHy`uVBq!ia0vp^f?NMQuI$%1#8??M1uoZK
z0}62#ctjR6FvuMOVaB`*rFK9;mUKs7M+SzC{oH>NS%G}l0G|-o|NsA=J-p%i`2!7U
zCdJ_j4{u-SDsoA1U`TRixpVcz%O`iHHAYk?=&YaLkmD!Pp6~GW^M_S4D^grJKD>P~
zuPf!ku`N^TLavn`Edv_JSQ6wH%;50sMjDXg>*?YcQgJIe!GUqln>_|<+Os&OOUQS1
zY~Wzutud*iVS#|PHMc&?2WHoZpEo8l+6!Oc$x~=%U)469Gl^f?nq7UBw#1AXkrEde
cmFKWBXcRFE*(?@T0vgQV>FVdQ&MBb@0LpZ4r2qf`
diff --git a/docs/epy/epydoc.css b/docs/epy/epydoc.css
deleted file mode 100644
index 86d41706824..00000000000
--- a/docs/epy/epydoc.css
+++ /dev/null
@@ -1,322 +0,0 @@
-
-
-/* Epydoc CSS Stylesheet
- *
- * This stylesheet can be used to customize the appearance of epydoc's
- * HTML output.
- *
- */
-
-/* Default Colors & Styles
- * - Set the default foreground & background color with 'body'; and
- * link colors with 'a:link' and 'a:visited'.
- * - Use bold for decision list terms.
- * - The heading styles defined here are used for headings *within*
- * docstring descriptions. All headings used by epydoc itself use
- * either class='epydoc' or class='toc' (CSS styles for both
- * defined below).
- */
-body { background: #ffffff; color: #000000; }
-p { margin-top: 0.5em; margin-bottom: 0.5em; }
-a:link { color: #0000ff; }
-a:visited { color: #204080; }
-dt { font-weight: bold; }
-h1 { font-size: +140%; font-style: italic;
- font-weight: bold; }
-h2 { font-size: +125%; font-style: italic;
- font-weight: bold; }
-h3 { font-size: +110%; font-style: italic;
- font-weight: normal; }
-code { font-size: 100%; }
-/* N.B.: class, not pseudoclass */
-a.link { font-family: monospace; }
-
-/* Page Header & Footer
- * - The standard page header consists of a navigation bar (with
- * pointers to standard pages such as 'home' and 'trees'); a
- * breadcrumbs list, which can be used to navigate to containing
- * classes or modules; options links, to show/hide private
- * variables and to show/hide frames; and a page title (using
- *
). The page title may be followed by a link to the
- * corresponding source code (using 'span.codelink').
- * - The footer consists of a navigation bar, a timestamp, and a
- * pointer to epydoc's homepage.
- */
-h1.epydoc { margin: 0; font-size: +140%; font-weight: bold; }
-h2.epydoc { font-size: +130%; font-weight: bold; }
-h3.epydoc { font-size: +115%; font-weight: bold;
- margin-top: 0.2em; }
-td h3.epydoc { font-size: +115%; font-weight: bold;
- margin-bottom: 0; }
-table.navbar { background: #a0c0ff; color: #000000;
- border: 2px groove #c0d0d0; }
-table.navbar table { color: #000000; }
-th.navbar-select { background: #70b0ff;
- color: #000000; }
-table.navbar a { text-decoration: none; }
-table.navbar a:link { color: #0000ff; }
-table.navbar a:visited { color: #204080; }
-span.breadcrumbs { font-size: 85%; font-weight: bold; }
-span.options { font-size: 70%; }
-span.codelink { font-size: 85%; }
-td.footer { font-size: 85%; }
-
-/* Table Headers
- * - Each summary table and details section begins with a 'header'
- * row. This row contains a section title (marked by
- * 'span.table-header') as well as a show/hide private link
- * (marked by 'span.options', defined above).
- * - Summary tables that contain user-defined groups mark those
- * groups using 'group header' rows.
- */
-td.table-header { background: #70b0ff; color: #000000;
- border: 1px solid #608090; }
-td.table-header table { color: #000000; }
-td.table-header table a:link { color: #0000ff; }
-td.table-header table a:visited { color: #204080; }
-span.table-header { font-size: 120%; font-weight: bold; }
-th.group-header { background: #c0e0f8; color: #000000;
- text-align: left; font-style: italic;
- font-size: 115%;
- border: 1px solid #608090; }
-
-/* Summary Tables (functions, variables, etc)
- * - Each object is described by a single row of the table with
- * two cells. The left cell gives the object's type, and is
- * marked with 'code.summary-type'. The right cell gives the
- * object's name and a summary description.
- * - CSS styles for the table's header and group headers are
- * defined above, under 'Table Headers'
- */
-table.summary { border-collapse: collapse;
- background: #e8f0f8; color: #000000;
- border: 1px solid #608090;
- margin-bottom: 0.5em; }
-td.summary { border: 1px solid #608090; }
-code.summary-type { font-size: 85%; }
-table.summary a:link { color: #0000ff; }
-table.summary a:visited { color: #204080; }
-
-
-/* Details Tables (functions, variables, etc)
- * - Each object is described in its own div.
- * - A single-row summary table w/ table-header is used as
- * a header for each details section (CSS style for table-header
- * is defined above, under 'Table Headers').
- */
-table.details { border-collapse: collapse;
- background: #e8f0f8; color: #000000;
- border: 1px solid #608090;
- margin: .2em 0 0 0; }
-table.details table { color: #000000; }
-table.details a:link { color: #0000ff; }
-table.details a:visited { color: #204080; }
-
-/* Fields */
-dl.fields { margin-left: 2em; margin-top: 1em;
- margin-bottom: 1em; }
-dl.fields dd ul { margin-left: 0em; padding-left: 0em; }
-dl.fields dd ul li ul { margin-left: 2em; padding-left: 0em; }
-div.fields { margin-left: 2em; }
-div.fields p { margin-bottom: 0.5em; }
-
-/* Index tables (identifier index, term index, etc)
- * - link-index is used for indices containing lists of links
- * (namely, the identifier index & term index).
- * - index-where is used in link indices for the text indicating
- * the container/source for each link.
- * - metadata-index is used for indices containing metadata
- * extracted from fields (namely, the bug index & todo index).
- */
-table.link-index { border-collapse: collapse;
- background: #e8f0f8; color: #000000;
- border: 1px solid #608090; }
-td.link-index { border-width: 0px; }
-table.link-index a:link { color: #0000ff; }
-table.link-index a:visited { color: #204080; }
-span.index-where { font-size: 70%; }
-table.metadata-index { border-collapse: collapse;
- background: #e8f0f8; color: #000000;
- border: 1px solid #608090;
- margin: .2em 0 0 0; }
-td.metadata-index { border-width: 1px; border-style: solid; }
-table.metadata-index a:link { color: #0000ff; }
-table.metadata-index a:visited { color: #204080; }
-
-/* Function signatures
- * - sig* is used for the signature in the details section.
- * - .summary-sig* is used for the signature in the summary
- * table, and when listing property accessor functions.
- * */
-.sig-name { color: #006080; }
-.sig-arg { color: #008060; }
-.sig-default { color: #602000; }
-.summary-sig { font-family: monospace; }
-.summary-sig-name { color: #006080; font-weight: bold; }
-table.summary a.summary-sig-name:link
- { color: #006080; font-weight: bold; }
-table.summary a.summary-sig-name:visited
- { color: #006080; font-weight: bold; }
-.summary-sig-arg { color: #006040; }
-.summary-sig-default { color: #501800; }
-
-/* Subclass list
- */
-ul.subclass-list { display: inline; }
-ul.subclass-list li { display: inline; }
-
-/* To render variables, classes etc. like functions */
-table.summary .summary-name { color: #006080; font-weight: bold;
- font-family: monospace; }
-table.summary
- a.summary-name:link { color: #006080; font-weight: bold;
- font-family: monospace; }
-table.summary
- a.summary-name:visited { color: #006080; font-weight: bold;
- font-family: monospace; }
-
-/* Variable values
- * - In the 'variable details' sections, each varaible's value is
- * listed in a 'pre.variable' box. The width of this box is
- * restricted to 80 chars; if the value's repr is longer than
- * this it will be wrapped, using a backslash marked with
- * class 'variable-linewrap'. If the value's repr is longer
- * than 3 lines, the rest will be ellided; and an ellipsis
- * marker ('...' marked with 'variable-ellipsis') will be used.
- * - If the value is a string, its quote marks will be marked
- * with 'variable-quote'.
- * - If the variable is a regexp, it is syntax-highlighted using
- * the re* CSS classes.
- */
-pre.variable { padding: .5em; margin: 0;
- background: #dce4ec; color: #000000;
- border: 1px solid #708890; }
-.variable-linewrap { color: #604000; font-weight: bold; }
-.variable-ellipsis { color: #604000; font-weight: bold; }
-.variable-quote { color: #604000; font-weight: bold; }
-.variable-group { color: #008000; font-weight: bold; }
-.variable-op { color: #604000; font-weight: bold; }
-.variable-string { color: #006030; }
-.variable-unknown { color: #a00000; font-weight: bold; }
-.re { color: #000000; }
-.re-char { color: #006030; }
-.re-op { color: #600000; }
-.re-group { color: #003060; }
-.re-ref { color: #404040; }
-
-/* Base tree
- * - Used by class pages to display the base class hierarchy.
- */
-pre.base-tree { font-size: 80%; margin: 0; }
-
-/* Frames-based table of contents headers
- * - Consists of two frames: one for selecting modules; and
- * the other listing the contents of the selected module.
- * - h1.toc is used for each frame's heading
- * - h2.toc is used for subheadings within each frame.
- */
-h1.toc { text-align: center; font-size: 105%;
- margin: 0; font-weight: bold;
- padding: 0; }
-h2.toc { font-size: 100%; font-weight: bold;
- margin: 0.5em 0 0 -0.3em; }
-
-/* Syntax Highlighting for Source Code
- * - doctest examples are displayed in a 'pre.py-doctest' block.
- * If the example is in a details table entry, then it will use
- * the colors specified by the 'table pre.py-doctest' line.
- * - Source code listings are displayed in a 'pre.py-src' block.
- * Each line is marked with 'span.py-line' (used to draw a line
- * down the left margin, separating the code from the line
- * numbers). Line numbers are displayed with 'span.py-lineno'.
- * The expand/collapse block toggle button is displayed with
- * 'a.py-toggle' (Note: the CSS style for 'a.py-toggle' should not
- * modify the font size of the text.)
- * - If a source code page is opened with an anchor, then the
- * corresponding code block will be highlighted. The code
- * block's header is highlighted with 'py-highlight-hdr'; and
- * the code block's body is highlighted with 'py-highlight'.
- * - The remaining py-* classes are used to perform syntax
- * highlighting (py-string for string literals, py-name for names,
- * etc.)
- */
-pre.py-doctest { padding: .5em; margin: 1em;
- background: #e8f0f8; color: #000000;
- border: 1px solid #708890; }
-table pre.py-doctest { background: #dce4ec;
- color: #000000; }
-pre.py-src { border: 2px solid #000000;
- background: #f0f0f0; color: #000000; }
-.py-line { border-left: 2px solid #000000;
- margin-left: .2em; padding-left: .4em; }
-.py-lineno { font-style: italic; font-size: 90%;
- padding-left: .5em; }
-a.py-toggle { text-decoration: none; }
-div.py-highlight-hdr { border-top: 2px solid #000000;
- border-bottom: 2px solid #000000;
- background: #d8e8e8; }
-div.py-highlight { border-bottom: 2px solid #000000;
- background: #d0e0e0; }
-.py-prompt { color: #005050; font-weight: bold;}
-.py-more { color: #005050; font-weight: bold;}
-.py-string { color: #006030; }
-.py-comment { color: #003060; }
-.py-keyword { color: #600000; }
-.py-output { color: #404040; }
-.py-name { color: #000050; }
-.py-name:link { color: #000050 !important; }
-.py-name:visited { color: #000050 !important; }
-.py-number { color: #005000; }
-.py-defname { color: #000060; font-weight: bold; }
-.py-def-name { color: #000060; font-weight: bold; }
-.py-base-class { color: #000060; }
-.py-param { color: #000060; }
-.py-docstring { color: #006030; }
-.py-decorator { color: #804020; }
-/* Use this if you don't want links to names underlined: */
-/*a.py-name { text-decoration: none; }*/
-
-/* Graphs & Diagrams
- * - These CSS styles are used for graphs & diagrams generated using
- * Graphviz dot. 'img.graph-without-title' is used for bare
- * diagrams (to remove the border created by making the image
- * clickable).
- */
-img.graph-without-title { border: none; }
-img.graph-with-title { border: 1px solid #000000; }
-span.graph-title { font-weight: bold; }
-span.graph-caption { }
-
-/* General-purpose classes
- * - 'p.indent-wrapped-lines' defines a paragraph whose first line
- * is not indented, but whose subsequent lines are.
- * - The 'nomargin-top' class is used to remove the top margin (e.g.
- * from lists). The 'nomargin' class is used to remove both the
- * top and bottom margin (but not the left or right margin --
- * for lists, that would cause the bullets to disappear.)
- */
-p.indent-wrapped-lines { padding: 0 0 0 7em; text-indent: -7em;
- margin: 0; }
-.nomargin-top { margin-top: 0; }
-.nomargin { margin-top: 0; margin-bottom: 0; }
-
-/* HTML Log */
-div.log-block { padding: 0; margin: .5em 0 .5em 0;
- background: #e8f0f8; color: #000000;
- border: 1px solid #000000; }
-div.log-error { padding: .1em .3em .1em .3em; margin: 4px;
- background: #ffb0b0; color: #000000;
- border: 1px solid #000000; }
-div.log-warning { padding: .1em .3em .1em .3em; margin: 4px;
- background: #ffffb0; color: #000000;
- border: 1px solid #000000; }
-div.log-info { padding: .1em .3em .1em .3em; margin: 4px;
- background: #b0ffb0; color: #000000;
- border: 1px solid #000000; }
-h2.log-hdr { background: #70b0ff; color: #000000;
- margin: 0; padding: 0em 0.5em 0em 0.5em;
- border-bottom: 1px solid #000000; font-size: 110%; }
-p.log { font-weight: bold; margin: .5em 0 .5em 0; }
-tr.opt-changed { color: #000000; font-weight: bold; }
-tr.opt-default { color: #606060; }
-pre.log { margin: 0; padding: 0; padding-left: 1em; }
diff --git a/docs/epy/epydoc.js b/docs/epy/epydoc.js
deleted file mode 100644
index e787dbcf471..00000000000
--- a/docs/epy/epydoc.js
+++ /dev/null
@@ -1,293 +0,0 @@
-function toggle_private() {
- // Search for any private/public links on this page. Store
- // their old text in "cmd," so we will know what action to
- // take; and change their text to the opposite action.
- var cmd = "?";
- var elts = document.getElementsByTagName("a");
- for(var i=0; i";
- s += " ";
- for (var i=0; i... ";
- elt.innerHTML = s;
- }
-}
-
-function toggle(id) {
- elt = document.getElementById(id+"-toggle");
- if (elt.innerHTML == "-")
- collapse(id);
- else
- expand(id);
- return false;
-}
-
-function highlight(id) {
- var elt = document.getElementById(id+"-def");
- if (elt) elt.className = "py-highlight-hdr";
- var elt = document.getElementById(id+"-expanded");
- if (elt) elt.className = "py-highlight";
- var elt = document.getElementById(id+"-collapsed");
- if (elt) elt.className = "py-highlight";
-}
-
-function num_lines(s) {
- var n = 1;
- var pos = s.indexOf("\n");
- while ( pos > 0) {
- n += 1;
- pos = s.indexOf("\n", pos+1);
- }
- return n;
-}
-
-// Collapse all blocks that mave more than `min_lines` lines.
-function collapse_all(min_lines) {
- var elts = document.getElementsByTagName("div");
- for (var i=0; i 0)
- if (elt.id.substring(split, elt.id.length) == "-expanded")
- if (num_lines(elt.innerHTML) > min_lines)
- collapse(elt.id.substring(0, split));
- }
-}
-
-function expandto(href) {
- var start = href.indexOf("#")+1;
- if (start != 0 && start != href.length) {
- if (href.substring(start, href.length) != "-") {
- collapse_all(4);
- pos = href.indexOf(".", start);
- while (pos != -1) {
- var id = href.substring(start, pos);
- expand(id);
- pos = href.indexOf(".", pos+1);
- }
- var id = href.substring(start, href.length);
- expand(id);
- highlight(id);
- }
- }
-}
-
-function kill_doclink(id) {
- var parent = document.getElementById(id);
- parent.removeChild(parent.childNodes.item(0));
-}
-function auto_kill_doclink(ev) {
- if (!ev) var ev = window.event;
- if (!this.contains(ev.toElement)) {
- var parent = document.getElementById(this.parentID);
- parent.removeChild(parent.childNodes.item(0));
- }
-}
-
-function doclink(id, name, targets_id) {
- var elt = document.getElementById(id);
-
- // If we already opened the box, then destroy it.
- // (This case should never occur, but leave it in just in case.)
- if (elt.childNodes.length > 1) {
- elt.removeChild(elt.childNodes.item(0));
- }
- else {
- // The outer box: relative + inline positioning.
- var box1 = document.createElement("div");
- box1.style.position = "relative";
- box1.style.display = "inline";
- box1.style.top = 0;
- box1.style.left = 0;
-
- // A shadow for fun
- var shadow = document.createElement("div");
- shadow.style.position = "absolute";
- shadow.style.left = "-1.3em";
- shadow.style.top = "-1.3em";
- shadow.style.background = "#404040";
-
- // The inner box: absolute positioning.
- var box2 = document.createElement("div");
- box2.style.position = "relative";
- box2.style.border = "1px solid #a0a0a0";
- box2.style.left = "-.2em";
- box2.style.top = "-.2em";
- box2.style.background = "white";
- box2.style.padding = ".3em .4em .3em .4em";
- box2.style.fontStyle = "normal";
- box2.onmouseout=auto_kill_doclink;
- box2.parentID = id;
-
- // Get the targets
- var targets_elt = document.getElementById(targets_id);
- var targets = targets_elt.getAttribute("targets");
- var links = "";
- target_list = targets.split(",");
- for (var i=0; i" +
- target[0] + "";
- }
-
- // Put it all together.
- elt.insertBefore(box1, elt.childNodes.item(0));
- //box1.appendChild(box2);
- box1.appendChild(shadow);
- shadow.appendChild(box2);
- box2.innerHTML =
- "Which "+name+" do you want to see documentation for?" +
- "
- 1# Copyright 2014 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
-10# distributed under the License is distributed on an "AS IS" BASIS,
-11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-12# See the License for the specific language governing permissions and
-13# limitations under the License.
-14
-15# Set default logging handler to avoid "No handler found" warnings.
-16importlogging
-17
-18try:# Python 2.7+
-19fromloggingimportNullHandler
-20exceptImportError:
-21
-
-
-Do whatever it takes to actually log the specified logging record.
-
-This version is intended to be implemented by subclasses and so
-raises a NotImplementedError.
-
-
-Scopes the credentials if necessary.
-
-Args:
- credentials (Union[
- google.auth.credentials.Credentials,
- oauth2client.client.Credentials]): The credentials to scope.
- scopes (Sequence[str]): The list of scopes.
-
-Returns:
- Union[google.auth.credentials.Credentials,
- oauth2client.client.Credentials]: The scoped credentials.
-
-
-Returns an http client that is authorized with the given credentials.
-
-Args:
- credentials (Union[
- google.auth.credentials.Credentials,
- oauth2client.client.Credentials]): The credentials to use.
-
-Returns:
- Union[httplib2.Http, google_auth_httplib2.AuthorizedHttp]: An
- authorized http client.
-
-
- 1# Copyright 2016 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Helpers for authentication using oauth2client or google-auth."""
- 16
- 17importhttplib2
- 18
- 19try:
- 20importgoogle.auth
- 21importgoogle.auth.credentials
- 22
- 23HAS_GOOGLE_AUTH=True
- 24exceptImportError:# pragma: NO COVER
- 25HAS_GOOGLE_AUTH=False
- 26
- 27try:
- 28importgoogle_auth_httplib2
- 29exceptImportError:# pragma: NO COVER
- 30google_auth_httplib2=None
- 31
- 32try:
- 33importoauth2client
- 34importoauth2client.client
- 35
- 36HAS_OAUTH2CLIENT=True
- 37exceptImportError:# pragma: NO COVER
- 38HAS_OAUTH2CLIENT=False
- 39
- 40
-
42"""Returns credentials loaded from a file."""
- 43ifHAS_GOOGLE_AUTH:
- 44credentials,_=google.auth.load_credentials_from_file(filename,scopes=scopes,quota_project_id=quota_project_id)
- 45returncredentials
- 46else:
- 47raiseEnvironmentError(
- 48"client_options.credentials_file is only supported in google-auth.")
-
96"""Returns an http client that is authorized with the given credentials.
- 97
- 98 Args:
- 99 credentials (Union[
-100 google.auth.credentials.Credentials,
-101 oauth2client.client.Credentials]): The credentials to use.
-102
-103 Returns:
-104 Union[httplib2.Http, google_auth_httplib2.AuthorizedHttp]: An
-105 authorized http client.
-106 """
-107fromgoogleapiclient.httpimportbuild_http
-108
-109ifHAS_GOOGLE_AUTHandisinstance(credentials,google.auth.credentials.Credentials):
-110ifgoogle_auth_httplib2isNone:
-111raiseValueError(
-112"Credentials from google.auth specified, but "
-113"google-api-python-client is unable to use these credentials "
-114"unless google-auth-httplib2 is installed. Please install "
-115"google-auth-httplib2."
-116)
-117returngoogle_auth_httplib2.AuthorizedHttp(credentials,http=build_http())
-118else:
-119returncredentials.authorize(build_http())
-
123# Refresh must use a new http instance, as the one associated with the
-124# credentials could be a AuthorizedHttp or an oauth2client-decorated
-125# Http instance which would cause a weird recursive loop of refreshing
-126# and likely tear a hole in spacetime.
-127refresh_http=httplib2.Http()
-128ifHAS_GOOGLE_AUTHandisinstance(credentials,google.auth.credentials.Credentials):
-129request=google_auth_httplib2.Request(refresh_http)
-130returncredentials.refresh(request)
-131else:
-132returncredentials.refresh(refresh_http)
-
136# oauth2client and google-auth have the same interface for this.
-137ifnotis_valid(credentials):
-138refresh_credentials(credentials)
-139returncredentials.apply(headers)
-
-A decorator to declare that only the first N arguments may be positional.
-
-This decorator makes it easy to support Python 3 style keyword-only
-parameters. For example, in Python 3 it is possible to write::
-
- def fn(pos1, *, kwonly1=None, kwonly1=None):
- ...
-
-All named parameters after ``*`` must be a keyword::
-
- fn(10, 'kw1', 'kw2') # Raises exception.
- fn(10, kwonly1='kw1') # Ok.
-
-Example
-^^^^^^^
-
-To define a function like above, do::
-
- @positional(1)
- def fn(pos1, kwonly1=None, kwonly2=None):
- ...
-
-If no default value is provided to a keyword argument, it becomes a
-required keyword argument::
-
- @positional(0)
- def fn(required_kw):
- ...
-
-This must be called with the keyword parameter::
-
- fn() # Raises exception.
- fn(10) # Raises exception.
- fn(required_kw=10) # Ok.
-
-When defining instance or class methods always remember to account for
-``self`` and ``cls``::
-
- class MyClass(object):
-
- @positional(2)
- def my_method(self, pos1, kwonly1=None):
- ...
-
- @classmethod
- @positional(2)
- def my_method(cls, pos1, kwonly1=None):
- ...
-
-The positional decorator behavior is controlled by
-``_helpers.positional_parameters_enforcement``, which may be set to
-``POSITIONAL_EXCEPTION``, ``POSITIONAL_WARNING`` or
-``POSITIONAL_IGNORE`` to raise an exception, log a warning, or do
-nothing, respectively, if a declaration is violated.
-
-Args:
- max_positional_arguments: Maximum number of positional arguments. All
- parameters after the this index must be
- keyword only.
-
-Returns:
- A decorator that prevents using arguments after max_positional_args
- from being used as positional parameters.
-
-Raises:
- TypeError: if a key-word only argument is provided as a positional
- parameter, but only if
- _helpers.positional_parameters_enforcement is set to
- POSITIONAL_EXCEPTION.
-
-
-Parses unique key-value parameters from urlencoded content.
-
-Args:
- content: string, URL-encoded key-value pairs.
-
-Returns:
- dict, The key-value pairs from ``content``.
-
-Raises:
- ValueError: if one of the keys is repeated.
-
-
-Updates a URI with new query parameters.
-
-If a given key from ``params`` is repeated in the ``uri``, then
-the URI will be considered invalid and an error will occur.
-
-If the URI is valid, then each value from ``params`` will
-replace the corresponding value in the query parameters (if
-it exists).
-
-Args:
- uri: string, A valid URI, with potential existing query parameters.
- params: dict, A dictionary of query parameters.
-
-Returns:
- The same URI but with the new query parameters added.
-
-
-Adds a query parameter to a url.
-
-Replaces the current value if it already exists in the URL.
-
-Args:
- url: string, url to add the query parameter to.
- name: string, query parameter name.
- value: string, query parameter value.
-
-Returns:
- Updated query parameter. Does not update the url if value is None.
-
-
- 1# Copyright 2015 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Helper functions for commonly used utilities."""
- 16
- 17importfunctools
- 18importinspect
- 19importlogging
- 20importwarnings
- 21
- 22importsix
- 23fromsix.movesimporturllib
- 24
- 25
- 26logger=logging.getLogger(__name__)
- 27
- 28POSITIONAL_WARNING="WARNING"
- 29POSITIONAL_EXCEPTION="EXCEPTION"
- 30POSITIONAL_IGNORE="IGNORE"
- 31POSITIONAL_SET=frozenset(
- 32[POSITIONAL_WARNING,POSITIONAL_EXCEPTION,POSITIONAL_IGNORE]
- 33)
- 34
- 35positional_parameters_enforcement=POSITIONAL_WARNING
- 36
- 37_SYM_LINK_MESSAGE="File: {0}: Is a symbolic link."
- 38_IS_DIR_MESSAGE="{0}: Is a directory"
- 39_MISSING_FILE_MESSAGE="Cannot access {0}: No such file or directory"
-
43"""A decorator to declare that only the first N arguments may be positional.
- 44
- 45 This decorator makes it easy to support Python 3 style keyword-only
- 46 parameters. For example, in Python 3 it is possible to write::
- 47
- 48 def fn(pos1, *, kwonly1=None, kwonly1=None):
- 49 ...
- 50
- 51 All named parameters after ``*`` must be a keyword::
- 52
- 53 fn(10, 'kw1', 'kw2') # Raises exception.
- 54 fn(10, kwonly1='kw1') # Ok.
- 55
- 56 Example
- 57 ^^^^^^^
- 58
- 59 To define a function like above, do::
- 60
- 61 @positional(1)
- 62 def fn(pos1, kwonly1=None, kwonly2=None):
- 63 ...
- 64
- 65 If no default value is provided to a keyword argument, it becomes a
- 66 required keyword argument::
- 67
- 68 @positional(0)
- 69 def fn(required_kw):
- 70 ...
- 71
- 72 This must be called with the keyword parameter::
- 73
- 74 fn() # Raises exception.
- 75 fn(10) # Raises exception.
- 76 fn(required_kw=10) # Ok.
- 77
- 78 When defining instance or class methods always remember to account for
- 79 ``self`` and ``cls``::
- 80
- 81 class MyClass(object):
- 82
- 83 @positional(2)
- 84 def my_method(self, pos1, kwonly1=None):
- 85 ...
- 86
- 87 @classmethod
- 88 @positional(2)
- 89 def my_method(cls, pos1, kwonly1=None):
- 90 ...
- 91
- 92 The positional decorator behavior is controlled by
- 93 ``_helpers.positional_parameters_enforcement``, which may be set to
- 94 ``POSITIONAL_EXCEPTION``, ``POSITIONAL_WARNING`` or
- 95 ``POSITIONAL_IGNORE`` to raise an exception, log a warning, or do
- 96 nothing, respectively, if a declaration is violated.
- 97
- 98 Args:
- 99 max_positional_arguments: Maximum number of positional arguments. All
-100 parameters after the this index must be
-101 keyword only.
-102
-103 Returns:
-104 A decorator that prevents using arguments after max_positional_args
-105 from being used as positional parameters.
-106
-107 Raises:
-108 TypeError: if a key-word only argument is provided as a positional
-109 parameter, but only if
-110 _helpers.positional_parameters_enforcement is set to
-111 POSITIONAL_EXCEPTION.
-112 """
-113
-114defpositional_decorator(wrapped):
-115@functools.wraps(wrapped)
-116defpositional_wrapper(*args,**kwargs):
-117iflen(args)>max_positional_args:
-118plural_s=""
-119ifmax_positional_args!=1:
-120plural_s="s"
-121message=(
-122"{function}() takes at most {args_max} positional "
-123"argument{plural} ({args_given} given)".format(
-124function=wrapped.__name__,
-125args_max=max_positional_args,
-126args_given=len(args),
-127plural=plural_s,
-128)
-129)
-130ifpositional_parameters_enforcement==POSITIONAL_EXCEPTION:
-131raiseTypeError(message)
-132elifpositional_parameters_enforcement==POSITIONAL_WARNING:
-133logger.warning(message)
-134returnwrapped(*args,**kwargs)
-
171"""Updates a URI with new query parameters.
-172
-173 If a given key from ``params`` is repeated in the ``uri``, then
-174 the URI will be considered invalid and an error will occur.
-175
-176 If the URI is valid, then each value from ``params`` will
-177 replace the corresponding value in the query parameters (if
-178 it exists).
-179
-180 Args:
-181 uri: string, A valid URI, with potential existing query parameters.
-182 params: dict, A dictionary of query parameters.
-183
-184 Returns:
-185 The same URI but with the new query parameters added.
-186 """
-187parts=urllib.parse.urlparse(uri)
-188query_params=parse_unique_urlencoded(parts.query)
-189query_params.update(params)
-190new_query=urllib.parse.urlencode(query_params)
-191new_parts=parts._replace(query=new_query)
-192returnurllib.parse.urlunparse(new_parts)
-
196"""Adds a query parameter to a url.
-197
-198 Replaces the current value if it already exists in the URL.
-199
-200 Args:
-201 url: string, url to add the query parameter to.
-202 name: string, query parameter name.
-203 value: string, query parameter value.
-204
-205 Returns:
-206 Updated query parameter. Does not update the url if value is None.
-207 """
-208ifvalueisNone:
-209returnurl
-210else:
-211returnupdate_query_params(url,{name:value})
-
-Channel notifications support.
-
-Classes and functions to support channel subscriptions and notifications
-on those channels.
-
-Notes:
- - This code is based on experimental APIs and is subject to change.
- - Notification does not do deduplication of notification ids, that's up to
- the receiver.
- - Storing the Channel between calls is up to the caller.
-
-
-Example setting up a channel:
-
- # Create a new channel that gets notifications via webhook.
- channel = new_webhook_channel("https://example.com/my_web_hook")
-
- # Store the channel, keyed by 'channel.id'. Store it before calling the
- # watch method because notifications may start arriving before the watch
- # method returns.
- ...
-
- resp = service.objects().watchAll(
- bucket="some_bucket_id", body=channel.body()).execute()
- channel.update(resp)
-
- # Store the channel, keyed by 'channel.id'. Store it after being updated
- # since the resource_id value will now be correct, and that's needed to
- # stop a subscription.
- ...
-
-
-An example Webhook implementation using webapp2. Note that webapp2 puts
-headers in a case insensitive dictionary, as headers aren't guaranteed to
-always be upper case.
-
- id = self.request.headers[X_GOOG_CHANNEL_ID]
-
- # Retrieve the channel by id.
- channel = ...
-
- # Parse notification from the headers, including validating the id.
- n = notification_from_headers(channel, self.request.headers)
-
- # Do app specific stuff with the notification here.
- if n.resource_state == 'sync':
- # Code to handle sync state.
- elif n.resource_state == 'exists':
- # Code to handle the exists state.
- elif n.resource_state == 'not_exists':
- # Code to handle the not exists state.
-
-
-Example of unsubscribing.
-
- service.channels().stop(channel.body()).execute()
-
-
notification_from_headers(channel,
- headers)
- Parse a notification from the webhook request headers, validate
- the notification, and return a Notification object.
-Parse a notification from the webhook request headers, validate
- the notification, and return a Notification object.
-
-Args:
- channel: Channel, The channel that the notification is associated with.
- headers: dict, A dictionary like object that contains the request headers
- from the webhook HTTP request.
-
-Returns:
- A Notification object.
-
-Raises:
- errors.InvalidNotificationError if the notification is invalid.
- ValueError if the X-GOOG-MESSAGE-NUMBER can't be converted to an int.
-
-
-Create a new webhook Channel.
-
-Args:
- url: str, URL to post notifications to.
- token: str, An arbitrary string associated with the channel that
- is delivered to the target address with each notification delivered
- over this channel.
- expiration: datetime.datetime, A time in the future when the channel
- should expire. Can also be None if the subscription should use the
- default expiration. Note that different services may have different
- limits on how long a subscription lasts. Check the response from the
- watch() method to see the value the service has set for an expiration
- time.
- params: dict, Extra parameters to pass on channel creation. Currently
- not used for webhook channels.
-
-
- 1# Copyright 2014 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Channel notifications support.
- 16
- 17Classes and functions to support channel subscriptions and notifications
- 18on those channels.
- 19
- 20Notes:
- 21 - This code is based on experimental APIs and is subject to change.
- 22 - Notification does not do deduplication of notification ids, that's up to
- 23 the receiver.
- 24 - Storing the Channel between calls is up to the caller.
- 25
- 26
- 27Example setting up a channel:
- 28
- 29 # Create a new channel that gets notifications via webhook.
- 30 channel = new_webhook_channel("https://example.com/my_web_hook")
- 31
- 32 # Store the channel, keyed by 'channel.id'. Store it before calling the
- 33 # watch method because notifications may start arriving before the watch
- 34 # method returns.
- 35 ...
- 36
- 37 resp = service.objects().watchAll(
- 38 bucket="some_bucket_id", body=channel.body()).execute()
- 39 channel.update(resp)
- 40
- 41 # Store the channel, keyed by 'channel.id'. Store it after being updated
- 42 # since the resource_id value will now be correct, and that's needed to
- 43 # stop a subscription.
- 44 ...
- 45
- 46
- 47An example Webhook implementation using webapp2. Note that webapp2 puts
- 48headers in a case insensitive dictionary, as headers aren't guaranteed to
- 49always be upper case.
- 50
- 51 id = self.request.headers[X_GOOG_CHANNEL_ID]
- 52
- 53 # Retrieve the channel by id.
- 54 channel = ...
- 55
- 56 # Parse notification from the headers, including validating the id.
- 57 n = notification_from_headers(channel, self.request.headers)
- 58
- 59 # Do app specific stuff with the notification here.
- 60 if n.resource_state == 'sync':
- 61 # Code to handle sync state.
- 62 elif n.resource_state == 'exists':
- 63 # Code to handle the exists state.
- 64 elif n.resource_state == 'not_exists':
- 65 # Code to handle the not exists state.
- 66
- 67
- 68Example of unsubscribing.
- 69
- 70 service.channels().stop(channel.body()).execute()
- 71"""
- 72from__future__importabsolute_import
- 73
- 74importdatetime
- 75importuuid
- 76
- 77fromgoogleapiclientimporterrors
- 78fromgoogleapiclientimport_helpersasutil
- 79importsix
- 80
- 81
- 82# The unix time epoch starts at midnight 1970.
- 83EPOCH=datetime.datetime.utcfromtimestamp(0)
- 84
- 85# Map the names of the parameters in the JSON channel description to
- 86# the parameter names we use in the Channel class.
- 87CHANNEL_PARAMS={
- 88"address":"address",
- 89"id":"id",
- 90"expiration":"expiration",
- 91"params":"params",
- 92"resourceId":"resource_id",
- 93"resourceUri":"resource_uri",
- 94"type":"type",
- 95"token":"token",
- 96}
- 97
- 98X_GOOG_CHANNEL_ID="X-GOOG-CHANNEL-ID"
- 99X_GOOG_MESSAGE_NUMBER="X-GOOG-MESSAGE-NUMBER"
-100X_GOOG_RESOURCE_STATE="X-GOOG-RESOURCE-STATE"
-101X_GOOG_RESOURCE_URI="X-GOOG-RESOURCE-URI"
-102X_GOOG_RESOURCE_ID="X-GOOG-RESOURCE-ID"
-
113"""A Notification from a Channel.
-114
-115 Notifications are not usually constructed directly, but are returned
-116 from functions like notification_from_headers().
-117
-118 Attributes:
-119 message_number: int, The unique id number of this notification.
-120 state: str, The state of the resource being monitored.
-121 uri: str, The address of the resource being monitored.
-122 resource_id: str, The unique identifier of the version of the resource at
-123 this event.
-124 """
-125
-126@util.positional(5)
-
128"""Notification constructor.
-129
-130 Args:
-131 message_number: int, The unique id number of this notification.
-132 state: str, The state of the resource being monitored. Can be one
-133 of "exists", "not_exists", or "sync".
-134 resource_uri: str, The address of the resource being monitored.
-135 resource_id: str, The identifier of the watched resource.
-136 """
-137self.message_number=message_number
-138self.state=state
-139self.resource_uri=resource_uri
-140self.resource_id=resource_id
-
144"""A Channel for notifications.
-145
-146 Usually not constructed directly, instead it is returned from helper
-147 functions like new_webhook_channel().
-148
-149 Attributes:
-150 type: str, The type of delivery mechanism used by this channel. For
-151 example, 'web_hook'.
-152 id: str, A UUID for the channel.
-153 token: str, An arbitrary string associated with the channel that
-154 is delivered to the target address with each event delivered
-155 over this channel.
-156 address: str, The address of the receiving entity where events are
-157 delivered. Specific to the channel type.
-158 expiration: int, The time, in milliseconds from the epoch, when this
-159 channel will expire.
-160 params: dict, A dictionary of string to string, with additional parameters
-161 controlling delivery channel behavior.
-162 resource_id: str, An opaque id that identifies the resource that is
-163 being watched. Stable across different API versions.
-164 resource_uri: str, The canonicalized ID of the watched resource.
-165 """
-166
-167@util.positional(5)
-
179"""Create a new Channel.
-180
-181 In user code, this Channel constructor will not typically be called
-182 manually since there are functions for creating channels for each specific
-183 type with a more customized set of arguments to pass.
-184
-185 Args:
-186 type: str, The type of delivery mechanism used by this channel. For
-187 example, 'web_hook'.
-188 id: str, A UUID for the channel.
-189 token: str, An arbitrary string associated with the channel that
-190 is delivered to the target address with each event delivered
-191 over this channel.
-192 address: str, The address of the receiving entity where events are
-193 delivered. Specific to the channel type.
-194 expiration: int, The time, in milliseconds from the epoch, when this
-195 channel will expire.
-196 params: dict, A dictionary of string to string, with additional parameters
-197 controlling delivery channel behavior.
-198 resource_id: str, An opaque id that identifies the resource that is
-199 being watched. Stable across different API versions.
-200 resource_uri: str, The canonicalized ID of the watched resource.
-201 """
-202self.type=type
-203self.id=id
-204self.token=token
-205self.address=address
-206self.expiration=expiration
-207self.params=params
-208self.resource_id=resource_id
-209self.resource_uri=resource_uri
-
212"""Build a body from the Channel.
-213
-214 Constructs a dictionary that's appropriate for passing into watch()
-215 methods as the value of body argument.
-216
-217 Returns:
-218 A dictionary representation of the channel.
-219 """
-220result={
-221"id":self.id,
-222"token":self.token,
-223"type":self.type,
-224"address":self.address,
-225}
-226ifself.params:
-227result["params"]=self.params
-228ifself.resource_id:
-229result["resourceId"]=self.resource_id
-230ifself.resource_uri:
-231result["resourceUri"]=self.resource_uri
-232ifself.expiration:
-233result["expiration"]=self.expiration
-234
-235returnresult
-
238"""Update a channel with information from the response of watch().
-239
-240 When a request is sent to watch() a resource, the response returned
-241 from the watch() request is a dictionary with updated channel information,
-242 such as the resource_id, which is needed when stopping a subscription.
-243
-244 Args:
-245 resp: dict, The response from a watch() method.
-246 """
-247forjson_name,param_nameinsix.iteritems(CHANNEL_PARAMS):
-248value=resp.get(json_name)
-249ifvalueisnotNone:
-250setattr(self,param_name,value)
-
254"""Parse a notification from the webhook request headers, validate
-255 the notification, and return a Notification object.
-256
-257 Args:
-258 channel: Channel, The channel that the notification is associated with.
-259 headers: dict, A dictionary like object that contains the request headers
-260 from the webhook HTTP request.
-261
-262 Returns:
-263 A Notification object.
-264
-265 Raises:
-266 errors.InvalidNotificationError if the notification is invalid.
-267 ValueError if the X-GOOG-MESSAGE-NUMBER can't be converted to an int.
-268 """
-269headers=_upper_header_keys(headers)
-270channel_id=headers[X_GOOG_CHANNEL_ID]
-271ifchannel.id!=channel_id:
-272raiseerrors.InvalidNotificationError(
-273"Channel id mismatch: %s != %s"%(channel.id,channel_id)
-274)
-275else:
-276message_number=int(headers[X_GOOG_MESSAGE_NUMBER])
-277state=headers[X_GOOG_RESOURCE_STATE]
-278resource_uri=headers[X_GOOG_RESOURCE_URI]
-279resource_id=headers[X_GOOG_RESOURCE_ID]
-280returnNotification(message_number,state,resource_uri,resource_id)
-
285"""Create a new webhook Channel.
-286
-287 Args:
-288 url: str, URL to post notifications to.
-289 token: str, An arbitrary string associated with the channel that
-290 is delivered to the target address with each notification delivered
-291 over this channel.
-292 expiration: datetime.datetime, A time in the future when the channel
-293 should expire. Can also be None if the subscription should use the
-294 default expiration. Note that different services may have different
-295 limits on how long a subscription lasts. Check the response from the
-296 watch() method to see the value the service has set for an expiration
-297 time.
-298 params: dict, Extra parameters to pass on channel creation. Currently
-299 not used for webhook channels.
-300 """
-301expiration_ms=0
-302ifexpiration:
-303delta=expiration-EPOCH
-304expiration_ms=(
-305delta.microseconds/1000+(delta.seconds+delta.days*24*3600)*1000
-306)
-307ifexpiration_ms<0:
-308expiration_ms=0
-309
-310returnChannel(
-311"web_hook",
-312str(uuid.uuid4()),
-313token,
-314url,
-315expiration=expiration_ms,
-316params=params,
-317)
-
-A Channel for notifications.
-
-Usually not constructed directly, instead it is returned from helper
-functions like new_webhook_channel().
-
-Attributes:
- type: str, The type of delivery mechanism used by this channel. For
- example, 'web_hook'.
- id: str, A UUID for the channel.
- token: str, An arbitrary string associated with the channel that
- is delivered to the target address with each event delivered
- over this channel.
- address: str, The address of the receiving entity where events are
- delivered. Specific to the channel type.
- expiration: int, The time, in milliseconds from the epoch, when this
- channel will expire.
- params: dict, A dictionary of string to string, with additional parameters
- controlling delivery channel behavior.
- resource_id: str, An opaque id that identifies the resource that is
- being watched. Stable across different API versions.
- resource_uri: str, The canonicalized ID of the watched resource.
-
-
-Create a new Channel.
-
-In user code, this Channel constructor will not typically be called
-manually since there are functions for creating channels for each specific
-type with a more customized set of arguments to pass.
-
-Args:
- type: str, The type of delivery mechanism used by this channel. For
- example, 'web_hook'.
- id: str, A UUID for the channel.
- token: str, An arbitrary string associated with the channel that
- is delivered to the target address with each event delivered
- over this channel.
- address: str, The address of the receiving entity where events are
- delivered. Specific to the channel type.
- expiration: int, The time, in milliseconds from the epoch, when this
- channel will expire.
- params: dict, A dictionary of string to string, with additional parameters
- controlling delivery channel behavior.
- resource_id: str, An opaque id that identifies the resource that is
- being watched. Stable across different API versions.
- resource_uri: str, The canonicalized ID of the watched resource.
-
-
-Build a body from the Channel.
-
-Constructs a dictionary that's appropriate for passing into watch()
-methods as the value of body argument.
-
-Returns:
- A dictionary representation of the channel.
-
-
-Update a channel with information from the response of watch().
-
-When a request is sent to watch() a resource, the response returned
-from the watch() request is a dictionary with updated channel information,
-such as the resource_id, which is needed when stopping a subscription.
-
-Args:
- resp: dict, The response from a watch() method.
-
-
-A Notification from a Channel.
-
-Notifications are not usually constructed directly, but are returned
-from functions like notification_from_headers().
-
-Attributes:
- message_number: int, The unique id number of this notification.
- state: str, The state of the resource being monitored.
- uri: str, The address of the resource being monitored.
- resource_id: str, The unique identifier of the version of the resource at
- this event.
-
-
-Notification constructor.
-
-Args:
- message_number: int, The unique id number of this notification.
- state: str, The state of the resource being monitored. Can be one
- of "exists", "not_exists", or "sync".
- resource_uri: str, The address of the resource being monitored.
- resource_id: str, The identifier of the watched resource.
-
-
_retrieve_discovery_doc(url,
- http,
- cache_discovery,
- cache=None,
- developerKey=None,
- num_retries=1)
- Retrieves the discovery_doc from cache or the internet.
createNextMethod(methodName,
- pageTokenName="pageToken",
- nextPageTokenName="nextPageToken",
- isPageTokenParameter=True)
- Creates any _next methods for attaching to a Resource.
-Fix method names to avoid '$' characters and reserved word conflicts.
-
-Args:
- name: string, method name.
-
-Returns:
- The name with '_' appended if the name is a reserved word and '$' and '-'
- replaced with '_'.
-
-
-Converts key names into parameter names.
-
-For example, converting "max-results" -> "max_results"
-
-Args:
- key: string, the method key name.
-
-Returns:
- A safe method name based on the key name.
-
-
-Construct a Resource for interacting with an API.
-
-Construct a Resource object for interacting with an API. The serviceName and
-version are the names from the Discovery service.
-
-Args:
- serviceName: string, name of the service.
- version: string, the version of the service.
- http: httplib2.Http, An instance of httplib2.Http or something that acts
- like it that HTTP requests will be made through.
- discoveryServiceUrl: string, a URI Template that points to the location of
- the discovery service. It should have two parameters {api} and
- {apiVersion} that when filled in produce an absolute URI to the discovery
- document for that service.
- developerKey: string, key obtained from
- https://code.google.com/apis/console.
- model: googleapiclient.Model, converts to and from the wire format.
- requestBuilder: googleapiclient.http.HttpRequest, encapsulator for an HTTP
- request.
- credentials: oauth2client.Credentials or
- google.auth.credentials.Credentials, credentials to be used for
- authentication.
- cache_discovery: Boolean, whether or not to cache the discovery doc.
- cache: googleapiclient.discovery_cache.base.CacheBase, an optional
- cache object for the discovery documents.
- client_options: Mapping object or google.api_core.client_options, client
- options to set user options on the client.
- (1) The API endpoint should be set through client_options. If API endpoint
- is not set, `GOOGLE_API_USE_MTLS_ENDPOINT` environment variable can be used
- to control which endpoint to use.
- (2) client_cert_source is not supported, client cert should be provided using
- client_encrypted_cert_source instead. In order to use the provided client
- cert, `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be
- set to `true`.
- More details on the environment variables are here:
- https://google.aip.dev/auth/4114
- adc_cert_path: str, client certificate file path to save the application
- default client certificate for mTLS. This field is required if you want to
- use the default client certificate. `GOOGLE_API_USE_CLIENT_CERTIFICATE`
- environment variable must be set to `true` in order to use this field,
- otherwise this field doesn't nothing.
- More details on the environment variables are here:
- https://google.aip.dev/auth/4114
- adc_key_path: str, client encrypted private key file path to save the
- application default client encrypted private key for mTLS. This field is
- required if you want to use the default client certificate.
- `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be set to
- `true` in order to use this field, otherwise this field doesn't nothing.
- More details on the environment variables are here:
- https://google.aip.dev/auth/4114
- num_retries: Integer, number of times to retry discovery with
- randomized exponential backoff in case of intermittent/connection issues.
-
-Returns:
- A Resource object with methods for interacting with the service.
-
-Raises:
- google.auth.exceptions.MutualTLSChannelError: if there are any problems
- setting up mutual TLS channel.
-
-
-
- Returns Discovery URIs to be used for attemnting to build the API Resource.
-
-Args:
- discoveryServiceUrl:
- string, the Original Discovery Service URL preferred by the customer.
- version:
- string, API Version requested
-
-Returns:
- A list of URIs to be tried for the Service Discovery, in order.
-
-
-
-Retrieves the discovery_doc from cache or the internet.
-
-Args:
- url: string, the URL of the discovery document.
- http: httplib2.Http, An instance of httplib2.Http or something that acts
- like it through which HTTP requests will be made.
- cache_discovery: Boolean, whether or not to cache the discovery doc.
- cache: googleapiclient.discovery_cache.base.Cache, an optional cache
- object for the discovery documents.
- developerKey: string, Key for controlling API usage, generated
- from the API Console.
- num_retries: Integer, number of times to retry discovery with
- randomized exponential backoff in case of intermittent/connection issues.
-
-Returns:
- A unicode string representation of the discovery document.
-
-
-Create a Resource for interacting with an API.
-
-Same as `build()`, but constructs the Resource object from a discovery
-document that is it given, as opposed to retrieving one over HTTP.
-
-Args:
- service: string or object, the JSON discovery document describing the API.
- The value passed in may either be the JSON string or the deserialized
- JSON.
- base: string, base URI for all HTTP requests, usually the discovery URI.
- This parameter is no longer used as rootUrl and servicePath are included
- within the discovery document. (deprecated)
- future: string, discovery document with future capabilities (deprecated).
- http: httplib2.Http, An instance of httplib2.Http or something that acts
- like it that HTTP requests will be made through.
- developerKey: string, Key for controlling API usage, generated
- from the API Console.
- model: Model class instance that serializes and de-serializes requests and
- responses.
- requestBuilder: Takes an http request and packages it up to be executed.
- credentials: oauth2client.Credentials or
- google.auth.credentials.Credentials, credentials to be used for
- authentication.
- client_options: Mapping object or google.api_core.client_options, client
- options to set user options on the client.
- (1) The API endpoint should be set through client_options. If API endpoint
- is not set, `GOOGLE_API_USE_MTLS_ENDPOINT` environment variable can be used
- to control which endpoint to use.
- (2) client_cert_source is not supported, client cert should be provided using
- client_encrypted_cert_source instead. In order to use the provided client
- cert, `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be
- set to `true`.
- More details on the environment variables are here:
- https://google.aip.dev/auth/4114
- adc_cert_path: str, client certificate file path to save the application
- default client certificate for mTLS. This field is required if you want to
- use the default client certificate. `GOOGLE_API_USE_CLIENT_CERTIFICATE`
- environment variable must be set to `true` in order to use this field,
- otherwise this field doesn't nothing.
- More details on the environment variables are here:
- https://google.aip.dev/auth/4114
- adc_key_path: str, client encrypted private key file path to save the
- application default client encrypted private key for mTLS. This field is
- required if you want to use the default client certificate.
- `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be set to
- `true` in order to use this field, otherwise this field doesn't nothing.
- More details on the environment variables are here:
- https://google.aip.dev/auth/4114
-
-Returns:
- A Resource object with methods for interacting with the service.
-
-Raises:
- google.auth.exceptions.MutualTLSChannelError: if there are any problems
- setting up mutual TLS channel.
-
-
-Convert value to a string based on JSON Schema type.
-
-See http://tools.ietf.org/html/draft-zyp-json-schema-03 for more details on
-JSON Schema.
-
-Args:
- value: any, the value to convert
- schema_type: string, the type that value should be interpreted as
-
-Returns:
- A string representation of 'value' based on the schema_type.
-
-
-Convert a string media size, such as 10GB or 3TB into an integer.
-
-Args:
- maxSize: string, size as a string, such as 2MB or 7GB.
-
-Returns:
- The size as an integer value.
-
-
-Creates an absolute media path URL.
-
-Constructed using the API root URI and service path from the discovery
-document and the relative path for the API method.
-
-Args:
- root_desc: Dictionary; the entire original deserialized discovery document.
- path_url: String; the relative URL for the API method. Relative to the API
- root, which is specified in the discovery document.
-
-Returns:
- String; the absolute URI for media upload for the API method.
-
-
-Updates parameters of an API method with values specific to this library.
-
-Specifically, adds whatever global parameters are specified by the API to the
-parameters for the individual method. Also adds parameters which don't
-appear in the discovery document, but are available to all discovery based
-APIs (these are listed in STACK_QUERY_PARAMETERS).
-
-SIDE EFFECTS: This updates the parameters dictionary object in the method
-description.
-
-Args:
- method_desc: Dictionary with metadata describing an API method. Value comes
- from the dictionary of methods stored in the 'methods' key in the
- deserialized discovery document.
- root_desc: Dictionary; the entire original deserialized discovery document.
- http_method: String; the HTTP method used to call the API method described
- in method_desc.
- schema: Object, mapping of schema names to schema descriptions.
-
-Returns:
- The updated Dictionary stored in the 'parameters' key of the method
- description dictionary.
-
-
-Adds 'media_body' and 'media_mime_type' parameters if supported by method.
-
-SIDE EFFECTS: If there is a 'mediaUpload' in the method description, adds
-'media_upload' key to parameters.
-
-Args:
- method_desc: Dictionary with metadata describing an API method. Value comes
- from the dictionary of methods stored in the 'methods' key in the
- deserialized discovery document.
- root_desc: Dictionary; the entire original deserialized discovery document.
- path_url: String; the relative URL for the API method. Relative to the API
- root, which is specified in the discovery document.
- parameters: A dictionary describing method parameters for method described
- in method_desc.
-
-Returns:
- Triple (accept, max_size, media_path_url) where:
- - accept is a list of strings representing what content types are
- accepted for media upload. Defaults to empty list if not in the
- discovery document.
- - max_size is a long representing the max size in bytes allowed for a
- media upload. Defaults to 0L if not in the discovery document.
- - media_path_url is a String; the absolute URI for media upload for the
- API method. Constructed using the API root URI and service path from
- the discovery document and the relative path for the API method. If
- media upload is not supported, this is None.
-
-
-Updates a method description in a discovery document.
-
-SIDE EFFECTS: Changes the parameters dictionary in the method description with
-extra parameters which are used locally.
-
-Args:
- method_desc: Dictionary with metadata describing an API method. Value comes
- from the dictionary of methods stored in the 'methods' key in the
- deserialized discovery document.
- root_desc: Dictionary; the entire original deserialized discovery document.
- schema: Object, mapping of schema names to schema descriptions.
-
-Returns:
- Tuple (path_url, http_method, method_id, accept, max_size, media_path_url)
- where:
- - path_url is a String; the relative URL for the API method. Relative to
- the API root, which is specified in the discovery document.
- - http_method is a String; the HTTP method used to call the API method
- described in the method description.
- - method_id is a String; the name of the RPC method associated with the
- API method, and is in the method description in the 'id' key.
- - accept is a list of strings representing what content types are
- accepted for media upload. Defaults to empty list if not in the
- discovery document.
- - max_size is a long representing the max size in bytes allowed for a
- media upload. Defaults to 0L if not in the discovery document.
- - media_path_url is a String; the absolute URI for media upload for the
- API method. Constructed using the API root URI and service path from
- the discovery document and the relative path for the API method. If
- media upload is not supported, this is None.
-
-
-Creates a method for attaching to a Resource.
-
-Args:
- methodName: string, name of the method to use.
- methodDesc: object, fragment of deserialized discovery document that
- describes the method.
- rootDesc: object, the entire deserialized discovery document.
- schema: object, mapping of schema names to schema descriptions.
-
-
-Creates any _next methods for attaching to a Resource.
-
-The _next methods allow for easy iteration through list() responses.
-
-Args:
- methodName: string, name of the method to use.
- pageTokenName: string, name of request page token field.
- nextPageTokenName: string, name of response page token field.
- isPageTokenParameter: Boolean, True if request page token is a query
- parameter, False if request page token is a field of the request body.
-
-
-Search field names for one like a page token.
-
-Args:
- fields: container of string, names of fields.
-
-Returns:
- First name that is either 'pageToken' or 'nextPageToken' if one exists,
- otherwise None.
-
-
-Get properties of a field in a method description.
-
-Args:
- methodDesc: object, fragment of deserialized discovery document that
- describes the method.
- schema: object, mapping of schema names to schema descriptions.
- name: string, name of top-level field in method description.
-
-Returns:
- Object representing fragment of deserialized discovery document
- corresponding to 'properties' field of object corresponding to named field
- in method description, if it exists, otherwise empty dict.
-
-
140"""Fix method names to avoid '$' characters and reserved word conflicts.
- 141
- 142 Args:
- 143 name: string, method name.
- 144
- 145 Returns:
- 146 The name with '_' appended if the name is a reserved word and '$' and '-'
- 147 replaced with '_'.
- 148 """
- 149name=name.replace("$","_").replace("-","_")
- 150ifkeyword.iskeyword(name)ornameinRESERVED_WORDS:
- 151returnname+"_"
- 152else:
- 153returnname
-
197"""Construct a Resource for interacting with an API.
- 198
- 199 Construct a Resource object for interacting with an API. The serviceName and
- 200 version are the names from the Discovery service.
- 201
- 202 Args:
- 203 serviceName: string, name of the service.
- 204 version: string, the version of the service.
- 205 http: httplib2.Http, An instance of httplib2.Http or something that acts
- 206 like it that HTTP requests will be made through.
- 207 discoveryServiceUrl: string, a URI Template that points to the location of
- 208 the discovery service. It should have two parameters {api} and
- 209 {apiVersion} that when filled in produce an absolute URI to the discovery
- 210 document for that service.
- 211 developerKey: string, key obtained from
- 212 https://code.google.com/apis/console.
- 213 model: googleapiclient.Model, converts to and from the wire format.
- 214 requestBuilder: googleapiclient.http.HttpRequest, encapsulator for an HTTP
- 215 request.
- 216 credentials: oauth2client.Credentials or
- 217 google.auth.credentials.Credentials, credentials to be used for
- 218 authentication.
- 219 cache_discovery: Boolean, whether or not to cache the discovery doc.
- 220 cache: googleapiclient.discovery_cache.base.CacheBase, an optional
- 221 cache object for the discovery documents.
- 222 client_options: Mapping object or google.api_core.client_options, client
- 223 options to set user options on the client.
- 224 (1) The API endpoint should be set through client_options. If API endpoint
- 225 is not set, `GOOGLE_API_USE_MTLS_ENDPOINT` environment variable can be used
- 226 to control which endpoint to use.
- 227 (2) client_cert_source is not supported, client cert should be provided using
- 228 client_encrypted_cert_source instead. In order to use the provided client
- 229 cert, `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be
- 230 set to `true`.
- 231 More details on the environment variables are here:
- 232 https://google.aip.dev/auth/4114
- 233 adc_cert_path: str, client certificate file path to save the application
- 234 default client certificate for mTLS. This field is required if you want to
- 235 use the default client certificate. `GOOGLE_API_USE_CLIENT_CERTIFICATE`
- 236 environment variable must be set to `true` in order to use this field,
- 237 otherwise this field doesn't nothing.
- 238 More details on the environment variables are here:
- 239 https://google.aip.dev/auth/4114
- 240 adc_key_path: str, client encrypted private key file path to save the
- 241 application default client encrypted private key for mTLS. This field is
- 242 required if you want to use the default client certificate.
- 243 `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be set to
- 244 `true` in order to use this field, otherwise this field doesn't nothing.
- 245 More details on the environment variables are here:
- 246 https://google.aip.dev/auth/4114
- 247 num_retries: Integer, number of times to retry discovery with
- 248 randomized exponential backoff in case of intermittent/connection issues.
- 249
- 250 Returns:
- 251 A Resource object with methods for interacting with the service.
- 252
- 253 Raises:
- 254 google.auth.exceptions.MutualTLSChannelError: if there are any problems
- 255 setting up mutual TLS channel.
- 256 """
- 257params={"api":serviceName,"apiVersion":version}
- 258
- 259ifhttpisNone:
- 260discovery_http=build_http()
- 261else:
- 262discovery_http=http
- 263
- 264service=None
- 265
- 266fordiscovery_urlin_discovery_service_uri_options(discoveryServiceUrl,version):
- 267requested_url=uritemplate.expand(discovery_url,params)
- 268
- 269try:
- 270content=_retrieve_discovery_doc(
- 271requested_url,
- 272discovery_http,
- 273cache_discovery,
- 274cache,
- 275developerKey,
- 276num_retries=num_retries,
- 277)
- 278service=build_from_document(
- 279content,
- 280base=discovery_url,
- 281http=http,
- 282developerKey=developerKey,
- 283model=model,
- 284requestBuilder=requestBuilder,
- 285credentials=credentials,
- 286client_options=client_options,
- 287adc_cert_path=adc_cert_path,
- 288adc_key_path=adc_key_path,
- 289)
- 290break# exit if a service was created
- 291exceptHttpErrorase:
- 292ife.resp.status==http_client.NOT_FOUND:
- 293continue
- 294else:
- 295raisee
- 296
- 297# If discovery_http was created by this function, we are done with it
- 298# and can safely close it
- 299ifhttpisNone:
- 300discovery_http.close()
- 301
- 302ifserviceisNone:
- 303raiseUnknownApiNameOrVersion("name: %s version: %s"%(serviceName,version))
- 304else:
- 305returnservice
-
309"""
- 310 Returns Discovery URIs to be used for attemnting to build the API Resource.
- 311
- 312 Args:
- 313 discoveryServiceUrl:
- 314 string, the Original Discovery Service URL preferred by the customer.
- 315 version:
- 316 string, API Version requested
- 317
- 318 Returns:
- 319 A list of URIs to be tried for the Service Discovery, in order.
- 320 """
- 321
- 322urls=[discoveryServiceUrl,V2_DISCOVERY_URI]
- 323# V1 Discovery won't work if the requested version is None
- 324ifdiscoveryServiceUrl==V1_DISCOVERY_URIandversionisNone:
- 325logger.warning(
- 326"Discovery V1 does not support empty versions. Defaulting to V2..."
- 327)
- 328urls.pop(0)
- 329returnlist(OrderedDict.fromkeys(urls))
-
335"""Retrieves the discovery_doc from cache or the internet.
- 336
- 337 Args:
- 338 url: string, the URL of the discovery document.
- 339 http: httplib2.Http, An instance of httplib2.Http or something that acts
- 340 like it through which HTTP requests will be made.
- 341 cache_discovery: Boolean, whether or not to cache the discovery doc.
- 342 cache: googleapiclient.discovery_cache.base.Cache, an optional cache
- 343 object for the discovery documents.
- 344 developerKey: string, Key for controlling API usage, generated
- 345 from the API Console.
- 346 num_retries: Integer, number of times to retry discovery with
- 347 randomized exponential backoff in case of intermittent/connection issues.
- 348
- 349 Returns:
- 350 A unicode string representation of the discovery document.
- 351 """
- 352ifcache_discovery:
- 353from.importdiscovery_cache
- 354
- 355ifcacheisNone:
- 356cache=discovery_cache.autodetect()
- 357ifcache:
- 358content=cache.get(url)
- 359ifcontent:
- 360returncontent
- 361
- 362actual_url=url
- 363# REMOTE_ADDR is defined by the CGI spec [RFC3875] as the environment
- 364# variable that contains the network address of the client sending the
- 365# request. If it exists then add that to the request for the discovery
- 366# document to avoid exceeding the quota on discovery requests.
- 367if"REMOTE_ADDR"inos.environ:
- 368actual_url=_add_query_parameter(url,"userIp",os.environ["REMOTE_ADDR"])
- 369ifdeveloperKey:
- 370actual_url=_add_query_parameter(url,"key",developerKey)
- 371logger.debug("URL being requested: GET %s",actual_url)
- 372
- 373# Execute this request with retries build into HttpRequest
- 374# Note that it will already raise an error if we don't get a 2xx response
- 375req=HttpRequest(http,HttpRequest.null_postproc,actual_url)
- 376resp,content=req.execute(num_retries=num_retries)
- 377
- 378try:
- 379content=content.decode("utf-8")
- 380exceptAttributeError:
- 381pass
- 382
- 383try:
- 384service=json.loads(content)
- 385exceptValueErrorase:
- 386logger.error("Failed to parse as JSON: "+content)
- 387raiseInvalidJsonError()
- 388ifcache_discoveryandcache:
- 389cache.set(url,content)
- 390returncontent
-
407"""Create a Resource for interacting with an API.
- 408
- 409 Same as `build()`, but constructs the Resource object from a discovery
- 410 document that is it given, as opposed to retrieving one over HTTP.
- 411
- 412 Args:
- 413 service: string or object, the JSON discovery document describing the API.
- 414 The value passed in may either be the JSON string or the deserialized
- 415 JSON.
- 416 base: string, base URI for all HTTP requests, usually the discovery URI.
- 417 This parameter is no longer used as rootUrl and servicePath are included
- 418 within the discovery document. (deprecated)
- 419 future: string, discovery document with future capabilities (deprecated).
- 420 http: httplib2.Http, An instance of httplib2.Http or something that acts
- 421 like it that HTTP requests will be made through.
- 422 developerKey: string, Key for controlling API usage, generated
- 423 from the API Console.
- 424 model: Model class instance that serializes and de-serializes requests and
- 425 responses.
- 426 requestBuilder: Takes an http request and packages it up to be executed.
- 427 credentials: oauth2client.Credentials or
- 428 google.auth.credentials.Credentials, credentials to be used for
- 429 authentication.
- 430 client_options: Mapping object or google.api_core.client_options, client
- 431 options to set user options on the client.
- 432 (1) The API endpoint should be set through client_options. If API endpoint
- 433 is not set, `GOOGLE_API_USE_MTLS_ENDPOINT` environment variable can be used
- 434 to control which endpoint to use.
- 435 (2) client_cert_source is not supported, client cert should be provided using
- 436 client_encrypted_cert_source instead. In order to use the provided client
- 437 cert, `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be
- 438 set to `true`.
- 439 More details on the environment variables are here:
- 440 https://google.aip.dev/auth/4114
- 441 adc_cert_path: str, client certificate file path to save the application
- 442 default client certificate for mTLS. This field is required if you want to
- 443 use the default client certificate. `GOOGLE_API_USE_CLIENT_CERTIFICATE`
- 444 environment variable must be set to `true` in order to use this field,
- 445 otherwise this field doesn't nothing.
- 446 More details on the environment variables are here:
- 447 https://google.aip.dev/auth/4114
- 448 adc_key_path: str, client encrypted private key file path to save the
- 449 application default client encrypted private key for mTLS. This field is
- 450 required if you want to use the default client certificate.
- 451 `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be set to
- 452 `true` in order to use this field, otherwise this field doesn't nothing.
- 453 More details on the environment variables are here:
- 454 https://google.aip.dev/auth/4114
- 455
- 456 Returns:
- 457 A Resource object with methods for interacting with the service.
- 458
- 459 Raises:
- 460 google.auth.exceptions.MutualTLSChannelError: if there are any problems
- 461 setting up mutual TLS channel.
- 462 """
- 463
- 464ifclient_optionsisNone:
- 465client_options=google.api_core.client_options.ClientOptions()
- 466ifisinstance(client_options,six.moves.collections_abc.Mapping):
- 467client_options=google.api_core.client_options.from_dict(client_options)
- 468
- 469ifhttpisnotNone:
- 470# if http is passed, the user cannot provide credentials
- 471banned_options=[
- 472(credentials,"credentials"),
- 473(client_options.credentials_file,"client_options.credentials_file"),
- 474]
- 475foroption,nameinbanned_options:
- 476ifoptionisnotNone:
- 477raiseValueError("Arguments http and {} are mutually exclusive".format(name))
- 478
- 479ifisinstance(service,six.string_types):
- 480service=json.loads(service)
- 481elifisinstance(service,six.binary_type):
- 482service=json.loads(service.decode("utf-8"))
- 483
- 484if"rootUrl"notinserviceandisinstance(http,(HttpMock,HttpMockSequence)):
- 485logger.error(
- 486"You are using HttpMock or HttpMockSequence without"
- 487+"having the service discovery doc in cache. Try calling "
- 488+"build() without mocking once first to populate the "
- 489+"cache."
- 490)
- 491raiseInvalidJsonError()
- 492
- 493# If an API Endpoint is provided on client options, use that as the base URL
- 494base=urljoin(service["rootUrl"],service["servicePath"])
- 495ifclient_options.api_endpoint:
- 496base=client_options.api_endpoint
- 497
- 498schema=Schemas(service)
- 499
- 500# If the http client is not specified, then we must construct an http client
- 501# to make requests. If the service has scopes, then we also need to setup
- 502# authentication.
- 503ifhttpisNone:
- 504# Does the service require scopes?
- 505scopes=list(
- 506service.get("auth",{}).get("oauth2",{}).get("scopes",{}).keys()
- 507)
- 508
- 509# If so, then the we need to setup authentication if no developerKey is
- 510# specified.
- 511ifscopesandnotdeveloperKey:
- 512# Make sure the user didn't pass multiple credentials
- 513ifclient_options.credentials_fileandcredentials:
- 514raisegoogle.api_core.exceptions.DuplicateCredentialArgs(
- 515"client_options.credentials_file and credentials are mutually exclusive."
- 516)
- 517# Check for credentials file via client options
- 518ifclient_options.credentials_file:
- 519credentials=_auth.credentials_from_file(
- 520client_options.credentials_file,
- 521scopes=client_options.scopes,
- 522quota_project_id=client_options.quota_project_id,
- 523)
- 524# If the user didn't pass in credentials, attempt to acquire application
- 525# default credentials.
- 526ifcredentialsisNone:
- 527credentials=_auth.default_credentials(
- 528scopes=client_options.scopes,
- 529quota_project_id=client_options.quota_project_id,
- 530)
- 531
- 532# The credentials need to be scoped.
- 533# If the user provided scopes via client_options don't override them
- 534ifnotclient_options.scopes:
- 535credentials=_auth.with_scopes(credentials,scopes)
- 536
- 537# If credentials are provided, create an authorized http instance;
- 538# otherwise, skip authentication.
- 539ifcredentials:
- 540http=_auth.authorized_http(credentials)
- 541
- 542# If the service doesn't require scopes then there is no need for
- 543# authentication.
- 544else:
- 545http=build_http()
- 546
- 547# Obtain client cert and create mTLS http channel if cert exists.
- 548client_cert_to_use=None
- 549use_client_cert=os.getenv(GOOGLE_API_USE_CLIENT_CERTIFICATE,"false")
- 550ifnotuse_client_certin("true","false"):
- 551raiseMutualTLSChannelError(
- 552"Unsupported GOOGLE_API_USE_CLIENT_CERTIFICATE value. Accepted values: true, false"
- 553)
- 554ifclient_optionsandclient_options.client_cert_source:
- 555raiseMutualTLSChannelError(
- 556"ClientOptions.client_cert_source is not supported, please use ClientOptions.client_encrypted_cert_source."
- 557)
- 558ifuse_client_cert=="true":
- 559if(
- 560client_options
- 561andhasattr(client_options,"client_encrypted_cert_source")
- 562andclient_options.client_encrypted_cert_source
- 563):
- 564client_cert_to_use=client_options.client_encrypted_cert_source
- 565elif(
- 566adc_cert_pathandadc_key_pathandmtls.has_default_client_cert_source()
- 567):
- 568client_cert_to_use=mtls.default_client_encrypted_cert_source(
- 569adc_cert_path,adc_key_path
- 570)
- 571ifclient_cert_to_use:
- 572cert_path,key_path,passphrase=client_cert_to_use()
- 573
- 574# The http object we built could be google_auth_httplib2.AuthorizedHttp
- 575# or httplib2.Http. In the first case we need to extract the wrapped
- 576# httplib2.Http object from google_auth_httplib2.AuthorizedHttp.
- 577http_channel=(
- 578http.http
- 579ifgoogle_auth_httplib2
- 580andisinstance(http,google_auth_httplib2.AuthorizedHttp)
- 581elsehttp
- 582)
- 583http_channel.add_certificate(key_path,cert_path,"",passphrase)
- 584
- 585# If user doesn't provide api endpoint via client options, decide which
- 586# api endpoint to use.
- 587if"mtlsRootUrl"inserviceand(
- 588notclient_optionsornotclient_options.api_endpoint
- 589):
- 590mtls_endpoint=urljoin(service["mtlsRootUrl"],service["servicePath"])
- 591use_mtls_endpoint=os.getenv(GOOGLE_API_USE_MTLS_ENDPOINT,"auto")
- 592
- 593ifnotuse_mtls_endpointin("never","auto","always"):
- 594raiseMutualTLSChannelError(
- 595"Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always"
- 596)
- 597
- 598# Switch to mTLS endpoint, if environment variable is "always", or
- 599# environment varibable is "auto" and client cert exists.
- 600ifuse_mtls_endpoint=="always"or(
- 601use_mtls_endpoint=="auto"andclient_cert_to_use
- 602):
- 603base=mtls_endpoint
- 604
- 605ifmodelisNone:
- 606features=service.get("features",[])
- 607model=JsonModel("dataWrapper"infeatures)
- 608
- 609returnResource(
- 610http=http,
- 611baseUrl=base,
- 612model=model,
- 613developerKey=developerKey,
- 614requestBuilder=requestBuilder,
- 615resourceDesc=service,
- 616rootDesc=service,
- 617schema=schema,
- 618)
-
622"""Convert value to a string based on JSON Schema type.
- 623
- 624 See http://tools.ietf.org/html/draft-zyp-json-schema-03 for more details on
- 625 JSON Schema.
- 626
- 627 Args:
- 628 value: any, the value to convert
- 629 schema_type: string, the type that value should be interpreted as
- 630
- 631 Returns:
- 632 A string representation of 'value' based on the schema_type.
- 633 """
- 634ifschema_type=="string":
- 635iftype(value)==type("")ortype(value)==type(u""):
- 636returnvalue
- 637else:
- 638returnstr(value)
- 639elifschema_type=="integer":
- 640returnstr(int(value))
- 641elifschema_type=="number":
- 642returnstr(float(value))
- 643elifschema_type=="boolean":
- 644returnstr(bool(value)).lower()
- 645else:
- 646iftype(value)==type("")ortype(value)==type(u""):
- 647returnvalue
- 648else:
- 649returnstr(value)
-
653"""Convert a string media size, such as 10GB or 3TB into an integer.
- 654
- 655 Args:
- 656 maxSize: string, size as a string, such as 2MB or 7GB.
- 657
- 658 Returns:
- 659 The size as an integer value.
- 660 """
- 661iflen(maxSize)<2:
- 662return0
- 663units=maxSize[-2:].upper()
- 664bit_shift=_MEDIA_SIZE_BIT_SHIFTS.get(units)
- 665ifbit_shiftisnotNone:
- 666returnint(maxSize[:-2])<<bit_shift
- 667else:
- 668returnint(maxSize)
-
672"""Creates an absolute media path URL.
- 673
- 674 Constructed using the API root URI and service path from the discovery
- 675 document and the relative path for the API method.
- 676
- 677 Args:
- 678 root_desc: Dictionary; the entire original deserialized discovery document.
- 679 path_url: String; the relative URL for the API method. Relative to the API
- 680 root, which is specified in the discovery document.
- 681
- 682 Returns:
- 683 String; the absolute URI for media upload for the API method.
- 684 """
- 685return"%(root)supload/%(service_path)s%(path)s"%{
- 686"root":root_desc["rootUrl"],
- 687"service_path":root_desc["servicePath"],
- 688"path":path_url,
- 689}
-
693"""Updates parameters of an API method with values specific to this library.
- 694
- 695 Specifically, adds whatever global parameters are specified by the API to the
- 696 parameters for the individual method. Also adds parameters which don't
- 697 appear in the discovery document, but are available to all discovery based
- 698 APIs (these are listed in STACK_QUERY_PARAMETERS).
- 699
- 700 SIDE EFFECTS: This updates the parameters dictionary object in the method
- 701 description.
- 702
- 703 Args:
- 704 method_desc: Dictionary with metadata describing an API method. Value comes
- 705 from the dictionary of methods stored in the 'methods' key in the
- 706 deserialized discovery document.
- 707 root_desc: Dictionary; the entire original deserialized discovery document.
- 708 http_method: String; the HTTP method used to call the API method described
- 709 in method_desc.
- 710 schema: Object, mapping of schema names to schema descriptions.
- 711
- 712 Returns:
- 713 The updated Dictionary stored in the 'parameters' key of the method
- 714 description dictionary.
- 715 """
- 716parameters=method_desc.setdefault("parameters",{})
- 717
- 718# Add in the parameters common to all methods.
- 719forname,descriptioninsix.iteritems(root_desc.get("parameters",{})):
- 720parameters[name]=description
- 721
- 722# Add in undocumented query parameters.
- 723fornameinSTACK_QUERY_PARAMETERS:
- 724parameters[name]=STACK_QUERY_PARAMETER_DEFAULT_VALUE.copy()
- 725
- 726# Add 'body' (our own reserved word) to parameters if the method supports
- 727# a request payload.
- 728ifhttp_methodinHTTP_PAYLOAD_METHODSand"request"inmethod_desc:
- 729body=BODY_PARAMETER_DEFAULT_VALUE.copy()
- 730body.update(method_desc["request"])
- 731parameters["body"]=body
- 732
- 733returnparameters
-
737"""Adds 'media_body' and 'media_mime_type' parameters if supported by method.
- 738
- 739 SIDE EFFECTS: If there is a 'mediaUpload' in the method description, adds
- 740 'media_upload' key to parameters.
- 741
- 742 Args:
- 743 method_desc: Dictionary with metadata describing an API method. Value comes
- 744 from the dictionary of methods stored in the 'methods' key in the
- 745 deserialized discovery document.
- 746 root_desc: Dictionary; the entire original deserialized discovery document.
- 747 path_url: String; the relative URL for the API method. Relative to the API
- 748 root, which is specified in the discovery document.
- 749 parameters: A dictionary describing method parameters for method described
- 750 in method_desc.
- 751
- 752 Returns:
- 753 Triple (accept, max_size, media_path_url) where:
- 754 - accept is a list of strings representing what content types are
- 755 accepted for media upload. Defaults to empty list if not in the
- 756 discovery document.
- 757 - max_size is a long representing the max size in bytes allowed for a
- 758 media upload. Defaults to 0L if not in the discovery document.
- 759 - media_path_url is a String; the absolute URI for media upload for the
- 760 API method. Constructed using the API root URI and service path from
- 761 the discovery document and the relative path for the API method. If
- 762 media upload is not supported, this is None.
- 763 """
- 764media_upload=method_desc.get("mediaUpload",{})
- 765accept=media_upload.get("accept",[])
- 766max_size=_media_size_to_long(media_upload.get("maxSize",""))
- 767media_path_url=None
- 768
- 769ifmedia_upload:
- 770media_path_url=_media_path_url_from_info(root_desc,path_url)
- 771parameters["media_body"]=MEDIA_BODY_PARAMETER_DEFAULT_VALUE.copy()
- 772parameters["media_mime_type"]=MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE.copy()
- 773
- 774returnaccept,max_size,media_path_url
-
778"""Updates a method description in a discovery document.
- 779
- 780 SIDE EFFECTS: Changes the parameters dictionary in the method description with
- 781 extra parameters which are used locally.
- 782
- 783 Args:
- 784 method_desc: Dictionary with metadata describing an API method. Value comes
- 785 from the dictionary of methods stored in the 'methods' key in the
- 786 deserialized discovery document.
- 787 root_desc: Dictionary; the entire original deserialized discovery document.
- 788 schema: Object, mapping of schema names to schema descriptions.
- 789
- 790 Returns:
- 791 Tuple (path_url, http_method, method_id, accept, max_size, media_path_url)
- 792 where:
- 793 - path_url is a String; the relative URL for the API method. Relative to
- 794 the API root, which is specified in the discovery document.
- 795 - http_method is a String; the HTTP method used to call the API method
- 796 described in the method description.
- 797 - method_id is a String; the name of the RPC method associated with the
- 798 API method, and is in the method description in the 'id' key.
- 799 - accept is a list of strings representing what content types are
- 800 accepted for media upload. Defaults to empty list if not in the
- 801 discovery document.
- 802 - max_size is a long representing the max size in bytes allowed for a
- 803 media upload. Defaults to 0L if not in the discovery document.
- 804 - media_path_url is a String; the absolute URI for media upload for the
- 805 API method. Constructed using the API root URI and service path from
- 806 the discovery document and the relative path for the API method. If
- 807 media upload is not supported, this is None.
- 808 """
- 809path_url=method_desc["path"]
- 810http_method=method_desc["httpMethod"]
- 811method_id=method_desc["id"]
- 812
- 813parameters=_fix_up_parameters(method_desc,root_desc,http_method,schema)
- 814# Order is important. `_fix_up_media_upload` needs `method_desc` to have a
- 815# 'parameters' key and needs to know if there is a 'body' parameter because it
- 816# also sets a 'media_body' parameter.
- 817accept,max_size,media_path_url=_fix_up_media_upload(
- 818method_desc,root_desc,path_url,parameters
- 819)
- 820
- 821returnpath_url,http_method,method_id,accept,max_size,media_path_url
-
825"""Custom urljoin replacement supporting : before / in url."""
- 826# In general, it's unsafe to simply join base and url. However, for
- 827# the case of discovery documents, we know:
- 828# * base will never contain params, query, or fragment
- 829# * url will never contain a scheme or net_loc.
- 830# In general, this means we can safely join on /; we just need to
- 831# ensure we end up with precisely one / joining base and url. The
- 832# exception here is the case of media uploads, where url will be an
- 833# absolute url.
- 834ifurl.startswith("http://")orurl.startswith("https://"):
- 835returnurljoin(base,url)
- 836new_base=baseifbase.endswith("/")elsebase+"/"
- 837new_url=url[1:]ifurl.startswith("/")elseurl
- 838returnnew_base+new_url
-
839
-
840
- 841# TODO(dhermes): Convert this class to ResourceMethod and make it callable
- 842-classResourceMethodParameters(object):
-
843"""Represents the parameters associated with a method.
- 844
- 845 Attributes:
- 846 argmap: Map from method parameter name (string) to query parameter name
- 847 (string).
- 848 required_params: List of required parameters (represented by parameter
- 849 name as string).
- 850 repeated_params: List of repeated parameters (represented by parameter
- 851 name as string).
- 852 pattern_params: Map from method parameter name (string) to regular
- 853 expression (as a string). If the pattern is set for a parameter, the
- 854 value for that parameter must match the regular expression.
- 855 query_params: List of parameters (represented by parameter name as string)
- 856 that will be used in the query string.
- 857 path_params: Set of parameters (represented by parameter name as string)
- 858 that will be used in the base URL path.
- 859 param_types: Map from method parameter name (string) to parameter type. Type
- 860 can be any valid JSON schema type; valid values are 'any', 'array',
- 861 'boolean', 'integer', 'number', 'object', or 'string'. Reference:
- 862 http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.1
- 863 enum_params: Map from method parameter name (string) to list of strings,
- 864 where each list of strings is the list of acceptable enum values.
- 865 """
- 866
-
868"""Constructor for ResourceMethodParameters.
- 869
- 870 Sets default values and defers to set_parameters to populate.
- 871
- 872 Args:
- 873 method_desc: Dictionary with metadata describing an API method. Value
- 874 comes from the dictionary of methods stored in the 'methods' key in
- 875 the deserialized discovery document.
- 876 """
- 877self.argmap={}
- 878self.required_params=[]
- 879self.repeated_params=[]
- 880self.pattern_params={}
- 881self.query_params=[]
- 882# TODO(dhermes): Change path_params to a list if the extra URITEMPLATE
- 883# parsing is gotten rid of.
- 884self.path_params=set()
- 885self.param_types={}
- 886self.enum_params={}
- 887
- 888self.set_parameters(method_desc)
-
891"""Populates maps and lists based on method description.
- 892
- 893 Iterates through each parameter for the method and parses the values from
- 894 the parameter dictionary.
- 895
- 896 Args:
- 897 method_desc: Dictionary with metadata describing an API method. Value
- 898 comes from the dictionary of methods stored in the 'methods' key in
- 899 the deserialized discovery document.
- 900 """
- 901forarg,descinsix.iteritems(method_desc.get("parameters",{})):
- 902param=key2param(arg)
- 903self.argmap[param]=arg
- 904
- 905ifdesc.get("pattern"):
- 906self.pattern_params[param]=desc["pattern"]
- 907ifdesc.get("enum"):
- 908self.enum_params[param]=desc["enum"]
- 909ifdesc.get("required"):
- 910self.required_params.append(param)
- 911ifdesc.get("repeated"):
- 912self.repeated_params.append(param)
- 913ifdesc.get("location")=="query":
- 914self.query_params.append(param)
- 915ifdesc.get("location")=="path":
- 916self.path_params.add(param)
- 917self.param_types[param]=desc.get("type","string")
- 918
- 919# TODO(dhermes): Determine if this is still necessary. Discovery based APIs
- 920# should have all path parameters already marked with
- 921# 'location: path'.
- 922formatchinURITEMPLATE.finditer(method_desc["path"]):
- 923fornamematchinVARNAME.finditer(match.group(0)):
- 924name=key2param(namematch.group(0))
- 925self.path_params.add(name)
- 926ifnameinself.query_params:
- 927self.query_params.remove(name)
-
931"""Creates a method for attaching to a Resource.
- 932
- 933 Args:
- 934 methodName: string, name of the method to use.
- 935 methodDesc: object, fragment of deserialized discovery document that
- 936 describes the method.
- 937 rootDesc: object, the entire deserialized discovery document.
- 938 schema: object, mapping of schema names to schema descriptions.
- 939 """
- 940methodName=fix_method_name(methodName)
- 941(
- 942pathUrl,
- 943httpMethod,
- 944methodId,
- 945accept,
- 946maxSize,
- 947mediaPathUrl,
- 948)=_fix_up_method_description(methodDesc,rootDesc,schema)
- 949
- 950parameters=ResourceMethodParameters(methodDesc)
- 951
- 952defmethod(self,**kwargs):
- 953# Don't bother with doc string, it will be over-written by createMethod.
- 954
- 955fornameinsix.iterkeys(kwargs):
- 956ifnamenotinparameters.argmap:
- 957raiseTypeError('Got an unexpected keyword argument "%s"'%name)
- 958
- 959# Remove args that have a value of None.
- 960keys=list(kwargs.keys())
- 961fornameinkeys:
- 962ifkwargs[name]isNone:
- 963delkwargs[name]
- 964
- 965fornameinparameters.required_params:
- 966ifnamenotinkwargs:
- 967# temporary workaround for non-paging methods incorrectly requiring
- 968# page token parameter (cf. drive.changes.watch vs. drive.changes.list)
- 969ifnamenotin_PAGE_TOKEN_NAMESor_findPageTokenName(
- 970_methodProperties(methodDesc,schema,"response")
- 971):
- 972raiseTypeError('Missing required parameter "%s"'%name)
- 973
- 974forname,regexinsix.iteritems(parameters.pattern_params):
- 975ifnameinkwargs:
- 976ifisinstance(kwargs[name],six.string_types):
- 977pvalues=[kwargs[name]]
- 978else:
- 979pvalues=kwargs[name]
- 980forpvalueinpvalues:
- 981ifre.match(regex,pvalue)isNone:
- 982raiseTypeError(
- 983'Parameter "%s" value "%s" does not match the pattern "%s"'
- 984%(name,pvalue,regex)
- 985)
- 986
- 987forname,enumsinsix.iteritems(parameters.enum_params):
- 988ifnameinkwargs:
- 989# We need to handle the case of a repeated enum
- 990# name differently, since we want to handle both
- 991# arg='value' and arg=['value1', 'value2']
- 992ifnameinparameters.repeated_paramsandnotisinstance(
- 993kwargs[name],six.string_types
- 994):
- 995values=kwargs[name]
- 996else:
- 997values=[kwargs[name]]
- 998forvalueinvalues:
- 999ifvaluenotinenums:
-1000raiseTypeError(
-1001'Parameter "%s" value "%s" is not an allowed value in "%s"'
-1002%(name,value,str(enums))
-1003)
-1004
-1005actual_query_params={}
-1006actual_path_params={}
-1007forkey,valueinsix.iteritems(kwargs):
-1008to_type=parameters.param_types.get(key,"string")
-1009# For repeated parameters we cast each member of the list.
-1010ifkeyinparameters.repeated_paramsandtype(value)==type([]):
-1011cast_value=[_cast(x,to_type)forxinvalue]
-1012else:
-1013cast_value=_cast(value,to_type)
-1014ifkeyinparameters.query_params:
-1015actual_query_params[parameters.argmap[key]]=cast_value
-1016ifkeyinparameters.path_params:
-1017actual_path_params[parameters.argmap[key]]=cast_value
-1018body_value=kwargs.get("body",None)
-1019media_filename=kwargs.get("media_body",None)
-1020media_mime_type=kwargs.get("media_mime_type",None)
-1021
-1022ifself._developerKey:
-1023actual_query_params["key"]=self._developerKey
-1024
-1025model=self._model
-1026ifmethodName.endswith("_media"):
-1027model=MediaModel()
-1028elif"response"notinmethodDesc:
-1029model=RawModel()
-1030
-1031headers={}
-1032headers,params,query,body=model.request(
-1033headers,actual_path_params,actual_query_params,body_value
-1034)
-1035
-1036expanded_url=uritemplate.expand(pathUrl,params)
-1037url=_urljoin(self._baseUrl,expanded_url+query)
-1038
-1039resumable=None
-1040multipart_boundary=""
-1041
-1042ifmedia_filename:
-1043# Ensure we end up with a valid MediaUpload object.
-1044ifisinstance(media_filename,six.string_types):
-1045ifmedia_mime_typeisNone:
-1046logger.warning(
-1047"media_mime_type argument not specified: trying to auto-detect for %s",
-1048media_filename,
-1049)
-1050media_mime_type,_=mimetypes.guess_type(media_filename)
-1051ifmedia_mime_typeisNone:
-1052raiseUnknownFileType(media_filename)
-1053ifnotmimeparse.best_match([media_mime_type],",".join(accept)):
-1054raiseUnacceptableMimeTypeError(media_mime_type)
-1055media_upload=MediaFileUpload(media_filename,mimetype=media_mime_type)
-1056elifisinstance(media_filename,MediaUpload):
-1057media_upload=media_filename
-1058else:
-1059raiseTypeError("media_filename must be str or MediaUpload.")
-1060
-1061# Check the maxSize
-1062ifmedia_upload.size()isnotNoneandmedia_upload.size()>maxSize>0:
-1063raiseMediaUploadSizeError("Media larger than: %s"%maxSize)
-1064
-1065# Use the media path uri for media uploads
-1066expanded_url=uritemplate.expand(mediaPathUrl,params)
-1067url=_urljoin(self._baseUrl,expanded_url+query)
-1068ifmedia_upload.resumable():
-1069url=_add_query_parameter(url,"uploadType","resumable")
-1070
-1071ifmedia_upload.resumable():
-1072# This is all we need to do for resumable, if the body exists it gets
-1073# sent in the first request, otherwise an empty body is sent.
-1074resumable=media_upload
-1075else:
-1076# A non-resumable upload
-1077ifbodyisNone:
-1078# This is a simple media upload
-1079headers["content-type"]=media_upload.mimetype()
-1080body=media_upload.getbytes(0,media_upload.size())
-1081url=_add_query_parameter(url,"uploadType","media")
-1082else:
-1083# This is a multipart/related upload.
-1084msgRoot=MIMEMultipart("related")
-1085# msgRoot should not write out it's own headers
-1086setattr(msgRoot,"_write_headers",lambdaself:None)
-1087
-1088# attach the body as one part
-1089msg=MIMENonMultipart(*headers["content-type"].split("/"))
-1090msg.set_payload(body)
-1091msgRoot.attach(msg)
-1092
-1093# attach the media as the second part
-1094msg=MIMENonMultipart(*media_upload.mimetype().split("/"))
-1095msg["Content-Transfer-Encoding"]="binary"
-1096
-1097payload=media_upload.getbytes(0,media_upload.size())
-1098msg.set_payload(payload)
-1099msgRoot.attach(msg)
-1100# encode the body: note that we can't use `as_string`, because
-1101# it plays games with `From ` lines.
-1102fp=BytesIO()
-1103g=_BytesGenerator(fp,mangle_from_=False)
-1104g.flatten(msgRoot,unixfrom=False)
-1105body=fp.getvalue()
-1106
-1107multipart_boundary=msgRoot.get_boundary()
-1108headers["content-type"]=(
-1109"multipart/related; "'boundary="%s"'
-1110)%multipart_boundary
-1111url=_add_query_parameter(url,"uploadType","multipart")
-1112
-1113logger.debug("URL being requested: %s %s"%(httpMethod,url))
-1114returnself._requestBuilder(
-1115self._http,
-1116model.response,
-1117url,
-1118method=httpMethod,
-1119body=body,
-1120headers=headers,
-1121methodId=methodId,
-1122resumable=resumable,
-1123)
-
1124
-1125docs=[methodDesc.get("description",DEFAULT_METHOD_DOC),"\n\n"]
-1126iflen(parameters.argmap)>0:
-1127docs.append("Args:\n")
-1128
-1129# Skip undocumented params and params common to all methods.
-1130skip_parameters=list(rootDesc.get("parameters",{}).keys())
-1131skip_parameters.extend(STACK_QUERY_PARAMETERS)
-1132
-1133all_args=list(parameters.argmap.keys())
-1134args_ordered=[key2param(s)forsinmethodDesc.get("parameterOrder",[])]
-1135
-1136# Move body to the front of the line.
-1137if"body"inall_args:
-1138args_ordered.append("body")
-1139
-1140fornameinall_args:
-1141ifnamenotinargs_ordered:
-1142args_ordered.append(name)
-1143
-1144forarginargs_ordered:
-1145ifarginskip_parameters:
-1146continue
-1147
-1148repeated=""
-1149ifarginparameters.repeated_params:
-1150repeated=" (repeated)"
-1151required=""
-1152ifarginparameters.required_params:
-1153required=" (required)"
-1154paramdesc=methodDesc["parameters"][parameters.argmap[arg]]
-1155paramdoc=paramdesc.get("description","A parameter")
-1156if"$ref"inparamdesc:
-1157docs.append(
-1158(" %s: object, %s%s%s\n The object takes the"" form of:\n\n%s\n\n")
-1159%(
-1160arg,
-1161paramdoc,
-1162required,
-1163repeated,
-1164schema.prettyPrintByName(paramdesc["$ref"]),
-1165)
-1166)
-1167else:
-1168paramtype=paramdesc.get("type","string")
-1169docs.append(
-1170" %s: %s, %s%s%s\n"%(arg,paramtype,paramdoc,required,repeated)
-1171)
-1172enum=paramdesc.get("enum",[])
-1173enumDesc=paramdesc.get("enumDescriptions",[])
-1174ifenumandenumDesc:
-1175docs.append(" Allowed values\n")
-1176for(name,desc)inzip(enum,enumDesc):
-1177docs.append(" %s - %s\n"%(name,desc))
-1178if"response"inmethodDesc:
-1179ifmethodName.endswith("_media"):
-1180docs.append("\nReturns:\n The media object as a string.\n\n ")
-1181else:
-1182docs.append("\nReturns:\n An object of the form:\n\n ")
-1183docs.append(schema.prettyPrintSchema(methodDesc["response"]))
-1184
-1185setattr(method,"__doc__","".join(docs))
-1186return(methodName,method)
-1187
-
1195"""Creates any _next methods for attaching to a Resource.
-1196
-1197 The _next methods allow for easy iteration through list() responses.
-1198
-1199 Args:
-1200 methodName: string, name of the method to use.
-1201 pageTokenName: string, name of request page token field.
-1202 nextPageTokenName: string, name of response page token field.
-1203 isPageTokenParameter: Boolean, True if request page token is a query
-1204 parameter, False if request page token is a field of the request body.
-1205 """
-1206methodName=fix_method_name(methodName)
-1207
-1208defmethodNext(self,previous_request,previous_response):
-1209"""Retrieves the next page of results.
-1210
-1211Args:
-1212 previous_request: The request for the previous page. (required)
-1213 previous_response: The response from the request for the previous page. (required)
-1214
-1215Returns:
-1216 A request object that you can call 'execute()' on to request the next
-1217 page. Returns None if there are no more items in the collection.
-1218 """
-1219# Retrieve nextPageToken from previous_response
-1220# Use as pageToken in previous_request to create new request.
-1221
-1222nextPageToken=previous_response.get(nextPageTokenName,None)
-1223ifnotnextPageToken:
-1224returnNone
-1225
-1226request=copy.copy(previous_request)
-1227
-1228ifisPageTokenParameter:
-1229# Replace pageToken value in URI
-1230request.uri=_add_query_parameter(
-1231request.uri,pageTokenName,nextPageToken
-1232)
-1233logger.debug("Next page request URL: %s %s"%(methodName,request.uri))
-1234else:
-1235# Replace pageToken value in request body
-1236model=self._model
-1237body=model.deserialize(request.body)
-1238body[pageTokenName]=nextPageToken
-1239request.body=model.serialize(body)
-1240logger.debug("Next page request body: %s %s"%(methodName,body))
-1241
-1242returnrequest
-
1261"""Build a Resource from the API description.
-1262
-1263 Args:
-1264 http: httplib2.Http, Object to make http requests with.
-1265 baseUrl: string, base URL for the API. All requests are relative to this
-1266 URI.
-1267 model: googleapiclient.Model, converts to and from the wire format.
-1268 requestBuilder: class or callable that instantiates an
-1269 googleapiclient.HttpRequest object.
-1270 developerKey: string, key obtained from
-1271 https://code.google.com/apis/console
-1272 resourceDesc: object, section of deserialized discovery document that
-1273 describes a resource. Note that the top level discovery document
-1274 is considered a resource.
-1275 rootDesc: object, the entire deserialized discovery document.
-1276 schema: object, mapping of schema names to schema descriptions.
-1277 """
-1278self._dynamic_attrs=[]
-1279
-1280self._http=http
-1281self._baseUrl=baseUrl
-1282self._model=model
-1283self._developerKey=developerKey
-1284self._requestBuilder=requestBuilder
-1285self._resourceDesc=resourceDesc
-1286self._rootDesc=rootDesc
-1287self._schema=schema
-1288
-1289self._set_service_methods()
-
1292"""Sets an instance attribute and tracks it in a list of dynamic attributes.
-1293
-1294 Args:
-1295 attr_name: string; The name of the attribute to be set
-1296 value: The value being set on the object and tracked in the dynamic cache.
-1297 """
-1298self._dynamic_attrs.append(attr_name)
-1299self.__dict__[attr_name]=value
-
1302"""Trim the state down to something that can be pickled.
-1303
-1304 Uses the fact that the instance variable _dynamic_attrs holds attrs that
-1305 will be wiped and restored on pickle serialization.
-1306 """
-1307state_dict=copy.copy(self.__dict__)
-1308fordynamic_attrinself._dynamic_attrs:
-1309delstate_dict[dynamic_attr]
-1310delstate_dict["_dynamic_attrs"]
-1311returnstate_dict
-
1314"""Reconstitute the state of the object from being pickled.
-1315
-1316 Uses the fact that the instance variable _dynamic_attrs holds attrs that
-1317 will be wiped and restored on pickle serialization.
-1318 """
-1319self.__dict__.update(state)
-1320self._dynamic_attrs=[]
-1321self._set_service_methods()
-
1331"""Close httplib2 connections."""
-1332# httplib2 leaves sockets open by default.
-1333# Cleanup using the `close` method.
-1334# https://github.com/httplib2/httplib2/issues/148
-1335self._http.http.close()
-
1343# If this is the root Resource, add a new_batch_http_request() method.
-1344ifresourceDesc==rootDesc:
-1345batch_uri="%s%s"%(
-1346rootDesc["rootUrl"],
-1347rootDesc.get("batchPath","batch"),
-1348)
-1349
-1350defnew_batch_http_request(callback=None):
-1351"""Create a BatchHttpRequest object based on the discovery document.
-1352
-1353 Args:
-1354 callback: callable, A callback to be called for each response, of the
-1355 form callback(id, response, exception). The first parameter is the
-1356 request id, and the second is the deserialized response object. The
-1357 third is an apiclient.errors.HttpError exception object if an HTTP
-1358 error occurred while processing the request, or None if no error
-1359 occurred.
-1360
-1361 Returns:
-1362 A BatchHttpRequest object based on the discovery document.
-1363 """
-1364returnBatchHttpRequest(callback=callback,batch_uri=batch_uri)
-
1365
-1366self._set_dynamic_attr("new_batch_http_request",new_batch_http_request)
-1367
-1368# Add basic methods to Resource
-1369if"methods"inresourceDesc:
-1370formethodName,methodDescinsix.iteritems(resourceDesc["methods"]):
-1371fixedMethodName,method=createMethod(
-1372methodName,methodDesc,rootDesc,schema
-1373)
-1374self._set_dynamic_attr(
-1375fixedMethodName,method.__get__(self,self.__class__)
-1376)
-1377# Add in _media methods. The functionality of the attached method will
-1378# change when it sees that the method name ends in _media.
-1379ifmethodDesc.get("supportsMediaDownload",False):
-1380fixedMethodName,method=createMethod(
-1381methodName+"_media",methodDesc,rootDesc,schema
-1382)
-1383self._set_dynamic_attr(
-1384fixedMethodName,method.__get__(self,self.__class__)
-1385)
-
1388# Add in nested resources
-1389if"resources"inresourceDesc:
-1390
-1391defcreateResourceMethod(methodName,methodDesc):
-1392"""Create a method on the Resource to access a nested Resource.
-1393
-1394 Args:
-1395 methodName: string, name of the method to use.
-1396 methodDesc: object, fragment of deserialized discovery document that
-1397 describes the method.
-1398 """
-1399methodName=fix_method_name(methodName)
-1400
-1401defmethodResource(self):
-1402returnResource(
-1403http=self._http,
-1404baseUrl=self._baseUrl,
-1405model=self._model,
-1406developerKey=self._developerKey,
-1407requestBuilder=self._requestBuilder,
-1408resourceDesc=methodDesc,
-1409rootDesc=rootDesc,
-1410schema=schema,
-1411)
-
1425# Add _next() methods if and only if one of the names 'pageToken' or
-1426# 'nextPageToken' occurs among the fields of both the method's response
-1427# type either the method's request (query parameters) or request body.
-1428if"methods"notinresourceDesc:
-1429return
-1430formethodName,methodDescinsix.iteritems(resourceDesc["methods"]):
-1431nextPageTokenName=_findPageTokenName(
-1432_methodProperties(methodDesc,schema,"response")
-1433)
-1434ifnotnextPageTokenName:
-1435continue
-1436isPageTokenParameter=True
-1437pageTokenName=_findPageTokenName(methodDesc.get("parameters",{}))
-1438ifnotpageTokenName:
-1439isPageTokenParameter=False
-1440pageTokenName=_findPageTokenName(
-1441_methodProperties(methodDesc,schema,"request")
-1442)
-1443ifnotpageTokenName:
-1444continue
-1445fixedMethodName,method=createNextMethod(
-1446methodName+"_next",
-1447pageTokenName,
-1448nextPageTokenName,
-1449isPageTokenParameter,
-1450)
-1451self._set_dynamic_attr(
-1452fixedMethodName,method.__get__(self,self.__class__)
-1453)
-
1457"""Search field names for one like a page token.
-1458
-1459 Args:
-1460 fields: container of string, names of fields.
-1461
-1462 Returns:
-1463 First name that is either 'pageToken' or 'nextPageToken' if one exists,
-1464 otherwise None.
-1465 """
-1466returnnext(
-1467(tokenNamefortokenNamein_PAGE_TOKEN_NAMESiftokenNameinfields),None
-1468)
-
1472"""Get properties of a field in a method description.
-1473
-1474 Args:
-1475 methodDesc: object, fragment of deserialized discovery document that
-1476 describes the method.
-1477 schema: object, mapping of schema names to schema descriptions.
-1478 name: string, name of top-level field in method description.
-1479
-1480 Returns:
-1481 Object representing fragment of deserialized discovery document
-1482 corresponding to 'properties' field of object corresponding to named field
-1483 in method description, if it exists, otherwise empty dict.
-1484 """
-1485desc=methodDesc.get(name,{})
-1486if"$ref"indesc:
-1487desc=schema.get(desc["$ref"],{})
-1488returndesc.get("properties",{})
-
-Build a Resource from the API description.
-
-Args:
- http: httplib2.Http, Object to make http requests with.
- baseUrl: string, base URL for the API. All requests are relative to this
- URI.
- model: googleapiclient.Model, converts to and from the wire format.
- requestBuilder: class or callable that instantiates an
- googleapiclient.HttpRequest object.
- developerKey: string, key obtained from
- https://code.google.com/apis/console
- resourceDesc: object, section of deserialized discovery document that
- describes a resource. Note that the top level discovery document
- is considered a resource.
- rootDesc: object, the entire deserialized discovery document.
- schema: object, mapping of schema names to schema descriptions.
-
-
-Sets an instance attribute and tracks it in a list of dynamic attributes.
-
-Args:
- attr_name: string; The name of the attribute to be set
- value: The value being set on the object and tracked in the dynamic cache.
-
-
-Represents the parameters associated with a method.
-
-Attributes:
- argmap: Map from method parameter name (string) to query parameter name
- (string).
- required_params: List of required parameters (represented by parameter
- name as string).
- repeated_params: List of repeated parameters (represented by parameter
- name as string).
- pattern_params: Map from method parameter name (string) to regular
- expression (as a string). If the pattern is set for a parameter, the
- value for that parameter must match the regular expression.
- query_params: List of parameters (represented by parameter name as string)
- that will be used in the query string.
- path_params: Set of parameters (represented by parameter name as string)
- that will be used in the base URL path.
- param_types: Map from method parameter name (string) to parameter type. Type
- can be any valid JSON schema type; valid values are 'any', 'array',
- 'boolean', 'integer', 'number', 'object', or 'string'. Reference:
- http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.1
- enum_params: Map from method parameter name (string) to list of strings,
- where each list of strings is the list of acceptable enum values.
-
-
-Constructor for ResourceMethodParameters.
-
-Sets default values and defers to set_parameters to populate.
-
-Args:
- method_desc: Dictionary with metadata describing an API method. Value
- comes from the dictionary of methods stored in the 'methods' key in
- the deserialized discovery document.
-
-
-Populates maps and lists based on method description.
-
-Iterates through each parameter for the method and parses the values from
-the parameter dictionary.
-
-Args:
- method_desc: Dictionary with metadata describing an API method. Value
- comes from the dictionary of methods stored in the 'methods' key in
- the deserialized discovery document.
-
-
-Detects an appropriate cache module and returns it.
-
-Returns:
- googleapiclient.discovery_cache.base.Cache, a cache object which
- is auto detected, or None if no cache object is available.
-
-
- 1# Copyright 2014 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
-10# distributed under the License is distributed on an "AS IS" BASIS,
-11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-12# See the License for the specific language governing permissions and
-13# limitations under the License.
-14
-15"""Caching utility for the discovery document."""
-16
-17from__future__importabsolute_import
-18
-19importlogging
-20importdatetime
-21importos
-22
-23LOGGER=logging.getLogger(__name__)
-24
-25DISCOVERY_DOC_MAX_AGE=60*60*24# 1 day
-26
-27
-
29"""Detects an appropriate cache module and returns it.
-30
-31 Returns:
-32 googleapiclient.discovery_cache.base.Cache, a cache object which
-33 is auto detected, or None if no cache object is available.
-34 """
-35if'APPENGINE_RUNTIME'inos.environ:
-36try:
-37fromgoogle.appengine.apiimportmemcache
-38from.importappengine_memcache
-39
-40returnappengine_memcache.cache
-41exceptException:
-42pass
-43try:
-44from.importfile_cache
-45
-46returnfile_cache.cache
-47exceptExceptionase:
-48LOGGER.warning(e,exc_info=True)
-49returnNone
-
- 1# Copyright 2014 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
-10# distributed under the License is distributed on an "AS IS" BASIS,
-11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-12# See the License for the specific language governing permissions and
-13# limitations under the License.
-14
-15"""App Engine memcache based cache for the discovery document."""
-16
-17importlogging
-18
-19# This is only an optional dependency because we only import this
-20# module when google.appengine.api.memcache is available.
-21fromgoogle.appengine.apiimportmemcache
-22
-23from.importbase
-24from..discovery_cacheimportDISCOVERY_DOC_MAX_AGE
-25
-26
-27LOGGER=logging.getLogger(__name__)
-28
-29NAMESPACE="google-api-client"
-30
-31
-
-Gets the content from the memcache with a given key.
-
-Args:
- url: string, the key for the cache.
-
-Returns:
- object, the value in the cache for the given key, or None if the key is
- not in the cache.
-
-
- 1# Copyright 2014 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
-10# distributed under the License is distributed on an "AS IS" BASIS,
-11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-12# See the License for the specific language governing permissions and
-13# limitations under the License.
-14
-15"""An abstract class for caching the discovery document."""
-16
-17importabc
-
27"""Gets the content from the memcache with a given key.
-28
-29 Args:
-30 url: string, the key for the cache.
-31
-32 Returns:
-33 object, the value in the cache for the given key, or None if the key is
-34 not in the cache.
-35 """
-36raiseNotImplementedError()
-
40"""Sets the given key and content in the cache.
-41
-42 Args:
-43 url: string, the key for the cache.
-44 content: string, the discovery document.
-45 """
-46raiseNotImplementedError()
-
-Gets the content from the memcache with a given key.
-
-Args:
- url: string, the key for the cache.
-
-Returns:
- object, the value in the cache for the given key, or None if the key is
- not in the cache.
-
-
The cache is stored in a single file so that multiple processes can
- share the same cache. It locks the file whenever accesing to the file.
- When the cache content is corrupted, it will be initialized with an empty
- cache.
- 1# Copyright 2014 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""File based cache for the discovery document.
- 16
- 17The cache is stored in a single file so that multiple processes can
- 18share the same cache. It locks the file whenever accesing to the
- 19file. When the cache content is corrupted, it will be initialized with
- 20an empty cache.
- 21"""
- 22
- 23from__future__importdivision
- 24
- 25importdatetime
- 26importjson
- 27importlogging
- 28importos
- 29importtempfile
- 30importthreading
- 31
- 32try:
- 33fromoauth2client.contrib.locked_fileimportLockedFile
- 34exceptImportError:
- 35# oauth2client < 2.0.0
- 36try:
- 37fromoauth2client.locked_fileimportLockedFile
- 38exceptImportError:
- 39# oauth2client > 4.0.0 or google-auth
- 40raiseImportError(
- 41"file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth"
- 42)
- 43
- 44from.importbase
- 45from..discovery_cacheimportDISCOVERY_DOC_MAX_AGE
- 46
- 47LOGGER=logging.getLogger(__name__)
- 48
- 49FILENAME="google-api-python-client-discovery-doc.cache"
- 50EPOCH=datetime.datetime.utcfromtimestamp(0)
- 51
- 52
-
54try:
- 55return(date-EPOCH).total_seconds()
- 56exceptAttributeError:
- 57# The following is the equivalent of total_seconds() in Python2.6.
- 58# See also: https://docs.python.org/2/library/datetime.html
- 59delta=date-EPOCH
- 60return(
- 61delta.microseconds+(delta.seconds+delta.days*24*3600)*10**6
- 62)/10**6
-
66f.file_handle().seek(0)
- 67try:
- 68cache=json.load(f.file_handle())
- 69exceptException:
- 70# This means it opens the file for the first time, or the cache is
- 71# corrupted, so initializing the file with an empty dict.
- 72cache={}
- 73f.file_handle().truncate(0)
- 74f.file_handle().seek(0)
- 75json.dump(cache,f.file_handle())
- 76returncache
-
83"""Constructor.
- 84
- 85 Args:
- 86 max_age: Cache expiration in seconds.
- 87 """
- 88self._max_age=max_age
- 89self._file=os.path.join(tempfile.gettempdir(),FILENAME)
- 90f=LockedFile(self._file,"a+","r")
- 91try:
- 92f.open_and_lock()
- 93iff.is_locked():
- 94_read_or_initialize_cache(f)
- 95# If we can not obtain the lock, other process or thread must
- 96# have initialized the file.
- 97exceptExceptionase:
- 98LOGGER.warning(e,exc_info=True)
- 99finally:
-100f.unlock_and_close()
-
-Gets the content from the memcache with a given key.
-
-Args:
- url: string, the key for the cache.
-
-Returns:
- object, the value in the cache for the given key, or None if the key is
- not in the cache.
-
-
- 1# Copyright 2014 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Errors for the library.
- 16
- 17All exceptions defined by the library
- 18should be defined in this file.
- 19"""
- 20from__future__importabsolute_import
- 21
- 22__author__="jcgregorio@google.com (Joe Gregorio)"
- 23
- 24importjson
- 25
- 26fromgoogleapiclientimport_helpersasutil
-
-Determines whether a response should be retried.
-
-Args:
- resp_status: The response status received.
- content: The response content body.
-
-Returns:
- True if the response should be retried, otherwise False.
-
-
-Retries an HTTP request multiple times while handling errors.
-
-If after all retries the request still fails, last error is either returned as
-return value (for HTTP 5xx errors) or thrown (for ssl.SSLError).
-
-Args:
- http: Http object to be used to execute request.
- num_retries: Maximum number of retries.
- req_type: Type of the request (used for logging retries).
- sleep, rand: Functions to sleep for random time between retries.
- uri: URI to be requested.
- method: HTTP method to be used.
- args, kwargs: Additional arguments passed to http.request.
-
-Returns:
- resp, content - Response from the http request (may be HTTP 5xx).
-
-
-Set the user-agent on every request.
-
-Args:
- http - An instance of httplib2.Http
- or something that acts like it.
- user_agent: string, the value for the user-agent header.
-
-Returns:
- A modified instance of http that was passed in.
-
-Example:
-
- h = httplib2.Http()
- h = set_user_agent(h, "my-app-name/6.0")
-
-Most of the time the user-agent will be set doing auth, this is for the rare
-cases where you are accessing an unauthenticated endpoint.
-
-
-Tunnel PATCH requests over POST.
-Args:
- http - An instance of httplib2.Http
- or something that acts like it.
-
-Returns:
- A modified instance of http that was passed in.
-
-Example:
-
- h = httplib2.Http()
- h = tunnel_patch(h, "my-app-name/6.0")
-
-Useful if you are running on a platform that doesn't support PATCH.
-Apply this last if you are using OAuth 1.0, as changing the method
-will result in a different signature.
-
-
-Builds httplib2.Http object
-
-Returns:
-A httplib2.Http object, which is used to make http requests, and which has timeout set by default.
-To override default timeout call
-
- socket.setdefaulttimeout(timeout_in_sec)
-
-before interacting with this method.
-
-
- 1# Copyright 2014 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Classes to encapsulate a single HTTP request.
- 16
- 17The classes implement a command pattern, with every
- 18object supporting an execute() method that does the
- 19actual HTTP request.
- 20"""
- 21from__future__importabsolute_import
- 22importsix
- 23fromsix.movesimporthttp_client
- 24fromsix.movesimportrange
- 25
- 26__author__="jcgregorio@google.com (Joe Gregorio)"
- 27
- 28fromsiximportBytesIO,StringIO
- 29fromsix.moves.urllib.parseimporturlparse,urlunparse,quote,unquote
- 30
- 31importbase64
- 32importcopy
- 33importgzip
- 34importhttplib2
- 35importjson
- 36importlogging
- 37importmimetypes
- 38importos
- 39importrandom
- 40importsocket
- 41importsys
- 42importtime
- 43importuuid
- 44
- 45# TODO(issue 221): Remove this conditional import jibbajabba.
- 46try:
- 47importssl
- 48exceptImportError:
- 49_ssl_SSLError=object()
- 50else:
- 51_ssl_SSLError=ssl.SSLError
- 52
- 53fromemail.generatorimportGenerator
- 54fromemail.mime.multipartimportMIMEMultipart
- 55fromemail.mime.nonmultipartimportMIMENonMultipart
- 56fromemail.parserimportFeedParser
- 57
- 58fromgoogleapiclientimport_helpersasutil
- 59
- 60fromgoogleapiclientimport_auth
- 61fromgoogleapiclient.errorsimportBatchError
- 62fromgoogleapiclient.errorsimportHttpError
- 63fromgoogleapiclient.errorsimportInvalidChunkSizeError
- 64fromgoogleapiclient.errorsimportResumableUploadError
- 65fromgoogleapiclient.errorsimportUnexpectedBodyError
- 66fromgoogleapiclient.errorsimportUnexpectedMethodError
- 67fromgoogleapiclient.modelimportJsonModel
- 68
- 69
- 70LOGGER=logging.getLogger(__name__)
- 71
- 72DEFAULT_CHUNK_SIZE=100*1024*1024
- 73
- 74MAX_URI_LENGTH=2048
- 75
- 76MAX_BATCH_LIMIT=1000
- 77
- 78_TOO_MANY_REQUESTS=429
- 79
- 80DEFAULT_HTTP_TIMEOUT_SEC=60
- 81
- 82_LEGACY_BATCH_URI="https://www.googleapis.com/batch"
- 83
- 84ifsix.PY2:
- 85# That's a builtin python3 exception, nonexistent in python2.
- 86# Defined to None to avoid NameError while trying to catch it
- 87ConnectionError=None
-
91"""Determines whether a response should be retried.
- 92
- 93 Args:
- 94 resp_status: The response status received.
- 95 content: The response content body.
- 96
- 97 Returns:
- 98 True if the response should be retried, otherwise False.
- 99 """
- 100# Retry on 5xx errors.
- 101ifresp_status>=500:
- 102returnTrue
- 103
- 104# Retry on 429 errors.
- 105ifresp_status==_TOO_MANY_REQUESTS:
- 106returnTrue
- 107
- 108# For 403 errors, we have to check for the `reason` in the response to
- 109# determine if we should retry.
- 110ifresp_status==six.moves.http_client.FORBIDDEN:
- 111# If there's no details about the 403 type, don't retry.
- 112ifnotcontent:
- 113returnFalse
- 114
- 115# Content is in JSON format.
- 116try:
- 117data=json.loads(content.decode("utf-8"))
- 118ifisinstance(data,dict):
- 119reason=data["error"]["errors"][0]["reason"]
- 120else:
- 121reason=data[0]["error"]["errors"]["reason"]
- 122except(UnicodeDecodeError,ValueError,KeyError):
- 123LOGGER.warning("Invalid JSON content from response: %s",content)
- 124returnFalse
- 125
- 126LOGGER.warning('Encountered 403 Forbidden with reason "%s"',reason)
- 127
- 128# Only retry on rate limit related failures.
- 129ifreasonin("userRateLimitExceeded","rateLimitExceeded"):
- 130returnTrue
- 131
- 132# Everything else is a success or non-retriable so break.
- 133returnFalse
-
139"""Retries an HTTP request multiple times while handling errors.
- 140
- 141 If after all retries the request still fails, last error is either returned as
- 142 return value (for HTTP 5xx errors) or thrown (for ssl.SSLError).
- 143
- 144 Args:
- 145 http: Http object to be used to execute request.
- 146 num_retries: Maximum number of retries.
- 147 req_type: Type of the request (used for logging retries).
- 148 sleep, rand: Functions to sleep for random time between retries.
- 149 uri: URI to be requested.
- 150 method: HTTP method to be used.
- 151 args, kwargs: Additional arguments passed to http.request.
- 152
- 153 Returns:
- 154 resp, content - Response from the http request (may be HTTP 5xx).
- 155 """
- 156resp=None
- 157content=None
- 158exception=None
- 159forretry_numinrange(num_retries+1):
- 160ifretry_num>0:
- 161# Sleep before retrying.
- 162sleep_time=rand()*2**retry_num
- 163LOGGER.warning(
- 164"Sleeping %.2f seconds before retry %d of %d for %s: %s %s, after %s",
- 165sleep_time,
- 166retry_num,
- 167num_retries,
- 168req_type,
- 169method,
- 170uri,
- 171resp.statusifrespelseexception,
- 172)
- 173sleep(sleep_time)
- 174
- 175try:
- 176exception=None
- 177resp,content=http.request(uri,method,*args,**kwargs)
- 178# Retry on SSL errors and socket timeout errors.
- 179except_ssl_SSLErrorasssl_error:
- 180exception=ssl_error
- 181exceptsocket.timeoutassocket_timeout:
- 182# It's important that this be before socket.error as it's a subclass
- 183# socket.timeout has no errorcode
- 184exception=socket_timeout
- 185exceptConnectionErrorasconnection_error:
- 186# Needs to be before socket.error as it's a subclass of
- 187# OSError (socket.error)
- 188exception=connection_error
- 189exceptsocket.errorassocket_error:
- 190# errno's contents differ by platform, so we have to match by name.
- 191ifsocket.errno.errorcode.get(socket_error.errno)notin{
- 192"WSAETIMEDOUT",
- 193"ETIMEDOUT",
- 194"EPIPE",
- 195"ECONNABORTED",
- 196}:
- 197raise
- 198exception=socket_error
- 199excepthttplib2.ServerNotFoundErrorasserver_not_found_error:
- 200exception=server_not_found_error
- 201
- 202ifexception:
- 203ifretry_num==num_retries:
- 204raiseexception
- 205else:
- 206continue
- 207
- 208ifnot_should_retry_response(resp.status,content):
- 209break
- 210
- 211returnresp,content
-
218"""Constructor.
- 219
- 220 Args:
- 221 resumable_progress: int, bytes sent so far.
- 222 total_size: int, total bytes in complete upload, or None if the total
- 223 upload size isn't known ahead of time.
- 224 """
- 225self.resumable_progress=resumable_progress
- 226self.total_size=total_size
-
229"""Percent of upload completed, as a float.
- 230
- 231 Returns:
- 232 the percentage complete as a float, returning 0.0 if the total size of
- 233 the upload is unknown.
- 234 """
- 235ifself.total_sizeisnotNoneandself.total_size!=0:
- 236returnfloat(self.resumable_progress)/float(self.total_size)
- 237else:
- 238return0.0
-
255"""Percent of download completed, as a float.
- 256
- 257 Returns:
- 258 the percentage complete as a float, returning 0.0 if the total size of
- 259 the download is unknown.
- 260 """
- 261ifself.total_sizeisnotNoneandself.total_size!=0:
- 262returnfloat(self.resumable_progress)/float(self.total_size)
- 263else:
- 264return0.0
-
268"""Describes a media object to upload.
- 269
- 270 Base class that defines the interface of MediaUpload subclasses.
- 271
- 272 Note that subclasses of MediaUpload may allow you to control the chunksize
- 273 when uploading a media object. It is important to keep the size of the chunk
- 274 as large as possible to keep the upload efficient. Other factors may influence
- 275 the size of the chunk you use, particularly if you are working in an
- 276 environment where individual HTTP requests may have a hardcoded time limit,
- 277 such as under certain classes of requests under Google App Engine.
- 278
- 279 Streams are io.Base compatible objects that support seek(). Some MediaUpload
- 280 subclasses support using streams directly to upload data. Support for
- 281 streaming may be indicated by a MediaUpload sub-class and if appropriate for a
- 282 platform that stream will be used for uploading the media object. The support
- 283 for streaming is indicated by has_stream() returning True. The stream() method
- 284 should return an io.Base object that supports seek(). On platforms where the
- 285 underlying httplib module supports streaming, for example Python 2.6 and
- 286 later, the stream will be passed into the http library which will result in
- 287 less memory being used and possibly faster uploads.
- 288
- 289 If you need to upload media that can't be uploaded using any of the existing
- 290 MediaUpload sub-class then you can sub-class MediaUpload for your particular
- 291 needs.
- 292 """
- 293
-
327"""Get bytes from the media.
- 328
- 329 Args:
- 330 begin: int, offset from beginning of file.
- 331 length: int, number of bytes to read, starting at begin.
- 332
- 333 Returns:
- 334 A string of bytes read. May be shorter than length if EOF was reached
- 335 first.
- 336 """
- 337raiseNotImplementedError()
-
340"""Does the underlying upload support a streaming interface.
- 341
- 342 Streaming means it is an io.IOBase subclass that supports seek, i.e.
- 343 seekable() returns True.
- 344
- 345 Returns:
- 346 True if the call to stream() will return an instance of a seekable io.Base
- 347 subclass.
- 348 """
- 349returnFalse
-
352"""A stream interface to the data being uploaded.
- 353
- 354 Returns:
- 355 The returned value is an io.IOBase subclass that supports seek, i.e.
- 356 seekable() returns True.
- 357 """
- 358raiseNotImplementedError()
-
362"""Utility function for creating a JSON representation of a MediaUpload.
- 363
- 364 Args:
- 365 strip: array, An array of names of members to not include in the JSON.
- 366
- 367 Returns:
- 368 string, a JSON representation of this instance, suitable to pass to
- 369 from_json().
- 370 """
- 371t=type(self)
- 372d=copy.copy(self.__dict__)
- 373ifstripisnotNone:
- 374formemberinstrip:
- 375deld[member]
- 376d["_class"]=t.__name__
- 377d["_module"]=t.__module__
- 378returnjson.dumps(d)
-
381"""Create a JSON representation of an instance of MediaUpload.
- 382
- 383 Returns:
- 384 string, a JSON representation of this instance, suitable to pass to
- 385 from_json().
- 386 """
- 387returnself._to_json()
-
391"""Utility class method to instantiate a MediaUpload subclass from a JSON
- 392 representation produced by to_json().
- 393
- 394 Args:
- 395 s: string, JSON from to_json().
- 396
- 397 Returns:
- 398 An instance of the subclass of MediaUpload that was serialized with
- 399 to_json().
- 400 """
- 401data=json.loads(s)
- 402# Find and call the right classmethod from_json() to restore the object.
- 403module=data["_module"]
- 404m=__import__(module,fromlist=module.split(".")[:-1])
- 405kls=getattr(m,data["_class"])
- 406from_json=getattr(kls,"from_json")
- 407returnfrom_json(s)
-
411"""A MediaUpload for a io.Base objects.
- 412
- 413 Note that the Python file object is compatible with io.Base and can be used
- 414 with this class also.
- 415
- 416 fh = BytesIO('...Some data to upload...')
- 417 media = MediaIoBaseUpload(fh, mimetype='image/png',
- 418 chunksize=1024*1024, resumable=True)
- 419 farm.animals().insert(
- 420 id='cow',
- 421 name='cow.png',
- 422 media_body=media).execute()
- 423
- 424 Depending on the platform you are working on, you may pass -1 as the
- 425 chunksize, which indicates that the entire file should be uploaded in a single
- 426 request. If the underlying platform supports streams, such as Python 2.6 or
- 427 later, then this can be very efficient as it avoids multiple connections, and
- 428 also avoids loading the entire file into memory before sending it. Note that
- 429 Google App Engine has a 5MB limit on request size, so you should never set
- 430 your chunksize larger than 5MB, or to -1.
- 431 """
- 432
- 433@util.positional(3)
-
435"""Constructor.
- 436
- 437 Args:
- 438 fd: io.Base or file object, The source of the bytes to upload. MUST be
- 439 opened in blocking mode, do not use streams opened in non-blocking mode.
- 440 The given stream must be seekable, that is, it must be able to call
- 441 seek() on fd.
- 442 mimetype: string, Mime-type of the file.
- 443 chunksize: int, File will be uploaded in chunks of this many bytes. Only
- 444 used if resumable=True. Pass in a value of -1 if the file is to be
- 445 uploaded as a single chunk. Note that Google App Engine has a 5MB limit
- 446 on request size, so you should never set your chunksize larger than 5MB,
- 447 or to -1.
- 448 resumable: bool, True if this is a resumable upload. False means upload
- 449 in a single request.
- 450 """
- 451super(MediaIoBaseUpload,self).__init__()
- 452self._fd=fd
- 453self._mimetype=mimetype
- 454ifnot(chunksize==-1orchunksize>0):
- 455raiseInvalidChunkSizeError()
- 456self._chunksize=chunksize
- 457self._resumable=resumable
- 458
- 459self._fd.seek(0,os.SEEK_END)
- 460self._size=self._fd.tell()
-
495"""Get bytes from the media.
- 496
- 497 Args:
- 498 begin: int, offset from beginning of file.
- 499 length: int, number of bytes to read, starting at begin.
- 500
- 501 Returns:
- 502 A string of bytes read. May be shorted than length if EOF was reached
- 503 first.
- 504 """
- 505self._fd.seek(begin)
- 506returnself._fd.read(length)
-
509"""Does the underlying upload support a streaming interface.
- 510
- 511 Streaming means it is an io.IOBase subclass that supports seek, i.e.
- 512 seekable() returns True.
- 513
- 514 Returns:
- 515 True if the call to stream() will return an instance of a seekable io.Base
- 516 subclass.
- 517 """
- 518returnTrue
-
521"""A stream interface to the data being uploaded.
- 522
- 523 Returns:
- 524 The returned value is an io.IOBase subclass that supports seek, i.e.
- 525 seekable() returns True.
- 526 """
- 527returnself._fd
-
535"""A MediaUpload for a file.
- 536
- 537 Construct a MediaFileUpload and pass as the media_body parameter of the
- 538 method. For example, if we had a service that allowed uploading images:
- 539
- 540 media = MediaFileUpload('cow.png', mimetype='image/png',
- 541 chunksize=1024*1024, resumable=True)
- 542 farm.animals().insert(
- 543 id='cow',
- 544 name='cow.png',
- 545 media_body=media).execute()
- 546
- 547 Depending on the platform you are working on, you may pass -1 as the
- 548 chunksize, which indicates that the entire file should be uploaded in a single
- 549 request. If the underlying platform supports streams, such as Python 2.6 or
- 550 later, then this can be very efficient as it avoids multiple connections, and
- 551 also avoids loading the entire file into memory before sending it. Note that
- 552 Google App Engine has a 5MB limit on request size, so you should never set
- 553 your chunksize larger than 5MB, or to -1.
- 554 """
- 555
- 556@util.positional(2)
-
560"""Constructor.
- 561
- 562 Args:
- 563 filename: string, Name of the file.
- 564 mimetype: string, Mime-type of the file. If None then a mime-type will be
- 565 guessed from the file extension.
- 566 chunksize: int, File will be uploaded in chunks of this many bytes. Only
- 567 used if resumable=True. Pass in a value of -1 if the file is to be
- 568 uploaded in a single chunk. Note that Google App Engine has a 5MB limit
- 569 on request size, so you should never set your chunksize larger than 5MB,
- 570 or to -1.
- 571 resumable: bool, True if this is a resumable upload. False means upload
- 572 in a single request.
- 573 """
- 574self._filename=filename
- 575fd=open(self._filename,"rb")
- 576ifmimetypeisNone:
- 577# No mimetype provided, make a guess.
- 578mimetype,_=mimetypes.guess_type(filename)
- 579ifmimetypeisNone:
- 580# Guess failed, use octet-stream.
- 581mimetype="application/octet-stream"
- 582super(MediaFileUpload,self).__init__(
- 583fd,mimetype,chunksize=chunksize,resumable=resumable
- 584)
-
590"""Creating a JSON representation of an instance of MediaFileUpload.
- 591
- 592 Returns:
- 593 string, a JSON representation of this instance, suitable to pass to
- 594 from_json().
- 595 """
- 596returnself._to_json(strip=["_fd"])
-
610"""MediaUpload for a chunk of bytes.
- 611
- 612 DEPRECATED: Use MediaIoBaseUpload with either io.TextIOBase or StringIO for
- 613 the stream.
- 614 """
- 615
- 616@util.positional(2)
-
624"""Create a new MediaInMemoryUpload.
- 625
- 626 DEPRECATED: Use MediaIoBaseUpload with either io.TextIOBase or StringIO for
- 627 the stream.
- 628
- 629 Args:
- 630 body: string, Bytes of body content.
- 631 mimetype: string, Mime-type of the file or default of
- 632 'application/octet-stream'.
- 633 chunksize: int, File will be uploaded in chunks of this many bytes. Only
- 634 used if resumable=True.
- 635 resumable: bool, True if this is a resumable upload. False means upload
- 636 in a single request.
- 637 """
- 638fd=BytesIO(body)
- 639super(MediaInMemoryUpload,self).__init__(
- 640fd,mimetype,chunksize=chunksize,resumable=resumable
- 641)
-
666"""Constructor.
- 667
- 668 Args:
- 669 fd: io.Base or file object, The stream in which to write the downloaded
- 670 bytes.
- 671 request: googleapiclient.http.HttpRequest, the media request to perform in
- 672 chunks.
- 673 chunksize: int, File will be downloaded in chunks of this many bytes.
- 674 """
- 675self._fd=fd
- 676self._request=request
- 677self._uri=request.uri
- 678self._chunksize=chunksize
- 679self._progress=0
- 680self._total_size=None
- 681self._done=False
- 682
- 683# Stubs for testing.
- 684self._sleep=time.sleep
- 685self._rand=random.random
- 686
- 687self._headers={}
- 688fork,vinsix.iteritems(request.headers):
- 689# allow users to supply custom headers by setting them on the request
- 690# but strip out the ones that are set by default on requests generated by
- 691# API methods like Drive's files().get(fileId=...)
- 692ifnotk.lower()in("accept","accept-encoding","user-agent"):
- 693self._headers[k]=v
-
697"""Get the next chunk of the download.
- 698
- 699 Args:
- 700 num_retries: Integer, number of times to retry with randomized
- 701 exponential backoff. If all retries fail, the raised HttpError
- 702 represents the last request. If zero (default), we attempt the
- 703 request only once.
- 704
- 705 Returns:
- 706 (status, done): (MediaDownloadProgress, boolean)
- 707 The value of 'done' will be True when the media has been fully
- 708 downloaded or the total size of the media is unknown.
- 709
- 710 Raises:
- 711 googleapiclient.errors.HttpError if the response was not a 2xx.
- 712 httplib2.HttpLib2Error if a transport error has occurred.
- 713 """
- 714headers=self._headers.copy()
- 715headers["range"]="bytes=%d-%d"%(
- 716self._progress,
- 717self._progress+self._chunksize,
- 718)
- 719http=self._request.http
- 720
- 721resp,content=_retry_request(
- 722http,
- 723num_retries,
- 724"media download",
- 725self._sleep,
- 726self._rand,
- 727self._uri,
- 728"GET",
- 729headers=headers,
- 730)
- 731
- 732ifresp.statusin[200,206]:
- 733if"content-location"inrespandresp["content-location"]!=self._uri:
- 734self._uri=resp["content-location"]
- 735self._progress+=len(content)
- 736self._fd.write(content)
- 737
- 738if"content-range"inresp:
- 739content_range=resp["content-range"]
- 740length=content_range.rsplit("/",1)[1]
- 741self._total_size=int(length)
- 742elif"content-length"inresp:
- 743self._total_size=int(resp["content-length"])
- 744
- 745ifself._total_sizeisNoneorself._progress==self._total_size:
- 746self._done=True
- 747returnMediaDownloadProgress(self._progress,self._total_size),self._done
- 748else:
- 749raiseHttpError(resp,content,uri=self._uri)
-
753"""Truncated stream.
- 754
- 755 Takes a stream and presents a stream that is a slice of the original stream.
- 756 This is used when uploading media in chunks. In later versions of Python a
- 757 stream can be passed to httplib in place of the string of data to send. The
- 758 problem is that httplib just blindly reads to the end of the stream. This
- 759 wrapper presents a virtual stream that only reads to the end of the chunk.
- 760 """
- 761
-
776"""Read n bytes.
- 777
- 778 Args:
- 779 n, int, the number of bytes to read.
- 780
- 781 Returns:
- 782 A string of length 'n', or less if EOF is reached.
- 783 """
- 784# The data left available to read sits in [cur, end)
- 785cur=self._stream.tell()
- 786end=self._begin+self._chunksize
- 787ifn==-1orcur+n>end:
- 788n=end-cur
- 789returnself._stream.read(n)
-
807"""Constructor for an HttpRequest.
- 808
- 809 Args:
- 810 http: httplib2.Http, the transport object to use to make a request
- 811 postproc: callable, called on the HTTP response and content to transform
- 812 it into a data object before returning, or raising an exception
- 813 on an error.
- 814 uri: string, the absolute URI to send the request to
- 815 method: string, the HTTP method to use
- 816 body: string, the request body of the HTTP request,
- 817 headers: dict, the HTTP request headers
- 818 methodId: string, a unique identifier for the API method being called.
- 819 resumable: MediaUpload, None if this is not a resumbale request.
- 820 """
- 821self.uri=uri
- 822self.method=method
- 823self.body=body
- 824self.headers=headersor{}
- 825self.methodId=methodId
- 826self.http=http
- 827self.postproc=postproc
- 828self.resumable=resumable
- 829self.response_callbacks=[]
- 830self._in_error_state=False
- 831
- 832# The size of the non-media part of the request.
- 833self.body_size=len(self.bodyor"")
- 834
- 835# The resumable URI to send chunks to.
- 836self.resumable_uri=None
- 837
- 838# The bytes that have been uploaded.
- 839self.resumable_progress=0
- 840
- 841# Stubs for testing.
- 842self._rand=random.random
- 843self._sleep=time.sleep
-
847"""Execute the request.
- 848
- 849 Args:
- 850 http: httplib2.Http, an http object to be used in place of the
- 851 one the HttpRequest request object was constructed with.
- 852 num_retries: Integer, number of times to retry with randomized
- 853 exponential backoff. If all retries fail, the raised HttpError
- 854 represents the last request. If zero (default), we attempt the
- 855 request only once.
- 856
- 857 Returns:
- 858 A deserialized object model of the response body as determined
- 859 by the postproc.
- 860
- 861 Raises:
- 862 googleapiclient.errors.HttpError if the response was not a 2xx.
- 863 httplib2.HttpLib2Error if a transport error has occurred.
- 864 """
- 865ifhttpisNone:
- 866http=self.http
- 867
- 868ifself.resumable:
- 869body=None
- 870whilebodyisNone:
- 871_,body=self.next_chunk(http=http,num_retries=num_retries)
- 872returnbody
- 873
- 874# Non-resumable case.
- 875
- 876if"content-length"notinself.headers:
- 877self.headers["content-length"]=str(self.body_size)
- 878# If the request URI is too long then turn it into a POST request.
- 879# Assume that a GET request never contains a request body.
- 880iflen(self.uri)>MAX_URI_LENGTHandself.method=="GET":
- 881self.method="POST"
- 882self.headers["x-http-method-override"]="GET"
- 883self.headers["content-type"]="application/x-www-form-urlencoded"
- 884parsed=urlparse(self.uri)
- 885self.uri=urlunparse(
- 886(parsed.scheme,parsed.netloc,parsed.path,parsed.params,None,None)
- 887)
- 888self.body=parsed.query
- 889self.headers["content-length"]=str(len(self.body))
- 890
- 891# Handle retries for server-side errors.
- 892resp,content=_retry_request(
- 893http,
- 894num_retries,
- 895"request",
- 896self._sleep,
- 897self._rand,
- 898str(self.uri),
- 899method=str(self.method),
- 900body=self.body,
- 901headers=self.headers,
- 902)
- 903
- 904forcallbackinself.response_callbacks:
- 905callback(resp)
- 906ifresp.status>=300:
- 907raiseHttpError(resp,content,uri=self.uri)
- 908returnself.postproc(resp,content)
-
912"""add_response_headers_callback
- 913
- 914 Args:
- 915 cb: Callback to be called on receiving the response headers, of signature:
- 916
- 917 def cb(resp):
- 918 # Where resp is an instance of httplib2.Response
- 919 """
- 920self.response_callbacks.append(cb)
-
924"""Execute the next step of a resumable upload.
- 925
- 926 Can only be used if the method being executed supports media uploads and
- 927 the MediaUpload object passed in was flagged as using resumable upload.
- 928
- 929 Example:
- 930
- 931 media = MediaFileUpload('cow.png', mimetype='image/png',
- 932 chunksize=1000, resumable=True)
- 933 request = farm.animals().insert(
- 934 id='cow',
- 935 name='cow.png',
- 936 media_body=media)
- 937
- 938 response = None
- 939 while response is None:
- 940 status, response = request.next_chunk()
- 941 if status:
- 942 print "Upload %d%% complete." % int(status.progress() * 100)
- 943
- 944
- 945 Args:
- 946 http: httplib2.Http, an http object to be used in place of the
- 947 one the HttpRequest request object was constructed with.
- 948 num_retries: Integer, number of times to retry with randomized
- 949 exponential backoff. If all retries fail, the raised HttpError
- 950 represents the last request. If zero (default), we attempt the
- 951 request only once.
- 952
- 953 Returns:
- 954 (status, body): (ResumableMediaStatus, object)
- 955 The body will be None until the resumable media is fully uploaded.
- 956
- 957 Raises:
- 958 googleapiclient.errors.HttpError if the response was not a 2xx.
- 959 httplib2.HttpLib2Error if a transport error has occurred.
- 960 """
- 961ifhttpisNone:
- 962http=self.http
- 963
- 964ifself.resumable.size()isNone:
- 965size="*"
- 966else:
- 967size=str(self.resumable.size())
- 968
- 969ifself.resumable_uriisNone:
- 970start_headers=copy.copy(self.headers)
- 971start_headers["X-Upload-Content-Type"]=self.resumable.mimetype()
- 972ifsize!="*":
- 973start_headers["X-Upload-Content-Length"]=size
- 974start_headers["content-length"]=str(self.body_size)
- 975
- 976resp,content=_retry_request(
- 977http,
- 978num_retries,
- 979"resumable URI request",
- 980self._sleep,
- 981self._rand,
- 982self.uri,
- 983method=self.method,
- 984body=self.body,
- 985headers=start_headers,
- 986)
- 987
- 988ifresp.status==200and"location"inresp:
- 989self.resumable_uri=resp["location"]
- 990else:
- 991raiseResumableUploadError(resp,content)
- 992elifself._in_error_state:
- 993# If we are in an error state then query the server for current state of
- 994# the upload by sending an empty PUT and reading the 'range' header in
- 995# the response.
- 996headers={"Content-Range":"bytes */%s"%size,"content-length":"0"}
- 997resp,content=http.request(self.resumable_uri,"PUT",headers=headers)
- 998status,body=self._process_response(resp,content)
- 999ifbody:
-1000# The upload was complete.
-1001return(status,body)
-1002
-1003ifself.resumable.has_stream():
-1004data=self.resumable.stream()
-1005ifself.resumable.chunksize()==-1:
-1006data.seek(self.resumable_progress)
-1007chunk_end=self.resumable.size()-self.resumable_progress-1
-1008else:
-1009# Doing chunking with a stream, so wrap a slice of the stream.
-1010data=_StreamSlice(
-1011data,self.resumable_progress,self.resumable.chunksize()
-1012)
-1013chunk_end=min(
-1014self.resumable_progress+self.resumable.chunksize()-1,
-1015self.resumable.size()-1,
-1016)
-1017else:
-1018data=self.resumable.getbytes(
-1019self.resumable_progress,self.resumable.chunksize()
-1020)
-1021
-1022# A short read implies that we are at EOF, so finish the upload.
-1023iflen(data)<self.resumable.chunksize():
-1024size=str(self.resumable_progress+len(data))
-1025
-1026chunk_end=self.resumable_progress+len(data)-1
-1027
-1028headers={
-1029"Content-Range":"bytes %d-%d/%s"
-1030%(self.resumable_progress,chunk_end,size),
-1031# Must set the content-length header here because httplib can't
-1032# calculate the size when working with _StreamSlice.
-1033"Content-Length":str(chunk_end-self.resumable_progress+1),
-1034}
-1035
-1036forretry_numinrange(num_retries+1):
-1037ifretry_num>0:
-1038self._sleep(self._rand()*2**retry_num)
-1039LOGGER.warning(
-1040"Retry #%d for media upload: %s %s, following status: %d"
-1041%(retry_num,self.method,self.uri,resp.status)
-1042)
-1043
-1044try:
-1045resp,content=http.request(
-1046self.resumable_uri,method="PUT",body=data,headers=headers
-1047)
-1048except:
-1049self._in_error_state=True
-1050raise
-1051ifnot_should_retry_response(resp.status,content):
-1052break
-1053
-1054returnself._process_response(resp,content)
-
1057"""Process the response from a single chunk upload.
-1058
-1059 Args:
-1060 resp: httplib2.Response, the response object.
-1061 content: string, the content of the response.
-1062
-1063 Returns:
-1064 (status, body): (ResumableMediaStatus, object)
-1065 The body will be None until the resumable media is fully uploaded.
-1066
-1067 Raises:
-1068 googleapiclient.errors.HttpError if the response was not a 2xx or a 308.
-1069 """
-1070ifresp.statusin[200,201]:
-1071self._in_error_state=False
-1072returnNone,self.postproc(resp,content)
-1073elifresp.status==308:
-1074self._in_error_state=False
-1075# A "308 Resume Incomplete" indicates we are not done.
-1076try:
-1077self.resumable_progress=int(resp["range"].split("-")[1])+1
-1078exceptKeyError:
-1079# If resp doesn't contain range header, resumable progress is 0
-1080self.resumable_progress=0
-1081if"location"inresp:
-1082self.resumable_uri=resp["location"]
-1083else:
-1084self._in_error_state=True
-1085raiseHttpError(resp,content,uri=self.uri)
-1086
-1087return(
-1088MediaUploadProgress(self.resumable_progress,self.resumable.size()),
-1089None,
-1090)
-
1106"""Returns an HttpRequest populated with info from a JSON object."""
-1107d=json.loads(s)
-1108ifd["resumable"]isnotNone:
-1109d["resumable"]=MediaUpload.new_from_json(d["resumable"])
-1110returnHttpRequest(
-1111http,
-1112postproc,
-1113uri=d["uri"],
-1114method=d["method"],
-1115body=d["body"],
-1116headers=d["headers"],
-1117methodId=d["methodId"],
-1118resumable=d["resumable"],
-1119)
-
1127"""Batches multiple HttpRequest objects into a single HTTP request.
-1128
-1129 Example:
-1130 from googleapiclient.http import BatchHttpRequest
-1131
-1132 def list_animals(request_id, response, exception):
-1133 \"\"\"Do something with the animals list response.\"\"\"
-1134 if exception is not None:
-1135 # Do something with the exception.
-1136 pass
-1137 else:
-1138 # Do something with the response.
-1139 pass
-1140
-1141 def list_farmers(request_id, response, exception):
-1142 \"\"\"Do something with the farmers list response.\"\"\"
-1143 if exception is not None:
-1144 # Do something with the exception.
-1145 pass
-1146 else:
-1147 # Do something with the response.
-1148 pass
-1149
-1150 service = build('farm', 'v2')
-1151
-1152 batch = BatchHttpRequest()
-1153
-1154 batch.add(service.animals().list(), list_animals)
-1155 batch.add(service.farmers().list(), list_farmers)
-1156 batch.execute(http=http)
-1157 """
-1158
-1159@util.positional(1)
-
1161"""Constructor for a BatchHttpRequest.
-1162
-1163 Args:
-1164 callback: callable, A callback to be called for each response, of the
-1165 form callback(id, response, exception). The first parameter is the
-1166 request id, and the second is the deserialized response object. The
-1167 third is an googleapiclient.errors.HttpError exception object if an HTTP error
-1168 occurred while processing the request, or None if no error occurred.
-1169 batch_uri: string, URI to send batch requests to.
-1170 """
-1171ifbatch_uriisNone:
-1172batch_uri=_LEGACY_BATCH_URI
-1173
-1174ifbatch_uri==_LEGACY_BATCH_URI:
-1175LOGGER.warning(
-1176"You have constructed a BatchHttpRequest using the legacy batch "
-1177"endpoint %s. This endpoint will be turned down on August 12, 2020. "
-1178"Please provide the API-specific endpoint or use "
-1179"service.new_batch_http_request(). For more details see "
-1180"https://developers.googleblog.com/2018/03/discontinuing-support-for-json-rpc-and.html"
-1181"and https://developers.google.com/api-client-library/python/guide/batch.",
-1182_LEGACY_BATCH_URI,
-1183)
-1184self._batch_uri=batch_uri
-1185
-1186# Global callback to be called for each individual response in the batch.
-1187self._callback=callback
-1188
-1189# A map from id to request.
-1190self._requests={}
-1191
-1192# A map from id to callback.
-1193self._callbacks={}
-1194
-1195# List of request ids, in the order in which they were added.
-1196self._order=[]
-1197
-1198# The last auto generated id.
-1199self._last_auto_id=0
-1200
-1201# Unique ID on which to base the Content-ID headers.
-1202self._base_id=None
-1203
-1204# A map from request id to (httplib2.Response, content) response pairs
-1205self._responses={}
-1206
-1207# A map of id(Credentials) that have been refreshed.
-1208self._refreshed_credentials={}
-
1211"""Refresh the credentials and apply to the request.
-1212
-1213 Args:
-1214 request: HttpRequest, the request.
-1215 http: httplib2.Http, the global http object for the batch.
-1216 """
-1217# For the credentials to refresh, but only once per refresh_token
-1218# If there is no http per the request then refresh the http passed in
-1219# via execute()
-1220creds=None
-1221request_credentials=False
-1222
-1223ifrequest.httpisnotNone:
-1224creds=_auth.get_credentials_from_http(request.http)
-1225request_credentials=True
-1226
-1227ifcredsisNoneandhttpisnotNone:
-1228creds=_auth.get_credentials_from_http(http)
-1229
-1230ifcredsisnotNone:
-1231ifid(creds)notinself._refreshed_credentials:
-1232_auth.refresh_credentials(creds)
-1233self._refreshed_credentials[id(creds)]=1
-1234
-1235# Only apply the credentials if we are using the http object passed in,
-1236# otherwise apply() will get called during _serialize_request().
-1237ifrequest.httpisNoneornotrequest_credentials:
-1238_auth.apply_credentials(creds,request.headers)
-
1241"""Convert an id to a Content-ID header value.
-1242
-1243 Args:
-1244 id_: string, identifier of individual request.
-1245
-1246 Returns:
-1247 A Content-ID header with the id_ encoded into it. A UUID is prepended to
-1248 the value because Content-ID headers are supposed to be universally
-1249 unique.
-1250 """
-1251ifself._base_idisNone:
-1252self._base_id=uuid.uuid4()
-1253
-1254# NB: we intentionally leave whitespace between base/id and '+', so RFC2822
-1255# line folding works properly on Python 3; see
-1256# https://github.com/googleapis/google-api-python-client/issues/164
-1257return"<%s + %s>"%(self._base_id,quote(id_))
-
1260"""Convert a Content-ID header value to an id.
-1261
-1262 Presumes the Content-ID header conforms to the format that _id_to_header()
-1263 returns.
-1264
-1265 Args:
-1266 header: string, Content-ID header value.
-1267
-1268 Returns:
-1269 The extracted id value.
-1270
-1271 Raises:
-1272 BatchError if the header is not in the expected format.
-1273 """
-1274ifheader[0]!="<"orheader[-1]!=">":
-1275raiseBatchError("Invalid value for Content-ID: %s"%header)
-1276if"+"notinheader:
-1277raiseBatchError("Invalid value for Content-ID: %s"%header)
-1278base,id_=header[1:-1].split(" + ",1)
-1279
-1280returnunquote(id_)
-
1331"""Convert string into httplib2 response and content.
-1332
-1333 Args:
-1334 payload: string, headers and body as a string.
-1335
-1336 Returns:
-1337 A pair (resp, content), such as would be returned from httplib2.request.
-1338 """
-1339# Strip off the status line
-1340status_line,payload=payload.split("\n",1)
-1341protocol,status,reason=status_line.split(" ",2)
-1342
-1343# Parse the rest of the response
-1344parser=FeedParser()
-1345parser.feed(payload)
-1346msg=parser.close()
-1347msg["status"]=status
-1348
-1349# Create httplib2.Response from the parsed headers.
-1350resp=httplib2.Response(msg)
-1351resp.reason=reason
-1352resp.version=int(protocol.split("/",1)[1].replace(".",""))
-1353
-1354content=payload.split("\r\n\r\n",1)[1]
-1355
-1356returnresp,content
-
1359"""Create a new id.
-1360
-1361 Auto incrementing number that avoids conflicts with ids already used.
-1362
-1363 Returns:
-1364 string, a new unique id.
-1365 """
-1366self._last_auto_id+=1
-1367whilestr(self._last_auto_id)inself._requests:
-1368self._last_auto_id+=1
-1369returnstr(self._last_auto_id)
-
1373"""Add a new request.
-1374
-1375 Every callback added will be paired with a unique id, the request_id. That
-1376 unique id will be passed back to the callback when the response comes back
-1377 from the server. The default behavior is to have the library generate it's
-1378 own unique id. If the caller passes in a request_id then they must ensure
-1379 uniqueness for each request_id, and if they are not an exception is
-1380 raised. Callers should either supply all request_ids or never supply a
-1381 request id, to avoid such an error.
-1382
-1383 Args:
-1384 request: HttpRequest, Request to add to the batch.
-1385 callback: callable, A callback to be called for this response, of the
-1386 form callback(id, response, exception). The first parameter is the
-1387 request id, and the second is the deserialized response object. The
-1388 third is an googleapiclient.errors.HttpError exception object if an HTTP error
-1389 occurred while processing the request, or None if no errors occurred.
-1390 request_id: string, A unique id for the request. The id will be passed
-1391 to the callback with the response.
-1392
-1393 Returns:
-1394 None
-1395
-1396 Raises:
-1397 BatchError if a media request is added to a batch.
-1398 KeyError is the request_id is not unique.
-1399 """
-1400
-1401iflen(self._order)>=MAX_BATCH_LIMIT:
-1402raiseBatchError(
-1403"Exceeded the maximum calls(%d) in a single batch request."
-1404%MAX_BATCH_LIMIT
-1405)
-1406ifrequest_idisNone:
-1407request_id=self._new_id()
-1408ifrequest.resumableisnotNone:
-1409raiseBatchError("Media requests cannot be used in a batch request.")
-1410ifrequest_idinself._requests:
-1411raiseKeyError("A request with this ID already exists: %s"%request_id)
-1412self._requests[request_id]=request
-1413self._callbacks[request_id]=callback
-1414self._order.append(request_id)
-
1417"""Serialize batch request, send to server, process response.
-1418
-1419 Args:
-1420 http: httplib2.Http, an http object to be used to make the request with.
-1421 order: list, list of request ids in the order they were added to the
-1422 batch.
-1423 requests: list, list of request objects to send.
-1424
-1425 Raises:
-1426 httplib2.HttpLib2Error if a transport error has occurred.
-1427 googleapiclient.errors.BatchError if the response is the wrong format.
-1428 """
-1429message=MIMEMultipart("mixed")
-1430# Message should not write out it's own headers.
-1431setattr(message,"_write_headers",lambdaself:None)
-1432
-1433# Add all the individual requests.
-1434forrequest_idinorder:
-1435request=requests[request_id]
-1436
-1437msg=MIMENonMultipart("application","http")
-1438msg["Content-Transfer-Encoding"]="binary"
-1439msg["Content-ID"]=self._id_to_header(request_id)
-1440
-1441body=self._serialize_request(request)
-1442msg.set_payload(body)
-1443message.attach(msg)
-1444
-1445# encode the body: note that we can't use `as_string`, because
-1446# it plays games with `From ` lines.
-1447fp=StringIO()
-1448g=Generator(fp,mangle_from_=False)
-1449g.flatten(message,unixfrom=False)
-1450body=fp.getvalue()
-1451
-1452headers={}
-1453headers["content-type"]=(
-1454"multipart/mixed; "'boundary="%s"'
-1455)%message.get_boundary()
-1456
-1457resp,content=http.request(
-1458self._batch_uri,method="POST",body=body,headers=headers
-1459)
-1460
-1461ifresp.status>=300:
-1462raiseHttpError(resp,content,uri=self._batch_uri)
-1463
-1464# Prepend with a content-type header so FeedParser can handle it.
-1465header="content-type: %s\r\n\r\n"%resp["content-type"]
-1466# PY3's FeedParser only accepts unicode. So we should decode content
-1467# here, and encode each payload again.
-1468ifsix.PY3:
-1469content=content.decode("utf-8")
-1470for_parser=header+content
-1471
-1472parser=FeedParser()
-1473parser.feed(for_parser)
-1474mime_response=parser.close()
-1475
-1476ifnotmime_response.is_multipart():
-1477raiseBatchError(
-1478"Response not in multipart/mixed format.",resp=resp,content=content
-1479)
-1480
-1481forpartinmime_response.get_payload():
-1482request_id=self._header_to_id(part["Content-ID"])
-1483response,content=self._deserialize_response(part.get_payload())
-1484# We encode content here to emulate normal http response.
-1485ifisinstance(content,six.text_type):
-1486content=content.encode("utf-8")
-1487self._responses[request_id]=(response,content)
-
1491"""Execute all the requests as a single batched HTTP request.
-1492
-1493 Args:
-1494 http: httplib2.Http, an http object to be used in place of the one the
-1495 HttpRequest request object was constructed with. If one isn't supplied
-1496 then use a http object from the requests in this batch.
-1497
-1498 Returns:
-1499 None
-1500
-1501 Raises:
-1502 httplib2.HttpLib2Error if a transport error has occurred.
-1503 googleapiclient.errors.BatchError if the response is the wrong format.
-1504 """
-1505# If we have no requests return
-1506iflen(self._order)==0:
-1507returnNone
-1508
-1509# If http is not supplied use the first valid one given in the requests.
-1510ifhttpisNone:
-1511forrequest_idinself._order:
-1512request=self._requests[request_id]
-1513ifrequestisnotNone:
-1514http=request.http
-1515break
-1516
-1517ifhttpisNone:
-1518raiseValueError("Missing a valid http object.")
-1519
-1520# Special case for OAuth2Credentials-style objects which have not yet been
-1521# refreshed with an initial access_token.
-1522creds=_auth.get_credentials_from_http(http)
-1523ifcredsisnotNone:
-1524ifnot_auth.is_valid(creds):
-1525LOGGER.info("Attempting refresh to obtain initial access_token")
-1526_auth.refresh_credentials(creds)
-1527
-1528self._execute(http,self._order,self._requests)
-1529
-1530# Loop over all the requests and check for 401s. For each 401 request the
-1531# credentials should be refreshed and then sent again in a separate batch.
-1532redo_requests={}
-1533redo_order=[]
-1534
-1535forrequest_idinself._order:
-1536resp,content=self._responses[request_id]
-1537ifresp["status"]=="401":
-1538redo_order.append(request_id)
-1539request=self._requests[request_id]
-1540self._refresh_and_apply_credentials(request,http)
-1541redo_requests[request_id]=request
-1542
-1543ifredo_requests:
-1544self._execute(http,redo_order,redo_requests)
-1545
-1546# Now process all callbacks that are erroring, and raise an exception for
-1547# ones that return a non-2xx response? Or add extra parameter to callback
-1548# that contains an HttpError?
-1549
-1550forrequest_idinself._order:
-1551resp,content=self._responses[request_id]
-1552
-1553request=self._requests[request_id]
-1554callback=self._callbacks[request_id]
-1555
-1556response=None
-1557exception=None
-1558try:
-1559ifresp.status>=300:
-1560raiseHttpError(resp,content,uri=request.uri)
-1561response=request.postproc(resp,content)
-1562exceptHttpErrorase:
-1563exception=e
-1564
-1565ifcallbackisnotNone:
-1566callback(request_id,response,exception)
-1567ifself._callbackisnotNone:
-1568self._callback(request_id,response,exception)
-
1578"""Constructor for HttpRequestMock
-1579
-1580 Args:
-1581 resp: httplib2.Response, the response to emulate coming from the request
-1582 content: string, the response body
-1583 postproc: callable, the post processing function usually supplied by
-1584 the model class. See model.JsonModel.response() as an example.
-1585 """
-1586self.resp=resp
-1587self.content=content
-1588self.postproc=postproc
-1589ifrespisNone:
-1590self.resp=httplib2.Response({"status":200,"reason":"OK"})
-1591if"reason"inself.resp:
-1592self.resp.reason=self.resp["reason"]
-
1595"""Execute the request.
-1596
-1597 Same behavior as HttpRequest.execute(), but the response is
-1598 mocked and not really from an HTTP request/response.
-1599 """
-1600returnself.postproc(self.resp,self.content)
-
1604"""A simple mock of HttpRequest
-1605
-1606 Pass in a dictionary to the constructor that maps request methodIds to
-1607 tuples of (httplib2.Response, content, opt_expected_body) that should be
-1608 returned when that method is called. None may also be passed in for the
-1609 httplib2.Response, in which case a 200 OK response will be generated.
-1610 If an opt_expected_body (str or dict) is provided, it will be compared to
-1611 the body and UnexpectedBodyError will be raised on inequality.
-1612
-1613 Example:
-1614 response = '{"data": {"id": "tag:google.c...'
-1615 requestBuilder = RequestMockBuilder(
-1616 {
-1617 'plus.activities.get': (None, response),
-1618 }
-1619 )
-1620 googleapiclient.discovery.build("plus", "v1", requestBuilder=requestBuilder)
-1621
-1622 Methods that you do not supply a response for will return a
-1623 200 OK with an empty string as the response content or raise an excpetion
-1624 if check_unexpected is set to True. The methodId is taken from the rpcName
-1625 in the discovery document.
-1626
-1627 For more details see the project wiki.
-1628 """
-1629
-
1631"""Constructor for RequestMockBuilder
-1632
-1633 The constructed object should be a callable object
-1634 that can replace the class HttpResponse.
-1635
-1636 responses - A dictionary that maps methodIds into tuples
-1637 of (httplib2.Response, content). The methodId
-1638 comes from the 'rpcName' field in the discovery
-1639 document.
-1640 check_unexpected - A boolean setting whether or not UnexpectedMethodError
-1641 should be raised on unsupplied method.
-1642 """
-1643self.responses=responses
-1644self.check_unexpected=check_unexpected
-
1657"""Implements the callable interface that discovery.build() expects
-1658 of requestBuilder, which is to build an object compatible with
-1659 HttpRequest.execute(). See that method for the description of the
-1660 parameters and the expected response.
-1661 """
-1662ifmethodIdinself.responses:
-1663response=self.responses[methodId]
-1664resp,content=response[:2]
-1665iflen(response)>2:
-1666# Test the body against the supplied expected_body.
-1667expected_body=response[2]
-1668ifbool(expected_body)!=bool(body):
-1669# Not expecting a body and provided one
-1670# or expecting a body and not provided one.
-1671raiseUnexpectedBodyError(expected_body,body)
-1672ifisinstance(expected_body,str):
-1673expected_body=json.loads(expected_body)
-1674body=json.loads(body)
-1675ifbody!=expected_body:
-1676raiseUnexpectedBodyError(expected_body,body)
-1677returnHttpRequestMock(resp,content,postproc)
-1678elifself.check_unexpected:
-1679raiseUnexpectedMethodError(methodId=methodId)
-1680else:
-1681model=JsonModel(False)
-1682returnHttpRequestMock(None,"{}",model.response)
-
1727"""Mock of httplib2.Http
-1728
-1729 Mocks a sequence of calls to request returning different responses for each
-1730 call. Create an instance initialized with the desired response headers
-1731 and content and then use as if an httplib2.Http instance.
-1732
-1733 http = HttpMockSequence([
-1734 ({'status': '401'}, ''),
-1735 ({'status': '200'}, '{"access_token":"1/3w","expires_in":3600}'),
-1736 ({'status': '200'}, 'echo_request_headers'),
-1737 ])
-1738 resp, content = http.request("http://examples.com")
-1739
-1740 There are special values you can pass in for content to trigger
-1741 behavours that are helpful in testing.
-1742
-1743 'echo_request_headers' means return the request headers in the response body
-1744 'echo_request_headers_as_json' means return the request headers in
-1745 the response body
-1746 'echo_request_body' means return the request body in the response body
-1747 'echo_request_uri' means return the request uri in the response body
-1748 """
-1749
-
1768# Remember the request so after the fact this mock can be examined
-1769self.request_sequence.append((uri,method,body,headers))
-1770resp,content=self._iterable.pop(0)
-1771content=six.ensure_binary(content)
-1772
-1773ifcontent==b"echo_request_headers":
-1774content=headers
-1775elifcontent==b"echo_request_headers_as_json":
-1776content=json.dumps(headers)
-1777elifcontent==b"echo_request_body":
-1778ifhasattr(body,"read"):
-1779content=body.read()
-1780else:
-1781content=body
-1782elifcontent==b"echo_request_uri":
-1783content=uri
-1784ifisinstance(content,six.text_type):
-1785content=content.encode("utf-8")
-1786returnhttplib2.Response(resp),content
-
1790"""Set the user-agent on every request.
-1791
-1792 Args:
-1793 http - An instance of httplib2.Http
-1794 or something that acts like it.
-1795 user_agent: string, the value for the user-agent header.
-1796
-1797 Returns:
-1798 A modified instance of http that was passed in.
-1799
-1800 Example:
-1801
-1802 h = httplib2.Http()
-1803 h = set_user_agent(h, "my-app-name/6.0")
-1804
-1805 Most of the time the user-agent will be set doing auth, this is for the rare
-1806 cases where you are accessing an unauthenticated endpoint.
-1807 """
-1808request_orig=http.request
-1809
-1810# The closure that will replace 'httplib2.Http.request'.
-1811defnew_request(
-1812uri,
-1813method="GET",
-1814body=None,
-1815headers=None,
-1816redirections=httplib2.DEFAULT_MAX_REDIRECTS,
-1817connection_type=None,
-1818):
-1819"""Modify the request headers to add the user-agent."""
-1820ifheadersisNone:
-1821headers={}
-1822if"user-agent"inheaders:
-1823headers["user-agent"]=user_agent+" "+headers["user-agent"]
-1824else:
-1825headers["user-agent"]=user_agent
-1826resp,content=request_orig(
-1827uri,
-1828method=method,
-1829body=body,
-1830headers=headers,
-1831redirections=redirections,
-1832connection_type=connection_type,
-1833)
-1834returnresp,content
-
1841"""Tunnel PATCH requests over POST.
-1842 Args:
-1843 http - An instance of httplib2.Http
-1844 or something that acts like it.
-1845
-1846 Returns:
-1847 A modified instance of http that was passed in.
-1848
-1849 Example:
-1850
-1851 h = httplib2.Http()
-1852 h = tunnel_patch(h, "my-app-name/6.0")
-1853
-1854 Useful if you are running on a platform that doesn't support PATCH.
-1855 Apply this last if you are using OAuth 1.0, as changing the method
-1856 will result in a different signature.
-1857 """
-1858request_orig=http.request
-1859
-1860# The closure that will replace 'httplib2.Http.request'.
-1861defnew_request(
-1862uri,
-1863method="GET",
-1864body=None,
-1865headers=None,
-1866redirections=httplib2.DEFAULT_MAX_REDIRECTS,
-1867connection_type=None,
-1868):
-1869"""Modify the request headers to add the user-agent."""
-1870ifheadersisNone:
-1871headers={}
-1872ifmethod=="PATCH":
-1873if"oauth_token"inheaders.get("authorization",""):
-1874LOGGER.warning(
-1875"OAuth 1.0 request made with Credentials after tunnel_patch."
-1876)
-1877headers["x-http-method-override"]="PATCH"
-1878method="POST"
-1879resp,content=request_orig(
-1880uri,
-1881method=method,
-1882body=body,
-1883headers=headers,
-1884redirections=redirections,
-1885connection_type=connection_type,
-1886)
-1887returnresp,content
-
1894"""Builds httplib2.Http object
-1895
-1896 Returns:
-1897 A httplib2.Http object, which is used to make http requests, and which has timeout set by default.
-1898 To override default timeout call
-1899
-1900 socket.setdefaulttimeout(timeout_in_sec)
-1901
-1902 before interacting with this method.
-1903 """
-1904ifsocket.getdefaulttimeout()isnotNone:
-1905http_timeout=socket.getdefaulttimeout()
-1906else:
-1907http_timeout=DEFAULT_HTTP_TIMEOUT_SEC
-1908http=httplib2.Http(timeout=http_timeout)
-1909# 308's are used by several Google APIs (Drive, YouTube)
-1910# for Resumable Uploads rather than Permanent Redirects.
-1911# This asks httplib2 to exclude 308s from the status codes
-1912# it treats as redirects
-1913try:
-1914http.redirect_codes=http.redirect_codes-{308}
-1915exceptAttributeError:
-1916# Apache Beam tests depend on this library and cannot
-1917# currently upgrade their httplib2 version
-1918# http.redirect_codes does not exist in previous versions
-1919# of httplib2, so pass
-1920pass
-1921
-1922returnhttp
-
-Batches multiple HttpRequest objects into a single HTTP request.
-
-Example:
- from googleapiclient.http import BatchHttpRequest
-
- def list_animals(request_id, response, exception):
- """Do something with the animals list response."""
- if exception is not None:
- # Do something with the exception.
- pass
- else:
- # Do something with the response.
- pass
-
- def list_farmers(request_id, response, exception):
- """Do something with the farmers list response."""
- if exception is not None:
- # Do something with the exception.
- pass
- else:
- # Do something with the response.
- pass
-
- service = build('farm', 'v2')
-
- batch = BatchHttpRequest()
-
- batch.add(service.animals().list(), list_animals)
- batch.add(service.farmers().list(), list_farmers)
- batch.execute(http=http)
-
-
-Constructor for a BatchHttpRequest.
-
-Args:
- callback: callable, A callback to be called for each response, of the
- form callback(id, response, exception). The first parameter is the
- request id, and the second is the deserialized response object. The
- third is an googleapiclient.errors.HttpError exception object if an HTTP error
- occurred while processing the request, or None if no error occurred.
- batch_uri: string, URI to send batch requests to.
-
-
-Refresh the credentials and apply to the request.
-
-Args:
- request: HttpRequest, the request.
- http: httplib2.Http, the global http object for the batch.
-
-
-Convert an id to a Content-ID header value.
-
-Args:
- id_: string, identifier of individual request.
-
-Returns:
- A Content-ID header with the id_ encoded into it. A UUID is prepended to
- the value because Content-ID headers are supposed to be universally
- unique.
-
-
-Convert a Content-ID header value to an id.
-
-Presumes the Content-ID header conforms to the format that _id_to_header()
-returns.
-
-Args:
- header: string, Content-ID header value.
-
-Returns:
- The extracted id value.
-
-Raises:
- BatchError if the header is not in the expected format.
-
-
-Convert an HttpRequest object into a string.
-
-Args:
- request: HttpRequest, the request to serialize.
-
-Returns:
- The request as a string in application/http format.
-
-
-Convert string into httplib2 response and content.
-
-Args:
- payload: string, headers and body as a string.
-
-Returns:
- A pair (resp, content), such as would be returned from httplib2.request.
-
-
-Add a new request.
-
-Every callback added will be paired with a unique id, the request_id. That
-unique id will be passed back to the callback when the response comes back
-from the server. The default behavior is to have the library generate it's
-own unique id. If the caller passes in a request_id then they must ensure
-uniqueness for each request_id, and if they are not an exception is
-raised. Callers should either supply all request_ids or never supply a
-request id, to avoid such an error.
-
-Args:
- request: HttpRequest, Request to add to the batch.
- callback: callable, A callback to be called for this response, of the
- form callback(id, response, exception). The first parameter is the
- request id, and the second is the deserialized response object. The
- third is an googleapiclient.errors.HttpError exception object if an HTTP error
- occurred while processing the request, or None if no errors occurred.
- request_id: string, A unique id for the request. The id will be passed
- to the callback with the response.
-
-Returns:
- None
-
-Raises:
- BatchError if a media request is added to a batch.
- KeyError is the request_id is not unique.
-
-
-Serialize batch request, send to server, process response.
-
-Args:
- http: httplib2.Http, an http object to be used to make the request with.
- order: list, list of request ids in the order they were added to the
- batch.
- requests: list, list of request objects to send.
-
-Raises:
- httplib2.HttpLib2Error if a transport error has occurred.
- googleapiclient.errors.BatchError if the response is the wrong format.
-
-
-Execute all the requests as a single batched HTTP request.
-
-Args:
- http: httplib2.Http, an http object to be used in place of the one the
- HttpRequest request object was constructed with. If one isn't supplied
- then use a http object from the requests in this batch.
-
-Returns:
- None
-
-Raises:
- httplib2.HttpLib2Error if a transport error has occurred.
- googleapiclient.errors.BatchError if the response is the wrong format.
-
-
-Mock of httplib2.Http
-
-Mocks a sequence of calls to request returning different responses for each
-call. Create an instance initialized with the desired response headers
-and content and then use as if an httplib2.Http instance.
-
- http = HttpMockSequence([
- ({'status': '401'}, ''),
- ({'status': '200'}, '{"access_token":"1/3w","expires_in":3600}'),
- ({'status': '200'}, 'echo_request_headers'),
- ])
- resp, content = http.request("http://examples.com")
-
-There are special values you can pass in for content to trigger
-behavours that are helpful in testing.
-
-'echo_request_headers' means return the request headers in the response body
-'echo_request_headers_as_json' means return the request headers in
- the response body
-'echo_request_body' means return the request body in the response body
-'echo_request_uri' means return the request uri in the response body
-
-
-Constructor for an HttpRequest.
-
-Args:
- http: httplib2.Http, the transport object to use to make a request
- postproc: callable, called on the HTTP response and content to transform
- it into a data object before returning, or raising an exception
- on an error.
- uri: string, the absolute URI to send the request to
- method: string, the HTTP method to use
- body: string, the request body of the HTTP request,
- headers: dict, the HTTP request headers
- methodId: string, a unique identifier for the API method being called.
- resumable: MediaUpload, None if this is not a resumbale request.
-
-
-Execute the request.
-
-Args:
- http: httplib2.Http, an http object to be used in place of the
- one the HttpRequest request object was constructed with.
- num_retries: Integer, number of times to retry with randomized
- exponential backoff. If all retries fail, the raised HttpError
- represents the last request. If zero (default), we attempt the
- request only once.
-
-Returns:
- A deserialized object model of the response body as determined
- by the postproc.
-
-Raises:
- googleapiclient.errors.HttpError if the response was not a 2xx.
- httplib2.HttpLib2Error if a transport error has occurred.
-
-
-add_response_headers_callback
-
-Args:
- cb: Callback to be called on receiving the response headers, of signature:
-
- def cb(resp):
- # Where resp is an instance of httplib2.Response
-
-
-Execute the next step of a resumable upload.
-
-Can only be used if the method being executed supports media uploads and
-the MediaUpload object passed in was flagged as using resumable upload.
-
-Example:
-
- media = MediaFileUpload('cow.png', mimetype='image/png',
- chunksize=1000, resumable=True)
- request = farm.animals().insert(
- id='cow',
- name='cow.png',
- media_body=media)
-
- response = None
- while response is None:
- status, response = request.next_chunk()
- if status:
- print "Upload %d%% complete." % int(status.progress() * 100)
-
-
-Args:
- http: httplib2.Http, an http object to be used in place of the
- one the HttpRequest request object was constructed with.
- num_retries: Integer, number of times to retry with randomized
- exponential backoff. If all retries fail, the raised HttpError
- represents the last request. If zero (default), we attempt the
- request only once.
-
-Returns:
- (status, body): (ResumableMediaStatus, object)
- The body will be None until the resumable media is fully uploaded.
-
-Raises:
- googleapiclient.errors.HttpError if the response was not a 2xx.
- httplib2.HttpLib2Error if a transport error has occurred.
-
-
-Process the response from a single chunk upload.
-
-Args:
- resp: httplib2.Response, the response object.
- content: string, the content of the response.
-
-Returns:
- (status, body): (ResumableMediaStatus, object)
- The body will be None until the resumable media is fully uploaded.
-
-Raises:
- googleapiclient.errors.HttpError if the response was not a 2xx or a 308.
-
-
-Constructor for HttpRequestMock
-
-Args:
- resp: httplib2.Response, the response to emulate coming from the request
- content: string, the response body
- postproc: callable, the post processing function usually supplied by
- the model class. See model.JsonModel.response() as an example.
-
-
-Percent of download completed, as a float.
-
-Returns:
- the percentage complete as a float, returning 0.0 if the total size of
- the download is unknown.
-
-
-A MediaUpload for a file.
-
-Construct a MediaFileUpload and pass as the media_body parameter of the
-method. For example, if we had a service that allowed uploading images:
-
- media = MediaFileUpload('cow.png', mimetype='image/png',
- chunksize=1024*1024, resumable=True)
- farm.animals().insert(
- id='cow',
- name='cow.png',
- media_body=media).execute()
-
-Depending on the platform you are working on, you may pass -1 as the
-chunksize, which indicates that the entire file should be uploaded in a single
-request. If the underlying platform supports streams, such as Python 2.6 or
-later, then this can be very efficient as it avoids multiple connections, and
-also avoids loading the entire file into memory before sending it. Note that
-Google App Engine has a 5MB limit on request size, so you should never set
-your chunksize larger than 5MB, or to -1.
-
-
-Constructor.
-
-Args:
- filename: string, Name of the file.
- mimetype: string, Mime-type of the file. If None then a mime-type will be
- guessed from the file extension.
- chunksize: int, File will be uploaded in chunks of this many bytes. Only
- used if resumable=True. Pass in a value of -1 if the file is to be
- uploaded in a single chunk. Note that Google App Engine has a 5MB limit
- on request size, so you should never set your chunksize larger than 5MB,
- or to -1.
- resumable: bool, True if this is a resumable upload. False means upload
- in a single request.
-
-
-Creating a JSON representation of an instance of MediaFileUpload.
-
-Returns:
- string, a JSON representation of this instance, suitable to pass to
- from_json().
-
-
-Create a new MediaInMemoryUpload.
-
-DEPRECATED: Use MediaIoBaseUpload with either io.TextIOBase or StringIO for
-the stream.
-
-Args:
- body: string, Bytes of body content.
- mimetype: string, Mime-type of the file or default of
- 'application/octet-stream'.
- chunksize: int, File will be uploaded in chunks of this many bytes. Only
- used if resumable=True.
- resumable: bool, True if this is a resumable upload. False means upload
- in a single request.
-
-
-
-Constructor.
-
-Args:
- fd: io.Base or file object, The stream in which to write the downloaded
- bytes.
- request: googleapiclient.http.HttpRequest, the media request to perform in
- chunks.
- chunksize: int, File will be downloaded in chunks of this many bytes.
-
-
-Get the next chunk of the download.
-
-Args:
- num_retries: Integer, number of times to retry with randomized
- exponential backoff. If all retries fail, the raised HttpError
- represents the last request. If zero (default), we attempt the
- request only once.
-
-Returns:
- (status, done): (MediaDownloadProgress, boolean)
- The value of 'done' will be True when the media has been fully
- downloaded or the total size of the media is unknown.
-
-Raises:
- googleapiclient.errors.HttpError if the response was not a 2xx.
- httplib2.HttpLib2Error if a transport error has occurred.
-
-
-A MediaUpload for a io.Base objects.
-
-Note that the Python file object is compatible with io.Base and can be used
-with this class also.
-
- fh = BytesIO('...Some data to upload...')
- media = MediaIoBaseUpload(fh, mimetype='image/png',
- chunksize=1024*1024, resumable=True)
- farm.animals().insert(
- id='cow',
- name='cow.png',
- media_body=media).execute()
-
-Depending on the platform you are working on, you may pass -1 as the
-chunksize, which indicates that the entire file should be uploaded in a single
-request. If the underlying platform supports streams, such as Python 2.6 or
-later, then this can be very efficient as it avoids multiple connections, and
-also avoids loading the entire file into memory before sending it. Note that
-Google App Engine has a 5MB limit on request size, so you should never set
-your chunksize larger than 5MB, or to -1.
-
-
-Constructor.
-
-Args:
- fd: io.Base or file object, The source of the bytes to upload. MUST be
- opened in blocking mode, do not use streams opened in non-blocking mode.
- The given stream must be seekable, that is, it must be able to call
- seek() on fd.
- mimetype: string, Mime-type of the file.
- chunksize: int, File will be uploaded in chunks of this many bytes. Only
- used if resumable=True. Pass in a value of -1 if the file is to be
- uploaded as a single chunk. Note that Google App Engine has a 5MB limit
- on request size, so you should never set your chunksize larger than 5MB,
- or to -1.
- resumable: bool, True if this is a resumable upload. False means upload
- in a single request.
-
-
-Get bytes from the media.
-
-Args:
- begin: int, offset from beginning of file.
- length: int, number of bytes to read, starting at begin.
-
-Returns:
- A string of bytes read. May be shorted than length if EOF was reached
- first.
-
-
-Does the underlying upload support a streaming interface.
-
-Streaming means it is an io.IOBase subclass that supports seek, i.e.
-seekable() returns True.
-
-Returns:
- True if the call to stream() will return an instance of a seekable io.Base
- subclass.
-
-
-A stream interface to the data being uploaded.
-
-Returns:
- The returned value is an io.IOBase subclass that supports seek, i.e.
- seekable() returns True.
-
-
Base class that defines the interface of MediaUpload subclasses.
-
Note that subclasses of MediaUpload may allow you to control the
- chunksize when uploading a media object. It is important to keep the size
- of the chunk as large as possible to keep the upload efficient. Other
- factors may influence the size of the chunk you use, particularly if you
- are working in an environment where individual HTTP requests may have a
- hardcoded time limit, such as under certain classes of requests under
- Google App Engine.
-
Streams are io.Base compatible objects that support seek(). Some
- MediaUpload subclasses support using streams directly to upload data.
- Support for streaming may be indicated by a MediaUpload sub-class and if
- appropriate for a platform that stream will be used for uploading the
- media object. The support for streaming is indicated by has_stream()
- returning True. The stream() method should return an io.Base object that
- supports seek(). On platforms where the underlying httplib module
- supports streaming, for example Python 2.6 and later, the stream will be
- passed into the http library which will result in less memory being used
- and possibly faster uploads.
-
If you need to upload media that can't be uploaded using any of the
- existing MediaUpload sub-class then you can sub-class MediaUpload for
- your particular needs.
-Get bytes from the media.
-
-Args:
- begin: int, offset from beginning of file.
- length: int, number of bytes to read, starting at begin.
-
-Returns:
- A string of bytes read. May be shorter than length if EOF was reached
- first.
-
-
-Does the underlying upload support a streaming interface.
-
-Streaming means it is an io.IOBase subclass that supports seek, i.e.
-seekable() returns True.
-
-Returns:
- True if the call to stream() will return an instance of a seekable io.Base
- subclass.
-
-
-A stream interface to the data being uploaded.
-
-Returns:
- The returned value is an io.IOBase subclass that supports seek, i.e.
- seekable() returns True.
-
-
-Utility function for creating a JSON representation of a MediaUpload.
-
-Args:
- strip: array, An array of names of members to not include in the JSON.
-
-Returns:
- string, a JSON representation of this instance, suitable to pass to
- from_json().
-
-
-Create a JSON representation of an instance of MediaUpload.
-
-Returns:
- string, a JSON representation of this instance, suitable to pass to
- from_json().
-
-
-Utility class method to instantiate a MediaUpload subclass from a JSON
-representation produced by to_json().
-
-Args:
- s: string, JSON from to_json().
-
-Returns:
- An instance of the subclass of MediaUpload that was serialized with
- to_json().
-
-
-Constructor.
-
-Args:
- resumable_progress: int, bytes sent so far.
- total_size: int, total bytes in complete upload, or None if the total
- upload size isn't known ahead of time.
-
-
-Percent of upload completed, as a float.
-
-Returns:
- the percentage complete as a float, returning 0.0 if the total size of
- the upload is unknown.
-
-
-A simple mock of HttpRequest
-
-Pass in a dictionary to the constructor that maps request methodIds to
-tuples of (httplib2.Response, content, opt_expected_body) that should be
-returned when that method is called. None may also be passed in for the
-httplib2.Response, in which case a 200 OK response will be generated.
-If an opt_expected_body (str or dict) is provided, it will be compared to
-the body and UnexpectedBodyError will be raised on inequality.
-
-Example:
- response = '{"data": {"id": "tag:google.c...'
- requestBuilder = RequestMockBuilder(
- {
- 'plus.activities.get': (None, response),
- }
- )
- googleapiclient.discovery.build("plus", "v1", requestBuilder=requestBuilder)
-
-Methods that you do not supply a response for will return a
-200 OK with an empty string as the response content or raise an excpetion
-if check_unexpected is set to True. The methodId is taken from the rpcName
-in the discovery document.
-
-For more details see the project wiki.
-
-
__call__(self,
- http,
- postproc,
- uri,
- method='GET',
- body=None,
- headers=None,
- methodId=None,
- resumable=None)
- Implements the callable interface that discovery.build() expects of
- requestBuilder, which is to build an object compatible with
- HttpRequest.execute().
-Constructor for RequestMockBuilder
-
-The constructed object should be a callable object
-that can replace the class HttpResponse.
-
-responses - A dictionary that maps methodIds into tuples
- of (httplib2.Response, content). The methodId
- comes from the 'rpcName' field in the discovery
- document.
-check_unexpected - A boolean setting whether or not UnexpectedMethodError
- should be raised on unsupplied method.
-
-
Implements the callable interface that discovery.build() expects of
- requestBuilder, which is to build an object compatible with
- HttpRequest.execute(). See that method for the description of the
- parameters and the expected response.
Takes a stream and presents a stream that is a slice of the original
- stream. This is used when uploading media in chunks. In later versions of
- Python a stream can be passed to httplib in place of the string of data
- to send. The problem is that httplib just blindly reads to the end of the
- stream. This wrapper presents a virtual stream that only reads to the end
- of the chunk.
-Constructor.
-
-Args:
- stream: (io.Base, file object), the stream to wrap.
- begin: int, the seek position the chunk begins at.
- chunksize: int, the size of the chunk.
-
-
-MIME-Type Parser
-
-This module provides basic functions for handling mime-types. It can handle
-matching mime-types against a list of media-ranges. See section 14.1 of the
-HTTP specification [RFC 2616] for a complete explanation.
-
- http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
-
-Contents:
- - parse_mime_type(): Parses a mime-type into its component parts.
- - parse_media_range(): Media-ranges are mime-types with wild-cards and a 'q'
- quality parameter.
- - quality(): Determines the quality ('q') of a mime-type when
- compared against a list of media-ranges.
- - quality_parsed(): Just like quality() except the second parameter must be
- pre-parsed.
- - best_match(): Choose the mime-type with the highest quality ('q')
- from a list of candidates.
-
-
-Parses a mime-type into its component parts.
-
-Carves up a mime-type and returns a tuple of the (type, subtype, params)
-where 'params' is a dictionary of all the parameters for the media range.
-For example, the media range 'application/xhtml;q=0.5' would get parsed
-into:
-
- ('application', 'xhtml', {'q', '0.5'})
-
-
-
-Parse a media-range into its component parts.
-
-Carves up a media range and returns a tuple of the (type, subtype,
-params) where 'params' is a dictionary of all the parameters for the media
-range. For example, the media range 'application/*;q=0.5' would get parsed
-into:
-
- ('application', '*', {'q', '0.5'})
-
-In addition this function also guarantees that there is a value for 'q'
-in the params dictionary, filling it in with a proper default if
-necessary.
-
-
Find the best match for a mime-type amongst parsed media-ranges.
-
Find the best match for a given mime-type against a list of
- media_ranges that have already been parsed by parse_media_range().
- Returns a tuple of the fitness value and the value of the 'q' quality
- parameter of the best match, or (-1, 0) if no match was found. Just as
- for quality_parsed(), 'parsed_ranges' must be a list of parsed media
- ranges.
Find the best match for a mime-type amongst parsed media-ranges.
-
Find the best match for a given mime-type against a list of
- media_ranges that have already been parsed by parse_media_range().
- Returns the 'q' quality parameter of the best match, 0 if no match was
- found. This function bahaves the same as quality() except that
- 'parsed_ranges' must be a list of parsed media ranges.
Return mime-type with the highest quality ('q') from list of
- candidates.
-
Takes a list of supported mime-types and finds the best match for all
- the media-ranges listed in header. The value of header must be a string
- that conforms to the format of the HTTP Accept: header. The value of
- 'supported' is a list of mime-types. The list of supported mime-types
- should be sorted in order of increasing desirability, in case of a
- situation where there is a tie.
- 1# Copyright 2014 Joe Gregorio
- 2#
- 3# Licensed under the MIT License
- 4
- 5"""MIME-Type Parser
- 6
- 7This module provides basic functions for handling mime-types. It can handle
- 8matching mime-types against a list of media-ranges. See section 14.1 of the
- 9HTTP specification [RFC 2616] for a complete explanation.
- 10
- 11 http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
- 12
- 13Contents:
- 14 - parse_mime_type(): Parses a mime-type into its component parts.
- 15 - parse_media_range(): Media-ranges are mime-types with wild-cards and a 'q'
- 16 quality parameter.
- 17 - quality(): Determines the quality ('q') of a mime-type when
- 18 compared against a list of media-ranges.
- 19 - quality_parsed(): Just like quality() except the second parameter must be
- 20 pre-parsed.
- 21 - best_match(): Choose the mime-type with the highest quality ('q')
- 22 from a list of candidates.
- 23"""
- 24from__future__importabsolute_import
- 25fromfunctoolsimportreduce
- 26importsix
- 27
- 28__version__="0.1.3"
- 29__author__="Joe Gregorio"
- 30__email__="joe@bitworking.org"
- 31__license__="MIT License"
- 32__credits__=""
- 33
- 34
-
36"""Parses a mime-type into its component parts.
- 37
- 38 Carves up a mime-type and returns a tuple of the (type, subtype, params)
- 39 where 'params' is a dictionary of all the parameters for the media range.
- 40 For example, the media range 'application/xhtml;q=0.5' would get parsed
- 41 into:
- 42
- 43 ('application', 'xhtml', {'q', '0.5'})
- 44 """
- 45parts=mime_type.split(";")
- 46params=dict(
- 47[tuple([s.strip()forsinparam.split("=",1)])forparaminparts[1:]]
- 48)
- 49full_type=parts[0].strip()
- 50# Java URLConnection class sends an Accept header that includes a
- 51# single '*'. Turn it into a legal wildcard.
- 52iffull_type=="*":
- 53full_type="*/*"
- 54(type,subtype)=full_type.split("/")
- 55
- 56return(type.strip(),subtype.strip(),params)
-
60"""Parse a media-range into its component parts.
- 61
- 62 Carves up a media range and returns a tuple of the (type, subtype,
- 63 params) where 'params' is a dictionary of all the parameters for the media
- 64 range. For example, the media range 'application/*;q=0.5' would get parsed
- 65 into:
- 66
- 67 ('application', '*', {'q', '0.5'})
- 68
- 69 In addition this function also guarantees that there is a value for 'q'
- 70 in the params dictionary, filling it in with a proper default if
- 71 necessary.
- 72 """
- 73(type,subtype,params)=parse_mime_type(range)
- 74if(
- 75"q"notinparams
- 76ornotparams["q"]
- 77ornotfloat(params["q"])
- 78orfloat(params["q"])>1
- 79orfloat(params["q"])<0
- 80):
- 81params["q"]="1"
- 82
- 83return(type,subtype,params)
-
87"""Find the best match for a mime-type amongst parsed media-ranges.
- 88
- 89 Find the best match for a given mime-type against a list of media_ranges
- 90 that have already been parsed by parse_media_range(). Returns a tuple of
- 91 the fitness value and the value of the 'q' quality parameter of the best
- 92 match, or (-1, 0) if no match was found. Just as for quality_parsed(),
- 93 'parsed_ranges' must be a list of parsed media ranges.
- 94 """
- 95best_fitness=-1
- 96best_fit_q=0
- 97(target_type,target_subtype,target_params)=parse_media_range(mime_type)
- 98for(type,subtype,params)inparsed_ranges:
- 99type_match=type==target_typeortype=="*"ortarget_type=="*"
-100subtype_match=(
-101subtype==target_subtypeorsubtype=="*"ortarget_subtype=="*"
-102)
-103iftype_matchandsubtype_match:
-104param_matches=reduce(
-105lambdax,y:x+y,
-106[
-1071
-108for(key,value)insix.iteritems(target_params)
-109ifkey!="q"andkeyinparamsandvalue==params[key]
-110],
-1110,
-112)
-113fitness=(type==target_type)and100or0
-114fitness+=(subtype==target_subtype)and10or0
-115fitness+=param_matches
-116iffitness>best_fitness:
-117best_fitness=fitness
-118best_fit_q=params["q"]
-119
-120returnbest_fitness,float(best_fit_q)
-
124"""Find the best match for a mime-type amongst parsed media-ranges.
-125
-126 Find the best match for a given mime-type against a list of media_ranges
-127 that have already been parsed by parse_media_range(). Returns the 'q'
-128 quality parameter of the best match, 0 if no match was found. This function
-129 bahaves the same as quality() except that 'parsed_ranges' must be a list of
-130 parsed media ranges.
-131 """
-132
-133returnfitness_and_quality_parsed(mime_type,parsed_ranges)[1]
-
137"""Return the quality ('q') of a mime-type against a list of media-ranges.
-138
-139 Returns the quality 'q' of a mime-type when compared against the
-140 media-ranges in ranges. For example:
-141
-142 >>> quality('text/html','text/*;q=0.3, text/html;q=0.7,
-143 text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5')
-144 0.7
-145
-146 """
-147parsed_ranges=[parse_media_range(r)forrinranges.split(",")]
-148
-149returnquality_parsed(mime_type,parsed_ranges)
-
153"""Return mime-type with the highest quality ('q') from list of candidates.
-154
-155 Takes a list of supported mime-types and finds the best match for all the
-156 media-ranges listed in header. The value of header must be a string that
-157 conforms to the format of the HTTP Accept: header. The value of 'supported'
-158 is a list of mime-types. The list of supported mime-types should be sorted
-159 in order of increasing desirability, in case of a situation where there is
-160 a tie.
-161
-162 >>> best_match(['application/xbel+xml', 'text/xml'],
-163 'text/*;q=0.5,*/*; q=0.1')
-164 'text/xml'
-165 """
-166split_header=_filter_blank(header.split(","))
-167parsed_header=[parse_media_range(r)forrinsplit_header]
-168weighted_matches=[]
-169pos=0
-170formime_typeinsupported:
-171weighted_matches.append(
-172(fitness_and_quality_parsed(mime_type,parsed_header),pos,mime_type)
-173)
-174pos+=1
-175weighted_matches.sort()
-176
-177returnweighted_matches[-1][0][1]andweighted_matches[-1][2]or""
-
Each API may support one or more serializations, such as JSON, Atom,
- etc. The model classes are responsible for converting between the wire
- format and the Python object representation.
-Create a patch object.
-
-Some methods support PATCH, an efficient way to send updates to a resource.
-This method allows the easy construction of patch bodies by looking at the
-differences between a resource before and after it was modified.
-
-Args:
- original: object, the original deserialized resource
- modified: object, the modified deserialized resource
-Returns:
- An object that contains only the changes from original to modified, in a
- form suitable to pass to a PATCH method.
-
-Example usage:
- item = service.activities().get(postid=postid, userid=userid).execute()
- original = copy.deepcopy(item)
- item['object']['content'] = 'This is updated.'
- service.activities.patch(postid=postid, userid=userid,
- body=makepatch(original, item)).execute()
-
-
- 1# Copyright 2014 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Model objects for requests and responses.
- 16
- 17Each API may support one or more serializations, such
- 18as JSON, Atom, etc. The model classes are responsible
- 19for converting between the wire format and the Python
- 20object representation.
- 21"""
- 22from__future__importabsolute_import
- 23importsix
- 24
- 25__author__="jcgregorio@google.com (Joe Gregorio)"
- 26
- 27importjson
- 28importlogging
- 29importplatform
- 30importpkg_resources
- 31
- 32fromsix.moves.urllib.parseimporturlencode
- 33
- 34fromgoogleapiclient.errorsimportHttpError
- 35
- 36_LIBRARY_VERSION=pkg_resources.get_distribution("google-api-python-client").version
- 37_PY_VERSION=platform.python_version()
- 38
- 39LOGGER=logging.getLogger(__name__)
- 40
- 41dump_request_response=False
-
49"""Model base class.
- 50
- 51 All Model classes should implement this interface.
- 52 The Model serializes and de-serializes between a wire
- 53 format such as JSON and a Python object representation.
- 54 """
- 55
-
57"""Updates outgoing requests with a serialized body.
- 58
- 59 Args:
- 60 headers: dict, request headers
- 61 path_params: dict, parameters that appear in the request path
- 62 query_params: dict, parameters that appear in the query
- 63 body_value: object, the request body as a Python object, which must be
- 64 serializable.
- 65 Returns:
- 66 A tuple of (headers, path_params, query, body)
- 67
- 68 headers: dict, request headers
- 69 path_params: dict, parameters that appear in the request path
- 70 query: string, query part of the request URI
- 71 body: string, the body serialized in the desired wire format.
- 72 """
- 73_abstract()
-
76"""Convert the response wire format into a Python object.
- 77
- 78 Args:
- 79 resp: httplib2.Response, the HTTP response headers and status
- 80 content: string, the body of the HTTP response
- 81
- 82 Returns:
- 83 The body de-serialized as a Python object.
- 84
- 85 Raises:
- 86 googleapiclient.errors.HttpError if a non 2xx response is received.
- 87 """
- 88_abstract()
-
92"""Base model class.
- 93
- 94 Subclasses should provide implementations for the "serialize" and
- 95 "deserialize" methods, as well as values for the following class attributes.
- 96
- 97 Attributes:
- 98 accept: The value to use for the HTTP Accept header.
- 99 content_type: The value to use for the HTTP Content-type header.
-100 no_content_response: The value to return when deserializing a 204 "No
-101 Content" response.
-102 alt_param: The value to supply as the "alt" query parameter for requests.
-103 """
-104
-105accept=None
-106content_type=None
-107no_content_response=None
-108alt_param=None
-109
-
127"""Updates outgoing requests with a serialized body.
-128
-129 Args:
-130 headers: dict, request headers
-131 path_params: dict, parameters that appear in the request path
-132 query_params: dict, parameters that appear in the query
-133 body_value: object, the request body as a Python object, which must be
-134 serializable by json.
-135 Returns:
-136 A tuple of (headers, path_params, query, body)
-137
-138 headers: dict, request headers
-139 path_params: dict, parameters that appear in the request path
-140 query: string, query part of the request URI
-141 body: string, the body serialized as JSON
-142 """
-143query=self._build_query(query_params)
-144headers["accept"]=self.accept
-145headers["accept-encoding"]="gzip, deflate"
-146if"user-agent"inheaders:
-147headers["user-agent"]+=" "
-148else:
-149headers["user-agent"]=""
-150headers["user-agent"]+="(gzip)"
-151if"x-goog-api-client"inheaders:
-152headers["x-goog-api-client"]+=" "
-153else:
-154headers["x-goog-api-client"]=""
-155headers["x-goog-api-client"]+="gdcl/%s gl-python/%s"%(
-156_LIBRARY_VERSION,
-157_PY_VERSION,
-158)
-159
-160ifbody_valueisnotNone:
-161headers["content-type"]=self.content_type
-162body_value=self.serialize(body_value)
-163self._log_request(headers,path_params,query,body_value)
-164return(headers,path_params,query,body_value)
-
190"""Logs debugging information about the response if requested."""
-191ifdump_request_response:
-192LOGGER.info("--response-start--")
-193forh,vinsix.iteritems(resp):
-194LOGGER.info("%s: %s",h,v)
-195ifcontent:
-196LOGGER.info(content)
-197LOGGER.info("--response-end--")
-
200"""Convert the response wire format into a Python object.
-201
-202 Args:
-203 resp: httplib2.Response, the HTTP response headers and status
-204 content: string, the body of the HTTP response
-205
-206 Returns:
-207 The body de-serialized as a Python object.
-208
-209 Raises:
-210 googleapiclient.errors.HttpError if a non 2xx response is received.
-211 """
-212self._log_response(resp,content)
-213# Error handling is TBD, for example, do we retry
-214# for some operation/error combinations?
-215ifresp.status<300:
-216ifresp.status==204:
-217# A 204: No Content response should be treated differently
-218# to all the other success states
-219returnself.no_content_response
-220returnself.deserialize(content)
-221else:
-222LOGGER.debug("Content from bad request was: %r"%content)
-223raiseHttpError(resp,content)
-
226"""Perform the actual Python object serialization.
-227
-228 Args:
-229 body_value: object, the request body as a Python object.
-230
-231 Returns:
-232 string, the body in serialized form.
-233 """
-234_abstract()
-
237"""Perform the actual deserialization from response string to Python
-238 object.
-239
-240 Args:
-241 content: string, the body of the HTTP response
-242
-243 Returns:
-244 The body de-serialized as a Python object.
-245 """
-246_abstract()
-
250"""Model class for JSON.
-251
-252 Serializes and de-serializes between JSON and the Python
-253 object representation of HTTP request and response bodies.
-254 """
-255
-256accept="application/json"
-257content_type="application/json"
-258alt_param="json"
-259
-
261"""Construct a JsonModel.
-262
-263 Args:
-264 data_wrapper: boolean, wrap requests and responses in a data wrapper
-265 """
-266self._data_wrapper=data_wrapper
-
293"""Model class for requests that don't return JSON.
-294
-295 Serializes and de-serializes between JSON and the Python
-296 object representation of HTTP request, and returns the raw bytes
-297 of the response body.
-298 """
-299
-300accept="*/*"
-301content_type="application/json"
-302alt_param=None
-303
-
313"""Model class for requests that return Media.
-314
-315 Serializes and de-serializes between JSON and the Python
-316 object representation of HTTP request, and returns the raw bytes
-317 of the response body.
-318 """
-319
-320accept="*/*"
-321content_type="application/json"
-322alt_param="media"
-323
-
333"""Model class for protocol buffers.
-334
-335 Serializes and de-serializes the binary protocol buffer sent in the HTTP
-336 request and response bodies.
-337 """
-338
-339accept="application/x-protobuf"
-340content_type="application/x-protobuf"
-341alt_param="proto"
-342
-
344"""Constructs a ProtocolBufferModel.
-345
-346 The serialized protocol buffer returned in an HTTP response will be
-347 de-serialized using the given protocol buffer class.
-348
-349 Args:
-350 protocol_buffer: The protocol buffer class used to de-serialize a
-351 response from the API.
-352 """
-353self._protocol_buffer=protocol_buffer
-
367"""Create a patch object.
-368
-369 Some methods support PATCH, an efficient way to send updates to a resource.
-370 This method allows the easy construction of patch bodies by looking at the
-371 differences between a resource before and after it was modified.
-372
-373 Args:
-374 original: object, the original deserialized resource
-375 modified: object, the modified deserialized resource
-376 Returns:
-377 An object that contains only the changes from original to modified, in a
-378 form suitable to pass to a PATCH method.
-379
-380 Example usage:
-381 item = service.activities().get(postid=postid, userid=userid).execute()
-382 original = copy.deepcopy(item)
-383 item['object']['content'] = 'This is updated.'
-384 service.activities.patch(postid=postid, userid=userid,
-385 body=makepatch(original, item)).execute()
-386 """
-387patch={}
-388forkey,original_valueinsix.iteritems(original):
-389modified_value=modified.get(key,None)
-390ifmodified_valueisNone:
-391# Use None to signal that the element is deleted
-392patch[key]=None
-393eliforiginal_value!=modified_value:
-394iftype(original_value)==type({}):
-395# Recursively descend objects
-396patch[key]=makepatch(original_value,modified_value)
-397else:
-398# In the case of simple types or arrays we just replace
-399patch[key]=modified_value
-400else:
-401# Don't add anything to patch if there's no change
-402pass
-403forkeyinmodified:
-404ifkeynotinoriginal:
-405patch[key]=modified[key]
-406
-407returnpatch
-
-Base model class.
-
-Subclasses should provide implementations for the "serialize" and
-"deserialize" methods, as well as values for the following class attributes.
-
-Attributes:
- accept: The value to use for the HTTP Accept header.
- content_type: The value to use for the HTTP Content-type header.
- no_content_response: The value to return when deserializing a 204 "No
- Content" response.
- alt_param: The value to supply as the "alt" query parameter for requests.
-
-
-Updates outgoing requests with a serialized body.
-
-Args:
- headers: dict, request headers
- path_params: dict, parameters that appear in the request path
- query_params: dict, parameters that appear in the query
- body_value: object, the request body as a Python object, which must be
- serializable by json.
-Returns:
- A tuple of (headers, path_params, query, body)
-
- headers: dict, request headers
- path_params: dict, parameters that appear in the request path
- query: string, query part of the request URI
- body: string, the body serialized as JSON
-
-
-Builds a query string.
-
-Args:
- params: dict, the query parameters
-
-Returns:
- The query parameters properly encoded into an HTTP URI query string.
-
-
-Convert the response wire format into a Python object.
-
-Args:
- resp: httplib2.Response, the HTTP response headers and status
- content: string, the body of the HTTP response
-
-Returns:
- The body de-serialized as a Python object.
-
-Raises:
- googleapiclient.errors.HttpError if a non 2xx response is received.
-
-
-Perform the actual Python object serialization.
-
-Args:
- body_value: object, the request body as a Python object.
-
-Returns:
- string, the body in serialized form.
-
-
-Perform the actual deserialization from response string to Python
-object.
-
-Args:
- content: string, the body of the HTTP response
-
-Returns:
- The body de-serialized as a Python object.
-
-
-Perform the actual Python object serialization.
-
-Args:
- body_value: object, the request body as a Python object.
-
-Returns:
- string, the body in serialized form.
-
-
-Perform the actual deserialization from response string to Python
-object.
-
-Args:
- content: string, the body of the HTTP response
-
-Returns:
- The body de-serialized as a Python object.
-
-
-Perform the actual deserialization from response string to Python
-object.
-
-Args:
- content: string, the body of the HTTP response
-
-Returns:
- The body de-serialized as a Python object.
-
-
All Model classes should implement this interface. The Model
- serializes and de-serializes between a wire format such as JSON and a
- Python object representation.
-Updates outgoing requests with a serialized body.
-
-Args:
- headers: dict, request headers
- path_params: dict, parameters that appear in the request path
- query_params: dict, parameters that appear in the query
- body_value: object, the request body as a Python object, which must be
- serializable.
-Returns:
- A tuple of (headers, path_params, query, body)
-
- headers: dict, request headers
- path_params: dict, parameters that appear in the request path
- query: string, query part of the request URI
- body: string, the body serialized in the desired wire format.
-
-
-Convert the response wire format into a Python object.
-
-Args:
- resp: httplib2.Response, the HTTP response headers and status
- content: string, the body of the HTTP response
-
-Returns:
- The body de-serialized as a Python object.
-
-Raises:
- googleapiclient.errors.HttpError if a non 2xx response is received.
-
-
-Constructs a ProtocolBufferModel.
-
-The serialized protocol buffer returned in an HTTP response will be
-de-serialized using the given protocol buffer class.
-
-Args:
- protocol_buffer: The protocol buffer class used to de-serialize a
- response from the API.
-
-
-Perform the actual Python object serialization.
-
-Args:
- body_value: object, the request body as a Python object.
-
-Returns:
- string, the body in serialized form.
-
-
-Perform the actual deserialization from response string to Python
-object.
-
-Args:
- content: string, the body of the HTTP response
-
-Returns:
- The body de-serialized as a Python object.
-
-
-Perform the actual deserialization from response string to Python
-object.
-
-Args:
- content: string, the body of the HTTP response
-
-Returns:
- The body de-serialized as a Python object.
-
-
-A common initialization routine for samples.
-
-Many of the sample applications do the same initialization, which has now
-been consolidated into this function. This function uses common idioms found
-in almost all the samples, i.e. for an API with name 'apiname', the
-credentials are stored in a file named apiname.dat, and the
-client_secrets.json file is stored in the same directory as the application
-main file.
-
-Args:
- argv: list of string, the command-line parameters of the application.
- name: string, name of the API.
- version: string, version of the API.
- doc: string, description of the application. Usually set to __doc__.
- file: string, filename of the application. Usually set to __file__.
- parents: list of argparse.ArgumentParser, additional command-line flags.
- scope: string, The OAuth scope used.
- discovery_filename: string, name of local discovery file (JSON). Use when discovery doc not available via URL.
-
-Returns:
- A tuple of (service, flags), where service is the service object and flags
- is the parsed command-line flags.
-
-
- 1# Copyright 2014 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Utilities for making samples.
- 16
- 17Consolidates a lot of code commonly repeated in sample applications.
- 18"""
- 19from__future__importabsolute_import
- 20
- 21__author__="jcgregorio@google.com (Joe Gregorio)"
- 22__all__=["init"]
- 23
- 24
- 25importargparse
- 26importos
- 27
- 28fromgoogleapiclientimportdiscovery
- 29fromgoogleapiclient.httpimportbuild_http
- 30
- 31
-
35"""A common initialization routine for samples.
- 36
- 37 Many of the sample applications do the same initialization, which has now
- 38 been consolidated into this function. This function uses common idioms found
- 39 in almost all the samples, i.e. for an API with name 'apiname', the
- 40 credentials are stored in a file named apiname.dat, and the
- 41 client_secrets.json file is stored in the same directory as the application
- 42 main file.
- 43
- 44 Args:
- 45 argv: list of string, the command-line parameters of the application.
- 46 name: string, name of the API.
- 47 version: string, version of the API.
- 48 doc: string, description of the application. Usually set to __doc__.
- 49 file: string, filename of the application. Usually set to __file__.
- 50 parents: list of argparse.ArgumentParser, additional command-line flags.
- 51 scope: string, The OAuth scope used.
- 52 discovery_filename: string, name of local discovery file (JSON). Use when discovery doc not available via URL.
- 53
- 54 Returns:
- 55 A tuple of (service, flags), where service is the service object and flags
- 56 is the parsed command-line flags.
- 57 """
- 58try:
- 59fromoauth2clientimportclient
- 60fromoauth2clientimportfile
- 61fromoauth2clientimporttools
- 62exceptImportError:
- 63raiseImportError(
- 64"googleapiclient.sample_tools requires oauth2client. Please install oauth2client and try again."
- 65)
- 66
- 67ifscopeisNone:
- 68scope="https://www.googleapis.com/auth/"+name
- 69
- 70# Parser command-line arguments.
- 71parent_parsers=[tools.argparser]
- 72parent_parsers.extend(parents)
- 73parser=argparse.ArgumentParser(
- 74description=doc,
- 75formatter_class=argparse.RawDescriptionHelpFormatter,
- 76parents=parent_parsers,
- 77)
- 78flags=parser.parse_args(argv[1:])
- 79
- 80# Name of a file containing the OAuth 2.0 information for this
- 81# application, including client_id and client_secret, which are found
- 82# on the API Access tab on the Google APIs
- 83# Console <http://code.google.com/apis/console>.
- 84client_secrets=os.path.join(os.path.dirname(filename),"client_secrets.json")
- 85
- 86# Set up a Flow object to be used if we need to authenticate.
- 87flow=client.flow_from_clientsecrets(
- 88client_secrets,scope=scope,message=tools.message_if_missing(client_secrets)
- 89)
- 90
- 91# Prepare credentials, and authorize HTTP object with them.
- 92# If the credentials don't exist or are invalid run through the native client
- 93# flow. The Storage object will ensure that if successful the good
- 94# credentials will get written back to a file.
- 95storage=file.Storage(name+".dat")
- 96credentials=storage.get()
- 97ifcredentialsisNoneorcredentials.invalid:
- 98credentials=tools.run_flow(flow,storage,flags)
- 99http=credentials.authorize(http=build_http())
-100
-101ifdiscovery_filenameisNone:
-102# Construct a service object via the discovery service.
-103service=discovery.build(name,version,http=http)
-104else:
-105# Construct a service object using a local discovery document file.
-106withopen(discovery_filename)asdiscovery_file:
-107service=discovery.build_from_document(
-108discovery_file.read(),base="https://www.googleapis.com/",http=http
-109)
-110return(service,flags)
-
-Schema processing for discovery based APIs
-
-Schemas holds an APIs discovery schemas. It can return those schema as
-deserialized JSON objects, or pretty print them as prototype objects that
-conform to the schema.
-
-For example, given the schema:
-
- schema = """{
- "Foo": {
- "type": "object",
- "properties": {
- "etag": {
- "type": "string",
- "description": "ETag of the collection."
- },
- "kind": {
- "type": "string",
- "description": "Type of the collection ('calendar#acl').",
- "default": "calendar#acl"
- },
- "nextPageToken": {
- "type": "string",
- "description": "Token used to access the next
- page of this result. Omitted if no further results are available."
- }
- }
- }
- }"""
-
- s = Schemas(schema)
- print s.prettyPrintByName('Foo')
-
- Produces the following output:
-
- {
- "nextPageToken": "A String", # Token used to access the
- # next page of this result. Omitted if no further results are available.
- "kind": "A String", # Type of the collection ('calendar#acl').
- "etag": "A String", # ETag of the collection.
- },
-
-The constructor takes a discovery document in which to look up named schema.
-
-
- 1# Copyright 2014 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# You may obtain a copy of the License at
- 6#
- 7# http://www.apache.org/licenses/LICENSE-2.0
- 8#
- 9# Unless required by applicable law or agreed to in writing, software
- 10# distributed under the License is distributed on an "AS IS" BASIS,
- 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- 12# See the License for the specific language governing permissions and
- 13# limitations under the License.
- 14
- 15"""Schema processing for discovery based APIs
- 16
- 17Schemas holds an APIs discovery schemas. It can return those schema as
- 18deserialized JSON objects, or pretty print them as prototype objects that
- 19conform to the schema.
- 20
- 21For example, given the schema:
- 22
- 23 schema = \"\"\"{
- 24 "Foo": {
- 25 "type": "object",
- 26 "properties": {
- 27 "etag": {
- 28 "type": "string",
- 29 "description": "ETag of the collection."
- 30 },
- 31 "kind": {
- 32 "type": "string",
- 33 "description": "Type of the collection ('calendar#acl').",
- 34 "default": "calendar#acl"
- 35 },
- 36 "nextPageToken": {
- 37 "type": "string",
- 38 "description": "Token used to access the next
- 39 page of this result. Omitted if no further results are available."
- 40 }
- 41 }
- 42 }
- 43 }\"\"\"
- 44
- 45 s = Schemas(schema)
- 46 print s.prettyPrintByName('Foo')
- 47
- 48 Produces the following output:
- 49
- 50 {
- 51 "nextPageToken": "A String", # Token used to access the
- 52 # next page of this result. Omitted if no further results are available.
- 53 "kind": "A String", # Type of the collection ('calendar#acl').
- 54 "etag": "A String", # ETag of the collection.
- 55 },
- 56
- 57The constructor takes a discovery document in which to look up named schema.
- 58"""
- 59from__future__importabsolute_import
- 60importsix
- 61
- 62# TODO(jcgregorio) support format, enum, minimum, maximum
- 63
- 64__author__="jcgregorio@google.com (Joe Gregorio)"
- 65
- 66importcopy
- 67
- 68fromgoogleapiclientimport_helpersasutil
-
88"""Get pretty printed object prototype from the schema name.
- 89
- 90 Args:
- 91 name: string, Name of schema in the discovery document.
- 92 seen: list of string, Names of schema already seen. Used to handle
- 93 recursive definitions.
- 94
- 95 Returns:
- 96 string, A string that contains a prototype object with
- 97 comments that conforms to the given schema.
- 98 """
- 99ifseenisNone:
-100seen=[]
-101
-102ifnameinseen:
-103# Do not fall into an infinite loop over recursive definitions.
-104return"# Object with schema name: %s"%name
-105seen.append(name)
-106
-107ifnamenotinself.pretty:
-108self.pretty[name]=_SchemaToStruct(
-109self.schemas[name],seen,dent=dent
-110).to_str(self._prettyPrintByName)
-111
-112seen.pop()
-113
-114returnself.pretty[name]
-
117"""Get pretty printed object prototype from the schema name.
-118
-119 Args:
-120 name: string, Name of schema in the discovery document.
-121
-122 Returns:
-123 string, A string that contains a prototype object with
-124 comments that conforms to the given schema.
-125 """
-126# Return with trailing comma and newline removed.
-127returnself._prettyPrintByName(name,seen=[],dent=1)[:-2]
-
131"""Get pretty printed object prototype of schema.
-132
-133 Args:
-134 schema: object, Parsed JSON schema.
-135 seen: list of string, Names of schema already seen. Used to handle
-136 recursive definitions.
-137
-138 Returns:
-139 string, A string that contains a prototype object with
-140 comments that conforms to the given schema.
-141 """
-142ifseenisNone:
-143seen=[]
-144
-145return_SchemaToStruct(schema,seen,dent=dent).to_str(self._prettyPrintByName)
-
148"""Get pretty printed object prototype of schema.
-149
-150 Args:
-151 schema: object, Parsed JSON schema.
-152
-153 Returns:
-154 string, A string that contains a prototype object with
-155 comments that conforms to the given schema.
-156 """
-157# Return with trailing comma and newline removed.
-158returnself._prettyPrintSchema(schema,dent=1)[:-2]
-
161"""Get deserialized JSON schema from the schema name.
-162
-163 Args:
-164 name: string, Schema name.
-165 default: object, return value if name not found.
-166 """
-167returnself.schemas.get(name,default)
-
175"""Constructor.
-176
-177 Args:
-178 schema: object, Parsed JSON schema.
-179 seen: list, List of names of schema already seen while parsing. Used to
-180 handle recursive definitions.
-181 dent: int, Initial indentation depth.
-182 """
-183# The result of this parsing kept as list of strings.
-184self.value=[]
-185
-186# The final value of the parsing.
-187self.string=None
-188
-189# The parsed JSON schema.
-190self.schema=schema
-191
-192# Indentation level.
-193self.dent=dent
-194
-195# Method that when called returns a prototype object for the schema with
-196# the given name.
-197self.from_cache=None
-198
-199# List of names of schema already seen while parsing.
-200self.seen=seen
-
211"""Add text to the output, but with no line terminator.
-212
-213 Args:
-214 text: string, Text to output.
-215 """
-216self.value.extend([" "*self.dent,text])
-
219"""Add text and comment to the output with line terminator.
-220
-221 Args:
-222 text: string, Text to output.
-223 comment: string, Python comment.
-224 """
-225ifcomment:
-226divider="\n"+" "*(self.dent+2)+"# "
-227lines=comment.splitlines()
-228lines=[x.rstrip()forxinlines]
-229comment=divider.join(lines)
-230self.value.extend([text," # ",comment,"\n"])
-231else:
-232self.value.extend([text,"\n"])
-
303"""Prototype object based on the schema, in Python code with comments.
-304
-305 Args:
-306 from_cache: callable(name, seen), Callable that retrieves an object
-307 prototype for a schema with the given name. Seen is a list of schema
-308 names already seen as we recursively descend the schema definition.
-309
-310 Returns:
-311 Prototype object based on the schema, in Python code with comments.
-312 The lines of the code will all be properly indented.
-313 """
-314self.from_cache=from_cache
-315returnself._to_str_impl(self.schema)
-
-Get pretty printed object prototype from the schema name.
-
-Args:
- name: string, Name of schema in the discovery document.
- seen: list of string, Names of schema already seen. Used to handle
- recursive definitions.
-
-Returns:
- string, A string that contains a prototype object with
- comments that conforms to the given schema.
-
-
-Get pretty printed object prototype from the schema name.
-
-Args:
- name: string, Name of schema in the discovery document.
-
-Returns:
- string, A string that contains a prototype object with
- comments that conforms to the given schema.
-
-
-Get pretty printed object prototype of schema.
-
-Args:
- schema: object, Parsed JSON schema.
- seen: list of string, Names of schema already seen. Used to handle
- recursive definitions.
-
-Returns:
- string, A string that contains a prototype object with
- comments that conforms to the given schema.
-
-
-Get pretty printed object prototype of schema.
-
-Args:
- schema: object, Parsed JSON schema.
-
-Returns:
- string, A string that contains a prototype object with
- comments that conforms to the given schema.
-
-
-Constructor.
-
-Args:
- schema: object, Parsed JSON schema.
- seen: list, List of names of schema already seen while parsing. Used to
- handle recursive definitions.
- dent: int, Initial indentation depth.
-
-
-Prototype object based on the schema, in Python code with comments.
-
-Args:
- schema: object, Parsed JSON schema file.
-
-Returns:
- Prototype object based on the schema, in Python code with comments.
-
-
-Prototype object based on the schema, in Python code with comments.
-
-Args:
- from_cache: callable(name, seen), Callable that retrieves an object
- prototype for a schema with the given name. Seen is a list of schema
- names already seen as we recursively descend the schema definition.
-
-Returns:
- Prototype object based on the schema, in Python code with comments.
- The lines of the code will all be properly indented.
-
-
This document contains the API (Application Programming Interface)
-documentation for this project. Documentation for the Python
-objects defined by the project is divided into separate pages for each
-package, module, and class. The API documentation also includes two
-pages containing information about the project as a whole: a trees
-page, and an index page.
-
-
Object Documentation
-
-
Each Package Documentation page contains:
-
-
A description of the package.
-
A list of the modules and sub-packages contained by the
- package.
-
A summary of the classes defined by the package.
-
A summary of the functions defined by the package.
-
A summary of the variables defined by the package.
-
A detailed description of each function defined by the
- package.
-
A detailed description of each variable defined by the
- package.
-
-
-
Each Module Documentation page contains:
-
-
A description of the module.
-
A summary of the classes defined by the module.
-
A summary of the functions defined by the module.
-
A summary of the variables defined by the module.
-
A detailed description of each function defined by the
- module.
-
A detailed description of each variable defined by the
- module.
-
-
-
Each Class Documentation page contains:
-
-
A class inheritance diagram.
-
A list of known subclasses.
-
A description of the class.
-
A summary of the methods defined by the class.
-
A summary of the instance variables defined by the class.
-
A summary of the class (static) variables defined by the
- class.
-
A detailed description of each method defined by the
- class.
-
A detailed description of each instance variable defined by the
- class.
-
A detailed description of each class (static) variable defined
- by the class.
-
-
-
Project Documentation
-
-
The Trees page contains the module and class hierarchies:
-
-
The module hierarchy lists every package and module, with
- modules grouped into packages. At the top level, and within each
- package, modules and sub-packages are listed alphabetically.
-
The class hierarchy lists every class, grouped by base
- class. If a class has more than one base class, then it will be
- listed under each base class. At the top level, and under each base
- class, classes are listed alphabetically.
-
-
-
The Index page contains indices of terms and
- identifiers:
-
-
The term index lists every term indexed by any object's
- documentation. For each term, the index provides links to each
- place where the term is indexed.
-
The identifier index lists the (short) name of every package,
- module, class, method, function, variable, and parameter. For each
- identifier, the index provides a short description, and a link to
- its documentation.
-
-
-
The Table of Contents
-
-
The table of contents occupies the two frames on the left side of
-the window. The upper-left frame displays the project
-contents, and the lower-left frame displays the module
-contents:
-
-
-
-
- Project Contents...
-
- API Documentation Frame
-
-
-
-
- Module Contents ...
-
-
-
-
-
The project contents frame contains a list of all packages
-and modules that are defined by the project. Clicking on an entry
-will display its contents in the module contents frame. Clicking on a
-special entry, labeled "Everything," will display the contents of
-the entire project.
-
-
The module contents frame contains a list of every
-submodule, class, type, exception, function, and variable defined by a
-module or package. Clicking on an entry will display its
-documentation in the API documentation frame. Clicking on the name of
-the module, at the top of the frame, will display the documentation
-for the module itself.
-
-
The "frames" and "no frames" buttons below the top
-navigation bar can be used to control whether the table of contents is
-displayed or not.
-
-
The Navigation Bar
-
-
A navigation bar is located at the top and bottom of every page.
-It indicates what type of page you are currently viewing, and allows
-you to go to related pages. The following table describes the labels
-on the navigation bar. Note that not some labels (such as
-[Parent]) are not displayed on all pages.
-
-
-
-
Label
-
Highlighted when...
-
Links to...
-
-
[Parent]
-
(never highlighted)
-
the parent of the current package
-
[Package]
-
viewing a package
-
the package containing the current object
-
-
[Module]
-
viewing a module
-
the module containing the current object
-
-
[Class]
-
viewing a class
-
the class containing the current object
-
[Trees]
-
viewing the trees page
-
the trees page
-
[Index]
-
viewing the index page
-
the index page
-
[Help]
-
viewing the help page
-
the help page
-
-
-
The "show private" and "hide private" buttons below
-the top navigation bar can be used to control whether documentation
-for private objects is displayed. Private objects are usually defined
-as objects whose (short) names begin with a single underscore, but do
-not end with an underscore. For example, "_x",
-"__pprint", and "epydoc.epytext._tokenize"
-are private objects; but "re.sub",
-"__init__", and "type_" are not. However,
-if a module defines the "__all__" variable, then its
-contents are used to decide which objects are private.
-
-
A timestamp below the bottom navigation bar indicates when each
-page was last updated.
When javascript is enabled, this page will redirect URLs of
-the form redirect.html#dotted.name to the
-documentation for the object with the given fully-qualified
-dotted name.