From 5ec50af64a6a217f00232cffb862814c60c2c735 Mon Sep 17 00:00:00 2001 From: Kartik Pradeepan Date: Wed, 17 Sep 2025 15:30:19 -0400 Subject: [PATCH 1/2] remove catalogue --- brainio-test/MANIFEST.in | 2 - brainio-test/brainio_test/__init__.py | 0 brainio-test/brainio_test/entrypoint.py | 24 ---- brainio-test/brainio_test/lookup.csv | 13 -- brainio-test/brainio_test/lookup2.csv | 10 -- brainio-test/setup.py | 18 --- brainio/__init__.py | 8 +- brainio/catalogs.py | 49 ------- brainio/fetch.py | 53 +++----- brainio/lookup.py | 170 +----------------------- brainio/packaging.py | 29 +--- pyproject.toml | 3 +- tests/test_lookup.py | 106 --------------- tests/test_transform.py | 1 - 14 files changed, 34 insertions(+), 452 deletions(-) delete mode 100644 brainio-test/MANIFEST.in delete mode 100644 brainio-test/brainio_test/__init__.py delete mode 100644 brainio-test/brainio_test/entrypoint.py delete mode 100644 brainio-test/brainio_test/lookup.csv delete mode 100644 brainio-test/brainio_test/lookup2.csv delete mode 100644 brainio-test/setup.py delete mode 100644 brainio/catalogs.py delete mode 100644 tests/test_lookup.py diff --git a/brainio-test/MANIFEST.in b/brainio-test/MANIFEST.in deleted file mode 100644 index 5fdedf1..0000000 --- a/brainio-test/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include brainio_test/lookup.csv -include brainio_test/lookup2.csv diff --git a/brainio-test/brainio_test/__init__.py b/brainio-test/brainio_test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/brainio-test/brainio_test/entrypoint.py b/brainio-test/brainio_test/entrypoint.py deleted file mode 100644 index 402690b..0000000 --- a/brainio-test/brainio_test/entrypoint.py +++ /dev/null @@ -1,24 +0,0 @@ -import logging -from pathlib import Path - -from brainio.catalogs import Catalog - -_logger = logging.getLogger(__name__) - -# Note that setup.py is where the entrypoint's published name is set - -def brainio_test(): - path = Path(__file__).parent / "lookup.csv" - _logger.debug(f"Loading catalog from {path}") - print(f"Loading catalog from {path}") # print because logging usually isn't set up at this point during import - catalog = Catalog.from_files("brainio_test", path) # setup.py is where the entrypoint's published name is set - return catalog - - -def brainio_test2(): - path = Path(__file__).parent / "lookup2.csv" - _logger.debug(f"Loading catalog from {path}") - print(f"Loading catalog from {path}") # print because logging usually isn't set up at this point during import - catalog = Catalog.from_files("brainio_test2", path) # setup.py is where the entrypoint's published name is set - return catalog - diff --git a/brainio-test/brainio_test/lookup.csv b/brainio-test/brainio_test/lookup.csv deleted file mode 100644 index 32b8301..0000000 --- a/brainio-test/brainio_test/lookup.csv +++ /dev/null @@ -1,13 +0,0 @@ -identifier,lookup_type,class,location_type,location,sha1,stimulus_set_identifier -dicarlo.hvm,stimulus_set,StimulusSet,S3,https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_hvm.csv,a56f55205904d5fb5ead4d0dc7bfad5cc4083b94, -dicarlo.hvm,stimulus_set,,S3,https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_hvm.zip,6fd5080deccfb061699909ffcb86a26209516811, -tolias.Cadena2017,stimulus_set,StimulusSet,S3,https://brainio.contrib.s3.amazonaws.com/image_tolias_Cadena2017.csv,f55b174cc4540e5612cfba5e695324328064b051, -tolias.Cadena2017,stimulus_set,,S3,https://brainio.contrib.s3.amazonaws.com/image_tolias_Cadena2017.zip,88cc2ce3ef5e197ffd1477144a2e6a68d424ef6c, -dicarlo.MajajHong2015,assembly,NeuronRecordingAssembly,S3,https://brainio.dicarlo.s3.amazonaws.com/assy_dicarlo_MajajHong2015.nc,bf8f8d01010d727e3db3f85a9bd5f95f9456b7ec,dicarlo.hvm -tolias.Cadena2017,assembly,NeuronRecordingAssembly,S3,https://brainio.contrib.s3.amazonaws.com/assy_tolias_Cadena2017.nc,69bcaaa9370dceb0027beaa06235ef418c3d7063,tolias.Cadena2017 -dicarlo.BashivanKar2019.naturalistic,stimulus_set,StimulusSet,S3,https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_BashivanKar2019_naturalistic.csv,48ef84282552b8796142ffe7d0d2c632f8ef061a, -dicarlo.BashivanKar2019.naturalistic,stimulus_set,,S3,https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_BashivanKar2019_naturalistic.zip,d7b71b431cf23d435395205f1e38036a9e10acca, -dicarlo.BashivanKar2019.naturalistic,assembly,NeuronRecordingAssembly,S3,https://brainio.dicarlo.s3.amazonaws.com/assy_dicarlo_BashivanKar2019_naturalistic.nc,1ec2f32ef800f0c6e15879d883be1d55b51b8b67,dicarlo.BashivanKar2019.naturalistic -dicarlo.BashivanKar2019.synthetic,stimulus_set,StimulusSet,S3,https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_BashivanKar2019_synthetic.csv,81da195e9b2a128b228fc4867e23ae6b21bd7abd, -dicarlo.BashivanKar2019.synthetic,stimulus_set,,S3,https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_BashivanKar2019_synthetic.zip,e2de33f25c5c19bcfb400055c1db399d553487e5, -dicarlo.BashivanKar2019.synthetic,assembly,NeuronRecordingAssembly,S3,https://brainio.dicarlo.s3.amazonaws.com/assy_dicarlo_BashivanKar2019_synthetic.nc,f687c8d26f8943dc379dbcbe94d3feb148400c6b,dicarlo.BashivanKar2019.synthetic diff --git a/brainio-test/brainio_test/lookup2.csv b/brainio-test/brainio_test/lookup2.csv deleted file mode 100644 index fdcfdca..0000000 --- a/brainio-test/brainio_test/lookup2.csv +++ /dev/null @@ -1,10 +0,0 @@ -identifier,lookup_type,class,location_type,location,sha1,stimulus_set_identifier -dicarlo.hvm,stimulus_set,StimulusSet,S3,https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_hvm.csv,a56f55205904d5fb5ead4d0dc7bfad5cc4083b94, -dicarlo.hvm,stimulus_set,,S3,https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_hvm.zip,6fd5080deccfb061699909ffcb86a26209516811, -dicarlo.hvm-public,stimulus_set,StimulusSet,S3,https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_hvm-public.csv,5ca7a3da00d8e9c694a9cd725df5ba0ad6d735af, -dicarlo.hvm-public,stimulus_set,,S3,https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_hvm-public.zip,8aa44e038d7b551efa8077467622f9d48d72e473, -dicarlo.hvm-private,stimulus_set,StimulusSet,S3,https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_hvm-private.csv,6ff4981722fa05feb73a2bd26bbbba8b50dc29a6, -dicarlo.hvm-private,stimulus_set,,S3,https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_hvm-private.zip,d7b1ca1876dad87e15b0242b4c82c0203ff3cbd3, -dicarlo.MajajHong2015,assembly,NeuronRecordingAssembly,S3,https://brainio.dicarlo.s3.amazonaws.com/assy_dicarlo_MajajHong2015.nc,bf8f8d01010d727e3db3f85a9bd5f95f9456b7ec,dicarlo.hvm -dicarlo.MajajHong2015.public,assembly,NeuronRecordingAssembly,S3,https://brainio.dicarlo.s3.amazonaws.com/assy_dicarlo_MajajHong2015_public.nc,13d28ca0ce88ee550b54db3004374ae19096e9b9,dicarlo.hvm-public -dicarlo.MajajHong2015.private,assembly,NeuronRecordingAssembly,S3,https://brainio.dicarlo.s3.amazonaws.com/assy_dicarlo_MajajHong2015_private.nc,7a40d16148d6f82939155f0bd976d310857fb156,dicarlo.hvm-private diff --git a/brainio-test/setup.py b/brainio-test/setup.py deleted file mode 100644 index 8ac1232..0000000 --- a/brainio-test/setup.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from setuptools import setup, find_packages - -setup( - name='brainio-test', - version='0.1.0', - description="Lookup data for testing BrainIO", - packages=find_packages(), - include_package_data=True, - entry_points={ - 'brainio_lookups': [ - 'brainio_test = brainio_test.entrypoint:brainio_test', - 'brainio_test2 = brainio_test.entrypoint:brainio_test2', - ], - }, -) diff --git a/brainio/__init__.py b/brainio/__init__.py index 21e9ff9..725d3da 100644 --- a/brainio/__init__.py +++ b/brainio/__init__.py @@ -1,3 +1,7 @@ -from .fetch import get_assembly, get_stimulus_set -from .lookup import get_catalog, list_stimulus_sets, list_assemblies, list_catalogs +# Import functions that depend on external dependencies (boto3) only when available +try: + from .fetch import get_assembly, get_stimulus_set +except ImportError: + # External dependencies not available - functions will raise NotImplementedError anyway + pass diff --git a/brainio/catalogs.py b/brainio/catalogs.py deleted file mode 100644 index 9738b0d..0000000 --- a/brainio/catalogs.py +++ /dev/null @@ -1,49 +0,0 @@ -from pathlib import Path - -import pandas as pd -from pandas import DataFrame - - -SOURCE_CATALOG = "source_catalog" - - -class Catalog(DataFrame): - # http://pandas.pydata.org/pandas-docs/stable/development/extending.html#subclassing-pandas-data-structures - _metadata = pd.DataFrame._metadata + ["identifier", "url", "get_loader_class", "from_files"] - - @property - def _constructor(self): - return Catalog - - @classmethod - def get_loader_class(cls): - return CatalogLoader - - @classmethod - def from_files(cls, identifier, csv_path, url=None): - loader_class = cls.get_loader_class() - loader = loader_class( - cls=cls, - identifier=identifier, - csv_path=csv_path, - url=url - ) - return loader.load() - - -class CatalogLoader: - def __init__(self, cls, identifier, csv_path, url=None): - self.cls = cls - self.identifier = identifier - self.csv_path = Path(csv_path) - self.url = url - - def load(self): - catalog = pd.read_csv(self.csv_path) - catalog = self.cls(catalog) - catalog.identifier = self.identifier - catalog.attrs['source_path'] = self.csv_path - catalog.url = self.url - return catalog - - diff --git a/brainio/fetch.py b/brainio/fetch.py index c71b441..3696cbc 100644 --- a/brainio/fetch.py +++ b/brainio/fetch.py @@ -13,7 +13,7 @@ import brainio.assemblies as assemblies import brainio.stimuli as stimuli -from brainio.lookup import lookup_assembly, lookup_stimulus_set, sha1_hash +from brainio.lookup import sha1_hash from brainio.stimuli import StimulusSetLoader BRAINIO_HOME = 'BRAINIO_HOME' @@ -157,42 +157,31 @@ def resolve_stimulus_set_class(class_name): def get_assembly(identifier): - assembly_lookup = lookup_assembly(identifier) - file_path = fetch_file(location_type=assembly_lookup['location_type'], - location=assembly_lookup['location'], sha1=assembly_lookup['sha1']) - stimulus_set = get_stimulus_set(assembly_lookup['stimulus_set_identifier']) - cls = resolve_assembly_class(assembly_lookup['class']) - loader = cls.get_loader_class()( - cls=cls, - file_path=file_path, - stimulus_set_identifier=assembly_lookup['stimulus_set_identifier'], - stimulus_set=stimulus_set, + """ + DEPRECATED: The catalog-based assembly loading has been removed. + + Use direct loading methods instead: + - For S3: Use brainscore_vision.data_helpers.s3.load_assembly_from_s3() + - For files: Use DataAssembly.from_files() + """ + raise NotImplementedError( + "get_assembly() has been deprecated. The catalog system has been removed. " + "Use direct loading methods like load_assembly_from_s3() or DataAssembly.from_files() instead." ) - assembly = loader.load() - assembly.attrs['identifier'] = identifier - return assembly def get_stimulus_set(identifier): - csv_lookup, zip_lookup = lookup_stimulus_set(identifier) - csv_path = fetch_file(location_type=csv_lookup['location_type'], location=csv_lookup['location'], - sha1=csv_lookup['sha1']) - zip_path = fetch_file(location_type=zip_lookup['location_type'], location=zip_lookup['location'], - sha1=zip_lookup['sha1']) - stimuli_directory = unzip(zip_path) - loader = StimulusSetLoader( - csv_path=csv_path, - stimuli_directory=stimuli_directory, - cls=resolve_stimulus_set_class(csv_lookup['class']) + """ + DEPRECATED: The catalog-based stimulus set loading has been removed. + + Use direct loading methods instead: + - For S3: Use brainscore_vision.data_helpers.s3.load_stimulus_set_from_s3() + - For files: Use StimulusSet.from_files() + """ + raise NotImplementedError( + "get_stimulus_set() has been deprecated. The catalog system has been removed. " + "Use direct loading methods like load_stimulus_set_from_s3() or StimulusSet.from_files() instead." ) - stimulus_set = loader.load() - stimulus_set.identifier = identifier - # ensure perfect overlap - stimuli_paths = [Path(stimuli_directory) / local_path for local_path in os.listdir(stimuli_directory) - if not local_path.endswith('.zip') and not local_path.endswith('.csv')] - assert set(stimulus_set.stimulus_paths.values()) == set(stimuli_paths), \ - "Inconsistency: unzipped stimuli paths do not match csv paths" - return stimulus_set def fullname(obj): diff --git a/brainio/lookup.py b/brainio/lookup.py index 82db528..b6e3b30 100644 --- a/brainio/lookup.py +++ b/brainio/lookup.py @@ -1,177 +1,11 @@ import hashlib import logging -import entrypoints -import numpy as np -import pandas as pd - -from brainio.catalogs import Catalog, SOURCE_CATALOG - -ENTRYPOINT = "brainio_lookups" -TYPE_ASSEMBLY = 'assembly' -TYPE_STIMULUS_SET = 'stimulus_set' -_catalogs = {} - _logger = logging.getLogger(__name__) -def list_catalogs(): - return sorted(list(entrypoints.get_group_named(ENTRYPOINT).keys())) - - -def _load_catalog(identifier, entry_point): - catalog = entry_point.load()() - assert isinstance(catalog, Catalog) - assert catalog.identifier == identifier - return catalog - - -def _load_installed_catalogs(): - installed_catalogs = entrypoints.get_group_named(ENTRYPOINT) - _logger.debug(f"Loading catalog from entrypoints") - print(f"Loading catalog from entrypoints") - for k, v in installed_catalogs.items(): - catalog = _load_catalog(k, v) - _catalogs[k] = catalog - return _catalogs - - -def get_catalog(identifier): - catalogs = get_catalogs() - return catalogs[identifier] - - -def get_catalogs(): - if not _catalogs: - _load_installed_catalogs() - return _catalogs - - -def combined_catalog(): - source_catalogs = get_catalogs() - target_catalogs = {} - for identifier, source_catalog in source_catalogs.items(): - target_catalog = source_catalog.copy() - target_catalog[SOURCE_CATALOG] = identifier - target_catalogs[identifier] = target_catalog - concat_catalogs = pd.concat(target_catalogs.values(), ignore_index=True) - return concat_catalogs - - -def list_stimulus_sets(): - combined = combined_catalog() - stimuli_rows = combined[combined['lookup_type'] == TYPE_STIMULUS_SET] - return sorted(list(set(stimuli_rows['identifier']))) - - -def list_assemblies(): - combined = combined_catalog() - assembly_rows = combined[combined['lookup_type'] == TYPE_ASSEMBLY] - return sorted(list(set(assembly_rows['identifier']))) - - -def lookup_stimulus_set(identifier): - combined = combined_catalog() - lookup = combined[(combined['identifier'] == identifier) & (combined['lookup_type'] == TYPE_STIMULUS_SET)] - if len(lookup) == 0: - raise StimulusSetLookupError(f"Stimulus set {identifier} not found") - csv_lookup = _lookup_stimulus_set_filtered(lookup, filter_func=_is_csv_lookup, label="CSV") - zip_lookup = _lookup_stimulus_set_filtered(lookup, filter_func=_is_zip_lookup, label="ZIP") - return csv_lookup, zip_lookup - - -def _lookup_stimulus_set_filtered(lookup, filter_func, label): - cols = [n for n in lookup.columns if n != SOURCE_CATALOG] - # filter for csv vs. zip - # if there are any groups of rows where every field except source is the same, - # we only want one from each group - filtered_rows = lookup[lookup.apply(filter_func, axis=1)].drop_duplicates(subset=cols) - identifier = lookup.iloc[0]['identifier'] - if len(filtered_rows) == 0: - raise StimulusSetLookupError(f"{label} for stimulus set {identifier} not found") - if len(filtered_rows) > 1: # there were multiple rows but not all identical - raise RuntimeError( - f"Internal data inconsistency: Found more than 2 lookup rows for stimulus_set {label} for identifier {identifier}") - assert len(filtered_rows) == 1 - return filtered_rows.squeeze() - - -def lookup_assembly(identifier): - combined = combined_catalog() - lookup = combined[(combined['identifier'] == identifier) & (combined['lookup_type'] == TYPE_ASSEMBLY)] - if len(lookup) == 0: - raise AssemblyLookupError(f"Data assembly {identifier} not found") - cols = [n for n in lookup.columns if n != SOURCE_CATALOG] - # if there are any groups of rows where every field except source is the same, - # we only want one from each group - de_dupe = lookup.drop_duplicates(subset=cols) - if len(de_dupe) > 1: # there were multiple rows but not all identical - raise RuntimeError(f"Internal data inconsistency: Found multiple lookup rows for identifier {identifier}") - assert len(de_dupe) == 1 - return de_dupe.squeeze() - - -class StimulusSetLookupError(KeyError): - pass - - -class AssemblyLookupError(KeyError): - pass - - -def append(catalog_identifier, object_identifier, cls, lookup_type, - bucket_name, sha1, s3_key, stimulus_set_identifier=None): - catalogs = get_catalogs() - catalog = catalogs[catalog_identifier] - catalog_path = catalog.attrs['source_path'] - _logger.debug(f"Adding {lookup_type} {object_identifier} to catalog {catalog_identifier}") - object_lookup = { - 'identifier': object_identifier, - 'lookup_type': lookup_type, - 'class': cls, - 'location_type': "S3", - 'location': f"https://{bucket_name}.s3.amazonaws.com/{s3_key}", - 'sha1': sha1, - 'stimulus_set_identifier': stimulus_set_identifier, - } - # check duplicates - assert object_lookup['lookup_type'] in [TYPE_ASSEMBLY, TYPE_STIMULUS_SET] - duplicates = catalog[(catalog['identifier'] == object_lookup['identifier']) & - (catalog['lookup_type'] == object_lookup['lookup_type'])] - if len(duplicates) > 0: - if object_lookup['lookup_type'] == TYPE_ASSEMBLY: - raise ValueError(f"Trying to add duplicate identifier {object_lookup['identifier']}, " - f"existing \n{duplicates.to_string()}") - elif object_lookup['lookup_type'] == TYPE_STIMULUS_SET: - if len(duplicates) == 1 and duplicates.squeeze()['identifier'] == object_lookup['identifier'] and ( - (_is_csv_lookup(duplicates.squeeze()) and _is_zip_lookup(object_lookup)) or - (_is_zip_lookup(duplicates.squeeze()) and _is_csv_lookup(object_lookup))): - pass # all good, we're just adding the second part of a stimulus set - else: - raise ValueError( - f"Trying to add duplicate identifier {object_lookup['identifier']}, existing {duplicates}") - # append and save - add_lookup = pd.DataFrame({key: [value] for key, value in object_lookup.items()}) - catalog = pd.concat((catalog, add_lookup)) - catalog.attrs['source_path'] = catalog_path # explicitly set since concat does not always preserve - catalog.to_csv(catalog_path, index=False) - _catalogs[catalog_identifier] = catalog - return catalog - - -def _is_csv_lookup(data_row): - return data_row['lookup_type'] == TYPE_STIMULUS_SET \ - and data_row['location'].endswith('.csv') \ - and data_row['class'] not in [None, np.nan] - - -def _is_zip_lookup(data_row): - return data_row['lookup_type'] == TYPE_STIMULUS_SET \ - and data_row['location'].endswith('.zip') \ - and data_row['class'] in [None, np.nan] - - def sha1_hash(path, buffer_size=64 * 2 ** 10): + """Calculate SHA1 hash of a file.""" _logger.debug(f'BEGIN sha1_hash on {path}') sha1 = hashlib.sha1() with open(path, "rb") as f: @@ -180,4 +14,4 @@ def sha1_hash(path, buffer_size=64 * 2 ** 10): sha1.update(buffer) buffer = f.read(buffer_size) _logger.debug(f'END sha1_hash on {path}') - return sha1.hexdigest() + return sha1.hexdigest() \ No newline at end of file diff --git a/brainio/packaging.py b/brainio/packaging.py index f82574f..7be4041 100644 --- a/brainio/packaging.py +++ b/brainio/packaging.py @@ -14,9 +14,9 @@ from tqdm import tqdm from xarray import DataArray -from brainio import lookup, list_stimulus_sets, fetch +from brainio import fetch from brainio.fetch import resolve_assembly_class -from brainio.lookup import TYPE_ASSEMBLY, TYPE_STIMULUS_SET, sha1_hash +from brainio.lookup import sha1_hash _logger = logging.getLogger(__name__) @@ -197,21 +197,7 @@ def package_stimulus_set(catalog_name, proto_stimulus_set, stimulus_set_identifi zip_object_properties = upload_to_s3(str(target_zip_path), bucket_name, target_s3_key=zip_file_name) # link to csv and zip from same identifier. The csv however is the only one of the two rows with a class. - if catalog_name is not None: - lookup.append( - catalog_identifier=catalog_name, - object_identifier=stimulus_set_identifier, cls='StimulusSet', - lookup_type=TYPE_STIMULUS_SET, - bucket_name=bucket_name, sha1=csv_sha1, s3_key=csv_file_name, - stimulus_set_identifier=None - ) - lookup.append( - catalog_identifier=catalog_name, - object_identifier=stimulus_set_identifier, cls=None, - lookup_type=TYPE_STIMULUS_SET, - bucket_name=bucket_name, sha1=stimulus_zip_sha1, s3_key=zip_file_name, - stimulus_set_identifier=None - ) + # Catalog functionality has been removed - no longer updating catalogs csv_version_id = csv_object_properties['VersionId'] if 'VersionId' in csv_object_properties else None zip_version_id = zip_object_properties['VersionId'] if 'VersionId' in zip_object_properties else None _logger.debug(f"stimulus set {stimulus_set_identifier} packaged:\n bucket={bucket_name}, csv_sha1={csv_sha1}," @@ -299,14 +285,7 @@ def package_data_assembly(catalog_identifier, proto_data_assembly, assembly_iden netcdf_kf_sha1 = write_netcdf(ex, target_netcdf_path, append=True, group=k) object_properties = upload_to_s3(target_netcdf_path, bucket_name, s3_key) - if catalog_identifier is not None: - lookup.append( - catalog_identifier=catalog_identifier, - object_identifier=assembly_identifier, stimulus_set_identifier=stimulus_set_identifier, - lookup_type=TYPE_ASSEMBLY, - bucket_name=bucket_name, sha1=netcdf_kf_sha1, - s3_key=s3_key, cls=assembly_class_name, - ) + # Catalog functionality has been removed - no longer updating catalogs version_id = object_properties['VersionId'] if 'VersionId' in object_properties else None _logger.debug(f"assembly {assembly_identifier} packaged:\n, version_id={version_id}, sha1={netcdf_kf_sha1}, " f"bucket_name={bucket_name}, cls={assembly_class_name}") diff --git a/pyproject.toml b/pyproject.toml index 25de1bc..23d7d97 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,6 @@ dependencies = [ "boto3", "tqdm", "Pillow", - "entrypoints", "numpy", "pandas", "xarray<2022.6", # groupby bug was introduced in index refactor: https://github.com/pydata/xarray/issues/6836 @@ -44,4 +43,4 @@ tests = [ [tool.setuptools.packages.find] where = ["."] -exclude = ["tests", "brainio-test"] +exclude = ["tests"] diff --git a/tests/test_lookup.py b/tests/test_lookup.py deleted file mode 100644 index 4d86f2d..0000000 --- a/tests/test_lookup.py +++ /dev/null @@ -1,106 +0,0 @@ -import os -from pathlib import Path - -import pytest -from pytest import approx -import numpy as np -import pandas as pd -import xarray as xr -from xarray import DataArray -from PIL import Image - -import brainio -from brainio import assemblies -from brainio import fetch -from brainio.assemblies import DataAssembly, get_levels, gather_indexes, is_fastpath - - -@pytest.mark.parametrize('assembly', ( - 'dicarlo.MajajHong2015', - 'dicarlo.MajajHong2015.private', - 'dicarlo.MajajHong2015.public', - 'tolias.Cadena2017', - 'dicarlo.BashivanKar2019.naturalistic', - 'dicarlo.BashivanKar2019.synthetic', -)) -def test_list_assembly(assembly): - l = brainio.list_assemblies() - assert assembly in l - - -@pytest.mark.parametrize('stimulus_set', ( - 'dicarlo.hvm', - 'dicarlo.hvm-public', - 'dicarlo.hvm-private', - 'tolias.Cadena2017', - 'dicarlo.BashivanKar2019.naturalistic', - 'dicarlo.BashivanKar2019.synthetic' -)) -def test_list_stimulus_set(stimulus_set): - l = brainio.list_stimulus_sets() - assert stimulus_set in l - - -def test_lookup_stim(): - stim_csv, stim_zip = brainio.lookup.lookup_stimulus_set("dicarlo.hvm") - assert stim_csv['identifier'] == "dicarlo.hvm" - assert stim_csv['location_type'] == "S3" - hvm_s3_csv_url = "https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_hvm.csv" - assert stim_csv['location'] == hvm_s3_csv_url - assert stim_zip['identifier'] == "dicarlo.hvm" - assert stim_zip['location_type'] == "S3" - hvm_s3_zip_url = "https://brainio.dicarlo.s3.amazonaws.com/image_dicarlo_hvm.zip" - assert stim_zip['location'] == hvm_s3_zip_url - - -def test_lookup_assy(): - assy = brainio.lookup.lookup_assembly("dicarlo.MajajHong2015.public") - assert assy['identifier'] == "dicarlo.MajajHong2015.public" - assert assy['location_type'] == "S3" - hvm_s3_url = "https://brainio.dicarlo.s3.amazonaws.com/assy_dicarlo_MajajHong2015_public.nc" - assert assy['location'] == hvm_s3_url - - -def test_lookup_bad_name(): - with pytest.raises(brainio.lookup.AssemblyLookupError): - brainio.lookup.lookup_assembly("BadName") - - -def test_catalogs(): - cats = brainio.lookup.list_catalogs() - assert len(cats) == 2 - assert "brainio_test" in cats - assert "brainio_test2" in cats - dfs = brainio.lookup._load_installed_catalogs() - assert str(dfs["brainio_test"].attrs['source_path']).endswith(".csv") - assert str(dfs["brainio_test2"].attrs['source_path']).endswith(".csv") - assert len(dfs["brainio_test"]) == 12 - assert len(dfs["brainio_test2"]) == 9 - concat = brainio.lookup.combined_catalog() - assert len(concat) == len(dfs["brainio_test"]) + len(dfs["brainio_test2"]) - - -def test_duplicates(): - all_lookups = brainio.lookup.combined_catalog() - match_stim = all_lookups['lookup_type'] == brainio.lookup.TYPE_STIMULUS_SET - match_csv = all_lookups.apply(brainio.lookup._is_csv_lookup, axis=1) - match_zip = all_lookups.apply(brainio.lookup._is_zip_lookup, axis=1) - match_assy = all_lookups['lookup_type'] == brainio.lookup.TYPE_ASSEMBLY - - match_hvm = all_lookups['identifier'] == "dicarlo.hvm" - assert np.count_nonzero(match_hvm) == 4 - assert len(all_lookups[match_hvm & match_stim & match_csv]) == 2 - assert len(all_lookups[match_hvm & match_stim & match_zip]) == 2 - match_mh15 = all_lookups['identifier'] == "dicarlo.MajajHong2015" - match_mh15_pub = all_lookups['identifier'] == "dicarlo.MajajHong2015.public" - match_mh15_pvt = all_lookups['identifier'] == "dicarlo.MajajHong2015.private" - assert len(all_lookups[match_mh15 & match_assy]) == 2 - assert len(all_lookups[match_mh15_pub & match_assy]) == 1 - assert len(all_lookups[match_mh15_pvt & match_assy]) == 1 - match_c17 = all_lookups['identifier'] == "tolias.Cadena2017" - assert len(all_lookups[match_c17 & match_stim & match_csv]) == 1 - assert len(all_lookups[match_c17 & match_stim & match_zip]) == 1 - assert len(all_lookups[match_c17 & match_assy]) == 1 - - - diff --git a/tests/test_transform.py b/tests/test_transform.py index 8885248..d183c71 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -1,7 +1,6 @@ import numpy as np import pytest import xarray as xr -from brainio import get_assembly from brainio.assemblies import NeuroidAssembly from brainio.transform import subset, index_efficient From 0849c00cc13a3d8e417751906e8bae0047c23e29 Mon Sep 17 00:00:00 2001 From: Kartik Pradeepan Date: Wed, 17 Sep 2025 15:56:47 -0400 Subject: [PATCH 2/2] remove TYPE_ASSEMBLY --- brainio/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 345 bytes .../__pycache__/assemblies.cpython-311.pyc | Bin 0 -> 37608 bytes brainio/__pycache__/fetch.cpython-311.pyc | Bin 0 -> 12020 bytes tests/test_packaging.py | 62 ++++++++---------- 4 files changed, 29 insertions(+), 33 deletions(-) create mode 100644 brainio/__pycache__/__init__.cpython-311.pyc create mode 100644 brainio/__pycache__/assemblies.cpython-311.pyc create mode 100644 brainio/__pycache__/fetch.cpython-311.pyc diff --git a/brainio/__pycache__/__init__.cpython-311.pyc b/brainio/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5f47028647b69a1b350410d310d8796f08f4a625 GIT binary patch literal 345 zcmZ3^%ge<81Pi%OXK*qyFgylvV1Nb6_-p`VOlRO^NMT4}%wfo7jAG1Xieh49NMQ~&0SX`W%o0L;|O8~?xF3HR- z%_%L8FHSA-(`32DnwDCUoKeIK)KCPn^%l2hZb5!giEB|&e$h&X&p;uDUzYlz#i>Qb z`q_y^C7Id!sTBn|`9-PmNkxg7d71h8=yZHMP`D&MUaz3?7l%!5eoARhs$CJtw;*>F zdjW|L%#4hTA6QrzId8C-Ucilh++Y{_z`(@Fi69=Za5T6yxO`v*ivIZVqnHP1ToVTm NLl3Jc^Atu=W&mwiU4sAs literal 0 HcmV?d00001 diff --git a/brainio/__pycache__/assemblies.cpython-311.pyc b/brainio/__pycache__/assemblies.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a4197bc34df2e6e8f6a94995699208a7307785b6 GIT binary patch literal 37608 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 E4=;dExc~qF literal 0 HcmV?d00001 diff --git a/brainio/__pycache__/fetch.cpython-311.pyc b/brainio/__pycache__/fetch.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f75c9f71228a4bef3853fcc13e78c2c2edf949f GIT binary patch literal 12020 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