Skip to content

Commit a9b4a89

Browse files
committed
Implement reference parsing
1 parent 584fd73 commit a9b4a89

2 files changed

Lines changed: 84 additions & 37 deletions

File tree

gitolog.py

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,41 +25,71 @@ def bump(version, part='patch'):
2525
return '.'.join((major, minor, patch))
2626

2727

28-
class GitHub:
29-
ISSUE_REF = re.compile(
30-
r'(?P<keyword>(close[sd]?|fix(e[sd])?|resolve[sd]?):? +)?'
31-
r'(?P<ref>([-\w]+/[-\w]+)?#[1-9]\d*)', re.IGNORECASE)
28+
class Ref:
29+
BB = r'(?:^|[\s,])'
30+
BA = r'(?:[\s,]|$)'
31+
NP = r'(?:(?P<namespace>[-\w]+)/)?(?P<project>[-\w]+)'
32+
ID = r'(?P<ref>{symbol}[1-9]\d*)'
33+
ONE_WORD = r'(?P<ref>{symbol}\w[-\w]*)'
34+
MULTI_WORD = r'(?P<ref>{symbol}"\w[- \w]*")'
35+
COMMIT = r'(?P<ref>[0-9a-f]{{{min},{max}}})'
36+
COMMIT_RANGE = r'(?P<ref>[0-9a-f]{{{min},{max}}}\.\.\.[0-9a-f]{{{min},{max}}})'
37+
MENTION = r'(?P<ref>@\w[-\w]*)'
38+
39+
40+
class ProviderRefParser:
41+
REF = {}
3242

3343
@classmethod
34-
def parse_issues_refs(cls, s):
35-
return [m.groupdict() for m in cls.ISSUE_REF.finditer(s)]
36-
37-
38-
class GitLab(GitHub):
39-
ISSUE_REF = re.compile(
40-
r'(?P<keyword>' # start keyword group
41-
r'clos(?:e[sd]?|ing)|'
42-
r'fix(?:e[sd]|ing)?|'
43-
r'resolv(?:e[sd]?|ing)|'
44-
r'implement(?:s|ed|ing)?'
45-
r')' # end keyword group
46-
r':? +' # optional : and spaces
47-
r'(?:' # start repetition of issues sep by "," or "and"
48-
r'(?:issues? +)?' # optional issue[s] word
49-
r'(?P<ref>' # start ref group
50-
r'(?:(?:[-\w]+/)?[-\w]+)' # [namespace/]project
51-
r'?#[1-9]\d*' # number
52-
r')' # end ref group
53-
r'(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+)'
54-
r')+', # end repetition of issues
55-
re.IGNORECASE)
44+
def parse_refs(cls, ref_type, text):
45+
return [m.groupdict() for m in cls.REF[ref_type].finditer(text)]
46+
47+
48+
class GitHub(ProviderRefParser):
49+
REF = dict(
50+
issue=re.compile(Ref.BB + Ref.NP + '?' + Ref.ID.format(symbol='#'), re.I),
51+
commit=re.compile(Ref.BB + r'(?:{np}@)?{commit}{ba}'.format(
52+
np=Ref.NP, commit=Ref.COMMIT.format(min=7, max=40), ba=Ref.BA), re.I),
53+
commit_range=re.compile(Ref.BB + r'(?:{np}@)?{commit_range}'.format(
54+
np=Ref.NP, commit_range=Ref.COMMIT_RANGE.format(min=7, max=40)), re.I),
55+
mention=re.compile(Ref.BB + Ref.MENTION, re.I)
56+
)
57+
58+
59+
class GitLab(ProviderRefParser):
60+
REF = dict(
61+
issue=re.compile(
62+
Ref.BB + Ref.NP + '?' + Ref.ID.format(symbol='#'), re.I),
63+
merge_request=re.compile(
64+
Ref.BB + Ref.NP + '?' + Ref.ID.format(symbol=r'!'), re.I),
65+
snippet=re.compile(
66+
Ref.BB + Ref.NP + '?' + Ref.ID.format(symbol=r'\$'), re.I),
67+
label_id=re.compile(
68+
Ref.BB + Ref.NP + '?' + Ref.ID.format(symbol=r'~'), re.I),
69+
label_one_word=re.compile(
70+
Ref.BB + Ref.NP + '?' + Ref.ONE_WORD.format(symbol=r'~'), re.I),
71+
label_multi_word=re.compile(
72+
Ref.BB + Ref.NP + '?' + Ref.MULTI_WORD.format(symbol=r'~'), re.I),
73+
milestone_id=re.compile(
74+
Ref.BB + Ref.NP + '?' + Ref.ID.format(symbol=r'%'), re.I),
75+
milestone_one_word=re.compile(
76+
Ref.BB + Ref.NP + '?' + Ref.ONE_WORD.format(symbol=r'%'), re.I),
77+
milestone_multi_word=re.compile(
78+
Ref.BB + Ref.NP + '?' + Ref.MULTI_WORD.format(symbol=r'%'), re.I),
79+
commit=re.compile(
80+
Ref.BB + r'(?:{np}@)?{commit}{ba}'.format(
81+
np=Ref.NP, commit=Ref.COMMIT.format(min=8, max=40), ba=Ref.BA), re.I),
82+
commit_range=re.compile(
83+
Ref.BB + r'(?:{np}@)?{commit_range}'.format(
84+
np=Ref.NP, commit_range=Ref.COMMIT_RANGE.format(min=8, max=40)), re.I),
85+
mention=re.compile(Ref.BB + Ref.MENTION, re.I)
86+
)
5687

5788

5889
class Issue:
59-
def __init__(self, number='', url='', closed=False):
90+
def __init__(self, number='', url=''):
6091
self.number = number
6192
self.url = url
62-
self.closed = closed
6393

6494

6595
class Commit:
@@ -106,7 +136,7 @@ class DefaultStyle(Style):
106136
'doc': 'Documented'
107137
}
108138

109-
TYPE_REGEX = re.compile(r'^(?P<type>(%s))' % '|'.join(TYPES.keys()), re.IGNORECASE)
139+
TYPE_REGEX = re.compile(r'^(?P<type>(%s))' % '|'.join(TYPES.keys()), re.I)
110140
CLOSED = ('close', 'fix')
111141
ISSUE_REGEX = re.compile(r'(?P<issues>((%s)\w* )?(#\d+,? ?)+)' % '|'.join(CLOSED))
112142
BREAK_REGEX = re.compile(r'^break(s|ing changes)?[ :].+$')

tests.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import gitolog
22

3-
message = """
3+
text = """
44
This is the subject #1
55
66
This is the body. Related: #2. Also mentions #3 and #4.
@@ -9,21 +9,38 @@
99
Also support other projects references like shellm-org/shellm-data#19!!
1010
Or fix pawamoy/gitolog#1.
1111
Don't match this one: #01.
12+
13+
Now some other references:
14+
15+
A merge request: !153!
16+
A mention: @hello
17+
A commit gitolog@06abf793
18+
A longer commit 3879fabda896da89954adec8
19+
A commit range: 00000000...11111111
20+
A snippet: $45
21+
Some labels: ~18, ~bug, ~"multi word label"
22+
Some milestones: %2, %version1, %"awesome version"
1223
"""
1324

1425

1526
def test_github_issue_parsing():
16-
matches = gitolog.GitHub.parse_issues_refs(message)
17-
for match in matches:
18-
print(match)
27+
for ref in gitolog.GitHub.REF.keys():
28+
print('Searching %s references for GitHub' % ref)
29+
matches = gitolog.GitHub.parse_refs(ref, text)
30+
for match in matches:
31+
print(match)
32+
print('')
1933

2034

2135
def test_gitlab_issue_parsing():
22-
matches = gitolog.GitLab.parse_issues_refs(message)
23-
for match in matches:
24-
print(match)
36+
for ref in gitolog.GitLab.REF.keys():
37+
print('Searching %s references for GitLab' % ref)
38+
matches = gitolog.GitLab.parse_refs(ref, text)
39+
for match in matches:
40+
print(match)
41+
print('')
2542

2643

2744
if __name__ == '__main__':
28-
# test_github_issue_parsing()
45+
test_github_issue_parsing()
2946
test_gitlab_issue_parsing()

0 commit comments

Comments
 (0)