1414 - [`GitProject.get_revision`](libvcs.git.GitProject.get_revision)
1515 - [`GitProject.get_git_version`](libvcs.git.GitProject.get_git_version)
1616""" # NOQA: E501
17+ import dataclasses
1718import logging
1819import pathlib
1920import re
20- from typing import Dict , NamedTuple , Optional , TypedDict , Union
21+ from typing import Dict , Optional , TypedDict , Union
2122from urllib import parse as urlparse
2223
2324from .. import exc
2627logger = logging .getLogger (__name__ )
2728
2829
29- class GitRemote (NamedTuple ):
30- """Structure containing git repo information.
30+ class GitRemoteDict (TypedDict ):
31+ """For use when hydrating GitProject via dict."""
32+
33+ fetch_url : str
34+ push_url : str
3135
32- Supports `collections.namedtuple._asdict()`
33- """
36+
37+ @dataclasses .dataclass
38+ class GitRemote :
39+ """Structure containing git working copy information."""
3440
3541 name : str
3642 fetch_url : str
3743 push_url : str
3844
45+ def to_dict (self ):
46+ return dataclasses .asdict (self )
47+
48+ def to_tuple (self ):
49+ return dataclasses .astuple (self )
50+
3951
40- class GitStatus (NamedTuple ):
52+ GitProjectRemoteDict = Dict [str , GitRemote ]
53+ GitFullRemoteDict = Dict [str , GitRemoteDict ]
54+ GitRemotesArgs = Union [None , GitFullRemoteDict , Dict [str , str ]]
55+
56+
57+ @dataclasses .dataclass
58+ class GitStatus :
4159 branch_oid : Optional [str ]
4260 branch_head : Optional [str ]
4361 branch_upstream : Optional [str ]
4462 branch_ab : Optional [str ]
4563 branch_ahead : Optional [str ]
4664 branch_behind : Optional [str ]
4765
66+ def to_dict (self ):
67+ return dataclasses .asdict (self )
4868
49- def extract_status ( value ) -> GitStatus :
50- """Returns ``git status -sb --porcelain=2`` extracted to a dict
69+ def to_tuple ( self ) :
70+ return dataclasses . astuple ( self )
5171
52- Returns
53- -------
54- Dictionary of git repo's status
55- """
56- pattern = re .compile (
57- r"""[\n\r]?
58- (
59- #
60- \W+
61- branch.oid\W+
62- (?P<branch_oid>
63- [a-f0-9]{40}
64- )
65- )?
66- (
67- #
68- \W+
69- branch.head
70- [\W]+
71- (?P<branch_head>
72- .*
73- )
72+ @classmethod
73+ def from_stdout (cls , value : str ):
74+ """Returns ``git status -sb --porcelain=2`` extracted to a dict
7475
75- )?
76- (
77- #
78- \W+
79- branch.upstream
80- [\W]+
81- (?P<branch_upstream>
82- .*
83- )
84- )?
85- (
86- #
87- \W+
88- branch.ab
89- [\W]+
90- (?P<branch_ab>
91- \+(?P<branch_ahead>\d+)
92- \W{1}
93- \-(?P<branch_behind>\d+)
94- )
95- )?
96- """ ,
97- re .VERBOSE | re .MULTILINE ,
98- )
99- matches = pattern .search (value )
100- return GitStatus (** matches .groupdict ())
76+ Returns
77+ -------
78+ Dictionary of git repo's status
79+ """
80+ pattern = re .compile (
81+ r"""[\n\r]?
82+ (
83+ #
84+ \W+
85+ branch.oid\W+
86+ (?P<branch_oid>
87+ [a-f0-9]{40}
88+ )
89+ )?
90+ (
91+ #
92+ \W+
93+ branch.head
94+ [\W]+
95+ (?P<branch_head>
96+ .*
97+ )
98+
99+ )?
100+ (
101+ #
102+ \W+
103+ branch.upstream
104+ [\W]+
105+ (?P<branch_upstream>
106+ .*
107+ )
108+ )?
109+ (
110+ #
111+ \W+
112+ branch.ab
113+ [\W]+
114+ (?P<branch_ab>
115+ \+(?P<branch_ahead>\d+)
116+ \W{1}
117+ \-(?P<branch_behind>\d+)
118+ )
119+ )?
120+ """ ,
121+ re .VERBOSE | re .MULTILINE ,
122+ )
123+ matches = pattern .search (value )
124+ return cls (** matches .groupdict ())
101125
102126
103127def convert_pip_url (pip_url : str ) -> VCSLocation :
@@ -125,18 +149,6 @@ def convert_pip_url(pip_url: str) -> VCSLocation:
125149 return VCSLocation (url = url , rev = rev )
126150
127151
128- class GitRemoteDict (TypedDict ):
129- """For use when hydrating GitProject via dict."""
130-
131- fetch_url : str
132- push_url : str
133-
134-
135- GitProjectRemoteDict = Dict [str , GitRemote ]
136- GitFullRemoteDict = Dict [str , GitRemoteDict ]
137- GitRemotesArgs = Union [None , GitFullRemoteDict , Dict [str , str ]]
138-
139-
140152class GitProject (BaseProject ):
141153 bin_name = "git"
142154 schemes = ("git" , "git+http" , "git+https" , "git+ssh" , "git+git" , "git+file" )
@@ -487,7 +499,7 @@ def remotes(self, flat=False) -> Dict:
487499
488500 for remote_name in ret :
489501 remotes [remote_name ] = (
490- self .remote (remote_name ) if flat else self .remote (remote_name )._asdict ()
502+ self .remote (remote_name ) if flat else self .remote (remote_name ).to_dict ()
491503 )
492504 return remotes
493505
@@ -581,7 +593,7 @@ def get_git_version(self) -> str:
581593 version = ""
582594 return "." .join (version .split ("." )[:3 ])
583595
584- def status (self ) -> dict :
596+ def status (self ):
585597 """Retrieve status of project in dict format.
586598
587599 Wraps ``git status --sb --porcelain=2``. Does not include changed files, yet.
@@ -606,7 +618,7 @@ def status(self) -> dict:
606618 branch_behind='0'\
607619 )
608620 """
609- return extract_status (self .run (["status" , "-sb" , "--porcelain=2" ]))
621+ return GitStatus . from_stdout (self .run (["status" , "-sb" , "--porcelain=2" ]))
610622
611623 def get_current_remote_name (self ) -> str :
612624 """Retrieve name of the remote / upstream of currently checked out branch.
0 commit comments