| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 4674163 commit a5db3d3
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -44,6 +44,7 @@ | |||
| 44 | 44 | is_win, | |
| 45 | 45 | ) | |
| 46 | 46 | import io | |
| 47 | + from _io import UnsupportedOperation | ||
| 47 | 48 | ||
| 48 | 49 | execute_kwargs = set(('istream', 'with_keep_cwd', 'with_extended_output', | |
| 49 | 50 | 'with_exceptions', 'as_process', 'stdout_as_string', | |
@@ -56,7 +57,7 @@ | |||
| 56 | 57 | __all__ = ('Git',) | |
| 57 | 58 | ||
| 58 | 59 | if is_win: | |
| 59 | - WindowsError = OSError | ||
| 60 | + WindowsError = OSError # @ReservedAssignment | ||
| 60 | 61 | ||
| 61 | 62 | if PY3: | |
| 62 | 63 | _bchr = bchr | |
@@ -72,7 +73,8 @@ def _bchr(c): | |||
| 72 | 73 | # Documentation | |
| 73 | 74 | ## @{ | |
| 74 | 75 | ||
| 75 | - def handle_process_output(process, stdout_handler, stderr_handler, finalizer): | ||
| 76 | + def handle_process_output(process, stdout_handler, stderr_handler, finalizer, | ||
| 77 | + decode_stdout=True, decode_stderr=True): | ||
| 76 | 78 | """Registers for notifications to lean that process output is ready to read, and dispatches lines to | |
| 77 | 79 | the respective line handlers. We are able to handle carriage returns in case progress is sent by that | |
| 78 | 80 | mean. For performance reasons, we only apply this to stderr. | |
@@ -82,8 +84,6 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer): | |||
| 82 | 84 | :param stdout_handler: f(stdout_line_string), or None | |
| 83 | 85 | :param stderr_hanlder: f(stderr_line_string), or None | |
| 84 | 86 | :param finalizer: f(proc) - wait for proc to finish""" | |
| 85 | - fdmap = {process.stdout.fileno(): (stdout_handler, [b'']), | ||
| 86 | - process.stderr.fileno(): (stderr_handler, [b''])} | ||
| 87 | 87 | ||
| 88 | 88 | def _parse_lines_from_buffer(buf): | |
| 89 | 89 | line = b'' | |
@@ -94,7 +94,7 @@ def _parse_lines_from_buffer(buf): | |||
| 94 | 94 | bi += 1 | |
| 95 | 95 | ||
| 96 | 96 | if char in (b'\r', b'\n') and line: | |
| 97 | - yield bi, line | ||
| 97 | + yield bi, line + b'\n' | ||
| 98 | 98 | line = b'' | |
| 99 | 99 | else: | |
| 100 | 100 | line += char | |
@@ -114,105 +114,111 @@ def _read_lines_from_fno(fno, last_buf_list): | |||
| 114 | 114 | # keep remainder | |
| 115 | 115 | last_buf_list[0] = buf[bi:] | |
| 116 | 116 | ||
| 117 | - def _dispatch_single_line(line, handler): | ||
| 118 | - line = line.decode(defenc) | ||
| 117 | + def _dispatch_single_line(line, handler, decode): | ||
| 118 | + if decode: | ||
| 119 | + line = line.decode(defenc) | ||
| 119 | 120 | if line and handler: | |
| 120 | 121 | handler(line) | |
| 121 | 122 | # end dispatch helper | |
| 122 | 123 | # end single line helper | |
| 123 | 124 | ||
| 124 | - def _dispatch_lines(fno, handler, buf_list): | ||
| 125 | + def _dispatch_lines(fno, handler, buf_list, decode): | ||
| 125 | 126 | lc = 0 | |
| 126 | 127 | for line in _read_lines_from_fno(fno, buf_list): | |
| 127 | - _dispatch_single_line(line, handler) | ||
| 128 | + _dispatch_single_line(line, handler, decode) | ||
| 128 | 129 | lc += 1 | |
| 129 | 130 | # for each line | |
| 130 | 131 | return lc | |
| 131 | 132 | # end | |
| 132 | 133 | ||
| 133 | - def _deplete_buffer(fno, handler, buf_list, wg=None): | ||
| 134 | + def _deplete_buffer(fno, handler, buf_list, decode): | ||
| 134 | 135 | lc = 0 | |
| 135 | 136 | while True: | |
| 136 | - line_count = _dispatch_lines(fno, handler, buf_list) | ||
| 137 | + line_count = _dispatch_lines(fno, handler, buf_list, decode) | ||
| 137 | 138 | lc += line_count | |
| 138 | 139 | if line_count == 0: | |
| 139 | 140 | break | |
| 140 | 141 | # end deplete buffer | |
| 141 | 142 | ||
| 142 | 143 | if buf_list[0]: | |
| 143 | - _dispatch_single_line(buf_list[0], handler) | ||
| 144 | + _dispatch_single_line(buf_list[0], handler, decode) | ||
| 144 | 145 | lc += 1 | |
| 145 | 146 | # end | |
| 146 | 147 | ||
| 147 | - if wg: | ||
| 148 | - wg.done() | ||
| 149 | - | ||
| 150 | 148 | return lc | |
| 151 | 149 | # end | |
| 152 | 150 | ||
| 153 | - if hasattr(select, 'poll'): | ||
| 154 | - # poll is preferred, as select is limited to file handles up to 1024 ... . This could otherwise be | ||
| 155 | - # an issue for us, as it matters how many handles our own process has | ||
| 156 | - poll = select.poll() | ||
| 157 | - READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR | ||
| 158 | - CLOSED = select.POLLHUP | select.POLLERR | ||
| 159 | - | ||
| 160 | - poll.register(process.stdout, READ_ONLY) | ||
| 161 | - poll.register(process.stderr, READ_ONLY) | ||
| 162 | - | ||
| 163 | - closed_streams = set() | ||
| 164 | - while True: | ||
| 165 | - # no timeout | ||
| 166 | - | ||
| 167 | - try: | ||
| 168 | - poll_result = poll.poll() | ||
| 169 | - except select.error as e: | ||
| 170 | - if e.args[0] == errno.EINTR: | ||
| 171 | - continue | ||
| 172 | - raise | ||
| 173 | - # end handle poll exception | ||
| 174 | - | ||
| 175 | - for fd, result in poll_result: | ||
| 176 | - if result & CLOSED: | ||
| 177 | - closed_streams.add(fd) | ||
| 178 | - else: | ||
| 179 | - _dispatch_lines(fd, *fdmap[fd]) | ||
| 180 | - # end handle closed stream | ||
| 181 | - # end for each poll-result tuple | ||
| 182 | - | ||
| 183 | - if len(closed_streams) == len(fdmap): | ||
| 184 | - break | ||
| 185 | - # end its all done | ||
| 186 | - # end endless loop | ||
| 187 | - | ||
| 188 | - # Depelete all remaining buffers | ||
| 189 | - for fno, (handler, buf_list) in fdmap.items(): | ||
| 190 | - _deplete_buffer(fno, handler, buf_list) | ||
| 191 | - # end for each file handle | ||
| 192 | - | ||
| 193 | - for fno in fdmap.keys(): | ||
| 194 | - poll.unregister(fno) | ||
| 195 | - # end don't forget to unregister ! | ||
| 196 | - else: | ||
| 197 | - # Oh ... probably we are on windows. select.select() can only handle sockets, we have files | ||
| 151 | + try: | ||
| 152 | + outfn = process.stdout.fileno() | ||
| 153 | + errfn = process.stderr.fileno() | ||
| 154 | + poll = select.poll() # @UndefinedVariable | ||
| 155 | + except (UnsupportedOperation, AttributeError): | ||
| 156 | + # Oh ... probably we are on windows. or TC mockap provided for streams. | ||
| 157 | + # Anyhow, select.select() can only handle sockets, we have files | ||
| 198 | 158 | # The only reliable way to do this now is to use threads and wait for both to finish | |
| 199 | - def _handle_lines(fd, handler): | ||
| 159 | + def _handle_lines(fd, handler, decode): | ||
| 200 | 160 | for line in fd: | |
| 201 | - line = line.decode(defenc) | ||
| 202 | - if line and handler: | ||
| 161 | + if handler: | ||
| 162 | + if decode: | ||
| 163 | + line = line.decode(defenc) | ||
| 203 | 164 | handler(line) | |
| 204 | 165 | ||
| 205 | 166 | threads = [] | |
| 206 | - for fd, handler in zip((process.stdout, process.stderr), | ||
| 207 | - (stdout_handler, stderr_handler)): | ||
| 208 | - t = threading.Thread(target=_handle_lines, args=(fd, handler)) | ||
| 167 | + for fd, handler, decode in zip((process.stdout, process.stderr), | ||
| 168 | + (stdout_handler, stderr_handler), | ||
| 169 | + (decode_stdout, decode_stderr),): | ||
| 170 | + t = threading.Thread(target=_handle_lines, args=(fd, handler, decode)) | ||
| 209 | 171 | t.setDaemon(True) | |
| 210 | 172 | t.start() | |
| 211 | 173 | threads.append(t) | |
| 212 | 174 | ||
| 213 | 175 | for t in threads: | |
| 214 | 176 | t.join() | |
| 215 | - # end | ||
| 177 | + else: | ||
| 178 | + # poll is preferred, as select is limited to file handles up to 1024 ... . This could otherwise be | ||
| 179 | + # an issue for us, as it matters how many handles our own process has | ||
| 180 | + fdmap = {outfn: (stdout_handler, [b''], decode_stdout), | ||
| 181 | + errfn: (stderr_handler, [b''], decode_stderr)} | ||
| 182 | + | ||
| 183 | + READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR # @UndefinedVariable | ||
| 184 | + CLOSED = select.POLLHUP | select.POLLERR # @UndefinedVariable | ||
| 185 | + | ||
| 186 | + poll.register(process.stdout, READ_ONLY) | ||
| 187 | + poll.register(process.stderr, READ_ONLY) | ||
| 188 | + | ||
| 189 | + closed_streams = set() | ||
| 190 | + while True: | ||
| 191 | + # no timeout | ||
| 192 | + | ||
| 193 | + try: | ||
| 194 | + poll_result = poll.poll() | ||
| 195 | + except select.error as e: | ||
| 196 | + if e.args[0] == errno.EINTR: | ||
| 197 | + continue | ||
| 198 | + raise | ||
| 199 | + # end handle poll exception | ||
| 200 | + | ||
| 201 | + for fd, result in poll_result: | ||
| 202 | + if result & CLOSED: | ||
| 203 | + closed_streams.add(fd) | ||
| 204 | + else: | ||
| 205 | + _dispatch_lines(fd, *fdmap[fd]) | ||
| 206 | + # end handle closed stream | ||
| 207 | + # end for each poll-result tuple | ||
| 208 | + | ||
| 209 | + if len(closed_streams) == len(fdmap): | ||
| 210 | + break | ||
| 211 | + # end its all done | ||
| 212 | + # end endless loop | ||
| 213 | + | ||
| 214 | + # Depelete all remaining buffers | ||
| 215 | + for fno, (handler, buf_list, decode) in fdmap.items(): | ||
| 216 | + _deplete_buffer(fno, handler, buf_list, decode) | ||
| 217 | + # end for each file handle | ||
| 218 | + | ||
| 219 | + for fno in fdmap.keys(): | ||
| 220 | + poll.unregister(fno) | ||
| 221 | + # end don't forget to unregister ! | ||
| 216 | 222 | ||
| 217 | 223 | return finalizer(process) | |
| 218 | 224 | ||
@@ -458,6 +464,7 @@ def next(self): | |||
| 458 | 464 | line = self.readline() | |
| 459 | 465 | if not line: | |
| 460 | 466 | raise StopIteration | |
| 467 | + | ||
| 461 | 468 | return line | |
| 462 | 469 | ||
| 463 | 470 | def __del__(self): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,6 +15,8 @@ | |||
| 15 | 15 | defenc, | |
| 16 | 16 | PY3 | |
| 17 | 17 | ) | |
| 18 | + from git.cmd import handle_process_output | ||
| 19 | + from git.util import finalize_process | ||
| 18 | 20 | ||
| 19 | 21 | __all__ = ('Diffable', 'DiffIndex', 'Diff', 'NULL_TREE') | |
| 20 | 22 | ||
@@ -145,10 +147,10 @@ def diff(self, other=Index, paths=None, create_patch=False, **kwargs): | |||
| 145 | 147 | kwargs['as_process'] = True | |
| 146 | 148 | proc = diff_cmd(*self._process_diff_args(args), **kwargs) | |
| 147 | 149 | ||
| 148 | - diff_method = Diff._index_from_raw_format | ||
| 149 | - if create_patch: | ||
| 150 | - diff_method = Diff._index_from_patch_format | ||
| 151 | - index = diff_method(self.repo, proc.stdout) | ||
| 150 | + diff_method = (Diff._index_from_patch_format | ||
| 151 | + if create_patch | ||
| 152 | + else Diff._index_from_raw_format) | ||
| 153 | + index = diff_method(self.repo, proc) | ||
| 152 | 154 | ||
| 153 | 155 | proc.wait() | |
| 154 | 156 | return index | |
@@ -397,13 +399,18 @@ def _pick_best_path(cls, path_match, rename_match, path_fallback_match): | |||
| 397 | 399 | return None | |
| 398 | 400 | ||
| 399 | 401 | @classmethod | |
| 400 | - def _index_from_patch_format(cls, repo, stream): | ||
| 402 | + def _index_from_patch_format(cls, repo, proc): | ||
| 401 | 403 | """Create a new DiffIndex from the given text which must be in patch format | |
| 402 | 404 | :param repo: is the repository we are operating on - it is required | |
| 403 | 405 | :param stream: result of 'git diff' as a stream (supporting file protocol) | |
| 404 | 406 | :return: git.DiffIndex """ | |
| 407 | + | ||
| 408 | + ## FIXME: Here SLURPING raw, need to re-phrase header-regexes linewise. | ||
| 409 | + text = [] | ||
| 410 | + handle_process_output(proc, text.append, None, finalize_process, decode_stdout=False) | ||
| 411 | + | ||
| 405 | 412 | # for now, we have to bake the stream | |
| 406 | - text = stream.read() | ||
| 413 | + text = b''.join(text) | ||
| 407 | 414 | index = DiffIndex() | |
| 408 | 415 | previous_header = None | |
| 409 | 416 | for header in cls.re_header.finditer(text): | |
@@ -450,17 +457,19 @@ def _index_from_patch_format(cls, repo, stream): | |||
| 450 | 457 | return index | |
| 451 | 458 | ||
| 452 | 459 | @classmethod | |
| 453 | - def _index_from_raw_format(cls, repo, stream): | ||
| 460 | + def _index_from_raw_format(cls, repo, proc): | ||
| 454 | 461 | """Create a new DiffIndex from the given stream which must be in raw format. | |
| 455 | 462 | :return: git.DiffIndex""" | |
| 456 | 463 | # handles | |
| 457 | 464 | # :100644 100644 687099101... 37c5e30c8... M .gitignore | |
| 465 | + | ||
| 458 | 466 | index = DiffIndex() | |
| 459 | - for line in stream.readlines(): | ||
| 467 | + | ||
| 468 | + def handle_diff_line(line): | ||
| 460 | 469 | line = line.decode(defenc) | |
| 461 | 470 | if not line.startswith(":"): | |
| 462 | - continue | ||
| 463 | - # END its not a valid diff line | ||
| 471 | + return | ||
| 472 | + | ||
| 464 | 473 | meta, _, path = line[1:].partition('\t') | |
| 465 | 474 | old_mode, new_mode, a_blob_id, b_blob_id, change_type = meta.split(None, 4) | |
| 466 | 475 | path = path.strip() | |
@@ -489,6 +498,7 @@ def _index_from_raw_format(cls, repo, stream): | |||
| 489 | 498 | diff = Diff(repo, a_path, b_path, a_blob_id, b_blob_id, old_mode, new_mode, | |
| 490 | 499 | new_file, deleted_file, rename_from, rename_to, '', change_type) | |
| 491 | 500 | index.append(diff) | |
| 492 | - # END for each line | ||
| 501 | + | ||
| 502 | + handle_process_output(proc, handle_diff_line, None, finalize_process, decode_stdout=False) | ||
| 493 | 503 | ||
| 494 | 504 | return index | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -79,15 +79,15 @@ def test_diff_with_staged_file(self, rw_dir): | |||
| 79 | 79 | ||
| 80 | 80 | def test_list_from_string_new_mode(self): | |
| 81 | 81 | output = StringProcessAdapter(fixture('diff_new_mode')) | |
| 82 | - diffs = Diff._index_from_patch_format(self.rorepo, output.stdout) | ||
| 82 | + diffs = Diff._index_from_patch_format(self.rorepo, output) | ||
| 83 | 83 | self._assert_diff_format(diffs) | |
| 84 | 84 | ||
| 85 | 85 | assert_equal(1, len(diffs)) | |
| 86 | 86 | assert_equal(8, len(diffs[0].diff.splitlines())) | |
| 87 | 87 | ||
| 88 | 88 | def test_diff_with_rename(self): | |
| 89 | 89 | output = StringProcessAdapter(fixture('diff_rename')) | |
| 90 | - diffs = Diff._index_from_patch_format(self.rorepo, output.stdout) | ||
| 90 | + diffs = Diff._index_from_patch_format(self.rorepo, output) | ||
| 91 | 91 | self._assert_diff_format(diffs) | |
| 92 | 92 | ||
| 93 | 93 | assert_equal(1, len(diffs)) | |
@@ -102,7 +102,7 @@ def test_diff_with_rename(self): | |||
| 102 | 102 | assert isinstance(str(diff), str) | |
| 103 | 103 | ||
| 104 | 104 | output = StringProcessAdapter(fixture('diff_rename_raw')) | |
| 105 | - diffs = Diff._index_from_raw_format(self.rorepo, output.stdout) | ||
| 105 | + diffs = Diff._index_from_raw_format(self.rorepo, output) | ||
| 106 | 106 | self.assertEqual(len(diffs), 1) | |
| 107 | 107 | diff = diffs[0] | |
| 108 | 108 | self.assertIsNotNone(diff.renamed_file) | |
@@ -113,7 +113,7 @@ def test_diff_with_rename(self): | |||
| 113 | 113 | ||
| 114 | 114 | def test_diff_of_modified_files_not_added_to_the_index(self): | |
| 115 | 115 | output = StringProcessAdapter(fixture('diff_abbrev-40_full-index_M_raw_no-color')) | |
| 116 | - diffs = Diff._index_from_raw_format(self.rorepo, output.stdout) | ||
| 116 | + diffs = Diff._index_from_raw_format(self.rorepo, output) | ||
| 117 | 117 | ||
| 118 | 118 | self.assertEqual(len(diffs), 1, 'one modification') | |
| 119 | 119 | self.assertEqual(len(list(diffs.iter_change_type('M'))), 1, 'one modification') | |
@@ -126,7 +126,7 @@ def test_diff_of_modified_files_not_added_to_the_index(self): | |||
| 126 | 126 | ) | |
| 127 | 127 | def test_binary_diff(self, case): | |
| 128 | 128 | method, file_name = case | |
| 129 | - res = method(None, StringProcessAdapter(fixture(file_name)).stdout) | ||
| 129 | + res = method(None, StringProcessAdapter(fixture(file_name))) | ||
| 130 | 130 | self.assertEqual(len(res), 1) | |
| 131 | 131 | self.assertEqual(len(list(res.iter_change_type('M'))), 1) | |
| 132 | 132 | if res[0].diff: | |
@@ -137,7 +137,7 @@ def test_binary_diff(self, case): | |||
| 137 | 137 | ||
| 138 | 138 | def test_diff_index(self): | |
| 139 | 139 | output = StringProcessAdapter(fixture('diff_index_patch')) | |
| 140 | - res = Diff._index_from_patch_format(None, output.stdout) | ||
| 140 | + res = Diff._index_from_patch_format(None, output) | ||
| 141 | 141 | self.assertEqual(len(res), 6) | |
| 142 | 142 | for dr in res: | |
| 143 | 143 | self.assertTrue(dr.diff.startswith(b'@@'), dr) | |
@@ -149,7 +149,7 @@ def test_diff_index(self): | |||
| 149 | 149 | ||
| 150 | 150 | def test_diff_index_raw_format(self): | |
| 151 | 151 | output = StringProcessAdapter(fixture('diff_index_raw')) | |
| 152 | - res = Diff._index_from_raw_format(None, output.stdout) | ||
| 152 | + res = Diff._index_from_raw_format(None, output) | ||
| 153 | 153 | self.assertIsNotNone(res[0].deleted_file) | |
| 154 | 154 | self.assertIsNone(res[0].b_path,) | |
| 155 | 155 | ||
@@ -171,7 +171,7 @@ def test_diff_initial_commit(self): | |||
| 171 | 171 | ||
| 172 | 172 | def test_diff_unsafe_paths(self): | |
| 173 | 173 | output = StringProcessAdapter(fixture('diff_patch_unsafe_paths')) | |
| 174 | - res = Diff._index_from_patch_format(None, output.stdout) | ||
| 174 | + res = Diff._index_from_patch_format(None, output) | ||
| 175 | 175 | ||
| 176 | 176 | # The "Additions" | |
| 177 | 177 | self.assertEqual(res[0].b_path, u'path/ starting with a space') | |
@@ -207,12 +207,12 @@ def test_diff_patch_format(self): | |||
| 207 | 207 | ||
| 208 | 208 | for fixture_name in fixtures: | |
| 209 | 209 | diff_proc = StringProcessAdapter(fixture(fixture_name)) | |
| 210 | - Diff._index_from_patch_format(self.rorepo, diff_proc.stdout) | ||
| 210 | + Diff._index_from_patch_format(self.rorepo, diff_proc) | ||
| 211 | 211 | # END for each fixture | |
| 212 | 212 | ||
| 213 | 213 | def test_diff_with_spaces(self): | |
| 214 | 214 | data = StringProcessAdapter(fixture('diff_file_with_spaces')) | |
| 215 | - diff_index = Diff._index_from_patch_format(self.rorepo, data.stdout) | ||
| 215 | + diff_index = Diff._index_from_patch_format(self.rorepo, data) | ||
| 216 | 216 | self.assertIsNone(diff_index[0].a_path, repr(diff_index[0].a_path)) | |
| 217 | 217 | self.assertEqual(diff_index[0].b_path, u'file with spaces', repr(diff_index[0].b_path)) | |
| 218 | 218 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments