You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
pathlib.Path.iterdir doesn't raise an exception until you start iterating #78722
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=Noneclosed_at=Nonecreated_at=<Date2018-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=<Date2019-05-23.22:47:36.119>user='https://bugs.python.org/PaulPinterits'
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:
forpathinpath_that_doesnt_exist.iterdir():
print(path.stat().st_size) # this can also throw FileNotFoundErrorexceptFileNotFoundError:
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:
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:
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.
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.
prudvinit commented on Aug 29, 2018
prudvinitmannequin
Mannequin
More actions
Made changes, pathlib.Path('.').iterdir() now throws a FileNotFoundError if the path is not valid.
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.
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.
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
Activity
PaulPinterits commentedon Aug 29, 2018
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...excepthas no effect whatsoever:If we explicitly check whether the path exists (and is a directory), we end up with a race condition:
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.)We can manually call
nexton the iterator inside of atry..exceptblock, but that's awfully verbose and requires knowledge about iterators and thenextfunction: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:As you can see, writing correct (and good) code with
iterdiris more difficult than it has any right to be. Please change this behavior so that exceptions are thrown immediately wheniterdiris called.PaulPinterits commentedon Aug 29, 2018
As an afterthought, I'd like to suggest an alternative solution: If changing the
iterdirbehavior is not possible or not desirable for some reason, please add aPath.listdirmethod 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.prudvinit commentedon Aug 29, 2018
Made changes, pathlib.Path('.').iterdir() now throws a FileNotFoundError if the path is not valid.
barneygale commentedon Apr 13, 2022
Work-around: call
list(path.iterdir()):I don't think we can change the
iterdir()behaviour at this stage, nor does it justify the introduction of a similar method likelistdir()to my mind.CAM-Gerlach commentedon Jan 8, 2023
Yeah, the fact that it returns a generator/lazy iterator seems pretty fundamental to
iterdir's behavior, and matches the other current and pastiter*functions/methods in Pythons, as well aspathlib.globand similar methods (as well as builtins likerangeand 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.barneygale commentedon Apr 14, 2023
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.jaraco commentedon Jul 18, 2023
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
StopIterationas a stop condition.pythonGH-78722: Raise exceptions from `pathlib.Path.iterdir()` withou…
barneygale commentedon Jul 26, 2023
Upon reflection I agree. I've put a PR up. Do you have a preference on whether it should be backported?
[-]pathlib.Path.iterdir doesn't throw an exception until you start iterating[/-][+]pathlib.Path.iterdir doesn't raise an exception until you start iterating[/+]barneygale commentedon Sep 2, 2023
A comment from @AA-Turner:
GH-78722: Raise exceptions from `pathlib.Path.iterdir()` without dela…
barneygale commentedon Sep 2, 2023
Fixed in 3.13 / #107320 / bdc3c88
jaraco commentedon Sep 3, 2023
Thanks for the fix!
This feels like a change in behavior / improvement, so probably not suitable for a backport.