Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(225)

Side by Side Diff: recipe_modules/json/api.py

Issue 2750793002: [json] improve output to get leak_to and better logging. (Closed)
Patch Set: Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | recipe_modules/json/example.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2013 The LUCI Authors. All rights reserved. 1 # Copyright 2013 The LUCI Authors. All rights reserved.
2 # Use of this source code is governed under the Apache License, Version 2.0 2 # Use of this source code is governed under the Apache License, Version 2.0
3 # that can be found in the LICENSE file. 3 # that can be found in the LICENSE file.
4 4
5 import functools 5 import functools
6 import collections 6 import collections
7 import contextlib 7 import contextlib
8 import json 8 import json
9 9
10 from recipe_engine import recipe_api 10 from recipe_engine import recipe_api
11 from recipe_engine import util as recipe_util 11 from recipe_engine import util as recipe_util
12 from recipe_engine import config_types 12 from recipe_engine import config_types
13 13
14 14
15 class JsonOutputPlaceholder(recipe_util.OutputPlaceholder): 15 class JsonOutputPlaceholder(recipe_util.OutputPlaceholder):
16 """JsonOutputPlaceholder is meant to be a placeholder object which, when added 16 """JsonOutputPlaceholder is meant to be a placeholder object which, when added
17 to a step's cmd list, will be replaced by annotated_run with the path to a 17 to a step's cmd list, will be replaced by annotated_run with the path to a
18 temporary file (e.g. /tmp/tmp4lp1qM) which will exist only for the duration of 18 temporary file (e.g. /tmp/tmp4lp1qM) which will exist only for the duration of
tikuta1 2017/03/14 09:55:31 Better to update comment near here?
iannucci 2017/03/14 10:02:01 Oh, wow, this comment is very out of date! I've fi
19 the step. If the script requires a flag (e.g. --output-json /path/to/file), 19 the step. If the script requires a flag (e.g. --output-json /path/to/file),
20 you must supply that flag yourself in the cmd list. 20 you must supply that flag yourself in the cmd list.
21 21
22 This placeholder can be optionally added when you use the Steps.step() 22 This placeholder can be optionally added when you use the Steps.step()
23 method in this module. 23 method in this module.
24 24
25 FIXME 25 FIXME
26 After the termination of the step, this file is expected to contain a valid 26 After the termination of the step, this file is expected to contain a valid
27 JSON document, which will be set as the json.output for that step in the 27 JSON document, which will be set as the json.output for that step in the
28 step_history OrderedDict passed to your recipe generator. 28 step_history OrderedDict passed to your recipe generator.
29 """ 29 """
30 def __init__(self, api, add_json_log, name=None): 30 def __init__(self, api, add_json_log, name=None, leak_to=None):
31 self.raw = api.m.raw_io.output_text('.json') 31 self.raw = api.m.raw_io.output_text('.json', leak_to=leak_to)
32 self.add_json_log = add_json_log 32 self.add_json_log = add_json_log
33 super(JsonOutputPlaceholder, self).__init__(name=name) 33 super(JsonOutputPlaceholder, self).__init__(name=name)
34 34
35 @property 35 @property
36 def backing_file(self): 36 def backing_file(self):
37 return self.raw.backing_file 37 return self.raw.backing_file
38 38
39 def render(self, test): 39 def render(self, test):
40 return self.raw.render(test) 40 return self.raw.render(test)
41 41
42 def result(self, presentation, test): 42 def result(self, presentation, test):
43 raw_data = self.raw.result(presentation, test) 43 raw_data = self.raw.result(presentation, test)
44 44
45 valid = False 45 valid = False
46 ret = None 46 ret = None
47 try: 47 try:
48 ret = JsonApi.loads( 48 ret = JsonApi.loads(
49 raw_data, object_pairs_hook=collections.OrderedDict) 49 raw_data, object_pairs_hook=collections.OrderedDict)
50 valid = True 50 valid = True
51 # TypeError is raised when raw_data is None, which can happen if the json 51 # TypeError is raised when raw_data is None, which can happen if the json
52 # file was not created. We then correctly handle this as invalid result. 52 # file was not created. We then correctly handle this as invalid result.
53 except (ValueError, TypeError): # pragma: no cover 53 except (ValueError, TypeError): # pragma: no cover
54 pass 54 pass
55 55
56 if self.add_json_log: 56 if self.add_json_log:
57 key = self.label + ('' if valid else ' (invalid)') 57 if valid:
58 with contextlib.closing(recipe_util.StringListIO()) as listio: 58 with contextlib.closing(recipe_util.StringListIO()) as listio:
59 json.dump(ret, listio, indent=2, sort_keys=True) 59 json.dump(ret, listio, indent=2, sort_keys=True)
60 presentation.logs[key] = listio.lines 60 presentation.logs[self.label] = listio.lines
61 else:
62 presentation.logs[self.label + ' (invalid)'] = raw_data.splitlines()
61 63
62 return ret 64 return ret
63 65
64 66
65 class JsonApi(recipe_api.RecipeApi): 67 class JsonApi(recipe_api.RecipeApi):
66 def __init__(self, **kwargs): 68 def __init__(self, **kwargs):
67 super(JsonApi, self).__init__(**kwargs) 69 super(JsonApi, self).__init__(**kwargs)
68 @functools.wraps(json.dumps) 70 @functools.wraps(json.dumps)
69 def dumps(*args, **kwargs): 71 def dumps(*args, **kwargs):
70 kwargs['sort_keys'] = True 72 kwargs['sort_keys'] = True
(...skipping 26 matching lines...) Expand all
97 return True 99 return True
98 except Exception: 100 except Exception:
99 return False 101 return False
100 102
101 @recipe_util.returns_placeholder 103 @recipe_util.returns_placeholder
102 def input(self, data): 104 def input(self, data):
103 """A placeholder which will expand to a file path containing <data>.""" 105 """A placeholder which will expand to a file path containing <data>."""
104 return self.m.raw_io.input_text(self.dumps(data), '.json') 106 return self.m.raw_io.input_text(self.dumps(data), '.json')
105 107
106 @recipe_util.returns_placeholder 108 @recipe_util.returns_placeholder
107 def output(self, add_json_log=True, name=None): 109 def output(self, add_json_log=True, name=None, leak_to=None):
108 """A placeholder which will expand to '/tmp/file'.""" 110 """A placeholder which will expand to '/tmp/file'.
109 return JsonOutputPlaceholder(self, add_json_log, name=name) 111
112 If leak_to is provided, it must be a Path object. This path will be used in
113 place of a random temporary file, and the file will not be deleted at the
114 end of the step.
115 """
116 return JsonOutputPlaceholder(self, add_json_log, name=name, leak_to=leak_to)
110 117
111 # TODO(you): This method should be in the `file` recipe_module 118 # TODO(you): This method should be in the `file` recipe_module
112 def read(self, name, path, add_json_log=True, output_name=None, **kwargs): 119 def read(self, name, path, add_json_log=True, output_name=None, **kwargs):
113 """Returns a step that reads a JSON file.""" 120 """Returns a step that reads a JSON file."""
114 return self.m.python.inline( 121 return self.m.python.inline(
115 name, 122 name,
116 """ 123 """
117 import shutil 124 import shutil
118 import sys 125 import sys
119 shutil.copy(sys.argv[1], sys.argv[2]) 126 shutil.copy(sys.argv[1], sys.argv[2])
120 """, 127 """,
121 args=[path, 128 args=[path,
122 self.output(add_json_log=add_json_log, name=output_name)], 129 self.output(add_json_log=add_json_log, name=output_name)],
123 add_python_log=False, 130 add_python_log=False,
124 **kwargs 131 **kwargs
125 ) 132 )
OLDNEW
« no previous file with comments | « no previous file | recipe_modules/json/example.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698