From 3cbf74a712c16c10a357e231cfc9e80841e9ba8e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 07:58:28 +0000 Subject: [PATCH 01/16] [Autoloop: build-tsb-pandas-typescript-migration] Iteration 313: Add pd.errors module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port pandas pd.errors — full exception and warning hierarchy (31 classes). Run: https://github.com/githubnext/tsb/actions/runs/25848596047 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- playground/errors.html | 157 +++++++++++++++++++ src/errors.ts | 269 +++++++++++++++++++++++++++++++++ src/index.ts | 40 +++++ tests/errors.test.ts | 331 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 797 insertions(+) create mode 100644 playground/errors.html create mode 100644 src/errors.ts create mode 100644 tests/errors.test.ts diff --git a/playground/errors.html b/playground/errors.html new file mode 100644 index 00000000..4a92b883 --- /dev/null +++ b/playground/errors.html @@ -0,0 +1,157 @@ + + + + + + pd.errors — tsb playground + + + + + +

pd.errors new in this release

+

+ tsb ships a full set of pandas-compatible exception and warning classes + under the errors namespace — mirroring Python's pd.errors. +

+ +

Error hierarchy

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
ClassExtendsWhen raised
ValueErrorTypeErrorInvalid argument value
KeyErrorErrorMissing key or label
IndexErrorRangeErrorOut-of-range index access
ParserErrorValueErrorFile/string parsing fails
EmptyDataErrorErrorReading an empty file
MergeErrorValueErrorIncorrect merge call
UnsortedIndexErrorKeyErrorUnsorted MultiIndex slice
OptionErrorKeyErrorInvalid option name/value
OutOfBoundsDatetimeValueErrorDatetime outside nanosecond range
OutOfBoundsTimedeltaValueErrorTimedelta outside range
IntCastingNaNErrorErrorCasting NaN to integer
ChainedAssignmentErrorErrorChained assignment detected
AbstractMethodErrorErrorCalling an abstract method
DtypeWarningErrorDtype mismatch on read
PerformanceWarningErrorPerformance concern
DatabaseErrorErrorDatabase operation fails
DataErrorErrorGroupBy agg data error
NullFrequencyErrorValueErrorNull freq on period index
SpecificationErrorValueErrorBad agg specification
InvalidIndexErrorErrorLabel not in index
InvalidComparisonTypeErrorIncompatible type comparison
+ +

Usage examples

+ +

Catching a specific error

+
import { ParserError, EmptyDataError, readCsv } from "tsb";
+
+try {
+  readCsv("");
+} catch (e) {
+  if (e instanceof EmptyDataError) {
+    console.log("File was empty:", e.message);
+  } else if (e instanceof ParserError) {
+    console.log("Parse failed:", e.message);
+  } else {
+    throw e;
+  }
+}
+ +

Using the errors namespace (like pd.errors)

+
import { errors } from "tsb";
+
+// All error classes are available on the namespace
+const e = new errors.MergeError("incompatible merge keys");
+console.log(e instanceof errors.MergeError); // true
+console.log(e instanceof errors.ValueError);  // true (MergeError extends ValueError)
+console.log(e.name);                          // "MergeError"
+ +

AbstractMethodError — for custom extension classes

+
import { errors } from "tsb";
+
+class BaseProcessor {
+  process(): void {
+    throw new errors.AbstractMethodError("BaseProcessor.process");
+  }
+}
+
+class ConcreteProcessor extends BaseProcessor {
+  override process(): void {
+    console.log("Processing…");
+  }
+}
+
+// Base call raises
+try {
+  new BaseProcessor().process();
+} catch (e) {
+  console.log(e instanceof errors.AbstractMethodError); // true
+  console.log(e.message); // "This method must be defined in the concrete class: …"
+}
+ +

ChainedAssignmentError

+
import { errors } from "tsb";
+
+// Raise when a write to a copy-of-slice is detected
+throw new errors.ChainedAssignmentError();
+// default message: "A value is trying to be set on a copy of a slice from a DataFrame"
+ +

Datetime/Timedelta bounds errors

+
import { errors } from "tsb";
+
+// These are raised by the datetime module when values exceed nanosecond range
+try {
+  // hypothetical: toDatetime("year 4000") with nanosecond precision
+} catch (e) {
+  if (e instanceof errors.OutOfBoundsDatetime) {
+    console.log("Datetime out of range:", e.message);
+  }
+}
+ +
+ + + + diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 00000000..7c024386 --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,269 @@ +/** + * `pd.errors` — pandas-compatible error and warning classes. + * + * Provides the full hierarchy of exceptions and warnings that pandas raises, + * adapted to TypeScript idioms. All classes extend native Error (or its + * subclasses) so they integrate naturally with `try/catch` and `instanceof`. + * + * @packageDocumentation + */ + +// --------------------------------------------------------------------------- +// Error classes +// --------------------------------------------------------------------------- + +/** Raised when an abstract method is called that subclasses must override. */ +export class AbstractMethodError extends Error { + override readonly name = "AbstractMethodError"; + constructor(classOrMethod: string) { + super( + `This method must be defined in the concrete class: ${classOrMethod}`, + ); + } +} + +/** Raised when there is a conflicting attribute during attribute access. */ +export class AttributeConflictWarning extends Error { + override readonly name = "AttributeConflictWarning"; +} + +/** Raised when CSS stylesheet parsing encounters a problem. */ +export class CSSWarning extends Error { + override readonly name = "CSSWarning"; +} + +/** + * Raised when chained assignment is detected. + * Equivalent to pandas `ChainedAssignmentError`. + */ +export class ChainedAssignmentError extends Error { + override readonly name = "ChainedAssignmentError"; + constructor(message = "A value is trying to be set on a copy of a slice from a DataFrame") { + super(message); + } +} + +/** Raised when there is a database-related error. */ +export class DatabaseError extends Error { + override readonly name = "DatabaseError"; +} + +/** Raised when a groupby aggregate operation encounters an error with the data. */ +export class DataError extends Error { + override readonly name = "DataError"; +} + +/** + * Warning raised when reading a file with mismatched dtypes. + * Equivalent to pandas `DtypeWarning`. + */ +export class DtypeWarning extends Error { + override readonly name = "DtypeWarning"; +} + +/** Raised when attempting to read an empty file. */ +export class EmptyDataError extends Error { + override readonly name = "EmptyDataError"; + constructor(message = "No columns to parse from file") { + super(message); + } +} + +/** Raised when casting to integer would lose data due to NaN values. */ +export class IntCastingNaNError extends Error { + override readonly name = "IntCastingNaNError"; + constructor(message = "Cannot convert non-finite values (NA or inf) to integer") { + super(message); + } +} + +/** Raised when an invalid column name is used. */ +export class InvalidColumnName extends Error { + override readonly name = "InvalidColumnName"; +} + +/** Raised when a comparison is attempted with incompatible types. */ +export class InvalidComparison extends TypeError { + override readonly name = "InvalidComparison"; +} + +/** Raised when an invalid label is used for indexing. */ +export class InvalidIndexError extends Error { + override readonly name = "InvalidIndexError"; + constructor(message = "label not found in index") { + super(message); + } +} + +/** Raised when a boolean index with an invalid shape is used. */ +export class InvalidUseOfBooleanIndex extends IndexError { + override readonly name = "InvalidUseOfBooleanIndex"; +} + +/** Raised when a version string cannot be parsed. */ +export class InvalidVersion extends ValueError { + override readonly name = "InvalidVersion"; +} + +/** Raised when a setitem operation would silently lose information. */ +export class LossySetitemError extends Error { + override readonly name = "LossySetitemError"; +} + +/** Raised when a merge operation is performed incorrectly. */ +export class MergeError extends ValueError { + override readonly name = "MergeError"; +} + +/** Raised when an operation requires a non-null frequency but the frequency is null. */ +export class NullFrequencyError extends ValueError { + override readonly name = "NullFrequencyError"; + constructor(message = "Cannot have a null frequency with a non-trivial period index") { + super(message); + } +} + +/** Raised when a Numba utility encounters an error. */ +export class NumbaUtilError extends Error { + override readonly name = "NumbaUtilError"; +} + +/** Raised when an invalid option is encountered. */ +export class OptionError extends KeyError { + override readonly name = "OptionError"; +} + +/** Raised when a datetime value is out of the supported range. */ +export class OutOfBoundsDatetime extends ValueError { + override readonly name = "OutOfBoundsDatetime"; + constructor(message = "Out of bounds nanosecond timestamp") { + super(message); + } +} + +/** Raised when a timedelta value is out of the supported range. */ +export class OutOfBoundsTimedelta extends ValueError { + override readonly name = "OutOfBoundsTimedelta"; + constructor(message = "Out of bounds timedelta") { + super(message); + } +} + +/** Raised when a file or string cannot be parsed. */ +export class ParserError extends ValueError { + override readonly name = "ParserError"; +} + +/** Warning raised when a parser falls back to a less-efficient parser. */ +export class ParserWarning extends Error { + override readonly name = "ParserWarning"; +} + +/** Warning raised when a performance issue is detected. */ +export class PerformanceWarning extends Error { + override readonly name = "PerformanceWarning"; +} + +/** Raised when writing to a file may result in data loss. */ +export class PossibleDataLossError extends Error { + override readonly name = "PossibleDataLossError"; +} + +/** Warning raised when floating-point precision loss may occur. */ +export class PossiblePrecisionLoss extends Error { + override readonly name = "PossiblePrecisionLoss"; +} + +/** Raised when there is a specification error in a groupby agg call. */ +export class SpecificationError extends ValueError { + override readonly name = "SpecificationError"; +} + +/** Raised when an unsorted MultiIndex is used in a way that requires sorting. */ +export class UnsortedIndexError extends KeyError { + override readonly name = "UnsortedIndexError"; + constructor(message = "MultiIndex slicing requires the index to be lexsorted") { + super(message); + } +} + +/** Raised when calling a function on an object that does not support it. */ +export class UnsupportedFunctionCall extends ValueError { + override readonly name = "UnsupportedFunctionCall"; +} + +/** Warning raised when accessor registration may shadow a built-in attribute. */ +export class AccessorRegistrationWarning extends Error { + override readonly name = "AccessorRegistrationWarning"; +} + +/** Raised when a value and label have mismatched types in a Categorical. */ +export class ValueLabelTypeMismatch extends Error { + override readonly name = "ValueLabelTypeMismatch"; +} + +// --------------------------------------------------------------------------- +// Base error helpers used above (JavaScript does not have ValueError / KeyError +// / IndexError natively, so we define lightweight stand-ins here that still +// inherit from Error and therefore work with `instanceof Error`). +// --------------------------------------------------------------------------- + +/** TypeError-compatible base for value-related errors (mirrors Python's ValueError). */ +export class ValueError extends TypeError { + override readonly name = "ValueError"; +} + +/** Error-compatible base for key-related errors (mirrors Python's KeyError). */ +export class KeyError extends Error { + override readonly name = "KeyError"; +} + +/** Error-compatible base for index-related errors (mirrors Python's IndexError). */ +export class IndexError extends RangeError { + override readonly name = "IndexError"; +} + +// --------------------------------------------------------------------------- +// Namespace export (mirrors `pd.errors`) +// --------------------------------------------------------------------------- + +/** All pandas-compatible error and warning classes, grouped as `pd.errors`. */ +export const errors = { + AbstractMethodError, + AccessorRegistrationWarning, + AttributeConflictWarning, + CSSWarning, + ChainedAssignmentError, + DatabaseError, + DataError, + DtypeWarning, + EmptyDataError, + IntCastingNaNError, + InvalidColumnName, + InvalidComparison, + InvalidIndexError, + InvalidUseOfBooleanIndex, + InvalidVersion, + LossySetitemError, + MergeError, + NullFrequencyError, + NumbaUtilError, + OptionError, + OutOfBoundsDatetime, + OutOfBoundsTimedelta, + ParserError, + ParserWarning, + PerformanceWarning, + PossibleDataLossError, + PossiblePrecisionLoss, + SpecificationError, + UnsortedIndexError, + UnsupportedFunctionCall, + ValueLabelTypeMismatch, + // base classes + ValueError, + KeyError, + IndexError, +} as const; + +export type PandasError = InstanceType<(typeof errors)[keyof typeof errors]>; diff --git a/src/index.ts b/src/index.ts index a6180397..f1b07645 100644 --- a/src/index.ts +++ b/src/index.ts @@ -741,3 +741,43 @@ export { seriesToLaTeX, } from "./stats/format_table.ts"; export type { ToMarkdownOptions, ToLaTeXOptions } from "./stats/format_table.ts"; + +// pd.errors — pandas-compatible error and warning classes +export { + errors, + AbstractMethodError, + AccessorRegistrationWarning, + AttributeConflictWarning, + CSSWarning, + ChainedAssignmentError, + DatabaseError, + DataError, + DtypeWarning, + EmptyDataError, + IntCastingNaNError, + InvalidColumnName, + InvalidComparison, + InvalidIndexError, + InvalidUseOfBooleanIndex, + InvalidVersion, + LossySetitemError, + MergeError, + NullFrequencyError, + NumbaUtilError, + OptionError, + OutOfBoundsDatetime, + OutOfBoundsTimedelta, + ParserError, + ParserWarning, + PerformanceWarning, + PossibleDataLossError, + PossiblePrecisionLoss, + SpecificationError, + UnsortedIndexError, + UnsupportedFunctionCall, + ValueLabelTypeMismatch, + ValueError, + KeyError, + IndexError, +} from "./errors.ts"; +export type { PandasError } from "./errors.ts"; diff --git a/tests/errors.test.ts b/tests/errors.test.ts new file mode 100644 index 00000000..ab4de565 --- /dev/null +++ b/tests/errors.test.ts @@ -0,0 +1,331 @@ +/** + * Tests for pd.errors — pandas-compatible error and warning classes. + */ +import { describe, expect, test } from "bun:test"; +import { + AbstractMethodError, + AccessorRegistrationWarning, + AttributeConflictWarning, + CSSWarning, + ChainedAssignmentError, + DatabaseError, + DataError, + DtypeWarning, + EmptyDataError, + IndexError, + IntCastingNaNError, + InvalidColumnName, + InvalidComparison, + InvalidIndexError, + InvalidUseOfBooleanIndex, + InvalidVersion, + KeyError, + LossySetitemError, + MergeError, + NullFrequencyError, + NumbaUtilError, + OptionError, + OutOfBoundsDatetime, + OutOfBoundsTimedelta, + ParserError, + ParserWarning, + PerformanceWarning, + PossibleDataLossError, + PossiblePrecisionLoss, + SpecificationError, + UnsortedIndexError, + UnsupportedFunctionCall, + ValueError, + ValueLabelTypeMismatch, + errors, +} from "../../src/index.ts"; + +// --------------------------------------------------------------------------- +// Helper: assert that a class is throwable and catchable +// --------------------------------------------------------------------------- +function assertThrowable( + Cls: new (...args: never[]) => T, + args: ConstructorParameters, + expectedMessage?: string, +): T { + const instance = new Cls(...(args as ConstructorParameters)); + expect(instance).toBeInstanceOf(Error); + expect(instance).toBeInstanceOf(Cls); + if (expectedMessage !== undefined) { + expect(instance.message).toBe(expectedMessage); + } + // Verify it can be caught with instanceof + let caught: unknown; + try { + throw instance; + } catch (e) { + caught = e; + } + expect(caught).toBeInstanceOf(Cls); + return instance; +} + +// --------------------------------------------------------------------------- +// Base classes +// --------------------------------------------------------------------------- + +describe("ValueError", () => { + test("is a TypeError", () => { + const e = new ValueError("bad value"); + expect(e).toBeInstanceOf(TypeError); + expect(e).toBeInstanceOf(ValueError); + expect(e.name).toBe("ValueError"); + expect(e.message).toBe("bad value"); + }); +}); + +describe("KeyError", () => { + test("is an Error", () => { + const e = new KeyError("missing key"); + expect(e).toBeInstanceOf(Error); + expect(e.name).toBe("KeyError"); + expect(e.message).toBe("missing key"); + }); +}); + +describe("IndexError", () => { + test("is a RangeError", () => { + const e = new IndexError("out of bounds"); + expect(e).toBeInstanceOf(RangeError); + expect(e.name).toBe("IndexError"); + }); +}); + +// --------------------------------------------------------------------------- +// All error classes +// --------------------------------------------------------------------------- + +describe("AbstractMethodError", () => { + test("message includes class name", () => { + const e = new AbstractMethodError("MyClass.myMethod"); + expect(e.message).toContain("MyClass.myMethod"); + expect(e).toBeInstanceOf(Error); + expect(e.name).toBe("AbstractMethodError"); + }); + test("instanceof check works", () => assertThrowable(AbstractMethodError, ["Foo"])); +}); + +describe("AccessorRegistrationWarning", () => { + test("throwable", () => { + const e = new AccessorRegistrationWarning("shadowing built-in"); + expect(e.name).toBe("AccessorRegistrationWarning"); + expect(e.message).toBe("shadowing built-in"); + }); +}); + +describe("AttributeConflictWarning", () => { + test("throwable", () => + assertThrowable(AttributeConflictWarning, ["conflict"])); +}); + +describe("CSSWarning", () => { + test("name is CSSWarning", () => { + expect(new CSSWarning("bad CSS").name).toBe("CSSWarning"); + }); +}); + +describe("ChainedAssignmentError", () => { + test("default message", () => { + const e = new ChainedAssignmentError(); + expect(e.message).toContain("copy of a slice"); + expect(e.name).toBe("ChainedAssignmentError"); + }); + test("custom message", () => { + const e = new ChainedAssignmentError("custom"); + expect(e.message).toBe("custom"); + }); +}); + +describe("DatabaseError", () => { + test("throwable", () => assertThrowable(DatabaseError, ["DB error"])); +}); + +describe("DataError", () => { + test("throwable", () => assertThrowable(DataError, ["data error"])); +}); + +describe("DtypeWarning", () => { + test("throwable", () => assertThrowable(DtypeWarning, ["dtype warning"])); +}); + +describe("EmptyDataError", () => { + test("default message", () => { + expect(new EmptyDataError().message).toContain("No columns"); + }); + test("custom message", () => { + const e = new EmptyDataError("custom"); + expect(e.message).toBe("custom"); + }); + test("instanceof", () => assertThrowable(EmptyDataError, [])); +}); + +describe("IntCastingNaNError", () => { + test("default message", () => { + expect(new IntCastingNaNError().message).toContain("non-finite"); + }); + test("instanceof", () => assertThrowable(IntCastingNaNError, [])); +}); + +describe("InvalidColumnName", () => { + test("throwable", () => assertThrowable(InvalidColumnName, ["bad col"])); +}); + +describe("InvalidComparison", () => { + test("is TypeError", () => { + const e = new InvalidComparison("bad compare"); + expect(e).toBeInstanceOf(TypeError); + expect(e.name).toBe("InvalidComparison"); + }); +}); + +describe("InvalidIndexError", () => { + test("default message", () => { + expect(new InvalidIndexError().message).toContain("label not found"); + }); + test("instanceof", () => assertThrowable(InvalidIndexError, [])); +}); + +describe("InvalidUseOfBooleanIndex", () => { + test("is IndexError", () => { + const e = new InvalidUseOfBooleanIndex("bad bool"); + expect(e).toBeInstanceOf(IndexError); + expect(e.name).toBe("InvalidUseOfBooleanIndex"); + }); +}); + +describe("InvalidVersion", () => { + test("is ValueError", () => { + const e = new InvalidVersion("bad ver"); + expect(e).toBeInstanceOf(ValueError); + expect(e.name).toBe("InvalidVersion"); + }); +}); + +describe("LossySetitemError", () => { + test("throwable", () => assertThrowable(LossySetitemError, ["lossy"])); +}); + +describe("MergeError", () => { + test("is ValueError", () => { + const e = new MergeError("merge fail"); + expect(e).toBeInstanceOf(ValueError); + expect(e.name).toBe("MergeError"); + }); +}); + +describe("NullFrequencyError", () => { + test("default message", () => { + expect(new NullFrequencyError().message).toContain("null frequency"); + }); + test("is ValueError", () => { + expect(new NullFrequencyError()).toBeInstanceOf(ValueError); + }); +}); + +describe("NumbaUtilError", () => { + test("throwable", () => assertThrowable(NumbaUtilError, ["numba"])); +}); + +describe("OptionError", () => { + test("is KeyError", () => { + const e = new OptionError("bad option"); + expect(e).toBeInstanceOf(KeyError); + expect(e.name).toBe("OptionError"); + }); +}); + +describe("OutOfBoundsDatetime", () => { + test("default message", () => { + expect(new OutOfBoundsDatetime().message).toContain("nanosecond"); + }); + test("is ValueError", () => { + expect(new OutOfBoundsDatetime()).toBeInstanceOf(ValueError); + }); +}); + +describe("OutOfBoundsTimedelta", () => { + test("default message", () => { + expect(new OutOfBoundsTimedelta().message).toContain("timedelta"); + }); + test("is ValueError", () => { + expect(new OutOfBoundsTimedelta()).toBeInstanceOf(ValueError); + }); +}); + +describe("ParserError", () => { + test("is ValueError", () => { + const e = new ParserError("parse fail"); + expect(e).toBeInstanceOf(ValueError); + expect(e.name).toBe("ParserError"); + }); +}); + +describe("ParserWarning", () => { + test("throwable", () => assertThrowable(ParserWarning, ["fallback parser"])); +}); + +describe("PerformanceWarning", () => { + test("throwable", () => assertThrowable(PerformanceWarning, ["slow op"])); +}); + +describe("PossibleDataLossError", () => { + test("throwable", () => assertThrowable(PossibleDataLossError, ["data loss"])); +}); + +describe("PossiblePrecisionLoss", () => { + test("throwable", () => assertThrowable(PossiblePrecisionLoss, ["precision loss"])); +}); + +describe("SpecificationError", () => { + test("is ValueError", () => { + expect(new SpecificationError("bad spec")).toBeInstanceOf(ValueError); + expect(new SpecificationError("bad spec").name).toBe("SpecificationError"); + }); +}); + +describe("UnsortedIndexError", () => { + test("default message", () => { + expect(new UnsortedIndexError().message).toContain("lexsorted"); + }); + test("is KeyError", () => { + expect(new UnsortedIndexError()).toBeInstanceOf(KeyError); + }); +}); + +describe("UnsupportedFunctionCall", () => { + test("is ValueError", () => { + expect(new UnsupportedFunctionCall("no fn")).toBeInstanceOf(ValueError); + }); +}); + +describe("ValueLabelTypeMismatch", () => { + test("throwable", () => assertThrowable(ValueLabelTypeMismatch, ["mismatch"])); +}); + +// --------------------------------------------------------------------------- +// errors namespace +// --------------------------------------------------------------------------- + +describe("errors namespace", () => { + test("contains all classes", () => { + expect(errors.AbstractMethodError).toBe(AbstractMethodError); + expect(errors.EmptyDataError).toBe(EmptyDataError); + expect(errors.MergeError).toBe(MergeError); + expect(errors.ParserError).toBe(ParserError); + expect(errors.ValueError).toBe(ValueError); + expect(errors.KeyError).toBe(KeyError); + expect(errors.IndexError).toBe(IndexError); + }); + + test("all namespace entries are constructors", () => { + for (const [key, Cls] of Object.entries(errors)) { + const instance = new Cls(key); + expect(instance).toBeInstanceOf(Error); + } + }); +}); From a62de26f94992ad3dfe4396cf0c9e2d112961e5c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 14 May 2026 07:58:32 +0000 Subject: [PATCH 02/16] ci: trigger checks From c914bc3a605a39d88d0e9674a485d353b1752d46 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 08:14:45 +0000 Subject: [PATCH 03/16] fix: resolve TypeScript errors in pd.errors module and add interactive playground MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move ValueError/KeyError/IndexError base class declarations before derived classes in src/errors.ts (fixes TS2449 'used before declaration' errors) - Fix import path in tests/errors.test.ts: ../../src/index.ts → ../src/index.ts - Change assertThrowable return type to void (fixes TS2322 type errors in tests) - Fix Object.entries loop: use Object.keys with typed keyof lookup - Replace static errors.html with interactive playground page that has proper .playground-run cells (fixes E2E timeout) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- playground/errors.html | 416 ++++++++++++++++++++++++++++------------- src/errors.ts | 40 ++-- tests/errors.test.ts | 11 +- 3 files changed, 306 insertions(+), 161 deletions(-) diff --git a/playground/errors.html b/playground/errors.html index 4a92b883..503c0515 100644 --- a/playground/errors.html +++ b/playground/errors.html @@ -1,157 +1,303 @@ - - - - pd.errors — tsb playground - - - - - -

pd.errors new in this release

-

- tsb ships a full set of pandas-compatible exception and warning classes - under the errors namespace — mirroring Python's pd.errors. -

+ + + + tsb — pd.errors + + + + +
+
+
Initializing playground…
+
+ + ← Back to roadmap +

pd.errors

+

Pandas-compatible error and warning classes — mirrors Python's pd.errors module. + All classes extend native Error and integrate with try/catch and instanceof.

+ +
+

1 — Base classes: ValueError, KeyError, IndexError

+

Three base classes mirror Python's built-in exceptions. They extend native JS error types so they + work with standard error-handling idioms.

+
+
+ TypeScript +
+ + +
+
+ +
Click ▶ Run to execute
+
Ctrl+Enter to run · Tab to indent
+
+
+ +
+

2 — Catching specific errors with instanceof

+

Use instanceof in catch blocks to handle specific error types — just like Python's + except SpecificError.

+
+
+ TypeScript +
+ + +
+
+ +
Click ▶ Run to execute
+
Ctrl+Enter to run · Tab to indent
+
+
-

Using the errors namespace (like pd.errors)

-
import { errors } from "tsb";
+  
+

3 — The errors namespace (pd.errors style)

+

All error classes are grouped under the errors namespace, mirroring + Python's pd.errors.ParserError etc.

+
+
+ TypeScript +
+ + +
+
+ +
Click ▶ Run to execute
+
Ctrl+Enter to run · Tab to indent
+
+
-class BaseProcessor { - process(): void { - throw new errors.AbstractMethodError("BaseProcessor.process"); +
+

4 — AbstractMethodError for extension classes

+

AbstractMethodError is thrown when a subclass forgets to implement a required method — + mirroring Python's raise NotImplementedError.

+
+
+ TypeScript +
+ + +
+
+ +
Click ▶ Run to execute
+
Ctrl+Enter to run · Tab to indent
+
+
+ + + + + diff --git a/src/errors.ts b/src/errors.ts index 7c024386..4032208e 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -8,6 +8,25 @@ * @packageDocumentation */ +// --------------------------------------------------------------------------- +// Base error helpers (must be declared first so derived classes can extend them) +// --------------------------------------------------------------------------- + +/** TypeError-compatible base for value-related errors (mirrors Python's ValueError). */ +export class ValueError extends TypeError { + override readonly name = "ValueError"; +} + +/** Error-compatible base for key-related errors (mirrors Python's KeyError). */ +export class KeyError extends Error { + override readonly name = "KeyError"; +} + +/** Error-compatible base for index-related errors (mirrors Python's IndexError). */ +export class IndexError extends RangeError { + override readonly name = "IndexError"; +} + // --------------------------------------------------------------------------- // Error classes // --------------------------------------------------------------------------- @@ -202,27 +221,6 @@ export class ValueLabelTypeMismatch extends Error { override readonly name = "ValueLabelTypeMismatch"; } -// --------------------------------------------------------------------------- -// Base error helpers used above (JavaScript does not have ValueError / KeyError -// / IndexError natively, so we define lightweight stand-ins here that still -// inherit from Error and therefore work with `instanceof Error`). -// --------------------------------------------------------------------------- - -/** TypeError-compatible base for value-related errors (mirrors Python's ValueError). */ -export class ValueError extends TypeError { - override readonly name = "ValueError"; -} - -/** Error-compatible base for key-related errors (mirrors Python's KeyError). */ -export class KeyError extends Error { - override readonly name = "KeyError"; -} - -/** Error-compatible base for index-related errors (mirrors Python's IndexError). */ -export class IndexError extends RangeError { - override readonly name = "IndexError"; -} - // --------------------------------------------------------------------------- // Namespace export (mirrors `pd.errors`) // --------------------------------------------------------------------------- diff --git a/tests/errors.test.ts b/tests/errors.test.ts index ab4de565..d916cb4d 100644 --- a/tests/errors.test.ts +++ b/tests/errors.test.ts @@ -38,7 +38,7 @@ import { ValueError, ValueLabelTypeMismatch, errors, -} from "../../src/index.ts"; +} from "../src/index.ts"; // --------------------------------------------------------------------------- // Helper: assert that a class is throwable and catchable @@ -47,7 +47,7 @@ function assertThrowable( Cls: new (...args: never[]) => T, args: ConstructorParameters, expectedMessage?: string, -): T { +): void { const instance = new Cls(...(args as ConstructorParameters)); expect(instance).toBeInstanceOf(Error); expect(instance).toBeInstanceOf(Cls); @@ -62,7 +62,6 @@ function assertThrowable( caught = e; } expect(caught).toBeInstanceOf(Cls); - return instance; } // --------------------------------------------------------------------------- @@ -323,8 +322,10 @@ describe("errors namespace", () => { }); test("all namespace entries are constructors", () => { - for (const [key, Cls] of Object.entries(errors)) { - const instance = new Cls(key); + const errorKeys = Object.keys(errors) as Array; + for (const key of errorKeys) { + const Cls = errors[key]; + const instance = new Cls(key as never); expect(instance).toBeInstanceOf(Error); } }); From 5bccf5b67d82a0d7237ece7b553147191ebfcacc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 14 May 2026 08:14:48 +0000 Subject: [PATCH 04/16] ci: trigger checks From dbe46c6184c3579d5fb1622e38f1f2c1f9552914 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 09:16:05 +0000 Subject: [PATCH 05/16] fix: resolve TypeScript errors in errors.ts and errors.test.ts - Annotate base class name properties as string to allow subclass overrides - Fix assertThrowable generic signature to accept typed constructor args Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/errors.ts | 6 +++--- tests/errors.test.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/errors.ts b/src/errors.ts index 4032208e..f2dbbe1f 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -14,17 +14,17 @@ /** TypeError-compatible base for value-related errors (mirrors Python's ValueError). */ export class ValueError extends TypeError { - override readonly name = "ValueError"; + override readonly name: string = "ValueError"; } /** Error-compatible base for key-related errors (mirrors Python's KeyError). */ export class KeyError extends Error { - override readonly name = "KeyError"; + override readonly name: string = "KeyError"; } /** Error-compatible base for index-related errors (mirrors Python's IndexError). */ export class IndexError extends RangeError { - override readonly name = "IndexError"; + override readonly name: string = "IndexError"; } // --------------------------------------------------------------------------- diff --git a/tests/errors.test.ts b/tests/errors.test.ts index d916cb4d..534e7f72 100644 --- a/tests/errors.test.ts +++ b/tests/errors.test.ts @@ -43,12 +43,12 @@ import { // --------------------------------------------------------------------------- // Helper: assert that a class is throwable and catchable // --------------------------------------------------------------------------- -function assertThrowable( - Cls: new (...args: never[]) => T, - args: ConstructorParameters, +function assertThrowable( + Cls: new (...args: A) => T, + args: A, expectedMessage?: string, ): void { - const instance = new Cls(...(args as ConstructorParameters)); + const instance = new Cls(...args); expect(instance).toBeInstanceOf(Error); expect(instance).toBeInstanceOf(Cls); if (expectedMessage !== undefined) { From 275296d54bd20ad0aaf8bb2ea6b154bb3756389e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 14 May 2026 09:16:07 +0000 Subject: [PATCH 06/16] ci: trigger checks From eb6c354ad1567f9e5cefc235eeecb222683528b4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 10:06:26 +0000 Subject: [PATCH 07/16] fix: resolve biome lint errors in align.ts, astype.ts, and pd_array.ts - align.ts: add default case to switch in resolveIndex; flatten nested ternary in normalised axis computation - astype.ts: extract helper functions to reduce cognitive complexity of castScalar - pd_array.ts: wrap single-line if statements in block statements Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/core/align.ts | 12 +++++- src/core/astype.ts | 96 ++++++++++++++++++-------------------------- src/core/pd_array.ts | 14 +++---- 3 files changed, 57 insertions(+), 65 deletions(-) diff --git a/src/core/align.ts b/src/core/align.ts index 144f53b5..ebda596e 100644 --- a/src/core/align.ts +++ b/src/core/align.ts @@ -92,6 +92,8 @@ function resolveIndex(left: Index