Commits

Amaury Forgeot d'Arc committed 21f8faf

implement the pwd module at interp-level,
should help the cyclic imports on some platforms
(pwd imported ctypes which needs pwd.pw_dir to find libraries)

Comments (0)

Files changed (7)

lib-python/conftest.py

     RegrTest('test_property.py', core=True),
     RegrTest('test_pstats.py'),
     RegrTest('test_pty.py', skip="unsupported extension module"),
-    RegrTest('test_pwd.py', skip=skip_win32),
+    RegrTest('test_pwd.py', usemodules="pwd", skip=skip_win32),
     RegrTest('test_py3kwarn.py'),
     RegrTest('test_pyclbr.py'),
     RegrTest('test_pydoc.py'),

pypy/config/pypyoption.py

 # --allworkingmodules
 working_modules = default_modules.copy()
 working_modules.update(dict.fromkeys(
-    ["_socket", "unicodedata", "mmap", "fcntl", "_locale",
+    ["_socket", "unicodedata", "mmap", "fcntl", "_locale", "pwd",
      "rctime" , "select", "zipimport", "_lsprof",
      "crypt", "signal", "_rawffi", "termios", "zlib", "bz2",
      "struct", "_hashlib", "_md5", "_sha", "_minimal_curses", "cStringIO",
     # unix only modules
     del working_modules["crypt"]
     del working_modules["fcntl"]
+    del working_modules["pwd"]
     del working_modules["termios"]
     del working_modules["_minimal_curses"]
 

pypy/doc/config/objspace.usemodules.pwd.txt

+Use the 'pwd' module. 
+This module is expected to be fully working.

pypy/module/pwd/__init__.py

+from pypy.interpreter.mixedmodule import MixedModule
+
+class Module(MixedModule):
+    """
+    This module provides access to the Unix password database.
+    It is available on all Unix versions.
+
+    Password database entries are reported as 7-tuples containing the following
+    items from the password database (see `<pwd.h>'), in order:
+    pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell.
+    The uid and gid items are integers, all others are strings. An
+    exception is raised if the entry asked for cannot be found.
+    """
+
+    interpleveldefs = {
+        'getpwuid': 'interp_pwd.getpwuid',
+        'getpwnam': 'interp_pwd.getpwnam',
+        'getpwall': 'interp_pwd.getpwall',
+    }
+
+    appleveldefs = {
+        'struct_passwd': 'app_pwd.struct_passwd',
+        'struct_pwent': 'app_pwd.struct_passwd',
+    }
+

pypy/module/pwd/app_pwd.py

+from _structseq import structseqtype, structseqfield
+
+class struct_passwd:
+    """
+    pwd.struct_passwd: Results from getpw*() routines.
+
+    This object may be accessed either as a tuple of
+      (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell)
+    or via the object attributes as named in the above tuple.
+    """
+    __metaclass__ = structseqtype
+    name = "pwd.struct_passwd"
+
+    pw_name   = structseqfield(0, "user name")
+    pw_passwd = structseqfield(1, "password")
+    pw_uid    = structseqfield(2, "user id")
+    pw_gid    = structseqfield(3, "group id")
+    pw_gecos  = structseqfield(4, "real name")
+    pw_dir    = structseqfield(5, "home directory")
+    pw_shell  = structseqfield(6, "shell program")

pypy/module/pwd/interp_pwd.py

+from pypy.translator.tool.cbuild import ExternalCompilationInfo
+from pypy.rpython.tool import rffi_platform
+from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.error import OperationError, operationerrfmt
+
+class CConfig:
+    _compilation_info_ = ExternalCompilationInfo(
+        includes=['pwd.h']
+        )
+
+    uid_t = rffi_platform.SimpleType("uid_t")
+
+    passwd = rffi_platform.Struct(
+        'struct passwd',
+        [('pw_name', rffi.CCHARP),
+         ('pw_passwd', rffi.CCHARP),
+         ('pw_uid', rffi.INT),
+         ('pw_gid', rffi.INT),
+         ('pw_gecos', rffi.CCHARP),
+         ('pw_dir', rffi.CCHARP),
+         ('pw_shell', rffi.CCHARP),
+         ])
+
+config = rffi_platform.configure(CConfig)
+passwd_p = lltype.Ptr(config['passwd'])
+uid_t = config['uid_t']
+
+c_getpwuid = rffi.llexternal("getpwuid", [uid_t], passwd_p)
+c_getpwnam = rffi.llexternal("getpwnam", [rffi.CCHARP], passwd_p)
+c_setpwent = rffi.llexternal("setpwent", [], lltype.Void)
+c_getpwent = rffi.llexternal("getpwent", [], passwd_p)
+c_endpwent = rffi.llexternal("endpwent", [], lltype.Void)
+
+def make_struct_passwd(space, pw):
+    w_passwd_struct = space.getattr(space.getbuiltinmodule('pwd'),
+                                    space.wrap('struct_passwd'))
+    w_tuple = space.newtuple([
+        space.wrap(rffi.charp2str(pw.c_pw_name)),
+        space.wrap(rffi.charp2str(pw.c_pw_passwd)),
+        space.wrap(pw.c_pw_uid),
+        space.wrap(pw.c_pw_gid),
+        space.wrap(rffi.charp2str(pw.c_pw_gecos)),
+        space.wrap(rffi.charp2str(pw.c_pw_dir)),
+        space.wrap(rffi.charp2str(pw.c_pw_shell)),
+        ])
+    return space.call_function(w_passwd_struct, w_tuple)
+
+@unwrap_spec(uid=int)
+def getpwuid(space, uid):
+    """
+    getpwuid(uid) -> (pw_name,pw_passwd,pw_uid,
+                      pw_gid,pw_gecos,pw_dir,pw_shell)
+    Return the password database entry for the given numeric user ID.
+    See pwd.__doc__ for more on password database entries.
+    """
+    pw = c_getpwuid(uid)
+    if not pw:
+        raise operationerrfmt(space.w_KeyError,
+            "getpwuid(): uid not found: %d", uid)
+    return make_struct_passwd(space, pw)
+
+@unwrap_spec(name=str)
+def getpwnam(space, name):
+    """
+    getpwnam(name) -> (pw_name,pw_passwd,pw_uid,
+                        pw_gid,pw_gecos,pw_dir,pw_shell)
+    Return the password database entry for the given user name.
+    See pwd.__doc__ for more on password database entries.
+    """
+    pw = c_getpwnam(name)
+    if not pw:
+        raise operationerrfmt(space.w_KeyError,
+            "getpwnam(): name not found: %s", name)
+    return make_struct_passwd(space, pw)
+
+def getpwall(space):
+    users_w = []
+    c_setpwent()
+    try:
+        while True:
+            pw = c_getpwent()
+            if not pw:
+                break
+            users_w.append(make_struct_passwd(space, pw))
+    finally:
+        c_endpwent()
+    return space.newlist(users_w)
+    

pypy/module/pwd/test/test_pwd.py

+from pypy.conftest import gettestobjspace
+
+class AppTestPwd:
+    def setup_class(cls):
+        cls.space = gettestobjspace(usemodules=['pwd'])
+
+    def test_getpwuid(self):
+        import pwd
+        raises(KeyError, pwd.getpwuid, -1)
+        pw = pwd.getpwuid(0)
+        assert pw.pw_name == 'root'
+        assert isinstance(pw.pw_passwd, str)
+        assert pw.pw_uid == 0
+        assert pw.pw_gid == 0
+        assert pw.pw_dir == '/root'
+        assert pw.pw_shell.startswith('/')
+
+    def test_getpwnam(self):
+        import pwd
+        raises(KeyError, pwd.getpwnam, '~invalid~')
+        assert pwd.getpwnam('root').pw_name == 'root'
+
+    def test_getpwall(self):
+        import pwd
+        assert pwd.getpwnam('root') in pwd.getpwall()