test_nixio.py 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208
  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2016, German Neuroinformatics Node (G-Node)
  3. # Achilleas Koutsou <achilleas.k@gmail.com>
  4. #
  5. # All rights reserved.
  6. #
  7. # Redistribution and use in source and binary forms, with or without
  8. # modification, are permitted under the terms of the BSD License. See
  9. # LICENSE file in the root of the Project.
  10. """
  11. Tests for neo.io.nixio
  12. """
  13. import os
  14. from datetime import datetime
  15. try:
  16. import unittest2 as unittest
  17. except ImportError:
  18. import unittest
  19. try:
  20. from unittest import mock
  21. except ImportError:
  22. import mock
  23. import string
  24. import numpy as np
  25. import quantities as pq
  26. from neo.core import (Block, Segment, ChannelIndex, AnalogSignal,
  27. IrregularlySampledSignal, Unit, SpikeTrain, Event, Epoch)
  28. from neo.test.iotest.common_io_test import BaseTestIO
  29. try:
  30. import nixio as nix
  31. HAVE_NIX = True
  32. except ImportError:
  33. HAVE_NIX = False
  34. from neo.io.nixio import NixIO
  35. from neo.io.nixio import string_types
  36. @unittest.skipUnless(HAVE_NIX, "Requires NIX")
  37. class NixIOTest(unittest.TestCase):
  38. filename = None
  39. io = None
  40. def compare_blocks(self, neoblocks, nixblocks):
  41. for neoblock, nixblock in zip(neoblocks, nixblocks):
  42. self.compare_attr(neoblock, nixblock)
  43. self.assertEqual(len(neoblock.segments), len(nixblock.groups))
  44. for idx, neoseg in enumerate(neoblock.segments):
  45. nixgrp = nixblock.groups[neoseg.annotations["nix_name"]]
  46. self.compare_segment_group(neoseg, nixgrp)
  47. for idx, neochx in enumerate(neoblock.channel_indexes):
  48. nixsrc = nixblock.sources[neochx.annotations["nix_name"]]
  49. self.compare_chx_source(neochx, nixsrc)
  50. self.check_refs(neoblock, nixblock)
  51. def compare_chx_source(self, neochx, nixsrc):
  52. self.compare_attr(neochx, nixsrc)
  53. nix_channels = list(src for src in nixsrc.sources
  54. if src.type == "neo.channelindex")
  55. self.assertEqual(len(neochx.index), len(nix_channels))
  56. for nixchan in nix_channels:
  57. nixchanidx = nixchan.metadata["index"]
  58. try:
  59. neochanpos = list(neochx.index).index(nixchanidx)
  60. except ValueError:
  61. self.fail("Channel indexes do not match.")
  62. if len(neochx.channel_names):
  63. neochanname = neochx.channel_names[neochanpos]
  64. if ((not isinstance(neochanname, str)) and
  65. isinstance(neochanname, bytes)):
  66. neochanname = neochanname.decode()
  67. nixchanname = nixchan.metadata["neo_name"]
  68. self.assertEqual(neochanname, nixchanname)
  69. nix_units = list(src for src in nixsrc.sources
  70. if src.type == "neo.unit")
  71. self.assertEqual(len(neochx.units), len(nix_units))
  72. for neounit in neochx.units:
  73. nixunit = nixsrc.sources[neounit.annotations["nix_name"]]
  74. self.compare_attr(neounit, nixunit)
  75. def check_refs(self, neoblock, nixblock):
  76. """
  77. Checks whether the references between objects that are not nested are
  78. mapped correctly (e.g., SpikeTrains referenced by a Unit).
  79. :param neoblock: A Neo block
  80. :param nixblock: The corresponding NIX block
  81. """
  82. for idx, neochx in enumerate(neoblock.channel_indexes):
  83. nixchx = nixblock.sources[neochx.annotations["nix_name"]]
  84. # AnalogSignals referencing CHX
  85. neoasigs = list(sig.annotations["nix_name"]
  86. for sig in neochx.analogsignals)
  87. nixasigs = list(set(da.metadata.name for da in nixblock.data_arrays
  88. if da.type == "neo.analogsignal" and
  89. nixchx in da.sources))
  90. self.assertEqual(len(neoasigs), len(nixasigs),
  91. neochx.analogsignals)
  92. # IrregularlySampledSignals referencing CHX
  93. neoisigs = list(sig.annotations["nix_name"] for sig in
  94. neochx.irregularlysampledsignals)
  95. nixisigs = list(
  96. set(da.metadata.name for da in nixblock.data_arrays
  97. if da.type == "neo.irregularlysampledsignal" and
  98. nixchx in da.sources)
  99. )
  100. self.assertEqual(len(neoisigs), len(nixisigs))
  101. # SpikeTrains referencing CHX and Units
  102. for sidx, neounit in enumerate(neochx.units):
  103. nixunit = nixchx.sources[neounit.annotations["nix_name"]]
  104. neosts = list(st.annotations["nix_name"]
  105. for st in neounit.spiketrains)
  106. nixsts = list(mt for mt in nixblock.multi_tags
  107. if mt.type == "neo.spiketrain" and
  108. nixunit.name in mt.sources)
  109. # SpikeTrains must also reference CHX
  110. for nixst in nixsts:
  111. self.assertIn(nixchx.name, nixst.sources)
  112. nixsts = list(st.name for st in nixsts)
  113. self.assertEqual(len(neosts), len(nixsts))
  114. for neoname in neosts:
  115. if neoname:
  116. self.assertIn(neoname, nixsts)
  117. # Events and Epochs must reference all Signals in the Group (NIX only)
  118. for nixgroup in nixblock.groups:
  119. nixevep = list(mt for mt in nixgroup.multi_tags
  120. if mt.type in ["neo.event", "neo.epoch"])
  121. nixsigs = list(da.name for da in nixgroup.data_arrays
  122. if da.type in ["neo.analogsignal",
  123. "neo.irregularlysampledsignal"])
  124. for nee in nixevep:
  125. for ns in nixsigs:
  126. self.assertIn(ns, nee.references)
  127. def compare_segment_group(self, neoseg, nixgroup):
  128. self.compare_attr(neoseg, nixgroup)
  129. neo_signals = neoseg.analogsignals + neoseg.irregularlysampledsignals
  130. self.compare_signals_das(neo_signals, nixgroup.data_arrays)
  131. neo_eests = neoseg.epochs + neoseg.events + neoseg.spiketrains
  132. self.compare_eests_mtags(neo_eests, nixgroup.multi_tags)
  133. def compare_signals_das(self, neosignals, data_arrays):
  134. for sig in neosignals:
  135. if self.io._find_lazy_loaded(sig) is not None:
  136. sig = self.io.load_lazy_object(sig)
  137. dalist = list()
  138. nixname = sig.annotations["nix_name"]
  139. for da in data_arrays:
  140. if da.metadata.name == nixname:
  141. dalist.append(da)
  142. _, nsig = np.shape(sig)
  143. self.assertEqual(nsig, len(dalist))
  144. self.compare_signal_dalist(sig, dalist)
  145. def compare_signal_dalist(self, neosig, nixdalist):
  146. """
  147. Check if a Neo Analog or IrregularlySampledSignal matches a list of
  148. NIX DataArrays.
  149. :param neosig: Neo Analog or IrregularlySampledSignal
  150. :param nixdalist: List of DataArrays
  151. """
  152. nixmd = nixdalist[0].metadata
  153. self.assertTrue(all(nixmd == da.metadata for da in nixdalist))
  154. neounit = str(neosig.dimensionality)
  155. for sig, da in zip(np.transpose(neosig),
  156. sorted(nixdalist, key=lambda d: d.name)):
  157. self.compare_attr(neosig, da)
  158. np.testing.assert_almost_equal(sig.magnitude, da)
  159. self.assertEqual(neounit, da.unit)
  160. timedim = da.dimensions[0]
  161. if isinstance(neosig, AnalogSignal):
  162. self.assertEqual(timedim.dimension_type,
  163. nix.DimensionType.Sample)
  164. self.assertEqual(
  165. pq.Quantity(timedim.sampling_interval, timedim.unit),
  166. neosig.sampling_period
  167. )
  168. self.assertEqual(timedim.offset, neosig.t_start.magnitude)
  169. if "t_start.units" in da.metadata.props:
  170. self.assertEqual(da.metadata["t_start.units"],
  171. str(neosig.t_start.dimensionality))
  172. elif isinstance(neosig, IrregularlySampledSignal):
  173. self.assertEqual(timedim.dimension_type,
  174. nix.DimensionType.Range)
  175. np.testing.assert_almost_equal(neosig.times.magnitude,
  176. timedim.ticks)
  177. self.assertEqual(timedim.unit,
  178. str(neosig.times.dimensionality))
  179. def compare_eests_mtags(self, eestlist, mtaglist):
  180. self.assertEqual(len(eestlist), len(mtaglist))
  181. for eest in eestlist:
  182. if self.io._find_lazy_loaded(eest) is not None:
  183. eest = self.io.load_lazy_object(eest)
  184. mtag = mtaglist[eest.annotations["nix_name"]]
  185. if isinstance(eest, Epoch):
  186. self.compare_epoch_mtag(eest, mtag)
  187. elif isinstance(eest, Event):
  188. self.compare_event_mtag(eest, mtag)
  189. elif isinstance(eest, SpikeTrain):
  190. self.compare_spiketrain_mtag(eest, mtag)
  191. def compare_epoch_mtag(self, epoch, mtag):
  192. self.assertEqual(mtag.type, "neo.epoch")
  193. self.compare_attr(epoch, mtag)
  194. np.testing.assert_almost_equal(epoch.times.magnitude, mtag.positions)
  195. np.testing.assert_almost_equal(epoch.durations.magnitude, mtag.extents)
  196. self.assertEqual(mtag.positions.unit,
  197. str(epoch.times.units.dimensionality))
  198. self.assertEqual(mtag.extents.unit,
  199. str(epoch.durations.units.dimensionality))
  200. for neol, nixl in zip(epoch.labels,
  201. mtag.positions.dimensions[0].labels):
  202. # Dirty. Should find the root cause instead
  203. if isinstance(neol, bytes):
  204. neol = neol.decode()
  205. if isinstance(nixl, bytes):
  206. nixl = nixl.decode()
  207. self.assertEqual(neol, nixl)
  208. def compare_event_mtag(self, event, mtag):
  209. self.assertEqual(mtag.type, "neo.event")
  210. self.compare_attr(event, mtag)
  211. np.testing.assert_almost_equal(event.times.magnitude, mtag.positions)
  212. self.assertEqual(mtag.positions.unit, str(event.units.dimensionality))
  213. for neol, nixl in zip(event.labels,
  214. mtag.positions.dimensions[0].labels):
  215. # Dirty. Should find the root cause instead
  216. # Only happens in 3.2
  217. if isinstance(neol, bytes):
  218. neol = neol.decode()
  219. if isinstance(nixl, bytes):
  220. nixl = nixl.decode()
  221. self.assertEqual(neol, nixl)
  222. def compare_spiketrain_mtag(self, spiketrain, mtag):
  223. self.assertEqual(mtag.type, "neo.spiketrain")
  224. self.compare_attr(spiketrain, mtag)
  225. np.testing.assert_almost_equal(spiketrain.times.magnitude,
  226. mtag.positions)
  227. if len(mtag.features):
  228. neowf = spiketrain.waveforms
  229. nixwf = mtag.features[0].data
  230. self.assertEqual(np.shape(neowf), np.shape(nixwf))
  231. self.assertEqual(nixwf.unit, str(neowf.units.dimensionality))
  232. np.testing.assert_almost_equal(neowf.magnitude, nixwf)
  233. self.assertEqual(nixwf.dimensions[0].dimension_type,
  234. nix.DimensionType.Set)
  235. self.assertEqual(nixwf.dimensions[1].dimension_type,
  236. nix.DimensionType.Set)
  237. self.assertEqual(nixwf.dimensions[2].dimension_type,
  238. nix.DimensionType.Sample)
  239. def compare_attr(self, neoobj, nixobj):
  240. if isinstance(neoobj, (AnalogSignal, IrregularlySampledSignal)):
  241. nix_name = ".".join(nixobj.name.split(".")[:-1])
  242. else:
  243. nix_name = nixobj.name
  244. self.assertEqual(neoobj.annotations["nix_name"], nix_name)
  245. self.assertEqual(neoobj.description, nixobj.definition)
  246. if hasattr(neoobj, "rec_datetime") and neoobj.rec_datetime:
  247. self.assertEqual(neoobj.rec_datetime,
  248. datetime.fromtimestamp(nixobj.created_at))
  249. if hasattr(neoobj, "file_datetime") and neoobj.file_datetime:
  250. self.assertEqual(neoobj.file_datetime,
  251. datetime.fromtimestamp(
  252. nixobj.metadata["file_datetime"]))
  253. if neoobj.annotations:
  254. nixmd = nixobj.metadata
  255. for k, v, in neoobj.annotations.items():
  256. if k == "nix_name":
  257. continue
  258. if isinstance(v, pq.Quantity):
  259. self.assertEqual(nixmd.props[str(k)].unit,
  260. str(v.dimensionality))
  261. np.testing.assert_almost_equal(nixmd[str(k)],
  262. v.magnitude)
  263. else:
  264. self.assertEqual(nixmd[str(k)], v)
  265. @classmethod
  266. def create_full_nix_file(cls, filename):
  267. nixfile = nix.File.open(filename, nix.FileMode.Overwrite,
  268. backend="h5py")
  269. nix_block_a = nixfile.create_block(cls.rword(10), "neo.block")
  270. nix_block_a.definition = cls.rsentence(5, 10)
  271. nix_block_b = nixfile.create_block(cls.rword(10), "neo.block")
  272. nix_block_b.definition = cls.rsentence(3, 3)
  273. nix_block_a.metadata = nixfile.create_section(
  274. nix_block_a.name, nix_block_a.name+".metadata"
  275. )
  276. nix_block_b.metadata = nixfile.create_section(
  277. nix_block_b.name, nix_block_b.name+".metadata"
  278. )
  279. nix_blocks = [nix_block_a, nix_block_b]
  280. for blk in nix_blocks:
  281. for ind in range(3):
  282. group = blk.create_group(cls.rword(), "neo.segment")
  283. group.definition = cls.rsentence(10, 15)
  284. group_md = blk.metadata.create_section(group.name,
  285. group.name+".metadata")
  286. group.metadata = group_md
  287. blk = nix_blocks[0]
  288. group = blk.groups[0]
  289. allspiketrains = list()
  290. allsignalgroups = list()
  291. # analogsignals
  292. for n in range(3):
  293. siggroup = list()
  294. asig_name = "{}_asig{}".format(cls.rword(10), n)
  295. asig_definition = cls.rsentence(5, 5)
  296. asig_md = group.metadata.create_section(asig_name,
  297. asig_name+".metadata")
  298. for idx in range(3):
  299. da_asig = blk.create_data_array(
  300. "{}.{}".format(asig_name, idx),
  301. "neo.analogsignal",
  302. data=cls.rquant(100, 1)
  303. )
  304. da_asig.definition = asig_definition
  305. da_asig.unit = "mV"
  306. da_asig.metadata = asig_md
  307. timedim = da_asig.append_sampled_dimension(0.01)
  308. timedim.unit = "ms"
  309. timedim.label = "time"
  310. timedim.offset = 10
  311. da_asig.append_set_dimension()
  312. group.data_arrays.append(da_asig)
  313. siggroup.append(da_asig)
  314. allsignalgroups.append(siggroup)
  315. # irregularlysampledsignals
  316. for n in range(2):
  317. siggroup = list()
  318. isig_name = "{}_isig{}".format(cls.rword(10), n)
  319. isig_definition = cls.rsentence(12, 12)
  320. isig_md = group.metadata.create_section(isig_name,
  321. isig_name+".metadata")
  322. isig_times = cls.rquant(200, 1, True)
  323. for idx in range(10):
  324. da_isig = blk.create_data_array(
  325. "{}.{}".format(isig_name, idx),
  326. "neo.irregularlysampledsignal",
  327. data=cls.rquant(200, 1)
  328. )
  329. da_isig.definition = isig_definition
  330. da_isig.unit = "mV"
  331. da_isig.metadata = isig_md
  332. timedim = da_isig.append_range_dimension(isig_times)
  333. timedim.unit = "s"
  334. timedim.label = "time"
  335. da_isig.append_set_dimension()
  336. group.data_arrays.append(da_isig)
  337. siggroup.append(da_isig)
  338. allsignalgroups.append(siggroup)
  339. # SpikeTrains with Waveforms
  340. for n in range(4):
  341. stname = "{}-st{}".format(cls.rword(20), n)
  342. times = cls.rquant(400, 1, True)
  343. times_da = blk.create_data_array(
  344. "{}.times".format(stname),
  345. "neo.spiketrain.times",
  346. data=times
  347. )
  348. times_da.unit = "ms"
  349. mtag_st = blk.create_multi_tag(stname,
  350. "neo.spiketrain",
  351. times_da)
  352. group.multi_tags.append(mtag_st)
  353. mtag_st.definition = cls.rsentence(20, 30)
  354. mtag_st_md = group.metadata.create_section(
  355. mtag_st.name, mtag_st.name+".metadata"
  356. )
  357. mtag_st.metadata = mtag_st_md
  358. mtag_st_md.create_property(
  359. "t_stop", nix.Value(times[-1]+1.0)
  360. )
  361. waveforms = cls.rquant((10, 8, 5), 1)
  362. wfname = "{}.waveforms".format(mtag_st.name)
  363. wfda = blk.create_data_array(wfname, "neo.waveforms",
  364. data=waveforms)
  365. wfda.unit = "mV"
  366. mtag_st.create_feature(wfda, nix.LinkType.Indexed)
  367. wfda.append_set_dimension() # spike dimension
  368. wfda.append_set_dimension() # channel dimension
  369. wftimedim = wfda.append_sampled_dimension(0.1)
  370. wftimedim.unit = "ms"
  371. wftimedim.label = "time"
  372. wfda.metadata = mtag_st_md.create_section(
  373. wfname, "neo.waveforms.metadata"
  374. )
  375. wfda.metadata.create_property("left_sweep",
  376. [nix.Value(20)]*5)
  377. allspiketrains.append(mtag_st)
  378. # Epochs
  379. for n in range(3):
  380. epname = "{}-ep{}".format(cls.rword(5), n)
  381. times = cls.rquant(5, 1, True)
  382. times_da = blk.create_data_array(
  383. "{}.times".format(epname),
  384. "neo.epoch.times",
  385. data=times
  386. )
  387. times_da.unit = "s"
  388. extents = cls.rquant(5, 1)
  389. extents_da = blk.create_data_array(
  390. "{}.durations".format(epname),
  391. "neo.epoch.durations",
  392. data=extents
  393. )
  394. extents_da.unit = "s"
  395. mtag_ep = blk.create_multi_tag(
  396. epname, "neo.epoch", times_da
  397. )
  398. mtag_ep.metadata = group.metadata.create_section(
  399. epname, epname+".metadata"
  400. )
  401. group.multi_tags.append(mtag_ep)
  402. mtag_ep.definition = cls.rsentence(2)
  403. mtag_ep.extents = extents_da
  404. label_dim = mtag_ep.positions.append_set_dimension()
  405. label_dim.labels = cls.rsentence(5).split(" ")
  406. # reference all signals in the group
  407. for siggroup in allsignalgroups:
  408. mtag_ep.references.extend(siggroup)
  409. # Events
  410. for n in range(2):
  411. evname = "{}-ev{}".format(cls.rword(5), n)
  412. times = cls.rquant(5, 1, True)
  413. times_da = blk.create_data_array(
  414. "{}.times".format(evname),
  415. "neo.event.times",
  416. data=times
  417. )
  418. times_da.unit = "s"
  419. mtag_ev = blk.create_multi_tag(
  420. evname, "neo.event", times_da
  421. )
  422. mtag_ev.metadata = group.metadata.create_section(
  423. evname, evname+".metadata"
  424. )
  425. group.multi_tags.append(mtag_ev)
  426. mtag_ev.definition = cls.rsentence(2)
  427. label_dim = mtag_ev.positions.append_set_dimension()
  428. label_dim.labels = cls.rsentence(5).split(" ")
  429. # reference all signals in the group
  430. for siggroup in allsignalgroups:
  431. mtag_ev.references.extend(siggroup)
  432. # CHX
  433. nixchx = blk.create_source(cls.rword(10),
  434. "neo.channelindex")
  435. nixchx.metadata = nix_blocks[0].metadata.create_section(
  436. nixchx.name, "neo.channelindex.metadata"
  437. )
  438. chantype = "neo.channelindex"
  439. # 3 channels
  440. for idx, chan in enumerate([2, 5, 9]):
  441. channame = "{}.ChannelIndex{}".format(nixchx.name, idx)
  442. nixrc = nixchx.create_source(channame, chantype)
  443. nixrc.definition = cls.rsentence(13)
  444. nixrc.metadata = nixchx.metadata.create_section(
  445. nixrc.name, "neo.channelindex.metadata"
  446. )
  447. nixrc.metadata.create_property("index", nix.Value(chan))
  448. dims = tuple(map(nix.Value, cls.rquant(3, 1)))
  449. nixrc.metadata.create_property("coordinates", dims)
  450. nixrc.metadata.create_property("coordinates.units",
  451. nix.Value("um"))
  452. nunits = 1
  453. stsperunit = np.array_split(allspiketrains, nunits)
  454. for idx in range(nunits):
  455. unitname = "{}-unit{}".format(cls.rword(5), idx)
  456. nixunit = nixchx.create_source(unitname, "neo.unit")
  457. nixunit.metadata = nixchx.metadata.create_section(
  458. unitname, unitname+".metadata"
  459. )
  460. nixunit.definition = cls.rsentence(4, 10)
  461. for st in stsperunit[idx]:
  462. st.sources.append(nixchx)
  463. st.sources.append(nixunit)
  464. # pick a few signal groups to reference this CHX
  465. randsiggroups = np.random.choice(allsignalgroups, 5, False)
  466. for siggroup in randsiggroups:
  467. for sig in siggroup:
  468. sig.sources.append(nixchx)
  469. return nixfile
  470. @staticmethod
  471. def rdate():
  472. return datetime(year=np.random.randint(1980, 2020),
  473. month=np.random.randint(1, 13),
  474. day=np.random.randint(1, 29))
  475. @classmethod
  476. def populate_dates(cls, obj):
  477. obj.file_datetime = cls.rdate()
  478. obj.rec_datetime = cls.rdate()
  479. @staticmethod
  480. def rword(n=10):
  481. return "".join(np.random.choice(list(string.ascii_letters), n))
  482. @classmethod
  483. def rsentence(cls, n=3, maxwl=10):
  484. return " ".join(cls.rword(np.random.randint(1, maxwl))
  485. for _ in range(n))
  486. @classmethod
  487. def rdict(cls, nitems):
  488. rd = dict()
  489. for _ in range(nitems):
  490. key = cls.rword()
  491. value = cls.rword() if np.random.choice((0, 1)) \
  492. else np.random.uniform()
  493. rd[key] = value
  494. return rd
  495. @staticmethod
  496. def rquant(shape, unit, incr=False):
  497. try:
  498. dim = len(shape)
  499. except TypeError:
  500. dim = 1
  501. if incr and dim > 1:
  502. raise TypeError("Shape of quantity array may only be "
  503. "one-dimensional when incremental values are "
  504. "requested.")
  505. arr = np.random.random(shape)
  506. if incr:
  507. arr = np.array(np.cumsum(arr))
  508. return arr*unit
  509. @classmethod
  510. def create_all_annotated(cls):
  511. times = cls.rquant(1, pq.s)
  512. signal = cls.rquant(1, pq.V)
  513. blk = Block()
  514. blk.annotate(**cls.rdict(3))
  515. seg = Segment()
  516. seg.annotate(**cls.rdict(4))
  517. blk.segments.append(seg)
  518. asig = AnalogSignal(signal=signal, sampling_rate=pq.Hz)
  519. asig.annotate(**cls.rdict(2))
  520. seg.analogsignals.append(asig)
  521. isig = IrregularlySampledSignal(times=times, signal=signal,
  522. time_units=pq.s)
  523. isig.annotate(**cls.rdict(2))
  524. seg.irregularlysampledsignals.append(isig)
  525. epoch = Epoch(times=times, durations=times)
  526. epoch.annotate(**cls.rdict(4))
  527. seg.epochs.append(epoch)
  528. event = Event(times=times)
  529. event.annotate(**cls.rdict(4))
  530. seg.events.append(event)
  531. spiketrain = SpikeTrain(times=times, t_stop=pq.s, units=pq.s)
  532. d = cls.rdict(6)
  533. d["quantity"] = pq.Quantity(10, "mV")
  534. d["qarray"] = pq.Quantity(range(10), "mA")
  535. spiketrain.annotate(**d)
  536. seg.spiketrains.append(spiketrain)
  537. chx = ChannelIndex(name="achx", index=[1, 2])
  538. chx.annotate(**cls.rdict(5))
  539. blk.channel_indexes.append(chx)
  540. unit = Unit()
  541. unit.annotate(**cls.rdict(2))
  542. chx.units.append(unit)
  543. return blk
  544. class NixIOWriteTest(NixIOTest):
  545. def setUp(self):
  546. self.filename = "nixio_testfile_write.h5"
  547. self.writer = NixIO(self.filename, "ow")
  548. self.io = self.writer
  549. self.reader = nix.File.open(self.filename,
  550. nix.FileMode.ReadOnly,
  551. backend="h5py")
  552. def tearDown(self):
  553. self.writer.close()
  554. self.reader.close()
  555. os.remove(self.filename)
  556. def write_and_compare(self, blocks):
  557. self.writer.write_all_blocks(blocks)
  558. self.compare_blocks(self.writer.read_all_blocks(), self.reader.blocks)
  559. def test_block_write(self):
  560. block = Block(name=self.rword(),
  561. description=self.rsentence())
  562. self.write_and_compare([block])
  563. block.annotate(**self.rdict(5))
  564. self.write_and_compare([block])
  565. def test_segment_write(self):
  566. block = Block(name=self.rword())
  567. segment = Segment(name=self.rword(), description=self.rword())
  568. block.segments.append(segment)
  569. self.write_and_compare([block])
  570. segment.annotate(**self.rdict(2))
  571. self.write_and_compare([block])
  572. def test_channel_index_write(self):
  573. block = Block(name=self.rword())
  574. chx = ChannelIndex(name=self.rword(),
  575. description=self.rsentence(),
  576. index=[1, 2, 3, 5, 8, 13])
  577. block.channel_indexes.append(chx)
  578. self.write_and_compare([block])
  579. chx.annotate(**self.rdict(3))
  580. self.write_and_compare([block])
  581. chx.channel_names = ["one", "two", "three", "five",
  582. "eight", "xiii"]
  583. self.write_and_compare([block])
  584. def test_signals_write(self):
  585. block = Block()
  586. seg = Segment()
  587. block.segments.append(seg)
  588. asig = AnalogSignal(signal=self.rquant((10, 3), pq.mV),
  589. sampling_rate=pq.Quantity(10, "Hz"))
  590. seg.analogsignals.append(asig)
  591. self.write_and_compare([block])
  592. anotherblock = Block("ir signal block")
  593. seg = Segment("ir signal seg")
  594. anotherblock.segments.append(seg)
  595. irsig = IrregularlySampledSignal(
  596. signal=np.random.random((20, 3)),
  597. times=self.rquant(20, pq.ms, True),
  598. units=pq.A
  599. )
  600. seg.irregularlysampledsignals.append(irsig)
  601. self.write_and_compare([anotherblock])
  602. block.segments[0].analogsignals.append(
  603. AnalogSignal(signal=[10.0, 1.0, 3.0], units=pq.S,
  604. sampling_period=pq.Quantity(3, "s"),
  605. dtype=np.double, name="signal42",
  606. description="this is an analogsignal",
  607. t_start=45 * pq.ms),
  608. )
  609. self.write_and_compare([block, anotherblock])
  610. block.segments[0].irregularlysampledsignals.append(
  611. IrregularlySampledSignal(times=np.random.random(10),
  612. signal=np.random.random((10, 3)),
  613. units="mV", time_units="s",
  614. dtype=np.float,
  615. name="some sort of signal",
  616. description="the signal is described")
  617. )
  618. self.write_and_compare([block, anotherblock])
  619. def test_epoch_write(self):
  620. block = Block()
  621. seg = Segment()
  622. block.segments.append(seg)
  623. epoch = Epoch(times=[1, 1, 10, 3]*pq.ms, durations=[3, 3, 3, 1]*pq.ms,
  624. labels=np.array(["one", "two", "three", "four"]),
  625. name="test epoch", description="an epoch for testing")
  626. seg.epochs.append(epoch)
  627. self.write_and_compare([block])
  628. def test_event_write(self):
  629. block = Block()
  630. seg = Segment()
  631. block.segments.append(seg)
  632. event = Event(times=np.arange(0, 30, 10)*pq.s,
  633. labels=np.array(["0", "1", "2"]),
  634. name="event name",
  635. description="event description")
  636. seg.events.append(event)
  637. self.write_and_compare([block])
  638. def test_spiketrain_write(self):
  639. block = Block()
  640. seg = Segment()
  641. block.segments.append(seg)
  642. spiketrain = SpikeTrain(times=[3, 4, 5]*pq.s, t_stop=10.0,
  643. name="spikes!", description="sssssspikes")
  644. seg.spiketrains.append(spiketrain)
  645. self.write_and_compare([block])
  646. waveforms = self.rquant((3, 5, 10), pq.mV)
  647. spiketrain = SpikeTrain(times=[1, 1.1, 1.2]*pq.ms, t_stop=1.5*pq.s,
  648. name="spikes with wf",
  649. description="spikes for waveform test",
  650. waveforms=waveforms)
  651. seg.spiketrains.append(spiketrain)
  652. self.write_and_compare([block])
  653. spiketrain.left_sweep = np.random.random(10)*pq.ms
  654. self.write_and_compare([block])
  655. def test_metadata_structure_write(self):
  656. neoblk = self.create_all_annotated()
  657. self.io.write_block(neoblk)
  658. blk = self.io.nix_file.blocks[0]
  659. blkmd = blk.metadata
  660. self.assertEqual(blk.name, blkmd.name)
  661. grp = blk.groups[0] # segment
  662. self.assertIn(grp.name, blkmd.sections)
  663. grpmd = blkmd.sections[grp.name]
  664. for da in grp.data_arrays: # signals
  665. name = ".".join(da.name.split(".")[:-1])
  666. self.assertIn(name, grpmd.sections)
  667. for mtag in grp.multi_tags: # spiketrains, events, and epochs
  668. self.assertIn(mtag.name, grpmd.sections)
  669. srcchx = blk.sources[0] # chx
  670. self.assertIn(srcchx.name, blkmd.sections)
  671. for srcunit in blk.sources: # units
  672. self.assertIn(srcunit.name, blkmd.sections)
  673. self.write_and_compare([neoblk])
  674. def test_anonymous_objects_write(self):
  675. nblocks = 2
  676. nsegs = 2
  677. nanasig = 4
  678. nirrseg = 2
  679. nepochs = 3
  680. nevents = 4
  681. nspiketrains = 3
  682. nchx = 5
  683. nunits = 10
  684. times = self.rquant(1, pq.s)
  685. signal = self.rquant(1, pq.V)
  686. blocks = []
  687. for blkidx in range(nblocks):
  688. blk = Block()
  689. blocks.append(blk)
  690. for segidx in range(nsegs):
  691. seg = Segment()
  692. blk.segments.append(seg)
  693. for anaidx in range(nanasig):
  694. seg.analogsignals.append(AnalogSignal(signal=signal,
  695. sampling_rate=pq.Hz))
  696. for irridx in range(nirrseg):
  697. seg.irregularlysampledsignals.append(
  698. IrregularlySampledSignal(times=times,
  699. signal=signal,
  700. time_units=pq.s)
  701. )
  702. for epidx in range(nepochs):
  703. seg.epochs.append(Epoch(times=times, durations=times))
  704. for evidx in range(nevents):
  705. seg.events.append(Event(times=times))
  706. for stidx in range(nspiketrains):
  707. seg.spiketrains.append(SpikeTrain(times=times,
  708. t_stop=times[-1]+pq.s,
  709. units=pq.s))
  710. for chidx in range(nchx):
  711. chx = ChannelIndex(name="chx{}".format(chidx),
  712. index=[1, 2])
  713. blk.channel_indexes.append(chx)
  714. for unidx in range(nunits):
  715. unit = Unit()
  716. chx.units.append(unit)
  717. self.writer.write_all_blocks(blocks)
  718. self.compare_blocks(blocks, self.reader.blocks)
  719. def test_to_value(self):
  720. section = self.io.nix_file.create_section("Metadata value test",
  721. "Test")
  722. writeprop = self.io._write_property
  723. # quantity
  724. qvalue = pq.Quantity(10, "mV")
  725. writeprop(section, "qvalue", qvalue)
  726. self.assertEqual(section["qvalue"], 10)
  727. self.assertEqual(section.props["qvalue"].unit, "mV")
  728. # datetime
  729. dt = self.rdate()
  730. writeprop(section, "dt", dt)
  731. self.assertEqual(datetime.fromtimestamp(section["dt"]), dt)
  732. # string
  733. randstr = self.rsentence()
  734. writeprop(section, "randstr", randstr)
  735. self.assertEqual(section["randstr"], randstr)
  736. # bytes
  737. bytestring = b"bytestring"
  738. writeprop(section, "randbytes", bytestring)
  739. self.assertEqual(section["randbytes"], bytestring.decode())
  740. # iterables
  741. randlist = np.random.random(10).tolist()
  742. writeprop(section, "randlist", randlist)
  743. self.assertEqual(randlist, section["randlist"])
  744. randarray = np.random.random(10)
  745. writeprop(section, "randarray", randarray)
  746. np.testing.assert_almost_equal(randarray, section["randarray"])
  747. # numpy item
  748. npval = np.float64(2398)
  749. writeprop(section, "npval", npval)
  750. self.assertEqual(npval, section["npval"])
  751. # number
  752. val = 42
  753. writeprop(section, "val", val)
  754. self.assertEqual(val, section["val"])
  755. class NixIOReadTest(NixIOTest):
  756. filename = "testfile_readtest.h5"
  757. nixfile = None
  758. nix_blocks = None
  759. original_methods = dict()
  760. @classmethod
  761. def setUpClass(cls):
  762. if HAVE_NIX:
  763. cls.nixfile = cls.create_full_nix_file(cls.filename)
  764. def setUp(self):
  765. self.io = NixIO(self.filename, "ro")
  766. self.original_methods["_read_cascade"] = self.io._read_cascade
  767. self.original_methods["_update_maps"] = self.io._update_maps
  768. @classmethod
  769. def tearDownClass(cls):
  770. if HAVE_NIX:
  771. cls.nixfile.close()
  772. def tearDown(self):
  773. self.io.close()
  774. def test_all_read(self):
  775. neo_blocks = self.io.read_all_blocks(cascade=True, lazy=False)
  776. nix_blocks = self.io.nix_file.blocks
  777. self.compare_blocks(neo_blocks, nix_blocks)
  778. def test_lazyload_fullcascade_read(self):
  779. neo_blocks = self.io.read_all_blocks(cascade=True, lazy=True)
  780. nix_blocks = self.io.nix_file.blocks
  781. # data objects should be empty
  782. for block in neo_blocks:
  783. for seg in block.segments:
  784. for asig in seg.analogsignals:
  785. self.assertEqual(len(asig), 0)
  786. for isig in seg.irregularlysampledsignals:
  787. self.assertEqual(len(isig), 0)
  788. for epoch in seg.epochs:
  789. self.assertEqual(len(epoch), 0)
  790. for event in seg.events:
  791. self.assertEqual(len(event), 0)
  792. for st in seg.spiketrains:
  793. self.assertEqual(len(st), 0)
  794. self.compare_blocks(neo_blocks, nix_blocks)
  795. def test_lazyload_lazycascade_read(self):
  796. neo_blocks = self.io.read_all_blocks(cascade="lazy", lazy=True)
  797. nix_blocks = self.io.nix_file.blocks
  798. self.compare_blocks(neo_blocks, nix_blocks)
  799. def test_lazycascade_read(self):
  800. def getitem(self, index):
  801. return self._data.__getitem__(index)
  802. from neo.io.nixio import LazyList
  803. getitem_original = LazyList.__getitem__
  804. LazyList.__getitem__ = getitem
  805. neo_blocks = self.io.read_all_blocks(cascade="lazy", lazy=False)
  806. for block in neo_blocks:
  807. self.assertIsInstance(block.segments, LazyList)
  808. self.assertIsInstance(block.channel_indexes, LazyList)
  809. for seg in block.segments:
  810. self.assertIsInstance(seg, string_types)
  811. for chx in block.channel_indexes:
  812. self.assertIsInstance(chx, string_types)
  813. LazyList.__getitem__ = getitem_original
  814. def test_load_lazy_cascade(self):
  815. from neo.io.nixio import LazyList
  816. neo_blocks = self.io.read_all_blocks(cascade="lazy", lazy=False)
  817. for block in neo_blocks:
  818. self.assertIsInstance(block.segments, LazyList)
  819. self.assertIsInstance(block.channel_indexes, LazyList)
  820. name = block.annotations["nix_name"]
  821. block = self.io.load_lazy_cascade("/" + name, lazy=False)
  822. self.assertIsInstance(block.segments, list)
  823. self.assertIsInstance(block.channel_indexes, list)
  824. for seg in block.segments:
  825. self.assertIsInstance(seg.analogsignals, list)
  826. self.assertIsInstance(seg.irregularlysampledsignals, list)
  827. self.assertIsInstance(seg.epochs, list)
  828. self.assertIsInstance(seg.events, list)
  829. self.assertIsInstance(seg.spiketrains, list)
  830. def test_nocascade_read(self):
  831. self.io._read_cascade = mock.Mock()
  832. neo_blocks = self.io.read_all_blocks(cascade=False)
  833. self.io._read_cascade.assert_not_called()
  834. for block in neo_blocks:
  835. self.assertEqual(len(block.segments), 0)
  836. nix_block = self.io.nix_file.blocks[block.annotations["nix_name"]]
  837. self.compare_attr(block, nix_block)
  838. def test_lazy_load_subschema(self):
  839. blk = self.io.nix_file.blocks[0]
  840. segpath = "/" + blk.name + "/segments/" + blk.groups[0].name
  841. segment = self.io.load_lazy_cascade(segpath, lazy=True)
  842. self.assertIsInstance(segment, Segment)
  843. self.assertEqual(segment.annotations["nix_name"], blk.groups[0].name)
  844. self.assertIs(segment.block, None)
  845. self.assertEqual(len(segment.analogsignals[0]), 0)
  846. segment = self.io.load_lazy_cascade(segpath, lazy=False)
  847. self.assertEqual(np.shape(segment.analogsignals[0]), (100, 3))
  848. class NixIOHashTest(NixIOTest):
  849. def setUp(self):
  850. self.hash = NixIO._hash_object
  851. def _hash_test(self, objtype, argfuncs):
  852. attr = {}
  853. for arg, func in argfuncs.items():
  854. attr[arg] = func()
  855. obj_one = objtype(**attr)
  856. obj_two = objtype(**attr)
  857. hash_one = self.hash(obj_one)
  858. hash_two = self.hash(obj_two)
  859. self.assertEqual(hash_one, hash_two)
  860. for arg, func in argfuncs.items():
  861. chattr = attr.copy()
  862. chattr[arg] = func()
  863. obj_two = objtype(**chattr)
  864. hash_two = self.hash(obj_two)
  865. self.assertNotEqual(
  866. hash_one, hash_two,
  867. "Hash test failed with different '{}'".format(arg)
  868. )
  869. def test_block_seg_hash(self):
  870. argfuncs = {"name": self.rword,
  871. "description": self.rsentence,
  872. "rec_datetime": self.rdate,
  873. "file_datetime": self.rdate,
  874. # annotations
  875. self.rword(): self.rword,
  876. self.rword(): lambda: self.rquant((10, 10), pq.mV)}
  877. self._hash_test(Block, argfuncs)
  878. self._hash_test(Segment, argfuncs)
  879. self._hash_test(Unit, argfuncs)
  880. def test_chx_hash(self):
  881. argfuncs = {"name": self.rword,
  882. "description": self.rsentence,
  883. "index": lambda: np.random.random(10).tolist(),
  884. "channel_names": lambda: self.rsentence(10).split(" "),
  885. "coordinates": lambda: [(np.random.random() * pq.cm,
  886. np.random.random() * pq.cm,
  887. np.random.random() * pq.cm)]*10,
  888. # annotations
  889. self.rword(): self.rword,
  890. self.rword(): lambda: self.rquant((10, 10), pq.mV)}
  891. self._hash_test(ChannelIndex, argfuncs)
  892. def test_analogsignal_hash(self):
  893. argfuncs = {"name": self.rword,
  894. "description": self.rsentence,
  895. "signal": lambda: self.rquant((10, 10), pq.mV),
  896. "sampling_rate": lambda: np.random.random() * pq.Hz,
  897. "t_start": lambda: np.random.random() * pq.sec,
  898. "t_stop": lambda: np.random.random() * pq.sec,
  899. # annotations
  900. self.rword(): self.rword,
  901. self.rword(): lambda: self.rquant((10, 10), pq.mV)}
  902. self._hash_test(AnalogSignal, argfuncs)
  903. def test_irregularsignal_hash(self):
  904. argfuncs = {"name": self.rword,
  905. "description": self.rsentence,
  906. "signal": lambda: self.rquant((10, 10), pq.mV),
  907. "times": lambda: self.rquant(10, pq.ms, True),
  908. # annotations
  909. self.rword(): self.rword,
  910. self.rword(): lambda: self.rquant((10, 10), pq.mV)}
  911. self._hash_test(IrregularlySampledSignal, argfuncs)
  912. def test_event_hash(self):
  913. argfuncs = {"name": self.rword,
  914. "description": self.rsentence,
  915. "times": lambda: self.rquant(10, pq.ms),
  916. "durations": lambda: self.rquant(10, pq.ms),
  917. "labels": lambda: self.rsentence(10).split(" "),
  918. # annotations
  919. self.rword(): self.rword,
  920. self.rword(): lambda: self.rquant((10, 10), pq.mV)}
  921. self._hash_test(Event, argfuncs)
  922. self._hash_test(Epoch, argfuncs)
  923. def test_spiketrain_hash(self):
  924. argfuncs = {"name": self.rword,
  925. "description": self.rsentence,
  926. "times": lambda: self.rquant(10, pq.ms, True),
  927. "t_start": lambda: -np.random.random() * pq.sec,
  928. "t_stop": lambda: np.random.random() * 100 * pq.sec,
  929. "waveforms": lambda: self.rquant((10, 10, 20), pq.mV),
  930. # annotations
  931. self.rword(): self.rword,
  932. self.rword(): lambda: self.rquant((10, 10), pq.mV)}
  933. self._hash_test(SpikeTrain, argfuncs)
  934. class NixIOPartialWriteTest(NixIOTest):
  935. filename = "testfile_partialwrite.h5"
  936. nixfile = None
  937. neo_blocks = None
  938. original_methods = dict()
  939. @classmethod
  940. def setUpClass(cls):
  941. if HAVE_NIX:
  942. cls.nixfile = cls.create_full_nix_file(cls.filename)
  943. def setUp(self):
  944. self.io = NixIO(self.filename, "rw")
  945. self.neo_blocks = self.io.read_all_blocks()
  946. self.original_methods["_write_attr_annotations"] =\
  947. self.io._write_attr_annotations
  948. @classmethod
  949. def tearDownClass(cls):
  950. if HAVE_NIX:
  951. cls.nixfile.close()
  952. def tearDown(self):
  953. self.restore_methods()
  954. self.io.close()
  955. def restore_methods(self):
  956. for name, method in self.original_methods.items():
  957. setattr(self.io, name, self.original_methods[name])
  958. def _mock_write_attr(self, objclass):
  959. typestr = str(objclass.__name__).lower()
  960. self.io._write_attr_annotations = mock.Mock(
  961. wraps=self.io._write_attr_annotations,
  962. side_effect=self.check_obj_type("neo.{}".format(typestr))
  963. )
  964. neo_blocks = self.neo_blocks
  965. self.modify_objects(neo_blocks, excludes=[objclass])
  966. self.io.write_all_blocks(neo_blocks)
  967. self.restore_methods()
  968. def check_obj_type(self, typestring):
  969. neq = self.assertNotEqual
  970. def side_effect_func(*args, **kwargs):
  971. obj = kwargs.get("nixobj", args[0])
  972. if isinstance(obj, list):
  973. for sig in obj:
  974. neq(sig.type, typestring)
  975. else:
  976. neq(obj.type, typestring)
  977. return side_effect_func
  978. @classmethod
  979. def modify_objects(cls, objs, excludes=()):
  980. excludes = tuple(excludes)
  981. for obj in objs:
  982. if not (excludes and isinstance(obj, excludes)):
  983. obj.description = cls.rsentence()
  984. for container in getattr(obj, "_child_containers", []):
  985. children = getattr(obj, container)
  986. cls.modify_objects(children, excludes)
  987. def test_partial(self):
  988. for objclass in NixIO.supported_objects:
  989. self._mock_write_attr(objclass)
  990. self.compare_blocks(self.neo_blocks, self.io.nix_file.blocks)
  991. def test_no_modifications(self):
  992. self.io._write_attr_annotations = mock.Mock()
  993. self.io.write_all_blocks(self.neo_blocks)
  994. self.io._write_attr_annotations.assert_not_called()
  995. self.compare_blocks(self.neo_blocks, self.io.nix_file.blocks)
  996. # clearing hashes and checking again
  997. for k in self.io._object_hashes.keys():
  998. self.io._object_hashes[k] = None
  999. self.io.write_all_blocks(self.neo_blocks)
  1000. self.io._write_attr_annotations.assert_not_called()
  1001. # changing hashes to force rewrite
  1002. for k in self.io._object_hashes.keys():
  1003. self.io._object_hashes[k] = "_"
  1004. self.io.write_all_blocks(self.neo_blocks)
  1005. callcount = self.io._write_attr_annotations.call_count
  1006. self.assertEqual(callcount, len(self.io._object_hashes))
  1007. self.compare_blocks(self.neo_blocks, self.io.nix_file.blocks)
  1008. class NixIOContextTests(NixIOTest):
  1009. filename = "context_test.h5"
  1010. def test_context_write(self):
  1011. neoblock = Block(name=self.rword(), description=self.rsentence())
  1012. with NixIO(self.filename, "ow") as iofile:
  1013. iofile.write_block(neoblock)
  1014. nixfile = nix.File.open(self.filename, nix.FileMode.ReadOnly,
  1015. backend="h5py")
  1016. self.compare_blocks([neoblock], nixfile.blocks)
  1017. nixfile.close()
  1018. neoblock.annotate(**self.rdict(5))
  1019. with NixIO(self.filename, "rw") as iofile:
  1020. iofile.write_block(neoblock)
  1021. nixfile = nix.File.open(self.filename, nix.FileMode.ReadOnly,
  1022. backend="h5py")
  1023. self.compare_blocks([neoblock], nixfile.blocks)
  1024. nixfile.close()
  1025. def test_context_read(self):
  1026. nixfile = nix.File.open(self.filename, nix.FileMode.Overwrite,
  1027. backend="h5py")
  1028. name_one = self.rword()
  1029. name_two = self.rword()
  1030. nixfile.create_block(name_one, "neo.block")
  1031. nixfile.create_block(name_two, "neo.block")
  1032. nixfile.close()
  1033. with NixIO(self.filename, "ro") as iofile:
  1034. blocks = iofile.read_all_blocks()
  1035. self.assertEqual(blocks[0].annotations["nix_name"], name_one)
  1036. self.assertEqual(blocks[1].annotations["nix_name"], name_two)
  1037. @unittest.skipUnless(HAVE_NIX, "Requires NIX")
  1038. class CommonTests(BaseTestIO, unittest.TestCase):
  1039. ioclass = NixIO
  1040. read_and_write_is_bijective = False