Repository: pybel/pybel

Issue:
Allow unqualified translocations by default

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/pybel/pybel/pull/336?src=pr&el=h1) Report
> Merging [#336](https://codecov.io/gh/pybel/pybel/pull/336?src=pr&el=desc) into [develop](https://codecov.io/gh/pybel/pybel/commit/288e131c0b8d5463cefb5fe0beadec218013cf14?src=pr&el=desc) will **decrease** coverage by `0.02%`.
> The diff coverage is `84.61%`.

[![Impacted file tree graph](https://codecov.io/gh/pybel/pybel/pull/336/graphs/tree.svg?width=650&token=J7joRTRygG&height=150&src=pr)](https://codecov.io/gh/pybel/pybel/pull/336?src=pr&el=tree)

```diff
@@             Coverage Diff             @@
##           develop     #336      +/-   ##
===========================================
- Coverage    86.56%   86.53%   -0.03%     
===========================================
  Files          129      129              
  Lines         6230     6232       +2     
  Branches       911      912       +1     
===========================================
  Hits          5393     5393              
- Misses         643      645       +2     
  Partials       194      194
```


| [Impacted Files](https://codecov.io/gh/pybel/pybel/pull/336?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/pybel/struct/graph.py](https://codecov.io/gh/pybel/pybel/pull/336/diff?src=pr&el=tree#diff-c3JjL3B5YmVsL3N0cnVjdC9ncmFwaC5weQ==) | `90.09% <0%> (-0.58%)` | :arrow_down: |
| [src/pybel/io/line\_utils.py](https://codecov.io/gh/pybel/pybel/pull/336/diff?src=pr&el=tree#diff-c3JjL3B5YmVsL2lvL2xpbmVfdXRpbHMucHk=) | `70.65% <100%> (ø)` | :arrow_up: |
| [src/pybel/parser/parse\_bel.py](https://codecov.io/gh/pybel/pybel/pull/336/diff?src=pr&el=tree#diff-c3JjL3B5YmVsL3BhcnNlci9wYXJzZV9iZWwucHk=) | `96.24% <100%> (ø)` | :arrow_up: |
| [src/pybel/parser/\_\_init\_\_.py](https://codecov.io/gh/pybel/pybel/pull/336/diff?src=pr&el=tree#diff-c3JjL3B5YmVsL3BhcnNlci9fX2luaXRfXy5weQ==) | `100% <100%> (ø)` | :arrow_up: |
| [src/pybel/io/lines.py](https://codecov.io/gh/pybel/pybel/pull/336/diff?src=pr&el=tree#diff-c3JjL3B5YmVsL2lvL2xpbmVzLnB5) | `100% <100%> (ø)` | :arrow_up: |
| [src/pybel/io/jgif.py](https://codecov.io/gh/pybel/pybel/pull/336/diff?src=pr&el=tree#diff-c3JjL3B5YmVsL2lvL2pnaWYucHk=) | `74.07% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pybel/pybel/pull/336?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pybel/pybel/pull/336?src=pr&el=footer). Last update [288e131...0f0e4ed](https://codecov.io/gh/pybel/pybel/pull/336?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.flake8 b/.flake8
index 944adea5..539dd807 100644
--- a/.flake8
+++ b/.flake8
@@ -11,6 +11,7 @@ exclude =
     .git,
     __pycache__,
     docs/source/conf.py,
+    src/pybel/cli.py
     build,
     dist,
     tests/fixtures/*,
diff --git a/docs/source/parser.rst b/docs/source/parser.rst
index 8f6e98f8..db9c214e 100644
--- a/docs/source/parser.rst
+++ b/docs/source/parser.rst
@@ -28,7 +28,7 @@ Identifier Parser
 
 BEL Parser
 ----------
-.. autoclass:: pybel.parser.parse_bel.BelParser
+.. autoclass:: pybel.parser.parse_bel.BELParser
     :members:
 
 Sub-Parsers
diff --git a/src/pybel/cli.py b/src/pybel/cli.py
index 23ad0207..308af507 100644
--- a/src/pybel/cli.py
+++ b/src/pybel/cli.py
@@ -23,7 +23,9 @@ from click_plugins import with_plugins
 
 from .canonicalize import to_bel
 from .constants import get_cache_connection
-from .io import from_lines, to_csv, to_graphml, to_gsea, to_json_file, to_neo4j, to_pickle, to_sif
+from .io import (
+    from_lines, from_path, from_pickle, to_csv, to_graphml, to_gsea, to_json_file, to_neo4j, to_pickle, to_sif,
+)
 from .io.web import _get_host
 from .manager import Manager
 from .manager.database_io import to_database
@@ -52,6 +54,33 @@ def main():
     """PyBEL Command Line."""
 
 
+@main.command()
+@click.argument('path')
+@connection_option
+def compile(path, connection):
+    """Compile a BEL script to a graph pickle."""
+    manager = Manager(connection=connection)
+    graph = from_path(path, manager=manager, use_tqdm=True)
+    to_pickle(graph, '{path}.pickle'.format(path=path))
+    graph.describe()
+
+
+@main.command()
+@click.argument('path', type=click.File('rb'))
+def summarize(path):
+    """Summarize a pre-compiled graph."""
+    graph = from_pickle(path)
+    graph.describe()
+
+
+@main.command()
+@click.argument('path', type=click.File('rb'))
+def errors(path):
+    """Summarize errors from a pre-compiled graph."""
+    graph = from_pickle(path)
+    _warning_helper(graph.warnings)
+
+
 @main.command()
 @click.option('-p', '--path', type=click.File('r'), default=sys.stdin, help='Input BEL file file path')
 @connection_option
@@ -67,15 +96,15 @@ def main():
 @click.option('-s', '--store', is_flag=True, help='Stores to database specified by -c')
 @click.option('--allow-naked-names', is_flag=True, help="Enable lenient parsing for naked names")
 @click.option('--allow-nested', is_flag=True, help="Enable lenient parsing for nested statements")
-@click.option('--allow-unqualified-translocations', is_flag=True,
-              help="Enable lenient parsing for unqualified translocations")
+@click.option('--disallow-unqualified-translocations', is_flag=True,
+              help="Disable lenient parsing for unqualified translocations")
 @click.option('--no-identifier-validation', is_flag=True, help='Turn off identifier validation')
 @click.option('--no-citation-clearing', is_flag=True, help='Turn off citation clearing')
 @click.option('-r', '--required-annotations', multiple=True, help='Specify multiple required annotations')
 def convert(path, connection, csv, sif, gsea, graphml, json, pickle, bel, neo, neo_context, store, allow_naked_names,
-            allow_nested, allow_unqualified_translocations, no_identifier_validation, no_citation_clearing,
+            allow_nested, disallow_unqualified_translocations, no_identifier_validation, no_citation_clearing,
             required_annotations):
-    """Convert BEL."""
+    """Compile a BEL script and convert."""
     manager = Manager(connection=connection)
 
     g = from_lines(
@@ -83,7 +112,7 @@ def convert(path, connection, csv, sif, gsea, graphml, json, pickle, bel, neo, n
         manager=manager,
         allow_nested=allow_nested,
         allow_naked_names=allow_naked_names,
-        allow_unqualified_translocations=allow_unqualified_translocations,
+        disallow_unqualified_translocations=disallow_unqualified_translocations,
         citation_clearing=(not no_citation_clearing),
         required_annotations=required_annotations,
         no_identifier_validation=no_identifier_validation,
@@ -338,5 +367,42 @@ def summarize(manager):
     click.echo('Annotation entries: {}'.format(manager.count_annotation_entries()))
 
 
+def _warning_helper(warnings, sep='\t'):
+    # Exit if no warnings
+    if not warnings:
+        click.echo('Congratulations! No warnings.')
+        sys.exit(0)
+
+    max_line_width = max(
+        len(str(line_number))
+        for line_number, _, _, _ in warnings
+    )
+
+    max_warning_width = max(
+        len(exc.__class__.__name__)
+        for _, _, exc, _ in warnings
+    )
+
+    s1 = '{:>' + str(max_line_width) + '}' + sep
+    s2 = '{:>' + str(max_warning_width) + '}' + sep
+
+    def _make_line(line_number, line, exc):
+        s = click.style(s1.format(line_number), fg='blue', bold=True)
+
+        if exc.__class__.__name__.endswith('Error'):
+            s += click.style(s2.format(exc.__class__.__name__), fg='red')
+        else:
+            s += click.style(s2.format(exc.__class__.__name__), fg='yellow')
+
+        s += click.style(line, bold=True) + sep
+        s += click.style(str(exc))
+        return s
+
+    click.echo_via_pager('\n'.join(
+        _make_line(line_number, line, exc)
+        for line_number, line, exc, _ in warnings
+    ))
+
+
 if __name__ == '__main__':
     main()
diff --git a/src/pybel/io/jgif.py b/src/pybel/io/jgif.py
index e44af73e..993fe4c8 100644
--- a/src/pybel/io/jgif.py
+++ b/src/pybel/io/jgif.py
@@ -16,7 +16,7 @@ from ..constants import (
     ANNOTATIONS, CITATION, CITATION_REFERENCE, CITATION_TYPE, EVIDENCE, FUNCTION, METADATA_AUTHORS, METADATA_CONTACT,
     METADATA_INSERT_KEYS, METADATA_LICENSES, RELATION, UNQUALIFIED_EDGES,
 )
-from ..parser import BelParser
+from ..parser import BELParser
 from ..parser.exc import NakedNameWarning
 from ..struct import BELGraph
 
@@ -219,7 +219,7 @@ def from_jgif(graph_jgif_dict):
             if key in metadata:
                 graph.document[key] = metadata[key]
 
-    parser = BelParser(graph)
+    parser = BELParser(graph)
     parser.bel_term.addParseAction(parser.handle_term)
 
     for node in root['nodes']:
diff --git a/src/pybel/io/line_utils.py b/src/pybel/io/line_utils.py
index 894a925f..2f132c71 100644
--- a/src/pybel/io/line_utils.py
+++ b/src/pybel/io/line_utils.py
@@ -14,7 +14,7 @@ from tqdm import tqdm
 from ..constants import GRAPH_METADATA, INVERSE_DOCUMENT_KEYS, REQUIRED_METADATA
 from ..exceptions import PyBelWarning
 from ..manager import Manager
-from ..parser import BelParser, MetadataParser
+from ..parser import BELParser, MetadataParser
 from ..parser.exc import (
     BelSyntaxError, InconsistentDefinitionError, MalformedMetadataException, MissingMetadataException,
     VersionFormatWarning,
@@ -29,7 +29,7 @@ METADATA_LINE_RE = re.compile("(SET\s+DOCUMENT|DEFINE\s+NAMESPACE|DEFINE\s+ANNOT
 
 
 def parse_lines(graph, lines, manager=None, allow_nested=False, citation_clearing=True, use_tqdm=False,
-                no_identifier_validation=False, **kwargs):
+                no_identifier_validation=False, disallow_unqualified_translocations=False, **kwargs):
     """Parse an iterable of lines into this graph.
 
     Delegates to :func:`parse_document`, :func:`parse_definitions`, and :func:`parse_statements`.
@@ -41,6 +41,8 @@ def parse_lines(graph, lines, manager=None, allow_nested=False, citation_clearin
     :param bool citation_clearing: Should :code:`SET Citation` statements clear evidence and all annotations?
                                    Delegated to :class:`pybel.parser.ControlParser`
     :param bool use_tqdm: Use :mod:`tqdm` to show a progress bar?
+    :param bool no_identifier_validation: If true, turns off namespace validation
+    :param bool disallow_unqualified_translocations: If true, allow translocations without TO and FROM clauses.
 
     .. warning::
 
@@ -48,8 +50,9 @@ def parse_lines(graph, lines, manager=None, allow_nested=False, citation_clearin
         risk to reproducibility and validity of your results.
 
     :param bool allow_naked_names: If true, turns off naked namespace failures
-    :param bool allow_unqualified_translocations: If true, allow translocations without TO and FROM clauses.
-    :param bool no_identifier_validation: If true, turns off namespace validation
+    :param bool allow_redefinition: If true, doesn't fail on second definition of same name or annotation
+    :param bool allow_definition_failures: If true, allows parsing to continue if a terminology file download/parse
+     fails
     :param Optional[list[str]] required_annotations: Annotations that are required for all statements
     """
     docs, definitions, statements = split_file_to_annotations_and_definitions(lines)
@@ -77,7 +80,7 @@ def parse_lines(graph, lines, manager=None, allow_nested=False, citation_clearin
         use_tqdm=use_tqdm,
     )
 
-    bel_parser = BelParser(
+    bel_parser = BELParser(
         graph=graph,
         namespace_dict=metadata_parser.namespace_dict,
         annotation_dict=metadata_parser.annotation_dict,
@@ -87,7 +90,7 @@ def parse_lines(graph, lines, manager=None, allow_nested=False, citation_clearin
         citation_clearing=citation_clearing,
         skip_validation=no_identifier_validation,
         allow_naked_names=kwargs.get('allow_naked_names'),
-        allow_unqualified_translocations=kwargs.get('allow_unqualified_translocations'),
+        disallow_unqualified_translocations=disallow_unqualified_translocations,
         required_annotations=kwargs.get('required_annotations'),
     )
 
@@ -188,7 +191,7 @@ def parse_statements(graph, statements, bel_parser, use_tqdm=False):
 
     :param BELGraph graph: A BEL graph
     :param iter[str] statements: An enumerated iterable over the lines in the statements section of a BEL script
-    :param BelParser bel_parser: A BEL parser
+    :param BELParser bel_parser: A BEL parser
     :param bool use_tqdm: Use :mod:`tqdm` to show a progress bar?
     """
     parse_statements_start_time = time.time()
diff --git a/src/pybel/io/lines.py b/src/pybel/io/lines.py
index 0012abfb..3480f04e 100644
--- a/src/pybel/io/lines.py
+++ b/src/pybel/io/lines.py
@@ -19,7 +19,8 @@ __all__ = [
 log = logging.getLogger(__name__)
 
 
-def from_lines(lines, manager=None, allow_nested=False, citation_clearing=True, use_tqdm=False, **kwargs):
+def from_lines(lines, manager=None, allow_nested=False, citation_clearing=True, use_tqdm=False,
+               disallow_unqualified_translocations=False, **kwargs):
     """Load a BEL graph from an iterable over the lines of a BEL script.
 
     :param iter[str] lines: An iterable of strings (the lines in a BEL script)
@@ -28,8 +29,10 @@ def from_lines(lines, manager=None, allow_nested=False, citation_clearing=True,
     :param bool citation_clearing: Should :code:`SET Citation` statements clear evidence and all annotations?
                                 Delegated to :class:`pybel.parser.ControlParser`
     :param bool use_tqdm: If true, use tqdm for logging
-    :param dict kwargs: keyword arguments to :func:`pybel.io.line_utils.parse_lines`
+    :param bool disallow_unqualified_translocations: If true, allow translocations without TO and FROM clauses.
     :rtype: BELGraph
+
+    The remaining keyword arguments to :func:`pybel.io.line_utils.parse_lines`.
     """
     graph = BELGraph()
     parse_lines(
@@ -39,13 +42,14 @@ def from_lines(lines, manager=None, allow_nested=False, citation_clearing=True,
         allow_nested=allow_nested,
         citation_clearing=citation_clearing,
         use_tqdm=use_tqdm,
+        disallow_unqualified_translocations=disallow_unqualified_translocations,
         **kwargs
     )
     return graph
 
 
 def from_path(path, manager=None, allow_nested=False, citation_clearing=True, encoding='utf-8', use_tqdm=False,
-              **kwargs):
+              disallow_unqualified_translocations=False, **kwargs):
     """Load a BEL graph from a file resource. This function is a thin wrapper around :func:`from_lines`.
 
     :param str path: A file path
@@ -58,8 +62,10 @@ def from_path(path, manager=None, allow_nested=False, citation_clearing=True, en
                      list of standard encodings. For example, files starting with a UTF-8 BOM should use
                      :code:`utf_8_sig`
     :param bool use_tqdm: If true, use tqdm for logging
-    :param dict kwargs: keyword arguments to :func:`pybel.io.line_utils.parse_lines`
+    :param bool disallow_unqualified_translocations: If true, allow translocations without TO and FROM clauses.
     :rtype: BELGraph
+
+    The remaining keyword arguments to :func:`pybel.io.line_utils.parse_lines`.
     """
     log.info('Loading from path: %s', path)
     with codecs.open(os.path.expanduser(path), encoding=encoding) as file:
@@ -69,6 +75,7 @@ def from_path(path, manager=None, allow_nested=False, citation_clearing=True, en
             allow_nested=allow_nested,
             citation_clearing=citation_clearing,
             use_tqdm=use_tqdm,
+            disallow_unqualified_translocations=disallow_unqualified_translocations,
             **kwargs
         )
 
diff --git a/src/pybel/parser/__init__.py b/src/pybel/parser/__init__.py
index 8053a818..c71b10fa 100644
--- a/src/pybel/parser/__init__.py
+++ b/src/pybel/parser/__init__.py
@@ -3,7 +3,7 @@
 """The :mod:`pybel.parser` module contains utilities for parsing BEL documents and BEL statements."""
 
 from .modifiers import *
-from .parse_bel import BelParser
+from .parse_bel import BELParser
 from .parse_control import ControlParser
 from .parse_identifier import IdentifierParser
 from .parse_metadata import MetadataParser
diff --git a/src/pybel/parser/parse_bel.py b/src/pybel/parser/parse_bel.py
index a7e8f5ab..c5cfaf59 100644
--- a/src/pybel/parser/parse_bel.py
+++ b/src/pybel/parser/parse_bel.py
@@ -36,7 +36,7 @@ from ..dsl import cell_surface_expression, secretion
 from ..tokens import parse_result_to_dsl
 
 __all__ = [
-    'BelParser',
+    'BELParser',
     'modifier_po_to_dict',
 ]
 
@@ -215,13 +215,26 @@ equivalent_tag = one_of_tags(['eq', EQUIVALENT_TO], EQUIVALENT_TO)
 partof_tag = Keyword(PART_OF)
 
 
-class BelParser(BaseParser):
+class BELParser(BaseParser):
     """Build a parser backed by a given dictionary of namespaces"""
 
-    def __init__(self, graph, namespace_dict=None, annotation_dict=None, namespace_regex=None, annotation_regex=None,
-                 allow_naked_names=False, allow_nested=False, allow_unqualified_translocations=False,
-                 citation_clearing=True, skip_validation=False, autostreamline=True, required_annotations=None):
-        """
+    def __init__(
+            self,
+            graph,
+            namespace_dict=None,
+            annotation_dict=None,
+            namespace_regex=None,
+            annotation_regex=None,
+            allow_naked_names=False,
+            allow_nested=False,
+            disallow_unqualified_translocations=False,
+            citation_clearing=True,
+            skip_validation=False,
+            autostreamline=True,
+            required_annotations=None
+    ):
+        """Build a BEL parser.
+
         :param pybel.BELGraph graph: The BEL Graph to use to store the network
         :param namespace_dict: A dictionary of {namespace: {name: encoding}}. Delegated to
          :class:`pybel.parser.parse_identifier.IdentifierParser`
@@ -239,14 +252,16 @@ class BelParser(BaseParser):
          :class:`pybel.parser.parse_identifier.IdentifierParser`
         :param bool allow_nested: If true, turn off nested statement failures. Delegated to
          :class:`pybel.parser.parse_identifier.IdentifierParser`
-        :param bool allow_unqualified_translocations: If true, allow translocations without TO and FROM clauses.
+        :param bool disallow_unqualified_translocations: If true, allow translocations without TO and FROM clauses.
         :param bool citation_clearing: Should :code:`SET Citation` statements clear evidence and all annotations?
          Delegated to :class:`pybel.parser.ControlParser`
         :param bool autostreamline: Should the parser be streamlined on instantiation?
         :param Optional[list[str]] required_annotations: Optional list of required annotations
         """
         self.graph = graph
+
         self.allow_nested = allow_nested
+        self.disallow_unqualified_translocations = disallow_unqualified_translocations
 
         if skip_validation:
             self.control_parser = ControlParser(
@@ -430,7 +445,7 @@ class BelParser(BaseParser):
         self.translocation_legacy.addParseAction(handle_legacy_tloc)
         self.translocation_unqualified = nest(Group(self.simple_abundance)(TARGET))
 
-        if not allow_unqualified_translocations:
+        if self.disallow_unqualified_translocations:
             self.translocation_unqualified.setParseAction(self.handle_translocation_illegal)
 
         #: `2.5.1 <http://openbel.org/language/version_2.0/bel_specification_version_2.0.html#_translocations>`_
@@ -585,7 +600,7 @@ class BelParser(BaseParser):
         self.language = self.control_parser.language | self.statement
         self.language.setName('BEL')
 
-        super(BelParser, self).__init__(self.language, streamline=autostreamline)
+        super(BELParser, self).__init__(self.language, streamline=autostreamline)
 
     @property
     def namespace_dict(self):
@@ -694,7 +709,7 @@ class BelParser(BaseParser):
         return tokens
 
     def handle_term(self, line, position, tokens):
-        """Handles BEL terms (the subject and object of BEL relations)
+        """Handle BEL terms (the subject and object of BEL relations).
 
         :param str line: The line being parsed
         :param int position: The position in the line being parsed
@@ -704,7 +719,7 @@ class BelParser(BaseParser):
         return tokens
 
     def _handle_list_helper(self, tokens, relation):
-        """Provides the functionality for :meth:`handle_has_members` and :meth:`handle_has_components`"""
+        """Provide the functionality for :meth:`handle_has_members` and :meth:`handle_has_components`."""
         parent_node_dsl = self.ensure_node(tokens[0])
 
         for child_tokens in tokens[2]:
@@ -714,7 +729,7 @@ class BelParser(BaseParser):
         return tokens
 
     def handle_has_members(self, line, position, tokens):
-        """Handles list relations like ``p(X) hasMembers list(p(Y), p(Z), ...)``
+        """Handle list relations like ``p(X) hasMembers list(p(Y), p(Z), ...).``
 
         :param str line: The line being parsed
         :param int position: The position in the line being parsed
@@ -723,7 +738,7 @@ class BelParser(BaseParser):
         return self._handle_list_helper(tokens, HAS_MEMBER)
 
     def handle_has_components(self, line, position, tokens):
-        """Handles list relations like ``p(X) hasComponents list(p(Y), p(Z), ...)``
+        """Handle list relations like ``p(X) hasComponents list(p(Y), p(Z), ...)``.
 
         :param str line: The line being parsed
         :param int position: The position in the line being parsed
@@ -732,7 +747,7 @@ class BelParser(BaseParser):
         return self._handle_list_helper(tokens, HAS_COMPONENT)
 
     def _add_qualified_edge_helper(self, u, v, relation, annotations, subject_modifier, object_modifier):
-        """Adds a qualified edge from the internal aspects of the parser"""
+        """Add a qualified edge from the internal aspects of the parser."""
         self.graph.add_qualified_edge(
             u,
             v,
@@ -746,7 +761,7 @@ class BelParser(BaseParser):
         )
 
     def _add_qualified_edge(self, u, v, relation, annotations, subject_modifier, object_modifier):
-        """Adds an edge, then adds the opposite direction edge if it should"""
+        """Add an edge, then adds the opposite direction edge if it should."""
         self._add_qualified_edge_helper(
             u,
             v,
@@ -767,7 +782,7 @@ class BelParser(BaseParser):
             )
 
     def _handle_relation(self, tokens):
-        """A policy in which all annotations are stored as sets, including single annotations
+        """A policy in which all annotations are stored as sets, including single annotations.
 
         :param pyparsing.ParseResult tokens: The tokens from PyParsing
         """
@@ -801,8 +816,9 @@ class BelParser(BaseParser):
         )
 
     def _handle_relation_harness(self, line, position, tokens):
-        """Handles BEL relations based on the policy specified on instantiation. Note: this can't be changed after
-        instantiation!
+        """Handle BEL relations based on the policy specified on instantiation.
+
+        Note: this can't be changed after instantiation!
 
         :param str line: The line being parsed
         :param int position: The position in the line being parsed
@@ -823,7 +839,7 @@ class BelParser(BaseParser):
         return tokens
 
     def handle_unqualified_relation(self, line, position, tokens):
-        """Handles unqualified relations
+        """Handle unqualified relations.
 
         :param str line: The line being parsed
         :param int position: The position in the line being parsed
@@ -835,7 +851,7 @@ class BelParser(BaseParser):
         self.graph.add_unqualified_edge(subject_node_dsl, object_node_dsl, rel)
 
     def handle_label_relation(self, line, position, tokens):
-        """Handles statements like ``p(X) label "Label for X"``
+        """Handle statements like ``p(X) label "Label for X"``.
 
         :param str line: The line being parsed
         :param int position: The position in the line being parsed
@@ -872,7 +888,7 @@ class BelParser(BaseParser):
         return node_dsl
 
     def handle_translocation_illegal(self, line, position, tokens):
-        """Handles a malformed translocation
+        """Handle a malformed translocation.
 
         :param str line: The line being parsed
         :param int position: The position in the line being parsed
@@ -884,21 +900,20 @@ class BelParser(BaseParser):
 # HANDLERS
 
 def handle_molecular_activity_default(line, position, tokens):
-    """Handles BEL 2.0 style molecular activities with BEL 1.0 default names
+    """Handle a BEL 2.0 style molecular activity with BEL default names.
 
     :param str line: The line being parsed
     :param int position: The position in the line being parsed
     :param pyparsing.ParseResult tokens: The tokens from PyParsing
     """
     upgraded = language.activity_labels[tokens[0]]
-    log.log(5, 'upgraded legacy activity to %s', upgraded)
     tokens[NAMESPACE] = BEL_DEFAULT_NAMESPACE
     tokens[NAME] = upgraded
     return tokens
 
 
 def handle_activity_legacy(line, position, tokens):
-    """Handles nodes with BEL 1.0 activities
+    """Handle BEL 1.0 activities.
 
     :param str line: The line being parsed
     :param int position: The position in the line being parsed
diff --git a/src/pybel/struct/graph.py b/src/pybel/struct/graph.py
index 0bdc393e..2c81a3a9 100644
--- a/src/pybel/struct/graph.py
+++ b/src/pybel/struct/graph.py
@@ -1050,6 +1050,8 @@ class BELGraph(nx.MultiDiGraph):
         """Print a summary of the graph"""
         number_nodes = self.number_of_nodes()
         print(self, file=file)
+        if self.warnings:
+            print('Number of Warnings: {}'.format(len(self.warnings)), file=file)
         print('Number of Nodes: {}'.format(number_nodes), file=file)
         print('Number of Edges: {}'.format(self.number_of_edges()), file=file)
         print('Network Density: {}'.format(nx.density(self)), file=file)
diff --git a/tox.ini b/tox.ini
index b82ec864..b5dcdc7c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -56,7 +56,7 @@ deps =
     flake8-import-order
     pep8-naming
 commands =
-    flake8 src/pybel/ tests/ setup.py
+    flake8 src/pybel/ setup.py
 description = Run the flake8 tool with several plugins (bandit, docstrings, import order, pep8 naming).
 
 [testenv:pylint]


Test patch:
diff --git a/tests/constants.py b/tests/constants.py
index 7b9ce466..f1e65405 100644
--- a/tests/constants.py
+++ b/tests/constants.py
@@ -26,7 +26,7 @@ from pybel.parser.exc import (
     MissingNamespaceRegexWarning, MissingSupportWarning, NakedNameWarning, NestedRelationWarning,
     PlaceholderAminoAcidWarning, UndefinedAnnotationWarning, UndefinedNamespaceWarning, VersionFormatWarning,
 )
-from pybel.parser.parse_bel import BelParser
+from pybel.parser.parse_bel import BELParser
 from pybel.utils import subdict_matches
 
 log = logging.getLogger(__name__)
@@ -183,7 +183,11 @@ class TestTokenParserBase(unittest.TestCase):
     def setUpClass(cls):
         """Build a BEL graph and BEL parser that persist through the class."""
         cls.graph = BELGraph()
-        cls.parser = BelParser(cls.graph, autostreamline=False)
+        cls.parser = BELParser(
+            cls.graph,
+            autostreamline=False,
+            disallow_unqualified_translocations=True,
+        )
 
     def setUp(self):
         """Clear the parser at the beginning of each test."""
diff --git a/tests/test_io/test_import.py b/tests/test_io/test_import.py
index 5066c393..a12e9aef 100644
--- a/tests/test_io/test_import.py
+++ b/tests/test_io/test_import.py
@@ -21,7 +21,7 @@ from pybel.constants import (
 from pybel.dsl import gene, BaseEntity
 from pybel.examples import sialic_acid_graph
 from pybel.io.exc import ImportVersionWarning, import_version_message_fmt
-from pybel.parser import BelParser
+from pybel.parser import BELParser
 from pybel.parser.exc import InvalidFunctionSemantic, MissingCitationException, MissingNamespaceRegexWarning
 from pybel.struct.summary import get_syntax_errors
 from pybel.testing.cases import TemporaryCacheClsMixin
@@ -97,7 +97,7 @@ class TestInterchange(TemporaryCacheClsMixin, BelReconstitutionMixin):
 
         with mock_bel_resources:
             cls.thorough_graph = from_path(test_bel_thorough, manager=cls.manager, allow_nested=True)
-            cls.slushy_graph = from_path(test_bel_slushy, manager=cls.manager)
+            cls.slushy_graph = from_path(test_bel_slushy, manager=cls.manager, disallow_unqualified_translocations=True)
             cls.simple_graph = from_url(Path(test_bel_simple).as_uri(), manager=cls.manager)
             cls.isolated_graph = from_path(test_bel_isolated, manager=cls.manager)
             cls.misordered_graph = from_path(test_bel_misordered, manager=cls.manager, citation_clearing=False)
@@ -270,7 +270,7 @@ class TestFull(TestTokenParserBase):
     @classmethod
     def setUpClass(cls):
         cls.graph = BELGraph()
-        cls.parser = BelParser(
+        cls.parser = BELParser(
             cls.graph,
             namespace_dict=namespaces,
             annotation_dict=annotations,
diff --git a/tests/test_parse/test_parse_bel.py b/tests/test_parse/test_parse_bel.py
index 7ffc77e9..ba0c8013 100644
--- a/tests/test_parse/test_parse_bel.py
+++ b/tests/test_parse/test_parse_bel.py
@@ -18,7 +18,7 @@ from pybel.dsl import (
     named_complex_abundance, pathology, protein, protein_fusion, reaction, rna, rna_fusion, secretion, translocation,
 )
 from pybel.dsl.nodes import abundance, bioprocess, gene, gmod, hgvs, pmod
-from pybel.parser import BelParser
+from pybel.parser import BELParser
 from pybel.parser.exc import MalformedTranslocationWarning
 from pybel.parser.parse_bel import modifier_po_to_dict
 from tests.constants import TestTokenParserBase, assert_has_edge, assert_has_node, update_provenance
@@ -1567,7 +1567,10 @@ class TestTranslocationPermissive(unittest.TestCase):
     @classmethod
     def setUpClass(cls):
         cls.graph = BELGraph()
-        cls.parser = BelParser(cls.graph, allow_unqualified_translocations=True)
+        cls.parser = BELParser(
+            cls.graph,
+            disallow_unqualified_translocations=False,
+        )
 
     def setUp(self):
         self.parser.clear()
@@ -1605,9 +1608,9 @@ class TestTranslocationPermissive(unittest.TestCase):
         self.assert_has_node(node)
 
     def test_unqualified_translocation_relation(self):
-        """
+        """Test translocation in object.
+
         3.1.2 http://openbel.org/language/web/version_2.0/bel_specification_version_2.0.html#XdIncreases
-        Test translocation in object
         """
         update_provenance(self.parser.control_parser)
 
@@ -1879,7 +1882,7 @@ class TestTransformation(TestTokenParserBase):
 class TestSemantics(unittest.TestCase):
     def test_lenient_semantic_no_failure(self):
         graph = BELGraph()
-        parser = BelParser(graph, allow_naked_names=True)
+        parser = BELParser(graph, allow_naked_names=True)
 
         update_provenance(parser.control_parser)
 
diff --git a/tests/test_parse/test_parse_bel_relations.py b/tests/test_parse/test_parse_bel_relations.py
index e11abee1..a85e1493 100644
--- a/tests/test_parse/test_parse_bel_relations.py
+++ b/tests/test_parse/test_parse_bel_relations.py
@@ -19,7 +19,7 @@ from pybel.dsl import (
     abundance, activity, bioprocess, complex_abundance, composite_abundance, entity, gene, hgvs, pmod, protein,
     reaction, rna,
 )
-from pybel.parser import BelParser
+from pybel.parser import BELParser
 from pybel.parser.exc import (
     MissingNamespaceNameWarning, NestedRelationWarning, RelabelWarning, UndefinedNamespaceWarning,
 )
@@ -953,7 +953,7 @@ class TestCustom(unittest.TestCase):
             }
         }
 
-        self.parser = BelParser(graph, namespace_dict=namespace_dict, autostreamline=False)
+        self.parser = BELParser(graph, namespace_dict=namespace_dict, autostreamline=False)
 
     def test_tloc_undefined_namespace(self):
         s = 'tloc(p(HGNC:AKT1), fromLoc(MESHCS:nucleus), toLoc(MISSING:"undefined"))'