From a42c8aa8387ea9e0e3387be7381692d3607c326d Mon Sep 17 00:00:00 2001 From: Jeff Hull Date: Fri, 24 Apr 2020 12:58:13 -0400 Subject: [PATCH] add Object getattr for #287 --- solvebio/cli/data.py | 23 +++++++++++++---------- solvebio/resource/dataset.py | 5 +++++ solvebio/resource/object.py | 30 ++++++++++++++++++++---------- solvebio/test/client_mocks.py | 10 +++++++++- solvebio/test/test_shortcuts.py | 33 ++++++++++++++++----------------- 5 files changed, 63 insertions(+), 38 deletions(-) diff --git a/solvebio/cli/data.py b/solvebio/cli/data.py index 9694acc4..88e1d3dd 100644 --- a/solvebio/cli/data.py +++ b/solvebio/cli/data.py @@ -14,6 +14,9 @@ from solvebio import Vault from solvebio import Object +from solvebio import Dataset +from solvebio import DatasetImport +from solvebio import DatasetTemplate from solvebio.utils.files import check_gzip_path from solvebio.errors import SolveError from solvebio.errors import NotFoundError @@ -157,11 +160,11 @@ def _create_template_from_file(template_file, dry_run=False): sys.exit(1) if dry_run: - template = solvebio.DatasetTemplate(**template_json) + template = DatasetTemplate(**template_json) print("A new dataset template will be created from: {0}" .format(template_file)) else: - template = solvebio.DatasetTemplate.create(**template_json) + template = DatasetTemplate.create(**template_json) print("A new dataset template was created with id: {0}" .format(template.id)) @@ -189,7 +192,7 @@ def create_dataset(args, template=None): try: # Fail if a dataset already exists. - solvebio.Dataset.get_by_full_path(full_path) + Object.get_by_full_path(full_path, assert_type='dataset') print('A dataset already exists at path: {0}'.format(full_path)) sys.exit(1) except NotFoundError: @@ -202,8 +205,8 @@ def create_dataset(args, template=None): pass elif args.template_id: try: - template = solvebio.DatasetTemplate.retrieve(args.template_id) - except solvebio.SolveError as e: + template = DatasetTemplate.retrieve(args.template_id) + except SolveError as e: if e.status_code != 404: raise e print("No template with ID {0} found!".format(args.template_id)) @@ -256,7 +259,7 @@ def create_dataset(args, template=None): print("Metadata: {}".format(metadata)) return - return solvebio.Dataset.get_or_create_by_full_path( + return Dataset.get_or_create_by_full_path( full_path, capacity=args.capacity, fields=fields, @@ -394,8 +397,8 @@ def import_file(args): if args.template_id: try: - template = solvebio.DatasetTemplate.retrieve(args.template_id) - except solvebio.SolveError as e: + template = DatasetTemplate.retrieve(args.template_id) + except SolveError as e: if e.status_code != 404: raise e print("No template with ID {0} found!".format(args.template_id)) @@ -410,7 +413,7 @@ def import_file(args): dataset = create_dataset(args, template=template) else: try: - dataset = solvebio.Dataset.get_by_full_path(full_path) + dataset = Object.get_by_full_path(full_path, assert_type='dataset') except solvebio.errors.NotFoundError: print("Dataset not found: {0}".format(full_path)) print("Tip: use the --create-dataset flag " @@ -442,7 +445,7 @@ def import_file(args): kwargs.update(template.import_params) # Create the import - import_ = solvebio.DatasetImport.create( + import_ = DatasetImport.create( dataset_id=dataset.id, commit_mode=args.commit_mode, **kwargs diff --git a/solvebio/resource/dataset.py b/solvebio/resource/dataset.py index e2af79e7..0d43e01b 100644 --- a/solvebio/resource/dataset.py +++ b/solvebio/resource/dataset.py @@ -38,6 +38,10 @@ class Dataset(CreateableAPIResource, def make_full_path(cls, vault_name, path, name, **kwargs): from solvebio import SolveError + print('[Deprecated] Warning this method has been deprecated' + 'and will be removed in a future released.' + 'Use self.vault_object.full_path') + _client = kwargs.pop('client', None) or cls._client or client try: @@ -69,6 +73,7 @@ def get_or_create_by_full_path(cls, full_path, **kwargs): from solvebio import Object # Assert this is a dataset kwargs['assert_type'] = 'dataset' + kwargs['object_type'] = 'dataset' return Object.get_or_create_by_full_path(full_path, **kwargs) def saved_queries(self, **params): diff --git a/solvebio/resource/object.py b/solvebio/resource/object.py index b565233f..749121b3 100644 --- a/solvebio/resource/object.py +++ b/solvebio/resource/object.py @@ -418,17 +418,27 @@ def objects(self, **params): def ls(self, **params): return self.objects(**params) - def query(self, query=None, **params): - """Shortcut to query the underlying Dataset object""" - from solvebio import Dataset + def __getattr__(self, name): + """Shortcut to access attributes of the underlying Dataset resource""" + from solvebio.resource import Dataset + + valid_attrs = [ + # query data + 'query', 'lookup', + # transform data + 'import_file', 'export', 'migrate', + # dataset meta + 'fields', 'template', 'imports', 'commits', + # helpers + 'activity', 'saved_queries' + ] + if name in valid_attrs and self.is_dataset: + return getattr(Dataset(self.dataset_id, client=self._client), name) - if not self.is_dataset: - raise SolveError( - "The query method can only be used by a dataset. Found a {}" - .format(self.object_type)) - - return Dataset(self.dataset_id, client=self._client).query( - query=query, **params) + try: + return self[name] + except KeyError as err: + raise AttributeError(*err.args) @property def parent(self): diff --git a/solvebio/test/client_mocks.py b/solvebio/test/client_mocks.py index 6476c869..f47fdf3c 100644 --- a/solvebio/test/client_mocks.py +++ b/solvebio/test/client_mocks.py @@ -41,7 +41,7 @@ def solve_objects(self): return ExtendedList([self.create()]) def update_paths(self): - """Sets vault_name, path and full_path""" + """Sets vault_name, path and full_path based on mock inputs""" if not self.object['vault_name']: self.object['vault_name'] = 'mock_vault' @@ -55,6 +55,11 @@ def update_paths(self): self.object['full_path'] = 'solvebio:{0}:{1}'.format( self.object['vault_name'], self.object['path']) + def update_dataset(self): + """Sets dataset_id if object is a dataset""" + if self.object['object_type'] == 'dataset': + self.object['dataset_id'] = self.object['id'] + class FakeMigrationResponse(Fake201Response): @@ -122,6 +127,7 @@ def __init__(self, data): } self.object.update(data) self.update_paths() + self.update_dataset() class FakeDatasetResponse(Fake201Response): @@ -220,6 +226,8 @@ def fake_object_retrieve(*args, **kwargs): def fake_dataset_create(*args, **kwargs): + # Dataset.create() is deprecated + print("WARNING: Your code likely wants to be using FakeObjectCreate.") return FakeDatasetResponse(kwargs).create() diff --git a/solvebio/test/test_shortcuts.py b/solvebio/test/test_shortcuts.py index 38ac5b61..b83c01b1 100644 --- a/solvebio/test/test_shortcuts.py +++ b/solvebio/test/test_shortcuts.py @@ -20,7 +20,6 @@ from solvebio.test.client_mocks import fake_object_all from solvebio.test.client_mocks import fake_object_create from solvebio.test.client_mocks import fake_object_retrieve -from solvebio.test.client_mocks import fake_dataset_create from solvebio.test.client_mocks import fake_dataset_tmpl_create from solvebio.test.client_mocks import fake_dataset_tmpl_retrieve from solvebio.test.client_mocks import fake_dataset_import_create @@ -43,23 +42,23 @@ def setUp(self): @mock.patch('solvebio.resource.Vault.all') @mock.patch('solvebio.resource.Object.all') - @mock.patch('solvebio.resource.Dataset.create') + @mock.patch('solvebio.resource.Object.create') def test_create_dataset(self, DatasetCreate, ObjectAll, VaultAll): - DatasetCreate.side_effect = fake_dataset_create + DatasetCreate.side_effect = fake_object_create ObjectAll.side_effect = fake_object_all VaultAll.side_effect = fake_vault_all args = ['create-dataset', 'solvebio:test_vault:/test-dataset', '--capacity', 'small'] ds = main.main(args) - self.assertEqual(ds.name, 'test-dataset') + self.assertEqual(ds.filename, 'test-dataset') self.assertEqual(ds.path, '/test-dataset') @mock.patch('solvebio.resource.Vault.all') @mock.patch('solvebio.resource.Object.all') - @mock.patch('solvebio.resource.Dataset.create') + @mock.patch('solvebio.resource.Object.create') def test_create_dataset_by_filename(self, DatasetCreate, ObjectAll, VaultAll): - DatasetCreate.side_effect = fake_dataset_create + DatasetCreate.side_effect = fake_object_create ObjectAll.side_effect = fake_object_all VaultAll.side_effect = fake_vault_all args = [ @@ -71,7 +70,7 @@ def test_create_dataset_by_filename(self, DatasetCreate, ObjectAll, '--metadata', 'TEST2=tag2', ] ds = main.main(args) - self.assertEqual(ds.name, 'test-dataset-filename') + self.assertEqual(ds.filename, 'test-dataset-filename') self.assertEqual(ds.path, '/test-dataset-filename') self.assertEqual(ds.capacity, 'small') self.assertEqual(ds.tags, ['tag_test']) @@ -90,13 +89,13 @@ def _validate_tmpl_fields(self, fields): @mock.patch('solvebio.resource.Vault.all') @mock.patch('solvebio.resource.Object.all') - @mock.patch('solvebio.resource.Dataset.create') + @mock.patch('solvebio.resource.Object.create') @mock.patch('solvebio.resource.DatasetTemplate.create') def test_create_dataset_upload_template(self, TmplCreate, DatasetCreate, ObjectAll, VaultAll): TmplCreate.side_effect = fake_dataset_tmpl_create - DatasetCreate.side_effect = fake_dataset_create + DatasetCreate.side_effect = fake_object_create ObjectAll.side_effect = fake_object_all VaultAll.side_effect = fake_vault_all @@ -112,14 +111,14 @@ def test_create_dataset_upload_template(self, TmplCreate, @mock.patch('solvebio.resource.Vault.all') @mock.patch('solvebio.resource.Object.all') - @mock.patch('solvebio.resource.Dataset.create') + @mock.patch('solvebio.resource.Object.create') @mock.patch('solvebio.resource.DatasetTemplate.retrieve') @mock.patch('solvebio.resource.DatasetTemplate.create') def test_create_dataset_template_id(self, TmplCreate, TmplRetrieve, DatasetCreate, ObjectAll, VaultAll): VaultAll.side_effect = fake_vault_all ObjectAll.side_effect = fake_object_all - DatasetCreate.side_effect = fake_dataset_create + DatasetCreate.side_effect = fake_object_create TmplRetrieve.side_effect = fake_dataset_tmpl_retrieve TmplCreate.side_effect = fake_dataset_tmpl_create @@ -145,15 +144,15 @@ def test_create_dataset_template_id(self, TmplCreate, TmplRetrieve, @mock.patch('solvebio.resource.Vault.all') @mock.patch('solvebio.resource.Object.all') @mock.patch('solvebio.resource.Object.upload_file') - @mock.patch('solvebio.resource.Dataset.create') + @mock.patch('solvebio.resource.Object.create') @mock.patch('solvebio.resource.DatasetImport.create') - def _test_import_file(self, args, DatasetImportCreate, DatasetCreate, - ObjectCreate, ObjectAll, VaultAll, VaultLookup, + def _test_import_file(self, args, DatasetImportCreate, ObjectCreate, + UploadFile, ObjectAll, VaultAll, VaultLookup, UploadPath, TmplCreate, TmplRetrieve): DatasetImportCreate.side_effect = fake_dataset_import_create - DatasetCreate.side_effect = fake_dataset_create - ObjectAll.side_effect = fake_object_all ObjectCreate.side_effect = fake_object_create + UploadFile.side_effect = fake_object_create + ObjectAll.side_effect = fake_object_all VaultAll.side_effect = fake_vault_all UploadPath.side_effect = upload_path VaultLookup.side_effect = fake_vault_create @@ -199,7 +198,7 @@ def test_import_tilde(self): ]: args = ['import', '--create-dataset', dataset_path, file_] imports, ds = self._test_import_file(args) - self.assertEqual(ds.name, 'test-dataset') + self.assertEqual(ds.filename, 'test-dataset') # should be a manifest with a single file self.assertEqual(len(imports[0].manifest['files']), 1)