imported patch partial-and-patch
diff --git a/apiclient/discovery.py b/apiclient/discovery.py
index b90486a..2c89528 100644
--- a/apiclient/discovery.py
+++ b/apiclient/discovery.py
@@ -48,7 +48,7 @@
 DEFAULT_METHOD_DOC = 'A description of how to use this function'
 
 # Query parameters that work, but don't appear in discovery
-STACK_QUERY_PARAMETERS = ['trace']
+STACK_QUERY_PARAMETERS = ['trace', 'fields']
 
 
 def key2param(key):
@@ -243,7 +243,7 @@
           'restParameterType': 'query'
           }
 
-    if httpMethod in ['PUT', 'POST']:
+    if httpMethod in ['PUT', 'POST', 'PATCH']:
       methodDesc['parameters']['body'] = {
           'description': 'The request body.',
           'type': 'object',
diff --git a/apiclient/http.py b/apiclient/http.py
index f4627bd..206f3e6 100644
--- a/apiclient/http.py
+++ b/apiclient/http.py
@@ -10,6 +10,7 @@
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 __all__ = [
     'HttpRequest', 'RequestMockBuilder', 'HttpMock'
+    'set_user_agent', 'tunnel_patch'
     ]
 
 import httplib2
@@ -17,6 +18,7 @@
 
 from model import JsonModel
 from errors import HttpError
+from anyjson import simplejson
 
 
 class HttpRequest(object):
@@ -201,6 +203,7 @@
   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
   """
 
@@ -220,13 +223,16 @@
     resp, content = self._iterable.pop(0)
     if content == 'echo_request_headers':
       content = headers
+    elif content == 'echo_request_headers_as_json':
+      content = simplejson.dumps(headers)
     elif content == 'echo_request_body':
       content = body
     return httplib2.Response(resp), content
 
 
 def set_user_agent(http, user_agent):
-  """
+  """Set the user-agent on every request.
+
   Args:
      http - An instance of httplib2.Http
          or something that acts like it.
@@ -262,3 +268,43 @@
 
   http.request = new_request
   return http
+
+
+def tunnel_patch(http):
+  """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.
+  """
+  request_orig = http.request
+
+  # The closure that will replace 'httplib2.Http.request'.
+  def new_request(uri, method='GET', body=None, headers=None,
+                  redirections=httplib2.DEFAULT_MAX_REDIRECTS,
+                  connection_type=None):
+    """Modify the request headers to add the user-agent."""
+    if headers is None:
+      headers = {}
+    if method == 'PATCH':
+      if 'authorization' in headers and 'oauth_token' in headers['authorization']:
+        logging.warning('OAuth 1.0 request made with Credentials applied after tunnel_patch.')
+      headers['x-http-method-override'] = "PATCH"
+      method = 'POST'
+    resp, content = request_orig(uri, method, body, headers,
+                        redirections, connection_type)
+    return resp, content
+
+  http.request = new_request
+  return http
diff --git a/samples/localdiscovery/buzz.py b/samples/localdiscovery/buzz.py
index 3fb0c6f..c769576 100644
--- a/samples/localdiscovery/buzz.py
+++ b/samples/localdiscovery/buzz.py
@@ -18,39 +18,53 @@
 from apiclient.ext.file import Storage
 from apiclient.oauth import CredentialsInvalidError
 
+import gflags
+import sys
 import httplib2
+import logging
+import os
 import pprint
+import sys
 
-# Uncomment the next line to get very detailed logging
-#httplib2.debuglevel = 4
+FLAGS = gflags.FLAGS
+
+gflags.DEFINE_enum('logging_level', 'ERROR',
+    ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
+    'Set the level of logging detail.')
 
 
-def main():
+def main(argv):
+  try:
+    argv = FLAGS(argv)
+  except gflags.FlagsError, e:
+    print '%s\\nUsage: %s ARGS\\n%s' % (e, argv[0], FLAGS)
+    sys.exit(1)
+
+  logging.getLogger().setLevel(getattr(logging, FLAGS.logging_level))
+
   storage = Storage('buzz.dat')
   credentials = storage.get()
   if credentials is None or credentials.invalid == True:
     buzz_discovery = build("buzz", "v1").auth_discovery()
-
     flow = FlowThreeLegged(buzz_discovery,
-                           consumer_key='anonymous',
-                           consumer_secret='anonymous',
-                           user_agent='python-buzz-sample/1.0',
-                           domain='anonymous',
-                           scope='https://www.googleapis.com/auth/buzz',
-                           xoauth_displayname='Google API Client Example App')
-
+        consumer_key='anonymous',
+        consumer_secret='anonymous',
+        user_agent='python-buzz-sample/1.0',
+        domain='anonymous',
+        scope='https://www.googleapis.com/auth/buzz',
+        xoauth_displayname='Google API Client Example App')
     credentials = run(flow, storage)
 
   http = httplib2.Http()
   http = credentials.authorize(http)
 
   # Load the local copy of the discovery document
-  f = file("buzz.json", "r")
+  f = file(os.path.join(os.path.dirname(__file__), "buzz.json"), "r")
   discovery = f.read()
   f.close()
 
   # Optionally load a futures discovery document
-  f = file("../../apiclient/contrib/buzz/future.json", "r")
+  f = file(os.path.join(os.path.dirname(__file__), "../../apiclient/contrib/buzz/future.json"), "r")
   future = f.read()
   f.close()
 
@@ -73,4 +87,4 @@
 
 
 if __name__ == '__main__':
-  main()
+  main(sys.argv)
diff --git a/tests/data/zoo.json b/tests/data/zoo.json
index 43af0e8..284619e 100644
--- a/tests/data/zoo.json
+++ b/tests/data/zoo.json
@@ -1,9 +1,117 @@
 {
+ "kind": "discovery#describeItem",
  "name": "zoo",
  "version": "v1",
- "description": "Zoo API used for testing",
- "restBasePath": "/zoo",
+ "description": "Zoo API used for Apiary testing",
+ "restBasePath": "/zoo/",
  "rpcPath": "/rpc",
+ "features": [
+  "dataWrapper"
+ ],
+ "schemas": {
+  "Animal": {
+   "id": "Animal",
+   "type": "object",
+   "properties": {
+    "etag": {
+     "type": "string"
+    },
+    "kind": {
+     "type": "string",
+     "default": "zoo#animal"
+    },
+    "name": {
+     "type": "string"
+    },
+    "photo": {
+     "type": "object",
+     "properties": {
+      "filename": {
+       "type": "string"
+      },
+      "hash": {
+       "type": "string"
+      },
+      "hashAlgorithm": {
+       "type": "string"
+      },
+      "size": {
+       "type": "integer"
+      },
+      "type": {
+       "type": "string"
+      }
+     }
+    }
+   }
+  },
+  "Animal2": {
+   "id": "Animal2",
+   "type": "object",
+   "properties": {
+    "kind": {
+     "type": "string",
+     "default": "zoo#animal"
+    },
+    "name": {
+     "type": "string"
+    }
+   }
+  },
+  "AnimalFeed": {
+   "id": "AnimalFeed",
+   "type": "object",
+   "properties": {
+    "etag": {
+     "type": "string"
+    },
+    "items": {
+     "type": "array",
+     "items": {
+      "$ref": "Animal"
+     }
+    },
+    "kind": {
+     "type": "string",
+     "default": "zoo#animalFeed"
+    }
+   }
+  },
+  "LoadFeed": {
+   "id": "LoadFeed",
+   "type": "object",
+   "properties": {
+    "items": {
+     "type": "array",
+     "items": {
+      "type": "object",
+      "properties": {
+       "doubleVal": {
+        "type": "number"
+       },
+       "enumVal": {
+        "type": "string"
+       },
+       "kind": {
+        "type": "string",
+        "default": "zoo#loadValue"
+       },
+       "longVal": {
+        "type": "integer"
+       },
+       "stringVal": {
+        "type": "string"
+       }
+      }
+     }
+    },
+    "kind": {
+     "type": "string",
+     "default": "zoo#loadFeed"
+    }
+   }
+  }
+ },
  "methods": {
   "query": {
    "restPath": "query",
@@ -85,106 +193,218 @@
   "animals": {
    "methods": {
     "crossbreed": {
-     "restPath": "/animals/crossbreed",
+     "restPath": "animals/crossbreed",
      "rpcMethod": "zoo.animals.crossbreed",
-     "httpMethod": "GET",
-     "parameters": {
-      "father": {
-       "restParameterType": "query",
-       "required": false
-      },
-      "mother": {
-       "restParameterType": "query",
-       "required": false
-      }
+     "httpMethod": "POST",
+     "description": "Cross-breed animals",
+     "response": {
+      "$ref": "Animal2"
      }
     },
     "delete": {
-     "restPath": "/animals/{name}",
+     "restPath": "animals/{name}",
      "rpcMethod": "zoo.animals.delete",
      "httpMethod": "DELETE",
+     "description": "Delete animals",
      "parameters": {
       "name": {
        "restParameterType": "path",
-       "pattern": "[^/]+",
-       "required": true
+       "required": true,
+       "description": "Name of the animal to delete",
+       "type": "string"
       }
-     }
+     },
+     "parameterOrder": [
+      "name"
+     ]
     },
     "get": {
-     "restPath": "/animals/{name}",
+     "restPath": "animals/{name}",
      "rpcMethod": "zoo.animals.get",
      "httpMethod": "GET",
+     "description": "Get animals",
      "parameters": {
       "name": {
        "restParameterType": "path",
-       "pattern": "[^/]+",
-       "required": true
+       "required": true,
+       "description": "Name of the animal to load",
+       "type": "string"
       },
       "projection": {
        "restParameterType": "query",
-       "required": false
+       "type": "string",
+       "enum": [
+        "full"
+       ],
+       "enumDescriptions": [
+        "Include everything"
+       ]
       }
+     },
+     "parameterOrder": [
+      "name"
+     ],
+     "response": {
+      "$ref": "Animal"
      }
     },
     "insert": {
-     "restPath": "/animals",
+     "restPath": "animals",
      "rpcMethod": "zoo.animals.insert",
      "httpMethod": "POST",
-     "parameters": {
-      "photo": {
-       "restParameterType": "query",
-       "required": false
-      }
+     "description": "Insert animals",
+     "request": {
+      "$ref": "Animal"
+     },
+     "response": {
+      "$ref": "Animal"
      }
     },
     "list": {
-     "restPath": "/animals",
+     "restPath": "animals",
      "rpcMethod": "zoo.animals.list",
      "httpMethod": "GET",
+     "description": "List animals",
      "parameters": {
       "max-results": {
        "restParameterType": "query",
-       "required": false
+       "description": "Maximum number of results to return",
+       "type": "integer",
+       "minimum": "0"
       },
       "name": {
        "restParameterType": "query",
-       "required": false
+       "description": "Restrict result to animals with this name",
+       "type": "string"
       },
       "projection": {
        "restParameterType": "query",
-       "required": false
+       "type": "string",
+       "enum": [
+        "full"
+       ],
+       "enumDescriptions": [
+        "Include absolutely everything"
+       ]
       },
       "start-token": {
        "restParameterType": "query",
-       "required": false
+       "description": "Pagination token",
+       "type": "string"
       }
+     },
+     "response": {
+      "$ref": "AnimalFeed"
+     }
+    },
+    "patch": {
+     "restPath": "animals/{name}",
+     "rpcMethod": "zoo.animals.patch",
+     "httpMethod": "PATCH",
+     "description": "Update animals",
+     "parameters": {
+      "name": {
+       "restParameterType": "path",
+       "required": true,
+       "description": "Name of the animal to update",
+       "type": "string"
+      }
+     },
+     "parameterOrder": [
+      "name"
+     ],
+     "request": {
+      "$ref": "Animal"
+     },
+     "response": {
+      "$ref": "Animal"
      }
     },
     "update": {
-     "restPath": "/animals/{animal.name}",
+     "restPath": "animals/{name}",
      "rpcMethod": "zoo.animals.update",
-     "httpMethod": "PUT"
+     "httpMethod": "PUT",
+     "description": "Update animals",
+     "parameters": {
+      "name": {
+       "restParameterType": "path",
+       "description": "Name of the animal to update",
+       "type": "string"
+      }
+     },
+     "parameterOrder": [
+      "name"
+     ],
+     "request": {
+      "$ref": "Animal"
+     },
+     "response": {
+      "$ref": "Animal"
+     }
     }
    }
   },
   "load": {
    "methods": {
     "list": {
-     "restPath": "/load",
+     "restPath": "load",
      "rpcMethod": "zoo.load.list",
-     "httpMethod": "GET"
+     "httpMethod": "GET",
+     "response": {
+      "$ref": "LoadFeed"
+     }
     }
    }
   },
   "loadNoTemplate": {
    "methods": {
     "list": {
-     "restPath": "/loadNoTemplate",
+     "restPath": "loadNoTemplate",
      "rpcMethod": "zoo.loadNoTemplate.list",
      "httpMethod": "GET"
     }
    }
+  },
+  "scopedAnimals": {
+   "methods": {
+    "list": {
+     "restPath": "scopedanimals",
+     "rpcMethod": "zoo.scopedAnimals.list",
+     "httpMethod": "GET",
+     "description": "List animals (scoped)",
+     "parameters": {
+      "max-results": {
+       "restParameterType": "query",
+       "description": "Maximum number of results to return",
+       "type": "integer",
+       "minimum": "0"
+      },
+      "name": {
+       "restParameterType": "query",
+       "description": "Restrict result to animals with this name",
+       "type": "string"
+      },
+      "projection": {
+       "restParameterType": "query",
+       "type": "string",
+       "enum": [
+        "full"
+       ],
+       "enumDescriptions": [
+        "Include absolutely everything"
+       ]
+      },
+      "start-token": {
+       "restParameterType": "query",
+       "description": "Pagination token",
+       "type": "string"
+      }
+     },
+     "response": {
+      "$ref": "AnimalFeed"
+     }
+    }
+   }
   }
  }
 }
diff --git a/tests/test_discovery.py b/tests/test_discovery.py
index ecb59f9..4bdff20 100644
--- a/tests/test_discovery.py
+++ b/tests/test_discovery.py
@@ -34,6 +34,8 @@
 
 from apiclient.discovery import build, key2param
 from apiclient.http import HttpMock
+from apiclient.http import tunnel_patch
+from apiclient.http import HttpMockSequence
 from apiclient.errors import HttpError
 from apiclient.errors import InvalidJsonError
 
@@ -107,8 +109,8 @@
     self.assertEqual(q['e'], ['bar'])
 
   def test_type_coercion(self):
-    self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
-    zoo = build('zoo', 'v1', self.http)
+    http = HttpMock(datafile('zoo.json'), {'status': '200'})
+    zoo = build('zoo', 'v1', http)
 
     request = zoo.query(q="foo", i=1.0, n=1.0, b=0, a=[1,2,3], o={'a':1}, e='bar')
     self._check_query_types(request)
@@ -119,13 +121,32 @@
     self._check_query_types(request)
 
   def test_optional_stack_query_parameters(self):
-    self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
-    zoo = build('zoo', 'v1', self.http)
-    request = zoo.query(trace='html')
+    http = HttpMock(datafile('zoo.json'), {'status': '200'})
+    zoo = build('zoo', 'v1', http)
+    request = zoo.query(trace='html', fields='description')
 
     parsed = urlparse.urlparse(request.uri)
     q = parse_qs(parsed[4])
     self.assertEqual(q['trace'], ['html'])
+    self.assertEqual(q['fields'], ['description'])
+
+  def test_patch(self):
+    http = HttpMock(datafile('zoo.json'), {'status': '200'})
+    zoo = build('zoo', 'v1', http)
+    request = zoo.animals().patch(name='lion', body='{"description": "foo"}')
+
+    self.assertEqual(request.method, 'PATCH')
+
+  def test_tunnel_patch(self):
+    http = HttpMockSequence([
+      ({'status': '200'}, file(datafile('zoo.json'), 'r').read()),
+      ({'status': '200'}, 'echo_request_headers_as_json'),
+      ])
+    http = tunnel_patch(http)
+    zoo = build('zoo', 'v1', http)
+    resp = zoo.animals().patch(name='lion', body='{"description": "foo"}').execute()
+
+    self.assertTrue('x-http-method-override' in resp)
 
   def test_buzz_resources(self):
     self.http = HttpMock(datafile('buzz.json'), {'status': '200'})
@@ -150,11 +171,11 @@
     zoo = build('zoo', 'v1', self.http)
     self.assertTrue(getattr(zoo, 'animals'))
 
-    request = zoo.animals().list(name='bat', projection="size")
+    request = zoo.animals().list(name='bat', projection="full")
     parsed = urlparse.urlparse(request.uri)
     q = parse_qs(parsed[4])
     self.assertEqual(q['name'], ['bat'])
-    self.assertEqual(q['projection'], ['size'])
+    self.assertEqual(q['projection'], ['full'])
 
   def test_nested_resources(self):
     self.http = HttpMock(datafile('zoo.json'), {'status': '200'})