| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,6 +23,7 @@ import Papa from 'papaparse' | |||
| 23 | 23 | * hence all PapaParse options are supported. | |
| 24 | 24 | * @param options Configuration object. Supports all Papaparse parse config options. | |
| 25 | 25 | * @returns DataFrame containing the parsed CSV file. | |
| 26 | + * @throws {Error} If file cannot be read or parsed | ||
| 26 | 27 | * @example | |
| 27 | 28 | * ``` | |
| 28 | 29 | * import { readCSV } from "danfojs-node" | |
@@ -48,16 +49,39 @@ import Papa from 'papaparse' | |||
| 48 | 49 | const $readCSV = async (file: any, options?: CsvInputOptionsBrowser): Promise<DataFrame> => { | |
| 49 | 50 | const frameConfig = options?.frameConfig || {} | |
| 50 | 51 | ||
| 51 | - return new Promise(resolve => { | ||
| 52 | + return new Promise((resolve, reject) => { | ||
| 53 | + let hasError = false; | ||
| 54 | + | ||
| 52 | 55 | Papa.parse(file, { | |
| 53 | 56 | header: true, | |
| 54 | 57 | dynamicTyping: true, | |
| 55 | 58 | skipEmptyLines: 'greedy', | |
| 56 | 59 | ...options, | |
| 60 | + error: (error) => { | ||
| 61 | + hasError = true; | ||
| 62 | + reject(new Error(`Failed to parse CSV: ${error.message}`)); | ||
| 63 | + }, | ||
| 57 | 64 | download: true, | |
| 58 | - complete: results => { | ||
| 59 | - const df = new DataFrame(results.data, frameConfig); | ||
| 60 | - resolve(df); | ||
| 65 | + complete: (results) => { | ||
| 66 | + if (hasError) return; // Skip if error already occurred | ||
| 67 | + | ||
| 68 | + if (!results.data || results.data.length === 0) { | ||
| 69 | + reject(new Error('No data found in CSV file')); | ||
| 70 | + return; | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + if (results.errors && results.errors.length > 0) { | ||
| 74 | + reject(new Error(`CSV parsing errors: ${results.errors.map(e => e.message).join(', ')}`)); | ||
| 75 | + return; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + try { | ||
| 79 | + const df = new DataFrame(results.data, frameConfig); | ||
| 80 | + resolve(df); | ||
| 81 | + } catch (error) { | ||
| 82 | + const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; | ||
| 83 | + reject(new Error(`Failed to create DataFrame from CSV: ${errorMessage}`)); | ||
| 84 | + } | ||
| 61 | 85 | } | |
| 62 | 86 | }); | |
| 63 | 87 | }); | |
@@ -81,18 +105,35 @@ const $readCSV = async (file: any, options?: CsvInputOptionsBrowser): Promise<Da | |||
| 81 | 105 | const $streamCSV = async (file: string, callback: (df: DataFrame) => void, options: CsvInputOptionsBrowser,): Promise<null> => { | |
| 82 | 106 | const frameConfig = options?.frameConfig || {} | |
| 83 | 107 | ||
| 84 | - return new Promise(resolve => { | ||
| 108 | + return new Promise((resolve, reject) => { | ||
| 85 | 109 | let count = 0 | |
| 110 | + let hasError = false; | ||
| 111 | + | ||
| 86 | 112 | Papa.parse(file, { | |
| 87 | 113 | ...options, | |
| 88 | 114 | dynamicTyping: true, | |
| 89 | 115 | header: true, | |
| 90 | 116 | download: true, | |
| 91 | 117 | step: results => { | |
| 92 | - const df = new DataFrame([results.data], { ...frameConfig, index: [count++] }); | ||
| 93 | - callback(df); | ||
| 118 | + if (hasError) return; | ||
| 119 | + try { | ||
| 120 | + const df = new DataFrame([results.data], { ...frameConfig, index: [count++] }); | ||
| 121 | + callback(df); | ||
| 122 | + } catch (error) { | ||
| 123 | + hasError = true; | ||
| 124 | + const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; | ||
| 125 | + reject(new Error(`Failed to process CSV chunk: ${errorMessage}`)); | ||
| 126 | + } | ||
| 127 | + }, | ||
| 128 | + complete: () => { | ||
| 129 | + if (!hasError) { | ||
| 130 | + resolve(null); | ||
| 131 | + } | ||
| 94 | 132 | }, | |
| 95 | - complete: () => resolve(null) | ||
| 133 | + error: (error) => { | ||
| 134 | + hasError = true; | ||
| 135 | + reject(new Error(`Failed to parse CSV: ${error.message}`)); | ||
| 136 | + } | ||
| 96 | 137 | }); | |
| 97 | 138 | }); | |
| 98 | 139 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,6 +25,7 @@ import fs from 'fs' | |||
| 25 | 25 | * hence all PapaParse options are supported. | |
| 26 | 26 | * @param options Configuration object. Supports all Papaparse parse config options. | |
| 27 | 27 | * @returns DataFrame containing the parsed CSV file. | |
| 28 | + * @throws {Error} If file cannot be read or parsed | ||
| 28 | 29 | * @example | |
| 29 | 30 | * ``` | |
| 30 | 31 | * import { readCSV } from "danfojs-node" | |
@@ -52,6 +53,7 @@ const $readCSV = async (filePath: string, options?: CsvInputOptionsNode): Promis | |||
| 52 | 53 | ||
| 53 | 54 | if (filePath.startsWith("http") || filePath.startsWith("https")) { | |
| 54 | 55 | return new Promise((resolve, reject) => { | |
| 56 | + let hasError = false; | ||
| 55 | 57 | const optionsWithDefaults = { | |
| 56 | 58 | header: true, | |
| 57 | 59 | dynamicTyping: true, | |
@@ -63,6 +65,7 @@ const $readCSV = async (filePath: string, options?: CsvInputOptionsNode): Promis | |||
| 63 | 65 | // reject any non-2xx status codes | |
| 64 | 66 | dataStream.on('response', (response: any) => { | |
| 65 | 67 | if (response.statusCode < 200 || response.statusCode >= 300) { | |
| 68 | + hasError = true; | ||
| 66 | 69 | reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`)); | |
| 67 | 70 | } | |
| 68 | 71 | }); | |
@@ -72,30 +75,73 @@ const $readCSV = async (filePath: string, options?: CsvInputOptionsNode): Promis | |||
| 72 | 75 | ||
| 73 | 76 | const data: any = []; | |
| 74 | 77 | parseStream.on("data", (chunk: any) => { | |
| 75 | - data.push(chunk); | ||
| 78 | + if (!hasError) { | ||
| 79 | + data.push(chunk); | ||
| 80 | + } | ||
| 81 | + }); | ||
| 82 | + | ||
| 83 | + parseStream.on("error", (error: any) => { | ||
| 84 | + hasError = true; | ||
| 85 | + reject(new Error(`Failed to parse CSV: ${error.message}`)); | ||
| 76 | 86 | }); | |
| 77 | 87 | ||
| 78 | 88 | parseStream.on("finish", () => { | |
| 79 | - resolve(new DataFrame(data, frameConfig)); | ||
| 89 | + if (hasError) return; | ||
| 90 | + | ||
| 91 | + if (!data || data.length === 0) { | ||
| 92 | + reject(new Error('No data found in CSV file')); | ||
| 93 | + return; | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + try { | ||
| 97 | + const df = new DataFrame(data, frameConfig); | ||
| 98 | + resolve(df); | ||
| 99 | + } catch (error) { | ||
| 100 | + const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; | ||
| 101 | + reject(new Error(`Failed to create DataFrame: ${errorMessage}`)); | ||
| 102 | + } | ||
| 80 | 103 | }); | |
| 81 | 104 | }); | |
| 82 | 105 | ||
| 83 | 106 | } else { | |
| 84 | 107 | return new Promise((resolve, reject) => { | |
| 85 | 108 | fs.access(filePath, fs.constants.F_OK, (err) => { | |
| 86 | 109 | if (err) { | |
| 87 | - reject("ENOENT: no such file or directory"); | ||
| 110 | + reject(new Error("ENOENT: no such file or directory")); | ||
| 111 | + return; | ||
| 88 | 112 | } | |
| 89 | 113 | ||
| 90 | 114 | const fileStream = fs.createReadStream(filePath) | |
| 115 | + let hasError = false; | ||
| 91 | 116 | ||
| 92 | 117 | Papa.parse(fileStream, { | |
| 93 | 118 | header: true, | |
| 94 | 119 | dynamicTyping: true, | |
| 95 | 120 | ...options, | |
| 121 | + error: (error) => { | ||
| 122 | + hasError = true; | ||
| 123 | + reject(new Error(`Failed to parse CSV: ${error.message}`)); | ||
| 124 | + }, | ||
| 96 | 125 | complete: results => { | |
| 97 | - const df = new DataFrame(results.data, frameConfig); | ||
| 98 | - resolve(df); | ||
| 126 | + if (hasError) return; | ||
| 127 | + | ||
| 128 | + if (!results.data || results.data.length === 0) { | ||
| 129 | + reject(new Error('No data found in CSV file')); | ||
| 130 | + return; | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + if (results.errors && results.errors.length > 0) { | ||
| 134 | + reject(new Error(`CSV parsing errors: ${results.errors.map(e => e.message).join(', ')}`)); | ||
| 135 | + return; | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + try { | ||
| 139 | + const df = new DataFrame(results.data, frameConfig); | ||
| 140 | + resolve(df); | ||
| 141 | + } catch (error) { | ||
| 142 | + const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; | ||
| 143 | + reject(new Error(`Failed to create DataFrame: ${errorMessage}`)); | ||
| 144 | + } | ||
| 99 | 145 | } | |
| 100 | 146 | }); | |
| 101 | 147 | }) | |
@@ -109,6 +155,7 @@ const $readCSV = async (filePath: string, options?: CsvInputOptionsNode): Promis | |||
| 109 | 155 | * hence all PapaParse options are supported. | |
| 110 | 156 | * @param callback Callback function to be called once the specifed rows are parsed into DataFrame. | |
| 111 | 157 | * @param options Configuration object. Supports all Papaparse parse config options. | |
| 158 | + * @throws {Error} If file cannot be read or parsed | ||
| 112 | 159 | * @example | |
| 113 | 160 | * ``` | |
| 114 | 161 | * import { streamCSV } from "danfojs-node" | |
@@ -128,12 +175,14 @@ const $streamCSV = async (filePath: string, callback: (df: DataFrame) => void, o | |||
| 128 | 175 | ...options, | |
| 129 | 176 | } | |
| 130 | 177 | return new Promise((resolve, reject) => { | |
| 131 | - let count = 0 | ||
| 178 | + let count = 0; | ||
| 179 | + let hasError = false; | ||
| 132 | 180 | const dataStream = request.get(filePath); | |
| 133 | 181 | ||
| 134 | 182 | // reject any non-2xx status codes | |
| 135 | 183 | dataStream.on('response', (response: any) => { | |
| 136 | 184 | if (response.statusCode < 200 || response.statusCode >= 300) { | |
| 185 | + hasError = true; | ||
| 137 | 186 | reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`)); | |
| 138 | 187 | } | |
| 139 | 188 | }); | |
@@ -142,35 +191,71 @@ const $streamCSV = async (filePath: string, callback: (df: DataFrame) => void, o | |||
| 142 | 191 | dataStream.pipe(parseStream); | |
| 143 | 192 | ||
| 144 | 193 | parseStream.on("data", (chunk: any) => { | |
| 145 | - const df = new DataFrame([chunk], { ...frameConfig, index: [count++], }); | ||
| 146 | - callback(df); | ||
| 194 | + if (hasError) return; | ||
| 195 | + try { | ||
| 196 | + const df = new DataFrame([chunk], { ...frameConfig, index: [count++] }); | ||
| 197 | + callback(df); | ||
| 198 | + } catch (error) { | ||
| 199 | + hasError = true; | ||
| 200 | + const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; | ||
| 201 | + reject(new Error(`Failed to process CSV chunk: ${errorMessage}`)); | ||
| 202 | + } | ||
| 147 | 203 | }); | |
| 148 | 204 | ||
| 149 | - parseStream.on("finish", () => { | ||
| 150 | - resolve(null); | ||
| 205 | + parseStream.on("error", (error: any) => { | ||
| 206 | + hasError = true; | ||
| 207 | + reject(new Error(`Failed to parse CSV: ${error.message}`)); | ||
| 151 | 208 | }); | |
| 152 | 209 | ||
| 210 | + parseStream.on("finish", () => { | ||
| 211 | + if (!hasError) { | ||
| 212 | + resolve(null); | ||
| 213 | + } | ||
| 214 | + }); | ||
| 153 | 215 | }); | |
| 154 | 216 | } else { | |
| 155 | - | ||
| 156 | 217 | return new Promise((resolve, reject) => { | |
| 157 | 218 | fs.access(filePath, fs.constants.F_OK, (err) => { | |
| 158 | 219 | if (err) { | |
| 159 | - reject("ENOENT: no such file or directory"); | ||
| 220 | + reject(new Error("ENOENT: no such file or directory")); | ||
| 221 | + return; | ||
| 160 | 222 | } | |
| 161 | 223 | ||
| 162 | 224 | const fileStream = fs.createReadStream(filePath) | |
| 225 | + let hasError = false; | ||
| 226 | + let count = 0; | ||
| 163 | 227 | ||
| 164 | - let count = 0 | ||
| 165 | 228 | Papa.parse(fileStream, { | |
| 166 | 229 | header: true, | |
| 167 | 230 | dynamicTyping: true, | |
| 168 | 231 | ...options, | |
| 232 | + error: (error) => { | ||
| 233 | + hasError = true; | ||
| 234 | + reject(new Error(`Failed to parse CSV: ${error.message}`)); | ||
| 235 | + }, | ||
| 169 | 236 | step: results => { | |
| 170 | - const df = new DataFrame([results.data], { ...frameConfig, index: [count++] }); | ||
| 171 | - callback(df); | ||
| 237 | + if (hasError) return; | ||
| 238 | + | ||
| 239 | + if (results.errors && results.errors.length > 0) { | ||
| 240 | + hasError = true; | ||
| 241 | + reject(new Error(`CSV parsing errors: ${results.errors.map(e => e.message).join(', ')}`)); | ||
| 242 | + return; | ||
| 243 | + } | ||
| 244 | + | ||
| 245 | + try { | ||
| 246 | + const df = new DataFrame([results.data], { ...frameConfig, index: [count++] }); | ||
| 247 | + callback(df); | ||
| 248 | + } catch (error) { | ||
| 249 | + hasError = true; | ||
| 250 | + const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; | ||
| 251 | + reject(new Error(`Failed to process CSV chunk: ${errorMessage}`)); | ||
| 252 | + } | ||
| 172 | 253 | }, | |
| 173 | - complete: () => resolve(null) | ||
| 254 | + complete: () => { | ||
| 255 | + if (!hasError) { | ||
| 256 | + resolve(null); | ||
| 257 | + } | ||
| 258 | + } | ||
| 174 | 259 | }); | |
| 175 | 260 | }); | |
| 176 | 261 | }); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -68,6 +68,35 @@ describe("readCSV", function () { | |||
| 68 | 68 | assert.deepEqual(df.values, values); | |
| 69 | 69 | }); | |
| 70 | 70 | ||
| 71 | + it("Should throw error when reading non-existent remote file", async function () { | ||
| 72 | + const remoteFile = "https://raw.githubusercontent.com/javascriptdata/danfojs/dev/nonexistent.csv"; | ||
| 73 | + try { | ||
| 74 | + await dfd.readCSV(remoteFile); | ||
| 75 | + assert.fail("Should have thrown an error"); | ||
| 76 | + } catch (error) { | ||
| 77 | + assert.ok(error instanceof Error); | ||
| 78 | + } | ||
| 79 | + }); | ||
| 80 | + | ||
| 81 | + it("Should throw error when reading malformed CSV", async function () { | ||
| 82 | + const malformedCSV = new File([ "a,b,c\n1,2\n3,4,5,6" ], "malformed.csv", { type: "text/csv" }); | ||
| 83 | + try { | ||
| 84 | + await dfd.readCSV(malformedCSV); | ||
| 85 | + assert.fail("Should have thrown an error"); | ||
| 86 | + } catch (error) { | ||
| 87 | + assert.ok(error instanceof Error); | ||
| 88 | + } | ||
| 89 | + }); | ||
| 90 | + | ||
| 91 | + it("Should throw error when reading invalid file type", async function () { | ||
| 92 | + const invalidFile = new File([ "not a csv" ], "test.txt", { type: "text/plain" }); | ||
| 93 | + try { | ||
| 94 | + await dfd.readCSV(invalidFile); | ||
| 95 | + assert.fail("Should have thrown an error"); | ||
| 96 | + } catch (error) { | ||
| 97 | + assert.ok(error instanceof Error); | ||
| 98 | + } | ||
| 99 | + }); | ||
| 71 | 100 | }); | |
| 72 | 101 | ||
| 73 | 102 | // describe("streamCSV", function () { | |
@@ -114,5 +143,4 @@ describe("toCSV", function () { | |||
| 114 | 143 | let df = new dfd.Series(data); | |
| 115 | 144 | assert.deepEqual(dfd.toCSV(df, { sep: "+", download: false }), `1+2+3+4+5+6+7+8+9+10+11+12`); | |
| 116 | 145 | }); | |
| 117 | - | ||
| 118 | 146 | }); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments