Skip to content

Windows pathlib.Path.glob(pattern) fixed part of the pattern changed to lowercase whereas it should be unchanged. #75385

Closed

Description

@brice-gros
BPO 31202
Nosy @pfmoore, @tjguk, @zware, @serhiy-storchaka, @zooba, @brice-gros, @miss-islington, @iritkatriel
PRs
  • bpo-31202: Windows pathlib.Path.glob(pattern) fixed part of the pattern changed to lowercase whereas it should be unchanged. #3087
  • bpo-31202: Preserve case of literal parts in Path.glob() on Windows. #16860
  • [3.7] bpo-31202: Preserve case of literal parts in Path.glob() on Windows. (GH-16860) #16874
  • [3.8] bpo-31202: Preserve case of literal parts in Path.glob() on Windows. (GH-16860) #16875
  • 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 2020-10-17.00:11:35.555>
    created_at = <Date 2017-08-14.13:57:31.993>
    labels = ['type-bug', '3.8', '3.9', '3.7', 'library', 'OS-windows']
    title = 'Windows pathlib.Path.glob(pattern) fixed part of the pattern changed to lowercase whereas it should be unchanged.'
    updated_at = <Date 2020-10-17.00:11:35.552>
    user = 'https://github.com/brice-gros'

    bugs.python.org fields:

    activity = <Date 2020-10-17.00:11:35.552>
    actor = 'steve.dower'
    assignee = 'none'
    closed = True
    closed_date = <Date 2020-10-17.00:11:35.555>
    closer = 'steve.dower'
    components = ['Library (Lib)', 'Windows']
    creation = <Date 2017-08-14.13:57:31.993>
    creator = 'brice.gros'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 31202
    keywords = ['patch']
    message_count = 8.0
    messages = ['300245', '354976', '355087', '355093', '355095', '378786', '378792', '378794']
    nosy_count = 8.0
    nosy_names = ['paul.moore', 'tim.golden', 'zach.ware', 'serhiy.storchaka', 'steve.dower', 'brice.gros', 'miss-islington', 'iritkatriel']
    pr_nums = ['3087', '16860', '16874', '16875']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue31202'
    versions = ['Python 3.7', 'Python 3.8', 'Python 3.9']

    Activity

    1. brice-gros commented on Aug 14, 2017

      brice-grosmannequin
      MannequinAuthor

      Windows pathlib.Path.glob(pattern) fixed part of the pattern to lowercase whereas it should be unchanged.
      Note that this issue is different from http://bugs.python.org/issue26655 : "pathlib glob case sensitivity issue on Windows" where it was asked to get the actual case of the folder from the file system.

      Assuming a directory contains a folder named 'Folder'.
      On Windows, calling pathlib.Path().glob('Folder') gives 'folder', but 'Folde?' will return 'Folder'
      This is an issue for instance if trying to glob files to put them in an archive to be sent to a case sensitive platform.
      glob.glob() does behave properly though, Windows pathlib.Path is the only platform which has such a behavior.

      I would expect Path.glob to output the same as glob.glob() for each platform.
      From comments on http://bugs.python.org/issue19718 : "Path.glob() on case-insensitive Posix filesystems" it sounds that it should even match the native shell behavior.
      And it looks like it is the case for linux and macOS, I tested that with the following script, whose results on win32, darwin and linux platforms follow:

      #!/usr/bin/env python3.6
      # Let's say this path exists : ./Folder/file
      from pathlib import Path
      import glob
      import os
      import sys
      import subprocess
      
      def ls(pattern):
          if sys.platform in ('win32', 'win64'):
              process = subprocess.run(['powershell', '-Command', f'dir {pattern}'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=os.getcwd(), encoding='utf-8')
              if process.returncode:
                  return []
              # expected output:
              # 
              # 
              #     Directory: C:\path_to\Folder
              # 
              # 
              # Mode                LastWriteTime         Length Name
              # ----                -------------         ------ ----
              # -a----       2017-08-14     10:16              0 file
              lines = process.stdout.splitlines()
              folder = os.path.basename(lines[2].split()[-1])
              file = lines[7].split()[-1]
              result = f"{folder}{os.path.sep}{file}"
              return [result]
          else:
              cmd = ['ls', f'{pattern}']
              process = subprocess.run(' '.join(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=os.getcwd(), encoding='utf-8', shell=True)
              if process.returncode:
                  return []
              return [process.stdout.strip()]
      
      def main():
          print(sys.platform)
          p = Path('.')
          tests = ['Folder/*', 'FOlder/*', 'F?lder/*', 'FOlde?/*', 'folder/*', 'f?lder/*']
          for t in tests:
              print(f'{t}:')
              print(f'    Path.glob():  {[str(f) for f in p.glob(t)]}')
              print(f'    glob.glob():  {[f for f in glob.glob(str(p/t))]}')
              print(f'    shell:        {ls(str(p/t))}')
      
      if __name__ == '__main__':
          main()
      
      Output:
              win32                                            darwin                                        linux
      1:      Folder/*:                                        Folder/*:                                     Folder/*:
      1a:         Path.glob():  ['folder\\file']                   Path.glob():  ['Folder/file']                 Path.glob():  ['Folder/file']
      1b:         glob.glob():  ['Folder\\file']                   glob.glob():  ['Folder/file']                 glob.glob():  ['Folder/file']
      1c:         shell:        ['Folder\\file']                   shell:        ['Folder/file']                 shell:        ['Folder/file']
      2:      FOlder/*:                                        FOlder/*:                                     FOlder/*:
      2a:         Path.glob():  ['folder\\file']                   Path.glob():  ['FOlder/file']                 Path.glob():  []
      2b:         glob.glob():  ['FOlder\\file']                   glob.glob():  ['FOlder/file']                 glob.glob():  []
      2c:         shell:        ['FOlder\\file']                   shell:        ['FOlder/file']                 shell:        []
      3:      F?lder/*:                                        F?lder/*:                                     F?lder/*:
      3a:         Path.glob():  ['Folder\\file']                   Path.glob():  ['Folder/file']                 Path.glob():  ['Folder/file']
      3b:         glob.glob():  ['Folder\\file']                   glob.glob():  ['Folder/file']                 glob.glob():  ['Folder/file']
      3c:         shell:        ['Folder\\file']                   shell:        ['Folder/file']                 shell:        ['Folder/file']
      4:      FOlde?/*:                                        FOlde?/*:                                     FOlde?/*:
      4a:         Path.glob():  ['Folder\\file']                   Path.glob():  []                              Path.glob():  []
      4b:         glob.glob():  ['Folder\\file']                   glob.glob():  []                              glob.glob():  []
      4c:         shell:        ['Folder\\file']                   shell:        []                              shell:        []
      5:      folder/*:                                        folder/*:                                     folder/*:
      5a:         Path.glob():  ['folder\\file']                   Path.glob():  ['folder/file']                 Path.glob():  []
      5b:         glob.glob():  ['folder\\file']                   glob.glob():  ['folder/file']                 glob.glob():  []
      5c:         shell:        ['folder\\file']                   shell:        ['folder/file']                 shell:        []
      6:      f?lder/*:                                        f?lder/*:                                     f?lder/*:
      6a:         Path.glob():  ['Folder\\file']                   Path.glob():  []                              Path.glob():  []
      6b:         glob.glob():  ['Folder\\file']                   glob.glob():  []                              glob.glob():  []
      6c:         shell:        ['Folder\\file']                   shell:        []                              shell:        []
      
    2. added
      stdlibStandard Library Python modules in the Lib/ directory
      type-bugAn unexpected behavior, bug, or error
      on Aug 14, 2017
    3. zooba commented on Oct 19, 2019

      @zooba
      Member

      Bumping this - it's bitten me a couple of times as one of the build/release scripts relies on Path.glob().

    4. serhiy-storchaka commented on Oct 21, 2019

      @serhiy-storchaka
      Member

      New changeset 10ecbad by Serhiy Storchaka in branch 'master':
      bpo-31202: Preserve case of literal parts in Path.glob() on Windows. (GH-16860)
      10ecbad

    5. miss-islington commented on Oct 21, 2019

      @miss-islington
      Contributor

      New changeset 175abcc by Miss Skeleton (bot) in branch '3.7':
      bpo-31202: Preserve case of literal parts in Path.glob() on Windows. (GH-16860)
      175abcc

    6. miss-islington commented on Oct 21, 2019

      @miss-islington
      Contributor

      New changeset 2f8d4f0 by Miss Skeleton (bot) in branch '3.8':
      bpo-31202: Preserve case of literal parts in Path.glob() on Windows. (GH-16860)
      2f8d4f0

    7. iritkatriel commented on Oct 16, 2020

      @iritkatriel
      Member

      Can this be closed?

    8. brice-gros commented on Oct 16, 2020

      brice-grosmannequin
      MannequinAuthor

      I think so, the original PR has been ported to 3.9, 3.8, 3.7, and all the PRs were merged.

    9. zooba commented on Oct 17, 2020

      @zooba
      Member

      Yeah, looks done.

    10. 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 life3.8 (EOL)end of life3.9 (EOL)end of lifeOS-windowsstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions