gitbolt 0.0.0.dev25__tar.gz → 0.0.0.dev27__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 (39) hide show
  1. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/PKG-INFO +2 -2
  2. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/pyproject.toml +3 -2
  3. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/base.py +45 -34
  4. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/utils/session.py +20 -0
  5. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/utils.py +45 -9
  6. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt.egg-info/PKG-INFO +2 -2
  7. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/LICENSE +0 -0
  8. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/README.md +0 -0
  9. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/setup.cfg +0 -0
  10. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/__init__.py +0 -0
  11. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/_internal_init.py +0 -0
  12. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/add.py +0 -0
  13. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/base.py +0 -0
  14. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/constants.py +0 -0
  15. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/exceptions.py +0 -0
  16. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/ls_tree.py +0 -0
  17. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/models.py +0 -0
  18. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/py.typed +0 -0
  19. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/pytest_plugin.py +0 -0
  20. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/__init__.py +0 -0
  21. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/_internal_init.py +0 -0
  22. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/add.py +0 -0
  23. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/constants.py +0 -0
  24. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/exceptions.py +0 -0
  25. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/impl/__init__.py +0 -0
  26. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/impl/simple.py +0 -0
  27. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/ls_tree.py +0 -0
  28. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/runner/__init__.py +0 -0
  29. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/runner/base.py +0 -0
  30. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/runner/simple.py +0 -0
  31. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/utils/__init__.py +0 -0
  32. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/utils/main_cmd.py +0 -0
  33. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/utils/models.py +0 -0
  34. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt/subprocess/worktree.py +0 -0
  35. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt.egg-info/SOURCES.txt +0 -0
  36. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt.egg-info/dependency_links.txt +0 -0
  37. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt.egg-info/entry_points.txt +0 -0
  38. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt.egg-info/requires.txt +0 -0
  39. {gitbolt-0.0.0.dev25 → gitbolt-0.0.0.dev27}/src/gitbolt.egg-info/top_level.txt +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitbolt
3
- Version: 0.0.0.dev25
3
+ Version: 0.0.0.dev27
4
4
  Summary: Fast, flexible and type-safe Git commands in Python.
5
- Author-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>
5
+ Author-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>, Thamotharan R <thamotharan.r@vaastav.tech>
6
6
  Maintainer-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>
7
7
  License-Expression: Apache-2.0
8
8
  Project-URL: homepage, https://github.com/Vaastav-Technologies/py-gitbolt
@@ -1,11 +1,12 @@
1
1
  [project]
2
2
  name = "gitbolt"
3
- version = "0.0.0.dev25"
3
+ version = "0.0.0.dev27"
4
4
  description = "Fast, flexible and type-safe Git commands in Python."
5
5
  requires-python = ">=3.12"
6
6
  readme = "README.md"
7
7
  authors = [
8
- {name = "Suhas Krishna Srivastava", email = "suhas.srivastava@vaastav.tech"}
8
+ {name = "Suhas Krishna Srivastava", email = "suhas.srivastava@vaastav.tech"},
9
+ {name = "Thamotharan R", email = "thamotharan.r@vaastav.tech"}
9
10
  ]
10
11
  maintainers = [
11
12
  {name = "Suhas Krishna Srivastava", email = "suhas.srivastava@vaastav.tech"}
@@ -35,6 +35,7 @@ from gitbolt.models import GitOpts, GitLsTreeOpts, GitAddOpts, GitEnvVars
35
35
  from gitbolt.subprocess.worktree import WorktreeCLIArgsBuilder
36
36
  from gitbolt.utils import merge_git_opts, merge_git_envs
37
37
  from gitbolt.subprocess.constants import GIT_CMD, VERSION_CMD, LS_TREE_CMD, ADD_CMD, WORKTREE_CMD, WORKTREE_ADD_CMD
38
+ from gitbolt.subprocess.utils.session import _close_session_popen
38
39
 
39
40
 
40
41
  class GitCommand(Git, ABC):
@@ -50,6 +51,28 @@ class GitCommand(Git, ABC):
50
51
  self._main_cmd_opts: GitOpts = {}
51
52
  self._env_vars: GitEnvVars | None = None
52
53
  self._cmd_str_repr: str | None = None
54
+ self._main_cmd_opt_builders: dict[str, Callable[[], list[str]]] = {
55
+ "C": self._main_cmd_cap_c_args,
56
+ "c": self._main_cmd_small_c_args,
57
+ "config_env": self._main_cmd_config_env_args,
58
+ "exec_path": self._main_cmd_exec_path_args,
59
+ "paginate": self._main_cmd_paginate_args,
60
+ "no_pager": self._main_cmd_no_pager_args,
61
+ "git_dir": self._main_cmd_git_dir_args,
62
+ "work_tree": self._main_cmd_work_tree_args,
63
+ "namespace": self._main_cmd_namespace_args,
64
+ "bare": self._main_cmd_bare_args,
65
+ "no_replace_objects": self._main_cmd_no_replace_objects_args,
66
+ "no_lazy_fetch": self._main_cmd_no_lazy_fetch_args,
67
+ "no_optional_locks": self._main_cmd_no_optional_locks_args,
68
+ "no_advice": self._main_cmd_no_advice_args,
69
+ "literal_pathspecs": self._main_cmd_literal_pathspecs_args,
70
+ "glob_pathspecs": self._main_cmd_glob_pathspecs_args,
71
+ "noglob_pathspecs": self._main_cmd_noglob_pathspecs_args,
72
+ "icase_pathspecs": self._main_cmd_icase_pathspecs_args,
73
+ "list_cmds": self._main_cmd_list_cmds_args,
74
+ "attr_source": self._main_cmd_attr_source_args,
75
+ }
53
76
 
54
77
  @override
55
78
  def __str__(self) -> str:
@@ -65,7 +88,7 @@ class GitCommand(Git, ABC):
65
88
 
66
89
  >>> _b_git = _a_git.git_opts_override(C=[Path("a"), Path("b")], no_advice=True, no_replace_objects=True)
67
90
  >>> assert str(_a_git) == GIT_CMD # _a_git never changed
68
- >>> assert str(_b_git) == f"{GIT_CMD} -C a -C b --no-replace-objects --no-advice"
91
+ >>> assert str(_b_git) == f"{GIT_CMD} -C a -C b --no-advice --no-replace-objects"
69
92
 
70
93
  Adding git envs:
71
94
 
@@ -74,7 +97,7 @@ class GitCommand(Git, ABC):
74
97
  >>> assert str(_c_git) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD}"
75
98
 
76
99
  >>> _d_git = _c_git.git_opts_override(C=[Path("a"), Path("b")], no_advice=True, no_replace_objects=True, config_env=dict(conf1="val1", glob1="val2"))
77
- >>> assert str(_d_git) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} -C a -C b --config-env conf1=val1 --config-env glob1=val2 --no-replace-objects --no-advice"
100
+ >>> assert str(_d_git) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} -C a -C b --no-advice --no-replace-objects --config-env conf1=val1 --config-env glob1=val2"
78
101
 
79
102
  Does not affect repr:
80
103
 
@@ -127,28 +150,14 @@ class GitCommand(Git, ABC):
127
150
 
128
151
  :return: CLI args for git main cli command.
129
152
  """
130
- return (
131
- self._main_cmd_cap_c_args()
132
- + self._main_cmd_small_c_args()
133
- + self._main_cmd_config_env_args()
134
- + self._main_cmd_exec_path_args()
135
- + self._main_cmd_paginate_args()
136
- + self._main_cmd_no_pager_args()
137
- + self._main_cmd_git_dir_args()
138
- + self._main_cmd_work_tree_args()
139
- + self._main_cmd_namespace_args()
140
- + self._main_cmd_bare_args()
141
- + self._main_cmd_no_replace_objects_args()
142
- + self._main_cmd_no_lazy_fetch_args()
143
- + self._main_cmd_no_optional_locks_args()
144
- + self._main_cmd_no_advice_args()
145
- + self._main_cmd_literal_pathspecs_args()
146
- + self._main_cmd_glob_pathspecs_args()
147
- + self._main_cmd_noglob_pathspecs_args()
148
- + self._main_cmd_icase_pathspecs_args()
149
- + self._main_cmd_list_cmds_args()
150
- + self._main_cmd_attr_source_args()
151
- )
153
+ args: list[str] = []
154
+ for opt_key in self._main_cmd_opts:
155
+ args.extend(self._main_cmd_args_for_opt_key(opt_key))
156
+ return args
157
+
158
+ def _main_cmd_args_for_opt_key(self, opt_key: str) -> list[str]:
159
+ builder = self._main_cmd_opt_builders.get(opt_key)
160
+ return builder() if builder else []
152
161
 
153
162
  @override
154
163
  def git_opts_override(self, **overrides: Unpack[GitOpts]) -> Self:
@@ -504,7 +513,9 @@ class GitSession(HasGitUnderneath[GitCommand], AbstractContextManager):
504
513
  self.started_commands[unstarted_command_key] = unstarted_command().__enter__()
505
514
  except Exception:
506
515
  for k in reversed(processes_started_keys):
507
- self.started_commands[k].__exit__(*sys.exc_info())
516
+ _close_session_popen(
517
+ self.started_commands[k], *sys.exc_info()
518
+ )
508
519
  raise
509
520
  processes_started_keys.append(unstarted_command_key)
510
521
  self._commands = SimpleNamespace(**self.started_commands)
@@ -524,7 +535,7 @@ class GitSession(HasGitUnderneath[GitCommand], AbstractContextManager):
524
535
  if self.__depth == 0:
525
536
  process_done_keys: list[str] = []
526
537
  for started_popen_key, started_popen in self.started_commands.items():
527
- started_popen.__exit__(exc_type, exc_value, traceback)
538
+ _close_session_popen(started_popen, exc_type, exc_value, traceback)
528
539
  process_done_keys.append(started_popen_key)
529
540
  self._commands = None
530
541
  for pk in process_done_keys:
@@ -686,7 +697,7 @@ class VersionCommand(Version, GitSubcmdCommand, Protocol):
686
697
 
687
698
  >>> _b_git = _a_git.git_opts_override(C=[Path("a"), Path("b")], no_advice=True, no_replace_objects=True)
688
699
  >>> assert str(_a_git.version_subcmd()) == f"{GIT_CMD} {VERSION_CMD}" # _a_git never changed
689
- >>> assert str(_b_git.version_subcmd()) == f"{GIT_CMD} -C a -C b --no-replace-objects --no-advice {VERSION_CMD}"
700
+ >>> assert str(_b_git.version_subcmd()) == f"{GIT_CMD} -C a -C b --no-advice --no-replace-objects {VERSION_CMD}"
690
701
 
691
702
  Adding git envs:
692
703
 
@@ -695,7 +706,7 @@ class VersionCommand(Version, GitSubcmdCommand, Protocol):
695
706
  >>> assert str(_c_git.version_subcmd()) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} {VERSION_CMD}"
696
707
 
697
708
  >>> _d_git = _c_git.git_opts_override(C=[Path("a"), Path("b")], no_advice=True, no_replace_objects=True, config_env=dict(conf1="val1", glob1="val2"))
698
- >>> assert str(_d_git.version_subcmd()) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} -C a -C b --config-env conf1=val1 --config-env glob1=val2 --no-replace-objects --no-advice {VERSION_CMD}"
709
+ >>> assert str(_d_git.version_subcmd()) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} -C a -C b --no-advice --no-replace-objects --config-env conf1=val1 --config-env glob1=val2 {VERSION_CMD}"
699
710
 
700
711
  Does not affect repr:
701
712
 
@@ -756,7 +767,7 @@ class LsTreeCommand(LsTree, GitSubcmdCommand, Protocol):
756
767
 
757
768
  >>> _b_git = _a_git.git_opts_override(C=[Path("a"), Path("b")], no_advice=True, no_replace_objects=True)
758
769
  >>> assert str(_a_git.ls_tree_subcmd()) == f"{GIT_CMD} {LS_TREE_CMD}" # _a_git never changed
759
- >>> assert str(_b_git.ls_tree_subcmd()) == f"{GIT_CMD} -C a -C b --no-replace-objects --no-advice {LS_TREE_CMD}"
770
+ >>> assert str(_b_git.ls_tree_subcmd()) == f"{GIT_CMD} -C a -C b --no-advice --no-replace-objects {LS_TREE_CMD}"
760
771
 
761
772
  Adding git envs:
762
773
 
@@ -765,7 +776,7 @@ class LsTreeCommand(LsTree, GitSubcmdCommand, Protocol):
765
776
  >>> assert str(_c_git.ls_tree_subcmd()) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} {LS_TREE_CMD}"
766
777
 
767
778
  >>> _d_git = _c_git.git_opts_override(C=[Path("a"), Path("b")], no_advice=True, no_replace_objects=True, config_env=dict(conf1="val1", glob1="val2"))
768
- >>> assert str(_d_git.ls_tree_subcmd()) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} -C a -C b --config-env conf1=val1 --config-env glob1=val2 --no-replace-objects --no-advice {LS_TREE_CMD}"
779
+ >>> assert str(_d_git.ls_tree_subcmd()) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} -C a -C b --no-advice --no-replace-objects --config-env conf1=val1 --config-env glob1=val2 {LS_TREE_CMD}"
769
780
 
770
781
  Does not affect repr:
771
782
 
@@ -871,7 +882,7 @@ class AddCommand(Add, GitSubcmdCommand, Protocol):
871
882
 
872
883
  >>> _b_git = _a_git.git_opts_override(C=[Path("a"), Path("b")], no_advice=True, no_replace_objects=True)
873
884
  >>> assert str(_a_git.add_subcmd()) == f"{GIT_CMD} {ADD_CMD}" # _a_git never changed
874
- >>> assert str(_b_git.add_subcmd()) == f"{GIT_CMD} -C a -C b --no-replace-objects --no-advice {ADD_CMD}"
885
+ >>> assert str(_b_git.add_subcmd()) == f"{GIT_CMD} -C a -C b --no-advice --no-replace-objects {ADD_CMD}"
875
886
 
876
887
  Adding git envs:
877
888
 
@@ -880,7 +891,7 @@ class AddCommand(Add, GitSubcmdCommand, Protocol):
880
891
  >>> assert str(_c_git.add_subcmd()) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} {ADD_CMD}"
881
892
 
882
893
  >>> _d_git = _c_git.git_opts_override(C=[Path("a"), Path("b")], no_advice=True, no_replace_objects=True, config_env=dict(conf1="val1", glob1="val2"))
883
- >>> assert str(_d_git.add_subcmd()) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} -C a -C b --config-env conf1=val1 --config-env glob1=val2 --no-replace-objects --no-advice {ADD_CMD}"
894
+ >>> assert str(_d_git.add_subcmd()) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} -C a -C b --no-advice --no-replace-objects --config-env conf1=val1 --config-env glob1=val2 {ADD_CMD}"
884
895
 
885
896
  Does not affect repr:
886
897
 
@@ -1193,7 +1204,7 @@ class WorktreeCommand(Worktree, GitSubcmdCommand, abc.ABC):
1193
1204
 
1194
1205
  >>> _b_git = _a_git.git_opts_override(C=[Path("a"), Path("b")], no_advice=True, no_replace_objects=True)
1195
1206
  >>> assert str(_a_git.worktree_subcmd()) == f"{GIT_CMD} {WORKTREE_CMD}" # _a_git never changed
1196
- >>> assert str(_b_git.worktree_subcmd()) == f"{GIT_CMD} -C a -C b --no-replace-objects --no-advice {WORKTREE_CMD}"
1207
+ >>> assert str(_b_git.worktree_subcmd()) == f"{GIT_CMD} -C a -C b --no-advice --no-replace-objects {WORKTREE_CMD}"
1197
1208
 
1198
1209
  Adding git envs:
1199
1210
 
@@ -1202,7 +1213,7 @@ class WorktreeCommand(Worktree, GitSubcmdCommand, abc.ABC):
1202
1213
  >>> assert str(_c_git.worktree_subcmd()) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} {WORKTREE_CMD}"
1203
1214
 
1204
1215
  >>> _d_git = _c_git.git_opts_override(C=[Path("a"), Path("b")], no_advice=True, no_replace_objects=True, config_env=dict(conf1="val1", glob1="val2"))
1205
- >>> assert str(_d_git.worktree_subcmd()) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} -C a -C b --config-env conf1=val1 --config-env glob1=val2 --no-replace-objects --no-advice {WORKTREE_CMD}"
1216
+ >>> assert str(_d_git.worktree_subcmd()) == f"GIT_ADVICE=False GIT_AUTHOR_NAME=Suhas GIT_PAGER=vi {GIT_CMD} -C a -C b --no-advice --no-replace-objects --config-env conf1=val1 --config-env glob1=val2 {WORKTREE_CMD}"
1206
1217
 
1207
1218
  Does not affect repr:
1208
1219
 
@@ -8,6 +8,7 @@ Create sessions for long-running commands and communicate with them using their
8
8
 
9
9
  Much faster that subprocess creation for each input/output pair.
10
10
  """
11
+ import errno
11
12
  import subprocess
12
13
  from typing import Iterable, IO, cast
13
14
 
@@ -18,6 +19,25 @@ from gitbolt.subprocess.utils.models import GitRawActor, GitRawSignature, GPGGit
18
19
  RawCommitBytesObj, RawBytesValsOfCommit
19
20
 
20
21
 
22
+ def _close_session_popen(popen: subprocess.Popen[bytes], exc_type, exc_value, traceback) -> None:
23
+ """
24
+ Close a session ``Popen`` and ignore a broken pipe.
25
+
26
+ A git process started with a bad command can exit before the session does. Closing its stdin
27
+ then raises ``BrokenPipeError`` (common on macOS) or a matching ``OSError``.
28
+ """
29
+ try:
30
+ popen.__exit__(exc_type, exc_value, traceback)
31
+ except BrokenPipeError:
32
+ return
33
+ except OSError as err:
34
+ if err.errno in {errno.EPIPE, errno.ECONNRESET}:
35
+ return
36
+ if getattr(err, "winerror", None) in {109, 232}:
37
+ return
38
+ raise
39
+
40
+
21
41
  # region blob
22
42
  def cat_file_blob_content(
23
43
  cat_file_popen: subprocess.Popen[bytes], blob_hash: bytes, require_type: bytes | None = None
@@ -7,6 +7,7 @@ Utility functions related to processors specific to git commands.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ from vt.utils.commons.commons.core_py import is_unset
10
11
 
11
12
  from gitbolt.models import GitOpts, GitEnvVars
12
13
 
@@ -86,7 +87,7 @@ def merge_git_opts(primary: GitOpts, fallback: GitOpts) -> GitOpts:
86
87
  fallbacks on the corresponding property from the ``fallback`` ``GitOpts`` object if that corresponding property
87
88
  is ``None`` in the ``primary`` ``GitOpts`` object.
88
89
  """
89
- return merge_typed_dicts(primary, fallback, GitOpts)
90
+ return merge_typed_dicts(primary, fallback, GitOpts, omit_unset_keys=True)
90
91
 
91
92
 
92
93
  def merge_git_envs(primary: GitEnvVars, fallback: GitEnvVars) -> GitEnvVars:
@@ -164,16 +165,51 @@ def merge_git_envs(primary: GitEnvVars, fallback: GitEnvVars) -> GitEnvVars:
164
165
  fallbacks on the corresponding property from the ``fallback`` ``GitEnvVars`` object if that corresponding property
165
166
  is explicitly ``None`` in the ``primary`` ``GitEnvVars`` object.
166
167
  """
167
- return merge_typed_dicts(primary, fallback, GitEnvVars)
168
+ return merge_typed_dicts(primary, fallback, GitEnvVars, omit_unset_keys=False)
168
169
 
169
170
 
170
171
  # TODO: check for typing this function
171
- def merge_typed_dicts(primary, fallback, the_typed_dict):
172
- merged = {}
173
- for k in the_typed_dict.__annotations__.keys(): # type: ignore
174
- val = primary.get(k) # type: ignore # required as mypy thinks k is not str
172
+ def merge_typed_dicts(
173
+ primary, fallback, the_typed_dict, *, omit_unset_keys: bool = False
174
+ ):
175
+ """
176
+ Merge typed dicts preserving insertion order of ``fallback`` then new keys from ``primary``.
177
+
178
+ Value resolution matches the previous annotation-order merge: primary wins, ``None`` in primary
179
+ falls back, ``Unset`` is kept for env merges and omitted from opts when ``omit_unset_keys`` is True.
180
+ """
181
+ valid_keys = frozenset(the_typed_dict.__annotations__.keys())
182
+
183
+ keys_order: list[str] = []
184
+ seen: set[str] = set()
185
+ for k in fallback:
186
+ if k in valid_keys and k not in seen:
187
+ keys_order.append(k)
188
+ seen.add(k)
189
+ for k in primary:
190
+ if k in valid_keys and k not in seen:
191
+ keys_order.append(k)
192
+ seen.add(k)
193
+
194
+ merged: dict = {}
195
+ for k in keys_order:
196
+ if k in primary and is_unset(primary[k]):
197
+ if omit_unset_keys:
198
+ continue
199
+ merged[k] = primary[k]
200
+ continue
201
+
202
+ if k in primary:
203
+ val = primary[k]
204
+ if val is None:
205
+ val = fallback.get(k)
206
+ else:
207
+ val = fallback.get(k)
208
+
175
209
  if val is None:
176
- val = fallback.get(k) # type: ignore # required as mypy thinks k is not str
177
- if val is not None:
178
- merged[k] = val # type: ignore # required as mypy thinks k is not str
210
+ continue
211
+ if omit_unset_keys and is_unset(val):
212
+ continue
213
+ merged[k] = val
214
+
179
215
  return merged
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitbolt
3
- Version: 0.0.0.dev25
3
+ Version: 0.0.0.dev27
4
4
  Summary: Fast, flexible and type-safe Git commands in Python.
5
- Author-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>
5
+ Author-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>, Thamotharan R <thamotharan.r@vaastav.tech>
6
6
  Maintainer-email: Suhas Krishna Srivastava <suhas.srivastava@vaastav.tech>
7
7
  License-Expression: Apache-2.0
8
8
  Project-URL: homepage, https://github.com/Vaastav-Technologies/py-gitbolt
File without changes
File without changes
File without changes