]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Another patch for #1762972: __file__ points to the py file instead pyo/pyc file
authorChristian Heimes <christian@cheimes.de>
Mon, 7 Jan 2008 20:12:44 +0000 (20:12 +0000)
committerChristian Heimes <christian@cheimes.de>
Mon, 7 Jan 2008 20:12:44 +0000 (20:12 +0000)
Lib/test/test_import.py
Misc/NEWS
Python/import.c

index 6c9e0b00a1e3437f8d3dc66ee259da16e9623803..760713e69cf86700f28b97cc0431bab2c16aece1 100644 (file)
@@ -1,4 +1,4 @@
-from test.test_support import TESTFN, run_unittest, catch_warning
+from test.test_support import TESTFN, run_unittest, catch_warning
 
 import unittest
 import os
@@ -200,6 +200,27 @@ class ImportTest(unittest.TestCase):
             if TESTFN in sys.modules:
                 del sys.modules[TESTFN]
 
+    def test_file_to_source(self):
+        # check if __file__ points to the source file where available
+        source = TESTFN + ".py"
+        with open(source, "w") as f:
+            f.write("test = None\n")
+
+        sys.path.insert(0, os.curdir)
+        try:
+            mod = __import__(TESTFN)
+            self.failUnless(mod.__file__.endswith('.py'))
+            os.remove(source)
+            del sys.modules[TESTFN]
+            mod = __import__(TESTFN)
+            self.failUnless(mod.__file__.endswith('.pyc'))
+        finally:
+            sys.path.pop(0)
+            remove_files(TESTFN)
+            if TESTFN in sys.modules:
+                del sys.modules[TESTFN]
+
+
 class PathsTests(unittest.TestCase):
     SAMPLES = ('test', 'test\u00e4\u00f6\u00fc\u00df', 'test\u00e9\u00e8',
                'test\u00b0\u00b3\u00b2')
index 0bd12482ad267b6d9c2b84d6d0196e5d2dd70e27..8a770609bacf2db7e2aa0de20e2de6f03e77d23d 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's New in Python 3.0a3?
 Core and Builtins
 -----------------
 
+- Issue #1762972: __file__ points to the source file instead of the pyc/pyo
+  file if the py file exists.
+
 - Issue #1393: object_richcompare() returns NotImplemented instead of
   False if the objects aren't equal, to give the other side a chance.
 
index 07bcbbf398b803ac0cb371d20908a0b1cd5836dc..f342143563239cada78b7ef91551af0b46d9b6df 100644 (file)
@@ -78,9 +78,10 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
                      3080 (PEP 3137 make __file__ and __name__ unicode)
                      3090 (kill str8 interning)
                      3100 (merge from 2.6a0, see 62151)
+                     3102 (__file__ points to source file)
 .
 */
-#define MAGIC (3100 | ((long)'\r'<<16) | ((long)'\n'<<24))
+#define MAGIC (3102 | ((long)'\r'<<16) | ((long)'\n'<<24))
 
 /* Magic word as global; note that _PyImport_Init() can change the
    value of this global to accommodate for alterations of how the
@@ -624,6 +625,8 @@ _RemoveModule(const char *name)
                              "sys.modules failed");
 }
 
+static PyObject * get_sourcefile(const char *file);
+
 /* Execute a code object in a module and return the module object
  * WITH INCREMENTED REFERENCE COUNT.  If an error occurs, name is
  * removed from sys.modules, to avoid leaving damaged module objects
@@ -657,7 +660,7 @@ PyImport_ExecCodeModuleEx(char *name, PyObject *co, char *pathname)
        /* Remember the filename as the __file__ attribute */
        v = NULL;
        if (pathname != NULL) {
-               v = PyUnicode_DecodeFSDefault(pathname);
+               v = get_sourcefile(pathname);
                if (v == NULL)
                        PyErr_Clear();
        }
@@ -960,12 +963,43 @@ load_source_module(char *name, char *pathname, FILE *fp)
        return m;
 }
 
+/* Get source file -> unicode or None
+ * Returns the path to the py file if available, else the given path
+ */
+static PyObject *
+get_sourcefile(const char *file)
+{
+       char py[MAXPATHLEN + 1];
+       Py_ssize_t len;
+       PyObject *u;
+       struct stat statbuf;
+
+       if (!file || !*file) {
+               Py_RETURN_NONE;
+       }
+
+       len = strlen(file);
+       if (len > MAXPATHLEN || PyOS_stricmp(&file[len-4], ".pyc") != 0) {
+               return PyUnicode_DecodeFSDefault(file);
+       }
+
+       strncpy(py, file, len-1);
+       py[len] = '\0';
+       if (stat(py, &statbuf) == 0 &&
+               S_ISREG(statbuf.st_mode)) {
+               u = PyUnicode_DecodeFSDefault(py);
+       }
+       else {
+               u = PyUnicode_DecodeFSDefault(file);
+       }
+       return u;
+}
 
 /* Forward */
 static PyObject *load_module(char *, FILE *, char *, int, PyObject *);
 static struct filedescr *find_module(char *, char *, PyObject *,
                                     char *, size_t, FILE **, PyObject **);
-static struct _frozen *find_frozen(char *name);
+static struct _frozen * find_frozen(char *);
 
 /* Load a package and return its module object WITH INCREMENTED
    REFERENCE COUNT */
@@ -988,7 +1022,7 @@ load_package(char *name, char *pathname)
                PySys_WriteStderr("import %s # directory %s\n",
                        name, pathname);
        d = PyModule_GetDict(m);
-       file = PyUnicode_DecodeFSDefault(pathname);
+       file = get_sourcefile(pathname);
        if (file == NULL)
                goto error;
        path = Py_BuildValue("[O]", file);