gitbolt 0.0.0.dev14__tar.gz → 0.0.0.dev16__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.
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/PKG-INFO +59 -34
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/README.md +58 -33
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/pyproject.toml +2 -4
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/__init__.py +7 -7
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/base.py +5 -13
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/base.py +112 -16
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/impl/simple.py +10 -10
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/runner/base.py +34 -1
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/runner/simple.py +49 -2
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt.egg-info/PKG-INFO +59 -34
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/LICENSE +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/setup.cfg +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/_internal_init.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/add.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/constants.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/exceptions.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/ls_tree.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/models.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/py.typed +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/pytest_plugin.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/__init__.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/_internal_init.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/add.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/constants.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/exceptions.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/impl/__init__.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/ls_tree.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/runner/__init__.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/utils.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/utils.py +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt.egg-info/SOURCES.txt +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt.egg-info/dependency_links.txt +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt.egg-info/entry_points.txt +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt.egg-info/requires.txt +0 -0
- {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitbolt
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev16
|
|
4
4
|
Summary: Fast, flexible and type-safe Git commands in Python.
|
|
5
5
|
Author-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>
|
|
6
6
|
Maintainer-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>
|
|
@@ -114,9 +114,8 @@ Gitbolt lets you pass subcommands around as typed objects. This enables highly f
|
|
|
114
114
|
|
|
115
115
|
```python
|
|
116
116
|
import gitbolt
|
|
117
|
-
from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
|
|
118
117
|
|
|
119
|
-
git =
|
|
118
|
+
git = gitbolt.get_git()
|
|
120
119
|
version_subcmd = git.version_subcmd
|
|
121
120
|
add_subcmd = git.add_subcmd
|
|
122
121
|
|
|
@@ -136,11 +135,11 @@ method_which_only_adds_a_file(add_subcmd)
|
|
|
136
135
|
git subcommands are modeled as terminal functions that return stdout.
|
|
137
136
|
|
|
138
137
|
```python
|
|
139
|
-
|
|
138
|
+
import gitbolt
|
|
140
139
|
|
|
141
|
-
git =
|
|
142
|
-
|
|
143
|
-
print(
|
|
140
|
+
git = gitbolt.get_git()
|
|
141
|
+
version_stdout = git.version_subcmd.version().version()
|
|
142
|
+
print(version_stdout)
|
|
144
143
|
```
|
|
145
144
|
|
|
146
145
|
### 🪼 Modular Architecture
|
|
@@ -206,9 +205,9 @@ Extensive use of type-hints ensures that invalid usages fail early — at *compi
|
|
|
206
205
|
#### 🔁 Override a single Git env (e.g., `GIT_TRACE`)
|
|
207
206
|
|
|
208
207
|
```python
|
|
209
|
-
|
|
208
|
+
import gitbolt
|
|
210
209
|
|
|
211
|
-
git =
|
|
210
|
+
git = gitbolt.get_git()
|
|
212
211
|
git = git.git_envs_override(GIT_TRACE=True)
|
|
213
212
|
```
|
|
214
213
|
|
|
@@ -216,9 +215,9 @@ git = git.git_envs_override(GIT_TRACE=True)
|
|
|
216
215
|
|
|
217
216
|
```python
|
|
218
217
|
from pathlib import Path
|
|
219
|
-
|
|
218
|
+
import gitbolt
|
|
220
219
|
|
|
221
|
-
git =
|
|
220
|
+
git = gitbolt.get_git()
|
|
222
221
|
git = git.git_envs_override(GIT_TRACE=1, GIT_DIR=Path('/tmp/git-dir/'), GIT_EDITOR='vim')
|
|
223
222
|
```
|
|
224
223
|
|
|
@@ -226,9 +225,9 @@ git = git.git_envs_override(GIT_TRACE=1, GIT_DIR=Path('/tmp/git-dir/'), GIT_EDIT
|
|
|
226
225
|
|
|
227
226
|
```python
|
|
228
227
|
from pathlib import Path
|
|
229
|
-
|
|
228
|
+
import gitbolt
|
|
230
229
|
|
|
231
|
-
git =
|
|
230
|
+
git = gitbolt.get_git()
|
|
232
231
|
overridden_git = git.git_envs_override(GIT_SSH=Path('/tmp/SSH')).git_envs_override(
|
|
233
232
|
GIT_TERMINAL_PROMPT=1,
|
|
234
233
|
GIT_NO_REPLACE_OBJECTS=True
|
|
@@ -239,10 +238,10 @@ re_overridden_git = overridden_git.git_envs_override(GIT_TRACE=True)
|
|
|
239
238
|
#### ❌ Unset Git envs using a special `UNSET` marker
|
|
240
239
|
|
|
241
240
|
```python
|
|
242
|
-
|
|
241
|
+
import gitbolt
|
|
243
242
|
from vt.utils.commons.commons.core_py import UNSET
|
|
244
243
|
|
|
245
|
-
git =
|
|
244
|
+
git = gitbolt.get_git()
|
|
246
245
|
overridden_git = git.git_envs_override(GIT_ADVICE=True, GIT_TRACE=True)
|
|
247
246
|
no_advice_unset_git = overridden_git.git_envs_override(GIT_TRACE=UNSET)
|
|
248
247
|
```
|
|
@@ -250,9 +249,9 @@ no_advice_unset_git = overridden_git.git_envs_override(GIT_TRACE=UNSET)
|
|
|
250
249
|
#### 🔄 Reset Git envs by setting new values
|
|
251
250
|
|
|
252
251
|
```python
|
|
253
|
-
|
|
252
|
+
import gitbolt
|
|
254
253
|
|
|
255
|
-
git =
|
|
254
|
+
git = gitbolt.get_git()
|
|
256
255
|
overridden_git = git.git_envs_override(GIT_TRACE=True)
|
|
257
256
|
git_trace_reset_git = overridden_git.git_envs_override(GIT_TRACE=False)
|
|
258
257
|
```
|
|
@@ -268,9 +267,9 @@ git_trace_reset_git = overridden_git.git_envs_override(GIT_TRACE=False)
|
|
|
268
267
|
#### 🔁 Override a single Git opt (e.g., `--no-replace-objects`)
|
|
269
268
|
|
|
270
269
|
```python
|
|
271
|
-
|
|
270
|
+
import gitbolt
|
|
272
271
|
|
|
273
|
-
git =
|
|
272
|
+
git = gitbolt.get_git()
|
|
274
273
|
git = git.git_opts_override(no_replace_objects=True)
|
|
275
274
|
```
|
|
276
275
|
|
|
@@ -278,7 +277,7 @@ git = git.git_opts_override(no_replace_objects=True)
|
|
|
278
277
|
|
|
279
278
|
```python
|
|
280
279
|
from pathlib import Path
|
|
281
|
-
from gitbolt.
|
|
280
|
+
from gitbolt.subprocess.impl.simple import SimpleGitCommand
|
|
282
281
|
|
|
283
282
|
git = SimpleGitCommand()
|
|
284
283
|
git = git.git_opts_override(no_replace_objects=True, git_dir=Path(), paginate=True)
|
|
@@ -287,10 +286,10 @@ git = git.git_opts_override(no_replace_objects=True, git_dir=Path(), paginate=Tr
|
|
|
287
286
|
#### 🪢 Chain multiple option overrides fluently
|
|
288
287
|
|
|
289
288
|
```python
|
|
289
|
+
import gitbolt
|
|
290
290
|
from pathlib import Path
|
|
291
|
-
from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
|
|
292
291
|
|
|
293
|
-
git =
|
|
292
|
+
git = gitbolt.get_git()
|
|
294
293
|
overridden_git = git.git_opts_override(exec_path=Path('tmp')).git_opts_override(
|
|
295
294
|
noglob_pathspecs=True,
|
|
296
295
|
no_advice=True
|
|
@@ -303,11 +302,11 @@ re_overridden_git = overridden_git.git_opts_override(glob_pathspecs=True)
|
|
|
303
302
|
#### ❌ Unset Git opts using a special `UNSET` marker
|
|
304
303
|
|
|
305
304
|
```python
|
|
305
|
+
import gitbolt
|
|
306
306
|
from pathlib import Path
|
|
307
|
-
from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
|
|
308
307
|
from vt.utils.commons.commons.core_py import UNSET
|
|
309
308
|
|
|
310
|
-
git =
|
|
309
|
+
git = gitbolt.get_git()
|
|
311
310
|
overridden_git = git.git_opts_override(exec_path=Path('tmp'), no_advice=True)
|
|
312
311
|
no_advice_unset_git = overridden_git.git_opts_override(no_advice=UNSET)
|
|
313
312
|
```
|
|
@@ -315,9 +314,9 @@ no_advice_unset_git = overridden_git.git_opts_override(no_advice=UNSET)
|
|
|
315
314
|
#### 🔄 Reset Git opts by setting new values
|
|
316
315
|
|
|
317
316
|
```python
|
|
318
|
-
|
|
317
|
+
import gitbolt
|
|
319
318
|
|
|
320
|
-
git =
|
|
319
|
+
git = gitbolt.get_git()
|
|
321
320
|
overridden_git = git.git_opts_override(no_advice=True)
|
|
322
321
|
no_advice_reset_git = overridden_git.git_opts_override(no_advice=False)
|
|
323
322
|
```
|
|
@@ -328,33 +327,59 @@ no_advice_reset_git = overridden_git.git_opts_override(no_advice=False)
|
|
|
328
327
|
|
|
329
328
|
At last, run unchecked commands in git.
|
|
330
329
|
|
|
331
|
-
Introduced in `0.0.
|
|
330
|
+
Introduced in `0.0.0.dev4` to
|
|
332
331
|
- experiment.
|
|
333
332
|
- have consistent interfaced commands run until all subcommands are provided by the library.
|
|
334
333
|
|
|
334
|
+
#### 🖥️ Run one process per command
|
|
335
|
+
|
|
335
336
|
```python
|
|
336
|
-
|
|
337
|
+
import gitbolt
|
|
337
338
|
|
|
338
|
-
git =
|
|
339
|
+
git = gitbolt.get_git_command()
|
|
339
340
|
git = git.git_opts_override(no_advice=True)
|
|
340
341
|
git.subcmd_unchecked.run(['--version']) # run the version option for git.
|
|
341
342
|
git.subcmd_unchecked.run(['version']) # run the version subcommand.
|
|
342
343
|
```
|
|
343
344
|
|
|
345
|
+
#### 🖥️ Run one long-running process and communicate with it
|
|
346
|
+
|
|
347
|
+
Introduced in `0.0.0.dev16` to:
|
|
348
|
+
- Make communicable processes using `subprocess.Popen`.
|
|
349
|
+
|
|
350
|
+
Get a long-running process and communicate with it for batching and faster operations.
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
import gitbolt
|
|
354
|
+
import sys
|
|
355
|
+
|
|
356
|
+
git = gitbolt.get_git_command()
|
|
357
|
+
with git.subcmd_unchecked.popen(["cat-file", "--batch-command"]) as cf:
|
|
358
|
+
cf.stdin.write(b"contents HEAD\n")
|
|
359
|
+
cf.stdin.flush()
|
|
360
|
+
header = cf.stdout.readline().strip()
|
|
361
|
+
print(f"HEADER: {header}", file=sys.stderr)
|
|
362
|
+
obj, typ, size = header.split()
|
|
363
|
+
print(cf.stdout.read(int(size)))
|
|
364
|
+
cf.stdout.readline()
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Error handling and I/O management is left to the client/caller.
|
|
368
|
+
|
|
344
369
|
#### 💻 Run commands received from CLI
|
|
345
370
|
|
|
346
|
-
Introduced in `0.0.
|
|
371
|
+
Introduced in `0.0.0.dev11` is the ability to take commands from CLI and run it inside `gitbolt`.
|
|
347
372
|
|
|
348
373
|
While making a system it may be required to run cli commands as received from cli using gitbolt. An obvious example
|
|
349
374
|
would be to make a system that receives CLI commands and does certain modifications/additions inside `gitbolt` before
|
|
350
375
|
actually running them. An example:
|
|
351
376
|
|
|
352
377
|
```python
|
|
353
|
-
|
|
378
|
+
import gitbolt
|
|
354
379
|
|
|
355
|
-
opts = ["--no-pager", "--namespace", "n1"]
|
|
356
|
-
envs = dict(GIT_AUTHOR_NAME="ss")
|
|
357
|
-
git =
|
|
380
|
+
opts = ["--no-pager", "--namespace", "n1"] # options received from outside your program.
|
|
381
|
+
envs = dict(GIT_AUTHOR_NAME="ss") # env-vars received form outside your program.
|
|
382
|
+
git = gitbolt.get_git_command(opts=opts, envs=envs)
|
|
358
383
|
|
|
359
384
|
# these can later be overridden
|
|
360
385
|
git = git.git_opts_override(namespace="n2")
|
|
@@ -78,9 +78,8 @@ Gitbolt lets you pass subcommands around as typed objects. This enables highly f
|
|
|
78
78
|
|
|
79
79
|
```python
|
|
80
80
|
import gitbolt
|
|
81
|
-
from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
|
|
82
81
|
|
|
83
|
-
git =
|
|
82
|
+
git = gitbolt.get_git()
|
|
84
83
|
version_subcmd = git.version_subcmd
|
|
85
84
|
add_subcmd = git.add_subcmd
|
|
86
85
|
|
|
@@ -100,11 +99,11 @@ method_which_only_adds_a_file(add_subcmd)
|
|
|
100
99
|
git subcommands are modeled as terminal functions that return stdout.
|
|
101
100
|
|
|
102
101
|
```python
|
|
103
|
-
|
|
102
|
+
import gitbolt
|
|
104
103
|
|
|
105
|
-
git =
|
|
106
|
-
|
|
107
|
-
print(
|
|
104
|
+
git = gitbolt.get_git()
|
|
105
|
+
version_stdout = git.version_subcmd.version().version()
|
|
106
|
+
print(version_stdout)
|
|
108
107
|
```
|
|
109
108
|
|
|
110
109
|
### 🪼 Modular Architecture
|
|
@@ -170,9 +169,9 @@ Extensive use of type-hints ensures that invalid usages fail early — at *compi
|
|
|
170
169
|
#### 🔁 Override a single Git env (e.g., `GIT_TRACE`)
|
|
171
170
|
|
|
172
171
|
```python
|
|
173
|
-
|
|
172
|
+
import gitbolt
|
|
174
173
|
|
|
175
|
-
git =
|
|
174
|
+
git = gitbolt.get_git()
|
|
176
175
|
git = git.git_envs_override(GIT_TRACE=True)
|
|
177
176
|
```
|
|
178
177
|
|
|
@@ -180,9 +179,9 @@ git = git.git_envs_override(GIT_TRACE=True)
|
|
|
180
179
|
|
|
181
180
|
```python
|
|
182
181
|
from pathlib import Path
|
|
183
|
-
|
|
182
|
+
import gitbolt
|
|
184
183
|
|
|
185
|
-
git =
|
|
184
|
+
git = gitbolt.get_git()
|
|
186
185
|
git = git.git_envs_override(GIT_TRACE=1, GIT_DIR=Path('/tmp/git-dir/'), GIT_EDITOR='vim')
|
|
187
186
|
```
|
|
188
187
|
|
|
@@ -190,9 +189,9 @@ git = git.git_envs_override(GIT_TRACE=1, GIT_DIR=Path('/tmp/git-dir/'), GIT_EDIT
|
|
|
190
189
|
|
|
191
190
|
```python
|
|
192
191
|
from pathlib import Path
|
|
193
|
-
|
|
192
|
+
import gitbolt
|
|
194
193
|
|
|
195
|
-
git =
|
|
194
|
+
git = gitbolt.get_git()
|
|
196
195
|
overridden_git = git.git_envs_override(GIT_SSH=Path('/tmp/SSH')).git_envs_override(
|
|
197
196
|
GIT_TERMINAL_PROMPT=1,
|
|
198
197
|
GIT_NO_REPLACE_OBJECTS=True
|
|
@@ -203,10 +202,10 @@ re_overridden_git = overridden_git.git_envs_override(GIT_TRACE=True)
|
|
|
203
202
|
#### ❌ Unset Git envs using a special `UNSET` marker
|
|
204
203
|
|
|
205
204
|
```python
|
|
206
|
-
|
|
205
|
+
import gitbolt
|
|
207
206
|
from vt.utils.commons.commons.core_py import UNSET
|
|
208
207
|
|
|
209
|
-
git =
|
|
208
|
+
git = gitbolt.get_git()
|
|
210
209
|
overridden_git = git.git_envs_override(GIT_ADVICE=True, GIT_TRACE=True)
|
|
211
210
|
no_advice_unset_git = overridden_git.git_envs_override(GIT_TRACE=UNSET)
|
|
212
211
|
```
|
|
@@ -214,9 +213,9 @@ no_advice_unset_git = overridden_git.git_envs_override(GIT_TRACE=UNSET)
|
|
|
214
213
|
#### 🔄 Reset Git envs by setting new values
|
|
215
214
|
|
|
216
215
|
```python
|
|
217
|
-
|
|
216
|
+
import gitbolt
|
|
218
217
|
|
|
219
|
-
git =
|
|
218
|
+
git = gitbolt.get_git()
|
|
220
219
|
overridden_git = git.git_envs_override(GIT_TRACE=True)
|
|
221
220
|
git_trace_reset_git = overridden_git.git_envs_override(GIT_TRACE=False)
|
|
222
221
|
```
|
|
@@ -232,9 +231,9 @@ git_trace_reset_git = overridden_git.git_envs_override(GIT_TRACE=False)
|
|
|
232
231
|
#### 🔁 Override a single Git opt (e.g., `--no-replace-objects`)
|
|
233
232
|
|
|
234
233
|
```python
|
|
235
|
-
|
|
234
|
+
import gitbolt
|
|
236
235
|
|
|
237
|
-
git =
|
|
236
|
+
git = gitbolt.get_git()
|
|
238
237
|
git = git.git_opts_override(no_replace_objects=True)
|
|
239
238
|
```
|
|
240
239
|
|
|
@@ -242,7 +241,7 @@ git = git.git_opts_override(no_replace_objects=True)
|
|
|
242
241
|
|
|
243
242
|
```python
|
|
244
243
|
from pathlib import Path
|
|
245
|
-
from gitbolt.
|
|
244
|
+
from gitbolt.subprocess.impl.simple import SimpleGitCommand
|
|
246
245
|
|
|
247
246
|
git = SimpleGitCommand()
|
|
248
247
|
git = git.git_opts_override(no_replace_objects=True, git_dir=Path(), paginate=True)
|
|
@@ -251,10 +250,10 @@ git = git.git_opts_override(no_replace_objects=True, git_dir=Path(), paginate=Tr
|
|
|
251
250
|
#### 🪢 Chain multiple option overrides fluently
|
|
252
251
|
|
|
253
252
|
```python
|
|
253
|
+
import gitbolt
|
|
254
254
|
from pathlib import Path
|
|
255
|
-
from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
|
|
256
255
|
|
|
257
|
-
git =
|
|
256
|
+
git = gitbolt.get_git()
|
|
258
257
|
overridden_git = git.git_opts_override(exec_path=Path('tmp')).git_opts_override(
|
|
259
258
|
noglob_pathspecs=True,
|
|
260
259
|
no_advice=True
|
|
@@ -267,11 +266,11 @@ re_overridden_git = overridden_git.git_opts_override(glob_pathspecs=True)
|
|
|
267
266
|
#### ❌ Unset Git opts using a special `UNSET` marker
|
|
268
267
|
|
|
269
268
|
```python
|
|
269
|
+
import gitbolt
|
|
270
270
|
from pathlib import Path
|
|
271
|
-
from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
|
|
272
271
|
from vt.utils.commons.commons.core_py import UNSET
|
|
273
272
|
|
|
274
|
-
git =
|
|
273
|
+
git = gitbolt.get_git()
|
|
275
274
|
overridden_git = git.git_opts_override(exec_path=Path('tmp'), no_advice=True)
|
|
276
275
|
no_advice_unset_git = overridden_git.git_opts_override(no_advice=UNSET)
|
|
277
276
|
```
|
|
@@ -279,9 +278,9 @@ no_advice_unset_git = overridden_git.git_opts_override(no_advice=UNSET)
|
|
|
279
278
|
#### 🔄 Reset Git opts by setting new values
|
|
280
279
|
|
|
281
280
|
```python
|
|
282
|
-
|
|
281
|
+
import gitbolt
|
|
283
282
|
|
|
284
|
-
git =
|
|
283
|
+
git = gitbolt.get_git()
|
|
285
284
|
overridden_git = git.git_opts_override(no_advice=True)
|
|
286
285
|
no_advice_reset_git = overridden_git.git_opts_override(no_advice=False)
|
|
287
286
|
```
|
|
@@ -292,33 +291,59 @@ no_advice_reset_git = overridden_git.git_opts_override(no_advice=False)
|
|
|
292
291
|
|
|
293
292
|
At last, run unchecked commands in git.
|
|
294
293
|
|
|
295
|
-
Introduced in `0.0.
|
|
294
|
+
Introduced in `0.0.0.dev4` to
|
|
296
295
|
- experiment.
|
|
297
296
|
- have consistent interfaced commands run until all subcommands are provided by the library.
|
|
298
297
|
|
|
298
|
+
#### 🖥️ Run one process per command
|
|
299
|
+
|
|
299
300
|
```python
|
|
300
|
-
|
|
301
|
+
import gitbolt
|
|
301
302
|
|
|
302
|
-
git =
|
|
303
|
+
git = gitbolt.get_git_command()
|
|
303
304
|
git = git.git_opts_override(no_advice=True)
|
|
304
305
|
git.subcmd_unchecked.run(['--version']) # run the version option for git.
|
|
305
306
|
git.subcmd_unchecked.run(['version']) # run the version subcommand.
|
|
306
307
|
```
|
|
307
308
|
|
|
309
|
+
#### 🖥️ Run one long-running process and communicate with it
|
|
310
|
+
|
|
311
|
+
Introduced in `0.0.0.dev16` to:
|
|
312
|
+
- Make communicable processes using `subprocess.Popen`.
|
|
313
|
+
|
|
314
|
+
Get a long-running process and communicate with it for batching and faster operations.
|
|
315
|
+
|
|
316
|
+
```python
|
|
317
|
+
import gitbolt
|
|
318
|
+
import sys
|
|
319
|
+
|
|
320
|
+
git = gitbolt.get_git_command()
|
|
321
|
+
with git.subcmd_unchecked.popen(["cat-file", "--batch-command"]) as cf:
|
|
322
|
+
cf.stdin.write(b"contents HEAD\n")
|
|
323
|
+
cf.stdin.flush()
|
|
324
|
+
header = cf.stdout.readline().strip()
|
|
325
|
+
print(f"HEADER: {header}", file=sys.stderr)
|
|
326
|
+
obj, typ, size = header.split()
|
|
327
|
+
print(cf.stdout.read(int(size)))
|
|
328
|
+
cf.stdout.readline()
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Error handling and I/O management is left to the client/caller.
|
|
332
|
+
|
|
308
333
|
#### 💻 Run commands received from CLI
|
|
309
334
|
|
|
310
|
-
Introduced in `0.0.
|
|
335
|
+
Introduced in `0.0.0.dev11` is the ability to take commands from CLI and run it inside `gitbolt`.
|
|
311
336
|
|
|
312
337
|
While making a system it may be required to run cli commands as received from cli using gitbolt. An obvious example
|
|
313
338
|
would be to make a system that receives CLI commands and does certain modifications/additions inside `gitbolt` before
|
|
314
339
|
actually running them. An example:
|
|
315
340
|
|
|
316
341
|
```python
|
|
317
|
-
|
|
342
|
+
import gitbolt
|
|
318
343
|
|
|
319
|
-
opts = ["--no-pager", "--namespace", "n1"]
|
|
320
|
-
envs = dict(GIT_AUTHOR_NAME="ss")
|
|
321
|
-
git =
|
|
344
|
+
opts = ["--no-pager", "--namespace", "n1"] # options received from outside your program.
|
|
345
|
+
envs = dict(GIT_AUTHOR_NAME="ss") # env-vars received form outside your program.
|
|
346
|
+
git = gitbolt.get_git_command(opts=opts, envs=envs)
|
|
322
347
|
|
|
323
348
|
# these can later be overridden
|
|
324
349
|
git = git.git_opts_override(namespace="n2")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "gitbolt"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.0.dev16"
|
|
4
4
|
description = "Fast, flexible and type-safe Git commands in Python."
|
|
5
5
|
requires-python = ">=3.12"
|
|
6
6
|
readme = "README.md"
|
|
@@ -45,9 +45,7 @@ pygit2 = ['pygit2']
|
|
|
45
45
|
test = ['pytest']
|
|
46
46
|
|
|
47
47
|
[dependency-groups]
|
|
48
|
-
|
|
49
|
-
# btw, this error doesn't show in the latest developer version. So, this error will go away with 1.17.0 relase
|
|
50
|
-
dev = ["mypy==1.15.0", "ruff"]
|
|
48
|
+
dev = ["mypy", "ruff"]
|
|
51
49
|
doc = ['sphinx', 'sphinx-argparse']
|
|
52
50
|
test = ["pytest", 'pytest-cov', "pytest-xdist"]
|
|
53
51
|
multitest = ['tox']
|
|
@@ -52,8 +52,8 @@ def get_git_command(
|
|
|
52
52
|
git_root_dir: Path = Path.cwd(),
|
|
53
53
|
*,
|
|
54
54
|
git_prog: str | Path = GIT_CMD,
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
opts: list[str] | None = None,
|
|
56
|
+
envs: dict[str, str] | None = None,
|
|
57
57
|
prefer_cli: bool = False,
|
|
58
58
|
) -> GitCommand:
|
|
59
59
|
"""
|
|
@@ -75,8 +75,8 @@ def get_git_command(
|
|
|
75
75
|
:param git_root_dir: Path to the git repo root directory. Defaults to current working directory.
|
|
76
76
|
:param git_prog: git program name/location. Useful when user wants to run a separate git version/git emulator.
|
|
77
77
|
:param opts: main git cli options. The main git command options like ``--no-replace-objects``, ``--no-pager``, ``-C`` etc are git main command args.
|
|
78
|
-
:param envs: main git cli env vars. Not supplying any env vars (default behavior: ``None``)
|
|
79
|
-
the env vars to the underlying runner.
|
|
78
|
+
:param envs: main git cli environment variables (env vars). Not supplying any env vars (default behavior: ``None``)
|
|
79
|
+
simply supplies all the env vars to the underlying runner.
|
|
80
80
|
:param prefer_cli: cli opts and envs will be given priority over programmatically set opts and envs. Setting
|
|
81
81
|
this param to ``True`` will make cli opts and envs appear later in the opts and envs strings which will
|
|
82
82
|
make them override previously programmatically set opts and envs.
|
|
@@ -84,13 +84,13 @@ def get_git_command(
|
|
|
84
84
|
separate runner in subprocess.
|
|
85
85
|
"""
|
|
86
86
|
runner = _SimpleGitCR(git_prog)
|
|
87
|
-
if
|
|
87
|
+
if opts is None:
|
|
88
88
|
return _SimpleGitCommand(git_root_dir, runner)
|
|
89
89
|
else:
|
|
90
90
|
return _CLISimpleGitCommand(
|
|
91
91
|
git_root_dir,
|
|
92
92
|
runner,
|
|
93
|
-
opts=
|
|
94
|
-
envs=
|
|
93
|
+
opts=opts,
|
|
94
|
+
envs=envs,
|
|
95
95
|
prefer_cli=prefer_cli,
|
|
96
96
|
)
|
|
@@ -20,29 +20,21 @@ from gitbolt.ls_tree import LsTreeArgsValidator, UtilLsTreeArgsValidator
|
|
|
20
20
|
from gitbolt.add import AddArgsValidator, UtilAddArgsValidator
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
class
|
|
24
|
-
"""
|
|
25
|
-
Marker interface to mark an operation for git.
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
pass
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class HasGitUnderneath[G: "Git"](ForGit, Protocol):
|
|
23
|
+
class HasGitUnderneath[G: "Git"](Protocol):
|
|
32
24
|
"""
|
|
33
25
|
Stores a reference to main git instance.
|
|
34
26
|
"""
|
|
35
27
|
|
|
36
28
|
@property
|
|
37
29
|
@abstractmethod
|
|
38
|
-
def
|
|
30
|
+
def git(self) -> G:
|
|
39
31
|
"""
|
|
40
|
-
:return: stored git instance reference.
|
|
32
|
+
:return: stored underlying git instance reference.
|
|
41
33
|
"""
|
|
42
34
|
...
|
|
43
35
|
|
|
44
36
|
|
|
45
|
-
class CanOverrideGitOpts(
|
|
37
|
+
class CanOverrideGitOpts(Protocol):
|
|
46
38
|
"""
|
|
47
39
|
Can override main git command options.
|
|
48
40
|
|
|
@@ -65,7 +57,7 @@ class CanOverrideGitOpts(ForGit, Protocol):
|
|
|
65
57
|
...
|
|
66
58
|
|
|
67
59
|
|
|
68
|
-
class CanOverrideGitEnvs(
|
|
60
|
+
class CanOverrideGitEnvs(Protocol):
|
|
69
61
|
"""
|
|
70
62
|
Can override main git command environment variables.
|
|
71
63
|
|
|
@@ -10,7 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
from abc import abstractmethod, ABC
|
|
11
11
|
from collections.abc import Callable
|
|
12
12
|
from pathlib import Path
|
|
13
|
-
from subprocess import CompletedProcess
|
|
13
|
+
from subprocess import CompletedProcess, Popen, PIPE
|
|
14
14
|
from typing import override, Protocol, Unpack, Self, overload, Literal, Any
|
|
15
15
|
|
|
16
16
|
from vt.utils.commons.commons.core_py import is_unset, not_none_not_unset
|
|
@@ -306,13 +306,13 @@ class GitSubcmdCommand(GitSubCommand, HasGitUnderneath["GitCommand"], Protocol):
|
|
|
306
306
|
|
|
307
307
|
@override
|
|
308
308
|
def git_opts_override(self, **overrides: Unpack[GitOpts]) -> Self:
|
|
309
|
-
overridden_git = self.
|
|
309
|
+
overridden_git = self.git.git_opts_override(**overrides)
|
|
310
310
|
self._set_underlying_git(overridden_git)
|
|
311
311
|
return self
|
|
312
312
|
|
|
313
313
|
@override
|
|
314
314
|
def git_envs_override(self, **overrides: Unpack[GitEnvVars]) -> Self:
|
|
315
|
-
overridden_git = self.
|
|
315
|
+
overridden_git = self.git.git_envs_override(**overrides)
|
|
316
316
|
self._set_underlying_git(overridden_git)
|
|
317
317
|
return self
|
|
318
318
|
|
|
@@ -403,11 +403,11 @@ class LsTreeCommand(LsTree, GitSubcmdCommand, Protocol):
|
|
|
403
403
|
def ls_tree(self, tree_ish: str, **ls_tree_opts: Unpack[GitLsTreeOpts]) -> str:
|
|
404
404
|
self.args_validator.validate(tree_ish, **ls_tree_opts)
|
|
405
405
|
sub_cmd_args = self.cli_args_builder.build(tree_ish, **ls_tree_opts)
|
|
406
|
-
main_cmd_args = self.
|
|
407
|
-
env_vars = self.
|
|
406
|
+
main_cmd_args = self.git.build_main_cmd_args()
|
|
407
|
+
env_vars = self.git.build_git_envs()
|
|
408
408
|
|
|
409
409
|
# Run the git command
|
|
410
|
-
result = self.
|
|
410
|
+
result = self.git.runner.run_git_command(
|
|
411
411
|
main_cmd_args,
|
|
412
412
|
sub_cmd_args,
|
|
413
413
|
check=True,
|
|
@@ -485,11 +485,11 @@ class AddCommand(Add, GitSubcmdCommand, Protocol):
|
|
|
485
485
|
pathspec_file_nul=pathspec_file_nul,
|
|
486
486
|
**add_opts,
|
|
487
487
|
)
|
|
488
|
-
main_cmd_args = self.
|
|
489
|
-
env_vars = self.
|
|
488
|
+
main_cmd_args = self.git.build_main_cmd_args()
|
|
489
|
+
env_vars = self.git.build_git_envs()
|
|
490
490
|
|
|
491
491
|
# Run the git command
|
|
492
|
-
result = self.
|
|
492
|
+
result = self.git.runner.run_git_command(
|
|
493
493
|
main_cmd_args,
|
|
494
494
|
sub_cmd_args,
|
|
495
495
|
_input=pathspec_stdin,
|
|
@@ -584,17 +584,13 @@ class UncheckedSubcmd(GitSubcmdCommand, RootDirOp, Protocol):
|
|
|
584
584
|
|
|
585
585
|
:return: ``CompletedProcess`` capturing all the required stdout, stderr, return-code etc.
|
|
586
586
|
"""
|
|
587
|
-
main_cmd_args = self.
|
|
588
|
-
envs_vars = self.
|
|
589
|
-
another_supplied_env = subprocess_run_kwargs.pop("env", None)
|
|
590
|
-
if another_supplied_env:
|
|
591
|
-
if envs_vars is not None:
|
|
592
|
-
envs_vars.update(another_supplied_env)
|
|
587
|
+
main_cmd_args = self.git_main_cmd_args()
|
|
588
|
+
envs_vars = self.git_envs(subprocess_run_kwargs.pop("env", None))
|
|
593
589
|
cwd = subprocess_run_kwargs.pop("cwd", self.root_dir)
|
|
594
590
|
capture_output = subprocess_run_kwargs.pop("capture_output", True)
|
|
595
591
|
check = subprocess_run_kwargs.pop("check", True)
|
|
596
592
|
# Run the git command
|
|
597
|
-
result = self.
|
|
593
|
+
result = self.git.runner.run_git_command(
|
|
598
594
|
main_cmd_args,
|
|
599
595
|
subcommand_args,
|
|
600
596
|
*subprocess_run_args,
|
|
@@ -607,3 +603,103 @@ class UncheckedSubcmd(GitSubcmdCommand, RootDirOp, Protocol):
|
|
|
607
603
|
**subprocess_run_kwargs,
|
|
608
604
|
)
|
|
609
605
|
return result
|
|
606
|
+
|
|
607
|
+
@overload
|
|
608
|
+
def popen(
|
|
609
|
+
self,
|
|
610
|
+
subcommand_args: list[str],
|
|
611
|
+
*popen_args: Any,
|
|
612
|
+
text: Literal[True] = True,
|
|
613
|
+
**popen_kwargs: Any,
|
|
614
|
+
) -> Popen[str]: ...
|
|
615
|
+
|
|
616
|
+
@overload
|
|
617
|
+
def popen(
|
|
618
|
+
self,
|
|
619
|
+
subcommand_args: list[str],
|
|
620
|
+
*popen_args: Any,
|
|
621
|
+
text: Literal[False] = False,
|
|
622
|
+
**popen_kwargs: Any,
|
|
623
|
+
) -> Popen[bytes]: ...
|
|
624
|
+
|
|
625
|
+
def popen(
|
|
626
|
+
self,
|
|
627
|
+
subcommand_args: list[str],
|
|
628
|
+
*popen_args: Any,
|
|
629
|
+
text: Literal[True, False] = False,
|
|
630
|
+
**popen_kwargs: Any,
|
|
631
|
+
) -> Popen[str] | Popen[bytes]:
|
|
632
|
+
"""
|
|
633
|
+
Open unchecked git subcommand communicable process, using ``subprocess.Popen``.
|
|
634
|
+
|
|
635
|
+
All the arguments are congruent to ``subprocess.Popen`` and mostly passes as-is.
|
|
636
|
+
|
|
637
|
+
:param subcommand_args: the full subcommand argument list.
|
|
638
|
+
:param popen_args: additional subprocess positionals.
|
|
639
|
+
:param text: ``_input`` and returns both are str if this value is ``True``. Else, bytes are considered.
|
|
640
|
+
:param popen_kwargs: additional subprocess keyword arguments.
|
|
641
|
+
|
|
642
|
+
:return: ``Popen`` capturing all the required stdout, stderr etc and streaming stdin.
|
|
643
|
+
"""
|
|
644
|
+
main_cmd_args = self.git_main_cmd_args()
|
|
645
|
+
envs_vars = self.git_envs(popen_kwargs.pop("env", None))
|
|
646
|
+
cwd = popen_kwargs.pop("cwd", self.root_dir)
|
|
647
|
+
stdin = popen_kwargs.pop("stdin", PIPE)
|
|
648
|
+
stdout = popen_kwargs.pop("stdout", PIPE)
|
|
649
|
+
stderr = popen_kwargs.pop("stderr", PIPE)
|
|
650
|
+
bufsize = popen_kwargs.pop("bufsize", 0)
|
|
651
|
+
# Popen the git command
|
|
652
|
+
result = self.git.runner.popen_git_command(
|
|
653
|
+
main_cmd_args,
|
|
654
|
+
subcommand_args,
|
|
655
|
+
*popen_args,
|
|
656
|
+
text=text,
|
|
657
|
+
env=envs_vars,
|
|
658
|
+
cwd=cwd,
|
|
659
|
+
stdin=stdin,
|
|
660
|
+
stdout=stdout,
|
|
661
|
+
stderr=stderr,
|
|
662
|
+
bufsize=bufsize,
|
|
663
|
+
**popen_kwargs,
|
|
664
|
+
)
|
|
665
|
+
return result
|
|
666
|
+
|
|
667
|
+
def make_cmd(self, subcommand_args: list[str]) -> list[str]:
|
|
668
|
+
"""
|
|
669
|
+
Make full runnable command for execution from the supplied ``subcommand_args``.
|
|
670
|
+
|
|
671
|
+
As knowledge of the git program and the main command is encapsulated within this ``UncheckedSubcmd`` thus,
|
|
672
|
+
this is a convenience method for any external entities that want to run commands in their own subprocess.
|
|
673
|
+
|
|
674
|
+
:param subcommand_args: arguments for subcommand.
|
|
675
|
+
:returns: a fully made and runnable command for some external ``subprocess`` call.
|
|
676
|
+
"""
|
|
677
|
+
return self.git.runner.make_cmd(self.git.build_main_cmd_args(), subcommand_args)
|
|
678
|
+
|
|
679
|
+
def git_main_cmd_args(self) -> list[str]:
|
|
680
|
+
"""
|
|
681
|
+
Get CLI args for git main cli command.
|
|
682
|
+
|
|
683
|
+
For example, ``--no-pager --no-advice`` is the git main command in ``git --no-pager --no-advice log master -1``.
|
|
684
|
+
|
|
685
|
+
:return: CLI args for git main cli command.
|
|
686
|
+
"""
|
|
687
|
+
return self.git.build_main_cmd_args()
|
|
688
|
+
|
|
689
|
+
def git_envs(
|
|
690
|
+
self, extra_git_envs: dict[str, str] | None = None
|
|
691
|
+
) -> dict[str, str] | None:
|
|
692
|
+
"""
|
|
693
|
+
Get Git environment variables from the merged ``GitEnvVars`` object.
|
|
694
|
+
|
|
695
|
+
Skips values that are ``Unset`` or ``None``-like using ``not_none_not_unset()``.
|
|
696
|
+
Converts ``Path`` and ``datetime`` instances to ``str``.
|
|
697
|
+
|
|
698
|
+
:param extra_git_envs: extraneous git envs supplied by the caller. These will be merged into the resultant
|
|
699
|
+
git envs and then returned.
|
|
700
|
+
:return: A cleaned and normalized GitEnvVars dict suitable for use in subprocesses.
|
|
701
|
+
"""
|
|
702
|
+
env_vars = self.git.build_git_envs()
|
|
703
|
+
if extra_git_envs and env_vars is not None:
|
|
704
|
+
env_vars.update(extra_git_envs)
|
|
705
|
+
return env_vars
|
|
@@ -36,7 +36,7 @@ class GitSubcmdCommandImpl(GitSubcmdCommand, ABC):
|
|
|
36
36
|
self._underlying_git = git
|
|
37
37
|
|
|
38
38
|
@property
|
|
39
|
-
def
|
|
39
|
+
def git(self) -> GitCommand:
|
|
40
40
|
return self._underlying_git
|
|
41
41
|
|
|
42
42
|
def _set_underlying_git(self, git: "GitCommand") -> None:
|
|
@@ -55,14 +55,14 @@ class VersionCommandImpl(VersionCommand, GitSubcmdCommandImpl):
|
|
|
55
55
|
self, build_options: Literal[True, False] = False
|
|
56
56
|
) -> Version.VersionInfo | Version.VersionWithBuildInfo:
|
|
57
57
|
self._require_valid_args(build_options)
|
|
58
|
-
main_cmd_args = self.
|
|
58
|
+
main_cmd_args = self.git.build_main_cmd_args()
|
|
59
59
|
sub_cmd_args = [VERSION_CMD]
|
|
60
|
-
env_vars = self.
|
|
60
|
+
env_vars = self.git.build_git_envs()
|
|
61
61
|
if build_options:
|
|
62
62
|
sub_cmd_args.append("--build-options")
|
|
63
63
|
|
|
64
64
|
def rosetta_supplier():
|
|
65
|
-
return self.
|
|
65
|
+
return self.git.runner.run_git_command(
|
|
66
66
|
main_cmd_args,
|
|
67
67
|
sub_cmd_args,
|
|
68
68
|
check=True,
|
|
@@ -76,7 +76,7 @@ class VersionCommandImpl(VersionCommand, GitSubcmdCommandImpl):
|
|
|
76
76
|
return VersionCommand.VersionInfoForCmd(rosetta_supplier)
|
|
77
77
|
|
|
78
78
|
def clone(self) -> "VersionCommandImpl":
|
|
79
|
-
return VersionCommandImpl(self.
|
|
79
|
+
return VersionCommandImpl(self.git)
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
class LsTreeCommandImpl(LsTreeCommand, GitSubcmdCommandImpl):
|
|
@@ -117,7 +117,7 @@ class LsTreeCommandImpl(LsTreeCommand, GitSubcmdCommandImpl):
|
|
|
117
117
|
return self._cli_args_builder
|
|
118
118
|
|
|
119
119
|
def clone(self) -> "LsTreeCommandImpl":
|
|
120
|
-
return LsTreeCommandImpl(self.root_dir, self.
|
|
120
|
+
return LsTreeCommandImpl(self.root_dir, self.git)
|
|
121
121
|
|
|
122
122
|
|
|
123
123
|
class AddCommandImpl(AddCommand, GitSubcmdCommandImpl):
|
|
@@ -150,7 +150,7 @@ class AddCommandImpl(AddCommand, GitSubcmdCommandImpl):
|
|
|
150
150
|
return self._cli_args_builder
|
|
151
151
|
|
|
152
152
|
def clone(self) -> "AddCommandImpl":
|
|
153
|
-
return AddCommandImpl(self.root_dir, self.
|
|
153
|
+
return AddCommandImpl(self.root_dir, self.git)
|
|
154
154
|
|
|
155
155
|
|
|
156
156
|
class UncheckedSubcmdImpl(UncheckedSubcmd, GitSubcmdCommandImpl):
|
|
@@ -164,7 +164,7 @@ class UncheckedSubcmdImpl(UncheckedSubcmd, GitSubcmdCommandImpl):
|
|
|
164
164
|
return self._root_dir
|
|
165
165
|
|
|
166
166
|
def clone(self) -> "UncheckedSubcmdImpl":
|
|
167
|
-
return UncheckedSubcmdImpl(self.root_dir, self.
|
|
167
|
+
return UncheckedSubcmdImpl(self.root_dir, self.git)
|
|
168
168
|
|
|
169
169
|
|
|
170
170
|
class SimpleGitCommand(GitCommand, RootDirOp):
|
|
@@ -266,8 +266,8 @@ class CLISimpleGitCommand(SimpleGitCommand):
|
|
|
266
266
|
):
|
|
267
267
|
"""
|
|
268
268
|
:param opts: main git cli options.
|
|
269
|
-
:param envs: main git cli env vars. Not supplying any env
|
|
270
|
-
the env vars to the underlying runner.
|
|
269
|
+
:param envs: main git cli environment variables (env vars). Not supplying any env
|
|
270
|
+
vars (default behavior: ``None``) simply supplies all the env vars to the underlying runner.
|
|
271
271
|
:param prefer_cli: cli opts and envs will be given priority over programmatically set opts and envs. Setting
|
|
272
272
|
this param to ``True`` will make cli opts and envs appear later in the opts and envs strings which will
|
|
273
273
|
make them override previously programmatically set opts and envs.
|
|
@@ -9,7 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import pathlib
|
|
11
11
|
from abc import abstractmethod
|
|
12
|
-
from subprocess import CompletedProcess
|
|
12
|
+
from subprocess import CompletedProcess, Popen
|
|
13
13
|
from typing import Protocol, overload, Any, Literal
|
|
14
14
|
|
|
15
15
|
|
|
@@ -64,6 +64,39 @@ class GitCommandRunner(Protocol):
|
|
|
64
64
|
**subprocess_run_kwargs: Any,
|
|
65
65
|
) -> CompletedProcess[bytes]: ...
|
|
66
66
|
|
|
67
|
+
@overload
|
|
68
|
+
@abstractmethod
|
|
69
|
+
def popen_git_command(
|
|
70
|
+
self,
|
|
71
|
+
main_cmd_args: list[str],
|
|
72
|
+
subcommand_args: list[str],
|
|
73
|
+
*popen_run_args: Any,
|
|
74
|
+
text: Literal[False],
|
|
75
|
+
**popen_run_kwargs: Any,
|
|
76
|
+
) -> Popen[bytes]: ...
|
|
77
|
+
|
|
78
|
+
@overload
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def popen_git_command(
|
|
81
|
+
self,
|
|
82
|
+
main_cmd_args: list[str],
|
|
83
|
+
subcommand_args: list[str],
|
|
84
|
+
*popen_run_args: Any,
|
|
85
|
+
text: Literal[True],
|
|
86
|
+
**popen_run_kwargs: Any,
|
|
87
|
+
) -> Popen[str]: ...
|
|
88
|
+
|
|
89
|
+
@abstractmethod
|
|
90
|
+
def make_cmd(self, main_cmd_args: list[str], sub_cmd_args: list[str]) -> list[str]:
|
|
91
|
+
"""
|
|
92
|
+
Make command for execution for use in an external subprocess.
|
|
93
|
+
|
|
94
|
+
:param main_cmd_args: arguments for the main command.
|
|
95
|
+
:param sub_cmd_args: arguments for subcommand.
|
|
96
|
+
:returns: a fully made and runnable command for some external ``subprocess`` call.
|
|
97
|
+
"""
|
|
98
|
+
...
|
|
99
|
+
|
|
67
100
|
@property
|
|
68
101
|
@abstractmethod
|
|
69
102
|
def git_prog(self) -> str | pathlib.Path:
|
|
@@ -9,7 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import pathlib
|
|
11
11
|
import subprocess
|
|
12
|
-
from subprocess import CompletedProcess
|
|
12
|
+
from subprocess import CompletedProcess, Popen
|
|
13
13
|
from typing import overload, override, Any, Literal
|
|
14
14
|
|
|
15
15
|
from gitbolt.subprocess.constants import GIT_CMD
|
|
@@ -86,7 +86,7 @@ class SimpleGitCR(GitCommandRunner):
|
|
|
86
86
|
) -> CompletedProcess[str] | CompletedProcess[bytes]:
|
|
87
87
|
try:
|
|
88
88
|
return subprocess.run(
|
|
89
|
-
|
|
89
|
+
self.make_cmd(main_cmd_args, subcommand_args),
|
|
90
90
|
*subprocess_run_args,
|
|
91
91
|
input=_input,
|
|
92
92
|
text=text,
|
|
@@ -97,6 +97,53 @@ class SimpleGitCR(GitCommandRunner):
|
|
|
97
97
|
e.stderr, called_process_error=e, exit_code=e.returncode
|
|
98
98
|
) from e
|
|
99
99
|
|
|
100
|
+
@overload
|
|
101
|
+
@override
|
|
102
|
+
def popen_git_command(
|
|
103
|
+
self,
|
|
104
|
+
main_cmd_args: list[str],
|
|
105
|
+
subcommand_args: list[str],
|
|
106
|
+
*popen_run_args: Any,
|
|
107
|
+
text: Literal[False],
|
|
108
|
+
**popen_run_kwargs: Any,
|
|
109
|
+
) -> Popen[bytes]: ...
|
|
110
|
+
|
|
111
|
+
@overload
|
|
112
|
+
@override
|
|
113
|
+
def popen_git_command(
|
|
114
|
+
self,
|
|
115
|
+
main_cmd_args: list[str],
|
|
116
|
+
subcommand_args: list[str],
|
|
117
|
+
*popen_run_args: Any,
|
|
118
|
+
text: Literal[True],
|
|
119
|
+
**popen_run_kwargs: Any,
|
|
120
|
+
) -> Popen[str]: ...
|
|
121
|
+
|
|
122
|
+
@override
|
|
123
|
+
def popen_git_command(
|
|
124
|
+
self,
|
|
125
|
+
main_cmd_args: list[str],
|
|
126
|
+
subcommand_args: list[str],
|
|
127
|
+
*popen_run_args: Any,
|
|
128
|
+
text: Literal[True, False],
|
|
129
|
+
**popen_run_kwargs: Any,
|
|
130
|
+
) -> Popen[str] | Popen[bytes]:
|
|
131
|
+
try:
|
|
132
|
+
return subprocess.Popen(
|
|
133
|
+
self.make_cmd(main_cmd_args, subcommand_args),
|
|
134
|
+
*popen_run_args,
|
|
135
|
+
text=text,
|
|
136
|
+
**popen_run_kwargs,
|
|
137
|
+
)
|
|
138
|
+
except subprocess.CalledProcessError as e:
|
|
139
|
+
raise GitCmdException(
|
|
140
|
+
e.stderr, called_process_error=e, exit_code=e.returncode
|
|
141
|
+
) from e
|
|
142
|
+
|
|
143
|
+
@override
|
|
144
|
+
def make_cmd(self, main_cmd_args: list[str], sub_cmd_args: list[str]) -> list[str]:
|
|
145
|
+
return [str(self.git_prog), *main_cmd_args, *sub_cmd_args]
|
|
146
|
+
|
|
100
147
|
@override
|
|
101
148
|
@property
|
|
102
149
|
def git_prog(self) -> str | pathlib.Path:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitbolt
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev16
|
|
4
4
|
Summary: Fast, flexible and type-safe Git commands in Python.
|
|
5
5
|
Author-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>
|
|
6
6
|
Maintainer-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>
|
|
@@ -114,9 +114,8 @@ Gitbolt lets you pass subcommands around as typed objects. This enables highly f
|
|
|
114
114
|
|
|
115
115
|
```python
|
|
116
116
|
import gitbolt
|
|
117
|
-
from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
|
|
118
117
|
|
|
119
|
-
git =
|
|
118
|
+
git = gitbolt.get_git()
|
|
120
119
|
version_subcmd = git.version_subcmd
|
|
121
120
|
add_subcmd = git.add_subcmd
|
|
122
121
|
|
|
@@ -136,11 +135,11 @@ method_which_only_adds_a_file(add_subcmd)
|
|
|
136
135
|
git subcommands are modeled as terminal functions that return stdout.
|
|
137
136
|
|
|
138
137
|
```python
|
|
139
|
-
|
|
138
|
+
import gitbolt
|
|
140
139
|
|
|
141
|
-
git =
|
|
142
|
-
|
|
143
|
-
print(
|
|
140
|
+
git = gitbolt.get_git()
|
|
141
|
+
version_stdout = git.version_subcmd.version().version()
|
|
142
|
+
print(version_stdout)
|
|
144
143
|
```
|
|
145
144
|
|
|
146
145
|
### 🪼 Modular Architecture
|
|
@@ -206,9 +205,9 @@ Extensive use of type-hints ensures that invalid usages fail early — at *compi
|
|
|
206
205
|
#### 🔁 Override a single Git env (e.g., `GIT_TRACE`)
|
|
207
206
|
|
|
208
207
|
```python
|
|
209
|
-
|
|
208
|
+
import gitbolt
|
|
210
209
|
|
|
211
|
-
git =
|
|
210
|
+
git = gitbolt.get_git()
|
|
212
211
|
git = git.git_envs_override(GIT_TRACE=True)
|
|
213
212
|
```
|
|
214
213
|
|
|
@@ -216,9 +215,9 @@ git = git.git_envs_override(GIT_TRACE=True)
|
|
|
216
215
|
|
|
217
216
|
```python
|
|
218
217
|
from pathlib import Path
|
|
219
|
-
|
|
218
|
+
import gitbolt
|
|
220
219
|
|
|
221
|
-
git =
|
|
220
|
+
git = gitbolt.get_git()
|
|
222
221
|
git = git.git_envs_override(GIT_TRACE=1, GIT_DIR=Path('/tmp/git-dir/'), GIT_EDITOR='vim')
|
|
223
222
|
```
|
|
224
223
|
|
|
@@ -226,9 +225,9 @@ git = git.git_envs_override(GIT_TRACE=1, GIT_DIR=Path('/tmp/git-dir/'), GIT_EDIT
|
|
|
226
225
|
|
|
227
226
|
```python
|
|
228
227
|
from pathlib import Path
|
|
229
|
-
|
|
228
|
+
import gitbolt
|
|
230
229
|
|
|
231
|
-
git =
|
|
230
|
+
git = gitbolt.get_git()
|
|
232
231
|
overridden_git = git.git_envs_override(GIT_SSH=Path('/tmp/SSH')).git_envs_override(
|
|
233
232
|
GIT_TERMINAL_PROMPT=1,
|
|
234
233
|
GIT_NO_REPLACE_OBJECTS=True
|
|
@@ -239,10 +238,10 @@ re_overridden_git = overridden_git.git_envs_override(GIT_TRACE=True)
|
|
|
239
238
|
#### ❌ Unset Git envs using a special `UNSET` marker
|
|
240
239
|
|
|
241
240
|
```python
|
|
242
|
-
|
|
241
|
+
import gitbolt
|
|
243
242
|
from vt.utils.commons.commons.core_py import UNSET
|
|
244
243
|
|
|
245
|
-
git =
|
|
244
|
+
git = gitbolt.get_git()
|
|
246
245
|
overridden_git = git.git_envs_override(GIT_ADVICE=True, GIT_TRACE=True)
|
|
247
246
|
no_advice_unset_git = overridden_git.git_envs_override(GIT_TRACE=UNSET)
|
|
248
247
|
```
|
|
@@ -250,9 +249,9 @@ no_advice_unset_git = overridden_git.git_envs_override(GIT_TRACE=UNSET)
|
|
|
250
249
|
#### 🔄 Reset Git envs by setting new values
|
|
251
250
|
|
|
252
251
|
```python
|
|
253
|
-
|
|
252
|
+
import gitbolt
|
|
254
253
|
|
|
255
|
-
git =
|
|
254
|
+
git = gitbolt.get_git()
|
|
256
255
|
overridden_git = git.git_envs_override(GIT_TRACE=True)
|
|
257
256
|
git_trace_reset_git = overridden_git.git_envs_override(GIT_TRACE=False)
|
|
258
257
|
```
|
|
@@ -268,9 +267,9 @@ git_trace_reset_git = overridden_git.git_envs_override(GIT_TRACE=False)
|
|
|
268
267
|
#### 🔁 Override a single Git opt (e.g., `--no-replace-objects`)
|
|
269
268
|
|
|
270
269
|
```python
|
|
271
|
-
|
|
270
|
+
import gitbolt
|
|
272
271
|
|
|
273
|
-
git =
|
|
272
|
+
git = gitbolt.get_git()
|
|
274
273
|
git = git.git_opts_override(no_replace_objects=True)
|
|
275
274
|
```
|
|
276
275
|
|
|
@@ -278,7 +277,7 @@ git = git.git_opts_override(no_replace_objects=True)
|
|
|
278
277
|
|
|
279
278
|
```python
|
|
280
279
|
from pathlib import Path
|
|
281
|
-
from gitbolt.
|
|
280
|
+
from gitbolt.subprocess.impl.simple import SimpleGitCommand
|
|
282
281
|
|
|
283
282
|
git = SimpleGitCommand()
|
|
284
283
|
git = git.git_opts_override(no_replace_objects=True, git_dir=Path(), paginate=True)
|
|
@@ -287,10 +286,10 @@ git = git.git_opts_override(no_replace_objects=True, git_dir=Path(), paginate=Tr
|
|
|
287
286
|
#### 🪢 Chain multiple option overrides fluently
|
|
288
287
|
|
|
289
288
|
```python
|
|
289
|
+
import gitbolt
|
|
290
290
|
from pathlib import Path
|
|
291
|
-
from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
|
|
292
291
|
|
|
293
|
-
git =
|
|
292
|
+
git = gitbolt.get_git()
|
|
294
293
|
overridden_git = git.git_opts_override(exec_path=Path('tmp')).git_opts_override(
|
|
295
294
|
noglob_pathspecs=True,
|
|
296
295
|
no_advice=True
|
|
@@ -303,11 +302,11 @@ re_overridden_git = overridden_git.git_opts_override(glob_pathspecs=True)
|
|
|
303
302
|
#### ❌ Unset Git opts using a special `UNSET` marker
|
|
304
303
|
|
|
305
304
|
```python
|
|
305
|
+
import gitbolt
|
|
306
306
|
from pathlib import Path
|
|
307
|
-
from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
|
|
308
307
|
from vt.utils.commons.commons.core_py import UNSET
|
|
309
308
|
|
|
310
|
-
git =
|
|
309
|
+
git = gitbolt.get_git()
|
|
311
310
|
overridden_git = git.git_opts_override(exec_path=Path('tmp'), no_advice=True)
|
|
312
311
|
no_advice_unset_git = overridden_git.git_opts_override(no_advice=UNSET)
|
|
313
312
|
```
|
|
@@ -315,9 +314,9 @@ no_advice_unset_git = overridden_git.git_opts_override(no_advice=UNSET)
|
|
|
315
314
|
#### 🔄 Reset Git opts by setting new values
|
|
316
315
|
|
|
317
316
|
```python
|
|
318
|
-
|
|
317
|
+
import gitbolt
|
|
319
318
|
|
|
320
|
-
git =
|
|
319
|
+
git = gitbolt.get_git()
|
|
321
320
|
overridden_git = git.git_opts_override(no_advice=True)
|
|
322
321
|
no_advice_reset_git = overridden_git.git_opts_override(no_advice=False)
|
|
323
322
|
```
|
|
@@ -328,33 +327,59 @@ no_advice_reset_git = overridden_git.git_opts_override(no_advice=False)
|
|
|
328
327
|
|
|
329
328
|
At last, run unchecked commands in git.
|
|
330
329
|
|
|
331
|
-
Introduced in `0.0.
|
|
330
|
+
Introduced in `0.0.0.dev4` to
|
|
332
331
|
- experiment.
|
|
333
332
|
- have consistent interfaced commands run until all subcommands are provided by the library.
|
|
334
333
|
|
|
334
|
+
#### 🖥️ Run one process per command
|
|
335
|
+
|
|
335
336
|
```python
|
|
336
|
-
|
|
337
|
+
import gitbolt
|
|
337
338
|
|
|
338
|
-
git =
|
|
339
|
+
git = gitbolt.get_git_command()
|
|
339
340
|
git = git.git_opts_override(no_advice=True)
|
|
340
341
|
git.subcmd_unchecked.run(['--version']) # run the version option for git.
|
|
341
342
|
git.subcmd_unchecked.run(['version']) # run the version subcommand.
|
|
342
343
|
```
|
|
343
344
|
|
|
345
|
+
#### 🖥️ Run one long-running process and communicate with it
|
|
346
|
+
|
|
347
|
+
Introduced in `0.0.0.dev16` to:
|
|
348
|
+
- Make communicable processes using `subprocess.Popen`.
|
|
349
|
+
|
|
350
|
+
Get a long-running process and communicate with it for batching and faster operations.
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
import gitbolt
|
|
354
|
+
import sys
|
|
355
|
+
|
|
356
|
+
git = gitbolt.get_git_command()
|
|
357
|
+
with git.subcmd_unchecked.popen(["cat-file", "--batch-command"]) as cf:
|
|
358
|
+
cf.stdin.write(b"contents HEAD\n")
|
|
359
|
+
cf.stdin.flush()
|
|
360
|
+
header = cf.stdout.readline().strip()
|
|
361
|
+
print(f"HEADER: {header}", file=sys.stderr)
|
|
362
|
+
obj, typ, size = header.split()
|
|
363
|
+
print(cf.stdout.read(int(size)))
|
|
364
|
+
cf.stdout.readline()
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Error handling and I/O management is left to the client/caller.
|
|
368
|
+
|
|
344
369
|
#### 💻 Run commands received from CLI
|
|
345
370
|
|
|
346
|
-
Introduced in `0.0.
|
|
371
|
+
Introduced in `0.0.0.dev11` is the ability to take commands from CLI and run it inside `gitbolt`.
|
|
347
372
|
|
|
348
373
|
While making a system it may be required to run cli commands as received from cli using gitbolt. An obvious example
|
|
349
374
|
would be to make a system that receives CLI commands and does certain modifications/additions inside `gitbolt` before
|
|
350
375
|
actually running them. An example:
|
|
351
376
|
|
|
352
377
|
```python
|
|
353
|
-
|
|
378
|
+
import gitbolt
|
|
354
379
|
|
|
355
|
-
opts = ["--no-pager", "--namespace", "n1"]
|
|
356
|
-
envs = dict(GIT_AUTHOR_NAME="ss")
|
|
357
|
-
git =
|
|
380
|
+
opts = ["--no-pager", "--namespace", "n1"] # options received from outside your program.
|
|
381
|
+
envs = dict(GIT_AUTHOR_NAME="ss") # env-vars received form outside your program.
|
|
382
|
+
git = gitbolt.get_git_command(opts=opts, envs=envs)
|
|
358
383
|
|
|
359
384
|
# these can later be overridden
|
|
360
385
|
git = git.git_opts_override(namespace="n2")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|