Repository: web2py/web2py

Issue:
MARKMIN(T(...)) no longer works, at least in templates
Py27
This doesn't work for me in templates (was Ok in version from 2016):

{{=MARKMIN(T("**something bold** something more"))}}
This works:
{{=T("**something** something")}}

Error for the 1st case is 
  File "/home/www-data/web2py/gluon/restricted.py", line 219, in restricted
    exec(ccode, environment)
  File "<string>", line 113, in <module>
  File "/home/www-data/web2py/gluon/html.py", line 2791, in __init__
    self.text = to_bytes(text)
  File "/home/www-data/web2py/gluon/_compat.py", line 68, in to_bytes
    raise TypeError('Expected bytes')
TypeError: Expected bytes

---------------
It will work if we add .....+"" into 1st line of MARKMIN constructor (gluon/html.py),
however I don't believe this is a good fix.
So I don't try repair it (create a pull request).

class MARKMIN(XmlComponent):
    """
    For documentation: http://web2py.com/examples/static/markmin.html
    """
    def __init__(self,
                 text, extra=None, allowed=None, sep='p',
                 url=None, environment=None, latex='google',
                 autolinks='default',
                 protolinks='default',
                 class_prefix='',
                 id_prefix='markmin_',
                 **kwargs):
        self.text = to_bytes(text + '')  # hotfix 2.15.3: + ''



Discussion:

Resolving patch:
diff --git a/gluon/_compat.py b/gluon/_compat.py
--- a/gluon/_compat.py
+++ b/gluon/_compat.py
@@ -63,7 +63,7 @@ def to_bytes(obj, charset='utf-8', errors='strict'):
             return None
         if isinstance(obj, (bytes, bytearray, buffer)):
             return bytes(obj)
-        if isinstance(obj, unicode):
+        if hasattr(obj, 'encode'):
             return obj.encode(charset, errors)
         raise TypeError('Expected bytes')
 
@@ -122,7 +122,7 @@ def to_bytes(obj, charset='utf-8', errors='strict'):
             return None
         if isinstance(obj, (bytes, bytearray, memoryview)):
             return bytes(obj)
-        if isinstance(obj, str):
+        if hasattr(obj, 'encode'):
             return obj.encode(charset, errors)
         raise TypeError('Expected bytes')
 
@@ -151,7 +151,7 @@ def __new__(cls, name, this_bases, d):
 def to_unicode(obj, charset='utf-8', errors='strict'):
     if obj is None:
         return None
-    if not isinstance(obj, bytes):
+    if not hasattr(obj, 'decode'):
         return text_type(obj)
     return obj.decode(charset, errors)
 
diff --git a/gluon/languages.py b/gluon/languages.py
--- a/gluon/languages.py
+++ b/gluon/languages.py
@@ -18,10 +18,11 @@
 import logging
 from cgi import escape
 from threading import RLock
-from gluon.utf8 import Utf8
+
 from gluon.utils import local_html_escape
 
 from gluon._compat import copyreg, PY2, maketrans, iterkeys, unicodeT, to_unicode, to_bytes, iteritems, to_native, pjoin
+
 from pydal.contrib.portalocker import read_locked, LockedFile
 
 from gluon.fileutils import listdir
@@ -49,8 +50,10 @@
 
 if PY2:
     NUMBERS = (int, long, float)
+    from gluon.utf8 import Utf8
 else:
     NUMBERS = (int, float)
+    Utf8 = str
 
 # pattern to find T(blah blah blah) expressions
 PY_STRING_LITERAL_RE = r'(?<=[^\w]T\()(?P<name>'\


Test patch: