From 7842e92ebaf3fc3380cc8d704afa3841f333748c Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Thu, 15 Sep 2016 00:59:36 +0200 Subject: test, deps: FIX `mock` deps on py3. + Del extra spaces, import os.path as osp --- git/cmd.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index ceea2442..1cc656bf 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -5,7 +5,6 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php import os -import os.path import sys import select import logging @@ -213,11 +212,11 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer): def dashify(string): return string.replace('_', '-') - + def slots_to_dict(self, exclude=()): return dict((s, getattr(self, s)) for s in self.__slots__ if s not in exclude) - + def dict_to_slots_and__excluded_are_none(self, d, excluded=()): for k, v in d.items(): @@ -246,15 +245,15 @@ class Git(LazyMixin): """ __slots__ = ("_working_dir", "cat_file_all", "cat_file_header", "_version_info", "_git_options", "_environment") - + _excluded_ = ('cat_file_all', 'cat_file_header', '_version_info') - + def __getstate__(self): return slots_to_dict(self, exclude=self._excluded_) - + def __setstate__(self, d): dict_to_slots_and__excluded_are_none(self, d, excluded=self._excluded_) - + # CONFIGURATION # The size in bytes read from stdout when copying git's output to another stream max_chunk_size = 1024 * 64 @@ -267,7 +266,7 @@ class Git(LazyMixin): # value of Windows process creation flag taken from MSDN CREATE_NO_WINDOW = 0x08000000 - + # Provide the full path to the git executable. Otherwise it assumes git is in the path _git_exec_env_var = "GIT_PYTHON_GIT_EXECUTABLE" GIT_PYTHON_GIT_EXECUTABLE = os.environ.get(_git_exec_env_var, git_exec_name) @@ -339,7 +338,7 @@ class Git(LazyMixin): if stderr is None: stderr = b'' stderr = force_bytes(stderr) - + status = self.proc.wait() def read_all_from_possibly_closed_stream(stream): -- cgit v1.2.3 From 7ec2f8a4f26cec3fbbe1fb447058acaf508b39c0 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Mon, 26 Sep 2016 01:36:57 +0200 Subject: apveyor, #519: FIX incomplete Popen pump + The code in `_read_lines_from_fno()` was reading the stream only once per invocation, so when input was larger than `mmap.PAGESIZE`, bytes were forgotten in the stream. + Replaced buffer-building code with iterate-on-file-descriptors. + Also set deamon-threads. --- git/cmd.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index 1cc656bf..c700d7a4 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -193,14 +193,24 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer): else: # Oh ... probably we are on windows. select.select() can only handle sockets, we have files # The only reliable way to do this now is to use threads and wait for both to finish + def _handle_lines(fd, handler, wg): + for line in fd: + line = line.decode(defenc) + if line and handler: + handler(line) + if wg: + wg.done() + # Since the finalizer is expected to wait, we don't have to introduce our own wait primitive # NO: It's not enough unfortunately, and we will have to sync the threads wg = WaitGroup() - for fno, (handler, buf_list) in fdmap.items(): + for fd, handler in zip((process.stdout, process.stderr), + (stdout_handler, stderr_handler)): wg.add(1) - t = threading.Thread(target=lambda: _deplete_buffer(fno, handler, buf_list, wg)) + t = threading.Thread(target=_handle_lines, args=(fd, handler, wg)) + t.setDaemon(True) t.start() - # end + # NOTE: Just joining threads can possibly fail as there is a gap between .start() and when it's # actually started, which could make the wait() call to just return because the thread is not yet # active -- cgit v1.2.3 From b343718cc1290c8d5fd5b1217724b077153262a8 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Mon, 26 Sep 2016 02:37:38 +0200 Subject: test, #519: Popen() pump: remove WaitGroup --- git/cmd.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index c700d7a4..14f655ed 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -25,7 +25,6 @@ from subprocess import ( from .util import ( LazyMixin, stream_copy, - WaitGroup ) from .exc import ( GitCommandError, @@ -193,28 +192,22 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer): else: # Oh ... probably we are on windows. select.select() can only handle sockets, we have files # The only reliable way to do this now is to use threads and wait for both to finish - def _handle_lines(fd, handler, wg): + def _handle_lines(fd, handler): for line in fd: line = line.decode(defenc) if line and handler: handler(line) - if wg: - wg.done() - # Since the finalizer is expected to wait, we don't have to introduce our own wait primitive - # NO: It's not enough unfortunately, and we will have to sync the threads - wg = WaitGroup() + threads = [] for fd, handler in zip((process.stdout, process.stderr), (stdout_handler, stderr_handler)): - wg.add(1) - t = threading.Thread(target=_handle_lines, args=(fd, handler, wg)) + t = threading.Thread(target=_handle_lines, args=(fd, handler)) t.setDaemon(True) t.start() + threads.append(t) - # NOTE: Just joining threads can possibly fail as there is a gap between .start() and when it's - # actually started, which could make the wait() call to just return because the thread is not yet - # active - wg.wait() + for t in threads: + t.join() # end return finalizer(process) -- cgit v1.2.3 From 45f8f20bdf1447fbfebd19a07412d337626ed6b0 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Mon, 26 Sep 2016 19:42:42 +0200 Subject: Win, #519: FIX WinHangs: Popen() CREATE_NEW_PROCESS_GROUP to allow kill + FIXED most hangs BUT no more `git-daemon` un-killable! + Use logger for utils to replace stray print(). --- git/cmd.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index 14f655ed..f6cb0ce9 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -15,6 +15,7 @@ import mmap from git.odict import OrderedDict from contextlib import contextmanager import signal +import subprocess from subprocess import ( call, Popen, @@ -229,6 +230,15 @@ def dict_to_slots_and__excluded_are_none(self, d, excluded=()): ## -- End Utilities -- @} +# value of Windows process creation flag taken from MSDN +CREATE_NO_WINDOW = 0x08000000 + +## CREATE_NEW_PROCESS_GROUP is needed to allow killing it afterwards, +# seehttps://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal +PROC_CREATIONFLAGS = (CREATE_NO_WINDOW | subprocess.CREATE_NEW_PROCESS_GROUP + if sys.platform == 'win32' + else 0) + class Git(LazyMixin): @@ -267,9 +277,6 @@ class Git(LazyMixin): # Enables debugging of GitPython's git commands GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False) - # value of Windows process creation flag taken from MSDN - CREATE_NO_WINDOW = 0x08000000 - # Provide the full path to the git executable. Otherwise it assumes git is in the path _git_exec_env_var = "GIT_PYTHON_GIT_EXECUTABLE" GIT_PYTHON_GIT_EXECUTABLE = os.environ.get(_git_exec_env_var, git_exec_name) @@ -317,7 +324,7 @@ class Git(LazyMixin): # try to kill it try: - os.kill(proc.pid, 2) # interrupt signal + proc.terminate() proc.wait() # ensure process goes away except (OSError, WindowsError): pass # ignore error when process already died @@ -632,7 +639,6 @@ class Git(LazyMixin): cmd_not_found_exception = OSError # end handle - creationflags = self.CREATE_NO_WINDOW if sys.platform == 'win32' else 0 try: proc = Popen(command, env=env, @@ -644,7 +650,7 @@ class Git(LazyMixin): shell=self.USE_SHELL, close_fds=(os.name == 'posix'), # unsupported on windows universal_newlines=universal_newlines, - creationflags=creationflags, + creationflags=PROC_CREATIONFLAGS, **subprocess_kwargs ) except cmd_not_found_exception as err: @@ -655,7 +661,8 @@ class Git(LazyMixin): def _kill_process(pid): """ Callback method to kill a process. """ - p = Popen(['ps', '--ppid', str(pid)], stdout=PIPE, creationflags=creationflags) + p = Popen(['ps', '--ppid', str(pid)], stdout=PIPE, + creationflags=PROC_CREATIONFLAGS) child_pids = [] for line in p.stdout: if len(line.split()) > 0: -- cgit v1.2.3 From f495e94028bfddc264727ffc464cd694ddd05ab8 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Mon, 26 Sep 2016 20:41:41 +0200 Subject: src, #519: collect all is_() calls --- git/cmd.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index f6cb0ce9..7b032d58 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -40,6 +40,8 @@ from git.compat import ( # just to satisfy flake8 on py3 unicode, safe_decode, + is_posix, + is_win, ) execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', @@ -50,9 +52,9 @@ execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', log = logging.getLogger('git.cmd') log.addHandler(logging.NullHandler()) -__all__ = ('Git', ) +__all__ = ('Git',) -if sys.platform != 'win32': +if is_win(): WindowsError = OSError if PY3: @@ -236,7 +238,7 @@ CREATE_NO_WINDOW = 0x08000000 ## CREATE_NEW_PROCESS_GROUP is needed to allow killing it afterwards, # seehttps://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal PROC_CREATIONFLAGS = (CREATE_NO_WINDOW | subprocess.CREATE_NEW_PROCESS_GROUP - if sys.platform == 'win32' + if is_win() else 0) @@ -628,7 +630,7 @@ class Git(LazyMixin): env["LC_ALL"] = "C" env.update(self._environment) - if sys.platform == 'win32': + if is_win(): cmd_not_found_exception = WindowsError if kill_after_timeout: raise GitCommandError('"kill_after_timeout" feature is not supported on Windows.') @@ -648,7 +650,7 @@ class Git(LazyMixin): stderr=PIPE, stdout=PIPE if with_stdout else open(os.devnull, 'wb'), shell=self.USE_SHELL, - close_fds=(os.name == 'posix'), # unsupported on windows + close_fds=(is_posix()), # unsupported on windows universal_newlines=universal_newlines, creationflags=PROC_CREATIONFLAGS, **subprocess_kwargs @@ -688,7 +690,7 @@ class Git(LazyMixin): if kill_after_timeout: kill_check = threading.Event() - watchdog = threading.Timer(kill_after_timeout, _kill_process, args=(proc.pid, )) + watchdog = threading.Timer(kill_after_timeout, _kill_process, args=(proc.pid,)) # Wait for the process to return status = 0 @@ -932,7 +934,7 @@ class Git(LazyMixin): return call # END utility to recreate call after changes - if sys.platform == 'win32': + if is_win(): try: try: return self.execute(make_call(), **_kwargs) -- cgit v1.2.3 From 618e6259ef03a4b25415bae31a7540ac5eb2e38a Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Mon, 26 Sep 2016 22:20:33 +0200 Subject: test, #519: Try appveyor advice for never-ending builds + see http://help.appveyor.com/discussions/problems/5334-nosetests-finsih-bu-build-stuck-and-next-job-dealys-to-start + Use `io.DEFAULT_BUFFER_SIZE`. + test_commit: replace asserts with unittest-asserts. - TRY Popen() NO universal_newlines: NO, reverted in next commits. + [travisci skip] --- git/cmd.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index 7b032d58..682df006 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -43,6 +43,7 @@ from git.compat import ( is_posix, is_win, ) +import io execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', 'with_exceptions', 'as_process', 'stdout_as_string', @@ -271,7 +272,7 @@ class Git(LazyMixin): # CONFIGURATION # The size in bytes read from stdout when copying git's output to another stream - max_chunk_size = 1024 * 64 + max_chunk_size = io.DEFAULT_BUFFER_SIZE git_exec_name = "git" # default that should work on linux and windows git_exec_name_win = "git.cmd" # alternate command name, windows only -- cgit v1.2.3 From 25a2ebfa684f7ef37a9298c5ded2fc5af190cb42 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Tue, 27 Sep 2016 10:59:10 +0200 Subject: Win, #519: Remove `git.cmd` failback - no longer exists. + Simplify call_process, no win-code case, no `make_call()` nested func. + Del needless WinError try..catch, in `_call_process()` already converted as GitCommandNotFound by `execute()`. + pyism: kw-loop-->comprehension, facilitate debug-stepping --- git/cmd.py | 69 ++++++++++++++------------------------------------------------ 1 file changed, 15 insertions(+), 54 deletions(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index 682df006..4a2163d5 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -45,10 +45,10 @@ from git.compat import ( ) import io -execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', - 'with_exceptions', 'as_process', 'stdout_as_string', - 'output_stream', 'with_stdout', 'kill_after_timeout', - 'universal_newlines') +execute_kwargs = set(('istream', 'with_keep_cwd', 'with_extended_output', + 'with_exceptions', 'as_process', 'stdout_as_string', + 'output_stream', 'with_stdout', 'kill_after_timeout', + 'universal_newlines')) log = logging.getLogger('git.cmd') log.addHandler(logging.NullHandler()) @@ -275,7 +275,6 @@ class Git(LazyMixin): max_chunk_size = io.DEFAULT_BUFFER_SIZE git_exec_name = "git" # default that should work on linux and windows - git_exec_name_win = "git.cmd" # alternate command name, windows only # Enables debugging of GitPython's git commands GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False) @@ -778,10 +777,7 @@ class Git(LazyMixin): for key, value in kwargs.items(): # set value if it is None if value is not None: - if key in self._environment: - old_env[key] = self._environment[key] - else: - old_env[key] = None + old_env[key] = self._environment.get(key) self._environment[key] = value # remove key from environment if its value is None elif key in self._environment: @@ -897,12 +893,8 @@ class Git(LazyMixin): :return: Same as ``execute``""" # Handle optional arguments prior to calling transform_kwargs # otherwise these'll end up in args, which is bad. - _kwargs = dict() - for kwarg in execute_kwargs: - try: - _kwargs[kwarg] = kwargs.pop(kwarg) - except KeyError: - pass + _kwargs = {k: v for k, v in kwargs.items() if k in execute_kwargs} + kwargs = {k: v for k, v in kwargs.items() if k not in execute_kwargs} insert_after_this_arg = kwargs.pop('insert_kwargs_after', None) @@ -922,48 +914,17 @@ class Git(LazyMixin): args = ext_args[:index + 1] + opt_args + ext_args[index + 1:] # end handle kwargs - def make_call(): - call = [self.GIT_PYTHON_GIT_EXECUTABLE] + call = [self.GIT_PYTHON_GIT_EXECUTABLE] - # add the git options, the reset to empty - # to avoid side_effects - call.extend(self._git_options) - self._git_options = () - - call.extend([dashify(method)]) - call.extend(args) - return call - # END utility to recreate call after changes + # add the git options, the reset to empty + # to avoid side_effects + call.extend(self._git_options) + self._git_options = () - if is_win(): - try: - try: - return self.execute(make_call(), **_kwargs) - except WindowsError: - # did we switch to git.cmd already, or was it changed from default ? permanently fail - if self.GIT_PYTHON_GIT_EXECUTABLE != self.git_exec_name: - raise - # END handle overridden variable - type(self).GIT_PYTHON_GIT_EXECUTABLE = self.git_exec_name_win + call.append(dashify(method)) + call.extend(args) - try: - return self.execute(make_call(), **_kwargs) - finally: - import warnings - msg = "WARNING: Automatically switched to use git.cmd as git executable" - msg += ", which reduces performance by ~70%." - msg += "It is recommended to put git.exe into the PATH or to " - msg += "set the %s " % self._git_exec_env_var - msg += "environment variable to the executable's location" - warnings.warn(msg) - # END print of warning - # END catch first failure - except WindowsError: - raise WindowsError("The system cannot find or execute the file at %r" % self.GIT_PYTHON_GIT_EXECUTABLE) - # END provide better error message - else: - return self.execute(make_call(), **_kwargs) - # END handle windows default installation + return self.execute(call, **_kwargs) def _parse_object_header(self, header_line): """ -- cgit v1.2.3 From e61439b3018b0b9a8eb43e59d0d7cf32041e2fed Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Tue, 27 Sep 2016 16:05:58 +0200 Subject: src: constify is_() calls + TCs: unittest-asserts for git-tests. --- git/cmd.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index 4a2163d5..69844366 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -55,7 +55,7 @@ log.addHandler(logging.NullHandler()) __all__ = ('Git',) -if is_win(): +if is_win: WindowsError = OSError if PY3: @@ -239,7 +239,7 @@ CREATE_NO_WINDOW = 0x08000000 ## CREATE_NEW_PROCESS_GROUP is needed to allow killing it afterwards, # seehttps://docs.python.org/3/library/subprocess.html#subprocess.Popen.send_signal PROC_CREATIONFLAGS = (CREATE_NO_WINDOW | subprocess.CREATE_NEW_PROCESS_GROUP - if is_win() + if is_win else 0) @@ -630,7 +630,7 @@ class Git(LazyMixin): env["LC_ALL"] = "C" env.update(self._environment) - if is_win(): + if is_win: cmd_not_found_exception = WindowsError if kill_after_timeout: raise GitCommandError('"kill_after_timeout" feature is not supported on Windows.') @@ -650,13 +650,13 @@ class Git(LazyMixin): stderr=PIPE, stdout=PIPE if with_stdout else open(os.devnull, 'wb'), shell=self.USE_SHELL, - close_fds=(is_posix()), # unsupported on windows + close_fds=(is_posix), # unsupported on windows universal_newlines=universal_newlines, creationflags=PROC_CREATIONFLAGS, **subprocess_kwargs ) except cmd_not_found_exception as err: - raise GitCommandNotFound(str(err)) + raise GitCommandNotFound('%s: %s' % (command[0], err)) if as_process: return self.AutoInterrupt(proc, command) -- cgit v1.2.3 From 137ee6ef22c4e6480f95972ef220d1832cdc709a Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Tue, 27 Sep 2016 22:07:19 +0200 Subject: Win, #519: FIX with_rw_directory() to remove read-only dirs + Stop using gitdb's respective helper. + Fix files chmod(555) which CANNOT DELETE on Windows (but do on Linux). --- git/cmd.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index 69844366..fb94c200 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -1013,6 +1013,10 @@ class Git(LazyMixin): Currently persistent commands will be interrupted. :return: self""" + for cmd in (self.cat_file_all, self.cat_file_header): + if cmd: + cmd.__del__() + self.cat_file_all = None self.cat_file_header = None return self -- cgit v1.2.3 From a5db3d3c49ebe559cb80983d7bb855d4adf1b887 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Wed, 28 Sep 2016 01:05:38 +0200 Subject: io, dif: #519: FIX DIFF freeze when reading from GIL + CAUSE: In Windows, Diffs freeze while reading Popen streams, probably buffers smaller; good-thin(TM) in this case because reading a Popen-proc from the launching-thread freezes GIL. The alternative to use `proc.communicate()` also relies on big buffers. + SOLUTION: Use `cmd.handle_process_output()` to consume Diff-proc streams. + Retroffited `handle_process_output()` code to support also byte-streams, both Threading(Windows) and Select/Poll (Posix) paths updated. - TODO: Unfortunately, `Diff._index_from_patch_format()` still slurps input; need to re-phrase header-regexes linewise to resolve it. --- git/cmd.py | 141 ++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 74 insertions(+), 67 deletions(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index fb94c200..feb16e30 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -44,6 +44,7 @@ from git.compat import ( is_win, ) import io +from _io import UnsupportedOperation execute_kwargs = set(('istream', 'with_keep_cwd', 'with_extended_output', 'with_exceptions', 'as_process', 'stdout_as_string', @@ -56,7 +57,7 @@ log.addHandler(logging.NullHandler()) __all__ = ('Git',) if is_win: - WindowsError = OSError + WindowsError = OSError # @ReservedAssignment if PY3: _bchr = bchr @@ -72,7 +73,8 @@ else: # Documentation ## @{ -def handle_process_output(process, stdout_handler, stderr_handler, finalizer): +def handle_process_output(process, stdout_handler, stderr_handler, finalizer, + decode_stdout=True, decode_stderr=True): """Registers for notifications to lean that process output is ready to read, and dispatches lines to the respective line handlers. We are able to handle carriage returns in case progress is sent by that mean. For performance reasons, we only apply this to stderr. @@ -82,8 +84,6 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer): :param stdout_handler: f(stdout_line_string), or None :param stderr_hanlder: f(stderr_line_string), or None :param finalizer: f(proc) - wait for proc to finish""" - fdmap = {process.stdout.fileno(): (stdout_handler, [b'']), - process.stderr.fileno(): (stderr_handler, [b''])} def _parse_lines_from_buffer(buf): line = b'' @@ -94,7 +94,7 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer): bi += 1 if char in (b'\r', b'\n') and line: - yield bi, line + yield bi, line + b'\n' line = b'' else: line += char @@ -114,105 +114,111 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer): # keep remainder last_buf_list[0] = buf[bi:] - def _dispatch_single_line(line, handler): - line = line.decode(defenc) + def _dispatch_single_line(line, handler, decode): + if decode: + line = line.decode(defenc) if line and handler: handler(line) # end dispatch helper # end single line helper - def _dispatch_lines(fno, handler, buf_list): + def _dispatch_lines(fno, handler, buf_list, decode): lc = 0 for line in _read_lines_from_fno(fno, buf_list): - _dispatch_single_line(line, handler) + _dispatch_single_line(line, handler, decode) lc += 1 # for each line return lc # end - def _deplete_buffer(fno, handler, buf_list, wg=None): + def _deplete_buffer(fno, handler, buf_list, decode): lc = 0 while True: - line_count = _dispatch_lines(fno, handler, buf_list) + line_count = _dispatch_lines(fno, handler, buf_list, decode) lc += line_count if line_count == 0: break # end deplete buffer if buf_list[0]: - _dispatch_single_line(buf_list[0], handler) + _dispatch_single_line(buf_list[0], handler, decode) lc += 1 # end - if wg: - wg.done() - return lc # end - if hasattr(select, 'poll'): - # poll is preferred, as select is limited to file handles up to 1024 ... . This could otherwise be - # an issue for us, as it matters how many handles our own process has - poll = select.poll() - READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR - CLOSED = select.POLLHUP | select.POLLERR - - poll.register(process.stdout, READ_ONLY) - poll.register(process.stderr, READ_ONLY) - - closed_streams = set() - while True: - # no timeout - - try: - poll_result = poll.poll() - except select.error as e: - if e.args[0] == errno.EINTR: - continue - raise - # end handle poll exception - - for fd, result in poll_result: - if result & CLOSED: - closed_streams.add(fd) - else: - _dispatch_lines(fd, *fdmap[fd]) - # end handle closed stream - # end for each poll-result tuple - - if len(closed_streams) == len(fdmap): - break - # end its all done - # end endless loop - - # Depelete all remaining buffers - for fno, (handler, buf_list) in fdmap.items(): - _deplete_buffer(fno, handler, buf_list) - # end for each file handle - - for fno in fdmap.keys(): - poll.unregister(fno) - # end don't forget to unregister ! - else: - # Oh ... probably we are on windows. select.select() can only handle sockets, we have files + try: + outfn = process.stdout.fileno() + errfn = process.stderr.fileno() + poll = select.poll() # @UndefinedVariable + except (UnsupportedOperation, AttributeError): + # Oh ... probably we are on windows. or TC mockap provided for streams. + # Anyhow, select.select() can only handle sockets, we have files # The only reliable way to do this now is to use threads and wait for both to finish - def _handle_lines(fd, handler): + def _handle_lines(fd, handler, decode): for line in fd: - line = line.decode(defenc) - if line and handler: + if handler: + if decode: + line = line.decode(defenc) handler(line) threads = [] - for fd, handler in zip((process.stdout, process.stderr), - (stdout_handler, stderr_handler)): - t = threading.Thread(target=_handle_lines, args=(fd, handler)) + for fd, handler, decode in zip((process.stdout, process.stderr), + (stdout_handler, stderr_handler), + (decode_stdout, decode_stderr),): + t = threading.Thread(target=_handle_lines, args=(fd, handler, decode)) t.setDaemon(True) t.start() threads.append(t) for t in threads: t.join() - # end + else: + # poll is preferred, as select is limited to file handles up to 1024 ... . This could otherwise be + # an issue for us, as it matters how many handles our own process has + fdmap = {outfn: (stdout_handler, [b''], decode_stdout), + errfn: (stderr_handler, [b''], decode_stderr)} + + READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR # @UndefinedVariable + CLOSED = select.POLLHUP | select.POLLERR # @UndefinedVariable + + poll.register(process.stdout, READ_ONLY) + poll.register(process.stderr, READ_ONLY) + + closed_streams = set() + while True: + # no timeout + + try: + poll_result = poll.poll() + except select.error as e: + if e.args[0] == errno.EINTR: + continue + raise + # end handle poll exception + + for fd, result in poll_result: + if result & CLOSED: + closed_streams.add(fd) + else: + _dispatch_lines(fd, *fdmap[fd]) + # end handle closed stream + # end for each poll-result tuple + + if len(closed_streams) == len(fdmap): + break + # end its all done + # end endless loop + + # Depelete all remaining buffers + for fno, (handler, buf_list, decode) in fdmap.items(): + _deplete_buffer(fno, handler, buf_list, decode) + # end for each file handle + + for fno in fdmap.keys(): + poll.unregister(fno) + # end don't forget to unregister ! return finalizer(process) @@ -458,6 +464,7 @@ class Git(LazyMixin): line = self.readline() if not line: raise StopIteration + return line def __del__(self): -- cgit v1.2.3 From 44c6d0b368bc1ec6cd0a97b01678b38788c9bd9c Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Wed, 28 Sep 2016 05:46:50 +0200 Subject: Proc, #519: Rework error-exc msgs & log thread-pumps errors + No WindowsError exception. + Add `test_exc.py` for unicode issues. + Single-arg for decoding-streams in pump-func. --- git/cmd.py | 64 +++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 24 deletions(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index feb16e30..20da96bd 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -45,6 +45,7 @@ from git.compat import ( ) import io from _io import UnsupportedOperation +from git.exc import CommandError execute_kwargs = set(('istream', 'with_keep_cwd', 'with_extended_output', 'with_exceptions', 'as_process', 'stdout_as_string', @@ -56,9 +57,6 @@ log.addHandler(logging.NullHandler()) __all__ = ('Git',) -if is_win: - WindowsError = OSError # @ReservedAssignment - if PY3: _bchr = bchr else: @@ -73,17 +71,23 @@ else: # Documentation ## @{ -def handle_process_output(process, stdout_handler, stderr_handler, finalizer, - decode_stdout=True, decode_stderr=True): +def handle_process_output(process, stdout_handler, stderr_handler, finalizer, decode_streams=True): """Registers for notifications to lean that process output is ready to read, and dispatches lines to the respective line handlers. We are able to handle carriage returns in case progress is sent by that mean. For performance reasons, we only apply this to stderr. This function returns once the finalizer returns + :return: result of finalizer :param process: subprocess.Popen instance :param stdout_handler: f(stdout_line_string), or None :param stderr_hanlder: f(stderr_line_string), or None - :param finalizer: f(proc) - wait for proc to finish""" + :param finalizer: f(proc) - wait for proc to finish + :param decode_streams: + Assume stdout/stderr streams are binary and decode them vefore pushing \ + their contents to handlers. + Set it to False if `universal_newline == True` (then streams are in text-mode) + or if decoding must happen later (i.e. for Diffs). + """ def _parse_lines_from_buffer(buf): line = b'' @@ -156,18 +160,29 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer, # Oh ... probably we are on windows. or TC mockap provided for streams. # Anyhow, select.select() can only handle sockets, we have files # The only reliable way to do this now is to use threads and wait for both to finish - def _handle_lines(fd, handler, decode): - for line in fd: - if handler: - if decode: - line = line.decode(defenc) - handler(line) - + def pump_stream(cmdline, name, stream, is_decode, handler): + try: + for line in stream: + if handler: + if is_decode: + line = line.decode(defenc) + handler(line) + except Exception as ex: + log.error("Pumping %r of cmd(%s) failed due to: %r", name, cmdline, ex) + raise CommandError(['<%s-pump>' % name] + cmdline, ex) + finally: + stream.close() + + cmdline = getattr(process, 'args', '') # PY3+ only + if not isinstance(cmdline, (tuple, list)): + cmdline = cmdline.split() threads = [] - for fd, handler, decode in zip((process.stdout, process.stderr), - (stdout_handler, stderr_handler), - (decode_stdout, decode_stderr),): - t = threading.Thread(target=_handle_lines, args=(fd, handler, decode)) + for name, stream, handler in ( + ('stdout', process.stdout, stdout_handler), + ('stderr', process.stderr, stderr_handler), + ): + t = threading.Thread(target=pump_stream, + args=(cmdline, name, stream, decode_streams, handler)) t.setDaemon(True) t.start() threads.append(t) @@ -177,8 +192,8 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer, else: # poll is preferred, as select is limited to file handles up to 1024 ... . This could otherwise be # an issue for us, as it matters how many handles our own process has - fdmap = {outfn: (stdout_handler, [b''], decode_stdout), - errfn: (stderr_handler, [b''], decode_stderr)} + fdmap = {outfn: (stdout_handler, [b''], decode_streams), + errfn: (stderr_handler, [b''], decode_streams)} READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR # @UndefinedVariable CLOSED = select.POLLHUP | select.POLLERR # @UndefinedVariable @@ -334,7 +349,8 @@ class Git(LazyMixin): try: proc.terminate() proc.wait() # ensure process goes away - except (OSError, WindowsError): + except OSError as ex: + log.info("Ignored error after process has dies: %r", ex) pass # ignore error when process already died except AttributeError: # try windows @@ -638,12 +654,12 @@ class Git(LazyMixin): env.update(self._environment) if is_win: - cmd_not_found_exception = WindowsError + cmd_not_found_exception = OSError if kill_after_timeout: - raise GitCommandError('"kill_after_timeout" feature is not supported on Windows.') + raise GitCommandError(command, '"kill_after_timeout" feature is not supported on Windows.') else: if sys.version_info[0] > 2: - cmd_not_found_exception = FileNotFoundError # NOQA # this is defined, but flake8 doesn't know + cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable else: cmd_not_found_exception = OSError # end handle @@ -663,7 +679,7 @@ class Git(LazyMixin): **subprocess_kwargs ) except cmd_not_found_exception as err: - raise GitCommandNotFound('%s: %s' % (command[0], err)) + raise GitCommandNotFound(command, err) if as_process: return self.AutoInterrupt(proc, command) -- cgit v1.2.3 From 6e98416791566f44a407dcac07a1e1f1b0483544 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Wed, 28 Sep 2016 17:10:59 +0200 Subject: remote, #519: INCOMPLETE FIX-2 double-decoding push-infos + Unicode PY2/3 issues fixed also in pump stream func. --- git/cmd.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index 20da96bd..835be605 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -88,18 +88,26 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer, de Set it to False if `universal_newline == True` (then streams are in text-mode) or if decoding must happen later (i.e. for Diffs). """ + if decode_streams: + ZERO = b'' + LF = b'\n' + CR = b'\r' + else: + ZERO = u'' + LF = u'\n' + CR = u'\r' def _parse_lines_from_buffer(buf): - line = b'' + line = ZERO bi = 0 lb = len(buf) while bi < lb: - char = _bchr(buf[bi]) + char = buf[bi] bi += 1 - if char in (b'\r', b'\n') and line: - yield bi, line + b'\n' - line = b'' + if char in (LF, CR) and line: + yield bi, line + LF + line = ZERO else: line += char # END process parsed line @@ -107,7 +115,7 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer, de # end def _read_lines_from_fno(fno, last_buf_list): - buf = os.read(fno, mmap.PAGESIZE) + buf = fno.read(mmap.PAGESIZE) buf = last_buf_list[0] + buf bi = 0 @@ -192,8 +200,8 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer, de else: # poll is preferred, as select is limited to file handles up to 1024 ... . This could otherwise be # an issue for us, as it matters how many handles our own process has - fdmap = {outfn: (stdout_handler, [b''], decode_streams), - errfn: (stderr_handler, [b''], decode_streams)} + fdmap = {outfn: (process.stdout, stdout_handler, [ZERO], decode_streams), + errfn: (process.stderr, stderr_handler, [ZERO], decode_streams)} READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR # @UndefinedVariable CLOSED = select.POLLHUP | select.POLLERR # @UndefinedVariable @@ -217,7 +225,7 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer, de if result & CLOSED: closed_streams.add(fd) else: - _dispatch_lines(fd, *fdmap[fd]) + _dispatch_lines(*fdmap[fd]) # end handle closed stream # end for each poll-result tuple @@ -227,8 +235,8 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer, de # end endless loop # Depelete all remaining buffers - for fno, (handler, buf_list, decode) in fdmap.items(): - _deplete_buffer(fno, handler, buf_list, decode) + for fno, args in fdmap.items(): + _deplete_buffer(*args) # end for each file handle for fno in fdmap.keys(): -- cgit v1.2.3 From 0574b8b921dbfe1b39de68be7522b248b8404892 Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Wed, 28 Sep 2016 17:56:21 +0200 Subject: ABANDON select/poll --- git/cmd.py | 233 +++++++++++++------------------------------------------------ 1 file changed, 48 insertions(+), 185 deletions(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index 835be605..3d9435ba 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -4,48 +4,43 @@ # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php -import os -import sys -import select -import logging -import threading -import errno -import mmap - -from git.odict import OrderedDict from contextlib import contextmanager +import io +import logging +import os import signal -import subprocess from subprocess import ( call, Popen, PIPE ) +import subprocess +import sys +import threading - -from .util import ( - LazyMixin, - stream_copy, -) -from .exc import ( - GitCommandError, - GitCommandNotFound -) from git.compat import ( string_types, defenc, force_bytes, PY3, - bchr, # just to satisfy flake8 on py3 unicode, safe_decode, is_posix, is_win, ) -import io -from _io import UnsupportedOperation from git.exc import CommandError +from git.odict import OrderedDict + +from .exc import ( + GitCommandError, + GitCommandNotFound +) +from .util import ( + LazyMixin, + stream_copy, +) + execute_kwargs = set(('istream', 'with_keep_cwd', 'with_extended_output', 'with_exceptions', 'as_process', 'stdout_as_string', @@ -57,13 +52,6 @@ log.addHandler(logging.NullHandler()) __all__ = ('Git',) -if PY3: - _bchr = bchr -else: - def _bchr(c): - return c -# get custom byte character handling - # ============================================================================== ## @name Utilities @@ -73,8 +61,7 @@ else: def handle_process_output(process, stdout_handler, stderr_handler, finalizer, decode_streams=True): """Registers for notifications to lean that process output is ready to read, and dispatches lines to - the respective line handlers. We are able to handle carriage returns in case progress is sent by that - mean. For performance reasons, we only apply this to stderr. + the respective line handlers. This function returns once the finalizer returns :return: result of finalizer @@ -88,160 +75,36 @@ def handle_process_output(process, stdout_handler, stderr_handler, finalizer, de Set it to False if `universal_newline == True` (then streams are in text-mode) or if decoding must happen later (i.e. for Diffs). """ - if decode_streams: - ZERO = b'' - LF = b'\n' - CR = b'\r' - else: - ZERO = u'' - LF = u'\n' - CR = u'\r' - - def _parse_lines_from_buffer(buf): - line = ZERO - bi = 0 - lb = len(buf) - while bi < lb: - char = buf[bi] - bi += 1 - - if char in (LF, CR) and line: - yield bi, line + LF - line = ZERO - else: - line += char - # END process parsed line - # END while file is not done reading - # end - - def _read_lines_from_fno(fno, last_buf_list): - buf = fno.read(mmap.PAGESIZE) - buf = last_buf_list[0] + buf - - bi = 0 - for bi, line in _parse_lines_from_buffer(buf): - yield line - # for each line to parse from the buffer - - # keep remainder - last_buf_list[0] = buf[bi:] - - def _dispatch_single_line(line, handler, decode): - if decode: - line = line.decode(defenc) - if line and handler: - handler(line) - # end dispatch helper - # end single line helper - - def _dispatch_lines(fno, handler, buf_list, decode): - lc = 0 - for line in _read_lines_from_fno(fno, buf_list): - _dispatch_single_line(line, handler, decode) - lc += 1 - # for each line - return lc - # end - - def _deplete_buffer(fno, handler, buf_list, decode): - lc = 0 - while True: - line_count = _dispatch_lines(fno, handler, buf_list, decode) - lc += line_count - if line_count == 0: - break - # end deplete buffer - - if buf_list[0]: - _dispatch_single_line(buf_list[0], handler, decode) - lc += 1 - # end - - return lc - # end - - try: - outfn = process.stdout.fileno() - errfn = process.stderr.fileno() - poll = select.poll() # @UndefinedVariable - except (UnsupportedOperation, AttributeError): - # Oh ... probably we are on windows. or TC mockap provided for streams. - # Anyhow, select.select() can only handle sockets, we have files - # The only reliable way to do this now is to use threads and wait for both to finish - def pump_stream(cmdline, name, stream, is_decode, handler): - try: - for line in stream: - if handler: - if is_decode: - line = line.decode(defenc) - handler(line) - except Exception as ex: - log.error("Pumping %r of cmd(%s) failed due to: %r", name, cmdline, ex) - raise CommandError(['<%s-pump>' % name] + cmdline, ex) - finally: - stream.close() - - cmdline = getattr(process, 'args', '') # PY3+ only - if not isinstance(cmdline, (tuple, list)): - cmdline = cmdline.split() - threads = [] - for name, stream, handler in ( - ('stdout', process.stdout, stdout_handler), - ('stderr', process.stderr, stderr_handler), - ): - t = threading.Thread(target=pump_stream, - args=(cmdline, name, stream, decode_streams, handler)) - t.setDaemon(True) - t.start() - threads.append(t) - - for t in threads: - t.join() - else: - # poll is preferred, as select is limited to file handles up to 1024 ... . This could otherwise be - # an issue for us, as it matters how many handles our own process has - fdmap = {outfn: (process.stdout, stdout_handler, [ZERO], decode_streams), - errfn: (process.stderr, stderr_handler, [ZERO], decode_streams)} - - READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR # @UndefinedVariable - CLOSED = select.POLLHUP | select.POLLERR # @UndefinedVariable - - poll.register(process.stdout, READ_ONLY) - poll.register(process.stderr, READ_ONLY) - - closed_streams = set() - while True: - # no timeout - - try: - poll_result = poll.poll() - except select.error as e: - if e.args[0] == errno.EINTR: - continue - raise - # end handle poll exception - - for fd, result in poll_result: - if result & CLOSED: - closed_streams.add(fd) - else: - _dispatch_lines(*fdmap[fd]) - # end handle closed stream - # end for each poll-result tuple - - if len(closed_streams) == len(fdmap): - break - # end its all done - # end endless loop - - # Depelete all remaining buffers - for fno, args in fdmap.items(): - _deplete_buffer(*args) - # end for each file handle - - for fno in fdmap.keys(): - poll.unregister(fno) - # end don't forget to unregister ! + # Use 2 "pupm" threads and wait for both to finish. + def pump_stream(cmdline, name, stream, is_decode, handler): + try: + for line in stream: + if handler: + if is_decode: + line = line.decode(defenc) + handler(line) + except Exception as ex: + log.error("Pumping %r of cmd(%s) failed due to: %r", name, cmdline, ex) + raise CommandError(['<%s-pump>' % name] + cmdline, ex) + finally: + stream.close() + + cmdline = getattr(process, 'args', '') # PY3+ only + if not isinstance(cmdline, (tuple, list)): + cmdline = cmdline.split() + threads = [] + for name, stream, handler in ( + ('stdout', process.stdout, stdout_handler), + ('stderr', process.stderr, stderr_handler), + ): + t = threading.Thread(target=pump_stream, + args=(cmdline, name, stream, decode_streams, handler)) + t.setDaemon(True) + t.start() + threads.append(t) + + for t in threads: + t.join() return finalizer(process) -- cgit v1.2.3 From b114f3bbe50f50477778a0a13cf99c0cfee1392a Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Fri, 30 Sep 2016 00:49:38 +0200 Subject: ci: Capture logging for Popen() execute statements. + Collect all known commands --- git/cmd.py | 1 + 1 file changed, 1 insertion(+) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index 3d9435ba..b47b2a02 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -535,6 +535,7 @@ class Git(LazyMixin): cmd_not_found_exception = OSError # end handle + log.debug("Popen(%s, cwd=%s, universal_newlines=%s", command, cwd, universal_newlines) try: proc = Popen(command, env=env, -- cgit v1.2.3 From b8b025f719b2c3203e194580bbd0785a26c08ebd Mon Sep 17 00:00:00 2001 From: Kostis Anagnostopoulos Date: Sat, 1 Oct 2016 16:02:20 +0200 Subject: Win, #519: FIX repo TCs. + FIX TestRepo.test_submodule_update(): + submod: del `.git` file prior overwrite; Windows denied otherwise! + FIX TestRepo.test_untracked_files(): + In the `git add ` case, it failed with unicode args on PY2. Had to encode them with `locale.getpreferredencoding()` AND use SHELL. + cmd: add `shell` into `execute()` kwds, for overriding USE_SHELL per command. + repo: replace blocky `communicate()` in `_clone()` with thread-pumps. + test_repo.py: unittestize (almost all) assertions. + Replace open --> with open for index (base and TC). + test_index.py: Enabled a dormant assertion. --- git/cmd.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'git/cmd.py') diff --git a/git/cmd.py b/git/cmd.py index b47b2a02..f4f5f99a 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -45,7 +45,7 @@ from .util import ( execute_kwargs = set(('istream', 'with_keep_cwd', 'with_extended_output', 'with_exceptions', 'as_process', 'stdout_as_string', 'output_stream', 'with_stdout', 'kill_after_timeout', - 'universal_newlines')) + 'universal_newlines', 'shell')) log = logging.getLogger('git.cmd') log.addHandler(logging.NullHandler()) @@ -176,8 +176,8 @@ class Git(LazyMixin): GIT_PYTHON_GIT_EXECUTABLE = os.environ.get(_git_exec_env_var, git_exec_name) # If True, a shell will be used when executing git commands. - # This should only be desirable on windows, see https://github.com/gitpython-developers/GitPython/pull/126 - # for more information + # This should only be desirable on Windows, see https://github.com/gitpython-developers/GitPython/pull/126 + # and check `git/test_repo.py:TestRepo.test_untracked_files()` TC for an example where it is required. # Override this value using `Git.USE_SHELL = True` USE_SHELL = False @@ -422,6 +422,7 @@ class Git(LazyMixin): kill_after_timeout=None, with_stdout=True, universal_newlines=False, + shell=None, **subprocess_kwargs ): """Handles executing the command on the shell and consumes and returns @@ -479,6 +480,9 @@ class Git(LazyMixin): :param universal_newlines: if True, pipes will be opened as text, and lines are split at all known line endings. + :param shell: + Whether to invoke commands through a shell (see `Popen(..., shell=True)`). + It overrides :attr:`USE_SHELL` if it is not `None`. :param kill_after_timeout: To specify a timeout in seconds for the git command, after which the process should be killed. This will have no effect if as_process is set to True. It is @@ -544,7 +548,7 @@ class Git(LazyMixin): stdin=istream, stderr=PIPE, stdout=PIPE if with_stdout else open(os.devnull, 'wb'), - shell=self.USE_SHELL, + shell=shell is not None and shell or self.USE_SHELL, close_fds=(is_posix), # unsupported on windows universal_newlines=universal_newlines, creationflags=PROC_CREATIONFLAGS, -- cgit v1.2.3