Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save or open problem with path #925

Closed
RonanDD opened this issue Oct 1, 2019 · 6 comments
Closed

Save or open problem with path #925

RonanDD opened this issue Oct 1, 2019 · 6 comments
Milestone

Comments

@RonanDD
Copy link

RonanDD commented Oct 1, 2019

Hello,
I am a teacher in France. I regularly use Thonny at home without problems, but on the high school network I regularly have an error saving or opening the file. The path seems to be a problem of irregular hand: the same path can be validated or not. I get the error:


Traceback (most recent call last):
  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\workbench.py", line 166, in __init__
    self._editor_notebook.load_startup_files()
  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\code.py", line 485, in load_startup_files
    self.show_file(filename)
  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\code.py", line 673, in show_file
    editor = self.get_editor(filename, True)
  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\code.py", line 711, in get_editor
    return self._open_file(filename)
  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\code.py", line 699, in _open_file
    editor = Editor(self, filename)
  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\code.py", line 56, in __init__
    self._load_file(filename)
  File "C:\Program Files (x86)\Thonny\lib\site-packages\thonny\code.py", line 163, in _load_file
    self._last_known_mtime = os.path.getmtime(self._filename)
  File "C:\Program Files (x86)\Thonny\lib\genericpath.py", line 55, in getmtime
    return os.stat(filename).st_mtime
FileNotFoundError: [WinError 3] Le chemin d’accès spécifié est introuvable: 'P:\\M59UGM~S\\seconde\\algo\\PXDGBC~P\\reponse1.py'

I'm on Thonny 3.1.2 with python 3.7.2

@aivarannamaa
Copy link
Member

aivarannamaa commented Oct 1, 2019

Your case is very similar to #833. Both of you seem to use French as Windows (display?) language.

Which version of Windows do you have? 7, 8, 10? Home, professional, ...?

Is your home Windows also in French?

'P:\\M59UGM~S\\seconde\\algo\\PXDGBC~P\\reponse1.py'

When you open this path in Windows Explorer, does it also display ~-s or are those path segments actually longer?

@aivarannamaa aivarannamaa added this to the 3.2.2 milestone Oct 1, 2019
@RonanDD
Copy link
Author

RonanDD commented Oct 1, 2019

I have the problem with windows 7 and 10, professional. At home I'm on Linux Mint.
I tried the path in windows explorer :

'P:\\M59UGM~S\\seconde\\algo\\PXDGBC~P\\reponse1.py'

the file doesn't exit.
The real path should be :

P:\Mes documents\seconde\algo\programme dessin\reponse1.py

@aivarannamaa
Copy link
Member

Thank you, this is useful information. I suspect Thonny has problems with path normalization.

Can you please execute following code in your problematic computer (either paste it to Thonny's shell or store the script to a path which doesn't have spaces in it):

import os.path

def normpath_with_actual_case(name):
    """In Windows return the path with the case it is stored in the filesystem"""
    assert os.path.isabs(name) or os.path.ismount(name), "Not abs nor mount: " + name
    assert os.path.exists(name), "Not exists: " + name

    if os.name == "nt":
        name = os.path.realpath(name)

        from ctypes import create_unicode_buffer, windll

        buf = create_unicode_buffer(512)
        windll.kernel32.GetShortPathNameW(name, buf, 512)  # @UndefinedVariable
        windll.kernel32.GetLongPathNameW(buf.value, buf, 512)  # @UndefinedVariable
        if len(buf.value):
            result = buf.value
        else:
            result = name

        assert isinstance(result, str)

        if result[1] == ":":
            # ensure drive letter is capital
            return result[0].upper() + result[1:]
        else:
            return result
    else:
        return os.path.normpath(name)

print(normpath_with_actual_case(r"P:\Mes documents\seconde\algo\programme dessin\reponse1.py"))

@RonanDD
Copy link
Author

RonanDD commented Oct 1, 2019

The result is :
P:\Mes documents\seconde\algo\programme dessin\reponse1.py

And, this time, I can open the file.
But if I try another path (P:\Mes groupes\2turquoi\maths 2019-2020\python - algo\programme dessin)
The result is :
P:\MQUYPQ~J\2turquoi\MN702M~R\P4FENW~R\PXDGBC~P
And I can't open the file.

I save a file with Thony in : P:\Mes groupes\2turquoi
It works.

I try again your script with the path : P:\Mes groupes\2turquoi\maths 2019-2020\python - algo\programme dessin\reponse1.py
the result :
P:\Mes groupes\2turquoi\maths 2019-2020\python - algo\programme dessin\reponse1.py
and I can open the file.

@aivarannamaa
Copy link
Member

That's really weird. Does your P:\ represent a network location?

Unfortunately I don't know how to reproduce this situation in my computer. I modified normpath_with_actual_case to be more robust:

def normpath_with_actual_case(name: str) -> str:
    """In Windows return the path with the case it is stored in the filesystem"""
    assert os.path.isabs(name) or os.path.ismount(name), "Not abs nor mount: " + name
    assert os.path.exists(name), "Not exists: " + name

    if os.name == "nt":
        # https://stackoverflow.com/questions/2113822/python-getting-filename-case-as-stored-in-windows/2114975
        name = os.path.realpath(name)

        from ctypes import create_unicode_buffer, windll

        buf = create_unicode_buffer(512)
        # GetLongPathNameW alone doesn't fix filename part
        windll.kernel32.GetShortPathNameW(name, buf, 512)  # @UndefinedVariable
        windll.kernel32.GetLongPathNameW(buf.value, buf, 512)  # @UndefinedVariable
        if len(buf.value):
            result = buf.value
        else:
            result = name

        assert isinstance(result, str)
        
        if result.casefold() != name.casefold():
            # Sometimes GetShortPathNameW + GetLongPathNameW doesn't work
            # see eg. https://github.com/thonny/thonny/issues/925
            windll.kernel32.GetLongPathNameW(name, buf, 512)  # @UndefinedVariable
            if len(buf.value) and result.casefold() == name.casefold():
                result = buf.value
            else:
                result = name

        if result[1] == ":":
            # ensure drive letter is capital
            return result[0].upper() + result[1:]
        else:
            return result
    else:
        # easy on Linux
        # too difficult on mac
        # https://stackoverflow.com/questions/14515073/in-python-on-osx-with-hfs-how-can-i-get-the-correct-case-of-an-existing-filenam
        return os.path.normpath(name)

If it seems to work in your system, then you could replace it in your Thonny:

  • Select "Tools => Open Thonny program folder"
  • edit "common.py"
  • replace existing function with same name
  • restart Thonny

If it solves your problem, then I'll include it in the next release.

@RonanDD
Copy link
Author

RonanDD commented Oct 3, 2019

Does your P:\ represent a network location?

Yes.

I've tried the new version of normpath_with_actual_case and it seems to work, thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants