Merge pull request #1644 from fonttools/Fix-CFF2-PrivateDict_bug

[varLib.cff] Fix important bug in merging CFF2 PrivateDicts.
diff --git a/Lib/fontTools/misc/loggingTools.py b/Lib/fontTools/misc/loggingTools.py
index aacc085..b4b9a9b 100644
--- a/Lib/fontTools/misc/loggingTools.py
+++ b/Lib/fontTools/misc/loggingTools.py
@@ -250,8 +250,8 @@
 	upon exiting the with-statement.
 
 	>>> import logging
-	>>> log = logging.getLogger("fontTools")
-	>>> configLogger(level="DEBUG", format="%(message)s", stream=sys.stdout)
+	>>> log = logging.getLogger("my-fancy-timer-logger")
+	>>> configLogger(logger=log, level="DEBUG", format="%(message)s", stream=sys.stdout)
 	>>> with Timer(log, 'do something'):
 	...     time.sleep(0.01)
 	Took ... to do something
@@ -530,67 +530,6 @@
 	return decorator
 
 
-class LastResortLogger(logging.Logger):
-	""" Adds support for 'lastResort' handler introduced in Python 3.2.
-	It allows to print messages to sys.stderr even when no explicit handler
-	was configured.
-	To enable it, you can do:
-
-		import logging
-		logging.lastResort = StderrHandler(logging.WARNING)
-		logging.setLoggerClass(LastResortLogger)
-	"""
-
-	def callHandlers(self, record):
-		# this is the same as Python 3.5's logging.Logger.callHandlers
-		c = self
-		found = 0
-		while c:
-			for hdlr in c.handlers:
-				found = found + 1
-				if record.levelno >= hdlr.level:
-					hdlr.handle(record)
-			if not c.propagate:
-				c = None  # break out
-			else:
-				c = c.parent
-		if found == 0:
-			if logging.lastResort:
-				if record.levelno >= logging.lastResort.level:
-					logging.lastResort.handle(record)
-			elif (
-				logging.raiseExceptions
-				and not self.manager.emittedNoHandlerWarning
-			):
-				sys.stderr.write(
-					"No handlers could be found for logger"
-					' "%s"\n' % self.name
-				)
-				self.manager.emittedNoHandlerWarning = True
-
-
-class StderrHandler(logging.StreamHandler):
-	""" This class is like a StreamHandler using sys.stderr, but always uses
-	whateve sys.stderr is currently set to rather than the value of
-	sys.stderr at handler construction time.
-	"""
-
-	def __init__(self, level=logging.NOTSET):
-		"""
-		Initialize the handler.
-		"""
-		logging.Handler.__init__(self, level)
-
-	@property
-	def stream(self):
-		# the try/execept avoids failures during interpreter shutdown, when
-		# globals are set to None
-		try:
-			return sys.stderr
-		except AttributeError:
-			return __import__("sys").stderr
-
-
 if __name__ == "__main__":
 	import doctest
 	sys.exit(doctest.testmod(optionflags=doctest.ELLIPSIS).failed)
diff --git a/Tests/feaLib/builder_test.py b/Tests/feaLib/builder_test.py
index eb80078..3d852af 100644
--- a/Tests/feaLib/builder_test.py
+++ b/Tests/feaLib/builder_test.py
@@ -513,10 +513,9 @@
         addOpenTypeFeatures(font, tree)
         assert "GSUB" in font
 
-    @unittest.skipIf(sys.version_info[0:2] < (3, 4),
-                     "assertLogs() was introduced in 3.4")
     def test_unsupported_subtable_break(self):
-        with self.assertLogs(level='WARNING') as logs:
+        logger = logging.getLogger("fontTools.feaLib.builder")
+        with CapturingLogHandler(logger, level='WARNING') as captor:
             self.build(
                 "feature test {"
                 "    pos a 10;"
@@ -524,9 +523,10 @@
                 "    pos b 10;"
                 "} test;"
             )
-        self.assertEqual(logs.output,
-                ['WARNING:fontTools.feaLib.builder:<features>:1:32: '
-                 'unsupported "subtable" statement for lookup type'])
+
+        captor.assertRegex(
+            '<features>:1:32: unsupported "subtable" statement for lookup type'
+        )
 
     def test_skip_featureNames_if_no_name_table(self):
         features = (
diff --git a/Tests/misc/loggingTools_test.py b/Tests/misc/loggingTools_test.py
index 18b71b1..fd64b8b 100644
--- a/Tests/misc/loggingTools_test.py
+++ b/Tests/misc/loggingTools_test.py
@@ -6,15 +6,11 @@
     configLogger,
     ChannelsFilter,
     LogMixin,
-    StderrHandler,
-    LastResortLogger,
-    _resetExistingLoggers,
 )
 import logging
 import textwrap
 import time
 import re
-import sys
 import pytest
 
 
@@ -179,32 +175,3 @@
     assert isinstance(b.log, logging.Logger)
     assert a.log.name == "loggingTools_test.A"
     assert b.log.name == "loggingTools_test.B"
-
-
-@pytest.mark.skipif(sys.version_info[:2] > (2, 7), reason="only for python2.7")
-@pytest.mark.parametrize(
-    "reset", [True, False], ids=["reset", "no-reset"]
-)
-def test_LastResortLogger(reset, capsys, caplog):
-    current = logging.getLoggerClass()
-    msg = "The quick brown fox jumps over the lazy dog"
-    try:
-        if reset:
-            _resetExistingLoggers()
-        else:
-            caplog.set_level(logging.ERROR, logger="myCustomLogger")
-        logging.lastResort = StderrHandler(logging.WARNING)
-        logging.setLoggerClass(LastResortLogger)
-        logger = logging.getLogger("myCustomLogger")
-        logger.error(msg)
-    finally:
-        del logging.lastResort
-        logging.setLoggerClass(current)
-
-    captured = capsys.readouterr()
-    if reset:
-        assert msg in captured.err
-        msg not in caplog.text
-    else:
-        msg in caplog.text
-        msg not in captured.err
diff --git a/Tests/ttLib/woff2_test.py b/Tests/ttLib/woff2_test.py
index 55c4b77..c270295 100644
--- a/Tests/ttLib/woff2_test.py
+++ b/Tests/ttLib/woff2_test.py
@@ -145,6 +145,7 @@
 	def test_reconstruct_loca(self):
 		woff2Reader = WOFF2Reader(self.file)
 		reconstructedData = woff2Reader['loca']
+		self.font.getTableData("glyf")  # 'glyf' needs to be compiled before 'loca'
 		self.assertEqual(self.font.getTableData('loca'), reconstructedData)
 		self.assertTrue(hasattr(woff2Reader.tables['glyf'], 'data'))
 
@@ -360,7 +361,7 @@
 	def setUpClass(cls):
 		cls.font = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False, flavor="woff2")
 		cls.font.importXML(OTX)
-		cls.tags = [t for t in cls.font.keys() if t != 'GlyphOrder']
+		cls.tags = sorted(t for t in cls.font.keys() if t != 'GlyphOrder')
 		cls.numTables = len(cls.tags)
 		cls.file = BytesIO(CFF_WOFF2.getvalue())
 		cls.file.seek(0, 2)
@@ -518,7 +519,7 @@
 	def setUpClass(cls):
 		cls.font = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False, flavor="woff2")
 		cls.font.importXML(TTX)
-		cls.tags = [t for t in cls.font.keys() if t != 'GlyphOrder']
+		cls.tags = sorted(t for t in cls.font.keys() if t != 'GlyphOrder')
 		cls.numTables = len(cls.tags)
 		cls.file = BytesIO(TT_WOFF2.getvalue())
 		cls.file.seek(0, 2)
diff --git a/tox.ini b/tox.ini
index 2e8d9ee..6a01501 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,6 +6,7 @@
 deps =
     cov: coverage>=4.3
     pytest
+    pytest-randomly
     -rrequirements.txt
 extras =
     ufo