ATLAS Offline Software
quick_retrieve.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 #define likely(x) __builtin_expect((x),1)
6 #define unlikely(x) __builtin_expect((x),0)
7 
8 #include <Python.h>
9 
10 #include <CoolKernel/ChannelSelection.h>
11 #include <CoolKernel/ITime.h>
12 #include <CoolKernel/IField.h>
13 #include <CoolKernel/FieldSelection.h>
14 #include <CoolKernel/CompositeSelection.h>
15 #include <CoolKernel/IObject.h>
16 #include <CoolKernel/IObjectIterator.h>
17 #include <CoolKernel/IFolder.h>
18 #include <CoolKernel/IDatabase.h>
19 #include <CoolKernel/IDatabaseSvc.h>
20 
21 #include <CoolApplication/DatabaseSvcFactory.h>
22 
23 #include <boost/typeof/typeof.hpp>
24 #include <boost/bind/bind.hpp>
25 #include <boost/function.hpp>
26 
27 #include <vector>
28 #include <string>
29 #include <iostream>
30 #include <stdexcept>
31 
34 
35 using std::cout;
36 using std::endl;
37 using std::string;
38 using std::vector;
39 
40 using boost::bind;
41 using boost::placeholders::_1;
42 
43 using cool::DatabaseSvcFactory;
44 using cool::IDatabasePtr;
45 using cool::IField;
46 using cool::IObject;
47 using cool::IFolder;
48 using cool::IFolderPtr;
49 using cool::ChannelSelection;
50 using cool::IObjectIteratorPtr;
51 using cool::IObjectVectorPtr;
52 using cool::ValidityKey;
53 using cool::ITime;
54 
55 cool::IRecordSelection* make_fieldselection(
56  const std::string& name,
57  const cool::StorageType::TypeId typeId,
58  cool::FieldSelection::Relation relation,
59  PyObject* refValue)
60 {
61  #define MAKE_FS(type, converter) \
62  if (typeId == cool::StorageType::type) \
63  return new cool::FieldSelection(name, typeId, relation, \
64  static_cast<cool::type>(converter(refValue)));
65 
66  MAKE_FS(Bool, PyLong_AsLong)
67  MAKE_FS(Float, PyFloat_AsDouble)
68  MAKE_FS(Double, PyFloat_AsDouble)
69 
70  MAKE_FS(UChar, PyLong_AsUnsignedLong)
71  MAKE_FS(Int16, PyLong_AsLong)
72  MAKE_FS(UInt16, PyLong_AsUnsignedLong)
73  MAKE_FS(Int32, PyLong_AsLong)
74  MAKE_FS(UInt32, PyLong_AsUnsignedLong)
75  MAKE_FS(Int64, PyLong_AsLongLong)
76  MAKE_FS(UInt63, PyLong_AsUnsignedLongLong)
77 
78 #if PY_VERSION_HEX < 0x03000000
79  MAKE_FS(String255, PyString_AsString)
80  MAKE_FS(String4k, PyString_AsString)
81  MAKE_FS(String64k, PyString_AsString)
82  MAKE_FS(String16M, PyString_AsString)
83 #else
84  MAKE_FS(String255, _PyUnicode_AsString)
85  MAKE_FS(String4k, _PyUnicode_AsString)
86  MAKE_FS(String64k, _PyUnicode_AsString)
87  MAKE_FS(String16M, _PyUnicode_AsString)
88 #endif
89 
90  //MAKE_FS(Blob16M, PyString_AsString)
91  //MAKE_FS(Blob64k, PyString_AsString)
92  throw (std::runtime_error("Unsupported cool type encountered in python conversion"));
93 }
94 
95 vector<const cool::IRecordSelection*> make_selection_vector()
96 {
97  return vector<const cool::IRecordSelection*>();
98 
99 }
100 
101 // A function taking an IObject and returning a PyObject*
102 typedef boost::function<PyObject* (const IObject&)> payload_fetcher_t;
103 
104 // A function to signal that a conversion object could not be found
105 PyObject *no_conversion_available(const IObject&) {return NULL;}
106 
107 // Helpers to create python strings/byte objects from Blob and std::string
109 {
110  const char* data = reinterpret_cast<const char*>(blob.startingAddress());
111 #if PY_VERSION_HEX < 0x03000000
112  return PyString_FromStringAndSize(data, blob.size());
113 #else
114  return PyBytes_FromStringAndSize(data, blob.size());
115 #endif
116 }
117 
119 {
120 #if PY_VERSION_HEX < 0x03000000
121  return PyString_FromStringAndSize(str.c_str(), str.size());
122 #else
123  return PyUnicode_FromStringAndSize(str.c_str(), str.size());
124 #endif
125 }
126 
128 {
129  return PyUnicode_DecodeUTF8(str.c_str(), str.size(), "backslashreplace");
130 }
131 
132 // payload_fetcher takes an IObject `o` to be retrieved from, a `name` to
133 // retrieve and a converter_function which can take a COOL type and create a
134 // python object for it.
135 template<typename T, typename FUNC_TYPE>
136 inline PyObject* payload_fetcher(const IObject& o, const string& name,
137  FUNC_TYPE* converter_function)
138 {
139  // We have to call this, because if one calls payloadValue on a null record,
140  // it throws an exception.
141  const IField& field = o.payload()[name];
142  if (field.isNull())
143  Py_RETURN_NONE;
144  //cppcheck-suppress CastIntegerToAddressAtReturn
145  return converter_function(field.data<T>());
146 }
147 
148 // create_payload_fetcher is called for each field being retrieved from COOL,
149 // once for a given browseObjects() on the first object.
151  const string& type_name,
152  bool string_to_unicode = false)
153 {
154  // A macro which takes a cool `type` and a python api function which returns
155  // a PyObject*, `converter`.
156  #define MAKE_FETCHER(type, converter) \
157  if (type_name == #type) \
158  return bind(payload_fetcher<cool::type, BOOST_TYPEOF(converter)>, \
159  _1, name, &converter);
160 
161  // See the python c-api reference for python conversion functions
162  // Python/C API Reference Manual >> Concrete Objects Layer
163  // http://docs.python.org/c-api/concrete.html
164 
165  MAKE_FETCHER(Bool, PyBool_FromLong)
166  MAKE_FETCHER(Float, PyFloat_FromDouble)
167  MAKE_FETCHER(Double, PyFloat_FromDouble)
168  MAKE_FETCHER(UChar, PyLong_FromLong)
169  MAKE_FETCHER(Int16, PyLong_FromLong)
170  MAKE_FETCHER(UInt16, PyLong_FromUnsignedLong)
171  MAKE_FETCHER(Int32, PyLong_FromLong)
172  MAKE_FETCHER(UInt32, PyLong_FromUnsignedLong)
173  MAKE_FETCHER(Int64, PyLong_FromLong)
174  MAKE_FETCHER(UInt63, PyLong_FromUnsignedLongLong)
175  MAKE_FETCHER(UInt64, PyLong_FromUnsignedLongLong)
176  if (string_to_unicode)
177  {
182  } else
183  {
188  }
191 
192  PyErr_Format(PyExc_RuntimeError,
193  "Type '%s' is not in type conversion table. "
194  "Please add it to quick_retrieve.cxx. "
195  "Can't convert field '%s'.",
196  type_name.c_str(),
197  name);
199 }
200 
201 // Scope guard IObjectIterator so that resource is freed even on exception
203 {
204  const IObjectIteratorPtr& m_iterator;
205 public:
206  IObjectIterator_Guard(const IObjectIteratorPtr& iterator)
207  : m_iterator(iterator) {}
209 };
210 
211 inline PyObject* make_iov_key(PyObject *iovkey_wrapper,
212  unsigned long long value)
213 {
214  static const char * const argtypes = const_cast<char *>("K");
215  if (iovkey_wrapper && iovkey_wrapper != Py_None)
216  return PyObject_CallFunction(iovkey_wrapper, argtypes, value);
217  return PyLong_FromUnsignedLongLong(value);
218 }
219 
220 inline PyObject *apply_function(PyObject *function, PyObject *object)
221 {
222  // Convert object according to function, taking care of references
223  // If function is null, return unmodified object
224  if (!function || function == Py_None) return object;
225  PyObject *old_object = object;
226  PyObject *new_object = PyObject_CallObject(function, object);
227  Py_DECREF(old_object);
228  return new_object;
229 }
230 
231 // Quickly retrieve IObjects from an IObjectIterator
232 // to_fetch is a list of strings specifying which variables to look at
233 // This function closes the iterator when it completes.
234 // The return result is a list of tuples
235 // [(since, until, channel, ...) ] where ... are variables specified by to_fetch
236 PyObject* quick_retrieve(const IObjectIteratorPtr& objects,
237  PyObject *object_converter,
238  PyObject *to_fetch = NULL,
239  const long max_records = -1,
240  const bool with_channel = true,
241  const bool loud = false,
242  PyObject *iovkey_wrapper = NULL,
243  PyObject *channel_name_mapping = NULL,
244  const bool with_time = false,
245  const bool as_unicode = false)
246 {
247  IObjectIterator_Guard closeiterator_guard(objects);
248 
249  PyObject* result = PyList_New(0);
250 
251  const long long records = objects->size();
252  const Py_ssize_t count = to_fetch ? PySequence_Size(to_fetch) : 0;
253  const unsigned int fetch_start = 2 + with_channel + with_time;
254  bool first = true;
255  int j = 0;
256  PyObject *py_datetime_class = NULL;
257 
258  vector<payload_fetcher_t> payload_fetchers;
259 
260  while (objects->goToNext())
261  {
262  const IObject& object = objects->currentRef();
263 
264  if (unlikely(first))
265  {
266  // On the first iteration, compute the types in to_fetch, and bind
267  // them into the payload_fetchers.
268  first = false;
269  for (Py_ssize_t i = 0; i < count; i++)
270  {
271  PyObject *py_name = PySequence_GetItem(to_fetch, i);
272 #if PY_VERSION_HEX < 0x03000000
273  const char *name = PyString_AsString(py_name);
274 #else
275  const char *name = _PyUnicode_AsString(py_name);
276 #endif
277  const string type = (object.payload()
278  .specification()[name]
279  .storageType()
280  .name());
281 
283  as_unicode);
285  return NULL;
286  payload_fetchers.push_back(pf);
287  Py_DECREF(py_name);
288  }
289 
290  if (with_time)
291  {
292  PyObject *py_datetime_module = PyImport_ImportModule("datetime");
293  if (!py_datetime_module)
294  throw (std::runtime_error("Could not import python datetime_module"));
295 
296  py_datetime_class = PyObject_GetAttrString(py_datetime_module,
297  "datetime");
298 
299  Py_DECREF(py_datetime_module);
300  }
301  }
302 
303  PyObject *one = PyTuple_New(fetch_start + count);
304 
305  // Fetch (since, until, channel[optional])
306 
307  PyTuple_SET_ITEM(one, 0, make_iov_key(iovkey_wrapper, object.since()));
308  PyTuple_SET_ITEM(one, 1, make_iov_key(iovkey_wrapper, object.until()));
309 
310  if (with_time)
311  {
312  const ITime& t = object.insertionTime();
313  static const char * const argtypes = const_cast<char *>("iiiiiil");
314 
315  PyObject *py_record_time =
316  PyObject_CallFunction(py_datetime_class, argtypes,
317  t.year(), t.month(), t.day(),
318  t.hour(), t.minute(), t.second(), t.nanosecond() / 1000);
319 
320  PyTuple_SetItem(one, 2, py_record_time);
321  }
322 
323  if (with_channel)
324  {
325  if (channel_name_mapping)
326  {
327  // Use a channel name if it is available, otherwise fall back
328  // to standard channelid.
329  PyObject *channelId = PyLong_FromUnsignedLong(object.channelId());
330  PyObject *channelName = PyDict_GetItem(channel_name_mapping,
331  channelId);
332  if (channelName)
333  {
334  Py_INCREF(channelName);
335  Py_DECREF(channelId);
336  PyTuple_SET_ITEM(one, 2+with_time, channelName);
337  } else
338  PyTuple_SET_ITEM(one, 2+with_time, channelId);
339 
340  } else
341  PyTuple_SET_ITEM(one, 2+with_time,
342  PyLong_FromLong(object.channelId()));
343  }
344 
345  // Fetch the objects specified in to_fetch
346  for (Py_ssize_t i = 0; i < count; i++)
347  PyTuple_SET_ITEM(one, fetch_start + i, payload_fetchers[i](object));
348 
349  one = apply_function(object_converter, one);
350  if (!one)
351  {
352  // apply_function returned an error
353  // We won't return the list to python so we need to tell python it
354  // can be deleted
355  Py_DECREF(result);
356  return NULL;
357  }
358 
359  PyList_Append(result, one);
360  Py_DECREF(one);
361 
362  // Print status if loud is active
363  if (unlikely(loud && ((++j) % 1000 == 0)))
364  {
365  cout << "Progress: " << j << " / " << records << "\r";
366  cout.flush();
367  }
368 
369  if (unlikely(max_records > 0 && j >= max_records))
370  break;
371  }
372 
373  if (loud)
374  cout << "Done fetching " << j << " records.\x1B[K" << endl;
375 
376  if (py_datetime_class)
377  {
378  Py_DECREF(py_datetime_class);
379  }
380 
381  return result;
382 }
383 
xAOD::iterator
JetConstituentVector::iterator iterator
Definition: JetConstituentVector.cxx:68
create_payload_fetcher
payload_fetcher_t create_payload_fetcher(const char *name, const string &type_name, bool string_to_unicode=false)
Definition: quick_retrieve.cxx:150
data
char data[hepevt_bytes_allocation_ATLAS]
Definition: HepEvt.cxx:11
get_generator_info.result
result
Definition: get_generator_info.py:21
LArConditions2Ntuple.objects
objects
Definition: LArConditions2Ntuple.py:56
unlikely
#define unlikely(x)
Definition: quick_retrieve.cxx:6
make_fieldselection
cool::IRecordSelection * make_fieldselection(const std::string &name, const cool::StorageType::TypeId typeId, cool::FieldSelection::Relation relation, PyObject *refValue)
Definition: quick_retrieve.cxx:55
Trk::one
@ one
Definition: TrkDetDescr/TrkSurfaces/TrkSurfaces/RealQuadraticEquation.h:22
python.subdetectors.tile.Blob
Blob
Definition: tile.py:17
qr_PyString_FromStdString
PyObject * qr_PyString_FromStdString(const string &str)
Definition: quick_retrieve.cxx:118
athena.value
value
Definition: athena.py:122
MAKE_FETCHER
#define MAKE_FETCHER(type, converter)
payload_fetcher
PyObject * payload_fetcher(const IObject &o, const string &name, FUNC_TYPE *converter_function)
Definition: quick_retrieve.cxx:136
ReadOfcFromCool.field
field
Definition: ReadOfcFromCool.py:48
read_hist_ntuple.t
t
Definition: read_hist_ntuple.py:5
MAKE_FS
#define MAKE_FS(type, converter)
XMLtoHeader.count
count
Definition: XMLtoHeader.py:85
quick_retrieve
PyObject * quick_retrieve(const IObjectIteratorPtr &objects, PyObject *object_converter, PyObject *to_fetch=NULL, const long max_records=-1, const bool with_channel=true, const bool loud=false, PyObject *iovkey_wrapper=NULL, PyObject *channel_name_mapping=NULL, const bool with_time=false, const bool as_unicode=false)
Definition: quick_retrieve.cxx:236
python.AthDsoLogger.argtypes
argtypes
Definition: AthDsoLogger.py:44
dq_defect_copy_defect_database.since
def since
Definition: dq_defect_copy_defect_database.py:54
dq_defect_copy_defect_database.until
def until
Definition: dq_defect_copy_defect_database.py:55
IObjectIterator_Guard::m_iterator
const IObjectIteratorPtr & m_iterator
Definition: quick_retrieve.cxx:204
qr_PyString_FromBlob
PyObject * qr_PyString_FromBlob(const coral::Blob &blob)
Definition: quick_retrieve.cxx:108
lumiFormat.i
int i
Definition: lumiFormat.py:92
qr_PyUnicode_FromStdString
PyObject * qr_PyUnicode_FromStdString(const string &str)
Definition: quick_retrieve.cxx:127
apply_function
PyObject * apply_function(PyObject *function, PyObject *object)
Definition: quick_retrieve.cxx:220
CaloCondBlobAlgs_fillNoiseFromASCII.channelId
channelId
Definition: CaloCondBlobAlgs_fillNoiseFromASCII.py:122
payload_fetcher_t
boost::function< PyObject *(const IObject &)> payload_fetcher_t
Definition: quick_retrieve.cxx:102
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:192
no_conversion_available
PyObject * no_conversion_available(const IObject &)
Definition: quick_retrieve.cxx:105
make_iov_key
PyObject * make_iov_key(PyObject *iovkey_wrapper, unsigned long long value)
Definition: quick_retrieve.cxx:211
IObjectIterator_Guard
Definition: quick_retrieve.cxx:203
python.output.AtlRunQueryRoot.pf
pf
Definition: AtlRunQueryRoot.py:988
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
DeMoScan.first
bool first
Definition: DeMoScan.py:534
IObjectIterator_Guard::IObjectIterator_Guard
IObjectIterator_Guard(const IObjectIteratorPtr &iterator)
Definition: quick_retrieve.cxx:206
ATLAS_NO_CHECK_FILE_THREAD_SAFETY
ATLAS_NO_CHECK_FILE_THREAD_SAFETY
Definition: quick_retrieve.cxx:33
pickleTool.object
object
Definition: pickleTool.py:30
str
Definition: BTagTrackIpAccessor.cxx:11
checker_macros.h
Define macros for attributes used to control the static checker.
make_selection_vector
vector< const cool::IRecordSelection * > make_selection_vector()
Definition: quick_retrieve.cxx:95
PyObject
_object PyObject
Definition: IPyComponent.h:26
TSU::T
unsigned long long T
Definition: L1TopoDataTypes.h:35
CaloCondBlobAlgs_fillNoiseFromASCII.blob
blob
Definition: CaloCondBlobAlgs_fillNoiseFromASCII.py:96
IObjectIterator_Guard::~IObjectIterator_Guard
~IObjectIterator_Guard()
Definition: quick_retrieve.cxx:208