which()   F
last analyzed

Complexity

Conditions 18

Size

Total Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 18
c 2
b 0
f 0
dl 0
loc 46
rs 2.6493

How to fix   Complexity   

Complexity

Complex classes like which() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
"""Print where on the path an executable is located.
3
"""
4
from __future__ import print_function
5
import sys
6
import os
7
from stat import ST_MODE, S_IXUSR, S_IXGRP, S_IXOTH
8
9
10
def get_executable(name):
11
    """Return the first executable on the path that matches `name`.
12
    """
13
    for result in which(name):
14
        return result
15
    return None
16
17
18
def get_path_directories():
19
    """Return a list of all the directories on the path.
20
    """
21
    pth = os.environ['PATH']
22
    if sys.platform == 'win32' and os.environ.get("BASH"):
23
        # winbash has a bug..
24
        if pth[1] == ';':  # pragma: nocover
25
            pth = pth.replace(';', ':', 1)
26
    return [p.strip() for p in pth.split(os.pathsep) if p.strip()]
27
28
29
def is_executable(fname):
30
    """Check if a file is executable.
31
    """
32
    return os.stat(fname)[ST_MODE] & (S_IXUSR | S_IXGRP | S_IXOTH)
33
34
35
def _listdir(pth, extensions):
36
    """Non-raising listdir."""
37
    try:
38
        return [fname for fname in os.listdir(pth)
39
                if os.path.splitext(fname)[1] in extensions]
40
    except OSError:  # pragma: nocover
41
        pass
42
43
44
def _normalize(pth):
45
    return os.path.normcase(os.path.normpath(pth))
46
47
48
def which(filename, interactive=False, verbose=False):
49
    """Yield all executable files on path that matches `filename`.
50
    """
51
    exe = [e.lower() for e in os.environ.get('PATHEXT', '').split(';')]
52
    if sys.platform != 'win32':  # pragma: nocover
53
        exe.append('')
54
55
    name, ext = os.path.splitext(filename)
56
    has_extension = bool(ext)
57
    if has_extension and ext.lower() not in exe:
58
        raise ValueError("which can only search for executable files")
59
60
    def match(filenames):
61
        """Returns the sorted subset of ``filenames`` that matches ``filename``.
62
        """
63
        res = set()
64
        for fname in filenames:
65
            if fname == filename:  # pragma: nocover
66
                res.add(fname)  # exact match
67
                continue
68
            fname_name, fname_ext = os.path.splitext(fname)
69
            if fname_name == name and fname_ext.lower() in exe: # pragma: nocover
70
                res.add(fname)
71
        return sorted(res)
72
73
    returnset = set()
74
    found = False
75
    for pth in get_path_directories():
76
        if verbose:  # pragma: nocover
77
            print('checking pth..')
78
79
        fnames = _listdir(pth, exe)
80
        if not fnames:
81
            continue
82
83
        for m in match(fnames):
84
            found_file = _normalize(os.path.join(pth, m))
85
            if found_file not in returnset:  # pragma: nocover
86
                if is_executable(found_file):
87
                    yield found_file
88
                returnset.add(found_file)
89
        found = True
90
91
    if not found and interactive:  # pragma: nocover
92
        print("Couldn't find %r anywhere on the path.." % filename)
93
        sys.exit(1)
94
95
96
if __name__ == "__main__":  # pragma: nocover
97
    _args = sys.argv
98
    for _fname in which(_args[1], interactive=True, verbose='-v' in _args):
99
        print(_fname)
100
    sys.exit(0)
101