1
2
3
4
5 """
6 Wrapper for the NeXus shared library.
7
8
9 Library Location
10 ================
11
12 This wrapper needs the location of the libNeXus precompiled binary. It
13 looks in the following places in order:
14 os.environ['NEXUSLIB'] - All
15 directory containing nxs.py - All
16 os.environ['NEXUSDIR']\bin - Windows
17 os.environ['LD_LIBRARY_PATH'] - Unix
18 os.environ['DYLD_LIBRARY_PATH'] - Darwin
19 PREFIX/lib - Unix and Darwin
20 /usr/local/lib - Unix and Darwin
21 /usr/lib - Unix and Darwin
22
23 On Windows it looks for libNeXus.dll and libNeXus-0.dll;
24 NEXUSDIR defaults to r'C:\Program Files\NeXus Data Format'
25 On OS X it looks for libNeXus.dylib
26 On Unix it looks for libNeXus.so
27 PREFIX defaults to /usr/local, but is replaced by the value of
28 --prefix during configure.
29
30 The import will raise an OSError exception if the library wasn't found
31 or couldn't be loaded. Note that on Windows in particular this may be
32 because the supporting HDF5 dlls were not available in the usual places.
33
34 If you are extracting the nexus library from a bundle at runtime, set
35 os.environ['NEXUSLIB'] to the path where it is extracted before the
36 first import of nxs.
37
38 Interface
39 =========
40
41 Full documentation of the NeXus API is available at nexusformat.org.
42
43 This wrapper differs from napi in several respects:
44 - Data values are loaded/stored directly from numpy arrays.
45 - Return codes are turned into exceptions.
46 - The file handle is stored in a file object
47 - Constants are handled somewhat differently (see below)
48 - Type checking on data/parameter storage
49 - Adds iterators file.entries() and file.attrs()
50 - Adds link() function to return the name of the linked to group, if any
51 - NXmalloc/NXfree are not needed.
52
53 Example:
54
55 import nxs
56 file = nxs.open('filename.nxs','rw')
57 file.opengroup('entry1')
58 file.opendata('definition')
59 print file.getdata()
60 file.close()
61
62 See nxstest.py for a more complete example.
63
64 File open modes can be constants or strings:
65
66 nxs.ACC_READ 'r'
67 nxs.ACC_RDWR 'rw'
68 nxs.ACC_CREATE 'w'
69 nxs.ACC_CREATE4 'w4'
70 nxs.ACC_CREATE5 'w5'
71 nxs.ACC_CREATEXML 'wx'
72
73 Dimension constants:
74
75 nxs.UNLIMITED - for the extensible data dimension
76 nxs.MAXRANK - for the number of possible dimensions
77
78 Data types are strings corresponding to the numpy data types:
79
80 'float32' 'float64'
81 'int8' 'int16' 'int32' 'int64'
82 'uint8' 'uint16' 'uint32' 'uint64'
83
84 Use 'char' for strings. You can use the numpy dtype attribute for the
85 data type.
86
87 Dimensions are lists of integers or numpy arrays. You can use the
88 numpy shape attribute for the dimensions.
89
90 Compression codes are:
91
92 'none' 'lzw' 'rle' 'huffman'
93
94 As of this writing NeXus only supports 'none' and 'lzw'.
95
96 Miscellaneous constants:
97
98 nxs.MAXNAMELEN - names must be shorter than this
99 nxs.MAXPATHLEN - total path length must be shorter than this
100
101
102 Caveats
103 =======
104
105 TODO: NOSTRIP constant is probably not handled properly,
106 TODO: Embedded nulls in strings is not supported
107
108 WARNING: We have a memory leak. Calling open/close costs about 90k a pair.
109 This is an eigenbug:
110 - if I test ctypes on a simple library it does not leak
111 - if I use the leak_test1 code in the nexus distribution it doesn't leak
112 - if I remove the open/close call in the wrapper it doesn't leak.
113
114 """
115 import sys, os, numpy, ctypes
116
117
118 from ctypes import c_void_p, c_int, c_long, c_char, c_char_p
119 from ctypes import byref as _ref
120 c_void_pp = ctypes.POINTER(c_void_p)
121 c_int_p = ctypes.POINTER(c_int)
123 _fields_ = [("iTag", c_long),
124 ("iRef", c_long),
125 ("targetPath", c_char*1024),
126 ("linktype", c_int)]
127 _pack_ = False
128 c_NXlink_p = ctypes.POINTER(_NXlink)
129
130
131
132 ACC_READ,ACC_RDWR,ACC_CREATE=1,2,3
133 ACC_CREATE4,ACC_CREATE5,ACC_CREATEXML=4,5,6
134 _nxopen_mode=dict(r=1,rw=2,w=3,w4=4,w5=5,wx=6)
135 NOSTRIP=128
136
137
138 OK,ERROR,EOD=1,0,-1
139
140
141 UNLIMITED=-1
142 MAXRANK=32
143 MAXNAMELEN=64
144 MAXPATHLEN=1024
145
146
147 _nxtype_code=dict(
148 char=4,
149 float32=5,float64=6,
150 int8=20,uint8=21,
151 int16=22,uint16=23,
152 int32=24,uint32=25,
153 int64=26,uint64=27,
154 )
155
156
157
158
159 _pytype_code=(lambda : dict([(v,k) for (k,v) in _nxtype_code.iteritems()]))()
160
161
162 _compression_code=dict(
163 none=100,
164 lzw=200,
165 rle=300,
166 huffman=400)
167
169 """
170 Return True if object acts like a string.
171 """
172
173
174 if hasattr(obj, 'shape'): return False
175 try: obj + ''
176 except (TypeError, ValueError): return False
177 return True
178
180 """
181 Return True if object acts like a list
182 """
183 try: obj + []
184 except TypeError: return False
185 return True
186
188 """
189 Load the NeXus library whereever it may be.
190 """
191
192
193 nxprefix = '/usr/local'
194
195 if 'NEXUSLIB' in os.environ:
196 file = os.environ['NEXUSLIB']
197 if not os.path.isfile(file):
198 raise OSError, \
199 "File %s from environment variable NEXUSLIB does exist"%(file)
200 files = [file]
201 else:
202 files = []
203
204
205 filedir = os.path.dirname(__file__)
206 if sys.platform in ('win32','cygwin'):
207
208 if 'NEXUSDIR' in os.environ:
209 winnxdir = os.environ['NEXUSDIR']
210 else:
211 winnxdir = 'C:/Program Files/NeXus Data Format'
212
213 files += [filedir+"/libNeXus.dll",
214 filedir+"/libNeXus-0.dll",
215 winnxdir + '/bin/libNeXus-0.dll']
216 else:
217 if sys.platform in ('darwin'):
218 lib = 'libNeXus.dylib'
219 ldenv = 'DYLD_LIBRARY_PATH'
220 else:
221 lib = 'libNeXus.so'
222 ldenv = 'LD_LIBRARY_PATH'
223
224 ldpath = [p for p in os.environ.get(ldenv,'').split(':') if p != '']
225 stdpath = [ nxprefix+'/lib', '/usr/local/lib', '/usr/lib']
226 files += [os.path.join(p,lib) for p in [filedir]+ldpath+stdpath]
227
228
229 for file in files:
230 if not os.path.isfile(file): continue
231 try:
232 return ctypes.cdll[file]
233 except:
234 raise OSError, \
235 "NeXus library %s could not be loaded: %s"%(file,sys.exc_info()[0])
236 raise OSError, "Set NEXUSLIB or move NeXus to one of: %s"%(", ".join(files))
237
238
239
240 -def open(filename, mode='r'):
241 """
242 Returns a NeXus file object.
243 """
244 return NeXus(filename, mode)
245
247
248
249 lib = _libnexus()
250
251
252
253
254 - def __init__(self, filename, mode='r'):
255 """
256 Open the NeXus file returning a handle.
257
258 mode can be one of the following:
259 nxs.ACC_READ 'r'
260 nxs.ACC_RDWR 'rw'
261 nxs.ACC_CREATE 'w'
262 nxs.ACC_CREATE4 'w4'
263 nxs.ACC_CREATE5 'w5'
264 nxs.ACC_CREATEXML 'wx'
265
266 Raises RuntimeError if the file could not be opened, with the
267 filename as part of the error message.
268
269 Corresponds to NXopen(filename,mode,&handle)
270 """
271 self.isopen = False
272
273
274 if mode in _nxopen_mode: mode = _nxopen_mode[mode]
275 if mode not in _nxopen_mode.values():
276 raise ValueError, "Invalid open mode %s",str(mode)
277
278 self.filename, self.mode = filename, mode
279 self.handle = c_void_p(None)
280 self.path = []
281 status = self.lib.nxiopen_(filename,mode,_ref(self.handle))
282 if status == ERROR:
283 if mode in [ACC_READ, ACC_RDWR]:
284 op = 'open'
285 else:
286 op = 'create'
287 raise RuntimeError, "Could not %s %s"%(op,filename)
288 self.isopen = True
289
291 """
292 Be sure to close the file before deleting the last reference.
293 """
294 if self.isopen: self.close()
295
296
298 """
299 Return a string representation of the NeXus file handle.
300 """
301 return "NeXus('%s')"%self.filename
302
303
305 """
306 Opens the NeXus file handle if it is not already open.
307 """
308 if self.isopen: return
309 if self.mode==ACC_READ:
310 mode = ACC_READ
311 else:
312 mode = ACC_RDWR
313 status = self.lib.nxiopen_(self.filename,mode,_ref(self.handle))
314 if status == ERROR:
315 raise RuntimeError, "Could not open %s"%(self.filename)
316 self.path = []
317
318
319
321 """
322 Close the NeXus file associated with handle.
323
324 Raises RuntimeError if file could not be opened.
325
326 Corresponds to NXclose(&handle)
327 """
328 if self.isopen:
329 self.isopen = False
330 status = self.lib.nxiclose_(_ref(self.handle))
331 if status == ERROR:
332 raise RuntimeError, "Could not close NeXus file %s"%(self.filename)
333 self.path = []
334
335 lib.nxiflush_.restype = c_int
336 lib.nxiflush_.argtypes = [c_void_pp]
338 """
339 Flush all data to the NeXus file.
340
341 Raises RuntimeError if this fails.
342
343 Corresponds to NXflush(&handle)
344 """
345 status = self.lib.nxiflush_(_ref(self.handle))
346 if status == ERROR:
347 raise RuntimeError, "Could not flush NeXus file %s"%(self.filename)
348
349 lib.nxisetnumberformat_.restype = c_int
350 lib.nxisetnumberformat_.argtypes = [c_void_p, c_int, c_char_p]
365
366
367 lib.nximakegroup_.restype = c_int
368 lib.nximakegroup_.argtypes = [c_void_p, c_char_p, c_char_p]
370 """
371 Create the group nxclass:name.
372
373 Raises RuntimeError if the group could not be created.
374
375 Corresponds to NXmakegroup(handle, name, nxclass)
376 """
377 status = self.lib.nximakegroup_(self.handle, name, nxclass)
378 if status == ERROR:
379 raise RuntimeError,\
380 "Could not create %s:%s in %s"%(nxclass,name,self._loc())
381
382 lib.nxiopenpath_.restype = c_int
383 lib.nxiopenpath_.argtypes = [c_void_p, c_char_p]
385 """
386 Open a particular group '/path/to/group'. Paths can
387 be relative to the currently open group.
388
389 Raises ValueError.
390
391 Corresponds to NXopenpath(handle, path)
392 """
393 status = self.lib.nxiopenpath_(self.handle, path)
394 if status == ERROR:
395 raise ValueError, "Could not open %s in %s"%(path,self._loc())
396 n,path,nxclass = self.getgroupinfo()
397 if path != 'root':
398 self.path = path.split('/')
399 else:
400 self.path = []
401
402
403 lib.nxiopengrouppath_.restype = c_int
404 lib.nxiopengrouppath_.argtypes = [c_void_p, c_char_p]
406 """
407 Open a particular group '/path/to/group', or the dataset containing
408 the group if the path refers to a dataset. Paths can be relative to
409 the currently open group.
410
411 Raises ValueError.
412
413 Corresponds to NXopengrouppath(handle, path)
414 """
415 status = self.lib.nxiopengrouppath_(self.handle, path)
416 if status == ERROR:
417 raise ValueError, "Could not open %s in %s"%(path,self.filename)
418 n,path,nxclass = self.getgroupinfo()
419 if path != 'root':
420 self.path = path.split('/')
421 else:
422 self.path = []
423
424
425 lib.nxiopengroup_.restype = c_int
426 lib.nxiopengroup_.argtypes = [c_void_p, c_char_p, c_char_p]
428 """
429 Open the group nxclass:name.
430
431 Raises ValueError if the group could not be opened.
432
433 Corresponds to NXopengroup(handle, name, nxclass)
434 """
435
436 status = self.lib.nxiopengroup_(self.handle, name, nxclass)
437 if status == ERROR:
438 raise ValueError,\
439 "Could not open %s:%s in %s"%(nxclass,name,self._loc())
440 self.path.append(name)
441
442 lib.nxiclosegroup_.restype = c_int
443 lib.nxiclosegroup_.argtypes = [c_void_p]
445 """
446 Close the currently open group.
447
448 Raises RuntimeError if the group could not be closed.
449
450 Corresponds to NXclosegroup(handle)
451 """
452
453 status = self.lib.nxiclosegroup_(self.handle)
454 group = self.path.pop()
455 if status == ERROR:
456 raise RuntimeError, "Could not close %s:"%(group,self._loc())
457
458 lib.nxigetinfo_.restype = c_int
459 lib.nxigetinfo_.argtypes = [c_void_p, c_int_p, c_char_p, c_char_p]
461 """
462 Query the currently open group returning the tuple
463 numentries, path, nxclass. The path consists of names
464 of subgroups starting at the root separated by "/".
465
466 Raises ValueError if the group could not be opened.
467
468 Corresponds to NXgetgroupinfo(handle)
469 """
470
471 path = ctypes.create_string_buffer(MAXPATHLEN)
472 nxclass = ctypes.create_string_buffer(MAXNAMELEN)
473 n = c_int(0)
474 status = self.lib.nxigetgroupinfo_(self.handle,_ref(n),path,nxclass)
475 if status == ERROR:
476 raise ValueError, "Could not get group info: %s"%(self._loc())
477
478 return n.value,path.value,nxclass.value
479
480 lib.nxiinitgroupdir_.restype = c_int
481 lib.nxiinitgroupdir_.argtypes = [c_void_p]
483 """
484 Reset getnextentry to return the first entry in the group.
485
486 Raises RuntimeError if this fails.
487
488 Corresponds to NXinitgroupdir(handle)
489 """
490 status = self.lib.nxiinitgroupdir_(self.handle)
491 if status == ERROR:
492 raise RuntimeError, \
493 "Could not reset group scan: %s"%(self._loc())
494
495 lib.nxigetnextentry_.restype = c_int
496 lib.nxigetnextentry_.argtypes = [c_void_p, c_char_p, c_char_p, c_int_p]
497 - def getnextentry(self):
498 """
499 Return the next entry in the group as name,nxclass tuple.
500
501 Raises RuntimeError if this fails, or if there is no next entry.
502
503 Corresponds to NXgetnextentry(handle,name,nxclass,&storage).
504
505 This function doesn't return the storage class for data entries
506 since getinfo returns shape and storage, both of which are required
507 to read the data.
508 """
509 name = ctypes.create_string_buffer(MAXNAMELEN)
510 nxclass = ctypes.create_string_buffer(MAXNAMELEN)
511 storage = c_int(0)
512 status = self.lib.nxigetnextentry_(self.handle,name,nxclass,_ref(storage))
513 if status == ERROR or status == EOD:
514 raise RuntimeError, \
515 "Could not get next entry: %s"%(self._loc())
516
517
518
519
520 return name.value,nxclass.value
521
523 """
524 Iterator of entries.
525
526 for name,nxclass in nxs.entries():
527 process(name,nxclass)
528
529 This automatically opens the corresponding group/data for you,
530 and closes it when you are done. Do not rely on any paths
531 remaining open between entries as we restore the current
532 path each time.
533
534 This does not correspond to an existing NeXus API function,
535 but instead combines the work of initgroupdir/getnextentry
536 and open/close on data and group.
537 """
538
539
540
541 n,path,_ = self.getgroupinfo()
542
543 if not path == "root":
544 path = "/"+path
545 else:
546 path = "/"
547
548
549 self.initgroupdir()
550 L = []
551 for i in range(n):
552 L.append(self.getnextentry())
553 for name,nxclass in L:
554 self.openpath(path)
555 if nxclass == "SDS":
556 self.opendata(name)
557 else:
558 self.opengroup(name,nxclass)
559 yield name,nxclass
560
561
562 lib.nxigetinfo_.restype = c_int
563 lib.nxigetinfo_.argtypes = [c_void_p, c_int_p, c_void_p, c_int_p]
565 """
566 Returns the tuple dimensions,type for the currently open dataset.
567 Dimensions is an integer array whose length corresponds to the rank
568 of the dataset and whose elements are the size of the individual
569 dimensions. Storage type is returned as a string, with 'char' for
570 a stored string, '[u]int[8|16|32]' for various integer values or
571 'float[32|64]' for floating point values. No support for
572 complex values.
573
574 Raises RuntimeError if this fails.
575
576 Note that this is the recommended way to establish if you have
577 a dataset open.
578
579 Corresponds to NXgetinfo(handle, &rank, dims, &storage),
580 but with storage converted from HDF values to numpy compatible
581 strings, and rank implicit in the length of the returned dimensions.
582 """
583 rank = c_int(0)
584 shape = numpy.zeros(MAXRANK, 'i')
585 storage = c_int(0)
586 status = self.lib.nxigetinfo_(self.handle, _ref(rank), shape.ctypes.data,
587 _ref(storage))
588 if status == ERROR:
589 raise RuntimeError, "Could not get data info: %s"%(self._loc())
590 shape = shape[:rank.value]+0
591 dtype = _pytype_code[storage.value]
592
593 return shape,dtype
594
595 lib.nxiopendata_.restype = c_int
596 lib.nxiopendata_.argtypes = [c_void_p, c_char_p]
598 """
599 Open the named data set within the current group.
600
601 Raises ValueError if could not open the dataset.
602
603 Corresponds to NXopendata(handle, name)
604 """
605
606 status = self.lib.nxiopendata_(self.handle, name)
607 if status == ERROR:
608 raise ValueError, "Could not open data %s: %s"%(name, self._loc())
609 self.path.append(name)
610
611 lib.nxiclosedata_.restype = c_int
612 lib.nxiclosedata_.argtypes = [c_void_p]
614 """
615 Close the currently open data set.
616
617 Raises RuntimeError if this fails (e.g., because no
618 dataset is open).
619
620 Corresponds to NXclosedata(handle)
621 """
622
623 status = self.lib.nxiclosedata_(self.handle)
624 name = self.path.pop()
625 if status == ERROR:
626 raise RuntimeError,\
627 "Could not close data %s: %s"%(name,self._loc())
628
629 lib.nximakedata_.restype = c_int
630 lib.nximakedata_.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int_p]
631 - def makedata(self, name, dtype=None, shape=None):
632 """
633 Create a data element of the given type and shape. See getinfo
634 for details on types. This does not open the data for writing.
635
636 Set the first dimension to nxs.UNLIMITED, for extensible data sets,
637 and use putslab to write individual slabs.
638
639 Raises ValueError if it fails.
640
641 Corresponds to NXmakedata(handle,name,type,rank,dims)
642 """
643
644
645
646
647
648 storage = _nxtype_code[str(dtype)]
649 shape = numpy.array(shape,'i')
650 status = self.lib.nximakedata_(self.handle,name,storage,len(shape),
651 shape.ctypes.data_as(c_int_p))
652 if status == ERROR:
653 raise ValueError, "Could not create data %s: %s"%(name,self._loc())
654
655 lib.nxicompmakedata_.restype = c_int
656 lib.nxicompmakedata_.argtypes = [c_void_p, c_char_p, c_int, c_int, c_int_p,
657 c_int, c_int_p]
658 - def compmakedata(self, name, dtype=None, shape=None, mode='lzw',
659 chunks=None):
660 """
661 Create a data element of the given dimensions and type. See
662 getinfo for details on types. Compression mode is one of
663 'none', 'lzw', 'rle' or 'huffman'. chunks gives the alignment
664 of the compressed chunks in the data file. There should be one
665 chunk size for each dimension in the data.
666
667 Defaults to mode='lzw' with chunk size set to the length of the
668 fastest varying dimension.
669
670 Raises ValueError if it fails.
671
672 Corresponds to NXmakedata(handle,name,type,rank,dims).
673 """
674 storage = _nxtype_code[str(dtype)]
675
676
677 dims = numpy.array(shape,'i')
678 if chunks == None:
679 chunks = numpy.ones(dims.shape)
680 chunks[-1] = shape[-1]
681 else:
682 chunks = numpy.array(chunks,'i')
683 status = self.lib.nxicompmakedata_(self.handle,name,storage,len(dims),
684 dims.ctypes.data_as(c_int_p),
685 _compression_code[mode],
686 chunks.ctypes.data_as(c_int_p))
687 if status == ERROR:
688 raise ValueError, \
689 "Could not create compressed data %s: %s"%(name,self._loc())
690
691 lib.nxigetdata_.restype = c_int
692 lib.nxigetdata_.argtypes = [c_void_p, c_void_p]
694 """
695 Return the data. If data is a string (1-D char array), a python
696 string is returned. If data is a scalar (1-D numeric array of
697 length 1), a python numeric scalar is returned.
698
699 Raises RuntimeError if this fails.
700
701 Corresponds to NXgetdata(handle, data)
702 """
703
704 shape,dtype = self.getinfo()
705 datafn,pdata,size = self._poutput(dtype,shape)
706 status = self.lib.nxigetdata_(self.handle,pdata)
707 if status == ERROR:
708 raise ValueError, "Could not read data: %s"%(self._loc())
709
710 return datafn()
711
712 lib.nxigetslab_.restype = c_int
713 lib.nxigetslab_.argtypes = [c_void_p, c_void_p, c_int_p, c_int_p]
714 - def getslab(self, slab_offset, slab_shape):
715 """
716 Get a slab from the data array.
717
718 Offsets are 0-origin. Shape can be inferred from the data.
719 Offset and shape must each have one entry per dimension.
720
721 Raises ValueError if this fails.
722
723 Corresponds to NXgetslab(handle,data,offset,shape)
724 """
725
726 shape,dtype = self.getinfo()
727 datafn,pdata,size = self._poutput(dtype,slab_shape)
728 slab_offset = numpy.array(slab_offset,'i')
729 slab_shape = numpy.array(slab_shape,'i')
730 status = self.lib.nxigetslab_(self.handle,pdata,
731 slab_offset.ctypes.data_as(c_int_p),
732 slab_shape.ctypes.data_as(c_int_p))
733
734 if status == ERROR:
735 raise ValueError, "Could not read slab: %s"%(self._loc())
736 return datafn()
737
738 lib.nxiputdata_.restype = c_int
739 lib.nxiputdata_.argtypes = [c_void_p, c_void_p]
741 """
742 Write data into the currently open data block.
743
744 Raises ValueError if this fails.
745
746 Corresponds to NXputdata(handle, data)
747 """
748 shape,dtype = self.getinfo()
749 data,pdata = self._pinput(data,dtype,shape)
750 status = self.lib.nxiputdata_(self.handle,pdata)
751 if status == ERROR:
752 raise ValueError, "Could not write data: %s"%(self._loc())
753
754 lib.nxiputslab_.restype = c_int
755 lib.nxiputslab_.argtypes = [c_void_p, c_void_p, c_int_p, c_int_p]
756 - def putslab(self, data, slab_offset, slab_shape):
757 """
758 Put a slab into the data array.
759
760 Offsets are 0-origin. Shape can be inferred from the data.
761 Offset and shape must each have one entry per dimension.
762
763 Raises ValueError if this fails.
764
765 Corresponds to NXputslab(handle,data,offset,shape)
766 """
767 shape,dtype = self.getinfo()
768 data,pdata = self._pinput(data,dtype,slab_shape)
769 slab_offset = numpy.array(slab_offset,'i')
770 slab_shape = numpy.array(slab_shape,'i')
771
772 status = self.lib.nxiputslab_(self.handle,pdata,
773 slab_offset.ctypes.data_as(c_int_p),
774 slab_shape.ctypes.data_as(c_int_p))
775 if status == ERROR:
776 raise ValueError, "Could not write slab: %s"%(self._loc())
777
778
779
780
781 lib.nxiinitattrdir_.restype = c_int
782 lib.nxiinitattrdir_.argtypes = [c_void_p]
784 """
785 Reset the getnextattr list to the first attribute.
786
787 Raises RuntimeError if this fails.
788
789 Corresponds to NXinitattrdir(handle)
790 """
791 status = self.lib.nxiinitattrdir_(self.handle)
792 if status == ERROR:
793 raise RuntimeError, \
794 "Could not reset attribute list: %s"%(self._loc())
795
796 lib.nxigetattrinfo_.restype = c_int
797 lib.nxigetattrinfo_.argtypes = [c_void_p, c_int_p]
799 """
800 Returns the number of attributes for the currently open
801 group/data object. Do not call getnextattr() more than
802 this number of times.
803
804 Raises RuntimeError if this fails.
805
806 Corresponds to NXgetattrinfo(handl, &n)
807 """
808 n = c_int(0)
809 status = self.lib.nxigetattrinfo_(self.handle,_ref(n))
810 if status == ERROR:
811 raise RuntimeError, "Could not get attr info: %s"%(self._loc())
812
813 return n.value
814
815 lib.nxigetnextattr_.restype = c_int
816 lib.nxigetnextattr_.argtypes = [c_void_p, c_char_p, c_int_p, c_int_p]
818 """
819 Returns the name, length, and data type for the next attribute.
820 Call getattrinfo to determine the number of attributes before
821 calling getnextattr. Data type is returned as a string. See
822 getinfo for details. Length is the number of elements in the
823 attribute.
824
825 Raises RuntimeError if NeXus returns ERROR or EOD.
826
827 Corresponds to NXgetnextattr(handle,name,&length,&storage)
828 but with storage converted from HDF values to numpy compatible
829 strings.
830
831 Note: NeXus API documentation seems to say that length is the number
832 of bytes required to store the entire attribute.
833 """
834 name = ctypes.create_string_buffer(MAXNAMELEN)
835 length = c_int(0)
836 storage = c_int(0)
837 status = self.lib.nxigetnextattr_(self.handle,name,_ref(length),_ref(storage))
838 if status == ERROR or status == EOD:
839 raise RuntimeError, "Could not get next attr: %s"%(self._loc())
840 dtype = _pytype_code[storage.value]
841
842 return name.value, length.value, dtype
843
844
845
846 lib.nxigetattr_.restype = c_int
847 lib.nxigetattr_.argtypes = [c_void_p, c_char_p, c_void_p, c_int_p, c_int_p]
848 - def getattr(self, name, length, dtype):
849 """
850 Returns the value of the named attribute. Requires length and
851 data type from getnextattr to allocate the appropriate amount of
852 space for the attribute.
853
854 Corresponds to NXgetattr(handle,name,data,&length,&storage)
855 """
856 datafn,pdata,size = self._poutput(str(dtype),[length])
857 storage = c_int(_nxtype_code[str(dtype)])
858
859 size = c_int(size)
860 status = self.lib.nxigetattr_(self.handle,name,pdata,_ref(size),_ref(storage))
861 if status == ERROR:
862 raise ValueError, "Could not read attr %s: %s" % (name,self._loc())
863
864 return datafn()
865
866 lib.nxiputattr_.restype = c_int
867 lib.nxiputattr_.argtypes = [c_void_p, c_char_p, c_void_p, c_int, c_int]
868 - def putattr(self, name, value, dtype = None):
869 """
870 Saves the named attribute. The attribute value is a string
871 or a scalar.
872
873 Raises ValueError if the attribute could not be saved.
874
875 Corresponds to NXputattr(handle,name,data,length,storage)
876
877 Note length is the number of elements to write rather
878 than the number of bytes to write.
879 """
880
881 if dtype == None:
882
883 if hasattr(value,'dtype'):
884 dtype = str(value.dtype)
885 elif _is_string_like(value):
886 dtype = 'char'
887 else:
888 value = numpy.array(value)
889 dtype = str(value.dtype)
890 else:
891
892 dtype = str(dtype)
893 if dtype == 'char' and not _is_string_like(value):
894 raise TypeError, "Expected string for 'char' attribute value"
895 if dtype != 'char':
896 value = numpy.array(value,dtype=dtype)
897
898
899 if dtype == 'char':
900 length = len(value)
901 data = value
902 elif numpy.prod(value.shape) != 1:
903
904 raise TypeError, "Attribute value must be scalar or string"
905 else:
906 length = 1
907 data = value.ctypes.data
908
909
910 storage = c_int(_nxtype_code[dtype])
911 status = self.lib.nxiputattr_(self.handle,name,data,length,storage)
912 if status == ERROR:
913 raise ValueError, "Could not write attr %s: %s"%(name,self._loc())
914
916 """
917 Iterate over attributes.
918
919 for name,value in file.attrs():
920 process(name,value)
921
922 This automatically reads the attributes of the group/data. Do not
923 change the active group/data while processing the list.
924
925 This does not correspond to an existing NeXus API function, but
926 combines the work of attrinfo/initattrdir/getnextattr/getattr.
927 """
928 self.initattrdir()
929 n = self.getattrinfo()
930 for i in range(n):
931 name,length,dtype = self.getnextattr()
932 value = self.getattr(name,length,dtype)
933 yield name,value
934
935
936 lib.nxigetgroupid_.restype = c_int
937 lib.nxigetgroupid_.argtypes = [c_void_p, c_NXlink_p]
939 """
940 Return the id of the current group so we can link to it later.
941
942 Raises RuntimeError
943
944 Corresponds to NXgetgroupID(handle, &ID)
945 """
946 ID = _NXlink()
947 status = self.lib.nxigetgroupid_(self.handle,_ref(ID))
948 if status == ERROR:
949 raise RuntimeError, "Could not link to group: %s"%(self._loc())
950 return ID
951
952 lib.nxigetdataid_.restype = c_int
953 lib.nxigetdataid_.argtypes = [c_void_p, c_NXlink_p]
955 """
956 Return the id of the current data so we can link to it later.
957
958 Raises RuntimeError
959
960 Corresponds to NXgetdataID(handle, &ID)
961 """
962 ID = _NXlink()
963 status = self.lib.nxigetdataid_(self.handle,_ref(ID))
964 if status == ERROR:
965 raise RuntimeError, "Could not link to data: %s"%(self._loc())
966 return ID
967
968 lib.nximakelink_.restype = c_int
969 lib.nximakelink_.argtypes = [c_void_p, c_NXlink_p]
971 """
972 Link the previously captured group/data ID into the currently
973 open group.
974
975 Raises RuntimeError
976
977 Corresponds to NXmakelink(handle, &ID)
978 """
979 status = self.lib.nximakelink_(self.handle,_ref(ID))
980 if status == ERROR:
981 raise RuntimeError, "Could not make link: %s"%(self._loc())
982
983 lib.nximakenamedlink_.restype = c_int
984 lib.nximakenamedlink_.argtypes = [c_void_p, c_char_p, c_NXlink_p]
986 """
987 Link the previously captured group/data ID into the currently
988 open group, but under a different name.
989
990 Raises RuntimeError
991
992 Corresponds to NXmakenamedlink(handle,name,&ID)
993 """
994 status = self.lib.nximakenamedlink_(self.handle,name,_ref(ID))
995 if status == ERROR:
996 raise RuntimeError, "Could not make link %s: %s"%(name,self._loc())
997
998 lib.nxisameid_.restype = c_int
999 lib.nxisameid_.argtypes = [c_void_p, c_NXlink_p, c_NXlink_p]
1001 """
1002 Return True of ID1 and ID2 point to the same group/data.
1003
1004 This should not raise any errors.
1005
1006 Corresponds to NXsameID(handle,&ID1,&ID2)
1007 """
1008 status = self.lib.nxisameid_(self.handle, _ref(ID1), _ref(ID2))
1009 return status == OK
1010
1011 lib.nxiopensourcegroup_.restype = c_int
1012 lib.nxiopensourcegroup_.argtyps = [c_void_p]
1014 """
1015 If the current node is a linked to another group or data, then
1016 open the group or data that it is linked to.
1017
1018 Note: it is unclear how can we tell if we are linked, other than
1019 perhaps the existence of a 'target' attribute in the current item.
1020
1021 Raises RuntimeError
1022
1023 Corresponds to NXopensourcegroup(handle)
1024 """
1025 status = self.lib.nxiopensourcegroup_(self.handle)
1026 if status == ERROR:
1027 raise RuntimeError, "Could not open source group: %s"%(self._loc())
1028
1030 """
1031 Returns the item which the current item links to, or None if the
1032 current item is not linked. This is equivalent to scanning the
1033 attributes for target and returning it if target is not equal
1034 to self.
1035
1036 This does not correspond to an existing NeXus API function, but
1037 combines the work of attrinfo/initattrdir/getnextattr/getattr.
1038 """
1039
1040
1041 pathstr = "/"+"/".join(self.path)
1042
1043 n = self.getattrinfo()
1044 self.initattrdir()
1045 for i in range(n):
1046 name,length,dtype = self.getnextattr()
1047 if name == "target":
1048 target = self.getattr(name,length,dtype)
1049
1050 if target != pathstr:
1051 return target
1052 else:
1053 return None
1054 return None
1055
1056
1057 lib.nxiinquirefile_.restype = c_int
1058 lib.nxiinquirefile_.argtypes = [c_void_p, c_char_p, c_int]
1060 """
1061 Return the filename for the current file. This may be different
1062 from the file that was opened (file.filename) if the current
1063 group is an external link to another file.
1064
1065 Raises RuntimeError if this fails.
1066
1067 Corresponds to NXinquirefile(&handle,file,len)
1068 """
1069 filename = ctypes.create_string_buffer(maxnamelen)
1070 status = self.lib.nxiinquirefile_(self.handle,filename,maxnamelen)
1071 if status == ERROR:
1072 raise RuntimeError,\
1073 "Could not determine filename: %s"%(self._loc())
1074 return filename.value
1075
1076 lib.nxilinkexternal_.restype = c_int
1077 lib.nxilinkexternal_.argtyps = [c_void_p, c_char_p,
1078 c_char_p, c_char_p]
1080 """
1081 Return the filename for the external link if there is one,
1082 otherwise return None.
1083
1084 Corresponds to NXisexternalgroup(&handle,name,nxclass,file,len)
1085 """
1086 status = self.lib.nxilinkexternal_(self.handle,name,nxclass,url)
1087 if status == ERROR:
1088 raise RuntimeError,\
1089 "Could not link %s to %s: %s"%(name,url,self._loc())
1090
1091
1092
1093 lib.nxiisexternalgroup_.restype = c_int
1094 lib.nxiisexternalgroup_.argtyps = [c_void_p, c_char_p,
1095 c_char_p, c_char_p, c_int]
1097 """
1098 Return the filename for the external link if there is one,
1099 otherwise return None.
1100
1101 Corresponds to NXisexternalgroup(&handle,name,nxclass,file,len)
1102 """
1103 url = ctypes.create_string_buffer(maxnamelen)
1104 status = self.lib.nxiisexternalgroup_(self.handle,name,nxclass,
1105 url,maxnamelen)
1106 if status == ERROR:
1107 return None
1108 else:
1109 return url.value
1110
1111
1113 """
1114 Return file location as string filename:path
1115
1116 This is an extension to the NeXus API.
1117 """
1118 if not self.path == []:
1119 pathstr = "/".join(self.path)
1120 else:
1121 pathstr = "root"
1122 return "%s(%s)"%(self.filename,pathstr)
1123
1125 """
1126 Build space to collect a nexus data element.
1127 Returns datafn,data,size where
1128 - datafn is a lamba expression to extract the value out of the element.
1129 - pdata is the value to pass to C (effectively a void *)
1130 - size is the number of bytes in the data block
1131 Note that ret can return a string, a scalar or an array depending
1132 on the data type and shape of the data group.
1133 """
1134 if len(shape) == 1 and dtype == 'char':
1135
1136 size = int(shape[0])
1137 pdata = ctypes.create_string_buffer(size)
1138 datafn = lambda: pdata.value
1139 else:
1140
1141 if dtype=='char': dtype = 'uint8'
1142 data = numpy.zeros(shape,dtype)
1143 if len(shape) == 1 and shape[0] == 1:
1144 datafn = lambda: data[0]
1145 else:
1146 datafn = lambda: data
1147 pdata = data.ctypes.data
1148 size = data.nbytes
1149 return datafn,pdata,size
1150
1189
1190 - def show(self, path=None, indent=0):
1191 """
1192 Print the structure of a NeXus file from the current node.
1193
1194 TODO: Break this into a tree walker and a visitor.
1195 """
1196 oldpath = "/"+"/".join(self.path)
1197 if not path: path = oldpath
1198 self.openpath(path)
1199
1200 print "=== File",self.inquirefile(),path
1201 self._show(indent=indent)
1202 self.openpath(oldpath)
1203
1204 - def _show(self, indent=0):
1205 """
1206 Print the structure of a NeXus file from the current node.
1207
1208 TODO: Break this into a tree walker and a visitor.
1209 """
1210 prefix = ' '*indent
1211 link = self.link()
1212 if link:
1213 print "%(prefix)s-> %(link)s" % locals()
1214 return
1215 for attr,value in self.attrs():
1216 print "%(prefix)s@%(attr)s: %(value)s" % locals()
1217 for name,nxclass in self.entries():
1218 if nxclass == "SDS":
1219 shape,dtype = self.getinfo()
1220 dims = "x".join([str(x) for x in shape])
1221 print "%(prefix)s%(name)s %(dtype)s %(dims)s" % locals()
1222 link = self.link()
1223 if link:
1224 print " %(prefix)s-> %(link)s" % locals()
1225 else:
1226 for attr,value in self.attrs():
1227 print " %(prefix)s@%(attr)s: %(value)s" % locals()
1228 if numpy.prod(shape) < 8:
1229 value = self.getdata()
1230 print " %s%s"%(prefix,str(value))
1231 else:
1232 print "%(prefix)s%(name)s %(nxclass)s" % locals()
1233 self._show(indent=indent+2)
1234
1235
1236 __id__ = "$ID$"
1237