git-project 0.0.27__tar.gz → 0.0.28__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.
- git_project-0.0.28/.#pyproject.toml +1 -0
- git_project-0.0.28/.git +1 -0
- {git_project-0.0.27 → git_project-0.0.28}/PKG-INFO +1 -1
- {git_project-0.0.27 → git_project-0.0.28}/_version.py +9 -4
- git_project-0.0.28/git_project-0.0.28.dev1+g2184a37-py3-none-any.whl +0 -0
- git_project-0.0.28/git_project-0.0.28.dev1+g2184a37.tar.gz +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/substitutable.py +13 -0
- git_project-0.0.28/test.out +131 -0
- {git_project-0.0.27 → git_project-0.0.28}/tests/test_substitutable.py +40 -0
- git_project-0.0.27/.git +0 -1
- git_project-0.0.27/build/lib/git_project/__init__.py +0 -33
- git_project-0.0.27/build/lib/git_project/commandline.py +0 -164
- git_project-0.0.27/build/lib/git_project/configobj.py +0 -300
- git_project-0.0.27/build/lib/git_project/exception.py +0 -30
- git_project-0.0.27/build/lib/git_project/git.py +0 -873
- git_project-0.0.27/build/lib/git_project/gitproject.py +0 -61
- git_project-0.0.27/build/lib/git_project/main.py +0 -63
- git_project-0.0.27/build/lib/git_project/parsermanager.py +0 -360
- git_project-0.0.27/build/lib/git_project/plugin.py +0 -118
- git_project-0.0.27/build/lib/git_project/pluginmanager.py +0 -54
- git_project-0.0.27/build/lib/git_project/project.py +0 -197
- git_project-0.0.27/build/lib/git_project/runnable.py +0 -71
- git_project-0.0.27/build/lib/git_project/scopedobj.py +0 -207
- git_project-0.0.27/build/lib/git_project/shell.py +0 -119
- git_project-0.0.27/build/lib/git_project/substitutable.py +0 -139
- git_project-0.0.27/test.out +0 -18
- {git_project-0.0.27 → git_project-0.0.28}/.gitignore +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/CONTRIBUTORS +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/COPYING +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/MANIFEST.in +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/docs/Makefile +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/docs/authors.rst +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/docs/changelog.rst +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/docs/conf.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/docs/index.rst +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/docs/intro.rst +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/docs/license.rst +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/docs/make.bat +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/pyproject.toml +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/requirements.txt +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/__init__.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/commandline.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/configobj.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/exception.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/git.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/gitproject.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/main.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/parsermanager.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/plugin.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/pluginmanager.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/project.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/runnable.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/scopedobj.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/shell.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/test_support/__init__.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/src/git_project/test_support/common.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/tests/conftest.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/tests/test_confobj.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/tests/test_git.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/tests/test_gitproject.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/tests/test_main.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/tests/test_project.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/tests/test_runnable.py +0 -0
- {git_project-0.0.27 → git_project-0.0.28}/tests/test_scopedobj.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dag@basie.2617:1746908671
|
git_project-0.0.28/.git
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gitdir: /home/dag/src/git-project/.git/worktrees/dev/
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
# file generated by
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
2
|
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
|
|
5
|
+
|
|
3
6
|
TYPE_CHECKING = False
|
|
4
7
|
if TYPE_CHECKING:
|
|
5
|
-
from typing import Tuple
|
|
8
|
+
from typing import Tuple
|
|
9
|
+
from typing import Union
|
|
10
|
+
|
|
6
11
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
12
|
else:
|
|
8
13
|
VERSION_TUPLE = object
|
|
@@ -12,5 +17,5 @@ __version__: str
|
|
|
12
17
|
__version_tuple__: VERSION_TUPLE
|
|
13
18
|
version_tuple: VERSION_TUPLE
|
|
14
19
|
|
|
15
|
-
__version__ = version = '0.0.
|
|
16
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
20
|
+
__version__ = version = '0.0.28'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 0, 28)
|
|
Binary file
|
|
Binary file
|
|
@@ -119,7 +119,17 @@ class SubstitutableConfigObject(ConfigObject):
|
|
|
119
119
|
value = scope.get_ident()
|
|
120
120
|
formats[key] = value
|
|
121
121
|
|
|
122
|
+
# Allow escaping braces by surrounding each brace with braces. This is
|
|
123
|
+
# not valid f-string syntax and is unlikely to be used in ordinary
|
|
124
|
+
# config values. Replace with an equally unlikely sequence, then
|
|
125
|
+
# substitute on that string.
|
|
126
|
+
|
|
127
|
+
escaped_braces = False
|
|
122
128
|
while True:
|
|
129
|
+
escaped_braces = escaped_braces or '{{}' in string or '{}}' in string
|
|
130
|
+
if escaped_braces:
|
|
131
|
+
string = string.replace('{{}', '[[[')
|
|
132
|
+
string = string.replace('{}}', ']]]')
|
|
123
133
|
try:
|
|
124
134
|
newstring = try_format(string, formats)
|
|
125
135
|
except KeyError as exception:
|
|
@@ -140,6 +150,9 @@ class SubstitutableConfigObject(ConfigObject):
|
|
|
140
150
|
changed = False if newstring == string else True
|
|
141
151
|
string = newstring
|
|
142
152
|
if not changed:
|
|
153
|
+
if escaped_braces:
|
|
154
|
+
string = string.replace('[[[', '{')
|
|
155
|
+
string = string.replace(']]]', '}')
|
|
143
156
|
break
|
|
144
157
|
|
|
145
158
|
return string
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
============================= test session starts ==============================
|
|
2
|
+
platform linux -- Python 3.11.9, pytest-8.2.2, pluggy-1.5.0
|
|
3
|
+
rootdir: /home/dag/src/git-project/dev
|
|
4
|
+
configfile: pyproject.toml
|
|
5
|
+
plugins: anyio-4.3.0, console-scripts-1.4.1
|
|
6
|
+
collected 106 items
|
|
7
|
+
|
|
8
|
+
tests/test_confobj.py ............ [ 11%]
|
|
9
|
+
tests/test_git.py .......................FF............................ [ 61%]
|
|
10
|
+
tests/test_gitproject.py .. [ 63%]
|
|
11
|
+
tests/test_main.py . [ 64%]
|
|
12
|
+
tests/test_project.py ........... [ 74%]
|
|
13
|
+
tests/test_runnable.py ........ [ 82%]
|
|
14
|
+
tests/test_scopedobj.py ..... [ 86%]
|
|
15
|
+
tests/test_substitutable.py .............. [100%]
|
|
16
|
+
|
|
17
|
+
=================================== FAILURES ===================================
|
|
18
|
+
________________________________ test_git_clone ________________________________
|
|
19
|
+
|
|
20
|
+
reset_directory = None
|
|
21
|
+
remote_repository = pygit2.Repository('/tmp/pytest-of-dag/pytest-14/local_remote_test_git_clone.git0/')
|
|
22
|
+
tmp_path_factory = TempPathFactory(_given_basetemp=None, _trace=<pluggy._tracing.TagTracerSub object at 0x7ff199ba1e50>, _basetemp=PosixPath('/tmp/pytest-of-dag/pytest-14'), _retention_count=3, _retention_policy='all')
|
|
23
|
+
|
|
24
|
+
def test_git_clone(reset_directory, remote_repository, tmp_path_factory):
|
|
25
|
+
path = tmp_path_factory.mktemp('clone-workdir')
|
|
26
|
+
|
|
27
|
+
os.chdir(path)
|
|
28
|
+
|
|
29
|
+
git = git_project.Git()
|
|
30
|
+
|
|
31
|
+
remote_url = 'file://' + remote_repository.path
|
|
32
|
+
|
|
33
|
+
> clone_path = git.clone(remote_url)
|
|
34
|
+
|
|
35
|
+
/home/dag/src/git-project/dev/tests/test_git.py:441:
|
|
36
|
+
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
|
37
|
+
/home/dag/src/git-project/dev/src/git_project/git.py:744: in clone
|
|
38
|
+
self._repo = pygit2.clone_repository(url, target_path, bare, callbacks=callbacks)
|
|
39
|
+
/home/dag/.virtualenvs/dev/lib/python3.11/site-packages/pygit2/__init__.py:217: in clone_repository
|
|
40
|
+
payload.check_error(err)
|
|
41
|
+
/home/dag/.virtualenvs/dev/lib/python3.11/site-packages/pygit2/callbacks.py:93: in check_error
|
|
42
|
+
raise self._stored_exception
|
|
43
|
+
/home/dag/.virtualenvs/dev/lib/python3.11/site-packages/pygit2/callbacks.py:424: in wrapper
|
|
44
|
+
return f(*args)
|
|
45
|
+
/home/dag/.virtualenvs/dev/lib/python3.11/site-packages/pygit2/callbacks.py:552: in _transfer_progress_cb
|
|
46
|
+
transfer_progress(TransferProgress(stats_ptr))
|
|
47
|
+
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
|
48
|
+
|
|
49
|
+
self = <git_project.git.Git.RemoteCallbacks object at 0x7ff199a06950>
|
|
50
|
+
stats = <pygit2.remotes.TransferProgress object at 0x7ff199a9f790>
|
|
51
|
+
|
|
52
|
+
def transfer_progress(self, stats):
|
|
53
|
+
if not self.started_transfer:
|
|
54
|
+
self.started_transfer = True
|
|
55
|
+
print(f'Receiving objects ({stats.total_objects})...')
|
|
56
|
+
> self.progress.start(max_value=stats.total_objects)
|
|
57
|
+
E TypeError: ProgressBar.start() got an unexpected keyword argument 'max_value'
|
|
58
|
+
|
|
59
|
+
/home/dag/src/git-project/dev/src/git_project/git.py:77: TypeError
|
|
60
|
+
----------------------------- Captured stdout call -----------------------------
|
|
61
|
+
Remote: Counting objects 1
|
|
62
|
+
Remote: Counting objects 9
|
|
63
|
+
Remote: Counting objects 14
|
|
64
|
+
Remote: Counting objects 14
|
|
65
|
+
|
|
66
|
+
Remote: Compressing objects: 0% (0/14)
|
|
67
|
+
Remote: Compressing objects: 100% (14/14), done
|
|
68
|
+
|
|
69
|
+
Receiving objects (14)...
|
|
70
|
+
___________________________ test_git_clone_with_path ___________________________
|
|
71
|
+
|
|
72
|
+
reset_directory = None
|
|
73
|
+
remote_repository = pygit2.Repository('/tmp/pytest-of-dag/pytest-14/local_remote_test_git_clone_with_path.git0/')
|
|
74
|
+
tmp_path_factory = TempPathFactory(_given_basetemp=None, _trace=<pluggy._tracing.TagTracerSub object at 0x7ff199ba1e50>, _basetemp=PosixPath('/tmp/pytest-of-dag/pytest-14'), _retention_count=3, _retention_policy='all')
|
|
75
|
+
|
|
76
|
+
def test_git_clone_with_path(reset_directory,
|
|
77
|
+
remote_repository,
|
|
78
|
+
tmp_path_factory):
|
|
79
|
+
path = tmp_path_factory.mktemp('clone-workdir')
|
|
80
|
+
os.chdir(path)
|
|
81
|
+
|
|
82
|
+
git = git_project.Git()
|
|
83
|
+
|
|
84
|
+
remote_url = 'file://' + remote_repository.path
|
|
85
|
+
|
|
86
|
+
path = Path.cwd() / 'foo' / 'bar'
|
|
87
|
+
|
|
88
|
+
os.makedirs(path)
|
|
89
|
+
|
|
90
|
+
path = path / 'test-clone'
|
|
91
|
+
|
|
92
|
+
> clone_path = git.clone(remote_url, path)
|
|
93
|
+
|
|
94
|
+
/home/dag/src/git-project/dev/tests/test_git.py:482:
|
|
95
|
+
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
|
96
|
+
/home/dag/src/git-project/dev/src/git_project/git.py:744: in clone
|
|
97
|
+
self._repo = pygit2.clone_repository(url, target_path, bare, callbacks=callbacks)
|
|
98
|
+
/home/dag/.virtualenvs/dev/lib/python3.11/site-packages/pygit2/__init__.py:217: in clone_repository
|
|
99
|
+
payload.check_error(err)
|
|
100
|
+
/home/dag/.virtualenvs/dev/lib/python3.11/site-packages/pygit2/callbacks.py:93: in check_error
|
|
101
|
+
raise self._stored_exception
|
|
102
|
+
/home/dag/.virtualenvs/dev/lib/python3.11/site-packages/pygit2/callbacks.py:424: in wrapper
|
|
103
|
+
return f(*args)
|
|
104
|
+
/home/dag/.virtualenvs/dev/lib/python3.11/site-packages/pygit2/callbacks.py:552: in _transfer_progress_cb
|
|
105
|
+
transfer_progress(TransferProgress(stats_ptr))
|
|
106
|
+
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
|
107
|
+
|
|
108
|
+
self = <git_project.git.Git.RemoteCallbacks object at 0x7ff198a8e350>
|
|
109
|
+
stats = <pygit2.remotes.TransferProgress object at 0x7ff198a8eb10>
|
|
110
|
+
|
|
111
|
+
def transfer_progress(self, stats):
|
|
112
|
+
if not self.started_transfer:
|
|
113
|
+
self.started_transfer = True
|
|
114
|
+
print(f'Receiving objects ({stats.total_objects})...')
|
|
115
|
+
> self.progress.start(max_value=stats.total_objects)
|
|
116
|
+
E TypeError: ProgressBar.start() got an unexpected keyword argument 'max_value'
|
|
117
|
+
|
|
118
|
+
/home/dag/src/git-project/dev/src/git_project/git.py:77: TypeError
|
|
119
|
+
----------------------------- Captured stdout call -----------------------------
|
|
120
|
+
Remote: Counting objects 1
|
|
121
|
+
Remote: Counting objects 14
|
|
122
|
+
Remote: Counting objects 14
|
|
123
|
+
|
|
124
|
+
Remote: Compressing objects: 0% (0/14)
|
|
125
|
+
Remote: Compressing objects: 100% (14/14), done
|
|
126
|
+
|
|
127
|
+
Receiving objects (14)...
|
|
128
|
+
=========================== short test summary info ============================
|
|
129
|
+
FAILED tests/test_git.py::test_git_clone - TypeError: ProgressBar.start() got...
|
|
130
|
+
FAILED tests/test_git.py::test_git_clone_with_path - TypeError: ProgressBar.s...
|
|
131
|
+
======================== 2 failed, 104 passed in 2.74s =========================
|
|
@@ -502,3 +502,43 @@ def test_substitutable_substitute_git_workdir(reset_directory, git):
|
|
|
502
502
|
substitutable.command)
|
|
503
503
|
|
|
504
504
|
assert command == f'cd {git.get_working_copy_root()}/path/to/build/{git.get_current_branch()} && make {project.target}'
|
|
505
|
+
|
|
506
|
+
def test_substitutable_substitute_escaped_braces(reset_directory, git):
|
|
507
|
+
class MyProject(git_project.ScopedConfigObject):
|
|
508
|
+
def __init__(self):
|
|
509
|
+
super().__init__(git,
|
|
510
|
+
'project',
|
|
511
|
+
None,
|
|
512
|
+
'myproject',
|
|
513
|
+
builddir='{git_workdir}/{{}path{}}/to/build',
|
|
514
|
+
target='debug')
|
|
515
|
+
|
|
516
|
+
substitutable = MySubstitutable.get(git, 'project', 'test')
|
|
517
|
+
|
|
518
|
+
project = MyProject()
|
|
519
|
+
|
|
520
|
+
command = substitutable.substitute_value(git,
|
|
521
|
+
project,
|
|
522
|
+
substitutable.command)
|
|
523
|
+
|
|
524
|
+
assert command == f'cd {git.get_working_copy_root()}/{{path}}/to/build/{git.get_current_branch()} && make {project.target}'
|
|
525
|
+
|
|
526
|
+
def test_substitutable_substitute_preserve_escaped_braces(reset_directory, git):
|
|
527
|
+
class MyProject(git_project.ScopedConfigObject):
|
|
528
|
+
def __init__(self):
|
|
529
|
+
super().__init__(git,
|
|
530
|
+
'project',
|
|
531
|
+
None,
|
|
532
|
+
'myproject',
|
|
533
|
+
builddir='{git_workdir}/{{}{{}{}}path{{}{}}{}}/to/build',
|
|
534
|
+
target='debug')
|
|
535
|
+
|
|
536
|
+
substitutable = MySubstitutable.get(git, 'project', 'test')
|
|
537
|
+
|
|
538
|
+
project = MyProject()
|
|
539
|
+
|
|
540
|
+
command = substitutable.substitute_value(git,
|
|
541
|
+
project,
|
|
542
|
+
substitutable.command)
|
|
543
|
+
|
|
544
|
+
assert command == f'cd {git.get_working_copy_root()}/{{{{}}path{{}}}}/to/build/{git.get_current_branch()} && make {project.target}'
|
git_project-0.0.27/.git
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
gitdir: /Users/dag/projects/git-project/.git/worktrees/master/
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
#
|
|
3
|
-
# Copyright 2020 David A. Greene
|
|
4
|
-
#
|
|
5
|
-
# This program is free software: you can redistribute it and/or modify it under
|
|
6
|
-
# the terms of the GNU General Public License as published by the Free Software
|
|
7
|
-
# Foundation, either version 3 of the License, or (at your option) any later
|
|
8
|
-
# version.
|
|
9
|
-
#
|
|
10
|
-
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
11
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
12
|
-
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
13
|
-
# details.
|
|
14
|
-
#
|
|
15
|
-
# You should have received a copy of the GNU General Public License along with
|
|
16
|
-
# this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
from .commandline import parse_arguments, add_top_level_command
|
|
20
|
-
from .commandline import get_or_add_top_level_command
|
|
21
|
-
from .configobj import ConfigObject
|
|
22
|
-
from .exception import GitProjectException
|
|
23
|
-
from .git import Git
|
|
24
|
-
from .gitproject import GitProject
|
|
25
|
-
from .main import main_impl
|
|
26
|
-
from .parsermanager import ParserManager
|
|
27
|
-
from .plugin import Plugin
|
|
28
|
-
from .pluginmanager import PluginManager
|
|
29
|
-
from .project import Project
|
|
30
|
-
from .runnable import RunnableConfigObject
|
|
31
|
-
from .scopedobj import ScopedConfigObject
|
|
32
|
-
from .substitutable import SubstitutableConfigObject
|
|
33
|
-
from .shell import run_command_with_shell, iter_command, capture_command
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
#
|
|
3
|
-
# Copyright 2020 David A. Greene
|
|
4
|
-
#
|
|
5
|
-
# This program is free software: you can redistribute it and/or modify it under
|
|
6
|
-
# the terms of the GNU General Public License as published by the Free Software
|
|
7
|
-
# Foundation, either version 3 of the License, or (at your option) any later
|
|
8
|
-
# version.
|
|
9
|
-
#
|
|
10
|
-
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
11
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
12
|
-
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
13
|
-
# details.
|
|
14
|
-
#
|
|
15
|
-
# You should have received a copy of the GNU General Public License along with
|
|
16
|
-
# this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
#
|
|
18
|
-
"""git-project: Manage project work.
|
|
19
|
-
|
|
20
|
-
Summary:
|
|
21
|
-
|
|
22
|
-
git project <command> [<options>]
|
|
23
|
-
|
|
24
|
-
Commands are registered by plugins and show with 'git project --help.' Each
|
|
25
|
-
command also has a --help option to explore workings of th command.
|
|
26
|
-
|
|
27
|
-
Users may link to git-project with a name appropriate to the project (for
|
|
28
|
-
example, git-fizzbin to invoke as 'git fizzbin <command> [<options>]).
|
|
29
|
-
git-project will then place all configuration in a section named after the link
|
|
30
|
-
('fizzbin' in this case). This way one may have multiple projects active in a
|
|
31
|
-
single repository by invoking git-project via various links.
|
|
32
|
-
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
import argparse
|
|
36
|
-
import getpass
|
|
37
|
-
import io
|
|
38
|
-
import os
|
|
39
|
-
from pathlib import Path
|
|
40
|
-
import pygit2
|
|
41
|
-
import re
|
|
42
|
-
import shlex
|
|
43
|
-
import subprocess
|
|
44
|
-
import sys
|
|
45
|
-
import urllib.parse
|
|
46
|
-
|
|
47
|
-
try:
|
|
48
|
-
from importlib import metadata
|
|
49
|
-
except ImportError:
|
|
50
|
-
# Running on pre-3.8 Python; use importlib-metadata package
|
|
51
|
-
import importlib_metadata as metadata
|
|
52
|
-
|
|
53
|
-
from .parsermanager import ParserManager
|
|
54
|
-
|
|
55
|
-
def get_version_string(package):
|
|
56
|
-
version = metadata.version(package)
|
|
57
|
-
|
|
58
|
-
return f'{package} version {version}'
|
|
59
|
-
|
|
60
|
-
def add_version_argument(parser, package):
|
|
61
|
-
parser.add_argument('--version',
|
|
62
|
-
action='version',
|
|
63
|
-
version=get_version_string(package),
|
|
64
|
-
help='Print version')
|
|
65
|
-
|
|
66
|
-
def parse_arguments(git, gitproject, project, plugin_manager, args):
|
|
67
|
-
"""Register plugin comand-line arguments. Parse command-link arguments and
|
|
68
|
-
invoke command functions.
|
|
69
|
-
|
|
70
|
-
git: Git object to examine the repository.
|
|
71
|
-
|
|
72
|
-
gitproject: GitProject object to provide cross-project configuration.
|
|
73
|
-
|
|
74
|
-
project: Project object to provide project-specific configuration.
|
|
75
|
-
|
|
76
|
-
plugin_manager: A manager holding available plugins.
|
|
77
|
-
|
|
78
|
-
"""
|
|
79
|
-
parser_manager = ParserManager(gitproject, project)
|
|
80
|
-
|
|
81
|
-
parser = parser_manager.find_parser('__main__')
|
|
82
|
-
|
|
83
|
-
# --version
|
|
84
|
-
add_version_argument(parser, 'git-project')
|
|
85
|
-
|
|
86
|
-
command_subparser = parser_manager.add_subparser(parser,
|
|
87
|
-
'command',
|
|
88
|
-
dest='command',
|
|
89
|
-
help='commands')
|
|
90
|
-
command_subparser.required = True
|
|
91
|
-
|
|
92
|
-
for plugin in plugin_manager.iterplugins():
|
|
93
|
-
plugin.add_arguments(git,
|
|
94
|
-
gitproject,
|
|
95
|
-
project,
|
|
96
|
-
parser_manager,
|
|
97
|
-
plugin_manager)
|
|
98
|
-
|
|
99
|
-
# Once all plugins have added arguments, give them a chance to modify
|
|
100
|
-
# arguments other plugsin may have added.
|
|
101
|
-
for plugin in plugin_manager.iterplugins():
|
|
102
|
-
plugin.modify_arguments(git,
|
|
103
|
-
gitproject,
|
|
104
|
-
project,
|
|
105
|
-
parser_manager,
|
|
106
|
-
plugin_manager)
|
|
107
|
-
|
|
108
|
-
clargs = parser_manager.parse_args(args)
|
|
109
|
-
|
|
110
|
-
if not hasattr(clargs, 'func'):
|
|
111
|
-
parser_manager.error()
|
|
112
|
-
sys.exit(1)
|
|
113
|
-
|
|
114
|
-
return clargs
|
|
115
|
-
|
|
116
|
-
def add_top_level_command(parser_manager,
|
|
117
|
-
name,
|
|
118
|
-
key,
|
|
119
|
-
**kwargs):
|
|
120
|
-
"""Create a top-level command. Throw an exception if the command already
|
|
121
|
-
exists.
|
|
122
|
-
|
|
123
|
-
parser_manager - The active ParserManager.
|
|
124
|
-
|
|
125
|
-
name - The name of the command to be invoked.
|
|
126
|
-
|
|
127
|
-
key - A unique identifier for the created parser.
|
|
128
|
-
|
|
129
|
-
kwargs - Passthrough to argparse parser.
|
|
130
|
-
|
|
131
|
-
"""
|
|
132
|
-
|
|
133
|
-
command_subparser = parser_manager.find_subparser('command')
|
|
134
|
-
assert command_subparser
|
|
135
|
-
parser = parser_manager.add_parser(command_subparser,
|
|
136
|
-
name,
|
|
137
|
-
key,
|
|
138
|
-
**kwargs)
|
|
139
|
-
|
|
140
|
-
return parser
|
|
141
|
-
|
|
142
|
-
def get_or_add_top_level_command(parser_manager,
|
|
143
|
-
name,
|
|
144
|
-
key,
|
|
145
|
-
**kwargs):
|
|
146
|
-
"""Retrieve a top-level command parser or create one if none exists.
|
|
147
|
-
|
|
148
|
-
parser_manager - The active ParserManager.
|
|
149
|
-
|
|
150
|
-
name - The name of the command to be invoked.
|
|
151
|
-
|
|
152
|
-
key - A unique identifier for the created parser.
|
|
153
|
-
|
|
154
|
-
kwargs - Passthrough to argparse parser.
|
|
155
|
-
|
|
156
|
-
"""
|
|
157
|
-
|
|
158
|
-
parser = parser_manager.find_parser(key)
|
|
159
|
-
if not parser:
|
|
160
|
-
parser = add_top_level_command(parser_manager,
|
|
161
|
-
name,
|
|
162
|
-
key,
|
|
163
|
-
**kwargs)
|
|
164
|
-
return parser
|