GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — pathlib.Path.iterdir doesn't raise an exception until you start iterating · Issue #78722 · python/cpython

pathlib.Path.iterdir doesn't raise an exception until you start iterating #78722

New issue
Copy link
New issue
Copy link
Closed
Closed
pathlib.Path.iterdir doesn't raise an exception until you start iterating#78722
Copy link
Labels
3.8 (EOL)end of lifeend of lifestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-pathlibtype-featureA feature request or enhancementA feature request or enhancement

Description

PaulPinterits
mannequin
opened on Aug 29, 2018
Issue body actions
BPO 34541
Nosy @pitrou, @prudvinit
PRs
  • bpo-34541: pathlib.Path.iterdir throw an exception when path is not valid #8996
  • gh-78722: Fixed, pathlib.Path.iterdir now throws an exception when path is not valid #8999
  • 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 = None
    created_at = <Date 2018-08-29.08:20:21.120>
    labels = ['3.8', 'type-feature', 'library']
    title = "pathlib.Path.iterdir doesn't throw an exception until you start iterating"
    updated_at = <Date 2019-05-23.22:47:36.119>
    user = 'https://bugs.python.org/PaulPinterits'

    bugs.python.org fields:

    activity = <Date 2019-05-23.22:47:36.119>
    actor = 'cheryl.sabella'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2018-08-29.08:20:21.120>
    creator = 'Paul Pinterits'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 34541
    keywords = ['patch']
    message_count = 3.0
    messages = ['324307', '324332', '324334']
    nosy_count = 3.0
    nosy_names = ['pitrou', 'Paul Pinterits', 'prudvinit']
    pr_nums = ['8996', '8999']
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue34541'
    versions = ['Python 3.8']

    Linked PRs

    • GH-78722: Raise exceptions from pathlib.Path.iterdir() without delay. #107320

    Activity

    1. PaulPinterits commented on Aug 29, 2018

      @PaulPinterits
      PaulPinteritsmannequin
      on Aug 29, 2018
      MannequinAuthor
      More actions

      The fact that Path.iterdir() only throws exceptions once you start iterating over it makes it very difficult to write correct code.

      Let's look at an example: We'll iterate over all children of a directory and print their file size.

      If we try to do it like this, the try...except has no effect whatsoever:

      try:
          children = path_that_doesnt_exist.iterdir()
      except FileNotFoundError:
          print("directory doesn't exist")
      for path in children:
          print(path.stat().st_size)

      If we explicitly check whether the path exists (and is a directory), we end up with a race condition:

      if path_that_doesnt_exist.is_dir():
          print("directory doesn't exist")
      else:
          for path in children:
              print(path.stat().st_size)

      We can wrap the whole loop in a try...except, but then we might end up catching exceptions we didn't intend to catch. (For example, the exception that's thrown when we try to get the size of a broken symlink.)

      try:
          for path in path_that_doesnt_exist.iterdir():
              print(path.stat().st_size)  # this can also throw FileNotFoundError
      except FileNotFoundError:
          print("directory doesn't exist")

      We can manually call next on the iterator inside of a try..except block, but that's awfully verbose and requires knowledge about iterators and the next function:

      children = iter(path_that_doesnt_exist.iterdir())
      while True:
          try:
              path = next(children)
          except FileNotFoundError:
              print("directory doesn't exist")
              break
          print(path.stat().st_size)

      Or we can turn the iterator into a list inside of a try...except, which seems to be the best option, but completely defeats the point of having an iterator:

      try:
          children = list(path_that_doesnt_exist.iterdir())
      except FileNotFoundError:
          print("directory doesn't exist")
      else:
          for path in children:
              print(path.stat().st_size)

      As you can see, writing correct (and good) code with iterdir is more difficult than it has any right to be. Please change this behavior so that exceptions are thrown immediately when iterdir is called.

    2. PaulPinterits
      added
      type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
      3.7 (EOL)end of lifeend of life
      stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
      on Aug 29, 2018
    3. PaulPinterits commented on Aug 29, 2018

      @PaulPinterits
      PaulPinteritsmannequin
      on Aug 29, 2018
      MannequinAuthor
      More actions

      As an afterthought, I'd like to suggest an alternative solution: If changing the iterdir behavior is not possible or not desirable for some reason, please add a Path.listdir method that returns a list instead of an iterator. Lazy file system operations can often introduce unnecessary race conditions in the code, so I believe it would be very useful if every lazy method had an eager equivalent.

    4. prudvinit commented on Aug 29, 2018

      @prudvinit
      prudvinitmannequin
      on Aug 29, 2018
      Mannequin
      More actions

      Made changes, pathlib.Path('.').iterdir() now throws a FileNotFoundError if the path is not valid.

    5. csabella
      added
      3.8 (EOL)end of lifeend of life
      type-featureA feature request or enhancementA feature request or enhancement
      and removed
      3.7 (EOL)end of lifeend of life
      type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
      on May 23, 2019
    6. ezio-melotti
      transferred this issue fromon Apr 10, 2022
    7. barneygale commented on Apr 13, 2022

      @barneygale
      barneygale
      on Apr 13, 2022
      Contributor
      More actions

      Work-around: call list(path.iterdir()):

      try:
          children = list(path_that_doesnt_exist.iterdir())
      except FileNotFoundError:
          print("directory doesn't exist")
      for path in children:
          print(path.stat().st_size)

      I don't think we can change the iterdir() behaviour at this stage, nor does it justify the introduction of a similar method like listdir() to my mind.

      Reacted by C.A.M. Gerlach
    8. brettcannon
      added
      topic-pathlib
      on Jun 21, 2022
    9. CAM-Gerlach commented on Jan 8, 2023

      @CAM-Gerlach
      CAM-Gerlach
      on Jan 8, 2023
      Member
      More actions

      Yeah, the fact that it returns a generator/lazy iterator seems pretty fundamental to iterdir's behavior, and matches the other current and past iter* functions/methods in Pythons, as well as pathlib.glob and similar methods (as well as builtins like range and others) which would make it suddenly behaving eagerly quite surprising to many/most users, as well as sacrificing performance/efficiency.

      list(<lazy iterator>) to make something iterable is a common idiom in Python to handle this situation (and others), and adding a separate method to do the same thing eagerly seems thoroughly redundant.

    10. barneygale commented on Apr 14, 2023

      @barneygale
      barneygale
      on Apr 14, 2023
      Contributor
      More actions

      I'm going to close this issue per CAM's rationale above. Use list(path.iterdir()) if you need to handle exceptions before you iterate. Thanks all.

      Reacted by C.A.M. Gerlach
    11. barneygale
      closed this as not plannedon Apr 14, 2023
    12. jaraco commented on Jul 18, 2023

      @jaraco
      jaraco
      on Jul 18, 2023
      Member
      More actions

      I don't believe list(iterdir()) is a suitable workaround, as it requires the entire directory to be materialized in memory, increasing storage with little benefit. I'd argue that itertools (or similar) should provide a mechanism to filter or stop on exceptions, so consuming the iterable can continue to be lazy.

      more_itertools.iter_except almost handles this case, but (a) requires the callable to be passed and (b) can't handle StopIteration as a stop condition.

      Reacted by Barney Gale
    13. barneygale
      reopened this on Jul 26, 2023
    14. barneygale
      added a commit that references this issue on Jul 26, 2023

      pythonGH-78722: Raise exceptions from `pathlib.Path.iterdir()` withou…

      4d552aa
    15. barneygale commented on Jul 26, 2023

      @barneygale
      barneygale
      on Jul 26, 2023
      Contributor
      More actions

      Upon reflection I agree. I've put a PR up. Do you have a preference on whether it should be backported?

    16. merwok
      changed the title [-]pathlib.Path.iterdir doesn't throw an exception until you start iterating[/-] [+]pathlib.Path.iterdir doesn't raise an exception until you start iterating[/+] on Aug 28, 2023
    17. barneygale commented on Sep 2, 2023

      @barneygale
      barneygale
      on Sep 2, 2023
      Contributor
      More actions

      A comment from @AA-Turner:

      I agree fixing iterdir at the source would be cleaner

    18. barneygale
      added a commit that references this issue on Sep 2, 2023

      GH-78722: Raise exceptions from `pathlib.Path.iterdir()` without dela…

      bdc3c88
    19. barneygale commented on Sep 2, 2023

      @barneygale
      barneygale
      on Sep 2, 2023
      Contributor
      More actions

      Fixed in 3.13 / #107320 / bdc3c88

      Reacted by Adam Turner
    20. barneygale
      closed this as completedon Sep 2, 2023
    21. jaraco commented on Sep 3, 2023

      @jaraco
      jaraco
      on Sep 3, 2023
      Member
      More actions

      Thanks for the fix!

      Upon reflection I agree. I've put a PR up. Do you have a preference on whether it should be backported?

      This feels like a change in behavior / improvement, so probably not suitable for a backport.

      Reacted by Barney Gale
    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.8 (EOL)end of lifeend of lifestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-pathlibtype-featureA feature request or enhancementA feature request or enhancement

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        You are visitor 01402503 — GeezerVison does not store the pages you look up.
        Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.