Cross-platform cross-python shutil.which functionality for finding executable programs in the system PATH.
npx @tessl/cli install tessl/pypi-whichcraft@0.6.00
# whichcraft
1
2
whichcraft provides cross-platform cross-python shutil.which functionality for finding executable programs in the system PATH. It serves as a compatibility shim that works across multiple Python versions (2.7, 3.6, 3.7+) and operating systems (Linux, macOS, Windows), addressing the gap where shutil.which was only available in Python 3.3+.
3
4
## Package Information
5
6
- **Package Name**: whichcraft
7
- **Package Type**: pypi
8
- **Language**: Python
9
- **Installation**: `pip install whichcraft`
10
11
## Core Imports
12
13
```python
14
from whichcraft import which
15
```
16
17
Module import:
18
19
```python
20
import whichcraft
21
# Access as whichcraft.which()
22
```
23
24
## Basic Usage
25
26
```python
27
from whichcraft import which
28
29
# Find a command in PATH
30
executable_path = which('python')
31
if executable_path:
32
print(f"Python found at: {executable_path}")
33
else:
34
print("Python not found in PATH")
35
36
# Search for common utilities
37
date_cmd = which('date') # '/bin/date' on Unix-like systems
38
git_cmd = which('git') # Path to git executable if installed
39
nonexistent = which('fake-cmd') # None if not found
40
41
# Custom search path
42
custom_path = which('python', path='/usr/local/bin:/usr/bin')
43
44
# Custom file access mode
45
executable = which('script.py', mode=os.F_OK | os.X_OK)
46
```
47
48
## Capabilities
49
50
### Executable Discovery
51
52
Locates executable programs in the system PATH with cross-platform compatibility and Python version independence.
53
54
```python { .api }
55
def which(cmd, mode=os.F_OK | os.X_OK, path=None):
56
"""
57
Given a command, mode, and a PATH string, return the path which
58
conforms to the given mode on the PATH, or None if there is no such
59
file.
60
61
Parameters:
62
- cmd (str): Command name to search for
63
- mode (int, optional): File access mode, defaults to os.F_OK | os.X_OK
64
- path (str, optional): Custom search path, defaults to os.environ.get("PATH")
65
66
Returns:
67
str or None: Full path to executable if found, None otherwise
68
69
Platform-specific behavior:
70
- Windows: Handles PATHEXT environment variable and current directory precedence
71
- Unix-like: Direct command search without extensions
72
"""
73
```
74
75
#### Usage Examples
76
77
**Basic executable search:**
78
79
```python
80
from whichcraft import which
81
82
# Find Python interpreter
83
python_path = which('python')
84
if python_path:
85
print(f"Python executable: {python_path}")
86
87
# Find system utilities
88
date_path = which('date') # Unix/Linux/macOS
89
where_path = which('where') # Windows
90
```
91
92
**Custom search path:**
93
94
```python
95
import os
96
from whichcraft import which
97
98
# Search in specific directories
99
custom_path = '/usr/local/bin:/opt/bin'
100
result = which('custom-tool', path=custom_path)
101
102
# Search in environment PATH plus additional directory
103
extended_path = os.environ.get('PATH') + os.pathsep + '/home/user/bin'
104
result = which('my-script', path=extended_path)
105
```
106
107
**Custom file access mode:**
108
109
```python
110
import os
111
from whichcraft import which
112
113
# Default mode: readable and executable
114
default_result = which('python')
115
116
# Custom mode: only check if file exists
117
exists_result = which('python', mode=os.F_OK)
118
119
# Custom mode: readable, writable, and executable
120
rwx_result = which('python', mode=os.F_OK | os.R_OK | os.W_OK | os.X_OK)
121
```
122
123
## Platform-Specific Behavior
124
125
### Windows
126
- **PATHEXT handling**: Automatically appends extensions (.exe, .bat, .cmd, etc.) from PATHEXT environment variable
127
- **Current directory precedence**: Searches current directory first if not explicitly in PATH
128
- **Case insensitive**: Command matching is case-insensitive
129
130
### Unix-like Systems (Linux, macOS)
131
- **No extensions**: Searches for exact command name without adding extensions
132
- **Case sensitive**: Command matching is case-sensitive
133
- **Standard PATH search**: Follows traditional Unix PATH behavior
134
135
## Compatibility
136
137
- **Python 2.7**: Full support with custom implementation
138
- **Python 3.6+**: Uses shutil.which when available, falls back to custom implementation
139
- **Cross-platform**: Windows, Linux, macOS support
140
- **Fallback implementation**: Provides shutil.which functionality for Python < 3.3
141
142
## Types
143
144
```python { .api }
145
# Module-level constants
146
__author__: str = "Daniel Roy Greenfeld"
147
__email__: str = "pydanny@gmail.com"
148
__version__: str = "0.6.1"
149
```
150
151
## Error Handling
152
153
The `which` function handles errors gracefully:
154
155
- **Non-existent commands**: Returns `None` instead of raising exceptions
156
- **Invalid paths**: Returns `None` for inaccessible directories
157
- **Permission errors**: Uses `os.access()` to check permissions before returning paths
158
- **Import fallback**: Gracefully falls back to custom implementation when `shutil.which` is unavailable