Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add checkError parameter to os.walkDir #13011

Closed
wants to merge 5 commits into from
Closed
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
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@

- Added `os.normalizePathEnd` for additional path sanitization.

- Added `checkError` parameter to `os.walkPattern`, `os.walkFiles`,
`os.walkDirs`, `os.walkDir` and `os.walkDirRec`. If it is true, raises
`OSError` in case of an error.

## Library changes

- `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations`
Expand Down
51 changes: 40 additions & 11 deletions lib/pure/os.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1809,7 +1809,7 @@ template defaultWalkFilter(item): bool =
## files and directories
true

template walkCommon(pattern: string, filter) =
template walkCommon(pattern: string, filter: typed, checkError: bool) =
## Common code for getting the files and directories with the
## specified `pattern`
when defined(windows):
Expand All @@ -1833,63 +1833,79 @@ template walkCommon(pattern: string, filter) =
let errCode = getLastError()
if errCode == ERROR_NO_MORE_FILES: break
else: raiseOSError(errCode.OSErrorCode)
elif checkError:
raiseOSError(osLastError(), pattern)
else: # here we use glob
var
f: Glob
res: int
f.gl_offs = 0
f.gl_pathc = 0
f.gl_pathv = nil
res = glob(pattern, 0, nil, addr(f))
res = glob(pattern, if checkError: GLOB_ERR else: 0, nil, addr(f))
defer: globfree(addr(f))
if res == 0:
for i in 0.. f.gl_pathc - 1:
assert(f.gl_pathv[i] != nil)
let path = $f.gl_pathv[i]
if filter(path):
yield path
elif checkError:
raiseOSError(osLastError(), pattern)

iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} =
iterator walkPattern*(pattern: string; checkError = false): string {.
tags: [ReadDirEffect], noNimScript.} =
## Iterate over all the files and directories that match the `pattern`.
##
## On POSIX this uses the `glob`:idx: call.
## `pattern` is OS dependent, but at least the `"\*.ext"`
## notation is supported.
##
## In case of an error, raises `OSError` if `checkError` is true,
## otherwise the error is ignored and yields nothing.
##
## See also:
## * `walkFiles iterator <#walkFiles.i,string>`_
## * `walkDirs iterator <#walkDirs.i,string>`_
## * `walkDir iterator <#walkDir.i,string>`_
## * `walkDirRec iterator <#walkDirRec.i,string>`_
walkCommon(pattern, defaultWalkFilter)
walkCommon(pattern, defaultWalkFilter, checkError)

iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} =
iterator walkFiles*(pattern: string; checkError = false): string {.
tags: [ReadDirEffect], noNimScript.} =
## Iterate over all the files that match the `pattern`.
##
## On POSIX this uses the `glob`:idx: call.
## `pattern` is OS dependent, but at least the `"\*.ext"`
## notation is supported.
##
## In case of an error, raises `OSError` if `checkError` is true,
## otherwise the error is ignored and yields nothing.
##
## See also:
## * `walkPattern iterator <#walkPattern.i,string>`_
## * `walkDirs iterator <#walkDirs.i,string>`_
## * `walkDir iterator <#walkDir.i,string>`_
## * `walkDirRec iterator <#walkDirRec.i,string>`_
walkCommon(pattern, isFile)
walkCommon(pattern, isFile, checkError)

iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} =
iterator walkDirs*(pattern: string; checkError = false): string {.
tags: [ReadDirEffect], noNimScript.} =
## Iterate over all the directories that match the `pattern`.
##
## On POSIX this uses the `glob`:idx: call.
## `pattern` is OS dependent, but at least the `"\*.ext"`
## notation is supported.
##
## In case of an error, raises `OSError` if `checkError` is true,
## otherwise the error is ignored and yields nothing.
##
## See also:
## * `walkPattern iterator <#walkPattern.i,string>`_
## * `walkFiles iterator <#walkFiles.i,string>`_
## * `walkDir iterator <#walkDir.i,string>`_
## * `walkDirRec iterator <#walkDirRec.i,string>`_
walkCommon(pattern, isDir)
walkCommon(pattern, isDir, checkError)

proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
tags: [ReadDirEffect], noNimScript.} =
Expand Down Expand Up @@ -1973,7 +1989,9 @@ proc staticWalkDir(dir: string; relative: bool): seq[
tuple[kind: PathComponent, path: string]] =
discard

iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] {.
iterator walkDir*(dir: string;
relative=false;
checkError=false): tuple[kind: PathComponent, path: string] {.
tags: [ReadDirEffect].} =
## Walks over the directory `dir` and yields for each directory or file in
## `dir`. The component type and full path for each item are returned.
Expand All @@ -1998,6 +2016,9 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
## dirA/fileA1.txt
## dirA/fileA2.txt
##
## When `dir` cannot be open, raises `OSError` if `checkError` is true,
## otherwise the error is ignored and yields nothing.
##
## See also:
## * `walkPattern iterator <#walkPattern.i,string>`_
## * `walkFiles iterator <#walkFiles.i,string>`_
Expand Down Expand Up @@ -2030,6 +2051,8 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
let errCode = getLastError()
if errCode == ERROR_NO_MORE_FILES: break
else: raiseOSError(errCode.OSErrorCode)
elif checkError:
raiseOSError(osLastError(), dir)
else:
var d = opendir(dir)
if d != nil:
Expand Down Expand Up @@ -2064,10 +2087,13 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
elif S_ISLNK(s.st_mode):
k = getSymlinkFileKind(path)
yield (k, y)
elif checkError:
raiseOSError(osLastError(), dir)

iterator walkDirRec*(dir: string,
yieldFilter = {pcFile}, followFilter = {pcDir},
relative = false): string {.tags: [ReadDirEffect].} =
relative = false,
checkError = false): string {.tags: [ReadDirEffect].} =
## Recursively walks over the directory `dir` and yields for each file
## or directory in `dir`.
##
Expand Down Expand Up @@ -2096,6 +2122,9 @@ iterator walkDirRec*(dir: string,
## ``pcLinkToDir`` follow symbolic links to directories
## --------------------- ---------------------------------------------
##
## When `dir` or any directory in `dir` cannot be open, raises `OSError`
## if `checkError` is true, otherwise the error is ignored and yield nothing
## from the directory.
##
## See also:
## * `walkPattern iterator <#walkPattern.i,string>`_
Expand All @@ -2106,7 +2135,7 @@ iterator walkDirRec*(dir: string,
var stack = @[""]
while stack.len > 0:
let d = stack.pop()
for k, p in walkDir(dir / d, relative = true):
for k, p in walkDir(dir / d, relative = true, checkError):
let rel = d / p
if k in {pcDir, pcLinkToDir} and k in followFilter:
stack.add rel
Expand Down