Coverage for python/lsst/daf/butler/core/utils.py: 38%
Shortcuts 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
Shortcuts 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
23__all__ = (
24 "stripIfNotNone",
25 "transactional",
26)
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)
43from lsst.utils.iteration import ensure_iterable
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
49_LOG = logging.getLogger(__name__)
52F = TypeVar("F", bound=Callable)
55def transactional(func: F) -> F:
56 """Decorate a method and makes it transactional.
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
68def stripIfNotNone(s: Optional[str]) -> Optional[str]:
69 """Strip leading and trailing whitespace if the given object is not None.
71 Parameters
72 ----------
73 s : `str`, optional
74 Input string.
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
87def globToRegex(expressions: Union[str, EllipsisType, None,
88 List[str]]) -> Union[List[Union[str, Pattern]], EllipsisType]:
89 """Translate glob-style search terms to regex.
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.
95 Parameters
96 ----------
97 expressions : `str` or `list` [`str`]
98 A list of glob-style pattern strings to convert.
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
112 nomagic = re.compile(r"^[\w/\.\-]+$", re.ASCII)
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