All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version)
@ 2016-06-01 12:36 Richard Purdie
  2016-06-01 12:36 ` [PATCH 02/28] bitbake/pyinotify.py: Upgrade to py3 version Richard Purdie
                   ` (26 more replies)
  0 siblings, 27 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

Upgrade to 4.4.1 which has been run through 2to3 as per the maintainers
recommendation for v3 use.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/bs4/__init__.py                    | 112 ++++++++---
 lib/bs4/builder/__init__.py            |   7 +-
 lib/bs4/builder/_html5lib.py           |  71 +++++--
 lib/bs4/builder/_htmlparser.py         |  56 +++---
 lib/bs4/builder/_lxml.py               |  47 +++--
 lib/bs4/dammit.py                      |  31 ++-
 lib/bs4/diagnose.py                    |  68 ++++---
 lib/bs4/element.py                     | 346 ++++++++++++++++++++++-----------
 lib/bs4/testing.py                     | 129 ++++++++++--
 lib/bs4/tests/test_builder_registry.py |  14 +-
 lib/bs4/tests/test_html5lib.py         |  19 +-
 lib/bs4/tests/test_htmlparser.py       |  13 ++
 lib/bs4/tests/test_lxml.py             |  19 +-
 lib/bs4/tests/test_soup.py             | 107 +++++++---
 lib/bs4/tests/test_tree.py             | 294 ++++++++++++++++++++++------
 15 files changed, 972 insertions(+), 361 deletions(-)

diff --git a/lib/bs4/__init__.py b/lib/bs4/__init__.py
index 7ba3426..f6fdfd5 100644
--- a/lib/bs4/__init__.py
+++ b/lib/bs4/__init__.py
@@ -17,8 +17,8 @@ http://www.crummy.com/software/BeautifulSoup/bs4/doc/
 """
 
 __author__ = "Leonard Richardson (leonardr@segfault.org)"
-__version__ = "4.3.2"
-__copyright__ = "Copyright (c) 2004-2013 Leonard Richardson"
+__version__ = "4.4.1"
+__copyright__ = "Copyright (c) 2004-2015 Leonard Richardson"
 __license__ = "MIT"
 
 __all__ = ['BeautifulSoup']
@@ -45,7 +45,7 @@ from .element import (
 
 # The very first thing we do is give a useful error if someone is
 # running this code under Python 3 without converting it.
-syntax_error = u'You are trying to run the Python 2 version of Beautiful Soup under Python 3. This will not work. You need to convert the code, either by installing it (`python setup.py install`) or by running 2to3 (`2to3 -w bs4`).'
+'You are trying to run the Python 2 version of Beautiful Soup under Python 3. This will not work.'!='You need to convert the code, either by installing it (`python setup.py install`) or by running 2to3 (`2to3 -w bs4`).'
 
 class BeautifulSoup(Tag):
     """
@@ -69,7 +69,7 @@ class BeautifulSoup(Tag):
     like HTML's <br> tag), call handle_starttag and then
     handle_endtag.
     """
-    ROOT_TAG_NAME = u'[document]'
+    ROOT_TAG_NAME = '[document]'
 
     # If the end-user gives no indication which tree builder they
     # want, look for one with these features.
@@ -77,8 +77,11 @@ class BeautifulSoup(Tag):
 
     ASCII_SPACES = '\x20\x0a\x09\x0c\x0d'
 
+    NO_PARSER_SPECIFIED_WARNING = "No parser was explicitly specified, so I'm using the best available %(markup_type)s parser for this system (\"%(parser)s\"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.\n\nTo get rid of this warning, change this:\n\n BeautifulSoup([your markup])\n\nto this:\n\n BeautifulSoup([your markup], \"%(parser)s\")\n"
+
     def __init__(self, markup="", features=None, builder=None,
-                 parse_only=None, from_encoding=None, **kwargs):
+                 parse_only=None, from_encoding=None, exclude_encodings=None,
+                 **kwargs):
         """The Soup object is initialized as the 'root tag', and the
         provided markup (which can be a string or a file-like object)
         is fed into the underlying parser."""
@@ -114,9 +117,9 @@ class BeautifulSoup(Tag):
             del kwargs['isHTML']
             warnings.warn(
                 "BS4 does not respect the isHTML argument to the "
-                "BeautifulSoup constructor. You can pass in features='html' "
-                "or features='xml' to get a builder capable of handling "
-                "one or the other.")
+                "BeautifulSoup constructor. Suggest you use "
+                "features='lxml' for HTML and features='lxml-xml' for "
+                "XML.")
 
         def deprecated_argument(old_name, new_name):
             if old_name in kwargs:
@@ -135,12 +138,13 @@ class BeautifulSoup(Tag):
             "fromEncoding", "from_encoding")
 
         if len(kwargs) > 0:
-            arg = kwargs.keys().pop()
+            arg = list(kwargs.keys()).pop()
             raise TypeError(
                 "__init__() got an unexpected keyword argument '%s'" % arg)
 
         if builder is None:
-            if isinstance(features, basestring):
+            original_features = features
+            if isinstance(features, str):
                 features = [features]
             if features is None or len(features) == 0:
                 features = self.DEFAULT_BUILDER_FEATURES
@@ -151,6 +155,16 @@ class BeautifulSoup(Tag):
                     "requested: %s. Do you need to install a parser library?"
                     % ",".join(features))
             builder = builder_class()
+            if not (original_features == builder.NAME or
+                    original_features in builder.ALTERNATE_NAMES):
+                if builder.is_xml:
+                    markup_type = "XML"
+                else:
+                    markup_type = "HTML"
+                warnings.warn(self.NO_PARSER_SPECIFIED_WARNING % dict(
+                    parser=builder.NAME,
+                    markup_type=markup_type))
+
         self.builder = builder
         self.is_xml = builder.is_xml
         self.builder.soup = self
@@ -164,7 +178,7 @@ class BeautifulSoup(Tag):
             # involving passing non-markup to Beautiful Soup.
             # Beautiful Soup will still parse the input as markup,
             # just in case that's what the user really wants.
-            if (isinstance(markup, unicode)
+            if (isinstance(markup, str)
                 and not os.path.supports_unicode_filenames):
                 possible_filename = markup.encode("utf8")
             else:
@@ -172,25 +186,30 @@ class BeautifulSoup(Tag):
             is_file = False
             try:
                 is_file = os.path.exists(possible_filename)
-            except Exception, e:
+            except Exception as e:
                 # This is almost certainly a problem involving
                 # characters not valid in filenames on this
                 # system. Just let it go.
                 pass
             if is_file:
+                if isinstance(markup, str):
+                    markup = markup.encode("utf8")
                 warnings.warn(
                     '"%s" looks like a filename, not markup. You should probably open this file and pass the filehandle into Beautiful Soup.' % markup)
             if markup[:5] == "http:" or markup[:6] == "https:":
                 # TODO: This is ugly but I couldn't get it to work in
                 # Python 3 otherwise.
                 if ((isinstance(markup, bytes) and not b' ' in markup)
-                    or (isinstance(markup, unicode) and not u' ' in markup)):
+                    or (isinstance(markup, str) and not ' ' in markup)):
+                    if isinstance(markup, str):
+                        markup = markup.encode("utf8")
                     warnings.warn(
                         '"%s" looks like a URL. Beautiful Soup is not an HTTP client. You should probably use an HTTP client to get the document behind the URL, and feed that document to Beautiful Soup.' % markup)
 
         for (self.markup, self.original_encoding, self.declared_html_encoding,
          self.contains_replacement_characters) in (
-            self.builder.prepare_markup(markup, from_encoding)):
+             self.builder.prepare_markup(
+                 markup, from_encoding, exclude_encodings=exclude_encodings)):
             self.reset()
             try:
                 self._feed()
@@ -203,6 +222,16 @@ class BeautifulSoup(Tag):
         self.markup = None
         self.builder.soup = None
 
+    def __copy__(self):
+        return type(self)(self.encode(), builder=self.builder)
+
+    def __getstate__(self):
+        # Frequently a tree builder can't be pickled.
+        d = dict(self.__dict__)
+        if 'builder' in d and not self.builder.picklable:
+            del d['builder']
+        return d
+
     def _feed(self):
         # Convert the document to Unicode.
         self.builder.reset()
@@ -229,9 +258,7 @@ class BeautifulSoup(Tag):
 
     def new_string(self, s, subclass=NavigableString):
         """Create a new NavigableString associated with this soup."""
-        navigable = subclass(s)
-        navigable.setup()
-        return navigable
+        return subclass(s)
 
     def insert_before(self, successor):
         raise NotImplementedError("BeautifulSoup objects don't support insert_before().")
@@ -259,7 +286,7 @@ class BeautifulSoup(Tag):
 
     def endData(self, containerClass=NavigableString):
         if self.current_data:
-            current_data = u''.join(self.current_data)
+            current_data = ''.join(self.current_data)
             # If whitespace is not preserved, and this string contains
             # nothing but ASCII spaces, replace it with a single space
             # or newline.
@@ -290,14 +317,49 @@ class BeautifulSoup(Tag):
     def object_was_parsed(self, o, parent=None, most_recent_element=None):
         """Add an object to the parse tree."""
         parent = parent or self.currentTag
-        most_recent_element = most_recent_element or self._most_recent_element
-        o.setup(parent, most_recent_element)
+        previous_element = most_recent_element or self._most_recent_element
+
+        next_element = previous_sibling = next_sibling = None
+        if isinstance(o, Tag):
+            next_element = o.next_element
+            next_sibling = o.next_sibling
+            previous_sibling = o.previous_sibling
+            if not previous_element:
+                previous_element = o.previous_element
+
+        o.setup(parent, previous_element, next_element, previous_sibling, next_sibling)
 
-        if most_recent_element is not None:
-            most_recent_element.next_element = o
         self._most_recent_element = o
         parent.contents.append(o)
 
+        if parent.next_sibling:
+            # This node is being inserted into an element that has
+            # already been parsed. Deal with any dangling references.
+            index = parent.contents.index(o)
+            if index == 0:
+                previous_element = parent
+                previous_sibling = None
+            else:
+                previous_element = previous_sibling = parent.contents[index-1]
+            if index == len(parent.contents)-1:
+                next_element = parent.next_sibling
+                next_sibling = None
+            else:
+                next_element = next_sibling = parent.contents[index+1]
+
+            o.previous_element = previous_element
+            if previous_element:
+                previous_element.next_element = o
+            o.next_element = next_element
+            if next_element:
+                next_element.previous_element = o
+            o.next_sibling = next_sibling
+            if next_sibling:
+                next_sibling.previous_sibling = o
+            o.previous_sibling = previous_sibling
+            if previous_sibling:
+                previous_sibling.next_sibling = o
+
     def _popToTag(self, name, nsprefix=None, inclusivePop=True):
         """Pops the tag stack up to and including the most recent
         instance of the given tag. If inclusivePop is false, pops the tag
@@ -367,9 +429,9 @@ class BeautifulSoup(Tag):
             encoding_part = ''
             if eventual_encoding != None:
                 encoding_part = ' encoding="%s"' % eventual_encoding
-            prefix = u'<?xml version="1.0"%s?>\n' % encoding_part
+            prefix = '<?xml version="1.0"%s?>\n' % encoding_part
         else:
-            prefix = u''
+            prefix = ''
         if not pretty_print:
             indent_level = None
         else:
@@ -403,4 +465,4 @@ class FeatureNotFound(ValueError):
 if __name__ == '__main__':
     import sys
     soup = BeautifulSoup(sys.stdin)
-    print soup.prettify()
+    print(soup.prettify())
diff --git a/lib/bs4/builder/__init__.py b/lib/bs4/builder/__init__.py
index 740f5f2..6ccd4d2 100644
--- a/lib/bs4/builder/__init__.py
+++ b/lib/bs4/builder/__init__.py
@@ -80,9 +80,12 @@ builder_registry = TreeBuilderRegistry()
 class TreeBuilder(object):
     """Turn a document into a Beautiful Soup object tree."""
 
+    NAME = "[Unknown tree builder]"
+    ALTERNATE_NAMES = []
     features = []
 
     is_xml = False
+    picklable = False
     preserve_whitespace_tags = set()
     empty_element_tags = None # A tag will be considered an empty-element
                               # tag when and only when it has no contents.
@@ -153,13 +156,13 @@ class TreeBuilder(object):
             universal = self.cdata_list_attributes.get('*', [])
             tag_specific = self.cdata_list_attributes.get(
                 tag_name.lower(), None)
-            for attr in attrs.keys():
+            for attr in list(attrs.keys()):
                 if attr in universal or (tag_specific and attr in tag_specific):
                     # We have a "class"-type attribute whose string
                     # value is a whitespace-separated list of
                     # values. Split it into a list.
                     value = attrs[attr]
-                    if isinstance(value, basestring):
+                    if isinstance(value, str):
                         values = whitespace_re.split(value)
                     else:
                         # html5lib sometimes calls setAttributes twice
diff --git a/lib/bs4/builder/_html5lib.py b/lib/bs4/builder/_html5lib.py
index 7de36ae..f0e5924 100644
--- a/lib/bs4/builder/_html5lib.py
+++ b/lib/bs4/builder/_html5lib.py
@@ -2,6 +2,7 @@ __all__ = [
     'HTML5TreeBuilder',
     ]
 
+from pdb import set_trace
 import warnings
 from bs4.builder import (
     PERMISSIVE,
@@ -9,7 +10,10 @@ from bs4.builder import (
     HTML_5,
     HTMLTreeBuilder,
     )
-from bs4.element import NamespacedAttribute
+from bs4.element import (
+    NamespacedAttribute,
+    whitespace_re,
+)
 import html5lib
 from html5lib.constants import namespaces
 from bs4.element import (
@@ -22,11 +26,20 @@ from bs4.element import (
 class HTML5TreeBuilder(HTMLTreeBuilder):
     """Use html5lib to build a tree."""
 
-    features = ['html5lib', PERMISSIVE, HTML_5, HTML]
+    NAME = "html5lib"
+
+    features = [NAME, PERMISSIVE, HTML_5, HTML]
 
-    def prepare_markup(self, markup, user_specified_encoding):
+    def prepare_markup(self, markup, user_specified_encoding,
+                       document_declared_encoding=None, exclude_encodings=None):
         # Store the user-specified encoding for use later on.
         self.user_specified_encoding = user_specified_encoding
+
+        # document_declared_encoding and exclude_encodings aren't used
+        # ATM because the html5lib TreeBuilder doesn't use
+        # UnicodeDammit.
+        if exclude_encodings:
+            warnings.warn("You provided a value for exclude_encoding, but the html5lib tree builder doesn't support exclude_encoding.")
         yield (markup, None, None, False)
 
     # These methods are defined by Beautiful Soup.
@@ -37,7 +50,7 @@ class HTML5TreeBuilder(HTMLTreeBuilder):
         doc = parser.parse(markup, encoding=self.user_specified_encoding)
 
         # Set the character encoding detected by the tokenizer.
-        if isinstance(markup, unicode):
+        if isinstance(markup, str):
             # We need to special-case this because html5lib sets
             # charEncoding to UTF-8 if it gets Unicode input.
             doc.original_encoding = None
@@ -51,7 +64,7 @@ class HTML5TreeBuilder(HTMLTreeBuilder):
 
     def test_fragment_to_document(self, fragment):
         """See `TreeBuilder`."""
-        return u'<html><head></head><body>%s</body></html>' % fragment
+        return '<html><head></head><body>%s</body></html>' % fragment
 
 
 class TreeBuilderForHtml5lib(html5lib.treebuilders._base.TreeBuilder):
@@ -101,7 +114,16 @@ class AttrList(object):
     def __iter__(self):
         return list(self.attrs.items()).__iter__()
     def __setitem__(self, name, value):
-        "set attr", name, value
+        # If this attribute is a multi-valued attribute for this element,
+        # turn its value into a list.
+        list_attr = HTML5TreeBuilder.cdata_list_attributes
+        if (name in list_attr['*']
+            or (self.element.name in list_attr
+                and name in list_attr[self.element.name])):
+            # A node that is being cloned may have already undergone
+            # this procedure.
+            if not isinstance(value, list):
+                value = whitespace_re.split(value)
         self.element[name] = value
     def items(self):
         return list(self.attrs.items())
@@ -124,7 +146,7 @@ class Element(html5lib.treebuilders._base.Node):
 
     def appendChild(self, node):
         string_child = child = None
-        if isinstance(node, basestring):
+        if isinstance(node, str):
             # Some other piece of code decided to pass in a string
             # instead of creating a TextElement object to contain the
             # string.
@@ -139,7 +161,7 @@ class Element(html5lib.treebuilders._base.Node):
         else:
             child = node.element
 
-        if not isinstance(child, basestring) and child.parent is not None:
+        if not isinstance(child, str) and child.parent is not None:
             node.element.extract()
 
         if (string_child and self.element.contents
@@ -152,7 +174,7 @@ class Element(html5lib.treebuilders._base.Node):
             old_element.replace_with(new_element)
             self.soup._most_recent_element = new_element
         else:
-            if isinstance(node, basestring):
+            if isinstance(node, str):
                 # Create a brand new NavigableString from this string.
                 child = self.soup.new_string(node)
 
@@ -161,6 +183,12 @@ class Element(html5lib.treebuilders._base.Node):
             # immediately after the parent, if it has no children.)
             if self.element.contents:
                 most_recent_element = self.element._last_descendant(False)
+            elif self.element.next_element is not None:
+                # Something from further ahead in the parse tree is
+                # being inserted into this earlier element. This is
+                # very annoying because it means an expensive search
+                # for the last element in the tree.
+                most_recent_element = self.soup._last_descendant()
             else:
                 most_recent_element = self.element
 
@@ -172,6 +200,7 @@ class Element(html5lib.treebuilders._base.Node):
         return AttrList(self.element)
 
     def setAttributes(self, attributes):
+
         if attributes is not None and len(attributes) > 0:
 
             converted_attributes = []
@@ -183,7 +212,7 @@ class Element(html5lib.treebuilders._base.Node):
 
             self.soup.builder._replace_cdata_list_attribute_values(
                 self.name, attributes)
-            for name, value in attributes.items():
+            for name, value in list(attributes.items()):
                 self.element[name] = value
 
             # The attributes may contain variables that need substitution.
@@ -218,6 +247,9 @@ class Element(html5lib.treebuilders._base.Node):
 
     def reparentChildren(self, new_parent):
         """Move all of this tag's children into another tag."""
+        # print "MOVE", self.element.contents
+        # print "FROM", self.element
+        # print "TO", new_parent.element
         element = self.element
         new_parent_element = new_parent.element
         # Determine what this tag's next_element will be once all the children
@@ -236,17 +268,28 @@ class Element(html5lib.treebuilders._base.Node):
             new_parents_last_descendant_next_element = new_parent_element.next_element
 
         to_append = element.contents
-        append_after = new_parent.element.contents
+        append_after = new_parent_element.contents
         if len(to_append) > 0:
             # Set the first child's previous_element and previous_sibling
             # to elements within the new parent
             first_child = to_append[0]
-            first_child.previous_element = new_parents_last_descendant
+            if new_parents_last_descendant:
+                first_child.previous_element = new_parents_last_descendant
+            else:
+                first_child.previous_element = new_parent_element
             first_child.previous_sibling = new_parents_last_child
+            if new_parents_last_descendant:
+                new_parents_last_descendant.next_element = first_child
+            else:
+                new_parent_element.next_element = first_child
+            if new_parents_last_child:
+                new_parents_last_child.next_sibling = first_child
 
             # Fix the last child's next_element and next_sibling
             last_child = to_append[-1]
             last_child.next_element = new_parents_last_descendant_next_element
+            if new_parents_last_descendant_next_element:
+                new_parents_last_descendant_next_element.previous_element = last_child
             last_child.next_sibling = None
 
         for child in to_append:
@@ -257,6 +300,10 @@ class Element(html5lib.treebuilders._base.Node):
         element.contents = []
         element.next_element = final_next_element
 
+        # print "DONE WITH MOVE"
+        # print "FROM", self.element
+        # print "TO", new_parent_element
+
     def cloneNode(self):
         tag = self.soup.new_tag(self.element.name, self.namespace)
         node = Element(tag, self.soup, self.namespace)
diff --git a/lib/bs4/builder/_htmlparser.py b/lib/bs4/builder/_htmlparser.py
index ca8d8b8..bb0a63f 100644
--- a/lib/bs4/builder/_htmlparser.py
+++ b/lib/bs4/builder/_htmlparser.py
@@ -4,10 +4,16 @@ __all__ = [
     'HTMLParserTreeBuilder',
     ]
 
-from HTMLParser import (
-    HTMLParser,
-    HTMLParseError,
-    )
+from html.parser import HTMLParser
+
+try:
+    from html.parser import HTMLParseError
+except ImportError as e:
+    # HTMLParseError is removed in Python 3.5. Since it can never be
+    # thrown in 3.5, we can just define our own class as a placeholder.
+    class HTMLParseError(Exception):
+        pass
+
 import sys
 import warnings
 
@@ -19,10 +25,10 @@ import warnings
 # At the end of this file, we monkeypatch HTMLParser so that
 # strict=True works well on Python 3.2.2.
 major, minor, release = sys.version_info[:3]
-CONSTRUCTOR_TAKES_STRICT = (
-    major > 3
-    or (major == 3 and minor > 2)
-    or (major == 3 and minor == 2 and release >= 3))
+CONSTRUCTOR_TAKES_STRICT = major == 3 and minor == 2 and release >= 3
+CONSTRUCTOR_STRICT_IS_DEPRECATED = major == 3 and minor == 3
+CONSTRUCTOR_TAKES_CONVERT_CHARREFS = major == 3 and minor >= 4
+
 
 from bs4.element import (
     CData,
@@ -63,7 +69,8 @@ class BeautifulSoupHTMLParser(HTMLParser):
 
     def handle_charref(self, name):
         # XXX workaround for a bug in HTMLParser. Remove this once
-        # it's fixed.
+        # it's fixed in all supported versions.
+        # http://bugs.python.org/issue13633
         if name.startswith('x'):
             real_name = int(name.lstrip('x'), 16)
         elif name.startswith('X'):
@@ -72,9 +79,9 @@ class BeautifulSoupHTMLParser(HTMLParser):
             real_name = int(name)
 
         try:
-            data = unichr(real_name)
-        except (ValueError, OverflowError), e:
-            data = u"\N{REPLACEMENT CHARACTER}"
+            data = chr(real_name)
+        except (ValueError, OverflowError) as e:
+            data = "\N{REPLACEMENT CHARACTER}"
 
         self.handle_data(data)
 
@@ -113,14 +120,6 @@ class BeautifulSoupHTMLParser(HTMLParser):
 
     def handle_pi(self, data):
         self.soup.endData()
-        if data.endswith("?") and data.lower().startswith("xml"):
-            # "An XHTML processing instruction using the trailing '?'
-            # will cause the '?' to be included in data." - HTMLParser
-            # docs.
-            #
-            # Strip the question mark so we don't end up with two
-            # question marks.
-            data = data[:-1]
         self.soup.handle_data(data)
         self.soup.endData(ProcessingInstruction)
 
@@ -128,26 +127,31 @@ class BeautifulSoupHTMLParser(HTMLParser):
 class HTMLParserTreeBuilder(HTMLTreeBuilder):
 
     is_xml = False
-    features = [HTML, STRICT, HTMLPARSER]
+    picklable = True
+    NAME = HTMLPARSER
+    features = [NAME, HTML, STRICT]
 
     def __init__(self, *args, **kwargs):
-        if CONSTRUCTOR_TAKES_STRICT:
+        if CONSTRUCTOR_TAKES_STRICT and not CONSTRUCTOR_STRICT_IS_DEPRECATED:
             kwargs['strict'] = False
+        if CONSTRUCTOR_TAKES_CONVERT_CHARREFS:
+            kwargs['convert_charrefs'] = False
         self.parser_args = (args, kwargs)
 
     def prepare_markup(self, markup, user_specified_encoding=None,
-                       document_declared_encoding=None):
+                       document_declared_encoding=None, exclude_encodings=None):
         """
         :return: A 4-tuple (markup, original encoding, encoding
         declared within markup, whether any characters had to be
         replaced with REPLACEMENT CHARACTER).
         """
-        if isinstance(markup, unicode):
+        if isinstance(markup, str):
             yield (markup, None, None, False)
             return
 
         try_encodings = [user_specified_encoding, document_declared_encoding]
-        dammit = UnicodeDammit(markup, try_encodings, is_html=True)
+        dammit = UnicodeDammit(markup, try_encodings, is_html=True,
+                               exclude_encodings=exclude_encodings)
         yield (dammit.markup, dammit.original_encoding,
                dammit.declared_html_encoding,
                dammit.contains_replacement_characters)
@@ -158,7 +162,7 @@ class HTMLParserTreeBuilder(HTMLTreeBuilder):
         parser.soup = self.soup
         try:
             parser.feed(markup)
-        except HTMLParseError, e:
+        except HTMLParseError as e:
             warnings.warn(RuntimeWarning(
                 "Python's built-in HTMLParser cannot parse the given document. This is not a bug in Beautiful Soup. The best solution is to install an external parser (lxml or html5lib), and use Beautiful Soup with that parser. See http://www.crummy.com/software/BeautifulSoup/bs4/doc/#installing-a-parser for help."))
             raise e
diff --git a/lib/bs4/builder/_lxml.py b/lib/bs4/builder/_lxml.py
index fa5d498..9c6c14e 100644
--- a/lib/bs4/builder/_lxml.py
+++ b/lib/bs4/builder/_lxml.py
@@ -4,10 +4,15 @@ __all__ = [
     ]
 
 from io import BytesIO
-from StringIO import StringIO
+from io import StringIO
 import collections
 from lxml import etree
-from bs4.element import Comment, Doctype, NamespacedAttribute
+from bs4.element import (
+    Comment,
+    Doctype,
+    NamespacedAttribute,
+    ProcessingInstruction,
+)
 from bs4.builder import (
     FAST,
     HTML,
@@ -25,8 +30,11 @@ class LXMLTreeBuilderForXML(TreeBuilder):
 
     is_xml = True
 
+    NAME = "lxml-xml"
+    ALTERNATE_NAMES = ["xml"]
+
     # Well, it's permissive by XML parser standards.
-    features = [LXML, XML, FAST, PERMISSIVE]
+    features = [NAME, LXML, XML, FAST, PERMISSIVE]
 
     CHUNK_SIZE = 512
 
@@ -70,6 +78,7 @@ class LXMLTreeBuilderForXML(TreeBuilder):
             return (None, tag)
 
     def prepare_markup(self, markup, user_specified_encoding=None,
+                       exclude_encodings=None,
                        document_declared_encoding=None):
         """
         :yield: A series of 4-tuples.
@@ -78,12 +87,12 @@ class LXMLTreeBuilderForXML(TreeBuilder):
 
         Each 4-tuple represents a strategy for parsing the document.
         """
-        if isinstance(markup, unicode):
+        if isinstance(markup, str):
             # We were given Unicode. Maybe lxml can parse Unicode on
             # this system?
             yield markup, None, document_declared_encoding, False
 
-        if isinstance(markup, unicode):
+        if isinstance(markup, str):
             # No, apparently not. Convert the Unicode to UTF-8 and
             # tell lxml to parse it as UTF-8.
             yield (markup.encode("utf8"), "utf8",
@@ -95,14 +104,15 @@ class LXMLTreeBuilderForXML(TreeBuilder):
         # the document as each one in turn.
         is_html = not self.is_xml
         try_encodings = [user_specified_encoding, document_declared_encoding]
-        detector = EncodingDetector(markup, try_encodings, is_html)
+        detector = EncodingDetector(
+            markup, try_encodings, is_html, exclude_encodings)
         for encoding in detector.encodings:
             yield (detector.markup, encoding, document_declared_encoding, False)
 
     def feed(self, markup):
         if isinstance(markup, bytes):
             markup = BytesIO(markup)
-        elif isinstance(markup, unicode):
+        elif isinstance(markup, str):
             markup = StringIO(markup)
 
         # Call feed() at least once, even if the markup is empty,
@@ -117,7 +127,7 @@ class LXMLTreeBuilderForXML(TreeBuilder):
                 if len(data) != 0:
                     self.parser.feed(data)
             self.parser.close()
-        except (UnicodeDecodeError, LookupError, etree.ParserError), e:
+        except (UnicodeDecodeError, LookupError, etree.ParserError) as e:
             raise ParserRejectedMarkup(str(e))
 
     def close(self):
@@ -135,12 +145,12 @@ class LXMLTreeBuilderForXML(TreeBuilder):
             self.nsmaps.append(None)
         elif len(nsmap) > 0:
             # A new namespace mapping has come into play.
-            inverted_nsmap = dict((value, key) for key, value in nsmap.items())
+            inverted_nsmap = dict((value, key) for key, value in list(nsmap.items()))
             self.nsmaps.append(inverted_nsmap)
             # Also treat the namespace mapping as a set of attributes on the
             # tag, so we can recreate it later.
             attrs = attrs.copy()
-            for prefix, namespace in nsmap.items():
+            for prefix, namespace in list(nsmap.items()):
                 attribute = NamespacedAttribute(
                     "xmlns", prefix, "http://www.w3.org/2000/xmlns/")
                 attrs[attribute] = namespace
@@ -149,7 +159,7 @@ class LXMLTreeBuilderForXML(TreeBuilder):
         # from lxml with namespaces attached to their names, and
         # turn then into NamespacedAttribute objects.
         new_attrs = {}
-        for attr, value in attrs.items():
+        for attr, value in list(attrs.items()):
             namespace, attr = self._getNsTag(attr)
             if namespace is None:
                 new_attrs[attr] = value
@@ -189,7 +199,9 @@ class LXMLTreeBuilderForXML(TreeBuilder):
             self.nsmaps.pop()
 
     def pi(self, target, data):
-        pass
+        self.soup.endData()
+        self.soup.handle_data(target + ' ' + data)
+        self.soup.endData(ProcessingInstruction)
 
     def data(self, content):
         self.soup.handle_data(content)
@@ -207,12 +219,15 @@ class LXMLTreeBuilderForXML(TreeBuilder):
 
     def test_fragment_to_document(self, fragment):
         """See `TreeBuilder`."""
-        return u'<?xml version="1.0" encoding="utf-8"?>\n%s' % fragment
+        return '<?xml version="1.0" encoding="utf-8"?>\n%s' % fragment
 
 
 class LXMLTreeBuilder(HTMLTreeBuilder, LXMLTreeBuilderForXML):
 
-    features = [LXML, HTML, FAST, PERMISSIVE]
+    NAME = LXML
+    ALTERNATE_NAMES = ["lxml-html"]
+
+    features = ALTERNATE_NAMES + [NAME, HTML, FAST, PERMISSIVE]
     is_xml = False
 
     def default_parser(self, encoding):
@@ -224,10 +239,10 @@ class LXMLTreeBuilder(HTMLTreeBuilder, LXMLTreeBuilderForXML):
             self.parser = self.parser_for(encoding)
             self.parser.feed(markup)
             self.parser.close()
-        except (UnicodeDecodeError, LookupError, etree.ParserError), e:
+        except (UnicodeDecodeError, LookupError, etree.ParserError) as e:
             raise ParserRejectedMarkup(str(e))
 
 
     def test_fragment_to_document(self, fragment):
         """See `TreeBuilder`."""
-        return u'<html><body>%s</body></html>' % fragment
+        return '<html><body>%s</body></html>' % fragment
diff --git a/lib/bs4/dammit.py b/lib/bs4/dammit.py
index 59640b7..68d419f 100644
--- a/lib/bs4/dammit.py
+++ b/lib/bs4/dammit.py
@@ -3,12 +3,14 @@
 
 This library converts a bytestream to Unicode through any means
 necessary. It is heavily based on code from Mark Pilgrim's Universal
-Feed Parser. It works best on XML and XML, but it does not rewrite the
+Feed Parser. It works best on XML and HTML, but it does not rewrite the
 XML or HTML to reflect a new encoding; that's the tree builder's job.
 """
+__license__ = "MIT"
 
+from pdb import set_trace
 import codecs
-from htmlentitydefs import codepoint2name
+from html.entities import codepoint2name
 import re
 import logging
 import string
@@ -56,7 +58,7 @@ class EntitySubstitution(object):
         reverse_lookup = {}
         characters_for_re = []
         for codepoint, name in list(codepoint2name.items()):
-            character = unichr(codepoint)
+            character = chr(codepoint)
             if codepoint != 34:
                 # There's no point in turning the quotation mark into
                 # &quot;, unless it happens within an attribute value, which
@@ -212,8 +214,11 @@ class EncodingDetector:
 
     5. Windows-1252.
     """
-    def __init__(self, markup, override_encodings=None, is_html=False):
+    def __init__(self, markup, override_encodings=None, is_html=False,
+                 exclude_encodings=None):
         self.override_encodings = override_encodings or []
+        exclude_encodings = exclude_encodings or []
+        self.exclude_encodings = set([x.lower() for x in exclude_encodings])
         self.chardet_encoding = None
         self.is_html = is_html
         self.declared_encoding = None
@@ -224,6 +229,8 @@ class EncodingDetector:
     def _usable(self, encoding, tried):
         if encoding is not None:
             encoding = encoding.lower()
+            if encoding in self.exclude_encodings:
+                return False
             if encoding not in tried:
                 tried.add(encoding)
                 return True
@@ -266,6 +273,9 @@ class EncodingDetector:
     def strip_byte_order_mark(cls, data):
         """If a byte-order mark is present, strip it and return the encoding it implies."""
         encoding = None
+        if isinstance(data, str):
+            # Unicode data cannot have a byte-order mark.
+            return data, encoding
         if (len(data) >= 4) and (data[:2] == b'\xfe\xff') \
                and (data[2:4] != '\x00\x00'):
             encoding = 'utf-16be'
@@ -306,7 +316,7 @@ class EncodingDetector:
             declared_encoding_match = html_meta_re.search(markup, endpos=html_endpos)
         if declared_encoding_match is not None:
             declared_encoding = declared_encoding_match.groups()[0].decode(
-                'ascii')
+                'ascii', 'replace')
         if declared_encoding:
             return declared_encoding.lower()
         return None
@@ -331,18 +341,19 @@ class UnicodeDammit:
         ]
 
     def __init__(self, markup, override_encodings=[],
-                 smart_quotes_to=None, is_html=False):
+                 smart_quotes_to=None, is_html=False, exclude_encodings=[]):
         self.smart_quotes_to = smart_quotes_to
         self.tried_encodings = []
         self.contains_replacement_characters = False
         self.is_html = is_html
 
-        self.detector = EncodingDetector(markup, override_encodings, is_html)
+        self.detector = EncodingDetector(
+            markup, override_encodings, is_html, exclude_encodings)
 
         # Short-circuit if the data is in Unicode to begin with.
-        if isinstance(markup, unicode) or markup == '':
+        if isinstance(markup, str) or markup == '':
             self.markup = markup
-            self.unicode_markup = unicode(markup)
+            self.unicode_markup = str(markup)
             self.original_encoding = None
             return
 
@@ -425,7 +436,7 @@ class UnicodeDammit:
     def _to_unicode(self, data, encoding, errors="strict"):
         '''Given a string and its encoding, decodes the string into Unicode.
         %encoding is a string recognized by encodings.aliases'''
-        return unicode(data, encoding, errors)
+        return str(data, encoding, errors)
 
     @property
     def declared_html_encoding(self):
diff --git a/lib/bs4/diagnose.py b/lib/bs4/diagnose.py
index 4d0b00a..083395f 100644
--- a/lib/bs4/diagnose.py
+++ b/lib/bs4/diagnose.py
@@ -1,7 +1,10 @@
 """Diagnostic functions, mainly for use when doing tech support."""
+
+__license__ = "MIT"
+
 import cProfile
-from StringIO import StringIO
-from HTMLParser import HTMLParser
+from io import StringIO
+from html.parser import HTMLParser
 import bs4
 from bs4 import BeautifulSoup, __version__
 from bs4.builder import builder_registry
@@ -17,8 +20,8 @@ import cProfile
 
 def diagnose(data):
     """Diagnostic suite for isolating common problems."""
-    print "Diagnostic running on Beautiful Soup %s" % __version__
-    print "Python version %s" % sys.version
+    print("Diagnostic running on Beautiful Soup %s" % __version__)
+    print("Python version %s" % sys.version)
 
     basic_parsers = ["html.parser", "html5lib", "lxml"]
     for name in basic_parsers:
@@ -27,44 +30,53 @@ def diagnose(data):
                 break
         else:
             basic_parsers.remove(name)
-            print (
+            print((
                 "I noticed that %s is not installed. Installing it may help." %
-                name)
+                name))
 
     if 'lxml' in basic_parsers:
         basic_parsers.append(["lxml", "xml"])
-        from lxml import etree
-        print "Found lxml version %s" % ".".join(map(str,etree.LXML_VERSION))
+        try:
+            from lxml import etree
+            print("Found lxml version %s" % ".".join(map(str,etree.LXML_VERSION)))
+        except ImportError as e:
+            print (
+                "lxml is not installed or couldn't be imported.")
+
 
     if 'html5lib' in basic_parsers:
-        import html5lib
-        print "Found html5lib version %s" % html5lib.__version__
+        try:
+            import html5lib
+            print("Found html5lib version %s" % html5lib.__version__)
+        except ImportError as e:
+            print (
+                "html5lib is not installed or couldn't be imported.")
 
     if hasattr(data, 'read'):
         data = data.read()
     elif os.path.exists(data):
-        print '"%s" looks like a filename. Reading data from the file.' % data
+        print('"%s" looks like a filename. Reading data from the file.' % data)
         data = open(data).read()
     elif data.startswith("http:") or data.startswith("https:"):
-        print '"%s" looks like a URL. Beautiful Soup is not an HTTP client.' % data
-        print "You need to use some other library to get the document behind the URL, and feed that document to Beautiful Soup."
+        print('"%s" looks like a URL. Beautiful Soup is not an HTTP client.' % data)
+        print("You need to use some other library to get the document behind the URL, and feed that document to Beautiful Soup.")
         return
-    print
+    print()
 
     for parser in basic_parsers:
-        print "Trying to parse your markup with %s" % parser
+        print("Trying to parse your markup with %s" % parser)
         success = False
         try:
             soup = BeautifulSoup(data, parser)
             success = True
-        except Exception, e:
-            print "%s could not parse the markup." % parser
+        except Exception as e:
+            print("%s could not parse the markup." % parser)
             traceback.print_exc()
         if success:
-            print "Here's what %s did with the markup:" % parser
-            print soup.prettify()
+            print("Here's what %s did with the markup:" % parser)
+            print(soup.prettify())
 
-        print "-" * 80
+        print("-" * 80)
 
 def lxml_trace(data, html=True, **kwargs):
     """Print out the lxml events that occur during parsing.
@@ -74,7 +86,7 @@ def lxml_trace(data, html=True, **kwargs):
     """
     from lxml import etree
     for event, element in etree.iterparse(StringIO(data), html=html, **kwargs):
-        print("%s, %4s, %s" % (event, element.tag, element.text))
+        print(("%s, %4s, %s" % (event, element.tag, element.text)))
 
 class AnnouncingParser(HTMLParser):
     """Announces HTMLParser parse events, without doing anything else."""
@@ -156,9 +168,9 @@ def rdoc(num_elements=1000):
 
 def benchmark_parsers(num_elements=100000):
     """Very basic head-to-head performance benchmark."""
-    print "Comparative parser benchmark on Beautiful Soup %s" % __version__
+    print("Comparative parser benchmark on Beautiful Soup %s" % __version__)
     data = rdoc(num_elements)
-    print "Generated a large invalid HTML document (%d bytes)." % len(data)
+    print("Generated a large invalid HTML document (%d bytes)." % len(data))
     
     for parser in ["lxml", ["lxml", "html"], "html5lib", "html.parser"]:
         success = False
@@ -167,24 +179,24 @@ def benchmark_parsers(num_elements=100000):
             soup = BeautifulSoup(data, parser)
             b = time.time()
             success = True
-        except Exception, e:
-            print "%s could not parse the markup." % parser
+        except Exception as e:
+            print("%s could not parse the markup." % parser)
             traceback.print_exc()
         if success:
-            print "BS4+%s parsed the markup in %.2fs." % (parser, b-a)
+            print("BS4+%s parsed the markup in %.2fs." % (parser, b-a))
 
     from lxml import etree
     a = time.time()
     etree.HTML(data)
     b = time.time()
-    print "Raw lxml parsed the markup in %.2fs." % (b-a)
+    print("Raw lxml parsed the markup in %.2fs." % (b-a))
 
     import html5lib
     parser = html5lib.HTMLParser()
     a = time.time()
     parser.parse(data)
     b = time.time()
-    print "Raw html5lib parsed the markup in %.2fs." % (b-a)
+    print("Raw html5lib parsed the markup in %.2fs." % (b-a))
 
 def profile(num_elements=100000, parser="lxml"):
 
diff --git a/lib/bs4/element.py b/lib/bs4/element.py
index da9afdf..0e62c2e 100644
--- a/lib/bs4/element.py
+++ b/lib/bs4/element.py
@@ -1,3 +1,6 @@
+__license__ = "MIT"
+
+from pdb import set_trace
 import collections
 import re
 import sys
@@ -21,22 +24,22 @@ def _alias(attr):
     return alias
 
 
-class NamespacedAttribute(unicode):
+class NamespacedAttribute(str):
 
     def __new__(cls, prefix, name, namespace=None):
         if name is None:
-            obj = unicode.__new__(cls, prefix)
+            obj = str.__new__(cls, prefix)
         elif prefix is None:
             # Not really namespaced.
-            obj = unicode.__new__(cls, name)
+            obj = str.__new__(cls, name)
         else:
-            obj = unicode.__new__(cls, prefix + ":" + name)
+            obj = str.__new__(cls, prefix + ":" + name)
         obj.prefix = prefix
         obj.name = name
         obj.namespace = namespace
         return obj
 
-class AttributeValueWithCharsetSubstitution(unicode):
+class AttributeValueWithCharsetSubstitution(str):
     """A stand-in object for a character encoding specified in HTML."""
 
 class CharsetMetaAttributeValue(AttributeValueWithCharsetSubstitution):
@@ -47,7 +50,7 @@ class CharsetMetaAttributeValue(AttributeValueWithCharsetSubstitution):
     """
 
     def __new__(cls, original_value):
-        obj = unicode.__new__(cls, original_value)
+        obj = str.__new__(cls, original_value)
         obj.original_value = original_value
         return obj
 
@@ -70,9 +73,9 @@ class ContentMetaAttributeValue(AttributeValueWithCharsetSubstitution):
         match = cls.CHARSET_RE.search(original_value)
         if match is None:
             # No substitution necessary.
-            return unicode.__new__(unicode, original_value)
+            return str.__new__(str, original_value)
 
-        obj = unicode.__new__(cls, original_value)
+        obj = str.__new__(cls, original_value)
         obj.original_value = original_value
         return obj
 
@@ -152,7 +155,7 @@ class PageElement(object):
 
     def format_string(self, s, formatter='minimal'):
         """Format the given string using the given formatter."""
-        if not callable(formatter):
+        if not isinstance(formatter, collections.Callable):
             formatter = self._formatter_for_name(formatter)
         if formatter is None:
             output = s
@@ -185,24 +188,40 @@ class PageElement(object):
             return self.HTML_FORMATTERS.get(
                 name, HTMLAwareEntitySubstitution.substitute_xml)
 
-    def setup(self, parent=None, previous_element=None):
+    def setup(self, parent=None, previous_element=None, next_element=None,
+              previous_sibling=None, next_sibling=None):
         """Sets up the initial relations between this element and
         other elements."""
         self.parent = parent
+
         self.previous_element = previous_element
         if previous_element is not None:
             self.previous_element.next_element = self
-        self.next_element = None
-        self.previous_sibling = None
-        self.next_sibling = None
-        if self.parent is not None and self.parent.contents:
-            self.previous_sibling = self.parent.contents[-1]
+
+        self.next_element = next_element
+        if self.next_element:
+            self.next_element.previous_element = self
+
+        self.next_sibling = next_sibling
+        if self.next_sibling:
+            self.next_sibling.previous_sibling = self
+
+        if (not previous_sibling
+            and self.parent is not None and self.parent.contents):
+            previous_sibling = self.parent.contents[-1]
+
+        self.previous_sibling = previous_sibling
+        if previous_sibling:
             self.previous_sibling.next_sibling = self
 
     nextSibling = _alias("next_sibling")  # BS3
     previousSibling = _alias("previous_sibling")  # BS3
 
     def replace_with(self, replace_with):
+        if not self.parent:
+            raise ValueError(
+                "Cannot replace one element with another when the"
+                "element to be replaced is not part of a tree.")
         if replace_with is self:
             return
         if replace_with is self.parent:
@@ -216,6 +235,10 @@ class PageElement(object):
 
     def unwrap(self):
         my_parent = self.parent
+        if not self.parent:
+            raise ValueError(
+                "Cannot replace an element with its contents when that"
+                "element is not part of a tree.")
         my_index = self.parent.index(self)
         self.extract()
         for child in reversed(self.contents[:]):
@@ -240,17 +263,20 @@ class PageElement(object):
         last_child = self._last_descendant()
         next_element = last_child.next_element
 
-        if self.previous_element is not None:
+        if (self.previous_element is not None and
+            self.previous_element is not next_element):
             self.previous_element.next_element = next_element
-        if next_element is not None:
+        if next_element is not None and next_element is not self.previous_element:
             next_element.previous_element = self.previous_element
         self.previous_element = None
         last_child.next_element = None
 
         self.parent = None
-        if self.previous_sibling is not None:
+        if (self.previous_sibling is not None
+            and self.previous_sibling is not self.next_sibling):
             self.previous_sibling.next_sibling = self.next_sibling
-        if self.next_sibling is not None:
+        if (self.next_sibling is not None
+            and self.next_sibling is not self.previous_sibling):
             self.next_sibling.previous_sibling = self.previous_sibling
         self.previous_sibling = self.next_sibling = None
         return self
@@ -263,16 +289,18 @@ class PageElement(object):
             last_child = self
             while isinstance(last_child, Tag) and last_child.contents:
                 last_child = last_child.contents[-1]
-        if not accept_self and last_child == self:
+        if not accept_self and last_child is self:
             last_child = None
         return last_child
     # BS3: Not part of the API!
     _lastRecursiveChild = _last_descendant
 
     def insert(self, position, new_child):
+        if new_child is None:
+            raise ValueError("Cannot insert None into a tag.")
         if new_child is self:
             raise ValueError("Cannot insert a tag into itself.")
-        if (isinstance(new_child, basestring)
+        if (isinstance(new_child, str)
             and not isinstance(new_child, NavigableString)):
             new_child = NavigableString(new_child)
 
@@ -478,6 +506,10 @@ class PageElement(object):
     def _find_all(self, name, attrs, text, limit, generator, **kwargs):
         "Iterates over a generator looking for things that match."
 
+        if text is None and 'string' in kwargs:
+            text = kwargs['string']
+            del kwargs['string']
+
         if isinstance(name, SoupStrainer):
             strainer = name
         else:
@@ -489,7 +521,7 @@ class PageElement(object):
                 result = (element for element in generator
                           if isinstance(element, Tag))
                 return ResultSet(strainer, result)
-            elif isinstance(name, basestring):
+            elif isinstance(name, str):
                 # Optimization to find all tags with a given name.
                 result = (element for element in generator
                           if isinstance(element, Tag)
@@ -548,17 +580,17 @@ class PageElement(object):
 
     # Methods for supporting CSS selectors.
 
-    tag_name_re = re.compile('^[a-z0-9]+$')
+    tag_name_re = re.compile('^[a-zA-Z0-9][-.a-zA-Z0-9:_]*$')
 
-    # /^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
-    #   \---/  \---/\-------------/    \-------/
-    #     |      |         |               |
-    #     |      |         |           The value
-    #     |      |    ~,|,^,$,* or =
-    #     |   Attribute
+    # /^([a-zA-Z0-9][-.a-zA-Z0-9:_]*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
+    #   \---------------------------/  \---/\-------------/    \-------/
+    #     |                              |         |               |
+    #     |                              |         |           The value
+    #     |                              |    ~,|,^,$,* or =
+    #     |                           Attribute
     #    Tag
     attribselect_re = re.compile(
-        r'^(?P<tag>\w+)?\[(?P<attribute>\w+)(?P<operator>[=~\|\^\$\*]?)' +
+        r'^(?P<tag>[a-zA-Z0-9][-.a-zA-Z0-9:_]*)?\[(?P<attribute>[\w-]+)(?P<operator>[=~\|\^\$\*]?)' +
         r'=?"?(?P<value>[^\]"]*)"?\]$'
         )
 
@@ -640,7 +672,7 @@ class PageElement(object):
         return self.parents
 
 
-class NavigableString(unicode, PageElement):
+class NavigableString(str, PageElement):
 
     PREFIX = ''
     SUFFIX = ''
@@ -653,15 +685,21 @@ class NavigableString(unicode, PageElement):
         passed in to the superclass's __new__ or the superclass won't know
         how to handle non-ASCII characters.
         """
-        if isinstance(value, unicode):
-            return unicode.__new__(cls, value)
-        return unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING)
+        if isinstance(value, str):
+            u = str.__new__(cls, value)
+        else:
+            u = str.__new__(cls, value, DEFAULT_OUTPUT_ENCODING)
+        u.setup()
+        return u
 
     def __copy__(self):
-        return self
+        """A copy of a NavigableString has the same contents and class
+        as the original, but it is not connected to the parse tree.
+        """
+        return type(self)(self)
 
     def __getnewargs__(self):
-        return (unicode(self),)
+        return (str(self),)
 
     def __getattr__(self, attr):
         """text.string gives you text. This is for backwards
@@ -701,23 +739,23 @@ class PreformattedString(NavigableString):
 
 class CData(PreformattedString):
 
-    PREFIX = u'<![CDATA['
-    SUFFIX = u']]>'
+    PREFIX = '<![CDATA['
+    SUFFIX = ']]>'
 
 class ProcessingInstruction(PreformattedString):
 
-    PREFIX = u'<?'
-    SUFFIX = u'?>'
+    PREFIX = '<?'
+    SUFFIX = '>'
 
 class Comment(PreformattedString):
 
-    PREFIX = u'<!--'
-    SUFFIX = u'-->'
+    PREFIX = '<!--'
+    SUFFIX = '-->'
 
 
 class Declaration(PreformattedString):
-    PREFIX = u'<!'
-    SUFFIX = u'!>'
+    PREFIX = '<?'
+    SUFFIX = '?>'
 
 
 class Doctype(PreformattedString):
@@ -734,8 +772,8 @@ class Doctype(PreformattedString):
 
         return Doctype(value)
 
-    PREFIX = u'<!DOCTYPE '
-    SUFFIX = u'>\n'
+    PREFIX = '<!DOCTYPE '
+    SUFFIX = '>\n'
 
 
 class Tag(PageElement):
@@ -759,9 +797,12 @@ class Tag(PageElement):
         self.prefix = prefix
         if attrs is None:
             attrs = {}
-        elif attrs and builder.cdata_list_attributes:
-            attrs = builder._replace_cdata_list_attribute_values(
-                self.name, attrs)
+        elif attrs:
+            if builder is not None and builder.cdata_list_attributes:
+                attrs = builder._replace_cdata_list_attribute_values(
+                    self.name, attrs)
+            else:
+                attrs = dict(attrs)
         else:
             attrs = dict(attrs)
         self.attrs = attrs
@@ -778,6 +819,18 @@ class Tag(PageElement):
 
     parserClass = _alias("parser_class")  # BS3
 
+    def __copy__(self):
+        """A copy of a Tag is a new Tag, unconnected to the parse tree.
+        Its contents are a copy of the old Tag's contents.
+        """
+        clone = type(self)(None, self.builder, self.name, self.namespace,
+                           self.nsprefix, self.attrs)
+        for attr in ('can_be_empty_element', 'hidden'):
+            setattr(clone, attr, getattr(self, attr))
+        for child in self.contents:
+            clone.append(child.__copy__())
+        return clone
+
     @property
     def is_empty_element(self):
         """Is this tag an empty-element tag? (aka a self-closing tag)
@@ -843,7 +896,7 @@ class Tag(PageElement):
         for string in self._all_strings(True):
             yield string
 
-    def get_text(self, separator=u"", strip=False,
+    def get_text(self, separator="", strip=False,
                  types=(NavigableString, CData)):
         """
         Get all child strings, concatenated using the given separator.
@@ -915,7 +968,7 @@ class Tag(PageElement):
     def __contains__(self, x):
         return x in self.contents
 
-    def __nonzero__(self):
+    def __bool__(self):
         "A tag is non-None even if it has no contents."
         return True
 
@@ -971,15 +1024,25 @@ class Tag(PageElement):
         as defined in __eq__."""
         return not self == other
 
-    def __repr__(self, encoding=DEFAULT_OUTPUT_ENCODING):
+    def __repr__(self, encoding="unicode-escape"):
         """Renders this tag as a string."""
-        return self.encode(encoding)
+        if PY3K:
+            # "The return value must be a string object", i.e. Unicode
+            return self.decode()
+        else:
+            # "The return value must be a string object", i.e. a bytestring.
+            # By convention, the return value of __repr__ should also be
+            # an ASCII string.
+            return self.encode(encoding)
 
     def __unicode__(self):
         return self.decode()
 
     def __str__(self):
-        return self.encode()
+        if PY3K:
+            return self.decode()
+        else:
+            return self.encode()
 
     if PY3K:
         __str__ = __repr__ = __unicode__
@@ -1014,7 +1077,7 @@ class Tag(PageElement):
 
         # First off, turn a string formatter into a function. This
         # will stop the lookup from happening over and over again.
-        if not callable(formatter):
+        if not isinstance(formatter, collections.Callable):
             formatter = self._formatter_for_name(formatter)
 
         attrs = []
@@ -1025,8 +1088,8 @@ class Tag(PageElement):
                 else:
                     if isinstance(val, list) or isinstance(val, tuple):
                         val = ' '.join(val)
-                    elif not isinstance(val, basestring):
-                        val = unicode(val)
+                    elif not isinstance(val, str):
+                        val = str(val)
                     elif (
                         isinstance(val, AttributeValueWithCharsetSubstitution)
                         and eventual_encoding is not None):
@@ -1034,7 +1097,7 @@ class Tag(PageElement):
 
                     text = self.format_string(val, formatter)
                     decoded = (
-                        unicode(key) + '='
+                        str(key) + '='
                         + EntitySubstitution.quoted_attribute_value(text))
                 attrs.append(decoded)
         close = ''
@@ -1103,16 +1166,22 @@ class Tag(PageElement):
                        formatter="minimal"):
         """Renders the contents of this tag as a Unicode string.
 
+        :param indent_level: Each line of the rendering will be
+           indented this many spaces.
+
         :param eventual_encoding: The tag is destined to be
            encoded into this encoding. This method is _not_
            responsible for performing that encoding. This information
            is passed in so that it can be substituted in if the
            document contains a <META> tag that mentions the document's
            encoding.
+
+        :param formatter: The output formatter responsible for converting
+           entities to Unicode characters.
         """
         # First off, turn a string formatter into a function. This
         # will stop the lookup from happening over and over again.
-        if not callable(formatter):
+        if not isinstance(formatter, collections.Callable):
             formatter = self._formatter_for_name(formatter)
 
         pretty_print = (indent_level is not None)
@@ -1137,7 +1206,17 @@ class Tag(PageElement):
     def encode_contents(
         self, indent_level=None, encoding=DEFAULT_OUTPUT_ENCODING,
         formatter="minimal"):
-        """Renders the contents of this tag as a bytestring."""
+        """Renders the contents of this tag as a bytestring.
+
+        :param indent_level: Each line of the rendering will be
+           indented this many spaces.
+
+        :param eventual_encoding: The bytestring will be in this encoding.
+
+        :param formatter: The output formatter responsible for converting
+           entities to Unicode characters.
+        """
+
         contents = self.decode_contents(indent_level, encoding, formatter)
         return contents.encode(encoding)
 
@@ -1201,26 +1280,57 @@ class Tag(PageElement):
 
     _selector_combinators = ['>', '+', '~']
     _select_debug = False
-    def select(self, selector, _candidate_generator=None):
+    def select_one(self, selector):
+        """Perform a CSS selection operation on the current element."""
+        value = self.select(selector, limit=1)
+        if value:
+            return value[0]
+        return None
+
+    def select(self, selector, _candidate_generator=None, limit=None):
         """Perform a CSS selection operation on the current element."""
+
+        # Handle grouping selectors if ',' exists, ie: p,a
+        if ',' in selector:
+            context = []
+            for partial_selector in selector.split(','):
+                partial_selector = partial_selector.strip()
+                if partial_selector == '':
+                    raise ValueError('Invalid group selection syntax: %s' % selector)
+                candidates = self.select(partial_selector, limit=limit)
+                for candidate in candidates:
+                    if candidate not in context:
+                        context.append(candidate)
+
+                if limit and len(context) >= limit:
+                    break
+            return context
+
         tokens = selector.split()
         current_context = [self]
 
         if tokens[-1] in self._selector_combinators:
             raise ValueError(
                 'Final combinator "%s" is missing an argument.' % tokens[-1])
+
         if self._select_debug:
-            print 'Running CSS selector "%s"' % selector
+            print('Running CSS selector "%s"' % selector)
+
         for index, token in enumerate(tokens):
-            if self._select_debug:
-                print ' Considering token "%s"' % token
-            recursive_candidate_generator = None
-            tag_name = None
+            new_context = []
+            new_context_ids = set([])
+
             if tokens[index-1] in self._selector_combinators:
                 # This token was consumed by the previous combinator. Skip it.
                 if self._select_debug:
-                    print '  Token was consumed by the previous combinator.'
+                    print('  Token was consumed by the previous combinator.')
                 continue
+
+            if self._select_debug:
+                print(' Considering token "%s"' % token)
+            recursive_candidate_generator = None
+            tag_name = None
+
             # Each operation corresponds to a checker function, a rule
             # for determining whether a candidate matches the
             # selector. Candidates are generated by the active
@@ -1256,35 +1366,38 @@ class Tag(PageElement):
                         "A pseudo-class must be prefixed with a tag name.")
                 pseudo_attributes = re.match('([a-zA-Z\d-]+)\(([a-zA-Z\d]+)\)', pseudo)
                 found = []
-                if pseudo_attributes is not None:
+                if pseudo_attributes is None:
+                    pseudo_type = pseudo
+                    pseudo_value = None
+                else:
                     pseudo_type, pseudo_value = pseudo_attributes.groups()
-                    if pseudo_type == 'nth-of-type':
-                        try:
-                            pseudo_value = int(pseudo_value)
-                        except:
-                            raise NotImplementedError(
-                                'Only numeric values are currently supported for the nth-of-type pseudo-class.')
-                        if pseudo_value < 1:
-                            raise ValueError(
-                                'nth-of-type pseudo-class value must be at least 1.')
-                        class Counter(object):
-                            def __init__(self, destination):
-                                self.count = 0
-                                self.destination = destination
-
-                            def nth_child_of_type(self, tag):
-                                self.count += 1
-                                if self.count == self.destination:
-                                    return True
-                                if self.count > self.destination:
-                                    # Stop the generator that's sending us
-                                    # these things.
-                                    raise StopIteration()
-                                return False
-                        checker = Counter(pseudo_value).nth_child_of_type
-                    else:
+                if pseudo_type == 'nth-of-type':
+                    try:
+                        pseudo_value = int(pseudo_value)
+                    except:
                         raise NotImplementedError(
-                            'Only the following pseudo-classes are implemented: nth-of-type.')
+                            'Only numeric values are currently supported for the nth-of-type pseudo-class.')
+                    if pseudo_value < 1:
+                        raise ValueError(
+                            'nth-of-type pseudo-class value must be at least 1.')
+                    class Counter(object):
+                        def __init__(self, destination):
+                            self.count = 0
+                            self.destination = destination
+
+                        def nth_child_of_type(self, tag):
+                            self.count += 1
+                            if self.count == self.destination:
+                                return True
+                            if self.count > self.destination:
+                                # Stop the generator that's sending us
+                                # these things.
+                                raise StopIteration()
+                            return False
+                    checker = Counter(pseudo_value).nth_child_of_type
+                else:
+                    raise NotImplementedError(
+                        'Only the following pseudo-classes are implemented: nth-of-type.')
 
             elif token == '*':
                 # Star selector -- matches everything
@@ -1311,7 +1424,6 @@ class Tag(PageElement):
             else:
                 raise ValueError(
                     'Unsupported or invalid CSS selector: "%s"' % token)
-
             if recursive_candidate_generator:
                 # This happens when the selector looks like  "> foo".
                 #
@@ -1325,14 +1437,14 @@ class Tag(PageElement):
                 next_token = tokens[index+1]
                 def recursive_select(tag):
                     if self._select_debug:
-                        print '    Calling select("%s") recursively on %s %s' % (next_token, tag.name, tag.attrs)
-                        print '-' * 40
+                        print('    Calling select("%s") recursively on %s %s' % (next_token, tag.name, tag.attrs))
+                        print('-' * 40)
                     for i in tag.select(next_token, recursive_candidate_generator):
                         if self._select_debug:
-                            print '(Recursive select picked up candidate %s %s)' % (i.name, i.attrs)
+                            print('(Recursive select picked up candidate %s %s)' % (i.name, i.attrs))
                         yield i
                     if self._select_debug:
-                        print '-' * 40
+                        print('-' * 40)
                 _use_candidate_generator = recursive_select
             elif _candidate_generator is None:
                 # By default, a tag's candidates are all of its
@@ -1343,7 +1455,7 @@ class Tag(PageElement):
                         check = "[any]"
                     else:
                         check = tag_name
-                    print '   Default candidate generator, tag name="%s"' % check
+                    print('   Default candidate generator, tag name="%s"' % check)
                 if self._select_debug:
                     # This is redundant with later code, but it stops
                     # a bunch of bogus tags from cluttering up the
@@ -1361,12 +1473,11 @@ class Tag(PageElement):
             else:
                 _use_candidate_generator = _candidate_generator
 
-            new_context = []
-            new_context_ids = set([])
+            count = 0
             for tag in current_context:
                 if self._select_debug:
-                    print "    Running candidate generator on %s %s" % (
-                        tag.name, repr(tag.attrs))
+                    print("    Running candidate generator on %s %s" % (
+                        tag.name, repr(tag.attrs)))
                 for candidate in _use_candidate_generator(tag):
                     if not isinstance(candidate, Tag):
                         continue
@@ -1381,21 +1492,24 @@ class Tag(PageElement):
                             break
                     if checker is None or result:
                         if self._select_debug:
-                            print "     SUCCESS %s %s" % (candidate.name, repr(candidate.attrs))
+                            print("     SUCCESS %s %s" % (candidate.name, repr(candidate.attrs)))
                         if id(candidate) not in new_context_ids:
                             # If a tag matches a selector more than once,
                             # don't include it in the context more than once.
                             new_context.append(candidate)
                             new_context_ids.add(id(candidate))
+                            if limit and len(new_context) >= limit:
+                                break
                     elif self._select_debug:
-                        print "     FAILURE %s %s" % (candidate.name, repr(candidate.attrs))
+                        print("     FAILURE %s %s" % (candidate.name, repr(candidate.attrs)))
+
 
             current_context = new_context
 
         if self._select_debug:
-            print "Final verdict:"
+            print("Final verdict:")
             for i in current_context:
-                print " %s %s" % (i.name, i.attrs)
+                print(" %s %s" % (i.name, i.attrs))
         return current_context
 
     # Old names for backwards compatibility
@@ -1439,7 +1553,7 @@ class SoupStrainer(object):
             else:
                 attrs = kwargs
         normalized_attrs = {}
-        for key, value in attrs.items():
+        for key, value in list(attrs.items()):
             normalized_attrs[key] = self._normalize_search_value(value)
 
         self.attrs = normalized_attrs
@@ -1448,7 +1562,7 @@ class SoupStrainer(object):
     def _normalize_search_value(self, value):
         # Leave it alone if it's a Unicode string, a callable, a
         # regular expression, a boolean, or None.
-        if (isinstance(value, unicode) or callable(value) or hasattr(value, 'match')
+        if (isinstance(value, str) or isinstance(value, collections.Callable) or hasattr(value, 'match')
             or isinstance(value, bool) or value is None):
             return value
 
@@ -1461,7 +1575,7 @@ class SoupStrainer(object):
             new_value = []
             for v in value:
                 if (hasattr(v, '__iter__') and not isinstance(v, bytes)
-                    and not isinstance(v, unicode)):
+                    and not isinstance(v, str)):
                     # This is almost certainly the user's mistake. In the
                     # interests of avoiding infinite loops, we'll let
                     # it through as-is rather than doing a recursive call.
@@ -1473,7 +1587,7 @@ class SoupStrainer(object):
         # Otherwise, convert it into a Unicode string.
         # The unicode(str()) thing is so this will do the same thing on Python 2
         # and Python 3.
-        return unicode(str(value))
+        return str(str(value))
 
     def __str__(self):
         if self.text:
@@ -1527,7 +1641,7 @@ class SoupStrainer(object):
         found = None
         # If given a list of items, scan it for a text element that
         # matches.
-        if hasattr(markup, '__iter__') and not isinstance(markup, (Tag, basestring)):
+        if hasattr(markup, '__iter__') and not isinstance(markup, (Tag, str)):
             for element in markup:
                 if isinstance(element, NavigableString) \
                        and self.search(element):
@@ -1540,7 +1654,7 @@ class SoupStrainer(object):
                 found = self.search_tag(markup)
         # If it's text, make sure the text matches.
         elif isinstance(markup, NavigableString) or \
-                 isinstance(markup, basestring):
+                 isinstance(markup, str):
             if not self.name and not self.attrs and self._matches(markup, self.text):
                 found = markup
         else:
@@ -1554,7 +1668,7 @@ class SoupStrainer(object):
         if isinstance(markup, list) or isinstance(markup, tuple):
             # This should only happen when searching a multi-valued attribute
             # like 'class'.
-            if (isinstance(match_against, unicode)
+            if (isinstance(match_against, str)
                 and ' ' in match_against):
                 # A bit of a special case. If they try to match "foo
                 # bar" on a multivalue attribute's value, only accept
@@ -1589,7 +1703,7 @@ class SoupStrainer(object):
             # None matches None, False, an empty string, an empty list, and so on.
             return not match_against
 
-        if isinstance(match_against, unicode):
+        if isinstance(match_against, str):
             # Exact string match
             return markup == match_against
 
diff --git a/lib/bs4/testing.py b/lib/bs4/testing.py
index fd4495a..3a2f260 100644
--- a/lib/bs4/testing.py
+++ b/lib/bs4/testing.py
@@ -1,5 +1,8 @@
 """Helper classes for tests."""
 
+__license__ = "MIT"
+
+import pickle
 import copy
 import functools
 import unittest
@@ -43,6 +46,16 @@ class SoupTest(unittest.TestCase):
 
         self.assertEqual(obj.decode(), self.document_for(compare_parsed_to))
 
+    def assertConnectedness(self, element):
+        """Ensure that next_element and previous_element are properly
+        set for all descendants of the given element.
+        """
+        earlier = None
+        for e in element.descendants:
+            if earlier:
+                self.assertEqual(e, earlier.next_element)
+                self.assertEqual(earlier, e.previous_element)
+            earlier = e
 
 class HTMLTreeBuilderSmokeTest(object):
 
@@ -54,6 +67,15 @@ class HTMLTreeBuilderSmokeTest(object):
     markup in these tests, there's not much room for interpretation.
     """
 
+    def test_pickle_and_unpickle_identity(self):
+        # Pickling a tree, then unpickling it, yields a tree identical
+        # to the original.
+        tree = self.soup("<a><b>foo</a>")
+        dumped = pickle.dumps(tree, 2)
+        loaded = pickle.loads(dumped)
+        self.assertEqual(loaded.__class__, BeautifulSoup)
+        self.assertEqual(loaded.decode(), tree.decode())
+
     def assertDoctypeHandled(self, doctype_fragment):
         """Assert that a given doctype string is handled correctly."""
         doctype_str, soup = self._document_with_doctype(doctype_fragment)
@@ -114,6 +136,11 @@ class HTMLTreeBuilderSmokeTest(object):
             soup.encode("utf-8").replace(b"\n", b""),
             markup.replace(b"\n", b""))
 
+    def test_processing_instruction(self):
+        markup = b"""<?PITarget PIContent?>"""
+        soup = self.soup(markup)
+        self.assertEqual(markup, soup.encode("utf8"))
+
     def test_deepcopy(self):
         """Make sure you can copy the tree builder.
 
@@ -155,6 +182,23 @@ class HTMLTreeBuilderSmokeTest(object):
     def test_nested_formatting_elements(self):
         self.assertSoupEquals("<em><em></em></em>")
 
+    def test_double_head(self):
+        html = '''<!DOCTYPE html>
+<html>
+<head>
+<title>Ordinary HEAD element test</title>
+</head>
+<script type="text/javascript">
+alert("Help!");
+</script>
+<body>
+Hello, world!
+</body>
+</html>
+'''
+        soup = self.soup(html)
+        self.assertEqual("text/javascript", soup.find('script')['type'])
+
     def test_comment(self):
         # Comments are represented as Comment objects.
         markup = "<p>foo<!--foobar-->baz</p>"
@@ -221,18 +265,26 @@ class HTMLTreeBuilderSmokeTest(object):
         soup = self.soup(markup)
         self.assertEqual(["css"], soup.div.div['class'])
 
+    def test_multivalued_attribute_on_html(self):
+        # html5lib uses a different API to set the attributes ot the
+        # <html> tag. This has caused problems with multivalued
+        # attributes.
+        markup = '<html class="a b"></html>'
+        soup = self.soup(markup)
+        self.assertEqual(["a", "b"], soup.html['class'])
+
     def test_angle_brackets_in_attribute_values_are_escaped(self):
         self.assertSoupEquals('<a b="<a>"></a>', '<a b="&lt;a&gt;"></a>')
 
     def test_entities_in_attributes_converted_to_unicode(self):
-        expect = u'<p id="pi\N{LATIN SMALL LETTER N WITH TILDE}ata"></p>'
+        expect = '<p id="pi\N{LATIN SMALL LETTER N WITH TILDE}ata"></p>'
         self.assertSoupEquals('<p id="pi&#241;ata"></p>', expect)
         self.assertSoupEquals('<p id="pi&#xf1;ata"></p>', expect)
         self.assertSoupEquals('<p id="pi&#Xf1;ata"></p>', expect)
         self.assertSoupEquals('<p id="pi&ntilde;ata"></p>', expect)
 
     def test_entities_in_text_converted_to_unicode(self):
-        expect = u'<p>pi\N{LATIN SMALL LETTER N WITH TILDE}ata</p>'
+        expect = '<p>pi\N{LATIN SMALL LETTER N WITH TILDE}ata</p>'
         self.assertSoupEquals("<p>pi&#241;ata</p>", expect)
         self.assertSoupEquals("<p>pi&#xf1;ata</p>", expect)
         self.assertSoupEquals("<p>pi&#Xf1;ata</p>", expect)
@@ -243,7 +295,7 @@ class HTMLTreeBuilderSmokeTest(object):
                               '<p>I said "good day!"</p>')
 
     def test_out_of_range_entity(self):
-        expect = u"\N{REPLACEMENT CHARACTER}"
+        expect = "\N{REPLACEMENT CHARACTER}"
         self.assertSoupEquals("&#10000000000000;", expect)
         self.assertSoupEquals("&#x10000000000000;", expect)
         self.assertSoupEquals("&#1000000000;", expect)
@@ -253,6 +305,35 @@ class HTMLTreeBuilderSmokeTest(object):
         soup = self.soup("<html><h2>\nfoo</h2><p></p></html>")
         self.assertEqual("p", soup.h2.string.next_element.name)
         self.assertEqual("p", soup.p.name)
+        self.assertConnectedness(soup)
+
+    def test_head_tag_between_head_and_body(self):
+        "Prevent recurrence of a bug in the html5lib treebuilder."
+        content = """<html><head></head>
+  <link></link>
+  <body>foo</body>
+</html>
+"""
+        soup = self.soup(content)
+        self.assertNotEqual(None, soup.html.body)
+        self.assertConnectedness(soup)
+
+    def test_multiple_copies_of_a_tag(self):
+        "Prevent recurrence of a bug in the html5lib treebuilder."
+        content = """<!DOCTYPE html>
+<html>
+ <body>
+   <article id="a" >
+   <div><a href="1"></div>
+   <footer>
+     <a href="2"></a>
+   </footer>
+  </article>
+  </body>
+</html>
+"""
+        soup = self.soup(content)
+        self.assertConnectedness(soup.article)
 
     def test_basic_namespaces(self):
         """Parsers don't need to *understand* namespaces, but at the
@@ -285,9 +366,9 @@ class HTMLTreeBuilderSmokeTest(object):
         # A seemingly innocuous document... but it's in Unicode! And
         # it contains characters that can't be represented in the
         # encoding found in the  declaration! The horror!
-        markup = u'<html><head><meta encoding="euc-jp"></head><body>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</body>'
+        markup = '<html><head><meta encoding="euc-jp"></head><body>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</body>'
         soup = self.soup(markup)
-        self.assertEqual(u'Sacr\xe9 bleu!', soup.body.string)
+        self.assertEqual('Sacr\xe9 bleu!', soup.body.string)
 
     def test_soupstrainer(self):
         """Parsers should be able to work with SoupStrainers."""
@@ -327,7 +408,7 @@ class HTMLTreeBuilderSmokeTest(object):
         # Both XML and HTML entities are converted to Unicode characters
         # during parsing.
         text = "<p>&lt;&lt;sacr&eacute;&#32;bleu!&gt;&gt;</p>"
-        expected = u"<p>&lt;&lt;sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</p>"
+        expected = "<p>&lt;&lt;sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</p>"
         self.assertSoupEquals(text, expected)
 
     def test_smart_quotes_converted_on_the_way_in(self):
@@ -337,15 +418,15 @@ class HTMLTreeBuilderSmokeTest(object):
         soup = self.soup(quote)
         self.assertEqual(
             soup.p.string,
-            u"\N{LEFT SINGLE QUOTATION MARK}Foo\N{RIGHT SINGLE QUOTATION MARK}")
+            "\N{LEFT SINGLE QUOTATION MARK}Foo\N{RIGHT SINGLE QUOTATION MARK}")
 
     def test_non_breaking_spaces_converted_on_the_way_in(self):
         soup = self.soup("<a>&nbsp;&nbsp;</a>")
-        self.assertEqual(soup.a.string, u"\N{NO-BREAK SPACE}" * 2)
+        self.assertEqual(soup.a.string, "\N{NO-BREAK SPACE}" * 2)
 
     def test_entities_converted_on_the_way_out(self):
         text = "<p>&lt;&lt;sacr&eacute;&#32;bleu!&gt;&gt;</p>"
-        expected = u"<p>&lt;&lt;sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</p>".encode("utf-8")
+        expected = "<p>&lt;&lt;sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</p>".encode("utf-8")
         soup = self.soup(text)
         self.assertEqual(soup.p.encode("utf-8"), expected)
 
@@ -354,7 +435,7 @@ class HTMLTreeBuilderSmokeTest(object):
         # easy-to-understand document.
 
         # Here it is in Unicode. Note that it claims to be in ISO-Latin-1.
-        unicode_html = u'<html><head><meta content="text/html; charset=ISO-Latin-1" http-equiv="Content-type"/></head><body><p>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</p></body></html>'
+        unicode_html = '<html><head><meta content="text/html; charset=ISO-Latin-1" http-equiv="Content-type"/></head><body><p>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</p></body></html>'
 
         # That's because we're going to encode it into ISO-Latin-1, and use
         # that to test.
@@ -463,11 +544,25 @@ class HTMLTreeBuilderSmokeTest(object):
 
 class XMLTreeBuilderSmokeTest(object):
 
+    def test_pickle_and_unpickle_identity(self):
+        # Pickling a tree, then unpickling it, yields a tree identical
+        # to the original.
+        tree = self.soup("<a><b>foo</a>")
+        dumped = pickle.dumps(tree, 2)
+        loaded = pickle.loads(dumped)
+        self.assertEqual(loaded.__class__, BeautifulSoup)
+        self.assertEqual(loaded.decode(), tree.decode())
+
     def test_docstring_generated(self):
         soup = self.soup("<root/>")
         self.assertEqual(
             soup.encode(), b'<?xml version="1.0" encoding="utf-8"?>\n<root/>')
 
+    def test_xml_declaration(self):
+        markup = b"""<?xml version="1.0" encoding="utf8"?>\n<foo/>"""
+        soup = self.soup(markup)
+        self.assertEqual(markup, soup.encode("utf8"))
+
     def test_real_xhtml_document(self):
         """A real XHTML document should come out *exactly* the same as it went in."""
         markup = b"""<?xml version="1.0" encoding="utf-8"?>
@@ -485,7 +580,7 @@ class XMLTreeBuilderSmokeTest(object):
   <script type="text/javascript">
   </script>
 """
-        soup = BeautifulSoup(doc, "xml")
+        soup = BeautifulSoup(doc, "lxml-xml")
         # lxml would have stripped this while parsing, but we can add
         # it later.
         soup.script.string = 'console.log("< < hey > > ");'
@@ -493,15 +588,15 @@ class XMLTreeBuilderSmokeTest(object):
         self.assertTrue(b"&lt; &lt; hey &gt; &gt;" in encoded)
 
     def test_can_parse_unicode_document(self):
-        markup = u'<?xml version="1.0" encoding="euc-jp"><root>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</root>'
+        markup = '<?xml version="1.0" encoding="euc-jp"><root>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</root>'
         soup = self.soup(markup)
-        self.assertEqual(u'Sacr\xe9 bleu!', soup.root.string)
+        self.assertEqual('Sacr\xe9 bleu!', soup.root.string)
 
     def test_popping_namespaced_tag(self):
         markup = '<rss xmlns:dc="foo"><dc:creator>b</dc:creator><dc:date>2012-07-02T20:33:42Z</dc:date><dc:rights>c</dc:rights><image>d</image></rss>'
         soup = self.soup(markup)
         self.assertEqual(
-            unicode(soup.rss), markup)
+            str(soup.rss), markup)
 
     def test_docstring_includes_correct_encoding(self):
         soup = self.soup("<root/>")
@@ -532,17 +627,17 @@ class XMLTreeBuilderSmokeTest(object):
     def test_closing_namespaced_tag(self):
         markup = '<p xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>20010504</dc:date></p>'
         soup = self.soup(markup)
-        self.assertEqual(unicode(soup.p), markup)
+        self.assertEqual(str(soup.p), markup)
 
     def test_namespaced_attributes(self):
         markup = '<foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><bar xsi:schemaLocation="http://www.example.com"/></foo>'
         soup = self.soup(markup)
-        self.assertEqual(unicode(soup.foo), markup)
+        self.assertEqual(str(soup.foo), markup)
 
     def test_namespaced_attributes_xml_namespace(self):
         markup = '<foo xml:lang="fr">bar</foo>'
         soup = self.soup(markup)
-        self.assertEqual(unicode(soup.foo), markup)
+        self.assertEqual(str(soup.foo), markup)
 
 class HTML5TreeBuilderSmokeTest(HTMLTreeBuilderSmokeTest):
     """Smoke test for a tree builder that supports HTML5."""
diff --git a/lib/bs4/tests/test_builder_registry.py b/lib/bs4/tests/test_builder_registry.py
index 92ad10f..90cad82 100644
--- a/lib/bs4/tests/test_builder_registry.py
+++ b/lib/bs4/tests/test_builder_registry.py
@@ -1,6 +1,7 @@
 """Tests of the builder registry."""
 
 import unittest
+import warnings
 
 from bs4 import BeautifulSoup
 from bs4.builder import (
@@ -67,10 +68,15 @@ class BuiltInRegistryTest(unittest.TestCase):
                           HTMLParserTreeBuilder)
 
     def test_beautifulsoup_constructor_does_lookup(self):
-        # You can pass in a string.
-        BeautifulSoup("", features="html")
-        # Or a list of strings.
-        BeautifulSoup("", features=["html", "fast"])
+
+        with warnings.catch_warnings(record=True) as w:
+            # This will create a warning about not explicitly
+            # specifying a parser, but we'll ignore it.
+
+            # You can pass in a string.
+            BeautifulSoup("", features="html")
+            # Or a list of strings.
+            BeautifulSoup("", features=["html", "fast"])
 
         # You'll get an exception if BS can't find an appropriate
         # builder.
diff --git a/lib/bs4/tests/test_html5lib.py b/lib/bs4/tests/test_html5lib.py
index 594c3e1..a7494ca 100644
--- a/lib/bs4/tests/test_html5lib.py
+++ b/lib/bs4/tests/test_html5lib.py
@@ -5,7 +5,7 @@ import warnings
 try:
     from bs4.builder import HTML5TreeBuilder
     HTML5LIB_PRESENT = True
-except ImportError, e:
+except ImportError as e:
     HTML5LIB_PRESENT = False
 from bs4.element import SoupStrainer
 from bs4.testing import (
@@ -74,12 +74,25 @@ class HTML5LibBuilderSmokeTest(SoupTest, HTML5TreeBuilderSmokeTest):
     def test_reparented_markup(self):
         markup = '<p><em>foo</p>\n<p>bar<a></a></em></p>'
         soup = self.soup(markup)
-        self.assertEqual(u"<body><p><em>foo</em></p><em>\n</em><p><em>bar<a></a></em></p></body>", soup.body.decode())
+        self.assertEqual("<body><p><em>foo</em></p><em>\n</em><p><em>bar<a></a></em></p></body>", soup.body.decode())
         self.assertEqual(2, len(soup.find_all('p')))
 
 
     def test_reparented_markup_ends_with_whitespace(self):
         markup = '<p><em>foo</p>\n<p>bar<a></a></em></p>\n'
         soup = self.soup(markup)
-        self.assertEqual(u"<body><p><em>foo</em></p><em>\n</em><p><em>bar<a></a></em></p>\n</body>", soup.body.decode())
+        self.assertEqual("<body><p><em>foo</em></p><em>\n</em><p><em>bar<a></a></em></p>\n</body>", soup.body.decode())
         self.assertEqual(2, len(soup.find_all('p')))
+
+    def test_processing_instruction(self):
+        """Processing instructions become comments."""
+        markup = b"""<?PITarget PIContent?>"""
+        soup = self.soup(markup)
+        assert str(soup).startswith("<!--?PITarget PIContent?-->")
+
+    def test_cloned_multivalue_node(self):
+        markup = b"""<a class="my_class"><p></a>"""
+        soup = self.soup(markup)
+        a1, a2 = soup.find_all('a')
+        self.assertEqual(a1, a2)
+        assert a1 is not a2
diff --git a/lib/bs4/tests/test_htmlparser.py b/lib/bs4/tests/test_htmlparser.py
index bcb5ed2..b45e35f 100644
--- a/lib/bs4/tests/test_htmlparser.py
+++ b/lib/bs4/tests/test_htmlparser.py
@@ -1,6 +1,8 @@
 """Tests to ensure that the html.parser tree builder generates good
 trees."""
 
+from pdb import set_trace
+import pickle
 from bs4.testing import SoupTest, HTMLTreeBuilderSmokeTest
 from bs4.builder import HTMLParserTreeBuilder
 
@@ -17,3 +19,14 @@ class HTMLParserTreeBuilderSmokeTest(SoupTest, HTMLTreeBuilderSmokeTest):
     def test_namespaced_public_doctype(self):
         # html.parser can't handle namespaced doctypes, so skip this one.
         pass
+
+    def test_builder_is_pickled(self):
+        """Unlike most tree builders, HTMLParserTreeBuilder and will
+        be restored after pickling.
+        """
+        tree = self.soup("<a><b>foo</a>")
+        dumped = pickle.dumps(tree, 2)
+        loaded = pickle.loads(dumped)
+        self.assertTrue(isinstance(loaded.builder, type(tree.builder)))
+
+
diff --git a/lib/bs4/tests/test_lxml.py b/lib/bs4/tests/test_lxml.py
index 2b2e9b7..6c2a1d7 100644
--- a/lib/bs4/tests/test_lxml.py
+++ b/lib/bs4/tests/test_lxml.py
@@ -7,7 +7,7 @@ try:
     import lxml.etree
     LXML_PRESENT = True
     LXML_VERSION = lxml.etree.LXML_VERSION
-except ImportError, e:
+except ImportError as e:
     LXML_PRESENT = False
     LXML_VERSION = (0,)
 
@@ -62,24 +62,9 @@ class LXMLTreeBuilderSmokeTest(SoupTest, HTMLTreeBuilderSmokeTest):
         # if one is installed.
         with warnings.catch_warnings(record=True) as w:
             soup = BeautifulStoneSoup("<b />")
-        self.assertEqual(u"<b/>", unicode(soup.b))
+        self.assertEqual("<b/>", str(soup.b))
         self.assertTrue("BeautifulStoneSoup class is deprecated" in str(w[0].message))
 
-    def test_real_xhtml_document(self):
-        """lxml strips the XML definition from an XHTML doc, which is fine."""
-        markup = b"""<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head><title>Hello.</title></head>
-<body>Goodbye.</body>
-</html>"""
-        soup = self.soup(markup)
-        self.assertEqual(
-            soup.encode("utf-8").replace(b"\n", b''),
-            markup.replace(b'\n', b'').replace(
-                b'<?xml version="1.0" encoding="utf-8"?>', b''))
-
-
 @skipIf(
     not LXML_PRESENT,
     "lxml seems not to be present, not testing its XML tree builder.")
diff --git a/lib/bs4/tests/test_soup.py b/lib/bs4/tests/test_soup.py
index 47ac245..f87949e 100644
--- a/lib/bs4/tests/test_soup.py
+++ b/lib/bs4/tests/test_soup.py
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 """Tests of Beautiful Soup as a whole."""
 
+from pdb import set_trace
 import logging
 import unittest
 import sys
@@ -20,6 +21,7 @@ import bs4.dammit
 from bs4.dammit import (
     EntitySubstitution,
     UnicodeDammit,
+    EncodingDetector,
 )
 from bs4.testing import (
     SoupTest,
@@ -30,7 +32,7 @@ import warnings
 try:
     from bs4.builder import LXMLTreeBuilder, LXMLTreeBuilderForXML
     LXML_PRESENT = True
-except ImportError, e:
+except ImportError as e:
     LXML_PRESENT = False
 
 PYTHON_2_PRE_2_7 = (sys.version_info < (2,7))
@@ -39,17 +41,43 @@ PYTHON_3_PRE_3_2 = (sys.version_info[0] == 3 and sys.version_info < (3,2))
 class TestConstructor(SoupTest):
 
     def test_short_unicode_input(self):
-        data = u"<h1>éé</h1>"
+        data = "<h1>éé</h1>"
         soup = self.soup(data)
-        self.assertEqual(u"éé", soup.h1.string)
+        self.assertEqual("éé", soup.h1.string)
 
     def test_embedded_null(self):
-        data = u"<h1>foo\0bar</h1>"
+        data = "<h1>foo\0bar</h1>"
         soup = self.soup(data)
-        self.assertEqual(u"foo\0bar", soup.h1.string)
+        self.assertEqual("foo\0bar", soup.h1.string)
 
+    def test_exclude_encodings(self):
+        utf8_data = "Räksmörgås".encode("utf-8")
+        soup = self.soup(utf8_data, exclude_encodings=["utf-8"])
+        self.assertEqual("windows-1252", soup.original_encoding)
 
-class TestDeprecatedConstructorArguments(SoupTest):
+
+class TestWarnings(SoupTest):
+
+    def _no_parser_specified(self, s, is_there=True):
+        v = s.startswith(BeautifulSoup.NO_PARSER_SPECIFIED_WARNING[:80])
+        self.assertTrue(v)
+
+    def test_warning_if_no_parser_specified(self):
+        with warnings.catch_warnings(record=True) as w:
+            soup = self.soup("<a><b></b></a>")
+        msg = str(w[0].message)
+        self._assert_no_parser_specified(msg)
+
+    def test_warning_if_parser_specified_too_vague(self):
+        with warnings.catch_warnings(record=True) as w:
+            soup = self.soup("<a><b></b></a>", "html")
+        msg = str(w[0].message)
+        self._assert_no_parser_specified(msg)
+
+    def test_no_warning_if_explicit_parser_specified(self):
+        with warnings.catch_warnings(record=True) as w:
+            soup = self.soup("<a><b></b></a>", "html.parser")
+        self.assertEqual([], w)
 
     def test_parseOnlyThese_renamed_to_parse_only(self):
         with warnings.catch_warnings(record=True) as w:
@@ -117,9 +145,9 @@ class TestEntitySubstitution(unittest.TestCase):
     def test_simple_html_substitution(self):
         # Unicode characters corresponding to named HTML entites
         # are substituted, and no others.
-        s = u"foo\u2200\N{SNOWMAN}\u00f5bar"
+        s = "foo\u2200\N{SNOWMAN}\u00f5bar"
         self.assertEqual(self.sub.substitute_html(s),
-                          u"foo&forall;\N{SNOWMAN}&otilde;bar")
+                          "foo&forall;\N{SNOWMAN}&otilde;bar")
 
     def test_smart_quote_substitution(self):
         # MS smart quotes are a common source of frustration, so we
@@ -184,7 +212,7 @@ class TestEncodingConversion(SoupTest):
 
     def setUp(self):
         super(TestEncodingConversion, self).setUp()
-        self.unicode_data = u'<html><head><meta charset="utf-8"/></head><body><foo>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</foo></body></html>'
+        self.unicode_data = '<html><head><meta charset="utf-8"/></head><body><foo>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</foo></body></html>'
         self.utf8_data = self.unicode_data.encode("utf-8")
         # Just so you know what it looks like.
         self.assertEqual(
@@ -204,7 +232,7 @@ class TestEncodingConversion(SoupTest):
             ascii = b"<foo>a</foo>"
             soup_from_ascii = self.soup(ascii)
             unicode_output = soup_from_ascii.decode()
-            self.assertTrue(isinstance(unicode_output, unicode))
+            self.assertTrue(isinstance(unicode_output, str))
             self.assertEqual(unicode_output, self.document_for(ascii.decode()))
             self.assertEqual(soup_from_ascii.original_encoding.lower(), "utf-8")
         finally:
@@ -216,7 +244,7 @@ class TestEncodingConversion(SoupTest):
         # is not set.
         soup_from_unicode = self.soup(self.unicode_data)
         self.assertEqual(soup_from_unicode.decode(), self.unicode_data)
-        self.assertEqual(soup_from_unicode.foo.string, u'Sacr\xe9 bleu!')
+        self.assertEqual(soup_from_unicode.foo.string, 'Sacr\xe9 bleu!')
         self.assertEqual(soup_from_unicode.original_encoding, None)
 
     def test_utf8_in_unicode_out(self):
@@ -224,7 +252,7 @@ class TestEncodingConversion(SoupTest):
         # attribute is set.
         soup_from_utf8 = self.soup(self.utf8_data)
         self.assertEqual(soup_from_utf8.decode(), self.unicode_data)
-        self.assertEqual(soup_from_utf8.foo.string, u'Sacr\xe9 bleu!')
+        self.assertEqual(soup_from_utf8.foo.string, 'Sacr\xe9 bleu!')
 
     def test_utf8_out(self):
         # The internal data structures can be encoded as UTF-8.
@@ -235,14 +263,14 @@ class TestEncodingConversion(SoupTest):
         PYTHON_2_PRE_2_7 or PYTHON_3_PRE_3_2,
         "Bad HTMLParser detected; skipping test of non-ASCII characters in attribute name.")
     def test_attribute_name_containing_unicode_characters(self):
-        markup = u'<div><a \N{SNOWMAN}="snowman"></a></div>'
+        markup = '<div><a \N{SNOWMAN}="snowman"></a></div>'
         self.assertEqual(self.soup(markup).div.encode("utf8"), markup.encode("utf8"))
 
 class TestUnicodeDammit(unittest.TestCase):
     """Standalone tests of UnicodeDammit."""
 
     def test_unicode_input(self):
-        markup = u"I'm already Unicode! \N{SNOWMAN}"
+        markup = "I'm already Unicode! \N{SNOWMAN}"
         dammit = UnicodeDammit(markup)
         self.assertEqual(dammit.unicode_markup, markup)
 
@@ -250,7 +278,7 @@ class TestUnicodeDammit(unittest.TestCase):
         markup = b"<foo>\x91\x92\x93\x94</foo>"
         dammit = UnicodeDammit(markup)
         self.assertEqual(
-            dammit.unicode_markup, u"<foo>\u2018\u2019\u201c\u201d</foo>")
+            dammit.unicode_markup, "<foo>\u2018\u2019\u201c\u201d</foo>")
 
     def test_smart_quotes_to_xml_entities(self):
         markup = b"<foo>\x91\x92\x93\x94</foo>"
@@ -271,16 +299,17 @@ class TestUnicodeDammit(unittest.TestCase):
             dammit.unicode_markup, """<foo>''""</foo>""")
 
     def test_detect_utf8(self):
-        utf8 = b"\xc3\xa9"
+        utf8 = b"Sacr\xc3\xa9 bleu! \xe2\x98\x83"
         dammit = UnicodeDammit(utf8)
-        self.assertEqual(dammit.unicode_markup, u'\xe9')
         self.assertEqual(dammit.original_encoding.lower(), 'utf-8')
+        self.assertEqual(dammit.unicode_markup, 'Sacr\xe9 bleu! \N{SNOWMAN}')
+
 
     def test_convert_hebrew(self):
         hebrew = b"\xed\xe5\xec\xf9"
         dammit = UnicodeDammit(hebrew, ["iso-8859-8"])
         self.assertEqual(dammit.original_encoding.lower(), 'iso-8859-8')
-        self.assertEqual(dammit.unicode_markup, u'\u05dd\u05d5\u05dc\u05e9')
+        self.assertEqual(dammit.unicode_markup, '\u05dd\u05d5\u05dc\u05e9')
 
     def test_dont_see_smart_quotes_where_there_are_none(self):
         utf_8 = b"\343\202\261\343\203\274\343\202\277\343\202\244 Watch"
@@ -289,16 +318,36 @@ class TestUnicodeDammit(unittest.TestCase):
         self.assertEqual(dammit.unicode_markup.encode("utf-8"), utf_8)
 
     def test_ignore_inappropriate_codecs(self):
-        utf8_data = u"Räksmörgås".encode("utf-8")
+        utf8_data = "Räksmörgås".encode("utf-8")
         dammit = UnicodeDammit(utf8_data, ["iso-8859-8"])
         self.assertEqual(dammit.original_encoding.lower(), 'utf-8')
 
     def test_ignore_invalid_codecs(self):
-        utf8_data = u"Räksmörgås".encode("utf-8")
+        utf8_data = "Räksmörgås".encode("utf-8")
         for bad_encoding in ['.utf8', '...', 'utF---16.!']:
             dammit = UnicodeDammit(utf8_data, [bad_encoding])
             self.assertEqual(dammit.original_encoding.lower(), 'utf-8')
 
+    def test_exclude_encodings(self):
+        # This is UTF-8.
+        utf8_data = "Räksmörgås".encode("utf-8")
+
+        # But if we exclude UTF-8 from consideration, the guess is
+        # Windows-1252.
+        dammit = UnicodeDammit(utf8_data, exclude_encodings=["utf-8"])
+        self.assertEqual(dammit.original_encoding.lower(), 'windows-1252')
+
+        # And if we exclude that, there is no valid guess at all.
+        dammit = UnicodeDammit(
+            utf8_data, exclude_encodings=["utf-8", "windows-1252"])
+        self.assertEqual(dammit.original_encoding, None)
+
+    def test_encoding_detector_replaces_junk_in_encoding_name_with_replacement_character(self):
+        detected = EncodingDetector(
+            b'<?xml version="1.0" encoding="UTF-\xdb" ?>')
+        encodings = list(detected.encodings)
+        assert 'utf-\N{REPLACEMENT CHARACTER}' in encodings
+
     def test_detect_html5_style_meta_tag(self):
 
         for data in (
@@ -337,7 +386,7 @@ class TestUnicodeDammit(unittest.TestCase):
             bs4.dammit.chardet_dammit = noop
             dammit = UnicodeDammit(doc)
             self.assertEqual(True, dammit.contains_replacement_characters)
-            self.assertTrue(u"\ufffd" in dammit.unicode_markup)
+            self.assertTrue("\ufffd" in dammit.unicode_markup)
 
             soup = BeautifulSoup(doc, "html.parser")
             self.assertTrue(soup.contains_replacement_characters)
@@ -349,17 +398,17 @@ class TestUnicodeDammit(unittest.TestCase):
         # A document written in UTF-16LE will have its byte order marker stripped.
         data = b'\xff\xfe<\x00a\x00>\x00\xe1\x00\xe9\x00<\x00/\x00a\x00>\x00'
         dammit = UnicodeDammit(data)
-        self.assertEqual(u"<a>áé</a>", dammit.unicode_markup)
+        self.assertEqual("<a>áé</a>", dammit.unicode_markup)
         self.assertEqual("utf-16le", dammit.original_encoding)
 
     def test_detwingle(self):
         # Here's a UTF8 document.
-        utf8 = (u"\N{SNOWMAN}" * 3).encode("utf8")
+        utf8 = ("\N{SNOWMAN}" * 3).encode("utf8")
 
         # Here's a Windows-1252 document.
         windows_1252 = (
-            u"\N{LEFT DOUBLE QUOTATION MARK}Hi, I like Windows!"
-            u"\N{RIGHT DOUBLE QUOTATION MARK}").encode("windows_1252")
+            "\N{LEFT DOUBLE QUOTATION MARK}Hi, I like Windows!"
+            "\N{RIGHT DOUBLE QUOTATION MARK}").encode("windows_1252")
 
         # Through some unholy alchemy, they've been stuck together.
         doc = utf8 + windows_1252 + utf8
@@ -374,7 +423,7 @@ class TestUnicodeDammit(unittest.TestCase):
 
         fixed = UnicodeDammit.detwingle(doc)
         self.assertEqual(
-            u"☃☃☃“Hi, I like Windows!”☃☃☃", fixed.decode("utf8"))
+            "☃☃☃“Hi, I like Windows!”☃☃☃", fixed.decode("utf8"))
 
     def test_detwingle_ignores_multibyte_characters(self):
         # Each of these characters has a UTF-8 representation ending
@@ -382,9 +431,9 @@ class TestUnicodeDammit(unittest.TestCase):
         # Windows-1252. But our code knows to skip over multibyte
         # UTF-8 characters, so they'll survive the process unscathed.
         for tricky_unicode_char in (
-            u"\N{LATIN SMALL LIGATURE OE}", # 2-byte char '\xc5\x93'
-            u"\N{LATIN SUBSCRIPT SMALL LETTER X}", # 3-byte char '\xe2\x82\x93'
-            u"\xf0\x90\x90\x93", # This is a CJK character, not sure which one.
+            "\N{LATIN SMALL LIGATURE OE}", # 2-byte char '\xc5\x93'
+            "\N{LATIN SUBSCRIPT SMALL LETTER X}", # 3-byte char '\xe2\x82\x93'
+            "\xf0\x90\x90\x93", # This is a CJK character, not sure which one.
             ):
             input = tricky_unicode_char.encode("utf8")
             self.assertTrue(input.endswith(b'\x93'))
diff --git a/lib/bs4/tests/test_tree.py b/lib/bs4/tests/test_tree.py
index f8515c0..6d3e67f 100644
--- a/lib/bs4/tests/test_tree.py
+++ b/lib/bs4/tests/test_tree.py
@@ -9,6 +9,7 @@ same markup, but all Beautiful Soup trees can be traversed with the
 methods tested here.
 """
 
+from pdb import set_trace
 import copy
 import pickle
 import re
@@ -19,8 +20,10 @@ from bs4.builder import (
     HTMLParserTreeBuilder,
 )
 from bs4.element import (
+    PY3K,
     CData,
     Comment,
+    Declaration,
     Doctype,
     NavigableString,
     SoupStrainer,
@@ -67,8 +70,14 @@ class TestFind(TreeTest):
         self.assertEqual(soup.find("b").string, "2")
 
     def test_unicode_text_find(self):
-        soup = self.soup(u'<h1>Räksmörgås</h1>')
-        self.assertEqual(soup.find(text=u'Räksmörgås'), u'Räksmörgås')
+        soup = self.soup('<h1>Räksmörgås</h1>')
+        self.assertEqual(soup.find(string='Räksmörgås'), 'Räksmörgås')
+
+    def test_unicode_attribute_find(self):
+        soup = self.soup('<h1 id="Räksmörgås">here it is</h1>')
+        str(soup)
+        self.assertEqual("here it is", soup.find(id='Räksmörgås').text)
+
 
     def test_find_everything(self):
         """Test an optimization that finds all tags."""
@@ -87,16 +96,17 @@ class TestFindAll(TreeTest):
         """You can search the tree for text nodes."""
         soup = self.soup("<html>Foo<b>bar</b>\xbb</html>")
         # Exact match.
-        self.assertEqual(soup.find_all(text="bar"), [u"bar"])
+        self.assertEqual(soup.find_all(string="bar"), ["bar"])
+        self.assertEqual(soup.find_all(text="bar"), ["bar"])
         # Match any of a number of strings.
         self.assertEqual(
-            soup.find_all(text=["Foo", "bar"]), [u"Foo", u"bar"])
+            soup.find_all(text=["Foo", "bar"]), ["Foo", "bar"])
         # Match a regular expression.
         self.assertEqual(soup.find_all(text=re.compile('.*')),
-                         [u"Foo", u"bar", u'\xbb'])
+                         ["Foo", "bar", '\xbb'])
         # Match anything.
         self.assertEqual(soup.find_all(text=True),
-                         [u"Foo", u"bar", u'\xbb'])
+                         ["Foo", "bar", '\xbb'])
 
     def test_find_all_limit(self):
         """You can limit the number of items returned by find_all."""
@@ -227,8 +237,8 @@ class TestFindAllByAttribute(TreeTest):
                            ["Matching a.", "Matching b."])
 
     def test_find_all_by_utf8_attribute_value(self):
-        peace = u"םולש".encode("utf8")
-        data = u'<a title="םולש"></a>'.encode("utf8")
+        peace = "םולש".encode("utf8")
+        data = '<a title="םולש"></a>'.encode("utf8")
         soup = self.soup(data)
         self.assertEqual([soup.a], soup.find_all(title=peace))
         self.assertEqual([soup.a], soup.find_all(title=peace.decode("utf8")))
@@ -688,7 +698,7 @@ class TestTagCreation(SoupTest):
 
     def test_tag_inherits_self_closing_rules_from_builder(self):
         if XML_BUILDER_PRESENT:
-            xml_soup = BeautifulSoup("", "xml")
+            xml_soup = BeautifulSoup("", "lxml-xml")
             xml_br = xml_soup.new_tag("br")
             xml_p = xml_soup.new_tag("p")
 
@@ -697,7 +707,7 @@ class TestTagCreation(SoupTest):
             self.assertEqual(b"<br/>", xml_br.encode())
             self.assertEqual(b"<p/>", xml_p.encode())
 
-        html_soup = BeautifulSoup("", "html")
+        html_soup = BeautifulSoup("", "html.parser")
         html_br = html_soup.new_tag("br")
         html_p = html_soup.new_tag("p")
 
@@ -773,6 +783,14 @@ class TestTreeModification(SoupTest):
         new_a = a.unwrap()
         self.assertEqual(a, new_a)
 
+    def test_replace_with_and_unwrap_give_useful_exception_when_tag_has_no_parent(self):
+        soup = self.soup("<a><b>Foo</b></a><c>Bar</c>")
+        a = soup.a
+        a.extract()
+        self.assertEqual(None, a.parent)
+        self.assertRaises(ValueError, a.unwrap)
+        self.assertRaises(ValueError, a.replace_with, soup.c)
+
     def test_replace_tag_with_itself(self):
         text = "<a><b></b><c>Foo<d></d></c></a><a><e></e></a>"
         soup = self.soup(text)
@@ -1067,6 +1085,31 @@ class TestTreeModification(SoupTest):
         self.assertEqual(foo_2, soup.a.string)
         self.assertEqual(bar_2, soup.b.string)
 
+    def test_extract_multiples_of_same_tag(self):
+        soup = self.soup("""
+<html>
+<head>
+<script>foo</script>
+</head>
+<body>
+ <script>bar</script>
+ <a></a>
+</body>
+<script>baz</script>
+</html>""")
+        [soup.script.extract() for i in soup.find_all("script")]
+        self.assertEqual("<body>\n\n<a></a>\n</body>", str(soup.body))
+
+
+    def test_extract_works_when_element_is_surrounded_by_identical_strings(self):
+        soup = self.soup(
+ '<html>\n'
+ '<body>hi</body>\n'
+ '</html>')
+        soup.find('body').extract()
+        self.assertEqual(None, soup.find('body'))
+
+
     def test_clear(self):
         """Tag.clear()"""
         soup = self.soup("<p><a>String <em>Italicized</em></a> and another</p>")
@@ -1287,27 +1330,72 @@ class TestPersistence(SoupTest):
 
     def test_unicode_pickle(self):
         # A tree containing Unicode characters can be pickled.
-        html = u"<b>\N{SNOWMAN}</b>"
+        html = "<b>\N{SNOWMAN}</b>"
         soup = self.soup(html)
         dumped = pickle.dumps(soup, pickle.HIGHEST_PROTOCOL)
         loaded = pickle.loads(dumped)
         self.assertEqual(loaded.decode(), soup.decode())
 
+    def test_copy_navigablestring_is_not_attached_to_tree(self):
+        html = "<b>Foo<a></a></b><b>Bar</b>"
+        soup = self.soup(html)
+        s1 = soup.find(string="Foo")
+        s2 = copy.copy(s1)
+        self.assertEqual(s1, s2)
+        self.assertEqual(None, s2.parent)
+        self.assertEqual(None, s2.next_element)
+        self.assertNotEqual(None, s1.next_sibling)
+        self.assertEqual(None, s2.next_sibling)
+        self.assertEqual(None, s2.previous_element)
+
+    def test_copy_navigablestring_subclass_has_same_type(self):
+        html = "<b><!--Foo--></b>"
+        soup = self.soup(html)
+        s1 = soup.string
+        s2 = copy.copy(s1)
+        self.assertEqual(s1, s2)
+        self.assertTrue(isinstance(s2, Comment))
+
+    def test_copy_entire_soup(self):
+        html = "<div><b>Foo<a></a></b><b>Bar</b></div>end"
+        soup = self.soup(html)
+        soup_copy = copy.copy(soup)
+        self.assertEqual(soup, soup_copy)
+
+    def test_copy_tag_copies_contents(self):
+        html = "<div><b>Foo<a></a></b><b>Bar</b></div>end"
+        soup = self.soup(html)
+        div = soup.div
+        div_copy = copy.copy(div)
+
+        # The two tags look the same, and evaluate to equal.
+        self.assertEqual(str(div), str(div_copy))
+        self.assertEqual(div, div_copy)
+
+        # But they're not the same object.
+        self.assertFalse(div is div_copy)
+
+        # And they don't have the same relation to the parse tree. The
+        # copy is not associated with a parse tree at all.
+        self.assertEqual(None, div_copy.parent)
+        self.assertEqual(None, div_copy.previous_element)
+        self.assertEqual(None, div_copy.find(string='Bar').next_element)
+        self.assertNotEqual(None, div.find(string='Bar').next_element)
 
 class TestSubstitutions(SoupTest):
 
     def test_default_formatter_is_minimal(self):
-        markup = u"<b>&lt;&lt;Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</b>"
+        markup = "<b>&lt;&lt;Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</b>"
         soup = self.soup(markup)
         decoded = soup.decode(formatter="minimal")
         # The < is converted back into &lt; but the e-with-acute is left alone.
         self.assertEqual(
             decoded,
             self.document_for(
-                u"<b>&lt;&lt;Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</b>"))
+                "<b>&lt;&lt;Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</b>"))
 
     def test_formatter_html(self):
-        markup = u"<b>&lt;&lt;Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</b>"
+        markup = "<b>&lt;&lt;Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</b>"
         soup = self.soup(markup)
         decoded = soup.decode(formatter="html")
         self.assertEqual(
@@ -1315,49 +1403,49 @@ class TestSubstitutions(SoupTest):
             self.document_for("<b>&lt;&lt;Sacr&eacute; bleu!&gt;&gt;</b>"))
 
     def test_formatter_minimal(self):
-        markup = u"<b>&lt;&lt;Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</b>"
+        markup = "<b>&lt;&lt;Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</b>"
         soup = self.soup(markup)
         decoded = soup.decode(formatter="minimal")
         # The < is converted back into &lt; but the e-with-acute is left alone.
         self.assertEqual(
             decoded,
             self.document_for(
-                u"<b>&lt;&lt;Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</b>"))
+                "<b>&lt;&lt;Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</b>"))
 
     def test_formatter_null(self):
-        markup = u"<b>&lt;&lt;Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</b>"
+        markup = "<b>&lt;&lt;Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!&gt;&gt;</b>"
         soup = self.soup(markup)
         decoded = soup.decode(formatter=None)
         # Neither the angle brackets nor the e-with-acute are converted.
         # This is not valid HTML, but it's what the user wanted.
         self.assertEqual(decoded,
-                          self.document_for(u"<b><<Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>></b>"))
+                          self.document_for("<b><<Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>></b>"))
 
     def test_formatter_custom(self):
-        markup = u"<b>&lt;foo&gt;</b><b>bar</b>"
+        markup = "<b>&lt;foo&gt;</b><b>bar</b>"
         soup = self.soup(markup)
         decoded = soup.decode(formatter = lambda x: x.upper())
         # Instead of normal entity conversion code, the custom
         # callable is called on every string.
         self.assertEqual(
             decoded,
-            self.document_for(u"<b><FOO></b><b>BAR</b>"))
+            self.document_for("<b><FOO></b><b>BAR</b>"))
 
     def test_formatter_is_run_on_attribute_values(self):
-        markup = u'<a href="http://a.com?a=b&c=é">e</a>'
+        markup = '<a href="http://a.com?a=b&c=é">e</a>'
         soup = self.soup(markup)
         a = soup.a
 
-        expect_minimal = u'<a href="http://a.com?a=b&amp;c=é">e</a>'
+        expect_minimal = '<a href="http://a.com?a=b&amp;c=é">e</a>'
 
         self.assertEqual(expect_minimal, a.decode())
         self.assertEqual(expect_minimal, a.decode(formatter="minimal"))
 
-        expect_html = u'<a href="http://a.com?a=b&amp;c=&eacute;">e</a>'
+        expect_html = '<a href="http://a.com?a=b&amp;c=&eacute;">e</a>'
         self.assertEqual(expect_html, a.decode(formatter="html"))
 
         self.assertEqual(markup, a.decode(formatter=None))
-        expect_upper = u'<a href="HTTP://A.COM?A=B&C=É">E</a>'
+        expect_upper = '<a href="HTTP://A.COM?A=B&C=É">E</a>'
         self.assertEqual(expect_upper, a.decode(formatter=lambda x: x.upper()))
 
     def test_formatter_skips_script_tag_for_html_documents(self):
@@ -1366,7 +1454,7 @@ class TestSubstitutions(SoupTest):
    console.log("< < hey > > ");
   </script>
 """
-        encoded = BeautifulSoup(doc).encode()
+        encoded = BeautifulSoup(doc, 'html.parser').encode()
         self.assertTrue(b"< < hey > >" in encoded)
 
     def test_formatter_skips_style_tag_for_html_documents(self):
@@ -1375,7 +1463,7 @@ class TestSubstitutions(SoupTest):
    console.log("< < hey > > ");
   </style>
 """
-        encoded = BeautifulSoup(doc).encode()
+        encoded = BeautifulSoup(doc, 'html.parser').encode()
         self.assertTrue(b"< < hey > >" in encoded)
 
     def test_prettify_leaves_preformatted_text_alone(self):
@@ -1383,24 +1471,24 @@ class TestSubstitutions(SoupTest):
         # Everything outside the <pre> tag is reformatted, but everything
         # inside is left alone.
         self.assertEqual(
-            u'<div>\n foo\n <pre>  \tbar\n  \n  </pre>\n baz\n</div>',
+            '<div>\n foo\n <pre>  \tbar\n  \n  </pre>\n baz\n</div>',
             soup.div.prettify())
 
     def test_prettify_accepts_formatter(self):
-        soup = BeautifulSoup("<html><body>foo</body></html>")
+        soup = BeautifulSoup("<html><body>foo</body></html>", 'html.parser')
         pretty = soup.prettify(formatter = lambda x: x.upper())
         self.assertTrue("FOO" in pretty)
 
     def test_prettify_outputs_unicode_by_default(self):
         soup = self.soup("<a></a>")
-        self.assertEqual(unicode, type(soup.prettify()))
+        self.assertEqual(str, type(soup.prettify()))
 
     def test_prettify_can_encode_data(self):
         soup = self.soup("<a></a>")
         self.assertEqual(bytes, type(soup.prettify("utf-8")))
 
     def test_html_entity_substitution_off_by_default(self):
-        markup = u"<b>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</b>"
+        markup = "<b>Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!</b>"
         soup = self.soup(markup)
         encoded = soup.b.encode("utf-8")
         self.assertEqual(encoded, markup.encode('utf-8'))
@@ -1444,45 +1532,53 @@ class TestEncoding(SoupTest):
     """Test the ability to encode objects into strings."""
 
     def test_unicode_string_can_be_encoded(self):
-        html = u"<b>\N{SNOWMAN}</b>"
+        html = "<b>\N{SNOWMAN}</b>"
         soup = self.soup(html)
         self.assertEqual(soup.b.string.encode("utf-8"),
-                          u"\N{SNOWMAN}".encode("utf-8"))
+                          "\N{SNOWMAN}".encode("utf-8"))
 
     def test_tag_containing_unicode_string_can_be_encoded(self):
-        html = u"<b>\N{SNOWMAN}</b>"
+        html = "<b>\N{SNOWMAN}</b>"
         soup = self.soup(html)
         self.assertEqual(
             soup.b.encode("utf-8"), html.encode("utf-8"))
 
     def test_encoding_substitutes_unrecognized_characters_by_default(self):
-        html = u"<b>\N{SNOWMAN}</b>"
+        html = "<b>\N{SNOWMAN}</b>"
         soup = self.soup(html)
         self.assertEqual(soup.b.encode("ascii"), b"<b>&#9731;</b>")
 
     def test_encoding_can_be_made_strict(self):
-        html = u"<b>\N{SNOWMAN}</b>"
+        html = "<b>\N{SNOWMAN}</b>"
         soup = self.soup(html)
         self.assertRaises(
             UnicodeEncodeError, soup.encode, "ascii", errors="strict")
 
     def test_decode_contents(self):
-        html = u"<b>\N{SNOWMAN}</b>"
+        html = "<b>\N{SNOWMAN}</b>"
         soup = self.soup(html)
-        self.assertEqual(u"\N{SNOWMAN}", soup.b.decode_contents())
+        self.assertEqual("\N{SNOWMAN}", soup.b.decode_contents())
 
     def test_encode_contents(self):
-        html = u"<b>\N{SNOWMAN}</b>"
+        html = "<b>\N{SNOWMAN}</b>"
         soup = self.soup(html)
         self.assertEqual(
-            u"\N{SNOWMAN}".encode("utf8"), soup.b.encode_contents(
+            "\N{SNOWMAN}".encode("utf8"), soup.b.encode_contents(
                 encoding="utf8"))
 
     def test_deprecated_renderContents(self):
-        html = u"<b>\N{SNOWMAN}</b>"
+        html = "<b>\N{SNOWMAN}</b>"
         soup = self.soup(html)
         self.assertEqual(
-            u"\N{SNOWMAN}".encode("utf8"), soup.b.renderContents())
+            "\N{SNOWMAN}".encode("utf8"), soup.b.renderContents())
+
+    def test_repr(self):
+        html = "<b>\N{SNOWMAN}</b>"
+        soup = self.soup(html)
+        if PY3K:
+            self.assertEqual(html, repr(soup))
+        else:
+            self.assertEqual(b'<b>\\u2603</b>', repr(soup))
 
 class TestNavigableStringSubclasses(SoupTest):
 
@@ -1522,6 +1618,9 @@ class TestNavigableStringSubclasses(SoupTest):
         soup.insert(1, doctype)
         self.assertEqual(soup.encode(), b"<!DOCTYPE foo>\n")
 
+    def test_declaration(self):
+        d = Declaration("foo")
+        self.assertEqual("<?foo?>", d.output_ready())
 
 class TestSoupSelector(TreeTest):
 
@@ -1534,7 +1633,7 @@ class TestSoupSelector(TreeTest):
 <link rel="stylesheet" href="blah.css" type="text/css" id="l1">
 </head>
 <body>
-
+<custom-dashed-tag class="dashed" id="dash1">Hello there.</custom-dashed-tag>
 <div id="main" class="fancy">
 <div id="inner">
 <h1 id="header1">An H1</h1>
@@ -1552,8 +1651,18 @@ class TestSoupSelector(TreeTest):
 <a href="#" id="s2a1">span2a1</a>
 </span>
 <span class="span3"></span>
+<custom-dashed-tag class="dashed" id="dash2"/>
+<div data-tag="dashedvalue" id="data1"/>
 </span>
 </div>
+<x id="xid">
+<z id="zida"/>
+<z id="zidab"/>
+<z id="zidac"/>
+</x>
+<y id="yid">
+<z id="zidb"/>
+</y>
 <p lang="en" id="lang-en">English</p>
 <p lang="en-gb" id="lang-en-gb">English UK</p>
 <p lang="en-us" id="lang-en-us">English US</p>
@@ -1565,7 +1674,7 @@ class TestSoupSelector(TreeTest):
 """
 
     def setUp(self):
-        self.soup = BeautifulSoup(self.HTML)
+        self.soup = BeautifulSoup(self.HTML, 'html.parser')
 
     def assertSelects(self, selector, expected_ids):
         el_ids = [el['id'] for el in self.soup.select(selector)]
@@ -1587,21 +1696,29 @@ class TestSoupSelector(TreeTest):
         els = self.soup.select('title')
         self.assertEqual(len(els), 1)
         self.assertEqual(els[0].name, 'title')
-        self.assertEqual(els[0].contents, [u'The title'])
+        self.assertEqual(els[0].contents, ['The title'])
 
     def test_one_tag_many(self):
         els = self.soup.select('div')
-        self.assertEqual(len(els), 3)
+        self.assertEqual(len(els), 4)
         for div in els:
             self.assertEqual(div.name, 'div')
 
+        el = self.soup.select_one('div')
+        self.assertEqual('main', el['id'])
+
+    def test_select_one_returns_none_if_no_match(self):
+        match = self.soup.select_one('nonexistenttag')
+        self.assertEqual(None, match)
+
+
     def test_tag_in_tag_one(self):
         els = self.soup.select('div div')
-        self.assertSelects('div div', ['inner'])
+        self.assertSelects('div div', ['inner', 'data1'])
 
     def test_tag_in_tag_many(self):
         for selector in ('html div', 'html body div', 'body div'):
-            self.assertSelects(selector, ['main', 'inner', 'footer'])
+            self.assertSelects(selector, ['data1', 'main', 'inner', 'footer'])
 
     def test_tag_no_match(self):
         self.assertEqual(len(self.soup.select('del')), 0)
@@ -1609,6 +1726,20 @@ class TestSoupSelector(TreeTest):
     def test_invalid_tag(self):
         self.assertRaises(ValueError, self.soup.select, 'tag%t')
 
+    def test_select_dashed_tag_ids(self):
+        self.assertSelects('custom-dashed-tag', ['dash1', 'dash2'])
+
+    def test_select_dashed_by_id(self):
+        dashed = self.soup.select('custom-dashed-tag[id=\"dash2\"]')
+        self.assertEqual(dashed[0].name, 'custom-dashed-tag')
+        self.assertEqual(dashed[0]['id'], 'dash2')
+
+    def test_dashed_tag_text(self):
+        self.assertEqual(self.soup.select('body > custom-dashed-tag')[0].text, 'Hello there.')
+
+    def test_select_dashed_matches_find_all(self):
+        self.assertEqual(self.soup.select('custom-dashed-tag'), self.soup.find_all('custom-dashed-tag'))
+
     def test_header_tags(self):
         self.assertSelectMultiple(
             ('h1', ['header1']),
@@ -1709,6 +1840,7 @@ class TestSoupSelector(TreeTest):
             ('[id^="m"]', ['me', 'main']),
             ('div[id^="m"]', ['main']),
             ('a[id^="m"]', ['me']),
+            ('div[data-tag^="dashed"]', ['data1'])
         )
 
     def test_attribute_endswith(self):
@@ -1716,8 +1848,8 @@ class TestSoupSelector(TreeTest):
             ('[href$=".css"]', ['l1']),
             ('link[href$=".css"]', ['l1']),
             ('link[id$="1"]', ['l1']),
-            ('[id$="1"]', ['l1', 'p1', 'header1', 's1a1', 's2a1', 's1a2s1']),
-            ('div[id$="1"]', []),
+            ('[id$="1"]', ['data1', 'l1', 'p1', 'header1', 's1a1', 's2a1', 's1a2s1', 'dash1']),
+            ('div[id$="1"]', ['data1']),
             ('[id$="noending"]', []),
         )
 
@@ -1730,7 +1862,6 @@ class TestSoupSelector(TreeTest):
             ('[rel*="notstyle"]', []),
             ('link[rel*="notstyle"]', []),
             ('link[href*="bla"]', ['l1']),
-            ('a[href*="http://"]', ['bob', 'me']),
             ('[href*="http://"]', ['bob', 'me']),
             ('[id*="p"]', ['pmulti', 'p1']),
             ('div[id*="m"]', ['main']),
@@ -1739,8 +1870,8 @@ class TestSoupSelector(TreeTest):
             ('[href*=".css"]', ['l1']),
             ('link[href*=".css"]', ['l1']),
             ('link[id*="1"]', ['l1']),
-            ('[id*="1"]', ['l1', 'p1', 'header1', 's1a1', 's1a2', 's2a1', 's1a2s1']),
-            ('div[id*="1"]', []),
+            ('[id*="1"]', ['data1', 'l1', 'p1', 'header1', 's1a1', 's1a2', 's2a1', 's1a2s1', 'dash1']),
+            ('div[id*="1"]', ['data1']),
             ('[id*="noending"]', []),
             # New for this test
             ('[href*="."]', ['bob', 'me', 'l1']),
@@ -1748,6 +1879,7 @@ class TestSoupSelector(TreeTest):
             ('link[href*="."]', ['l1']),
             ('div[id*="n"]', ['main', 'inner']),
             ('div[id*="nn"]', ['inner']),
+            ('div[data-tag*="edval"]', ['data1'])
         )
 
     def test_attribute_exact_or_hypen(self):
@@ -1767,18 +1899,27 @@ class TestSoupSelector(TreeTest):
             ('p[class]', ['p1', 'pmulti']),
             ('[blah]', []),
             ('p[blah]', []),
+            ('div[data-tag]', ['data1'])
         )
 
+    def test_unsupported_pseudoclass(self):
+        self.assertRaises(
+            NotImplementedError, self.soup.select, "a:no-such-pseudoclass")
+
+        self.assertRaises(
+            NotImplementedError, self.soup.select, "a:nth-of-type(a)")
+
+
     def test_nth_of_type(self):
         # Try to select first paragraph
         els = self.soup.select('div#inner p:nth-of-type(1)')
         self.assertEqual(len(els), 1)
-        self.assertEqual(els[0].string, u'Some text')
+        self.assertEqual(els[0].string, 'Some text')
 
         # Try to select third paragraph
         els = self.soup.select('div#inner p:nth-of-type(3)')
         self.assertEqual(len(els), 1)
-        self.assertEqual(els[0].string, u'Another')
+        self.assertEqual(els[0].string, 'Another')
 
         # Try to select (non-existent!) fourth paragraph
         els = self.soup.select('div#inner p:nth-of-type(4)')
@@ -1791,7 +1932,7 @@ class TestSoupSelector(TreeTest):
     def test_nth_of_type_direct_descendant(self):
         els = self.soup.select('div#inner > p:nth-of-type(1)')
         self.assertEqual(len(els), 1)
-        self.assertEqual(els[0].string, u'Some text')
+        self.assertEqual(els[0].string, 'Some text')
 
     def test_id_child_selector_nth_of_type(self):
         self.assertSelects('#inner > p:nth-of-type(2)', ['p1'])
@@ -1803,7 +1944,7 @@ class TestSoupSelector(TreeTest):
         selected = inner.select("div")
         # The <div id="inner"> tag was selected. The <div id="footer">
         # tag was not.
-        self.assertSelectsIDs(selected, ['inner'])
+        self.assertSelectsIDs(selected, ['inner', 'data1'])
 
     def test_overspecified_child_id(self):
         self.assertSelects(".fancy #inner", ['inner'])
@@ -1827,3 +1968,44 @@ class TestSoupSelector(TreeTest):
 
     def test_sibling_combinator_wont_select_same_tag_twice(self):
         self.assertSelects('p[lang] ~ p', ['lang-en-gb', 'lang-en-us', 'lang-fr'])
+
+    # Test the selector grouping operator (the comma)
+    def test_multiple_select(self):
+        self.assertSelects('x, y', ['xid', 'yid'])
+
+    def test_multiple_select_with_no_space(self):
+        self.assertSelects('x,y', ['xid', 'yid'])
+
+    def test_multiple_select_with_more_space(self):
+        self.assertSelects('x,    y', ['xid', 'yid'])
+
+    def test_multiple_select_duplicated(self):
+        self.assertSelects('x, x', ['xid'])
+
+    def test_multiple_select_sibling(self):
+        self.assertSelects('x, y ~ p[lang=fr]', ['xid', 'lang-fr'])
+
+    def test_multiple_select_tag_and_direct_descendant(self):
+        self.assertSelects('x, y > z', ['xid', 'zidb'])
+
+    def test_multiple_select_direct_descendant_and_tags(self):
+        self.assertSelects('div > x, y, z', ['xid', 'yid', 'zida', 'zidb', 'zidab', 'zidac'])
+
+    def test_multiple_select_indirect_descendant(self):
+        self.assertSelects('div x,y,  z', ['xid', 'yid', 'zida', 'zidb', 'zidab', 'zidac'])
+
+    def test_invalid_multiple_select(self):
+        self.assertRaises(ValueError, self.soup.select, ',x, y')
+        self.assertRaises(ValueError, self.soup.select, 'x,,y')
+
+    def test_multiple_select_attrs(self):
+        self.assertSelects('p[lang=en], p[lang=en-gb]', ['lang-en', 'lang-en-gb'])
+
+    def test_multiple_select_ids(self):
+        self.assertSelects('x, y > z[id=zida], z[id=zidab], z[id=zidb]', ['xid', 'zidb', 'zidab'])
+
+    def test_multiple_select_nested(self):
+        self.assertSelects('body > div > x, y > z', ['xid', 'zidb'])
+
+
+
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 02/28] bitbake/pyinotify.py: Upgrade to py3 version
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 03/28] bitbake: Drop futures usage since we're python 3 Richard Purdie
                   ` (25 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/pyinotify.py | 256 ++++++++++++++++++++++---------------------------------
 1 file changed, 101 insertions(+), 155 deletions(-)

diff --git a/lib/pyinotify.py b/lib/pyinotify.py
index 2dae002..4eb03b0 100644
--- a/lib/pyinotify.py
+++ b/lib/pyinotify.py
@@ -42,13 +42,14 @@ class UnsupportedPythonVersionError(PyinotifyError):
         @param version: Current Python version
         @type version: string
         """
-        err = 'Python %s is unsupported, requires at least Python 2.4'
-        PyinotifyError.__init__(self, err % version)
+        PyinotifyError.__init__(self,
+                                ('Python %s is unsupported, requires '
+                                 'at least Python 3.0') % version)
 
 
 # Check Python version
 import sys
-if sys.version_info < (2, 4):
+if sys.version_info < (3, 0):
     raise UnsupportedPythonVersionError(sys.version)
 
 
@@ -68,6 +69,8 @@ from datetime import datetime, timedelta
 import time
 import re
 import asyncore
+import glob
+import locale
 import subprocess
 
 try:
@@ -76,12 +79,6 @@ except ImportError:
     pass  # Will fail on Python 2.4 which has reduce() builtin anyway.
 
 try:
-    from glob import iglob as glob
-except ImportError:
-    # Python 2.4 does not have glob.iglob().
-    from glob import glob as glob
-
-try:
     import ctypes
     import ctypes.util
 except ImportError:
@@ -95,9 +92,7 @@ except ImportError:
 
 __author__ = "seb@dbzteam.org (Sebastien Martini)"
 
-__version__ = "0.9.5"
-
-__metaclass__ = type  # Use new-style classes by default
+__version__ = "0.9.6"
 
 
 # Compatibity mode: set to True to improve compatibility with
@@ -122,6 +117,9 @@ class INotifyWrapper:
     """
     @staticmethod
     def create():
+        """
+        Factory method instanciating and returning the right wrapper.
+        """
         # First, try to use ctypes.
         if ctypes:
             inotify = _CtypesLibcINotifyWrapper()
@@ -173,7 +171,7 @@ class _INotifySyscallsWrapper(INotifyWrapper):
     def _inotify_init(self):
         try:
             fd = inotify_syscalls.inotify_init()
-        except IOError, err:
+        except IOError as err:
             self._last_errno = err.errno
             return -1
         return fd
@@ -181,7 +179,7 @@ class _INotifySyscallsWrapper(INotifyWrapper):
     def _inotify_add_watch(self, fd, pathname, mask):
         try:
             wd = inotify_syscalls.inotify_add_watch(fd, pathname, mask)
-        except IOError, err:
+        except IOError as err:
             self._last_errno = err.errno
             return -1
         return wd
@@ -189,7 +187,7 @@ class _INotifySyscallsWrapper(INotifyWrapper):
     def _inotify_rm_watch(self, fd, wd):
         try:
             ret = inotify_syscalls.inotify_rm_watch(fd, wd)
-        except IOError, err:
+        except IOError as err:
             self._last_errno = err.errno
             return -1
         return ret
@@ -213,17 +211,8 @@ class _CtypesLibcINotifyWrapper(INotifyWrapper):
         except (OSError, IOError):
             pass  # Will attemp to load it with None anyway.
 
-        if sys.version_info >= (2, 6):
-            self._libc = ctypes.CDLL(libc_name, use_errno=True)
-            self._get_errno_func = ctypes.get_errno
-        else:
-            self._libc = ctypes.CDLL(libc_name)
-            try:
-                location = self._libc.__errno_location
-                location.restype = ctypes.POINTER(ctypes.c_int)
-                self._get_errno_func = lambda: location().contents.value
-            except AttributeError:
-                pass
+        self._libc = ctypes.CDLL(libc_name, use_errno=True)
+        self._get_errno_func = ctypes.get_errno
 
         # Eventually check that libc has needed inotify bindings.
         if (not hasattr(self._libc, 'inotify_init') or
@@ -241,9 +230,8 @@ class _CtypesLibcINotifyWrapper(INotifyWrapper):
         return True
 
     def _get_errno(self):
-        if self._get_errno_func is not None:
-            return self._get_errno_func()
-        return None
+        assert self._get_errno_func
+        return self._get_errno_func()
 
     def _inotify_init(self):
         assert self._libc is not None
@@ -251,6 +239,11 @@ class _CtypesLibcINotifyWrapper(INotifyWrapper):
 
     def _inotify_add_watch(self, fd, pathname, mask):
         assert self._libc is not None
+        # Encodes path to a bytes string. This conversion seems required because
+        # ctypes.create_string_buffer seems to manipulate bytes internally.
+        # Moreover it seems that inotify_add_watch does not work very well when
+        # it receives an ctypes.create_unicode_buffer instance as argument.
+        pathname = pathname.encode(sys.getfilesystemencoding())
         pathname = ctypes.create_string_buffer(pathname)
         return self._libc.inotify_add_watch(fd, pathname, mask)
 
@@ -258,10 +251,6 @@ class _CtypesLibcINotifyWrapper(INotifyWrapper):
         assert self._libc is not None
         return self._libc.inotify_rm_watch(fd, wd)
 
-    def _sysctl(self, *args):
-        assert self._libc is not None
-        return self._libc.sysctl(*args)
-
 
 # Logging
 def logger_init():
@@ -278,97 +267,58 @@ log = logger_init()
 
 
 # inotify's variables
-class SysCtlINotify:
+class ProcINotify:
     """
-    Access (read, write) inotify's variables through sysctl. Usually it
-    requires administrator rights to update them.
+    Access (read, write) inotify's variables through /proc/sys/. Note that
+    usually it requires administrator rights to update them.
 
     Examples:
       - Read max_queued_events attribute: myvar = max_queued_events.value
       - Update max_queued_events attribute: max_queued_events.value = 42
     """
-
-    inotify_attrs = {'max_user_instances': 1,
-                     'max_user_watches': 2,
-                     'max_queued_events': 3}
-
-    def __init__(self, attrname, inotify_wrapper):
-        # FIXME: right now only supporting ctypes
-        assert ctypes
-        self._attrname = attrname
-        self._inotify_wrapper = inotify_wrapper
-        sino = ctypes.c_int * 3
-        self._attr = sino(5, 20, SysCtlINotify.inotify_attrs[attrname])
-
-    @staticmethod
-    def create(attrname):
-        """
-        Factory method instanciating and returning the right wrapper.
-        """
-        # FIXME: right now only supporting ctypes
-        if ctypes is None:
-            return None
-        inotify_wrapper = _CtypesLibcINotifyWrapper()
-        if not inotify_wrapper.init():
-            return None
-        return SysCtlINotify(attrname, inotify_wrapper)
+    def __init__(self, attr):
+        self._base = "/proc/sys/fs/inotify"
+        self._attr = attr
 
     def get_val(self):
         """
-        Gets attribute's value. Raises OSError if the operation failed.
+        Gets attribute's value.
 
         @return: stored value.
         @rtype: int
+        @raise IOError: if corresponding file in /proc/sys cannot be read.
         """
-        oldv = ctypes.c_int(0)
-        size = ctypes.c_int(ctypes.sizeof(oldv))
-        sysctl = self._inotify_wrapper._sysctl
-        res = sysctl(self._attr, 3,
-                     ctypes.c_voidp(ctypes.addressof(oldv)),
-                     ctypes.addressof(size),
-                     None, 0)
-        if res == -1:
-            raise OSError(self._inotify_wrapper.get_errno(),
-                          self._inotify_wrapper.str_errno())
-        return oldv.value
+        with open(os.path.join(self._base, self._attr), 'r') as file_obj:
+            return int(file_obj.readline())
 
     def set_val(self, nval):
         """
-        Sets new attribute's value. Raises OSError if the operation failed.
+        Sets new attribute's value.
 
         @param nval: replaces current value by nval.
         @type nval: int
+        @raise IOError: if corresponding file in /proc/sys cannot be written.
         """
-        oldv = ctypes.c_int(0)
-        sizeo = ctypes.c_int(ctypes.sizeof(oldv))
-        newv = ctypes.c_int(nval)
-        sizen = ctypes.c_int(ctypes.sizeof(newv))
-        sysctl = self._inotify_wrapper._sysctl
-        res = sysctl(self._attr, 3,
-                     ctypes.c_voidp(ctypes.addressof(oldv)),
-                     ctypes.addressof(sizeo),
-                     ctypes.c_voidp(ctypes.addressof(newv)),
-                     sizen)
-        if res == -1:
-            raise OSError(self._inotify_wrapper.get_errno(),
-                          self._inotify_wrapper.str_errno())
+        with open(os.path.join(self._base, self._attr), 'w') as file_obj:
+            file_obj.write(str(nval) + '\n')
 
     value = property(get_val, set_val)
 
     def __repr__(self):
-        return '<%s=%d>' % (self._attrname, self.get_val())
+        return '<%s=%d>' % (self._attr, self.get_val())
 
 
 # Inotify's variables
 #
-# FIXME: currently these variables are only accessible when ctypes is used,
-#        otherwise there are set to None.
+# Note: may raise IOError if the corresponding value in /proc/sys
+#       cannot be accessed.
 #
-# read: myvar = max_queued_events.value
-# update: max_queued_events.value = 42
+# Examples:
+#  - read: myvar = max_queued_events.value
+#  - update: max_queued_events.value = 42
 #
 for attrname in ('max_queued_events', 'max_user_instances', 'max_user_watches'):
-    globals()[attrname] = SysCtlINotify.create(attrname)
+    globals()[attrname] = ProcINotify(attrname)
 
 
 class EventsCodes:
@@ -536,7 +486,7 @@ class _Event:
                 continue
             if attr == 'mask':
                 value = hex(getattr(self, attr))
-            elif isinstance(value, basestring) and not value:
+            elif isinstance(value, str) and not value:
                 value = "''"
             s += ' %s%s%s' % (output_format.field_name(attr),
                               output_format.punctuation('='),
@@ -628,7 +578,7 @@ class Event(_Event):
                                                              self.name))
             else:
                 self.pathname = os.path.abspath(self.path)
-        except AttributeError, err:
+        except AttributeError as err:
             # Usually it is not an error some events are perfectly valids
             # despite the lack of these attributes.
             log.debug(err)
@@ -718,8 +668,8 @@ class _SysProcessEvent(_ProcessEvent):
         and self._mv.
         """
         date_cur_ = datetime.now()
-        for seq in [self._mv_cookie, self._mv]:
-            for k in seq.keys():
+        for seq in (self._mv_cookie, self._mv):
+            for k in list(seq.keys()):
                 if (date_cur_ - seq[k][1]) > timedelta(minutes=1):
                     log.debug('Cleanup: deleting entry %s', seq[k][0])
                     del seq[k]
@@ -767,9 +717,9 @@ class _SysProcessEvent(_ProcessEvent):
                                 continue
                             rawevent = _RawEvent(created_dir_wd, flags, 0, name)
                             self._notifier.append_event(rawevent)
-                    except OSError, err:
-                        msg = "process_IN_CREATE, invalid directory %s: %s"
-                        log.debug(msg % (created_dir, str(err)))
+                    except OSError as err:
+                        msg = "process_IN_CREATE, invalid directory: %s"
+                        log.debug(msg % str(err))
         return self.process_default(raw_event)
 
     def process_IN_MOVED_FROM(self, raw_event):
@@ -1097,8 +1047,8 @@ class Stats(ProcessEvent):
         @type filename: string
         """
         flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL
-        fd = os.open(filename, flags, 0600)
-        os.write(fd, str(self))
+        fd = os.open(filename, flags, 0o0600)
+        os.write(fd, bytes(self.__str__(), locale.getpreferredencoding()))
         os.close(fd)
 
     def __str__(self, scale=45):
@@ -1107,7 +1057,7 @@ class Stats(ProcessEvent):
             return ''
 
         m = max(stats.values())
-        unity = float(scale) / m
+        unity = scale / m
         fmt = '%%-26s%%-%ds%%s' % (len(output_format.field_value('@' * scale))
                                    + 1)
         def func(x):
@@ -1149,7 +1099,7 @@ class Notifier:
         @type default_proc_fun: instance of ProcessEvent
         @param read_freq: if read_freq == 0, events are read asap,
                           if read_freq is > 0, this thread sleeps
-                          max(0, read_freq - timeout) seconds. But if
+                          max(0, read_freq - (timeout / 1000)) seconds. But if
                           timeout is None it may be different because
                           poll is blocking waiting for something to read.
         @type read_freq: int
@@ -1161,8 +1111,9 @@ class Notifier:
                           until the amount of events to read is >= threshold.
                           At least with read_freq set you might sleep.
         @type threshold: int
-        @param timeout:
-            https://docs.python.org/3/library/select.html#polling-objects
+        @param timeout: see read_freq above. If provided, it must be set in
+                        milliseconds. See
+                        https://docs.python.org/3/library/select.html#select.poll.poll
         @type timeout: int
         """
         # Watch Manager instance
@@ -1228,7 +1179,8 @@ class Notifier:
         milliseconds.
 
         @param timeout: If specified it overrides the corresponding instance
-                        attribute _timeout.
+                        attribute _timeout. timeout must be sepcified in
+                        milliseconds.
         @type timeout: int
 
         @return: New events to read.
@@ -1240,8 +1192,8 @@ class Notifier:
                 if timeout is None:
                     timeout = self._timeout
                 ret = self._pollobj.poll(timeout)
-            except select.error, err:
-                if err[0] == errno.EINTR:
+            except select.error as err:
+                if err.args[0] == errno.EINTR:
                     continue # interrupted, retry
                 else:
                     raise
@@ -1271,7 +1223,7 @@ class Notifier:
         try:
             # Read content from file
             r = os.read(self._fd, queue_size)
-        except Exception, msg:
+        except Exception as msg:
             raise NotifierError(msg)
         log.debug('Event queue size: %d', queue_size)
         rsum = 0  # counter
@@ -1281,9 +1233,11 @@ class Notifier:
             wd, mask, cookie, fname_len = struct.unpack('iIII',
                                                         r[rsum:rsum+s_size])
             # Retrieve name
-            fname, = struct.unpack('%ds' % fname_len,
+            bname, = struct.unpack('%ds' % fname_len,
                                    r[rsum + s_size:rsum + s_size + fname_len])
-            rawevent = _RawEvent(wd, mask, cookie, fname)
+            # FIXME: should we explictly call sys.getdefaultencoding() here ??
+            uname = bname.decode()
+            rawevent = _RawEvent(wd, mask, cookie, uname)
             if self._coalesce:
                 # Only enqueue new (unique) events.
                 raweventstr = str(rawevent)
@@ -1326,13 +1280,10 @@ class Notifier:
     def __daemonize(self, pid_file=None, stdin=os.devnull, stdout=os.devnull,
                     stderr=os.devnull):
         """
-        @param pid_file: file where the pid will be written. If pid_file=None
-                         the pid is written to
-                         /var/run/<sys.argv[0]|pyinotify>.pid, if pid_file=False
-                         no pid_file is written.
-        @param stdin:
-        @param stdout:
-        @param stderr: files associated to common streams.
+        pid_file: file where the pid will be written. If pid_file=None the pid
+                  is written to /var/run/<sys.argv[0]|pyinotify>.pid, if
+                  pid_file=False no pid_file is written.
+        stdin, stdout, stderr: files associated to common streams.
         """
         if pid_file is None:
             dirname = '/var/run/'
@@ -1354,7 +1305,7 @@ class Notifier:
                 if (pid == 0):
                     # child
                     os.chdir('/')
-                    os.umask(022)
+                    os.umask(0o022)
                 else:
                     # parent 2
                     os._exit(0)
@@ -1364,9 +1315,9 @@ class Notifier:
 
             fd_inp = os.open(stdin, os.O_RDONLY)
             os.dup2(fd_inp, 0)
-            fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT, 0600)
+            fd_out = os.open(stdout, os.O_WRONLY|os.O_CREAT, 0o0600)
             os.dup2(fd_out, 1)
-            fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT, 0600)
+            fd_err = os.open(stderr, os.O_WRONLY|os.O_CREAT, 0o0600)
             os.dup2(fd_err, 2)
 
         # Detach task
@@ -1375,8 +1326,9 @@ class Notifier:
         # Write pid
         if pid_file != False:
             flags = os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW|os.O_EXCL
-            fd_pid = os.open(pid_file, flags, 0600)
-            os.write(fd_pid, str(os.getpid()) + '\n')
+            fd_pid = os.open(pid_file, flags, 0o0600)
+            os.write(fd_pid,  bytes(str(os.getpid()) + '\n',
+                                    locale.getpreferredencoding()))
             os.close(fd_pid)
             # Register unlink function
             atexit.register(lambda : os.unlink(pid_file))
@@ -1441,9 +1393,12 @@ class Notifier:
         Close inotify's instance (close its file descriptor).
         It destroys all existing watches, pending events,...
         This method is automatically called at the end of loop().
+        Afterward it is invalid to access this instance.
         """
-        self._pollobj.unregister(self._fd)
-        os.close(self._fd)
+        if self._fd is not None:
+            self._pollobj.unregister(self._fd)
+            os.close(self._fd)
+            self._fd = None
         self._sys_proc_fun = None
 
 
@@ -1468,7 +1423,7 @@ class ThreadedNotifier(threading.Thread, Notifier):
         @type default_proc_fun: instance of ProcessEvent
         @param read_freq: if read_freq == 0, events are read asap,
                           if read_freq is > 0, this thread sleeps
-                          max(0, read_freq - timeout) seconds.
+                          max(0, read_freq - (timeout / 1000)) seconds.
         @type read_freq: int
         @param threshold: File descriptor will be read only if the accumulated
                           size to read becomes >= threshold. If != 0, you likely
@@ -1478,8 +1433,9 @@ class ThreadedNotifier(threading.Thread, Notifier):
                           until the amount of events to read is >= threshold. At
                           least with read_freq you might sleep.
         @type threshold: int
-        @param timeout:
-            https://docs.python.org/3/library/select.html#polling-objects
+        @param timeout: see read_freq above. If provided, it must be set in
+                        milliseconds. See
+                        https://docs.python.org/3/library/select.html#select.poll.poll
         @type timeout: int
         """
         # Init threading base class
@@ -1498,7 +1454,7 @@ class ThreadedNotifier(threading.Thread, Notifier):
         Stop notifier's loop. Stop notification. Join the thread.
         """
         self._stop_event.set()
-        os.write(self._pipe[1], 'stop')
+        os.write(self._pipe[1], b'stop')
         threading.Thread.join(self)
         Notifier.stop(self)
         self._pollobj.unregister(self._pipe[0])
@@ -1699,7 +1655,6 @@ class Watch:
 class ExcludeFilter:
     """
     ExcludeFilter is an exclusion filter.
-
     """
     def __init__(self, arg_lst):
         """
@@ -1731,16 +1686,13 @@ class ExcludeFilter:
 
     def _load_patterns_from_file(self, filename):
         lst = []
-        file_obj = file(filename, 'r')
-        try:
+        with open(filename, 'r') as file_obj:
             for line in file_obj.readlines():
                 # Trim leading an trailing whitespaces
                 pattern = line.strip()
                 if not pattern or pattern.startswith('#'):
                     continue
                 lst.append(pattern)
-        finally:
-            file_obj.close()
         return lst
 
     def _match(self, regex, path):
@@ -1764,7 +1716,6 @@ class WatchManagerError(Exception):
     """
     WatchManager Exception. Raised on error encountered on watches
     operations.
-
     """
     def __init__(self, msg, wmd):
         """
@@ -1851,7 +1802,7 @@ class WatchManager:
         """
         try:
             del self._wmd[wd]
-        except KeyError, err:
+        except KeyError as err:
             log.error('Cannot delete unknown watch descriptor %s' % str(err))
 
     @property
@@ -1868,13 +1819,7 @@ class WatchManager:
         """
         Format path to its internal (stored in watch manager) representation.
         """
-        # Unicode strings are converted back to strings, because it seems
-        # that inotify_add_watch from ctypes does not work well when
-        # it receives an ctypes.create_unicode_buffer instance as argument.
-        # Therefore even wd are indexed with bytes string and not with
-        # unicode paths.
-        if isinstance(path, unicode):
-            path = path.encode(sys.getfilesystemencoding())
+        # path must be a unicode string (str) and is just normalized.
         return os.path.normpath(path)
 
     def __add_watch(self, path, mask, proc_fun, auto_add, exclude_filter):
@@ -1890,13 +1835,14 @@ class WatchManager:
             return wd
         watch = Watch(wd=wd, path=path, mask=mask, proc_fun=proc_fun,
                       auto_add=auto_add, exclude_filter=exclude_filter)
+        # wd are _always_ indexed with their original unicode paths in wmd.
         self._wmd[wd] = watch
         log.debug('New %s', watch)
         return wd
 
     def __glob(self, path, do_glob):
         if do_glob:
-            return glob(path)
+            return glob.iglob(path)
         else:
             return [path]
 
@@ -1907,11 +1853,8 @@ class WatchManager:
         Add watch(s) on the provided |path|(s) with associated |mask| flag
         value and optionally with a processing |proc_fun| function and
         recursive flag |rec| set to True.
-        Ideally |path| components should not be unicode objects. Note that
-        although unicode paths are accepted there are converted to byte
-        strings before a watch is put on that path. The encoding used for
-        converting the unicode object is given by sys.getfilesystemencoding().
-        If |path| si already watched it is ignored, but if it is called with
+        All |path| components _must_ be str (i.e. unicode) objects.
+        If |path| is already watched it is ignored, but if it is called with
         option rec=True a watch is put on each one of its not-watched
         subdirectory.
 
@@ -1945,10 +1888,9 @@ class WatchManager:
                                the class' constructor.
         @type exclude_filter: callable object
         @return: dict of paths associated to watch descriptors. A wd value
-                 is positive if the watch was added sucessfully,
-                 otherwise the value is negative. If the path was invalid
-                 or was already watched it is not included into this returned
-                 dictionary.
+                 is positive if the watch was added sucessfully, otherwise
+                 the value is negative. If the path was invalid or was already
+                 watched it is not included into this returned dictionary.
         @rtype: dict of {str: int}
         """
         ret_ = {} # return {path: wd, ...}
@@ -1958,6 +1900,11 @@ class WatchManager:
 
         # normalize args as list elements
         for npath in self.__format_param(path):
+            # Require that path be a unicode string
+            if not isinstance(npath, str):
+                ret_[path] = -3
+                continue
+
             # unix pathname pattern expansion
             for apath in self.__glob(npath, do_glob):
                 # recursively list subdirs according to rec param
@@ -2242,7 +2189,6 @@ class WatchManager:
                              "Make watch manager ignoring new events.")
 
 
-
 class RawOutputFormat:
     """
     Format string representations.
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 03/28] bitbake: Drop futures usage since we're python 3
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
  2016-06-01 12:36 ` [PATCH 02/28] bitbake/pyinotify.py: Upgrade to py3 version Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 04/28] bitbake: Convert to python 3 megacommit This needs breaking up into smaller changes Richard Purdie
                   ` (24 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/bb/cooker.py                   | 2 +-
 lib/bb/exceptions.py               | 2 +-
 lib/bb/fetch2/__init__.py          | 2 --
 lib/bb/fetch2/perforce.py          | 1 -
 lib/bb/parse/ast.py                | 3 +--
 lib/bb/parse/parse_py/BBHandler.py | 2 +-
 6 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/lib/bb/cooker.py b/lib/bb/cooker.py
index fb86446..11f17ef 100644
--- a/lib/bb/cooker.py
+++ b/lib/bb/cooker.py
@@ -22,7 +22,7 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
-from __future__ import print_function
+
 import sys, os, glob, os.path, re, time
 import atexit
 import itertools
diff --git a/lib/bb/exceptions.py b/lib/bb/exceptions.py
index f182c8f..eadfc57 100644
--- a/lib/bb/exceptions.py
+++ b/lib/bb/exceptions.py
@@ -1,4 +1,4 @@
-from __future__ import absolute_import
+
 import inspect
 import traceback
 import bb.namedtuple_with_abc
diff --git a/lib/bb/fetch2/__init__.py b/lib/bb/fetch2/__init__.py
index 7d2f350..14fe3c7 100644
--- a/lib/bb/fetch2/__init__.py
+++ b/lib/bb/fetch2/__init__.py
@@ -25,8 +25,6 @@ BitBake build tools.
 #
 # Based on functions from the base bb module, Copyright 2003 Holger Schurig
 
-from __future__ import absolute_import
-from __future__ import print_function
 import os, re
 import signal
 import logging
diff --git a/lib/bb/fetch2/perforce.py b/lib/bb/fetch2/perforce.py
index 3a10c7c..1aef246 100644
--- a/lib/bb/fetch2/perforce.py
+++ b/lib/bb/fetch2/perforce.py
@@ -25,7 +25,6 @@ BitBake build tools.
 #
 # Based on functions from the base bb module, Copyright 2003 Holger Schurig
 
-from future_builtins import zip
 import os
 import subprocess
 import logging
diff --git a/lib/bb/parse/ast.py b/lib/bb/parse/ast.py
index 5f55af5..bc3a2f8 100644
--- a/lib/bb/parse/ast.py
+++ b/lib/bb/parse/ast.py
@@ -21,8 +21,7 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
-from __future__ import absolute_import
-from future_builtins import filter
+
 import re
 import string
 import logging
diff --git a/lib/bb/parse/parse_py/BBHandler.py b/lib/bb/parse/parse_py/BBHandler.py
index ef72c37..c54a079 100644
--- a/lib/bb/parse/parse_py/BBHandler.py
+++ b/lib/bb/parse/parse_py/BBHandler.py
@@ -25,7 +25,7 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
-from __future__ import absolute_import
+
 import re, bb, os
 import logging
 import bb.build, bb.utils
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 04/28] bitbake: Convert to python 3 megacommit This needs breaking up into smaller changes.
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
  2016-06-01 12:36 ` [PATCH 02/28] bitbake/pyinotify.py: Upgrade to py3 version Richard Purdie
  2016-06-01 12:36 ` [PATCH 03/28] bitbake: Drop futures usage since we're python 3 Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 05/28] data_smart: Simplify ExpansionError exception Richard Purdie
                   ` (23 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 bin/bitbake                      |   5 +-
 bin/bitbake-diffsigs             |   4 +-
 bin/bitbake-dumpsig              |   4 +-
 bin/bitbake-layers               |   5 +-
 bin/bitbake-prserv               |   2 +-
 bin/bitbake-selftest             |   2 +-
 bin/bitbake-worker               |  60 ++++++++++----------
 bin/bitdoc                       |   2 +-
 bin/image-writer                 |   8 ++-
 bin/toaster-eventreplay          |   2 +-
 contrib/dump_cache.py            |   2 +-
 lib/bb/COW.py                    |  44 +++++++--------
 lib/bb/__init__.py               |   2 +-
 lib/bb/build.py                  |  13 ++---
 lib/bb/cache.py                  |  18 ++----
 lib/bb/checksum.py               |   9 +--
 lib/bb/codeparser.py             |  15 ++---
 lib/bb/command.py                |   2 +-
 lib/bb/cooker.py                 |  38 ++++++-------
 lib/bb/data_smart.py             |   4 +-
 lib/bb/event.py                  |   9 +--
 lib/bb/exceptions.py             |   2 +-
 lib/bb/fetch2/__init__.py        |  36 ++++++------
 lib/bb/fetch2/local.py           |   4 +-
 lib/bb/fetch2/npm.py             |   6 +-
 lib/bb/fetch2/perforce.py        |   2 +-
 lib/bb/fetch2/sftp.py            |   8 +--
 lib/bb/fetch2/ssh.py             |   6 +-
 lib/bb/fetch2/wget.py            |  48 ++++++++--------
 lib/bb/main.py                   |  11 ++--
 lib/bb/msg.py                    |   2 +-
 lib/bb/parse/ast.py              |  18 +++---
 lib/bb/persist_data.py           |  10 ++--
 lib/bb/process.py                |  36 ++++++++----
 lib/bb/providers.py              |   2 +-
 lib/bb/pysh/builtin.py           |   2 +-
 lib/bb/pysh/interp.py            |   4 +-
 lib/bb/pysh/pyshlex.py           |   2 +-
 lib/bb/pysh/pyshyacc.py          |   4 +-
 lib/bb/runqueue.py               | 117 +++++++++++++++++++--------------------
 lib/bb/server/process.py         |   8 +--
 lib/bb/server/xmlrpc.py          |  48 ++++++++--------
 lib/bb/siggen.py                 |  19 +++----
 lib/bb/taskdata.py               |  12 ++--
 lib/bb/tests/codeparser.py       |   4 +-
 lib/bb/tests/data.py             |  12 ++--
 lib/bb/tests/parse.py            |   2 +-
 lib/bb/ui/crumbs/hobwidget.py    |   2 +-
 lib/bb/ui/crumbs/progressbar.py  |   6 +-
 lib/bb/ui/crumbs/runningbuild.py |  12 ++--
 lib/bb/ui/goggle.py              |  11 +++-
 lib/bb/ui/knotty.py              |   6 +-
 lib/bb/ui/ncurses.py             |   8 +--
 lib/bb/ui/uievent.py             |   4 +-
 lib/bb/utils.py                  |  20 +++----
 lib/bblayers/action.py           |   2 +-
 lib/bblayers/common.py           |   2 +-
 lib/bblayers/layerindex.py       |  12 ++--
 lib/bblayers/query.py            |   4 +-
 lib/codegen.py                   |   8 +--
 lib/ply/yacc.py                  |   2 +
 lib/prserv/db.py                 |   2 +-
 lib/prserv/serv.py               |  14 ++---
 63 files changed, 390 insertions(+), 400 deletions(-)

diff --git a/bin/bitbake b/bin/bitbake
index bba87b0..9813a08 100755
--- a/bin/bitbake
+++ b/bin/bitbake
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # ex:ts=4:sw=4:sts=4:et
 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
 #
@@ -35,6 +35,9 @@ except RuntimeError as exc:
 from bb import cookerdata
 from bb.main import bitbake_main, BitBakeConfigParameters, BBMainException
 
+if sys.getfilesystemencoding() != "utf-8":
+    sys.exit("Please use a locale setting which supports utf-8.\nPython can't change the filesystem locale after loading so we need a utf-8 when python starts or things won't work.")
+
 __version__ = "1.31.0"
 
 if __name__ == "__main__":
diff --git a/bin/bitbake-diffsigs b/bin/bitbake-diffsigs
index 67c60db..3b6ef88 100755
--- a/bin/bitbake-diffsigs
+++ b/bin/bitbake-diffsigs
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # bitbake-diffsigs
 # BitBake task signature data comparison utility
@@ -24,6 +24,7 @@ import warnings
 import fnmatch
 import optparse
 import logging
+import pickle
 
 sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
 
@@ -121,7 +122,6 @@ else:
     if len(args) == 1:
         parser.print_help()
     else:
-        import cPickle
         try:
             if len(args) == 2:
                 output = bb.siggen.dump_sigfile(sys.argv[1])
diff --git a/bin/bitbake-dumpsig b/bin/bitbake-dumpsig
index ffaed1f..58ba1ca 100755
--- a/bin/bitbake-dumpsig
+++ b/bin/bitbake-dumpsig
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # bitbake-dumpsig
 # BitBake task signature dump utility
@@ -23,6 +23,7 @@ import sys
 import warnings
 import optparse
 import logging
+import pickle
 
 sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(sys.argv[0])), 'lib'))
 
@@ -51,7 +52,6 @@ options, args = parser.parse_args(sys.argv)
 if len(args) == 1:
     parser.print_help()
 else:
-    import cPickle
     try:
         output = bb.siggen.dump_sigfile(args[1])
     except IOError as e:
diff --git a/bin/bitbake-layers b/bin/bitbake-layers
index d8ffa95..0c973df 100755
--- a/bin/bitbake-layers
+++ b/bin/bitbake-layers
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # This script has subcommands which operate against your bitbake layers, either
 # displaying useful information, or acting against them.
@@ -48,7 +48,6 @@ def logger_create(name, output=sys.stderr):
     logger.setLevel(logging.INFO)
     return logger
 
-
 def logger_setup_color(logger, color='auto'):
     from bb.msg import BBLogFormatter
     console = logging.StreamHandler(sys.stdout)
@@ -61,7 +60,6 @@ def logger_setup_color(logger, color='auto'):
 
 logger = logger_create('bitbake-layers', sys.stdout)
 
-
 def main():
     parser = argparse.ArgumentParser(
         description="BitBake layers utility",
@@ -78,6 +76,7 @@ def main():
     parser.add_argument('-h', '--help', action='help', default=argparse.SUPPRESS,
                         help='show this help message and exit')
     subparsers = parser.add_subparsers(title='subcommands', metavar='<subcommand>')
+    subparsers.required = True
 
     if global_args.debug:
         logger.setLevel(logging.DEBUG)
diff --git a/bin/bitbake-prserv b/bin/bitbake-prserv
index 0382144..f38d2dd 100755
--- a/bin/bitbake-prserv
+++ b/bin/bitbake-prserv
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 import os
 import sys,logging
 import optparse
diff --git a/bin/bitbake-selftest b/bin/bitbake-selftest
index 462eb1b..1e6f35e 100755
--- a/bin/bitbake-selftest
+++ b/bin/bitbake-selftest
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2012 Richard Purdie
 #
diff --git a/bin/bitbake-worker b/bin/bitbake-worker
index 6a6b26b..5d062a2 100755
--- a/bin/bitbake-worker
+++ b/bin/bitbake-worker
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import os
 import sys
@@ -10,8 +10,12 @@ import bb
 import select
 import errno
 import signal
+import pickle
 from multiprocessing import Lock
 
+if sys.getfilesystemencoding() != "utf-8":
+    sys.exit("Please use a locale setting which supports utf-8.\nPython can't change the filesystem locale after loading so we need a utf-8 when python starts or things won't work.")
+
 # Users shouldn't be running this code directly
 if len(sys.argv) != 2 or not sys.argv[1].startswith("decafbad"):
     print("bitbake-worker is meant for internal execution by bitbake itself, please don't use it standalone.")
@@ -30,19 +34,16 @@ if sys.argv[1].startswith("decafbadbad"):
 # updates to log files for use with tail
 try:
     if sys.stdout.name == '<stdout>':
-        sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
+        import fcntl
+        fl = fcntl.fcntl(sys.stdout.fileno(), fcntl.F_GETFL)
+        fl |= os.O_SYNC 
+        fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, fl)
+        #sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
 except:
     pass
 
 logger = logging.getLogger("BitBake")
 
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-    bb.msg.note(1, bb.msg.domain.Cache, "Importing cPickle failed. Falling back to a very slow implementation.")
-
-
 worker_pipe = sys.stdout.fileno()
 bb.utils.nonblockingfd(worker_pipe)
 # Need to guard against multiprocessing being used in child processes
@@ -62,10 +63,10 @@ if 0:
     consolelog.setFormatter(conlogformat)
     logger.addHandler(consolelog)
 
-worker_queue = ""
+worker_queue = b""
 
 def worker_fire(event, d):
-    data = "<event>" + pickle.dumps(event) + "</event>"
+    data = b"<event>" + pickle.dumps(event) + b"</event>"
     worker_fire_prepickled(data)
 
 def worker_fire_prepickled(event):
@@ -91,7 +92,7 @@ def worker_child_fire(event, d):
     global worker_pipe
     global worker_pipe_lock
 
-    data = "<event>" + pickle.dumps(event) + "</event>"
+    data = b"<event>" + pickle.dumps(event) + b"</event>"
     try:
         worker_pipe_lock.acquire()
         worker_pipe.write(data)
@@ -251,7 +252,7 @@ def fork_off_task(cfg, data, workerdata, fn, task, taskname, appends, taskdepdat
                 bb.utils.process_profilelog(profname)
                 os._exit(ret)
     else:
-        for key, value in envbackup.iteritems():
+        for key, value in iter(envbackup.items()):
             if value is None:
                 del os.environ[key]
             else:
@@ -268,22 +269,22 @@ class runQueueWorkerPipe():
         if pipeout:
             pipeout.close()
         bb.utils.nonblockingfd(self.input)
-        self.queue = ""
+        self.queue = b""
 
     def read(self):
         start = len(self.queue)
         try:
-            self.queue = self.queue + self.input.read(102400)
+            self.queue = self.queue + (self.input.read(102400) or b"")
         except (OSError, IOError) as e:
             if e.errno != errno.EAGAIN:
                 raise
 
         end = len(self.queue)
-        index = self.queue.find("</event>")
+        index = self.queue.find(b"</event>")
         while index != -1:
             worker_fire_prepickled(self.queue[:index+8])
             self.queue = self.queue[index+8:]
-            index = self.queue.find("</event>")
+            index = self.queue.find(b"</event>")
         return (end > start)
 
     def close(self):
@@ -299,7 +300,7 @@ class BitbakeWorker(object):
     def __init__(self, din):
         self.input = din
         bb.utils.nonblockingfd(self.input)
-        self.queue = ""
+        self.queue = b""
         self.cookercfg = None
         self.databuilder = None
         self.data = None
@@ -336,12 +337,12 @@ class BitbakeWorker(object):
                 except (OSError, IOError):
                     pass
             if len(self.queue):
-                self.handle_item("cookerconfig", self.handle_cookercfg)
-                self.handle_item("workerdata", self.handle_workerdata)
-                self.handle_item("runtask", self.handle_runtask)
-                self.handle_item("finishnow", self.handle_finishnow)
-                self.handle_item("ping", self.handle_ping)
-                self.handle_item("quit", self.handle_quit)
+                self.handle_item(b"cookerconfig", self.handle_cookercfg)
+                self.handle_item(b"workerdata", self.handle_workerdata)
+                self.handle_item(b"runtask", self.handle_runtask)
+                self.handle_item(b"finishnow", self.handle_finishnow)
+                self.handle_item(b"ping", self.handle_ping)
+                self.handle_item(b"quit", self.handle_quit)
 
             for pipe in self.build_pipes:
                 self.build_pipes[pipe].read()
@@ -351,12 +352,12 @@ class BitbakeWorker(object):
 
 
     def handle_item(self, item, func):
-        if self.queue.startswith("<" + item + ">"):
-            index = self.queue.find("</" + item + ">")
+        if self.queue.startswith(b"<" + item + b">"):
+            index = self.queue.find(b"</" + item + b">")
             while index != -1:
                 func(self.queue[(len(item) + 2):index])
                 self.queue = self.queue[(index + len(item) + 3):]
-                index = self.queue.find("</" + item + ">")
+                index = self.queue.find(b"</" + item + b">")
 
     def handle_cookercfg(self, data):
         self.cookercfg = pickle.loads(data)
@@ -420,12 +421,12 @@ class BitbakeWorker(object):
         self.build_pipes[pid].close()
         del self.build_pipes[pid]
 
-        worker_fire_prepickled("<exitcode>" + pickle.dumps((task, status)) + "</exitcode>")
+        worker_fire_prepickled(b"<exitcode>" + pickle.dumps((task, status)) + b"</exitcode>")
 
     def handle_finishnow(self, _):
         if self.build_pids:
             logger.info("Sending SIGTERM to remaining %s tasks", len(self.build_pids))
-            for k, v in self.build_pids.iteritems():
+            for k, v in iter(self.build_pids.items()):
                 try:
                     os.kill(-k, signal.SIGTERM)
                     os.waitpid(-1, 0)
@@ -435,6 +436,7 @@ class BitbakeWorker(object):
             self.build_pipes[pipe].read()
 
 try:
+    sys.stdin = sys.stdin.detach()
     worker = BitbakeWorker(sys.stdin)
     if not profiling:
         worker.serve()
diff --git a/bin/bitdoc b/bin/bitdoc
index defb3dd..2744678 100755
--- a/bin/bitdoc
+++ b/bin/bitdoc
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # ex:ts=4:sw=4:sts=4:et
 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
 #
diff --git a/bin/image-writer b/bin/image-writer
index e30ab45..7ab1d8c 100755
--- a/bin/image-writer
+++ b/bin/image-writer
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright (c) 2012 Wind River Systems, Inc.
 #
@@ -24,9 +24,13 @@ try:
 except RuntimeError as exc:
     sys.exit(str(exc))
 
+from gi import pygtkcompat
+
+pygtkcompat.enable()
+pygtkcompat.enable_gtk(version='3.0')
+
 import gtk
 import optparse
-import pygtk
 
 from bb.ui.crumbs.hobwidget import HobAltButton, HobButton
 from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
diff --git a/bin/toaster-eventreplay b/bin/toaster-eventreplay
index 615a7ae..bdddb90 100755
--- a/bin/toaster-eventreplay
+++ b/bin/toaster-eventreplay
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # ex:ts=4:sw=4:sts=4:et
 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
 #
diff --git a/contrib/dump_cache.py b/contrib/dump_cache.py
index 97c5463..a1f09b4 100755
--- a/contrib/dump_cache.py
+++ b/contrib/dump_cache.py
@@ -29,7 +29,7 @@ import warnings
 sys.path.insert(0, os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), '../lib'))
 from bb.cache import CoreRecipeInfo
 
-import cPickle as pickle
+import pickle as pickle
 
 def main(argv=None):
     """
diff --git a/lib/bb/COW.py b/lib/bb/COW.py
index 6917ec3..77a05cf 100644
--- a/lib/bb/COW.py
+++ b/lib/bb/COW.py
@@ -23,19 +23,17 @@
 # Assign a file to __warn__ to get warnings about slow operations.
 #
 
-from __future__ import print_function
+
 import copy
 import types
 ImmutableTypes = (
-    types.NoneType,
     bool,
     complex,
     float,
     int,
-    long,
     tuple,
     frozenset,
-    basestring
+    str
 )
 
 MUTABLE = "__mutable__"
@@ -61,7 +59,7 @@ class COWDictMeta(COWMeta):
     __call__ = cow
 
     def __setitem__(cls, key, value):
-        if not isinstance(value, ImmutableTypes):
+        if value is not None and not isinstance(value, ImmutableTypes):
             if not isinstance(value, COWMeta):
                 cls.__hasmutable__ = True
             key += MUTABLE
@@ -116,7 +114,7 @@ class COWDictMeta(COWMeta):
         cls.__setitem__(key, cls.__marker__)
 
     def __revertitem__(cls, key):
-        if not cls.__dict__.has_key(key):
+        if key not in cls.__dict__:
             key += MUTABLE
         delattr(cls, key)
 
@@ -183,7 +181,7 @@ class COWSetMeta(COWDictMeta):
         COWDictMeta.__delitem__(cls, repr(hash(value)))
 
     def __in__(cls, value):
-        return COWDictMeta.has_key(repr(hash(value)))
+        return repr(hash(value)) in COWDictMeta
 
     def iterkeys(cls):
         raise TypeError("sets don't have keys")
@@ -192,12 +190,10 @@ class COWSetMeta(COWDictMeta):
         raise TypeError("sets don't have 'items'")
 
 # These are the actual classes you use!
-class COWDictBase(object):
-    __metaclass__ = COWDictMeta
+class COWDictBase(object, metaclass = COWDictMeta):
     __count__ = 0
 
-class COWSetBase(object):
-    __metaclass__ = COWSetMeta
+class COWSetBase(object, metaclass = COWSetMeta):
     __count__ = 0
 
 if __name__ == "__main__":
@@ -217,11 +213,11 @@ if __name__ == "__main__":
     print()
 
     print("a", a)
-    for x in a.iteritems():
+    for x in a.items():
         print(x)
     print("--")
     print("b", b)
-    for x in b.iteritems():
+    for x in b.items():
         print(x)
     print()
 
@@ -229,11 +225,11 @@ if __name__ == "__main__":
     b['a'] = 'c'
 
     print("a", a)
-    for x in a.iteritems():
+    for x in a.items():
         print(x)
     print("--")
     print("b", b)
-    for x in b.iteritems():
+    for x in b.items():
         print(x)
     print()
 
@@ -248,22 +244,22 @@ if __name__ == "__main__":
     a['set'].add("o2")
 
     print("a", a)
-    for x in a['set'].itervalues():
+    for x in a['set'].values():
         print(x)
     print("--")
     print("b", b)
-    for x in b['set'].itervalues():
+    for x in b['set'].values():
         print(x)
     print()
 
     b['set'].add('o3')
 
     print("a", a)
-    for x in a['set'].itervalues():
+    for x in a['set'].values():
         print(x)
     print("--")
     print("b", b)
-    for x in b['set'].itervalues():
+    for x in b['set'].values():
         print(x)
     print()
 
@@ -273,7 +269,7 @@ if __name__ == "__main__":
     a['set2'].add("o2")
 
     print("a", a)
-    for x in a.iteritems():
+    for x in a.items():
         print(x)
     print("--")
     print("b", b)
@@ -287,13 +283,13 @@ if __name__ == "__main__":
     except KeyError:
         print("Yay! deleted key raises error")
 
-    if b.has_key('b'):
+    if 'b' in b:
         print("Boo!")
     else:
         print("Yay - has_key with delete works!")
 
     print("a", a)
-    for x in a.iteritems():
+    for x in a.items():
         print(x)
     print("--")
     print("b", b)
@@ -304,7 +300,7 @@ if __name__ == "__main__":
     b.__revertitem__('b')
 
     print("a", a)
-    for x in a.iteritems():
+    for x in a.items():
         print(x)
     print("--")
     print("b", b)
@@ -314,7 +310,7 @@ if __name__ == "__main__":
 
     b.__revertitem__('dict')
     print("a", a)
-    for x in a.iteritems():
+    for x in a.items():
         print(x)
     print("--")
     print("b", b)
diff --git a/lib/bb/__init__.py b/lib/bb/__init__.py
index 4ae6b8a..6b85984 100644
--- a/lib/bb/__init__.py
+++ b/lib/bb/__init__.py
@@ -84,7 +84,7 @@ def plain(*args):
     mainlogger.plain(''.join(args))
 
 def debug(lvl, *args):
-    if isinstance(lvl, basestring):
+    if isinstance(lvl, str):
         mainlogger.warning("Passed invalid debug level '%s' to bb.debug", lvl)
         args = (lvl,) + args
         lvl = 1
diff --git a/lib/bb/build.py b/lib/bb/build.py
index 9854553..e016ae3 100644
--- a/lib/bb/build.py
+++ b/lib/bb/build.py
@@ -35,8 +35,7 @@ import stat
 import bb
 import bb.msg
 import bb.process
-from contextlib import nested
-from bb import event, utils
+from bb import data, event, utils
 
 bblogger = logging.getLogger('BitBake')
 logger = logging.getLogger('BitBake.Build')
@@ -328,7 +327,7 @@ trap '' 0
 exit $ret
 ''')
 
-    os.chmod(runfile, 0775)
+    os.chmod(runfile, 0o775)
 
     cmd = runfile
     if d.getVarFlag(func, 'fakeroot', False):
@@ -342,12 +341,12 @@ exit $ret
         logfile = sys.stdout
 
     def readfifo(data):
-        lines = data.split('\0')
+        lines = data.split(b'\0')
         for line in lines:
-            splitval = line.split(' ', 1)
+            splitval = line.split(b' ', 1)
             cmd = splitval[0]
             if len(splitval) > 1:
-                value = splitval[1]
+                value = splitval[1].decode("utf-8")
             else:
                 value = ''
             if cmd == 'bbplain':
@@ -375,7 +374,7 @@ exit $ret
     if os.path.exists(fifopath):
         os.unlink(fifopath)
     os.mkfifo(fifopath)
-    with open(fifopath, 'r+') as fifo:
+    with open(fifopath, 'r+b', buffering=0) as fifo:
         try:
             bb.debug(2, "Executing shell function %s" % func)
 
diff --git a/lib/bb/cache.py b/lib/bb/cache.py
index 393d541..96abd71 100644
--- a/lib/bb/cache.py
+++ b/lib/bb/cache.py
@@ -28,21 +28,15 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
-
 import os
+import sys
 import logging
+import pickle
 from collections import defaultdict
 import bb.utils
 
 logger = logging.getLogger("BitBake.Cache")
 
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-    logger.info("Importing cPickle failed. "
-                "Falling back to a very slow implementation.")
-
 __cache_version__ = "150"
 
 def getCacheFile(path, filename, data_hash):
@@ -80,7 +74,7 @@ class RecipeInfoCommon(object):
         out_dict = dict((var, metadata.getVarFlag(var, flag, True))
                     for var in varlist)
         if squash:
-            return dict((k,v) for (k,v) in out_dict.iteritems() if v)
+            return dict((k,v) for (k,v) in out_dict.items() if v)
         else:
             return out_dict
 
@@ -240,7 +234,7 @@ class CoreRecipeInfo(RecipeInfoCommon):
         cachedata.universe_target.append(self.pn)
 
         cachedata.hashfn[fn] = self.hashfilename
-        for task, taskhash in self.basetaskhashes.iteritems():
+        for task, taskhash in self.basetaskhashes.items():
             identifier = '%s.%s' % (fn, task)
             cachedata.basetaskhash[identifier] = taskhash
 
@@ -404,7 +398,7 @@ class Cache(object):
         infos = []
         datastores = cls.load_bbfile(filename, appends, configdata)
         depends = []
-        for variant, data in sorted(datastores.iteritems(),
+        for variant, data in sorted(datastores.items(),
                                     key=lambda i: i[0],
                                     reverse=True):
             virtualfn = cls.realfn2virtual(filename, variant)
@@ -616,7 +610,7 @@ class Cache(object):
         pickler_dict['CoreRecipeInfo'].dump(bb.__version__)
 
         try:
-            for key, info_array in self.depends_cache.iteritems():
+            for key, info_array in self.depends_cache.items():
                 for info in info_array:
                     if isinstance(info, RecipeInfoCommon):
                         cache_class_name = info.__class__.__name__
diff --git a/lib/bb/checksum.py b/lib/bb/checksum.py
index 2ec964d..be4ab68 100644
--- a/lib/bb/checksum.py
+++ b/lib/bb/checksum.py
@@ -19,20 +19,13 @@ import glob
 import operator
 import os
 import stat
+import pickle
 import bb.utils
 import logging
 from bb.cache import MultiProcessCache
 
 logger = logging.getLogger("BitBake.Cache")
 
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-    logger.info("Importing cPickle failed. "
-                "Falling back to a very slow implementation.")
-
-
 # mtime cache (non-persistent)
 # based upon the assumption that files do not change during bitbake run
 class FileMtimeCache(object):
diff --git a/lib/bb/codeparser.py b/lib/bb/codeparser.py
index 70b0a8d..b1d067a 100644
--- a/lib/bb/codeparser.py
+++ b/lib/bb/codeparser.py
@@ -1,22 +1,17 @@
 import ast
+import sys
 import codegen
 import logging
+import pickle
+import bb.pysh as pysh
 import os.path
 import bb.utils, bb.data
 from itertools import chain
-from pysh import pyshyacc, pyshlex, sherrors
+from bb.pysh import pyshyacc, pyshlex, sherrors
 from bb.cache import MultiProcessCache
 
-
 logger = logging.getLogger('BitBake.CodeParser')
 
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-    logger.info('Importing cPickle failed.  Falling back to a very slow implementation.')
-
-
 def check_indent(codestr):
     """If the code is indented, add a top level piece of code to 'remove' the indentation"""
 
@@ -68,7 +63,7 @@ class SetCache(object):
         
         new = []
         for i in items:
-            new.append(intern(i))
+            new.append(sys.intern(i))
         s = frozenset(new)
         if hash(s) in self.setcache:
             return self.setcache[hash(s)]
diff --git a/lib/bb/command.py b/lib/bb/command.py
index 0559ffc..caa3e4d 100644
--- a/lib/bb/command.py
+++ b/lib/bb/command.py
@@ -110,7 +110,7 @@ class Command:
             return False
         except SystemExit as exc:
             arg = exc.args[0]
-            if isinstance(arg, basestring):
+            if isinstance(arg, str):
                 self.finishAsyncCommand(arg)
             else:
                 self.finishAsyncCommand("Exited with %s" % arg)
diff --git a/lib/bb/cooker.py b/lib/bb/cooker.py
index 11f17ef..2154ef4 100644
--- a/lib/bb/cooker.py
+++ b/lib/bb/cooker.py
@@ -30,13 +30,13 @@ import logging
 import multiprocessing
 import sre_constants
 import threading
-from cStringIO import StringIO
+from io import StringIO
 from contextlib import closing
 from functools import wraps
 from collections import defaultdict
 import bb, bb.exceptions, bb.command
 from bb import utils, data, parse, event, cache, providers, taskdata, runqueue, build
-import Queue
+import queue
 import signal
 import subprocess
 import errno
@@ -65,7 +65,7 @@ class CollectionError(bb.BBHandledException):
     """
 
 class state:
-    initial, parsing, running, shutdown, forceshutdown, stopped, error = range(7)
+    initial, parsing, running, shutdown, forceshutdown, stopped, error = list(range(7))
 
     @classmethod
     def get_name(cls, code):
@@ -93,7 +93,7 @@ class SkippedPackage:
 
 
 class CookerFeatures(object):
-    _feature_list = [HOB_EXTRA_CACHES, BASEDATASTORE_TRACKING, SEND_SANITYEVENTS] = range(3)
+    _feature_list = [HOB_EXTRA_CACHES, BASEDATASTORE_TRACKING, SEND_SANITYEVENTS] = list(range(3))
 
     def __init__(self):
         self._features=set()
@@ -110,8 +110,8 @@ class CookerFeatures(object):
     def __iter__(self):
         return self._features.__iter__()
 
-    def next(self):
-        return self._features.next()
+    def __next__(self):
+        return next(self._features)
 
 
 #============================================================================#
@@ -726,13 +726,13 @@ class BBCooker:
         depend_tree['providermap'] = {}
         depend_tree["layer-priorities"] = self.recipecache.bbfile_config_priorities
 
-        for name, fn in taskdata.get_providermap().iteritems():
+        for name, fn in list(taskdata.get_providermap().items()):
             pn = self.recipecache.pkg_fn[fn]
             if name != pn:
                 version = "%s:%s-%s" % self.recipecache.pkg_pepvpr[fn]
                 depend_tree['providermap'][name] = (pn, version)
 
-        for task in xrange(len(rq.rqdata.runq_fnid)):
+        for task in range(len(rq.rqdata.runq_fnid)):
             taskname = rq.rqdata.runq_task[task]
             fnid = rq.rqdata.runq_fnid[task]
             fn = taskdata.fn_index[fnid]
@@ -807,7 +807,7 @@ class BBCooker:
         _, taskdata = self.prepareTreeData(pkgs_to_build, task)
         tasks_fnid = []
         if len(taskdata.tasks_name) != 0:
-            for task in xrange(len(taskdata.tasks_name)):
+            for task in range(len(taskdata.tasks_name)):
                 tasks_fnid.append(taskdata.tasks_fnid[task])
 
         seen_fnids = []
@@ -825,7 +825,7 @@ class BBCooker:
                 cachefields = getattr(cache_class, 'cachefields', [])
                 extra_info = extra_info + cachefields
 
-        for task in xrange(len(tasks_fnid)):
+        for task in range(len(tasks_fnid)):
             fnid = tasks_fnid[task]
             fn = taskdata.fn_index[fnid]
             pn = self.recipecache.pkg_fn[fn]
@@ -953,7 +953,7 @@ class BBCooker:
         # Determine which bbappends haven't been applied
 
         # First get list of recipes, including skipped
-        recipefns = self.recipecache.pkg_fn.keys()
+        recipefns = list(self.recipecache.pkg_fn.keys())
         recipefns.extend(self.skiplist.keys())
 
         # Work out list of bbappends that have been applied
@@ -1152,7 +1152,7 @@ class BBCooker:
                         deplist = bb.utils.explode_dep_versions2(deps)
                     except bb.utils.VersionStringException as vse:
                         bb.fatal('Error parsing LAYERDEPENDS_%s: %s' % (c, str(vse)))
-                    for dep, oplist in deplist.iteritems():
+                    for dep, oplist in list(deplist.items()):
                         if dep in collection_list:
                             for opstr in oplist:
                                 layerver = self.data.getVar("LAYERVERSION_%s" % dep, True)
@@ -1888,7 +1888,7 @@ class Feeder(multiprocessing.Process):
         while True:
             try:
                 quit = self.quit.get_nowait()
-            except Queue.Empty:
+            except queue.Empty:
                 pass
             else:
                 if quit == 'cancel':
@@ -1902,7 +1902,7 @@ class Feeder(multiprocessing.Process):
 
             try:
                 self.to_parsers.put(job, timeout=0.5)
-            except Queue.Full:
+            except queue.Full:
                 self.jobs.insert(0, job)
                 continue
 
@@ -1942,7 +1942,7 @@ class Parser(multiprocessing.Process):
         while True:
             try:
                 self.quit.get_nowait()
-            except Queue.Empty:
+            except queue.Empty:
                 pass
             else:
                 self.results.cancel_join_thread()
@@ -1953,7 +1953,7 @@ class Parser(multiprocessing.Process):
             else:
                 try:
                     job = self.jobs.get(timeout=0.25)
-                except Queue.Empty:
+                except queue.Empty:
                     continue
 
                 if job is None:
@@ -1962,7 +1962,7 @@ class Parser(multiprocessing.Process):
 
             try:
                 self.results.put(result, timeout=0.25)
-            except Queue.Full:
+            except queue.Full:
                 pending.append(result)
 
     def parse(self, filename, appends, caches_array):
@@ -2115,7 +2115,7 @@ class CookerParser(object):
 
             try:
                 result = self.result_queue.get(timeout=0.25)
-            except Queue.Empty:
+            except queue.Empty:
                 pass
             else:
                 value = result[1]
@@ -2128,7 +2128,7 @@ class CookerParser(object):
         result = []
         parsed = None
         try:
-            parsed, result = self.results.next()
+            parsed, result = next(self.results)
         except StopIteration:
             self.shutdown()
             return False
diff --git a/lib/bb/data_smart.py b/lib/bb/data_smart.py
index 2ab884b..2a61386 100644
--- a/lib/bb/data_smart.py
+++ b/lib/bb/data_smart.py
@@ -372,7 +372,7 @@ class DataSmart(MutableMapping):
 
     def expandWithRefs(self, s, varname):
 
-        if not isinstance(s, basestring): # sanity check
+        if not isinstance(s, str): # sanity check
             return VariableParse(varname, self, s)
 
         if varname and varname in self.expand_cache:
@@ -966,4 +966,4 @@ class DataSmart(MutableMapping):
                     data.update({i:value})
 
         data_str = str([(k, data[k]) for k in sorted(data.keys())])
-        return hashlib.md5(data_str).hexdigest()
+        return hashlib.md5(data_str.encode("utf-8")).hexdigest()
diff --git a/lib/bb/event.py b/lib/bb/event.py
index 29b14f6..6fb3712 100644
--- a/lib/bb/event.py
+++ b/lib/bb/event.py
@@ -24,10 +24,7 @@ BitBake build tools.
 
 import os, sys
 import warnings
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
+import pickle
 import logging
 import atexit
 import traceback
@@ -107,7 +104,7 @@ def fire_class_handlers(event, d):
 
     eid = str(event.__class__)[8:-2]
     evt_hmap = _event_handler_map.get(eid, {})
-    for name, handler in _handlers.iteritems():
+    for name, handler in list(_handlers.items()):
         if name in _catchall_handlers or name in evt_hmap:
             if _eventfilter:
                 if not _eventfilter(name, handler, event, d):
@@ -192,7 +189,7 @@ def register(name, handler, mask=None, filename=None, lineno=None):
 
     if handler is not None:
         # handle string containing python code
-        if isinstance(handler, basestring):
+        if isinstance(handler, str):
             tmp = "def %s(e):\n%s" % (name, handler)
             try:
                 code = bb.methodpool.compile_cache(tmp)
diff --git a/lib/bb/exceptions.py b/lib/bb/exceptions.py
index eadfc57..cd71343 100644
--- a/lib/bb/exceptions.py
+++ b/lib/bb/exceptions.py
@@ -86,6 +86,6 @@ def format_exception(etype, value, tb, context=1, limit=None, formatter=None):
 
 def to_string(exc):
     if isinstance(exc, SystemExit):
-        if not isinstance(exc.code, basestring):
+        if not isinstance(exc.code, str):
             return 'Exited with "%d"' % exc.code
     return str(exc)
diff --git a/lib/bb/fetch2/__init__.py b/lib/bb/fetch2/__init__.py
index 14fe3c7..be01bdb 100644
--- a/lib/bb/fetch2/__init__.py
+++ b/lib/bb/fetch2/__init__.py
@@ -28,27 +28,23 @@ BitBake build tools.
 import os, re
 import signal
 import logging
-import urllib
-import urlparse
+import urllib.request, urllib.parse, urllib.error
+if 'git' not in urllib.parse.uses_netloc:
+    urllib.parse.uses_netloc.append('git')
+import operator
 import collections
+import subprocess
+import pickle
 import bb.persist_data, bb.utils
 import bb.checksum
 from bb import data
 import bb.process
-import subprocess
 
 __version__ = "2"
 _checksum_cache = bb.checksum.FileChecksumCache()
 
 logger = logging.getLogger("BitBake.Fetcher")
 
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-    logger.info("Importing cPickle failed. "
-                "Falling back to a very slow implementation.")
-
 class BBFetchException(Exception):
     """Class all fetch exceptions inherit from"""
     def __init__(self, message):
@@ -230,14 +226,14 @@ class URI(object):
         # them are not quite RFC compliant.
         uri, param_str = (uri.split(";", 1) + [None])[:2]
 
-        urlp = urlparse.urlparse(uri)
+        urlp = urllib.parse.urlparse(uri)
         self.scheme = urlp.scheme
 
         reparse = 0
 
         # Coerce urlparse to make URI scheme use netloc
-        if not self.scheme in urlparse.uses_netloc:
-            urlparse.uses_params.append(self.scheme)
+        if not self.scheme in urllib.parse.uses_netloc:
+            urllib.parse.uses_params.append(self.scheme)
             reparse = 1
 
         # Make urlparse happy(/ier) by converting local resources
@@ -248,7 +244,7 @@ class URI(object):
             reparse = 1
 
         if reparse:
-            urlp = urlparse.urlparse(uri)
+            urlp = urllib.parse.urlparse(uri)
 
         # Identify if the URI is relative or not
         if urlp.scheme in self._relative_schemes and \
@@ -264,7 +260,7 @@ class URI(object):
             if urlp.password:
                 self.userinfo += ':%s' % urlp.password
 
-        self.path = urllib.unquote(urlp.path)
+        self.path = urllib.parse.unquote(urlp.path)
 
         if param_str:
             self.params = self._param_str_split(param_str, ";")
@@ -312,11 +308,11 @@ class URI(object):
 
     @property
     def path_quoted(self):
-        return urllib.quote(self.path)
+        return urllib.parse.quote(self.path)
 
     @path_quoted.setter
     def path_quoted(self, path):
-        self.path = urllib.unquote(path)
+        self.path = urllib.parse.unquote(path)
 
     @property
     def path(self):
@@ -398,7 +394,7 @@ def decodeurl(url):
                 s1, s2 = s.split('=')
                 p[s1] = s2
 
-    return type, host, urllib.unquote(path), user, pswd, p
+    return type, host, urllib.parse.unquote(path), user, pswd, p
 
 def encodeurl(decoded):
     """Encodes a URL from tokens (scheme, network location, path,
@@ -422,7 +418,7 @@ def encodeurl(decoded):
     # Standardise path to ensure comparisons work
     while '//' in path:
         path = path.replace("//", "/")
-    url += "%s" % urllib.quote(path)
+    url += "%s" % urllib.parse.quote(path)
     if p:
         for parm in p:
             url += ";%s=%s" % (parm, p[parm])
@@ -1735,7 +1731,7 @@ class FetchConnectionCache(object):
             del self.cache[cn]
 
     def close_connections(self):
-        for cn in self.cache.keys():
+        for cn in list(self.cache.keys()):
             self.cache[cn].close()
             del self.cache[cn]
 
diff --git a/lib/bb/fetch2/local.py b/lib/bb/fetch2/local.py
index 303a52b..51ca78d 100644
--- a/lib/bb/fetch2/local.py
+++ b/lib/bb/fetch2/local.py
@@ -26,7 +26,7 @@ BitBake build tools.
 # Based on functions from the base bb module, Copyright 2003 Holger Schurig
 
 import os
-import urllib
+import urllib.request, urllib.parse, urllib.error
 import bb
 import bb.utils
 from   bb import data
@@ -42,7 +42,7 @@ class Local(FetchMethod):
 
     def urldata_init(self, ud, d):
         # We don't set localfile as for this fetcher the file is already local!
-        ud.decodedurl = urllib.unquote(ud.url.split("://")[1].split(";")[0])
+        ud.decodedurl = urllib.parse.unquote(ud.url.split("://")[1].split(";")[0])
         ud.basename = os.path.basename(ud.decodedurl)
         ud.basepath = ud.decodedurl
         ud.needdonestamp = False
diff --git a/lib/bb/fetch2/npm.py b/lib/bb/fetch2/npm.py
index d9e46b2..2fd4303 100644
--- a/lib/bb/fetch2/npm.py
+++ b/lib/bb/fetch2/npm.py
@@ -20,7 +20,7 @@ Usage in the recipe:
 
 import os
 import sys
-import urllib
+import urllib.request, urllib.parse, urllib.error
 import json
 import subprocess
 import signal
@@ -196,9 +196,9 @@ class Npm(FetchMethod):
                 optdepsfound[dep] = dependencies[dep]
             else:
                 depsfound[dep] = dependencies[dep]
-        for dep, version in optdepsfound.iteritems():
+        for dep, version in optdepsfound.items():
             self._getdependencies(dep, data[pkg]['deps'], version, d, ud, optional=True)
-        for dep, version in depsfound.iteritems():
+        for dep, version in depsfound.items():
             self._getdependencies(dep, data[pkg]['deps'], version, d, ud)
 
     def _getshrinkeddependencies(self, pkg, data, version, d, ud, lockdown, manifest):
diff --git a/lib/bb/fetch2/perforce.py b/lib/bb/fetch2/perforce.py
index 1aef246..ce3cda2 100644
--- a/lib/bb/fetch2/perforce.py
+++ b/lib/bb/fetch2/perforce.py
@@ -61,7 +61,7 @@ class Perforce(FetchMethod):
                     keys.append(key)
                     values.append(value)
 
-            parm = dict(zip(keys, values))
+            parm = dict(list(zip(keys, values)))
         path = "//" + path.split(';')[0]
         host += ":%s" % (port)
         parm["cset"] = Perforce.getcset(d, path, host, user, pswd, parm)
diff --git a/lib/bb/fetch2/sftp.py b/lib/bb/fetch2/sftp.py
index cb2f753..7989fcc 100644
--- a/lib/bb/fetch2/sftp.py
+++ b/lib/bb/fetch2/sftp.py
@@ -61,8 +61,7 @@ SRC_URI = "sftp://user@host.example.com/dir/path.file.txt"
 
 import os
 import bb
-import urllib
-import commands
+import urllib.request, urllib.parse, urllib.error
 from bb import data
 from bb.fetch2 import URI
 from bb.fetch2 import FetchMethod
@@ -93,7 +92,7 @@ class SFTP(FetchMethod):
         else:
             ud.basename = os.path.basename(ud.path)
 
-        ud.localfile = data.expand(urllib.unquote(ud.basename), d)
+        ud.localfile = data.expand(urllib.parse.unquote(ud.basename), d)
 
     def download(self, ud, d):
         """Fetch urls"""
@@ -121,8 +120,7 @@ class SFTP(FetchMethod):
 
         remote = '%s%s:%s' % (user, urlo.hostname, path)
 
-        cmd = '%s %s %s %s' % (basecmd, port, commands.mkarg(remote),
-                               commands.mkarg(lpath))
+        cmd = '%s %s %s %s' % (basecmd, port, remote, lpath)
 
         bb.fetch2.check_network_access(d, cmd, ud.url)
         runfetchcmd(cmd, d)
diff --git a/lib/bb/fetch2/ssh.py b/lib/bb/fetch2/ssh.py
index 635578a..56f9b7e 100644
--- a/lib/bb/fetch2/ssh.py
+++ b/lib/bb/fetch2/ssh.py
@@ -114,12 +114,10 @@ class SSH(FetchMethod):
             fr = host
         fr += ':%s' % path
 
-
-        import commands
         cmd = 'scp -B -r %s %s %s/' % (
             portarg,
-            commands.mkarg(fr),
-            commands.mkarg(dldir)
+            fr,
+            dldir
         )
 
         bb.fetch2.check_network_access(d, cmd, urldata.url)
diff --git a/lib/bb/fetch2/wget.py b/lib/bb/fetch2/wget.py
index 8bc9e93..d688fd9 100644
--- a/lib/bb/fetch2/wget.py
+++ b/lib/bb/fetch2/wget.py
@@ -31,7 +31,7 @@ import subprocess
 import os
 import logging
 import bb
-import urllib
+import urllib.request, urllib.parse, urllib.error
 from   bb import data
 from   bb.fetch2 import FetchMethod
 from   bb.fetch2 import FetchError
@@ -62,9 +62,9 @@ class Wget(FetchMethod):
         else:
             ud.basename = os.path.basename(ud.path)
 
-        ud.localfile = data.expand(urllib.unquote(ud.basename), d)
+        ud.localfile = data.expand(urllib.parse.unquote(ud.basename), d)
         if not ud.localfile:
-            ud.localfile = data.expand(urllib.unquote(ud.host + ud.path).replace("/", "."), d)
+            ud.localfile = data.expand(urllib.parse.unquote(ud.host + ud.path).replace("/", "."), d)
 
         self.basecmd = d.getVar("FETCHCMD_wget", True) or "/usr/bin/env wget -t 2 -T 30 -nv --passive-ftp --no-check-certificate"
 
@@ -105,11 +105,11 @@ class Wget(FetchMethod):
         return True
 
     def checkstatus(self, fetch, ud, d):
-        import urllib2, socket, httplib
-        from urllib import addinfourl
+        import urllib.request, urllib.error, urllib.parse, socket, http.client
+        from urllib.response import addinfourl
         from bb.fetch2 import FetchConnectionCache
 
-        class HTTPConnectionCache(httplib.HTTPConnection):
+        class HTTPConnectionCache(http.client.HTTPConnection):
             if fetch.connection_cache:
                 def connect(self):
                     """Connect to the host and port specified in __init__."""
@@ -125,7 +125,7 @@ class Wget(FetchMethod):
                     if self._tunnel_host:
                         self._tunnel()
 
-        class CacheHTTPHandler(urllib2.HTTPHandler):
+        class CacheHTTPHandler(urllib.request.HTTPHandler):
             def http_open(self, req):
                 return self.do_open(HTTPConnectionCache, req)
 
@@ -139,7 +139,7 @@ class Wget(FetchMethod):
                     - geturl(): return the original request URL
                     - code: HTTP status code
                 """
-                host = req.get_host()
+                host = req.host
                 if not host:
                     raise urlllib2.URLError('no host given')
 
@@ -147,7 +147,7 @@ class Wget(FetchMethod):
                 h.set_debuglevel(self._debuglevel)
 
                 headers = dict(req.unredirected_hdrs)
-                headers.update(dict((k, v) for k, v in req.headers.items()
+                headers.update(dict((k, v) for k, v in list(req.headers.items())
                             if k not in headers))
 
                 # We want to make an HTTP/1.1 request, but the addinfourl
@@ -164,7 +164,7 @@ class Wget(FetchMethod):
                     headers["Connection"] = "Keep-Alive" # Works for HTTP/1.0
 
                 headers = dict(
-                    (name.title(), val) for name, val in headers.items())
+                    (name.title(), val) for name, val in list(headers.items()))
 
                 if req._tunnel_host:
                     tunnel_headers = {}
@@ -177,12 +177,12 @@ class Wget(FetchMethod):
                     h.set_tunnel(req._tunnel_host, headers=tunnel_headers)
 
                 try:
-                    h.request(req.get_method(), req.get_selector(), req.data, headers)
-                except socket.error, err: # XXX what error?
+                    h.request(req.get_method(), req.selector, req.data, headers)
+                except socket.error as err: # XXX what error?
                     # Don't close connection when cache is enabled.
                     if fetch.connection_cache is None:
                         h.close()
-                    raise urllib2.URLError(err)
+                    raise urllib.error.URLError(err)
                 else:
                     try:
                         r = h.getresponse(buffering=True)
@@ -222,7 +222,7 @@ class Wget(FetchMethod):
 
                 return resp
 
-        class HTTPMethodFallback(urllib2.BaseHandler):
+        class HTTPMethodFallback(urllib.request.BaseHandler):
             """
             Fallback to GET if HEAD is not allowed (405 HTTP error)
             """
@@ -230,11 +230,11 @@ class Wget(FetchMethod):
                 fp.read()
                 fp.close()
 
-                newheaders = dict((k,v) for k,v in req.headers.items()
+                newheaders = dict((k,v) for k,v in list(req.headers.items())
                                   if k.lower() not in ("content-length", "content-type"))
-                return self.parent.open(urllib2.Request(req.get_full_url(),
+                return self.parent.open(urllib.request.Request(req.get_full_url(),
                                                         headers=newheaders,
-                                                        origin_req_host=req.get_origin_req_host(),
+                                                        origin_req_host=req.origin_req_host,
                                                         unverifiable=True))
 
             """
@@ -249,35 +249,35 @@ class Wget(FetchMethod):
             """
             http_error_406 = http_error_405
 
-        class FixedHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
+        class FixedHTTPRedirectHandler(urllib.request.HTTPRedirectHandler):
             """
             urllib2.HTTPRedirectHandler resets the method to GET on redirect,
             when we want to follow redirects using the original method.
             """
             def redirect_request(self, req, fp, code, msg, headers, newurl):
-                newreq = urllib2.HTTPRedirectHandler.redirect_request(self, req, fp, code, msg, headers, newurl)
+                newreq = urllib.request.HTTPRedirectHandler.redirect_request(self, req, fp, code, msg, headers, newurl)
                 newreq.get_method = lambda: req.get_method()
                 return newreq
         exported_proxies = export_proxies(d)
 
         handlers = [FixedHTTPRedirectHandler, HTTPMethodFallback]
         if export_proxies:
-            handlers.append(urllib2.ProxyHandler())
+            handlers.append(urllib.request.ProxyHandler())
         handlers.append(CacheHTTPHandler())
         # XXX: Since Python 2.7.9 ssl cert validation is enabled by default
         # see PEP-0476, this causes verification errors on some https servers
         # so disable by default.
         import ssl
         if hasattr(ssl, '_create_unverified_context'):
-            handlers.append(urllib2.HTTPSHandler(context=ssl._create_unverified_context()))
-        opener = urllib2.build_opener(*handlers)
+            handlers.append(urllib.request.HTTPSHandler(context=ssl._create_unverified_context()))
+        opener = urllib.request.build_opener(*handlers)
 
         try:
             uri = ud.url.split(";")[0]
-            r = urllib2.Request(uri)
+            r = urllib.request.Request(uri)
             r.get_method = lambda: "HEAD"
             opener.open(r)
-        except urllib2.URLError as e:
+        except urllib.error.URLError as e:
             # debug for now to avoid spamming the logs in e.g. remote sstate searches
             logger.debug(2, "checkstatus() urlopen failed: %s" % e)
             return False
diff --git a/lib/bb/main.py b/lib/bb/main.py
index 761ea45..b296ef8 100755
--- a/lib/bb/main.py
+++ b/lib/bb/main.py
@@ -27,6 +27,7 @@ import sys
 import logging
 import optparse
 import warnings
+import fcntl
 
 import bb
 from bb import event
@@ -336,10 +337,7 @@ def start_server(servermodule, configParams, configuration, features):
         server.saveConnectionDetails()
     except Exception as e:
         while hasattr(server, "event_queue"):
-            try:
-                import queue
-            except ImportError:
-                import Queue as queue
+            import queue
             try:
                 event = server.event_queue.get(block=False)
             except (queue.Empty, IOError):
@@ -363,7 +361,10 @@ def bitbake_main(configParams, configuration):
     # updates to log files for use with tail
     try:
         if sys.stdout.name == '<stdout>':
-            sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
+            # Reopen with O_SYNC (unbuffered)
+            fl = fcntl.fcntl(sys.stdout.fileno(), fcntl.F_GETFL)
+            fl |= os.O_SYNC
+            fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, fl)
     except:
         pass
 
diff --git a/lib/bb/msg.py b/lib/bb/msg.py
index 6fdd1f5..8c3ab47 100644
--- a/lib/bb/msg.py
+++ b/lib/bb/msg.py
@@ -57,7 +57,7 @@ class BBLogFormatter(logging.Formatter):
     }
 
     color_enabled = False
-    BASECOLOR, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(29,38)
+    BASECOLOR, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = list(range(29,38))
 
     COLORS = {
         DEBUG3  : CYAN,
diff --git a/lib/bb/parse/ast.py b/lib/bb/parse/ast.py
index bc3a2f8..548929f 100644
--- a/lib/bb/parse/ast.py
+++ b/lib/bb/parse/ast.py
@@ -138,7 +138,7 @@ class DataNode(AstNode):
             data.setVar(key, val, parsing=True, **loginfo)
 
 class MethodNode(AstNode):
-    tr_tbl = string.maketrans('/.+-@%&', '_______')
+    tr_tbl = str.maketrans('/.+-@%&', '_______')
 
     def __init__(self, filename, lineno, func_name, body, python, fakeroot):
         AstNode.__init__(self, filename, lineno)
@@ -340,17 +340,17 @@ def _create_variants(datastores, names, function, onlyfinalise):
         function(arg or name, new_d)
         datastores[name] = new_d
 
-    for variant, variant_d in datastores.items():
+    for variant in list(datastores.keys()):
         for name in names:
             if not variant:
                 # Based on main recipe
-                create_variant(name, variant_d)
+                create_variant(name, datastores[""])
             else:
-                create_variant("%s-%s" % (variant, name), variant_d, name)
+                create_variant("%s-%s" % (variant, name), datastores[variant], name)
 
 def _expand_versions(versions):
     def expand_one(version, start, end):
-        for i in xrange(start, end + 1):
+        for i in range(start, end + 1):
             ver = _bbversions_re.sub(str(i), version, 1)
             yield ver
 
@@ -459,16 +459,16 @@ def multi_finalize(fn, d):
         safe_d.setVar("BBCLASSEXTEND", extended)
         _create_variants(datastores, extendedmap.keys(), extendfunc, onlyfinalise)
 
-    for variant, variant_d in datastores.iteritems():
+    for variant in datastores.keys():
         if variant:
             try:
                 if not onlyfinalise or variant in onlyfinalise:
-                    finalize(fn, variant_d, variant)
+                    finalize(fn, datastores[variant], variant)
             except bb.parse.SkipRecipe as e:
-                variant_d.setVar("__SKIPPED", e.args[0])
+                datastores[variant].setVar("__SKIPPED", e.args[0])
 
     if len(datastores) > 1:
-        variants = filter(None, datastores.iterkeys())
+        variants = filter(None, datastores.keys())
         safe_d.setVar("__VARIANTS", " ".join(variants))
 
     datastores[""] = d
diff --git a/lib/bb/persist_data.py b/lib/bb/persist_data.py
index e450423..403f3a5 100644
--- a/lib/bb/persist_data.py
+++ b/lib/bb/persist_data.py
@@ -92,9 +92,9 @@ class SQLTable(collections.MutableMapping):
         self._execute("DELETE from %s where key=?;" % self.table, [key])
 
     def __setitem__(self, key, value):
-        if not isinstance(key, basestring):
+        if not isinstance(key, str):
             raise TypeError('Only string keys are supported')
-        elif not isinstance(value, basestring):
+        elif not isinstance(value, str):
             raise TypeError('Only string values are supported')
 
         data = self._execute("SELECT * from %s where key=?;" %
@@ -131,14 +131,14 @@ class SQLTable(collections.MutableMapping):
         return [row[1] for row in data]
 
     def values(self):
-        return list(self.itervalues())
+        return list(self.values())
 
     def itervalues(self):
         data = self._execute("SELECT value FROM %s;" % self.table)
         return (row[0] for row in data)
 
     def items(self):
-        return list(self.iteritems())
+        return list(self.items())
 
     def iteritems(self):
         return self._execute("SELECT * FROM %s;" % self.table)
@@ -178,7 +178,7 @@ class PersistData(object):
         """
         Return a list of key + value pairs for a domain
         """
-        return self.data[domain].items()
+        return list(self.data[domain].items())
 
     def getValue(self, domain, key):
         """
diff --git a/lib/bb/process.py b/lib/bb/process.py
index 1c07f2d..c62d7bc 100644
--- a/lib/bb/process.py
+++ b/lib/bb/process.py
@@ -17,7 +17,7 @@ class CmdError(RuntimeError):
         self.msg = msg
 
     def __str__(self):
-        if not isinstance(self.command, basestring):
+        if not isinstance(self.command, str):
             cmd = subprocess.list2cmdline(self.command)
         else:
             cmd = self.command
@@ -97,6 +97,8 @@ def _logged_communicate(pipe, log, input, extrafiles):
     try:
         while pipe.poll() is None:
             rlist = rin
+            stdoutbuf = b""
+            stderrbuf = b""
             try:
                 r,w,e = select.select (rlist, [], [], 1)
             except OSError as e:
@@ -104,16 +106,26 @@ def _logged_communicate(pipe, log, input, extrafiles):
                     raise
 
             if pipe.stdout in r:
-                data = pipe.stdout.read()
-                if data is not None:
-                    outdata.append(data)
-                    log.write(data)
+                data = stdoutbuf + pipe.stdout.read()
+                if data is not None and len(data) > 0:
+                    try:
+                        data = data.decode("utf-8")
+                        outdata.append(data)
+                        log.write(data)
+                        stdoutbuf = b""
+                    except UnicodeDecodeError:
+                        stdoutbuf = data
 
             if pipe.stderr in r:
-                data = pipe.stderr.read()
-                if data is not None:
-                    errdata.append(data)
-                    log.write(data)
+                data = stderrbuf + pipe.stderr.read()
+                if data is not None and len(data) > 0:
+                    try:
+                        data = data.decode("utf-8")
+                        errdata.append(data)
+                        log.write(data)
+                        stderrbuf = b""
+                    except UnicodeDecodeError:
+                        stderrbuf = data
 
             readextras(r)
 
@@ -135,7 +147,7 @@ def run(cmd, input=None, log=None, extrafiles=None, **options):
     if not extrafiles:
         extrafiles = []
 
-    if isinstance(cmd, basestring) and not "shell" in options:
+    if isinstance(cmd, str) and not "shell" in options:
         options["shell"] = True
 
     try:
@@ -150,6 +162,10 @@ def run(cmd, input=None, log=None, extrafiles=None, **options):
         stdout, stderr = _logged_communicate(pipe, log, input, extrafiles)
     else:
         stdout, stderr = pipe.communicate(input)
+        if stdout:
+            stdout = stdout.decode("utf-8")
+        if stderr:
+            stderr = stderr.decode("utf-8")
 
     if pipe.returncode != 0:
         raise ExecutionError(cmd, pipe.returncode, stdout, stderr)
diff --git a/lib/bb/providers.py b/lib/bb/providers.py
index 563a091..80701b2 100644
--- a/lib/bb/providers.py
+++ b/lib/bb/providers.py
@@ -245,7 +245,7 @@ def _filterProviders(providers, item, cfgData, dataCache):
             pkg_pn[pn] = []
         pkg_pn[pn].append(p)
 
-    logger.debug(1, "providers for %s are: %s", item, pkg_pn.keys())
+    logger.debug(1, "providers for %s are: %s", item, list(pkg_pn.keys()))
 
     # First add PREFERRED_VERSIONS
     for pn in pkg_pn:
diff --git a/lib/bb/pysh/builtin.py b/lib/bb/pysh/builtin.py
index b748e4a..a8814dc 100644
--- a/lib/bb/pysh/builtin.py
+++ b/lib/bb/pysh/builtin.py
@@ -527,7 +527,7 @@ def utility_sed(name, args, interp, env, stdin, stdout, stderr, debugflags):
         print interp.log(' '.join([name, str(args), interp['PWD']]) + '\n')
         
     # Scan pattern arguments and append a space if necessary
-    for i in xrange(len(args)):
+    for i in range(len(args)):
         if not RE_SED.search(args[i]):
             continue
         args[i] = args[i] + ' '
diff --git a/lib/bb/pysh/interp.py b/lib/bb/pysh/interp.py
index 25d8c92..d14ecf3 100644
--- a/lib/bb/pysh/interp.py
+++ b/lib/bb/pysh/interp.py
@@ -474,7 +474,7 @@ class Environment:
         """
         # Save and remove previous arguments
         prevargs = []        
-        for i in xrange(int(self._env['#'])):
+        for i in range(int(self._env['#'])):
             i = str(i+1)
             prevargs.append(self._env[i])
             del self._env[i]
@@ -488,7 +488,7 @@ class Environment:
         return prevargs
         
     def get_positional_args(self):
-        return [self._env[str(i+1)] for i in xrange(int(self._env['#']))]
+        return [self._env[str(i+1)] for i in range(int(self._env['#']))]
         
     def get_variables(self):
         return dict(self._env)
diff --git a/lib/bb/pysh/pyshlex.py b/lib/bb/pysh/pyshlex.py
index b301236..fbf094b 100644
--- a/lib/bb/pysh/pyshlex.py
+++ b/lib/bb/pysh/pyshlex.py
@@ -20,7 +20,7 @@ except NameError:
     from Set import Set as set
 
 from ply import lex
-from sherrors import *
+from bb.pysh.sherrors import *
 
 class NeedMore(Exception):
     pass
diff --git a/lib/bb/pysh/pyshyacc.py b/lib/bb/pysh/pyshyacc.py
index e8e80aa..ba4cefd 100644
--- a/lib/bb/pysh/pyshyacc.py
+++ b/lib/bb/pysh/pyshyacc.py
@@ -10,11 +10,11 @@
 import os.path
 import sys
 
-import pyshlex
+import bb.pysh.pyshlex as pyshlex
 tokens = pyshlex.tokens
 
 from ply import yacc
-import sherrors
+import bb.pysh.sherrors as sherrors
     
 class IORedirect:
     def __init__(self, op, filename, io_number=None):
diff --git a/lib/bb/runqueue.py b/lib/bb/runqueue.py
index 5ab7e97..376c9f5 100644
--- a/lib/bb/runqueue.py
+++ b/lib/bb/runqueue.py
@@ -35,11 +35,7 @@ import bb
 from bb import msg, data, event
 from bb import monitordisk
 import subprocess
-
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
+import pickle
 
 bblogger = logging.getLogger("BitBake")
 logger = logging.getLogger("BitBake.RunQueue")
@@ -108,7 +104,7 @@ class RunQueueScheduler(object):
 
         self.buildable = []
         self.stamps = {}
-        for taskid in xrange(self.numTasks):
+        for taskid in range(self.numTasks):
             fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[taskid]]
             taskname = self.rqdata.runq_task[taskid]
             self.stamps[taskid] = bb.build.stampfile(taskname, self.rqdata.dataCache, fn)
@@ -127,12 +123,12 @@ class RunQueueScheduler(object):
         if len(self.buildable) == 1:
             taskid = self.buildable[0]
             stamp = self.stamps[taskid]
-            if stamp not in self.rq.build_stamps.itervalues():
+            if stamp not in self.rq.build_stamps.values():
                 return taskid
 
         if not self.rev_prio_map:
-            self.rev_prio_map = range(self.numTasks)
-            for taskid in xrange(self.numTasks):
+            self.rev_prio_map = list(range(self.numTasks))
+            for taskid in range(self.numTasks):
                 self.rev_prio_map[self.prio_map[taskid]] = taskid
 
         best = None
@@ -141,7 +137,7 @@ class RunQueueScheduler(object):
             prio = self.rev_prio_map[taskid]
             if bestprio is None or bestprio > prio:
                 stamp = self.stamps[taskid]
-                if stamp in self.rq.build_stamps.itervalues():
+                if stamp in self.rq.build_stamps.values():
                     continue
                 bestprio = prio
                 best = taskid
@@ -269,7 +265,7 @@ class RunQueueData:
 
 
     def get_task_id(self, fnid, taskname):
-        for listid in xrange(len(self.runq_fnid)):
+        for listid in range(len(self.runq_fnid)):
             if self.runq_fnid[listid] == fnid and self.runq_task[listid] == taskname:
                 return listid
         return None
@@ -291,7 +287,7 @@ class RunQueueData:
             """
             lowest = 0
             new_chain = []
-            for entry in xrange(len(chain)):
+            for entry in range(len(chain)):
                 if chain[entry] < chain[lowest]:
                     lowest = entry
             new_chain.extend(chain[lowest:])
@@ -304,7 +300,7 @@ class RunQueueData:
             """
             if len(chain1) != len(chain2):
                 return False
-            for index in xrange(len(chain1)):
+            for index in range(len(chain1)):
                 if chain1[index] != chain2[index]:
                     return False
             return True
@@ -375,7 +371,7 @@ class RunQueueData:
         deps_left = []
         task_done = []
 
-        for listid in xrange(numTasks):
+        for listid in range(numTasks):
             task_done.append(False)
             weight.append(1)
             deps_left.append(len(self.runq_revdeps[listid]))
@@ -399,7 +395,7 @@ class RunQueueData:
 
         # Circular dependency sanity check
         problem_tasks = []
-        for task in xrange(numTasks):
+        for task in range(numTasks):
             if task_done[task] is False or deps_left[task] != 0:
                 problem_tasks.append(task)
                 logger.debug(2, "Task %s (%s) is not buildable", task, self.get_user_idstring(task))
@@ -482,7 +478,7 @@ class RunQueueData:
                     if taskid is not None:
                         depends.add(taskid)
 
-        for task in xrange(len(taskData.tasks_name)):
+        for task in range(len(taskData.tasks_name)):
             depends = set()
             fnid = taskData.tasks_fnid[task]
             fn = taskData.fn_index[fnid]
@@ -597,7 +593,7 @@ class RunQueueData:
         for task in recursivetasks:
             extradeps[task].difference_update(recursivetasksselfref)
 
-        for task in xrange(len(taskData.tasks_name)):
+        for task in range(len(taskData.tasks_name)):
             # Add in extra dependencies
             if task in extradeps:
                  self.runq_depends[task] = extradeps[task]
@@ -675,7 +671,7 @@ class RunQueueData:
 
         maps = []
         delcount = 0
-        for listid in xrange(len(self.runq_fnid)):
+        for listid in range(len(self.runq_fnid)):
             if runq_build[listid-delcount] == 1:
                 maps.append(listid-delcount)
             else:
@@ -703,7 +699,7 @@ class RunQueueData:
 
         # Remap the dependencies to account for the deleted tasks
         # Check we didn't delete a task we depend on
-        for listid in xrange(len(self.runq_fnid)):
+        for listid in range(len(self.runq_fnid)):
             newdeps = []
             origdeps = self.runq_depends[listid]
             for origdep in origdeps:
@@ -715,14 +711,14 @@ class RunQueueData:
         logger.verbose("Assign Weightings")
 
         # Generate a list of reverse dependencies to ease future calculations
-        for listid in xrange(len(self.runq_fnid)):
+        for listid in range(len(self.runq_fnid)):
             for dep in self.runq_depends[listid]:
                 self.runq_revdeps[dep].add(listid)
 
         # Identify tasks at the end of dependency chains
         # Error on circular dependency loops (length two)
         endpoints = []
-        for listid in xrange(len(self.runq_fnid)):
+        for listid in range(len(self.runq_fnid)):
             revdeps = self.runq_revdeps[listid]
             if len(revdeps) == 0:
                 endpoints.append(listid)
@@ -740,7 +736,7 @@ class RunQueueData:
         # Sanity Check - Check for multiple tasks building the same provider
         prov_list = {}
         seen_fn = []
-        for task in xrange(len(self.runq_fnid)):
+        for task in range(len(self.runq_fnid)):
             fn = taskData.fn_index[self.runq_fnid[task]]
             if fn in seen_fn:
                 continue
@@ -905,7 +901,7 @@ class RunQueueData:
         Dump some debug information on the internal data structures
         """
         logger.debug(3, "run_tasks:")
-        for task in xrange(len(self.rqdata.runq_task)):
+        for task in range(len(self.rqdata.runq_task)):
             logger.debug(3, " (%s)%s - %s: %s   Deps %s RevDeps %s", task,
                          taskQueue.fn_index[self.rqdata.runq_fnid[task]],
                          self.rqdata.runq_task[task],
@@ -914,7 +910,7 @@ class RunQueueData:
                          self.rqdata.runq_revdeps[task])
 
         logger.debug(3, "sorted_tasks:")
-        for task1 in xrange(len(self.rqdata.runq_task)):
+        for task1 in range(len(self.rqdata.runq_task)):
             if task1 in self.prio_map:
                 task = self.prio_map[task1]
                 logger.debug(3, " (%s)%s - %s: %s   Deps %s RevDeps %s", task,
@@ -982,8 +978,8 @@ class RunQueue:
             "time" : self.cfgData.getVar("TIME", True),
         }
 
-        worker.stdin.write("<cookerconfig>" + pickle.dumps(self.cooker.configuration) + "</cookerconfig>")
-        worker.stdin.write("<workerdata>" + pickle.dumps(workerdata) + "</workerdata>")
+        worker.stdin.write(b"<cookerconfig>" + pickle.dumps(self.cooker.configuration) + b"</cookerconfig>")
+        worker.stdin.write(b"<workerdata>" + pickle.dumps(workerdata) + b"</workerdata>")
         worker.stdin.flush()
 
         return worker, workerpipe
@@ -993,8 +989,9 @@ class RunQueue:
             return
         logger.debug(1, "Teardown for bitbake-worker")
         try:
-           worker.stdin.write("<quit></quit>")
+           worker.stdin.write(b"<quit></quit>")
            worker.stdin.flush()
+           worker.stdin.close()
         except IOError:
            pass
         while worker.returncode is None:
@@ -1245,7 +1242,7 @@ class RunQueue:
         stamppresent = []
         valid_new = set()
 
-        for task in xrange(len(self.rqdata.runq_fnid)):
+        for task in range(len(self.rqdata.runq_fnid)):
             fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[task]]
             taskname = self.rqdata.runq_task[task]
             taskdep = self.rqdata.dataCache.task_deps[fn]
@@ -1287,7 +1284,7 @@ class RunQueue:
                     valid_new.add(dep)
 
         invalidtasks = set()
-        for task in xrange(len(self.rqdata.runq_fnid)):
+        for task in range(len(self.rqdata.runq_fnid)):
             if task not in valid_new and task not in noexec:
                 invalidtasks.add(task)
 
@@ -1346,7 +1343,7 @@ class RunQueue:
                     match = m
             if match is None:
                 bb.fatal("Can't find a task we're supposed to have written out? (hash: %s)?" % h)
-            matches = {k : v for k, v in matches.iteritems() if h not in k}
+            matches = {k : v for k, v in iter(matches.items()) if h not in k}
             if matches:
                 latestmatch = sorted(matches.keys(), key=lambda f: matches[f])[-1]
                 prevh = __find_md5__.search(latestmatch).group(0)
@@ -1395,17 +1392,15 @@ class RunQueueExecute:
         return True
 
     def finish_now(self):
-
         for worker in [self.rq.worker, self.rq.fakeworker]:
             if not worker:
                 continue
             try:
-                worker.stdin.write("<finishnow></finishnow>")
+                worker.stdin.write(b"<finishnow></finishnow>")
                 worker.stdin.flush()
             except IOError:
                 # worker must have died?
                 pass
-
         if len(self.failed_fnids) != 0:
             self.rq.state = runQueueFailed
             return
@@ -1468,7 +1463,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
         initial_covered = self.rq.scenequeue_covered.copy()
 
         # Mark initial buildable tasks
-        for task in xrange(self.stats.total):
+        for task in range(self.stats.total):
             self.runq_running.append(0)
             self.runq_complete.append(0)
             if len(self.rqdata.runq_depends[task]) == 0:
@@ -1481,7 +1476,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
         found = True
         while found:
             found = False
-            for task in xrange(self.stats.total):
+            for task in range(self.stats.total):
                 if task in self.rq.scenequeue_covered:
                     continue
                 logger.debug(1, 'Considering %s (%s): %s' % (task, self.rqdata.get_user_idstring(task), str(self.rqdata.runq_revdeps[task])))
@@ -1496,7 +1491,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
         covered_remove = set()
         if self.rq.setsceneverify:
             invalidtasks = []
-            for task in xrange(len(self.rqdata.runq_task)):
+            for task in range(len(self.rqdata.runq_task)):
                 fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[task]]
                 taskname = self.rqdata.runq_task[task]
                 taskdep = self.rqdata.dataCache.task_deps[fn]
@@ -1684,10 +1679,10 @@ class RunQueueExecuteTasks(RunQueueExecute):
                         logger.critical("Failed to spawn fakeroot worker to run %s:%s: %s" % (fn, taskname, str(exc)))
                         self.rq.state = runQueueFailed
                         return True
-                self.rq.fakeworker.stdin.write("<runtask>" + pickle.dumps((fn, task, taskname, False, self.cooker.collection.get_file_appends(fn), taskdepdata)) + "</runtask>")
+                self.rq.fakeworker.stdin.write(b"<runtask>" + pickle.dumps((fn, task, taskname, False, self.cooker.collection.get_file_appends(fn), taskdepdata)) + b"</runtask>")
                 self.rq.fakeworker.stdin.flush()
             else:
-                self.rq.worker.stdin.write("<runtask>" + pickle.dumps((fn, task, taskname, False, self.cooker.collection.get_file_appends(fn), taskdepdata)) + "</runtask>")
+                self.rq.worker.stdin.write(b"<runtask>" + pickle.dumps((fn, task, taskname, False, self.cooker.collection.get_file_appends(fn), taskdepdata)) + b"</runtask>")
                 self.rq.worker.stdin.flush()
 
             self.build_stamps[task] = bb.build.stampfile(taskname, self.rqdata.dataCache, fn)
@@ -1706,7 +1701,7 @@ class RunQueueExecuteTasks(RunQueueExecute):
             return True
 
         # Sanity Checks
-        for task in xrange(self.stats.total):
+        for task in range(self.stats.total):
             if self.runq_buildable[task] == 0:
                 logger.error("Task %s never buildable!", task)
             if self.runq_running[task] == 0:
@@ -1764,14 +1759,14 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
         # therefore aims to collapse the huge runqueue dependency tree into a smaller one
         # only containing the setscene functions.
 
-        for task in xrange(self.stats.total):
+        for task in range(self.stats.total):
             self.runq_running.append(0)
             self.runq_complete.append(0)
             self.runq_buildable.append(0)
 
         # First process the chains up to the first setscene task.
         endpoints = {}
-        for task in xrange(len(self.rqdata.runq_fnid)):
+        for task in range(len(self.rqdata.runq_fnid)):
             sq_revdeps.append(copy.copy(self.rqdata.runq_revdeps[task]))
             sq_revdeps_new.append(set())
             if (len(self.rqdata.runq_revdeps[task]) == 0) and task not in self.rqdata.runq_setscene:
@@ -1833,7 +1828,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
                         newendpoints[dep] = tasks
             if len(newendpoints) != 0:
                 process_endpoints2(newendpoints)
-        for task in xrange(len(self.rqdata.runq_fnid)):
+        for task in range(len(self.rqdata.runq_fnid)):
             sq_revdeps2.append(copy.copy(self.rqdata.runq_revdeps[task]))
             sq_revdeps_new2.append(set())
             if (len(self.rqdata.runq_revdeps[task]) == 0) and task not in self.rqdata.runq_setscene:
@@ -1844,7 +1839,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
             if sq_revdeps_new2[task]:
                 self.unskippable.append(self.rqdata.runq_setscene.index(task))
 
-        for task in xrange(len(self.rqdata.runq_fnid)):
+        for task in range(len(self.rqdata.runq_fnid)):
             if task in self.rqdata.runq_setscene:
                 deps = set()
                 for dep in sq_revdeps_new[task]:
@@ -1883,7 +1878,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
              for dep in self.sq_harddeps[task]:
                  sq_revdeps_squash[dep].add(task)
 
-        #for task in xrange(len(sq_revdeps_squash)):
+        #for task in range(len(sq_revdeps_squash)):
         #    realtask = self.rqdata.runq_setscene[task]
         #    bb.warn("Task %s: %s_setscene is %s " % (task, self.rqdata.get_user_idstring(realtask) , sq_revdeps_squash[task]))
 
@@ -1891,13 +1886,13 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
         self.sq_revdeps = sq_revdeps_squash
         self.sq_revdeps2 = copy.deepcopy(self.sq_revdeps)
 
-        for task in xrange(len(self.sq_revdeps)):
+        for task in range(len(self.sq_revdeps)):
             self.sq_deps.append(set())
-        for task in xrange(len(self.sq_revdeps)):
+        for task in range(len(self.sq_revdeps)):
             for dep in self.sq_revdeps[task]:
                 self.sq_deps[dep].add(task)
 
-        for task in xrange(len(self.sq_revdeps)):
+        for task in range(len(self.sq_revdeps)):
             if len(self.sq_revdeps[task]) == 0:
                 self.runq_buildable[task] = 1
 
@@ -1910,7 +1905,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
             sq_task = []
             noexec = []
             stamppresent = []
-            for task in xrange(len(self.sq_revdeps)):
+            for task in range(len(self.sq_revdeps)):
                 realtask = self.rqdata.runq_setscene[task]
                 fn = self.rqdata.taskData.fn_index[self.rqdata.runq_fnid[realtask]]
                 taskname = self.rqdata.runq_task[realtask]
@@ -1947,7 +1942,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
             for v in valid:
                 valid_new.append(sq_task[v])
 
-            for task in xrange(len(self.sq_revdeps)):
+            for task in range(len(self.sq_revdeps)):
                 if task not in valid_new and task not in noexec:
                     realtask = self.rqdata.runq_setscene[task]
                     logger.debug(2, 'No package found, so skipping setscene task %s',
@@ -2024,7 +2019,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
         task = None
         if self.stats.active < self.number_tasks:
             # Find the next setscene to run
-            for nexttask in xrange(self.stats.total):
+            for nexttask in range(self.stats.total):
                 if self.runq_buildable[nexttask] == 1 and self.runq_running[nexttask] != 1:
                     if nexttask in self.unskippable:
                         logger.debug(2, "Setscene task %s is unskippable" % self.rqdata.get_user_idstring(self.rqdata.runq_setscene[nexttask]))                      
@@ -2076,10 +2071,10 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
             if 'fakeroot' in taskdep and taskname in taskdep['fakeroot'] and not self.cooker.configuration.dry_run:
                 if not self.rq.fakeworker:
                     self.rq.start_fakeworker(self)
-                self.rq.fakeworker.stdin.write("<runtask>" + pickle.dumps((fn, realtask, taskname, True, self.cooker.collection.get_file_appends(fn), None)) + "</runtask>")
+                self.rq.fakeworker.stdin.write(b"<runtask>" + pickle.dumps((fn, realtask, taskname, True, self.cooker.collection.get_file_appends(fn), None)) + b"</runtask>")
                 self.rq.fakeworker.stdin.flush()
             else:
-                self.rq.worker.stdin.write("<runtask>" + pickle.dumps((fn, realtask, taskname, True, self.cooker.collection.get_file_appends(fn), None)) + "</runtask>")
+                self.rq.worker.stdin.write(b"<runtask>" + pickle.dumps((fn, realtask, taskname, True, self.cooker.collection.get_file_appends(fn), None)) + b"</runtask>")
                 self.rq.worker.stdin.flush()
 
             self.runq_running[task] = 1
@@ -2091,7 +2086,7 @@ class RunQueueExecuteScenequeue(RunQueueExecute):
             self.rq.read_workers()
             return self.rq.active_fds()
 
-        #for task in xrange(self.stats.total):
+        #for task in range(self.stats.total):
         #    if self.runq_running[task] != 1:
         #        buildable = self.runq_buildable[task]
         #        revdeps = self.sq_revdeps[task]
@@ -2227,7 +2222,7 @@ class runQueuePipe():
         if pipeout:
             pipeout.close()
         bb.utils.nonblockingfd(self.input)
-        self.queue = ""
+        self.queue = b""
         self.d = d
         self.rq = rq
         self.rqexec = rqexec
@@ -2251,7 +2246,7 @@ class runQueuePipe():
 
         start = len(self.queue)
         try:
-            self.queue = self.queue + self.input.read(102400)
+            self.queue = self.queue + (self.input.read(102400) or b"")
         except (OSError, IOError) as e:
             if e.errno != errno.EAGAIN:
                 raise
@@ -2259,8 +2254,8 @@ class runQueuePipe():
         found = True
         while found and len(self.queue):
             found = False
-            index = self.queue.find("</event>")
-            while index != -1 and self.queue.startswith("<event>"):
+            index = self.queue.find(b"</event>")
+            while index != -1 and self.queue.startswith(b"<event>"):
                 try:
                     event = pickle.loads(self.queue[7:index])
                 except ValueError as e:
@@ -2268,9 +2263,9 @@ class runQueuePipe():
                 bb.event.fire_from_worker(event, self.d)
                 found = True
                 self.queue = self.queue[index+8:]
-                index = self.queue.find("</event>")
-            index = self.queue.find("</exitcode>")
-            while index != -1 and self.queue.startswith("<exitcode>"):
+                index = self.queue.find(b"</event>")
+            index = self.queue.find(b"</exitcode>")
+            while index != -1 and self.queue.startswith(b"<exitcode>"):
                 try:
                     task, status = pickle.loads(self.queue[10:index])
                 except ValueError as e:
@@ -2278,7 +2273,7 @@ class runQueuePipe():
                 self.rqexec.runqueue_process_waitpid(task, status)
                 found = True
                 self.queue = self.queue[index+11:]
-                index = self.queue.find("</exitcode>")
+                index = self.queue.find(b"</exitcode>")
         return (end > start)
 
     def close(self):
diff --git a/lib/bb/server/process.py b/lib/bb/server/process.py
index cc58c72..982fcf7 100644
--- a/lib/bb/server/process.py
+++ b/lib/bb/server/process.py
@@ -30,7 +30,7 @@ import signal
 import sys
 import time
 import select
-from Queue import Empty
+from queue import Empty
 from multiprocessing import Event, Process, util, Queue, Pipe, queues, Manager
 
 from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
@@ -137,7 +137,7 @@ class ProcessServer(Process, BaseImplServer):
         if not fds:
             fds = []
 
-        for function, data in self._idlefuns.items():
+        for function, data in list(self._idlefuns.items()):
             try:
                 retval = function(self, data, False)
                 if retval is False:
@@ -145,7 +145,7 @@ class ProcessServer(Process, BaseImplServer):
                     nextsleep = None
                 elif retval is True:
                     nextsleep = None
-                elif isinstance(retval, float):
+                elif isinstance(retval, float) and nextsleep:
                     if (retval < nextsleep):
                         nextsleep = retval
                 elif nextsleep is None:
@@ -213,7 +213,7 @@ class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
 # Wrap Queue to provide API which isn't server implementation specific
 class ProcessEventQueue(multiprocessing.queues.Queue):
     def __init__(self, maxsize):
-        multiprocessing.queues.Queue.__init__(self, maxsize)
+        multiprocessing.queues.Queue.__init__(self, maxsize, ctx=multiprocessing.get_context())
         self.exit = False
         bb.utils.set_process_name("ProcessEQueue")
 
diff --git a/lib/bb/server/xmlrpc.py b/lib/bb/server/xmlrpc.py
index ace1cf6..146ca17 100644
--- a/lib/bb/server/xmlrpc.py
+++ b/lib/bb/server/xmlrpc.py
@@ -31,31 +31,33 @@
     in the server's main loop.
 """
 
+import os
+import sys
+
+import hashlib
+import time
+import socket
+import signal
+import threading
+import pickle
+import inspect
+import select
+import http.client
+import xmlrpc.client
+from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
+
 import bb
-import xmlrpclib, sys
 from bb import daemonize
 from bb.ui import uievent
-import hashlib, time
-import socket
-import os, signal
-import threading
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
+from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
 
 DEBUG = False
 
-from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
-import inspect, select, httplib
-
-from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
-
-class BBTransport(xmlrpclib.Transport):
+class BBTransport(xmlrpc.client.Transport):
     def __init__(self, timeout):
         self.timeout = timeout
         self.connection_token = None
-        xmlrpclib.Transport.__init__(self)
+        xmlrpc.client.Transport.__init__(self)
 
     # Modified from default to pass timeout to HTTPConnection
     def make_connection(self, host):
@@ -67,7 +69,7 @@ class BBTransport(xmlrpclib.Transport):
         # create a HTTP connection object from a host descriptor
         chost, self._extra_headers, x509 = self.get_host_info(host)
         #store the host argument along with the connection object
-        self._connection = host, httplib.HTTPConnection(chost, timeout=self.timeout)
+        self._connection = host, http.client.HTTPConnection(chost, timeout=self.timeout)
         return self._connection[1]
 
     def set_connection_token(self, token):
@@ -76,11 +78,11 @@ class BBTransport(xmlrpclib.Transport):
     def send_content(self, h, body):
         if self.connection_token:
             h.putheader("Bitbake-token", self.connection_token)
-        xmlrpclib.Transport.send_content(self, h, body)
+        xmlrpc.client.Transport.send_content(self, h, body)
 
 def _create_server(host, port, timeout = 60):
     t = BBTransport(timeout)
-    s = xmlrpclib.ServerProxy("http://%s:%d/" % (host, port), transport=t, allow_none=True)
+    s = xmlrpc.client.ServerProxy("http://%s:%d/" % (host, port), transport=t, allow_none=True, use_builtin_types=True)
     return s, t
 
 class BitBakeServerCommands():
@@ -128,7 +130,7 @@ class BitBakeServerCommands():
     def addClient(self):
         if self.has_client:
             return None
-        token = hashlib.md5(str(time.time())).hexdigest()
+        token = hashlib.md5(str(time.time()).encode("utf-8")).hexdigest()
         self.server.set_connection_token(token)
         self.has_client = True
         return token
@@ -232,7 +234,7 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
         while not self.quit:
             fds = [self]
             nextsleep = 0.1
-            for function, data in self._idlefuns.items():
+            for function, data in list(self._idlefuns.items()):
                 retval = None
                 try:
                     retval = function(self, data, False)
@@ -267,7 +269,7 @@ class XMLRPCServer(SimpleXMLRPCServer, BaseImplServer):
                 pass
 
         # Tell idle functions we're exiting
-        for function, data in self._idlefuns.items():
+        for function, data in list(self._idlefuns.items()):
             try:
                 retval = function(self, data, True)
             except:
@@ -379,7 +381,7 @@ class BitBakeXMLRPCClient(BitBakeBaseServer):
             bb.warn("Could not create socket for %s:%s (%s)" % (host, port, str(e)))
             raise e
         try:
-            self.serverImpl = XMLRPCProxyServer(host, port)
+            self.serverImpl = XMLRPCProxyServer(host, port, use_builtin_types=True)
             self.connection = BitBakeXMLRPCServerConnection(self.serverImpl, (ip, 0), self.observer_only, featureset)
             return self.connection.connect(self.token)
         except Exception as e:
diff --git a/lib/bb/siggen.py b/lib/bb/siggen.py
index e4085cf..db3daef 100644
--- a/lib/bb/siggen.py
+++ b/lib/bb/siggen.py
@@ -3,19 +3,14 @@ import logging
 import os
 import re
 import tempfile
+import pickle
 import bb.data
 from bb.checksum import FileChecksumCache
 
 logger = logging.getLogger('BitBake.SigGen')
 
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-    logger.info('Importing cPickle failed.  Falling back to a very slow implementation.')
-
 def init(d):
-    siggens = [obj for obj in globals().itervalues()
+    siggens = [obj for obj in globals().values()
                       if type(obj) is type and issubclass(obj, SignatureGenerator)]
 
     desired = d.getVar("BB_SIGNATURE_HANDLER", True) or "noop"
@@ -138,7 +133,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
                 var = lookupcache[dep]
                 if var is not None:
                     data = data + str(var)
-            self.basehash[fn + "." + task] = hashlib.md5(data).hexdigest()
+            self.basehash[fn + "." + task] = hashlib.md5(data.encode("utf-8")).hexdigest()
             taskdeps[task] = alldeps
 
         self.taskdeps[fn] = taskdeps
@@ -223,7 +218,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
             self.taints[k] = taint
             logger.warning("%s is tainted from a forced run" % k)
 
-        h = hashlib.md5(data).hexdigest()
+        h = hashlib.md5(data.encode("utf-8")).hexdigest()
         self.taskhash[k] = h
         #d.setVar("BB_TASKHASH_task-%s" % task, taskhash[task])
         return h
@@ -287,7 +282,7 @@ class SignatureGeneratorBasic(SignatureGenerator):
             with os.fdopen(fd, "wb") as stream:
                 p = pickle.dump(data, stream, -1)
                 stream.flush()
-            os.chmod(tmpfile, 0664)
+            os.chmod(tmpfile, 0o664)
             os.rename(tmpfile, sigfile)
         except (OSError, IOError) as err:
             try:
@@ -545,7 +540,7 @@ def calc_basehash(sigdata):
         if val is not None:
             basedata = basedata + str(val)
 
-    return hashlib.md5(basedata).hexdigest()
+    return hashlib.md5(basedata.encode("utf-8")).hexdigest()
 
 def calc_taskhash(sigdata):
     data = sigdata['basehash']
@@ -562,7 +557,7 @@ def calc_taskhash(sigdata):
         else:
             data = data + sigdata['taint']
 
-    return hashlib.md5(data).hexdigest()
+    return hashlib.md5(data.encode("utf-8")).hexdigest()
 
 
 def dump_sigfile(a):
diff --git a/lib/bb/taskdata.py b/lib/bb/taskdata.py
index 9ae52d7..65628c8 100644
--- a/lib/bb/taskdata.py
+++ b/lib/bb/taskdata.py
@@ -446,7 +446,7 @@ class TaskData:
             return
 
         if not item in dataCache.providers:
-            close_matches = self.get_close_matches(item, dataCache.providers.keys())
+            close_matches = self.get_close_matches(item, list(dataCache.providers.keys()))
             # Is it in RuntimeProviders ?
             all_p = bb.providers.getRuntimeProviders(dataCache, item)
             for fn in all_p:
@@ -576,7 +576,7 @@ class TaskData:
         dependees = self.get_dependees(targetid)
         for fnid in dependees:
             self.fail_fnid(fnid, missing_list)
-        for taskid in xrange(len(self.tasks_idepends)):
+        for taskid in range(len(self.tasks_idepends)):
             idepends = self.tasks_idepends[taskid]
             for (idependid, idependtask) in idepends:
                 if idependid == targetid:
@@ -602,7 +602,7 @@ class TaskData:
         dependees = self.get_rdependees(targetid)
         for fnid in dependees:
             self.fail_fnid(fnid, missing_list)
-        for taskid in xrange(len(self.tasks_irdepends)):
+        for taskid in range(len(self.tasks_irdepends)):
             irdepends = self.tasks_irdepends[taskid]
             for (idependid, idependtask) in irdepends:
                 if idependid == targetid:
@@ -658,7 +658,7 @@ class TaskData:
         logger.debug(3, ", ".join(self.run_names_index))
 
         logger.debug(3, "build_targets:")
-        for buildid in xrange(len(self.build_names_index)):
+        for buildid in range(len(self.build_names_index)):
             target = self.build_names_index[buildid]
             targets = "None"
             if buildid in self.build_targets:
@@ -666,7 +666,7 @@ class TaskData:
             logger.debug(3, " (%s)%s: %s", buildid, target, targets)
 
         logger.debug(3, "run_targets:")
-        for runid in xrange(len(self.run_names_index)):
+        for runid in range(len(self.run_names_index)):
             target = self.run_names_index[runid]
             targets = "None"
             if runid in self.run_targets:
@@ -674,7 +674,7 @@ class TaskData:
             logger.debug(3, " (%s)%s: %s", runid, target, targets)
 
         logger.debug(3, "tasks:")
-        for task in xrange(len(self.tasks_name)):
+        for task in range(len(self.tasks_name)):
             logger.debug(3, " (%s)%s - %s: %s",
                        task,
                        self.fn_index[self.tasks_fnid[task]],
diff --git a/lib/bb/tests/codeparser.py b/lib/bb/tests/codeparser.py
index 5ea9d84..14f0e25 100644
--- a/lib/bb/tests/codeparser.py
+++ b/lib/bb/tests/codeparser.py
@@ -191,8 +191,8 @@ class PythonReferenceTest(ReferenceTest):
         if hasattr(bb.utils, "_context"):
             self.context = bb.utils._context
         else:
-            import __builtin__
-            self.context = __builtin__.__dict__
+            import builtins
+            self.context = builtins.__dict__
 
     def parseExpression(self, exp):
         parsedvar = self.d.expandWithRefs(exp, None)
diff --git a/lib/bb/tests/data.py b/lib/bb/tests/data.py
index 1223230..b54eb06 100644
--- a/lib/bb/tests/data.py
+++ b/lib/bb/tests/data.py
@@ -147,14 +147,14 @@ class DataExpansions(unittest.TestCase):
         self.assertEqual(self.d.getVar("foo", False), None)
 
     def test_keys(self):
-        keys = self.d.keys()
-        self.assertEqual(keys, ['value_of_foo', 'foo', 'bar'])
+        keys = list(self.d.keys())
+        self.assertCountEqual(keys, ['value_of_foo', 'foo', 'bar'])
 
     def test_keys_deletion(self):
         newd = bb.data.createCopy(self.d)
         newd.delVar("bar")
-        keys = newd.keys()
-        self.assertEqual(keys, ['value_of_foo', 'foo'])
+        keys = list(newd.keys())
+        self.assertCountEqual(keys, ['value_of_foo', 'foo'])
 
 class TestNestedExpansions(unittest.TestCase):
     def setUp(self):
@@ -334,7 +334,7 @@ class TestOverrides(unittest.TestCase):
         self.d.setVar("TEST2_bar", "testvalue2")
         bb.data.update_data(self.d)
         self.assertEqual(self.d.getVar("TEST2", True), "testvalue2")
-        self.assertItemsEqual(self.d.keys(), ['TEST', 'TEST2', 'OVERRIDES', 'TEST2_bar'])
+        self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST2', 'OVERRIDES', 'TEST2_bar'])
 
     def test_multiple_override(self):
         self.d.setVar("TEST_bar", "testvalue2")
@@ -342,7 +342,7 @@ class TestOverrides(unittest.TestCase):
         self.d.setVar("TEST_foo", "testvalue4")
         bb.data.update_data(self.d)
         self.assertEqual(self.d.getVar("TEST", True), "testvalue3")
-        self.assertItemsEqual(self.d.keys(), ['TEST', 'TEST_foo', 'OVERRIDES', 'TEST_bar', 'TEST_local'])
+        self.assertCountEqual(list(self.d.keys()), ['TEST', 'TEST_foo', 'OVERRIDES', 'TEST_bar', 'TEST_local'])
 
     def test_multiple_combined_overrides(self):
         self.d.setVar("TEST_local_foo_bar", "testvalue3")
diff --git a/lib/bb/tests/parse.py b/lib/bb/tests/parse.py
index 6beb76a..c296db2 100644
--- a/lib/bb/tests/parse.py
+++ b/lib/bb/tests/parse.py
@@ -50,7 +50,7 @@ C = "3"
     def parsehelper(self, content, suffix = ".bb"):
 
         f = tempfile.NamedTemporaryFile(suffix = suffix)
-        f.write(content)
+        f.write(bytes(content, "utf-8"))
         f.flush()
         os.chdir(os.path.dirname(f.name))
         return f
diff --git a/lib/bb/ui/crumbs/hobwidget.py b/lib/bb/ui/crumbs/hobwidget.py
index 2b969c1..1f51a3c 100644
--- a/lib/bb/ui/crumbs/hobwidget.py
+++ b/lib/bb/ui/crumbs/hobwidget.py
@@ -612,7 +612,7 @@ class HobIconChecker(hic):
     def set_hob_icon_to_stock_icon(self, file_path, stock_id=""):
         try:
             pixbuf = gtk.gdk.pixbuf_new_from_file(file_path)
-        except Exception, e:
+        except Exception as e:
             return None
 
         if stock_id and (gtk.icon_factory_lookup_default(stock_id) == None):
diff --git a/lib/bb/ui/crumbs/progressbar.py b/lib/bb/ui/crumbs/progressbar.py
index 3e2c660..03230ae 100644
--- a/lib/bb/ui/crumbs/progressbar.py
+++ b/lib/bb/ui/crumbs/progressbar.py
@@ -44,9 +44,9 @@ class HobProgressBar (gtk.ProgressBar):
         self.set_text(text)
 
     def set_stop_title(self, text=None):
-	if not text:
-	    text = ""
-	self.set_text(text)
+        if not text:
+            text = ""
+        self.set_text(text)
 
     def reset(self):
         self.set_fraction(0)
diff --git a/lib/bb/ui/crumbs/runningbuild.py b/lib/bb/ui/crumbs/runningbuild.py
index 16a955d..9b695ac 100644
--- a/lib/bb/ui/crumbs/runningbuild.py
+++ b/lib/bb/ui/crumbs/runningbuild.py
@@ -23,14 +23,14 @@ import gtk
 import gobject
 import logging
 import time
-import urllib
-import urllib2
+import urllib.request, urllib.parse, urllib.error
+import urllib.request, urllib.error, urllib.parse
 import pango
 from bb.ui.crumbs.hobcolor import HobColors
 from bb.ui.crumbs.hobwidget import HobWarpCellRendererText, HobCellRendererPixbuf
 
 class RunningBuildModel (gtk.TreeStore):
-    (COL_LOG, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_COLOR, COL_NUM_ACTIVE) = range(7)
+    (COL_LOG, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_COLOR, COL_NUM_ACTIVE) = list(range(7))
 
     def __init__ (self):
         gtk.TreeStore.__init__ (self,
@@ -443,8 +443,8 @@ def do_pastebin(text):
     url = 'http://pastebin.com/api_public.php'
     params = {'paste_code': text, 'paste_format': 'text'}
 
-    req = urllib2.Request(url, urllib.urlencode(params))
-    response = urllib2.urlopen(req)
+    req = urllib.request.Request(url, urllib.parse.urlencode(params))
+    response = urllib.request.urlopen(req)
     paste_url = response.read()
 
     return paste_url
@@ -519,7 +519,7 @@ class RunningBuildTreeView (gtk.TreeView):
 
         # @todo Provide visual feedback to the user that it is done and that
         # it worked.
-        print paste_url
+        print(paste_url)
 
         self._add_to_clipboard(paste_url)
 
diff --git a/lib/bb/ui/goggle.py b/lib/bb/ui/goggle.py
index f4ee7b4..f5f8f16 100644
--- a/lib/bb/ui/goggle.py
+++ b/lib/bb/ui/goggle.py
@@ -18,13 +18,18 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
+from gi import pygtkcompat
+
+pygtkcompat.enable()
+pygtkcompat.enable_gtk(version='3.0')
+
 import gobject
 import gtk
-import xmlrpclib
+import xmlrpc.client
 from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild
 from bb.ui.crumbs.progress import ProgressBar
 
-import Queue
+import queue
 
 
 def event_handle_idle_func (eventHandler, build, pbar):
@@ -96,7 +101,7 @@ def main (server, eventHandler, params):
         elif ret != True:
             print("Error running command '%s': returned %s" % (cmdline, ret))
             return 1
-    except xmlrpclib.Fault as x:
+    except xmlrpcclient.Fault as x:
         print("XMLRPC Fault getting commandline:\n %s" % x)
         return 1
 
diff --git a/lib/bb/ui/knotty.py b/lib/bb/ui/knotty.py
index 08c872e..9605c8e 100644
--- a/lib/bb/ui/knotty.py
+++ b/lib/bb/ui/knotty.py
@@ -22,7 +22,7 @@ from __future__ import division
 
 import os
 import sys
-import xmlrpclib
+import xmlrpc.client as xmlrpclib
 import logging
 import progressbar
 import signal
@@ -184,8 +184,8 @@ class TerminalFilter(object):
     def clearFooter(self):
         if self.footer_present:
             lines = self.footer_present
-            sys.stdout.write(self.curses.tparm(self.cuu, lines))
-            sys.stdout.write(self.curses.tparm(self.ed))
+            sys.stdout.buffer.write(self.curses.tparm(self.cuu, lines))
+            sys.stdout.buffer.write(self.curses.tparm(self.ed))
             sys.stdout.flush()
         self.footer_present = False
 
diff --git a/lib/bb/ui/ncurses.py b/lib/bb/ui/ncurses.py
index 9589a77..d81e413 100644
--- a/lib/bb/ui/ncurses.py
+++ b/lib/bb/ui/ncurses.py
@@ -45,7 +45,7 @@
 """
 
 
-from __future__ import division
+
 import logging
 import os, sys, itertools, time, subprocess
 
@@ -55,7 +55,7 @@ except ImportError:
     sys.exit("FATAL: The ncurses ui could not load the required curses python module.")
 
 import bb
-import xmlrpclib
+import xmlrpc.client
 from bb import ui
 from bb.ui import uihelper
 
@@ -252,7 +252,7 @@ class NCursesUI:
             elif ret != True:
                 print("Couldn't get default commandlind! %s" % ret)
                 return
-        except xmlrpclib.Fault as x:
+        except xmlrpc.client.Fault as x:
             print("XMLRPC Fault getting commandline:\n %s" % x)
             return
 
@@ -331,7 +331,7 @@ class NCursesUI:
                     taw.setText(0, 0, "")
                     if activetasks:
                         taw.appendText("Active Tasks:\n")
-                        for task in activetasks.itervalues():
+                        for task in activetasks.values():
                             taw.appendText(task["title"] + '\n')
                     if failedtasks:
                         taw.appendText("Failed Tasks:\n")
diff --git a/lib/bb/ui/uievent.py b/lib/bb/ui/uievent.py
index df093c5..ca19166 100644
--- a/lib/bb/ui/uievent.py
+++ b/lib/bb/ui/uievent.py
@@ -25,7 +25,7 @@ client/server deadlocks.
 """
 
 import socket, threading, pickle, collections
-from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
+from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
 
 class BBUIEventQueue:
     def __init__(self, BBServer, clientinfo=("localhost, 0")):
@@ -137,7 +137,7 @@ class UIXMLRPCServer (SimpleXMLRPCServer):
         SimpleXMLRPCServer.__init__( self,
                                     interface,
                                     requestHandler=SimpleXMLRPCRequestHandler,
-                                    logRequests=False, allow_none=True)
+                                    logRequests=False, allow_none=True, use_builtin_types=True)
 
     def get_request(self):
         while not self.quit:
diff --git a/lib/bb/utils.py b/lib/bb/utils.py
index 588c192..138da44 100644
--- a/lib/bb/utils.py
+++ b/lib/bb/utils.py
@@ -37,7 +37,7 @@ import errno
 import signal
 import ast
 import collections
-from commands import getstatusoutput
+from subprocess import getstatusoutput
 from contextlib import contextmanager
 from ctypes import cdll
 
@@ -76,7 +76,7 @@ def explode_version(s):
             r.append((0, int(m.group(1))))
             s = m.group(2)
             continue
-        if s[0] in string.letters:
+        if s[0] in string.ascii_letters:
             m = alpha_regexp.match(s)
             r.append((1, m.group(1)))
             s = m.group(2)
@@ -588,7 +588,7 @@ def filter_environment(good_vars):
     """
 
     removed_vars = {}
-    for key in os.environ.keys():
+    for key in list(os.environ):
         if key in good_vars:
             continue
 
@@ -641,7 +641,7 @@ def empty_environment():
     """
     Remove all variables from the environment.
     """
-    for s in os.environ.keys():
+    for s in list(os.environ.keys()):
         os.unsetenv(s)
         del os.environ[s]
 
@@ -958,7 +958,7 @@ def contains(variable, checkvalues, truevalue, falsevalue, d):
     if not val:
         return falsevalue
     val = set(val.split())
-    if isinstance(checkvalues, basestring):
+    if isinstance(checkvalues, str):
         checkvalues = set(checkvalues.split())
     else:
         checkvalues = set(checkvalues)
@@ -971,7 +971,7 @@ def contains_any(variable, checkvalues, truevalue, falsevalue, d):
     if not val:
         return falsevalue
     val = set(val.split())
-    if isinstance(checkvalues, basestring):
+    if isinstance(checkvalues, str):
         checkvalues = set(checkvalues.split())
     else:
         checkvalues = set(checkvalues)
@@ -1040,7 +1040,7 @@ def exec_flat_python_func(func, *args, **kwargs):
         aidx += 1
     # Handle keyword arguments
     context.update(kwargs)
-    funcargs.extend(['%s=%s' % (arg, arg) for arg in kwargs.iterkeys()])
+    funcargs.extend(['%s=%s' % (arg, arg) for arg in kwargs.keys()])
     code = 'retval = %s(%s)' % (func, ', '.join(funcargs))
     comp = bb.utils.better_compile(code, '<string>', '<string>')
     bb.utils.better_exec(comp, context, code, '<string>')
@@ -1127,7 +1127,7 @@ def edit_metadata(meta_lines, variables, varfunc, match_overrides=False):
             else:
                 varset_new = varset_start
 
-            if isinstance(indent, (int, long)):
+            if isinstance(indent, int):
                 if indent == -1:
                     indentspc = ' ' * (len(varset_new) + 2)
                 else:
@@ -1195,7 +1195,7 @@ def edit_metadata(meta_lines, variables, varfunc, match_overrides=False):
                 in_var = None
         else:
             skip = False
-            for (varname, var_re) in var_res.iteritems():
+            for (varname, var_re) in var_res.items():
                 res = var_re.match(line)
                 if res:
                     isfunc = varname.endswith('()')
@@ -1373,7 +1373,7 @@ def get_file_layer(filename, d):
         # Use longest path so we handle nested layers
         matchlen = 0
         match = None
-        for collection, regex in collection_res.iteritems():
+        for collection, regex in collection_res.items():
             if len(regex) > matchlen and re.match(regex, path):
                 matchlen = len(regex)
                 match = collection
diff --git a/lib/bblayers/action.py b/lib/bblayers/action.py
index 5b95e2e..d4c1792 100644
--- a/lib/bblayers/action.py
+++ b/lib/bblayers/action.py
@@ -117,7 +117,7 @@ build results (as the layer priority order has effectively changed).
         applied_appends = []
         for layer in layers:
             overlayed = []
-            for f in self.tinfoil.cooker.collection.overlayed.iterkeys():
+            for f in self.tinfoil.cooker.collection.overlayed.keys():
                 for of in self.tinfoil.cooker.collection.overlayed[f]:
                     if of.startswith(layer):
                         overlayed.append(of)
diff --git a/lib/bblayers/common.py b/lib/bblayers/common.py
index 360b9d7..b10fb4c 100644
--- a/lib/bblayers/common.py
+++ b/lib/bblayers/common.py
@@ -14,7 +14,7 @@ class LayerPlugin():
         self.tinfoil = tinfoil
         self.bblayers = (self.tinfoil.config_data.getVar('BBLAYERS', True) or "").split()
         layerconfs = self.tinfoil.config_data.varhistory.get_variable_items_files('BBFILE_COLLECTIONS', self.tinfoil.config_data)
-        self.bbfile_collections = {layer: os.path.dirname(os.path.dirname(path)) for layer, path in layerconfs.iteritems()}
+        self.bbfile_collections = {layer: os.path.dirname(os.path.dirname(path)) for layer, path in layerconfs.items()}
 
     @staticmethod
     def add_command(subparsers, cmdname, function, parserecipes=True, *args, **kwargs):
diff --git a/lib/bblayers/layerindex.py b/lib/bblayers/layerindex.py
index 3c39d8a..10ad718 100644
--- a/lib/bblayers/layerindex.py
+++ b/lib/bblayers/layerindex.py
@@ -1,10 +1,10 @@
 import argparse
-import httplib
+import http.client
 import json
 import logging
 import os
 import subprocess
-import urlparse
+import urllib.parse
 
 from bblayers.action import ActionPlugin
 
@@ -24,12 +24,12 @@ class LayerIndexPlugin(ActionPlugin):
     def get_json_data(self, apiurl):
         proxy_settings = os.environ.get("http_proxy", None)
         conn = None
-        _parsedurl = urlparse.urlparse(apiurl)
+        _parsedurl = urllib.parse.urlparse(apiurl)
         path = _parsedurl.path
         query = _parsedurl.query
 
         def parse_url(url):
-            parsedurl = urlparse.urlparse(url)
+            parsedurl = urllib.parse.urlparse(url)
             if parsedurl.netloc[0] == '[':
                 host, port = parsedurl.netloc[1:].split(']', 1)
                 if ':' in port:
@@ -46,11 +46,11 @@ class LayerIndexPlugin(ActionPlugin):
 
         if proxy_settings is None:
             host, port = parse_url(apiurl)
-            conn = httplib.HTTPConnection(host, port)
+            conn = http.client.HTTPConnection(host, port)
             conn.request("GET", path + "?" + query)
         else:
             host, port = parse_url(proxy_settings)
-            conn = httplib.HTTPConnection(host, port)
+            conn = http.client.HTTPConnection(host, port)
             conn.request("GET", apiurl)
 
         r = conn.getresponse()
diff --git a/lib/bblayers/query.py b/lib/bblayers/query.py
index b5b98f7..b8c817b 100644
--- a/lib/bblayers/query.py
+++ b/lib/bblayers/query.py
@@ -128,7 +128,7 @@ skipped recipes will also be listed, with a " (skipped)" suffix.
         # Ensure we list skipped recipes
         # We are largely guessing about PN, PV and the preferred version here,
         # but we have no choice since skipped recipes are not fully parsed
-        skiplist = self.tinfoil.cooker.skiplist.keys()
+        skiplist = list(self.tinfoil.cooker.skiplist.keys())
         skiplist.sort( key=lambda fileitem: self.tinfoil.cooker.collection.calc_bbfile_priority(fileitem) )
         skiplist.reverse()
         for fn in skiplist:
@@ -275,7 +275,7 @@ Lists recipes with the bbappends that apply to them as subitems.
 
     def show_appends_for_skipped(self):
         filenames = [os.path.basename(f)
-                    for f in self.tinfoil.cooker.skiplist.iterkeys()]
+                    for f in self.tinfoil.cooker.skiplist.keys()]
         return self.show_appends_output(filenames, None, " (skipped)")
 
     def show_appends_output(self, filenames, best_filename, name_suffix = ''):
diff --git a/lib/codegen.py b/lib/codegen.py
index be772d5..62a6748 100644
--- a/lib/codegen.py
+++ b/lib/codegen.py
@@ -214,11 +214,11 @@ class SourceGenerator(NodeVisitor):
                 paren_or_comma()
                 self.write(keyword.arg + '=')
                 self.visit(keyword.value)
-            if node.starargs is not None:
+            if hasattr(node, 'starargs') and node.starargs is not None:
                 paren_or_comma()
                 self.write('*')
                 self.visit(node.starargs)
-            if node.kwargs is not None:
+            if hasattr(node, 'kwargs') and node.kwargs is not None:
                 paren_or_comma()
                 self.write('**')
                 self.visit(node.kwargs)
@@ -379,11 +379,11 @@ class SourceGenerator(NodeVisitor):
             write_comma()
             self.write(keyword.arg + '=')
             self.visit(keyword.value)
-        if node.starargs is not None:
+        if hasattr(node, 'starargs') and node.starargs is not None:
             write_comma()
             self.write('*')
             self.visit(node.starargs)
-        if node.kwargs is not None:
+        if hasattr(node, 'kwargs') and node.kwargs is not None:
             write_comma()
             self.write('**')
             self.visit(node.kwargs)
diff --git a/lib/ply/yacc.py b/lib/ply/yacc.py
index 6168fd9..d50886e 100644
--- a/lib/ply/yacc.py
+++ b/lib/ply/yacc.py
@@ -195,6 +195,8 @@ class YaccProduction:
         self.lexer = None
         self.parser= None
     def __getitem__(self,n):
+        if isinstance(n,slice):
+            return [self[i] for i in range(*(n.indices(len(self.slice))))]
         if n >= 0: return self.slice[n].value
         else: return self.stack[n].value
 
diff --git a/lib/prserv/db.py b/lib/prserv/db.py
index 2a86184..495d09f 100644
--- a/lib/prserv/db.py
+++ b/lib/prserv/db.py
@@ -260,7 +260,7 @@ class PRData(object):
         self.connection.close()
 
     def __getitem__(self,tblname):
-        if not isinstance(tblname, basestring):
+        if not isinstance(tblname, str):
             raise TypeError("tblname argument must be a string, not '%s'" %
                             type(tblname))
         if tblname in self._tables:
diff --git a/lib/prserv/serv.py b/lib/prserv/serv.py
index 8cec9f8..cafcc82 100644
--- a/lib/prserv/serv.py
+++ b/lib/prserv/serv.py
@@ -1,10 +1,10 @@
 import os,sys,logging
 import signal, time
-from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
+from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
 import threading
-import Queue
+import queue
 import socket
-import StringIO
+import io
 
 try:
     import sqlite3
@@ -64,7 +64,7 @@ class PRServer(SimpleXMLRPCServer):
         self.register_function(self.importone, "importone")
         self.register_introspection_functions()
 
-        self.requestqueue = Queue.Queue()
+        self.requestqueue = queue.Queue()
         self.handlerthread = threading.Thread(target = self.process_request_thread)
         self.handlerthread.daemon = False
 
@@ -83,7 +83,7 @@ class PRServer(SimpleXMLRPCServer):
         while not self.quit:
             try:
                 (request, client_address) = self.requestqueue.get(True, 30)
-            except Queue.Empty:
+            except queue.Empty:
                 self.table.sync_if_dirty()
                 continue
             try:
@@ -126,7 +126,7 @@ class PRServer(SimpleXMLRPCServer):
         Returns None if the database engine does not support dumping to
         script or if some other error is encountered in processing.
         """
-        buff = StringIO.StringIO()
+        buff = io.StringIO()
         try:
             self.table.sync()
             self.table.dump_db(buff)
@@ -420,7 +420,7 @@ class PRServiceConfigError(Exception):
 def auto_start(d):
     global singleton
 
-    host_params = filter(None, (d.getVar('PRSERV_HOST', True) or '').split(':'))
+    host_params = list(filter(None, (d.getVar('PRSERV_HOST', True) or '').split(':')))
     if not host_params:
         return None
 
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 05/28] data_smart: Simplify ExpansionError exception
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (2 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 04/28] bitbake: Convert to python 3 megacommit This needs breaking up into smaller changes Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 06/28] lib/bb: Set required python 3 version to 3.4.0 Richard Purdie
                   ` (22 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

This seemingly convoluted syntax doesn't work in python3. Instead
use the chained exception handling syntax which appears to make more
sense here.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/bb/data_smart.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/lib/bb/data_smart.py b/lib/bb/data_smart.py
index 2a61386..25c412c 100644
--- a/lib/bb/data_smart.py
+++ b/lib/bb/data_smart.py
@@ -397,8 +397,7 @@ class DataSmart(MutableMapping):
             except bb.parse.SkipRecipe:
                 raise
             except Exception as exc:
-                exc_class, exc, tb = sys.exc_info()
-                raise ExpansionError, ExpansionError(varname, s, exc), tb
+                raise ExpansionError(varname, s, exc) from exc
 
         varparse.value = s
 
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 06/28] lib/bb: Set required python 3 version to 3.4.0
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (3 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 05/28] data_smart: Simplify ExpansionError exception Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 07/28] image-writer/goggle: Disable pygtkcompat problems Richard Purdie
                   ` (21 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Jeremy Puhlman <jpuhlman@mvista.com>

get_context was added to mutliprocessing as part of 3.4.0

Signed-off-by: Jeremy Puhlman <jpuhlman@mvista.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/bb/__init__.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/bb/__init__.py b/lib/bb/__init__.py
index 6b85984..a98ebd1 100644
--- a/lib/bb/__init__.py
+++ b/lib/bb/__init__.py
@@ -24,8 +24,8 @@
 __version__ = "1.31.0"
 
 import sys
-if sys.version_info < (2, 7, 3):
-    raise RuntimeError("Sorry, python 2.7.3 or later is required for this version of bitbake")
+if sys.version_info < (3, 4, 0):
+    raise RuntimeError("Sorry, python 3.4.0 or later is required for this version of bitbake")
 
 
 class BBHandledException(Exception):
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 07/28] image-writer/goggle: Disable pygtkcompat problems
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (4 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 06/28] lib/bb: Set required python 3 version to 3.4.0 Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 08/28] google/image-writer: Drop since bitrotting and no longer used Richard Purdie
                   ` (20 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

Disable the problematic gtk usage for use with pygtkcompat. The following
commit removes these tools/UIs entirely but we may as well leave this
piece in the history in case anyone does want a starting point for reusing
them.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 bin/image-writer                     |  3 ++-
 lib/bb/ui/crumbs/hig/crumbsdialog.py |  2 +-
 lib/bb/ui/crumbs/hobwidget.py        |  8 ++++----
 lib/bb/ui/crumbs/progressbar.py      | 15 ++++++++-------
 4 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/bin/image-writer b/bin/image-writer
index 7ab1d8c..40e284d 100755
--- a/bin/image-writer
+++ b/bin/image-writer
@@ -67,7 +67,8 @@ class DeployWindow(gtk.Window):
 
         self.deploy_dialog = DeployImageDialog(Title, image_path, self,
                                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT
-                                        | gtk.DIALOG_NO_SEPARATOR, None, standalone=True)
+                                        , None, standalone=True)
+#FIXME | gtk.DIALOG_NO_SEPARATOR
         close_button = self.deploy_dialog.add_button("Close", gtk.RESPONSE_NO)
         HobAltButton.style_button(close_button)
         close_button.connect('clicked', gtk.main_quit)
diff --git a/lib/bb/ui/crumbs/hig/crumbsdialog.py b/lib/bb/ui/crumbs/hig/crumbsdialog.py
index c679f9a..73b9234 100644
--- a/lib/bb/ui/crumbs/hig/crumbsdialog.py
+++ b/lib/bb/ui/crumbs/hig/crumbsdialog.py
@@ -36,7 +36,7 @@ class CrumbsDialog(gtk.Dialog):
     def __init__(self, title="", parent=None, flags=0, buttons=None):
         super(CrumbsDialog, self).__init__(title, parent, flags, buttons)
 
-        self.set_property("has-separator", False) # note: deprecated in 2.22
+        #FIXME self.set_property("has-separator", False) # note: deprecated in 2.22
 
         self.set_border_width(6)
         self.vbox.set_property("spacing", 12)
diff --git a/lib/bb/ui/crumbs/hobwidget.py b/lib/bb/ui/crumbs/hobwidget.py
index 1f51a3c..3f6138b 100644
--- a/lib/bb/ui/crumbs/hobwidget.py
+++ b/lib/bb/ui/crumbs/hobwidget.py
@@ -273,13 +273,13 @@ class BaseHobButton(gtk.Button):
         style = button.get_style()
         style = gtk.rc_get_style_by_paths(gtk.settings_get_default(), 'gtk-button', 'gtk-button', gobject.TYPE_NONE)
 
-        button.set_flags(gtk.CAN_DEFAULT)
+        #FIXME button.set_flags(gtk.CAN_DEFAULT)
         button.grab_default()
 
 #        label = "<span size='x-large'><b>%s</b></span>" % gobject.markup_escape_text(button.get_label())
         label = button.get_label()
         button.set_label(label)
-        button.child.set_use_markup(True)
+        #FIXME button.child.set_use_markup(True)
 
 class HobButton(BaseHobButton):
     """
@@ -399,8 +399,8 @@ class HobInfoButton(gtk.EventBox):
         self.dialog = PropertyDialog(title = '',
                     parent = self.my_parent,
                     information = self.tip_markup,
-                    flags = gtk.DIALOG_DESTROY_WITH_PARENT
-                        | gtk.DIALOG_NO_SEPARATOR)
+                    flags = gtk.DIALOG_DESTROY_WITH_PARENT)
+                        #FMXIE | gtk.DIALOG_NO_SEPARATOR
 
         button = self.dialog.add_button("Close", gtk.RESPONSE_CANCEL)
         HobAltButton.style_button(button)
diff --git a/lib/bb/ui/crumbs/progressbar.py b/lib/bb/ui/crumbs/progressbar.py
index 03230ae..ba3c7a0 100644
--- a/lib/bb/ui/crumbs/progressbar.py
+++ b/lib/bb/ui/crumbs/progressbar.py
@@ -28,13 +28,14 @@ class HobProgressBar (gtk.ProgressBar):
 
     def set_rcstyle(self, status):
         rcstyle = gtk.RcStyle()
-        rcstyle.fg[2] = gtk.gdk.Color(HobColors.BLACK)
-        if status == "stop":
-            rcstyle.bg[3] = gtk.gdk.Color(HobColors.WARNING)
-        elif status == "fail":
-            rcstyle.bg[3] = gtk.gdk.Color(HobColors.ERROR)
-        else:
-            rcstyle.bg[3] = gtk.gdk.Color(HobColors.RUNNING)
+#FIXME
+#        rcstyle.fg[2] = gtk.gdk.Color(HobColors.BLACK)
+#        if status == "stop":
+#            rcstyle.bg[3] = gtk.gdk.Color(HobColors.WARNING)
+#        elif status == "fail":
+#            rcstyle.bg[3] = gtk.gdk.Color(HobColors.ERROR)
+#        else:
+#            rcstyle.bg[3] = gtk.gdk.Color(HobColors.RUNNING)
         self.modify_style(rcstyle)
 
     def set_title(self, text=None):
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 08/28] google/image-writer: Drop since bitrotting and no longer used
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (5 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 07/28] image-writer/goggle: Disable pygtkcompat problems Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 09/28] data/event/siggen: Fix various exceptions [FIXME] Richard Purdie
                   ` (19 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

The upgrade to python3 is the final nail in the coffin for image-writer
and the goggle UI. Neither seem used or recieve patches and are based
on old versions of GTK+ so drop them, and the remaining crumbs support
pieces.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 bin/image-writer                             | 127 ----
 lib/bb/ui/crumbs/__init__.py                 |  17 -
 lib/bb/ui/crumbs/hig/__init__.py             |   0
 lib/bb/ui/crumbs/hig/crumbsdialog.py         |  44 --
 lib/bb/ui/crumbs/hig/crumbsmessagedialog.py  |  70 ---
 lib/bb/ui/crumbs/hig/deployimagedialog.py    | 219 -------
 lib/bb/ui/crumbs/hig/imageselectiondialog.py | 172 -----
 lib/bb/ui/crumbs/hig/layerselectiondialog.py | 298 ---------
 lib/bb/ui/crumbs/hig/propertydialog.py       | 437 -------------
 lib/bb/ui/crumbs/hig/settingsuihelper.py     | 122 ----
 lib/bb/ui/crumbs/hobcolor.py                 |  38 --
 lib/bb/ui/crumbs/hobwidget.py                | 904 ---------------------------
 lib/bb/ui/crumbs/persistenttooltip.py        | 186 ------
 lib/bb/ui/crumbs/progress.py                 |  23 -
 lib/bb/ui/crumbs/progressbar.py              |  60 --
 lib/bb/ui/crumbs/puccho.glade                | 606 ------------------
 lib/bb/ui/crumbs/runningbuild.py             | 551 ----------------
 lib/bb/ui/crumbs/utils.py                    |  34 -
 lib/bb/ui/goggle.py                          | 126 ----
 19 files changed, 4034 deletions(-)
 delete mode 100755 bin/image-writer
 delete mode 100644 lib/bb/ui/crumbs/__init__.py
 delete mode 100644 lib/bb/ui/crumbs/hig/__init__.py
 delete mode 100644 lib/bb/ui/crumbs/hig/crumbsdialog.py
 delete mode 100644 lib/bb/ui/crumbs/hig/crumbsmessagedialog.py
 delete mode 100644 lib/bb/ui/crumbs/hig/deployimagedialog.py
 delete mode 100644 lib/bb/ui/crumbs/hig/imageselectiondialog.py
 delete mode 100644 lib/bb/ui/crumbs/hig/layerselectiondialog.py
 delete mode 100644 lib/bb/ui/crumbs/hig/propertydialog.py
 delete mode 100644 lib/bb/ui/crumbs/hig/settingsuihelper.py
 delete mode 100644 lib/bb/ui/crumbs/hobcolor.py
 delete mode 100644 lib/bb/ui/crumbs/hobwidget.py
 delete mode 100644 lib/bb/ui/crumbs/persistenttooltip.py
 delete mode 100644 lib/bb/ui/crumbs/progress.py
 delete mode 100644 lib/bb/ui/crumbs/progressbar.py
 delete mode 100644 lib/bb/ui/crumbs/puccho.glade
 delete mode 100644 lib/bb/ui/crumbs/runningbuild.py
 delete mode 100644 lib/bb/ui/crumbs/utils.py
 delete mode 100644 lib/bb/ui/goggle.py

diff --git a/bin/image-writer b/bin/image-writer
deleted file mode 100755
index 40e284d..0000000
--- a/bin/image-writer
+++ /dev/null
@@ -1,127 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (c) 2012 Wind River Systems, Inc.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-import os
-import sys
-sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname( \
-                                    os.path.abspath(__file__))), 'lib'))
-try:
-    import bb
-except RuntimeError as exc:
-    sys.exit(str(exc))
-
-from gi import pygtkcompat
-
-pygtkcompat.enable()
-pygtkcompat.enable_gtk(version='3.0')
-
-import gtk
-import optparse
-
-from bb.ui.crumbs.hobwidget import HobAltButton, HobButton
-from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
-from bb.ui.crumbs.hig.deployimagedialog import DeployImageDialog
-from bb.ui.crumbs.hig.imageselectiondialog import ImageSelectionDialog
-
-# I put all the fs bitbake supported here. Need more test.
-DEPLOYABLE_IMAGE_TYPES = ["jffs2", "cramfs", "ext2", "ext3", "ext4", "btrfs", "squashfs", "ubi", "vmdk"]
-Title = "USB Image Writer"
-
-class DeployWindow(gtk.Window):
-    def __init__(self, image_path=''):
-        super(DeployWindow, self).__init__()
-
-        if len(image_path) > 0:
-            valid = True
-            if not os.path.exists(image_path):
-                valid = False
-                lbl = "<b>Invalid image file path: %s.</b>\nPress <b>Select Image</b> to select an image." % image_path
-            else:
-                image_path = os.path.abspath(image_path)
-                extend_name = os.path.splitext(image_path)[1][1:]
-                if extend_name not in DEPLOYABLE_IMAGE_TYPES:
-                    valid = False
-                    lbl = "<b>Undeployable imge type: %s</b>\nPress <b>Select Image</b> to select an image." % extend_name
-
-            if not valid:
-                image_path = ''
-                crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
-                button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
-                HobButton.style_button(button)
-                crumbs_dialog.run()
-                crumbs_dialog.destroy()
-
-        self.deploy_dialog = DeployImageDialog(Title, image_path, self,
-                                        gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT
-                                        , None, standalone=True)
-#FIXME | gtk.DIALOG_NO_SEPARATOR
-        close_button = self.deploy_dialog.add_button("Close", gtk.RESPONSE_NO)
-        HobAltButton.style_button(close_button)
-        close_button.connect('clicked', gtk.main_quit)
-
-        write_button = self.deploy_dialog.add_button("Write USB image", gtk.RESPONSE_YES)
-        HobAltButton.style_button(write_button)
-
-        self.deploy_dialog.connect('select_image_clicked', self.select_image_clicked_cb)
-        self.deploy_dialog.connect('destroy', gtk.main_quit)
-        response = self.deploy_dialog.show()
-
-    def select_image_clicked_cb(self, dialog):
-        cwd = os.getcwd()
-        dialog = ImageSelectionDialog(cwd, DEPLOYABLE_IMAGE_TYPES, Title, self, gtk.FILE_CHOOSER_ACTION_SAVE )
-        button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
-        HobAltButton.style_button(button)
-        button = dialog.add_button("Open", gtk.RESPONSE_YES)
-        HobAltButton.style_button(button)
-        response = dialog.run()
-
-        if response == gtk.RESPONSE_YES:
-            if not dialog.image_names:
-                lbl = "<b>No selections made</b>\nClicked the radio button to select a image."
-                crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.STOCK_DIALOG_INFO)
-                button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
-                HobButton.style_button(button)
-                crumbs_dialog.run()
-                crumbs_dialog.destroy()
-                dialog.destroy()
-                return
-
-            # get the full path of image
-            image_path = os.path.join(dialog.image_folder, dialog.image_names[0])
-            self.deploy_dialog.set_image_text_buffer(image_path)
-            self.deploy_dialog.set_image_path(image_path)
-
-        dialog.destroy()
-
-def main():
-    parser = optparse.OptionParser(
-                usage = """%prog [-h] [image_file]
-
-%prog writes bootable images to USB devices. You can
-provide the image file on the command line or select it using the GUI.""")
-
-    options, args = parser.parse_args(sys.argv)
-    image_file = args[1] if len(args) > 1 else ''
-    dw = DeployWindow(image_file)
-
-if __name__ == '__main__':
-    try:
-        main()
-        gtk.main()
-    except Exception:
-        import traceback
-        traceback.print_exc()
diff --git a/lib/bb/ui/crumbs/__init__.py b/lib/bb/ui/crumbs/__init__.py
deleted file mode 100644
index b7cbe1a..0000000
--- a/lib/bb/ui/crumbs/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# Gtk+ UI pieces for BitBake
-#
-# Copyright (C) 2006-2007 Richard Purdie
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
diff --git a/lib/bb/ui/crumbs/hig/__init__.py b/lib/bb/ui/crumbs/hig/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/lib/bb/ui/crumbs/hig/crumbsdialog.py b/lib/bb/ui/crumbs/hig/crumbsdialog.py
deleted file mode 100644
index 73b9234..0000000
--- a/lib/bb/ui/crumbs/hig/crumbsdialog.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2011-2012   Intel Corporation
-#
-# Authored by Joshua Lock <josh@linux.intel.com>
-# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
-# Authored by Shane Wang <shane.wang@intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-import gtk
-
-"""
-The following are convenience classes for implementing GNOME HIG compliant
-BitBake GUI's
-In summary: spacing = 12px, border-width = 6px
-"""
-
-class CrumbsDialog(gtk.Dialog):
-    """
-    A GNOME HIG compliant dialog widget.
-    Add buttons with gtk.Dialog.add_button or gtk.Dialog.add_buttons
-    """
-    def __init__(self, title="", parent=None, flags=0, buttons=None):
-        super(CrumbsDialog, self).__init__(title, parent, flags, buttons)
-
-        #FIXME self.set_property("has-separator", False) # note: deprecated in 2.22
-
-        self.set_border_width(6)
-        self.vbox.set_property("spacing", 12)
-        self.action_area.set_property("spacing", 12)
-        self.action_area.set_property("border-width", 6)
diff --git a/lib/bb/ui/crumbs/hig/crumbsmessagedialog.py b/lib/bb/ui/crumbs/hig/crumbsmessagedialog.py
deleted file mode 100644
index 3b998e4..0000000
--- a/lib/bb/ui/crumbs/hig/crumbsmessagedialog.py
+++ /dev/null
@@ -1,70 +0,0 @@
-#
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2011-2012   Intel Corporation
-#
-# Authored by Joshua Lock <josh@linux.intel.com>
-# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
-# Authored by Shane Wang <shane.wang@intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-import glib
-import gtk
-from bb.ui.crumbs.hobwidget import HobIconChecker
-from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
-
-"""
-The following are convenience classes for implementing GNOME HIG compliant
-BitBake GUI's
-In summary: spacing = 12px, border-width = 6px
-"""
-
-class CrumbsMessageDialog(gtk.MessageDialog):
-    """
-    A GNOME HIG compliant dialog widget.
-    Add buttons with gtk.Dialog.add_button or gtk.Dialog.add_buttons
-    """
-    def __init__(self, parent = None, label="", dialog_type = gtk.MESSAGE_QUESTION, msg=""):
-        super(CrumbsMessageDialog, self).__init__(None,
-                                                  gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
-                                                  dialog_type,
-                                                  gtk.BUTTONS_NONE,
-                                                  None)
-
-        self.set_skip_taskbar_hint(False)
-
-        self.set_markup(label)
-
-        if 0 <= len(msg) < 300:
-            self.format_secondary_markup(msg)
-        else:
-            vbox = self.get_message_area()
-            vbox.set_border_width(1)
-            vbox.set_property("spacing", 12)
-            self.textWindow = gtk.ScrolledWindow()
-            self.textWindow.set_shadow_type(gtk.SHADOW_IN)
-            self.textWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-            self.msgView = gtk.TextView()
-            self.msgView.set_editable(False)
-            self.msgView.set_wrap_mode(gtk.WRAP_WORD)
-            self.msgView.set_cursor_visible(False)
-            self.msgView.set_size_request(300, 300)
-            self.buf = gtk.TextBuffer()
-            self.buf.set_text(msg)
-            self.msgView.set_buffer(self.buf)
-            self.textWindow.add(self.msgView)
-            self.msgView.show()
-            vbox.add(self.textWindow)
-            self.textWindow.show()
diff --git a/lib/bb/ui/crumbs/hig/deployimagedialog.py b/lib/bb/ui/crumbs/hig/deployimagedialog.py
deleted file mode 100644
index a13fff9..0000000
--- a/lib/bb/ui/crumbs/hig/deployimagedialog.py
+++ /dev/null
@@ -1,219 +0,0 @@
-#
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2011-2012   Intel Corporation
-#
-# Authored by Joshua Lock <josh@linux.intel.com>
-# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
-# Authored by Shane Wang <shane.wang@intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-import glob
-import gtk
-import gobject
-import os
-import re
-import shlex
-import subprocess
-import tempfile
-from bb.ui.crumbs.hobwidget import hic, HobButton
-from bb.ui.crumbs.progressbar import HobProgressBar
-import bb.ui.crumbs.utils
-import bb.process
-from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
-from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
-
-"""
-The following are convenience classes for implementing GNOME HIG compliant
-BitBake GUI's
-In summary: spacing = 12px, border-width = 6px
-"""
-
-class DeployImageDialog (CrumbsDialog):
-
-    __dummy_usb__ = "--select a usb drive--"
-
-    def __init__(self, title, image_path, parent, flags, buttons=None, standalone=False):
-        super(DeployImageDialog, self).__init__(title, parent, flags, buttons)
-
-        self.image_path = image_path
-        self.standalone = standalone
-
-        self.create_visual_elements()
-        self.connect("response", self.response_cb)
-
-    def create_visual_elements(self):
-        self.set_size_request(600, 400)
-        label = gtk.Label()
-        label.set_alignment(0.0, 0.5)
-        markup = "<span font_desc='12'>The image to be written into usb drive:</span>"
-        label.set_markup(markup)
-        self.vbox.pack_start(label, expand=False, fill=False, padding=2)
-
-        table = gtk.Table(2, 10, False)
-        table.set_col_spacings(5)
-        table.set_row_spacings(5)
-        self.vbox.pack_start(table, expand=True, fill=True)
-
-        scroll = gtk.ScrolledWindow()
-        scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
-        scroll.set_shadow_type(gtk.SHADOW_IN)
-        tv = gtk.TextView()
-        tv.set_editable(False)
-        tv.set_wrap_mode(gtk.WRAP_WORD)
-        tv.set_cursor_visible(False)
-        self.buf = gtk.TextBuffer()
-        self.buf.set_text(self.image_path)
-        tv.set_buffer(self.buf)
-        scroll.add(tv)
-        table.attach(scroll, 0, 10, 0, 1)
-
-        # There are 2 ways to use DeployImageDialog
-        # One way is that called by HOB when the 'Deploy Image' button is clicked
-        # The other way is that called by a standalone script.
-        # Following block of codes handles the latter way. It adds a 'Select Image' button and
-        # emit a signal when the button is clicked.
-        if self.standalone:
-                gobject.signal_new("select_image_clicked", self, gobject.SIGNAL_RUN_FIRST,
-                                   gobject.TYPE_NONE, ())
-                icon = gtk.Image()
-                pix_buffer = gtk.gdk.pixbuf_new_from_file(hic.ICON_IMAGES_DISPLAY_FILE)
-                icon.set_from_pixbuf(pix_buffer)
-                button = gtk.Button("Select Image")
-                button.set_image(icon)
-                #button.set_size_request(140, 50)
-                table.attach(button, 9, 10, 1, 2, gtk.FILL, 0, 0, 0)
-                button.connect("clicked", self.select_image_button_clicked_cb)
-
-        separator = gtk.HSeparator()
-        self.vbox.pack_start(separator, expand=False, fill=False, padding=10)
-
-        self.usb_desc = gtk.Label()
-        self.usb_desc.set_alignment(0.0, 0.5)
-        markup = "<span font_desc='12'>You haven't chosen any USB drive.</span>"
-        self.usb_desc.set_markup(markup)
-
-        self.usb_combo = gtk.combo_box_new_text()
-        self.usb_combo.connect("changed", self.usb_combo_changed_cb)
-        model = self.usb_combo.get_model()
-        model.clear()
-        self.usb_combo.append_text(self.__dummy_usb__)
-        for usb in self.find_all_usb_devices():
-            self.usb_combo.append_text("/dev/" + usb)
-        self.usb_combo.set_active(0)
-        self.vbox.pack_start(self.usb_combo, expand=False, fill=False)
-        self.vbox.pack_start(self.usb_desc, expand=False, fill=False, padding=2)
-
-        self.progress_bar = HobProgressBar()
-        self.vbox.pack_start(self.progress_bar, expand=False, fill=False)
-        separator = gtk.HSeparator()
-        self.vbox.pack_start(separator, expand=False, fill=True, padding=10)
-
-        self.vbox.show_all()
-        self.progress_bar.hide()
-
-    def set_image_text_buffer(self, image_path):
-        self.buf.set_text(image_path)
-
-    def set_image_path(self, image_path):
-        self.image_path = image_path
-
-    def popen_read(self, cmd):
-        tmpout, errors = bb.process.run("%s" % cmd)
-        return tmpout.strip()
-
-    def find_all_usb_devices(self):
-        usb_devs = [ os.readlink(u)
-            for u in glob.glob('/dev/disk/by-id/usb*')
-            if not re.search(r'part\d+', u) ]
-        return [ '%s' % u[u.rfind('/')+1:] for u in usb_devs ]
-
-    def get_usb_info(self, dev):
-        return "%s %s" % \
-            (self.popen_read('cat /sys/class/block/%s/device/vendor' % dev),
-            self.popen_read('cat /sys/class/block/%s/device/model' % dev))
-
-    def select_image_button_clicked_cb(self, button):
-            self.emit('select_image_clicked')
-
-    def usb_combo_changed_cb(self, usb_combo):
-        combo_item = self.usb_combo.get_active_text()
-        if not combo_item or combo_item == self.__dummy_usb__:
-            markup = "<span font_desc='12'>You haven't chosen any USB drive.</span>"
-            self.usb_desc.set_markup(markup)
-        else:
-            markup = "<span font_desc='12'>" + self.get_usb_info(combo_item.lstrip("/dev/")) + "</span>"
-            self.usb_desc.set_markup(markup)
-
-    def response_cb(self, dialog, response_id):
-        if response_id == gtk.RESPONSE_YES:
-            lbl = ''
-            msg = ''
-            combo_item = self.usb_combo.get_active_text()
-            if combo_item and combo_item != self.__dummy_usb__ and self.image_path:
-                cmdline = bb.ui.crumbs.utils.which_terminal()
-                if cmdline:
-                    tmpfile = tempfile.NamedTemporaryFile()
-                    cmdline += "\"sudo dd if=" + self.image_path + \
-                                " of=" + combo_item + " && sync; echo $? > " + tmpfile.name + "\""
-                    subprocess.call(shlex.split(cmdline))
-
-                    if int(tmpfile.readline().strip()) == 0:
-                        lbl = "<b>Deploy image successfully.</b>"
-                    else:
-                        lbl = "<b>Failed to deploy image.</b>"
-                        msg = "Please check image <b>%s</b> exists and USB device <b>%s</b> is writable." % (self.image_path, combo_item)
-                    tmpfile.close()
-            else:
-                if not self.image_path:
-                    lbl = "<b>No selection made.</b>"
-                    msg = "You have not selected an image to deploy."
-                else:
-                    lbl = "<b>No selection made.</b>"
-                    msg = "You have not selected a USB device."
-            if len(lbl):
-                crumbs_dialog = CrumbsMessageDialog(self, lbl, gtk.MESSAGE_INFO, msg)
-                button = crumbs_dialog.add_button("Close", gtk.RESPONSE_OK)
-                HobButton.style_button(button)
-                crumbs_dialog.run()
-                crumbs_dialog.destroy()
-
-    def update_progress_bar(self, title, fraction, status=None):
-        self.progress_bar.update(fraction)
-        self.progress_bar.set_title(title)
-        self.progress_bar.set_rcstyle(status)
-
-    def write_file(self, ifile, ofile):
-        self.progress_bar.reset()
-        self.progress_bar.show()
-
-        f_from = os.open(ifile, os.O_RDONLY)
-        f_to = os.open(ofile, os.O_WRONLY)
-
-        total_size = os.stat(ifile).st_size
-        written_size = 0
-
-        while True:
-            buf = os.read(f_from, 1024*1024)
-            if not buf:
-                break
-            os.write(f_to, buf)
-            written_size += 1024*1024
-            self.update_progress_bar("Writing to usb:", written_size * 1.0/total_size)
-
-        self.update_progress_bar("Writing completed:", 1.0)
-        os.close(f_from)
-        os.close(f_to)
-        self.progress_bar.hide()
diff --git a/lib/bb/ui/crumbs/hig/imageselectiondialog.py b/lib/bb/ui/crumbs/hig/imageselectiondialog.py
deleted file mode 100644
index 21216ad..0000000
--- a/lib/bb/ui/crumbs/hig/imageselectiondialog.py
+++ /dev/null
@@ -1,172 +0,0 @@
-#
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2011-2012   Intel Corporation
-#
-# Authored by Joshua Lock <josh@linux.intel.com>
-# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
-# Authored by Shane Wang <shane.wang@intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-import gtk
-import gobject
-import os
-from bb.ui.crumbs.hobwidget import HobViewTable, HobInfoButton, HobButton, HobAltButton
-from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
-from bb.ui.crumbs.hig.layerselectiondialog import LayerSelectionDialog
-
-"""
-The following are convenience classes for implementing GNOME HIG compliant
-BitBake GUI's
-In summary: spacing = 12px, border-width = 6px
-"""
-
-class ImageSelectionDialog (CrumbsDialog):
-
-    __columns__ = [{
-            'col_name' : 'Image name',
-            'col_id'   : 0,
-            'col_style': 'text',
-            'col_min'  : 400,
-            'col_max'  : 400
-        }, {
-            'col_name' : 'Select',
-            'col_id'   : 1,
-            'col_style': 'radio toggle',
-            'col_min'  : 160,
-            'col_max'  : 160
-    }]
-
-
-    def __init__(self, image_folder, image_types, title, parent, flags, buttons=None, image_extension = {}):
-        super(ImageSelectionDialog, self).__init__(title, parent, flags, buttons)
-        self.connect("response", self.response_cb)
-
-        self.image_folder = image_folder
-        self.image_types  = image_types
-        self.image_list = []
-        self.image_names = []
-        self.image_extension = image_extension
-
-        # create visual elements on the dialog
-        self.create_visual_elements()
-
-        self.image_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
-        self.fill_image_store()
-
-    def create_visual_elements(self):
-        hbox = gtk.HBox(False, 6)
-
-        self.vbox.pack_start(hbox, expand=False, fill=False)
-
-        entry = gtk.Entry()
-        entry.set_text(self.image_folder)
-        table = gtk.Table(1, 10, True)
-        table.set_size_request(560, -1)
-        hbox.pack_start(table, expand=False, fill=False)
-        table.attach(entry, 0, 9, 0, 1)
-        image = gtk.Image()
-        image.set_from_stock(gtk.STOCK_OPEN, gtk.ICON_SIZE_BUTTON)
-        open_button = gtk.Button()
-        open_button.set_image(image)
-        open_button.connect("clicked", self.select_path_cb, self, entry)
-        table.attach(open_button, 9, 10, 0, 1)
-
-        self.image_table = HobViewTable(self.__columns__, "Images")
-        self.image_table.set_size_request(-1, 300)
-        self.image_table.connect("toggled", self.toggled_cb)
-        self.image_table.connect_group_selection(self.table_selected_cb)
-        self.image_table.connect("row-activated", self.row_actived_cb)
-        self.vbox.pack_start(self.image_table, expand=True, fill=True)
-
-        self.show_all()
-
-    def change_image_cb(self, model, path, columnid):
-        if not model:
-            return
-        iter = model.get_iter_first()
-        while iter:
-            rowpath = model.get_path(iter)
-            model[rowpath][columnid] = False
-            iter = model.iter_next(iter)
-
-        model[path][columnid] = True
-
-    def toggled_cb(self, table, cell, path, columnid, tree):
-        model = tree.get_model()
-        self.change_image_cb(model, path, columnid)
-
-    def table_selected_cb(self, selection):
-        model, paths = selection.get_selected_rows()
-        if paths:
-            self.change_image_cb(model, paths[0], 1)
-
-    def row_actived_cb(self, tab, model, path):
-        self.change_image_cb(model, path, 1)
-        self.emit('response', gtk.RESPONSE_YES)
-
-    def select_path_cb(self, action, parent, entry):
-        dialog = gtk.FileChooserDialog("", parent,
-                                       gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
-        text = entry.get_text()
-        dialog.set_current_folder(text if len(text) > 0 else os.getcwd())
-        button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
-        HobAltButton.style_button(button)
-        button = dialog.add_button("Open", gtk.RESPONSE_YES)
-        HobButton.style_button(button)
-        response = dialog.run()
-        if response == gtk.RESPONSE_YES:
-            path = dialog.get_filename()
-            entry.set_text(path)
-            self.image_folder = path
-            self.fill_image_store()
-
-        dialog.destroy()
-
-    def fill_image_store(self):
-        self.image_list = []
-        self.image_store.clear()
-        imageset = set()
-        for root, dirs, files in os.walk(self.image_folder):
-            # ignore the sub directories
-            dirs[:] = []
-            for f in files:
-                for image_type in self.image_types:
-                    if image_type in self.image_extension:
-                        real_types = self.image_extension[image_type]
-                    else:
-                        real_types = [image_type]
-                    for real_image_type in real_types:
-                        if f.endswith('.' + real_image_type):
-                            imageset.add(f.rsplit('.' + real_image_type)[0].rsplit('.rootfs')[0])
-                            self.image_list.append(f)
-
-        for image in imageset:
-            self.image_store.set(self.image_store.append(), 0, image, 1, False)
-
-        self.image_table.set_model(self.image_store)
-
-    def response_cb(self, dialog, response_id):
-        self.image_names = []
-        if response_id == gtk.RESPONSE_YES:
-            iter = self.image_store.get_iter_first()
-            while iter:
-                path = self.image_store.get_path(iter)
-                if self.image_store[path][1]:
-                    for f in self.image_list:
-                        if f.startswith(self.image_store[path][0] + '.'):
-                            self.image_names.append(f)
-                    break
-                iter = self.image_store.iter_next(iter)
diff --git a/lib/bb/ui/crumbs/hig/layerselectiondialog.py b/lib/bb/ui/crumbs/hig/layerselectiondialog.py
deleted file mode 100644
index 52d57b6..0000000
--- a/lib/bb/ui/crumbs/hig/layerselectiondialog.py
+++ /dev/null
@@ -1,298 +0,0 @@
-#
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2011-2012   Intel Corporation
-#
-# Authored by Joshua Lock <josh@linux.intel.com>
-# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
-# Authored by Shane Wang <shane.wang@intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-import gtk
-import gobject
-import os
-import tempfile
-from bb.ui.crumbs.hobwidget import hic, HobButton, HobAltButton
-from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
-from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
-
-"""
-The following are convenience classes for implementing GNOME HIG compliant
-BitBake GUI's
-In summary: spacing = 12px, border-width = 6px
-"""
-
-class CellRendererPixbufActivatable(gtk.CellRendererPixbuf):
-    """
-    A custom CellRenderer implementation which is activatable
-    so that we can handle user clicks
-    """
-    __gsignals__    = { 'clicked' : (gobject.SIGNAL_RUN_LAST,
-                                     gobject.TYPE_NONE,
-                                     (gobject.TYPE_STRING,)), }
-
-    def __init__(self):
-        gtk.CellRendererPixbuf.__init__(self)
-        self.set_property('mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE)
-        self.set_property('follow-state', True)
-
-    """
-    Respond to a user click on a cell
-    """
-    def do_activate(self, even, widget, path, background_area, cell_area, flags):
-        self.emit('clicked', path)
-
-#
-# LayerSelectionDialog
-#
-class LayerSelectionDialog (CrumbsDialog):
-
-    TARGETS = [
-        ("MY_TREE_MODEL_ROW", gtk.TARGET_SAME_WIDGET, 0),
-        ("text/plain", 0, 1),
-        ("TEXT", 0, 2),
-        ("STRING", 0, 3),
-        ]
-
-    def gen_label_widget(self, content):
-        label = gtk.Label()
-        label.set_alignment(0, 0)
-        label.set_markup(content)
-        label.show()
-        return label
-
-    def layer_widget_toggled_cb(self, cell, path, layer_store):
-        name = layer_store[path][0]
-        toggle = not layer_store[path][1]
-        layer_store[path][1] = toggle
-
-    def layer_widget_add_clicked_cb(self, action, layer_store, parent):
-        dialog = gtk.FileChooserDialog("Add new layer", parent,
-                                       gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
-        button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
-        HobAltButton.style_button(button)
-        button = dialog.add_button("Open", gtk.RESPONSE_YES)
-        HobButton.style_button(button)
-        label = gtk.Label("Select the layer you wish to add")
-        label.show()
-        dialog.set_extra_widget(label)
-        response = dialog.run()
-        path = dialog.get_filename()
-        dialog.destroy()
-
-        lbl = "<b>Error</b>"
-        msg = "Unable to load layer <i>%s</i> because " % path
-        if response == gtk.RESPONSE_YES:
-            import os
-            import os.path
-            layers = []
-            it = layer_store.get_iter_first()
-            while it:
-                layers.append(layer_store.get_value(it, 0))
-                it = layer_store.iter_next(it)
-
-            if not path:
-                msg += "it is an invalid path."
-            elif not os.path.exists(path+"/conf/layer.conf"):
-                msg += "there is no layer.conf inside the directory."
-            elif path in layers:
-                msg += "it is already in loaded layers."
-            else:
-                layer_store.append([path])
-                return
-            dialog = CrumbsMessageDialog(parent, lbl, gtk.MESSAGE_ERROR, msg)
-            dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_OK)
-            response = dialog.run()
-            dialog.destroy()
-
-    def layer_widget_del_clicked_cb(self, action, tree_selection, layer_store):
-        model, iter = tree_selection.get_selected()
-        if iter:
-            layer_store.remove(iter)
-
-
-    def gen_layer_widget(self, layers, layers_avail, window, tooltip=""):
-        hbox = gtk.HBox(False, 6)
-
-        layer_tv = gtk.TreeView()
-        layer_tv.set_rules_hint(True)
-        layer_tv.set_headers_visible(False)
-        tree_selection = layer_tv.get_selection()
-        tree_selection.set_mode(gtk.SELECTION_SINGLE)
-
-        # Allow enable drag and drop of rows including row move
-        dnd_internal_target = ''
-        dnd_targets = [(dnd_internal_target, gtk.TARGET_SAME_WIDGET, 0)]
-        layer_tv.enable_model_drag_source( gtk.gdk.BUTTON1_MASK,
-            dnd_targets,
-            gtk.gdk.ACTION_MOVE)
-        layer_tv.enable_model_drag_dest(dnd_targets,
-            gtk.gdk.ACTION_MOVE)
-        layer_tv.connect("drag_data_get", self.drag_data_get_cb)
-        layer_tv.connect("drag_data_received", self.drag_data_received_cb)
-
-        col0= gtk.TreeViewColumn('Path')
-        cell0 = gtk.CellRendererText()
-        cell0.set_padding(5,2)
-        col0.pack_start(cell0, True)
-        col0.set_cell_data_func(cell0, self.draw_layer_path_cb)
-        layer_tv.append_column(col0)
-
-        scroll = gtk.ScrolledWindow()
-        scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
-        scroll.set_shadow_type(gtk.SHADOW_IN)
-        scroll.add(layer_tv)
-
-        table_layer = gtk.Table(2, 10, False)
-        hbox.pack_start(table_layer, expand=True, fill=True)
-
-        table_layer.attach(scroll, 0, 10, 0, 1)
-
-        layer_store = gtk.ListStore(gobject.TYPE_STRING)
-        for layer in layers:
-            layer_store.append([layer])
-
-        col1 = gtk.TreeViewColumn('Enabled')
-        layer_tv.append_column(col1)
-
-        cell1 = CellRendererPixbufActivatable()
-        cell1.set_fixed_size(-1,35)
-        cell1.connect("clicked", self.del_cell_clicked_cb, layer_store)
-        col1.pack_start(cell1, True)
-        col1.set_cell_data_func(cell1, self.draw_delete_button_cb, layer_tv)
-
-        add_button = gtk.Button()
-        add_button.set_relief(gtk.RELIEF_NONE)
-        box = gtk.HBox(False, 6)
-        box.show()
-        add_button.add(box)
-        add_button.connect("enter-notify-event", self.add_hover_cb)
-        add_button.connect("leave-notify-event", self.add_leave_cb)
-        self.im = gtk.Image()
-        self.im.set_from_file(hic.ICON_INDI_ADD_FILE)
-        self.im.show()
-        box.pack_start(self.im, expand=False, fill=False, padding=6)
-        lbl = gtk.Label("Add layer")
-        lbl.set_alignment(0.0, 0.5)
-        lbl.show()
-        box.pack_start(lbl, expand=True, fill=True, padding=6)
-        add_button.connect("clicked", self.layer_widget_add_clicked_cb, layer_store, window)
-        table_layer.attach(add_button, 0, 10, 1, 2, gtk.EXPAND | gtk.FILL, 0, 0, 6)
-        layer_tv.set_model(layer_store)
-
-        hbox.show_all()
-
-        return hbox, layer_store
-
-    def drag_data_get_cb(self, treeview, context, selection, target_id, etime):
-        treeselection = treeview.get_selection()
-        model, iter = treeselection.get_selected()
-        data = model.get_value(iter, 0)
-        selection.set(selection.target, 8, data)
-
-    def drag_data_received_cb(self, treeview, context, x, y, selection, info, etime):
-        model = treeview.get_model()
-        data = selection.data
-        drop_info = treeview.get_dest_row_at_pos(x, y)
-        if drop_info:
-            path, position = drop_info
-            iter = model.get_iter(path)
-            if (position == gtk.TREE_VIEW_DROP_BEFORE or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
-                model.insert_before(iter, [data])
-            else:
-                model.insert_after(iter, [data])
-        else:
-            model.append([data])
-        if context.action == gtk.gdk.ACTION_MOVE:
-            context.finish(True, True, etime)
-        return
-
-    def add_hover_cb(self, button, event):
-        self.im.set_from_file(hic.ICON_INDI_ADD_HOVER_FILE)
-
-    def add_leave_cb(self, button, event):
-        self.im.set_from_file(hic.ICON_INDI_ADD_FILE)
-
-    def __init__(self, title, layers, layers_non_removable, all_layers, parent, flags, buttons=None):
-        super(LayerSelectionDialog, self).__init__(title, parent, flags, buttons)
-
-        # class members from other objects
-        self.layers = layers
-        self.layers_non_removable = layers_non_removable
-        self.all_layers = all_layers
-        self.layers_changed = False
-
-        # icon for remove button in TreeView
-        im = gtk.Image()
-        im.set_from_file(hic.ICON_INDI_REMOVE_FILE)
-        self.rem_icon = im.get_pixbuf()
-
-        # class members for internal use
-        self.layer_store = None
-
-        # create visual elements on the dialog
-        self.create_visual_elements()
-        self.connect("response", self.response_cb)
-
-    def create_visual_elements(self):
-        layer_widget, self.layer_store = self.gen_layer_widget(self.layers, self.all_layers, self, None)
-        layer_widget.set_size_request(450, 250)
-        self.vbox.pack_start(layer_widget, expand=True, fill=True)
-        self.show_all()
-
-    def response_cb(self, dialog, response_id):
-        model = self.layer_store
-        it = model.get_iter_first()
-        layers = []
-        while it:
-            layers.append(model.get_value(it, 0))
-            it = model.iter_next(it)
-
-        self.layers_changed = (self.layers != layers)
-        self.layers = layers
-
-    """
-    A custom cell_data_func to draw a delete 'button' in the TreeView for layers
-    other than the meta layer. The deletion of which is prevented so that the
-    user can't shoot themselves in the foot too badly.
-    """
-    def draw_delete_button_cb(self, col, cell, model, it, tv):
-        path =  model.get_value(it, 0)
-        if path in self.layers_non_removable:
-            cell.set_sensitive(False)
-            cell.set_property('pixbuf', None)
-            cell.set_property('mode', gtk.CELL_RENDERER_MODE_INERT)
-        else:
-            cell.set_property('pixbuf', self.rem_icon)
-            cell.set_sensitive(True)
-            cell.set_property('mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE)
-
-        return True
-
-    """
-    A custom cell_data_func to write an extra message into the layer path cell
-    for the meta layer. We should inform the user that they can't remove it for
-    their own safety.
-    """
-    def draw_layer_path_cb(self, col, cell, model, it):
-        path = model.get_value(it, 0)
-        if path in self.layers_non_removable:
-            cell.set_property('markup', "<b>It cannot be removed</b>\n%s" % path)
-        else:
-            cell.set_property('text', path)
-
-    def del_cell_clicked_cb(self, cell, path, model):
-        it = model.get_iter_from_string(path)
-        model.remove(it)
diff --git a/lib/bb/ui/crumbs/hig/propertydialog.py b/lib/bb/ui/crumbs/hig/propertydialog.py
deleted file mode 100644
index 09b9ce6..0000000
--- a/lib/bb/ui/crumbs/hig/propertydialog.py
+++ /dev/null
@@ -1,437 +0,0 @@
-#
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2011-2013   Intel Corporation
-#
-# Authored by Andrei Dinu <andrei.adrianx.dinu@intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-import string
-import gtk
-import gobject
-import os
-import tempfile
-import glib
-from bb.ui.crumbs.hig.crumbsdialog import CrumbsDialog
-from bb.ui.crumbs.hig.settingsuihelper import SettingsUIHelper
-from bb.ui.crumbs.hig.crumbsmessagedialog import CrumbsMessageDialog
-from bb.ui.crumbs.hig.layerselectiondialog import LayerSelectionDialog
-
-"""
-The following are convenience classes for implementing GNOME HIG compliant
-BitBake GUI's
-In summary: spacing = 12px, border-width = 6px
-"""
-
-class PropertyDialog(CrumbsDialog):
-	
-	def __init__(self, title, parent, information, flags, buttons=None):
-		
-		super(PropertyDialog, self).__init__(title, parent, flags, buttons)
-                
-                self.properties = information
-
-                if len(self.properties) == 10:
-		        self.create_recipe_visual_elements()
-                elif len(self.properties) == 5:
-                        self.create_package_visual_elements()
-                else:
-                        self.create_information_visual_elements()
-
-
-        def create_information_visual_elements(self):
-
-                HOB_ICON_BASE_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), ("icons/"))
-                ICON_PACKAGES_DISPLAY_FILE    = os.path.join(HOB_ICON_BASE_DIR, ('info/info_display.png'))
-
-                self.set_resizable(False)
-
-                self.table = gtk.Table(1,1,False)
-                self.table.set_row_spacings(0)
-                self.table.set_col_spacings(0)
-
-                self.image = gtk.Image()
-                self.image.set_from_file(ICON_PACKAGES_DISPLAY_FILE)
-                self.image.set_property("xalign",0)
-                #self.vbox.add(self.image)
-
-                image_info = self.properties.split("*")[0]
-                info = self.properties.split("*")[1]
-                
-                vbox = gtk.VBox(True, spacing=30)        
-                
-                self.label_short = gtk.Label()
-                self.label_short.set_line_wrap(False)
-                self.label_short.set_markup(image_info)
-                self.label_short.set_property("xalign", 0)
-
-                self.info_label = gtk.Label()
-                self.info_label.set_line_wrap(True)
-                self.info_label.set_markup(info)
-                self.info_label.set_property("yalign", 0.5)
-
-                self.table.attach(self.image, 0,1,0,1, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL,xpadding=5,ypadding=5)
-                self.table.attach(self.label_short, 0,1,0,1, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL,xpadding=40,ypadding=5)
-                self.table.attach(self.info_label, 0,1,1,2, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL,xpadding=40,ypadding=10)
-        
-                self.vbox.add(self.table)
-                self.connect('delete-event', lambda w, e: self.destroy() or True) 
-
-        def treeViewTooltip( self, widget, e, tooltips, cell, emptyText="" ):
-                 try:
-                           (path,col,x,y) = widget.get_path_at_pos( int(e.x), int(e.y) )
-                           it = widget.get_model().get_iter(path)
-                           value = widget.get_model().get_value(it,cell)
-                           if value in self.tooltip_items:
-                                tooltips.set_tip(widget, self.tooltip_items[value])
-                                tooltips.enable()
-                           else:
-                                tooltips.set_tip(widget, emptyText)
-                 except:
-                           tooltips.set_tip(widget, emptyText)
-
-                
-        def create_package_visual_elements(self):
-
-                import json
-
-                name = self.properties['name']
-                binb = self.properties['binb']
-                size = self.properties['size']
-                recipe = self.properties['recipe']
-                file_list = json.loads(self.properties['files_list'])
-
-                files_temp = ''
-                paths_temp = ''
-                files_binb = []
-                paths_binb = []
-
-                self.tooltip_items = {}
-
-                self.set_resizable(False)
-                
-                #cleaning out the recipe variable
-                recipe = recipe.split("+")[0]
-
-                vbox = gtk.VBox(True,spacing = 0)
-
-                ###################################### NAME ROW + COL #################################
-
-                self.label_short = gtk.Label()
-                self.label_short.set_size_request(300,-1)
-                self.label_short.set_selectable(True)
-                self.label_short.set_line_wrap(True)
-                self.label_short.set_markup("<span weight=\"bold\">Name: </span>" + name)
-                self.label_short.set_property("xalign", 0)
-                                
-                self.vbox.add(self.label_short)
-
-                ###################################### SIZE ROW + COL ######################################
-
-                self.label_short = gtk.Label()
-                self.label_short.set_size_request(300,-1)
-                self.label_short.set_selectable(True)
-                self.label_short.set_line_wrap(True)
-                self.label_short.set_markup("<span weight=\"bold\">Size: </span>" + size)
-                self.label_short.set_property("xalign", 0)
-                
-                self.vbox.add(self.label_short)
-
-                ##################################### RECIPE ROW + COL #########################################
-
-                self.label_short = gtk.Label()
-                self.label_short.set_size_request(300,-1)
-                self.label_short.set_selectable(True)
-                self.label_short.set_line_wrap(True)
-                self.label_short.set_markup("<span weight=\"bold\">Recipe: </span>" + recipe)
-                self.label_short.set_property("xalign", 0)
-
-                self.vbox.add(self.label_short)
-
-                ##################################### BINB ROW + COL #######################################
-                
-                if binb != '':
-                        self.label_short = gtk.Label()
-                        self.label_short.set_selectable(True)
-                        self.label_short.set_line_wrap(True)
-                        self.label_short.set_markup("<span weight=\"bold\">Brought in by: </span>")
-                        self.label_short.set_property("xalign", 0)                
-
-                        self.label_info = gtk.Label()
-                        self.label_info.set_size_request(300,-1)
-                        self.label_info.set_selectable(True)
-                        self.label_info.set_line_wrap(True)
-                        self.label_info.set_markup(binb)
-                        self.label_info.set_property("xalign", 0)               
-                                                        
-                        self.vbox.add(self.label_short)
-                        self.vbox.add(self.label_info)
-
-                #################################### FILES BROUGHT BY PACKAGES ###################################
-
-                if file_list:
-                
-                        self.textWindow = gtk.ScrolledWindow()
-                        self.textWindow.set_shadow_type(gtk.SHADOW_IN)
-                        self.textWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-                        self.textWindow.set_size_request(100, 170)
-
-                        packagefiles_store = gtk.ListStore(str)
-
-                        self.packagefiles_tv = gtk.TreeView()
-                        self.packagefiles_tv.set_rules_hint(True)
-                        self.packagefiles_tv.set_headers_visible(True)
-                        self.textWindow.add(self.packagefiles_tv)
-
-                        self.cell1 = gtk.CellRendererText()
-                        col1 = gtk.TreeViewColumn('Package files', self.cell1)
-                        col1.set_cell_data_func(self.cell1, self.regex_field)
-                        self.packagefiles_tv.append_column(col1)
-
-                        items = file_list.keys()
-                        items.sort()
-                        for item in items:
-                                fullpath = item
-                                while len(item) > 35:
-                                        item = item[:len(item)/2] + "" + item[len(item)/2+1:]
-                                if len(item) == 35:
-                                        item = item[:len(item)/2] + "..." + item[len(item)/2+3:]
-                                        self.tooltip_items[item] = fullpath
-
-                                packagefiles_store.append([str(item)])
-
-                        self.packagefiles_tv.set_model(packagefiles_store)
-
-                        tips = gtk.Tooltips()
-                        tips.set_tip(self.packagefiles_tv, "")
-                        self.packagefiles_tv.connect("motion-notify-event", self.treeViewTooltip, tips, 0)
-                        self.packagefiles_tv.set_events(gtk.gdk.POINTER_MOTION_MASK)
-                        
-                        self.vbox.add(self.textWindow)                                      
-
-                self.vbox.show_all()
-
-
-        def regex_field(self, column, cell, model, iter):
-                cell.set_property('text', model.get_value(iter, 0))
-                return
-
-
-	def create_recipe_visual_elements(self):
-
-                summary = self.properties['summary']
-		name = self.properties['name']
-		version = self.properties['version']
-		revision = self.properties['revision']
-		binb = self.properties['binb']
-		group = self.properties['group']
-		license = self.properties['license']
-		homepage = self.properties['homepage']
-		bugtracker = self.properties['bugtracker']
-		description = self.properties['description']
-
-                self.set_resizable(False)
-		
-                #cleaning out the version variable and also the summary
-                version = version.split(":")[1]
-                if len(version) > 30:
-                        version = version.split("+")[0]
-                else:
-                        version = version.split("-")[0]
-                license = license.replace("&" , "and")
-                if (homepage == ''):
-                        homepage = 'unknown'
-                if (bugtracker == ''):
-                        bugtracker = 'unknown'
-                summary = summary.split("+")[0]
-                
-                #calculating the rows needed for the table
-                binb_items_count = len(binb.split(','))
-                binb_items = binb.split(',')
-                               		
-		vbox = gtk.VBox(False,spacing = 0)
-
-                ######################################## SUMMARY LABEL #########################################                	                
-                
-                if summary != '':                
-                        self.label_short = gtk.Label()
-                        self.label_short.set_width_chars(37)
-                        self.label_short.set_selectable(True)                
-                        self.label_short.set_line_wrap(True)
-                        self.label_short.set_markup("<b>" + summary + "</b>")
-                        self.label_short.set_property("xalign", 0)
-                        
-                        self.vbox.add(self.label_short)
-                
-                ########################################## NAME ROW + COL #######################################
-                                
-                self.label_short = gtk.Label()
-                self.label_short.set_selectable(True)
-                self.label_short.set_line_wrap(True)
-                self.label_short.set_markup("<span weight=\"bold\">Name: </span>" + name)
-                self.label_short.set_property("xalign", 0)
-                                
-                self.vbox.add(self.label_short)
-
-                ####################################### VERSION ROW + COL ####################################
-                
-                self.label_short = gtk.Label()
-                self.label_short.set_selectable(True)
-                self.label_short.set_line_wrap(True)
-                self.label_short.set_markup("<span weight=\"bold\">Version: </span>" + version)
-                self.label_short.set_property("xalign", 0)
-                
-                self.vbox.add(self.label_short)
-
-                ##################################### REVISION ROW + COL #####################################
-
-                self.label_short = gtk.Label()
-                self.label_short.set_line_wrap(True)
-                self.label_short.set_selectable(True)
-                self.label_short.set_markup("<span weight=\"bold\">Revision: </span>" + revision)
-                self.label_short.set_property("xalign", 0)                
-                
-                self.vbox.add(self.label_short)
-
-                ################################## GROUP ROW + COL ############################################
-               
-                self.label_short = gtk.Label()
-                self.label_short.set_selectable(True)              
-                self.label_short.set_line_wrap(True)                                
-                self.label_short.set_markup("<span weight=\"bold\">Group: </span>" + group)
-                self.label_short.set_property("xalign", 0)
-                                
-                self.vbox.add(self.label_short)
-
-                ################################# HOMEPAGE ROW + COL ############################################
-                
-                if homepage != 'unknown':
-                        self.label_info = gtk.Label()
-                        self.label_info.set_selectable(True)
-                        self.label_info.set_line_wrap(True)
-                        if len(homepage) > 35:
-                                self.label_info.set_markup("<a href=\"" + homepage + "\">" + homepage[0:35] + "..." + "</a>")
-                        else:
-                                self.label_info.set_markup("<a href=\"" + homepage + "\">" + homepage[0:60] + "</a>")
-
-                        self.label_info.set_property("xalign", 0)
-                       
-                        self.label_short = gtk.Label()
-                        self.label_short.set_selectable(True)                     
-                        self.label_short.set_line_wrap(True)                        
-                        self.label_short.set_markup("<b>Homepage: </b>")                      
-                        self.label_short.set_property("xalign", 0)
-                        
-                        self.vbox.add(self.label_short)
-                        self.vbox.add(self.label_info)
-               
-                ################################# BUGTRACKER ROW + COL ###########################################
-                
-                if bugtracker != 'unknown':
-                        self.label_info = gtk.Label()
-                        self.label_info.set_selectable(True)
-                        self.label_info.set_line_wrap(True)
-                        if len(bugtracker) > 35:
-                                self.label_info.set_markup("<a href=\"" + bugtracker + "\">" + bugtracker[0:35] + "..." + "</a>")
-                        else:
-                                self.label_info.set_markup("<a href=\"" + bugtracker + "\">" + bugtracker[0:60] + "</a>")
-                        self.label_info.set_property("xalign", 0)  
-
-                        self.label_short = gtk.Label()
-                        self.label_short.set_selectable(True)                    
-                        self.label_short.set_line_wrap(True)
-                        self.label_short.set_markup("<b>Bugtracker: </b>")                   
-                        self.label_short.set_property("xalign", 0)
-                        
-                        self.vbox.add(self.label_short)
-                        self.vbox.add(self.label_info)
-
-                ################################# LICENSE ROW + COL ############################################
-                
-                self.label_info = gtk.Label()
-                self.label_info.set_selectable(True)
-                self.label_info.set_line_wrap(True)
-                self.label_info.set_markup(license)
-                self.label_info.set_property("xalign", 0)
-
-                self.label_short = gtk.Label()
-                self.label_short.set_selectable(True)                
-                self.label_short.set_line_wrap(True)                
-                self.label_short.set_markup("<span weight=\"bold\">License: </span>")
-                self.label_short.set_property("xalign", 0)
-                
-                self.vbox.add(self.label_short)
-                self.vbox.add(self.label_info)
-
-                ################################### BINB ROW+COL #############################################
-                
-                if binb != '':
-                        self.label_short = gtk.Label()
-                        self.label_short.set_selectable(True)
-                        self.label_short.set_line_wrap(True)
-                        self.label_short.set_markup("<span weight=\"bold\">Brought in by: </span>")
-                        self.label_short.set_property("xalign", 0)
-                        self.vbox.add(self.label_short)
-                        self.label_info = gtk.Label()
-                        self.label_info.set_selectable(True)
-                        self.label_info.set_width_chars(36)
-                        if len(binb) > 200:
-                            scrolled_window = gtk.ScrolledWindow()
-                            scrolled_window.set_policy(gtk.POLICY_NEVER,gtk.POLICY_ALWAYS)
-                            scrolled_window.set_size_request(100,100)
-                            self.label_info.set_markup(binb)
-                            self.label_info.set_padding(6,6)
-                            self.label_info.set_alignment(0,0)
-                            self.label_info.set_line_wrap(True)
-                            scrolled_window.add_with_viewport(self.label_info)
-                            self.vbox.add(scrolled_window)
-                        else:
-                            self.label_info.set_markup(binb)
-                            self.label_info.set_property("xalign", 0)
-                            self.label_info.set_line_wrap(True) 
-                            self.vbox.add(self.label_info)
-
-                ################################ DESCRIPTION TAG ROW #################################################
-                
-                self.label_short = gtk.Label()
-                self.label_short.set_line_wrap(True)
-                self.label_short.set_markup("<span weight=\"bold\">Description </span>")
-                self.label_short.set_property("xalign", 0)
-                self.vbox.add(self.label_short)
-                
-                ################################ DESCRIPTION INFORMATION ROW ##########################################
-                
-                hbox = gtk.HBox(True,spacing = 0)
-
-                self.label_short = gtk.Label()
-                self.label_short.set_selectable(True)
-                self.label_short.set_width_chars(36)
-                if len(description) > 200:
-                    scrolled_window = gtk.ScrolledWindow()
-                    scrolled_window.set_policy(gtk.POLICY_NEVER,gtk.POLICY_ALWAYS)
-                    scrolled_window.set_size_request(100,100)
-                    self.label_short.set_markup(description)
-                    self.label_short.set_padding(6,6)
-                    self.label_short.set_alignment(0,0)
-                    self.label_short.set_line_wrap(True)
-                    scrolled_window.add_with_viewport(self.label_short)
-                    self.vbox.add(scrolled_window)
-                else:    
-                    self.label_short.set_markup(description)
-                    self.label_short.set_property("xalign", 0)
-                    self.label_short.set_line_wrap(True)
-                    self.vbox.add(self.label_short)
-
-                self.vbox.show_all()
diff --git a/lib/bb/ui/crumbs/hig/settingsuihelper.py b/lib/bb/ui/crumbs/hig/settingsuihelper.py
deleted file mode 100644
index e0285c9..0000000
--- a/lib/bb/ui/crumbs/hig/settingsuihelper.py
+++ /dev/null
@@ -1,122 +0,0 @@
-#
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2011-2012   Intel Corporation
-#
-# Authored by Joshua Lock <josh@linux.intel.com>
-# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
-# Authored by Shane Wang <shane.wang@intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-import gtk
-import os
-from bb.ui.crumbs.hobwidget import HobInfoButton, HobButton, HobAltButton
-
-"""
-The following are convenience classes for implementing GNOME HIG compliant
-BitBake GUI's
-In summary: spacing = 12px, border-width = 6px
-"""
-
-class SettingsUIHelper():
-
-    def gen_label_widget(self, content):
-        label = gtk.Label()
-        label.set_alignment(0, 0)
-        label.set_markup(content)
-        label.show()
-        return label
-
-    def gen_label_info_widget(self, content, tooltip):
-        table = gtk.Table(1, 10, False)
-        label = self.gen_label_widget(content)
-        info = HobInfoButton(tooltip, self)
-        table.attach(label, 0, 1, 0, 1, xoptions=gtk.FILL)
-        table.attach(info, 1, 2, 0, 1, xoptions=gtk.FILL, xpadding=10)
-        return table
-
-    def gen_spinner_widget(self, content, lower, upper, tooltip=""):
-        hbox = gtk.HBox(False, 12)
-        adjust = gtk.Adjustment(value=content, lower=lower, upper=upper, step_incr=1)
-        spinner = gtk.SpinButton(adjustment=adjust, climb_rate=1, digits=0)
-
-        spinner.set_value(content)
-        hbox.pack_start(spinner, expand=False, fill=False)
-
-        info = HobInfoButton(tooltip, self)
-        hbox.pack_start(info, expand=False, fill=False)
-
-        hbox.show_all()
-        return hbox, spinner
-
-    def gen_combo_widget(self, curr_item, all_item, tooltip=""):
-        hbox = gtk.HBox(False, 12)
-        combo = gtk.combo_box_new_text()
-        hbox.pack_start(combo, expand=False, fill=False)
-
-        index = 0
-        for item in all_item or []:
-            combo.append_text(item)
-            if item == curr_item:
-                combo.set_active(index)
-            index += 1
-
-        info = HobInfoButton(tooltip, self)
-        hbox.pack_start(info, expand=False, fill=False)
-
-        hbox.show_all()
-        return hbox, combo
-
-    def entry_widget_select_path_cb(self, action, parent, entry):
-        dialog = gtk.FileChooserDialog("", parent,
-                                       gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
-        text = entry.get_text()
-        dialog.set_current_folder(text if len(text) > 0 else os.getcwd())
-        button = dialog.add_button("Cancel", gtk.RESPONSE_NO)
-        HobAltButton.style_button(button)
-        button = dialog.add_button("Open", gtk.RESPONSE_YES)
-        HobButton.style_button(button)
-        response = dialog.run()
-        if response == gtk.RESPONSE_YES:
-            path = dialog.get_filename()
-            entry.set_text(path)
-
-        dialog.destroy()
-
-    def gen_entry_widget(self, content, parent, tooltip="", need_button=True):
-        hbox = gtk.HBox(False, 12)
-        entry = gtk.Entry()
-        entry.set_text(content)
-        entry.set_size_request(350,30)
-
-        if need_button:
-            table = gtk.Table(1, 10, False)
-            hbox.pack_start(table, expand=True, fill=True)
-            table.attach(entry, 0, 9, 0, 1, xoptions=gtk.SHRINK)
-            image = gtk.Image()
-            image.set_from_stock(gtk.STOCK_OPEN,gtk.ICON_SIZE_BUTTON)
-            open_button = gtk.Button()
-            open_button.set_image(image)
-            open_button.connect("clicked", self.entry_widget_select_path_cb, parent, entry)
-            table.attach(open_button, 9, 10, 0, 1, xoptions=gtk.SHRINK)
-        else:
-            hbox.pack_start(entry, expand=True, fill=True)
-
-        if tooltip != "":
-            info = HobInfoButton(tooltip, self)
-            hbox.pack_start(info, expand=False, fill=False)
-
-        hbox.show_all()
-        return hbox, entry
diff --git a/lib/bb/ui/crumbs/hobcolor.py b/lib/bb/ui/crumbs/hobcolor.py
deleted file mode 100644
index 3316542..0000000
--- a/lib/bb/ui/crumbs/hobcolor.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2012        Intel Corporation
-#
-# Authored by Shane Wang <shane.wang@intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-class HobColors:
-    WHITE        = "#ffffff"
-    PALE_GREEN   = "#aaffaa"
-    ORANGE       = "#eb8e68"
-    PALE_RED     = "#ffaaaa"
-    GRAY         = "#aaaaaa"
-    LIGHT_GRAY   = "#dddddd"
-    SLIGHT_DARK  = "#5f5f5f"
-    DARK         = "#3c3b37"
-    BLACK        = "#000000"
-    PALE_BLUE    = "#53b8ff"
-    DEEP_RED     = "#aa3e3e"
-    KHAKI        = "#fff68f"
-
-    OK = WHITE
-    RUNNING = PALE_GREEN
-    WARNING = ORANGE
-    ERROR = PALE_RED
diff --git a/lib/bb/ui/crumbs/hobwidget.py b/lib/bb/ui/crumbs/hobwidget.py
deleted file mode 100644
index 3f6138b..0000000
--- a/lib/bb/ui/crumbs/hobwidget.py
+++ /dev/null
@@ -1,904 +0,0 @@
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2011-2012   Intel Corporation
-#
-# Authored by Dongxiao Xu <dongxiao.xu@intel.com>
-# Authored by Shane Wang <shane.wang@intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-import gtk
-import gobject
-import os
-import os.path
-import sys
-import pango, pangocairo
-import cairo
-import math
-
-from bb.ui.crumbs.hobcolor import HobColors
-from bb.ui.crumbs.persistenttooltip import PersistentTooltip
-
-class hwc:
-
-    MAIN_WIN_WIDTH   = 1024
-    MAIN_WIN_HEIGHT  = 700
-
-class hic:
-
-    HOB_ICON_BASE_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), ("ui/icons/"))
-
-    ICON_RCIPE_DISPLAY_FILE       = os.path.join(HOB_ICON_BASE_DIR, ('recipe/recipe_display.png'))
-    ICON_RCIPE_HOVER_FILE         = os.path.join(HOB_ICON_BASE_DIR, ('recipe/recipe_hover.png'))
-    ICON_PACKAGES_DISPLAY_FILE    = os.path.join(HOB_ICON_BASE_DIR, ('packages/packages_display.png'))
-    ICON_PACKAGES_HOVER_FILE      = os.path.join(HOB_ICON_BASE_DIR, ('packages/packages_hover.png'))
-    ICON_LAYERS_DISPLAY_FILE      = os.path.join(HOB_ICON_BASE_DIR, ('layers/layers_display.png'))
-    ICON_LAYERS_HOVER_FILE        = os.path.join(HOB_ICON_BASE_DIR, ('layers/layers_hover.png'))
-    ICON_IMAGES_DISPLAY_FILE      = os.path.join(HOB_ICON_BASE_DIR, ('images/images_display.png'))
-    ICON_IMAGES_HOVER_FILE        = os.path.join(HOB_ICON_BASE_DIR, ('images/images_hover.png'))
-    ICON_SETTINGS_DISPLAY_FILE    = os.path.join(HOB_ICON_BASE_DIR, ('settings/settings_display.png'))
-    ICON_SETTINGS_HOVER_FILE      = os.path.join(HOB_ICON_BASE_DIR, ('settings/settings_hover.png'))
-    ICON_INFO_DISPLAY_FILE        = os.path.join(HOB_ICON_BASE_DIR, ('info/info_display.png'))
-    ICON_INFO_HOVER_FILE          = os.path.join(HOB_ICON_BASE_DIR, ('info/info_hover.png'))
-    ICON_INDI_CONFIRM_FILE        = os.path.join(HOB_ICON_BASE_DIR, ('indicators/confirmation.png'))
-    ICON_INDI_ERROR_FILE          = os.path.join(HOB_ICON_BASE_DIR, ('indicators/denied.png'))
-    ICON_INDI_REMOVE_FILE         = os.path.join(HOB_ICON_BASE_DIR, ('indicators/remove.png'))
-    ICON_INDI_REMOVE_HOVER_FILE   = os.path.join(HOB_ICON_BASE_DIR, ('indicators/remove-hover.png'))
-    ICON_INDI_ADD_FILE            = os.path.join(HOB_ICON_BASE_DIR, ('indicators/add.png'))
-    ICON_INDI_ADD_HOVER_FILE      = os.path.join(HOB_ICON_BASE_DIR, ('indicators/add-hover.png'))
-    ICON_INDI_REFRESH_FILE        = os.path.join(HOB_ICON_BASE_DIR, ('indicators/refresh.png'))
-    ICON_INDI_ALERT_FILE          = os.path.join(HOB_ICON_BASE_DIR, ('indicators/alert.png'))
-    ICON_INDI_TICK_FILE           = os.path.join(HOB_ICON_BASE_DIR, ('indicators/tick.png'))
-    ICON_INDI_INFO_FILE           = os.path.join(HOB_ICON_BASE_DIR, ('indicators/info.png'))
-
-class HobViewTable (gtk.VBox):
-    """
-    A VBox to contain the table for different recipe views and package view
-    """
-    __gsignals__ = {
-         "toggled"       : (gobject.SIGNAL_RUN_LAST,
-                            gobject.TYPE_NONE,
-                           (gobject.TYPE_PYOBJECT,
-                            gobject.TYPE_STRING,
-                            gobject.TYPE_INT,
-                            gobject.TYPE_PYOBJECT,)),
-         "row-activated" : (gobject.SIGNAL_RUN_LAST,
-                            gobject.TYPE_NONE,
-                           (gobject.TYPE_PYOBJECT,
-                            gobject.TYPE_PYOBJECT,)),
-         "cell-fadeinout-stopped" : (gobject.SIGNAL_RUN_LAST,
-                            gobject.TYPE_NONE,
-                           (gobject.TYPE_PYOBJECT,
-                            gobject.TYPE_PYOBJECT,
-                            gobject.TYPE_PYOBJECT,)),
-    }
-
-    def __init__(self, columns, name):
-        gtk.VBox.__init__(self, False, 6)
-        self.table_tree = gtk.TreeView()
-        self.table_tree.set_headers_visible(True)
-        self.table_tree.set_headers_clickable(True)
-        self.table_tree.set_rules_hint(True)
-        self.table_tree.set_enable_tree_lines(True)
-        self.table_tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
-        self.toggle_columns = []
-        self.table_tree.connect("row-activated", self.row_activated_cb)
-        self.top_bar = None
-        self.tab_name = name
-
-        for i, column in enumerate(columns):
-            col_name = column['col_name']
-            col = gtk.TreeViewColumn(col_name)
-            col.set_clickable(True)
-            col.set_resizable(True)
-            if self.tab_name.startswith('Included'):
-                if col_name!='Included':
-                    col.set_sort_column_id(column['col_id'])
-            else:
-                col.set_sort_column_id(column['col_id'])
-            if 'col_min' in column.keys():
-                col.set_min_width(column['col_min'])
-            if 'col_max' in column.keys():
-                col.set_max_width(column['col_max'])
-            if 'expand' in column.keys():
-                col.set_expand(True)
-            self.table_tree.append_column(col)
-
-            if (not 'col_style' in column.keys()) or column['col_style'] == 'text':
-                cell = gtk.CellRendererText()
-                col.pack_start(cell, True)
-                col.set_attributes(cell, text=column['col_id'])
-                if 'col_t_id' in column.keys():
-                    col.add_attribute(cell, 'font', column['col_t_id'])
-            elif column['col_style'] == 'check toggle':
-                cell = HobCellRendererToggle()
-                cell.set_property('activatable', True)
-                cell.connect("toggled", self.toggled_cb, i, self.table_tree)
-                cell.connect_render_state_changed(self.stop_cell_fadeinout_cb, self.table_tree)
-                self.toggle_id = i
-                col.pack_end(cell, True)
-                col.set_attributes(cell, active=column['col_id'])
-                self.toggle_columns.append(col_name)
-                if 'col_group' in column.keys():
-                    col.set_cell_data_func(cell, self.set_group_number_cb)
-            elif column['col_style'] == 'radio toggle':
-                cell = gtk.CellRendererToggle()
-                cell.set_property('activatable', True)
-                cell.set_radio(True)
-                cell.connect("toggled", self.toggled_cb, i, self.table_tree)
-                self.toggle_id = i
-                col.pack_end(cell, True)
-                col.set_attributes(cell, active=column['col_id'])
-                self.toggle_columns.append(col_name)
-            elif column['col_style'] == 'binb':
-                cell = gtk.CellRendererText()
-                col.pack_start(cell, True)
-                col.set_cell_data_func(cell, self.display_binb_cb, column['col_id'])
-                if 'col_t_id' in column.keys():
-                    col.add_attribute(cell, 'font', column['col_t_id'])
-
-        self.scroll = gtk.ScrolledWindow()
-        self.scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
-        self.scroll.add(self.table_tree)
-
-        self.pack_end(self.scroll, True, True, 0)
-
-    def add_no_result_bar(self, entry):
-        color = HobColors.KHAKI
-        self.top_bar = gtk.EventBox()
-        self.top_bar.set_size_request(-1, 70)
-        self.top_bar.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
-        self.top_bar.set_flags(gtk.CAN_DEFAULT)
-        self.top_bar.grab_default()
-
-        no_result_tab = gtk.Table(5, 20, True)
-        self.top_bar.add(no_result_tab)
-
-        label = gtk.Label()
-        label.set_alignment(0.0, 0.5)
-        title = "No results matching your search"
-        label.set_markup("<span size='x-large'><b>%s</b></span>" % title)
-        no_result_tab.attach(label, 1, 14, 1, 4)
-
-        clear_button = HobButton("Clear search")
-        clear_button.set_tooltip_text("Clear search query")
-        clear_button.connect('clicked', self.set_search_entry_clear_cb, entry)
-        no_result_tab.attach(clear_button, 16, 19, 1, 4)
-
-        self.pack_start(self.top_bar, False, True, 12)
-        self.top_bar.show_all()
-
-    def set_search_entry_clear_cb(self, button, search):
-        if search.get_editable() == True:
-            search.set_text("")
-        search.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
-        search.grab_focus()
-
-    def display_binb_cb(self, col, cell, model, it, col_id):
-        binb =  model.get_value(it, col_id)
-        # Just display the first item
-        if binb:
-            bin = binb.split(', ')
-            total_no = len(bin)
-            if total_no > 1 and bin[0] == "User Selected":
-                if total_no > 2:
-                    present_binb = bin[1] + ' (+' + str(total_no - 1) + ')'
-                else:
-                    present_binb = bin[1]
-            else:
-                if total_no > 1:
-                    present_binb = bin[0] + ' (+' + str(total_no - 1) + ')'
-                else:
-                    present_binb = bin[0]
-            cell.set_property('text', present_binb)
-        else:
-            cell.set_property('text', "")
-        return True
-
-    def set_model(self, tree_model):
-        self.table_tree.set_model(tree_model)
-
-    def toggle_default(self):
-        model = self.table_tree.get_model()
-        if not model:
-            return
-        iter = model.get_iter_first()
-        if iter:
-            rowpath = model.get_path(iter)
-            model[rowpath][self.toggle_id] = True
-
-    def toggled_cb(self, cell, path, columnid, tree):
-        self.emit("toggled", cell, path, columnid, tree)
-
-    def row_activated_cb(self, tree, path, view_column):
-        if not view_column.get_title() in self.toggle_columns:
-            self.emit("row-activated", tree.get_model(), path)
-
-    def stop_cell_fadeinout_cb(self, ctrl, cell, tree):
-        self.emit("cell-fadeinout-stopped", ctrl, cell, tree)
-
-    def set_group_number_cb(self, col, cell, model, iter):
-        if model and (model.iter_parent(iter) == None):
-            cell.cell_attr["number_of_children"] = model.iter_n_children(iter)
-        else:
-            cell.cell_attr["number_of_children"] = 0
-
-    def connect_group_selection(self, cb_func):
-        self.table_tree.get_selection().connect("changed", cb_func)
-
-"""
-A method to calculate a softened value for the colour of widget when in the
-provided state.
-
-widget: the widget whose style to use
-state: the state of the widget to use the style for
-
-Returns a string value representing the softened colour
-"""
-def soften_color(widget, state=gtk.STATE_NORMAL):
-    # this colour munging routine is heavily inspired bu gdu_util_get_mix_color()
-    # from gnome-disk-utility:
-    # http://git.gnome.org/browse/gnome-disk-utility/tree/src/gdu-gtk/gdu-gtk.c?h=gnome-3-0
-    blend = 0.7
-    style = widget.get_style()
-    color = style.text[state]
-    color.red = color.red * blend + style.base[state].red * (1.0 - blend)
-    color.green = color.green * blend + style.base[state].green * (1.0 - blend)
-    color.blue = color.blue * blend + style.base[state].blue * (1.0 - blend)
-    return color.to_string()
-
-class BaseHobButton(gtk.Button):
-    """
-    A gtk.Button subclass which follows the visual design of Hob for primary
-    action buttons
-
-    label: the text to display as the button's label
-    """
-    def __init__(self, label):
-        gtk.Button.__init__(self, label)
-        HobButton.style_button(self)
-
-    @staticmethod
-    def style_button(button):
-        style = button.get_style()
-        style = gtk.rc_get_style_by_paths(gtk.settings_get_default(), 'gtk-button', 'gtk-button', gobject.TYPE_NONE)
-
-        #FIXME button.set_flags(gtk.CAN_DEFAULT)
-        button.grab_default()
-
-#        label = "<span size='x-large'><b>%s</b></span>" % gobject.markup_escape_text(button.get_label())
-        label = button.get_label()
-        button.set_label(label)
-        #FIXME button.child.set_use_markup(True)
-
-class HobButton(BaseHobButton):
-    """
-    A gtk.Button subclass which follows the visual design of Hob for primary
-    action buttons
-
-    label: the text to display as the button's label
-    """
-    def __init__(self, label):
-        BaseHobButton.__init__(self, label)
-        HobButton.style_button(self)
-
-class HobAltButton(BaseHobButton):
-    """
-    A gtk.Button subclass which has no relief, and so is more discrete
-    """
-    def __init__(self, label):
-        BaseHobButton.__init__(self, label)
-        HobAltButton.style_button(self)
-
-    """
-    A callback for the state-changed event to ensure the text is displayed
-    differently when the widget is not sensitive
-    """
-    @staticmethod
-    def desensitise_on_state_change_cb(button, state):
-        if not button.get_property("sensitive"):
-            HobAltButton.set_text(button, False)
-        else:
-            HobAltButton.set_text(button, True)
-
-    """
-    Set the button label with an appropriate colour for the current widget state
-    """
-    @staticmethod
-    def set_text(button, sensitive=True):
-        if sensitive:
-            colour = HobColors.PALE_BLUE
-        else:
-            colour = HobColors.LIGHT_GRAY
-        button.set_label("<span size='large' color='%s'><b>%s</b></span>" % (colour, gobject.markup_escape_text(button.text)))
-        button.child.set_use_markup(True)
-
-class HobImageButton(gtk.Button):
-    """
-    A gtk.Button with an icon and two rows of text, the second of which is
-    displayed in a blended colour.
-
-    primary_text: the main button label
-    secondary_text: optional second line of text
-    icon_path: path to the icon file to display on the button
-    """
-    def __init__(self, primary_text, secondary_text="", icon_path="", hover_icon_path=""):
-        gtk.Button.__init__(self)
-        self.set_relief(gtk.RELIEF_NONE)
-
-        self.icon_path = icon_path
-        self.hover_icon_path = hover_icon_path
-
-        hbox = gtk.HBox(False, 10)
-        hbox.show()
-        self.add(hbox)
-        self.icon = gtk.Image()
-        self.icon.set_from_file(self.icon_path)
-        self.icon.set_alignment(0.5, 0.0)
-        self.icon.show()
-        if self.hover_icon_path and len(self.hover_icon_path):
-            self.connect("enter-notify-event", self.set_hover_icon_cb)
-            self.connect("leave-notify-event", self.set_icon_cb)
-        hbox.pack_start(self.icon, False, False, 0)
-        label = gtk.Label()
-        label.set_alignment(0.0, 0.5)
-        colour = soften_color(label)
-        mark = "<span size='x-large'>%s</span>\n<span size='medium' fgcolor='%s' weight='ultralight'>%s</span>" % (primary_text, colour, secondary_text)
-        label.set_markup(mark)
-        label.show()
-        hbox.pack_start(label, True, True, 0)
-
-    def set_hover_icon_cb(self, widget, event):
-        self.icon.set_from_file(self.hover_icon_path)
-
-    def set_icon_cb(self, widget, event):
-        self.icon.set_from_file(self.icon_path)
-
-class HobInfoButton(gtk.EventBox):
-    """
-    This class implements a button-like widget per the Hob visual and UX designs
-    which will display a persistent tooltip, with the contents of tip_markup, when
-    clicked.
-
-    tip_markup: the Pango Markup to be displayed in the persistent tooltip
-    """
-    def __init__(self, tip_markup, parent=None):
-        gtk.EventBox.__init__(self)
-        self.image = gtk.Image()
-        self.image.set_from_file(
-        hic.ICON_INFO_DISPLAY_FILE)
-        self.image.show()
-        self.add(self.image)
-        self.tip_markup = tip_markup
-        self.my_parent = parent
-
-        self.set_events(gtk.gdk.BUTTON_RELEASE |
-                        gtk.gdk.ENTER_NOTIFY_MASK |
-                        gtk.gdk.LEAVE_NOTIFY_MASK)
-
-        self.connect("button-release-event", self.button_release_cb)
-        self.connect("enter-notify-event", self.mouse_in_cb)
-        self.connect("leave-notify-event", self.mouse_out_cb)
-
-    """
-    When the mouse click is released emulate a button-click and show the associated
-    PersistentTooltip
-    """
-    def button_release_cb(self, widget, event):
-        from bb.ui.crumbs.hig.propertydialog import PropertyDialog
-        self.dialog = PropertyDialog(title = '',
-                    parent = self.my_parent,
-                    information = self.tip_markup,
-                    flags = gtk.DIALOG_DESTROY_WITH_PARENT)
-                        #FMXIE | gtk.DIALOG_NO_SEPARATOR
-
-        button = self.dialog.add_button("Close", gtk.RESPONSE_CANCEL)
-        HobAltButton.style_button(button)
-        button.connect("clicked", lambda w: self.dialog.destroy())
-        self.dialog.show_all()
-        self.dialog.run()
-
-    """
-    Change to the prelight image when the mouse enters the widget
-    """
-    def mouse_in_cb(self, widget, event):
-        self.image.set_from_file(hic.ICON_INFO_HOVER_FILE)
-
-    """
-    Change to the stock image when the mouse enters the widget
-    """
-    def mouse_out_cb(self, widget, event):
-        self.image.set_from_file(hic.ICON_INFO_DISPLAY_FILE)
-
-class HobIndicator(gtk.DrawingArea):
-    def __init__(self, count):
-        gtk.DrawingArea.__init__(self)
-        # Set no window for transparent background
-        self.set_has_window(False)
-        self.set_size_request(38,38)
-        # We need to pass through button clicks
-        self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
-
-        self.connect('expose-event', self.expose)
-
-        self.count = count
-        self.color = HobColors.GRAY
-
-    def expose(self, widget, event):
-        if self.count and self.count > 0:
-            ctx = widget.window.cairo_create()
-
-            x, y, w, h = self.allocation
-
-            ctx.set_operator(cairo.OPERATOR_OVER)
-            ctx.set_source_color(gtk.gdk.color_parse(self.color))
-            ctx.translate(w/2, h/2)
-            ctx.arc(x, y, min(w,h)/2 - 2, 0, 2*math.pi)
-            ctx.fill_preserve()
-
-            layout = self.create_pango_layout(str(self.count))
-            textw, texth = layout.get_pixel_size()
-            x = (w/2)-(textw/2) + x
-            y = (h/2) - (texth/2) + y
-            ctx.move_to(x, y)
-            self.window.draw_layout(self.style.light_gc[gtk.STATE_NORMAL], int(x), int(y), layout)
-
-    def set_count(self, count):
-        self.count = count
-
-    def set_active(self, active):
-        if active:
-            self.color = HobColors.DEEP_RED
-        else:
-            self.color = HobColors.GRAY
-
-class HobTabLabel(gtk.HBox):
-    def __init__(self, text, count=0):
-        gtk.HBox.__init__(self, False, 0)
-        self.indicator = HobIndicator(count)
-        self.indicator.show()
-        self.pack_end(self.indicator, False, False)
-        self.lbl = gtk.Label(text)
-        self.lbl.set_alignment(0.0, 0.5)
-        self.lbl.show()
-        self.pack_end(self.lbl, True, True, 6)
-
-    def set_count(self, count):
-        self.indicator.set_count(count)
-
-    def set_active(self, active=True):
-        self.indicator.set_active(active)
-
-class HobNotebook(gtk.Notebook):
-    def __init__(self):
-        gtk.Notebook.__init__(self)
-        self.set_property('homogeneous', True)
-
-        self.pages = []
-
-        self.search = None
-        self.search_focus = False
-        self.page_changed = False
-
-        self.connect("switch-page", self.page_changed_cb)
-
-        self.show_all()
-
-    def page_changed_cb(self, nb, page, page_num):
-        for p, lbl in enumerate(self.pages):
-            if p == page_num:
-                lbl.set_active()
-            else:
-                lbl.set_active(False)
-
-        if self.search:
-            self.page_changed = True
-            self.reset_entry(self.search, page_num)
-
-    def append_page(self, child, tab_label, tab_tooltip=None):
-        label = HobTabLabel(tab_label)
-        if tab_tooltip:
-            label.set_tooltip_text(tab_tooltip)
-        label.set_active(False)
-        self.pages.append(label)
-        gtk.Notebook.append_page(self, child, label)
-
-    def set_entry(self, names, tips):
-        self.search = gtk.Entry()
-        self.search_names = names
-        self.search_tips = tips
-        style = self.search.get_style()
-        style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.GRAY, False, False)
-        self.search.set_style(style)
-        self.search.set_text(names[0])
-        self.search.set_tooltip_text(self.search_tips[0])
-        self.search.props.has_tooltip = True
-
-        self.search.set_editable(False)
-        self.search.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, gtk.STOCK_CLEAR)
-        self.search.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
-        self.search.connect("icon-release", self.set_search_entry_clear_cb)
-        self.search.set_width_chars(30)
-        self.search.show()
-
-        self.search.connect("focus-in-event", self.set_search_entry_editable_cb)
-        self.search.connect("focus-out-event", self.set_search_entry_reset_cb)
-        self.set_action_widget(self.search, gtk.PACK_END)
-
-    def show_indicator_icon(self, title, number):
-        for child in self.pages:
-            if child.lbl.get_label() == title:
-                child.set_count(number)
-
-    def hide_indicator_icon(self, title):
-        for child in self.pages:
-            if child.lbl.get_label() == title:
-                child.set_count(0)
-
-    def set_search_entry_editable_cb(self, search, event):
-        self.search_focus = True
-        search.set_editable(True)
-        text = search.get_text()
-        if text in self.search_names:
-            search.set_text("")
-        style = self.search.get_style()
-        style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.BLACK, False, False)
-        search.set_style(style)
-
-    def set_search_entry_reset_cb(self, search, event):
-        page_num = self.get_current_page()
-        text = search.get_text()
-        if not text:
-            self.reset_entry(search, page_num)
-
-    def reset_entry(self, entry, page_num):
-        style = entry.get_style()
-        style.text[gtk.STATE_NORMAL] = self.get_colormap().alloc_color(HobColors.GRAY, False, False)
-        entry.set_style(style)
-        entry.set_text(self.search_names[page_num])
-        entry.set_tooltip_text(self.search_tips[page_num])
-        entry.set_editable(False)
-        entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
-
-    def set_search_entry_clear_cb(self, search, icon_pos, event):
-        if search.get_editable() == True:
-            search.set_text("")
-        search.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False)
-        search.grab_focus()
-
-    def set_page(self, title):
-        for child in self.pages:
-            if child.lbl.get_label() == title:
-                child.grab_focus()
-                self.set_current_page(self.pages.index(child))
-                return
-
-class HobWarpCellRendererText(gtk.CellRendererText):
-    def __init__(self, col_number):
-        gtk.CellRendererText.__init__(self)
-        self.set_property("wrap-mode", pango.WRAP_WORD_CHAR)
-        self.set_property("wrap-width", 300) # default value wrap width is 300
-        self.col_n = col_number
-
-    def do_render(self, window, widget, background_area, cell_area, expose_area, flags):
-        if widget:
-            self.props.wrap_width = self.get_resized_wrap_width(widget, widget.get_column(self.col_n))
-        return gtk.CellRendererText.do_render(self, window, widget, background_area, cell_area, expose_area, flags)
-
-    def get_resized_wrap_width(self, treeview, column):
-        otherCols = []
-        for col in treeview.get_columns():
-            if col != column:
-                otherCols.append(col)
-        adjwidth = treeview.allocation.width - sum(c.get_width() for c in otherCols)
-        adjwidth -= treeview.style_get_property("horizontal-separator") * 4
-        if self.props.wrap_width == adjwidth or adjwidth <= 0:
-                adjwidth = self.props.wrap_width
-        return adjwidth
-
-gobject.type_register(HobWarpCellRendererText)
-
-class HobIconChecker(hic):
-    def set_hob_icon_to_stock_icon(self, file_path, stock_id=""):
-        try:
-            pixbuf = gtk.gdk.pixbuf_new_from_file(file_path)
-        except Exception as e:
-            return None
-
-        if stock_id and (gtk.icon_factory_lookup_default(stock_id) == None):
-            icon_factory = gtk.IconFactory()
-            icon_factory.add_default()
-            icon_factory.add(stock_id, gtk.IconSet(pixbuf))
-            gtk.stock_add([(stock_id, '_label', 0, 0, '')])
-
-            return icon_factory.lookup(stock_id)
-
-        return None
-
-    """
-    For make hob icon consistently by request, and avoid icon view diff by system or gtk version, we use some 'hob icon' to replace the 'gtk icon'.
-    this function check the stock_id and make hob_id to replaced the gtk_id then return it or ""
-    """
-    def check_stock_icon(self, stock_name=""):
-        HOB_CHECK_STOCK_NAME = {
-            ('hic-dialog-info', 'gtk-dialog-info', 'dialog-info')           : self.ICON_INDI_INFO_FILE,
-            ('hic-ok',          'gtk-ok',           'ok')                   : self.ICON_INDI_TICK_FILE,
-            ('hic-dialog-error', 'gtk-dialog-error', 'dialog-error')        : self.ICON_INDI_ERROR_FILE,
-            ('hic-dialog-warning', 'gtk-dialog-warning', 'dialog-warning')  : self.ICON_INDI_ALERT_FILE,
-            ('hic-task-refresh', 'gtk-execute', 'execute')                  : self.ICON_INDI_REFRESH_FILE,
-        }
-        valid_stock_id = stock_name
-        if stock_name:
-            for names, path in HOB_CHECK_STOCK_NAME.iteritems():
-                if stock_name in names:
-                    valid_stock_id = names[0]
-                    if not gtk.icon_factory_lookup_default(valid_stock_id):
-                        self.set_hob_icon_to_stock_icon(path, valid_stock_id)
-
-        return valid_stock_id
-
-class HobCellRendererController(gobject.GObject):
-    (MODE_CYCLE_RUNNING, MODE_ONE_SHORT) = range(2)
-    __gsignals__ = {
-        "run-timer-stopped" : (gobject.SIGNAL_RUN_LAST,
-                                gobject.TYPE_NONE,
-                                ()),
-    }
-    def __init__(self, runningmode=MODE_CYCLE_RUNNING, is_draw_row=False):
-        gobject.GObject.__init__(self)
-        self.timeout_id = None
-        self.current_angle_pos = 0.0
-        self.step_angle = 0.0
-        self.tree_headers_height = 0
-        self.running_cell_areas = []
-        self.running_mode = runningmode
-        self.is_queue_draw_row_area = is_draw_row
-        self.force_stop_enable = False
-
-    def is_active(self):
-        if self.timeout_id:
-            return True
-        else:
-            return False
-
-    def reset_run(self):
-        self.force_stop()
-        self.running_cell_areas = []
-        self.current_angle_pos = 0.0
-        self.step_angle = 0.0
-
-    ''' time_iterval: (1~1000)ms, which will be as the basic interval count for timer
-        init_usrdata: the current data which related the progress-bar will be at
-        min_usrdata: the range of min of user data
-        max_usrdata: the range of max of user data
-        step: each step which you want to progress
-        Note: the init_usrdata should in the range of from min to max, and max should > min
-             step should < (max - min)
-    '''
-    def start_run(self, time_iterval, init_usrdata, min_usrdata, max_usrdata, step, tree):
-        if (not time_iterval) or (not max_usrdata):
-            return
-        usr_range = (max_usrdata - min_usrdata) * 1.0
-        self.current_angle_pos = (init_usrdata * 1.0) / usr_range
-        self.step_angle = (step * 1) / usr_range
-        self.timeout_id = gobject.timeout_add(int(time_iterval),
-        self.make_image_on_progressing_cb, tree)
-        self.tree_headers_height = self.get_treeview_headers_height(tree)
-        self.force_stop_enable = False
-
-    def force_stop(self):
-        self.emit("run-timer-stopped")
-        self.force_stop_enable = True
-        if self.timeout_id:
-            if gobject.source_remove(self.timeout_id):
-                self.timeout_id = None
-
-    def on_draw_pixbuf_cb(self, pixbuf, cr, x, y, img_width, img_height, do_refresh=True):
-        if pixbuf:
-            r = max(img_width/2, img_height/2)
-            cr.translate(x + r, y + r)
-            if do_refresh:
-                cr.rotate(2 * math.pi * self.current_angle_pos)
-
-            cr.set_source_pixbuf(pixbuf, -img_width/2, -img_height/2)
-            cr.paint()
-
-    def on_draw_fadeinout_cb(self, cr, color, x, y, width, height, do_fadeout=True):
-        if do_fadeout:
-            alpha = self.current_angle_pos * 0.8
-        else:
-            alpha = (1.0 - self.current_angle_pos) * 0.8
-
-        cr.set_source_rgba(color.red, color.green, color.blue, alpha)
-        cr.rectangle(x, y, width, height)
-        cr.fill()
-
-    def get_treeview_headers_height(self, tree):
-        if tree and (tree.get_property("headers-visible") == True):
-            height = tree.get_allocation().height - tree.get_bin_window().get_size()[1]
-            return height
-
-        return 0
-
-    def make_image_on_progressing_cb(self, tree):
-        self.current_angle_pos += self.step_angle
-        if self.running_mode == self.MODE_CYCLE_RUNNING:
-            if (self.current_angle_pos >= 1):
-                self.current_angle_pos = 0
-        else:
-            if self.current_angle_pos > 1:
-                self.force_stop()
-                return False
-
-        if self.is_queue_draw_row_area:
-            for path in self.running_cell_areas:
-                rect = tree.get_cell_area(path, tree.get_column(0))
-                row_x, _, row_width, _ = tree.get_visible_rect()
-                tree.queue_draw_area(row_x, rect.y + self.tree_headers_height, row_width, rect.height)
-        else:
-            for rect in self.running_cell_areas:
-                tree.queue_draw_area(rect.x, rect.y + self.tree_headers_height, rect.width, rect.height)
-
-        return (not self.force_stop_enable)
-
-    def append_running_cell_area(self, cell_area):
-        if cell_area and (cell_area not in self.running_cell_areas):
-            self.running_cell_areas.append(cell_area)
-
-    def remove_running_cell_area(self, cell_area):
-        if cell_area in self.running_cell_areas:
-            self.running_cell_areas.remove(cell_area)
-        if not self.running_cell_areas:
-            self.reset_run()
-
-gobject.type_register(HobCellRendererController)
-
-class HobCellRendererPixbuf(gtk.CellRendererPixbuf):
-    def __init__(self):
-        gtk.CellRendererPixbuf.__init__(self)
-        self.control = HobCellRendererController()
-        # add icon checker for make the gtk-icon transfer to hob-icon
-        self.checker = HobIconChecker()
-        self.set_property("stock-size", gtk.ICON_SIZE_DND)
-
-    def get_pixbuf_from_stock_icon(self, widget, stock_id="", size=gtk.ICON_SIZE_DIALOG):
-        if widget and stock_id and gtk.icon_factory_lookup_default(stock_id):
-            return widget.render_icon(stock_id, size)
-
-        return None
-
-    def set_icon_name_to_id(self, new_name):
-        if new_name and type(new_name) == str:
-            # check the name is need to transfer to hob icon or not
-            name = self.checker.check_stock_icon(new_name)
-            if name.startswith("hic") or name.startswith("gtk"):
-                stock_id = name
-            else:
-                stock_id = 'gtk-' + name
-
-        return stock_id
-
-    ''' render cell exactly, "icon-name" is priority
-        if use the 'hic-task-refresh' will make the pix animation
-        if 'pix' will change the pixbuf for it from the pixbuf or image.
-    '''
-    def do_render(self, window, tree, background_area,cell_area, expose_area, flags):
-        if (not self.control) or (not tree):
-            return
-
-        x, y, w, h = self.on_get_size(tree, cell_area)
-        x += cell_area.x
-        y += cell_area.y
-        w -= 2 * self.get_property("xpad")
-        h -= 2 * self.get_property("ypad")
-
-        stock_id = ""
-        if self.props.icon_name:
-            stock_id = self.set_icon_name_to_id(self.props.icon_name)
-        elif self.props.stock_id:
-            stock_id = self.props.stock_id
-        elif self.props.pixbuf:
-            pix = self.props.pixbuf
-        else:
-            return
-
-        if stock_id:
-            pix = self.get_pixbuf_from_stock_icon(tree, stock_id, self.props.stock_size)
-        if stock_id == 'hic-task-refresh':
-            self.control.append_running_cell_area(cell_area)
-            if self.control.is_active():
-                self.control.on_draw_pixbuf_cb(pix, window.cairo_create(), x, y, w, h, True)
-            else:
-                self.control.start_run(200, 0, 0, 1000, 150, tree)
-        else:
-            self.control.remove_running_cell_area(cell_area)
-            self.control.on_draw_pixbuf_cb(pix, window.cairo_create(), x, y, w, h, False)
-
-    def on_get_size(self, widget, cell_area):
-        if self.props.icon_name or self.props.pixbuf or self.props.stock_id:
-            w, h = gtk.icon_size_lookup(self.props.stock_size)
-            calc_width = self.get_property("xpad") * 2 + w
-            calc_height = self.get_property("ypad") * 2 + h
-            x_offset = 0
-            y_offset = 0
-            if cell_area and w > 0 and h > 0:
-                x_offset = self.get_property("xalign") * (cell_area.width - calc_width - self.get_property("xpad"))
-                y_offset = self.get_property("yalign") * (cell_area.height - calc_height - self.get_property("ypad"))
-
-            return x_offset, y_offset, w, h
-
-        return 0, 0, 0, 0
-
-gobject.type_register(HobCellRendererPixbuf)
-
-class HobCellRendererToggle(gtk.CellRendererToggle):
-    def __init__(self):
-        gtk.CellRendererToggle.__init__(self)
-        self.ctrl = HobCellRendererController(is_draw_row=True)
-        self.ctrl.running_mode = self.ctrl.MODE_ONE_SHORT
-        self.cell_attr = {"fadeout": False, "number_of_children": 0}
-
-    def do_render(self, window, widget, background_area, cell_area, expose_area, flags):
-        if (not self.ctrl) or (not widget):
-            return
-
-        if flags & gtk.CELL_RENDERER_SELECTED:
-            state = gtk.STATE_SELECTED
-        else:
-            state = gtk.STATE_NORMAL
-
-        if self.ctrl.is_active():
-            path = widget.get_path_at_pos(cell_area.x + cell_area.width/2, cell_area.y + cell_area.height/2)
-            # sometimes the parameters of cell_area will be a negative number,such as pull up down the scroll bar
-            # it's over the tree container range, so the path will be bad
-            if not path: return
-            path = path[0]
-            if path in self.ctrl.running_cell_areas:
-                cr = window.cairo_create()
-                color = widget.get_style().base[state]
-
-                row_x, _, row_width, _ = widget.get_visible_rect()
-                border_y = self.get_property("ypad")
-                self.ctrl.on_draw_fadeinout_cb(cr, color, row_x, cell_area.y - border_y, row_width, \
-                                               cell_area.height + border_y * 2, self.cell_attr["fadeout"])
-        # draw number of a group
-        if self.cell_attr["number_of_children"]:
-            text = "%d pkg" % self.cell_attr["number_of_children"]
-            pangolayout = widget.create_pango_layout(text)
-            textw, texth = pangolayout.get_pixel_size()
-            x = cell_area.x + (cell_area.width/2) - (textw/2)
-            y = cell_area.y + (cell_area.height/2) - (texth/2)
-
-            widget.style.paint_layout(window, state, True, cell_area, widget, "checkbox", x, y, pangolayout)
-        else:
-            return gtk.CellRendererToggle.do_render(self, window, widget, background_area, cell_area, expose_area, flags)
-
-    '''delay: normally delay time is 1000ms
-       cell_list: whilch cells need to be render
-    '''
-    def fadeout(self, tree, delay, cell_list=None):
-        if (delay < 200) or (not tree):
-            return
-        self.cell_attr["fadeout"] = True
-        self.ctrl.running_cell_areas = cell_list
-        self.ctrl.start_run(200, 0, 0, delay, (delay * 200 / 1000), tree)
-
-    def connect_render_state_changed(self, func, usrdata=None):
-        if not func:
-            return
-        if usrdata:
-            self.ctrl.connect("run-timer-stopped", func, self, usrdata)
-        else:
-            self.ctrl.connect("run-timer-stopped", func, self)
-
-gobject.type_register(HobCellRendererToggle)
diff --git a/lib/bb/ui/crumbs/persistenttooltip.py b/lib/bb/ui/crumbs/persistenttooltip.py
deleted file mode 100644
index 927c194..0000000
--- a/lib/bb/ui/crumbs/persistenttooltip.py
+++ /dev/null
@@ -1,186 +0,0 @@
-#
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2012   Intel Corporation
-#
-# Authored by Joshua Lock <josh@linux.intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-import gobject
-import gtk
-try:
-    import gconf
-except:
-    pass
-
-class PersistentTooltip(gtk.Window):
-    """
-    A tooltip which persists once shown until the user dismisses it with the Esc
-    key or by clicking the close button.
-
-    # FIXME: the PersistentTooltip should be disabled when the user clicks anywhere off
-    # it. We can't do this with focus-out-event becuase modal ensures we have focus?
-
-    markup: some Pango text markup to display in the tooltip
-    """
-    def __init__(self, markup, parent_win=None):
-        gtk.Window.__init__(self, gtk.WINDOW_POPUP)
-
-        # Inherit the system theme for a tooltip
-        style = gtk.rc_get_style_by_paths(gtk.settings_get_default(),
-                        'gtk-tooltip', 'gtk-tooltip', gobject.TYPE_NONE)
-        self.set_style(style)
-
-        # The placement of the close button on the tip should reflect how the
-        # window manager of the users system places close buttons. Try to read
-        # the metacity gconf key to determine whether the close button is on the
-        # left or the right.
-        # In the case that we can't determine the users configuration we default
-        # to close buttons being on the right.
-        __button_right = True
-        try:
-            client = gconf.client_get_default()
-            order = client.get_string("/apps/metacity/general/button_layout")
-            if order and order.endswith(":"):
-                __button_right = False
-        except NameError:
-            pass
-
-        # We need to ensure we're only shown once
-        self.shown = False
-
-        # We don't want any WM decorations
-        self.set_decorated(False)
-        # We don't want to show in the taskbar or window switcher
-        self.set_skip_pager_hint(True)
-        self.set_skip_taskbar_hint(True)
-        # We must be modal to ensure we grab focus when presented from a gtk.Dialog
-        self.set_modal(True)
-
-        self.set_border_width(0)
-        self.set_position(gtk.WIN_POS_MOUSE)
-        self.set_opacity(0.95)
-
-        # Ensure a reasonable minimum size
-        self.set_geometry_hints(self, 100, 50)
-
-        # Set this window as a transient window for parent(main window)
-        if parent_win:
-            self.set_transient_for(parent_win)
-            self.set_destroy_with_parent(True)
-        # Draw our label and close buttons
-        hbox = gtk.HBox(False, 0)
-        hbox.show()
-        self.add(hbox)
-
-        img = gtk.Image()
-        img.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_BUTTON)
-
-        self.button = gtk.Button()
-        self.button.set_image(img)
-        self.button.connect("clicked", self._dismiss_cb)
-        self.button.set_flags(gtk.CAN_DEFAULT)
-        self.button.grab_focus()
-        self.button.show()
-        vbox = gtk.VBox(False, 0)
-        vbox.show()
-        vbox.pack_start(self.button, False, False, 0)
-        if __button_right:
-            hbox.pack_end(vbox, True, True, 0)
-        else:
-            hbox.pack_start(vbox, True, True, 0)
-
-        self.set_default(self.button)
-
-        bin = gtk.HBox(True, 6)
-        bin.set_border_width(6)
-        bin.show()
-        self.label = gtk.Label()
-        self.label.set_line_wrap(True)
-        # We want to match the colours of the normal tooltips, as dictated by
-        # the users gtk+-2.0 theme, wherever possible - on some systems this
-        # requires explicitly setting a fg_color for the label which matches the
-        # tooltip_fg_color
-        settings = gtk.settings_get_default()
-        colours = settings.get_property('gtk-color-scheme').split('\n')
-        # remove any empty lines, there's likely to be a trailing one after
-        # calling split on a dictionary-like string
-        colours = filter(None, colours)
-        for col in colours:
-            item, val = col.split(': ')
-            if item == 'tooltip_fg_color':
-                style = self.label.get_style()
-                style.fg[gtk.STATE_NORMAL] = gtk.gdk.color_parse(val)
-                self.label.set_style(style)
-                break # we only care for the tooltip_fg_color
-
-        self.label.set_markup(markup)
-        self.label.show()
-        bin.add(self.label)
-        hbox.pack_end(bin, True, True, 6)
-
-        # add the original URL display for user reference
-        if 'a href' in markup:
-            hbox.set_tooltip_text(self.get_markup_url(markup))
-        hbox.show()
-
-        self.connect("key-press-event", self._catch_esc_cb)
-
-    """
-    Callback when the PersistentTooltip's close button is clicked.
-    Hides the PersistentTooltip.
-    """
-    def _dismiss_cb(self, button):
-        self.hide()
-        return True
-
-    """
-    Callback when the Esc key is detected. Hides the PersistentTooltip.
-    """
-    def _catch_esc_cb(self, widget, event):
-        keyname = gtk.gdk.keyval_name(event.keyval)
-        if keyname == "Escape":
-            self.hide()
-        return True
-
-    """
-    Called to present the PersistentTooltip.
-    Overrides the superclasses show() method to include state tracking.
-    """
-    def show(self):
-        if not self.shown:
-            self.shown = True
-            gtk.Window.show(self)
-
-    """
-    Called to hide the PersistentTooltip.
-    Overrides the superclasses hide() method to include state tracking.
-    """
-    def hide(self):
-        self.shown = False
-        gtk.Window.hide(self)
-
-    """
-    Called to get the hyperlink URL from markup text.
-    """
-    def get_markup_url(self, markup):
-        url = "http:"
-        if markup and type(markup) == str:
-            s = markup
-            if 'http:' in s:
-                import re
-                url = re.search('(http:[^,\\ "]+)', s).group(0)
-
-        return url
diff --git a/lib/bb/ui/crumbs/progress.py b/lib/bb/ui/crumbs/progress.py
deleted file mode 100644
index 1d28a11..0000000
--- a/lib/bb/ui/crumbs/progress.py
+++ /dev/null
@@ -1,23 +0,0 @@
-import gtk
-
-class ProgressBar(gtk.Dialog):
-    def __init__(self, parent):
-
-        gtk.Dialog.__init__(self, flags=(gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT))
-        self.set_title("Parsing metadata, please wait...")
-        self.set_default_size(500, 0)
-        self.set_transient_for(parent)
-        self.progress = gtk.ProgressBar()
-        self.vbox.pack_start(self.progress)
-        self.show_all()
-
-    def set_text(self, msg):
-        self.progress.set_text(msg)
-
-    def update(self, x, y):
-        self.progress.set_fraction(float(x)/float(y))
-        self.progress.set_text("%2d %%" % (x*100/y))
-
-    def pulse(self):
-        self.progress.set_text("Loading...")
-        self.progress.pulse()
diff --git a/lib/bb/ui/crumbs/progressbar.py b/lib/bb/ui/crumbs/progressbar.py
deleted file mode 100644
index ba3c7a0..0000000
--- a/lib/bb/ui/crumbs/progressbar.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2011        Intel Corporation
-#
-# Authored by Shane Wang <shane.wang@intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-import gtk
-from bb.ui.crumbs.hobcolor import HobColors
-
-class HobProgressBar (gtk.ProgressBar):
-    def __init__(self):
-        gtk.ProgressBar.__init__(self)
-        self.set_rcstyle(True)
-        self.percentage = 0
-
-    def set_rcstyle(self, status):
-        rcstyle = gtk.RcStyle()
-#FIXME
-#        rcstyle.fg[2] = gtk.gdk.Color(HobColors.BLACK)
-#        if status == "stop":
-#            rcstyle.bg[3] = gtk.gdk.Color(HobColors.WARNING)
-#        elif status == "fail":
-#            rcstyle.bg[3] = gtk.gdk.Color(HobColors.ERROR)
-#        else:
-#            rcstyle.bg[3] = gtk.gdk.Color(HobColors.RUNNING)
-        self.modify_style(rcstyle)
-
-    def set_title(self, text=None):
-        if not text:
-            text = ""
-        text += " %.0f%%" % self.percentage
-        self.set_text(text)
-
-    def set_stop_title(self, text=None):
-        if not text:
-            text = ""
-        self.set_text(text)
-
-    def reset(self):
-        self.set_fraction(0)
-        self.set_text("")
-        self.set_rcstyle(True)
-        self.percentage = 0
-
-    def update(self, fraction):
-        self.percentage = int(fraction * 100)
-        self.set_fraction(fraction)
diff --git a/lib/bb/ui/crumbs/puccho.glade b/lib/bb/ui/crumbs/puccho.glade
deleted file mode 100644
index d7553a6..0000000
--- a/lib/bb/ui/crumbs/puccho.glade
+++ /dev/null
@@ -1,606 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.5 on Mon Nov 10 12:24:12 2008 -->
-<glade-interface>
-  <widget class="GtkDialog" id="build_dialog">
-    <property name="title" translatable="yes">Start a build</property>
-    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
-    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
-    <property name="has_separator">False</property>
-    <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox1">
-        <property name="visible">True</property>
-        <property name="spacing">2</property>
-        <child>
-          <widget class="GtkTable" id="build_table">
-            <property name="visible">True</property>
-            <property name="border_width">6</property>
-            <property name="n_rows">7</property>
-            <property name="n_columns">3</property>
-            <property name="column_spacing">5</property>
-            <property name="row_spacing">6</property>
-            <child>
-              <widget class="GtkAlignment" id="status_alignment">
-                <property name="visible">True</property>
-                <property name="left_padding">12</property>
-                <child>
-                  <widget class="GtkHBox" id="status_hbox">
-                    <property name="spacing">6</property>
-                    <child>
-                      <widget class="GtkImage" id="status_image">
-                        <property name="visible">True</property>
-                        <property name="no_show_all">True</property>
-                        <property name="xalign">0</property>
-                        <property name="stock">gtk-dialog-error</property>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkLabel" id="status_label">
-                        <property name="visible">True</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">If you see this text something is wrong...</property>
-                        <property name="use_markup">True</property>
-                        <property name="use_underline">True</property>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-              <packing>
-                <property name="right_attach">3</property>
-                <property name="top_attach">2</property>
-                <property name="bottom_attach">3</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label2">
-                <property name="visible">True</property>
-                <property name="xalign">0</property>
-                <property name="label" translatable="yes">&lt;b&gt;Build configuration&lt;/b&gt;</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="right_attach">3</property>
-                <property name="top_attach">3</property>
-                <property name="bottom_attach">4</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkComboBox" id="image_combo">
-                <property name="visible">True</property>
-                <property name="sensitive">False</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">6</property>
-                <property name="bottom_attach">7</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="image_label">
-                <property name="visible">True</property>
-                <property name="sensitive">False</property>
-                <property name="xalign">0</property>
-                <property name="xpad">12</property>
-                <property name="label" translatable="yes">Image:</property>
-              </widget>
-              <packing>
-                <property name="top_attach">6</property>
-                <property name="bottom_attach">7</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkComboBox" id="distribution_combo">
-                <property name="visible">True</property>
-                <property name="sensitive">False</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">5</property>
-                <property name="bottom_attach">6</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="distribution_label">
-                <property name="visible">True</property>
-                <property name="sensitive">False</property>
-                <property name="xalign">0</property>
-                <property name="xpad">12</property>
-                <property name="label" translatable="yes">Distribution:</property>
-              </widget>
-              <packing>
-                <property name="top_attach">5</property>
-                <property name="bottom_attach">6</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkComboBox" id="machine_combo">
-                <property name="visible">True</property>
-                <property name="sensitive">False</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">4</property>
-                <property name="bottom_attach">5</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="machine_label">
-                <property name="visible">True</property>
-                <property name="sensitive">False</property>
-                <property name="xalign">0</property>
-                <property name="xpad">12</property>
-                <property name="label" translatable="yes">Machine:</property>
-              </widget>
-              <packing>
-                <property name="top_attach">4</property>
-                <property name="bottom_attach">5</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="refresh_button">
-                <property name="visible">True</property>
-                <property name="sensitive">False</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="label" translatable="yes">gtk-refresh</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkEntry" id="location_entry">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="width_chars">32</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label3">
-                <property name="visible">True</property>
-                <property name="xalign">0</property>
-                <property name="xpad">12</property>
-                <property name="label" translatable="yes">Location:</property>
-              </widget>
-              <packing>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label1">
-                <property name="visible">True</property>
-                <property name="xalign">0</property>
-                <property name="label" translatable="yes">&lt;b&gt;Repository&lt;/b&gt;</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="right_attach">3</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkAlignment" id="alignment1">
-                <property name="visible">True</property>
-                <child>
-                  <placeholder/>
-                </child>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">4</property>
-                <property name="bottom_attach">5</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkAlignment" id="alignment2">
-                <property name="visible">True</property>
-                <child>
-                  <placeholder/>
-                </child>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">5</property>
-                <property name="bottom_attach">6</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkAlignment" id="alignment3">
-                <property name="visible">True</property>
-                <child>
-                  <placeholder/>
-                </child>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">6</property>
-                <property name="bottom_attach">7</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area1">
-            <property name="visible">True</property>
-            <property name="layout_style">GTK_BUTTONBOX_END</property>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
-            <child>
-              <placeholder/>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">GTK_PACK_END</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="GtkDialog" id="dialog2">
-    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
-    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
-    <property name="has_separator">False</property>
-    <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox2">
-        <property name="visible">True</property>
-        <property name="spacing">2</property>
-        <child>
-          <widget class="GtkTable" id="table2">
-            <property name="visible">True</property>
-            <property name="border_width">6</property>
-            <property name="n_rows">7</property>
-            <property name="n_columns">3</property>
-            <property name="column_spacing">6</property>
-            <property name="row_spacing">6</property>
-            <child>
-              <widget class="GtkLabel" id="label7">
-                <property name="visible">True</property>
-                <property name="xalign">0</property>
-                <property name="label" translatable="yes">&lt;b&gt;Repositories&lt;/b&gt;</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="right_attach">3</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkAlignment" id="alignment4">
-                <property name="visible">True</property>
-                <property name="xalign">0</property>
-                <property name="left_padding">12</property>
-                <child>
-                  <widget class="GtkScrolledWindow" id="scrolledwindow1">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <child>
-                      <widget class="GtkTreeView" id="treeview1">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="headers_clickable">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-              <packing>
-                <property name="right_attach">3</property>
-                <property name="top_attach">2</property>
-                <property name="bottom_attach">3</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkEntry" id="entry1">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label9">
-                <property name="visible">True</property>
-                <property name="xalign">0</property>
-                <property name="label" translatable="yes">&lt;b&gt;Additional packages&lt;/b&gt;</property>
-                <property name="use_markup">True</property>
-              </widget>
-              <packing>
-                <property name="right_attach">3</property>
-                <property name="top_attach">4</property>
-                <property name="bottom_attach">5</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkAlignment" id="alignment6">
-                <property name="visible">True</property>
-                <property name="xalign">0</property>
-                <property name="xscale">0</property>
-                <child>
-                  <widget class="GtkLabel" id="label8">
-                    <property name="visible">True</property>
-                    <property name="xalign">0</property>
-                    <property name="yalign">0</property>
-                    <property name="xpad">12</property>
-                    <property name="label" translatable="yes">Location: </property>
-                  </widget>
-                </child>
-              </widget>
-              <packing>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkAlignment" id="alignment7">
-                <property name="visible">True</property>
-                <property name="xalign">1</property>
-                <property name="xscale">0</property>
-                <child>
-                  <widget class="GtkHButtonBox" id="hbuttonbox1">
-                    <property name="visible">True</property>
-                    <property name="spacing">5</property>
-                    <child>
-                      <widget class="GtkButton" id="button7">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">True</property>
-                        <property name="label" translatable="yes">gtk-remove</property>
-                        <property name="use_stock">True</property>
-                        <property name="response_id">0</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkButton" id="button6">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">True</property>
-                        <property name="label" translatable="yes">gtk-edit</property>
-                        <property name="use_stock">True</property>
-                        <property name="response_id">0</property>
-                      </widget>
-                      <packing>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkButton" id="button5">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">True</property>
-                        <property name="label" translatable="yes">gtk-add</property>
-                        <property name="use_stock">True</property>
-                        <property name="response_id">0</property>
-                      </widget>
-                      <packing>
-                        <property name="position">2</property>
-                      </packing>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">3</property>
-                <property name="bottom_attach">4</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkAlignment" id="alignment5">
-                <property name="visible">True</property>
-                <child>
-                  <placeholder/>
-                </child>
-              </widget>
-              <packing>
-                <property name="top_attach">3</property>
-                <property name="bottom_attach">4</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label10">
-                <property name="visible">True</property>
-                <property name="xalign">0</property>
-                <property name="yalign">0</property>
-                <property name="xpad">12</property>
-                <property name="label" translatable="yes">Search:</property>
-              </widget>
-              <packing>
-                <property name="top_attach">5</property>
-                <property name="bottom_attach">6</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkEntry" id="entry2">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">5</property>
-                <property name="bottom_attach">6</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkAlignment" id="alignment8">
-                <property name="visible">True</property>
-                <property name="xalign">0</property>
-                <property name="left_padding">12</property>
-                <child>
-                  <widget class="GtkScrolledWindow" id="scrolledwindow2">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                    <child>
-                      <widget class="GtkTreeView" id="treeview2">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="headers_clickable">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-              <packing>
-                <property name="right_attach">3</property>
-                <property name="top_attach">6</property>
-                <property name="bottom_attach">7</property>
-                <property name="y_options"></property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area2">
-            <property name="visible">True</property>
-            <property name="layout_style">GTK_BUTTONBOX_END</property>
-            <child>
-              <widget class="GtkButton" id="button4">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="label" translatable="yes">gtk-close</property>
-                <property name="use_stock">True</property>
-                <property name="response_id">0</property>
-              </widget>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">GTK_PACK_END</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="GtkWindow" id="main_window">
-    <child>
-      <widget class="GtkVBox" id="main_window_vbox">
-        <property name="visible">True</property>
-        <child>
-          <widget class="GtkToolbar" id="main_toolbar">
-            <property name="visible">True</property>
-            <child>
-              <widget class="GtkToolButton" id="main_toolbutton_build">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Build</property>
-                <property name="stock_id">gtk-execute</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="GtkVPaned" id="vpaned1">
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <child>
-              <widget class="GtkScrolledWindow" id="results_scrolledwindow">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                <child>
-                  <placeholder/>
-                </child>
-              </widget>
-              <packing>
-                <property name="resize">False</property>
-                <property name="shrink">True</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkScrolledWindow" id="progress_scrolledwindow">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-                <child>
-                  <placeholder/>
-                </child>
-              </widget>
-              <packing>
-                <property name="resize">True</property>
-                <property name="shrink">True</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-</glade-interface>
diff --git a/lib/bb/ui/crumbs/runningbuild.py b/lib/bb/ui/crumbs/runningbuild.py
deleted file mode 100644
index 9b695ac..0000000
--- a/lib/bb/ui/crumbs/runningbuild.py
+++ /dev/null
@@ -1,551 +0,0 @@
-
-#
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2008        Intel Corporation
-#
-# Authored by Rob Bradford <rob@linux.intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-import gtk
-import gobject
-import logging
-import time
-import urllib.request, urllib.parse, urllib.error
-import urllib.request, urllib.error, urllib.parse
-import pango
-from bb.ui.crumbs.hobcolor import HobColors
-from bb.ui.crumbs.hobwidget import HobWarpCellRendererText, HobCellRendererPixbuf
-
-class RunningBuildModel (gtk.TreeStore):
-    (COL_LOG, COL_PACKAGE, COL_TASK, COL_MESSAGE, COL_ICON, COL_COLOR, COL_NUM_ACTIVE) = list(range(7))
-
-    def __init__ (self):
-        gtk.TreeStore.__init__ (self,
-                                gobject.TYPE_STRING,
-                                gobject.TYPE_STRING,
-                                gobject.TYPE_STRING,
-                                gobject.TYPE_STRING,
-                                gobject.TYPE_STRING,
-                                gobject.TYPE_STRING,
-                                gobject.TYPE_INT)
-
-    def failure_model_filter(self, model, it):
-        color = model.get(it, self.COL_COLOR)[0]
-        if not color:
-            return False
-        if color == HobColors.ERROR or color == HobColors.WARNING:
-            return True
-        return False
-
-    def failure_model(self):
-        model = self.filter_new()
-        model.set_visible_func(self.failure_model_filter)
-        return model
-
-    def foreach_cell_func(self, model, path, iter, usr_data=None):
-        if model.get_value(iter, self.COL_ICON) == "gtk-execute":
-            model.set(iter, self.COL_ICON, "")
-
-    def close_task_refresh(self):
-        self.foreach(self.foreach_cell_func, None)
-
-class RunningBuild (gobject.GObject):
-    __gsignals__ = {
-          'build-started'   :  (gobject.SIGNAL_RUN_LAST,
-                                gobject.TYPE_NONE,
-                               ()),
-          'build-succeeded' :  (gobject.SIGNAL_RUN_LAST,
-                                gobject.TYPE_NONE,
-                               ()),
-          'build-failed'    :  (gobject.SIGNAL_RUN_LAST,
-                                gobject.TYPE_NONE,
-                               ()),
-          'build-complete'  :  (gobject.SIGNAL_RUN_LAST,
-                                gobject.TYPE_NONE,
-                               ()),
-          'build-aborted'   :  (gobject.SIGNAL_RUN_LAST,
-                                gobject.TYPE_NONE,
-                               ()),
-          'task-started'    :  (gobject.SIGNAL_RUN_LAST,
-                                gobject.TYPE_NONE,
-                               (gobject.TYPE_PYOBJECT,)),
-          'log-error'       :  (gobject.SIGNAL_RUN_LAST,
-                                gobject.TYPE_NONE,
-                               ()),
-          'log-warning'     :  (gobject.SIGNAL_RUN_LAST,
-                                gobject.TYPE_NONE,
-                               ()),
-          'disk-full'       :  (gobject.SIGNAL_RUN_LAST,
-                                gobject.TYPE_NONE,
-                               ()),
-          'no-provider'     :  (gobject.SIGNAL_RUN_LAST,
-                                gobject.TYPE_NONE,
-                               (gobject.TYPE_PYOBJECT,)),
-          'log'             :  (gobject.SIGNAL_RUN_LAST,
-                                gobject.TYPE_NONE,
-                               (gobject.TYPE_STRING, gobject.TYPE_PYOBJECT,)),
-          }
-    pids_to_task = {}
-    tasks_to_iter = {}
-
-    def __init__ (self, sequential=False):
-        gobject.GObject.__init__ (self)
-        self.model = RunningBuildModel()
-        self.sequential = sequential
-        self.buildaborted = False
-
-    def reset (self):
-        self.pids_to_task.clear()
-        self.tasks_to_iter.clear()
-        self.model.clear()
-
-    def handle_event (self, event, pbar=None):
-        # Handle an event from the event queue, this may result in updating
-        # the model and thus the UI. Or it may be to tell us that the build
-        # has finished successfully (or not, as the case may be.)
-
-        parent = None
-        pid = 0
-        package = None
-        task = None
-
-        # If we have a pid attached to this message/event try and get the
-        # (package, task) pair for it. If we get that then get the parent iter
-        # for the message.
-        if hasattr(event, 'pid'):
-            pid = event.pid
-        if hasattr(event, 'process'):
-            pid = event.process
-
-        if pid and pid in self.pids_to_task:
-            (package, task) = self.pids_to_task[pid]
-            parent = self.tasks_to_iter[(package, task)]
-
-        if(isinstance(event, logging.LogRecord)):
-            if event.taskpid == 0 or event.levelno > logging.INFO:
-                self.emit("log", "handle", event)
-            # FIXME: this is a hack! More info in Yocto #1433
-            # http://bugzilla.pokylinux.org/show_bug.cgi?id=1433, temporarily
-            # mask the error message as it's not informative for the user.
-            if event.msg.startswith("Execution of event handler 'run_buildstats' failed"):
-                return
-
-            if (event.levelno < logging.INFO or
-                event.msg.startswith("Running task")):
-                return # don't add these to the list
-
-            if event.levelno >= logging.ERROR:
-                icon = "dialog-error"
-                color = HobColors.ERROR
-                self.emit("log-error")
-            elif event.levelno >= logging.WARNING:
-                icon = "dialog-warning"
-                color = HobColors.WARNING
-                self.emit("log-warning")
-            else:
-                icon = None
-                color = HobColors.OK
-
-            # if we know which package we belong to, we'll append onto its list.
-            # otherwise, we'll jump to the top of the master list
-            if self.sequential or not parent:
-                tree_add = self.model.append
-            else:
-                tree_add = self.model.prepend
-            tree_add(parent,
-                     (None,
-                      package,
-                      task,
-                      event.getMessage(),
-                      icon,
-                      color,
-                      0))
-
-            # if there are warnings while processing a package
-            #  (parent), mark the task with warning color;
-            # in case there are errors, the updates will be
-            #  handled on TaskFailed.
-            if color == HobColors.WARNING and parent:
-                self.model.set(parent, self.model.COL_COLOR, color)
-                if task: #then we have a parent (package), and update it's color
-                    self.model.set(self.tasks_to_iter[(package, None)], self.model.COL_COLOR, color)
-
-        elif isinstance(event, bb.build.TaskStarted):
-            (package, task) = (event._package, event._task)
-
-            # Save out this PID.
-            self.pids_to_task[pid] = (package, task)
-
-            # Check if we already have this package in our model. If so then
-            # that can be the parent for the task. Otherwise we create a new
-            # top level for the package.
-            if ((package, None) in self.tasks_to_iter):
-                parent = self.tasks_to_iter[(package, None)]
-            else:
-                if self.sequential:
-                    add = self.model.append
-                else:
-                    add = self.model.prepend
-                parent = add(None, (None,
-                                    package,
-                                    None,
-                                    "Package: %s" % (package),
-                                    None,
-                                    HobColors.OK,
-                                    0))
-                self.tasks_to_iter[(package, None)] = parent
-
-            # Because this parent package now has an active child mark it as
-            # such.
-            self.model.set(parent, self.model.COL_ICON, "gtk-execute")
-            parent_color = self.model.get(parent, self.model.COL_COLOR)[0]
-            if parent_color != HobColors.ERROR and parent_color != HobColors.WARNING:
-                self.model.set(parent, self.model.COL_COLOR, HobColors.RUNNING)
-
-            # Add an entry in the model for this task
-            i = self.model.append (parent, (None,
-                                            package,
-                                            task,
-                                            "Task: %s" % (task),
-                                            "gtk-execute",
-                                            HobColors.RUNNING,
-                                            0))
-
-            # update the parent's active task count
-            num_active = self.model.get(parent, self.model.COL_NUM_ACTIVE)[0] + 1
-            self.model.set(parent, self.model.COL_NUM_ACTIVE, num_active)
-
-            # Save out the iter so that we can find it when we have a message
-            # that we need to attach to a task.
-            self.tasks_to_iter[(package, task)] = i
-
-        elif isinstance(event, bb.build.TaskBase):
-            self.emit("log", "info", event._message)
-            current = self.tasks_to_iter[(package, task)]
-            parent = self.tasks_to_iter[(package, None)]
-
-            # remove this task from the parent's active count
-            num_active = self.model.get(parent, self.model.COL_NUM_ACTIVE)[0] - 1
-            self.model.set(parent, self.model.COL_NUM_ACTIVE, num_active)
-
-            if isinstance(event, bb.build.TaskFailed):
-                # Mark the task and parent as failed
-                icon = "dialog-error"
-                color = HobColors.ERROR
-
-                logfile = event.logfile
-                if logfile and os.path.exists(logfile):
-                    with open(logfile) as f:
-                        logdata = f.read()
-                        self.model.append(current, ('pastebin', None, None, logdata, 'gtk-error', HobColors.OK, 0))
-
-                for i in (current, parent):
-                    self.model.set(i, self.model.COL_ICON, icon,
-                                   self.model.COL_COLOR, color)
-            else:
-                # Mark the parent package and the task as inactive,
-                # but make sure to preserve error, warnings and active
-                # states
-                parent_color = self.model.get(parent, self.model.COL_COLOR)[0]
-                task_color = self.model.get(current, self.model.COL_COLOR)[0]
-
-                # Mark the task as inactive
-                self.model.set(current, self.model.COL_ICON, None)
-                if task_color != HobColors.ERROR:
-                    if task_color == HobColors.WARNING:
-                        self.model.set(current, self.model.COL_ICON, 'dialog-warning')
-                    else:
-                        self.model.set(current, self.model.COL_COLOR, HobColors.OK)
-
-                # Mark the parent as inactive
-                if parent_color != HobColors.ERROR:
-                    if parent_color == HobColors.WARNING:
-                        self.model.set(parent, self.model.COL_ICON, "dialog-warning")
-                    else:
-                        self.model.set(parent, self.model.COL_ICON, None)
-                        if num_active == 0:
-                            self.model.set(parent, self.model.COL_COLOR, HobColors.OK)
-
-            # Clear the iters and the pids since when the task goes away the
-            # pid will no longer be used for messages
-            del self.tasks_to_iter[(package, task)]
-            del self.pids_to_task[pid]
-
-        elif isinstance(event, bb.event.BuildStarted):
-
-            self.emit("build-started")
-            self.model.prepend(None, (None,
-                                      None,
-                                      None,
-                                      "Build Started (%s)" % time.strftime('%m/%d/%Y %H:%M:%S'),
-                                      None,
-                                      HobColors.OK,
-                                      0))
-            if pbar:
-                pbar.update(0, self.progress_total)
-                pbar.set_title(bb.event.getName(event))
-
-        elif isinstance(event, bb.event.BuildCompleted):
-            failures = int (event._failures)
-            self.model.prepend(None, (None,
-                                      None,
-                                      None,
-                                      "Build Completed (%s)" % time.strftime('%m/%d/%Y %H:%M:%S'),
-                                      None,
-                                      HobColors.OK,
-                                      0))
-
-            # Emit the appropriate signal depending on the number of failures
-            if self.buildaborted:
-                self.emit ("build-aborted")
-                self.buildaborted = False
-            elif (failures >= 1):
-                self.emit ("build-failed")
-            else:
-                self.emit ("build-succeeded")
-            # Emit a generic "build-complete" signal for things wishing to
-            # handle when the build is finished
-            self.emit("build-complete")
-            # reset the all cell's icon indicator
-            self.model.close_task_refresh()
-            if pbar:
-                pbar.set_text(event.msg)
-
-        elif isinstance(event, bb.event.DiskFull):
-            self.buildaborted = True
-            self.emit("disk-full")
-
-        elif isinstance(event, bb.command.CommandFailed):
-            self.emit("log", "error", "Command execution failed: %s" % (event.error))
-            if event.error.startswith("Exited with"):
-                # If the command fails with an exit code we're done, emit the
-                # generic signal for the UI to notify the user
-                self.emit("build-complete")
-                # reset the all cell's icon indicator
-                self.model.close_task_refresh()
-
-        elif isinstance(event, bb.event.CacheLoadStarted) and pbar:
-            pbar.set_title("Loading cache")
-            self.progress_total = event.total
-            pbar.update(0, self.progress_total)
-        elif isinstance(event, bb.event.CacheLoadProgress) and pbar:
-            pbar.update(event.current, self.progress_total)
-        elif isinstance(event, bb.event.CacheLoadCompleted) and pbar:
-            pbar.update(self.progress_total, self.progress_total)
-            pbar.hide()
-        elif isinstance(event, bb.event.ParseStarted) and pbar:
-            if event.total == 0:
-                return
-            pbar.set_title("Processing recipes")
-            self.progress_total = event.total
-            pbar.update(0, self.progress_total)
-        elif isinstance(event, bb.event.ParseProgress) and pbar:
-            pbar.update(event.current, self.progress_total)
-        elif isinstance(event, bb.event.ParseCompleted) and pbar:
-            pbar.hide()
-        #using runqueue events as many as possible to update the progress bar
-        elif isinstance(event, bb.runqueue.runQueueTaskFailed):
-            self.emit("log", "error", "Task %s (%s) failed with exit code '%s'" % (event.taskid, event.taskstring, event.exitcode))
-        elif isinstance(event, bb.runqueue.sceneQueueTaskFailed):
-            self.emit("log", "warn", "Setscene task %s (%s) failed with exit code '%s' - real task will be run instead" \
-                                     % (event.taskid, event.taskstring, event.exitcode))
-        elif isinstance(event, (bb.runqueue.runQueueTaskStarted, bb.runqueue.sceneQueueTaskStarted)):
-            if isinstance(event, bb.runqueue.sceneQueueTaskStarted):
-                self.emit("log", "info", "Running setscene task %d of %d (%s)" % \
-                                         (event.stats.completed + event.stats.active + event.stats.failed + 1,
-                                          event.stats.total, event.taskstring))
-            else:
-                if event.noexec:
-                    tasktype = 'noexec task'
-                else:
-                    tasktype = 'task'
-                self.emit("log", "info", "Running %s %s of %s (ID: %s, %s)" % \
-                                         (tasktype, event.stats.completed + event.stats.active + event.stats.failed + 1,
-                                          event.stats.total, event.taskid, event.taskstring))
-            message = {}
-            message["eventname"] = bb.event.getName(event)
-            num_of_completed = event.stats.completed + event.stats.failed
-            message["current"] = num_of_completed
-            message["total"] = event.stats.total
-            message["title"] = ""
-            message["task"] = event.taskstring
-            self.emit("task-started", message)
-        elif isinstance(event, bb.event.MultipleProviders):
-            self.emit("log", "info", "multiple providers are available for %s%s (%s)" \
-                                     % (event._is_runtime and "runtime " or "", event._item, ", ".join(event._candidates)))
-            self.emit("log", "info", "consider defining a PREFERRED_PROVIDER entry to match %s" % (event._item))
-        elif isinstance(event, bb.event.NoProvider):
-            msg = ""
-            if event._runtime:
-                r = "R"
-            else:
-                r = ""
-
-            extra = ''
-            if not event._reasons:
-                if event._close_matches:
-                    extra = ". Close matches:\n  %s" % '\n  '.join(event._close_matches)
-
-            if event._dependees:
-                msg = "Nothing %sPROVIDES '%s' (but %s %sDEPENDS on or otherwise requires it)%s\n" % (r, event._item, ", ".join(event._dependees), r, extra)
-            else:
-                msg = "Nothing %sPROVIDES '%s'%s\n" % (r, event._item, extra)
-            if event._reasons:
-                for reason in event._reasons:
-                    msg += ("%s\n" % reason)
-            self.emit("no-provider", msg)
-            self.emit("log", "error", msg)
-        elif isinstance(event, bb.event.LogExecTTY):
-            icon = "dialog-warning"
-            color = HobColors.WARNING
-            if self.sequential or not parent:
-                tree_add = self.model.append
-            else:
-                tree_add = self.model.prepend
-            tree_add(parent,
-                     (None,
-                      package,
-                      task,
-                      event.msg,
-                      icon,
-                      color,
-                      0))
-        else:
-            if not isinstance(event, (bb.event.BuildBase,
-                                      bb.event.StampUpdate,
-                                      bb.event.ConfigParsed,
-                                      bb.event.RecipeParsed,
-                                      bb.event.RecipePreFinalise,
-                                      bb.runqueue.runQueueEvent,
-                                      bb.runqueue.runQueueExitWait,
-                                      bb.event.OperationStarted,
-                                      bb.event.OperationCompleted,
-                                      bb.event.OperationProgress)):
-                self.emit("log", "error", "Unknown event: %s" % (event.error if hasattr(event, 'error') else 'error'))
-
-        return
-
-
-def do_pastebin(text):
-    url = 'http://pastebin.com/api_public.php'
-    params = {'paste_code': text, 'paste_format': 'text'}
-
-    req = urllib.request.Request(url, urllib.parse.urlencode(params))
-    response = urllib.request.urlopen(req)
-    paste_url = response.read()
-
-    return paste_url
-
-
-class RunningBuildTreeView (gtk.TreeView):
-    __gsignals__ = {
-        "button_press_event" : "override"
-        }
-    def __init__ (self, readonly=False, hob=False):
-        gtk.TreeView.__init__ (self)
-        self.readonly = readonly
-
-        # The icon that indicates whether we're building or failed.
-        # add 'hob' flag because there has not only hob to share this code
-        if hob:
-            renderer = HobCellRendererPixbuf ()
-        else:
-            renderer = gtk.CellRendererPixbuf()
-        col = gtk.TreeViewColumn ("Status", renderer)
-        col.add_attribute (renderer, "icon-name", 4)
-        self.append_column (col)
-
-        # The message of the build.
-        # add 'hob' flag because there has not only hob to share this code
-        if hob:
-            self.message_renderer = HobWarpCellRendererText (col_number=1)
-        else:
-            self.message_renderer = gtk.CellRendererText ()
-        self.message_column = gtk.TreeViewColumn ("Message", self.message_renderer, text=3)
-        self.message_column.add_attribute(self.message_renderer, 'background', 5)
-        self.message_renderer.set_property('editable', (not self.readonly))
-        self.append_column (self.message_column)
-
-    def do_button_press_event(self, event):
-        gtk.TreeView.do_button_press_event(self, event)
-
-        if event.button == 3:
-            selection = super(RunningBuildTreeView, self).get_selection()
-            (model, it) = selection.get_selected()
-            if it is not None:
-                can_paste = model.get(it, model.COL_LOG)[0]
-                if can_paste == 'pastebin':
-                    # build a simple menu with a pastebin option
-                    menu = gtk.Menu()
-                    menuitem = gtk.MenuItem("Copy")
-                    menu.append(menuitem)
-                    menuitem.connect("activate", self.clipboard_handler, (model, it))
-                    menuitem.show()
-                    menuitem = gtk.MenuItem("Send log to pastebin")
-                    menu.append(menuitem)
-                    menuitem.connect("activate", self.pastebin_handler, (model, it))
-                    menuitem.show()
-                    menu.show()
-                    menu.popup(None, None, None, event.button, event.time)
-
-    def _add_to_clipboard(self, clipping):
-        """
-        Add the contents of clipping to the system clipboard.
-        """
-        clipboard = gtk.clipboard_get()
-        clipboard.set_text(clipping)
-        clipboard.store()
-
-    def pastebin_handler(self, widget, data):
-        """
-        Send the log data to pastebin, then add the new paste url to the
-        clipboard.
-        """
-        (model, it) = data
-        paste_url = do_pastebin(model.get(it, model.COL_MESSAGE)[0])
-
-        # @todo Provide visual feedback to the user that it is done and that
-        # it worked.
-        print(paste_url)
-
-        self._add_to_clipboard(paste_url)
-
-    def clipboard_handler(self, widget, data):
-        """
-        """
-        (model, it) = data
-        message = model.get(it, model.COL_MESSAGE)[0]
-
-        self._add_to_clipboard(message)
-
-class BuildFailureTreeView(gtk.TreeView):
-
-    def __init__ (self):
-        gtk.TreeView.__init__(self)
-        self.set_rules_hint(False)
-        self.set_headers_visible(False)
-        self.get_selection().set_mode(gtk.SELECTION_SINGLE)
-
-        # The icon that indicates whether we're building or failed.
-        renderer = HobCellRendererPixbuf ()
-        col = gtk.TreeViewColumn ("Status", renderer)
-        col.add_attribute (renderer, "icon-name", RunningBuildModel.COL_ICON)
-        self.append_column (col)
-
-        # The message of the build.
-        self.message_renderer = HobWarpCellRendererText (col_number=1)
-        self.message_column = gtk.TreeViewColumn ("Message", self.message_renderer, text=RunningBuildModel.COL_MESSAGE, background=RunningBuildModel.COL_COLOR)
-        self.append_column (self.message_column)
diff --git a/lib/bb/ui/crumbs/utils.py b/lib/bb/ui/crumbs/utils.py
deleted file mode 100644
index 939864f..0000000
--- a/lib/bb/ui/crumbs/utils.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# BitBake UI Utils 
-#
-# Copyright (C) 2012 Intel Corporation
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-# This utility method looks for xterm or vte and return the 
-# frist to exist, currently we are keeping this simple, but 
-# we will likely move the oe.terminal implementation into 
-# bitbake which will allow more flexibility.
-
-import os
-import bb
-
-def which_terminal():
-    term = bb.utils.which(os.environ["PATH"], "xterm")
-    if term:
-        return term + " -e "
-    term = bb.utils.which(os.environ["PATH"], "vte")
-    if term:
-        return term + " -c "
-    return None
diff --git a/lib/bb/ui/goggle.py b/lib/bb/ui/goggle.py
deleted file mode 100644
index f5f8f16..0000000
--- a/lib/bb/ui/goggle.py
+++ /dev/null
@@ -1,126 +0,0 @@
-#
-# BitBake Graphical GTK User Interface
-#
-# Copyright (C) 2008        Intel Corporation
-#
-# Authored by Rob Bradford <rob@linux.intel.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-from gi import pygtkcompat
-
-pygtkcompat.enable()
-pygtkcompat.enable_gtk(version='3.0')
-
-import gobject
-import gtk
-import xmlrpc.client
-from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild
-from bb.ui.crumbs.progress import ProgressBar
-
-import queue
-
-
-def event_handle_idle_func (eventHandler, build, pbar):
-
-    # Consume as many messages as we can in the time available to us
-    event = eventHandler.getEvent()
-    while event:
-        build.handle_event (event, pbar)
-        event = eventHandler.getEvent()
-
-    return True
-
-def scroll_tv_cb (model, path, iter, view):
-    view.scroll_to_cell (path)
-
-
-# @todo hook these into the GUI so the user has feedback...
-def running_build_failed_cb (running_build):
-    pass
-
-
-def running_build_succeeded_cb (running_build):
-    pass
-
-
-class MainWindow (gtk.Window):
-    def __init__ (self):
-        gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL)
-
-        # Setup tree view and the scrolled window
-        scrolled_window = gtk.ScrolledWindow ()
-        self.add (scrolled_window)
-        self.cur_build_tv = RunningBuildTreeView()
-        self.connect("delete-event", gtk.main_quit)
-        self.set_default_size(640, 480)
-        scrolled_window.add (self.cur_build_tv)
-
-
-def main (server, eventHandler, params):
-    gobject.threads_init()
-    gtk.gdk.threads_init()
-
-    window = MainWindow ()
-    window.show_all ()
-    pbar = ProgressBar(window)
-    pbar.connect("delete-event", gtk.main_quit)
-
-    # Create the object for the current build
-    running_build = RunningBuild ()
-    window.cur_build_tv.set_model (running_build.model)
-    running_build.model.connect("row-inserted", scroll_tv_cb, window.cur_build_tv)
-    running_build.connect ("build-succeeded", running_build_succeeded_cb)
-    running_build.connect ("build-failed", running_build_failed_cb)
-
-    try:
-        params.updateFromServer(server)
-        cmdline = params.parseActions()
-        if not cmdline:
-            print("Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
-            return 1
-        if 'msg' in cmdline and cmdline['msg']:
-            logger.error(cmdline['msg'])
-            return 1
-        cmdline = cmdline['action']
-        ret, error = server.runCommand(cmdline)
-        if error:
-            print("Error running command '%s': %s" % (cmdline, error))
-            return 1
-        elif ret != True:
-            print("Error running command '%s': returned %s" % (cmdline, ret))
-            return 1
-    except xmlrpcclient.Fault as x:
-        print("XMLRPC Fault getting commandline:\n %s" % x)
-        return 1
-
-    # Use a timeout function for probing the event queue to find out if we
-    # have a message waiting for us.
-    gobject.timeout_add (100,
-                         event_handle_idle_func,
-                         eventHandler,
-                         running_build,
-                         pbar)
-
-    try:
-        gtk.main()
-    except EnvironmentError as ioerror:
-        # ignore interrupted io
-        if ioerror.args[0] == 4:
-            pass
-    except KeyboardInterrupt:
-        pass
-    finally:
-        server.runCommand(["stateForceShutdown"])
-
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 09/28] data/event/siggen: Fix various exceptions [FIXME]
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (6 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 08/28] google/image-writer: Drop since bitrotting and no longer used Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 13:00   ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 10/28] toaster: use force_text instead of force_bytes Richard Purdie
                   ` (18 subsequent siblings)
  26 siblings, 1 reply; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/bb/data.py   | 1 +
 lib/bb/event.py  | 3 ++-
 lib/bb/siggen.py | 3 ++-
 3 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/lib/bb/data.py b/lib/bb/data.py
index 48d990d..60b2033 100644
--- a/lib/bb/data.py
+++ b/lib/bb/data.py
@@ -410,6 +410,7 @@ def build_dependencies(key, keys, shelldeps, varflagsexcl, d):
         deps -= set(varflags.get("vardepsexclude", "").split())
     except Exception as e:
         bb.warn("Exception during build_dependencies for %s" % key)
+        bb.warn(str(e))
         raise
     return deps, value
     #bb.note("Variable %s references %s and calls %s" % (key, str(deps), str(execs)))
diff --git a/lib/bb/event.py b/lib/bb/event.py
index 6fb3712..023300b 100644
--- a/lib/bb/event.py
+++ b/lib/bb/event.py
@@ -157,7 +157,8 @@ def fire_ui_handlers(event, d):
                 _ui_handlers[h].event.sendpickle((pickle.dumps(event)))
              else:
                 _ui_handlers[h].event.send(event)
-        except:
+        except Exception as e:
+            print(str(e))
             errors.append(h)
     for h in errors:
         del _ui_handlers[h]
diff --git a/lib/bb/siggen.py b/lib/bb/siggen.py
index db3daef..f951020 100644
--- a/lib/bb/siggen.py
+++ b/lib/bb/siggen.py
@@ -149,8 +149,9 @@ class SignatureGeneratorBasic(SignatureGenerator):
 
         try:
             taskdeps = self._build_data(fn, d)
-        except:
+        except Exception as e:
             bb.warn("Error during finalise of %s" % fn)
+            bb.error(str(e))
             raise
 
         #Slow but can be useful for debugging mismatched basehashes
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 10/28] toaster: use force_text instead of force_bytes
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (7 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 09/28] data/event/siggen: Fix various exceptions [FIXME] Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 11/28] toaster: get rid of using reduce Richard Purdie
                   ` (17 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Usage of force_bytes in BuildRequest.__str__ method caused
python 3 to throw "__str__ returned non-string (type bytes)"
error.

Replaced force_bytes with force_text to make the code working
on both python 2 and python 3.

[YOCTO #9584]

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/bldcontrol/management/commands/runbuilds.py | 4 ++--
 lib/toaster/bldcontrol/models.py                        | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/toaster/bldcontrol/management/commands/runbuilds.py b/lib/toaster/bldcontrol/management/commands/runbuilds.py
index e3145e8..80782ef 100644
--- a/lib/toaster/bldcontrol/management/commands/runbuilds.py
+++ b/lib/toaster/bldcontrol/management/commands/runbuilds.py
@@ -54,8 +54,8 @@ class Command(NoArgsCommand):
                 logger.debug("runbuilds: No build env")
                 return
 
-            logger.debug("runbuilds: starting build %s, environment %s" % \
-                         (str(br).decode('utf-8'), bec.be))
+            logger.info("runbuilds: starting build %s, environment %s" % \
+                        (br, bec.be))
 
             # let the build request know where it is being executed
             br.environment = bec.be
diff --git a/lib/toaster/bldcontrol/models.py b/lib/toaster/bldcontrol/models.py
index e09ad20..f06c562 100644
--- a/lib/toaster/bldcontrol/models.py
+++ b/lib/toaster/bldcontrol/models.py
@@ -1,7 +1,7 @@
 from __future__ import unicode_literals
 from django.db import models
 from django.core.validators import MaxValueValidator, MinValueValidator
-from django.utils.encoding import force_bytes
+from django.utils.encoding import force_text
 from orm.models import Project, ProjectLayer, ProjectVariable, ProjectTarget, Build, Layer_Version
 
 import logging
@@ -121,7 +121,7 @@ class BuildRequest(models.Model):
         return self.brvariable_set.get(name="MACHINE").value
 
     def __str__(self):
-        return force_bytes('%s %s' % (self.project, self.get_state_display()))
+        return force_text('%s %s' % (self.project, self.get_state_display()))
 
 # These tables specify the settings for running an actual build.
 # They MUST be kept in sync with the tables in orm.models.Project*
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 11/28] toaster: get rid of using reduce
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (8 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 10/28] toaster: use force_text instead of force_bytes Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 12/28] toaster: fix imports to work for python 3 Richard Purdie
                   ` (16 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Replaced compicated calls of reduce with more clear code.
As reduce was removed from python 3 this change is mandatory
for the code to work on both pythons.

Here is an example change for illustration purposes:

original code:

  querydict = dict(zip(or_keys, or_values))
  query = reduce(operator.or_, map(lambda x: __get_q_for_val(x, querydict[x]), [k for k in querydict])))

replaced with:

  query = None
  for key, val in zip(or_keys, or_values):
      x = __get_q_for_val(k, val)
      query = query | x if query else x

[YOCTO #9584]

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/toastergui/views.py   | 64 +++++++++++++++++++++++++++------------
 lib/toaster/toastergui/widgets.py | 20 +++++++-----
 2 files changed, 57 insertions(+), 27 deletions(-)

diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index e78b0da..22d1a0c 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -229,10 +229,18 @@ OR_VALUE_SEPARATOR = "|"
 DESCENDING = "-"
 
 def __get_q_for_val(name, value):
-    if "OR" in value:
-        return reduce(operator.or_, map(lambda x: __get_q_for_val(name, x), [ x for x in value.split("OR") ]))
+    if "OR" in value or "AND" in value:
+        result = None
+        for x in value.split("OR"):
+             x = __get_q_for_val(name, x)
+             result = result | x if result else x
+        return result
     if "AND" in value:
-        return reduce(operator.and_, map(lambda x: __get_q_for_val(name, x), [ x for x in value.split("AND") ]))
+        result = None
+        for x in value.split("AND"):
+            x = __get_q_for_val(name, x)
+            result = result & x if result else x
+        return result
     if value.startswith("NOT"):
         value = value[3:]
         if value == 'None':
@@ -251,14 +259,18 @@ def _get_filtering_query(filter_string):
     and_keys = search_terms[0].split(AND_VALUE_SEPARATOR)
     and_values = search_terms[1].split(AND_VALUE_SEPARATOR)
 
-    and_query = []
+    and_query = None
     for kv in zip(and_keys, and_values):
         or_keys = kv[0].split(OR_VALUE_SEPARATOR)
         or_values = kv[1].split(OR_VALUE_SEPARATOR)
-        querydict = dict(zip(or_keys, or_values))
-        and_query.append(reduce(operator.or_, map(lambda x: __get_q_for_val(x, querydict[x]), [k for k in querydict])))
+        query = None
+        for key, val in zip(or_keys, or_values):
+            x = __get_q_for_val(k, val)
+            query = query | x if query else x
 
-    return reduce(operator.and_, [k for k in and_query])
+        and_query = and_query & query if and_query else query
+
+    return and_query
 
 def _get_toggle_order(request, orderkey, toggle_reverse = False):
     if toggle_reverse:
@@ -295,21 +307,24 @@ def _validate_input(field_input, model):
         # Check we are looking for a valid field
         valid_fields = model._meta.get_all_field_names()
         for field in field_input_list[0].split(AND_VALUE_SEPARATOR):
-            if not reduce(lambda x, y: x or y, [ field.startswith(x) for x in valid_fields ]):
-                return None, (field, [ x for x in valid_fields ])
+            if True in [field.startswith(x) for x in valid_fields]:
+                break
+        else:
+           return None, (field, valid_fields)
 
     return field_input, invalid
 
 # uses search_allowed_fields in orm/models.py to create a search query
 # for these fields with the supplied input text
 def _get_search_results(search_term, queryset, model):
-    search_objects = []
+    search_object = None
     for st in search_term.split(" "):
-        q_map = map(lambda x: Q(**{x+'__icontains': st}),
-                model.search_allowed_fields)
+        queries = None
+        for field in model.search_allowed_fields:
+            query =  Q(**{x+'__icontains': st})
+            queries = queries | query if queries else query
 
-        search_objects.append(reduce(operator.or_, q_map))
-    search_object = reduce(operator.and_, search_objects)
+        search_object = search_object & queries if search_object else queries
     queryset = queryset.filter(search_object)
 
     return queryset
@@ -1938,10 +1953,10 @@ if True:
                 if ptype == "build":
                     mandatory_fields.append('projectversion')
                 # make sure we have values for all mandatory_fields
-                if reduce( lambda x, y: x or y, map(lambda x: len(request.POST.get(x, '')) == 0, mandatory_fields)):
-                # set alert for missing fields
-                    raise BadParameterException("Fields missing: " +
-            ", ".join([x for x in mandatory_fields if len(request.POST.get(x, '')) == 0 ]))
+                missing = [field for field in mandatory_fields if len(request.POST.get(field, '')) == 0]
+                if missing:
+                    # set alert for missing fields
+                    raise BadParameterException("Fields missing: %s" % ", ".join(missing))
 
                 if not request.user.is_authenticated():
                     user = authenticate(username = request.POST.get('username', '_anonuser'), password = 'nopass')
@@ -2035,13 +2050,22 @@ if True:
         from collections import Counter
         freqtargets = []
         try:
-            freqtargets += map(lambda x: x.target, reduce(lambda x, y: x + y,   map(lambda x: list(x.target_set.all()), Build.objects.filter(project = prj, outcome__lt = Build.IN_PROGRESS))))
-            freqtargets += map(lambda x: x.target, reduce(lambda x, y: x + y,   map(lambda x: list(x.brtarget_set.all()), BuildRequest.objects.filter(project = prj, state = BuildRequest.REQ_FAILED))))
+            btargets = sum(build.target_set.all() for build in Build.objects.filter(project=prj, outcome__lt=Build.IN_PROGRESS))
+            brtargets = sum(br.brtarget_set.all() for br in BuildRequest.objects.filter(project = prj, state = BuildRequest.REQ_FAILED))
+            freqtargets = [x.target for x in btargets] + [x.target for x in brtargets]
         except TypeError:
             pass
         freqtargets = Counter(freqtargets)
         freqtargets = sorted(freqtargets, key = lambda x: freqtargets[x], reverse=True)
 
+        layers = [{"id": x.layercommit.pk, "orderid": x.pk, "name" : x.layercommit.layer.name,
+                   "vcs_url": x.layercommit.layer.vcs_url, "vcs_reference" : x.layercommit.get_vcs_reference(),
+                   "url": x.layercommit.layer.layer_index_url, "layerdetailurl": x.layercommit.get_detailspage_url(prj.pk),
+                   # This branch name is actually the release
+                   "branch" : {"name" : x.layercommit.get_vcs_reference(),
+                               "layersource" : x.layercommit.up_branch.layer_source.name if x.layercommit.up_branch != None else None}
+                   } for x in prj.projectlayer_set.all().order_by("id")]
+
         context = {
             "project" : prj,
             "lvs_nos" : Layer_Version.objects.all().count(),
diff --git a/lib/toaster/toastergui/widgets.py b/lib/toaster/toastergui/widgets.py
index 4117031..0f972d9 100644
--- a/lib/toaster/toastergui/widgets.py
+++ b/lib/toaster/toastergui/widgets.py
@@ -239,14 +239,20 @@ class ToasterTable(TemplateView):
             raise Exception("Search fields aren't defined in the model %s"
                             % self.queryset.model)
 
-        search_queries = []
+        search_queries = None
         for st in search_term.split(" "):
-            q_map = [Q(**{field + '__icontains': st})
-                     for field in self.queryset.model.search_allowed_fields]
-
-            search_queries.append(reduce(operator.or_, q_map))
-
-        search_queries = reduce(operator.and_, search_queries)
+            queries = None
+            for field in self.queryset.model.search_allowed_fields:
+                query = Q(**{field + '__icontains': st})
+                if queries:
+                    queries |= query
+                else:
+                    queries = query
+
+            if search_queries:
+               search_queries &= queries
+            else:
+               search_queries = queries
 
         self.queryset = self.queryset.filter(search_queries)
 
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 12/28] toaster: fix imports to work for python 3
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (9 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 11/28] toaster: get rid of using reduce Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 13/28] toaster: fix local imports Richard Purdie
                   ` (15 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Some APIs have been moved to other modules in python 3:
 getstatusoutput: moved from commands to subproces
 urlopen: moved from urllib2 to urllib.request
 urlparse: moved from urlparse to urllib.parse

Made the imports work for both python versions by
catching ImportError and importing APIs from different
modules.

[YOCTO #9584]

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/bb/utils.py           |  1 -
 lib/toaster/orm/models.py | 16 ++++++++++++----
 2 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/lib/bb/utils.py b/lib/bb/utils.py
index 138da44..8f75871 100644
--- a/lib/bb/utils.py
+++ b/lib/bb/utils.py
@@ -41,7 +41,6 @@ from subprocess import getstatusoutput
 from contextlib import contextmanager
 from ctypes import cdll
 
-
 logger = logging.getLogger("BitBake.Util")
 python_extensions = [e for e, _, _ in imp.get_suffixes()]
 
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index 88967a2..9183b0c 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -1147,18 +1147,26 @@ class LayerIndexLayerSource(LayerSource):
         assert self.apiurl is not None
         from django.db import transaction, connection
 
-        import urllib2, urlparse, json
+        import json
         import os
+
+        try:
+            from urllib.request import urlopen, URLError
+            from urllib.parse import urlparse
+        except ImportError:
+            from urllib2 import urlopen, URLError
+            from urlparse import urlparse
+
         proxy_settings = os.environ.get("http_proxy", None)
         oe_core_layer = 'openembedded-core'
 
         def _get_json_response(apiurl = self.apiurl):
-            _parsedurl = urlparse.urlparse(apiurl)
+            _parsedurl = urlparse(apiurl)
             path = _parsedurl.path
 
             try:
-                res = urllib2.urlopen(apiurl)
-            except urllib2.URLError as e:
+                res = urlopen(apiurl)
+            except URLError as e:
                 raise Exception("Failed to read %s: %s" % (path, e.reason))
 
             return json.loads(res.read())
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 13/28] toaster: fix local imports
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (10 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 12/28] toaster: fix imports to work for python 3 Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 14/28] toaster: modified list of dependencies Richard Purdie
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Replaced local imports with absolute imports.
Used .module for local imports.

This should make the code to work on python 2 and python 3.

[YOCTO #9584]

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/bldcontrol/bbcontroller.py                      | 2 +-
 lib/toaster/bldcontrol/localhostbecontroller.py             | 2 +-
 lib/toaster/bldcontrol/management/commands/checksettings.py | 2 +-
 lib/toaster/bldcontrol/management/commands/loadconf.py      | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/toaster/bldcontrol/bbcontroller.py b/lib/toaster/bldcontrol/bbcontroller.py
index d09ac17..9bddadb 100644
--- a/lib/toaster/bldcontrol/bbcontroller.py
+++ b/lib/toaster/bldcontrol/bbcontroller.py
@@ -79,7 +79,7 @@ def getBuildEnvironmentController(**kwargs):
         The return object MUST always be a BuildEnvironmentController.
     """
 
-    from localhostbecontroller import LocalhostBEController
+    from bldcontrol.localhostbecontroller import LocalhostBEController
 
     be = BuildEnvironment.objects.filter(Q(**kwargs))[0]
     if be.betype == BuildEnvironment.TYPE_LOCAL:
diff --git a/lib/toaster/bldcontrol/localhostbecontroller.py b/lib/toaster/bldcontrol/localhostbecontroller.py
index 7b2f126..b4e4196 100644
--- a/lib/toaster/bldcontrol/localhostbecontroller.py
+++ b/lib/toaster/bldcontrol/localhostbecontroller.py
@@ -32,7 +32,7 @@ import subprocess
 
 from toastermain import settings
 
-from bbcontroller import BuildEnvironmentController, ShellCmdException, BuildSetupException, BitbakeController
+from bldcontrol.bbcontroller import BuildEnvironmentController, ShellCmdException, BuildSetupException, BitbakeController
 
 import logging
 logger = logging.getLogger("toaster")
diff --git a/lib/toaster/bldcontrol/management/commands/checksettings.py b/lib/toaster/bldcontrol/management/commands/checksettings.py
index 6b1da1b..2407e1b 100644
--- a/lib/toaster/bldcontrol/management/commands/checksettings.py
+++ b/lib/toaster/bldcontrol/management/commands/checksettings.py
@@ -105,7 +105,7 @@ class Command(NoArgsCommand):
                     try:
                         config_file = os.environ.get('TOASTER_CONF')
                         print("\nImporting file: %s" % config_file)
-                        from loadconf import Command as LoadConfigCommand
+                        from .loadconf import Command as LoadConfigCommand
 
                         LoadConfigCommand()._import_layer_config(config_file)
                         # we run lsupdates after config update
diff --git a/lib/toaster/bldcontrol/management/commands/loadconf.py b/lib/toaster/bldcontrol/management/commands/loadconf.py
index 5022b59..6d25df9 100644
--- a/lib/toaster/bldcontrol/management/commands/loadconf.py
+++ b/lib/toaster/bldcontrol/management/commands/loadconf.py
@@ -4,7 +4,7 @@ from orm.models import BitbakeVersion, Release, ReleaseDefaultLayer, ReleaseLaye
 from django.db import IntegrityError
 import os
 
-from checksettings import DN
+from .checksettings import DN
 
 import logging
 logger = logging.getLogger("toaster")
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 14/28] toaster: modified list of dependencies
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (11 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 13/28] toaster: fix local imports Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 15/28] toaster: use decode('utf-8') for binary data Richard Purdie
                   ` (13 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Removed dependency to argparse and wsgiref as they're in
standard python library.

wsgiref crashes during installation with python 3 as it's not
compatible with python 3.

Added dependency to pytz as it's used in the toaster code.

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 toaster-requirements.txt | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/toaster-requirements.txt b/toaster-requirements.txt
index 0e8c742..e61c8e2 100644
--- a/toaster-requirements.txt
+++ b/toaster-requirements.txt
@@ -1,4 +1,3 @@
 Django>1.8,<1.9
-argparse==1.2.1
-wsgiref==0.1.2
 beautifulsoup4>=4.4.0
+pytz
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 15/28] toaster: use decode('utf-8') for binary data
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (12 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 14/28] toaster: modified list of dependencies Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 16/28] toaster: use items and range instead of old APIs Richard Purdie
                   ` (12 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Decoded binary data to utf-8 to fix the following
error when runnign on python 3:
TypeError: Type str doesn't support the buffer API

[YOCTO #9584]

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/bldcontrol/localhostbecontroller.py        | 2 +-
 lib/toaster/bldcontrol/management/commands/loadconf.py | 2 +-
 lib/toaster/orm/models.py                              | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/toaster/bldcontrol/localhostbecontroller.py b/lib/toaster/bldcontrol/localhostbecontroller.py
index b4e4196..3da8be4 100644
--- a/lib/toaster/bldcontrol/localhostbecontroller.py
+++ b/lib/toaster/bldcontrol/localhostbecontroller.py
@@ -70,7 +70,7 @@ class LocalhostBEController(BuildEnvironmentController):
             raise ShellCmdException(err)
         else:
             logger.debug("localhostbecontroller: shellcmd success")
-            return out
+            return out.decode('utf-8')
 
     def getGitCloneDirectory(self, url, branch):
         """Construct unique clone directory name out of url and branch."""
diff --git a/lib/toaster/bldcontrol/management/commands/loadconf.py b/lib/toaster/bldcontrol/management/commands/loadconf.py
index 6d25df9..173e3ce 100644
--- a/lib/toaster/bldcontrol/management/commands/loadconf.py
+++ b/lib/toaster/bldcontrol/management/commands/loadconf.py
@@ -54,7 +54,7 @@ class Command(BaseCommand):
             (out,err) = cmd.communicate()
             if cmd.returncode != 0:
                 logging.warning("Error while importing layer vcs_url: git error: %s" % err)
-            for line in out.split("\n"):
+            for line in out.decode('utf-8').split("\n"):
                 try:
                     (name, path) = line.split("\t", 1)
                     if name == remote_name:
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index 9183b0c..58dc753 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -1169,7 +1169,7 @@ class LayerIndexLayerSource(LayerSource):
             except URLError as e:
                 raise Exception("Failed to read %s: %s" % (path, e.reason))
 
-            return json.loads(res.read())
+            return json.loads(res.read().decode('utf-8'))
 
         # verify we can get the basic api
         try:
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 16/28] toaster: use items and range instead of old APIs
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (13 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 15/28] toaster: use decode('utf-8') for binary data Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 17/28] toaster: replace map with list comprehensions Richard Purdie
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Used items() and range() APIs instead of iteritems() and
xrange() as latter don't exist in python 3

[YOCTO #9584]

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/contrib/tts/config.py     | 2 +-
 lib/toaster/orm/models.py             | 2 +-
 lib/toaster/toastergui/tablefilter.py | 2 +-
 lib/toaster/toastergui/views.py       | 2 +-
 lib/toaster/toastergui/widgets.py     | 4 ++--
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/lib/toaster/contrib/tts/config.py b/lib/toaster/contrib/tts/config.py
index 40d45f3..87b427c 100644
--- a/lib/toaster/contrib/tts/config.py
+++ b/lib/toaster/contrib/tts/config.py
@@ -74,7 +74,7 @@ BACKLOGFILE = os.path.join(os.path.dirname(__file__), "backlog.txt")
 # task states
 def enum(*sequential, **named):
     enums = dict(zip(sequential, range(len(sequential))), **named)
-    reverse = dict((value, key) for key, value in enums.iteritems())
+    reverse = dict((value, key) for key, value in enums.items())
     enums['reverse_mapping'] = reverse
     return type('Enum', (), enums)
 
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index 58dc753..ee01a46 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -103,7 +103,7 @@ class GitURLValidator(validators.URLValidator):
 
 def GitURLField(**kwargs):
     r = models.URLField(**kwargs)
-    for i in xrange(len(r.validators)):
+    for i in range(len(r.validators)):
         if isinstance(r.validators[i], validators.URLValidator):
             r.validators[i] = GitURLValidator()
     return r
diff --git a/lib/toaster/toastergui/tablefilter.py b/lib/toaster/toastergui/tablefilter.py
index 9d15bcf..65454e1 100644
--- a/lib/toaster/toastergui/tablefilter.py
+++ b/lib/toaster/toastergui/tablefilter.py
@@ -286,7 +286,7 @@ class TableFilterMap(object):
     def to_json(self, queryset):
         data = {}
 
-        for filter_name, table_filter in self.__filters.iteritems():
+        for filter_name, table_filter in self.__filters.items():
             data[filter_name] = table_filter.to_json()
 
         return data
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 22d1a0c..b256db7 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -2279,7 +2279,7 @@ if True:
         # Strip trailing/leading whitespace from all values
         # put into a new dict because POST one is immutable
         post_data = dict()
-        for key,val in request.POST.iteritems():
+        for key,val in request.POST.items():
           post_data[key] = val.strip()
 
 
diff --git a/lib/toaster/toastergui/widgets.py b/lib/toaster/toastergui/widgets.py
index 0f972d9..19850fb 100644
--- a/lib/toaster/toastergui/widgets.py
+++ b/lib/toaster/toastergui/widgets.py
@@ -278,12 +278,12 @@ class ToasterTable(TemplateView):
         # Make a unique cache name
         cache_name = self.__class__.__name__
 
-        for key, val in request.GET.iteritems():
+        for key, val in request.GET.items():
             if key == 'nocache':
                 continue
             cache_name = cache_name + str(key) + str(val)
 
-        for key, val in kwargs.iteritems():
+        for key, val in kwargs.items():
             cache_name = cache_name + str(key) + str(val)
 
         # No special chars allowed in the cache name apart from dash
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 17/28] toaster: replace map with list comprehensions
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (14 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 16/28] toaster: use items and range instead of old APIs Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 18/28] toaster: use re.sub() instead of translate() Richard Purdie
                   ` (10 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

map returns map object in python 3. Replaced calls of
map to equivalent list comprehensions in the code which
requires lists.

[YOCTO #9584]

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/orm/models.py        |  2 +-
 lib/toaster/toastergui/tables.py |  3 +--
 lib/toaster/toastergui/views.py  | 45 ++++++++++++++++------------------------
 3 files changed, 20 insertions(+), 30 deletions(-)

diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index ee01a46..dd64664 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -1183,7 +1183,7 @@ class LayerIndexLayerSource(LayerSource):
 
         # update branches; only those that we already have names listed in the
         # Releases table
-        whitelist_branch_names = map(lambda x: x.branch_name, Release.objects.all())
+        whitelist_branch_names = [rel.branch_name for rel in Release.objects.all()]
         if len(whitelist_branch_names) == 0:
             raise Exception("Failed to make list of branches to fetch")
 
diff --git a/lib/toaster/toastergui/tables.py b/lib/toaster/toastergui/tables.py
index d21bed1..caa2fba 100644
--- a/lib/toaster/toastergui/tables.py
+++ b/lib/toaster/toastergui/tables.py
@@ -333,8 +333,7 @@ class RecipesTable(ToasterTable):
         context = super(RecipesTable, self).get_context_data(**kwargs)
 
         context['project'] = project
-
-        context['projectlayers'] = map(lambda prjlayer: prjlayer.layercommit.id, ProjectLayer.objects.filter(project=context['project']))
+        context['projectlayers'] = [player.layercommit.id for player in ProjectLayer.objects.filter(project=context['project'])]
 
         return context
 
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index b256db7..8e920f0 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -967,18 +967,19 @@ def dirinfo(request, build_id, target_id, file_path=None):
     return render(request, template, context)
 
 def _find_task_dep(task_object):
-    return map(lambda x: x.depends_on, Task_Dependency.objects.filter(task=task_object).filter(depends_on__order__gt = 0).exclude(depends_on__outcome = Task.OUTCOME_NA).select_related("depends_on"))
-
+    tdeps = Task_Dependency.objects.filter(task=task_object).filter(depends_on__order__gt=0)
+    tdeps = tdeps.exclude(depends_on__outcome=Task.OUTCOME_NA).select_related("depends_on")
+    return [x.depends_on for x in tdeps]
 
 def _find_task_revdep(task_object):
-    tp = []
-    tp = map(lambda t: t.task, Task_Dependency.objects.filter(depends_on=task_object).filter(task__order__gt=0).exclude(task__outcome = Task.OUTCOME_NA).select_related("task", "task__recipe", "task__build"))
-    return tp
+    tdeps = Task_Dependency.objects.filter(depends_on=task_object).filter(task__order__gt=0)
+    tdeps = tdeps.exclude(task__outcome = Task.OUTCOME_NA).select_related("task", "task__recipe", "task__build")
+    return [tdep.task for tdep in tdeps]
 
 def _find_task_revdep_list(tasklist):
-    tp = []
-    tp = map(lambda t: t.task, Task_Dependency.objects.filter(depends_on__in=tasklist).filter(task__order__gt=0).exclude(task__outcome = Task.OUTCOME_NA).select_related("task", "task__recipe", "task__build"))
-    return tp
+    tdeps = Task_Dependency.objects.filter(depends_on__in=tasklist).filter(task__order__gt=0)
+    tdeps = tdeps.exclude(task__outcome=Task.OUTCOME_NA).select_related("task", "task__recipe", "task__build")
+    return [tdep.task for tdep in tdeps]
 
 def _find_task_provider(task_object):
     task_revdeps = _find_task_revdep(task_object)
@@ -1979,7 +1980,8 @@ if True:
 
             except (IntegrityError, BadParameterException) as e:
                 # fill in page with previously submitted values
-                map(lambda x: context.__setitem__(x, request.POST.get(x, "-- missing")), mandatory_fields)
+                for field in mandatory_fields:
+                    context.__setitem__(field, request.POST.get(field, "-- missing"))
                 if isinstance(e, IntegrityError) and "username" in str(e):
                     context['alert'] = "Your chosen username is already used"
                 else:
@@ -2073,21 +2075,11 @@ if True:
             "prj" : {"name": prj.name, },
             "buildrequests" : prj.build_set.filter(outcome=Build.IN_PROGRESS),
             "builds" : Build.get_recent(prj),
-            "layers" :  map(lambda x: {
-                        "id": x.layercommit.pk,
-                        "orderid": x.pk,
-                        "name" : x.layercommit.layer.name,
-                        "vcs_url": x.layercommit.layer.vcs_url,
-                        "vcs_reference" : x.layercommit.get_vcs_reference(),
-                        "url": x.layercommit.layer.layer_index_url,
-                        "layerdetailurl": x.layercommit.get_detailspage_url(prj.pk),
-                # This branch name is actually the release
-                        "branch" : { "name" : x.layercommit.get_vcs_reference(), "layersource" : x.layercommit.up_branch.layer_source.name if x.layercommit.up_branch != None else None}},
-                    prj.projectlayer_set.all().order_by("id")),
-            "targets" : map(lambda x: {"target" : x.target, "task" : x.task, "pk": x.pk}, prj.projecttarget_set.all()),
-            "variables": map(lambda x: (x.name, x.value), prj.projectvariable_set.all()),
+            "layers" : layers,
+            "targets" : [{"target" : x.target, "task" : x.task, "pk": x.pk} for x in prj.projecttarget_set.all()],
+            "variables": [(x.name, x.value) for x in prj.projectvariable_set.all()],
             "freqtargets": freqtargets[:5],
-            "releases": map(lambda x: {"id": x.pk, "name": x.name, "description":x.description}, Release.objects.all()),
+            "releases": [{"id": x.pk, "name": x.name, "description":x.description} for x in Release.objects.all()],
             "project_html": 1,
             "recipesTypeAheadUrl": reverse('xhr_recipestypeahead', args=(prj.pk,)),
             "projectBuildsUrl": reverse('projectbuilds', args=(prj.pk,)),
@@ -2178,8 +2170,7 @@ if True:
                     retval.append(project)
 
             return response({"error":"ok",
-                             "rows" : map( _lv_to_dict(prj),
-                                          map(lambda x: x.layercommit, retval ))
+                             "rows": [_lv_to_dict(prj) for y in [x.layercommit for x in retval]]
                             })
 
         except Exception as e:
@@ -2225,7 +2216,7 @@ if True:
 
             return_data = {
                 "error": "ok",
-                'configvars'   : map(lambda x: (x.name, x.value, x.pk), configvars_query),
+                'configvars': [(x.name, x.value, x.pk) for x in configvars_query]
                }
             try:
                 return_data['distro'] = ProjectVariable.objects.get(project = prj, name = "DISTRO").value,
@@ -2848,7 +2839,7 @@ if True:
                 "vcs_url": dep.layer.vcs_url,
                 "vcs_reference": dep.get_vcs_reference()} \
                 for dep in layer_version.get_alldeps(project.id)]},
-            'projectlayers': map(lambda prjlayer: prjlayer.layercommit.id, ProjectLayer.objects.filter(project=project))
+            'projectlayers': [player.layercommit.id for player in ProjectLayer.objects.filter(project=project)]
         }
 
         return context
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 18/28] toaster: use re.sub() instead of translate()
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (15 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 17/28] toaster: replace map with list comprehensions Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 19/28] toaster: read timezone files in binary mode Richard Purdie
                   ` (9 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

translate has different set of parameters in python 3 and
can't be used the way it's used in toaster api module.

Replacing it with re.sub() should make the code work in
both python 2  and python 3.

[YOCTO #9584]

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/toastergui/api.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/toaster/toastergui/api.py b/lib/toaster/toastergui/api.py
index 42901f7..961b594 100644
--- a/lib/toaster/toastergui/api.py
+++ b/lib/toaster/toastergui/api.py
@@ -18,6 +18,7 @@
 
 
 # Temporary home for the UI's misc API
+import re
 
 from orm.models import Project, ProjectTarget, Build
 from bldcontrol.models import BuildRequest
@@ -92,7 +93,7 @@ class XhrBuildRequest(View):
         if 'targets' in request.POST:
             ProjectTarget.objects.filter(project = project).delete()
             s = str(request.POST['targets'])
-            for t in s.translate(None, ";%|\"").split(" "):
+            for t in re.sub(r'[;%|"]', '', s).split(" "):
                 if ":" in t:
                     target, task = t.split(":")
                 else:
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 19/28] toaster: read timezone files in binary mode
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (16 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 18/28] toaster: use re.sub() instead of translate() Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 20/28] toaster: moved import bb.server.xmlrpc Richard Purdie
                   ` (8 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Used 'rb' mode to open files to avoid unicode error when code
runs on python 3:
   UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80
                       in position 44: invalid start byte

[YOCTO #9584]

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/toastermain/settings.py | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/lib/toaster/toastermain/settings.py b/lib/toaster/toastermain/settings.py
index c7edff2..3dfa2b2 100644
--- a/lib/toaster/toastermain/settings.py
+++ b/lib/toaster/toastermain/settings.py
@@ -87,17 +87,16 @@ else:
             try:
                 import pytz
                 from pytz.exceptions import UnknownTimeZoneError
-                pass
                 try:
                     if pytz.timezone(zonename) is not None:
-                        zonefilelist[hashlib.md5(open(filepath).read()).hexdigest()] = zonename
+                        zonefilelist[hashlib.md5(open(filepath, 'rb').read()).hexdigest()] = zonename
                 except UnknownTimeZoneError as ValueError:
                     # we expect timezone failures here, just move over
                     pass
             except ImportError:
-                zonefilelist[hashlib.md5(open(filepath).read()).hexdigest()] = zonename
+                zonefilelist[hashlib.md5(open(filepath, 'rb').read()).hexdigest()] = zonename
 
-    TIME_ZONE = zonefilelist[hashlib.md5(open('/etc/localtime').read()).hexdigest()]
+    TIME_ZONE = zonefilelist[hashlib.md5(open('/etc/localtime', 'rb').read()).hexdigest()]
 
 # Language code for this installation. All choices can be found here:
 # http://www.i18nguy.com/unicode/language-identifiers.html
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 20/28] toaster: moved import bb.server.xmlrpc
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (17 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 19/28] toaster: read timezone files in binary mode Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 21/28] toaster: fix migrations Richard Purdie
                   ` (7 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Moved import xmlrpc module to the place where it's used
to avoid toaster crashes when importing bitbake code.

NOTE: This patch is made to be able to partly test toaster
with bitbake from master. It can be removed as soon as bb.server.xmlrpc
is ported to python 3.

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/bldcontrol/bbcontroller.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/toaster/bldcontrol/bbcontroller.py b/lib/toaster/bldcontrol/bbcontroller.py
index 9bddadb..912f67b 100644
--- a/lib/toaster/bldcontrol/bbcontroller.py
+++ b/lib/toaster/bldcontrol/bbcontroller.py
@@ -30,7 +30,6 @@ from bldcontrol.models import BuildEnvironment, BRLayer, BRVariable, BRTarget, B
 # load Bitbake components
 path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
 sys.path.insert(0, path)
-import bb.server.xmlrpc
 
 class BitbakeController(object):
     """ This is the basic class that controlls a bitbake server.
@@ -38,6 +37,7 @@ class BitbakeController(object):
     """
 
     def __init__(self, be):
+        import bb.server.xmlrpc
         self.connection = bb.server.xmlrpc._create_server(be.bbaddress,
                                                           int(be.bbport))[0]
 
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 21/28] toaster: fix migrations
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (18 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 20/28] toaster: moved import bb.server.xmlrpc Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 22/28] toaster: use knotty when working with bitbake server Richard Purdie
                   ` (6 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Created two new migrations due to the models changes made
in python3 patchset.

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 .../migrations/0004_auto_20160509_0946.py          | 34 +++++++++
 .../orm/migrations/0007_auto_20160509_0946.py      | 89 ++++++++++++++++++++++
 2 files changed, 123 insertions(+)
 create mode 100644 bitbake/lib/toaster/bldcontrol/migrations/0004_auto_20160509_0946.py
 create mode 100644 bitbake/lib/toaster/orm/migrations/0007_auto_20160509_0946.py

diff --git a/bitbake/lib/toaster/bldcontrol/migrations/0004_auto_20160509_0946.py b/bitbake/lib/toaster/bldcontrol/migrations/0004_auto_20160509_0946.py
new file mode 100644
index 0000000..4a70e2d
--- /dev/null
+++ b/bitbake/lib/toaster/bldcontrol/migrations/0004_auto_20160509_0946.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('bldcontrol', '0003_add_cancelling_state'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='buildenvironment',
+            name='bbstate',
+            field=models.IntegerField(choices=[(0, 'stopped'), (1, 'started')], default=0),
+        ),
+        migrations.AlterField(
+            model_name='buildenvironment',
+            name='betype',
+            field=models.IntegerField(choices=[(0, 'local')]),
+        ),
+        migrations.AlterField(
+            model_name='buildenvironment',
+            name='lock',
+            field=models.IntegerField(choices=[(0, 'free'), (1, 'lock'), (2, 'running')], default=0),
+        ),
+        migrations.AlterField(
+            model_name='buildrequest',
+            name='state',
+            field=models.IntegerField(choices=[(0, 'created'), (1, 'queued'), (2, 'in progress'), (3, 'completed'), (4, 'failed'), (5, 'deleted'), (6, 'cancelling'), (7, 'archive')], default=0),
+        ),
+    ]
diff --git a/bitbake/lib/toaster/orm/migrations/0007_auto_20160509_0946.py b/bitbake/lib/toaster/orm/migrations/0007_auto_20160509_0946.py
new file mode 100644
index 0000000..d866105
--- /dev/null
+++ b/bitbake/lib/toaster/orm/migrations/0007_auto_20160509_0946.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('orm', '0006_add_cancelled_state'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='build',
+            name='outcome',
+            field=models.IntegerField(choices=[(0, 'Succeeded'), (1, 'Failed'), (2, 'In Progress'), (3, 'Cancelled')], default=2),
+        ),
+        migrations.AlterField(
+            model_name='helptext',
+            name='area',
+            field=models.IntegerField(choices=[(0, 'variable')]),
+        ),
+        migrations.AlterField(
+            model_name='layer',
+            name='summary',
+            field=models.TextField(null=True, help_text='One-line description of the layer', default=None),
+        ),
+        migrations.AlterField(
+            model_name='layer_version',
+            name='local_path',
+            field=models.FilePathField(max_length=1024, default='/'),
+        ),
+        migrations.AlterField(
+            model_name='layersource',
+            name='sourcetype',
+            field=models.IntegerField(choices=[(0, 'local'), (1, 'layerindex'), (2, 'imported')]),
+        ),
+        migrations.AlterField(
+            model_name='logmessage',
+            name='level',
+            field=models.IntegerField(choices=[(0, 'info'), (1, 'warn'), (2, 'error'), (3, 'critical'), (-1, 'toaster exception')], default=0),
+        ),
+        migrations.AlterField(
+            model_name='package',
+            name='installed_name',
+            field=models.CharField(max_length=100, default=''),
+        ),
+        migrations.AlterField(
+            model_name='package_dependency',
+            name='dep_type',
+            field=models.IntegerField(choices=[(0, 'depends'), (1, 'depends'), (3, 'recommends'), (2, 'recommends'), (4, 'suggests'), (5, 'provides'), (6, 'replaces'), (7, 'conflicts')]),
+        ),
+        migrations.AlterField(
+            model_name='recipe_dependency',
+            name='dep_type',
+            field=models.IntegerField(choices=[(0, 'depends'), (1, 'rdepends')]),
+        ),
+        migrations.AlterField(
+            model_name='release',
+            name='branch_name',
+            field=models.CharField(max_length=50, default=''),
+        ),
+        migrations.AlterField(
+            model_name='releasedefaultlayer',
+            name='layer_name',
+            field=models.CharField(max_length=100, default=''),
+        ),
+        migrations.AlterField(
+            model_name='target_file',
+            name='inodetype',
+            field=models.IntegerField(choices=[(1, 'regular'), (2, 'directory'), (3, 'symlink'), (4, 'socket'), (5, 'fifo'), (6, 'character'), (7, 'block')]),
+        ),
+        migrations.AlterField(
+            model_name='task',
+            name='outcome',
+            field=models.IntegerField(choices=[(-1, 'Not Available'), (0, 'Succeeded'), (1, 'Covered'), (2, 'Cached'), (3, 'Prebuilt'), (4, 'Failed'), (5, 'Empty')], default=-1),
+        ),
+        migrations.AlterField(
+            model_name='task',
+            name='script_type',
+            field=models.IntegerField(choices=[(0, 'N/A'), (2, 'Python'), (3, 'Shell')], default=0),
+        ),
+        migrations.AlterField(
+            model_name='task',
+            name='sstate_result',
+            field=models.IntegerField(choices=[(0, 'Not Applicable'), (1, 'File not in cache'), (2, 'Failed'), (3, 'Succeeded')], default=0),
+        ),
+    ]
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 22/28] toaster: use knotty when working with bitbake server
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (19 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 21/28] toaster: fix migrations Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 23/28] xmlrpc: add parameter use_builtin_types Richard Purdie
                   ` (5 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Using empty BITBAKE_UI environment variable causes bitbake server
to fail with the error:
    FATAL: Unable to import extension module "" from bb.ui
           Valid extension modules: knotty or toasterui\n'

Used BITBAKE_UI="knotty" when starting and stoping bitbake
server to solve above issue.

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/bldcontrol/localhostbecontroller.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/toaster/bldcontrol/localhostbecontroller.py b/lib/toaster/bldcontrol/localhostbecontroller.py
index 3da8be4..9364900 100644
--- a/lib/toaster/bldcontrol/localhostbecontroller.py
+++ b/lib/toaster/bldcontrol/localhostbecontroller.py
@@ -287,7 +287,7 @@ class LocalhostBEController(BuildEnvironmentController):
 
         # run bitbake server from the clone
         bitbake = os.path.join(self.pokydirname, 'bitbake', 'bin', 'bitbake')
-        self._shellcmd('bash -c \"source %s %s; BITBAKE_UI="" %s --read %s '
+        self._shellcmd('bash -c \"source %s %s; BITBAKE_UI="knotty" %s --read %s '
                        '--server-only -t xmlrpc -B 0.0.0.0:0\"' % (oe_init,
                        builddir, bitbake, confpath), self.be.sourcedir)
 
@@ -324,7 +324,7 @@ class LocalhostBEController(BuildEnvironmentController):
                                      'bitbake')
         self._shellcmd(['bash -c \"(TOASTER_BRBE="%s" BBSERVER="0.0.0.0:-1" '
                         '%s %s -u toasterui --token="" >>%s 2>&1;'
-                        'BITBAKE_UI="" BBSERVER=0.0.0.0:-1 %s -m)&\"' \
+                        'BITBAKE_UI="knotty" BBSERVER=0.0.0.0:-1 %s -m)&\"' \
                         % (brbe, local_bitbake, bbtargets, log, bitbake)],
                         builddir, nowait=True)
 
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 23/28] xmlrpc: add parameter use_builtin_types
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (20 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 22/28] toaster: use knotty when working with bitbake server Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 24/28] Revert "buildinfohelper: work around unicode exceptions" Richard Purdie
                   ` (4 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Added use_builtin_types parameter to XMLRPCProxyServer.__init__
to fix this error:
    ERROR: Could not connect to server 0.0.0.0:37132
    : __init__() got an unexpected keyword argument 'use_builtin_types'

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/bb/server/xmlrpc.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/bb/server/xmlrpc.py b/lib/bb/server/xmlrpc.py
index 146ca17..57c59a8 100644
--- a/lib/bb/server/xmlrpc.py
+++ b/lib/bb/server/xmlrpc.py
@@ -180,7 +180,7 @@ class XMLRPCProxyServer(BaseImplServer):
     """ not a real working server, but a stub for a proxy server connection
 
     """
-    def __init__(self, host, port):
+    def __init__(self, host, port, use_builtin_types=True):
         self.host = host
         self.port = port
 
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 24/28] Revert "buildinfohelper: work around unicode exceptions"
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (21 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 23/28] xmlrpc: add parameter use_builtin_types Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 25/28] toaster: use // operator instead of / Richard Purdie
                   ` (3 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

This commit causes buildinfohelper to crash when run on python 3
as python 3 doesn't have unicode builtin function.

This reverts commit 7a309d964a86f3474eaab7df6d438ed797f935c0.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/bb/ui/buildinfohelper.py | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/lib/bb/ui/buildinfohelper.py b/lib/bb/ui/buildinfohelper.py
index cea53e0..d59d6a5 100644
--- a/lib/bb/ui/buildinfohelper.py
+++ b/lib/bb/ui/buildinfohelper.py
@@ -494,7 +494,7 @@ class ORMWrapper(object):
             parent_obj = self._cached_get(Target_File, target = target_obj, path = parent_path, inodetype = Target_File.ITYPE_DIRECTORY)
             tf_obj = Target_File.objects.create(
                         target = target_obj,
-                        path = unicode(path, 'utf-8'),
+                        path = path,
                         size = size,
                         inodetype = Target_File.ITYPE_DIRECTORY,
                         permission = permission,
@@ -519,7 +519,7 @@ class ORMWrapper(object):
 
             tf_obj = Target_File.objects.create(
                         target = target_obj,
-                        path = unicode(path, 'utf-8'),
+                        path = path,
                         size = size,
                         inodetype = inodetype,
                         permission = permission,
@@ -550,9 +550,7 @@ class ORMWrapper(object):
                 filetarget_path = "/".join(fcpl)
 
             try:
-                filetarget_obj = Target_File.objects.get(
-                                     target = target_obj,
-                                     path = unicode(filetarget_path, 'utf-8'))
+                filetarget_obj = Target_File.objects.get(target = target_obj, path = filetarget_path)
             except Target_File.DoesNotExist:
                 # we might have an invalid link; no way to detect this. just set it to None
                 filetarget_obj = None
@@ -561,7 +559,7 @@ class ORMWrapper(object):
 
             tf_obj = Target_File.objects.create(
                         target = target_obj,
-                        path = unicode(path, 'utf-8'),
+                        path = path,
                         size = size,
                         inodetype = Target_File.ITYPE_SYMLINK,
                         permission = permission,
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 25/28] toaster: use // operator instead of /
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (22 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 24/28] Revert "buildinfohelper: work around unicode exceptions" Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 26/28] toaster: don't use mru.count in the template Richard Purdie
                   ` (2 subsequent siblings)
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Division operator works differently in Python 3. It results in
float unlike in Python 2, where it results in int.

Explicitly used "floor division" operator instead of 'division'
operator. This should make the code to result in integer under
both pythons.

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/orm/models.py                          | 2 +-
 lib/toaster/toastergui/templatetags/projecttags.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index dd64664..25bc1db 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -424,7 +424,7 @@ class Build(models.Model):
         tf = Task.objects.filter(build = self)
         tfc = tf.count()
         if tfc > 0:
-            completeper = tf.exclude(order__isnull=True).count()*100/tfc
+            completeper = tf.exclude(order__isnull=True).count()*100 // tfc
         else:
             completeper = 0
         return completeper
diff --git a/lib/toaster/toastergui/templatetags/projecttags.py b/lib/toaster/toastergui/templatetags/projecttags.py
index 75f2261..1d68036 100644
--- a/lib/toaster/toastergui/templatetags/projecttags.py
+++ b/lib/toaster/toastergui/templatetags/projecttags.py
@@ -90,7 +90,7 @@ def whitespace_space_filter(value, arg):
 def divide(value, arg):
     if int(arg) == 0:
         return -1
-    return int(value) / int(arg)
+    return int(value) // int(arg)
 
 @register.filter
 def multiply(value, arg):
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 26/28] toaster: don't use mru.count in the template
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (23 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 25/28] toaster: use // operator instead of / Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 27/28] toaster: fix urllib imports Richard Purdie
  2016-06-01 12:36 ` [PATCH 28/28] toaster: use python3 explicitly Richard Purdie
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

mru is a list. It has a count method, but it differs from the
one for the queryset. Using mru.count causes 'Latest project builds'
section to silently disappear when toaster runs on python 3.

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/toastergui/templates/mrb_section.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/toaster/toastergui/templates/mrb_section.html b/lib/toaster/toastergui/templates/mrb_section.html
index 212998a..2780ac5 100644
--- a/lib/toaster/toastergui/templates/mrb_section.html
+++ b/lib/toaster/toastergui/templates/mrb_section.html
@@ -18,7 +18,7 @@
   });
 </script>
 
-{%if mru and mru.count > 0%}
+{%if mru %}
 
   {%if mrb_type == 'project' %}
       <h2 class="page-header">
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 27/28] toaster: fix urllib imports
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (24 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 26/28] toaster: don't use mru.count in the template Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  2016-06-01 12:36 ` [PATCH 28/28] toaster: use python3 explicitly Richard Purdie
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Some functions have been moved from urllib to urllib.parse
in python 3. Modifying the code to import unquote, urlencode and
unquote_plus from urllib.parse if import from urllib fails should
make it working on both python 2 and python 3.

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/toaster/toastergui/views.py   | 9 ++++++---
 lib/toaster/toastergui/widgets.py | 6 +++++-
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 8e920f0..0510897 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -200,16 +200,19 @@ def _verify_parameters(g, mandatory_parameters):
     return None
 
 def _redirect_parameters(view, g, mandatory_parameters, *args, **kwargs):
-    import urllib
+    try:
+        from urllib import unquote, urlencode
+    except ImportError:
+        from urllib.parse import unquote, urlencode
     url = reverse(view, kwargs=kwargs)
     params = {}
     for i in g:
         params[i] = g[i]
     for i in mandatory_parameters:
         if not i in params:
-            params[i] = urllib.unquote(str(mandatory_parameters[i]))
+            params[i] = unquote(str(mandatory_parameters[i]))
 
-    return redirect(url + "?%s" % urllib.urlencode(params), permanent = False, **kwargs)
+    return redirect(url + "?%s" % urlencode(params), permanent = False, **kwargs)
 
 class RedirectException(Exception):
     def __init__(self, view, g, mandatory_parameters, *args, **kwargs):
diff --git a/lib/toaster/toastergui/widgets.py b/lib/toaster/toastergui/widgets.py
index 19850fb..551c33c 100644
--- a/lib/toaster/toastergui/widgets.py
+++ b/lib/toaster/toastergui/widgets.py
@@ -38,7 +38,11 @@ import json
 import collections
 import operator
 import re
-import urllib
+
+try:
+    from urllib import unquote_plus
+except ImportError:
+    from urllib.parse import unquote_plus
 
 import logging
 logger = logging.getLogger("toaster")
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH 28/28] toaster: use python3 explicitly
  2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
                   ` (25 preceding siblings ...)
  2016-06-01 12:36 ` [PATCH 27/28] toaster: fix urllib imports Richard Purdie
@ 2016-06-01 12:36 ` Richard Purdie
  26 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 12:36 UTC (permalink / raw)
  To: bitbake-devel

From: Ed Bartosh <ed.bartosh@linux.intel.com>

Explicitly used python3 as default python for oe builds
will continue to be python2.

[YOCTO #9584]

Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 bin/toaster | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/bin/toaster b/bin/toaster
index 05b7935..be1bb9c 100755
--- a/bin/toaster
+++ b/bin/toaster
@@ -111,7 +111,7 @@ stop_system()
 
 verify_prereq() {
     # Verify Django version
-    reqfile=$(python -c "import os; print(os.path.realpath('$BBBASEDIR/toaster-requirements.txt'))")
+    reqfile=$(python3 -c "import os; print(os.path.realpath('$BBBASEDIR/toaster-requirements.txt'))")
     exp='s/Django\([><=]\+\)\([^,]\+\),\([><=]\+\)\(.\+\)/'
     exp=$exp'import sys,django;version=django.get_version().split(".");'
     exp=$exp'sys.exit(not (version \1 "\2".split(".") and version \3 "\4".split(".")))/p'
@@ -135,7 +135,7 @@ else
 fi
 
 export BBBASEDIR=`dirname $TOASTER`/..
-MANAGE=$BBBASEDIR/lib/toaster/manage.py
+MANAGE="python3 $BBBASEDIR/lib/toaster/manage.py"
 OEROOT=`dirname $TOASTER`/../..
 
 # this is the configuraton file we are using for toaster
@@ -162,7 +162,7 @@ fi
 
 if [ "$TOASTER_CONF" = "" ]; then
     TOASTER_CONF="$TEMPLATECONF/toasterconf.json"
-    export TOASTER_CONF=$(python -c "import os; print(os.path.realpath('$TOASTER_CONF'))")
+    export TOASTER_CONF=$(python3 -c "import os; print(os.path.realpath('$TOASTER_CONF'))")
 fi
 
 if [ ! -f $TOASTER_CONF ]; then
-- 
2.5.0



^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH 09/28] data/event/siggen: Fix various exceptions [FIXME]
  2016-06-01 12:36 ` [PATCH 09/28] data/event/siggen: Fix various exceptions [FIXME] Richard Purdie
@ 2016-06-01 13:00   ` Richard Purdie
  0 siblings, 0 replies; 29+ messages in thread
From: Richard Purdie @ 2016-06-01 13:00 UTC (permalink / raw)
  To: bitbake-devel

On Wed, 2016-06-01 at 13:36 +0100, Richard Purdie wrote:
> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
> ---
>  lib/bb/data.py   | 1 +
>  lib/bb/event.py  | 3 ++-
>  lib/bb/siggen.py | 3 ++-
>  3 files changed, 5 insertions(+), 2 deletions(-)

Ignore this patch, it shouldn't have been included. This one notes some
areas we really need to improve the exception handling. I need to clean
it up and resend as a separate issue.

Cheers,

Richard



^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2016-06-01 13:00 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-01 12:36 [PATCH 01/28] bitbake/bs4: Upgrade 4.3.2 -> 4.4.1 (python 3 version) Richard Purdie
2016-06-01 12:36 ` [PATCH 02/28] bitbake/pyinotify.py: Upgrade to py3 version Richard Purdie
2016-06-01 12:36 ` [PATCH 03/28] bitbake: Drop futures usage since we're python 3 Richard Purdie
2016-06-01 12:36 ` [PATCH 04/28] bitbake: Convert to python 3 megacommit This needs breaking up into smaller changes Richard Purdie
2016-06-01 12:36 ` [PATCH 05/28] data_smart: Simplify ExpansionError exception Richard Purdie
2016-06-01 12:36 ` [PATCH 06/28] lib/bb: Set required python 3 version to 3.4.0 Richard Purdie
2016-06-01 12:36 ` [PATCH 07/28] image-writer/goggle: Disable pygtkcompat problems Richard Purdie
2016-06-01 12:36 ` [PATCH 08/28] google/image-writer: Drop since bitrotting and no longer used Richard Purdie
2016-06-01 12:36 ` [PATCH 09/28] data/event/siggen: Fix various exceptions [FIXME] Richard Purdie
2016-06-01 13:00   ` Richard Purdie
2016-06-01 12:36 ` [PATCH 10/28] toaster: use force_text instead of force_bytes Richard Purdie
2016-06-01 12:36 ` [PATCH 11/28] toaster: get rid of using reduce Richard Purdie
2016-06-01 12:36 ` [PATCH 12/28] toaster: fix imports to work for python 3 Richard Purdie
2016-06-01 12:36 ` [PATCH 13/28] toaster: fix local imports Richard Purdie
2016-06-01 12:36 ` [PATCH 14/28] toaster: modified list of dependencies Richard Purdie
2016-06-01 12:36 ` [PATCH 15/28] toaster: use decode('utf-8') for binary data Richard Purdie
2016-06-01 12:36 ` [PATCH 16/28] toaster: use items and range instead of old APIs Richard Purdie
2016-06-01 12:36 ` [PATCH 17/28] toaster: replace map with list comprehensions Richard Purdie
2016-06-01 12:36 ` [PATCH 18/28] toaster: use re.sub() instead of translate() Richard Purdie
2016-06-01 12:36 ` [PATCH 19/28] toaster: read timezone files in binary mode Richard Purdie
2016-06-01 12:36 ` [PATCH 20/28] toaster: moved import bb.server.xmlrpc Richard Purdie
2016-06-01 12:36 ` [PATCH 21/28] toaster: fix migrations Richard Purdie
2016-06-01 12:36 ` [PATCH 22/28] toaster: use knotty when working with bitbake server Richard Purdie
2016-06-01 12:36 ` [PATCH 23/28] xmlrpc: add parameter use_builtin_types Richard Purdie
2016-06-01 12:36 ` [PATCH 24/28] Revert "buildinfohelper: work around unicode exceptions" Richard Purdie
2016-06-01 12:36 ` [PATCH 25/28] toaster: use // operator instead of / Richard Purdie
2016-06-01 12:36 ` [PATCH 26/28] toaster: don't use mru.count in the template Richard Purdie
2016-06-01 12:36 ` [PATCH 27/28] toaster: fix urllib imports Richard Purdie
2016-06-01 12:36 ` [PATCH 28/28] toaster: use python3 explicitly Richard Purdie

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.