requirements-installer 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Sina Mirshahi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ include requirements_installer.py
@@ -0,0 +1,247 @@
1
+ Metadata-Version: 2.4
2
+ Name: requirements-installer
3
+ Version: 1.0.0
4
+ Summary: Automatically detect and install Python dependencies from your code
5
+ Author-email: Sina Mirshahi <sina7th@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/FH-Prevail/requirements_installer
8
+ Project-URL: Repository, https://github.com/FH-Prevail/requirements-installer
9
+ Project-URL: Issues, https://github.com/FH-Prevail/requirements_installer/issues
10
+ Keywords: pip,requirements,dependencies,automation,installer
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.7
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: System :: Installation/Setup
23
+ Requires-Python: >=3.7
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Dynamic: license-file
27
+
28
+ # Requirements Installer πŸš€
29
+
30
+ **Tired of hunting down missing Python dependencies?** Requirements Installer automatically detects and installs all third-party packages your Python script needsβ€”no `requirements.txt` needed!
31
+
32
+ Simply point it at your Python file, and it handles the rest. Perfect for running unfamiliar scripts, quick prototyping, or setting up development environments.
33
+
34
+ ---
35
+
36
+ ## ✨ Features
37
+
38
+ - **πŸ” Smart Import Detection** - Automatically scans your Python files for all third-party imports
39
+ - **πŸ“¦ One-Command Install** - Detects and installs dependencies in a single step
40
+ - **🎯 Interactive Version Selection** - Optionally choose specific versions for each package
41
+ - **πŸ”’ Virtual Environment Support** - Create and install into isolated environments
42
+ - **🧠 Intelligent Filtering** - Excludes stdlib and local modules automatically
43
+ - **πŸ“ Module-to-Package Mapping** - Handles tricky cases like `cv2` β†’ `opencv-python`, `PIL` β†’ `Pillow`
44
+ - **🌐 One-Hop Local Import Scanning** - Follows local imports to catch all dependencies
45
+ - **⚑ Fast & Lightweight** - Pure Python with no external dependencies
46
+
47
+ ---
48
+
49
+ ## πŸ“₯ Installation
50
+
51
+ ```bash
52
+ pip install requirements-installer
53
+ ```
54
+
55
+ Or install from source:
56
+
57
+ ```bash
58
+ git clone https://github.com/FH-Prevail/requirements_installer.git
59
+ cd requirements-installer
60
+ pip install -e .
61
+ ```
62
+
63
+ ---
64
+
65
+ ## πŸš€ Quick Start
66
+
67
+ ### Basic Usage
68
+
69
+ ```bash
70
+ # Scan and install dependencies for a Python file
71
+ python requirements_installer.py --file mycode.py
72
+ ```
73
+
74
+ ### With Virtual Environment
75
+
76
+ ```bash
77
+ # Create a venv and install dependencies there
78
+ python requirements_installer.py --file mycode.py --use-venv --venv-path .venv
79
+ ```
80
+
81
+ ### Interactive Version Selection
82
+
83
+ ```bash
84
+ # Choose specific versions for each package
85
+ python requirements_installer.py --file mycode.py --ask-version
86
+ ```
87
+
88
+ Example interaction:
89
+ ```
90
+ ==================================================
91
+ Version Selection
92
+ ==================================================
93
+
94
+ Install latest version of 'numpy'? [Y/n]: n
95
+ Enter version for 'numpy' (e.g., 1.2.3): 1.24.0
96
+
97
+ Install latest version of 'pandas'? [Y/n]: y
98
+
99
+ Install latest version of 'requests'? [Y/n]:
100
+ ```
101
+
102
+ ### Print Requirements Only
103
+
104
+ ```bash
105
+ # Just show what would be installed, don't install
106
+ python requirements_installer.py --file mycode.py --print-requirements
107
+ ```
108
+
109
+ ---
110
+
111
+ ## πŸ“– Usage Examples
112
+
113
+ ### Example 1: Quick Script Setup
114
+ You clone a repo without a `requirements.txt`:
115
+
116
+ ```bash
117
+ python requirements_installer.py --file app.py
118
+ ```
119
+
120
+ Output:
121
+ ```
122
+ Detected environment: current Python at /usr/bin/python3
123
+ Entry file: /home/user/project/app.py
124
+ Installing: numpy pandas requests
125
+
126
+ ==================================================
127
+ Installation Summary
128
+ ==================================================
129
+ Installed: numpy, pandas, requests
130
+ Already satisfied: (none)
131
+ All requested packages are now present.
132
+ ```
133
+
134
+ ### Example 2: Isolated Development Environment
135
+
136
+ ```bash
137
+ python requirements_installer.py --file main.py --use-venv
138
+ ```
139
+
140
+ Creates a `.venv` folder and installs all dependencies there, keeping your system Python clean.
141
+
142
+ ### Example 3: Precise Version Control
143
+
144
+ ```bash
145
+ python requirements_installer.py --file analysis.py --ask-version
146
+ ```
147
+
148
+ Prompts you for each package, letting you pin versions for reproducibility.
149
+
150
+ ---
151
+
152
+ ## πŸ”§ Command-Line Options
153
+
154
+ ```
155
+ usage: requirements_installer.py [-h] --file FILE [--use-venv]
156
+ [--venv-path VENV_PATH]
157
+ [--print-requirements] [--ask-version]
158
+
159
+ options:
160
+ -h, --help Show this help message and exit
161
+ --file FILE Entry Python file to scan (e.g., mycode.py)
162
+ --use-venv Create and use a virtual environment
163
+ --venv-path VENV_PATH
164
+ Where to create the venv (default: .venv)
165
+ --print-requirements Only print inferred packages and exit
166
+ --ask-version Interactively ask for version preference for each package
167
+ ```
168
+
169
+ ---
170
+
171
+ ## 🎯 How It Works
172
+
173
+ 1. **Parse Imports**: Uses Python's AST to find all `import` and `from ... import` statements
174
+ 2. **Filter Standard Library**: Removes built-in Python modules (e.g., `os`, `sys`, `json`)
175
+ 3. **Filter Local Modules**: Excludes your project's own modules
176
+ 4. **Map Module Names**: Converts import names to PyPI package names (e.g., `cv2` β†’ `opencv-python`)
177
+ 5. **Check Installation**: Queries what's already installed to avoid redundant work
178
+ 6. **Install Packages**: Uses pip to install missing packages
179
+ 7. **Verify**: Confirms all required packages are present
180
+
181
+ ---
182
+
183
+ ## πŸ“Š Comparison with Other Tools
184
+
185
+ | Feature | requirements-installer | pipreqs | pigar | pythonrunscript |
186
+ |---------|----------------------|---------|-------|-----------------|
187
+ | Auto-detect imports | βœ… | βœ… | βœ… | ❌ |
188
+ | Auto-install packages | βœ… | ❌ | ❌ | βœ… |
189
+ | Interactive version selection | βœ… | ❌ | ❌ | ❌ |
190
+ | Virtual environment support | βœ… | ❌ | ❌ | βœ… |
191
+ | No source file modification | βœ… | βœ… | βœ… | ❌ |
192
+ | One-command operation | βœ… | ❌ | ❌ | βœ… |
193
+
194
+ **Why requirements-installer?**
195
+ - **pipreqs/pigar**: Generate `requirements.txt` but don't install (requires 2 steps)
196
+ - **pythonrunscript**: Installs but requires metadata comments in your code
197
+ - **requirements-installer**: Does both detection and installation, no modifications needed!
198
+
199
+ ---
200
+
201
+ ## πŸ§ͺ Module-to-Package Mappings
202
+
203
+ The tool includes smart mappings for common cases where the import name differs from the PyPI package name:
204
+
205
+ ```python
206
+ cv2 β†’ opencv-python
207
+ PIL β†’ Pillow
208
+ sklearn β†’ scikit-learn
209
+ yaml β†’ PyYAML
210
+ bs4 β†’ beautifulsoup4
211
+ dotenv β†’ python-dotenv
212
+ # ... and many more!
213
+ ```
214
+
215
+ ---
216
+
217
+ ## ⚠️ Limitations
218
+
219
+ - **One-hop scanning**: Only follows local imports one level deep (for speed)
220
+ - **Dynamic imports**: Limited detection of `importlib.import_module()` calls
221
+ - **Conditional imports**: Treats all imports equally (no context analysis)
222
+ - **Version conflicts**: Doesn't resolve complex dependency conflicts (delegates to pip)
223
+
224
+ These are deliberate trade-offs for simplicity and speed. For complex projects with intricate dependency trees, consider using Poetry or Pipenv.
225
+
226
+ ---
227
+
228
+ ## 🀝 Contributing
229
+
230
+ Contributions are welcome! Here are some ways you can help:
231
+
232
+ - πŸ› Report bugs and issues
233
+ - πŸ’‘ Suggest new features or improvements
234
+ - πŸ“ Improve documentation
235
+ - πŸ”§ Submit pull requests
236
+ - ⭐ Add more module-to-package mappings
237
+
238
+ ---
239
+
240
+
241
+ ## ⭐ Star History
242
+
243
+ If you find this tool useful, please consider giving it a star on GitHub! It helps others discover the project.
244
+
245
+ ---
246
+
247
+ **Made with ❀️ by developers, for developers**
@@ -0,0 +1,220 @@
1
+ # Requirements Installer πŸš€
2
+
3
+ **Tired of hunting down missing Python dependencies?** Requirements Installer automatically detects and installs all third-party packages your Python script needsβ€”no `requirements.txt` needed!
4
+
5
+ Simply point it at your Python file, and it handles the rest. Perfect for running unfamiliar scripts, quick prototyping, or setting up development environments.
6
+
7
+ ---
8
+
9
+ ## ✨ Features
10
+
11
+ - **πŸ” Smart Import Detection** - Automatically scans your Python files for all third-party imports
12
+ - **πŸ“¦ One-Command Install** - Detects and installs dependencies in a single step
13
+ - **🎯 Interactive Version Selection** - Optionally choose specific versions for each package
14
+ - **πŸ”’ Virtual Environment Support** - Create and install into isolated environments
15
+ - **🧠 Intelligent Filtering** - Excludes stdlib and local modules automatically
16
+ - **πŸ“ Module-to-Package Mapping** - Handles tricky cases like `cv2` β†’ `opencv-python`, `PIL` β†’ `Pillow`
17
+ - **🌐 One-Hop Local Import Scanning** - Follows local imports to catch all dependencies
18
+ - **⚑ Fast & Lightweight** - Pure Python with no external dependencies
19
+
20
+ ---
21
+
22
+ ## πŸ“₯ Installation
23
+
24
+ ```bash
25
+ pip install requirements-installer
26
+ ```
27
+
28
+ Or install from source:
29
+
30
+ ```bash
31
+ git clone https://github.com/FH-Prevail/requirements_installer.git
32
+ cd requirements-installer
33
+ pip install -e .
34
+ ```
35
+
36
+ ---
37
+
38
+ ## πŸš€ Quick Start
39
+
40
+ ### Basic Usage
41
+
42
+ ```bash
43
+ # Scan and install dependencies for a Python file
44
+ python requirements_installer.py --file mycode.py
45
+ ```
46
+
47
+ ### With Virtual Environment
48
+
49
+ ```bash
50
+ # Create a venv and install dependencies there
51
+ python requirements_installer.py --file mycode.py --use-venv --venv-path .venv
52
+ ```
53
+
54
+ ### Interactive Version Selection
55
+
56
+ ```bash
57
+ # Choose specific versions for each package
58
+ python requirements_installer.py --file mycode.py --ask-version
59
+ ```
60
+
61
+ Example interaction:
62
+ ```
63
+ ==================================================
64
+ Version Selection
65
+ ==================================================
66
+
67
+ Install latest version of 'numpy'? [Y/n]: n
68
+ Enter version for 'numpy' (e.g., 1.2.3): 1.24.0
69
+
70
+ Install latest version of 'pandas'? [Y/n]: y
71
+
72
+ Install latest version of 'requests'? [Y/n]:
73
+ ```
74
+
75
+ ### Print Requirements Only
76
+
77
+ ```bash
78
+ # Just show what would be installed, don't install
79
+ python requirements_installer.py --file mycode.py --print-requirements
80
+ ```
81
+
82
+ ---
83
+
84
+ ## πŸ“– Usage Examples
85
+
86
+ ### Example 1: Quick Script Setup
87
+ You clone a repo without a `requirements.txt`:
88
+
89
+ ```bash
90
+ python requirements_installer.py --file app.py
91
+ ```
92
+
93
+ Output:
94
+ ```
95
+ Detected environment: current Python at /usr/bin/python3
96
+ Entry file: /home/user/project/app.py
97
+ Installing: numpy pandas requests
98
+
99
+ ==================================================
100
+ Installation Summary
101
+ ==================================================
102
+ Installed: numpy, pandas, requests
103
+ Already satisfied: (none)
104
+ All requested packages are now present.
105
+ ```
106
+
107
+ ### Example 2: Isolated Development Environment
108
+
109
+ ```bash
110
+ python requirements_installer.py --file main.py --use-venv
111
+ ```
112
+
113
+ Creates a `.venv` folder and installs all dependencies there, keeping your system Python clean.
114
+
115
+ ### Example 3: Precise Version Control
116
+
117
+ ```bash
118
+ python requirements_installer.py --file analysis.py --ask-version
119
+ ```
120
+
121
+ Prompts you for each package, letting you pin versions for reproducibility.
122
+
123
+ ---
124
+
125
+ ## πŸ”§ Command-Line Options
126
+
127
+ ```
128
+ usage: requirements_installer.py [-h] --file FILE [--use-venv]
129
+ [--venv-path VENV_PATH]
130
+ [--print-requirements] [--ask-version]
131
+
132
+ options:
133
+ -h, --help Show this help message and exit
134
+ --file FILE Entry Python file to scan (e.g., mycode.py)
135
+ --use-venv Create and use a virtual environment
136
+ --venv-path VENV_PATH
137
+ Where to create the venv (default: .venv)
138
+ --print-requirements Only print inferred packages and exit
139
+ --ask-version Interactively ask for version preference for each package
140
+ ```
141
+
142
+ ---
143
+
144
+ ## 🎯 How It Works
145
+
146
+ 1. **Parse Imports**: Uses Python's AST to find all `import` and `from ... import` statements
147
+ 2. **Filter Standard Library**: Removes built-in Python modules (e.g., `os`, `sys`, `json`)
148
+ 3. **Filter Local Modules**: Excludes your project's own modules
149
+ 4. **Map Module Names**: Converts import names to PyPI package names (e.g., `cv2` β†’ `opencv-python`)
150
+ 5. **Check Installation**: Queries what's already installed to avoid redundant work
151
+ 6. **Install Packages**: Uses pip to install missing packages
152
+ 7. **Verify**: Confirms all required packages are present
153
+
154
+ ---
155
+
156
+ ## πŸ“Š Comparison with Other Tools
157
+
158
+ | Feature | requirements-installer | pipreqs | pigar | pythonrunscript |
159
+ |---------|----------------------|---------|-------|-----------------|
160
+ | Auto-detect imports | βœ… | βœ… | βœ… | ❌ |
161
+ | Auto-install packages | βœ… | ❌ | ❌ | βœ… |
162
+ | Interactive version selection | βœ… | ❌ | ❌ | ❌ |
163
+ | Virtual environment support | βœ… | ❌ | ❌ | βœ… |
164
+ | No source file modification | βœ… | βœ… | βœ… | ❌ |
165
+ | One-command operation | βœ… | ❌ | ❌ | βœ… |
166
+
167
+ **Why requirements-installer?**
168
+ - **pipreqs/pigar**: Generate `requirements.txt` but don't install (requires 2 steps)
169
+ - **pythonrunscript**: Installs but requires metadata comments in your code
170
+ - **requirements-installer**: Does both detection and installation, no modifications needed!
171
+
172
+ ---
173
+
174
+ ## πŸ§ͺ Module-to-Package Mappings
175
+
176
+ The tool includes smart mappings for common cases where the import name differs from the PyPI package name:
177
+
178
+ ```python
179
+ cv2 β†’ opencv-python
180
+ PIL β†’ Pillow
181
+ sklearn β†’ scikit-learn
182
+ yaml β†’ PyYAML
183
+ bs4 β†’ beautifulsoup4
184
+ dotenv β†’ python-dotenv
185
+ # ... and many more!
186
+ ```
187
+
188
+ ---
189
+
190
+ ## ⚠️ Limitations
191
+
192
+ - **One-hop scanning**: Only follows local imports one level deep (for speed)
193
+ - **Dynamic imports**: Limited detection of `importlib.import_module()` calls
194
+ - **Conditional imports**: Treats all imports equally (no context analysis)
195
+ - **Version conflicts**: Doesn't resolve complex dependency conflicts (delegates to pip)
196
+
197
+ These are deliberate trade-offs for simplicity and speed. For complex projects with intricate dependency trees, consider using Poetry or Pipenv.
198
+
199
+ ---
200
+
201
+ ## 🀝 Contributing
202
+
203
+ Contributions are welcome! Here are some ways you can help:
204
+
205
+ - πŸ› Report bugs and issues
206
+ - πŸ’‘ Suggest new features or improvements
207
+ - πŸ“ Improve documentation
208
+ - πŸ”§ Submit pull requests
209
+ - ⭐ Add more module-to-package mappings
210
+
211
+ ---
212
+
213
+
214
+ ## ⭐ Star History
215
+
216
+ If you find this tool useful, please consider giving it a star on GitHub! It helps others discover the project.
217
+
218
+ ---
219
+
220
+ **Made with ❀️ by developers, for developers**
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "requirements-installer"
7
+ version = "1.0.0"
8
+ description = "Automatically detect and install Python dependencies from your code"
9
+ readme = "README.md"
10
+ requires-python = ">=3.7"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ {name = "Sina Mirshahi", email = "sina7th@gmail.com"}
14
+ ]
15
+ keywords = ["pip", "requirements", "dependencies", "automation", "installer"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.7",
22
+ "Programming Language :: Python :: 3.8",
23
+ "Programming Language :: Python :: 3.9",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Topic :: Software Development :: Libraries :: Python Modules",
28
+ "Topic :: System :: Installation/Setup",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/FH-Prevail/requirements_installer"
33
+ Repository = "https://github.com/FH-Prevail/requirements-installer"
34
+ Issues = "https://github.com/FH-Prevail/requirements_installer/issues"
35
+
36
+ [project.scripts]
37
+ requirements-installer = "requirements_installer:main"
@@ -0,0 +1,247 @@
1
+ Metadata-Version: 2.4
2
+ Name: requirements-installer
3
+ Version: 1.0.0
4
+ Summary: Automatically detect and install Python dependencies from your code
5
+ Author-email: Sina Mirshahi <sina7th@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/FH-Prevail/requirements_installer
8
+ Project-URL: Repository, https://github.com/FH-Prevail/requirements-installer
9
+ Project-URL: Issues, https://github.com/FH-Prevail/requirements_installer/issues
10
+ Keywords: pip,requirements,dependencies,automation,installer
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.7
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: System :: Installation/Setup
23
+ Requires-Python: >=3.7
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Dynamic: license-file
27
+
28
+ # Requirements Installer πŸš€
29
+
30
+ **Tired of hunting down missing Python dependencies?** Requirements Installer automatically detects and installs all third-party packages your Python script needsβ€”no `requirements.txt` needed!
31
+
32
+ Simply point it at your Python file, and it handles the rest. Perfect for running unfamiliar scripts, quick prototyping, or setting up development environments.
33
+
34
+ ---
35
+
36
+ ## ✨ Features
37
+
38
+ - **πŸ” Smart Import Detection** - Automatically scans your Python files for all third-party imports
39
+ - **πŸ“¦ One-Command Install** - Detects and installs dependencies in a single step
40
+ - **🎯 Interactive Version Selection** - Optionally choose specific versions for each package
41
+ - **πŸ”’ Virtual Environment Support** - Create and install into isolated environments
42
+ - **🧠 Intelligent Filtering** - Excludes stdlib and local modules automatically
43
+ - **πŸ“ Module-to-Package Mapping** - Handles tricky cases like `cv2` β†’ `opencv-python`, `PIL` β†’ `Pillow`
44
+ - **🌐 One-Hop Local Import Scanning** - Follows local imports to catch all dependencies
45
+ - **⚑ Fast & Lightweight** - Pure Python with no external dependencies
46
+
47
+ ---
48
+
49
+ ## πŸ“₯ Installation
50
+
51
+ ```bash
52
+ pip install requirements-installer
53
+ ```
54
+
55
+ Or install from source:
56
+
57
+ ```bash
58
+ git clone https://github.com/FH-Prevail/requirements_installer.git
59
+ cd requirements-installer
60
+ pip install -e .
61
+ ```
62
+
63
+ ---
64
+
65
+ ## πŸš€ Quick Start
66
+
67
+ ### Basic Usage
68
+
69
+ ```bash
70
+ # Scan and install dependencies for a Python file
71
+ python requirements_installer.py --file mycode.py
72
+ ```
73
+
74
+ ### With Virtual Environment
75
+
76
+ ```bash
77
+ # Create a venv and install dependencies there
78
+ python requirements_installer.py --file mycode.py --use-venv --venv-path .venv
79
+ ```
80
+
81
+ ### Interactive Version Selection
82
+
83
+ ```bash
84
+ # Choose specific versions for each package
85
+ python requirements_installer.py --file mycode.py --ask-version
86
+ ```
87
+
88
+ Example interaction:
89
+ ```
90
+ ==================================================
91
+ Version Selection
92
+ ==================================================
93
+
94
+ Install latest version of 'numpy'? [Y/n]: n
95
+ Enter version for 'numpy' (e.g., 1.2.3): 1.24.0
96
+
97
+ Install latest version of 'pandas'? [Y/n]: y
98
+
99
+ Install latest version of 'requests'? [Y/n]:
100
+ ```
101
+
102
+ ### Print Requirements Only
103
+
104
+ ```bash
105
+ # Just show what would be installed, don't install
106
+ python requirements_installer.py --file mycode.py --print-requirements
107
+ ```
108
+
109
+ ---
110
+
111
+ ## πŸ“– Usage Examples
112
+
113
+ ### Example 1: Quick Script Setup
114
+ You clone a repo without a `requirements.txt`:
115
+
116
+ ```bash
117
+ python requirements_installer.py --file app.py
118
+ ```
119
+
120
+ Output:
121
+ ```
122
+ Detected environment: current Python at /usr/bin/python3
123
+ Entry file: /home/user/project/app.py
124
+ Installing: numpy pandas requests
125
+
126
+ ==================================================
127
+ Installation Summary
128
+ ==================================================
129
+ Installed: numpy, pandas, requests
130
+ Already satisfied: (none)
131
+ All requested packages are now present.
132
+ ```
133
+
134
+ ### Example 2: Isolated Development Environment
135
+
136
+ ```bash
137
+ python requirements_installer.py --file main.py --use-venv
138
+ ```
139
+
140
+ Creates a `.venv` folder and installs all dependencies there, keeping your system Python clean.
141
+
142
+ ### Example 3: Precise Version Control
143
+
144
+ ```bash
145
+ python requirements_installer.py --file analysis.py --ask-version
146
+ ```
147
+
148
+ Prompts you for each package, letting you pin versions for reproducibility.
149
+
150
+ ---
151
+
152
+ ## πŸ”§ Command-Line Options
153
+
154
+ ```
155
+ usage: requirements_installer.py [-h] --file FILE [--use-venv]
156
+ [--venv-path VENV_PATH]
157
+ [--print-requirements] [--ask-version]
158
+
159
+ options:
160
+ -h, --help Show this help message and exit
161
+ --file FILE Entry Python file to scan (e.g., mycode.py)
162
+ --use-venv Create and use a virtual environment
163
+ --venv-path VENV_PATH
164
+ Where to create the venv (default: .venv)
165
+ --print-requirements Only print inferred packages and exit
166
+ --ask-version Interactively ask for version preference for each package
167
+ ```
168
+
169
+ ---
170
+
171
+ ## 🎯 How It Works
172
+
173
+ 1. **Parse Imports**: Uses Python's AST to find all `import` and `from ... import` statements
174
+ 2. **Filter Standard Library**: Removes built-in Python modules (e.g., `os`, `sys`, `json`)
175
+ 3. **Filter Local Modules**: Excludes your project's own modules
176
+ 4. **Map Module Names**: Converts import names to PyPI package names (e.g., `cv2` β†’ `opencv-python`)
177
+ 5. **Check Installation**: Queries what's already installed to avoid redundant work
178
+ 6. **Install Packages**: Uses pip to install missing packages
179
+ 7. **Verify**: Confirms all required packages are present
180
+
181
+ ---
182
+
183
+ ## πŸ“Š Comparison with Other Tools
184
+
185
+ | Feature | requirements-installer | pipreqs | pigar | pythonrunscript |
186
+ |---------|----------------------|---------|-------|-----------------|
187
+ | Auto-detect imports | βœ… | βœ… | βœ… | ❌ |
188
+ | Auto-install packages | βœ… | ❌ | ❌ | βœ… |
189
+ | Interactive version selection | βœ… | ❌ | ❌ | ❌ |
190
+ | Virtual environment support | βœ… | ❌ | ❌ | βœ… |
191
+ | No source file modification | βœ… | βœ… | βœ… | ❌ |
192
+ | One-command operation | βœ… | ❌ | ❌ | βœ… |
193
+
194
+ **Why requirements-installer?**
195
+ - **pipreqs/pigar**: Generate `requirements.txt` but don't install (requires 2 steps)
196
+ - **pythonrunscript**: Installs but requires metadata comments in your code
197
+ - **requirements-installer**: Does both detection and installation, no modifications needed!
198
+
199
+ ---
200
+
201
+ ## πŸ§ͺ Module-to-Package Mappings
202
+
203
+ The tool includes smart mappings for common cases where the import name differs from the PyPI package name:
204
+
205
+ ```python
206
+ cv2 β†’ opencv-python
207
+ PIL β†’ Pillow
208
+ sklearn β†’ scikit-learn
209
+ yaml β†’ PyYAML
210
+ bs4 β†’ beautifulsoup4
211
+ dotenv β†’ python-dotenv
212
+ # ... and many more!
213
+ ```
214
+
215
+ ---
216
+
217
+ ## ⚠️ Limitations
218
+
219
+ - **One-hop scanning**: Only follows local imports one level deep (for speed)
220
+ - **Dynamic imports**: Limited detection of `importlib.import_module()` calls
221
+ - **Conditional imports**: Treats all imports equally (no context analysis)
222
+ - **Version conflicts**: Doesn't resolve complex dependency conflicts (delegates to pip)
223
+
224
+ These are deliberate trade-offs for simplicity and speed. For complex projects with intricate dependency trees, consider using Poetry or Pipenv.
225
+
226
+ ---
227
+
228
+ ## 🀝 Contributing
229
+
230
+ Contributions are welcome! Here are some ways you can help:
231
+
232
+ - πŸ› Report bugs and issues
233
+ - πŸ’‘ Suggest new features or improvements
234
+ - πŸ“ Improve documentation
235
+ - πŸ”§ Submit pull requests
236
+ - ⭐ Add more module-to-package mappings
237
+
238
+ ---
239
+
240
+
241
+ ## ⭐ Star History
242
+
243
+ If you find this tool useful, please consider giving it a star on GitHub! It helps others discover the project.
244
+
245
+ ---
246
+
247
+ **Made with ❀️ by developers, for developers**
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ requirements_installer.py
6
+ requirements_installer.egg-info/PKG-INFO
7
+ requirements_installer.egg-info/SOURCES.txt
8
+ requirements_installer.egg-info/dependency_links.txt
9
+ requirements_installer.egg-info/entry_points.txt
10
+ requirements_installer.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ requirements-installer = requirements_installer:main
@@ -0,0 +1 @@
1
+ requirements_installer
@@ -0,0 +1,436 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ requirements_installer.py
4
+
5
+ Usage:
6
+ python requirements_installer.py --file mycode.py
7
+ python requirements_installer.py --file mycode.py --use-venv
8
+ python requirements_installer.py --file mycode.py --use-venv --venv-path .venv
9
+ python requirements_installer.py --file mycode.py --ask-version
10
+
11
+ What it does:
12
+ - Parses your Python file (and any local modules it directly imports) for imports.
13
+ - Filters out stdlib and local modules; infers third-party packages.
14
+ - Maps common module names to PyPI package names.
15
+ - Installs everything (optionally inside a freshly created venv).
16
+ - Prints a concise summary of what was installed and what was already satisfied.
17
+ """
18
+
19
+ import argparse
20
+ import ast
21
+ import importlib.util
22
+ import os
23
+ import sys
24
+ import subprocess
25
+ import textwrap
26
+ from pathlib import Path
27
+ from typing import Set, Tuple, Dict, Iterable, List
28
+
29
+ # ------- Mapping: module name -> PyPI distribution name -------
30
+ MODULE_TO_DIST: Dict[str, str] = {
31
+ "cv2": "opencv-python",
32
+ "PIL": "Pillow",
33
+ "skimage": "scikit-image",
34
+ "sklearn": "scikit-learn",
35
+ "yaml": "PyYAML",
36
+ "bs4": "beautifulsoup4",
37
+ "Crypto": "pycryptodome",
38
+ "crypto": "pycryptodome",
39
+ "mpl_toolkits": "matplotlib",
40
+ "BeautifulSoup": "beautifulsoup4",
41
+ "OpenGL": "PyOpenGL",
42
+ "ruamel": "ruamel.yaml",
43
+ "dotenv": "python-dotenv",
44
+ "weasyprint": "WeasyPrint",
45
+ "orjson": "orjson",
46
+ "ujson": "ujson",
47
+ "yamlpath": "yamlpath",
48
+ # Often identical, listed to be explicit:
49
+ "numpy": "numpy",
50
+ "pandas": "pandas",
51
+ "matplotlib": "matplotlib",
52
+ "seaborn": "seaborn",
53
+ "requests": "requests",
54
+ "httpx": "httpx",
55
+ "fastapi": "fastapi",
56
+ "starlette": "starlette",
57
+ "uvicorn": "uvicorn",
58
+ "flask": "Flask",
59
+ "Django": "Django",
60
+ "jinja2": "Jinja2",
61
+ "lxml": "lxml",
62
+ "tqdm": "tqdm",
63
+ "dateutil": "python-dateutil",
64
+ "psutil": "psutil",
65
+ "Pillow": "Pillow",
66
+ "tabulate": "tabulate",
67
+ "rich": "rich",
68
+ "typer": "typer",
69
+ "click": "click",
70
+ "loguru": "loguru",
71
+ "pyyaml": "PyYAML",
72
+ "PILLOW": "Pillow",
73
+ "opencv": "opencv-python",
74
+ "opencv_python": "opencv-python",
75
+ "transformers": "transformers",
76
+ "torch": "torch",
77
+ "torchvision": "torchvision",
78
+ "torchaudio": "torchaudio",
79
+ "tensorflow": "tensorflow",
80
+ "jax": "jax",
81
+ "xgboost": "xgboost",
82
+ "lightgbm": "lightgbm",
83
+ "catboost": "catboost",
84
+ "skops": "skops",
85
+ "safetensors": "safetensors",
86
+ "scipy": "scipy",
87
+ "statsmodels": "statsmodels",
88
+ }
89
+
90
+ # Some stdlib fallbacks for older Python (<3.10 without sys.stdlib_module_names)
91
+ STDLIB_FALLBACK = {
92
+ "abc","argparse","array","asyncio","base64","binascii","bisect","builtins","calendar","cmath",
93
+ "collections","concurrent","configparser","contextlib","copy","csv","ctypes","datetime","decimal",
94
+ "difflib","email","enum","errno","faulthandler","fnmatch","fractions","functools","gc","getopt",
95
+ "getpass","gettext","glob","gzip","hashlib","heapq","hmac","html","http","imaplib","importlib",
96
+ "inspect","io","ipaddress","itertools","json","keyword","linecache","locale","logging","lzma",
97
+ "math","mimetypes","multiprocessing","numbers","operator","os","pathlib","pickle","pkgutil","platform",
98
+ "plistlib","pprint","profile","pstats","queue","random","re","resource","sched","secrets","select",
99
+ "selectors","shlex","shutil","signal","site","smtplib","socket","sqlite3","ssl","stat","statistics",
100
+ "string","stringprep","struct","subprocess","sys","sysconfig","tarfile","tempfile","textwrap","threading",
101
+ "time","timeit","tkinter","token","traceback","types","typing","unicodedata","unittest","urllib","uuid",
102
+ "venv","warnings","weakref","xml","xmlrpc","zipfile","zoneinfo",
103
+ }
104
+
105
+ def stdlib_names() -> Set[str]:
106
+ names = set()
107
+ try:
108
+ names.update(getattr(sys, "stdlib_module_names")) # Py3.10+
109
+ except Exception:
110
+ pass
111
+ if not names:
112
+ names.update(STDLIB_FALLBACK)
113
+ return names
114
+
115
+ def top_level_module(name: str) -> str:
116
+ return name.split(".", 1)[0]
117
+
118
+ def is_relative_import(mod: str) -> bool:
119
+ return mod.startswith(".")
120
+
121
+ def find_dynamic_imports(node: ast.AST) -> Set[str]:
122
+ """Very simple detection for importlib.import_module('pkg[.sub]')"""
123
+ found = set()
124
+ for n in ast.walk(node):
125
+ if isinstance(n, ast.Call):
126
+ # importlib.import_module("x") or import_module("x")
127
+ target = ""
128
+ if isinstance(n.func, ast.Attribute):
129
+ if getattr(n.func.value, "id", None) == "importlib" and n.func.attr == "import_module":
130
+ target = "importlib.import_module"
131
+ elif isinstance(n.func, ast.Name) and n.func.id == "import_module":
132
+ target = "import_module"
133
+ if target and n.args and isinstance(n.args[0], ast.Constant) and isinstance(n.args[0].value, str):
134
+ found.add(top_level_module(n.args[0].value))
135
+ return found
136
+
137
+ def parse_imports_from_file(path: Path) -> Tuple[Set[str], Set[str]]:
138
+ """
139
+ Returns (modules, local_modules)
140
+ modules: top-level imported module names (absolute)
141
+ local_modules: modules likely referring to local files/packages (within project)
142
+ """
143
+ modules: Set[str] = set()
144
+ local: Set[str] = set()
145
+ code = path.read_text(encoding="utf-8", errors="ignore")
146
+ tree = ast.parse(code, filename=str(path))
147
+ for node in ast.walk(tree):
148
+ if isinstance(node, ast.Import):
149
+ for alias in node.names:
150
+ mod = top_level_module(alias.name)
151
+ if not is_relative_import(mod):
152
+ modules.add(mod)
153
+ elif isinstance(node, ast.ImportFrom):
154
+ if node.module is None:
155
+ continue
156
+ if node.level and node.level > 0:
157
+ # relative import -> likely local
158
+ continue
159
+ mod = top_level_module(node.module)
160
+ if not is_relative_import(mod):
161
+ modules.add(mod)
162
+ modules |= find_dynamic_imports(tree)
163
+ return modules, local
164
+
165
+ def is_stdlib_module(mod: str, stdlib: Set[str]) -> bool:
166
+ if mod in stdlib:
167
+ return True
168
+ if mod in sys.builtin_module_names:
169
+ return True
170
+ # Heuristic: if spec exists and lives under base_prefix + "lib"
171
+ try:
172
+ spec = importlib.util.find_spec(mod)
173
+ if spec and spec.origin:
174
+ origin = str(spec.origin).lower()
175
+ base = str(Path(sys.base_prefix)).lower()
176
+ # stdlib often under .../lib/pythonX.Y/...
177
+ if "python" in origin and base in origin and "site-packages" not in origin:
178
+ return True
179
+ except Exception:
180
+ pass
181
+ return False
182
+
183
+ def is_local_module(mod: str, project_root: Path) -> bool:
184
+ """
185
+ Decide if 'mod' resolves to a module/package under project_root.
186
+ """
187
+ try:
188
+ spec = importlib.util.find_spec(mod)
189
+ if not spec or not spec.origin:
190
+ # Could be uninstalled third-party (desired) OR local not in sys.path.
191
+ # If a file/folder with that name exists next to project, treat as local.
192
+ pkg_dir = project_root / mod
193
+ py_file = project_root / f"{mod}.py"
194
+ return pkg_dir.exists() or py_file.exists()
195
+ origin = Path(spec.origin).resolve()
196
+ try:
197
+ return project_root.resolve() in origin.parents
198
+ except Exception:
199
+ return False
200
+ except Exception:
201
+ # If importable resolution fails, do a filesystem guess:
202
+ pkg_dir = project_root / mod
203
+ py_file = project_root / f"{mod}.py"
204
+ return pkg_dir.exists() or py_file.exists()
205
+
206
+ def map_to_distribution(mod: str) -> str:
207
+ # Prefer an explicit mapping, else use the module name itself
208
+ return MODULE_TO_DIST.get(mod, mod)
209
+
210
+ def collect_requirements(entry_file: Path, project_root: Path) -> Set[str]:
211
+ """
212
+ Parse entry file and any immediately local-imported files (one hop) to collect modules.
213
+ We don't recurse deep to keep it fast and simple.
214
+ """
215
+ stdlib = stdlib_names()
216
+ all_modules: Set[str] = set()
217
+
218
+ def add_from(path: Path):
219
+ mods, _ = parse_imports_from_file(path)
220
+ all_modules.update(mods)
221
+
222
+ add_from(entry_file)
223
+
224
+ # One-hop: if an import is local, parse that file/package's __init__.py
225
+ local_files_to_parse: Set[Path] = set()
226
+ for mod in list(all_modules):
227
+ if is_local_module(mod, project_root):
228
+ # Prefer package __init__.py if package; else module.py
229
+ pkg_dir = project_root / mod
230
+ if (pkg_dir / "__init__.py").exists():
231
+ local_files_to_parse.add(pkg_dir / "__init__.py")
232
+ elif (project_root / f"{mod}.py").exists():
233
+ local_files_to_parse.add(project_root / f"{mod}.py")
234
+
235
+ for lf in local_files_to_parse:
236
+ mods, _ = parse_imports_from_file(lf)
237
+ all_modules.update(mods)
238
+
239
+ # Filter stdlib & local
240
+ third_party = set()
241
+ for mod in all_modules:
242
+ if not mod or is_stdlib_module(mod, stdlib) or is_local_module(mod, project_root):
243
+ continue
244
+ third_party.add(mod)
245
+
246
+ # Map to distributions (deduplicate case-insensitively)
247
+ dists = {map_to_distribution(m) for m in third_party}
248
+ # Normalize "-" vs "_" inconsistencies to avoid duplicates
249
+ normed = set()
250
+ for d in dists:
251
+ normed.add(d.replace("_", "-"))
252
+ return normed
253
+
254
+ def ensure_venv(venv_path: Path) -> Tuple[Path, List[str]]:
255
+ """
256
+ Create a venv at venv_path if missing. Return (python_exe, pip_cmd_as_list)
257
+ """
258
+ from venv import EnvBuilder
259
+ if not venv_path.exists():
260
+ print(f"Creating virtual environment at: {venv_path}")
261
+ builder = EnvBuilder(with_pip=True, clear=False, upgrade=False, symlinks=True)
262
+ builder.create(str(venv_path))
263
+ # Paths
264
+ if os.name == "nt":
265
+ py = venv_path / "Scripts" / "python.exe"
266
+ else:
267
+ py = venv_path / "bin" / "python"
268
+ pip_cmd = [str(py), "-m", "pip"]
269
+ return py, pip_cmd
270
+
271
+ def current_pip_cmd() -> Tuple[Path, List[str]]:
272
+ py = Path(sys.executable)
273
+ return py, [str(py), "-m", "pip"]
274
+
275
+ def installed_distributions_lower(pip_python: Path) -> Set[str]:
276
+ """
277
+ Use importlib.metadata from the given python to list installed dists (lowercased).
278
+ """
279
+ code = (
280
+ "import importlib.metadata as m, json; "
281
+ "print(json.dumps([d.metadata['Name'].lower() for d in m.distributions() if 'Name' in d.metadata]))"
282
+ )
283
+ out = subprocess.check_output([str(pip_python), "-c", code], text=True)
284
+ import json
285
+ return set(json.loads(out))
286
+
287
+ def ask_for_version(package: str) -> str:
288
+ """
289
+ Interactively ask user if they want the latest version or a specific version.
290
+ Returns the package spec (e.g., 'package' or 'package==1.2.3')
291
+ """
292
+ while True:
293
+ response = input(f"\nInstall latest version of '{package}'? [Y/n]: ").strip().lower()
294
+ if response in ('', 'y', 'yes'):
295
+ return package
296
+ elif response in ('n', 'no'):
297
+ version = input(f"Enter version for '{package}' (e.g., 1.2.3): ").strip()
298
+ if version:
299
+ return f"{package}=={version}"
300
+ else:
301
+ print("No version specified, using latest.")
302
+ return package
303
+ else:
304
+ print("Please answer 'y' or 'n'.")
305
+
306
+ def install_packages(pip_cmd: List[str], pkgs: Set[str], ask_version: bool = False) -> Tuple[bool, str]:
307
+ """
308
+ Install packages. If ask_version is True, prompt for version preferences.
309
+ """
310
+ if not pkgs:
311
+ return True, "Nothing to install."
312
+
313
+ packages_to_install = []
314
+ if ask_version:
315
+ print("\n" + "="*50)
316
+ print("Version Selection")
317
+ print("="*50)
318
+ for pkg in sorted(pkgs):
319
+ pkg_spec = ask_for_version(pkg)
320
+ packages_to_install.append(pkg_spec)
321
+ else:
322
+ packages_to_install = sorted(pkgs)
323
+
324
+ cmd = list(pip_cmd) + ["install", "--upgrade"] + packages_to_install
325
+ print(f"\nInstalling: {' '.join(packages_to_install)}")
326
+ proc = subprocess.run(cmd, text=True, capture_output=True)
327
+ success = proc.returncode == 0
328
+ return success, proc.stdout + "\n" + proc.stderr
329
+
330
+ def summarize_install(before: Set[str], after: Set[str], requested: Set[str]) -> Tuple[Set[str], Set[str]]:
331
+ """
332
+ Summarize what was installed vs what was already present.
333
+ Note: requested contains base package names without version specs.
334
+ """
335
+ newly_installed = {p for p in requested if p.lower() in after and p.lower() not in before}
336
+ already_present = {p for p in requested if p.lower() in before}
337
+ return newly_installed, already_present
338
+
339
+ def main():
340
+ ap = argparse.ArgumentParser(
341
+ description="Scan a Python file for third-party imports and install them.",
342
+ formatter_class=argparse.RawDescriptionHelpFormatter,
343
+ epilog=textwrap.dedent("""\
344
+ Examples:
345
+ python requirements_installer.py --file mycode.py
346
+ python requirements_installer.py --file mycode.py --use-venv
347
+ python requirements_installer.py --file mycode.py --use-venv --venv-path .venv
348
+ python requirements_installer.py --file mycode.py --ask-version
349
+ """),
350
+ )
351
+ ap.add_argument("--file", required=True, help="Entry Python file to scan (e.g., mycode.py).")
352
+ ap.add_argument("--use-venv", action="store_true",
353
+ help="Create and use a virtual environment for installation.")
354
+ ap.add_argument("--venv-path", default=".venv", help="Where to create the venv (default: .venv).")
355
+ ap.add_argument("--print-requirements", action="store_true", help="Only print inferred packages and exit.")
356
+ ap.add_argument("--ask-version", action="store_true",
357
+ help="Interactively ask for version preference for each package.")
358
+ args = ap.parse_args()
359
+
360
+ entry_file = Path(args.file).resolve()
361
+ if not entry_file.exists():
362
+ ap.error(f"File not found: {entry_file}")
363
+
364
+ project_root = entry_file.parent
365
+
366
+ # Collect required distributions
367
+ required = collect_requirements(entry_file, project_root)
368
+
369
+ # Early exit if requested
370
+ if args.print_requirements:
371
+ if required:
372
+ print("\n".join(sorted(required)))
373
+ else:
374
+ print("(No external packages detected.)")
375
+ return
376
+
377
+ # Decide environment
378
+ if args.use_venv:
379
+ venv_path = Path(args.venv_path).resolve()
380
+ py_exe, pip_cmd = ensure_venv(venv_path)
381
+ env_desc = f"virtual environment at {venv_path}"
382
+ else:
383
+ py_exe, pip_cmd = current_pip_cmd()
384
+ env_desc = f"current Python at {py_exe}"
385
+
386
+ print(f"Detected environment: {env_desc}")
387
+ print(f"Entry file: {entry_file}")
388
+
389
+ if not required:
390
+ print("No external packages detected. Nothing to install.")
391
+ return
392
+
393
+ # Read installed packages before
394
+ try:
395
+ before = installed_distributions_lower(py_exe)
396
+ except Exception:
397
+ before = set()
398
+
399
+ # Determine what actually needs to be installed (case-insensitive)
400
+ needed = {p for p in required if p.lower() not in before}
401
+
402
+ if not needed:
403
+ print("All required packages are already installed.")
404
+ print("Packages:", ", ".join(sorted(required)))
405
+ return
406
+
407
+ ok, output = install_packages(pip_cmd, needed, ask_version=args.ask_version)
408
+
409
+ # Read installed packages after
410
+ try:
411
+ after = installed_distributions_lower(py_exe)
412
+ except Exception:
413
+ after = set()
414
+
415
+ newly, already = summarize_install(before, after, required)
416
+ print("\n" + "="*50)
417
+ print("Installation Summary")
418
+ print("="*50)
419
+ if newly:
420
+ print("Installed:", ", ".join(sorted(newly)))
421
+ else:
422
+ print("Installed: (none)")
423
+ if already:
424
+ print("Already satisfied:", ", ".join(sorted(already)))
425
+ missing = sorted({p for p in required if p.lower() not in after})
426
+ if missing:
427
+ print("Failed / Missing:", ", ".join(missing))
428
+ print("\n--- pip output (for debugging) ---")
429
+ print(output)
430
+ sys.exit(1)
431
+ else:
432
+ print("All requested packages are now present.")
433
+ sys.exit(0)
434
+
435
+ if __name__ == "__main__":
436
+ main()
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+