[PATCH] osutil: add darwin-only version of os.listdir using cffi

Maciej Fijalkowski fijall at gmail.com
Mon Jul 11 07:58:24 EDT 2016


There are two problems with this commit:

a) listdir_internal has "_" in name. What would be the official naming
convention here?
b) bare except - I would argue that a close() in finally totally
warrants the bare except (this is what C code is doing implicitly)

On Mon, Jul 11, 2016 at 11:13 AM, Maciej Fijalkowski <fijall at gmail.com> wrote:
> # HG changeset patch
> # User Maciej Fijalkowski <fijall at gmail.com>
> # Date 1468227908 -7200
> #      Mon Jul 11 11:05:08 2016 +0200
> # Node ID b40939504d0e01e3a7f067b8cf49f26718a7c27c
> # Parent  065fb34034a3849f07ca836ce2f4eb17c7f543db
> osutil: add darwin-only version of os.listdir using cffi
>
> diff -r 065fb34034a3 -r b40939504d0e mercurial/build_osutil_cffi.py
> --- /dev/null   Thu Jan 01 00:00:00 1970 +0000
> +++ b/mercurial/build_osutil_cffi.py    Mon Jul 11 11:05:08 2016 +0200
> @@ -0,0 +1,76 @@
> +from __future__ import absolute_import
> +
> +import cffi
> +
> +ffi = cffi.FFI()
> +ffi.set_source("_osutil_cffi", """
> +#include <sys/attr.h>
> +#include <sys/vnode.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <time.h>
> +""", include_dirs=['mercurial'])
> +ffi.cdef('''
> +
> +typedef uint32_t attrgroup_t;
> +
> +typedef struct attrlist {
> +    uint16_t     bitmapcount; /* number of attr. bit sets in list */
> +    uint16_t   reserved;    /* (to maintain 4-byte alignment) */
> +    attrgroup_t commonattr;  /* common attribute group */
> +    attrgroup_t volattr;     /* volume attribute group */
> +    attrgroup_t dirattr;     /* directory attribute group */
> +    attrgroup_t fileattr;    /* file attribute group */
> +    attrgroup_t forkattr;    /* fork attribute group */
> +    ...;
> +};
> +
> +typedef struct attribute_set {
> +    ...;
> +} attribute_set_t;
> +
> +typedef struct attrreference {
> +    ...;
> +} attrreference_t;
> +
> +typedef int ... time_t;
> +typedef int ... off_t;
> +
> +typedef struct timespec {
> +    time_t tv_sec;
> +    ...;
> +};
> +
> +int getattrlist(const char* path, struct attrlist * attrList, void * attrBuf,
> +                size_t attrBufSize, unsigned int options);
> +
> +int getattrlistbulk(int dirfd, struct attrlist * attrList, void * attrBuf,
> +                    size_t attrBufSize, uint64_t options);
> +
> +#define ATTR_BIT_MAP_COUNT ...
> +#define ATTR_CMN_NAME ...
> +#define ATTR_CMN_OBJTYPE ...
> +#define ATTR_CMN_MODTIME ...
> +#define ATTR_CMN_ACCESSMASK ...
> +#define ATTR_CMN_ERROR ...
> +#define ATTR_CMN_RETURNED_ATTRS ...
> +#define ATTR_FILE_DATALENGTH ...
> +
> +#define VREG ...
> +#define VDIR ...
> +#define VLNK ...
> +#define VBLK ...
> +#define VCHR ...
> +#define VFIFO ...
> +#define VSOCK ...
> +
> +#define S_IFMT ...
> +
> +int open(const char *path, int oflag, int perm);
> +int close(int);
> +
> +#define O_RDONLY ...
> +''')
> +
> +if __name__ == '__main__':
> +    ffi.compile()
> diff -r 065fb34034a3 -r b40939504d0e mercurial/pure/osutil.py
> --- a/mercurial/pure/osutil.py  Mon Jul 11 10:44:18 2016 +0200
> +++ b/mercurial/pure/osutil.py  Mon Jul 11 11:05:08 2016 +0200
> @@ -14,6 +14,10 @@
>  import stat as statmod
>  import sys
>
> +from . import policy
> +modulepolicy = policy.policy
> +policynocffi = policy.policynocffi
> +
>  def _mode_to_kind(mode):
>      if statmod.S_ISREG(mode):
>          return statmod.S_IFREG
> @@ -31,7 +35,7 @@
>          return statmod.S_IFSOCK
>      return mode
>
> -def listdir(path, stat=False, skip=None):
> +def listdir_pure(path, stat=False, skip=None):
>      '''listdir(path, stat=False) -> list_of_tuples
>
>      Return a sorted list containing information about the entries
> @@ -61,6 +65,97 @@
>              result.append((fn, _mode_to_kind(st.st_mode)))
>      return result
>
> +ffi = None
> +if modulepolicy not in policynocffi and sys.platform == 'darwin':
> +    try:
> +        from _osutil_cffi import ffi, lib
> +    except ImportError:
> +        if modulepolicy == 'cffi': # strict cffi import
> +            raise
> +        listdir = listdir_pure
> +
> +if sys.platform == 'darwin' and ffi is not None:
> +    listdir_batch_size = 4096
> +
> +    attrkinds = [None] * 20
> +
> +    attrkinds[lib.VREG] = statmod.S_IFREG
> +    attrkinds[lib.VDIR] = statmod.S_IFDIR
> +    attrkinds[lib.VLNK] = statmod.S_IFLNK
> +    attrkinds[lib.VBLK] = statmod.S_IFBLK
> +    attrkinds[lib.VCHR] = statmod.S_IFCHR
> +    attrkinds[lib.VFIFO] = statmod.S_IFIFO
> +    attrkinds[lib.VSOCK] = statmod.S_IFSOCK
> +
> +    class stat_res(object):
> +        def __init__(self, st_mode, st_mtime, st_size):
> +            self.st_mode = st_mode
> +            self.st_mtime = st_mtime
> +            self.st_size = st_size
> +
> +    tv_sec_ofs = ffi.offsetof("struct timespec", "tv_sec")
> +    buf = ffi.new("char[]", listdir_batch_size)
> +
> +    def listdir_internal(dfd, req, stat, skip):
> +        ret = []
> +        while True:
> +            r = lib.getattrlistbulk(dfd, req, buf, listdir_batch_size, 0)
> +            if r == 0:
> +                break
> +            if r == -1:
> +                raise OSError(ffi.errno, os.strerror(ffi.errno))
> +            cur = int(ffi.cast("intptr_t", buf))
> +            for i in range(r):
> +                lgt = ffi.cast("uint32_t*", cur)[0]
> +                c = cur + 4 + ffi.sizeof("attribute_set_t")
> +                ofs = ffi.cast("uint32_t*", c)[0]
> +                str_lgt = ffi.cast("uint32_t*", c)[1]
> +                name = str(ffi.buffer(ffi.cast("char*", c + ofs), str_lgt - 1))
> +                c += ffi.sizeof("attrreference_t")
> +                tp = attrkinds[ffi.cast("uint8_t*", c)[0]]
> +                c += ffi.sizeof("uint32_t")
> +                if name == "." or name == "..":
> +                    continue
> +                if skip == name and tp == statmod.S_ISDIR:
> +                    return []
> +                if stat:
> +                    c1 = c + tv_sec_ofs
> +                    mtime = ffi.cast("time_t*", c1)[0]
> +                    c += ffi.sizeof("struct timespec")
> +                    mode = (ffi.cast("uint32_t*", c)[0] & ~lib.S_IFMT)| tp
> +                    c += ffi.sizeof("uint32_t")
> +                    size = ffi.cast("off_t*", c)[0]
> +                    ret.append((name, tp, stat_res(st_mode=mode, st_mtime=mtime,
> +                                st_size=size)))
> +                else:
> +                    ret.append((name, tp))
> +                cur += lgt
> +        return ret
> +
> +    def listdir(path, stat=False, skip=None):
> +        req = ffi.new("struct attrlist*")
> +        req.bitmapcount = lib.ATTR_BIT_MAP_COUNT
> +        req.commonattr = (lib.ATTR_CMN_NAME | lib.ATTR_CMN_OBJTYPE |
> +                          lib.ATTR_CMN_RETURNED_ATTRS |
> +                          lib.ATTR_CMN_ACCESSMASK |
> +                          lib.ATTR_CMN_MODTIME)
> +        req.fileattr = lib.ATTR_FILE_DATALENGTH
> +        dfd = lib.open(path, lib.O_RDONLY, 0)
> +        if dfd == -1:
> +            raise OSError(ffi.errno, os.strerror(ffi.errno))
> +
> +        try:
> +            ret = listdir_internal(dfd, req, stat, skip)
> +        finally:
> +            try:
> +                lib.close(dfd)
> +            except:
> +                pass # we ignore all the errors from closing, not
> +                # much we can do about that
> +        return ret
> +else:
> +    listdir = listdir_pure
> +
>  if os.name != 'nt':
>      posixfile = open
>
> diff -r 065fb34034a3 -r b40939504d0e setup.py
> --- a/setup.py  Mon Jul 11 10:44:18 2016 +0200
> +++ b/setup.py  Mon Jul 11 11:05:08 2016 +0200
> @@ -320,6 +320,9 @@
>          elif self.distribution.cffi:
>              exts = []
>              # cffi modules go here
> +            if sys.platform == 'darwin':
> +                from mercurial import build_osutil_cffi
> +                exts.append(build_osutil_cffi.ffi.distutils_extension())
>              self.distribution.ext_modules = exts
>          else:
>              h = os.path.join(get_python_inc(), 'Python.h')


More information about the Mercurial-devel mailing list