Skip to content

Feature Request: Add header parameter to BData.save() method #131

Description

@micchu

Feature Request: Add header parameter to BData.save() method

Summary

Add an optional header parameter to the BData.save() method to allow users to control whether header information (creation time, call stack, code) is saved. This is particularly important when preparing data for public release.

Motivation

Currently, BData.save() automatically saves header information that may contain sensitive details such as:

  • Absolute file paths from the call stack
  • Source code contents
  • User-specific directory structures

When preparing datasets for public release or sharing with collaborators, it is often necessary to exclude such metadata for:

  • Public data repositories: Clean data for platforms like Figshare, OpenNeuro
  • Privacy and security: Prevent leakage of internal paths and code
  • Collaboration: Share data without exposing implementation details
  • File size optimization: Reduce unnecessary metadata

Current Workaround

The only way to avoid saving header information is to use the private method:

bdata._BData__save_h5(filepath, header=None)

This violates Python conventions and is neither documented nor officially supported.

Proposed Solution

Add an optional header parameter to the BData.save() method:

def save(self, file_name: str, file_type: Optional[str] = None, header: bool = True) -> None:
    """
    Save 'dataset' and 'metadata' to a file.
    
    Parameters
    ----------
    file_name : str
        File name
    file_type : str, optional
        File type (currently only 'HDF5' is supported)
    header : bool, optional (default: True)
        If True (default), save header information (creation time, call stack, code).
        If False, do not save header information.
    
    Returns
    -------
    None
    """

Implementation Details

  • Default behavior: header=True (preserve backward compatibility)
  • When header=True: Current behavior - update self.__header with timestamp, call stack, and code, then pass to __save_h5()
  • When header=False: Skip header update and pass None to __save_h5()
  • The conditional logic should be added before the header update section

Suggested Implementation

def save(self, file_name: str, file_type: Optional[str] = None, header: bool = True) -> None:
    """Save 'dataset' and 'metadata' to a file."""
    
    # Store data creation information (only if header=True)
    if header:
        t_now = time.time()
        t_now_str = datetime.datetime.fromtimestamp(t_now).strftime('%Y-%m-%d %H:%M:%S')

        callstack = []
        callstack_code = []
        f = inspect.currentframe()
        while True:
            f = f.f_back
            if f is None:
                break
            fname = os.path.abspath(f.f_code.co_filename)
            fline = f.f_lineno
            callstack.append('%s:%d' % (fname, fline))
            if os.path.exists(fname):
                with open(fname, 'r') as fl:
                    fcode = fl.read()
            else:
                fcode = ''
            callstack_code.append(fcode)

        self.__header.update({'ctime': t_now_str,
                              'ctime_epoch': t_now,
                              'callstack': callstack,
                              'callstack_code': callstack_code})
        header_to_save = self.__header
    else:
        header_to_save = None

    if file_type is None:
        file_type = self.__get_filetype(file_name)

    if file_type == "Matlab":
        raise RuntimeError('Saving BData as a mat file is no longer supported. Please save the data as HDF5 (.h5).')
    elif file_type == "HDF5":
        self.__save_h5(file_name, header=header_to_save)
    else:
        raise ValueError("Unknown file type: %s" % (file_type))

Example Usage

# Save with header (current default behavior)
bdata.save('output.h5')
bdata.save('output.h5', header=True)

# Save without header (new feature)
bdata.save('output.h5', header=False)

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions