1 """This module provides core functions for handling unicode and UNIX quirks
3 The @interruptable functions retry when system calls are interrupted,
4 e.g. when python raises an IOError or OSError with errno == EINTR.
7 from __future__
import division
, absolute_import
, unicode_literals
15 from .decorators
import interruptable
16 from .compat
import ustr
17 from .compat
import PY2
18 from .compat
import PY3
19 from .compat
import WIN32
21 # /usr/include/stdlib.h
22 # #define EXIT_SUCCESS 0 /* Successful exit status. */
23 # #define EXIT_FAILURE 1 /* Failing exit status. */
27 # /usr/include/sysexits.h
28 # #define EX_USAGE 64 /* command line usage error */
29 # #define EX_NOINPUT 66 /* cannot open input */
30 # #define EX_UNAVAILABLE 69 /* service unavailable */
38 # Some files are not in UTF-8; some other aren't in any codification.
39 # Remember that GIT doesn't care about encodings (saves binary data)
45 # <-- add encodings here
50 """Unicode string wrapper that remembers its encoding
52 UStr wraps unicode strings to provide the `encoding` attribute.
53 UStr is used when decoding strings of an unknown encoding.
54 In order to generate patches that contain the original byte sequences,
55 we must preserve the original encoding when calling decode()
56 so that it can later be used when reconstructing the original
60 def __new__(cls
, string
, encoding
):
62 if isinstance(string
, UStr
):
63 if encoding
!= string
.encoding
:
64 raise ValueError('Encoding conflict: %s vs. %s'
65 % (string
.encoding
, encoding
))
68 obj
= ustr
.__new
__(cls
, string
)
69 obj
.encoding
= encoding
73 def decode(value
, encoding
=None, errors
='strict'):
74 """decode(encoded_string) returns an unencoded unicode string
78 elif isinstance(value
, ustr
):
79 result
= UStr(value
, ENCODING
)
83 encoding_tests
= _encoding_tests
85 encoding_tests
= itertools
.chain([encoding
], _encoding_tests
)
87 for enc
in encoding_tests
:
89 decoded
= value
.decode(enc
, errors
)
90 result
= UStr(decoded
, enc
)
96 decoded
= value
.decode(ENCODING
, errors
='ignore')
97 result
= UStr(decoded
, ENCODING
)
102 def encode(string
, encoding
=None):
103 """encode(unencoded_string) returns a string encoded in utf-8
105 if not isinstance(string
, ustr
):
107 return string
.encode(encoding
or ENCODING
, 'replace')
110 def mkpath(path
, encoding
=None):
111 # The Windows API requires unicode strings regardless of python version
113 return decode(path
, encoding
=encoding
)
115 return encode(path
, encoding
=encoding
)
118 def list2cmdline(cmd
):
119 return subprocess
.list2cmdline([decode(c
) for c
in cmd
])
122 def read(filename
, size
=-1, encoding
=None, errors
='strict'):
123 """Read filename and return contents"""
124 with
xopen(filename
, 'rb') as fh
:
125 return xread(fh
, size
=size
, encoding
=encoding
, errors
=errors
)
128 def write(path
, contents
, encoding
=None):
129 """Writes a unicode string to a file"""
130 with
xopen(path
, 'wb') as fh
:
131 return xwrite(fh
, contents
, encoding
=encoding
)
135 def xread(fh
, size
=-1, encoding
=None, errors
='strict'):
136 """Read from a filehandle and retry when interrupted"""
137 return decode(fh
.read(size
), encoding
=encoding
, errors
=errors
)
141 def xwrite(fh
, content
, encoding
=None):
142 """Write to a filehandle and retry when interrupted"""
143 return fh
.write(encode(content
, encoding
=encoding
))
148 """Wait on a subprocess and retry when interrupted"""
153 def readline(fh
, encoding
=None):
154 return decode(fh
.readline(), encoding
=encoding
)
158 def start_command(cmd
, cwd
=None, add_env
=None,
159 universal_newlines
=False,
160 stdin
=subprocess
.PIPE
,
161 stdout
=subprocess
.PIPE
,
162 no_win32_startupinfo
=False,
163 stderr
=subprocess
.PIPE
,
165 """Start the given command, and return a subprocess object.
167 This provides a simpler interface to the subprocess module.
170 env
= extra
.pop('env', None)
171 if add_env
is not None:
172 env
= os
.environ
.copy()
175 # Python3 on windows always goes through list2cmdline() internally inside
176 # of subprocess.py so we must provide unicode strings here otherwise
177 # Python3 breaks when bytes are provided.
179 # Additionally, the preferred usage on Python3 is to pass unicode
180 # strings to subprocess. Python will automatically encode into the
181 # default encoding (utf-8) when it gets unicode strings.
182 shell
= extra
.get('shell', False)
183 cmd
= prep_for_subprocess(cmd
, shell
=shell
)
185 if WIN32
and cwd
== getcwd():
186 # Windows cannot deal with passing a cwd that contains unicode
187 # but we luckily can pass None when the supplied cwd is the same
188 # as our current directory and get the same effect.
189 # Not doing this causes unicode encoding errors when launching
197 # If git-cola is invoked on Windows using "start pythonw git-cola",
198 # a console window will briefly flash on the screen each time
199 # git-cola invokes git, which is very annoying. The code below
200 # prevents this by ensuring that any window will be hidden.
201 startupinfo
= subprocess
.STARTUPINFO()
202 startupinfo
.dwFlags
= subprocess
.STARTF_USESHOWWINDOW
203 startupinfo
.wShowWindow
= subprocess
.SW_HIDE
204 extra
['startupinfo'] = startupinfo
206 if WIN32
and not no_win32_startupinfo
:
207 CREATE_NO_WINDOW
= 0x08000000
208 extra
['creationflags'] = CREATE_NO_WINDOW
210 return subprocess
.Popen(cmd
, bufsize
=1, stdin
=stdin
, stdout
=stdout
,
211 stderr
=stderr
, cwd
=cwd
, env
=env
,
212 universal_newlines
=universal_newlines
, **extra
)
215 def prep_for_subprocess(cmd
, shell
=False):
216 """Decode on Python3, encode on Python2"""
217 # See the comment in start_command()
225 cmd
= [decode(c
) for c
in cmd
]
227 cmd
= [encode(c
) for c
in cmd
]
232 def communicate(proc
):
233 return proc
.communicate()
236 def run_command(cmd
, *args
, **kwargs
):
237 """Run the given command to completion, and return its results.
239 This provides a simpler interface to the subprocess module.
240 The results are formatted as a 3-tuple: (exit_code, output, errors)
241 The other arguments are passed on to start_command().
244 encoding
= kwargs
.pop('encoding', None)
245 process
= start_command(cmd
, *args
, **kwargs
)
246 (output
, errors
) = communicate(process
)
247 output
= decode(output
, encoding
=encoding
)
248 errors
= decode(errors
, encoding
=encoding
)
249 exit_code
= process
.returncode
251 output
or UStr('', ENCODING
),
252 errors
or UStr('', ENCODING
))
256 def _fork_posix(args
, cwd
=None):
257 """Launch a process in the background."""
258 encoded_args
= [encode(arg
) for arg
in args
]
259 return subprocess
.Popen(encoded_args
, cwd
=cwd
).pid
262 def _fork_win32(args
, cwd
=None):
263 """Launch a background process using crazy win32 voodoo."""
264 # This is probably wrong, but it works. Windows.. wow.
265 if args
[0] == 'git-dag':
266 # win32 can't exec python scripts
267 args
= [sys
.executable
] + args
268 args
[0] = _win32_find_exe(args
[0])
271 # see comment in start_command()
272 argv
= [decode(arg
) for arg
in args
]
274 argv
= [encode(arg
) for arg
in args
]
276 DETACHED_PROCESS
= 0x00000008 # Amazing!
277 return subprocess
.Popen(argv
, cwd
=cwd
, creationflags
=DETACHED_PROCESS
).pid
280 def _win32_find_exe(exe
):
281 """Find the actual file for a Windows executable.
283 This function goes through the same process that the Windows shell uses to
284 locate an executable, taking into account the PATH and PATHEXT environment
285 variables. This allows us to avoid passing shell=True to subprocess.Popen.
288 http://technet.microsoft.com/en-us/library/cc723564.aspx#XSLTsection127121120120
291 # try the argument itself
293 # if argument does not have an extension, also try it with each of the
294 # extensions specified in PATHEXT
296 extensions
= getenv('PATHEXT', '').split(os
.pathsep
)
297 candidates
.extend([(exe
+ ext
) for ext
in extensions
298 if ext
.startswith('.')])
299 # search the current directory first
300 for candidate
in candidates
:
301 if exists(candidate
):
303 # if the argument does not include a path separator, search each of the
304 # directories on the PATH
305 if not os
.path
.dirname(exe
):
306 for path
in getenv('PATH').split(os
.pathsep
):
308 for candidate
in candidates
:
309 full_path
= os
.path
.join(path
, candidate
)
310 if exists(full_path
):
312 # not found, punt and return the argument unchanged
316 # Portability wrappers
317 if sys
.platform
== 'win32' or sys
.platform
== 'cygwin':
323 def _decorator_noop(x
):
327 def wrap(action
, fn
, decorator
=None):
328 """Wrap arguments with `action`, optionally decorate the result"""
329 if decorator
is None:
330 decorator
= _decorator_noop
333 def wrapped(*args
, **kwargs
):
334 return decorator(fn(action(*args
, **kwargs
)))
339 def decorate(decorator
, fn
):
340 """Decorate the result of `fn` with `action`"""
342 def decorated(*args
, **kwargs
):
343 return decorator(fn(*args
, **kwargs
))
347 def getenv(name
, default
=None):
348 return decode(os
.getenv(name
, default
))
351 def xopen(path
, mode
='r', encoding
=None):
352 return open(mkpath(path
, encoding
=encoding
), mode
)
355 def print_stdout(msg
, linesep
='\n'):
358 msg
= encode(msg
, encoding
=ENCODING
)
359 sys
.stdout
.write(msg
)
362 def print_stderr(msg
, linesep
='\n'):
365 msg
= encode(msg
, encoding
=ENCODING
)
366 sys
.stderr
.write(msg
)
369 def error(msg
, status
=EXIT_FAILURE
, linesep
='\n'):
370 print_stderr(msg
, linesep
=linesep
)
376 return platform
.node()
379 abspath
= wrap(mkpath
, os
.path
.abspath
, decorator
=decode
)
380 chdir
= wrap(mkpath
, os
.chdir
)
381 exists
= wrap(mkpath
, os
.path
.exists
)
382 expanduser
= wrap(encode
, os
.path
.expanduser
, decorator
=decode
)
384 if hasattr(os
, 'getcwdu'):
385 # pylint: disable=no-member
388 getcwd
= decorate(decode
, os
.getcwd
)
393 # NOTE: find_executable() is originally from the stdlib, but starting with
394 # python3.7 the stdlib no longer bundles distutils.
395 def _find_executable(executable
, path
=None):
396 """Tries to find 'executable' in the directories listed in 'path'.
398 A string listing directories separated by 'os.pathsep'; defaults to
399 os.environ['PATH']. Returns the complete filename or None if not found.
402 path
= os
.environ
['PATH']
404 paths
= path
.split(os
.pathsep
)
405 _
, ext
= os
.path
.splitext(executable
)
407 if (sys
.platform
== 'win32') and (ext
!= '.exe'):
408 executable
= executable
+ '.exe'
410 if not os
.path
.isfile(executable
):
412 f
= os
.path
.join(p
, executable
)
413 if os
.path
.isfile(f
):
414 # the file exists, we have a shot at spawn working
422 find_executable
= wrap(mkpath
, _find_executable
, decorator
=decode
)
424 find_executable
= wrap(decode
, _find_executable
, decorator
=decode
)
425 isdir
= wrap(mkpath
, os
.path
.isdir
)
426 isfile
= wrap(mkpath
, os
.path
.isfile
)
427 islink
= wrap(mkpath
, os
.path
.islink
)
428 makedirs
= wrap(mkpath
, os
.makedirs
)
430 readlink
= wrap(mkpath
, os
.readlink
, decorator
=decode
)
431 except AttributeError:
433 def _readlink_noop(p
):
436 readlink
= _readlink_noop
438 realpath
= wrap(mkpath
, os
.path
.realpath
, decorator
=decode
)
439 relpath
= wrap(mkpath
, os
.path
.relpath
, decorator
=decode
)
440 stat
= wrap(mkpath
, os
.stat
)
441 unlink
= wrap(mkpath
, os
.unlink
)
442 walk
= wrap(mkpath
, os
.walk
)