rtutil: Add RTUtil.metadata_regexp() classmethod.
The accruals check script wants to be able to search RT links in all kinds of metadata, not just rt-id as the filter currently handles.
This commit is contained in:
		
							parent
							
								
									d41bc5e9b6
								
							
						
					
					
						commit
						d8507a1a35
					
				
					 4 changed files with 55 additions and 3 deletions
				
			
		| 
						 | 
					@ -17,6 +17,7 @@
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from . import data
 | 
					from . import data
 | 
				
			||||||
 | 
					from . import rtutil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from typing import (
 | 
					from typing import (
 | 
				
			||||||
    Iterable,
 | 
					    Iterable,
 | 
				
			||||||
| 
						 | 
					@ -53,5 +54,5 @@ def filter_for_rt_id(postings: Postings, ticket_id: Union[int, str]) -> Postings
 | 
				
			||||||
    This functions yields postings where the *first* rt-id matches the given
 | 
					    This functions yields postings where the *first* rt-id matches the given
 | 
				
			||||||
    ticket number.
 | 
					    ticket number.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    regexp = r'^\s*rt:(?://ticket/)?{}\b'.format(re.escape(str(ticket_id)))
 | 
					    regexp = rtutil.RT.metadata_regexp(ticket_id, first_link_only=True)
 | 
				
			||||||
    return filter_meta_match(postings, 'rt-id', regexp)
 | 
					    return filter_meta_match(postings, 'rt-id', regexp)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -266,6 +266,35 @@ class RT:
 | 
				
			||||||
    def exists(self, ticket_id: RTId, attachment_id: Optional[RTId]=None) -> bool:
 | 
					    def exists(self, ticket_id: RTId, attachment_id: Optional[RTId]=None) -> bool:
 | 
				
			||||||
        return self.url(ticket_id, attachment_id) is not None
 | 
					        return self.url(ticket_id, attachment_id) is not None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def metadata_regexp(self,
 | 
				
			||||||
 | 
					                        ticket_id: RTId,
 | 
				
			||||||
 | 
					                        attachment_id: Optional[RTId]=None,
 | 
				
			||||||
 | 
					                        *,
 | 
				
			||||||
 | 
					                        first_link_only: bool=False
 | 
				
			||||||
 | 
					    ) -> str:
 | 
				
			||||||
 | 
					        """Return a pattern to find RT links in metadata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Given a ticket ID and optional attachment ID, this method returns a
 | 
				
			||||||
 | 
					        regular expression pattern that will find matching RT links in a
 | 
				
			||||||
 | 
					        metadata value string, written in any format.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If the keyword-only argument first_link_only is true, the pattern will
 | 
				
			||||||
 | 
					        only match the first link in a metadata string. Otherwise the pattern
 | 
				
			||||||
 | 
					        matches any link in the string (the default).
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if first_link_only:
 | 
				
			||||||
 | 
					            prolog = r'^\s*'
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            prolog = r'(?:^|\s)'
 | 
				
			||||||
 | 
					        if attachment_id is None:
 | 
				
			||||||
 | 
					            attachment = ''
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            attachment = r'/(?:attachments?/)?{}'.format(attachment_id)
 | 
				
			||||||
 | 
					        ticket = r'rt:(?://ticket/)?{}'.format(ticket_id)
 | 
				
			||||||
 | 
					        epilog = r'/?(?:$|\s)'
 | 
				
			||||||
 | 
					        return f'{prolog}{ticket}{attachment}{epilog}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def parse(cls, s: str) -> Optional[Tuple[str, Optional[str]]]:
 | 
					    def parse(cls, s: str) -> Optional[Tuple[str, Optional[str]]]:
 | 
				
			||||||
        for regexp in cls.PARSE_REGEXPS:
 | 
					        for regexp in cls.PARSE_REGEXPS:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,11 +111,9 @@ def test_filter_for_rt_id(cc_txn_pair, ticket_id, expected_indexes):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize('rt_id', [
 | 
					@pytest.mark.parametrize('rt_id', [
 | 
				
			||||||
    'rt:450/',
 | 
					    'rt:450/',
 | 
				
			||||||
    'rt:450/678',
 | 
					 | 
				
			||||||
    ' rt:450 rt:540',
 | 
					    ' rt:450 rt:540',
 | 
				
			||||||
    'rt://ticket/450',
 | 
					    'rt://ticket/450',
 | 
				
			||||||
    'rt://ticket/450/',
 | 
					    'rt://ticket/450/',
 | 
				
			||||||
    'rt://ticket/450/678',
 | 
					 | 
				
			||||||
    ' rt://ticket/450',
 | 
					    ' rt://ticket/450',
 | 
				
			||||||
    'rt://ticket/450 rt://ticket/540',
 | 
					    'rt://ticket/450 rt://ticket/540',
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,8 @@
 | 
				
			||||||
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
					# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import contextlib
 | 
					import contextlib
 | 
				
			||||||
 | 
					import itertools
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,6 +64,28 @@ def test_url(rt, ticket_id, attachment_id, expected):
 | 
				
			||||||
        expected = DEFAULT_RT_URL + expected
 | 
					        expected = DEFAULT_RT_URL + expected
 | 
				
			||||||
    assert rt.url(ticket_id, attachment_id) == expected
 | 
					    assert rt.url(ticket_id, attachment_id) == expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.mark.parametrize('attachment_id,first_link_only', itertools.product(
 | 
				
			||||||
 | 
					    [245, None],
 | 
				
			||||||
 | 
					    [True, False],
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					def test_metadata_regexp(rt, attachment_id, first_link_only):
 | 
				
			||||||
 | 
					    if attachment_id is None:
 | 
				
			||||||
 | 
					        match_links = ['rt:220', 'rt://ticket/220']
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        match_links = [f'rt:220/{attachment_id}',
 | 
				
			||||||
 | 
					                       f'rt://ticket/220/attachments/{attachment_id}']
 | 
				
			||||||
 | 
					    regexp = rt.metadata_regexp(220, attachment_id, first_link_only=first_link_only)
 | 
				
			||||||
 | 
					    for link in match_links:
 | 
				
			||||||
 | 
					        assert re.search(regexp, link)
 | 
				
			||||||
 | 
					        assert re.search(regexp, link + ' link2')
 | 
				
			||||||
 | 
					        assert re.search(regexp, link + '0') is None
 | 
				
			||||||
 | 
					        assert re.search(regexp, 'a' + link) is None
 | 
				
			||||||
 | 
					        end_match = re.search(regexp, 'link0 ' + link)
 | 
				
			||||||
 | 
					        if first_link_only:
 | 
				
			||||||
 | 
					            assert end_match is None
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            assert end_match
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize('attachment_id', [
 | 
					@pytest.mark.parametrize('attachment_id', [
 | 
				
			||||||
    13,
 | 
					    13,
 | 
				
			||||||
    None,
 | 
					    None,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue