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

fix(path): improve & fix bugs in Path:rename() #485

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

tmillr
Copy link

@tmillr tmillr commented May 3, 2023

Improve Path:rename(). Mainly just focuses on fixing a few bugs that were in the implementation, but also adds types/annotations, comments, doc comments, etc., and brings the behavior much closer in line with that of Python's Path.rename().

Included in this pr is a change which could be considered breaking: upon successful rename, Path:rename() now returns a new path instance instead of changing Path.filename of self. This is how python does it, but I can change this back to how it was.

Fixes #484
Fixes bugs introduced in #90


TODO

  • Add test(s) covering changes
  • Run tests

@tmillr tmillr changed the title Fail to rename a file with different case in Mac fix(Path): Path:rename() bugs May 3, 2023
@tmillr tmillr changed the title fix(Path): Path:rename() bugs fix(path): Path:rename() bugs May 3, 2023
Comment on lines 545 to 553
-- BUG
-- handles `.`, `..`, `./`, and `../`
if opts.new_name:match "^%.%.?/?\\?.+" then
opts.new_name = {
uv.fs_realpath(opts.new_name:sub(1, 3)),
opts.new_name:sub(4),
}
end

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this as it contains a bug. Secondly, it is unnecessary, as uv.fs_rename already handles such relative paths.

Comment on lines -534 to -536
if new_path:exists() then
error "File or directory already exists!"
end
Copy link
Author

@tmillr tmillr May 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this. This was a bug because Path:exists() uses stat, not lstat.

For example, new_path could be a bad symlink, in which case it could incorrectly think that the file doesn't exist when it actually does (rename(2) does not resolve the final path component if it's a symlink, the symlink would be replaced instead).

lua/plenary/path.lua Outdated Show resolved Hide resolved
1. this is what python does
2. the underlying path has changed, and a new instance helps to reflect that
@tmillr tmillr marked this pull request as ready for review May 3, 2023 16:46
@tmillr tmillr changed the title fix(path): Path:rename() bugs fix(path): improve & fix bugs in Path:rename() May 3, 2023
Copy link
Contributor

@matu3ba matu3ba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

misc things. Mostly phrasing and unclear explanations.

lua/plenary/path.lua Outdated Show resolved Hide resolved
lua/plenary/path.lua Outdated Show resolved Hide resolved
function Path:rename(opts)
-- TODO: For reference, Python's `Path.rename()` actually says/does this:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to CI test this.

opts.new_name:sub(4, #opts.new_name),
}
end
-- Cannot rename a non-existing path (lstat is needed here, `Path:exists()`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont understand the difference to lstat from this text. Do you mean uv.lstat? Do I need to look into Path:exit()?

-- a different file.
--
-- NOTE: To elaborate, `uv.fs_rename()` won't/shouldn't do anything if old
-- and new both exist and are both hard links to the same file (inode),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing word: new both?

--
-- NOTE: To elaborate, `uv.fs_rename()` won't/shouldn't do anything if old
-- and new both exist and are both hard links to the same file (inode),
-- however, it appears to still allow you to change the case of a filename
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/allow/allows

-- NOTE: To elaborate, `uv.fs_rename()` won't/shouldn't do anything if old
-- and new both exist and are both hard links to the same file (inode),
-- however, it appears to still allow you to change the case of a filename
-- on case-insensitive file systems (i.e. if `new_name` doesn't _actually_
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the word actually? This reads like a code example would be much much simpler.

assert(status, ("%s: Rename failed!"):format(errmsg))

-- NOTE: `uv.fs_rename()` _can_ return success even if no rename actually
-- occurred (see rename(2)), and this is not an error. So we're not changing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this not an error? This does not describe how it does still uphold the users expectation of the api.

tests/plenary/path_spec.lua Outdated Show resolved Hide resolved
tests/plenary/path_spec.lua Outdated Show resolved Hide resolved

-- NOTE: `uv.fs_rename()` _can_ return success even if no rename actually
-- occurred (see rename(2)), and this is not an error.
return Path:new(new_path)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to the implementation of Path:new, this actually ends up returning opts.new_name (i.e. the very same Path instance) if a Path was passed as the argument. What should we do here?

  1. Leave it as is?
  2. Same, but force a new Path instance to be created?
  3. Revert to the previous behavior of mutating self by modifying self.filename (in which case we'd probably want to return self)?

Python's Path.rename() does number 2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fail to rename a file with different case in Mac
2 participants