Skip to content

Trailing slash in sys.path cause import failure #45634

Closed

Description

@guillaumegirard
BPO 1293
Nosy @gvanrossum, @brettcannon, @birkenfeld, @facundobatista, @tiran
Files
  • trailing_slash.patch
  • trailing_slash2.patch
  • trailing_slash3.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/brettcannon'
    closed_at = <Date 2007-11-07.17:46:51.011>
    created_at = <Date 2007-10-18.12:47:42.621>
    labels = ['interpreter-core', 'type-bug']
    title = 'Trailing slash in sys.path cause import failure'
    updated_at = <Date 2007-11-07.17:46:51.010>
    user = 'https://bugs.python.org/guillaumegirard'

    bugs.python.org fields:

    activity = <Date 2007-11-07.17:46:51.010>
    actor = 'gvanrossum'
    assignee = 'brett.cannon'
    closed = True
    closed_date = <Date 2007-11-07.17:46:51.011>
    closer = 'gvanrossum'
    components = ['Interpreter Core']
    creation = <Date 2007-10-18.12:47:42.621>
    creator = 'guillaumegirard'
    dependencies = []
    files = ['8674', '8675', '8704']
    hgrepos = []
    issue_num = 1293
    keywords = ['patch']
    message_count = 15.0
    messages = ['56523', '57025', '57030', '57038', '57039', '57044', '57177', '57178', '57179', '57182', '57183', '57185', '57199', '57203', '57206']
    nosy_count = 6.0
    nosy_names = ['gvanrossum', 'brett.cannon', 'georg.brandl', 'facundobatista', 'christian.heimes', 'guillaumegirard']
    pr_nums = []
    priority = 'low'
    resolution = 'fixed'
    stage = None
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue1293'
    versions = ['Python 2.6', 'Python 2.5', 'Python 3.0']

    Activity

    1. guillaumegirard commented on Oct 18, 2007

      guillaumegirardmannequin
      MannequinAuthor

      On win32, the following code:

      import sys
      sys.path.append('../bar/')
      import bar

      where the file bar.py is present in ../bar/ will return an import error
      "No module named bar". Remove the trailing slash and the bar.py is
      imported correctly. The problem is identical with backslash. This code
      works in Python 2.4.

      Not a very serious bug, but it breaks programs working with Python 2.4.

    2. facundobatista commented on Nov 1, 2007

      @facundobatista
      Member

      Only in win32, in Linux it behaves ok (I put a /tmp/w.py that prints 'w'):

      Python 2.5.1 (r251:54863, May  2 2007, 16:56:35) 
      [GCC 4.1.2 (Ubuntu 4.1.2-0ubuntu4)] on linux2
      Type "help", "copyright", "credits" or "license" for more information.
      >>> import sys
      >>> sys.path.append("/tmp/")
      >>> import w
      w
      >>>
    3. tiran commented on Nov 1, 2007

      @tiran
      Member

      I was able to reproduce the bug on Windows with Python 2.6 and 3.0. I've
      added an unit test to both versions.

    4. tiran commented on Nov 1, 2007

      @tiran
      Member

      Here is a patch that solves the problem. However the patch is against
      the py3k sources and I like somebody to review and test it. I don't have
      enough disk space in my VMWare box to test it against the trunk or 2.5.

      Reason for the problem: Windows' stat doesn't like trailing slashes.

    5. tiran commented on Nov 1, 2007

      @tiran
      Member

      Fixed patch. Georg pointed out that PyArg_ParseTuple("s") returns a
      reference to the internal data of the PyString object. The new version
      copies the path to a fixed width buffer before it mangles the trailing
      slashes.

      The new patch applies against the trunk.

      Brett, you are the import master. Can you review the patch, please?

    6. brettcannon commented on Nov 2, 2007

      @brettcannon
      Member

      I will have a look when I can.

    7. gvanrossum commented on Nov 6, 2007

      @gvanrossum
      Member

      I see several problems with this, but there's light at the end of the
      tunnel.

      (1) Don't use s#; it will allow null bytes in the string, which we don't
      want.

      (2) Put the entire trailing slash removal inside #ifdef MS_WINDOWS.

      (3) Careful! It seems the code you wrote would transform "C:/" into
      "C:" which isn't the same thing (the latter refers to the current
      directory on the C drive, while the former is the root of the C drive).

    8. tiran commented on Nov 6, 2007

      @tiran
      Member

      Guido van Rossum wrote:

      (1) Don't use s#; it will allow null bytes in the string, which we don't
      want.

      (2) Put the entire trailing slash removal inside #ifdef MS_WINDOWS.

      Done aand done

      (3) Careful! It seems the code you wrote would transform "C:/" into
      "C:" which isn't the same thing (the latter refers to the current
      directory on the C drive, while the former is the root of the C drive).

      Oh, good catch! I haven't thought of C:/

      How do you like
      #ifdef MS_WINDOWS
      		/* Remove trailing / and \ - Windows' stat doesn't like them - but
      		 * keep the trailing slash of C:/
      		 */
      
      		Py_ssize_t i;
      		char mangled[MAXPATHLEN+1];
      
      		if (pathlen > MAXPATHLEN) {
      			PyErr_SetString(PyExc_OverflowError,
      					"path is too long");
      			return -1;
      		}
      		strcpy(mangled, path);
      
      		for (i = pathlen-1; i > 3; i--) {
      			if (mangled[i] != '/' && mangled[i] != '\\') {
      				break;
      			}
      			mangled[i] = '\0';
      		}
      		rv = stat(mangled, &statbuf);
      #else
      		rv = stat(path, &statbuf);
      #endif

      i > 3 should take care of C:/ and C:\

      Christian

    9. gvanrossum commented on Nov 6, 2007

      @gvanrossum
      Member

      How do you like
      #ifdef MS_WINDOWS
      /* Remove trailing / and \ - Windows' stat doesn't like them - but
      * keep the trailing slash of C:/
      */

                  Py_ssize_t i;
                  char mangled[MAXPATHLEN+1];
      
                  if (pathlen \> MAXPATHLEN) {
                          PyErr_SetString(PyExc_OverflowError,
                                          "path is too long");
                          return -1;
                  }
                  strcpy(mangled, path);
      
                  for (i = pathlen-1; i \> 3; i--) {
                          if (mangled[i] != '/' && mangled[i] != '\\\\') {
                                  break;
                          }
                          mangled[i] = '\\0';
                  }
                  rv = stat(mangled, &statbuf);
      

      #else
      rv = stat(path, &statbuf);
      #endif

      i > 3 should take care of C:/ and C:\

      But the C: is optional. You will need to do more parsing. Some more
      examples (all with slashes; but backslashes work the same):

      //share/ -> //share/
      //share/x/ -> //share/x
      /x/ -> /x
      / -> /
      // -> // (probably illegal but doesn't matter)
      C:/ -> C:/
      x/ -> x
      C:x/ -> C:x

      Isn't it easier to handle this at the Python level? There probably
      already is code for parsing Windows paths. (Hm, maybe we have it in C
      too somewhere?)

    10. tiran commented on Nov 6, 2007

      @tiran
      Member

      Another patch that uses os.path.normpath.

    11. gvanrossum commented on Nov 6, 2007

      @gvanrossum
      Member

      Another patch that uses os.path.normpath.

      Sorry, I really don't like to be importing os.path in order to be able
      to process the path used for an import.

    12. brettcannon commented on Nov 6, 2007

      @brettcannon
      Member

      On 11/6/07, Guido van Rossum <report@bugs.python.org> wrote:

      Guido van Rossum added the comment:

      > Another patch that uses os.path.normpath.

      Sorry, I really don't like to be importing os.path in order to be able
      to process the path used for an import.

      Modules/getpath.c:joinpath() I think does what you want in C.

    13. tiran commented on Nov 7, 2007

      @tiran
      Member

      Brett Cannon wrote:

      Modules/getpath.c:joinpath() I think does what you want in C.

      I don't see how it is going to help us.

      I've a final offer before I declare the bug as SEP (someone else's problem):

      pseudo code

      #ifdef MS_WINDOWS
          rv = stat(...)
          if (rv == error)
              check for *one* trailign / or \ and remove it
              rv = stat(...)
              if (rv == error)
                  give up
      #endif

      It should fix the case when somebody adds a directory with a trailing /
      or \ on Windows. More complex cases are still broken but that's really
      not my concern. ;)

      Christian

    14. gvanrossum commented on Nov 7, 2007

      @gvanrossum
      Member

      Works for me!

      On Nov 7, 2007 6:08 AM, Christian Heimes <report@bugs.python.org> wrote:

      Christian Heimes added the comment:

      Brett Cannon wrote:
      > Modules/getpath.c:joinpath() I think does what you want in C.

      I don't see how it is going to help us.

      I've a final offer before I declare the bug as SEP (someone else's problem):

      pseudo code

      #ifdef MS_WINDOWS
      rv = stat(...)
      if (rv == error)
      check for *one* trailign / or \ and remove it
      rv = stat(...)
      if (rv == error)
      give up
      #endif

      It should fix the case when somebody adds a directory with a trailing /
      or \ on Windows. More complex cases are still broken but that's really
      not my concern. ;)

      Christian


      Tracker <report@bugs.python.org>
      <http://bugs.python.org/issue1293\>


    15. tiran commented on Nov 7, 2007

      @tiran
      Member

      I've checked in a fix in r58903 (py3k branch).

    16. transferred this issue fromon Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions