NumesSanguis
NumesSanguis

Reputation: 6282

Python 3.6 pathlib Path change name parent directory

The new Path package from the pathlib library, which has been added from Python 3.4, seems a powerful replacement of approaches such as os.path.join(), but I've some trouble working with it.

I have a path that can be anything from

folder_foo/file.csv 

to

long/path/to/folder_foo/file.csv

I read the .csv file in folder_foo with pandas, modify it and want to save it to

folder_bar/file.csv

or

long/path/to/folder_bar/file.csv

Essentially I want to rename folder_foo to folder_bar in the Path object.

EDIT: example path code

csv_path = Path("long/path/to/folder_foo/file.csv")

Attempts

1

csv_path.parents[0] = csv_path.parents[0] + "_clean")

Which leads to the error TypeError: unsupported operand type(s) for +: 'PosixPath' and 'str', which means you cannot use + to combine a PosixPath with a str as described in TypeError: unsupported operand type(s) for +: 'PosixPath' and 'str'.

2

To solve this I tried the following:

csv_path.parents[0] = Path(str(csv_path.parents[0]) + "_clean")

Which however results in the error : TypeError: '_PathParents' object does not support item assignment.

Since PosixPath is not a list, this error is understandable.

3

Maybe .parts is a better approach, but

csv_path.parts[-2] = csv_path.parts[-2][:-3] + "bar"

results in: TypeError: 'tuple' object does not support item assignment.

Question

How can I easily rename the file's parent folder?

Upvotes: 10

Views: 13481

Answers (3)

hume
hume

Reputation: 2553

You could also write a little function to replace the part of the path you want to change. Here's a runnable example:

from pathlib import Path

path1 = Path("a/b/c.txt")
path2 = Path("b/c.txt")

def rename_dir(path, src, dst):
    # convert to list so that we can change elements
    parts = list(path.parts)
    
    # replace part that matches src with dst
    parts[parts.index(src)] = dst
    
    return Path(*parts)

rename_dir(path1, 'b', 'q')
#> PosixPath('a/q/c.txt')

rename_dir(path2, 'b', 'q')
#> PosixPath('q/c.txt')

Created at 2021-03-06 10:44:00 PST by reprexlite v0.4.2

Upvotes: 3

user3066565
user3066565

Reputation: 131

Would rather split this up for readability:

bar_folder = csv_path.parent.parent / 'folder_bar'
csv_path2 = bar_folder / csv_path.name

Having the destination folder as a variable also enables you to create the folder using for example:

bar_folder.mkdir(exist_ok=True)

Upvotes: 9

NumesSanguis
NumesSanguis

Reputation: 6282

EDIT: Found a cleaner solution without str()

csv_path2 = csv_path.parents[1] / (csv_path.parts[-2][:-3] + "bar") / csv_path.parts[-1]
# result
PosixPath('long/path/to/folder_bar/file.csv')

Path.parents gets the whole path to the folder minus the file. Path.parents[1] goes 2 levels up (long/path/to/), which is still a Path object. Then we get the last folder name with csv_path.parts[-2], which is a string. We apply [:-3] to get all string characters except "foo". This means we have "folder_". Then with + "bar" we get "folder_bar", which is added to our Path object. Finally we re-add the file name to our Path object with / csv_path.parts[-1].

Hack like solution

csv_path = Path(str(csv_path.parents[0])[:-3] + 'bar/' + csv_path.parts[-1])

It seems to me a bit unintuitive, however. There should be a more clean solution?

Upvotes: 0

Related Questions