Changeset 1806 for trunk/bindings


Ignore:
Timestamp:
22/01/12 23:07:59 (4 months ago)
Author:
Ray Osborn
Message:

Refs #322: Further improvements to make file saves work more intuitively.

  • Added three NeXus object flags, 'infile', 'saved', and 'changed' to record whether

the NXobject currently exists in the file, whether its current value has been saved,
and whether it has been changed. The third flag is for third-party scripts that need
to keep track of when a variable has been changed. It can be reset using the
'set_unchanged' method.

  • Added write methods to the NXobjects to write the object directly to the NeXus file.

This required moving the context routines (enter and exit) to the NXobject
class. The use of the 'with ... as ...' syntax means that the API now requires
Python 2.5.

  • Used the new 'walk' methods in conjunction with the 'write' methods to allow

incremental saves (when no file name is specified) without needing to reload the
entire file.

  • Debugged the reading and writing of NXlink objects to NeXus files.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/bindings/python/nxs/tree.py

    r1805 r1806  
    215215For higher than two dimensions, only the top slice is plotted by default. 
    216216 
    217 The plot() method uses matplotlib by default to plot the data.  You can replace 
     217The plot method accepts as arguments the standard matplotlib.pyplot.plot format  
     218strings to customize one-dimensional plots, axis and scale limits, and will 
     219transmit keyword arguments to the matplotlib plotting methods. 
     220 
     221    >>> a=nx.load('chopper.nxs') 
     222    >>> a.entry.monitor1.plot() 
     223    >>> a.entry.monitor2.plot('r+', xmax=2600) 
     224     
     225It is possible to plot over the existing figure with the oplot() method and to 
     226plot with logarithmic intensity scales with the logplot() method. The x- and 
     227y-axes can also be rendered logarithmically using the logx and logy keywards. 
     228 
     229Although the plot() method uses matplotlib by default to plot the data, you can replace 
    218230this with your own plotter by setting nexus.NXgroup._plotter to your own plotter 
    219231class.  The plotter class has one method:: 
     
    225237title is the title of the group or the parent NXentry, if available. 
    226238""" 
     239from __future__ import with_statement 
     240from copy import copy, deepcopy 
     241 
     242import numpy as np 
     243import napi 
     244from napi import NeXusError 
     245 
     246#Memory in MB 
     247NX_MEMORY = 500 
    227248 
    228249__all__ = ['NeXusTree', 'NXobject', 'NXfield', 'NXgroup', 'NXattr', 
    229250           'NX_MEMORY', 'setmemory', 'load', 'save', 'tree', 'centers', 
    230251           'NXlink', 'NXlinkfield', 'NXlinkgroup', 'SDS', 'NXlinkdata'] 
    231  
    232 from copy import copy, deepcopy 
    233  
    234 import numpy as np 
    235 import napi 
    236 from napi import NeXusError 
    237  
    238 #Memory in MB 
    239 NX_MEMORY = 500 
    240252 
    241253#List of defined base classes (later added to __all__) 
     
    340352        if 'target' in attrs and attrs['target'] != self.path: 
    341353            # This is a linked dataset; don't try to load it. 
    342             #print "read link %s->%s"%(path,attrs['target'].nxdata) 
    343354            data = NXlinkfield(target=attrs['target'], name=name) 
    344355        else: 
     
    354365            data = NXfield(value=value,name=name,dtype=type,shape=dims,attrs=attrs) 
    355366        self.closedata() 
     367        data._infile = data._saved = data._changed = True 
    356368        return data 
    357369 
     
    383395        if 'target' in attrs and attrs['target'] != self.path: 
    384396            # This is a linked group; don't try to load it. 
    385             group = self.NXlinkgroup(target=attrs['target'], name=name) 
     397            group = NXlinkgroup(target=attrs['target'], name=name) 
    386398        else: 
    387399            children = self._readchildren(n) 
     
    389401            # NXentry class name use that constructor for the group 
    390402            # rather than the generic NXgroup class. 
    391 #            if hasattr(self,nxclass): 
    392 #                factory = getattr(self,nxclass) 
    393 #            else: 
    394 #                factory = self.NXgroup 
    395403            group = NXgroup(nxclass=nxclass,name=name,attrs=attrs,entries=children) 
    396404            # Build chain back structure 
    397405            for obj in children.values(): 
    398406                obj._group = group 
     407        group._infile = group._saved = group._changed = True 
    399408        return group 
    400409 
     
    434443 
    435444        path = path + "/" + data.nxname 
    436         shape = data.shape 
    437         if data.shape == (): shape = (1,) 
    438445 
    439446        # If the data is linked then 
    440447        if hasattr(data,'_target'): 
    441448            return [(path, data._target)] 
     449 
     450        shape = data.shape 
     451        if shape == (): shape = (1,) 
    442452 
    443453        #If the array size is too large, their product needs a long integer 
     
    720730    _group = None 
    721731    _file = None 
     732    _infile = False 
     733    _saved = False 
     734    _changed = True 
    722735 
    723736    def __str__(self): 
     
    794807        to True. 
    795808        """ 
    796         return self._str_tree(attrs=False,recursive=True) 
     809        return self._str_tree(attrs=True,recursive=True) 
    797810 
    798811    @property 
     
    801814        return self._str_tree(attrs=True,recursive=True) 
    802815 
     816    def __enter__(self): 
     817        """ 
     818        Open the datapath for reading or writing. 
     819 
     820        Note: the results are undefined if you try accessing 
     821        more than one slab at a time.  Don't nest your 
     822        "with data" statements! 
     823        """ 
     824        self._close_on_exit = not self.nxfile.isopen 
     825        self.nxfile.open() # Force file open even if closed 
     826        self.nxfile.openpath(self.nxpath) 
     827        self._incontext = True 
     828        return self.nxfile 
     829 
     830    def __exit__(self, type, value, traceback): 
     831        """ 
     832        Close the file associated with the data. 
     833        """ 
     834        self._incontext = False 
     835        if self._close_on_exit: 
     836            self.nxfile.close() 
     837 
    803838    def save(self, filename=None, format='w5'): 
    804839        """ 
     
    811846        NXentry group (with name 'entry'), if necessary, in order to produce 
    812847        a valid NeXus file. 
    813         """ 
    814         if self.nxclass == "NXroot": 
    815             root = self 
    816             if filename is None: 
    817                 if self._file: 
    818                     if self._file.mode == napi.ACC_READ: 
    819                         raise NeXusError, "NeXus file is readonly" 
    820                     else: 
    821                         self._file.close() 
    822                         filename = self._file.filename 
    823         elif filename: 
    824             if self.nxclass == "NXentry": 
     848         
     849        Example 
     850        ------- 
     851        >>> data = NXdata(sin(x), x) 
     852        >>> data.save('file.nxs') 
     853        >>> print data.nxroot.tree 
     854        root:NXroot 
     855          @HDF5_Version = 1.8.2 
     856          @NeXus_version = 4.2.1 
     857          @file_name = file.nxs 
     858          @file_time = 2012-01-20T13:14:49-06:00 
     859          entry:NXentry 
     860            data:NXdata 
     861              axis1 = float64(101) 
     862              signal = float64(101) 
     863                @axes = axis1 
     864                @signal = 1               
     865        >>> root.entry.data.axis1.units = 'meV' 
     866        >>> root.save() 
     867        """ 
     868        if filename: 
     869            if self.nxclass == "NXroot": 
     870                root = self 
     871            elif self.nxclass == "NXentry": 
    825872                root = NXroot(self) 
    826873            else: 
    827874                root = NXroot(NXentry(self)) 
    828  
    829         if filename is None: 
     875            if root.nxfile: root.nxfile.close() 
     876            file = NeXusTree(filename, format) 
     877            file.writefile(root) 
     878            file.close() 
     879            root._file = NeXusTree(filename, 'rw') 
     880            root._setattrs(root._file.getattrs()) 
     881            for node in root.walk(): 
     882                node._infile = node._saved = True 
     883             
     884        elif self.nxfile: 
     885            for entry in self.nxroot.values(): 
     886                entry.write() 
     887 
     888        else: 
    830889            raise NeXusError, "No output file specified" 
    831890 
    832         # Preload the source tree 
    833         for node in root.walk(): 
    834             #print "node",node.nxname, node 
    835             if isinstance(node, NXfield): node.nxdata 
    836         if root._file: root._file.close() 
    837         file = NeXusTree(filename, format) 
    838         file.writefile(root) 
    839         file.close() 
    840  
    841         return load(filename, 'rw') 
    842  
     891    @property 
     892    def infile(self): 
     893        """ 
     894        Returns True if the object has been created in a NeXus file. 
     895        """ 
     896        return self._infile 
     897     
     898    @property 
     899    def saved(self): 
     900        """ 
     901        Returns True if the object has been saved to a file. 
     902        """ 
     903        return self._saved 
     904 
     905    @property 
     906    def changed(self): 
     907        """ 
     908        Returns True if the object has been changed. 
     909         
     910        This property is for use by external scripts that need to track 
     911        which NeXus objects have been changed. 
     912        """ 
     913        return self._changed 
     914     
     915    def set_unchanged(self): 
     916        """ 
     917        Set an object's change status to unchanged. 
     918        """ 
     919        self._changed = False 
     920     
    843921    def _getclass(self): 
    844922        return self._class 
     
    847925        return self._name 
    848926 
     927    def _setname(self, value): 
     928        self._name = str(value) 
     929 
    849930    def _getgroup(self): 
    850931        return self._group 
     
    852933    def _getpath(self): 
    853934        if self.nxgroup is None: 
    854             return "" 
     935            return "/" 
    855936        elif isinstance(self.nxgroup, NXroot): 
    856937            return "/" + self.nxname 
     
    876957 
    877958    nxclass = property(_getclass, doc="Class of NeXus object") 
    878     nxname = property(_getname, doc="Name of NeXus object") 
     959    nxname = property(_getname, _setname, doc="Name of NeXus object") 
    879960    nxgroup = property(_getgroup, doc="Parent group of NeXus object") 
    880961    nxpath = property(_getpath, doc="Path to NeXus object") 
    881962    nxroot = property(_getroot, doc="Root group of NeXus object's tree") 
    882963    nxfile = property(_getfile, doc="File handle of NeXus object's tree") 
    883     filename = property(_getfilename, doc="File name of NeXus object's tree") 
    884964    attrs = property(_getattrs, doc="NeXus attributes for an object") 
    885965 
     
    11341214    """ 
    11351215 
    1136     def __init__(self, value=None, name='unknown', dtype=None, shape=(), attrs={}, group=None, 
     1216    def __init__(self, value=None, name='field', dtype=None, shape=(), attrs={}, group=None, 
    11371217                 **attr): 
    11381218        if isinstance(value, list) or isinstance(value, tuple): 
     
    11641244        if value is not None and dtype == 'char': value = str(value) 
    11651245        self._setdata(value) 
     1246        self._saved = False 
     1247        self._changed = True 
    11661248 
    11671249    def __repr__(self): 
     
    11901272        attributes for the NXfield class. 
    11911273        """ 
    1192         if name.startswith('_'): 
     1274        if name.startswith('_') or name.startswith('nx'): 
    11931275            object.__setattr__(self, name, value) 
    11941276        elif isinstance(value, NXattr): 
    11951277            self._attrs[name] = value 
     1278            self._saved = False 
     1279            self._changed = True 
    11961280        else: 
    11971281            self._attrs[name] = NXattr(value) 
     1282            self._saved = False 
     1283            self._changed = True 
    11981284 
    11991285    def __getitem__(self, index): 
     
    12431329        if self._value is not None: 
    12441330            self.nxdata[index] = value 
     1331            self._saved = False 
     1332            self._changed = True 
    12451333        else: 
    12461334            raise NeXusError, "NXfield dataspace not yet allocated" 
     
    14031491    def reshape(self, shape): 
    14041492        """ 
    1405         Returns an NXfield with shape. 
    1406         """ 
    1407         self.shape = list(shape) 
    1408         if isinstance(self.nxdata, np.ndarray): self.nxdata.shape = shape 
    1409         return self 
    1410  
    1411     def transpose(self,shape=None):#added shape variable here 9/8/2010 
    1412         """ 
    1413         Returns an NXfield containing the transpose of the data array. Equivalent 
    1414         to the Numpy ndarray.transpose. 
    1415         """ 
    1416         # error, no shape variable in this scope 
    1417         return NXfield(value=self.nxdata.transpose(shape), name=self.nxname, 
     1493        Returns an NXfield with the specified shape. 
     1494        """ 
     1495        return NXfield(value=self.nxdata.reshape(shape), name=self.nxname, 
    14181496                       attrs=self.attrs) 
     1497 
     1498    def transpose(self): 
     1499        """ 
     1500        Returns an NXfield containing the transpose of the data array. 
     1501        """ 
     1502        return NXfield(value=self.nxdata.transpose(), name=self.nxname, 
     1503                       attrs=self.attrs) 
     1504 
     1505    @property 
     1506    def T(self): 
     1507        return self.transpose() 
    14191508 
    14201509    def centers(self): 
     
    14261515                        name=self.nxname,attrs=self.attrs) 
    14271516 
    1428     def __enter__(self): 
    1429         """ 
    1430         Open the datapath for reading slab by slab. 
    1431  
    1432         Note: the results are undefined if you try accessing 
    1433         more than one slab at a time.  Don't nest your 
    1434         "with data" statements! 
    1435         """ 
    1436         # TODO: provide a file lock to prevent movement of the 
    1437         # file cursor when in the slab context. 
    1438         # TODO: if HDF allows multiple cursors, extend napi to support them 
    1439         self._close_on_exit = not self.nxroot._file.isopen 
    1440         self.nxroot._file.open() # Force file open even if closed 
    1441         self.nxroot._file.openpath(self.nxpath) 
    1442         self._incontext = True 
    1443         return self 
    1444  
    1445     def __exit__(self, *args): 
    1446         """ 
    1447         Close the file associated with the data after reading. 
    1448         """ 
    1449         self._incontext = False 
    1450         if self._close_on_exit: 
    1451             self.nxroot._file.close() 
     1517    def read(self): 
     1518        """ 
     1519        Read the NXfield, including attributes, from the NeXus file. 
     1520 
     1521        The data values are read provided they do not exceed NX_MEMORY. In that 
     1522        case, the data have to be read in as slabs using the get method. 
     1523        """ 
     1524        if self.nxfile: 
     1525            with self as path: 
     1526                self._setattrs(path.getattrs()) 
     1527                shape, dtype = path.getinfo() 
     1528                if dtype == 'char': 
     1529                    self._value = path.getdata() 
     1530                elif np.prod(shape) * np.dtype(dtype).itemsize <= NX_MEMORY*1024*1024: 
     1531                    self._value = path.getdata() 
     1532                else: 
     1533                    raise MemoryError, 'Data size larger than NX_MEMORY=%s MB' % NX_MEMORY 
     1534                self._shape = tuple(shape) 
     1535                self._dtype = dtype 
     1536                if dtype == 'char': 
     1537                    self._dtype = 'char' 
     1538                elif dtype in np.typeDict: 
     1539                    self._dtype = np.dtype(dtype) 
     1540                self._infile = self._saved = self._changed = True 
     1541        else: 
     1542            raise IOError("Data is not attached to a file") 
     1543 
     1544    def write(self): 
     1545        """ 
     1546        Write the NXfield, including attributes, to the NeXus file. 
     1547        """ 
     1548        if self.nxfile: 
     1549            if self.nxfile.mode == napi.ACC_READ: 
     1550                raise NeXusError, "NeXus file is readonly" 
     1551            if not self.infile: 
     1552                shape = self.shape 
     1553                if shape == (): shape = (1,) 
     1554                with self.nxgroup as path: 
     1555                    if np.prod(shape) > 10000 or np.prod(shape) < 0: 
     1556                    # Compress the fastest moving dimension of large datasets 
     1557                        slab_dims = np.ones(len(shape),'i') 
     1558                        if shape[-1] < 100000: 
     1559                            slab_dims[-1] = shape[-1] 
     1560                        else: 
     1561                            slab_dims[-1] = 100000 
     1562                        path.compmakedata(self.nxname, self.dtype, shape, 'lzw',  
     1563                                          slab_dims) 
     1564                    else: 
     1565                    # Don't use compression for small datasets 
     1566                        path.makedata(self.nxname, self.dtype, shape) 
     1567                self._infile = True 
     1568            if not self.saved:             
     1569                with self as path: 
     1570                    path._writeattrs(self.attrs) 
     1571                    value = self.nxdata 
     1572                    if value is not None: 
     1573                        path.putdata(value) 
     1574                self._saved = True 
     1575        else: 
     1576            raise IOError("Data is not attached to a file") 
    14521577 
    14531578    def get(self, offset, size): 
     
    14581583        Offset and shape must each have one entry per dimension. 
    14591584 
    1460         This operation should be performed in a "with group.data" 
    1461         context. 
    1462  
    1463         Raises ValueError cannot convert units. 
    1464  
    14651585        Corresponds to NXgetslab(handle,data,offset,shape) 
    14661586        """ 
    1467         if self.nxroot._file: 
    1468             self.__enter__() 
    1469             value = self.nxroot._file.getslab(offset,size) 
    1470             self.__exit__() 
    1471             return value 
    1472         else: 
    1473             raise NeXusError, "No input file specified for NXgetslab" 
     1587        if self.nxfile: 
     1588            with self as path: 
     1589                value = path.getslab(offset,size) 
     1590                return value 
     1591        else: 
     1592            raise IOError("Data is not attached to a file") 
    14741593 
    14751594    def put(self, data, offset, refresh=True): 
     
    14801599        Offset and shape must each have one entry per dimension. 
    14811600 
    1482         This operation should be performed in a "with group.data" 
    1483         context. 
    1484  
    1485         Raises NeXusError if this fails.  No error is raised when 
    1486         writing to a file which is open read-only. 
    1487  
    14881601        Corresponds to NXputslab(handle,data,offset,shape) 
    14891602        """ 
    1490         if self.nxroot._file: 
    1491             if self.nxroot._file.mode == napi.ACC_READ: 
     1603        if self.nxfile: 
     1604            if self.nxfile.mode == napi.ACC_READ: 
    14921605                raise NeXusError, "NeXus file is readonly" 
    1493             self.__enter__() 
    1494             if isinstance(data, NXfield): 
    1495                 self.nxroot._file.putslab(data.nxdata.astype(self.dtype), offset, 
    1496                                         data.shape) 
    1497             else: 
    1498                 data = np.array(data) 
    1499                 self.nxroot._file.putslab(data.astype(self.dtype), offset, 
    1500                                         data.shape) 
    1501             self.__exit__() 
     1606            with self as path: 
     1607                if isinstance(data, NXfield): 
     1608                    path.putslab(data.nxdata.astype(self.dtype), offset, data.shape) 
     1609                else: 
     1610                    data = np.array(data) 
     1611                    path.putslab(data.astype(self.dtype), offset, data.shape) 
    15021612            if refresh: self.refresh() 
    15031613        else: 
    1504             raise NeXusError, "No output file specified for NXputslab" 
    1505  
    1506     def add(self, data, offset): 
     1614            raise IOError("Data is not attached to a file") 
     1615 
     1616    def add(self, data, offset, refresh=True): 
    15071617        """ 
    15081618        Add a slab into the data array. 
     
    15181628            value = self.get(offset, data.shape) 
    15191629            self.put(data.astype(self.dtype)+value, offset) 
     1630        if refresh: self.refresh() 
    15201631 
    15211632    def refresh(self): 
     
    15231634        Rereads the data from the file. 
    15241635 
    1525         Calls net to read in data again. If put has been called, then 
    1526         nxdata is no longer synchronized with the file making a refresh 
    1527         necessary. This will only be performed if nxdata already stores the 
    1528         data. 
     1636        If put has been called, then nxdata is no longer synchronized with the 
     1637        file making a refresh necessary. This will only be performed if nxdata 
     1638        already stores the data. 
    15291639        """ 
    15301640        if self._value is not None: 
    1531             if self.nxroot._file: 
    1532                 self._value = self.nxroot._file.readpath(self.nxpath) 
     1641            if self.nxfile: 
     1642                self._value = self.nxfile.readpath(self.nxpath) 
     1643                self._infile = self._saved = True 
    15331644            else: 
    1534                 raise NeXusError, "No file specified for reads" 
     1645                raise IOError("Data is not attached to a file") 
    15351646 
    15361647    def convert(self, units=""): 
     
    15911702        """ 
    15921703        if self._value is None: 
    1593             if self.nxroot._file: 
     1704            if self.nxfile: 
    15941705                if str(self.dtype) == 'char': 
    1595                     self._value = self.nxroot._file.readpath(self.nxpath) 
     1706                    self._value = self.nxfile.readpath(self.nxpath) 
    15961707                elif np.prod(self.shape) * np.dtype(self.dtype).itemsize <= NX_MEMORY*1024*1024: 
    1597                     self._value = self.nxroot._file.readpath(self.nxpath) 
     1708                    self._value = self.nxfile.readpath(self.nxpath) 
    15981709                else: 
    15991710                    raise MemoryError, 'Data size larger than NX_MEMORY=%s MB' % NX_MEMORY 
     1711                self._saved = True 
    16001712            else: 
    16011713                return None 
     
    16161728                self._shape = self._value.shape 
    16171729                self._dtype = self._value.dtype 
    1618  
     1730            self._saved = False 
     1731            self._changed = True 
     1732        
    16191733    def _getdtype(self): 
    16201734        return self._dtype 
     
    16281742    nxdata = property(_getdata,_setdata,doc="The data values") 
    16291743    nxaxes = property(_getaxes,doc="The plotting axes") 
    1630     dtype = property(_getdtype, "Data type of NeXus field") 
    1631     shape = property(_getshape, "Shape of NeXus field") 
    1632     size = property(_getsize, "Size of NeXus field") 
     1744    dtype = property(_getdtype,doc="Data type of NeXus field") 
     1745    shape = property(_getshape,doc="Shape of NeXus field") 
     1746    size = property(_getsize,doc="Size of NeXus field") 
    16331747 
    16341748SDS = NXfield # For backward compatibility 
     
    17211835                plt.title(title) 
    17221836            plt.ion() 
    1723             plt.show() 
    17241837 
    17251838        #Two dimensional plot 
     
    17411854            z = np.clip(data,zmin,zmax).T 
    17421855             
    1743             ax = plt.gca() 
    1744             extent = (x[0],x[-1],y[0],y[-1]) 
    17451856            if log: 
    17461857                opts["norm"] = LogNorm() 
     
    17481859                    z = np.clip(z,0.1,zmax) 
    17491860                 
     1861            ax = plt.gca() 
     1862            extent = (x[0],x[-1],y[0],y[-1]) 
    17501863            im = NonUniformImage(ax, extent=extent, origin=None, **opts) 
    17511864            im.set_data(x,y,z) 
     
    17751888            plt.title(title) 
    17761889            plt.colorbar(im) 
    1777             plt.gcf().canvas.draw_idle() 
    1778          
    1779         return plt.gcf() 
     1890 
     1891        plt.gcf().canvas.draw_idle() 
    17801892 
    17811893    @staticmethod 
     
    20182130            except AttributeError: 
    20192131                raise NeXusError, "Non-keyword arguments must be valid NXobjects" 
     2132        self._saved = False 
     2133        self._changed = True 
    20202134 
    20212135#    def __cmp__(self, other): 
     
    20672181        is set to the assigned value. 
    20682182        """ 
    2069         if name.startswith('_'): 
     2183        if name.startswith('_') or name.startswith('nx'): 
    20702184            object.__setattr__(self, name, value) 
    20712185        elif isinstance(value, NXattr): 
    20722186            self._attrs[name] = value 
     2187            self._saved = False 
     2188            self._changed = True 
    20732189        else: 
    20742190            self[name] = value 
     
    21362252    def __setitem__(self, key, value): 
    21372253        """ 
    2138         Includes a NeXus group item by adding it to the 'entries' dictionary. 
    2139         """ 
     2254        Adds or modifies an item in the NeXus group. 
     2255        """ 
     2256        if key in self.entries:  
     2257            infile = self[key]._infile 
     2258            attrs = self[key].attrs 
     2259            if isinstance(self[key], NXlink): 
     2260                if self._entries[key].nxlink: 
     2261                    setattr(self._entries[key].nxlink.nxgroup, key, value) 
     2262                return 
     2263        else: 
     2264            infile = None 
     2265            attrs = {} 
    21402266        if isinstance(value, NXlink): 
    21412267            self._entries[key] = value 
     
    21492275            self._entries[key] = value 
    21502276        else: 
    2151             self._entries[key] = NXfield(value=value, name=key, group=self) 
     2277            self._entries[key] = NXfield(value=value, name=key, group=self, attrs=attrs) 
     2278        if infile is not None: self[key]._infile = infile 
     2279        self._changed = True 
    21522280 
    21532281    def __deepcopy__(self, memo): 
     
    22102338        """ 
    22112339        if isinstance(target, NXobject): 
    2212             self.entries[target.nxname] = NXlink(target=target, name=target.nxname, group=self) 
     2340            self[target.nxname] = NXlink(target=target, group=self) 
    22132341        else: 
    22142342            raise NeXusError, "Link target must be an NXobject" 
     2343 
     2344    def write(self): 
     2345        """ 
     2346        Write the NXgroup, including its children, to the NeXus file. 
     2347        """ 
     2348        if self.nxfile: 
     2349            if self.nxfile.mode == napi.ACC_READ: 
     2350                raise NeXusError, "NeXus file is readonly" 
     2351            if not self.infile: 
     2352                with self.nxgroup as path: 
     2353                    path.makegroup(self.nxname, self.nxclass) 
     2354                self._infile = True 
     2355            with self as path: 
     2356                path._writeattrs(self.attrs) 
     2357                for entry in self.walk(): 
     2358                    if entry is not self: entry.write() 
     2359                self._infile = self._saved = True 
     2360        else: 
     2361            raise IOError("Group is not attached to a file") 
    22152362 
    22162363    def sum(self, axis=None): 
     
    24232570    def __init__(self, target=None, name='link', group=None): 
    24242571        self._group = group 
    2425         self._name = name 
    24262572        if isinstance(target, NXobject): 
     2573            self._name = target.nxname 
    24272574            self._target = target.nxpath 
     2575            self.nxlink.attrs["target"] = target.nxpath 
    24282576            if target.nxclass == "NXlink": 
    24292577                raise NeXusError, "Cannot link to another NXlink object" 
     
    24332581                self.__class__ = NXlinkgroup 
    24342582        else: 
     2583            self._name = name 
    24352584            self._target = target 
    24362585 
     
    24442593            raise KeyError, (key+" not in %s" % self._target) 
    24452594 
     2595    def __setattr__(self, name, value): 
     2596        if name.startswith('_')  or name.startswith('nx'): 
     2597            object.__setattr__(self, name, value) 
     2598        elif self.nxlink: 
     2599            self.nxlink.__setattr__(name, value) 
     2600 
    24462601    def __repr__(self): 
    24472602        return "NXlink('%s')"%(self._target) 
    24482603 
    24492604    def __str__(self): 
    2450         return "NXlink('%s')"%(self._target) 
     2605        return str(self.nxlink) 
    24512606 
    24522607    def _str_tree(self, indent=0, attrs=False, recursive=False): 
     
    24712626        return self.nxlink.attrs 
    24722627 
     2628    nxlink = property(_getlink, "Linked object") 
     2629    attrs = property(_getattrs,doc="NeXus attributes for object") 
     2630 
     2631 
     2632class NXlinkfield(NXlink, NXfield): 
     2633 
     2634    """ 
     2635    Class for a NeXus linked field. 
     2636 
     2637    The real field will be accessible by following the link attribute. 
     2638    """ 
     2639 
     2640    def write(self): 
     2641        """ 
     2642        Write the linked NXfield. 
     2643        """ 
     2644        self.nxlink.write() 
     2645        if not self.infile: 
     2646            with self.nxlink as path: 
     2647                target = path.getdataID() 
     2648            with self.nxgroup as path: 
     2649                path.makelink(target) 
     2650            self._infile = self._saved = True 
     2651 
     2652 
     2653    def get(self, offset, size): 
     2654        """ 
     2655        Get a slab from the data array. 
     2656 
     2657        Offsets are 0-origin.  Shape can be inferred from the data. 
     2658        Offset and shape must each have one entry per dimension. 
     2659 
     2660        This operation should be performed in a "with group.data" 
     2661        conext. 
     2662 
     2663        Raises ValueError cannot convert units. 
     2664 
     2665        Corresponds to NXgetslab(handle,data,offset,shape) 
     2666        """ 
     2667        if self.nxfile: 
     2668            with self.nxlink as path: 
     2669                value = path.getslab(offset,size) 
     2670        else: 
     2671            raise IOError("Data is not attached to a file") 
     2672 
     2673NXlinkdata = NXlinkfield # For backward compatibility 
     2674 
     2675class NXlinkgroup(NXlink, NXgroup): 
     2676 
     2677    """ 
     2678    Class for a NeXus linked group. 
     2679 
     2680    The real group will be accessible by following the link attribute. 
     2681    """ 
     2682 
     2683    def write(self): 
     2684        """ 
     2685        Write the linked NXgroup. 
     2686        """ 
     2687        self.nxlink.write() 
     2688        if not self.infile: 
     2689            with self.nxlink as path: 
     2690                target = path.getgroupID() 
     2691            with self.nxgroup as path: 
     2692                path.makelink(target) 
     2693            self._infile = self._saved = True 
     2694 
    24732695    def _getentries(self): 
    24742696        return self.nxlink.entries 
    24752697 
    2476     nxlink = property(_getlink, "Linked object") 
    2477     attrs = property(_getattrs,doc="NeXus attributes for object") 
    24782698    entries = property(_getentries,doc="NeXus objects within group") 
    2479  
    2480  
    2481 class NXlinkfield(NXlink, NXfield): 
    2482  
    2483     """ 
    2484     Class for a NeXus linked field. 
    2485  
    2486     The real field will be accessible by following the link attribute. 
    2487     """ 
    2488  
    2489     def __setattr__(self, name, value): 
    2490         if name.startswith('_'): 
    2491             object.__setattr__(self, name, value) 
    2492         elif name == "name" or name == "nxclass" or name == "group": 
    2493             NXfield.__setattr__(self, name, value) 
    2494         elif self.nxlink: 
    2495             self.nxlink.__setattr__(name, value) 
    2496  
    2497     def get(self, offset, size): 
    2498         """ 
    2499         Get a slab from the data array. 
    2500  
    2501         Offsets are 0-origin.  Shape can be inferred from the data. 
    2502         Offset and shape must each have one entry per dimension. 
    2503  
    2504         This operation should be performed in a "with group.data" 
    2505         conext. 
    2506  
    2507         Raises ValueError cannot convert units. 
    2508  
    2509         Corresponds to NXgetslab(handle,data,offset,shape) 
    2510         """ 
    2511         if self.nxroot._file: 
    2512             self.nxlink.__enter__() 
    2513             value = self.nxlink.nxroot._file.getslab(offset,size) 
    2514             self.nxlink.__exit__() 
    2515         else: 
    2516             raise NeXusError, "No input file specified for NXgetslab" 
    2517  
    2518 NXlinkdata = NXlinkfield # For backward compatibility 
    2519  
    2520 class NXlinkgroup(NXlink, NXgroup): 
    2521  
    2522     """ 
    2523     Class for a NeXus linked group. 
    2524  
    2525     The real group will be accessible by following the link attribute. 
    2526     """ 
    2527  
    2528     def __setattr__(self, name, value): 
    2529         if name.startswith('_'): 
    2530             object.__setattr__(self, name, value) 
    2531         elif name == "name" or name == "nxclass" or name == "group": 
    2532             NXgroup.__setattr__(self, name, value) 
    2533         elif self.nxlink: 
    2534             if not isinstance(value, NXobject): value = NXfield(value=value, name=name) 
    2535             value._group = self 
    2536             value._name = name 
    2537             object.__setattr__(self, name, value) 
    2538             setattr(self.nxlink, name, value) 
    25392699 
    25402700 
Note: See TracChangeset for help on using the changeset viewer.