mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-07 21:43:33 +00:00
adds more functions to to dirs and files (#25083)
ref https://forum.nim-lang.org/t/13272
This commit is contained in:
14
changelog.md
14
changelog.md
@@ -33,6 +33,20 @@ errors.
|
||||
- `strutils.multiReplace` overload for character set replacements in a single pass.
|
||||
Useful for string sanitation. Follows existing multiReplace semantics.
|
||||
|
||||
- `std/files` adds:
|
||||
- Exports `CopyFlag` enum and `FilePermission` type for fine-grained control of file operations
|
||||
- New file operation procs with `Path` support:
|
||||
- `getFilePermissions`, `setFilePermissions` for managing permissions
|
||||
- `tryRemoveFile` for file deletion
|
||||
- `copyFile` with configurable buffer size and symlink handling
|
||||
- `copyFileWithPermissions` to preserve file attributes
|
||||
- `copyFileToDir` for copying files into directories
|
||||
|
||||
- `std/dirs` adds:
|
||||
- New directory operation procs with `Path` support:
|
||||
- `copyDir` with special file handling options
|
||||
- `copyDirWithPermissions` to recursively preserve attributes
|
||||
|
||||
- `system.setLenUninit` now supports refc, JS and VM backends.
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
@@ -4,6 +4,7 @@ from std/paths import Path, ReadDirEffect, WriteDirEffect
|
||||
|
||||
from std/private/osdirs import dirExists, createDir, existsOrCreateDir, removeDir,
|
||||
moveDir, walkDir, setCurrentDir,
|
||||
copyDir, copyDirWithPermissions,
|
||||
walkDirRec, PathComponent
|
||||
|
||||
export PathComponent
|
||||
@@ -133,3 +134,59 @@ proc setCurrentDir*(newDir: Path) {.inline, tags: [].} =
|
||||
## See also:
|
||||
## * `getCurrentDir proc <paths.html#getCurrentDir>`_
|
||||
osdirs.setCurrentDir(newDir.string)
|
||||
|
||||
proc copyDir*(source, dest: Path; skipSpecial = false) {.inline,
|
||||
tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect].} =
|
||||
## Copies a directory from `source` to `dest`.
|
||||
##
|
||||
## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
|
||||
## are skipped.
|
||||
##
|
||||
## If `skipSpecial` is true, then (besides all directories) only *regular*
|
||||
## files (**without** special "file" objects like FIFOs, device files,
|
||||
## etc) will be copied on Unix.
|
||||
##
|
||||
## If this fails, `OSError` is raised.
|
||||
##
|
||||
## On the Windows platform this proc will copy the attributes from
|
||||
## `source` into `dest`.
|
||||
##
|
||||
## On other platforms created files and directories will inherit the
|
||||
## default permissions of a newly created file/directory for the user.
|
||||
## Use `copyDirWithPermissions proc`_
|
||||
## to preserve attributes recursively on these platforms.
|
||||
##
|
||||
## See also:
|
||||
## * `copyDirWithPermissions proc`_
|
||||
copyDir(source.string, dest.string, skipSpecial)
|
||||
|
||||
proc copyDirWithPermissions*(source, dest: Path;
|
||||
ignorePermissionErrors = true,
|
||||
skipSpecial = false)
|
||||
{.inline, tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect].} =
|
||||
## Copies a directory from `source` to `dest` preserving file permissions.
|
||||
##
|
||||
## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
|
||||
## are skipped.
|
||||
##
|
||||
## If `skipSpecial` is true, then (besides all directories) only *regular*
|
||||
## files (**without** special "file" objects like FIFOs, device files,
|
||||
## etc) will be copied on Unix.
|
||||
##
|
||||
## If this fails, `OSError` is raised. This is a wrapper proc around
|
||||
## `copyDir`_ and `copyFileWithPermissions`_ procs
|
||||
## on non-Windows platforms.
|
||||
##
|
||||
## On Windows this proc is just a wrapper for `copyDir proc`_ since
|
||||
## that proc already copies attributes.
|
||||
##
|
||||
## On non-Windows systems permissions are copied after the file or directory
|
||||
## itself has been copied, which won't happen atomically and could lead to a
|
||||
## race condition. If `ignorePermissionErrors` is true (default), errors while
|
||||
## reading/setting file attributes will be ignored, otherwise will raise
|
||||
## `OSError`.
|
||||
##
|
||||
## See also:
|
||||
## * `copyDir proc`_
|
||||
copyDirWithPermissions(source.string, dest.string,
|
||||
ignorePermissionErrors, skipSpecial)
|
||||
|
||||
@@ -6,8 +6,43 @@
|
||||
from std/paths import Path, ReadDirEffect, WriteDirEffect
|
||||
|
||||
from std/private/osfiles import fileExists, removeFile,
|
||||
moveFile
|
||||
moveFile, copyFile, copyFileWithPermissions,
|
||||
copyFileToDir, tryRemoveFile,
|
||||
getFilePermissions, setFilePermissions,
|
||||
CopyFlag, FilePermission
|
||||
|
||||
export CopyFlag, FilePermission
|
||||
|
||||
|
||||
proc getFilePermissions*(filename: Path): set[FilePermission] {.inline, tags: [ReadDirEffect].} =
|
||||
## Retrieves file permissions for `filename`.
|
||||
##
|
||||
## `OSError` is raised in case of an error.
|
||||
## On Windows, only the ``readonly`` flag is checked, every other
|
||||
## permission is available in any case.
|
||||
##
|
||||
## See also:
|
||||
## * `setFilePermissions proc`_
|
||||
result = getFilePermissions(filename.string)
|
||||
|
||||
proc setFilePermissions*(filename: Path, permissions: set[FilePermission],
|
||||
followSymlinks = true)
|
||||
{.inline, tags: [ReadDirEffect, WriteDirEffect].} =
|
||||
## Sets the file permissions for `filename`.
|
||||
##
|
||||
## If `followSymlinks` set to true (default) and ``filename`` points to a
|
||||
## symlink, permissions are set to the file symlink points to.
|
||||
## `followSymlinks` set to false is a noop on Windows and some POSIX
|
||||
## systems (including Linux) on which `lchmod` is either unavailable or always
|
||||
## fails, given that symlinks permissions there are not observed.
|
||||
##
|
||||
## `OSError` is raised in case of an error.
|
||||
## On Windows, only the ``readonly`` flag is changed, depending on
|
||||
## ``fpUserWrite`` permission.
|
||||
##
|
||||
## See also:
|
||||
## * `getFilePermissions proc`_
|
||||
setFilePermissions(filename.string, permissions, followSymlinks)
|
||||
|
||||
proc fileExists*(filename: Path): bool {.inline, tags: [ReadDirEffect], sideEffect.} =
|
||||
## Returns true if `filename` exists and is a regular file or symlink.
|
||||
@@ -15,6 +50,18 @@ proc fileExists*(filename: Path): bool {.inline, tags: [ReadDirEffect], sideEffe
|
||||
## Directories, device files, named pipes and sockets return false.
|
||||
result = fileExists(filename.string)
|
||||
|
||||
proc tryRemoveFile*(file: Path): bool {.inline, tags: [WriteDirEffect].} =
|
||||
## Removes the `file`.
|
||||
##
|
||||
## If this fails, returns `false`. This does not fail
|
||||
## if the file never existed in the first place.
|
||||
##
|
||||
## On Windows, ignores the read-only attribute.
|
||||
##
|
||||
## See also:
|
||||
## * `removeFile proc`_
|
||||
result = tryRemoveFile(file.string)
|
||||
|
||||
proc removeFile*(file: Path) {.inline, tags: [WriteDirEffect].} =
|
||||
## Removes the `file`.
|
||||
##
|
||||
@@ -26,6 +73,7 @@ proc removeFile*(file: Path) {.inline, tags: [WriteDirEffect].} =
|
||||
## See also:
|
||||
## * `removeDir proc <dirs.html#removeDir>`_
|
||||
## * `moveFile proc`_
|
||||
## * `tryRemoveFile proc`_
|
||||
removeFile(file.string)
|
||||
|
||||
proc moveFile*(source, dest: Path) {.inline,
|
||||
@@ -44,3 +92,73 @@ proc moveFile*(source, dest: Path) {.inline,
|
||||
## * `moveDir proc <dirs.html#moveDir>`_
|
||||
## * `removeFile proc`_
|
||||
moveFile(source.string, dest.string)
|
||||
|
||||
proc copyFile*(source, dest: Path; options = cfSymlinkFollow; bufferSize = 16_384) {.inline, tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect].} =
|
||||
## Copies a file from `source` to `dest`, where `dest.parentDir` must exist.
|
||||
##
|
||||
## On non-Windows OSes, `options` specify the way file is copied; by default,
|
||||
## if `source` is a symlink, copies the file symlink points to. `options` is
|
||||
## ignored on Windows: symlinks are skipped.
|
||||
##
|
||||
## If this fails, `OSError` is raised.
|
||||
##
|
||||
## On the Windows platform this proc will
|
||||
## copy the source file's attributes into dest.
|
||||
##
|
||||
## On other platforms you need
|
||||
## to use `getFilePermissions`_ and
|
||||
## `setFilePermissions`_
|
||||
## procs
|
||||
## to copy them by hand (or use the convenience `copyFileWithPermissions
|
||||
## proc`_),
|
||||
## otherwise `dest` will inherit the default permissions of a newly
|
||||
## created file for the user.
|
||||
##
|
||||
## If `dest` already exists, the file attributes
|
||||
## will be preserved and the content overwritten.
|
||||
##
|
||||
## On OSX, `copyfile` C api will be used (available since OSX 10.5) unless
|
||||
## `-d:nimLegacyCopyFile` is used.
|
||||
##
|
||||
## `copyFile` allows to specify `bufferSize` to improve I/O performance.
|
||||
##
|
||||
## See also:
|
||||
## * `copyFileWithPermissions proc`_
|
||||
copyFile(source.string, dest.string, {options}, bufferSize)
|
||||
|
||||
proc copyFileWithPermissions*(source, dest: Path;
|
||||
ignorePermissionErrors = true,
|
||||
options = cfSymlinkFollow) {.inline.} =
|
||||
## Copies a file from `source` to `dest` preserving file permissions.
|
||||
##
|
||||
## On non-Windows OSes, `options` specify the way file is copied; by default,
|
||||
## if `source` is a symlink, copies the file symlink points to. `options` is
|
||||
## ignored on Windows: symlinks are skipped.
|
||||
##
|
||||
## This is a wrapper proc around `copyFile`_,
|
||||
## `getFilePermissions`_ and `setFilePermissions`_
|
||||
## procs on non-Windows platforms.
|
||||
##
|
||||
## On Windows this proc is just a wrapper for `copyFile proc`_ since
|
||||
## that proc already copies attributes.
|
||||
##
|
||||
## On non-Windows systems permissions are copied after the file itself has
|
||||
## been copied, which won't happen atomically and could lead to a race
|
||||
## condition. If `ignorePermissionErrors` is true (default), errors while
|
||||
## reading/setting file attributes will be ignored, otherwise will raise
|
||||
## `OSError`.
|
||||
##
|
||||
## See also:
|
||||
## * `copyFile proc`_
|
||||
copyFileWithPermissions(source.string, dest.string,
|
||||
ignorePermissionErrors, {options})
|
||||
|
||||
proc copyFileToDir*(source, dir: Path, options = cfSymlinkFollow; bufferSize = 16_384) {.inline.} =
|
||||
## Copies a file `source` into directory `dir`, which must exist.
|
||||
##
|
||||
## On non-Windows OSes, `options` specify the way file is copied; by default,
|
||||
## if `source` is a symlink, copies the file symlink points to. `options` is
|
||||
## ignored on Windows: symlinks are skipped.
|
||||
##
|
||||
## `copyFileToDir` allows to specify `bufferSize` to improve I/O performance.
|
||||
copyFileToDir(source.string, dir.string, {options}, bufferSize)
|
||||
|
||||
@@ -30,6 +30,9 @@ Raises
|
||||
from stdtest/specialpaths import buildDir
|
||||
import std/[syncio, assertions, osproc, os, strutils, pathnorm]
|
||||
|
||||
import std/paths except getCurrentDir
|
||||
import std/[files, dirs]
|
||||
|
||||
block fileOperations:
|
||||
let files = @["these.txt", "are.x", "testing.r", "files.q"]
|
||||
let dirs = @["some", "created", "test", "dirs"]
|
||||
@@ -52,11 +55,11 @@ block fileOperations:
|
||||
doAssertRaises(OSError): copyFile(file, dname/sub/fname2)
|
||||
doAssertRaises(OSError): copyFileToDir(file, dname/sub)
|
||||
doAssertRaises(ValueError): copyFileToDir(file, "")
|
||||
copyFile(file, file2)
|
||||
copyFile(Path file, Path file2)
|
||||
doAssert fileExists(file2)
|
||||
doAssert readFile(file2) == str
|
||||
createDir(dname/sub)
|
||||
copyFileToDir(file, dname/sub)
|
||||
copyFileToDir(Path file, Path dname/sub)
|
||||
doAssert fileExists(dname/sub/fname)
|
||||
removeDir(dname/sub)
|
||||
doAssert not dirExists(dname/sub)
|
||||
@@ -131,12 +134,13 @@ block fileOperations:
|
||||
removeDir(dname)
|
||||
|
||||
# test copyDir:
|
||||
createDir("a/b")
|
||||
createDir(Path "a/b")
|
||||
open("a/b/file.txt", fmWrite).close
|
||||
createDir("a/b/c")
|
||||
open("a/b/c/fileC.txt", fmWrite).close
|
||||
|
||||
copyDir("a", "../dest/a")
|
||||
createDir(Path"a/b")
|
||||
copyDir(Path "a", Path "../dest/a")
|
||||
removeDir("a")
|
||||
|
||||
doAssert dirExists("../dest/a/b")
|
||||
@@ -169,7 +173,7 @@ block fileOperations:
|
||||
doAssert execCmd("mkfifo -m 600 a/fifoFile") == 0
|
||||
|
||||
copyDir("a/", "../dest/a/", skipSpecial = true)
|
||||
copyDirWithPermissions("a/", "../dest2/a/", skipSpecial = true)
|
||||
copyDirWithPermissions(Path "a/", Path "../dest2/a/", skipSpecial = true)
|
||||
removeDir("a")
|
||||
|
||||
# Symlink handling in `copyFile`, `copyFileWithPermissions`, `copyFileToDir`,
|
||||
|
||||
Reference in New Issue
Block a user