PageRenderTime 51ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/h5py/_hl/group.py

https://code.google.com/p/h5py/
Python | 422 lines | 400 code | 7 blank | 15 comment | 4 complexity | 40b73116ccb41172d5a6f46cad938cf0 MD5 | raw file
  1. import posixpath as pp
  2. import numpy
  3. from h5py import h5g, h5i, h5o, h5r, h5t, h5l
  4. from . import base
  5. from .base import HLObject, DictCompat
  6. from . import dataset
  7. from . import datatype
  8. class Group(HLObject, DictCompat):
  9. """ Represents an HDF5 group.
  10. """
  11. def __init__(self, bind):
  12. """ Create a new Group object by binding to a low-level GroupID.
  13. """
  14. if not isinstance(bind, h5g.GroupID):
  15. raise ValueError("%s is not a GroupID" % bind)
  16. HLObject.__init__(self, bind)
  17. def create_group(self, name):
  18. """ Create and return a new subgroup.
  19. Name may be absolute or relative. Fails if the target name already
  20. exists.
  21. """
  22. name, lcpl = self._e(name, lcpl=True)
  23. gid = h5g.create(self.id, name, lcpl=lcpl)
  24. return Group(gid)
  25. def create_dataset(self, name, shape=None, dtype=None, data=None, **kwds):
  26. """ Create a new HDF5 dataset
  27. name
  28. Name of the dataset (absolute or relative). Provide None to make
  29. an anonymous dataset.
  30. shape
  31. Dataset shape. Use "()" for scalar datasets. Required if "data"
  32. isn't provided.
  33. dtype
  34. Numpy dtype or string. If omitted, dtype('f') will be used.
  35. Required if "data" isn't provided; otherwise, overrides data
  36. array's dtype.
  37. data
  38. Provide data to initialize the dataset. If used, you can omit
  39. shape and dtype arguments.
  40. Keyword-only arguments:
  41. chunks
  42. (Tuple) Chunk shape, or True to enable auto-chunking.
  43. maxshape
  44. (Tuple) Make the dataset resizable up to this shape. Use None for
  45. axes you want to be unlimited.
  46. compression
  47. (String) Compression strategy. Legal values are 'gzip', 'szip',
  48. 'lzf'. Can also use an integer in range(10) indicating gzip.
  49. compression_opts
  50. Compression settings. This is an integer for gzip, 2-tuple for
  51. szip, etc.
  52. shuffle
  53. (T/F) Enable shuffle filter.
  54. fletcher32
  55. (T/F) Enable fletcher32 error detection.
  56. fillvalue
  57. (Scalar) Use this value for uninitialized parts of the dataset.
  58. """
  59. dsid = dataset.make_new_dset(self, shape, dtype, data, **kwds)
  60. dset = dataset.Dataset(dsid)
  61. if name is not None:
  62. self[name] = dset
  63. return dset
  64. def require_dataset(self, name, shape, dtype, exact=False, **kwds):
  65. """ Open a dataset, creating it if it doesn't exist.
  66. If keyword "exact" is False (default), an existing dataset must have
  67. the same shape and a conversion-compatible dtype to be returned. If
  68. True, the shape and dtype must match exactly.
  69. Other dataset keywords (see create_dataset) may be provided, but are
  70. only used if a new dataset is to be created.
  71. Raises TypeError if an incompatible object already exists, or if the
  72. shape or dtype don't match according to the above rules.
  73. """
  74. if not name in self:
  75. return self.create_dataset(name, *(shape, dtype), **kwds)
  76. dset = self[name]
  77. if not isinstance(dset, dataset.Dataset):
  78. raise TypeError("Incompatible object (%s) already exists" % dset.__class__.__name__)
  79. if not shape == dset.shape:
  80. raise TypeError("Shapes do not match (existing %s vs new %s)" % (dset.shape, shape))
  81. if exact:
  82. if not dtype == dset.dtype:
  83. raise TypeError("Datatypes do not exactly match (existing %s vs new %s)" % (dset.dtype, dtype))
  84. elif not numpy.can_cast(dtype, dset.dtype):
  85. raise TypeError("Datatypes cannot be safely cast (existing %s vs new %s)" % (dset.dtype, dtype))
  86. return dset
  87. def require_group(self, name):
  88. """ Return a group, creating it if it doesn't exist.
  89. TypeError is raised if something with that name already exists that
  90. isn't a group.
  91. """
  92. if not name in self:
  93. return self.create_group(name)
  94. grp = self[name]
  95. if not isinstance(grp, Group):
  96. raise TypeError("Incompatible object (%s) already exists" % grp.__class__.__name__)
  97. return grp
  98. def __getitem__(self, name):
  99. """ Open an object in the file """
  100. if isinstance(name, h5r.Reference):
  101. oid = h5r.dereference(name, self.id)
  102. if oid is None:
  103. raise ValueError("Invalid HDF5 object reference")
  104. else:
  105. oid = h5o.open(self.id, self._e(name), lapl=self._lapl)
  106. otype = h5i.get_type(oid)
  107. if otype == h5i.GROUP:
  108. return Group(oid)
  109. elif otype == h5i.DATASET:
  110. return dataset.Dataset(oid)
  111. elif otype == h5i.DATATYPE:
  112. return datatype.Datatype(oid)
  113. else:
  114. raise TypeError("Unknown object type")
  115. def get(self, name, default=None, getclass=False, getlink=False):
  116. """ Retrieve an item or other information.
  117. "name" given only:
  118. Return the item, or "default" if it doesn't exist
  119. "getclass" is True:
  120. Return the class of object (Group, Dataset, etc.), or "default"
  121. if nothing with that name exists
  122. "getlink" is True:
  123. Return HardLink, SoftLink or ExternalLink instances. Return
  124. "default" if nothing with that name exists.
  125. "getlink" and "getclass" are True:
  126. Return HardLink, SoftLink and ExternalLink classes. Return
  127. "default" if nothing with that name exists.
  128. Example:
  129. >>> cls = group.get('foo', getclass=True)
  130. >>> if cls == SoftLink:
  131. ... print '"foo" is a soft link!'
  132. """
  133. if not name in self:
  134. return default
  135. if not (getclass or getlink):
  136. return self[name]
  137. elif getclass and not getlink:
  138. typecode = h5o.get_info(self.id, self._e(name)).type
  139. try:
  140. return {h5o.TYPE_GROUP: Group,
  141. h5o.TYPE_DATASET: dataset.Dataset,
  142. h5o.TYPE_NAMED_DATATYPE: datatype.Datatype}[typecode]
  143. except KeyError:
  144. raise TypeError("Unknown object type")
  145. elif getlink:
  146. typecode = self.id.links.get_info(self._e(name)).type
  147. if typecode == h5l.TYPE_SOFT:
  148. if getclass:
  149. return SoftLink
  150. linkbytes = self.id.links.get_val(self._e(name))
  151. return SoftLink(self._d(linkbytes))
  152. elif typecode == h5l.TYPE_EXTERNAL:
  153. if getclass:
  154. return ExternalLink
  155. filebytes, linkbytes = self.id.links.get_val(self._e(name))
  156. # TODO: I think this is wrong,
  157. # we should use filesystem decoding on the filename
  158. return ExternalLink(self._d(filebytes), self._d(linkbytes))
  159. elif typecode == h5l.TYPE_HARD:
  160. return HardLink if getclass else HardLink()
  161. else:
  162. raise TypeError("Unknown link type")
  163. def __setitem__(self, name, obj):
  164. """ Add an object to the group. The name must not already be in use.
  165. The action taken depends on the type of object assigned:
  166. Named HDF5 object (Dataset, Group, Datatype)
  167. A hard link is created at "name" which points to the
  168. given object.
  169. SoftLink or ExternalLink
  170. Create the corresponding link.
  171. Numpy ndarray
  172. The array is converted to a dataset object, with default
  173. settings (contiguous storage, etc.).
  174. Numpy dtype
  175. Commit a copy of the datatype as a named datatype in the file.
  176. Anything else
  177. Attempt to convert it to an ndarray and store it. Scalar
  178. values are stored as scalar datasets. Raise ValueError if we
  179. can't understand the resulting array dtype.
  180. """
  181. name, lcpl = self._e(name, lcpl=True)
  182. if isinstance(obj, HLObject):
  183. h5o.link(obj.id, self.id, name, lcpl=lcpl, lapl=self._lapl)
  184. elif isinstance(obj, SoftLink):
  185. self.id.links.create_soft(name, self._e(obj.path),
  186. lcpl=lcpl, lapl=self._lapl)
  187. elif isinstance(obj, ExternalLink):
  188. self.id.links.create_external(name, self._e(obj.filename),
  189. self._e(obj.path), lcpl=lcpl, lapl=self._lapl)
  190. elif isinstance(obj, numpy.dtype):
  191. htype = h5t.py_create(obj)
  192. htype.commit(self.id, name, lcpl=lcpl)
  193. else:
  194. ds = self.create_dataset(None, data=obj, dtype=base.guess_dtype(obj))
  195. h5o.link(ds.id, self.id, name, lcpl=lcpl)
  196. def __delitem__(self, name):
  197. """ Delete (unlink) an item from this group. """
  198. self.id.unlink(self._e(name))
  199. def __len__(self):
  200. """ Number of members attached to this group """
  201. return self.id.get_num_objs()
  202. def __iter__(self):
  203. """ Iterate over member names """
  204. for x in self.id.__iter__():
  205. yield self._d(x)
  206. def __contains__(self, name):
  207. """ Test if a member name exists """
  208. return self._e(name) in self.id
  209. def copy(self, source, dest, name=None):
  210. """ Copy an object or group.
  211. The source can be a path, Group, Dataset, or Datatype object. The
  212. destination can be either a path or a Group object. The source and
  213. destinations need not be in the same file.
  214. If the source is a Group object, all objects contained in that group
  215. will be copied recursively.
  216. When the destination is a Group object, by default the target will
  217. be created in that group with its current name (basename of obj.name).
  218. You can override that by setting "name" to a string.
  219. Example:
  220. >>> f = File('myfile.hdf5')
  221. >>> f.listnames()
  222. ['MyGroup']
  223. >>> f.copy('MyGroup', 'MyCopy')
  224. >>> f.listnames()
  225. ['MyGroup', 'MyCopy']
  226. """
  227. if isinstance(source, HLObject):
  228. source_path = '.'
  229. else:
  230. # Interpret source as a path relative to this group
  231. source_path = source
  232. source = self
  233. if isinstance(dest, Group):
  234. if name is not None:
  235. dest_path = name
  236. else:
  237. # copy source into dest group: dest_name/source_name
  238. dest_path = pp.basename(h5i.get_name(source[source_path].id))
  239. elif isinstance(dest, HLObject):
  240. raise TypeError("Destination must be path or Group object")
  241. else:
  242. # Interpret destination as a path relative to this group
  243. dest_path = dest
  244. dest = self
  245. h5o.copy(source.id, self._e(source_path), dest.id, self._e(dest_path))
  246. def visit(self, func):
  247. """ Recursively visit all names in this group and subgroups (HDF5 1.8).
  248. You supply a callable (function, method or callable object); it
  249. will be called exactly once for each link in this group and every
  250. group below it. Your callable must conform to the signature:
  251. func(<member name>) => <None or return value>
  252. Returning None continues iteration, returning anything else stops
  253. and immediately returns that value from the visit method. No
  254. particular order of iteration within groups is guranteed.
  255. Example:
  256. >>> # List the entire contents of the file
  257. >>> f = File("foo.hdf5")
  258. >>> list_of_names = []
  259. >>> f.visit(list_of_names.append)
  260. """
  261. def proxy(name):
  262. return func(self._d(name))
  263. return h5o.visit(self.id, proxy)
  264. def visititems(self, func):
  265. """ Recursively visit names and objects in this group (HDF5 1.8).
  266. You supply a callable (function, method or callable object); it
  267. will be called exactly once for each link in this group and every
  268. group below it. Your callable must conform to the signature:
  269. func(<member name>, <object>) => <None or return value>
  270. Returning None continues iteration, returning anything else stops
  271. and immediately returns that value from the visit method. No
  272. particular order of iteration within groups is guranteed.
  273. Example:
  274. # Get a list of all datasets in the file
  275. >>> mylist = []
  276. >>> def func(name, obj):
  277. ... if isinstance(obj, Dataset):
  278. ... mylist.append(name)
  279. ...
  280. >>> f = File('foo.hdf5')
  281. >>> f.visititems(func)
  282. """
  283. def proxy(name):
  284. name = self._d(name)
  285. return func(name, self[name])
  286. return h5o.visit(self.id, proxy)
  287. def __repr__(self):
  288. if not self:
  289. return "<Closed HDF5 group>"
  290. namestr = '"%s"' % self.name if self.name is not None else "(anonymous)"
  291. return '<HDF5 group %s (%d members)>' % \
  292. (namestr, len(self))
  293. class HardLink(object):
  294. """
  295. Represents a hard link in an HDF5 file. Provided only so that
  296. Group.get works in a sensible way. Has no other function.
  297. """
  298. pass
  299. #TODO: implement equality testing for these
  300. class SoftLink(object):
  301. """
  302. Represents a symbolic ("soft") link in an HDF5 file. The path
  303. may be absolute or relative. No checking is performed to ensure
  304. that the target actually exists.
  305. """
  306. @property
  307. def path(self):
  308. return self._path
  309. def __init__(self, path):
  310. self._path = str(path)
  311. def __repr__(self):
  312. return '<SoftLink to "%s">' % self.path
  313. class ExternalLink(object):
  314. """
  315. Represents an HDF5 external link. Paths may be absolute or relative.
  316. No checking is performed to ensure either the target or file exists.
  317. """
  318. @property
  319. def path(self):
  320. return self._path
  321. @property
  322. def filename(self):
  323. return self._filename
  324. def __init__(self, filename, path):
  325. self._filename = str(filename)
  326. self._path = str(path)
  327. def __repr__(self):
  328. return '<ExternalLink to "%s" in file "%s"' % (self.path, self.filename)