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

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

Issue 2672593002: raw_io module: Add text_input and text_output (Closed)
Patch Set: Add comment about no cover Created 3 years, 10 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/raw_io/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 # -*- encoding: utf-8 -*-
1 # Copyright 2014 The LUCI Authors. All rights reserved. 2 # Copyright 2014 The LUCI Authors. All rights reserved.
2 # Use of this source code is governed under the Apache License, Version 2.0 3 # Use of this source code is governed under the Apache License, Version 2.0
3 # that can be found in the LICENSE file. 4 # that can be found in the LICENSE file.
4 5
5 from recipe_engine import recipe_api 6 from recipe_engine import recipe_api
6 from recipe_engine import util as recipe_util 7 from recipe_engine import util as recipe_util
7 8
8 import os 9 import os
9 import shutil 10 import shutil
10 import tempfile 11 import tempfile
11 12
12 13
13 class InputDataPlaceholder(recipe_util.InputPlaceholder): 14 class InputDataPlaceholder(recipe_util.InputPlaceholder):
14 def __init__(self, data, suffix): 15 def __init__(self, data, suffix):
15 assert isinstance(data, basestring)
16 self.data = data 16 self.data = data
17 self.suffix = suffix 17 self.suffix = suffix
18 self._backing_file = None 18 self._backing_file = None
19 super(InputDataPlaceholder, self).__init__() 19 super(InputDataPlaceholder, self).__init__()
20 20
21 @property 21 @property
22 def backing_file(self): 22 def backing_file(self):
23 return self._backing_file 23 return self._backing_file
24 24
25 def render(self, test): 25 def render(self, test):
26 assert not self._backing_file, 'Placeholder can be used only once' 26 assert not self._backing_file, 'Placeholder can be used only once'
27 if test.enabled: 27 if test.enabled:
28 # cheat and pretend like we're going to pass the data on the 28 # cheat and pretend like we're going to pass the data on the
29 # cmdline for test expectation purposes. 29 # cmdline for test expectation purposes.
30 self._backing_file = self.data 30 self._backing_file = self.data
31 else: # pragma: no cover 31 else: # pragma: no cover
32 input_fd, self._backing_file = tempfile.mkstemp(suffix=self.suffix) 32 input_fd, self._backing_file = tempfile.mkstemp(suffix=self.suffix)
33 os.write(input_fd, self.data.encode('utf-8')) 33
34 os.write(input_fd, self.encode(self.data))
34 os.close(input_fd) 35 os.close(input_fd)
35 return [self._backing_file] 36 return [self._backing_file]
36 37
37 def cleanup(self, test_enabled): 38 def cleanup(self, test_enabled):
38 assert self._backing_file is not None 39 assert self._backing_file is not None
39 if not test_enabled: # pragma: no cover 40 if not test_enabled: # pragma: no cover
40 try: 41 try:
41 os.unlink(self._backing_file) 42 os.unlink(self._backing_file)
42 except OSError: 43 except OSError:
43 pass 44 pass
44 self._backing_file = None 45 self._backing_file = None
45 46
47 # Not covered because it's called in a branch in render() which isn't tested
48 # as well.
49 def encode(self, data): # pragma: no cover
50 """ Encodes data to be written out, when rendering this placeholder.
51 """
52 return data
53
54 class InputTextPlaceholder(InputDataPlaceholder):
55 """ A input placeholder which expects to write out text.
56 """
57 def __init__(self, data, suffix):
58 super(InputTextPlaceholder, self).__init__(data, suffix)
59 assert isinstance(data, basestring)
60
61 # Not covered because it's called in a branch in render() which isn't tested
62 # as well.
63 def encode(self, data): # pragma: no cover
64 # Sometimes users give us invalid utf-8 data. They shouldn't, but it does
65 # happen every once and a while. Just ignore it, and replace with �.
66 # We're assuming users only want to write text data out.
67 decoded = self.data.decode('utf-8', 'replace')
68 return decoded.encode('utf-8')
69
70
46 71
47 class OutputDataPlaceholder(recipe_util.OutputPlaceholder): 72 class OutputDataPlaceholder(recipe_util.OutputPlaceholder):
48 def __init__(self, suffix, leak_to, name=None): 73 def __init__(self, suffix, leak_to, name=None):
49 self.suffix = suffix 74 self.suffix = suffix
50 self.leak_to = leak_to 75 self.leak_to = leak_to
51 self._backing_file = None 76 self._backing_file = None
52 super(OutputDataPlaceholder, self).__init__(name=name) 77 super(OutputDataPlaceholder, self).__init__(name=name)
53 78
54 @property 79 @property
55 def backing_file(self): 80 def backing_file(self):
(...skipping 12 matching lines...) Expand all
68 return [self._backing_file] 93 return [self._backing_file]
69 94
70 def result(self, presentation, test): 95 def result(self, presentation, test):
71 assert self._backing_file 96 assert self._backing_file
72 if test.enabled: 97 if test.enabled:
73 self._backing_file = None 98 self._backing_file = None
74 return test.data 99 return test.data
75 else: # pragma: no cover 100 else: # pragma: no cover
76 try: 101 try:
77 with open(self._backing_file, 'rb') as f: 102 with open(self._backing_file, 'rb') as f:
78 return f.read() 103 return self.decode(f.read())
79 finally: 104 finally:
80 if not self.leak_to: 105 if not self.leak_to:
81 os.unlink(self._backing_file) 106 os.unlink(self._backing_file)
82 self._backing_file = None 107 self._backing_file = None
83 108
109 # Not covered because it's called in a branch in result() which isn't tested
110 # as well.
111 def decode(self, result): # pragma: no cover
112 """ Decodes data to be read in, when getting the result of this placeholder.
113 """
114 return result
115
116 class OutputTextPlaceholder(OutputDataPlaceholder):
117 """ A output placeholder which expects to write out text.
118 """
119 # Not covered because it's called in a branch in result() which isn't tested
120 # as well.
121 def decode(self, result): # pragma: no cover
122 return result.decode('utf-8', 'replace')
84 123
85 class OutputDataDirPlaceholder(recipe_util.OutputPlaceholder): 124 class OutputDataDirPlaceholder(recipe_util.OutputPlaceholder):
86 def __init__(self, suffix, leak_to, name=None): 125 def __init__(self, suffix, leak_to, name=None):
87 self.suffix = suffix 126 self.suffix = suffix
88 self.leak_to = leak_to 127 self.leak_to = leak_to
89 self._backing_dir = None 128 self._backing_dir = None
90 super(OutputDataDirPlaceholder, self).__init__(name=name) 129 super(OutputDataDirPlaceholder, self).__init__(name=name)
91 130
92 @property 131 @property
93 def backing_file(self): # pragma: no cover 132 def backing_file(self): # pragma: no cover
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
127 finally: 166 finally:
128 if not self.leak_to: 167 if not self.leak_to:
129 shutil.rmtree(self._backing_dir) 168 shutil.rmtree(self._backing_dir)
130 self._backing_dir = None 169 self._backing_dir = None
131 170
132 171
133 class RawIOApi(recipe_api.RecipeApi): 172 class RawIOApi(recipe_api.RecipeApi):
134 @recipe_util.returns_placeholder 173 @recipe_util.returns_placeholder
135 @staticmethod 174 @staticmethod
136 def input(data, suffix=''): 175 def input(data, suffix=''):
176 """Returns a Placeholder for use as a step argument.
177
178 This placeholder can be used to pass data to steps. The recipe engine will
179 dump the 'data' into a file, and pass the filename to the command line
180 argument.
181
182 If 'suffix' is not '', it will be used when the engine calls
183 tempfile.mkstemp.
184
185 See example.py for usage example.
186 """
137 return InputDataPlaceholder(data, suffix) 187 return InputDataPlaceholder(data, suffix)
138 188
139 @recipe_util.returns_placeholder 189 @recipe_util.returns_placeholder
140 @staticmethod 190 @staticmethod
191 def input_text(data, suffix=''):
192 """Returns a Placeholder for use as a step argument.
193
194 Similar to input(), but tries to encode 'data' as utf-8 text, replacing
195 any encoding and decoding errors with �.
196 """
197 return InputTextPlaceholder(data, suffix)
198
199 @recipe_util.returns_placeholder
200 @staticmethod
141 def output(suffix='', leak_to=None, name=None): 201 def output(suffix='', leak_to=None, name=None):
142 """Returns a Placeholder for use as a step argument, or for std{out,err}. 202 """Returns a Placeholder for use as a step argument, or for std{out,err}.
143 203
144 If 'leak_to' is None, the placeholder is backed by a temporary file with 204 If 'leak_to' is None, the placeholder is backed by a temporary file with
145 a suffix 'suffix'. The file is deleted when the step finishes. 205 a suffix 'suffix'. The file is deleted when the step finishes.
146 206
147 If 'leak_to' is not None, then it should be a Path and placeholder 207 If 'leak_to' is not None, then it should be a Path and placeholder
148 redirects IO to a file at that path. Once step finishes, the file is 208 redirects IO to a file at that path. Once step finishes, the file is
149 NOT deleted (i.e. it's 'leaking'). 'suffix' is ignored in that case. 209 NOT deleted (i.e. it's 'leaking'). 'suffix' is ignored in that case.
150 """ 210 """
151 return OutputDataPlaceholder(suffix, leak_to, name=name) 211 return OutputDataPlaceholder(suffix, leak_to, name=name)
152 212
153 @recipe_util.returns_placeholder 213 @recipe_util.returns_placeholder
154 @staticmethod 214 @staticmethod
215 def output_text(suffix='', leak_to=None, name=None):
216 """Returns a Placeholder for use as a step argument, or for std{out,err}.
217
218 Similar to output(), but uses an OutputTextPlaceholder, which expects utf-8
219 encoded text.
220 Similar to input(), but tries to decode the resulting data as utf-8 text,
221 replacing any decoding errors with �.
222 """
223 return OutputTextPlaceholder(suffix, leak_to, name=name)
224
225 @recipe_util.returns_placeholder
226 @staticmethod
155 def output_dir(suffix='', leak_to=None, name=None): 227 def output_dir(suffix='', leak_to=None, name=None):
156 """Returns a directory Placeholder for use as a step argument. 228 """Returns a directory Placeholder for use as a step argument.
157 229
158 If 'leak_to' is None, the placeholder is backed by a temporary dir with 230 If 'leak_to' is None, the placeholder is backed by a temporary dir with
159 a suffix 'suffix'. The dir is deleted when the step finishes. 231 a suffix 'suffix'. The dir is deleted when the step finishes.
160 232
161 If 'leak_to' is not None, then it should be a Path and placeholder 233 If 'leak_to' is not None, then it should be a Path and placeholder
162 redirects IO to a dir at that path. Once step finishes, the dir is 234 redirects IO to a dir at that path. Once step finishes, the dir is
163 NOT deleted (i.e. it's 'leaking'). 'suffix' is ignored in that case. 235 NOT deleted (i.e. it's 'leaking'). 'suffix' is ignored in that case.
164 """ 236 """
165 return OutputDataDirPlaceholder(suffix, leak_to, name=name) 237 return OutputDataDirPlaceholder(suffix, leak_to, name=name)
OLDNEW
« no previous file with comments | « no previous file | recipe_modules/raw_io/example.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698