Skip to content

Commit 2016408

Browse files
committed
PEP 471 -- os.scandir() function -- a better and faster directory iterator
1 parent f01acaf commit 2016408

File tree

5 files changed

+144
-0
lines changed

5 files changed

+144
-0
lines changed

CPythonLib.includes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ pdb.py
128128
pickle.py
129129
pickletools.py
130130
pipes.py
131+
pkgutil.py
131132
plistlib.py
132133
poplib.py
133134
posixfile.py

CoreExposed.includes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ org/python/modules/_weakref/ReferenceType.class
118118
org/python/modules/operator$PyAttrGetter.class
119119
org/python/modules/operator$PyItemGetter.class
120120
org/python/modules/operator$PyMethodCaller.class
121+
org/python/modules/posix/PyDirEntry.class
122+
org/python/modules/posix/PyScandirIterator.class
121123
org/python/modules/posix/PyStatResult.class
122124
org/python/modules/random/PyRandom.class
123125
org/python/modules/thread/PyLocal.class

src/org/python/modules/posix/PosixModule.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
import java.nio.channels.FileChannel;
1515
import java.nio.channels.Pipe;
1616
import java.nio.channels.ReadableByteChannel;
17+
import java.nio.file.DirectoryStream;
1718
import java.nio.file.FileAlreadyExistsException;
1819
import java.nio.file.Files;
1920
import java.nio.file.LinkOption;
21+
import java.nio.file.NotDirectoryException;
2022
import java.nio.file.NotLinkException;
2123
import java.nio.file.NoSuchFileException;
2224
import java.nio.file.Path;
@@ -733,6 +735,25 @@ public static PyList listdir(PyObject[] args, String[] keywords) {
733735
return list;
734736
}
735737

738+
public static PyObject scandir(PyObject[] args, String[] keywords) {
739+
ArgParser ap = new ArgParser("listdir", args, keywords, "path");
740+
String path = ap.getString(0, System.getProperty("user.home"));
741+
Path p = absolutePath(path);
742+
List<Path> paths = new ArrayList<Path>();
743+
try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) {
744+
for (Path f: stream) {
745+
paths.add(f);
746+
}
747+
} catch (NotDirectoryException e) {
748+
throw Py.OSError(Errno.ENOENT, path);
749+
} catch (IOException e) {
750+
throw Py.OSError(Errno.ENOTDIR, path);
751+
} catch (SecurityException e) {
752+
throw Py.OSError(Errno.EACCES, path);
753+
}
754+
return new PyScandirIterator(paths.iterator());
755+
}
756+
736757
public static PyString __doc__lseek = new PyString(
737758
"lseek(fd, pos, how) -> newpos\n\n" +
738759
"Set the current position of a file descriptor.");
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package org.python.modules.posix;
2+
3+
import jnr.constants.platform.Errno;
4+
import org.python.core.ArgParser;
5+
import org.python.core.Py;
6+
import org.python.core.PyObject;
7+
import org.python.core.PyType;
8+
import org.python.core.PyUnicode;
9+
import org.python.expose.ExposedGet;
10+
import org.python.expose.ExposedMethod;
11+
import org.python.expose.ExposedType;
12+
13+
import java.io.IOException;
14+
import java.nio.file.Files;
15+
import java.nio.file.LinkOption;
16+
import java.nio.file.NoSuchFileException;
17+
import java.nio.file.Path;
18+
import java.util.Map;
19+
20+
/**
21+
* Created by isaiah on 7/16/16.
22+
*/
23+
@ExposedType(name = "posix.DirEntry")
24+
public class PyDirEntry extends PyObject {
25+
private static final PyType TYPE = PyType.fromClass(PyDirEntry.class);
26+
27+
private Path entry;
28+
29+
public PyDirEntry(Path entry) {
30+
super(TYPE);
31+
this.entry = entry;
32+
}
33+
34+
@ExposedMethod
35+
public PyObject is_dir(PyObject[] args, String[] keywords) {
36+
return Py.newBoolean(Files.isDirectory(entry, follow_symlinks("is_dir", args, keywords)));
37+
}
38+
39+
@ExposedMethod
40+
public PyObject is_file(PyObject[] args, String[] keywords) {
41+
return Py.newBoolean(Files.isRegularFile(entry, follow_symlinks("is_file", args, keywords)));
42+
}
43+
44+
@ExposedMethod
45+
public PyObject is_symlink() {
46+
return Py.newBoolean(Files.isSymbolicLink(entry));
47+
}
48+
49+
@ExposedMethod
50+
public PyObject stat(PyObject[] args, String[] keywords) {
51+
return stat(follow_symlinks("stat", args, keywords));
52+
}
53+
54+
private PyObject stat(LinkOption... linkOptions) {
55+
try {
56+
Map<String, Object> attributes = Files.readAttributes(entry, "unix:*", linkOptions);
57+
return PyStatResult.fromUnixFileAttributes(attributes);
58+
} catch (NoSuchFileException ex) {
59+
throw Py.OSError(Errno.ENOENT, entry.toAbsolutePath().toString());
60+
} catch (IOException ioe) {
61+
throw Py.OSError(Errno.EBADF, entry.toAbsolutePath().toString());
62+
} catch (SecurityException ex) {
63+
throw Py.OSError(Errno.EACCES, entry.toAbsolutePath().toString());
64+
}
65+
}
66+
67+
@ExposedMethod
68+
public PyObject inode() {
69+
return ((PyStatResult) stat()).st_ino;
70+
}
71+
72+
@ExposedGet(name = "path")
73+
public PyObject getPath() {
74+
return new PyUnicode(entry.toAbsolutePath().toString());
75+
}
76+
77+
@ExposedGet(name = "name")
78+
public PyObject getName() {
79+
return new PyUnicode(entry.getFileName().toString());
80+
}
81+
82+
@Override
83+
public String toString() {
84+
return String.format("<%.500s '%s'>", getType().fastGetName(), getName());
85+
}
86+
87+
private LinkOption[] follow_symlinks(String methodName, PyObject[] args, String[] keywords) {
88+
ArgParser ap = new ArgParser(methodName, args, keywords, "*", "follow_symlinks");
89+
if (ap.getPyObject(1, Py.True).__bool__()) {
90+
return new LinkOption[] {LinkOption.NOFOLLOW_LINKS};
91+
}
92+
return new LinkOption[0];
93+
}
94+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.python.modules.posix;
2+
3+
import org.python.core.Py;
4+
import org.python.core.PyIterator;
5+
import org.python.core.PyObject;
6+
import org.python.expose.ExposedType;
7+
8+
import java.nio.file.Path;
9+
import java.util.Iterator;
10+
11+
/**
12+
* Created by isaiah on 7/16/16.
13+
*/
14+
@ExposedType(name = "posix.ScandirIterator")
15+
public class PyScandirIterator extends PyIterator {
16+
Iterator<Path> iter;
17+
public PyScandirIterator(Iterator<Path> iter) {
18+
this.iter = iter;
19+
}
20+
21+
@Override
22+
public PyObject __next__() {
23+
if (!iter.hasNext()) throw Py.StopIteration();
24+
return new PyDirEntry(iter.next());
25+
}
26+
}

0 commit comments

Comments
 (0)