From f9c09309a5d3fc459b59fe4ea9751e56c71290ac Mon Sep 17 00:00:00 2001 From: Andy Konwinski Date: Sat, 23 Jul 2022 13:40:27 -0700 Subject: [PATCH 1/2] add a demo of the PCS v0 CLI --- .../demos/demo_dependency_inference.sh | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 documentation/demos/demo_dependency_inference.sh diff --git a/documentation/demos/demo_dependency_inference.sh b/documentation/demos/demo_dependency_inference.sh new file mode 100644 index 00000000..98dd6168 --- /dev/null +++ b/documentation/demos/demo_dependency_inference.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -xe + +pip install pcs +pcs activate +# Create an empty VirtualEnv; point 'base' alias at it; activate it. +python --version +# 3.9.10 +python -c "import numpy; print(numpy.random)" +# Create a new VirtualEnv; pip install numpy into it; run this command in it. +# stdout: 28.37 <--some random number + +pcs list --type Command +# Identifier Type Aliases +# 3857a Command last, 0 + +pcs list +# Identifier Type Aliases +# 3857a Command last_Command, 0 +# 7b6c0 VirtualEnv last_VirtualEnv +# 45abd VirtualEnv base + +pcs config set default-py 3.8.3 # updates ~/.pcsconfig.yaml +# Create new empty VirtualEnv with Python 3.8.3; point alias 'base' at it +python --version # Creates a new VirtualEnv with Python 3.8.3 +# 3.8.3 + +virtualenv my_new_venv # creates a new alias in the local_registry pointing + # to the venv Component that is was currently in-use. +ls +# my_new_venv <-- this is a symlink to the venv in the ~/.pcs/cache/ dir +# other_files_and_folders + +pcs list --type VirtualEnv From c276cf7c1e54ead7fe51a31b8bb7f82931c9423e Mon Sep 17 00:00:00 2001 From: Andy Konwinski Date: Tue, 26 Jul 2022 17:25:38 -0700 Subject: [PATCH 2/2] add standalone demo script for printing import DAG when modules are all installed already --- documentation/demos/import_logging_demo.py | 87 ++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 documentation/demos/import_logging_demo.py diff --git a/documentation/demos/import_logging_demo.py b/documentation/demos/import_logging_demo.py new file mode 100644 index 00000000..31bcb2a4 --- /dev/null +++ b/documentation/demos/import_logging_demo.py @@ -0,0 +1,87 @@ +import builtins +import importlib +import pprint +import sys +from collections import defaultdict +from types import ModuleType +from graphviz import Digraph + + +class Graph: + """Utility class.""" + def __init__(self): + self.nodes = set() + self.edges = defaultdict(set) + + +class ImportLogger: + def __enter__(self): + self.original_import = builtins.__import__ + builtins.__import__ = self._import_wrapper + self.imports = {} + return self + + def __exit__(self, *args): + builtins.__import__ = self.original_import + + def log_import( + self, name, imported, globals, locals=None, fromlist=(), level=0 + ): + importer_name = globals["__name__"] + im_file = imported.__file__ if hasattr(imported, '__file__') else "" + self.imports[(importer_name, name)] = (imported, im_file) + + def _import_wrapper(self, name, *args, **kwargs): + imported = self.original_import(name, *args, **kwargs) + self.log_import(name, imported, *args, **kwargs) + return imported + + +class Module: + def __init__(self, name: str, version: str = None): + self.name = name + self._imports: [str, "Module"] = None + + @property + def imports(self) -> [str, "Module"]: + with ImportLogger() as importer: + self.get() + self._imports = importer.imports + return self._imports + + def get(self) -> ModuleType: + return importlib.import_module(self.name) + + def save_dag_file(self, filename): + """Save a file representing the DAG associated with this Module.""" + gv_graph = Digraph(comment=f"Python module import DAG of {self.name}") + g = self.generate_dag() # get back a Graph object + for node in g.nodes: + gv_graph.node(node) # Can specify 'shape=oval', etc. + for parent, children in g.edges.items(): + for child in children: + gv_graph.edge(parent, child) + gv_graph.render(filename, view=True) + print(f"Saved graph to {filename}") + + def generate_dag(self): + """return a dot representation of the import DAG associated with this + Module that will start at this Module and have edges to any Modules + that it imports, and the modules they import, etc.""" + graph = Graph() + for child, parent in self._imports.keys(): + graph.nodes.add(child) + graph.nodes.add(parent) + graph.edges[child].add(parent) + return graph + + +if __name__ == "__main__": + if len(sys.argv) > 1: + module_name = sys.argv[1] + else: + print(f"sys.argv: {sys.argv}") + exit("Please provide a package name as an arg to this script.") + m = Module(module_name) + pprint.pprint(m.imports) + m.save_dag_file(f"{module_name}_import_dag.gv")