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

A large number of source code tools (linters, formatters, and others) work with CST, a special representation of the source code that already has a tree shape (like AST), but still contains "extra" nodes such as spaces or comments. This library is a wrapper around such a tree, designed for convenient and iterative work with nodes: traversal and replacement.

Table of contents

Installation

Install it:

pip install cstvis

You can also quickly try out this and other packages without having to install using instld.

Usage

This library is a wrapper around the libcst library.

The flow of work is very simple:

  • Create an object of the Changer class.
  • Register converter functions that will convert some CST nodes to others, using the decorator. Each such function takes a node object as the first argument, and it must be accompanied by a type annotation. It is based on the annotation that the system will understand which nodes it needs to be applied to and which ones it does not.
  • If necessary, also register filters, which are special functions that can prevent the system from changing certain nodes.
  • Iterate over atomic changes and apply them if necessary.

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 the coordinates are iterated. What does it mean? The fact is that any change to the code that this library makes occurs in 2 stages: outline the coordinates of the change and make the change. Due to this separation, it becomes possible, for example, to divide this work between several threads or even several computers. However, this scheme also limits us. If you apply one coordinate change, the resulting code will differ from the original one and subsequent coordinates will no longer be possible to apply. You can only apply one change at a time.

A filter is a special function with the same signature as a converter, which we mark with the @filter decorator. This should decide whether to change a specific CST node or not, and return True if yes, or False if no. The filter is applied to all nodes if the node parameter does not have a type annotation, or if Any / CSTNode annotation is used. If you specify a specific type of node in the annotation, the filter will be applied only to them. 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, during the iteration, we got only the first version of possible changes, the rest are automatically filtered out because the filter function told us to do so.

So, now it's roughly clear how to use it. But what kind of context parameter do we see in converters and filters? It has 2 fields and 1 interesting method:

  • coordinate with fields start_line: int, start_column: int, end_line: int, end_column: int and some others. This identifies where we are at in the code.
  • comment - a comment line, if there is such a comment in the first line of this node, without a # at the beginning, 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 related to 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.1.tar.gz (11.8 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.1-py3-none-any.whl (9.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: cstvis-0.0.1.tar.gz
  • Upload date:
  • Size: 11.8 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.1.tar.gz
Algorithm Hash digest
SHA256 ac95f3fa1673da6f5add73c00df6a46677d829482a7fac08be283e1f1ec1f26d
MD5 aadb6f92504b468e1ad64673ef4e080f
BLAKE2b-256 430121e334c3685fbb47a14b94d5d5b02b16339d36d2bad74a617f4125b71b3c

See more details on using hashes here.

Provenance

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

Publisher: release.yml on pomponchik/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.1-py3-none-any.whl.

File metadata

  • Download URL: cstvis-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 9.4 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3e04313d79abd977b3d9f1d31e9737f34239f70b247a1eccd5c47cefe6903870
MD5 33be4501e3a44465a19b178f0b51b804
BLAKE2b-256 f1ede35e2334919f8d1c9f98b2889940de7fcf81c4a90e8a02d0a5d8bf35a3e2

See more details on using hashes here.

Provenance

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

Publisher: release.yml on pomponchik/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