 Chromium Code Reviews
 Chromium Code Reviews Issue 2868333004:
  Add URL recipe module from "depot_tools".  (Closed)
    
  
    Issue 2868333004:
  Add URL recipe module from "depot_tools".  (Closed) 
  | OLD | NEW | 
|---|---|
| (Empty) | |
| 1 # Copyright 2017 The LUCI Authors. All rights reserved. | |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 # that can be found in the LICENSE file. | |
| 4 | |
| 5 from recipe_engine import recipe_api | |
| 6 | |
| 7 import urllib | |
| 8 | |
| 9 class UrlApi(recipe_api.RecipeApi): | |
| 10 quote = staticmethod(urllib.quote) | |
| 
iannucci
2017/05/12 00:53:52
buh? staticmethod shouldn't be needed
 
dnj
2017/05/12 02:15:04
Done.
 | |
| 11 urlencode = staticmethod(urllib.urlencode) | |
| 12 | |
| 13 # JSON prefix used with Gerrit and Gitiles. | |
| 14 GERRIT_JSON_PREFIX = ')]}\n' | |
| 15 | |
| 16 | |
| 17 class Response(object): | |
| 
iannucci
2017/05/12 00:53:52
note: capture error body and stuff in different fi
 
dnj
2017/05/12 02:15:04
Done.
 | |
| 18 """Response is an HTTP response object.""" | |
| 19 | |
| 20 def __init__(self, method, output, status): | |
| 21 self._method = method | |
| 22 self._status = status | |
| 23 self._result = output | |
| 24 | |
| 25 @property | |
| 26 def method(self): | |
| 
iannucci
2017/05/12 00:53:52
DOOOOGGGGG STRIIING
 
dnj
2017/05/12 02:15:04
Done.
 | |
| 27 return self._method | |
| 28 | |
| 29 @property | |
| 30 def status_code(self): | |
| 31 return self._status['status_code'] | |
| 32 | |
| 33 def raise_on_error(self): | |
| 34 if not self._status['success']: | |
| 35 raise ValueError('HTTP status (%d)' % (self.status_code,)) | |
| 
iannucci
2017/05/12 00:53:52
real exception?
 
dnj
2017/05/12 02:15:04
Done.
 | |
| 36 | |
| 37 @property | |
| 38 def output(self): | |
| 39 return self._result | |
| 40 | |
| 41 | |
| 42 @recipe_api.non_step | |
| 43 def join(self, *parts): | |
| 44 """Constructs a URL path from composite parts. | |
| 45 | |
| 46 Args: | |
| 47 parts (str...): Strings to concastenate. Any leading or trailing slashes | |
| 48 will be stripped from intermediate strings to ensure that they join | |
| 49 together. Trailing slashes will not be stripped from the last part. | |
| 50 """ | |
| 51 if parts: | |
| 52 parts = list(parts) | |
| 53 if len(parts) > 1: | |
| 54 for i, p in enumerate(parts[:-1]): | |
| 55 parts[i] = p.strip('/') | |
| 56 parts[-1] = parts[-1].lstrip('/') | |
| 57 return '/'.join(parts) | |
| 58 | |
| 59 def get_file(self, url, path, step_name=None, headers=None, | |
| 60 transient_retry=True, strip_prefix=None, **kwargs): | |
| 61 """GET data at given URL and writes it to file. | |
| 62 | |
| 63 Args: | |
| 64 url: URL to request. | |
| 65 path (Path): the Path where the content will be written. | |
| 66 step_name: optional step name, 'fetch <url>' by default. | |
| 67 headers: a {header_name: value} dictionary for HTTP headers. | |
| 68 transient_retry (bool): If True (default), transient HTTP errors (>500) | |
| 69 will automatically be retried with exponential backoff. If False, | |
| 70 exactly one attempt will be made. | |
| 71 strip_prefix (str or None): If not None, this prefix must be present at | |
| 72 the beginning of the response, and will be stripped from the resulting | |
| 73 content (e.g., GERRIT_JSON_PREFIX). | |
| 74 | |
| 75 Returns: | |
| 76 Response with "path" as its "output" value. | |
| 77 """ | |
| 78 return self._get_step(url, path, step_name, headers, transient_retry, | |
| 79 strip_prefix, False, **kwargs) | |
| 80 | |
| 81 def must_get_file(self, *args, **kwargs): | |
| 82 """Like "get_file", but always raises an exception on error.""" | |
| 83 resp = self.get_file(*args, **kwargs) | |
| 84 resp.raise_on_error() | |
| 85 return resp | |
| 86 | |
| 87 def get_text(self, url, step_name=None, headers=None, transient_retry=True, | |
| 88 **kwargs): | |
| 89 """GET data at given URL and writes it to file. | |
| 90 | |
| 91 Args: | |
| 92 url: URL to request. | |
| 93 step_name: optional step name, 'fetch <url>' by default. | |
| 94 headers: a {header_name: value} dictionary for HTTP headers. | |
| 95 transient_retry (bool): If True (default), transient HTTP errors (>500) | |
| 96 will automatically be retried with exponential backoff. If False, | |
| 97 exactly one attempt will be made. | |
| 98 | |
| 99 Returns: | |
| 100 Response with a string "output" value. | |
| 101 """ | |
| 102 return self._get_step(url, None, step_name, headers, transient_retry, | |
| 103 None, False, **kwargs) | |
| 104 | |
| 105 def must_get_text(self, *args, **kwargs): | |
| 106 """Like "get_text", but always raises an exception on error.""" | |
| 107 resp = self.get_text(*args, **kwargs) | |
| 108 resp.raise_on_error() | |
| 109 return resp | |
| 110 | |
| 111 def get_json(self, url, step_name=None, headers=None, transient_retry=True, | |
| 112 strip_prefix=None, log=False, **kwargs): | |
| 
iannucci
2017/05/12 00:53:52
no kwargs just passthrough timeout
 
dnj
2017/05/12 02:15:04
Done.
 | |
| 113 """GET data at given URL and writes it to file. | |
| 114 | |
| 115 Args: | |
| 116 url: URL to request. | |
| 117 step_name: optional step name, 'fetch <url>' by default. | |
| 118 headers: a {header_name: value} dictionary for HTTP headers. | |
| 119 transient_retry (bool): If True (default), transient HTTP errors (>500) | |
| 120 will automatically be retried with exponential backoff. If False, | |
| 121 exactly one attempt will be made. | |
| 122 strip_prefix (str or None): If not None, this prefix must be present at | |
| 123 the beginning of the response, and will be stripped from the resulting | |
| 124 content (e.g., GERRIT_JSON_PREFIX). | |
| 125 log (bool): If True, emit the JSON content as a log. | |
| 126 | |
| 127 Returns: | |
| 128 Response with JSON "output" value. | |
| 129 """ | |
| 130 return self._get_step(url, None, step_name, headers, transient_retry, | |
| 131 strip_prefix, 'log' if log else True, **kwargs) | |
| 132 | |
| 133 def must_get_json(self, *args, **kwargs): | |
| 134 """Like "get_json", but always raises an exception on error.""" | |
| 135 resp = self.get_json(*args, **kwargs) | |
| 136 resp.raise_on_error() | |
| 137 return resp | |
| 138 | |
| 139 def _get_step(self, url, path, step_name, headers, transient_retry, | |
| 140 strip_prefix, as_json, **kwargs): | |
| 141 step_name = step_name or 'GET %s' % url | |
| 142 | |
| 143 args = [ | |
| 144 url, | |
| 145 '--status-json', self.m.json.output(add_json_log=False, | |
| 146 name='status_json'), | |
| 147 ] | |
| 148 | |
| 149 if as_json: | |
| 150 log = as_json == 'log' | |
| 151 args += ['--outfile', self.m.json.output(add_json_log=log, | |
| 152 name='output')] | |
| 153 else: | |
| 154 args += ['--outfile', self.m.raw_io.output_text(leak_to=path, | |
| 155 name='output')] | |
| 156 | |
| 157 if headers: | |
| 158 args += ['--headers-json', self.m.json.input(headers)] | |
| 159 if strip_prefix: | |
| 160 args += ['--strip-prefix', strip_prefix] | |
| 161 if not transient_retry: | |
| 162 args.append('--no-transient-retry') | |
| 163 | |
| 164 result = self.m.python( | |
| 165 step_name, | |
| 166 self.resource('pycurl.py'), | |
| 167 args=args, | |
| 168 venv=True, | |
| 169 **kwargs) | |
| 170 status = result.json.outputs['status_json'] | |
| 171 | |
| 172 output = path | |
| 173 if not output: | |
| 174 if as_json: | |
| 175 output = result.json.outputs['output'] | |
| 176 else: | |
| 177 output = result.raw_io.output_texts['output'] | |
| 178 return self.Response('GET', output, status) | |
| OLD | NEW |