Source code for pinspect.utils
import inspect
import logging
import re
import networkx as nx
from pyvis.network import Network
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
# does not match to any symbol
REGEX_NEVER_MATCH = '(?!x)x'
NON_EXECUTABLE = "save|write|remove|delete|duplicate"
[docs]def getmembers(obj_class):
"""
Parameters
----------
obj_class : type
An object class.
Returns
-------
member_names : set
A set of method and attribute names of the `obj_class` type.
"""
member_names = {func_name for func_name, func in inspect.getmembers(obj_class)}
return member_names
[docs]def get_module_root(obj):
return obj.__class__.__module__.split('.')[0]
[docs]class IgnoreFunc:
def __init__(self, key, obj_class=()):
"""
Parameters
----------
key : str or list, optional
A string or a list of strings to ignore `obj` attributes and methods from being accessed and executed.
Apart from user-provided strings, all methods that contain one of the following key-words will be ignored:
'save', 'write', 'remove', 'delete', 'duplicate'
For the total list of ignored key-words, see `NON_EXECUTABLE` in `utils.py`.
obj_class : list, optional
A list of class types to ignore.
Apart from user-provided class types, all numpy functions will not be executed.
"""
self.ignore = re.compile(key, flags=re.IGNORECASE)
self.ignored_functions = dict()
try:
import numpy as np
self.ignored_functions[np.ndarray] = getmembers(np.ndarray)
self.ignored_functions[np.ndarray].update(getmembers(np))
except ImportError:
pass
if not isinstance(obj_class, (list, tuple, set)):
obj_class = [obj_class]
for class_type in obj_class:
self.ignored_functions[class_type] = getmembers(class_type)
def __call__(self, obj, attribute_name):
"""
Check the `obj` for the attribute name `func_name`.
Parameters
----------
obj : object
Object to take the attribute from.
attribute_name : str
`obj` attribute name.
Returns
-------
bool
Whether this attribute should be ignored or not.
"""
for ignored_class, ignored_functions in self.ignored_functions.items():
if isinstance(obj, ignored_class) and attribute_name in ignored_functions:
return True
return self.ignore.search(attribute_name)
[docs]def to_pyvis(graph, layout=True):
"""
This method takes an exisitng Networkx graph and translates
it to a PyVis graph format that can be accepted by the VisJs
API in the Jinja2 template.
Parameters
----------
graph : nx.DiGraph
NetworkX directed graph.
layout : bool
Use hierarchical layout if this is set.
Returns
-------
net : Network
PyVis Network
"""
def add_node(node_id):
attr = nodes[node_id]
net.add_node(node_id, label=attr['label'], level=attr['level'], color=attr.get('color', None),
title=attr['title'])
edges = graph.edges.data()
nodes = graph.nodes
net = Network(height="960px", width="1280px", directed=True, layout=layout)
for v, u, edge_attr in edges:
add_node(v)
add_node(u)
net.add_edge(v, u, title=edge_attr['label'], color=edge_attr['color'])
return net
[docs]def to_string(graph, source, prefix=''):
"""
Traverse the graph and yield its string representation.
Parameters
----------
graph : nx.DiGraph
Graph, obtained by `GraphBuilder`.
source : int
Source node id.
prefix : str
This prefix will be accumulated in a full call history during successive calls of `to_string()`.
Returns
-------
generator
Generator of string traversal of the graph.
"""
if len(graph.adj[source]) == 0:
yield f"{prefix} -> '{graph.nodes[source]['label']}'"
else:
for adj, attr in graph.adj[source].items():
yield from to_string(graph, source=adj, prefix=f"{prefix}.{attr['label']}")
[docs]def check_edge(graph, edge_label):
"""
Parameters
----------
graph : nx.DiGraph
A graph.
edge_label : str
Edge label.
Returns
-------
int
Counts how many edges have the property `label` that matches `edge_label`.
"""
edge_label = re.compile(edge_label)
filtered = [triple for triple in graph.edges.data('label') if edge_label.search(triple[2])]
for v, u, label in filtered:
logging.info(f"{graph.nodes[v]['label']}.{label} -> {graph.nodes[u]['label']}")
return len(filtered)