5 from pydantic
import BaseModel, PrivateAttr, validator
6 from typing
import Optional
12 """Resource on the Internet, locally cached.
14 # Remote Resource mechanism
16 Most of the parametrizations Lamarr relies on are committed and maintained in
17 remote repositories. The PyLamarr.RemoteResource class implements a simple
18 caching mechanism to download the remote parametrizations on demand in case they
19 are not available locally. A hash of the URL identifying the remote resource is
20 used to represent the local cache of the remote resource.
21 Note that in case the remote resource is updated without modifying its URL,
22 the local cache is not automatically updated.
26 Consider the file `PrimaryVertexSmearing.db` encoding the parametrizations for
27 Primary Vertex reconstruction, and made available publicly here:
28 `https://github.com/LamarrSim/SQLamarr/raw/master/temporary_data/PrimaryVertex/PrimaryVertexSmearing.db`
30 The following snippet of code enables caching the file locally:
32 from PyLamarr import RemoteResource
33 url = ("https://github.com/LamarrSim/SQLamarr/raw/master/temporary_data/"
34 "PrimaryVertex/PrimaryVertexSmearing.db")
36 pv_params = RemoteResource(url)
38 # Now the file might not be available locally, but a lazy download is triggered
39 # when accessing its path:
42 with sqlite3.connect(pv_params.file) as db:
43 # ... do something ...
46 Now, in case the remote file is updated, it may be necessary to download the
47 updated version. This can be achieved forcing the download with:
49 pv_params.download(force=True)
52 or, replacing the connection attempt in the previous example:
55 with sqlite3.connect(pv_params.download(force=True).file) as db:
56 # ... do something ...
60 ### Accessing local resources
61 A local resourcce can be encapsulated inside `RemoteResource` which is
62 the expected format for most of the parametrization data in `PyLamarr`.
64 For example, if testing your local version of `MyPrimaryVertexSmearing.db`,
67 pv_params = RemoteResource("file://MyPrimaryVertexSmearing.db")
69 # Now the file might not be available locally, but a lazy download is triggered
70 # when accessing its path:
73 with sqlite3.connect(pv_params.file) as db:
77 Note, however, that forcing the download of a local resource would raise an
81 ### Implicit conversion from URL
82 Most of the parametrizations relying on external dependencies expect an
83 instance of `RemoteResource` identifying the file to obtain the parametrization
84 from. An implicit cast from sring to `RemoteResource` enables passing directly
85 a string with a URL (possibly pointing to a local file), which gets
86 transparently converted into a `RemoteResource` instance and used in the file.
89 local_filename: Optional[str] =
None
90 _file: Optional[str] = PrivateAttr()
92 def __init__ (self, *args, **kwargs):
93 """@private Constructor performing input validation"""
94 remote_url, *args = args
95 local_filename, *args = args
if len(args)
else None,
96 super().__init__(remote_url=remote_url, local_filename=local_filename)
99 file_protocols = (
'file://',)
100 requests_protocols = (
'https://',
'http://')
102 if any([self.remote_url.startswith(p)
for p
in file_protocols]):
103 self.
_file_file = remote_url[7:]
104 if self.local_filename
is not None:
105 raise NotImplementedError(
"Copy from local file system unavailable")
107 elif any([self.remote_url.startswith(p)
for p
in requests_protocols]):
108 if self.local_filename
is not None:
109 self.
_file_file = self.local_filename
112 h.update(self.remote_url.encode(
'utf-8'))
113 self.
_file_file = f
"/tmp/lamarr.resource.{h.hexdigest()[:16]}"
116 raise NotImplementedError(
117 f
"Protocol for {self.remote_url} unknown or not supported. "
118 f
"Supported protocols: {', '.join(file_protocols+requests_protocols)}"
123 def __get_validators__ (cls):
124 """@private Get validators for implicit casting from URL by pydantic"""
128 def validate (cls, v):
129 """@private Implement implicit casting"""
130 if isinstance(v, RemoteResource):
131 return RemoteResource
132 elif isinstance(v, str):
134 elif isinstance(v, dict):
135 if 'remote_url' in v.keys()
and 'local_filename' in v.keys():
137 remote_url=v[
'remote_url'],
138 local_filename=v[
'local_filename']
141 raise ValueError(f
"Unexpected initializer {v} for RemoteResource")
144 """Download the remote resource is not available locally or if forced.
145 Can raise an exception if the download fails or upon attempts of downloading
146 local resources (represented by protocol `file://`)
148 @param force: Force the download of the remote resource independently of
149 the cache availability
151 @return Updated instance of `RemoteResource` (`self`)
154 if os.path.exists(self.
_file_file)
and not force:
157 logger = logging.getLogger(self.__class__.__name__)
158 logger.info(f
"Downloading {self.remote_url} to {self._file}")
160 res = requests.get(self.remote_url, allow_redirects=
True)
161 res.raise_for_status()
163 with open(self.
_file_file,
'wb')
as f:
170 """@property Access the local file **path** downloading it if necessary."""
172 logger = logging.getLogger(self.__class__.__name__)
173 logger.debug(f
"Accessing {self._file} as cached version of {self.remote_url}")
174 return self.
_file_file
177 if __name__ ==
'__main__':
179 "https://github.com/LamarrSim/SQLamarr/raw/master/temporary_data/PrimaryVertex/PrimaryVertexSmearing.db"
181 print(pvdb.download(force=
True).file)
188 except NotImplementedError:
191 assert False,
"Failed raising exception on bad protocol"
Resource on the Internet, locally cached.
def download(self, bool force=False)
Download the remote resource is not available locally or if forced.