{ "cells": [ { "cell_type": "raw", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "=====================\n", "Working with Metadata\n", "=====================\n", "LibCST handles node metadata in a somewhat unusual manner in order to maintain the immutability of the tree. See :doc:`Metadata ` for the complete documentation. \n", "\n", "Providing Metadata\n", "==================\n", "While it's possible to write visitors that gather metadata from a tree ad hoc, using the provider interface gives you the advantage of being able to use dependency declaration to automatically run your providers in other visitors and type safety. For most cases, you'll want to extend :class:`~libcst.BatchableMetadataProvider` as providers that extend from that class can be resolved more efficiently in batches.\n", "\n", "Here's an example of a simple metadata provider that marks :class:`~libcst.Name` nodes that are function parameters:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "nbsphinx": "hidden" }, "outputs": [], "source": [ "import sys\n", "sys.path.append(\"../../\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import libcst as cst\n", "\n", "\n", "class IsParamProvider(cst.BatchableMetadataProvider[bool]):\n", " \"\"\"\n", " Marks Name nodes found as a parameter to a function.\n", " \"\"\"\n", " def __init__(self) -> None:\n", " super().__init__()\n", " self.is_param = False\n", " \n", " def visit_Param(self, node: cst.Param) -> None:\n", " # Mark the child Name node as a parameter \n", " self.set_metadata(node.name, True)\n", " \n", " def visit_Name(self, node: cst.Name) -> None:\n", " # Mark all other Name nodes as not parameters\n", " if not self.get_metadata(type(self), node, False):\n", " self.set_metadata(node, False)" ] }, { "cell_type": "raw", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "Line and Column Metadata\n", "------------------------\n", "LibCST ships with two built-in providers for line and column metadata. See :ref:`Position Metadata` for more information.\n", "\n", "Accessing Metadata\n", "==================\n", "Once you have a provider, the metadata interface gives you two primary ways of working with your providers. The first is using the resolve methods provided by :class:`~libcst.MetadataWrapper` and the second is through declaring metadata dependencies on a :class:`~libcst.CSTTransformer` or :class:`~libcst.CSTVisitor`.\n", "\n", "Using the :class:`~libcst.MetadataWrapper`\n", "------------------------------------------\n", "The metadata wrapper class provides a way to associate metadata with a module as well as a simple interface to run providers. Here's an example of using a wrapper with the provider we just wrote:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "module = cst.parse_module(\"x\")\n", "wrapper = cst.MetadataWrapper(module)\n", "\n", "isparam = wrapper.resolve(IsParamProvider)\n", "x_name_node = wrapper.module.body[0].body[0].value\n", "\n", "print(isparam[x_name_node]) # should print False" ] }, { "cell_type": "raw", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "Using Dependency Declaration\n", "----------------------------\n", "The visitors that ship with LibCST can declare metadata providers as dependencies that will be run automatically when visited by a wrapper. Here is a visitor that prints all names that are function parameters." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from libcst.metadata import PositionProvider\n", "\n", "class ParamPrinter(cst.CSTVisitor):\n", " METADATA_DEPENDENCIES = (IsParamProvider, PositionProvider,)\n", "\n", " def visit_Name(self, node: cst.Name) -> None:\n", " # Only print out names that are parameters\n", " if self.get_metadata(IsParamProvider, node):\n", " pos = self.get_metadata(PositionProvider, node).start\n", " print(f\"{node.value} found at line {pos.line}, column {pos.column}\")\n", "\n", "\n", "module = cst.parse_module(\"def foo(x):\\n y = 1\\n return x + y\")\n", "wrapper = cst.MetadataWrapper(module)\n", "result = wrapper.visit(ParamPrinter()) # NB: wrapper.visit not module.visit" ] } ], "metadata": { "celltoolbar": "Edit Metadata", "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 2 }