UNPKG

node-pandas

Version:

An npm package that incorporates minimal features of python pandas.

315 lines (293 loc) 9.85 kB
/** * @fileoverview Centralized error handling utilities for the node-pandas library. * Provides custom error classes with context information and suggestion generation * for common errors. * * Validates: Requirements 18.1, 18.2, 18.3, 18.4, 18.5 */ /** * Base error class for all DataFrame-related errors. * Extends the standard JavaScript Error class with context and suggestions. * * @class DataFrameError * @extends Error * @param {string} message - The error message * @param {Object} context - Additional context information about the error * @param {string} context.operation - The operation being performed when error occurred * @param {*} context.value - The problematic value that caused the error * @param {string} context.column - The column name involved in the error (if applicable) * @param {string} context.expected - The expected value or type * @param {string} context.actual - The actual value or type received * @example * throw new DataFrameError('Invalid column', { * operation: 'select', * column: 'age', * expected: 'existing column', * actual: 'non-existent column' * }); */ class DataFrameError extends Error { constructor(message, context = {}) { super(message); this.name = 'DataFrameError'; this.context = context; this.suggestions = generateSuggestions(this.name, context); } /** * Returns a formatted error message with context and suggestions * @returns {string} Formatted error message */ toString() { let output = `${this.name}: ${this.message}`; if (Object.keys(this.context).length > 0) { output += '\n\nContext:'; if (this.context.operation) { output += `\n Operation: ${this.context.operation}`; } if (this.context.column) { output += `\n Column: ${this.context.column}`; } if (this.context.expected) { output += `\n Expected: ${this.context.expected}`; } if (this.context.actual) { output += `\n Actual: ${this.context.actual}`; } } if (this.suggestions.length > 0) { output += '\n\nSuggestions:'; this.suggestions.forEach((suggestion, index) => { output += `\n ${index + 1}. ${suggestion}`; }); } return output; } } /** * Error class for validation failures. * Used when data validation fails or invalid parameters are provided. * * @class ValidationError * @extends DataFrameError * @param {string} message - The error message * @param {Object} context - Additional context information * @example * throw new ValidationError('Invalid column names', { * operation: 'DataFrame creation', * expected: 'array of strings', * actual: 'array with non-string elements' * }); */ class ValidationError extends DataFrameError { constructor(message, context = {}) { super(message, context); this.name = 'ValidationError'; } } /** * Error class for type-related errors. * Used when operations encounter type mismatches or incompatibilities. * * @class TypeError * @extends DataFrameError * @param {string} message - The error message * @param {Object} context - Additional context information * @example * throw new TypeError('Cannot perform numeric operation on string column', { * operation: 'mean', * column: 'name', * expected: 'numeric type', * actual: 'string type' * }); */ class TypeError extends DataFrameError { constructor(message, context = {}) { super(message, context); this.name = 'TypeError'; } } /** * Error class for index-related errors. * Used when accessing invalid indices or row/column positions. * * @class IndexError * @extends DataFrameError * @param {string} message - The error message * @param {Object} context - Additional context information * @example * throw new IndexError('Row index out of bounds', { * operation: 'row access', * value: 100, * expected: 'index between 0 and 50' * }); */ class IndexError extends DataFrameError { constructor(message, context = {}) { super(message, context); this.name = 'IndexError'; } } /** * Error class for column-related errors. * Used when accessing non-existent columns or invalid column operations. * * @class ColumnError * @extends DataFrameError * @param {string} message - The error message * @param {Object} context - Additional context information * @example * throw new ColumnError('Column not found', { * operation: 'select', * column: 'nonexistent', * value: ['col1', 'col2'] * }); */ class ColumnError extends DataFrameError { constructor(message, context = {}) { super(message, context); this.name = 'ColumnError'; } } /** * Error class for I/O operations. * Used when file reading/writing fails or data format is invalid. * * @class IOError * @extends DataFrameError * @param {string} message - The error message * @param {Object} context - Additional context information * @example * throw new IOError('File not found', { * operation: 'readCsv', * value: '/path/to/file.csv' * }); */ class IOError extends DataFrameError { constructor(message, context = {}) { super(message, context); this.name = 'IOError'; } } /** * Error class for operation-related errors. * Used when operations fail due to invalid state or parameters. * * @class OperationError * @extends DataFrameError * @param {string} message - The error message * @param {Object} context - Additional context information * @example * throw new OperationError('Cannot merge DataFrames without common key', { * operation: 'merge', * expected: 'common column name' * }); */ class OperationError extends DataFrameError { constructor(message, context = {}) { super(message, context); this.name = 'OperationError'; } } /** * Generates helpful suggestions based on error type and context. * Provides actionable recommendations to help users fix common errors. * * @param {string} errorType - The type of error (e.g., 'ColumnError', 'ValidationError') * @param {Object} context - The error context object * @returns {string[]} Array of suggestion strings * @private */ function generateSuggestions(errorType, context) { const suggestions = []; if (errorType === 'ColumnError') { if (context.operation === 'select') { suggestions.push('Check that all column names are spelled correctly'); suggestions.push('Use df.columns to see available columns'); suggestions.push('Column names are case-sensitive'); } } else if (errorType === 'ValidationError') { if (context.operation === 'DataFrame creation') { suggestions.push('Ensure all rows have the same number of elements'); suggestions.push('Verify column names array matches the number of columns'); suggestions.push('Check that data is a valid 2D array structure'); } } else if (errorType === 'TypeError') { if (context.operation === 'mean' || context.operation === 'sum') { suggestions.push(`Convert column to numeric type before performing ${context.operation}`); suggestions.push('Use df.select() to work with only numeric columns'); suggestions.push('Check for non-numeric values like strings or null'); } } else if (errorType === 'IndexError') { suggestions.push(`Use df.index to check valid row indices`); suggestions.push('Row indices are 0-based (first row is index 0)'); } else if (errorType === 'IOError') { if (context.operation === 'readCsv' || context.operation === 'readJson') { suggestions.push('Verify the file path is correct and the file exists'); suggestions.push('Check that you have read permissions for the file'); suggestions.push('Ensure the file format matches the operation (CSV for readCsv, JSON for readJson)'); } } else if (errorType === 'OperationError') { if (context.operation === 'merge') { suggestions.push('Specify a valid join key that exists in both DataFrames'); suggestions.push('Use df.columns to verify column names in both DataFrames'); } } return suggestions; } /** * Formats an error message with context information. * Creates a user-friendly error message that includes operation details and suggestions. * * @param {string} message - The base error message * @param {Object} context - Context information about the error * @returns {string} Formatted error message with context * @example * const msg = formatErrorMessage('Invalid operation', { * operation: 'filter', * column: 'age', * expected: 'numeric column' * }); */ function formatErrorMessage(message, context = {}) { let formatted = message; if (context.operation) { formatted += ` during ${context.operation}`; } if (context.column) { formatted += ` on column '${context.column}'`; } if (context.expected && context.actual) { formatted += ` (expected ${context.expected}, got ${context.actual})`; } return formatted; } /** * Creates a descriptive error with suggestions for common issues. * Wraps error creation with automatic suggestion generation. * * @param {string} ErrorClass - The error class to instantiate * @param {string} message - The error message * @param {Object} context - Context information * @returns {Error} An instance of the specified error class * @example * const error = createError(ColumnError, 'Column not found', { * operation: 'select', * column: 'age' * }); */ function createError(ErrorClass, message, context = {}) { const formattedMessage = formatErrorMessage(message, context); return new ErrorClass(formattedMessage, context); } module.exports = { DataFrameError, ValidationError, TypeError, IndexError, ColumnError, IOError, OperationError, formatErrorMessage, createError, generateSuggestions };