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.
Files changed (35) hide show
  1. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/PKG-INFO +59 -34
  2. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/README.md +58 -33
  3. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/pyproject.toml +2 -4
  4. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/__init__.py +7 -7
  5. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/base.py +5 -13
  6. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/base.py +112 -16
  7. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/impl/simple.py +10 -10
  8. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/runner/base.py +34 -1
  9. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/runner/simple.py +49 -2
  10. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt.egg-info/PKG-INFO +59 -34
  11. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/LICENSE +0 -0
  12. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/setup.cfg +0 -0
  13. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/_internal_init.py +0 -0
  14. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/add.py +0 -0
  15. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/constants.py +0 -0
  16. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/exceptions.py +0 -0
  17. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/ls_tree.py +0 -0
  18. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/models.py +0 -0
  19. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/py.typed +0 -0
  20. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/pytest_plugin.py +0 -0
  21. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/__init__.py +0 -0
  22. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/_internal_init.py +0 -0
  23. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/add.py +0 -0
  24. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/constants.py +0 -0
  25. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/exceptions.py +0 -0
  26. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/impl/__init__.py +0 -0
  27. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/ls_tree.py +0 -0
  28. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/runner/__init__.py +0 -0
  29. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/subprocess/utils.py +0 -0
  30. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt/utils.py +0 -0
  31. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt.egg-info/SOURCES.txt +0 -0
  32. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt.egg-info/dependency_links.txt +0 -0
  33. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt.egg-info/entry_points.txt +0 -0
  34. {gitbolt-0.0.0.dev14 → gitbolt-0.0.0.dev16}/src/gitbolt.egg-info/requires.txt +0 -0
  35. {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.dev14
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 = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
138
+ import gitbolt
140
139
 
141
- git = SimpleGitCommand()
142
- status_out = git.status_subcmd.status()
143
- print(status_out)
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
208
+ import gitbolt
210
209
 
211
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
218
+ import gitbolt
220
219
 
221
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
228
+ import gitbolt
230
229
 
231
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
241
+ import gitbolt
243
242
  from vt.utils.commons.commons.core_py import UNSET
244
243
 
245
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
252
+ import gitbolt
254
253
 
255
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
270
+ import gitbolt
272
271
 
273
- git = SimpleGitCommand()
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.git_subprocess.impl.simple import SimpleGitCommand
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 = SimpleGitCommand()
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 = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
317
+ import gitbolt
319
318
 
320
- git = SimpleGitCommand()
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.0dev4` to
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
337
+ import gitbolt
337
338
 
338
- git = SimpleGitCommand()
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.0dev11` is the ability to take commands from CLI and run it inside `gitbolt`.
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
- from gitbolt.git_subprocess.impl.simple import CLISimpleGitCommand
378
+ import gitbolt
354
379
 
355
- opts = ["--no-pager", "--namespace", "n1"] # options received from outside your program.
356
- envs = dict(GIT_AUTHOR_NAME="ss") # env-vars received form outside your program.
357
- git = CLISimpleGitCommand(opts=opts, envs=envs)
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 = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
102
+ import gitbolt
104
103
 
105
- git = SimpleGitCommand()
106
- status_out = git.status_subcmd.status()
107
- print(status_out)
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
172
+ import gitbolt
174
173
 
175
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
182
+ import gitbolt
184
183
 
185
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
192
+ import gitbolt
194
193
 
195
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
205
+ import gitbolt
207
206
  from vt.utils.commons.commons.core_py import UNSET
208
207
 
209
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
216
+ import gitbolt
218
217
 
219
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
234
+ import gitbolt
236
235
 
237
- git = SimpleGitCommand()
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.git_subprocess.impl.simple import SimpleGitCommand
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 = SimpleGitCommand()
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 = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
281
+ import gitbolt
283
282
 
284
- git = SimpleGitCommand()
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.0dev4` to
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
301
+ import gitbolt
301
302
 
302
- git = SimpleGitCommand()
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.0dev11` is the ability to take commands from CLI and run it inside `gitbolt`.
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
- from gitbolt.git_subprocess.impl.simple import CLISimpleGitCommand
342
+ import gitbolt
318
343
 
319
- opts = ["--no-pager", "--namespace", "n1"] # options received from outside your program.
320
- envs = dict(GIT_AUTHOR_NAME="ss") # env-vars received form outside your program.
321
- git = CLISimpleGitCommand(opts=opts, envs=envs)
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.0dev14"
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
- # TODO: report the mypy recent version (1.16.0, 1.16.1 issue) RuntimeError: ('Not implemented', <class 'mypy.types.TypeGuardedType'>)
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
- main_cmd_opts: list[str] | None = None,
56
- main_cmd_envs: dict[str, str] | None = None,
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``) simply supplies all
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 main_cmd_opts is None:
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=main_cmd_opts,
94
- envs=main_cmd_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 ForGit(Protocol):
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 underlying_git(self) -> G:
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(ForGit, Protocol):
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(ForGit, Protocol):
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.underlying_git.git_opts_override(**overrides)
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.underlying_git.git_envs_override(**overrides)
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.underlying_git.build_main_cmd_args()
407
- env_vars = self.underlying_git.build_git_envs()
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.underlying_git.runner.run_git_command(
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.underlying_git.build_main_cmd_args()
489
- env_vars = self.underlying_git.build_git_envs()
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.underlying_git.runner.run_git_command(
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.underlying_git.build_main_cmd_args()
588
- envs_vars = self.underlying_git.build_git_envs()
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.underlying_git.runner.run_git_command(
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 underlying_git(self) -> GitCommand:
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.underlying_git.build_main_cmd_args()
58
+ main_cmd_args = self.git.build_main_cmd_args()
59
59
  sub_cmd_args = [VERSION_CMD]
60
- env_vars = self.underlying_git.build_git_envs()
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.underlying_git.runner.run_git_command(
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.underlying_git)
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.underlying_git)
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.underlying_git)
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.underlying_git)
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 vars (default behavior: ``None``) simply supplies all
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
- [str(self.git_prog), *main_cmd_args, *subcommand_args],
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.dev14
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 = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
138
+ import gitbolt
140
139
 
141
- git = SimpleGitCommand()
142
- status_out = git.status_subcmd.status()
143
- print(status_out)
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
208
+ import gitbolt
210
209
 
211
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
218
+ import gitbolt
220
219
 
221
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
228
+ import gitbolt
230
229
 
231
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
241
+ import gitbolt
243
242
  from vt.utils.commons.commons.core_py import UNSET
244
243
 
245
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
252
+ import gitbolt
254
253
 
255
- git = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
270
+ import gitbolt
272
271
 
273
- git = SimpleGitCommand()
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.git_subprocess.impl.simple import SimpleGitCommand
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 = SimpleGitCommand()
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 = SimpleGitCommand()
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
317
+ import gitbolt
319
318
 
320
- git = SimpleGitCommand()
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.0dev4` to
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
- from gitbolt.git_subprocess.impl.simple import SimpleGitCommand
337
+ import gitbolt
337
338
 
338
- git = SimpleGitCommand()
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.0dev11` is the ability to take commands from CLI and run it inside `gitbolt`.
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
- from gitbolt.git_subprocess.impl.simple import CLISimpleGitCommand
378
+ import gitbolt
354
379
 
355
- opts = ["--no-pager", "--namespace", "n1"] # options received from outside your program.
356
- envs = dict(GIT_AUTHOR_NAME="ss") # env-vars received form outside your program.
357
- git = CLISimpleGitCommand(opts=opts, envs=envs)
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