Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 303 additions & 0 deletions playground/errors.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>tsb — pd.errors</title>
<style>
:root {
--bg: #0d1117;
--surface: #161b22;
--border: #30363d;
--text: #e6edf3;
--accent: #58a6ff;
--green: #3fb950;
--orange: #d29922;
--red: #f85149;
--font-mono: "Cascadia Code", "Fira Code", "JetBrains Mono", monospace;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
padding: 2rem;
max-width: 900px;
margin: 0 auto;
}
a { color: var(--accent); }
h1 { color: var(--accent); margin-bottom: 0.5rem; }
h2 { margin-top: 0; margin-bottom: 0.5rem; font-size: 1.25rem; }
p { color: #8b949e; margin-bottom: 1rem; }
code {
font-family: var(--font-mono);
font-size: 0.875em;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 0.3rem;
padding: 0.1rem 0.4rem;
}
.back { margin-bottom: 2rem; display: inline-block; }
.subtitle { margin-bottom: 1.5rem; }

#playground-loading {
position: fixed; inset: 0;
background: rgba(13, 17, 23, 0.92);
display: flex; flex-direction: column;
align-items: center; justify-content: center;
z-index: 1000; gap: 1rem;
}
.spinner {
width: 40px; height: 40px;
border: 3px solid var(--border);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
#playground-status { color: #8b949e; font-size: 0.95rem; }

.section {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 0.75rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.section p { margin-bottom: 0.75rem; }

.playground-block { margin-top: 0.75rem; }
.playground-header {
display: flex; align-items: center; justify-content: space-between;
background: #1c2128;
border: 1px solid var(--border);
border-bottom: none;
border-radius: 0.5rem 0.5rem 0 0;
padding: 0.4rem 0.75rem;
}
.playground-label {
font-size: 0.75rem; color: #8b949e;
text-transform: uppercase; letter-spacing: 0.05em;
}
.playground-actions { display: flex; gap: 0.5rem; }
.playground-actions button {
background: transparent; color: var(--accent);
border: 1px solid var(--border);
border-radius: 0.35rem;
padding: 0.25rem 0.7rem;
font-size: 0.8rem; cursor: pointer;
font-family: system-ui, sans-serif;
transition: background 0.15s, border-color 0.15s;
}
.playground-actions button:hover:not(:disabled) {
background: rgba(88, 166, 255, 0.1);
border-color: var(--accent);
}
.playground-actions button:disabled { opacity: 0.4; cursor: not-allowed; }
.playground-run { font-weight: 600; }

.playground-editor {
display: block; width: 100%; min-height: 80px;
background: #0d1117; color: var(--text);
border: 1px solid var(--border);
border-top: none; border-bottom: none;
padding: 1rem;
font-family: var(--font-mono);
font-size: 0.875rem; line-height: 1.55;
resize: vertical; outline: none;
tab-size: 2; white-space: pre; overflow-x: auto;
}
.playground-editor:focus {
border-color: var(--accent);
box-shadow: inset 0 0 0 1px var(--accent);
}

.playground-output {
background: #1c2333;
border: 1px solid var(--border);
border-radius: 0 0 0.5rem 0.5rem;
padding: 0.75rem 1rem;
font-family: var(--font-mono);
font-size: 0.85rem; color: #8b949e;
white-space: pre-wrap; min-height: 2rem;
word-break: break-word;
}
.playground-output.active { color: var(--green); border-color: var(--green); }
.playground-output.error { color: var(--red); border-color: var(--red); }
.playground-hint {
font-size: 0.75rem; color: #484f58;
margin-top: 0.35rem; text-align: right;
}

footer {
text-align: center;
padding: 2rem 0;
color: #8b949e;
font-size: 0.85rem;
border-top: 1px solid var(--border);
margin-top: 2rem;
}
</style>
</head>
<body>

<div id="playground-loading">
<div class="spinner"></div>
<div id="playground-status">Initializing playground…</div>
</div>

<a class="back" href="index.html">← Back to roadmap</a>
<h1>pd.errors</h1>
<p class="subtitle">Pandas-compatible error and warning classes — mirrors Python's <code>pd.errors</code> module.
All classes extend native <code>Error</code> and integrate with <code>try/catch</code> and <code>instanceof</code>.</p>

<div class="section">
<h2>1 — Base classes: ValueError, KeyError, IndexError</h2>
<p>Three base classes mirror Python's built-in exceptions. They extend native JS error types so they
work with standard error-handling idioms.</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">TypeScript</span>
<div class="playground-actions">
<button class="playground-run" disabled>▶ Run</button>
<button class="playground-reset">↺ Reset</button>
</div>
</div>
<textarea class="playground-editor" spellcheck="false">import { ValueError, KeyError, IndexError } from "tsb";

const ve = new ValueError("bad value");
console.log(ve instanceof TypeError); // true (ValueError extends TypeError)
console.log(ve.name); // "ValueError"

const ke = new KeyError("missing key");
console.log(ke instanceof Error); // true
console.log(ke.name); // "KeyError"

const ie = new IndexError("out of bounds");
console.log(ie instanceof RangeError); // true
console.log(ie.name); // "IndexError"</textarea>
<div class="playground-output">Click ▶ Run to execute</div>
<div class="playground-hint">Ctrl+Enter to run · Tab to indent</div>
</div>
</div>

<div class="section">
<h2>2 — Catching specific errors with instanceof</h2>
<p>Use <code>instanceof</code> in catch blocks to handle specific error types — just like Python's
<code>except SpecificError</code>.</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">TypeScript</span>
<div class="playground-actions">
<button class="playground-run" disabled>▶ Run</button>
<button class="playground-reset">↺ Reset</button>
</div>
</div>
<textarea class="playground-editor" spellcheck="false">import { ParserError, EmptyDataError, MergeError } from "tsb";

function riskyOperation(kind: string): void {
if (kind === "parse") throw new ParserError("invalid CSV format");
if (kind === "empty") throw new EmptyDataError();
if (kind === "merge") throw new MergeError("incompatible merge keys");
}

for (const kind of ["parse", "empty", "merge"]) {
try {
riskyOperation(kind);
} catch (e) {
if (e instanceof EmptyDataError) {
console.log("Empty file:", e.message);
} else if (e instanceof ParserError) {
console.log("Parse error:", e.message);
} else if (e instanceof MergeError) {
console.log("Merge error:", e.message);
}
}
}</textarea>
<div class="playground-output">Click ▶ Run to execute</div>
<div class="playground-hint">Ctrl+Enter to run · Tab to indent</div>
</div>
</div>

<div class="section">
<h2>3 — The errors namespace (pd.errors style)</h2>
<p>All error classes are grouped under the <code>errors</code> namespace, mirroring
Python's <code>pd.errors.ParserError</code> etc.</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">TypeScript</span>
<div class="playground-actions">
<button class="playground-run" disabled>▶ Run</button>
<button class="playground-reset">↺ Reset</button>
</div>
</div>
<textarea class="playground-editor" spellcheck="false">import { errors } from "tsb";

// Access any error class via 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"

// OutOfBoundsDatetime with default message
const dt = new errors.OutOfBoundsDatetime();
console.log(dt.message); // "Out of bounds nanosecond timestamp"
console.log(dt instanceof errors.ValueError); // true</textarea>
<div class="playground-output">Click ▶ Run to execute</div>
<div class="playground-hint">Ctrl+Enter to run · Tab to indent</div>
</div>
</div>

<div class="section">
<h2>4 — AbstractMethodError for extension classes</h2>
<p><code>AbstractMethodError</code> is thrown when a subclass forgets to implement a required method —
mirroring Python's <code>raise NotImplementedError</code>.</p>
<div class="playground-block">
<div class="playground-header">
<span class="playground-label">TypeScript</span>
<div class="playground-actions">
<button class="playground-run" disabled>▶ Run</button>
<button class="playground-reset">↺ Reset</button>
</div>
</div>
<textarea class="playground-editor" spellcheck="false">import { AbstractMethodError } from "tsb";

class BaseTransform {
transform(_value: number): number {
throw new AbstractMethodError("BaseTransform.transform");
}
}

class DoubleTransform extends BaseTransform {
override transform(value: number): number {
return value * 2;
}
}

const t = new DoubleTransform();
console.log(t.transform(5)); // 10

try {
const base = new BaseTransform();
base.transform(5);
} catch (e) {
if (e instanceof AbstractMethodError) {
console.log("Abstract method called:", e.message);
}
}</textarea>
<div class="playground-output">Click ▶ Run to execute</div>
<div class="playground-hint">Ctrl+Enter to run · Tab to indent</div>
</div>
</div>

<footer>
<p>
<a href="index.html">tsb playground</a> ·
Built by <a href="https://github.com/githubnext/autoloop">Autoloop</a>
</p>
</footer>

<script type="module" src="playground-runtime.js"></script>
</body>
</html>
12 changes: 10 additions & 2 deletions src/core/align.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ function resolveIndex(left: Index<Label>, right: Index<Label>, join: JoinHow): I
return left;
case "right":
return right;
default:
return left.union(right);
}
}

Expand Down Expand Up @@ -161,8 +163,14 @@ export function alignDataFrame(
const { join = "outer", fillValue = null, axis } = options;

// Normalise axis: null/undefined → align both
const normalised: 0 | 1 | null =
axis === null || axis === undefined ? null : axis === 0 || axis === "index" ? 0 : 1;
let normalised: 0 | 1 | null;
if (axis === null || axis === undefined) {
normalised = null;
} else if (axis === 0 || axis === "index") {
normalised = 0;
} else {
normalised = 1;
}

const alignRows = normalised === null || normalised === 0;
const alignCols = normalised === null || normalised === 1;
Expand Down
Loading
Loading