Browse Source

Fixed Ports in main code

rparedis 3 years ago
parent
commit
8afc1d4640

+ 1 - 1
README.md

@@ -11,7 +11,7 @@
   
  Purpose: simulates CBD models in Python.
 
- Requires Python version >= 2.7 or >= 3.6
+ Requires Python version >= 3.4
 ```
 ### Installation and Updates
 The simulator can easily be installed with one of the following commands, from

+ 1 - 1
doc/index.rst

@@ -19,7 +19,7 @@ to model complex systems of equations.
   * Claudio Gomes
   * Randy Paredis
 
-:Python Version: :code:`>= 2.7` or :code:`>= 3.2`
+:Python Version: :code:`>= 3.4`
 
 
 .. note::

+ 73 - 13
src/CBD/Core.py

@@ -1,4 +1,5 @@
 import re
+import CBD.naivelog
 import logging
 from copy import deepcopy
 from .util import enum, hash64
@@ -7,21 +8,21 @@ from collections import namedtuple
 InputLink = namedtuple("InputLink", ["block", "output_port"])
 Signal = namedtuple("Signal", ["time", "value"])
 
-# Error logging
-level = enum(WARNING=1, ERROR=2, CRITICAL=3)
-class LoggingHandler(logging.StreamHandler):
-    def emit(self, record):
-        super().emit(record)
-        if record.levelno >= logging.ERROR:
-            raise SystemExit(1)
-
-logging.basicConfig(handlers=[LoggingHandler()])
-
-
 epsilon = 0.001
 
 class Port:
+    """
+    Defines a port of a block.
+
+    Args:
+        name (str):                 The name of the port.
+        direction (Port.Direction): The direction of the port; i.e., if it is
+                                    an input or an output port.
+        block (BaseBlock):          The block to which this port belongs.
+    """
     Direction = enum(IN=0, OUT=1)
+    """Possible port directions."""
+
     def __init__(self, name, direction, block):
         self.name = name
         self.direction = direction
@@ -34,39 +35,80 @@ class Port:
         return "Port <%s> (%s)" % (self.name, repr(self.block))
 
     def set(self, value):
+        """
+        Sets the value of the port and transfers this to all outgoing connections.
+        This will mainly update the current history.
+
+        Args:
+            value (float):  The new value of the port.
+        """
         self.__history.append(value)
         for conn in self.__outgoing:
             conn.transfer()
 
     def get(self):
+        """
+        Obtains the current value of the port.
+        """
         return self.__history[-1]
 
     def clear(self):
+        """
+        Clears all port signal history.
+        """
         self.__history.clear()
 
     def getOutgoing(self):
+        """
+        Obtains all outgoing connections from this port.
+        """
         return self.__outgoing
 
     def getIncoming(self):
+        """
+        Obtains the incoming connection to this port.
+        """
         return self.__incoming
 
     def getHistory(self):
+        """
+        Obtains all historic information about the signals of this port.
+        """
         return self.__history
 
     def getPreviousPortClosure(self):
+        """
+        Find the previous port to which this port is connected that has no incoming connections.
+        Hierarchical blocks and useless connections will be solved using this method.
+
+        I.e., it obtains the port whos signal changes will eventually transfer to this port.
+        """
         inc = self.getIncoming().source
         while inc.getIncoming() is not None:
             inc = inc.getIncoming().source
         return inc
 
     def count(self):
+        """
+        Counts how often a signal was changed on this port.
+        """
         return len(self.__history)
 
     def _rewind(self):
+        """
+        Rewinds the port to the previous iteration.
+        """
         self.__history.pop()
 
     @staticmethod
     def connect(source, target):
+        """
+        Connects two ports together.
+
+        Args:
+            source (Port):  The source port from which a connection starts.
+            target (Port):  The target port where a connection ends.
+        """
         conn = Connector(source, target)
         source.__outgoing.append(conn)
         assert target.__incoming is None, "Fan-IN is not allowed!"
@@ -74,12 +116,27 @@ class Port:
 
     @staticmethod
     def disconnect(source, target):
+        """
+        Disconnects two previously connected ports.
+
+        Args:
+            source (Port):  The source port from which a connection starts.
+            target (Port):  The target port where a connection ends.
+        """
         conn = target.__incoming
+        assert conn.source == source
         source.__outgoing.remove(conn)
         target.__incoming = None
 
 
 class Connector:
+    """
+    A connection that links two ports together.
+
+    Args:
+        source (Port):  The source port from which a connection starts.
+        target (Port):  The target port where a connection ends.
+    """
     def __init__(self, source, target):
         self.source = source
         self.target = target
@@ -88,6 +145,9 @@ class Connector:
         return "Connector <%s ==> %s>" % (repr(self.source), repr(self.target))
 
     def transfer(self):
+        """
+        Transfers the signal from the source port to the target port.
+        """
         self.target.set(self.source.get())
 
 
@@ -108,7 +168,7 @@ class BaseBlock:
             self.setBlockName(name)
         if re.match(r"[^a-zA-Z0-9_]", self.__block_name):
             logger = logging.getLogger("CBD")
-            logger.warning("Block names should only contain alphanumeric characters or underscores.")
+            logger.warning("Block names should only contain alphanumeric characters or underscores.", extra={"block": self})
 
         # The output signals produced by this block are encoded in a dictionary.
         # The key of this dictionary is the name of the output port.
@@ -590,7 +650,7 @@ class CBD(BaseBlock):
                 self.__clock = block
         else:
             logger = logging.getLogger("CBD")
-            logger.warning("Warning: did not add this block as it has the same name '%s' as an already existing block/port" % block.getBlockName())
+            logger.warning("Did not add this block as it has the same name '%s' as an already existing block/port." % block.getBlockName(), extra={ "block":self})
 
     def removeBlock(self, block):
         """

+ 13 - 12
src/CBD/lib/std.py

@@ -1,7 +1,7 @@
 """
 This file contains the standard library for CBD building blocks.
 """
-from CBD.Core import BaseBlock, CBD, level
+from CBD.Core import BaseBlock, CBD
 import math
 
 __all__ = ['ConstantBlock', 'NegatorBlock', 'InverterBlock', 'AdderBlock', 'ProductBlock', 'ModuloBlock',
@@ -795,10 +795,10 @@ class LoggingBlock(BaseBlock):
 	A simple logging block that logs a string if the input is *truthy*.
 
 	Args:
-		block_name (str):       The name of the block.
-		string (str):           The string to log.
-		lev (CBD.Core.level):   The level at which to log. Defaults to :data:`CBD.Core.level.WARNING`.
-		logger (str):           The name of the logger. Defaults to :code:`CBDLogger`.
+		block_name (str):   The name of the block.
+		string (str):       The string to log.
+		lev (int):          The level at which to log. Defaults to :data:`logging.WARNING`.
+		logger (str):       The name of the logger. Defaults to :code:`CBDLogger`.
 
 	:Input Ports:
 		**IN1** -- The input.
@@ -806,7 +806,7 @@ class LoggingBlock(BaseBlock):
 	See Also:
 		`Truthy and Falsy values in Python <https://docs.python.org/3/library/stdtypes.html#truth-value-testing>`_
 	"""
-	def __init__(self, block_name, string, lev=level.WARNING, logger="CBDLogger"):
+	def __init__(self, block_name, string, lev=logging.WARNING, logger="CBD"):
 		BaseBlock.__init__(self, block_name, ["IN1"], [])
 		self.__string = string
 		self.__logger = logging.getLogger(logger)
@@ -814,12 +814,13 @@ class LoggingBlock(BaseBlock):
 
 	def compute(self, curIteration):
 		if self.getInputSignal(curIteration, "IN1").value:
-			if self.__lev == level.WARNING:
-				self.__logger.warning("Time " + str(self.getClock().getTime(curIteration)) + ": " + self.__string)
-			elif self.__lev == level.ERROR:
-				self.__logger.error("Time " + str(self.getClock().getTime(curIteration)) + ": " + self.__string)
-			elif self.__lev == level.CRITICAL:
-				self.__logger.critical("Time " + str(self.getClock().getTime(curIteration)) + ": " + self.__string)
+			simtime = str(self.getClock().getTime(-1))
+			if self.__lev == logging.WARNING:
+				self.__logger.warning("[" + simtime + "]  " + self.__string, extra={"block": self})
+			elif self.__lev == logging.ERROR:
+				self.__logger.error("[" + simtime + "]  " + self.__string, extra={"block": self})
+			elif self.__lev == logging.CRITICAL:
+				self.__logger.critical("[" + simtime + "]  " + self.__string, extra={"block": self})
 
 
 class TimeBlock(BaseBlock):

+ 2 - 2
src/CBD/loopsolvers/linearsolver.py

@@ -33,7 +33,7 @@ class LinearSolver(Solver):
 	"""
 	def checkValidity(self, path, component):
 		if not self.__isLinear(component):
-			self._logger.critical("Cannot solve non-linear algebraic loop.\nSelf: {}\nComponents: {}".format(path, component))
+			self._logger.critical("Cannot solve non-linear algebraic loop.\nSelf: {}\nComponents: {}".format(path, component), extra={"blockname": "Algebraic Loop"})
 
 	def __isLinear(self, strongComponent):
 		"""Determines if an algebraic loop describes a linear equation or not.
@@ -104,7 +104,7 @@ class LinearSolver(Solver):
 		try:
 			return self.get_matrix(strongComponent, sep, known)
 		except ValueError as e:
-			self._logger.exception(str(e))
+			self._logger.exception(str(e), extra={"block": "Strong Component"})
 
 	@staticmethod
 	def get_matrix(strongComponent, sep='.', known=None):

+ 56 - 273
src/CBD/naivelog.py

@@ -1,284 +1,67 @@
 """
 Helper module to allow for simplified logging.
 
+Note:
+	The logic in this file is based on https://stackoverflow.com/a/384125
+
 Warning:
 	In the future, this module may be removed. Its logic will
 	be separated into some tracers and a SysLog logger.
 """
-import os
-import sys
-import datetime
-
-DEBUG, INFO, WARNING, ERROR, FATAL = 0, 1, 2, 3, 4
-"""Level identifier."""
+import logging
 
-def strToLevel(elvl):
-	"""
-	Go from string identifier of a logging level to the
-	level identifier.
+__all__ = ["logger"]
 
-	Args:
-		elvl (str): Must be one of :code:`DEBUG`, :code:`INFO`,
-					:code:`WARNING`, :code:`ERROR` or :code:`FATAL`,
-					case-sensitive.
+#These are the sequences need to get colored ouput
+BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
+RESET_SEQ = "\033[0m"
+COLOR_SEQ = "\033[1;%dm"
+BOLD_SEQ = "\033[1m"
 
-	See Also:
-		- :func:`levelToStr`
-		- :func:`levelToShortStr`
-	"""
-	if elvl == "DEBUG":
-		return DEBUG
-	if elvl == "INFO":
-		return INFO
-	if elvl == "WARNING":
-		return WARNING
-	if elvl == "ERROR":
-		return ERROR
-	if elvl == "FATAL":
-		return FATAL
+def formatter_message(message, use_color = True):
+	if use_color:
+		message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ)
 	else:
-		return None
-
-def levelToStr(lvl):
-	"""
-	Go from a level identifier to the corresponding string representation.
-
-	Args:
-		lvl (int):  The level identifier.
-
-	See Also:
-		- :func:`strToLevel`
-		- :func:`levelToShortStr`
-	"""
-	if lvl == DEBUG:
-		return "DEBUG"
-	if lvl == INFO:
-		return "INFO"
-	if lvl == WARNING:
-		return "WARNING"
-	if lvl == ERROR:
-		return "ERROR"
-	if lvl == FATAL:
-		return "FATAL"
-	return None
-
-
-def levelToShortStr(lvl):
-	"""
-	Go from a level identifier to a short, representative string.
-
-	Args:
-		lvl (int):  The level identifier.
-
-	See Also:
-		- :func:`strToLevel`
-		- :func:`levelToStr`
-	"""
-	if lvl == DEBUG:
-		return "DBUG"
-	if lvl == INFO:
-		return "INFO"
-	if lvl == WARNING:
-		return "WARN"
-	if lvl == ERROR:
-		return "ERROR"
-	if lvl == FATAL:
-		return "FATAL"
-	return None
-
-class Logger:
-	"""
-	A simple logging class.
-
-	Args:
-		modulename (str):   The name of the module.
-		level (int):        Lowest level for the logger to output.
-		crashlevel (int):   Level at which the logger should terminate.
-	"""
-	def __init__(self, modulename, level, crashlevel):
-		self.__modulename = modulename
-		self.__level = level
-		self.__crashlevel = crashlevel
-
-	def debug(self, mainstr, *args, **kwargs):
-		"""
-		Send :code:`DEBUG` message. Wrapper around the :func:`log` function.
-
-		Args:
-			mainstr (str):  The main message information.
-			*args:          List of arguments for formatting the :code:`mainstr`.
-			**kwargs:       List of keyword arguments for formatting the
-							:code:`mainstr`.
-		"""
-		self.log(DEBUG, mainstr, *args, **kwargs)
-
-	def info(self, mainstr, *args, **kwargs):
-		"""
-		Send :code:`INFO` message. Wrapper around the :func:`log` function.
-
-		Args:
-			mainstr (str):  The main message information.
-			*args:          List of arguments for formatting the :code:`mainstr`.
-			**kwargs:       List of keyword arguments for formatting the
-							:code:`mainstr`.
-		"""
-		self.log(INFO, mainstr, *args, **kwargs)
-
-	def warning(self, mainstr, *args, **kwargs):
-		"""
-		Send :code:`WARNING` message. Wrapper around the :func:`log` function.
-
-		Args:
-			mainstr (str):  The main message information.
-			*args:          List of arguments for formatting the :code:`mainstr`.
-			**kwargs:       List of keyword arguments for formatting the
-							:code:`mainstr`.
-		"""
-		self.log(WARNING, mainstr, *args, **kwargs)
-
-	def error(self, mainstr, *args, **kwargs):
-		"""
-		Send :code:`ERROR` message. Wrapper around the :func:`log` function.
-
-		Args:
-			mainstr (str):  The main message information.
-			*args:          List of arguments for formatting the :code:`mainstr`.
-			**kwargs:       List of keyword arguments for formatting the
-							:code:`mainstr`.
-		"""
-		self.log(ERROR, mainstr, *args, **kwargs)
-
-	def fatal(self, mainstr, *args, **kwargs):
-		"""
-		Send :code:`FATAL` message. Wrapper around the :func:`log` function.
-
-		Args:
-			mainstr (str):  The main message information.
-			*args:          List of arguments for formatting the :code:`mainstr`.
-			**kwargs:       List of keyword arguments for formatting the
-							:code:`mainstr`.
-		"""
-		self.log(FATAL, mainstr, *args, **kwargs)
-
-	def log(self, level, mainstr, *args, **kwargs):
-		"""
-		Send a message.
-
-		Args:
-			level (int):    Level at which there must be logged.
-			mainstr (str):  The main message information.
-			*args:          List of arguments for formatting the :code:`mainstr`.
-			**kwargs:       List of keyword arguments for formatting the
-							:code:`mainstr`.
-
-		See Also:
-			- :func:`debug`
-			- :func:`info`
-			- :func:`warning`
-			- :func:`error`
-			- :func:`fatal`
-		"""
-		if level >= self.__level:
-			sys.stdout.write(self.formatmsg(level,str(mainstr).format(*args, **kwargs)))
-
-		if level >= self.__crashlevel:
-			os._exit(1)
-
-	def setLevel(self, level):
-		"""
-		Sets the level to a new value.
-
-		Args:
-			level (int):    The new level.
-		"""
-		self.__level = level
-
-	def formatmsg(self, level, mainstr):
-		"""
-		Formats the message, used internally.
-
-		Args:
-			level (int):    The level of the message.
-			mainstr (str):  The main message to print.
-		"""
-		class bcolors:
-			"""
-			Helper class to set the colors to the terminal.
-			"""
-			HEADER = '\033[95m'
-			OKBLUE = '\033[94m'
-			OKGREEN = '\033[92m'
-			WARNING = '\033[93m'
-			FAIL = '\033[91m'
-			ENDC = '\033[0m'
-
-
-		col = bcolors.OKGREEN
-		if level >= WARNING:
-			col = bcolors.WARNING
-		if level >= ERROR:
-			col = bcolors.FAIL
-
-		return "{startcol}[{now:%H:%M:%S.%f} {module} {lvl}] {mainstr}{endcol}\n".format(
-				lvl=levelToShortStr(level),
-				module=self.__modulename,
-				now=datetime.date.today(),
-				mainstr=mainstr,
-				startcol=col,
-				endcol=bcolors.ENDC)
-
-defaultLogLevel = INFO
-"""Default level at which logging is enabled."""
-
-defaultCrashLevel = FATAL
-"""Default level at which a termination must occur."""
-
-def getAbstractLogLevel(env, default):
-	"""
-	Obtains the log level from the environment variables.
-
-	Args:
-		env (str):      Variable name.
-		default (Any):  The default value if the variable does not
-						exist.
-	"""
-	elvl = os.environ[env] if env in os.environ else ''
-
-	lvl = strToLevel(elvl)
-	if lvl:
-		return lvl
-	else:
-		return default
-
-def getLogLevel():
-	"""
-	Gets the logging level from the environment.
-	"""
-	return getAbstractLogLevel('NAIVE_LOGLEVEL', defaultLogLevel)
-
-def getCrashLevel():
-	"""
-	Gets the crash level from the environment.
-	"""
-	return getAbstractLogLevel('NAIVE_CRASHLEVEL', defaultCrashLevel)
-
-def getLogger(modulename):
-	"""
-	Gets the logger for a certain module.
-
-	Args:
-		modulename (str):   The module's name.
-	"""
-	return Logger(modulename, getLogLevel(), getCrashLevel())
-
-if __name__ == "__main__":
-	l = getLogger('testmodule')
-	l.info("bla")
-	l.info("test nummer {}{}", 2, " is good")
-	l.info("test {hier} is ook ok", hier=3, daar=4)
-	l.info("should not see this")
-
-
-	l2 = getLogger('testmodule.m2')
-	l2.info("More info")
-	l2.info("and even more")
+		message = message.replace("$RESET", "").replace("$BOLD", "")
+	return message
+
+COLORS = {
+	'WARNING': YELLOW,
+	'INFO': CYAN,
+	'DEBUG': BLUE,
+	'CRITICAL': RED,
+	'ERROR': RED
+}
+
+class ColoredFormatter(logging.Formatter):
+	def __init__(self, msg, use_color = True):
+		logging.Formatter.__init__(self, msg)
+		self.use_color = use_color
+
+	def format(self, record):
+		levelname = record.levelname
+		if self.use_color and levelname in COLORS:
+			levelname_color = COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ
+			record.levelname = levelname_color
+		if hasattr(record, "block"):
+			record.simtime = record.block.getClock().getTime(-1)
+			record.blockname = record.block.getPath()
+		return logging.Formatter.format(self, record)
+
+class LoggingHandler(logging.StreamHandler):
+	FORMAT = "[$BOLD%(name)s$RESET][%(blockname)-18s][$BOLD%(levelname)-18s$RESET]  %(message)s"
+	COLOR_FORMAT = formatter_message(FORMAT, True)
+
+	def __init__(self, stream=None):
+		logging.StreamHandler.__init__(self, stream)
+		color_formatter = ColoredFormatter(self.COLOR_FORMAT)
+		self.setFormatter(color_formatter)
+
+	def emit(self, record):
+		super().emit(record)
+		if record.levelno >= logging.ERROR:
+			raise SystemExit(1)
+
+logger = logging.getLogger("CBD")
+logger.setLevel(logging.DEBUG)
+logger.addHandler(LoggingHandler())

+ 6 - 3
src/CBD/realtime/accurate_time.py

@@ -10,9 +10,12 @@ def time():
 	Gets the correct system time, independent of the Python version or the
 	platform.
 	"""
-	if PYTHON_VERSION == 2 and sys.platform == "win32":
-		# better precision on windows, but deprecated since 3.3
-		return python_time.clock()
+	if PYTHON_VERSION == 2:
+		if sys.platform == "win32":
+			# better precision on windows, but deprecated since 3.3
+			return python_time.clock()
+		else:
+			python_time.time()
 	else:
 		return python_time.perf_counter()
 

+ 0 - 1
src/CBD/realtime/threadingBackend.py

@@ -13,7 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from ..util import enum
 import threading
 
 

+ 9 - 9
src/test/sortedGraphCBDTest.py

@@ -33,10 +33,10 @@ class SortedGraphCBDTest(unittest.TestCase):
 		depGraph = createDepGraph(self.CBD, 0)
 		sortedGraph = self.scheduler.obtain(depGraph, 1, 0.0)
 		
-		self.assertEqual(len(sortedGraph), 5)
+		self.assertEqual(len(sortedGraph), 3)
 		self.assertEqual(sortedGraph[0][0], const)
-		self.assertEqual(sortedGraph[2][0], negCbd)
-		self.assertEqual(sortedGraph[4][0], neg)
+		self.assertEqual(sortedGraph[1][0], negCbd)
+		self.assertEqual(sortedGraph[2][0], neg)
 		
 	def testSortedGraph2(self):
 		CBDAdder = CBD("adderCBD", input_ports = ["in1", "in2"], output_ports = ["outAdd"])
@@ -64,7 +64,7 @@ class SortedGraphCBDTest(unittest.TestCase):
 		tester = self
 		ag = lambda x,y: tester.assertTrue(comps.index(x) > comps.index(y))
 		
-		self.assertEqual(len(sortedGraph), 7)
+		self.assertEqual(len(sortedGraph), 4)
 		ag(addCBD, const1)
 		ag(addCBD, const2)
 		ag(neg, addCBD)
@@ -96,7 +96,7 @@ class SortedGraphCBDTest(unittest.TestCase):
 		tester = self
 		ag = lambda x,y: tester.assertTrue(comps.index(x) > comps.index(y))
 
-		self.assertEqual(len(sortedGraph), 7)
+		self.assertEqual(len(sortedGraph), 4)
 		ag(negCbd, const)
 		ag(invCBD, const)
 		ag(add, negCbd)
@@ -123,8 +123,8 @@ class SortedGraphCBDTest(unittest.TestCase):
 		depGraph = createDepGraph(self.CBD, 0)
 		sortedGraph = self.scheduler.obtain(depGraph, 1, 0.0)
 				
-		self.assertEqual(len(sortedGraph), 5)
-		self.assertEqual(len(sortedGraph[4]), 3)
+		self.assertEqual(len(sortedGraph), 3)
+		self.assertEqual(len(sortedGraph[2]), 3)
 		
 	def testSortedGraph5(self):
 		CBDStrong = CBD("strongCBD", input_ports = ["inC1", "inC2", "inA"], output_ports = ["out1", "out2"])
@@ -150,8 +150,8 @@ class SortedGraphCBDTest(unittest.TestCase):
 		depGraph = createDepGraph(self.CBD, 0)
 		sortedGraph = self.scheduler.obtain(depGraph, 1, 0.0)
 				
-		self.assertEqual(len(sortedGraph), 5)
-		self.assertEqual(len(sortedGraph[4]), 6)
+		self.assertEqual(len(sortedGraph), 3)
+		self.assertEqual(len(sortedGraph[2]), 3)
 
 	def testAlwaysRecomputeSchedule(self):
 		self.scheduler.recompte_at = True

+ 4 - 4
src/test/stdCBDTest.py

@@ -444,19 +444,19 @@ class StdCBDTestCase(unittest.TestCase):
 
 	def testLoggingBlockError(self):
 		self.CBD.addBlock(ConstantBlock(block_name="One", value=1))
-		self.CBD.addBlock(LoggingBlock("L1", "Logging block test were level is error", level.ERROR))
+		self.CBD.addBlock(LoggingBlock("L1", "Logging block test were level is error", logging.ERROR))
 		self.CBD.addConnection("One", "L1")
-		self._run(1)
+		self.assertRaises(SystemExit, self._run, 1)
 
 	def testLoggingBlockWarning(self):
 		self.CBD.addBlock(ConstantBlock(block_name="One", value=1))
-		self.CBD.addBlock(LoggingBlock("L1", "Logging block test were level is warning", level.WARNING))
+		self.CBD.addBlock(LoggingBlock("L1", "Logging block test were level is warning", logging.WARNING))
 		self.CBD.addConnection("One", "L1")
 		self._run(1)
 
 	def testLoggingBlockFatal(self):
 		self.CBD.addBlock(ConstantBlock(block_name="One", value=1))
-		self.CBD.addBlock(LoggingBlock("L1", "Logging block test were level is fatal", level.CRITICAL))
+		self.CBD.addBlock(LoggingBlock("L1", "Logging block test were level is fatal", logging.CRITICAL))
 		self.CBD.addConnection("One", "L1")
 		self.assertRaises(SystemExit, self._run, 1)