Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of daf_butler. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21from __future__ import annotations 

22 

23__all__ = ( 

24 "stripIfNotNone", 

25 "transactional", 

26) 

27 

28import fnmatch 

29import functools 

30import logging 

31import re 

32from typing import ( 

33 Any, 

34 Callable, 

35 List, 

36 Optional, 

37 Pattern, 

38 TypeVar, 

39 TYPE_CHECKING, 

40 Union, 

41) 

42 

43from lsst.utils.iteration import ensure_iterable 

44 

45if TYPE_CHECKING: 45 ↛ 46line 45 didn't jump to line 46, because the condition on line 45 was never true

46 from ..registry.wildcards import Ellipsis, EllipsisType 

47 

48 

49_LOG = logging.getLogger(__name__) 

50 

51 

52F = TypeVar("F", bound=Callable) 

53 

54 

55def transactional(func: F) -> F: 

56 """Decorate a method and makes it transactional. 

57 

58 This depends on the class also defining a `transaction` method 

59 that takes no arguments and acts as a context manager. 

60 """ 

61 @functools.wraps(func) 

62 def inner(self: Any, *args: Any, **kwargs: Any) -> Any: 

63 with self.transaction(): 

64 return func(self, *args, **kwargs) 

65 return inner # type: ignore 

66 

67 

68def stripIfNotNone(s: Optional[str]) -> Optional[str]: 

69 """Strip leading and trailing whitespace if the given object is not None. 

70 

71 Parameters 

72 ---------- 

73 s : `str`, optional 

74 Input string. 

75 

76 Returns 

77 ------- 

78 r : `str` or `None` 

79 A string with leading and trailing whitespace stripped if `s` is not 

80 `None`, or `None` if `s` is `None`. 

81 """ 

82 if s is not None: 

83 s = s.strip() 

84 return s 

85 

86 

87def globToRegex(expressions: Union[str, EllipsisType, None, 

88 List[str]]) -> Union[List[Union[str, Pattern]], EllipsisType]: 

89 """Translate glob-style search terms to regex. 

90 

91 If a stand-alone '``*``' is found in ``expressions``, or expressions is 

92 empty or `None`, then the special value ``...`` will be returned, 

93 indicating that any string will match. 

94 

95 Parameters 

96 ---------- 

97 expressions : `str` or `list` [`str`] 

98 A list of glob-style pattern strings to convert. 

99 

100 Returns 

101 ------- 

102 expressions : `list` [`str` or `re.Pattern`] or ``...`` 

103 A list of regex Patterns or simple strings. Returns ``...`` if 

104 the provided expressions would match everything. 

105 """ 

106 if expressions is Ellipsis or expressions is None: 

107 return Ellipsis 

108 expressions = list(ensure_iterable(expressions)) 

109 if not expressions or "*" in expressions: 

110 return Ellipsis 

111 

112 nomagic = re.compile(r"^[\w/\.\-]+$", re.ASCII) 

113 

114 # Try not to convert simple string to a regex. 

115 results: List[Union[str, Pattern]] = [] 

116 for e in expressions: 

117 res: Union[str, Pattern] 

118 if nomagic.match(e): 

119 res = e 

120 else: 

121 res = re.compile(fnmatch.translate(e)) 

122 results.append(res) 

123 return results