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.3.tar.gz (14.4 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.3-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: cstvis-0.0.3.tar.gz
  • Upload date:
  • Size: 14.4 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.3.tar.gz
Algorithm Hash digest
SHA256 8932364bb04db06e56936711d24745f0b2b896a39f76e12d4545752dabc4781b
MD5 f308de62e3010dad56aa1e2934f5cd6b
BLAKE2b-256 0345edc15d327c466269c1fa26ea7414ecb3fc97f80d12fb11d79958ec05234a

See more details on using hashes here.

Provenance

The following attestation bundles were made for cstvis-0.0.3.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.3-py3-none-any.whl.

File metadata

  • Download URL: cstvis-0.0.3-py3-none-any.whl
  • Upload date:
  • Size: 11.8 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.3-py3-none-any.whl
Algorithm Hash digest
SHA256 b6fc26528c3d62259c7c139ccc3aa4d713f14ec50c156cdd60fd621a87fd7ccc
MD5 1850de9ac07ec866e39849f75b577b71
BLAKE2b-256 467c8a114940cdd672a07ed87cd026826b8f0a3a1b6e55db8d1e6d7e7b8bf85d

See more details on using hashes here.

Provenance

The following attestation bundles were made for cstvis-0.0.3-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