Skip to content

shutil.move does not work properly with pathlib.Path objects #83321

Closed

Description

@LucaPaganin
BPO 39140
Nosy @tirkarthi
Superseder
  • bpo-32689: shutil.move raises AttributeError if first argument is a pathlib.Path object and destination is a directory
  • Files
  • mover.tgz
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2019-12-27.03:13:08.929>
    created_at = <Date 2019-12-26.23:39:08.032>
    labels = ['type-bug', '3.7']
    title = 'shutil.move does not work properly with pathlib.Path objects'
    updated_at = <Date 2019-12-27.03:13:08.928>
    user = 'https://bugs.python.org/LucaPaganin'

    bugs.python.org fields:

    activity = <Date 2019-12-27.03:13:08.928>
    actor = 'xtreak'
    assignee = 'none'
    closed = True
    closed_date = <Date 2019-12-27.03:13:08.929>
    closer = 'xtreak'
    components = []
    creation = <Date 2019-12-26.23:39:08.032>
    creator = 'Luca Paganin'
    dependencies = []
    files = ['48803']
    hgrepos = []
    issue_num = 39140
    keywords = []
    message_count = 2.0
    messages = ['358891', '358894']
    nosy_count = 2.0
    nosy_names = ['xtreak', 'Luca Paganin']
    pr_nums = []
    priority = 'normal'
    resolution = 'duplicate'
    stage = 'resolved'
    status = 'closed'
    superseder = '32689'
    type = 'behavior'
    url = 'https://bugs.python.org/issue39140'
    versions = ['Python 3.7']

    Activity

    1. LucaPaganin commented on Dec 26, 2019

      LucaPaganinmannequin
      MannequinAuthor

      Suppose you have two pathlib objects representing source and destination of a move:

      src=pathlib.Path("foo/bar/barbar/myfile.txt")
      dst=pathlib.Path("foodst/bardst/")

      If you try to do the following

      shutil.move(src, dst)

      Then an AttributeError will be raised, saying that PosixPath objects do not have an rstrip attribute. The error is the following:

      Traceback (most recent call last):
        File "mover.py", line 10, in <module>
          shutil.move(src, dst)
        File "/Users/lucapaganin/opt/anaconda3/lib/python3.7/shutil.py", line 562, in move
          real_dst = os.path.join(dst, _basename(src))
        File "/Users/lucapaganin/opt/anaconda3/lib/python3.7/shutil.py", line 526, in _basename
          return os.path.basename(path.rstrip(sep))
      AttributeError: 'PosixPath' object has no attribute 'rstrip'

      Looking into shutil code, line 526, I see that the problem happens when you try to strip the trailing slash using rstrip, which is a method for strings, while PosixPath objects do not have it. Moreover, pathlib.Path objects already manage for trailing slashes, correctly getting basenames even when these are present.
      The following two workarounds work:

      1. Explicit cast both src and dst as string using

      shutil.move(str(src), str(dst))

      This work for both the cases in which dst contains the destination filename or not.

      1. Add the filename to the end of the PosixPath dst object:
      dst=pathlib.Path("foodst/bardst/myfile.txt")

      Then do

      shutil.move(src, dst)

      Surely one could use the method pathlib.Path.replace for PosixPath objects, which does the job without problems, even if it requires for dst to contain the destination filename at the end, and lacks generality, since it bugs when one tries to move files between different filesystems.
      I think that you should account for the possibility for shutil.move to manage pathlib.Path objects even if one does not provide the destination filename, since the source of the bug is due to a safety measure which is not necessary for pathlib.Path objects, i.e. the managing of the trailing slash.
      Do you think that is possible? Thank you in advance.

      Luca Paganin

      P.S.: I attach a tarball with the dirtree I used for the demonstration.

    2. tirkarthi commented on Dec 27, 2019

      @tirkarthi
      Member

      This is a duplicate of https://bugs.python.org/issue32689 that is fixed in 3.9 . Using os.fspath on path object is more correct here instead of str so that objects with both __str__ and __fspath__ implemented are handled properly.

    3. transferred this issue fromon Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      3.7 (EOL)end of lifetype-bugAn unexpected behavior, bug, or error

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions