Coverage for python/lsst/resources/utils.py: 46%
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 lsst-resources.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
12from __future__ import annotations
14import contextlib
15import logging
16import os
17import posixpath
18import shutil
19import tempfile
20from pathlib import Path, PurePath, PurePosixPath
21from typing import Optional
23__all__ = ("os2posix", "posix2os", "NoTransaction", "TransactionProtocol")
25from typing import Any, Callable, Iterator, Protocol, Union
27# Determine if the path separator for the OS looks like POSIX
28IS_POSIX = os.sep == posixpath.sep
30# Root path for this operating system. This can use getcwd which
31# can fail in some situations so in the default case assume that
32# posix means posix and only determine explicitly in the non-posix case.
33if IS_POSIX: 33 ↛ 36line 33 didn't jump to line 36, because the condition on line 33 was never false
34 OS_ROOT_PATH = posixpath.sep
35else:
36 OS_ROOT_PATH = Path().resolve().root
38log = logging.getLogger(__name__)
41def os2posix(ospath: str) -> str:
42 """Convert a local path description to a POSIX path description.
44 Parameters
45 ----------
46 ospath : `str`
47 Path using the local path separator.
49 Returns
50 -------
51 posix : `str`
52 Path using POSIX path separator
53 """
54 if IS_POSIX:
55 return ospath
57 posix = PurePath(ospath).as_posix()
59 # PurePath strips trailing "/" from paths such that you can no
60 # longer tell if a path is meant to be referring to a directory
61 # Try to fix this.
62 if ospath.endswith(os.sep) and not posix.endswith(posixpath.sep):
63 posix += posixpath.sep
65 return posix
68def posix2os(posix: Union[PurePath, str]) -> str:
69 """Convert a POSIX path description to a local path description.
71 Parameters
72 ----------
73 posix : `str`, `PurePath`
74 Path using the POSIX path separator.
76 Returns
77 -------
78 ospath : `str`
79 Path using OS path separator
80 """
81 if IS_POSIX:
82 return str(posix)
84 posixPath = PurePosixPath(posix)
85 paths = list(posixPath.parts)
87 # Have to convert the root directory after splitting
88 if paths[0] == posixPath.root:
89 paths[0] = OS_ROOT_PATH
91 # Trailing "/" is stripped so we need to add back an empty path
92 # for consistency
93 if str(posix).endswith(posixpath.sep):
94 paths.append("")
96 return os.path.join(*paths)
99class NoTransaction:
100 """A simple emulation of the `~lsst.daf.butler.DatastoreTransaction` class.
102 Notes
103 -----
104 Does nothing. Used as a fallback in the absence of an explicit transaction
105 class.
106 """
108 def __init__(self) -> None: # noqa: D107
109 return
111 @contextlib.contextmanager
112 def undoWith(self, name: str, undoFunc: Callable, *args: Any, **kwargs: Any) -> Iterator[None]:
113 """No-op context manager to replace `DatastoreTransaction`."""
114 yield None
117class TransactionProtocol(Protocol):
118 """Protocol for type checking transaction interface."""
120 @contextlib.contextmanager
121 def undoWith(self, name: str, undoFunc: Callable, *args: Any, **kwargs: Any) -> Iterator[None]:
122 ...
125def makeTestTempDir(default_base: str) -> str:
126 """Create a temporary directory for test usage.
128 The directory will be created within ``LSST_RESOURCES_TEST_TMP`` if that
129 environment variable is set, falling back to ``default_base`` if it is
130 not.
132 Parameters
133 ----------
134 default_base : `str`
135 Default parent directory.
137 Returns
138 -------
139 dir : `str`
140 Name of the new temporary directory.
141 """
142 base = os.environ.get("LSST_RESOURCES_TEST_TMP", default_base)
143 return tempfile.mkdtemp(dir=base)
146def removeTestTempDir(root: Optional[str]) -> None:
147 """Attempt to remove a temporary test directory, but do not raise if
148 unable to.
150 Unlike `tempfile.TemporaryDirectory`, this passes ``ignore_errors=True``
151 to ``shutil.rmtree`` at close, making it safe to use on NFS.
153 Parameters
154 ----------
155 root : `str`, optional
156 Name of the directory to be removed. If `None`, nothing will be done.
157 """
158 if root is not None and os.path.exists(root):
159 shutil.rmtree(root, ignore_errors=True)