New submission from Nick Coghlan:

Patch to implement PEP 366.

Note that it doesn't implement precisely the semantics described in the
version of the PEP posted in July, as some of those ideas didn't prove
feasible due to the fact that imp.new_module can't tell the difference
between normal modules and packages.

An updated version of the PEP will be posted shortly to correct those
problems.

----------
components: Interpreter Core, Library (Lib)
files: pep_366_v1.diff
keywords: patch
messages: 57761
nosy: ncoghlan
priority: normal
severity: normal
status: open
title: PEP 366 implementation
type: rfe
versions: Python 2.6, Python 3.0
Added file: http://bugs.python.org/file8794/pep_366_v1.diff

__________________________________
Tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue1487>
__________________________________
Index: Python/import.c
===================================================================
--- Python/import.c     (revision 59120)
+++ Python/import.c     (working copy)
@@ -2106,7 +2106,8 @@
 {
        static PyObject *namestr = NULL;
        static PyObject *pathstr = NULL;
-       PyObject *modname, *modpath, *modules, *parent;
+       static PyObject *pkgstr = NULL;
+       PyObject *pkgname, *modname, *modpath, *modules, *parent;
 
        if (globals == NULL || !PyDict_Check(globals) || !level)
                return Py_None;
@@ -2121,44 +2122,103 @@
                if (pathstr == NULL)
                        return NULL;
        }
+       if (pkgstr == NULL) {
+               pkgstr = PyString_InternFromString("__package__");
+               if (pkgstr == NULL)
+                       return NULL;
+       }
 
        *buf = '\0';
        *p_buflen = 0;
-       modname = PyDict_GetItem(globals, namestr);
-       if (modname == NULL || !PyString_Check(modname))
-               return Py_None;
+       pkgname = PyDict_GetItem(globals, pkgstr);
 
-       modpath = PyDict_GetItem(globals, pathstr);
-       if (modpath != NULL) {
-               Py_ssize_t len = PyString_GET_SIZE(modname);
-               if (len > MAXPATHLEN) {
+       if ((pkgname != NULL) && (pkgname != Py_None)) {
+               /* __package__ is set, so use it */
+               Py_ssize_t len;
+               if (!PyString_Check(pkgname)) {
                        PyErr_SetString(PyExc_ValueError,
-                                       "Module name too long");
+                                       "__package__ set to non-string");
                        return NULL;
                }
-               strcpy(buf, PyString_AS_STRING(modname));
-       }
-       else {
-               char *start = PyString_AS_STRING(modname);
-               char *lastdot = strrchr(start, '.');
-               size_t len;
-               if (lastdot == NULL && level > 0) {
+               len = PyString_GET_SIZE(pkgname);
+               if (len == 0) {
+                       if (level > 0) {
+                               PyErr_SetString(PyExc_ValueError,
+                                       "Attempted relative import in 
non-package");
+                               return NULL;
+                       }
+                       return Py_None;
+               }
+               if (len > MAXPATHLEN) {
                        PyErr_SetString(PyExc_ValueError,
-                               "Attempted relative import in non-package");
+                                       "Package name too long");
                        return NULL;
                }
-               if (lastdot == NULL)
+               strcpy(buf, PyString_AS_STRING(pkgname));
+       } else {
+               /* __package__ not set, so figure it out and set it */
+               modname = PyDict_GetItem(globals, namestr);
+               if (modname == NULL || !PyString_Check(modname))
                        return Py_None;
-               len = lastdot - start;
-               if (len >= MAXPATHLEN) {
-                       PyErr_SetString(PyExc_ValueError,
-                                       "Module name too long");
-                       return NULL;
+       
+               modpath = PyDict_GetItem(globals, pathstr);
+               if (modpath != NULL) {
+                       /* __path__ is set, so modname is already the package 
name */
+                       Py_ssize_t len = PyString_GET_SIZE(modname);
+                       int error;
+                       if (len > MAXPATHLEN) {
+                               PyErr_SetString(PyExc_ValueError,
+                                               "Module name too long");
+                               return NULL;
+                       }
+                       strcpy(buf, PyString_AS_STRING(modname));
+                       error = PyDict_SetItem(globals, pkgstr, modname);
+                       if (error) {
+                               PyErr_SetString(PyExc_ValueError,
+                                               "Could not set __package__");
+                               return NULL;
+                       }
+               } else {
+                       /* Normal module, so work out the package name if any */
+                       char *start = PyString_AS_STRING(modname);
+                       char *lastdot = strrchr(start, '.');
+                       size_t len;
+                       int error;
+                       if (lastdot == NULL && level > 0) {
+                               PyErr_SetString(PyExc_ValueError,
+                                       "Attempted relative import in 
non-package");
+                               return NULL;
+                       }
+                       if (lastdot == NULL) {
+                               error = PyDict_SetItem(globals, pkgstr, 
Py_None);
+                               if (error) {
+                                       PyErr_SetString(PyExc_ValueError,
+                                               "Could not set __package__");
+                                       return NULL;
+                               }
+                               return Py_None;
+                       }
+                       len = lastdot - start;
+                       if (len >= MAXPATHLEN) {
+                               PyErr_SetString(PyExc_ValueError,
+                                               "Module name too long");
+                               return NULL;
+                       }
+                       strncpy(buf, start, len);
+                       buf[len] = '\0';
+                       pkgname = PyString_FromString(buf);
+                       if (pkgname == NULL) {
+                               return NULL;
+                       }
+                       error = PyDict_SetItem(globals, pkgstr, pkgname);
+                       Py_DECREF(pkgname);
+                       if (error) {
+                               PyErr_SetString(PyExc_ValueError,
+                                               "Could not set __package__");
+                               return NULL;
+                       }
                }
-               strncpy(buf, start, len);
-               buf[len] = '\0';
        }
-
        while (--level > 0) {
                char *dot = strrchr(buf, '.');
                if (dot == NULL) {
Index: Objects/moduleobject.c
===================================================================
--- Objects/moduleobject.c      (revision 59120)
+++ Objects/moduleobject.c      (working copy)
@@ -30,6 +30,8 @@
                goto fail;
        if (PyDict_SetItemString(m->md_dict, "__doc__", Py_None) != 0)
                goto fail;
+       if (PyDict_SetItemString(m->md_dict, "__package__", Py_None) != 0)
+               goto fail;
        Py_DECREF(nameobj);
        PyObject_GC_Track(m);
        return (PyObject *)m;
Index: Lib/runpy.py
===================================================================
--- Lib/runpy.py        (revision 59120)
+++ Lib/runpy.py        (working copy)
@@ -23,19 +23,20 @@
 
 def _run_code(code, run_globals, init_globals=None,
               mod_name=None, mod_fname=None,
-              mod_loader=None):
+              mod_loader=None, pkg_name=None):
     """Helper for _run_module_code"""
     if init_globals is not None:
         run_globals.update(init_globals)
     run_globals.update(__name__ = mod_name,
                        __file__ = mod_fname,
-                       __loader__ = mod_loader)
+                       __loader__ = mod_loader,
+                       __package__ = pkg_name)
     exec code in run_globals
     return run_globals
 
 def _run_module_code(code, init_globals=None,
                     mod_name=None, mod_fname=None,
-                    mod_loader=None):
+                    mod_loader=None, pkg_name=None):
     """Helper for run_module"""
     # Set up the top level namespace dictionary
     temp_module = imp.new_module(mod_name)
@@ -49,7 +50,8 @@
     sys.modules[mod_name] = temp_module
     try:
         _run_code(code, mod_globals, init_globals,
-                    mod_name, mod_fname, mod_loader)
+                    mod_name, mod_fname,
+                    mod_loader, pkg_name)
     finally:
         sys.argv[0] = saved_argv0
         if restore_module:
@@ -110,13 +112,14 @@
     loader, code, fname = _get_module_details(mod_name)
     if run_name is None:
         run_name = mod_name
+    pkg_name = mod_name.rpartition('.')[0]
     if alter_sys:
         return _run_module_code(code, init_globals, run_name,
-                                fname, loader)
+                                fname, loader, pkg_name)
     else:
         # Leave the sys module alone
-        return _run_code(code, {}, init_globals,
-                         run_name, fname, loader)
+        return _run_code(code, {}, init_globals, run_name,
+                         fname, loader, pkg_name)
 
 
 if __name__ == "__main__":
Index: Lib/test/test_pkg.py
===================================================================
--- Lib/test/test_pkg.py        (revision 59120)
+++ Lib/test/test_pkg.py        (working copy)
@@ -188,11 +188,13 @@
         import t5
         self.assertEqual(fixdir(dir(t5)),
                          ['__doc__', '__file__', '__name__',
-                          '__path__', 'foo', 'string', 't5'])
+                          '__package__', '__path__', 'foo', 'string', 't5'])
         self.assertEqual(fixdir(dir(t5.foo)),
-                         ['__doc__', '__file__', '__name__', 'string'])
+                         ['__doc__', '__file__', '__name__', '__package__',
+                          'string'])
         self.assertEqual(fixdir(dir(t5.string)),
-                         ['__doc__', '__file__', '__name__', 'spam'])
+                         ['__doc__', '__file__', '__name__','__package__',
+                          'spam'])
 
     def test_6(self):
         hier = [
@@ -208,14 +210,14 @@
         import t6
         self.assertEqual(fixdir(dir(t6)),
                          ['__all__', '__doc__', '__file__',
-                          '__name__', '__path__'])
+                          '__name__', '__package__', '__path__'])
         s = """
             import t6
             from t6 import *
             self.assertEqual(fixdir(dir(t6)),
                              ['__all__', '__doc__', '__file__',
-                              '__name__', '__path__', 'eggs',
-                              'ham', 'spam'])
+                              '__name__', '__package__', '__path__',
+                              'eggs', 'ham', 'spam'])
             self.assertEqual(dir(), ['eggs', 'ham', 'self', 'spam', 't6'])
             """
         self.run_code(s)
@@ -241,17 +243,19 @@
         t7, sub, subsub = None, None, None
         import t7 as tas
         self.assertEqual(fixdir(dir(tas)),
-                         ['__doc__', '__file__', '__name__', '__path__'])
+                         ['__doc__', '__file__', '__name__',
+                          '__package__', '__path__'])
         self.failIf(t7)
         from t7 import sub as subpar
         self.assertEqual(fixdir(dir(subpar)),
-                         ['__doc__', '__file__', '__name__', '__path__'])
+                         ['__doc__', '__file__', '__name__',
+                          '__package__', '__path__'])
         self.failIf(t7)
         self.failIf(sub)
         from t7.sub import subsub as subsubsub
         self.assertEqual(fixdir(dir(subsubsub)),
-                         ['__doc__', '__file__', '__name__', '__path__',
-                          'spam'])
+                         ['__doc__', '__file__', '__name__',
+                         '__package__', '__path__', 'spam'])
         self.failIf(t7)
         self.failIf(sub)
         self.failIf(subsub)
Index: Lib/test/test_runpy.py
===================================================================
--- Lib/test/test_runpy.py      (revision 59120)
+++ Lib/test/test_runpy.py      (working copy)
@@ -36,6 +36,7 @@
         self.failUnless(d["__name__"] is None)
         self.failUnless(d["__file__"] is None)
         self.failUnless(d["__loader__"] is None)
+        self.failUnless(d["__package__"] is None)
         self.failUnless(d["run_argv0"] is saved_argv0)
         self.failUnless("run_name" not in d)
         self.failUnless(sys.argv[0] is saved_argv0)
@@ -45,13 +46,15 @@
         name = "<Nonsense>"
         file = "Some other nonsense"
         loader = "Now you're just being silly"
+        package = '' # Treat as a top level module
         d1 = dict(initial=initial)
         saved_argv0 = sys.argv[0]
         d2 = _run_module_code(self.test_source,
                               d1,
                               name,
                               file,
-                              loader)
+                              loader,
+                              package)
         self.failUnless("result" not in d1)
         self.failUnless(d2["initial"] is initial)
         self.failUnless(d2["result"] == self.expected_result)
@@ -62,6 +65,7 @@
         self.failUnless(d2["__file__"] is file)
         self.failUnless(d2["run_argv0"] is file)
         self.failUnless(d2["__loader__"] is loader)
+        self.failUnless(d2["__package__"] is package)
         self.failUnless(sys.argv[0] is saved_argv0)
         self.failUnless(name not in sys.modules)
 
@@ -164,7 +168,7 @@
             self._del_pkg(pkg_dir, depth, mod_name)
         if verbose: print "Module executed successfully"
 
-    def _add_relative_modules(self, base_dir, depth):
+    def _add_relative_modules(self, base_dir, source, depth):
         if depth <= 1:
             raise ValueError("Relative module test needs depth > 1")
         pkg_name = "__runpy_pkg__"
@@ -190,7 +194,7 @@
         if verbose: print "  Added nephew module:", nephew_fname
 
     def _check_relative_imports(self, depth, run_name=None):
-        contents = """\
+        contents = r"""\
 from __future__ import absolute_import
 from . import sibling
 from ..uncle.cousin import nephew
@@ -198,16 +202,21 @@
         pkg_dir, mod_fname, mod_name = (
                self._make_pkg(contents, depth))
         try:
-            self._add_relative_modules(pkg_dir, depth)
+            self._add_relative_modules(pkg_dir, contents, depth)
+            pkg_name = mod_name.rpartition('.')[0]
             if verbose: print "Running from source:", mod_name
-            d1 = run_module(mod_name) # Read from source
+            d1 = run_module(mod_name, run_name=run_name) # Read from source
+            self.failUnless("__package__" in d1)
+            self.failUnless(d1["__package__"] == pkg_name)
             self.failUnless("sibling" in d1)
             self.failUnless("nephew" in d1)
             del d1 # Ensure __loader__ entry doesn't keep file open
             __import__(mod_name)
             os.remove(mod_fname)
             if verbose: print "Running from compiled:", mod_name
-            d2 = run_module(mod_name) # Read from bytecode
+            d2 = run_module(mod_name, run_name=run_name) # Read from bytecode
+            self.failUnless("__package__" in d2)
+            self.failUnless(d2["__package__"] == pkg_name)
             self.failUnless("sibling" in d2)
             self.failUnless("nephew" in d2)
             del d2 # Ensure __loader__ entry doesn't keep file open
@@ -225,7 +234,12 @@
             if verbose: print "Testing relative imports at depth:", depth
             self._check_relative_imports(depth)
 
+    def test_main_relative_import(self):
+        for depth in range(2, 5):
+            if verbose: print "Testing main relative imports at depth:", depth
+            self._check_relative_imports(depth, "__main__")
 
+
 def test_main():
     run_unittest(RunModuleCodeTest)
     run_unittest(RunModuleTest)
_______________________________________________
Python-bugs-list mailing list 
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to