* [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
# ", 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="<a>"></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ñata"></p>', expect)
self.assertSoupEquals('<p id="piñata"></p>', expect)
self.assertSoupEquals('<p id="piñata"></p>', expect)
self.assertSoupEquals('<p id="piñ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ñata</p>", expect)
self.assertSoupEquals("<p>piñata</p>", expect)
self.assertSoupEquals("<p>piñ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("�", expect)
self.assertSoupEquals("�", expect)
self.assertSoupEquals("�", 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><<sacré bleu!>></p>"
- expected = u"<p><<sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>></p>"
+ expected = "<p><<sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>></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> </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><<sacré bleu!>></p>"
- expected = u"<p><<sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>></p>".encode("utf-8")
+ expected = "<p><<sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>></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"< < hey > >" 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∀\N{SNOWMAN}õbar")
+ "foo∀\N{SNOWMAN}õ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><<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)
decoded = soup.decode(formatter="minimal")
# The < is converted back into < but the e-with-acute is left alone.
self.assertEqual(
decoded,
self.document_for(
- u"<b><<Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>></b>"))
+ "<b><<Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>></b>"))
def test_formatter_html(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)
decoded = soup.decode(formatter="html")
self.assertEqual(
@@ -1315,49 +1403,49 @@ class TestSubstitutions(SoupTest):
self.document_for("<b><<Sacré bleu!>></b>"))
def test_formatter_minimal(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)
decoded = soup.decode(formatter="minimal")
# The < is converted back into < but the e-with-acute is left alone.
self.assertEqual(
decoded,
self.document_for(
- u"<b><<Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>></b>"))
+ "<b><<Sacr\N{LATIN SMALL LETTER E WITH ACUTE} bleu!>></b>"))
def test_formatter_null(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)
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><foo></b><b>bar</b>"
+ markup = "<b><foo></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&c=é">e</a>'
+ expect_minimal = '<a href="http://a.com?a=b&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&c=é">e</a>'
+ expect_html = '<a href="http://a.com?a=b&c=é">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>☃</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"><b>Build configuration</b></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"><b>Repository</b></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"><b>Repositories</b></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"><b>Additional packages</b></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.