Skip to content

Commit bda36c7

Browse files
authored
Fix plots paths breaking on windows (#4607)
* Fix plots paths breaking on windows * Standardise plots output paths * Add tests * Apply review comments
1 parent ddcd7f7 commit bda36c7

File tree

4 files changed

+96
-3
lines changed

4 files changed

+96
-3
lines changed

extension/src/fileSystem/util.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { sep, parse } from 'path'
22

33
export const getPathArray = (path: string): string[] => path.split(sep)
44

5+
export const ensureOsFileSep = (path: string): string =>
6+
path.replace(/\\/g, '/').split('/').join(sep)
7+
58
export const getPath = (pathArray: string[], idx: number) =>
69
pathArray.slice(0, idx).join(sep)
710

extension/src/plots/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { WebviewMessages } from './webview/messages'
55
import { PlotsData } from './data'
66
import { ErrorsModel } from './errors/model'
77
import { PlotsModel } from './model'
8+
import { ensurePlotsDataPathsOsSep } from './util'
89
import { collectEncodingElements, collectScale } from './paths/collect'
910
import { PathsModel } from './paths/model'
1011
import { pickCustomPlots, pickMetricAndParam } from './model/quickPick'
@@ -292,10 +293,11 @@ export class Plots extends BaseRepository<TPlotsData> {
292293
private onDidUpdateData() {
293294
this.dispose.track(
294295
this.data.onDidUpdate(async ({ data, revs }) => {
296+
const standardisedData = ensurePlotsDataPathsOsSep(data)
295297
await Promise.all([
296-
this.plots.transformAndSet(data, revs),
297-
this.paths.transformAndSet(data, revs),
298-
this.errors.transformAndSet(data, revs)
298+
this.plots.transformAndSet(standardisedData, revs),
299+
this.paths.transformAndSet(standardisedData, revs),
300+
this.errors.transformAndSet(standardisedData, revs)
299301
])
300302
this.notifyChanged()
301303
})

extension/src/plots/util.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { sep } from 'path'
2+
import { ensurePlotsDataPathsOsSep } from './util'
3+
import { PlotsType } from './webview/contract'
4+
import { FIELD_SEPARATOR } from '../cli/dvc/constants'
5+
import { PlotsOutput } from '../cli/dvc/contract'
6+
7+
const joinWithSep = (pathArr: string[], slash = sep) => pathArr.join(slash)
8+
9+
const getOutput = (slash = sep): PlotsOutput => {
10+
return {
11+
data: {
12+
[joinWithSep(['plots', 'heatmap.png'], slash)]: [
13+
{
14+
revisions: ['main'],
15+
type: PlotsType.IMAGE,
16+
url: joinWithSep(['plots', 'heatmap.png'])
17+
}
18+
],
19+
[joinWithSep([`dvc.yaml${FIELD_SEPARATOR}logs`, 'acc.tsv'], slash)]: [
20+
{
21+
content: {},
22+
datapoints: { main: [{}] },
23+
revisions: ['main'],
24+
type: PlotsType.VEGA
25+
}
26+
]
27+
},
28+
errors: [
29+
{
30+
msg: 'No such file or directory',
31+
name: joinWithSep(['plots', 'heatmap.png'], slash),
32+
rev: 'main',
33+
type: 'FileNotFoundError'
34+
}
35+
]
36+
}
37+
}
38+
39+
const windowsStyleOutput = getOutput('\\')
40+
const unixStyleOutput = getOutput('/')
41+
const osStyleOutput = getOutput()
42+
43+
describe('ensurePlotsDataPathsOsSep', () => {
44+
it('should update windows and unix style data paths to style based by os', () => {
45+
expect(ensurePlotsDataPathsOsSep(windowsStyleOutput)).toStrictEqual(
46+
osStyleOutput
47+
)
48+
expect(ensurePlotsDataPathsOsSep(unixStyleOutput)).toStrictEqual(
49+
osStyleOutput
50+
)
51+
})
52+
it('should return early if there is a cli error', () => {
53+
const cliError = {
54+
error: { msg: 'something has gone wrong', type: 'clierror' }
55+
}
56+
expect(ensurePlotsDataPathsOsSep(cliError)).toStrictEqual(cliError)
57+
})
58+
})

extension/src/plots/util.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { PlotsOutput, PlotsOutputOrError } from '../cli/dvc/contract'
2+
import { isDvcError } from '../cli/dvc/reader'
3+
import { ensureOsFileSep } from '../fileSystem/util'
4+
5+
export const ensurePlotsDataPathsOsSep = (
6+
plotsData: PlotsOutputOrError
7+
): PlotsOutputOrError => {
8+
if (isDvcError(plotsData)) {
9+
return plotsData
10+
}
11+
const standardisedData: PlotsOutput = { data: {} }
12+
const { data, errors } = plotsData
13+
14+
for (const path of Object.keys(data)) {
15+
standardisedData.data[ensureOsFileSep(path)] = data[path]
16+
}
17+
18+
if (!errors) {
19+
return standardisedData
20+
}
21+
standardisedData.errors = errors.map(error => {
22+
if (!error.name) {
23+
return error
24+
}
25+
26+
return { ...error, name: ensureOsFileSep(error.name) }
27+
})
28+
29+
return standardisedData
30+
}

0 commit comments

Comments
 (0)