##// END OF EJS Templates
Merge pull request #8244 from minrk/shutil.which...
Thomas Kluyver -
r21130:ec311f30 merge
parent child Browse files
Show More
@@ -26,32 +26,16 b' IPython.external.decorators, we import either numpy.testing.decorators if numpy '
26 available, OR use equivalent code in IPython.external._decorators, which
26 available, OR use equivalent code in IPython.external._decorators, which
27 we've copied verbatim from numpy.
27 we've copied verbatim from numpy.
28
28
29 Authors
30 -------
31
32 - Fernando Perez <Fernando.Perez@berkeley.edu>
33 """
29 """
34
30
35 #-----------------------------------------------------------------------------
31 # Copyright (c) IPython Development Team.
36 # Copyright (C) 2009-2011 The IPython Development Team
32 # Distributed under the terms of the Modified BSD License.
37 #
38 # Distributed under the terms of the BSD License. The full license is in
39 # the file COPYING, distributed as part of this software.
40 #-----------------------------------------------------------------------------
41
33
42 #-----------------------------------------------------------------------------
43 # Imports
44 #-----------------------------------------------------------------------------
45
46 # Stdlib imports
47 import sys
34 import sys
48 import os
35 import os
49 import tempfile
36 import tempfile
50 import unittest
37 import unittest
51
38
52 # Third-party imports
53
54 # This is Michele Simionato's decorator module, kept verbatim.
55 from decorator import decorator
39 from decorator import decorator
56
40
57 # Expose the unittest-driven decorators
41 # Expose the unittest-driven decorators
@@ -63,8 +47,7 b' from .ipunittest import ipdoctest, ipdocstring'
63 from IPython.external.decorators import *
47 from IPython.external.decorators import *
64
48
65 # For onlyif_cmd_exists decorator
49 # For onlyif_cmd_exists decorator
66 from IPython.utils.process import is_cmd_found
50 from IPython.utils.py3compat import string_types, which
67 from IPython.utils.py3compat import string_types
68
51
69 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
70 # Classes and functions
53 # Classes and functions
@@ -370,16 +353,9 b' def onlyif_cmds_exist(*commands):'
370 Decorator to skip test when at least one of `commands` is not found.
353 Decorator to skip test when at least one of `commands` is not found.
371 """
354 """
372 for cmd in commands:
355 for cmd in commands:
373 try:
356 if not which(cmd):
374 if not is_cmd_found(cmd):
357 return skip("This test runs only if command '{0}' "
375 return skip("This test runs only if command '{0}' "
358 "is installed".format(cmd))
376 "is installed".format(cmd))
377 except ImportError as e:
378 # is_cmd_found uses pywin32 on windows, which might not be available
379 if sys.platform == 'win32' and 'pywin32' in str(e):
380 return skip("This test runs only if pywin32 and command '{0}' "
381 "is installed".format(cmd))
382 raise e
383 return null_deco
359 return null_deco
384
360
385 def onlyif_any_cmd_exists(*commands):
361 def onlyif_any_cmd_exists(*commands):
@@ -387,14 +363,7 b' def onlyif_any_cmd_exists(*commands):'
387 Decorator to skip test unless at least one of `commands` is found.
363 Decorator to skip test unless at least one of `commands` is found.
388 """
364 """
389 for cmd in commands:
365 for cmd in commands:
390 try:
366 if which(cmd):
391 if is_cmd_found(cmd):
367 return null_deco
392 return null_deco
393 except ImportError as e:
394 # is_cmd_found uses pywin32 on windows, which might not be available
395 if sys.platform == 'win32' and 'pywin32' in str(e):
396 return skip("This test runs only if pywin32 and commands '{0}' "
397 "are installed".format(commands))
398 raise e
399 return skip("This test runs only if one of the commands {0} "
368 return skip("This test runs only if one of the commands {0} "
400 "is installed".format(commands))
369 "is installed".format(commands))
@@ -3,37 +3,24 b''
3 Utilities for working with external processes.
3 Utilities for working with external processes.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 # Copyright (c) IPython Development Team.
7 # Copyright (C) 2008-2011 The IPython Development Team
7 # Distributed under the terms of the Modified BSD License.
8 #
8
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
9 from __future__ import print_function
17
10
18 # Stdlib
19 import os
11 import os
20 import sys
12 import sys
21
13
22 # Our own
23 if sys.platform == 'win32':
14 if sys.platform == 'win32':
24 from ._process_win32 import _find_cmd, system, getoutput, arg_split, check_pid
15 from ._process_win32 import system, getoutput, arg_split, check_pid
25 elif sys.platform == 'cli':
16 elif sys.platform == 'cli':
26 from ._process_cli import _find_cmd, system, getoutput, arg_split, check_pid
17 from ._process_cli import system, getoutput, arg_split, check_pid
27 else:
18 else:
28 from ._process_posix import _find_cmd, system, getoutput, arg_split, check_pid
19 from ._process_posix import system, getoutput, arg_split, check_pid
29
20
30 from ._process_common import getoutputerror, get_output_error_code, process_handler
21 from ._process_common import getoutputerror, get_output_error_code, process_handler
31 from . import py3compat
22 from . import py3compat
32
23
33 #-----------------------------------------------------------------------------
34 # Code
35 #-----------------------------------------------------------------------------
36
37
24
38 class FindCmdError(Exception):
25 class FindCmdError(Exception):
39 pass
26 pass
@@ -59,14 +46,10 b' def find_cmd(cmd):'
59 cmd : str
46 cmd : str
60 The command line program to look for.
47 The command line program to look for.
61 """
48 """
62 try:
49 path = py3compat.which(cmd)
63 path = _find_cmd(cmd).rstrip()
50 if path is None:
64 except OSError:
65 raise FindCmdError('command could not be found: %s' % cmd)
66 # which returns empty if not found
67 if path == '':
68 raise FindCmdError('command could not be found: %s' % cmd)
51 raise FindCmdError('command could not be found: %s' % cmd)
69 return os.path.abspath(path)
52 return path
70
53
71
54
72 def is_cmd_found(cmd):
55 def is_cmd_found(cmd):
@@ -4,6 +4,7 b' import functools'
4 import os
4 import os
5 import sys
5 import sys
6 import re
6 import re
7 import shutil
7 import types
8 import types
8
9
9 from .encoding import DEFAULT_ENCODING
10 from .encoding import DEFAULT_ENCODING
@@ -75,6 +76,70 b' def safe_unicode(e):'
75
76
76 return u'Unrecoverably corrupt evalue'
77 return u'Unrecoverably corrupt evalue'
77
78
79 # shutil.which from Python 3.4
80 def _shutil_which(cmd, mode=os.F_OK | os.X_OK, path=None):
81 """Given a command, mode, and a PATH string, return the path which
82 conforms to the given mode on the PATH, or None if there is no such
83 file.
84
85 `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
86 of os.environ.get("PATH"), or can be overridden with a custom search
87 path.
88
89 This is a backport of shutil.which from Python 3.4
90 """
91 # Check that a given file can be accessed with the correct mode.
92 # Additionally check that `file` is not a directory, as on Windows
93 # directories pass the os.access check.
94 def _access_check(fn, mode):
95 return (os.path.exists(fn) and os.access(fn, mode)
96 and not os.path.isdir(fn))
97
98 # If we're given a path with a directory part, look it up directly rather
99 # than referring to PATH directories. This includes checking relative to the
100 # current directory, e.g. ./script
101 if os.path.dirname(cmd):
102 if _access_check(cmd, mode):
103 return cmd
104 return None
105
106 if path is None:
107 path = os.environ.get("PATH", os.defpath)
108 if not path:
109 return None
110 path = path.split(os.pathsep)
111
112 if sys.platform == "win32":
113 # The current directory takes precedence on Windows.
114 if not os.curdir in path:
115 path.insert(0, os.curdir)
116
117 # PATHEXT is necessary to check on Windows.
118 pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
119 # See if the given file matches any of the expected path extensions.
120 # This will allow us to short circuit when given "python.exe".
121 # If it does match, only test that one, otherwise we have to try
122 # others.
123 if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
124 files = [cmd]
125 else:
126 files = [cmd + ext for ext in pathext]
127 else:
128 # On other platforms you don't have things like PATHEXT to tell you
129 # what file suffixes are executable, so just pass on cmd as-is.
130 files = [cmd]
131
132 seen = set()
133 for dir in path:
134 normdir = os.path.normcase(dir)
135 if not normdir in seen:
136 seen.add(normdir)
137 for thefile in files:
138 name = os.path.join(dir, thefile)
139 if _access_check(name, mode):
140 return name
141 return None
142
78 if sys.version_info[0] >= 3:
143 if sys.version_info[0] >= 3:
79 PY3 = True
144 PY3 = True
80
145
@@ -97,6 +162,8 b' if sys.version_info[0] >= 3:'
97 string_types = (str,)
162 string_types = (str,)
98 unicode_type = str
163 unicode_type = str
99
164
165 which = shutil.which
166
100 def isidentifier(s, dotted=False):
167 def isidentifier(s, dotted=False):
101 if dotted:
168 if dotted:
102 return all(isidentifier(a) for a in s.split("."))
169 return all(isidentifier(a) for a in s.split("."))
@@ -184,6 +251,8 b' else:'
184 def get_closure(f):
251 def get_closure(f):
185 """Get a function's closure attribute"""
252 """Get a function's closure attribute"""
186 return f.func_closure
253 return f.func_closure
254
255 which = _shutil_which
187
256
188 # Abstract u'abc' syntax:
257 # Abstract u'abc' syntax:
189 @_modify_str_or_docstring
258 @_modify_str_or_docstring
@@ -52,7 +52,7 b' def has_pywin32():'
52 def test_find_cmd_pythonw():
52 def test_find_cmd_pythonw():
53 """Try to find pythonw on Windows."""
53 """Try to find pythonw on Windows."""
54 path = find_cmd('pythonw')
54 path = find_cmd('pythonw')
55 nt.assert_true(path.endswith('pythonw.exe'))
55 assert path.lower().endswith('pythonw.exe'), path
56
56
57
57
58 @dec.onlyif(lambda : sys.platform != 'win32' or has_pywin32(),
58 @dec.onlyif(lambda : sys.platform != 'win32' or has_pywin32(),
@@ -7,7 +7,7 b' import subprocess'
7 import os
7 import os
8 import sys
8 import sys
9
9
10 from IPython.utils.process import find_cmd
10 from IPython.utils.py3compat import which
11 from IPython.utils.traitlets import Integer, List, Bool, Instance
11 from IPython.utils.traitlets import Integer, List, Bool, Instance
12 from IPython.utils.tempdir import TemporaryWorkingDirectory
12 from IPython.utils.tempdir import TemporaryWorkingDirectory
13 from .latex import LatexExporter
13 from .latex import LatexExporter
@@ -65,10 +65,12 b' class PDFExporter(LatexExporter):'
65 #We must use cp1252 encoding for calling subprocess.Popen
65 #We must use cp1252 encoding for calling subprocess.Popen
66 #Note that sys.stdin.encoding and encoding.DEFAULT_ENCODING
66 #Note that sys.stdin.encoding and encoding.DEFAULT_ENCODING
67 # could be different (cp437 in case of dos console)
67 # could be different (cp437 in case of dos console)
68 command = [c.encode('cp1252') for c in command]
68 command = [c.encode('cp1252') for c in command]
69
69
70 # This will throw a clearer error if the command is not found
70 # This will throw a clearer error if the command is not found
71 find_cmd(command_list[0])
71 cmd = which(command_list[0])
72 if cmd is None:
73 raise OSError("%s not found on PATH" % command_list[0])
72
74
73 times = 'time' if count == 1 else 'times'
75 times = 'time' if count == 1 else 'times'
74 self.log.info("Running %s %i %s: %s", command_list[0], count, times, command)
76 self.log.info("Running %s %i %s: %s", command_list[0], count, times, command)
General Comments 0
You need to be logged in to leave comments. Login now