Blob Blame History Raw
From cec5c0d7768aceb04bf9df457351bc00141f48b6 Mon Sep 17 00:00:00 2001
From: Toshio Kuratomi <toshio@fedoraproject.org>
Date: Thu, 11 Apr 2013 15:21:06 -0700
Subject: [PATCH 1/3] Few changes to support running on python3

---
 q.py | 30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git a/q.py b/q.py
index c81a371..abe5b1e 100644
--- a/q.py
+++ b/q.py
@@ -70,11 +70,11 @@ class FileWriter(object):
 
         def __init__(self, path):
             self.path = path
-            self.open = file
+            self.open = open
             # App Engine's dev_appserver patches 'open' to simulate security
             # restrictions in production; we circumvent this to write output.
-            if file.__name__ == 'FakeFile':  # dev_appserver's patched 'file'
-                self.open = file.__bases__[0]  # the original built-in 'file'
+            if open.__name__ == 'FakeFile':  # dev_appserver's patched 'file'
+                self.open = open.__bases__[0]  # the original built-in 'file'
 
         def write(self, mode, content):
             try:
@@ -130,7 +130,7 @@ def newline(self):
 
         def add(self, items, sep='', wrap=True):
             """Adds a list of strings that are to be printed on one line."""
-            items = map(str, items)
+            items = list(map(str, items))
             size = sum([len(x) for x in items if not x.startswith('\x1b')])
             if (wrap and self.column > self.indent and
                 self.column + len(sep) + size > self.width):
@@ -155,17 +155,29 @@ def unindent(self, lines):
         indent = min(len(self.re.match(r'^ *', line).group()) for line in lines)
         return [line[indent:].rstrip() for line in lines]
 
+    def _isbasestring(self, value):
+        if self.sys.version_info >= (3,):
+            return isinstance(value, (str, bytes))
+        else:
+            return isinstance(value, basestring)
+
+    def _istext(self, value):
+        if self.sys.version_info >= (3,):
+            return isinstance(value, str)
+        else:
+            return isinstance(value, unicode)
+
     def safe_repr(self, value):
         # TODO: Use colour to distinguish '...' elision from actual '...' chars.
         # TODO: Show a nicer repr for SRE.Match objects.
         # TODO: Show a nicer repr for big multiline strings.
         result = self.TEXT_REPR.repr(value)
-        if isinstance(value, basestring) and len(value) > 80:
+        if self._isbasestring(value) and len(value) > 80:
             # If the string is big, save it to a file for later examination.
-            if isinstance(value, unicode):
+            if self._istext(value):
                 value = value.encode('utf-8')
             path = self.OUTPUT_PATH + '%08d.txt' % self.random.randrange(1e8)
-            self.FileWriter(path).write('w', value)
+            self.FileWriter(path).write('wb', value)
             result += ' (file://' + path + ')'
         return result
 
@@ -284,11 +296,13 @@ def __call__(self, *args):
         self.show(info.function, args, labels)
         return args and args[0]
 
-    def __div__(self, arg):  # a tight-binding operator
+    def __truediv__(self, arg):  # a tight-binding operator
         """Prints out and returns the argument."""
         info = self.inspect.getframeinfo(self.sys._getframe(1))
         self.show(info.function, [arg])
         return arg
+    # Compat for Python 2 without from future import __division__ turned on
+    __div__ = __truediv__
 
     __or__ = __div__  # a loose-binding operator
     q = __call__  # backward compatibility with @q.q
-- 
1.8.1.6


From d809647541025ac79b54ed6079272c11afd2daf0 Mon Sep 17 00:00:00 2001
From: Toshio Kuratomi <toshio@fedoraproject.org>
Date: Thu, 11 Apr 2013 17:16:23 -0700
Subject: [PATCH 2/3] Encode to utf-8 at the FileWriter level so that we catch
 all undecodable strings

---
 q.py | 35 ++++++++++++++++++++---------------
 1 file changed, 20 insertions(+), 15 deletions(-)

diff --git a/q.py b/q.py
index abe5b1e..3a99d2b 100644
--- a/q.py
+++ b/q.py
@@ -67,6 +67,7 @@ class Q(object):
 
     class FileWriter(object):
         """An object that appends to or overwrites a single file."""
+        import sys
 
         def __init__(self, path):
             self.path = path
@@ -76,7 +77,23 @@ def __init__(self, path):
             if open.__name__ == 'FakeFile':  # dev_appserver's patched 'file'
                 self.open = open.__bases__[0]  # the original built-in 'file'
 
+        def _isbasestring(self, value):
+            if self.sys.version_info >= (3,):
+                return isinstance(value, (str, bytes))
+            else:
+                return isinstance(value, basestring)
+
+        def _istext(self, value):
+            if self.sys.version_info >= (3,):
+                return isinstance(value, str)
+            else:
+                return isinstance(value, unicode)
+
         def write(self, mode, content):
+            if 'b' not in mode:
+                mode = '%sb' % mode
+            if self._isbasestring(content) and self._istext(content):
+                content = content.encode('utf-8')
             try:
                 f = self.open(self.path, mode)
                 f.write(content)
@@ -155,29 +172,17 @@ def unindent(self, lines):
         indent = min(len(self.re.match(r'^ *', line).group()) for line in lines)
         return [line[indent:].rstrip() for line in lines]
 
-    def _isbasestring(self, value):
-        if self.sys.version_info >= (3,):
-            return isinstance(value, (str, bytes))
-        else:
-            return isinstance(value, basestring)
-
-    def _istext(self, value):
-        if self.sys.version_info >= (3,):
-            return isinstance(value, str)
-        else:
-            return isinstance(value, unicode)
-
     def safe_repr(self, value):
         # TODO: Use colour to distinguish '...' elision from actual '...' chars.
         # TODO: Show a nicer repr for SRE.Match objects.
         # TODO: Show a nicer repr for big multiline strings.
         result = self.TEXT_REPR.repr(value)
-        if self._isbasestring(value) and len(value) > 80:
+        if self.writer.file_writer._isbasestring(value) and len(value) > 80:
             # If the string is big, save it to a file for later examination.
-            if self._istext(value):
+            if self.writer.file_writer._istext(value):
                 value = value.encode('utf-8')
             path = self.OUTPUT_PATH + '%08d.txt' % self.random.randrange(1e8)
-            self.FileWriter(path).write('wb', value)
+            self.FileWriter(path).write('w', value)
             result += ' (file://' + path + ')'
         return result
 
-- 
1.8.1.6


From 20cf7bcd4587a603497781bd1cfa140b58950e27 Mon Sep 17 00:00:00 2001
From: Toshio Kuratomi <toshio@fedoraproject.org>
Date: Mon, 6 May 2013 09:21:14 -0700
Subject: [PATCH 3/3] Switch from functions to variables to encapsulate
 python2/3 differences

---
 q.py | 36 ++++++++++++++++++++----------------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/q.py b/q.py
index 3a99d2b..9136313 100644
--- a/q.py
+++ b/q.py
@@ -44,6 +44,8 @@
 
 __author__ = 'Ka-Ping Yee <ping@zesty.ca>'
 
+import sys
+
 # WARNING: Horrible abuse of sys.modules, __call__, __div__, __or__, inspect,
 # sys._getframe, and more!  q's behaviour changes depending on the text of the
 # source code near its call site.  Don't ever do this in real code!
@@ -51,6 +53,14 @@
 # These are reused below in both Q and Writer.
 ESCAPE_SEQUENCES = ['\x1b[0m'] + ['\x1b[3%dm' % i for i in range(1, 7)]
 
+if sys.version_info >= (3,):
+    BASESTRING_TYPES = (str, bytes)
+    TEXT_TYPES = (str,)
+else:
+    BASESTRING_TYPES = (basestring,)
+    TEXT_TYPES = (unicode,)
+
+
 # When we insert Q() into sys.modules, all the globals become None, so we
 # have to keep everything we use inside the Q class.
 class Q(object):
@@ -65,9 +75,16 @@ class Q(object):
     NORMAL, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN = ESCAPE_SEQUENCES
     TEXT_REPR = pydoc.TextRepr()
 
+    # For portably converting strings between python2 and python3
+    BASESTRING_TYPES = BASESTRING_TYPES
+    TEXT_TYPES = TEXT_TYPES
+
     class FileWriter(object):
         """An object that appends to or overwrites a single file."""
         import sys
+        # For portably converting strings between python2 and python3
+        BASESTRING_TYPES = BASESTRING_TYPES
+        TEXT_TYPES = TEXT_TYPES
 
         def __init__(self, path):
             self.path = path
@@ -77,22 +94,10 @@ def __init__(self, path):
             if open.__name__ == 'FakeFile':  # dev_appserver's patched 'file'
                 self.open = open.__bases__[0]  # the original built-in 'file'
 
-        def _isbasestring(self, value):
-            if self.sys.version_info >= (3,):
-                return isinstance(value, (str, bytes))
-            else:
-                return isinstance(value, basestring)
-
-        def _istext(self, value):
-            if self.sys.version_info >= (3,):
-                return isinstance(value, str)
-            else:
-                return isinstance(value, unicode)
-
         def write(self, mode, content):
             if 'b' not in mode:
                 mode = '%sb' % mode
-            if self._isbasestring(content) and self._istext(content):
+            if isinstance(content, self.BASESTRING_TYPES) and isinstance(content, self.TEXT_TYPES):
                 content = content.encode('utf-8')
             try:
                 f = self.open(self.path, mode)
@@ -177,9 +182,9 @@ def safe_repr(self, value):
         # TODO: Show a nicer repr for SRE.Match objects.
         # TODO: Show a nicer repr for big multiline strings.
         result = self.TEXT_REPR.repr(value)
-        if self.writer.file_writer._isbasestring(value) and len(value) > 80:
+        if isinstance(value, self.BASESTRING_TYPES) and len(value) > 80:
             # If the string is big, save it to a file for later examination.
-            if self.writer.file_writer._istext(value):
+            if isinstance(value,  self.TEXT_TYPES):
                 value = value.encode('utf-8')
             path = self.OUTPUT_PATH + '%08d.txt' % self.random.randrange(1e8)
             self.FileWriter(path).write('w', value)
@@ -339,5 +344,4 @@ def d(self, depth=1):
 
 
 # Install the Q() object in sys.modules so that "import q" gives a callable q.
-import sys
 sys.modules['q'] = Q()
-- 
1.8.1.6