jpace121 / cefpython

Automatically exported from code.google.com/p/cefpython
0 stars 1 forks source link

Error launching app when path contains unicode characters on Windows/Py27 (solution in comment #4) #151

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Put app in a path that contains unicode characters and try to run wxpython.py 
example, an error occurs:

  C:\cefpython\cefpython\cefpython\cef3\windows\binaries ąś>python wxpython.py
  Traceback (most recent call last):
    File "wxpython.py", line 24, in <module>
      from cefpython3 import cefpython
  ImportError: No module named cefpython3

This code failed:

  libcef_dll = os.path.join(os.path.dirname(os.path.abspath(__file__)),
        'libcef.dll')
  if os.path.exists(libcef_dll):
      # Import a local module

To support unicode paths use code like this:

  def module_path():
      encoding = sys.getfilesystemencoding()
      if we_are_frozen():
          return os.path.dirname(unicode(sys.executable, encoding))
      return os.path.dirname(unicode(__file__, encoding))

Reference: http://stackoverflow.com/questions/2632199/

Fixes need to be applied to:

 * examples
   1. os.path.exists(libcef)
   2. GetApplicationPath() - check code in examples that calls it many times
   3. ExceptHook()

 * utils.pyx > GetModuleDirectory()
   1. Used by many examples and that needs fixing
   2. This function is used in cefpython internally only in two places:
       # __init__.py.template on Linux - unicode issue probably doesn't affect Linux, so may be ignored
       # chromectrl.py - browser_subprocess_path is set with GetModuleDirectory
   3. os.getcwd() cannot be used, use instead "os.path.dirname(unicode('.', encoding))"

Still not sure how to resolve issue with passing "browser_subprocess_path" to 
CEF. CEFPython needs to allow for settings strings to be unicode. This is the 
current code:

  cefString = new CefString(&cefAppSettings.browser_subprocess_path)
  PyToCefStringPointer(appSettings[key], cefString)

Source of PyToCefStringPointer:

  cdef void PyToCefStringPointer(
        py_string pyString,
        CefString* cefString
        ) except *:
    if PY_MAJOR_VERSION < 3:
        if type(pyString) == unicode:
            pyString = <bytes>(pyString.encode(
                    g_applicationSettings["string_encoding"],
                    errors=UNICODE_ENCODE_ERRORS))
    else:
        # The unicode type is not defined in Python 3.
        if type(pyString) == str:
            pyString = <bytes>(pyString.encode(
                    g_applicationSettings["string_encoding"],
                    errors=UNICODE_ENCODE_ERRORS))

    cdef cpp_string cppString = pyString
    # When used cefString.FromASCII(), a DCHECK failed
    # when passed a unicode string.
    cefString.FromString(cppString)

Not sure if that works. It converts unicode string to bytes, but is it a utf8 
encoded bytes? If it doesn't work then it would be required to call 
cefString.FromWString() instead of .FromString(). Also earlier convert it to 
cpp_wstring instead of cpp_string.

-------------------------------------------
HOW TO FIX IT RIGHT NOW IN YOUR APPLICATION
-------------------------------------------

If you use cefpython.wx.chromectrl then then you would need to fix it in the 
package.

If you're using examples like wxpython.py then you need to modify:
 * calls to os.path.exists
 * GetApplicationPath
 * ExceptHook
 * Set current working directory to where your application binary files are. Make sure you have done this, as in some cases on Windows current working directory may be different. Setting working directory is required for step 2 to work, which is to set options such as locales_dir_path, resources_dir_path, browser_subprocess_path to be relative paths.

Original issue reported on code.google.com by czarek.t...@gmail.com on 8 Dec 2014 at 10:21

GoogleCodeExporter commented 9 years ago
When in a unicode path and importing a local PYD module (portable zip 
distribution) it fails:

  import cefpython_py27 as cefpython

Error:

  Traceback (most recent call last):
  File "wxpython.py", line 51, in <module>
    import cefpython_py27 as cefpython
  ImportError: No module named cefpython_py27

It also fails with "import cefwindow" and that's a regular python file 
"cefwindow.py". So this is not an issue with the PYD module built with Cython.

Maybe that's not an issue when app is packaged with py2exe. But still looks 
like Python 2.7 has some serious issues with unicode. So I'm not going to make 
this a priority.

-------------------
THE PROPER SOLUTION
-------------------

Create a standalone installer for your application, using for example Inno 
Setup (http://www.jrsoftware.org/isinfo.php). Install application to C:\Program 
Files\.

Original comment by czarek.t...@gmail.com on 8 Dec 2014 at 11:25

GoogleCodeExporter commented 9 years ago
I've modified GetApplicationPath to return unicode path, but it's not needed 
anyway. Pasting it here so that it doesn't get lost, in case I reinvestigate 
this issue again later.

    def GetApplicationPath(file=None):
        # Returns a unicode string. To print unicode string to console:
        # print(GetApplicationPath().encode(sys.stdout.encoding, "replace"))
        encoding = sys.getfilesystemencoding()
        if file is None:
            # If file is None return current directory without trailing slash.
            file = u""
        else:
            file = unicode(file, encoding)
        # On Windows after downloading file and calling Browser.GoForward(),
        # current working directory is set to %UserProfile%.
        # Calling os.path.dirname(os.path.realpath(__file__))
        # returns for eg. "C:\Users\user\Downloads". A solution
        # is to cache path on first call.
        if not hasattr(GetApplicationPath, "dir"):
            if hasattr(sys, "frozen"):
                os.path.dirname(unicode(sys.executable, encoding))
            elif "__file__" in globals():
                dir = os.path.dirname(os.path.realpath(\
                        unicode(__file__, encoding)))
            else:
                dir = os.path.dirname(os.path.realpath(u"."))
            GetApplicationPath.dir = dir
        # When relative path.
        if not file.startswith(u"/") and not file.startswith(u"\\") and (
                not re.search(r"^[\w-]+:", file)):
            path = os.path.join(GetApplicationPath.dir, file)
            if platform.system() == "Windows":
                path = re.sub(r"[/\\]+", re.escape(os.sep), path)
            path = re.sub(r"[/\\]+$", "", path)
            return path
        return file

    if platform.architecture()[0] != "32bit":
        raise Exception("Architecture not supported: %s" \
                % platform.architecture()[0])

    libcef_dll = GetApplicationPath("libcef.dll")
    if os.path.exists(libcef_dll):
        # Import a local module. A case when using a Portable Zip distribution.
        if (2,7) <= sys.version_info < (2,8):
            import cefpython_py27 as cefpython
        elif (3,4) <= sys.version_info < (3,5):
            import cefpython_py34 as cefpython
        else:
            raise Exception("Unsupported python version: %s" % sys.version)
    else:
        # Import from an installed package
        from cefpython3 import cefpython

Original comment by czarek.t...@gmail.com on 8 Dec 2014 at 11:28

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
Looks like cefpython can work just fine in a unicode path. One of users 
reported that after packing his app using py2exe everything worked, except when 
launching CEF subprocesses. So seems like imports are working just fine on 
unicode paths after using py2exe. The only problem are CEF paths to the 
subprocess executable and some other CEF resources. Looks like you just need to 
set application settings passed to cefpython.Initialize(), that are paths, to 
be unicode strings. There are a few such settings:

  browser_subprocess_path
  locales_dir_path
  resources_dir_path
  # There is also cache_path, but it's optional to set

Example proof test:

  cefpython resides in "binaries/" directory. I've made a copy of it and named it "binaries ąś/". Removed subprocess.exe from "binaries/" and edited "binaries/wxpython.py" file with this line changed:

  "browser_subprocess_path": u"./../binaries ąś/subprocess",

Launched "binaries/wxpython.py" and it worked fine.

So, all examples are easily fixable, with the exception when using 
wx.chromectrl module. For wx.chromectrl to work, further fixes would be 
required to cefpython.GetModuleDirectory() and chromectrl.py > Initialize().

Original comment by czarek.t...@gmail.com on 8 Dec 2014 at 11:53

GoogleCodeExporter commented 9 years ago

Original comment by czarek.t...@gmail.com on 8 Dec 2014 at 12:32