def test_nodetype(self): mock_node = MagicMock(id=333) mock_node.type = 'vos:ContainerNode' client = Client() client.get_node = Mock(return_value=mock_node) self.assertEquals('vos:ContainerNode', client._node_type('vos:/somenode')) self.assertTrue(client.isdir('vos:/somenode')) mock_node.type = 'vos:DataNode' self.assertEquals('vos:DataNode', client._node_type('vos:/somenode')) self.assertTrue(client.isfile('vos:/somenode')) # through a link mock_node.type = 'vos:ContainerNode' mock_link_node = Mock(type='vos:LinkNode') mock_link_node.target = 'vos:/somefile' client.get_node = Mock( side_effect=[mock_link_node, mock_node, mock_link_node, mock_node]) self.assertEquals('vos:ContainerNode', client._node_type('vos:/somenode')) self.assertTrue(client.isdir('vos:/somenode')) # through an external link - not sure why the type is DataNode in this case??? mock_link_node.target = '/somefile' client.get_node = Mock(side_effect=[mock_link_node, mock_link_node]) self.assertEquals('vos:DataNode', client._node_type('vos:/somenode')) self.assertTrue(client.isfile('vos:/somenode'))
def test_nodetype(self): mock_node = MagicMock(id=333) mock_node.type = 'vos:ContainerNode' client = Client() client.get_node = Mock(return_value=mock_node) self.assertEquals('vos:ContainerNode', client._node_type('vos:/somenode')) self.assertTrue(client.isdir('vos:/somenode')) mock_node.type = 'vos:DataNode' self.assertEquals('vos:DataNode', client._node_type('vos:/somenode')) self.assertTrue(client.isfile('vos:/somenode')) # through a link mock_node.type = 'vos:ContainerNode' mock_link_node = Mock(type='vos:LinkNode') mock_link_node.target = 'vos:/somefile' client.get_node = Mock(side_effect=[mock_link_node, mock_node, mock_link_node, mock_node]) self.assertEquals('vos:ContainerNode', client._node_type('vos:/somenode')) self.assertTrue(client.isdir('vos:/somenode')) # through an external link - not sure why the type is DataNode in # this case??? mock_link_node.target = '/somefile' client.get_node = Mock(side_effect=[mock_link_node, mock_link_node]) self.assertEquals('vos:DataNode', client._node_type('vos:/somenode')) self.assertTrue(client.isfile('vos:/somenode'))
def test_copy(self, computed_md5_mock): # the md5sum of the file being copied md5sum = 'd41d8cd98f00b204e9800998ecf84eee' # patch the compute_md5 function in vos to return the above value computed_md5_mock.return_value = md5sum #mock the props of the corresponding node props = MagicMock() props.get.return_value = md5sum #add props to the mocked node node = MagicMock(spec=Node) node.props = props # mock one by one the chain of connection.session.response.headers conn = MagicMock(spec=Connection) session = MagicMock() response = MagicMock() headers = MagicMock() headers.get.return_value = md5sum response.headers = headers session.get.return_value = response conn.session = session test_client = Client() # use the mocked connection instead of the real one test_client.conn = conn get_node_url_mock = Mock( return_value=['http://cadc.ca/test', 'http://cadc.ca/test']) test_client.get_node_url = get_node_url_mock #patch Client.get_node to return our mocked node get_node_mock = Mock(return_value=node) test_client.get_node = get_node_mock # time to test... vospaceLocation = 'vos://test/foo' osLocation = '/tmp/foo' # copy from vospace test_client.copy(vospaceLocation, osLocation) get_node_url_mock.assert_called_once_with(vospaceLocation, method='GET', cutout=None, view='data') computed_md5_mock.assert_called_once_with(osLocation) get_node_mock.assert_called_once_with(vospaceLocation) # copy to vospace get_node_url_mock.reset_mock() computed_md5_mock.reset_mock() test_client.copy(osLocation, vospaceLocation) get_node_url_mock.assert_called_once_with(vospaceLocation, 'PUT') computed_md5_mock.assert_called_once_with(osLocation) # error tests - md5sum mismatch computed_md5_mock.return_value = '000bad000' with self.assertRaises(OSError): test_client.copy(vospaceLocation, osLocation) with self.assertRaises(OSError): test_client.copy(osLocation, vospaceLocation)
def test_getNode(self): """ @return: """ uri = "vos://foo.com!vospace/bar" nodes = (' <vos:nodes>\n' '<vos:node uri="vos://cadc.nrc.ca!vospace/mydir/file123" ' 'xs:type="vos:DataNode">\n' ' <vos:properties>\n' ' <vos:property ' 'uri="ivo://ivoa.net/vospace/core#date">2016-05-10T09:52:13' '</vos:property>\n' ' </vos:properties>\n' '</vos:node>\n' '<vos:node uri="vos://cadc.nrc.ca!vospace/mydir/file456" ' 'xs:type="vos:DataNode">\n' ' <vos:properties>\n' ' <vos:property uri="ivo://ivoa.net/vospace/core#date">' '2016-05-19T09:52:14</vos:property>\n' ' </vos:properties>\n' '</vos:node>\n' '</vos:nodes>\n') mock_vofile = Mock() client = Client() client.open = Mock(return_value=mock_vofile) mock_vofile.read = Mock( return_value=NODE_XML.format(uri, '').encode('UTF-8')) my_node = client.get_node(uri, limit=0, force=False) self.assertEqual(uri, my_node.uri) self.assertEqual(len(my_node.node_list), 0) mock_vofile.read = Mock( return_value=NODE_XML.format(uri, nodes).encode('UTF-8')) my_node = client.get_node(uri, limit=2, force=True) self.assertEqual(uri, my_node.uri) self.assertEqual(len(my_node.node_list), 2) my_node = client.get_node(uri, limit=2, force=False) self.assertEqual(uri, my_node.uri) self.assertEqual(len(my_node.node_list), 2)
def test_get_info_list(self): # list tuples of a LinkNode mock_node = MagicMock(type='vos:DataNode') mock_node.return_value = mock_node mock_node.name = 'testnode' mock_node.get_info.return_value = {'name': 'aa'} mock_link_node = Mock(type='vos:LinkNode') mock_link_node.target = 'vos:/somefile' client = Client() client.get_node = MagicMock(side_effect=[mock_link_node, mock_node]) self.assertEquals([mock_node], client.get_children_info('vos:/somenode'))
def test_add_props(self): old_node = Node(ElementTree.fromstring(NODE_XML)) new_node = Node(ElementTree.fromstring(NODE_XML)) new_node.props['quota'] = '1000' new_node.create = Mock(return_value=new_node.node) data = str(new_node) headers = {'size': str(len(data))} client = Client() client.get_node = Mock(return_value=old_node) client.get_node_url = Mock(return_value='http://foo.com/bar') client.conn = Mock() with patch('vos.Client', client) as mock: mock.add_props(new_node) mock.conn.session.post.assert_called_with('http://foo.com/bar', headers=headers, data=data)
def test_add_props(self): old_node = Node(ElementTree.fromstring(NODE_XML)) old_node.uri = 'vos:sometest' new_node = Node(ElementTree.fromstring(NODE_XML)) new_node.props['quota'] = '1000' new_node.create = Mock(return_value=new_node.node) data = str(new_node) headers = {'size': str(len(data))} client = Client() client.get_node = Mock(return_value=old_node) client.get_node_url = Mock(return_value='http://foo.com/bar') mock_session = Mock() client.get_session = Mock(return_value=mock_session) client.add_props(new_node) client.get_session.assert_called_with('vos://foo.com!vospace/bar') mock_session.post.assert_called_with('http://foo.com/bar', headers=headers, data=data)
def test_glob(self): # test the pattern matches in directories and file names # simple test for listing of directory, no wild cards # NOTE: Mock class also has a 'name' attribute so we cannot # instantiate a mock node with Mock(name='blah'). There are # two other wasy to do it as seen below mock_node = MagicMock(type='vos:ContainerNode') mock_node.configure_mock(name='anode') client = Client() client.get_node = Mock(return_value=mock_node) self.assertEquals(['vos:/anode/'], client.glob('vos:/anode/')) # simple test for file listing of file mock_node = MagicMock(type='vos:DataNode') mock_node.configure_mock(name='afile') client = Client() client.get_node = Mock(return_value=mock_node) self.assertEquals(['vos:/afile'], client.glob('vos:/afile')) # create a mock directory structure on the form # /anode/abc /anode/def - > anode/a* should return # /anode/adc mock_node = MagicMock(type='vos:ContainerNode') mock_node.configure_mock(name='anode') mock_child_node1 = Mock(type='vos:DataNode') mock_child_node1.name = 'abc' mock_child_node2 = Mock(type='vos:DataNode') mock_child_node2.name = 'def' # because we use wild characters in the root node, # we need to create a corresponding node for the base node mock_base_node = Mock(type='vos:ContainerNode') mock_base_node.name = 'vos:' mock_base_node.node_list = [mock_node] mock_node.node_list = [ mock_base_node, mock_child_node1, mock_child_node2 ] client = Client() client.get_node = Mock( side_effect=[mock_node, mock_base_node, mock_node]) self.assertEquals(['vos:/anode/abc'], client.glob('vos:/anode/a*')) self.assertEquals(['vos:/anode/abc'], client.glob('vos:/*node/abc')) # test nodes: # /anode/.test1 /bnode/sometests /bnode/blah # /[a,c]node/*test* should return /bnode/somtests (.test1 is filtered # out as a special file) mock_child_node1 = Mock(type='vos:DataNode') mock_child_node1.name = '.test1' mock_node1 = MagicMock(type='vos:ContainerNode') mock_node1.configure_mock(name='anode') mock_node1.node_list = [mock_child_node1] mock_child_node2 = Mock(type='vos:DataNode') mock_child_node2.name = 'sometests' mock_child_node3 = Mock(type='vos:DataNode') mock_child_node3.name = 'blah' mock_node2 = MagicMock(type='vos:ContainerNode') mock_node2.configure_mock(name='bnode') mock_node2.node_list = [mock_child_node2, mock_child_node3] # because we use wild characters in the root node, # we need to create a corresponding node for the base node mock_base_node = Mock(type='vos:DataNode') mock_base_node.name = 'vos:' mock_base_node.node_list = [mock_node1, mock_node2] client = Client() client.get_node = Mock( side_effect=[mock_base_node, mock_node1, mock_node2]) self.assertEquals(['vos:/bnode/sometests'], client.glob('vos:/[a,b]node/*test*'))
def test_copy(self, computed_md5_mock): # the md5sum of the file being copied md5sum = 'd41d8cd98f00b204e9800998ecf84eee' # patch the compute_md5 function in vos to return the above value computed_md5_mock.return_value = md5sum # mock the props of the corresponding node props = MagicMock() props.get.return_value = md5sum # add props to the mocked node node = MagicMock(spec=Node) node.props = props # mock one by one the chain of connection.session.response.headers conn = MagicMock(spec=Connection) session = MagicMock() response = MagicMock() headers = MagicMock() headers.get.return_value = md5sum response.headers = headers session.get.return_value = response conn.session = session test_client = Client() # use the mocked connection instead of the real one test_client.conn = conn get_node_url_mock = Mock( return_value=['http://cadc.ca/test', 'http://cadc.ca/test']) test_client.get_node_url = get_node_url_mock mock_update = Mock() test_client.update = mock_update # patch Client.get_node to return our mocked node get_node_mock = Mock(return_value=node) test_client.get_node = get_node_mock # time to test... vospaceLocation = 'vos://test/foo' osLocation = '/tmp/foo' if os.path.isfile(osLocation): os.remove(osLocation) # copy from vospace test_client.copy(vospaceLocation, osLocation) get_node_url_mock.assert_called_once_with(vospaceLocation, method='GET', cutout=None, view='data') computed_md5_mock.assert_called_once_with(osLocation) assert get_node_mock.called # repeat - local file and vospace file are now the same -> only # get_node is called to get the md5 of remote file get_node_url_mock.reset_mock() computed_md5_mock.reset_mock() get_node_mock.reset_mock() test_client.copy(vospaceLocation, osLocation) assert not get_node_url_mock.called computed_md5_mock.assert_called_once_with(osLocation) get_node_mock.assert_called_once_with(vospaceLocation) # change the content of local files to trigger a new copy get_node_url_mock.reset_mock() computed_md5_mock.reset_mock() computed_md5_mock.side_effect = ['d002233', md5sum] get_node_mock.reset_mock() test_client.copy(vospaceLocation, osLocation) get_node_url_mock.assert_called_once_with(vospaceLocation, method='GET', cutout=None, view='data') computed_md5_mock.assert_called_with(osLocation) get_node_mock.assert_called_once_with(vospaceLocation) # copy to vospace when md5 sums are the same -> only update occurs get_node_url_mock.reset_mock() computed_md5_mock.reset_mock() computed_md5_mock.side_effect = None computed_md5_mock.return_value = md5sum test_client.copy(osLocation, vospaceLocation) mock_update.assert_called_once() assert not get_node_url_mock.called # make md5 different get_node_url_mock.reset_mock() get_node_url_mock.return_value =\ ['http://cadc.ca/test', 'http://cadc.ca/test'] computed_md5_mock.reset_mock() mock_update.reset_mock() props.get.side_effect = ['d00223344', md5sum] test_client.copy(osLocation, vospaceLocation) assert not mock_update.called get_node_url_mock.assert_called_once_with(vospaceLocation, 'PUT') computed_md5_mock.assert_called_once_with(osLocation) # copy 0 size file -> delete and create on client but no bytes # transferred get_node_url_mock.reset_mock() computed_md5_mock.reset_mock() computed_md5_mock.return_value = vos.ZERO_MD5 props.get.side_effect = [md5sum] mock_delete = Mock() mock_create = Mock() test_client.delete = mock_delete test_client.create = mock_create test_client.copy(osLocation, vospaceLocation) mock_create.assert_called_once_with(vospaceLocation) mock_delete.assert_called_once_with(vospaceLocation) assert not get_node_url_mock.called # copy new 0 size file -> reate on client but no bytes transferred get_node_url_mock.reset_mock() computed_md5_mock.reset_mock() mock_delete.reset_mock() mock_create.reset_mock() computed_md5_mock.return_value = vos.ZERO_MD5 props.get.side_effect = [None] mock_delete = Mock() mock_create = Mock() test_client.delete = mock_delete test_client.create = mock_create test_client.copy(osLocation, vospaceLocation) mock_create.assert_called_once_with(vospaceLocation) assert not mock_delete.called assert not get_node_url_mock.called # error tests - md5sum mismatch props.get.side_effect = [md5sum] computed_md5_mock.return_value = '000bad000' with self.assertRaises(OSError): test_client.copy(vospaceLocation, osLocation) with self.assertRaises(OSError): test_client.copy(osLocation, vospaceLocation) # requests just the headers props.get.side_effect = [None] get_node_url_mock = Mock( return_value=['http://cadc.ca/test', 'http://cadc.ca/test']) test_client.get_node_url = get_node_url_mock computed_md5_mock.reset_mock() computed_md5_mock.side_effect = ['d002233', md5sum] get_node_mock.reset_mock() test_client.copy(vospaceLocation, osLocation, head=True) get_node_url_mock.assert_called_once_with(vospaceLocation, method='GET', cutout=None, view='header')
def test_copy(self, computed_md5_mock): file_content = 'File content'.encode('utf-8') # the md5sum of the file being copied transfer_md5 = hashlib.md5() transfer_md5.update(file_content) md5sum = transfer_md5.hexdigest() # patch the compute_md5 function in vos to return the above value computed_md5_mock.return_value = md5sum # mock the props of the corresponding node props = MagicMock() props.get.return_value = md5sum # add props to the mocked node node = MagicMock(spec=Node) node.props = {'MD5': md5sum, 'length': 12} # mock one by one the chain of connection.session.response.headers session = MagicMock() response = MagicMock() headers = MagicMock() headers.get.return_value = md5sum response.headers = headers session.get.return_value = response response.iter_content.return_value = BytesIO(file_content) test_client = Client() test_client.get_session = Mock(return_value=session) # use the mocked connection instead of the real one get_node_url_mock = Mock( return_value=['http://cadc.ca/test', 'http://cadc.ca/test']) test_client.get_node_url = get_node_url_mock mock_update = Mock() test_client.update = mock_update # patch Client.get_node to return our mocked node get_node_mock = Mock(return_value=node) test_client.get_node = get_node_mock # time to test... vospaceLocation = 'vos://test/foo' osLocation = '/tmp/foo' if os.path.isfile(osLocation): os.remove(osLocation) # copy from vospace test_client.copy(vospaceLocation, osLocation) get_node_url_mock.assert_called_once_with(vospaceLocation, method='GET', cutout=None, view='data') assert not computed_md5_mock.called,\ 'MD5 should be computed on the fly' assert get_node_mock.called # repeat - local file and vospace file are now the same -> only # get_node is called to get the md5 of remote file get_node_url_mock.reset_mock() computed_md5_mock.reset_mock() get_node_mock.reset_mock() props.reset_mock() props.get.return_value = md5sum test_client.copy(vospaceLocation, osLocation) assert not get_node_url_mock.called computed_md5_mock.assert_called_once_with(osLocation) get_node_mock.assert_called_once_with(vospaceLocation, force=True) # change the content of local files to trigger a new copy get_node_url_mock.reset_mock() get_node_mock.reset_mock() computed_md5_mock.reset_mock() computed_md5_mock.return_value = 'd002233' response.iter_content.return_value = BytesIO(file_content) test_client.copy(vospaceLocation, osLocation) get_node_url_mock.assert_called_once_with(vospaceLocation, method='GET', cutout=None, view='data') computed_md5_mock.assert_called_with(osLocation) get_node_mock.assert_called_once_with(vospaceLocation, force=True) # change the content of local files to trigger a new copy get_node_url_mock.reset_mock() get_node_url_mock.return_value = \ ['https://mysite.com/node/node123/cutout'] computed_md5_mock.reset_mock() computed_md5_mock.return_value = 'd002233' # computed_md5_mock.side_effect = ['d002233', md5sum] get_node_mock.reset_mock() response.iter_content.return_value = BytesIO(file_content) session.get.return_value = response test_client.get_session = Mock(return_value=session) test_client.copy('{}{}'.format(vospaceLocation, '[1][10:60]'), osLocation) get_node_url_mock.assert_called_once_with( vospaceLocation, method='GET', cutout='[1][10:60]', view='cutout') # copy to vospace when md5 sums are the same -> only update occurs get_node_url_mock.reset_mock() computed_md5_mock.reset_mock() computed_md5_mock.side_effect = None computed_md5_mock.return_value = md5sum test_client.copy(osLocation, vospaceLocation) mock_update.assert_called_once() assert not get_node_url_mock.called # make md5 different on destination get_node_url_mock.reset_mock() get_node_url_mock.return_value =\ ['http://cadc.ca/test', 'http://cadc.ca/test'] computed_md5_mock.reset_mock() mock_update.reset_mock() computed_md5_mock.return_value = md5sum to_update_node = MagicMock() to_update_node.props = {'MD5': 'abcde', 'length': 12} test_client.get_node = Mock(side_effect=[to_update_node, node]) test_client.copy(osLocation, vospaceLocation) assert not mock_update.called get_node_url_mock.assert_called_once_with(vospaceLocation, 'PUT', content_length=12, md5_checksum='abcde') computed_md5_mock.assert_called_once_with(osLocation) # copy 0 size file -> delete and create node but no bytes # transferred get_node_url_mock.reset_mock() computed_md5_mock.reset_mock() test_client.get_node = Mock(return_value=node) node.props['length'] = 0 mock_delete = Mock() mock_create = Mock() test_client.delete = mock_delete test_client.create = mock_create with patch('vos.vos.os.stat', Mock()) as stat_mock: stat_mock.return_value = Mock(st_size=0) test_client.copy(osLocation, vospaceLocation) mock_create.assert_called_once_with(vospaceLocation) mock_delete.assert_called_once_with(vospaceLocation) assert not get_node_url_mock.called # copy new 0 size file -> create node but no bytes transferred get_node_url_mock.reset_mock() computed_md5_mock.reset_mock() mock_delete.reset_mock() mock_create.reset_mock() test_client.get_node = Mock(side_effect=[Exception(), node]) mock_delete = Mock() mock_create = Mock() test_client.delete = mock_delete test_client.create = mock_create with patch('vos.vos.os.stat', Mock()) as stat_mock: stat_mock.return_value = Mock(st_size=0) test_client.copy(osLocation, vospaceLocation) mock_create.assert_called_once_with(vospaceLocation) assert not mock_delete.called assert not get_node_url_mock.called # error tests - md5sum mismatch node.props['length'] = 12 computed_md5_mock.return_value = '000bad000' test_client.get_node = Mock(return_value=node) with self.assertRaises(OSError): test_client.copy(vospaceLocation, osLocation) # existing file mock_delete.reset_mock() with self.assertRaises(OSError): with patch('vos.vos.os.stat', Mock()) as stat_mock: stat_mock.return_value = Mock(st_size=12) test_client.copy(osLocation, vospaceLocation) assert not mock_delete.called # server takes care of cleanup # new file mock_delete.reset_mock() with self.assertRaises(OSError): with patch('vos.vos.os.stat', Mock()) as stat_mock: stat_mock.return_value = Mock(st_size=12) node.props['MD5'] = None test_client.copy(osLocation, vospaceLocation) assert mock_delete.called # cleanup required # requests just the headers when md5 not provided in the header props.get.side_effect = [None] get_node_url_mock = Mock( return_value=['http://cadc.ca/test', 'http://cadc.ca/test']) test_client.get_node_url = get_node_url_mock get_node_mock.reset_mock() headers.get.return_value = None test_client.copy(vospaceLocation, osLocation, head=True) get_node_url_mock.assert_called_once_with(vospaceLocation, method='GET', cutout=None, view='header') # repeat headers request when md5 provided in the header props.get.side_effect = md5sum get_node_url_mock = Mock( return_value=['http://cadc.ca/test', 'http://cadc.ca/test']) test_client.get_node_url = get_node_url_mock get_node_mock.reset_mock() response.iter_content.return_value = BytesIO(file_content) headers.get.return_value = None test_client.copy(vospaceLocation, osLocation, head=True) get_node_url_mock.assert_called_once_with(vospaceLocation, method='GET', cutout=None, view='header')
def test_glob(self): # test the pattern matches in directories and file names # simple test for listing of directory, no wild cards # NOTE: Mock class also has a 'name' attribute so we cannot # instantiate a mock node with Mock(name='blah'). There are # two other wasy to do it as seen below mock_node = MagicMock(type='vos:ContainerNode') mock_node.configure_mock(name='anode') client = Client() client.get_node = Mock(return_value=mock_node) self.assertEquals(['vos:/anode/'], client.glob('vos:/anode/')) # simple test for file listing of file mock_node = MagicMock(type='vos:DataNode') mock_node.configure_mock(name='afile') client = Client() client.get_node = Mock(return_value=mock_node) self.assertEquals(['vos:/afile'], client.glob('vos:/afile')) # create a mock directory structure on the form # /anode/abc /anode/def - > anode/a* should return # /anode/adc mock_node = MagicMock(type='vos:ContainerNode') mock_node.configure_mock(name='anode') mock_child_node1 = Mock(type='vos:DataNode') mock_child_node1.name = 'abc' mock_child_node2 = Mock(type='vos:DataNode') mock_child_node2.name = 'def' # because we use wild characters in the root node, # we need to create a corresponding node for the base node mock_base_node = Mock(type='vos:ContainerNode') mock_base_node.name = 'vos:' mock_base_node.node_list = [mock_node] mock_node.node_list = [mock_base_node, mock_child_node1, mock_child_node2] client = Client() client.get_node = Mock( side_effect=[mock_node, mock_base_node, mock_node]) self.assertEquals(['vos:/anode/abc'], client.glob('vos:/anode/a*')) self.assertEquals(['vos:/anode/abc'], client.glob('vos:/*node/abc')) # test nodes: # /anode/.test1 /bnode/sometests /bnode/blah # /[a,c]node/*test* should return /bnode/somtests (.test1 is filtered # out as a special file) mock_node1 = MagicMock(type='vos:ContainerNode') mock_node1.configure_mock(name='anode') mock_node1.node_list = [mock_child_node1] mock_child_node2 = Mock(type='vos:DataNode') mock_child_node2.name = 'sometests' mock_child_node3 = Mock(type='vos:DataNode') mock_child_node3.name = 'blah' mock_node2 = MagicMock(type='vos:ContainerNode') mock_node2.configure_mock(name='bnode') mock_node2.node_list = [mock_child_node2, mock_child_node3] # because we use wild characters in the root node, # we need to create a corresponding node for the base node mock_base_node = Mock(type='vos:DataNode') mock_base_node.name = 'vos:' mock_base_node.node_list = [mock_node1, mock_node2] client = Client() client.get_node = Mock( side_effect=[mock_base_node, mock_node1, mock_node2]) self.assertEquals(['vos:/bnode/sometests'], client.glob('vos:/[a,b]node/*test*'))
from vos import Client c = Client() #for expnum in c.listdir('vos:cfis/solar_system/dbimages', force=True): for expnum in open('expnum_list.txt').readlines(): expnum = expnum.strip() n = c.get_node('vos:cfis/solar_system/dbimages/{}'.format(expnum), force=True) print n.uri for prop in n.props: if 'stationary' in prop: n.props[prop] = None c.add_props(n) print("Done")