1
1
import collections
2
- import operator
2
+ import itertools
3
3
import pathlib
4
+ import operator
4
5
import zipfile
5
6
6
7
from . import abc
7
8
8
- from ._itertools import unique_everseen
9
+ from ._itertools import only
9
10
10
11
11
12
def remove_duplicates (items ):
@@ -41,8 +42,10 @@ def open_resource(self, resource):
41
42
raise FileNotFoundError (exc .args [0 ])
42
43
43
44
def is_resource (self , path ):
44
- # workaround for `zipfile.Path.is_file` returning true
45
- # for non-existent paths.
45
+ """
46
+ Workaround for `zipfile.Path.is_file` returning true
47
+ for non-existent paths.
48
+ """
46
49
target = self .files ().joinpath (path )
47
50
return target .is_file () and target .exists ()
48
51
@@ -67,8 +70,10 @@ def __init__(self, *paths):
67
70
raise NotADirectoryError ('MultiplexedPath only supports directories' )
68
71
69
72
def iterdir (self ):
70
- files = (file for path in self ._paths for file in path .iterdir ())
71
- return unique_everseen (files , key = operator .attrgetter ('name' ))
73
+ children = (child for path in self ._paths for child in path .iterdir ())
74
+ by_name = operator .attrgetter ('name' )
75
+ groups = itertools .groupby (sorted (children , key = by_name ), key = by_name )
76
+ return map (self ._follow , (locs for name , locs in groups ))
72
77
73
78
def read_bytes (self ):
74
79
raise FileNotFoundError (f'{ self } is not a file' )
@@ -90,6 +95,25 @@ def joinpath(self, *descendants):
90
95
# Just return something that will not exist.
91
96
return self ._paths [0 ].joinpath (* descendants )
92
97
98
+ @classmethod
99
+ def _follow (cls , children ):
100
+ """
101
+ Construct a MultiplexedPath if needed.
102
+
103
+ If children contains a sole element, return it.
104
+ Otherwise, return a MultiplexedPath of the items.
105
+ Unless one of the items is not a Directory, then return the first.
106
+ """
107
+ subdirs , one_dir , one_file = itertools .tee (children , 3 )
108
+
109
+ try :
110
+ return only (one_dir )
111
+ except ValueError :
112
+ try :
113
+ return cls (* subdirs )
114
+ except NotADirectoryError :
115
+ return next (one_file )
116
+
93
117
def open (self , * args , ** kwargs ):
94
118
raise FileNotFoundError (f'{ self } is not a file' )
95
119
0 commit comments