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

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 pragma no covers. 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 def encode(self, data): # pragma: no cover
iannucci 2017/02/07 22:18:23 why uncovered?
martiniss 2017/02/08 00:25:56 They are called from uncovered code; line 32. I'll
48 """ Encodes data to be written out, when rendering this placeholder.
49 """
50 return data
51
52 class InputTextPlaceholder(InputDataPlaceholder):
53 """ A input placeholder which expects to write out text.
54 """
55 def __init__(self, data, suffix):
56 super(InputTextPlaceholder, self).__init__(data, suffix)
57 assert isinstance(data, basestring)
58
59 def encode(self, data): # pragma: no cover
60 # Sometimes users give us invalid utf-8 data. They shouldn't, but it does
61 # happen every once and a while. Just ignore it, and replace with �.
62 # We're assuming users only want to write text data out.
63 decoded = self.data.decode('utf-8', 'replace')
64 return decoded.encode('utf-8')
65
66
46 67
47 class OutputDataPlaceholder(recipe_util.OutputPlaceholder): 68 class OutputDataPlaceholder(recipe_util.OutputPlaceholder):
48 def __init__(self, suffix, leak_to, name=None): 69 def __init__(self, suffix, leak_to, name=None):
49 self.suffix = suffix 70 self.suffix = suffix
50 self.leak_to = leak_to 71 self.leak_to = leak_to
51 self._backing_file = None 72 self._backing_file = None
52 super(OutputDataPlaceholder, self).__init__(name=name) 73 super(OutputDataPlaceholder, self).__init__(name=name)
53 74
54 @property 75 @property
55 def backing_file(self): 76 def backing_file(self):
(...skipping 12 matching lines...) Expand all
68 return [self._backing_file] 89 return [self._backing_file]
69 90
70 def result(self, presentation, test): 91 def result(self, presentation, test):
71 assert self._backing_file 92 assert self._backing_file
72 if test.enabled: 93 if test.enabled:
73 self._backing_file = None 94 self._backing_file = None
74 return test.data 95 return test.data
75 else: # pragma: no cover 96 else: # pragma: no cover
76 try: 97 try:
77 with open(self._backing_file, 'rb') as f: 98 with open(self._backing_file, 'rb') as f:
78 return f.read() 99 return self.decode(f.read())
79 finally: 100 finally:
80 if not self.leak_to: 101 if not self.leak_to:
81 os.unlink(self._backing_file) 102 os.unlink(self._backing_file)
82 self._backing_file = None 103 self._backing_file = None
83 104
105 def decode(self, result): # pragma: no cover
iannucci 2017/02/07 22:18:23 why uncovered?
106 """ Decodes data to be read in, when getting the result of this placeholder.
107 """
108 return result
109
110 class OutputTextPlaceholder(OutputDataPlaceholder):
111 """ A output placeholder which expects to write out text.
112 """
113 def decode(self, result): # pragma: no cover
114 return result.decode('utf-8', 'replace')
84 115
85 class OutputDataDirPlaceholder(recipe_util.OutputPlaceholder): 116 class OutputDataDirPlaceholder(recipe_util.OutputPlaceholder):
86 def __init__(self, suffix, leak_to, name=None): 117 def __init__(self, suffix, leak_to, name=None):
87 self.suffix = suffix 118 self.suffix = suffix
88 self.leak_to = leak_to 119 self.leak_to = leak_to
89 self._backing_dir = None 120 self._backing_dir = None
90 super(OutputDataDirPlaceholder, self).__init__(name=name) 121 super(OutputDataDirPlaceholder, self).__init__(name=name)
91 122
92 @property 123 @property
93 def backing_file(self): # pragma: no cover 124 def backing_file(self): # pragma: no cover
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
127 finally: 158 finally:
128 if not self.leak_to: 159 if not self.leak_to:
129 shutil.rmtree(self._backing_dir) 160 shutil.rmtree(self._backing_dir)
130 self._backing_dir = None 161 self._backing_dir = None
131 162
132 163
133 class RawIOApi(recipe_api.RecipeApi): 164 class RawIOApi(recipe_api.RecipeApi):
134 @recipe_util.returns_placeholder 165 @recipe_util.returns_placeholder
135 @staticmethod 166 @staticmethod
136 def input(data, suffix=''): 167 def input(data, suffix=''):
168 """Returns a Placeholder for use as a step argument.
169
170 This placeholder can be used to pass data to steps. The recipe engine will
171 dump the 'data' into a file, and pass the filename to the command line
172 argument.
173
174 If 'suffix' is not '', it will be used when the engine calls
175 tempfile.mkstemp.
176
177 See example.py for usage example.
178 """
137 return InputDataPlaceholder(data, suffix) 179 return InputDataPlaceholder(data, suffix)
138 180
139 @recipe_util.returns_placeholder 181 @recipe_util.returns_placeholder
140 @staticmethod 182 @staticmethod
183 def input_text(data, suffix=''):
184 """Returns a Placeholder for use as a step argument.
185
186 Similar to input(), but tries to encode 'data' as utf-8 text, replacing
187 any encoding and decoding errors with �.
188 """
189 return InputTextPlaceholder(data, suffix)
190
191 @recipe_util.returns_placeholder
192 @staticmethod
141 def output(suffix='', leak_to=None, name=None): 193 def output(suffix='', leak_to=None, name=None):
142 """Returns a Placeholder for use as a step argument, or for std{out,err}. 194 """Returns a Placeholder for use as a step argument, or for std{out,err}.
143 195
144 If 'leak_to' is None, the placeholder is backed by a temporary file with 196 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. 197 a suffix 'suffix'. The file is deleted when the step finishes.
146 198
147 If 'leak_to' is not None, then it should be a Path and placeholder 199 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 200 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. 201 NOT deleted (i.e. it's 'leaking'). 'suffix' is ignored in that case.
150 """ 202 """
151 return OutputDataPlaceholder(suffix, leak_to, name=name) 203 return OutputDataPlaceholder(suffix, leak_to, name=name)
152 204
153 @recipe_util.returns_placeholder 205 @recipe_util.returns_placeholder
154 @staticmethod 206 @staticmethod
207 def output_text(suffix='', leak_to=None, name=None):
208 """Returns a Placeholder for use as a step argument, or for std{out,err}.
209
210 Similar to output(), but uses an OutputTextPlaceholder, which expects utf-8
211 encoded text.
212 Similar to input(), but tries to decode the resulting data as utf-8 text,
213 replacing any decoding errors with �.
214 """
215 return OutputTextPlaceholder(suffix, leak_to, name=name)
216
217 @recipe_util.returns_placeholder
218 @staticmethod
155 def output_dir(suffix='', leak_to=None, name=None): 219 def output_dir(suffix='', leak_to=None, name=None):
156 """Returns a directory Placeholder for use as a step argument. 220 """Returns a directory Placeholder for use as a step argument.
157 221
158 If 'leak_to' is None, the placeholder is backed by a temporary dir with 222 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. 223 a suffix 'suffix'. The dir is deleted when the step finishes.
160 224
161 If 'leak_to' is not None, then it should be a Path and placeholder 225 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 226 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. 227 NOT deleted (i.e. it's 'leaking'). 'suffix' is ignored in that case.
164 """ 228 """
165 return OutputDataDirPlaceholder(suffix, leak_to, name=name) 229 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