Skip to main content

Incremental change of CST

Project description

Downloads Downloads Coverage Status Lines of code Hits-of-Code Test-Package Python versions PyPI version Checked with mypy Ruff DeepWiki

logo

Many source code tools (linters, formatters, and others) work with CST, a tree-structured representation of source code (like AST, but it also retains nodes such as whitespace and comments). This library is a wrapper around such trees, designed for convenient iterative traversal and replacement of nodes.

Table of Contents

Installation

You can install cstvis with pip:

pip install cstvis

You can also use instld to quickly try this package and others without installing them.

Usage

This package is built on top of libcst.

The basic workflow is very simple:

  • Create an object of the Changer class.
  • Register converter functions with the @<changer object>.converter decorator. Each function converts one CST node type into another. It takes a node object as its first argument, and that argument must have a type annotation that tells the system which node types the converter should be applied to.
  • If needed, register filters to prevent changes to certain nodes.
  • Iterate over individual changes and apply them as needed.

Let me show you a simple example:

from libcst import Subtract, Add
from cstvis import Changer, Context
from pathlib import Path

# Content of the file:
# a = 4 + 5
# b = 15 - a
# c = b + a # kek
changer = Changer(Path('tests/some_code/simple_sum.py').read_text())

@changer.converter
def change_add(node: Add, context: Context):
    return Subtract(
        whitespace_before=node.whitespace_before,
        whitespace_after=node.whitespace_after,
    )

for x in changer.iterate_coordinates():
    print(x)
    print(changer.apply_coordinate(x))

#> Coordinate(file=None, class_name='Add', start_line=1, start_column=6, end_line=1, end_column=7)
#> a = 4 - 5
#> b = 15 - a
#> c = b + a # kek
#> 
#> Coordinate(file=None, class_name='Add', start_line=3, start_column=6, end_line=3, end_column=7)
#> a = 4 + 5
#> b = 15 - a
#> c = b - a # kek

The key part of this example is the last two lines, where we iterate over the coordinates. What does that mean? The fact is that any code change made by this library happens in two stages: identify the coordinates of the change and then apply it. This separation makes it possible to distribute the work across multiple threads or even multiple machines. However, this design also has limitations. If you apply one coordinate change, the resulting code will differ from the original and the remaining coordinates will no longer be valid. You can only apply one change at a time.

A filter is a special function with the same signature as a converter, registered with the @<changer object>.filter decorator. It decides whether a specific CST node should be changed, and returns True if yes, or False if no. The filter applies to all nodes if the node parameter has no type annotation, or if the parameter is annotated as Any or CSTNode. If you specify a node type in the annotation, the filter will be applied only to nodes of that type. Any other annotations are not allowed.

Let's look at another example (part of the code is omitted):

count_adds = 0

@changer.filter
def only_first(node: Add, context: Context) -> bool:
    global count_adds
    
    count_adds += 1
    
    return True if count_adds <= 1 else False

for x in changer.iterate_coordinates():
    print(x)
    print(changer.apply_coordinate(x))

#> Coordinate(file=None, class_name='Add', start_line=1, start_column=6, end_line=1, end_column=7)
#> a = 4 - 5
#> b = 15 - a
#> c = b + a # kek

You see? Now the iteration yields only the first possible change, the rest are filtered out automatically because the filter returns False for them.

At this point, the basic usage should be clear. But what is the context parameter passed to converters and filters? It has two fields and one useful method:

  • coordinate with fields start_line: int, start_column: int, end_line: int, end_column: int and some others. This identifies the current location in the code.
  • comment - the comment on the first line of the node, if there is one, without the leading #, or None if there is no comment.
  • get_metacodes(key: Union[str, List[str]]) -> List[ParsedComment] - a method that returns a list of parsed comments in metacode format associated with this line of code.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cstvis-0.0.4.tar.gz (14.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cstvis-0.0.4-py3-none-any.whl (11.7 kB view details)

Uploaded Python 3

File details

Details for the file cstvis-0.0.4.tar.gz.

File metadata

  • Download URL: cstvis-0.0.4.tar.gz
  • Upload date:
  • Size: 14.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for cstvis-0.0.4.tar.gz
Algorithm Hash digest
SHA256 ec41dc5b9640750f4db9024fc22beacb60f82329457497d7bc2219e58735ade7
MD5 c0e7bfddea292353ab0115bd98ccba2b
BLAKE2b-256 8781209fc42c266ab1036e3ebd4a9cfe2bb4b5721a73f4398deac31bd020c2a6

See more details on using hashes here.

Provenance

The following attestation bundles were made for cstvis-0.0.4.tar.gz:

Publisher: release.yml on mutating/cstvis

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cstvis-0.0.4-py3-none-any.whl.

File metadata

  • Download URL: cstvis-0.0.4-py3-none-any.whl
  • Upload date:
  • Size: 11.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for cstvis-0.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 d4cd9e94148c07093ab5a8a5a15cbc20fe7341fd96c255ccc72b64a3891edea3
MD5 60b4c60e1003baf4622b7f6850d5439f
BLAKE2b-256 e9df4715dc58cfa56f958d6c8dc5b87480df42095c7f61834cf843c527c442fd

See more details on using hashes here.

Provenance

The following attestation bundles were made for cstvis-0.0.4-py3-none-any.whl:

Publisher: release.yml on mutating/cstvis

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page