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

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

Issue 1773273003: Make output placeholders like json.output index-able by name. (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/recipes-py@master
Patch Set: Created 4 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
OLDNEW
1 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # 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.Placeholder): 15 class JsonOutputPlaceholder(recipe_util.Placeholder):
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
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): 30 def __init__(self, api, add_json_log, id=id):
iannucci 2016/03/10 03:17:42 we should use something other than 'id', which is
stgao 2016/03/10 20:34:23 Sounds good. Note: PlaceHolder already has a prop
31 self.raw = api.m.raw_io.output('.json') 31 self.raw = api.m.raw_io.output('.json', id=id)
32 self.add_json_log = add_json_log 32 self.add_json_log = add_json_log
33 super(JsonOutputPlaceholder, self).__init__() 33 super(JsonOutputPlaceholder, self).__init__(id=id)
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.name + ('' if valid else ' (invalid)') 57 # Use the combination of id and name to differentiate multiple
58 # json.outputs in the same step. Otherwise, only the last one shows up
59 # as the log.
60 key = self.id_name + ('' if valid else ' (invalid)')
58 with contextlib.closing(recipe_util.StringListIO()) as listio: 61 with contextlib.closing(recipe_util.StringListIO()) as listio:
59 json.dump(ret, listio, indent=2, sort_keys=True) 62 json.dump(ret, listio, indent=2, sort_keys=True)
60 presentation.logs[key] = listio.lines 63 presentation.logs[key] = listio.lines
61 64
62 return ret 65 return ret
63 66
64 67
65 class JsonApi(recipe_api.RecipeApi): 68 class JsonApi(recipe_api.RecipeApi):
66 def __init__(self, **kwargs): 69 def __init__(self, **kwargs):
67 super(JsonApi, self).__init__(**kwargs) 70 super(JsonApi, self).__init__(**kwargs)
(...skipping 27 matching lines...) Expand all
95 98
96 def is_serializable(self, obj): 99 def is_serializable(self, obj):
97 """Returns True if the object is JSON-serializable.""" 100 """Returns True if the object is JSON-serializable."""
98 try: 101 try:
99 self.dumps(obj) 102 self.dumps(obj)
100 return True 103 return True
101 except Exception: 104 except Exception:
102 return False 105 return False
103 106
104 @recipe_util.returns_placeholder 107 @recipe_util.returns_placeholder
105 def input(self, data): 108 def input(self, data, id=None):
106 """A placeholder which will expand to a file path containing <data>.""" 109 """A placeholder which will expand to a file path containing <data>."""
107 return self.m.raw_io.input(self.dumps(data), '.json') 110 return self.m.raw_io.input(self.dumps(data), '.json', id=id)
108 111
109 @recipe_util.returns_placeholder 112 @recipe_util.returns_placeholder
110 def output(self, add_json_log=True): 113 def output(self, add_json_log=True, id=None):
111 """A placeholder which will expand to '/tmp/file'.""" 114 """A placeholder which will expand to '/tmp/file'."""
112 return JsonOutputPlaceholder(self, add_json_log) 115 return JsonOutputPlaceholder(self, add_json_log, id=id)
113 116
114 # TODO(you): This method should be in the `file` recipe_module 117 # TODO(you): This method should be in the `file` recipe_module
115 def read(self, name, path, add_json_log=True, **kwargs): 118 def read(self, name, path, add_json_log=True, id=None, **kwargs):
116 """Returns a step that reads a JSON file.""" 119 """Returns a step that reads a JSON file."""
117 return self.m.python.inline( 120 return self.m.python.inline(
118 name, 121 name,
119 """ 122 """
120 import shutil 123 import shutil
121 import sys 124 import sys
122 shutil.copy(sys.argv[1], sys.argv[2]) 125 shutil.copy(sys.argv[1], sys.argv[2])
123 """, 126 """,
124 args=[path, self.output(add_json_log=add_json_log)], 127 args=[path, self.output(add_json_log=add_json_log, id=id)],
125 add_python_log=False, 128 add_python_log=False,
126 **kwargs 129 **kwargs
127 ) 130 )
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698