Skip to content

Commit

Permalink
Add unit tests (#59) and fix Python 3 issue (#121,#183) among others.
Browse files Browse the repository at this point in the history
Improve shutting down cleanly with ExceptHook.

Fix issue with str/bytes passed to Debug() function (#110).
  • Loading branch information
cztomczak committed Sep 18, 2016
1 parent f515403 commit 80d7c5a
Show file tree
Hide file tree
Showing 11 changed files with 667 additions and 20 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea/
build/
*.log
*.log
__pycache__/
5 changes: 4 additions & 1 deletion api/cefpython.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ Global except hook to exit app cleanly on error.

This hook does the following: in case of exception write it to
the "error.log" file, display it to the console, shutdown CEF
and exit application immediately by ignoring "finally" (_exit())
and exit application immediately by ignoring "finally" (_exit()).

If you would like to implement a custom hook take a look at the
source code of ExceptHook in the cefpython/src/helpers.pyx file.


### GetAppSetting
Expand Down
4 changes: 3 additions & 1 deletion examples/hello_world.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ def main():


class ClientHandler:
"""Client handler."""

def OnBeforeClose(self, browser):
"""Called just before a browser is destroyed."""
if not browser.IsPopup():
# Exit app when main window is closed.
cef.QuitMessageLoop()


Expand Down
25 changes: 25 additions & 0 deletions src/cefpython.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,31 @@ def Shutdown():
# This one is probably redundant. Additional testing should be done.
Debug("Shutdown: releasing shared request context")
g_sharedRequestContext.Assign(NULL)

if len(g_pyBrowsers):
Error("Shutdown called, but there are still browser references alive,"
" will try to close them and free references.")
# There might be a case when python error occured after creating
# browser, but before any message loop was run. In such case
# the renderer process won't be terminated unless we run some
# message loop work here, try to close browser and free reference,
# and then run some message loop work again.
for i in range(10):
with nogil:
CefDoMessageLoopWork()
time.sleep(0.01)
browsers_list = []
for browserId in g_pyBrowsers:
browser = g_pyBrowsers[browserId]
browser.TryCloseBrowser()
browsers_list.append(browserId)
for browserId in browsers_list:
RemovePyBrowser(browserId)
for i in range(10):
with nogil:
CefDoMessageLoopWork()
time.sleep(0.01)

Debug("Shutdown()")
with nogil:
# Temporary fix for possible errors on shutdown. See this post:
Expand Down
17 changes: 9 additions & 8 deletions src/helpers.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ cpdef str GetModuleDirectory():

g_GetAppPath_dir = None

cpdef GetAppPath(file=None):
cpdef str GetAppPath(file=None):
"""Get application path."""
# On Windows after downloading file and calling Browser.GoForward(),
# current working directory is set to %UserProfile%.
Expand Down Expand Up @@ -58,12 +58,16 @@ cpdef GetAppPath(file=None):
return str(file)


cpdef ExceptHook(excType, excValue, traceObject):
cpdef py_void ExceptHook(excType, excValue, traceObject):
"""Global except hook to exit app cleanly on error."""
# This hook does the following: in case of exception write it to
# the "error.log" file, display it to the console, shutdown CEF
# and exit application immediately by ignoring "finally" (_exit()).
errorMsg = "\n".join(traceback.format_exception(excType, excValue,
print("[CEF Python] ExceptHook: catched exception, will shutdown CEF now")
QuitMessageLoop()
Shutdown()
print("[CEF Python] ExceptHook: see the catched exception below:")
errorMsg = "".join(traceback.format_exception(excType, excValue,
traceObject))
errorFile = GetAppPath("error.log")
try:
Expand All @@ -77,16 +81,13 @@ cpdef ExceptHook(excType, excValue, traceObject):
fp.write("\n[%s] %s\n" % (
time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg))
except:
print("[pygtk_.py]: WARNING: failed writing to error file: %s" % (
print("[CEF Python] WARNING: failed writing to error file: %s" % (
errorFile))
# Convert error message to ascii before printing, otherwise
# you may get error like this:
# | UnicodeEncodeError: 'charmap' codec can't encode characters
errorMsg = errorMsg.encode("ascii", errors="replace")
errorMsg = errorMsg.decode("ascii", errors="replace")
print("\n"+errorMsg+"\n")
QuitMessageLoop()
Shutdown()
print("\n"+errorMsg)
# noinspection PyProtectedMember
os._exit(1)

14 changes: 7 additions & 7 deletions src/linux/setup/cefpython.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* Generated by Cython 0.24.1 */

#ifndef __PYX_HAVE__cefpython_py34
#define __PYX_HAVE__cefpython_py34
#ifndef __PYX_HAVE__cefpython_py35
#define __PYX_HAVE__cefpython_py35


#ifndef __PYX_HAVE_API__cefpython_py34
#ifndef __PYX_HAVE_API__cefpython_py35

#ifndef __PYX_EXTERN_C
#ifdef __cplusplus
Expand Down Expand Up @@ -89,12 +89,12 @@ __PYX_EXTERN_C DL_IMPORT(bool) ApplicationSettings_GetBoolFromDict(char const *,
__PYX_EXTERN_C DL_IMPORT(std::string) ApplicationSettings_GetString(char const *);
__PYX_EXTERN_C DL_IMPORT(int) CommandLineSwitches_GetInt(char const *);

#endif /* !__PYX_HAVE_API__cefpython_py34 */
#endif /* !__PYX_HAVE_API__cefpython_py35 */

#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC initcefpython_py34(void);
PyMODINIT_FUNC initcefpython_py35(void);
#else
PyMODINIT_FUNC PyInit_cefpython_py34(void);
PyMODINIT_FUNC PyInit_cefpython_py35(void);
#endif

#endif /* !__PYX_HAVE__cefpython_py34 */
#endif /* !__PYX_HAVE__cefpython_py35 */
24 changes: 22 additions & 2 deletions src/utils.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,15 @@ cpdef py_bool IsThread(int threadID):
# unicode strings and writing them to file (codecs.open).
# This change is required to work with Cython 0.20.

cpdef object Debug(str msg):
cpdef object Debug(py_string msg):
if not g_debug:
return
msg = "[CEF Python] "+str(msg)
# In Python 3 str or bytes may be passed
if type(msg) != str and type(msg) == bytes:
msg = msg.decode("utf-8", "replace")
# Convert to str in case other kind of object was passed
msg = str(msg)
msg = "[CEF Python] "+msg
print(msg)
if g_debugFile:
try:
Expand All @@ -54,6 +59,21 @@ cpdef object Debug(str msg):
print("[CEF Python] WARNING: failed writing to debug file: %s" % (
g_debugFile))

cdef void Error(py_string msg) except *:
# In Python 3 str or bytes may be passed
if type(msg) != str and type(msg) == bytes:
msg = msg.decode("utf-8", "replace")
# Convert to str in case other kind of object was passed
msg = str(msg)
msg = "[CEF Python] ERROR: "+msg
print(msg)
if g_debugFile:
try:
with open(g_debugFile, "a") as file_:
file_.write(msg+"\n")
except:
print("[CEF Python] WARNING: failed writing to debug file: %s" % (
g_debugFile))

cpdef str GetSystemError():
IF UNAME_SYSNAME == "Windows":
Expand Down
Loading

0 comments on commit 80d7c5a

Please sign in to comment.