Compare commits

...

378 commits
v0.4.4 ... main

Author SHA1 Message Date
martin
c5e40e8769
chore: remove macos-13 from ci (#1433)
Some checks failed
pypi_upload / build (push) Has been cancelled
GitHub Actions Security Analysis with zizmor 🌈 / zizmor latest via PyPI (push) Has been cancelled
CI / test (macos-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (macos-latest, 3.13) (push) Has been cancelled
CI / test (macos-latest, 3.13t) (push) Has been cancelled
CI / test (macos-latest, 3.14) (push) Has been cancelled
CI / test (macos-latest, 3.14t) (push) Has been cancelled
CI / test (macos-latest, 3.9) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.9) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.13) (push) Has been cancelled
CI / test (windows-latest, 3.13t) (push) Has been cancelled
CI / test (windows-latest, 3.14) (push) Has been cancelled
CI / test (windows-latest, 3.14t) (push) Has been cancelled
CI / test (windows-latest, 3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (ubuntu-latest, 3.13) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14t) (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / docs (push) Has been cancelled
pypi_upload / Upload wheels to pypi (push) Has been cancelled
remove macos-13 from ci
2025-12-17 13:01:40 -05:00
Frank Liu
b75343e74e
Create CodemodCommand Remove/Add Import helper functions (#1432)
* Create helper functions to abstract away usage of RemoveImportsVisitor's remove unused import functions in CodemodCommand

* Create helper functions to abstract away usage of AddImportsVisitor's add needed import functions in CodemodCommand

* Add tests for CodemodCommand helper functions

Add comprehensive tests for the new helper methods:
- remove_unused_import
- remove_unused_import_by_node
- add_needed_import

Tests cover simple cases, from imports, aliased imports,
relative imports, and combined add/remove operations.
2025-12-17 09:28:24 -08:00
martin
9275a8bf78
bump version to 1.8.6 (#1425)
Some checks failed
CI / test (macos-latest, 3.13) (push) Has been cancelled
CI / test (macos-latest, 3.13t) (push) Has been cancelled
CI / test (macos-latest, 3.14) (push) Has been cancelled
CI / test (macos-latest, 3.14t) (push) Has been cancelled
CI / test (macos-latest, 3.9) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.9) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.13) (push) Has been cancelled
CI / test (windows-latest, 3.13t) (push) Has been cancelled
CI / test (windows-latest, 3.14) (push) Has been cancelled
CI / test (windows-latest, 3.14t) (push) Has been cancelled
CI / test (windows-latest, 3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / Rust unit tests (push) Has been cancelled
CI / Rustfmt (push) Has been cancelled
CI / build (push) Has been cancelled
pypi_upload / build (push) Has been cancelled
GitHub Actions Security Analysis with zizmor 🌈 / zizmor latest via PyPI (push) Has been cancelled
pypi_upload / Upload wheels to pypi (push) Has been cancelled
2025-11-03 16:48:42 -05:00
Frank Liu
b66c0e2822
[CodemodCommand] Make transform_module supported_transforms order deterministic by using List over Dict (#1424)
Some checks are pending
CI / test (macos-latest, 3.13) (push) Waiting to run
CI / test (macos-latest, 3.13t) (push) Waiting to run
CI / test (macos-latest, 3.14) (push) Waiting to run
CI / test (macos-latest, 3.14t) (push) Waiting to run
CI / test (macos-latest, 3.9) (push) Waiting to run
CI / test (ubuntu-latest, 3.10) (push) Waiting to run
CI / test (ubuntu-latest, 3.11) (push) Waiting to run
CI / test (ubuntu-latest, 3.12) (push) Waiting to run
CI / test (ubuntu-latest, 3.13) (push) Waiting to run
CI / test (ubuntu-latest, 3.13t) (push) Waiting to run
CI / test (ubuntu-latest, 3.14) (push) Waiting to run
CI / test (ubuntu-latest, 3.14t) (push) Waiting to run
CI / test (ubuntu-latest, 3.9) (push) Waiting to run
CI / test (windows-latest, 3.10) (push) Waiting to run
CI / test (windows-latest, 3.11) (push) Waiting to run
CI / test (windows-latest, 3.12) (push) Waiting to run
CI / test (windows-latest, 3.13) (push) Waiting to run
CI / test (windows-latest, 3.13t) (push) Waiting to run
CI / test (windows-latest, 3.14) (push) Waiting to run
CI / test (windows-latest, 3.14t) (push) Waiting to run
CI / test (windows-latest, 3.9) (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / docs (push) Waiting to run
CI / Rust unit tests (push) Waiting to run
CI / Rustfmt (push) Waiting to run
CI / build (push) Waiting to run
pypi_upload / build (push) Waiting to run
pypi_upload / Upload wheels to pypi (push) Blocked by required conditions
GitHub Actions Security Analysis with zizmor 🌈 / zizmor latest via PyPI (push) Waiting to run
2025-11-02 20:27:32 -05:00
Colin Watson
c2169d240b
Update PyO3 to 0.26 (#1413)
Some checks failed
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (macos-latest, 3.13) (push) Has been cancelled
CI / test (macos-latest, 3.13t) (push) Has been cancelled
CI / test (macos-latest, 3.14t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (macos-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.9) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13) (push) Has been cancelled
CI / test (ubuntu-latest, 3.9) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.13) (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / test (ubuntu-latest, 3.13t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14t) (push) Has been cancelled
CI / test (windows-latest, 3.13t) (push) Has been cancelled
CI / test (windows-latest, 3.14) (push) Has been cancelled
CI / test (windows-latest, 3.14t) (push) Has been cancelled
CI / test (windows-latest, 3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / Rust unit tests (push) Has been cancelled
CI / Rustfmt (push) Has been cancelled
CI / build (push) Has been cancelled
GitHub Actions Security Analysis with zizmor 🌈 / zizmor latest via PyPI (push) Has been cancelled
pypi_upload / Upload wheels to pypi (push) Has been cancelled
2025-10-28 15:37:35 -04:00
Itamar Oren
73b17d8449
Update pyproject.toml for 3.14t (#1417)
Some checks failed
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (macos-latest, 3.13) (push) Has been cancelled
CI / test (macos-latest, 3.13t) (push) Has been cancelled
CI / test (macos-latest, 3.14t) (push) Has been cancelled
CI / test (macos-latest, 3.9) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.14) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.9) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.13t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.13) (push) Has been cancelled
CI / test (windows-latest, 3.14) (push) Has been cancelled
CI / test (windows-latest, 3.14t) (push) Has been cancelled
CI / test (windows-latest, 3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / Rust unit tests (push) Has been cancelled
CI / Rustfmt (push) Has been cancelled
CI / build (push) Has been cancelled
GitHub Actions Security Analysis with zizmor 🌈 / zizmor latest via PyPI (push) Has been cancelled
pypi_upload / Upload wheels to pypi (push) Has been cancelled
- Update description to include 3.14
- Add 3.14 and free-threading trove classifiers
- Update deps to switch back to pyyaml for 3.14
2025-10-24 13:49:25 -07:00
dependabot[bot]
421f7d3400
build(deps): bump pypa/cibuildwheel from 3.1.4 to 3.2.1 (#1414)
Some checks failed
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (macos-latest, 3.13) (push) Has been cancelled
CI / test (macos-latest, 3.13t) (push) Has been cancelled
CI / test (macos-latest, 3.14) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.9) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.13) (push) Has been cancelled
CI / test (windows-latest, 3.13t) (push) Has been cancelled
CI / test (windows-latest, 3.14) (push) Has been cancelled
CI / test (windows-latest, 3.14t) (push) Has been cancelled
CI / test (windows-latest, 3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / Rust unit tests (push) Has been cancelled
CI / Rustfmt (push) Has been cancelled
CI / build (push) Has been cancelled
pypi_upload / build (push) Has been cancelled
GitHub Actions Security Analysis with zizmor 🌈 / zizmor latest via PyPI (push) Has been cancelled
pypi_upload / Upload wheels to pypi (push) Has been cancelled
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 3.1.4 to 3.2.1.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v3.1.4...v3.2.1)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-version: 3.2.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-12 21:47:40 -07:00
dependabot[bot]
129b20f476
build(deps): bump github/codeql-action from 3 to 4 (#1415)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-12 21:47:18 -07:00
dependabot[bot]
6f5da5f998
build(deps): bump astral-sh/setup-uv from 6 to 7 (#1416)
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 6 to 7.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/v6...v7)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-12 21:46:50 -07:00
martin
7c906eb47c
bump version to 1.8.5 (#1407)
Some checks failed
CI / lint (push) Has been cancelled
CI / test (macos-latest, 3.13) (push) Has been cancelled
CI / test (macos-latest, 3.13t) (push) Has been cancelled
CI / test (macos-latest, 3.14) (push) Has been cancelled
CI / test (macos-latest, 3.14t) (push) Has been cancelled
CI / test (macos-latest, 3.9) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.9) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.13) (push) Has been cancelled
CI / test (windows-latest, 3.13t) (push) Has been cancelled
CI / test (windows-latest, 3.14) (push) Has been cancelled
CI / test (windows-latest, 3.14t) (push) Has been cancelled
CI / test (windows-latest, 3.9) (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / Rust unit tests (push) Has been cancelled
CI / Rustfmt (push) Has been cancelled
CI / build (push) Has been cancelled
pypi_upload / build (push) Has been cancelled
GitHub Actions Security Analysis with zizmor 🌈 / zizmor latest via PyPI (push) Has been cancelled
pypi_upload / Upload wheels to pypi (push) Has been cancelled
2025-09-26 01:03:35 -04:00
martin
de5635394b
fix: circular import error (#1406)
* fix: circular import error
2025-09-25 23:44:58 -04:00
martin
47cacb69a3
bump version to 1.8.4 (#1402)
Some checks failed
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (macos-latest, 3.13) (push) Has been cancelled
CI / test (macos-latest, 3.13t) (push) Has been cancelled
CI / test (macos-latest, 3.14) (push) Has been cancelled
CI / test (macos-latest, 3.14t) (push) Has been cancelled
CI / test (macos-latest, 3.9) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.9) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / Rustfmt (push) Has been cancelled
GitHub Actions Security Analysis with zizmor 🌈 / zizmor latest via PyPI (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.13) (push) Has been cancelled
CI / test (windows-latest, 3.13t) (push) Has been cancelled
CI / test (windows-latest, 3.14) (push) Has been cancelled
CI / test (windows-latest, 3.14t) (push) Has been cancelled
CI / test (windows-latest, 3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / Rust unit tests (push) Has been cancelled
pypi_upload / Upload wheels to pypi (push) Has been cancelled
2025-09-09 15:14:29 -04:00
martin
3b5329aa20
feat: add support for PEP758 (#1401)
Some checks are pending
CI / test (macos-latest, 3.13) (push) Waiting to run
CI / test (macos-latest, 3.13t) (push) Waiting to run
CI / test (macos-latest, 3.14) (push) Waiting to run
CI / test (macos-latest, 3.14t) (push) Waiting to run
CI / test (macos-latest, 3.9) (push) Waiting to run
CI / test (ubuntu-latest, 3.10) (push) Waiting to run
CI / test (ubuntu-latest, 3.11) (push) Waiting to run
CI / test (ubuntu-latest, 3.12) (push) Waiting to run
CI / test (ubuntu-latest, 3.13) (push) Waiting to run
CI / test (ubuntu-latest, 3.13t) (push) Waiting to run
CI / test (ubuntu-latest, 3.14) (push) Waiting to run
CI / test (ubuntu-latest, 3.14t) (push) Waiting to run
CI / test (ubuntu-latest, 3.9) (push) Waiting to run
CI / test (windows-latest, 3.10) (push) Waiting to run
CI / test (windows-latest, 3.11) (push) Waiting to run
CI / test (windows-latest, 3.12) (push) Waiting to run
CI / test (windows-latest, 3.13) (push) Waiting to run
CI / test (windows-latest, 3.13t) (push) Waiting to run
CI / test (windows-latest, 3.14) (push) Waiting to run
CI / test (windows-latest, 3.14t) (push) Waiting to run
CI / test (windows-latest, 3.9) (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / docs (push) Waiting to run
CI / Rust unit tests (push) Waiting to run
CI / Rustfmt (push) Waiting to run
CI / build (push) Waiting to run
pypi_upload / build (push) Waiting to run
pypi_upload / Upload wheels to pypi (push) Blocked by required conditions
GitHub Actions Security Analysis with zizmor 🌈 / zizmor latest via PyPI (push) Waiting to run
PEP758 removes the requirement for parentheses to surround exceptions
in except and except* expressions when 'as' is not present.

This pr implements support for parsing these types of statements
2025-09-09 11:16:49 -04:00
martin
48668dfabb
Support parsing of t-strings #1374 (#1398)
#1343
Adds support to parse t-strings

Couple things of note:

TemplatedString* is largely a copy of FormattedString*
Since clients operate of libcst objects I consider this this part of a public API - following the python grammar (where TStrings are distinct from FStrings) seems like a good way to avoid changes to the API in the future.
Within the tokenizer we reuse the fstring machinery
I consider this an implementation detail, fstrings and tstrings are (for now) identical, we can change this later without changes to the public api.
Since 2 -> we have a new FTStringType enum
We need to discriminate between f and t strings to know which token to return, a bit clumsy to use in my opinion - so looking for feedback here on how to improve this.
2025-09-09 11:16:20 -04:00
dependabot[bot]
0c82bfa761
build(deps): bump regex from 1.11.1 to 1.11.2 in /native (#1399)
Some checks failed
CI / test (macos-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (macos-latest, 3.13) (push) Has been cancelled
CI / test (macos-latest, 3.13t) (push) Has been cancelled
CI / test (macos-latest, 3.14) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.9) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.13) (push) Has been cancelled
CI / test (windows-latest, 3.13t) (push) Has been cancelled
CI / test (windows-latest, 3.14) (push) Has been cancelled
CI / test (windows-latest, 3.14t) (push) Has been cancelled
CI / test (windows-latest, 3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / Rust unit tests (push) Has been cancelled
CI / Rustfmt (push) Has been cancelled
CI / build (push) Has been cancelled
GitHub Actions Security Analysis with zizmor 🌈 / zizmor latest via PyPI (push) Has been cancelled
pypi_upload / Upload wheels to pypi (push) Has been cancelled
Bumps [regex](https://github.com/rust-lang/regex) from 1.11.1 to 1.11.2.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.11.1...1.11.2)

---
updated-dependencies:
- dependency-name: regex
  dependency-version: 1.11.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-07 21:30:06 -07:00
dependabot[bot]
f40d835145
build(deps): bump actions/setup-python from 5 to 6 (#1400)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-07 21:29:40 -07:00
Stephen Morton
d721a06c3f
generate Attribute nodes when applying type annotations (#1396)
Some checks failed
CI / docs (push) Has been cancelled
CI / Rust unit tests (push) Has been cancelled
CI / test (macos-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (macos-latest, 3.13) (push) Has been cancelled
CI / test (macos-latest, 3.13t) (push) Has been cancelled
CI / test (macos-latest, 3.14) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.14t) (push) Has been cancelled
CI / test (macos-latest, 3.9) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.9) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.13) (push) Has been cancelled
CI / test (windows-latest, 3.13t) (push) Has been cancelled
CI / test (windows-latest, 3.14) (push) Has been cancelled
CI / test (windows-latest, 3.14t) (push) Has been cancelled
CI / Rustfmt (push) Has been cancelled
CI / build (push) Has been cancelled
pypi_upload / build (push) Has been cancelled
GitHub Actions Security Analysis with zizmor 🌈 / zizmor latest via PyPI (push) Has been cancelled
pypi_upload / Upload wheels to pypi (push) Has been cancelled
* generate Attribute nodes when applying type annotations

The old version generated an incorrect CST which
happened to work as long as you didn't do further processing.

* add a test
2025-09-03 16:54:44 -04:00
dependabot[bot]
e064729b4c
build(deps): bump pypa/cibuildwheel from 3.0.1 to 3.1.4 (#1395)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 3.0.1 to 3.1.4.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v3.0.1...v3.1.4)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-version: 3.1.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-03 16:23:44 -04:00
dependabot[bot]
f746afd537
build(deps): bump rayon from 1.10.0 to 1.11.0 in /native (#1394)
Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.10.0 to 1.11.0.
- [Changelog](https://github.com/rayon-rs/rayon/blob/main/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.10.0...rayon-core-v1.11.0)

---
updated-dependencies:
- dependency-name: rayon
  dependency-version: 1.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-03 16:23:29 -04:00
martin
2048e6693c
bump version to 1.8.3 (#1397)
Some checks failed
CI / test (macos-latest, 3.13) (push) Has been cancelled
CI / test (macos-latest, 3.13t) (push) Has been cancelled
CI / test (macos-latest, 3.14) (push) Has been cancelled
CI / test (macos-latest, 3.14t) (push) Has been cancelled
CI / test (macos-latest, 3.9) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13) (push) Has been cancelled
CI / test (ubuntu-latest, 3.13t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14) (push) Has been cancelled
CI / test (ubuntu-latest, 3.14t) (push) Has been cancelled
CI / test (ubuntu-latest, 3.9) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.13) (push) Has been cancelled
CI / test (windows-latest, 3.13t) (push) Has been cancelled
CI / test (windows-latest, 3.14) (push) Has been cancelled
CI / test (windows-latest, 3.14t) (push) Has been cancelled
CI / test (windows-latest, 3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / Rust unit tests (push) Has been cancelled
CI / Rustfmt (push) Has been cancelled
CI / build (push) Has been cancelled
GitHub Actions Security Analysis with zizmor 🌈 / zizmor latest via PyPI (push) Has been cancelled
pypi_upload / build (push) Has been cancelled
pypi_upload / Upload wheels to pypi (push) Has been cancelled
2025-08-29 15:37:00 -04:00
dependabot[bot]
441a7f0c81
build(deps): bump actions/download-artifact from 4 to 5 (#1390)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-16 18:58:52 -07:00
Ken Kawamoto
7090a0db2b
fixes match statements to work with PositionProvider (#1389)
* add failing test

* fix issue

* fixes an issue with PositionProvider not working with case statement

* remove comments

---------

Co-authored-by: steve <steve@patreon.com>
2025-08-04 17:27:13 -04:00
Thomas Serre
b395d7ccf7
Fix noqa comments (#1379) 2025-08-04 17:03:20 -04:00
martin
9542fc3882
remove entry points to pure parser (#1375)
* rm: ci

* rm: entry point

* fix: tests

* fix: remove combine step from ci

* linter fixes

* omit the _parser

* fix newlines

* fix: remove optional

* fix: linter

---------

Co-authored-by: thereversiblewheel <martin.li@uwaterloo.ca>
2025-07-30 16:27:20 +00:00
Hunter Hogan
aa53960458
Fix typos in tutorial.ipynb (#1378) 2025-07-15 20:22:23 +01:00
dependabot[bot]
2931c86e07
build(deps): bump pypa/cibuildwheel from 3.0.0 to 3.0.1 (#1373)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-version: 3.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-07 16:44:33 -04:00
dependabot[bot]
2fb4b2dd58
build(deps): bump astral-sh/setup-uv from 5 to 6 (#1365)
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 5 to 6.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/v5...v6)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 07:49:28 +01:00
Zsolt Dollenstein
4bc2116d2a
ci: test built wheels (#1359)
* bump uv version
* bump cibuildwheel to v3
* enable GIL for smoke tests for now
2025-06-15 12:39:36 +01:00
Zsolt Dollenstein
287ab059a0
bump pyo3 to 0.25.1 (#1361) 2025-06-15 11:46:04 +01:00
Zsolt Dollenstein
03285dd4bf
bump version to 1.8.2 (#1360) 2025-06-13 21:36:01 +01:00
Wei Lee
67ba746bed
fix(dependency): add back typing-extensions for 3.9 (#1358)
Missing typing-extensions breaks "from libcst.codemod import CodemodContext"
2025-06-12 11:57:20 +01:00
Zsolt Dollenstein
8c35ae20ef
Switch from hatch to uv (#1356)
* use dependency-groups in pyproject.toml
* replace `hatch run foo` with `uv run poe foo`
* install uv @ 0.7.12 in CI and disable caching
* use `uv run --group docs` for the `docs` command
* DRY docs between CONTRIBUTING and README
* tell pyre to ignore `.venv`
* set up uv to rebuild on rust, pyproject.toml, git changes
2025-06-10 21:58:40 +01:00
Zsolt Dollenstein
ab12c4c266
bump version to 1.8.1 (#1357) 2025-06-10 17:29:03 +01:00
Lysandros Nikolaou
db38266f1d
Upgrade PyYAML-ft version and use new module name (#1353)
* Upgrade PyYAML-ft version and use new module name

* add pyre ignore

---------

Co-authored-by: Zsolt Dollenstein <zsol@meta.com>
2025-06-10 17:21:21 +01:00
Zsolt Dollenstein
0b1a9810ae
Use poe as a task runner (#1355)
Make `hatch run foo` wrap the corresponding `poe` command.
2025-06-10 08:23:03 +01:00
dependabot[bot]
9f3629e58e
build(deps): bump pypa/cibuildwheel from 3.0.0b4 to 3.0.0rc2 (#1354)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 3.0.0b4 to 3.0.0rc2.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v3.0.0b4...v3.0.0rc2)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-version: 3.0.0rc2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-09 00:33:03 -07:00
Zsolt Dollenstein
b818c0c983
put itertools-0.13.0 back into lockfile 2025-06-07 14:06:28 +01:00
dependabot[bot]
70ccffc543
build(deps): bump itertools from 0.13.0 to 0.14.0 in /native (#1337)
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.13.0 to 0.14.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-version: 0.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 01:54:32 -07:00
dependabot[bot]
5a6970a225
build(deps): bump criterion from 0.5.1 to 0.6.0 in /native (#1339)
Bumps [criterion](https://github.com/bheisler/criterion.rs) from 0.5.1 to 0.6.0.
- [Changelog](https://github.com/bheisler/criterion.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bheisler/criterion.rs/compare/0.5.1...0.6.0)

---
updated-dependencies:
- dependency-name: criterion
  dependency-version: 0.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 01:54:21 -07:00
zaicruvoir1rominet
ca1f81f049
Avoid raising bare Exception (#1168)
* Keep old exception messages (avoid breaking-changes for users relying on exception messages)

* Move ``get_expected_str`` out of _exceptions.py, where it does not belong, to its own file in _parser/_parsing_check.py
2025-06-07 01:53:44 -07:00
Zsolt Dollenstein
e12eef5810
add helper to convert nodes to matchers (#1351)
* add helper to convert nodes to matchers

* suppress type error
2025-06-04 14:02:21 -07:00
Zsolt Dollenstein
935415a35a
ci: stop using actions-rs actions (#1352) 2025-06-03 22:38:19 -07:00
dependabot[bot]
482a2e5f09
build(deps): bump pypa/cibuildwheel from 3.0.0b2 to 3.0.0b4 (#1349)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 3.0.0b2 to 3.0.0b4.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v3.0.0b2...v3.0.0b4)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-version: 3.0.0b4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-02 03:59:34 +01:00
Zsolt Dollenstein
18d4f6aded
bump version to 1.8.0 (#1348) 2025-05-27 15:02:58 +01:00
Zsolt Dollenstein
ae64e0d534
ci: fix zizmor warnings (#1347) 2025-05-27 14:15:49 +01:00
Zsolt Dollenstein
1e67a9bb84
Build 3.14 wheels for testing (#1345)
* Build 3.14 wheels for testing
* use cibuildwheel 3
2025-05-27 11:44:16 +01:00
Amethyst Reese
efae53d365
Run CI tests on 3.14 (#1331)
* Run CI tests on 3.14

* noop commit to retrigger CI

---------

Co-authored-by: Zsolt Dollenstein <zsol.zsol@gmail.com>
2025-05-26 11:02:44 +01:00
dependabot[bot]
356ac00586
build(deps): bump syn from 2.0.87 to 2.0.101 in /native (#1338)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.87 to 2.0.101.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.87...2.0.101)

---
updated-dependencies:
- dependency-name: syn
  dependency-version: 2.0.101
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 08:42:40 +01:00
dependabot[bot]
3389d4e231
build(deps): bump quote from 1.0.37 to 1.0.40 in /native (#1341)
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.37 to 1.0.40.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.37...1.0.40)

---
updated-dependencies:
- dependency-name: quote
  dependency-version: 1.0.40
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 08:42:06 +01:00
dependabot[bot]
50032882d0
build(deps): bump peg from 0.8.4 to 0.8.5 in /native (#1340)
Bumps [peg](https://github.com/kevinmehall/rust-peg) from 0.8.4 to 0.8.5.
- [Release notes](https://github.com/kevinmehall/rust-peg/releases)
- [Commits](https://github.com/kevinmehall/rust-peg/compare/0.8.4...0.8.5)

---
updated-dependencies:
- dependency-name: peg
  dependency-version: 0.8.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 08:41:36 +01:00
Zsolt Dollenstein
3dc2289bf6
codegen: Support pipe syntax for Union types (#1336)
From 3.14 onwards, we'll get `foo | bar` instead of `typing.Union[foo, bar]` as the annotation for union types (including optional). This PR prepares the codegen script for this.
2025-05-26 08:40:54 +01:00
Zsolt Dollenstein
b560ae815c
Threadpool should be used if GIL is disabled. (#1335) 2025-05-25 20:13:12 +01:00
Zsolt Dollenstein
c224665ed7
ci: start building cp313t wheels (#1333)
Closes #1242.
2025-05-25 11:44:16 +01:00
Zsolt Dollenstein
16ed48d74b
Enable support for free-threading (#1295)
This PR:
1. marks the `libcst.native` module as free-threading-compatible
2. replaces the use of ProcessPoolExecutor with ThreadPoolExecutor if free-threaded CPython is detected at runtime
2025-05-25 11:43:18 +01:00
Zsolt Dollenstein
52acdf4163
cli: Instantiate Codemods per file (#1334)
Instead of sharing instances of a Codemod across many files, this PR allows passing in a Codemod class to `parallel_exec_transform_with_prettyprint` which will then instantiate the Codemod for each file.  `tool._codemod_impl` now starts using this API.

The old behavior is deprecated, because sharing codemod instances across files is a surprising behavior, and causes hard-to-diagnose bugs when a Codemod keeps track of its state via instance variables.
2025-05-25 09:23:10 +01:00
Zsolt Dollenstein
d002c14d6b
Replace multiprocessing with ProcessPoolExecutor (#1294)
Instead of relying on `multiprocessing.Pool`, this PR replaces the implementation of `parallel_exec_transform_with_prettyprint` with `concurrent.futures.ProcessPoolExecutor`
2025-05-22 08:18:20 +01:00
Zsolt Dollenstein
88457646b8
ci: build windows arm64 wheels (#1304) 2025-05-21 21:01:18 +01:00
dependabot[bot]
6cfabc9a80
build(deps): bump thiserror from 1.0.63 to 2.0.12 in /native (#1308)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.63 to 2.0.12.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.63...2.0.12)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 17:49:34 -07:00
dependabot[bot]
91a5d7efed
build(deps): bump pypa/cibuildwheel from 2.23.2 to 2.23.3 (#1328)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.23.2 to 2.23.3.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.23.2...v2.23.3)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-version: 2.23.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 17:38:12 -07:00
dependabot[bot]
b8fa757749
Bump syn from 2.0.75 to 2.0.87 in /native (#1238)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.75 to 2.0.87.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.75...2.0.87)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 17:32:31 -07:00
dependabot[bot]
9046fba231
Bump regex from 1.10.6 to 1.11.1 in /native (#1233)
Bumps [regex](https://github.com/rust-lang/regex) from 1.10.6 to 1.11.1.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.10.6...1.11.1)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 17:32:24 -07:00
dependabot[bot]
be0b668d08
Bump black from 24.8.0 to 25.1.0 (#1290)
* Bump black from 24.8.0 to 25.1.0

Bumps [black](https://github.com/psf/black) from 24.8.0 to 25.1.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/24.8.0...25.1.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix formatting and tests

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Amethyst Reese <amethyst@n7.gg>
2025-05-19 20:53:44 -04:00
dependabot[bot]
d3386b168f
build(deps): bump astral-sh/setup-uv from 5 to 6 (#1327)
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 5 to 6.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/v5...v6)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-19 19:53:09 -04:00
dependabot[bot]
6e70e1cadc
build(deps): bump trybuild from 1.0.99 to 1.0.105 in /native (#1329) 2025-05-19 23:51:23 +00:00
dependabot[bot]
64c761d486
build(deps): bump flake8 from 7.1.2 to 7.2.0 (#1321)
Bumps [flake8](https://github.com/pycqa/flake8) from 7.1.2 to 7.2.0.
- [Commits](https://github.com/pycqa/flake8/compare/7.1.2...7.2.0)

---
updated-dependencies:
- dependency-name: flake8
  dependency-version: 7.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-19 19:14:36 -04:00
dependabot[bot]
26139e72de
build(deps): bump jinja2 from 3.1.5 to 3.1.6 (#1310)
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.5 to 3.1.6.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.5...3.1.6)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-19 19:14:19 -04:00
Nathan Goldbaum
b2406e799c
update pyo3 to 0.25 (#1324)
* build(deps): bump pyo3 from 0.23.5 to 0.25.0 in /native

Bumps [pyo3](https://github.com/pyo3/pyo3) from 0.23.5 to 0.25.0.
- [Release notes](https://github.com/pyo3/pyo3/releases)
- [Changelog](https://github.com/PyO3/pyo3/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pyo3/pyo3/compare/v0.23.5...v0.25.0)

---
updated-dependencies:
- dependency-name: pyo3
  dependency-version: 0.25.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* update pyo3 to 0.24

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Amethyst Reese <amethyst@n7.gg>
2025-05-19 19:13:17 -04:00
dependabot[bot]
11d6e36450
build(deps): bump pypa/cibuildwheel from 2.23.1 to 2.23.2 (#1317)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.23.1 to 2.23.2.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.23.1...v2.23.2)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 10:19:43 +01:00
Nathan Goldbaum
a4804cf07e
allow configuring empty formatter lists in codemod CLI (#1319)
* allow configuring empty formatter lists

* appease linter
2025-04-02 10:19:27 +01:00
Nathan Goldbaum
6d31b5ead5
use released version of setup-python (#1318) 2025-03-31 21:12:13 -07:00
dependabot[bot]
cef85096b6
build(deps): bump pypa/cibuildwheel from 2.23.0 to 2.23.1 (#1315)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.23.0 to 2.23.1.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/v2.23.1/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.23.0...v2.23.1)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-22 21:24:55 +00:00
Hadi Alqattan
2c7834eae6
ci: enable macos intel wheels (#1316) 2025-03-22 21:24:15 +00:00
Nathan Goldbaum
79f736ac60
ci: don't use --no-build-isolation for free-threaded CI (#1314) 2025-03-13 19:17:02 +00:00
Zsolt Dollenstein
5902ccede3
Bump version to 1.7.0 (#1313) 2025-03-13 09:56:58 +00:00
Michał Górny
17eafc3f43
Bump PyO3 to 0.23.5 (#1311) 2025-03-13 07:39:55 +00:00
Nathan Goldbaum
d580469ea5
add free-threaded CI (#1312) 2025-03-12 21:57:31 +00:00
Zsolt Dollenstein
129d9876d2
ci: force LIBCST_NO_LOCAL_SCHEME in cibuildwheel
Summary:

Test Plan:
2025-03-07 18:12:22 +00:00
Zsolt Dollenstein
cd959d66c0
ci: pass through LIBCST_NO_LOCAL_SCHEME
try #2
2025-03-07 17:23:58 +00:00
Zsolt Dollenstein
218e8e5d43
ci: strip local scheme from uploaded wheels 2025-03-07 16:29:53 +00:00
dependabot[bot]
e2e712d43f
Bump flake8 from 7.1.1 to 7.1.2 (#1292)
Bumps [flake8](https://github.com/pycqa/flake8) from 7.1.1 to 7.1.2.
- [Commits](https://github.com/pycqa/flake8/compare/7.1.1...7.1.2)

---
updated-dependencies:
- dependency-name: flake8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-07 15:36:26 +00:00
Nathan Goldbaum
727e433539
Update for Pyo3 0.23 (#1289)
* Update Cargo.lock and Cargo.toml for PyO3 0.23 support

* Replace deprecated _bound methods with their new undeprecated names

* Update TryIntoPy trait to use IntoPyObject

* Update ParserError wrapper to use IntoPyObject

* replace unwrap with early return
2025-03-07 15:35:17 +00:00
Zsolt Dollenstein
5eccb5f08b
ci: use native arm github runners (#1303) 2025-03-07 15:32:39 +00:00
Zsolt Dollenstein
64ca5ed8df
ci: move cibuildwheel config into pyproject.toml (#1277) 2025-03-07 14:21:41 +00:00
Zsolt Dollenstein
eae77997be
ci: install libatomic on linux before rustup (#1301) 2025-03-07 14:18:25 +00:00
dependabot[bot]
edd75bfa62
Bump pypa/cibuildwheel from 2.22.0 to 2.23.0 (#1299)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.22.0 to 2.23.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.22.0...v2.23.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-07 11:23:36 +00:00
Zanie Blue
985cec808e
Remove dependency on chic and upgrade annotate-snippets (#1293)
* Vendor `chic`

At 0761036492

* Remove unused `Error::help` method

* Upgrade to `annotate_snippets` 0.9.x

Applying 27c99b5038

* Upgrade to `annotate_snippets` 0.10.x

See https://salsa.debian.org/rust-team/debcargo-conf/-/blob/master/src/chic/debian/patches/annotate-snippets-0.10

* Upgrade to `annotate_snippets` 0.11.x

As in https://salsa.debian.org/rust-team/debcargo-conf/-/blob/master/src/chic/debian/patches/annotate-snippets-0.11

* Drop `chic` compatibility layer
2025-02-21 22:20:49 +00:00
Zsolt Dollenstein
c825afb87d
Bump to 1.6.0
Summary:

Test Plan:
2025-01-09 19:09:48 +00:00
dependabot[bot]
01c2939445
Bump jinja2 from 3.1.4 to 3.1.5 (#1265)
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.4 to 3.1.5.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.4...3.1.5)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-09 19:00:34 +00:00
dependabot[bot]
af136b91ac
Bump astral-sh/setup-uv from 4 to 5 (#1264)
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 4 to 5.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/v4...v5)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-09 18:59:38 +00:00
Danny Yang
6b483c6113
Add codemod to rename typing aliases of builtins (#1267)
* add codemod to rename typing aliases of builtins

* format
2025-01-09 18:59:00 +00:00
Zsolt Dollenstein
403782d5e9
Cargo.lock changes 2025-01-09 18:50:02 +00:00
Jelmer Vernooij
d2382d81ac
Upgrade pyo3 to 0.22 (#1180)
* Upgrade pyo3 to 0.22

* libcst_native: add optional signature

Newer versions of pyo3 warn about missing signatures
2025-01-09 18:47:12 +00:00
Zsolt Dollenstein
20837f7824
ci: disable macos intel wheels (#1275)
cibuildwheel fails to build these after a recent version upgrade
2025-01-09 18:39:18 +00:00
Zsolt Dollenstein
b523b360c1
run cargo fmt
Summary:

Test Plan:
2025-01-08 20:02:17 +00:00
Crozzers
595d7f6aaf
Expose TypeAlias and TypeVar related structs in rust library (#1274) 2025-01-08 19:58:37 +00:00
Danny Yang
c4e7934253
add types classifier and badge (#1272) 2025-01-04 09:59:40 +00:00
Danny Yang
776452f351
Add codemod to fix variadic callable annotations (#1269)
* add fix variadic callable codemod

* format
2025-01-02 19:49:03 -05:00
Danny Yang
d26987202b
Add codemod to convert typing.Union to | (#1270)
* add union to or codemod

* lint

* early return
2025-01-02 19:48:55 -05:00
Zsolt Dollenstein
230f177c84
ci: audit workflows with zizmor (#1262)
https://woodruffw.github.io/zizmor/
2024-12-16 10:01:02 +00:00
dependabot[bot]
3e4bae471b
Bump ufmt from 2.7.3 to 2.8.0 (#1236)
Bumps [ufmt](https://github.com/omnilib/ufmt) from 2.7.3 to 2.8.0.
- [Changelog](https://github.com/omnilib/ufmt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/omnilib/ufmt/compare/v2.7.3...v2.8.0)

---
updated-dependencies:
- dependency-name: ufmt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-13 11:41:48 +00:00
Zsolt Dollenstein
a3b5529bb3
rename: fix renaming toplevel names (#1260)
For toplevel module names imported via `import foo`, the rename codemod would fail to change these. This PR fixes that.
2024-12-11 20:30:33 +00:00
khameeteman
b04670c166
bump 3.12 to 3.13 in readme (#1228) 2024-12-07 21:33:56 +00:00
Zsolt Dollenstein
d24192a40f
rename: don't eat commas unnecessarily (#1256)
#1254 was a bit too aggressive in removing commas. They shouldn't be removed if there are parenthesis around the imported names.
2024-12-02 16:13:12 +00:00
Zsolt Dollenstein
8c30fcef30
rename: don't leave trailing commas (#1254)
When renaming the last element of a `from a import b,c` import, don't leave the trailing comma after `b`
2024-12-02 10:00:59 +00:00
Zsolt Dollenstein
c05ac74b9a
refactor: allow scheduled_removals to accept a tuple (#1253)
This fixes a TODO
2024-12-02 10:00:35 +00:00
Zsolt Dollenstein
a36432c958
rename: Fix imports with aliases (#1252)
When renaming `a.b` -> `c.d`, in imports like `import a.b as x` the as_name wasn't correctly removed even though references to `x` were renamed to `c.d`.

This PR makes the codemod remove the `x` asname in these cases.
2024-11-29 11:23:59 +00:00
Zsolt Dollenstein
28e0f397b2
rename: handle imports via a parent module (#1251)
When requesting a rename for `a.b.c`, we want to act on `import a` when it's used to access `a.b.c`
2024-11-28 20:02:23 +00:00
Zsolt Dollenstein
6fdca74c90
rename: store state in scratch (#1250)
This PR changes RenameCodemod to store its per-module state in `self.context.scratch` which gets properly reset between files.
2024-11-28 14:59:43 +00:00
dependabot[bot]
08da127e54
Bump pypa/cibuildwheel from 2.21.2 to 2.22.0 (#1247)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.21.2 to 2.22.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.21.2...v2.22.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-27 12:27:11 +00:00
Zsolt Dollenstein
4aa92f3857
Bump version to 1.5.1 (#1246) 2024-11-18 20:19:01 +00:00
Blazej Michalik
4ff38c039e
ci: skip musllinux builds for unsupported archs (#1244)
This fixes current CI failures by skipping Musl builds for `i686`,
`ppc64le`, `s390x`, and `armv7le` architectures.

The failures are due to Rust ecosystem having only partial support / not
having tool chains for these architectures. For the list of supported
archs and tiers of support, see:

https://doc.rust-lang.org/nightly/rustc/platform-support.html

The architectures skipped here are either, from the Rust PoV:

- Tier-2 support without host tools.
- Tier-3 support without host tools.
2024-11-17 18:01:34 +00:00
Blazej Michalik
bfd1000289
ci: build wheels for musllinux (#1243) 2024-11-17 10:19:27 +00:00
Zsolt Dollenstein
42df0881ba
Fix doc build error (#1221)
Apparently doc2path now returns a path not a string
2024-10-10 11:20:25 +01:00
Zsolt Dollenstein
527a4b04e1
bump versions in cargo.toml 2024-10-10 10:54:15 +01:00
Zsolt Dollenstein
dde88a2082
add changelog entry 2024-10-10 10:53:01 +01:00
khameeteman
a2b3456fe9
include python 3.13 in build (#1203) 2024-10-10 10:38:27 +01:00
dependabot[bot]
b49e705579
Bump pypa/cibuildwheel from 2.21.1 to 2.21.2 (#1218)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.21.1 to 2.21.2.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.21.1...v2.21.2)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-10 09:22:46 +01:00
dependabot[bot]
586b4d74e4
Bump ufmt from 2.7.0 to 2.7.3 (#1212)
Bumps [ufmt](https://github.com/omnilib/ufmt) from 2.7.0 to 2.7.3.
- [Changelog](https://github.com/omnilib/ufmt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/omnilib/ufmt/compare/v2.7.0...v2.7.3)

---
updated-dependencies:
- dependency-name: ufmt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-25 11:32:36 +01:00
Kirill Ignatev
9fd67bca49
fix certain matchers breaking under multiprocessing by initializing them late (#1204)
* Add is_property check

Skip properties to prevent exceptions

* Delayed initialization of matchers

To support multiprocessing on Windows/macOS
Issue #1181

* Add a test for matcher decorators with multiprocessing
2024-09-25 11:29:54 +01:00
dependabot[bot]
6a059bec9a
Bump pypa/cibuildwheel from 2.21.0 to 2.21.1 (#1211)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.21.0 to 2.21.1.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.21.0...v2.21.1)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-25 11:22:57 +01:00
Wim Jeantine-Glenn
0974a416a7
Typo fix in codemods_tutorial.rst (trivial) (#1208) 2024-09-18 09:23:30 +01:00
dependabot[bot]
61b9ac3a68
Bump pypa/cibuildwheel from 2.20.0 to 2.21.0 (#1206)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.20.0 to 2.21.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.20.0...v2.21.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 07:37:01 +01:00
dependabot[bot]
9834694730
Bump regex from 1.9.3 to 1.10.6 in /native (#1198)
Bumps [regex](https://github.com/rust-lang/regex) from 1.9.3 to 1.10.6.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.9.3...1.10.6)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-23 10:26:36 -07:00
dependabot[bot]
ccf9623ccf
Bump trybuild from 1.0.86 to 1.0.99 in /native (#1194)
Bumps [trybuild](https://github.com/dtolnay/trybuild) from 1.0.86 to 1.0.99.
- [Release notes](https://github.com/dtolnay/trybuild/releases)
- [Commits](https://github.com/dtolnay/trybuild/compare/1.0.86...1.0.99)

---
updated-dependencies:
- dependency-name: trybuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 18:47:34 -07:00
dependabot[bot]
8c5aa32000
Bump thiserror from 1.0.55 to 1.0.63 in /native (#1196)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.55 to 1.0.63.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.55...1.0.63)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 18:47:27 -07:00
dependabot[bot]
77e2a51d35
Bump peg from 0.8.1 to 0.8.4 in /native (#1197)
Bumps [peg](https://github.com/kevinmehall/rust-peg) from 0.8.1 to 0.8.4.
- [Release notes](https://github.com/kevinmehall/rust-peg/releases)
- [Commits](https://github.com/kevinmehall/rust-peg/compare/0.8.1...0.8.4)

---
updated-dependencies:
- dependency-name: peg
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 17:42:44 -07:00
dependabot[bot]
47b171b9a7
Bump rayon from 1.7.0 to 1.10.0 in /native (#1193)
Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.7.0 to 1.10.0.
- [Changelog](https://github.com/rayon-rs/rayon/blob/main/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.7.0...rayon-core-v1.10.0)

---
updated-dependencies:
- dependency-name: rayon
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 17:40:50 -07:00
dependabot[bot]
38cc0798b2
Bump trybuild from 1.0.71 to 1.0.86 in /native (#1076)
Bumps [trybuild](https://github.com/dtolnay/trybuild) from 1.0.71 to 1.0.86.
- [Release notes](https://github.com/dtolnay/trybuild/releases)
- [Commits](https://github.com/dtolnay/trybuild/compare/1.0.71...1.0.86)

---
updated-dependencies:
- dependency-name: trybuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 17:26:14 -07:00
dependabot[bot]
9f198179f3
Bump ts-graphviz/setup-graphviz from 1 to 2 (#1105)
Bumps [ts-graphviz/setup-graphviz](https://github.com/ts-graphviz/setup-graphviz) from 1 to 2.
- [Release notes](https://github.com/ts-graphviz/setup-graphviz/releases)
- [Commits](https://github.com/ts-graphviz/setup-graphviz/compare/v1...v2)

---
updated-dependencies:
- dependency-name: ts-graphviz/setup-graphviz
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 17:23:25 -07:00
dependabot[bot]
07ec61d8b0
Bump thiserror from 1.0.37 to 1.0.55 in /native (#1086)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.37 to 1.0.55.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.37...1.0.55)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 17:22:34 -07:00
dependabot[bot]
6017c40d19
Bump paste from 1.0.9 to 1.0.15 in /native (#1146)
Bumps [paste](https://github.com/dtolnay/paste) from 1.0.9 to 1.0.15.
- [Release notes](https://github.com/dtolnay/paste/releases)
- [Commits](https://github.com/dtolnay/paste/compare/1.0.9...1.0.15)

---
updated-dependencies:
- dependency-name: paste
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 17:22:20 -07:00
dependabot[bot]
b552469f1c
Bump itertools from 0.11.0 to 0.13.0 in /native (#1150)
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.11.0 to 0.13.0.
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/compare/v0.11.0...v0.13.0)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 17:21:56 -07:00
dependabot[bot]
2e49695427
Bump memchr from 2.5.0 to 2.7.4 in /native (#1165)
Bumps [memchr](https://github.com/BurntSushi/memchr) from 2.5.0 to 2.7.4.
- [Commits](https://github.com/BurntSushi/memchr/compare/2.5.0...2.7.4)

---
updated-dependencies:
- dependency-name: memchr
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 17:21:43 -07:00
dependabot[bot]
bf5fb4132e
Bump black from 23.12.1 to 24.8.0 (#1186)
* Bump black from 23.12.1 to 24.8.0

Bumps [black](https://github.com/psf/black) from 23.12.1 to 24.8.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/23.12.1...24.8.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update formatting

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Amethyst Reese <amethyst@n7.gg>
2024-08-22 16:46:01 -07:00
dependabot[bot]
cdf9ef414f
Bump flake8 from 7.0.0 to 7.1.1 (#1187)
Bumps [flake8](https://github.com/pycqa/flake8) from 7.0.0 to 7.1.1.
- [Commits](https://github.com/pycqa/flake8/compare/7.0.0...7.1.1)

---
updated-dependencies:
- dependency-name: flake8
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 16:29:31 -07:00
dependabot[bot]
be025613f9
Bump ufmt from 2.6.0 to 2.7.0 (#1163)
Bumps [ufmt](https://github.com/omnilib/ufmt) from 2.6.0 to 2.7.0.
- [Changelog](https://github.com/omnilib/ufmt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/omnilib/ufmt/compare/v2.6.0...v2.7.0)

---
updated-dependencies:
- dependency-name: ufmt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-22 16:29:16 -07:00
Amethyst Reese
a4203e5c49
Drop codecov from CI and readme (#1192)
* Drop codecov from CI and readme

* Remove upload job, move coverage check to test job with hatch
2024-08-22 16:06:01 -07:00
Michel Lind
52a59471c9
Use license instead of license-file (#1189)
Per the Cargo Book, `license-file` is only to be used if a package uses
a non-standard license; see https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields

Declare the licenses directly, and verify that the LICENSE file
containing the license breakdown is still included

```
…n LibCST/native/libcst_derive on  cargo-fixes [!] is 📦 v1.4.0 via 🦀 v1.77.1
⬢ [fedora:40] ❯ cargo package --list --allow-dirty | grep LICENSE
LICENSE

…n LibCST/native/libcst_derive on  cargo-fixes [!] is 📦 v1.4.0 via 🦀 v1.77.1
⬢ [fedora:40] ❯ cd ../libcst

michel in LibCST/native/libcst on  cargo-fixes [!] is 📦 v1.4.0 via 🦀 v1.77.1
⬢ [fedora:40] ❯ cargo package --list --allow-dirty | grep LICENSE
LICENSE
src/tokenizer/core/LICENSE
```

Signed-off-by: Michel Lind <salimma@fedoraproject.org>
2024-08-13 07:02:12 +01:00
dependabot[bot]
5f5fd386b0
Bump pypa/cibuildwheel from 2.19.2 to 2.20.0 (#1185)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.19.2 to 2.20.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.19.2...v2.20.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-09 09:26:31 +01:00
Kirill Ignatev
45234f198c
Clear warnings for each file in comemod cli (#1184)
* Clean warnings for each file in comemod cli

* Fix ZeroDivisionError: float division by zero

When codemodding too fast

* Recreate CodemodContext for each file

Keep only context.metadata_manager
Remove wrapper from context defaults on each file
2024-08-05 22:41:51 +01:00
dependabot[bot]
56cd1f9862
Update maturin requirement from <1.7,>=0.8.3 to >=1.7.0,<1.8 (#1170)
Updates the requirements on [maturin](https://github.com/pyo3/maturin) to permit the latest version.
- [Release notes](https://github.com/pyo3/maturin/releases)
- [Changelog](https://github.com/PyO3/maturin/blob/main/Changelog.md)
- [Commits](https://github.com/pyo3/maturin/compare/v0.8.3...v1.7.0)

---
updated-dependencies:
- dependency-name: maturin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-31 12:14:07 +01:00
dependabot[bot]
814f243a75
Bump pypa/cibuildwheel from 2.18.1 to 2.19.2 (#1171)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.18.1 to 2.19.2.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.18.1...v2.19.2)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-31 12:13:31 +01:00
Zsolt Dollenstein
fb9e47585b
make libcst_native::tokenizer public (#1182) 2024-07-31 12:13:05 +01:00
Kirill Ignatev
b0d145dddd
Add validation for If node (#1177)
* Add validation for If node

Don't allow no space no parentheses.
2024-07-30 09:01:07 +01:00
Jia Chen
e20e757159
Remove uses of # pyre-placeholder-stub (#1174) 2024-07-20 09:04:25 +01:00
Kirill Ignatev
72701e4b40
Mention codemod -x flag in docs (#1169) 2024-07-04 07:49:15 +01:00
dependabot[bot]
7bb00179d9
Bump pypa/cibuildwheel from 2.18.0 to 2.18.1 (#1155)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.18.0 to 2.18.1.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.18.0...v2.18.1)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 19:36:33 +01:00
Zsolt Dollenstein
8b97600fb3
fix various Match statement visitation errors (#1161)
Fixes #1160.

This PR also

- fixes `whitespace_before_colon` being swallowed during visitation on `MatchCase`s
- adds a new type of roundtrip test that catches issues of this class: the test applies a noop transformer to exercise the visitation API and compares the result with the original source.
- adds a few more cases to the match fixture
2024-06-12 17:29:25 +01:00
Camillo
9f6e27600f
FullyQualifiedNameProvider: Optionally consider pyproject.toml files when determining a file's module name and package (#1148) 2024-06-12 10:36:50 +01:00
dependabot[bot]
47ff8cbf22
Update maturin requirement from <1.6,>=0.8.3 to >=0.8.3,<1.7 (#1158)
Updates the requirements on [maturin](https://github.com/pyo3/maturin) to permit the latest version.
- [Release notes](https://github.com/pyo3/maturin/releases)
- [Changelog](https://github.com/PyO3/maturin/blob/main/Changelog.md)
- [Commits](https://github.com/pyo3/maturin/compare/v0.8.3...v1.6.0)

---
updated-dependencies:
- dependency-name: maturin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 20:45:18 +01:00
Zsolt Dollenstein
0b4016c5b3
use trusted publishing for pypi (#1154) 2024-05-26 22:57:47 +01:00
Zsolt Dollenstein
96f53416e3
Bump version to 1.4.0 (#1152) 2024-05-22 10:20:33 -04:00
dependabot[bot]
7b9907a560
Bump pypa/cibuildwheel from 2.17.0 to 2.18.0 (#1145)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.17.0 to 2.18.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.17.0...v2.18.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 08:41:05 -04:00
Zsolt Dollenstein
db696e6348
fix: don't reset context.scratch between files (#1151)
#453 fixed scratch leaking between files by setting it to empty, but that drops all the scratch space that was set up before the codemod runs (e.g. in the transformer's constructor)

This PR improves the fix by preserving the initial scratch.
2024-05-21 15:52:49 -04:00
martin
71b0a1288b
Implement Type Defaults for Type Parameters (PEP 696) (#1141)
Co-authored-by: thereversiblewheel <martin.li@uwaterloo.ca>
2024-05-20 11:26:38 -04:00
zaicruvoir1rominet
6bbc69316b
Add the ability to dump CST to .dot (graphviz) files (#1147)
* Make the nodes fields filtering process - from libcst.tool - public, so that other libraries may provide their own custom representation of LibCST graphs.

* Create functions to access & filter CST-node fields (with appropriate docstrings & tests), in libcst.helpers

* Add new CST-node fields functions to helpers documentation.
2024-05-20 11:25:13 -04:00
zaicruvoir1rominet
efc53af608
Add helper functions for common ways of filtering nodes (#1137)
* Make the nodes fields filtering process - from libcst.tool - public, so that other libraries may provide their own custom representation of LibCST graphs.

* Create functions to access & filter CST-node fields (with appropriate docstrings & tests), in libcst.helpers

* Add new CST-node fields functions to helpers documentation.
2024-05-13 10:20:47 +01:00
Zsolt Dollenstein
6783244eab
Add typechecker to CONTRIBUTING.md 2024-05-13 09:47:28 +01:00
zaicruvoir1rominet
e7b009655a
Update CONTRIBUTING.md (#1142)
* Update CONTRIBUTING.md

* Fix repo link

* Fix line break getting removed
2024-05-12 20:40:07 +01:00
dependabot[bot]
942dc8007a
Bump codecov/codecov-action from 3 to 4 (#1103)
* Bump codecov/codecov-action from 3 to 4

Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* set codecov token

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Zsolt Dollenstein <zsol.zsol@gmail.com>
2024-05-06 18:02:33 +01:00
dependabot[bot]
20ed6c49c4
Bump ufmt from 2.5.1 to 2.6.0 (#1139)
Bumps [ufmt](https://github.com/omnilib/ufmt) from 2.5.1 to 2.6.0.
- [Changelog](https://github.com/omnilib/ufmt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/omnilib/ufmt/compare/v2.5.1...v2.6.0)

---
updated-dependencies:
- dependency-name: ufmt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 11:42:51 +01:00
dependabot[bot]
a068f4bdd1
Bump jinja2 from 3.1.3 to 3.1.4 (#1140)
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.3 to 3.1.4.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.3...3.1.4)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 11:42:40 +01:00
dependabot[bot]
18a863741e
Update maturin requirement from <1.5,>=0.8.3 to >=0.8.3,<1.6 (#1117)
Updates the requirements on [maturin](https://github.com/pyo3/maturin) to permit the latest version.
- [Release notes](https://github.com/pyo3/maturin/releases)
- [Changelog](https://github.com/PyO3/maturin/blob/main/Changelog.md)
- [Commits](https://github.com/pyo3/maturin/compare/v0.8.3...v1.5.0)

---
updated-dependencies:
- dependency-name: maturin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-03 22:38:42 +01:00
Camillo
82f804a66a
Fix Literal parse error in RemoveImportsVisitor (#1130) 2024-05-03 22:36:20 +01:00
Sebastián Ramírez
0713a35548
Fix typo in docs/source/scope_tutorial.ipynb (#1135)
* ✏️ Fix typo in `docs/source/scope_tutorial.ipynb`

* ✏️ Fix another typo

* ✏️ Fix typos

* ✏️ Fix typos
2024-05-03 22:27:37 +01:00
Sebastián Ramírez
e9dc135ae4
Fix tiny typo in docs/source/metadata.rst (#1134)
* ✏️ Fix typo in metadata.rst

* ✏️ Fix typo
2024-05-03 22:27:20 +01:00
Sergii Dymchenko
0d087acdf6
Typo fix FullRepoManager (#1138) 2024-05-03 22:25:37 +01:00
Zsolt Dollenstein
9f54920d9d
bump version to 1.3.1 2024-04-03 21:13:08 +01:00
Zsolt Dollenstein
4fb66a33e6
remove mypy_extensions import (#1128) 2024-04-03 21:10:44 +01:00
Zsolt Dollenstein
8b33474001
bump version to 1.3.0
Summary:

Test Plan:
2024-04-03 19:54:30 +01:00
Zsolt Dollenstein
2ffca10845
remove typing dependencies (#1126)
Summary:
This PR removes the `typing_extensions` and `typing_inspect` dependencies as we can now rely on the built-in `typing` module since Python 3.9.

Test Plan:
existing tests
2024-04-03 19:50:14 +01:00
Zsolt Dollenstein
a35a05f056
ci: only build host-native wheels for macos 2024-03-22 09:05:20 -07:00
Zsolt Dollenstein
ffdea4d157
Build native mac arm64 wheels (#1121) 2024-03-21 17:11:45 -07:00
dependabot[bot]
5a50be26f1
Bump pypa/cibuildwheel from 2.16.5 to 2.17.0 (#1119)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.16.5 to 2.17.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.16.5...v2.17.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 16:57:57 -07:00
dependabot[bot]
36e791ebe5
Bump usort from 1.0.7 to 1.0.8.post1 (#1109)
Bumps [usort](https://github.com/facebook/usort) from 1.0.7 to 1.0.8.post1.
- [Changelog](https://github.com/facebook/usort/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/usort/compare/v1.0.7...v1.0.8.post1)

---
updated-dependencies:
- dependency-name: usort
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-04 17:39:10 -08:00
dependabot[bot]
f6493dbe8d
Bump ufmt from 2.3.0 to 2.5.1 (#1114)
Bumps [ufmt](https://github.com/omnilib/ufmt) from 2.3.0 to 2.5.1.
- [Changelog](https://github.com/omnilib/ufmt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/omnilib/ufmt/compare/v2.3.0...v2.5.1)

---
updated-dependencies:
- dependency-name: ufmt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-04 21:11:33 +00:00
Zsolt Dollenstein
627bb0c4ab
bump version to 1.2.0 (#1110) 2024-02-19 12:04:43 +00:00
Dimitris Iliopoulos
fa9300e3a3
Upgrade pyo3 to 0.20 (#1106)
Co-authored-by: Dimitris Iliopoulos <diliopoulos@fb.com>
2024-02-15 09:08:09 +00:00
Zsolt Dollenstein
8a19d05538
remove comment 2024-02-03 12:30:39 +00:00
Zsolt Dollenstein
a4fb999774
ci: build linux-arm64 wheels using emulation (#1102)
Stop using self-hosted runner
2024-02-03 11:39:41 +00:00
Zsolt Dollenstein
e5cc07c342
ci: upgrade to cibuildwheel 2.16.5 (#1101)
This fixes wheel build failures on Windows.
2024-02-03 09:52:16 +00:00
Zsolt Dollenstein
68f98c676c
ci: various tweaks (#1100) 2024-02-03 09:40:04 +00:00
Zsolt Dollenstein
724026aa65
Remove reference to distutil (#1099)
Distutil has been removed in Python 3.12.

Tested by:
```
py -m libcst.tool codemod noop.NOOPCommand .\libcst\tool.py
Calculating full-repo metadata...
Executing codemod...
Finished codemodding 1 files!
 - Transformed 1 files successfully.
 - Skipped 0 files.
 - Failed to codemod 0 files.
 - 0 warnings were generated.
```
2024-02-02 20:58:56 +00:00
Zsolt Dollenstein
55f3e34dfc
Add roundtrip tests from Python (#1098)
Our current roundtrip tests only excerise the Rust codepaths. This PR runs the same roundtrip scenarios but from Python.
2024-02-02 20:50:07 +00:00
Zsolt Dollenstein
c854c986b6
Fix parsing list matchers without explicit brackets (#1097)
```
match a:
  case 1, 2: pass
```

This is parsed correctly by the grammar, but the default values of `MatchList.lbracket` and `MatchList.rbracket` are inconsistent between Python and Rust, causing the above snippet to round-trip (from Python) to:
```
match a:
  case [1, 2]: pass
```

Fixes #1096.
2024-02-02 20:49:25 +00:00
Amethyst Reese
a2a60c147c
Make readme example use python syntax highlighting (#1092) 2024-01-18 19:09:52 +00:00
Amethyst Reese
fad448eb81
Upgrade rust to version 1.70 in readthedocs config (#1091)
Readthedocs builds are currently failing because the libcst wheel fails
to build, hitting an error when trying to get rust dependencies:

```
running build_rust
  Updating crates.io index
error: failed to select a version for the requirement `regex = "=1.9.3"`
candidate versions found which didn't match: 1.8.4, 1.8.3, 1.8.2, ...
location searched: crates.io index
required by package `libcst v1.1.0 (/home/docs/checkouts/readthedocs.org/user_builds/libcst/checkouts/latest/native/libcst)`
error: `cargo metadata --manifest-path native/libcst/Cargo.toml --format-version 1` failed with code 101
```

Assuming this is related to current configuration requesting rust v1.55,
rather than 1.70 that is currently offered.
2024-01-16 21:15:47 +00:00
dependabot[bot]
f5fe4eb25a
Bump flake8 from 6.1.0 to 7.0.0 (#1088)
Bumps [flake8](https://github.com/pycqa/flake8) from 6.1.0 to 7.0.0.
- [Commits](https://github.com/pycqa/flake8/compare/6.1.0...7.0.0)

---
updated-dependencies:
- dependency-name: flake8
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-16 09:02:10 -08:00
dependabot[bot]
c5ef75d0c3
Bump jinja2 from 3.1.2 to 3.1.3 (#1090)
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.2...3.1.3)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-16 09:01:32 -08:00
dependabot[bot]
dfcba1ff03
Bump ufmt from 2.2.0 to 2.3.0 (#1047)
Bumps [ufmt](https://github.com/omnilib/ufmt) from 2.2.0 to 2.3.0.
- [Changelog](https://github.com/omnilib/ufmt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/omnilib/ufmt/compare/v2.2.0...v2.3.0)

---
updated-dependencies:
- dependency-name: ufmt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-08 14:06:02 +00:00
dependabot[bot]
266f531de1
Bump fixit from 2.0.0.post1 to 2.1.0 (#1087)
Bumps [fixit](https://github.com/Instagram/Fixit) from 2.0.0.post1 to 2.1.0.
- [Changelog](https://github.com/Instagram/Fixit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Instagram/Fixit/compare/v2.0.0.post1...v2.1.0)

---
updated-dependencies:
- dependency-name: fixit
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-08 14:05:38 +00:00
dependabot[bot]
c6fa092565
Update maturin requirement from <0.16,>=0.8.3 to >=0.8.3,<1.5 (#1059)
Updates the requirements on [maturin](https://github.com/pyo3/maturin) to permit the latest version.
- [Release notes](https://github.com/pyo3/maturin/releases)
- [Changelog](https://github.com/PyO3/maturin/blob/main/Changelog.md)
- [Commits](https://github.com/pyo3/maturin/compare/v0.8.3...v1.4.0)

---
updated-dependencies:
- dependency-name: maturin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-05 09:39:05 +00:00
Wilfred Hughes
c011a48a24
Allow Element::codegen to be used by external users (#1071)
The `Codegen` trait is `pub`, but users wanting to explicitly perform
codegen for `Element` had to copy-paste this part of the code.
2024-01-04 19:26:27 +00:00
Zsolt Dollenstein
a5f1f9a231
ci: use separate artifact names (#1085) 2024-01-04 12:42:15 +00:00
Alvaro Leiva geisse
1757e0f5b4
installing rustc/cargo for mybinder demo (#1083)
when we switch to the rust compiler by default, mybinder stop working, as reoported in https://github.com/Instagram/LibCST/issues/1054 this is because the binder docker image does not have a rust compiler or tools, this install them by using the apt.txt file

Co-authored-by: Alvaro Leiva Geisse <aleivag@meta.com>
2024-01-03 22:16:52 +00:00
anonymousdouble
dbbfe1e0b8
Update test_fix_pyre_directives.py (#1082)
* Update test_fix_pyre_directives.py

refactor with fstring to format string to make code more Pythonic.

* Update test_fix_pyre_directives.py

refactor with fstring to format string to make code more Pythonic.

* Update test_fix_pyre_directives.py

refactor with fstring to format string to make code more Pythonic.

* Update test_fix_pyre_directives.py

refactor with fstring to format string to make code more Pythonic.

* Update test_fix_pyre_directives.py

refactor with chain constant value assignment to make code more Pythonic

* Update test_fix_pyre_directives.py

refactor with chain constant value assignment to make code more Pythonic
2024-01-03 13:06:33 -08:00
Zsolt Dollenstein
30df6fcdab
remove 3.8 support (#1073)
This PR also starts using 3.12 properly in CI
2024-01-03 12:06:37 -08:00
dependabot[bot]
4b31d3db49
Bump actions/setup-python from 4 to 5 (#1060)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-03 10:10:35 -08:00
dependabot[bot]
dc329f29ac
Bump actions/upload-artifact from 3 to 4 (#1065)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-03 10:05:13 -08:00
dependabot[bot]
976b84c618
Bump actions/download-artifact from 3 to 4 (#1066)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-03 10:02:15 -08:00
dependabot[bot]
5a8650b92e
Bump black from 23.9.1 to 23.12.1 (#1077)
Bumps [black](https://github.com/psf/black) from 23.9.1 to 23.12.1.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/23.9.1...23.12.1)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-03 10:01:27 -08:00
Zsolt Dollenstein
43a27b1222
cI: remove rust cache (#1074) 2023-12-20 22:23:21 +00:00
Zsolt Dollenstein
ce5903f4cb
ci: update rust toolchain GHA (#1072)
`actions-rs` is unmaintained
2023-12-20 16:58:39 +00:00
David Tolnay
d97fb9be80
Update syn to v2 (#1064) 2023-12-20 15:53:54 +00:00
David Tolnay
52bbff6dfc
Set repository metadata entry for Rust crates (#1063) 2023-12-12 22:40:40 +00:00
dependabot[bot]
f8a9b80d9e
Bump rustix from 0.38.9 to 0.38.19 in /native (#1043)
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.38.9 to 0.38.19.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v0.38.9...v0.38.19)

---
updated-dependencies:
- dependency-name: rustix
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-18 15:07:17 -07:00
dependabot[bot]
9dd3ea7ec7
Bump pypa/cibuildwheel from 2.16.1 to 2.16.2 (#1041)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.16.1 to 2.16.2.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.16.1...v2.16.2)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-09 11:14:31 +01:00
Zsolt Dollenstein
693c6dc947
upgrade flake8 (#1040) 2023-10-06 10:01:06 -07:00
Itamar Oren
88d0b36cdd
Update pyproject.toml for Python 3.12 support (#1038)
* Update pyproject.toml for Python 3.12 support

add 3.12 classifier and update description to correctly reflect supported Python versions

* Update pyproject.toml

make the stated parsable versions range consistent with the README
2023-10-05 20:36:12 -07:00
Kyle Into
83f0daed42
fix filepathprovider generic type (#1036)
* fix filepathprovider type

* remove extra import
2023-10-05 20:18:39 -07:00
Itamar Oren
19c2862ea3
Update README.rst (#1039)
update Python parseable versions range to include 3.12
2023-10-05 20:18:05 -07:00
Zsolt Dollenstein
8d4229d959
bump version to 1.1.0 (#1037) 2023-10-05 19:16:12 -07:00
Sergii Dymchenko
7ca5d7f173
Fix link in type_inference_provider.py (#1035)
Same change as https://github.com/Instagram/LibCST/pull/913, but in the docstring.
2023-10-05 15:46:25 -07:00
Zsolt Dollenstein
5df1569a40
Parse multiline expressions in f-strings (#1027) 2023-10-02 10:33:29 -07:00
Zsolt Dollenstein
738dc2f893
Upgrade pyre (#1032)
* Upgrade pyre

* regen fixtures
2023-10-02 09:43:17 -07:00
Zsolt Dollenstein
face393db0
eliminate relative paths from Cargo.toml (#1031)
* eliminate relative paths from Cargo.toml

* fix paths in LICENSE files
2023-10-02 08:05:33 -07:00
dependabot[bot]
74e8a0e7c0
Bump pypa/cibuildwheel from 2.16.0 to 2.16.1 (#1029)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.16.0 to 2.16.1.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.16.0...v2.16.1)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-02 08:03:49 -07:00
Zsolt Dollenstein
03179b55eb
Parse arbitrarily nested f-strings (#1026) 2023-10-01 20:58:40 +01:00
André C. Silva
552af63d29
ScopeProvider: Record Access for Attributes and Decorators (#1019)
* Support for Attributes and Decorators in _NameUtil

* Replaced _NameUtil with get_full_name_for_node

* Added tests
2023-10-01 18:34:42 +01:00
dependabot[bot]
e1da64b53e
Bump ufmt from 2.1.0 to 2.2.0 (#1005)
Bumps [ufmt](https://github.com/omnilib/ufmt) from 2.1.0 to 2.2.0.
- [Changelog](https://github.com/omnilib/ufmt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/omnilib/ufmt/compare/v2.1.0...v2.2.0)

---
updated-dependencies:
- dependency-name: ufmt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-01 14:40:48 +01:00
André C. Silva
f81cc8d00e
AddImportsVisitor: add imports before the first non-import statement (#1024)
* AddImportsVisitor will now only add at the top of module

- Also added new tests to cover these cases

* Fixed an issue with from imports

* Added a couple tests for AddImportsVisitor

* Refactoring of GatherImportsVisitor

* Refactors, typos and typing changes
2023-10-01 14:38:33 +01:00
Zsolt Dollenstein
46060119a4
Scope provider changes for type annotations (#1014) 2023-09-30 11:16:27 +01:00
dependabot[bot]
5346bbfbdd
Bump Swatinem/rust-cache from 2.6.2 to 2.7.0 (#1020)
Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.6.2 to 2.7.0.
- [Release notes](https://github.com/swatinem/rust-cache/releases)
- [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/swatinem/rust-cache/compare/v2.6.2...v2.7.0)

---
updated-dependencies:
- dependency-name: Swatinem/rust-cache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-30 10:20:56 +01:00
dependabot[bot]
a27c4c745c
Bump pypa/cibuildwheel from 2.15.0 to 2.16.0 (#1025)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.15.0 to 2.16.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.15.0...v2.16.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 09:16:01 +01:00
Zsolt Dollenstein
37277e5fe7 add upper bound to pyo3 dependency 2023-09-16 04:02:14 -07:00
Zsolt Dollenstein
9d869b6639
scope_provider: Simplify parent resolution (#1013)
This PR introduces `Scope._next_visible_parent` which deduplicates much of the logic between `_contains_in_self_or_parent`, `_find_assignment_target_parent`, and `_getitem_from_self_or_parent`.

This will be helpful when implementing scope resolution for the future `AnnotationScope`.

There should be no functionality change.
2023-09-16 03:59:29 -07:00
dependabot[bot]
b509cc8b08
Bump black from 23.7.0 to 23.9.1 (#1017)
Bumps [black](https://github.com/psf/black) from 23.7.0 to 23.9.1.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/23.7.0...23.9.1)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-16 03:37:50 -07:00
dependabot[bot]
f469bcc755
Bump actions/checkout from 3 to 4 (#1015)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-16 03:36:31 -07:00
Zsolt Dollenstein
94dd20e20e
parser: remove Regexes from whitespace parser (#1008)
removing Regexes from whitespace parser allows ditching of thread local storage + lazy initialization cost

This shows a modest 2% improvement in overall parse time (inflate is improved by 10%)
2023-09-09 17:03:01 +01:00
Zsolt Dollenstein
377a292d0d
Add crate metadata 2023-09-03 18:16:11 +01:00
Micha Reiser
9c263aa897
Support files with mixed newlines (#1007)
* Add test case with mixed newlines

* Split lines by any newline character and not just by default

* Add unit test, remove copied
2023-09-02 09:56:20 +01:00
Zsolt Dollenstein
9286446f88
PEP 695 - Type Parameter Syntax (#1004)
This PR adds support for parsing and representing Type Parameters and Type Aliases as specified by PEP 695. What's missing are the scope rules, to be implemented in a future PR.

Notable (user visible) changes:

- new `TypeAlias` CST node, which is a `SmallStatement`
- new CST nodes to represent TypeVarLikes: `TypeVar`, `TypeVarTuple`, `ParamSpec`
- new helper CST nodes:  `TypeParameters` to serve as a container for multiple TypeVarLikes, and `TypeParam` which is a single item in a `TypeParameters` (owning the separating comma)
- extended `FunctionDef` and `ClassDef` with an optional `type_parameters` field, as well as `whitespace_after_type_parameters` to own the extra whitespace between type parameters and the following token
  - these new fields are added after all others to avoid breaking callers passing in fields as positional arguments
- in `FunctionDef` and `ClassDef`, `whitespace_after_name` now owns the whitespace before the type parameters if they exist
2023-08-28 22:07:22 +01:00
Zsolt Dollenstein
7c09b5d046
Remove need for regex in TextPosition::matches (#1002) 2023-08-27 16:29:20 +01:00
Zsolt Dollenstein
3bb5ba5a86
ci: test with 3.12 (#1003) 2023-08-27 16:27:38 +01:00
Zsolt Dollenstein
2064e200af
Fix readme's Python version requirement 2023-08-27 09:31:27 +01:00
Tom Forbes
75b6331d55
Switch to using thread_local regular expressions to avoid regex mutex contention (#996) 2023-08-26 15:21:05 +01:00
Tom Forbes
b28777e9e5
Remove criterion-cycles-per-byte dependency and related benchmark measurement (#995) 2023-08-26 13:34:27 +01:00
dependabot[bot]
c2d176162f
Bump Swatinem/rust-cache from 2.4.0 to 2.6.2 (#990)
Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.4.0 to 2.6.2.
- [Release notes](https://github.com/swatinem/rust-cache/releases)
- [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/swatinem/rust-cache/compare/v2.4.0...v2.6.2)

---
updated-dependencies:
- dependency-name: Swatinem/rust-cache
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-26 13:29:33 +01:00
dependabot[bot]
e9bad94d58
Bump fixit from 0.1.1 to 2.0.0.post1 (#979)
Bumps [fixit](https://github.com/Instagram/Fixit) from 0.1.1 to 2.0.0.post1.
- [Changelog](https://github.com/Instagram/Fixit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Instagram/Fixit/compare/v0.1.1...v2.0.0.post1)

---
updated-dependencies:
- dependency-name: fixit
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-26 13:07:39 +01:00
dependabot[bot]
6d11068723
Bump black from 23.3.0 to 23.7.0 (#973)
Bumps [black](https://github.com/psf/black) from 23.3.0 to 23.7.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/23.3.0...23.7.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-26 13:07:12 +01:00
Zsolt Dollenstein
125f9c321b
ci: fix rust-cache config (#999) 2023-08-26 13:06:27 +01:00
Zsolt Dollenstein
cbfd9c30a3
drop support for Python 3.7 (#997) 2023-08-26 12:42:12 +01:00
Zsolt Dollenstein
43e21c8d71
ci: enable testing on windows + 3.11 (#998) 2023-08-26 12:23:13 +01:00
dependabot[bot]
b8a644bc58
Bump pypa/cibuildwheel from 2.13.0 to 2.15.0 (#987)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.13.0 to 2.15.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.13.0...v2.15.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-26 11:56:02 +01:00
Sergii Dymchenko
0f7766f451
Don't gather dirs ending .py (#994) 2023-08-26 10:54:32 +01:00
Zsolt Dollenstein
9eab2f037f
Don't insert duplicate imports (#981) 2023-07-25 16:13:48 +01:00
Zsolt Dollenstein
0fb9021218
Don't swallow trailing whitespace (#976) 2023-07-18 10:03:10 +01:00
Alessandro Pietro Bardelli
a3f5bf97d6
Allow pyo3 >=0.17 (#957) 2023-06-14 14:39:03 +01:00
Martin DeMello
50d48c1539
Do not annotate the same variable multiple times in ApplyTypeAnnotationsVisitor (#956) 2023-06-14 09:30:56 +01:00
Zsolt Dollenstein
3cacca1a10
Update changelog to 1.0.1 2023-06-07 13:26:02 +01:00
Sergii Dymchenko
203a2f5bc5
Codemod CLI: Print diff only when there is a change (#945)
Otherwise lots of empty lines are printed.
2023-06-07 12:45:53 +01:00
Zsolt Dollenstein
5eec991ef3
Fix parsing of code without trailing newlines (#940)
When the input doesn't have a trailing newline, but the last line had
exactly the amount of bytes as the current indentation level, the
tokenizer didn't emit a fake newline, causing parse errors (the grammar
expects newlines to conform with the Python spec).

I don't see any reason for fake newlines to be omitted in these cases,
so this PR removes that condition from the tokenizer.

Reported in #930.
2023-06-07 12:40:34 +01:00
Zsolt Dollenstein
2acc293347
Fix whitespace, fstring, walrus related parse errors (#939, #938, #937, #936, #935, #934, #933, #932, #931)
* Allow walrus in slices

See https://github.com/python/cpython/pull/23317

Raised in #930.

* Fix parsing of nested f-string specifiers

For an expression like `f"{one:{two:}{three}}"`, `three` is not in an f-string spec, and should be tokenized accordingly.

This PR fixes the `format_spec_count` bookkeeping in the tokenizer, so it properly decrements it when a closing `}` is encountered but only if the `}` closes a format_spec.

Reported in #930.

* Fix tokenizing `0else`

This is an obscure one.

`_ if 0else _` failed to parse with some very weird errors. It turns out that the tokenizer tries to parse `0else` as a single number, but when it encounters `l` it realizes it can't be a single number and it backtracks.

Unfortunately the backtracking logic was broken, and it failed to correctly backtrack one of the offsets used for whitespace parsing (the byte offset since the start of the line). This caused whitespace nodes to refer to incorrect parts of the input text, eventually resulting in the above behavior.

This PR fixes the bookkeeping when the tokenizer backtracks.

Reported in #930.

* Allow no whitespace between lambda keyword and params in certain cases

Python accepts code where `lambda` follows a `*`, so this PR relaxes validation rules for Lambdas.

Raised in #930.

* Allow any expression in comprehensions' evaluated expression


This PR relaxes the accepted types for the `elt` field in `ListComp`, `SetComp`, and `GenExp`, as well as the `key` and `value` fields in `DictComp`.

Fixes #500.

* Allow no space around an ifexp in certain cases

For example in `_ if _ else""if _ else _`.

Raised in #930. Also fixes #854.

* Allow no spaces after `as` in a contextmanager in certain cases

Like in `with foo()as():pass`

Raised in #930.

* Allow no spaces around walrus in certain cases

Like in `[_:=''for _ in _]`

Raised in #930.

* Allow no whitespace after lambda body in certain cases

Like in `[lambda:()for _ in _]`

Reported in #930.
2023-06-07 12:37:16 +01:00
dependabot[bot]
648e1616be
Bump usort from 1.0.6 to 1.0.7 (#946)
Bumps [usort](https://github.com/facebook/usort) from 1.0.6 to 1.0.7.
- [Changelog](https://github.com/facebook/usort/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/usort/compare/v1.0.6...v1.0.7)

---
updated-dependencies:
- dependency-name: usort
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-07 12:23:25 +01:00
Sergii Dymchenko
062bcdb07e
Fix Sentinal typo (#948) 2023-06-07 12:23:12 +01:00
John Litborn
0f78b810a4
remove quotes around charset in .editorconfig (#949) 2023-06-07 12:22:54 +01:00
dependabot[bot]
de57f7cc63
Bump pypa/cibuildwheel from 2.12.3 to 2.13.0 (#942)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.12.3 to 2.13.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.12.3...v2.13.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-01 07:25:40 +02:00
dependabot[bot]
59aeceb17e
Bump black from 23.1.0 to 23.3.0 (#918)
Bumps [black](https://github.com/psf/black) from 23.1.0 to 23.3.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/23.1.0...23.3.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-26 13:44:46 +01:00
dependabot[bot]
ee80bf20e9
Update maturin requirement from <0.14,>=0.8.3 to >=0.8.3,<0.16 (#920)
Updates the requirements on [maturin](https://github.com/pyo3/maturin) to permit the latest version.
- [Release notes](https://github.com/pyo3/maturin/releases)
- [Changelog](https://github.com/PyO3/maturin/blob/main/Changelog.md)
- [Commits](https://github.com/pyo3/maturin/compare/v0.8.3...v0.15.1)

---
updated-dependencies:
- dependency-name: maturin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-26 13:44:11 +01:00
Sigurd Ljødal
a594fe1dd2
Fix type of evaluated_value on string to allow bytes (#721)
* Fix type of evaluated_value on string

This can return bytes if the string is a bytestring, e.g.:

    In [1]: import libcst as cst

    In [2]: cst.parse_expression('b"foo"').evaluated_value
    Out[2]: b'foo'

* Fix type errors from changed signature
2023-05-26 13:43:05 +01:00
Zsolt Dollenstein
193fab4357
Switch default parser implementation to native (#929)
The old parser is now only available using LIBCST_PARSER_TYPE=pure
2023-05-25 18:24:59 +01:00
dependabot[bot]
8216b8add2
Bump Swatinem/rust-cache from 1.3.0 to 2.4.0 (#925)
Bumps [Swatinem/rust-cache](https://github.com/Swatinem/rust-cache) from 1.3.0 to 2.4.0.
- [Release notes](https://github.com/Swatinem/rust-cache/releases)
- [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Swatinem/rust-cache/compare/v1.3.0...v2.4.0)

---
updated-dependencies:
- dependency-name: Swatinem/rust-cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-24 21:13:56 +01:00
Zsolt Dollenstein
bd96010782
exclude native/target directory from sdist (#928) 2023-05-24 20:36:31 +01:00
Zsolt Dollenstein
f6d87cd968 update changelog 2023-05-23 15:19:45 +01:00
Zsolt Dollenstein
858dd3d9a9 CI: add build dev dependency
This should fix the release pipeline which calls `python -m build`
2023-05-23 14:37:44 +01:00
Zsolt Dollenstein
654b14f39c
CI: bump macos version (#927)
* CI: bump macos version

* use macos-latest
2023-05-23 12:32:16 +01:00
John Litborn
38b708b5ed
relax validation rules on decorators (#926)
* relax validation on decorators

* allow any expression

---------

Co-authored-by: Zsolt Dollenstein <zsol.zsol@gmail.com>
2023-05-23 09:56:49 +01:00
John Litborn
ea19578293
Fix crash on escaped backslashes in rf-string (#921) 2023-05-17 15:49:40 +01:00
dependabot[bot]
889ce56b0f
Bump ufmt from 2.0.1 to 2.1.0 (#904)
Bumps [ufmt](https://github.com/omnilib/ufmt) from 2.0.1 to 2.1.0.
- [Release notes](https://github.com/omnilib/ufmt/releases)
- [Changelog](https://github.com/omnilib/ufmt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/omnilib/ufmt/compare/v2.0.1...v2.1.0)

---
updated-dependencies:
- dependency-name: ufmt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-25 10:00:48 +01:00
dependabot[bot]
fbfb83d3c6
Bump usort from 1.0.5 to 1.0.6 (#905)
Bumps [usort](https://github.com/facebook/usort) from 1.0.5 to 1.0.6.
- [Release notes](https://github.com/facebook/usort/releases)
- [Changelog](https://github.com/facebook/usort/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/usort/compare/v1.0.5...v1.0.6)

---
updated-dependencies:
- dependency-name: usort
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-25 09:46:32 +01:00
dependabot[bot]
1889bca0e6
Bump pypa/cibuildwheel from 2.12.1 to 2.12.3 (#915)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.12.1 to 2.12.3.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.12.1...v2.12.3)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-25 09:46:17 +01:00
Marcelo Trylesinski
f1b973f6b3
Fix pyre setup link in metadata.rst (#913) 2023-04-25 09:45:51 +01:00
Shantanu
2055342fd6
Support PEP 604 in ApplyTypeAnnotationsVisitor (#868) 2023-04-21 13:38:19 +01:00
Steven Troxler
f0a4d62c3b
Fix spelling and grammar in some comments (#908)
I'm going to go ahead and land this, I don't think it needs review
2023-04-13 14:42:57 -07:00
Rebecca Chen
f936db240f
Fix ApplyTypeAnnotationsVisitor behavior on attribute assignments. (#903)
* Fixes an issue where ApplyTypeAnnotationsVisitor would crash on code
  like `SomeClass.some_attribute = 42` with a "Name is not a valid
  identifier" error message.
* Changes the above-mentioned error message to include the bad name in
  the message, for easier debugging.
* Adds tests for all valid assignment targets, as described here:
  https://libcst.readthedocs.io/en/latest/nodes.html#libcst.BaseAssignTargetExpression.
2023-04-05 14:23:53 -07:00
Aarni Koskela
4f810dbc13
Allow running codemods without configuring in YAML (#879)
* Simplify command specifier parsing

* Allow running codemods without configuring in YAML

This enables codemodding things by just plonking a CodemodCommand class
into any old importable module and running
`python -m libcst.tool codemod -x some_module.SomeClass ...`
2023-03-27 10:59:48 +01:00
Aarni Koskela
ae42deed9b
Ensure current Python interpreter is used for subprocesses (#898) 2023-03-27 10:56:25 +01:00
dependabot[bot]
c016df46cd
Bump bumpalo from 3.10.0 to 3.12.0 in /native (#856)
Bumps [bumpalo](https://github.com/fitzgen/bumpalo) from 3.10.0 to 3.12.0.
- [Release notes](https://github.com/fitzgen/bumpalo/releases)
- [Changelog](https://github.com/fitzgen/bumpalo/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fitzgen/bumpalo/compare/3.10.0...3.12.0)

---
updated-dependencies:
- dependency-name: bumpalo
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-27 10:55:42 +01:00
Aarni Koskela
9381fee9ab
Use subprocess.DEVNULL instead of opening os.devnull by hand (#897) 2023-03-24 10:03:11 +00:00
dependabot[bot]
46509dd5e1
Bump black from 22.12.0 to 23.1.0 (#860) 2023-03-15 11:53:50 +00:00
Amethyst Reese
6a7b82e2b6 PEP 621 + hatch to run tests/lint/etc
Moves PEP 621 metadata from `setup.py` and `requirements*.txt` into the
`[project]` table of `pyproject.toml`. This enables using hatch as a
task runner for the project, where previously one would need to remember
a bunch of different commands, or repeatedly consult the readme's
developer guide to find all of the relevant commands.

This creates the following hatch commands:

- docs
- fixtures
- format
- lint
- test
- typecheck

It also updates all of the github actions workflows to use the
appropriate hatch commands, and the readme's developer guide, so that
there is only one source of truth for what constitutes running tests.

The "test" workflows now drop the matrix distinction between "pure" or
"native", and run tests in both modes from a single build.

ghstack-source-id: 8834da7825
Pull Request resolved: https://github.com/Instagram/LibCST/pull/893
2023-03-14 19:37:41 -07:00
Mikhail Podtserkovskiy
497f7784c5
Fix: relative imports from '' package are not allowed (#894) 2023-03-14 15:07:40 -07:00
Amethyst Reese
c876db6d2d Add new FilePathProvider
Caches file path information on the root `Module` node.
Resolves paths when caching, so they are always absolute paths.

Adds a new `chdir` helper to change working directory and automatically
revert to previous directory, which makes testing file paths with the
`"."` repo root easier.

ghstack-source-id: 3413905fc1
Pull Request resolved: https://github.com/Instagram/LibCST/pull/892
2023-03-13 18:33:46 -07:00
dependabot[bot]
71183c65d7
Bump pypa/cibuildwheel from 2.12.0 to 2.12.1 (#891)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.12.0 to 2.12.1.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.12.0...v2.12.1)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-13 08:27:21 +00:00
Zsolt Dollenstein
577e5d5cd4
[ci] Fix pypi_upload workflow (#889)
pypi_upload has been broken since #810, because `actions/checkout` defaults to a shallow checkout that only checks out the revision triggering the workflow. This causes setuptools-scm to miss the most recent tag, causing the version to be detected as `0.1`.
2023-03-09 09:15:36 +00:00
Amethyst Reese
5ccba6b0d3
Use new setup-python caching actions (#874)
With the latest setup-python actions, there is a better caching
mechanism available that also requires less setup, and provides better
fallback behavior that should help avoid the random CI failures that
have been happening on 3.11 for setuptools-rust. This ensures that we
install the necessary dependencies before attempting to build the
package or run tests, while still enabling speedups in best case
scenario when requirements files haven't changed.

See the upstream readme for details:
https://github.com/actions/setup-python#caching-packages-dependencies
2023-03-08 11:14:43 +00:00
Amethyst Reese
f9536b522f
Pass root path to FullyQualifiedNameProvider (#867)
This allows FullyQualifiedNameProvider to work with absolute paths,
rather than assuming all paths given will be relative to the current
directory. This enables tools like Fixit to provide a root path, and
have the FullyQualifiedNameProvider correctly scope the final results
relative to that root path.

This does require that both the root path and the given file paths
match the other as relative or absolute, due to the
`calculate_module_and_package` helper comparing file paths relative
to the root path, but this seems like a reasonable tradeoff, and
unlikely to cause a problem in normal use cases.
2023-02-22 14:36:10 -08:00
Amethyst Reese
d94687e378
Script to regenerate test fixtures (#872)
Upgrading Pyre requires updating test fixtures with any upstream changes
to Pyre's query results for the `simple_class.py` fixture.

This adds a new `scripts/` directory to the repo, with a script to
regenerate test fixtures. The script regenerates the cache data fixture,
and updates the `TypeInferenceProvider` tests to use `assertDictEqual`
and helpful error messages for better behavior in future mismatches.

This also includes a slight bump to Pyre 0.9.10 to fix install issues on
Apple Silicon M1 Macs, and regenerated fixtures using the script above.
2023-02-22 12:35:23 -08:00
Amethyst Reese
944ff159f6
Add setuptools-rust to build requirements in setup.py (#873)
Hoping this resolves the CI failures on 3.11
2023-02-21 18:47:09 -08:00
Amethyst Reese
8aebbb6121
Ignore common virtualenv names (#863) 2023-02-17 14:16:03 -08:00
Steven Troxler
b5c34d39a0
Fix Github issue 855 - fail to parse with statement (#861)
* Fix Github issue 855 - fail to parse with statement

When we added support for parenthesized with statements, the
grammar on the with itself was correct (it's a right and left
parenthesis around a comma-separated list of with-items, with
a possible trailing comma).

But inside of the "as" variation of the with_item rule we have a peek at
the next character, which was allowing for a comma or a colon. That peek
needs to also accept right parentheses - otherwise, if the last item
contains an `as` and has no trailing comma we fail to parse.

The bug is exercisecd by, for example, this code snippet:
```
with (foo, bar as bar,):
    pass
```

The with_wickedness test fixture has been revised to include both
the plain and async variations of this example snippet with and without
trailing comma, and tests pass after the peek rule fix.

* Add more tests covering the plain expression form of `with_item`
2023-02-16 10:49:05 -08:00
dependabot[bot]
1ee04c6ce5
Bump black from 22.10.0 to 22.12.0 (#832)
Bumps [black](https://github.com/psf/black) from 22.10.0 to 22.12.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/22.10.0...22.12.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 13:53:37 +00:00
Sagar Badiyani
de28541fa3
[AddImportsVisitor] Docstring Check Only for the Top Element of the Body (#841)
* Initial Commit

* lint fix
2023-01-24 13:51:56 +00:00
dependabot[bot]
bfd8e495ac
Bump pypa/cibuildwheel from 2.11.2 to 2.12.0 (#857)
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.11.2 to 2.12.0.
- [Release notes](https://github.com/pypa/cibuildwheel/releases)
- [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md)
- [Commits](https://github.com/pypa/cibuildwheel/compare/v2.11.2...v2.12.0)

---
updated-dependencies:
- dependency-name: pypa/cibuildwheel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 13:50:19 +00:00
Carl Meyer
f668e88dd2
fix PEP 604 union annotations in decorators (#828) 2022-11-29 15:24:24 -07:00
dependabot[bot]
987aff6664
Bump actions/cache from 2 to 3 (#820)
Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-14 10:40:06 +00:00
dependabot[bot]
667c0c3e14
Bump actions/setup-python from 2 to 4 (#819)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 4.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-14 10:39:48 +00:00
dependabot[bot]
a284947b8f
Bump actions/checkout from 1 to 3 (#810)
Bumps [actions/checkout](https://github.com/actions/checkout) from 1 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v1...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-11 09:04:21 +00:00
Andrey Semakin
ff01b86786
Add py3.11 classifier (#816) 2022-11-11 09:00:42 +00:00
dependabot[bot]
ede2616ff2
Bump actions/download-artifact from 2 to 3 (#815)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-10 21:51:18 +00:00
dependabot[bot]
b62ce9218f
Bump codecov/codecov-action from 2 to 3 (#812)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 2 to 3.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-10 21:50:54 +00:00
dependabot[bot]
fc6e0c6a64
Bump actions/upload-artifact from 2 to 3 (#811)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-10 10:27:36 +00:00
Zsolt Dollenstein
95e65a4022
Remove deprecated ::set-output directive (#808) 2022-11-10 10:13:22 +00:00
Zsolt Dollenstein
ceb4619da5
Bump setuptools-rust version (#809) 2022-11-10 10:12:55 +00:00
Zsolt Dollenstein
94e607070d
Auto-update github actions using dependabot 2022-11-10 10:12:14 +00:00
Matthew Shaer
c44b182e88
Adding a provider which can tell what accessor to use to go from the parent to that child node (#807) 2022-11-10 09:38:32 +00:00
dependabot[bot]
bd4f541f2c
Bump pyo3 from 0.17.2 to 0.17.3 in /native (#805)
Bumps [pyo3](https://github.com/pyo3/pyo3) from 0.17.2 to 0.17.3.
- [Release notes](https://github.com/pyo3/pyo3/releases)
- [Changelog](https://github.com/PyO3/pyo3/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pyo3/pyo3/compare/v0.17.2...v0.17.3)

---
updated-dependencies:
- dependency-name: pyo3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-08 10:46:35 +00:00
dependabot[bot]
c105fd33ba
Bump regex from 1.6.0 to 1.7.0 in /native (#806)
Bumps [regex](https://github.com/rust-lang/regex) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.6.0...1.7.0)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-08 10:46:10 +00:00
Vincent Fazio
1e88f1ed42
Python 3.11 wheels (#801)
* [ci] narrow python 3.11 version window

Also, quote the versions for consistency.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>

* [ci] bump cibuildwheel to 2.11.2

Newer versions support building 3.11 wheels automatically, so just take
the latest currently available.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
2022-11-02 16:25:47 +00:00
dependabot[bot]
0ef632811a
Bump once_cell from 1.15.0 to 1.16.0 in /native (#802)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.15.0...v1.16.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-02 16:25:27 +00:00
dependabot[bot]
c606585672
Bump syn from 1.0.102 to 1.0.103 in /native (#799)
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.102 to 1.0.103.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.102...1.0.103)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-02 15:38:13 +00:00
dependabot[bot]
a7733f6c59
Bump peg from 0.8.0 to 0.8.1 in /native (#783)
Bumps [peg](https://github.com/kevinmehall/rust-peg) from 0.8.0 to 0.8.1.
- [Release notes](https://github.com/kevinmehall/rust-peg/releases)
- [Commits](https://github.com/kevinmehall/rust-peg/compare/0.8.0...0.8.1)

---
updated-dependencies:
- dependency-name: peg
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-10 08:41:50 +01:00
dependabot[bot]
29a3ddfb4d
Bump syn from 1.0.101 to 1.0.102 in /native (#793)
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.101 to 1.0.102.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.101...1.0.102)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-10 08:41:34 +01:00
dependabot[bot]
810edaece9
Bump pyo3 from 0.17.1 to 0.17.2 in /native (#794)
Bumps [pyo3](https://github.com/pyo3/pyo3) from 0.17.1 to 0.17.2.
- [Release notes](https://github.com/pyo3/pyo3/releases)
- [Changelog](https://github.com/PyO3/pyo3/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pyo3/pyo3/compare/v0.17.1...v0.17.2)

---
updated-dependencies:
- dependency-name: pyo3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-10 08:41:17 +01:00
dependabot[bot]
ce33ed31e8
Bump trybuild from 1.0.65 to 1.0.71 in /native (#795)
Bumps [trybuild](https://github.com/dtolnay/trybuild) from 1.0.65 to 1.0.71.
- [Release notes](https://github.com/dtolnay/trybuild/releases)
- [Commits](https://github.com/dtolnay/trybuild/compare/1.0.65...1.0.71)

---
updated-dependencies:
- dependency-name: trybuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-10 08:40:53 +01:00
dependabot[bot]
910d7923d3
Bump black from 22.8.0 to 22.10.0 (#796)
Bumps [black](https://github.com/psf/black) from 22.8.0 to 22.10.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/22.8.0...22.10.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-10 08:40:39 +01:00
dependabot[bot]
cd0988d4e7
Bump once_cell from 1.14.0 to 1.15.0 in /native (#789)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.14.0 to 1.15.0.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.14.0...v1.15.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-07 08:38:50 +01:00
dependabot[bot]
b61013d5a9
Bump thiserror from 1.0.34 to 1.0.37 in /native (#790)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.34 to 1.0.37.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.34...1.0.37)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-07 08:38:40 +01:00
dependabot[bot]
acec81f238
Bump usort from 1.0.4 to 1.0.5 (#781)
Bumps [usort](https://github.com/facebook/usort) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/facebook/usort/releases)
- [Changelog](https://github.com/facebook/usort/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/usort/compare/v1.0.4...v1.0.5)

---
updated-dependencies:
- dependency-name: usort
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-03 21:06:48 +01:00
dependabot[bot]
2a88673128
Bump criterion from 0.3.6 to 0.4.0 in /native (#774)
Bumps [criterion](https://github.com/bheisler/criterion.rs) from 0.3.6 to 0.4.0.
- [Release notes](https://github.com/bheisler/criterion.rs/releases)
- [Changelog](https://github.com/bheisler/criterion.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bheisler/criterion.rs/compare/0.3.6...0.4.0)

---
updated-dependencies:
- dependency-name: criterion
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-03 21:06:40 +01:00
dependabot[bot]
281b2f206f
Bump ufmt from 2.0.0 to 2.0.1 (#780)
Bumps [ufmt](https://github.com/omnilib/ufmt) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/omnilib/ufmt/releases)
- [Changelog](https://github.com/omnilib/ufmt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/omnilib/ufmt/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: ufmt
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-28 16:47:02 +01:00
dependabot[bot]
d7e3213281
Bump syn from 1.0.99 to 1.0.101 in /native (#786)
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.99 to 1.0.101.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.99...1.0.101)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-28 16:46:33 +01:00
dependabot[bot]
014605f269
Bump trybuild from 1.0.64 to 1.0.65 in /native (#787)
Bumps [trybuild](https://github.com/dtolnay/trybuild) from 1.0.64 to 1.0.65.
- [Release notes](https://github.com/dtolnay/trybuild/releases)
- [Commits](https://github.com/dtolnay/trybuild/compare/1.0.64...1.0.65)

---
updated-dependencies:
- dependency-name: trybuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-28 16:46:21 +01:00
dependabot[bot]
e30922bf09
Bump itertools from 0.10.3 to 0.10.5 in /native (#785)
Bumps [itertools](https://github.com/rust-itertools/itertools) from 0.10.3 to 0.10.5.
- [Release notes](https://github.com/rust-itertools/itertools/releases)
- [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-itertools/itertools/commits)

---
updated-dependencies:
- dependency-name: itertools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-27 08:46:47 +01:00
MapleCCC
c75dbd482c
Fix black configuration (#769)
* Raise black's output file's target version to 3.7, which is the lowest supported Python version that libcst can be run on

* Add to, instead of override, the exclusion rules of black

* Fix the bug that files in `stubs/libcst_native/` are inadvertently ignored by black

This is due to black's file exclusion mechanism is a file-system-unaware pure-string-based pattern match. We need to prepend "^/" to specify that we are referring to the root-level "native/" folder. Yeah, I know this looks strange, but blame black for it :) . See https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#configuration-format for further reference.

* It's conventional to use single-quote literal string to represent regular expression in TOML format, because in this way it doesn't perform any escaping

* When codemod, specify the black formatter to use the same target Python version we use

* Fix the `test_codemod_formatter_error_input` unit test

* Remove an unused import in `test_codemod_cli` module
2022-09-14 15:22:45 +01:00
MapleCCC
973895a6c0
Several trivial refactors (#770)
* Enumeration members are singletons. Copying on them would be no-op

* Avoid generating unnecessary `pass` statement

* Several trivial refactor

* Avoid building unnecessary intermediate lists, which are mere slight waste of time and space

* Remove unused import, an overlook from commit 8e6bf9e9

* `collections.abc.Mapping.get()` defaults to return `None` when key doesn't exist

* Just use unittest's `assertRaises` to specify expected exception types, instead of catching every possible `Exception`s, which could suppress legitimate errors and hide bugs

* We know for sure that the body of `CSTTypedTransformerFunctions` won't be empty, so don't bother with complex formal completeness
2022-09-14 14:33:45 +01:00
MapleCCC
667c713b38
Fix the bug that the use of formatter in codemods has undetermined target Python version, resulting in hard-to-reason-with behavior (#771)
* When codemod, specify the black formatter to use the same target Python version we use

* Fix the `test_codemod_formatter_error_input` unit test

* Remove an unused import in `test_codemod_cli` module
2022-09-14 14:31:36 +01:00
dependabot[bot]
986575d185
Bump once_cell from 1.13.1 to 1.14.0 in /native (#765)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.13.1 to 1.14.0.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.13.1...v1.14.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 11:38:51 +01:00
dependabot[bot]
c488ccb9df
Bump thiserror from 1.0.32 to 1.0.34 in /native (#764)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.32 to 1.0.34.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.32...1.0.34)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 11:37:55 +01:00
dependabot[bot]
fe706cada0
Bump paste from 1.0.8 to 1.0.9 in /native (#766)
Bumps [paste](https://github.com/dtolnay/paste) from 1.0.8 to 1.0.9.
- [Release notes](https://github.com/dtolnay/paste/releases)
- [Commits](https://github.com/dtolnay/paste/compare/1.0.8...1.0.9)

---
updated-dependencies:
- dependency-name: paste
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 11:04:36 +01:00
dependabot[bot]
901e97749e
Bump black from 22.6.0 to 22.8.0 (#767)
Bumps [black](https://github.com/psf/black) from 22.6.0 to 22.8.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/22.6.0...22.8.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 11:04:12 +01:00
MapleCCC
f92cbb7976
Fix doc build status badge (#763) 2022-09-04 20:36:55 +01:00
MapleCCC
27aa23f056
Raise informative exception when metadata is unresolved in a metadata-based match (#757)
* Raise informative exception when metadata is unresolved in a metadata-based match, instead of silently hide potential errors

* Fix unit test of `findall`

* Add unit test to cover the case of a resolved metadata provider doesn't provide metadata for all nodes

* Document the behavior of metadata-based match when the metadata provider is unresolved
2022-08-29 16:47:02 +01:00
MapleCCC
64811b7795
Tighten the metadata type of ExpressionContextProvider (#760) 2022-08-29 16:46:08 +01:00
dependabot[bot]
ea2490606a
Bump pyo3 from 0.16.5 to 0.17.1 in /native (#759)
Bumps [pyo3](https://github.com/pyo3/pyo3) from 0.16.5 to 0.17.1.
- [Release notes](https://github.com/pyo3/pyo3/releases)
- [Changelog](https://github.com/PyO3/pyo3/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pyo3/pyo3/compare/v0.16.5...v0.17.1)

---
updated-dependencies:
- dependency-name: pyo3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-29 16:44:47 +01:00
dependabot[bot]
a077104f39
Bump once_cell from 1.13.0 to 1.13.1 in /native (#754)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.13.0 to 1.13.1.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.13.0...v1.13.1)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 18:55:30 +01:00
MapleCCC
fc622ce790
Fix bug when TypeOf is one of options in OneOf / AllOf (#756)
* Fix a bug when one of the option of `OneOf` is a `TypeOf`

* Disallow `TypeOf` in `AllOf`, analogous to how `OneOf` is disallowed in `AllOf`
2022-08-26 18:54:07 +01:00
dependabot[bot]
5fc69d6e4e
Bump jinja2 from 3.0.3 to 3.1.2 (#744)
Bumps [jinja2](https://github.com/pallets/jinja) from 3.0.3 to 3.1.2.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.0.3...3.1.2)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-17 18:26:31 +01:00
dependabot[bot]
fa8ee152fb
Bump ufmt from 1.3.3 to 2.0.0 (#745)
Bumps [ufmt](https://github.com/omnilib/ufmt) from 1.3.3 to 2.0.0.
- [Release notes](https://github.com/omnilib/ufmt/releases)
- [Changelog](https://github.com/omnilib/ufmt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/omnilib/ufmt/compare/v1.3.3...v2.0.0)

---
updated-dependencies:
- dependency-name: ufmt
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-17 18:26:10 +01:00
dependabot[bot]
bfd09823ae
Bump paste from 1.0.7 to 1.0.8 in /native (#743)
Bumps [paste](https://github.com/dtolnay/paste) from 1.0.7 to 1.0.8.
- [Release notes](https://github.com/dtolnay/paste/releases)
- [Commits](https://github.com/dtolnay/paste/compare/1.0.7...1.0.8)

---
updated-dependencies:
- dependency-name: paste
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-17 14:04:05 +01:00
dependabot[bot]
ef2d70e37e
Bump thiserror from 1.0.31 to 1.0.32 in /native (#742)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.31 to 1.0.32.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.31...1.0.32)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-17 14:03:51 +01:00
dependabot[bot]
7f8e755fbe
Bump quote from 1.0.20 to 1.0.21 in /native (#741)
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.20 to 1.0.21.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.20...1.0.21)

---
updated-dependencies:
- dependency-name: quote
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-17 14:03:38 +01:00
dependabot[bot]
9f843cf4e1
Bump usort from 1.0.0rc1 to 1.0.4 (#746)
Bumps [usort](https://github.com/facebookexperimental/usort) from 1.0.0rc1 to 1.0.4.
- [Release notes](https://github.com/facebookexperimental/usort/releases)
- [Changelog](https://github.com/facebookexperimental/usort/blob/v1.0.4/CHANGELOG.md)
- [Commits](https://github.com/facebookexperimental/usort/compare/v1.0.0rc1...v1.0.4)

---
updated-dependencies:
- dependency-name: usort
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-17 14:03:12 +01:00
Zsolt Dollenstein
1f5f16aa77
Skip CI checks on Python 3.11 x Windows (#752)
There are no available binary wheels for lxml for Windows & Python 3.11 yet:
https://bugs.launchpad.net/lxml/+bug/1977998

Until that's resolved, let's skip tests in this configuration.
2022-08-17 14:00:48 +01:00
MapleCCC
79cf251896
Update Sphinx to 5.1.1 (#748) 2022-08-17 13:55:06 +01:00
MapleCCC
73cfc7f7fa
Fix docstring of FullRepoManager (#750)
The render error originates from how we violate the syntax rules of the `field list` markup element of reStructuredText. The `specification of field list states](https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#field-lists) that a multi-line `field body` must be indented relative to the `field marker`.
2022-08-17 13:42:31 +01:00
MapleCCC
2bd6a64780
Fix graph not appearing on readthedocs (#751)
Fix problematic doc build, due to the new builder image provided by readthedocs doesn't has the `graphviz-dev` package pre-installed any more
2022-08-17 13:32:00 +01:00
dependabot[bot]
977504f104
Update maturin requirement from <0.9,>=0.8.3 to >=0.8.3,<0.14 (#737)
Updates the requirements on [maturin](https://github.com/pyo3/maturin) to permit the latest version.
- [Release notes](https://github.com/pyo3/maturin/releases)
- [Changelog](https://github.com/PyO3/maturin/blob/main/Changelog.md)
- [Commits](https://github.com/pyo3/maturin/compare/v0.8.3...v0.13.1)

---
updated-dependencies:
- dependency-name: maturin
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-08 14:31:07 +01:00
dependabot[bot]
21550e6e04
Bump once_cell from 1.12.0 to 1.13.0 in /native (#732)
Bumps [once_cell](https://github.com/matklad/once_cell) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/matklad/once_cell/releases)
- [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md)
- [Commits](https://github.com/matklad/once_cell/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: once_cell
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-08 14:27:23 +01:00
dependabot[bot]
bcc169f60c
Bump syn from 1.0.98 to 1.0.99 in /native (#733)
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.98 to 1.0.99.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.98...1.0.99)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-08 14:27:12 +01:00
dependabot[bot]
09895298d5
Bump ufmt from 1.3 to 1.3.3 (#734)
Bumps [ufmt](https://github.com/omnilib/ufmt) from 1.3 to 1.3.3.
- [Release notes](https://github.com/omnilib/ufmt/releases)
- [Changelog](https://github.com/omnilib/ufmt/blob/main/CHANGELOG.md)
- [Commits](https://github.com/omnilib/ufmt/compare/v1.3.0...v1.3.3)

---
updated-dependencies:
- dependency-name: ufmt
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-08 14:26:47 +01:00
dependabot[bot]
dbfd83d811
Bump regex from 1.5.6 to 1.6.0 in /native (#729)
Bumps [regex](https://github.com/rust-lang/regex) from 1.5.6 to 1.6.0.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.5.6...1.6.0)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-08 13:14:10 +01:00
dependabot[bot]
8c29b395c2
Bump trybuild from 1.0.63 to 1.0.64 in /native (#730)
Bumps [trybuild](https://github.com/dtolnay/trybuild) from 1.0.63 to 1.0.64.
- [Release notes](https://github.com/dtolnay/trybuild/releases)
- [Commits](https://github.com/dtolnay/trybuild/compare/1.0.63...1.0.64)

---
updated-dependencies:
- dependency-name: trybuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-08 13:13:59 +01:00
dependabot[bot]
9b55dba06e
Bump criterion from 0.3.5 to 0.3.6 in /native (#731)
Bumps [criterion](https://github.com/bheisler/criterion.rs) from 0.3.5 to 0.3.6.
- [Release notes](https://github.com/bheisler/criterion.rs/releases)
- [Changelog](https://github.com/bheisler/criterion.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bheisler/criterion.rs/compare/0.3.5...0.3.6)

---
updated-dependencies:
- dependency-name: criterion
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-08 13:13:46 +01:00
dependabot[bot]
7307a6918f
Bump black from 22.3.0 to 22.6.0 (#735)
Bumps [black](https://github.com/psf/black) from 22.3.0 to 22.6.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/22.3.0...22.6.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-08 13:11:17 +01:00
Zsolt Dollenstein
2e441cb50f
pin flake8 below 5.0 (#739) 2022-08-08 12:44:04 +01:00
Dhruv Manilawala
345c7ba89b
add dependabot config file (#728) 2022-08-08 11:36:39 +01:00
zzl
47e5ea15e1
Fix parse error message for number parsing (#724)
Co-authored-by: zzl0 <zhuzhaolong0@mail.com>
2022-08-04 11:33:26 +01:00
Zsolt Dollenstein
367b14b052
test using python 3.11 beta versions (#723) 2022-07-15 15:26:58 +01:00
Zsolt Dollenstein
c85f9bf19d
bump version to 0.4.7 2022-07-12 15:52:10 +01:00
Chenguang Zhu
7cb229d175
Implement lazy loading mechanism for QualifiedNameProvider (#720)
* Implement lazy loading mechanism for expensive metadata providers
* Add support for lazy values in metadata matchers
* Fix type issues and implement lazy value support in base metadata provider too
* Add unit tests for BaseMetadataProvider

Co-authored-by: Zsolt Dollenstein <zsol.zsol@gmail.com>
2022-07-09 08:34:29 +01:00
Luke Petre
b3eda508d4
Fixing prefix matching bug from 0.4.6 (#719) 2022-07-05 11:25:39 +01:00
Zsolt Dollenstein
7042623ace
bump version to 0.4.6 2022-07-04 14:49:43 +01:00
Zsolt Dollenstein
343f56f607
[parser] bail on deeply nested expressions (#718) 2022-07-04 14:45:42 +01:00
Zsolt Dollenstein
c894160d4a
bump rust dependencies (#714) 2022-06-26 09:50:40 +01:00
Zsolt Dollenstein
9925117391
Support whitespace after ParamSlash (#713)
* add whitespace_after field to ParamSlash
* codegen
2022-06-26 09:42:37 +01:00
Zsolt Dollenstein
5592f2e00f
Fix parsing of parenthesized empty tuples (#712)
* Don't drop rpars from empty tuples during inflate
2022-06-26 09:41:49 +01:00
Luke Petre
aa4a2790db
Remove unnecessary qname work (#709) 2022-06-21 21:35:05 +01:00
Luke Petre
779163701c
Faster qualified name format (#710) 2022-06-21 14:26:45 +01:00
Luke Petre
42164f8672
Cache the scope name prefix to prevent scope traversal in a tight loop (#708)
* Cache the scope name prefix to prevent scope traversal in a tight loop

* Adding pyre-fixme. this attribute iclearly has a type in the base class.

* Clarify why we do join(filter(None,...
2022-06-21 10:11:02 +01:00
Sergei Lebedev
306a5f8175
convert_type_comments now preserves comments following type comments (#702)
For example,

    y = 5  # type: int  # foo

is converted to

    y: int = 5  # foo
2022-06-20 07:39:12 -06:00
Luke Petre
ea8d3d55a5
Update changelog for 0.4.5 (#707) 2022-06-17 13:14:18 +01:00
zzl
7ca1bd1cd5
expression: fix SimpleString's quote method (#704)
* expression: fix SimpleString's quote method

* Add missing copyright header

Co-authored-by: zzl0 <zhuzhaolong0@mail.com>
Co-authored-by: Luke Petre <lpetre@fb.com>
2022-06-17 13:05:05 +01:00
Luke Petre
6f28c799bb
Fix slow perf in 0.4.2+ (#698) 2022-06-16 12:45:02 +01:00
zzl
84da283604
Fix code example in metadata documentation. (#703)
Co-authored-by: zzl0 <zhuzhaolong0@mail.com>
2022-06-16 09:49:24 +01:00
Zsolt Dollenstein
4c9728ab12
Tokenize escaped quotes in raw f-strings correctly (#701) 2022-06-16 09:47:57 +01:00
Zsolt Dollenstein
153c6d12c0
Only skip supported escaped characters in f-strings (#700) 2022-06-16 09:47:36 +01:00
Zsolt Dollenstein
ff5fcf8dfb
Update pypi_upload.yml
Remove prod repo url override
2022-06-13 19:29:56 +01:00
237 changed files with 14783 additions and 3360 deletions

View file

@ -1,7 +1,7 @@
root = true
[*.{py,pyi,rs,toml,md}]
charset = "utf-8"
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space

210
.flake8
View file

@ -1,69 +1,126 @@
[flake8]
ignore =
C407, # unnecessary list comprehension; A generator only better than a list
# comprehension if we don't always need to iterate through all items in
# the generator (based on the use case).
# unnecessary list comprehension; A generator only better than a list
# comprehension if we don't always need to iterate through all items in
# the generator (based on the use case).
C407,
# The following codes belong to pycodestyle, and overlap with black:
E101, # indentation contains mixed spaces and tabs
E111, # indentation is not a multiple of four
E112, # expected an indented block
E113, # unexpected indentation
E114, # indentation is not a multiple of four (comment)
E115, # expected an indented block (comment)
E116, # unexpected indentation (comment)
E121, # continuation line under-indented for hanging indent
E122, # continuation line missing indentation or outdented
E123, # closing bracket does not match indentation of opening brackets line
E124, # closing bracket does not match visual indentation
E125, # continuation line with same indent as next logical line
E126, # continuation line over-indented for hanging indent
E127, # continuation line over-indented for visual indent; is harmless
# (over-indent is visually unambiguous) and currently generates too
# many warnings for existing code.
E128, # continuation line under-indented for visual indent
E129, # visually indented line with same indent as next logical line
E131, # continuation line unaligned for hanging indent
E133, # closing bracket is missing indentation
E201, # whitespace after (
E202, # whitespace before )
E203, # whitespace before :; this warning is invalid for slices
E211, # whitespace before (
E221, # multiple spaces before operator
E222, # multiple spaces after operator
E223, # tab before operator
E224, # tab after operator
E225, # missing whitespace around operator
E226, # missing whitespace around arithmetic operator
E227, # missing whitespace around bitwise or shift operator
E228, # missing whitespace around modulo operator
E231, # missing whitespace after ,, ;, or :
E241, # multiple spaces after ,
E242, # tab after ,
E251, # unexpected spaces around keyword / parameter equals
E261, # at least two spaces before inline comment
E262, # inline comment should start with #
E265, # block comment should start with #
E266, # too many leading # for block comment
E271, # multiple spaces after keyword
E272, # multiple spaces before keyword
E273, # tab after keyword
E274, # tab before keyword
E275, # missing whitespace after keyword
E301, # expected 1 blank line, found 0
E302, # expected 2 blank lines, found 0
E303, # too many blank lines (3)
E304, # blank lines found after function decorator
E305, # expected 2 blank lines after end of function or class
E306, # expected 1 blank line before a nested definition
E401, # multiple imports on one line
E501, # line too long (> 79 characters)
E502, # the backslash is redundant between brackets
E701, # multiple statements on one line (colon)
E702, # multiple statements on one line (semicolon)
E703, # statement ends with a semicolon
E704, # multiple statements on one line (def)
# indentation contains mixed spaces and tabs
E101,
# indentation is not a multiple of four
E111,
# expected an indented block
E112,
# unexpected indentation
E113,
# indentation is not a multiple of four (comment)
E114,
# expected an indented block (comment)
E115,
# unexpected indentation (comment)
E116,
# continuation line under-indented for hanging indent
E121,
# continuation line missing indentation or outdented
E122,
# closing bracket does not match indentation of opening brackets line
E123,
# closing bracket does not match visual indentation
E124,
# continuation line with same indent as next logical line
E125,
# continuation line over-indented for hanging indent
E126,
# continuation line over-indented for visual indent; is harmless
# (over-indent is visually unambiguous) and currently generates too
# many warnings for existing code.
E127,
# continuation line under-indented for visual indent
E128,
# visually indented line with same indent as next logical line
E129,
# continuation line unaligned for hanging indent
E131,
# closing bracket is missing indentation
E133,
# whitespace after (
E201,
# whitespace before )
E202,
# whitespace before :; this warning is invalid for slices
E203,
# whitespace before (
E211,
# multiple spaces before operator
E221,
# multiple spaces after operator
E222,
# tab before operator
E223,
# tab after operator
E224,
# missing whitespace around operator
E225,
# missing whitespace around arithmetic operator
E226,
# missing whitespace around bitwise or shift operator
E227,
# missing whitespace around modulo operator
E228,
# missing whitespace after ,, ;, or :
E231,
# multiple spaces after ,
E241,
# tab after ,
E242,
# unexpected spaces around keyword / parameter equals
E251,
# at least two spaces before inline comment
E261,
# inline comment should start with #
E262,
# block comment should start with #
E265,
# too many leading # for block comment
E266,
# multiple spaces after keyword
E271,
# multiple spaces before keyword
E272,
# tab after keyword
E273,
# tab before keyword
E274,
# missing whitespace after keyword
E275,
# expected 1 blank line, found 0
E301,
# expected 2 blank lines, found 0
E302,
# too many blank lines (3)
E303,
# blank lines found after function decorator
E304,
# expected 2 blank lines after end of function or class
E305,
# expected 1 blank line before a nested definition
E306,
# multiple imports on one line
E401,
# line too long (> 79 characters)
E501,
# the backslash is redundant between brackets
E502,
# multiple statements on one line (colon)
E701,
# multiple statements on one line (semicolon)
E702,
# statement ends with a semicolon
E703,
# multiple statements on one line (def)
E704,
# These are pycodestyle lints that black doesn't catch:
# E711, # comparison to None should be if cond is None:
# E712, # comparison to True should be if cond is True: or if cond:
@ -78,16 +135,25 @@ ignore =
# I think these are internal to pycodestyle?
# E901, # SyntaxError or IndentationError
# E902, # IOError
F811, # isn't aware of type-only imports, results in false-positives
W191, # indentation contains tabs
W291, # trailing whitespace
W292, # no newline at end of file
W293, # blank line contains whitespace
W391, # blank line at end of file
W503, # line break before binary operator; binary operator in a new line is
# the standard
W504, # line break after binary operator
W505, # not part of PEP8; doc line too long (> 79 characters)
# isn't aware of type-only imports, results in false-positives
F811,
# indentation contains tabs
W191,
# trailing whitespace
W291,
# no newline at end of file
W292,
# blank line contains whitespace
W293,
# blank line at end of file
W391,
# line break before binary operator; binary operator in a new line is
# the standard
W503,
# line break after binary operator
W504,
# not part of PEP8; doc line too long (> 79 characters)
W505,
# These are pycodestyle lints that black doesn't catch:
# W601, # .has_key() is deprecated, use in
# W602, # deprecated form of raising exception

View file

@ -9,11 +9,11 @@
},
{
"vers": "arm64",
"os": "macos-10.15"
"os": "macos-latest"
},
{
"vers": "auto64",
"os": "macos-10.15"
"os": "macos-latest"
},
{
"vers": "auto64",

18
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,18 @@
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: pip
directory: "/"
schedule:
interval: weekly
- package-ecosystem: cargo
directory: "/native"
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly

View file

@ -4,64 +4,42 @@ on:
jobs:
# Build python wheels
build_matrix:
name: Prepare job matrix for build job
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v1
- id: set-matrix
# match github.ref to the on_ref_regex field in the json
# to skip running linux/aarch64 builds on PRs
run: |
matrix=$(jq --arg ref "${{ github.ref }}" \
'map(select(.on_ref_regex as $pat | $pat == null or ($ref | test($pat))) | del(.on_ref_regex))' \
.github/build-matrix.json)
echo ::set-output name=matrix::{\"include\":$(echo $matrix)}\"
build:
name: Build wheels on ${{ join(matrix.os, '/') }}/${{ matrix.vers }}
needs: build_matrix
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix: ${{fromJson(needs.build_matrix.outputs.matrix)}}
matrix:
os:
[
macos-latest,
ubuntu-latest,
ubuntu-24.04-arm,
windows-latest,
windows-11-arm,
]
env:
SCCACHE_VERSION: 0.2.13
CIBW_BEFORE_ALL_LINUX: "curl https://sh.rustup.rs -sSf | env -u CARGO_HOME sh -s -- --default-toolchain stable --profile minimal -y"
CIBW_BEFORE_BUILD_LINUX: "rm -rf native/target; ln -s /host/${{github.workspace}}/native/target native/target; [ -d /host/${{github.workspace}}/native/target ] || mkdir /host/${{github.workspace}}/native/target"
CIBW_ENVIRONMENT_LINUX: 'PATH="$PATH:$HOME/.cargo/bin" LIBCST_NO_LOCAL_SCHEME=$LIBCST_NO_LOCAL_SCHEME CARGO_HOME=/host/home/runner/.cargo'
CIBW_BEFORE_ALL_MACOS: "rustup target add aarch64-apple-darwin x86_64-apple-darwin"
CIBW_BEFORE_ALL_WINDOWS: "rustup target add x86_64-pc-windows-msvc i686-pc-windows-msvc"
CIBW_ENVIRONMENT: 'PATH="$PATH:$HOME/.cargo/bin" LIBCST_NO_LOCAL_SCHEME=$LIBCST_NO_LOCAL_SCHEME'
CIBW_SKIP: "cp27-* cp34-* cp35-* pp* *-win32 *-win_arm64 *-musllinux_*"
CIBW_ARCHS: ${{ matrix.vers }}
CIBW_BUILD_VERBOSITY: 1
GITHUB_WORKSPACE: "${{github.workspace}}"
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v2
if: ${{ !contains(matrix.os, 'self-hosted') }}
- uses: actions/checkout@v4
with:
python-version: "3.10"
- uses: actions/cache@v2
id: cache
if: ${{ !contains(matrix.os, 'self-hosted') }}
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-python@v6
with:
path: ${{ env.pythonLocation }}
key: ${{ env.pythonLocation }}-${{ hashFiles('requirements.txt', 'requirements-dev.txt', 'setup.py') }}
- name: Rust Cache
if: ${{ !contains(matrix.os, 'self-hosted') }}
uses: Swatinem/rust-cache@v1.3.0
with:
working-directory: native
python-version: "3.12"
- uses: dtolnay/rust-toolchain@stable
- name: Disable scmtools local scheme
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
run: >-
echo LIBCST_NO_LOCAL_SCHEME=1 >> $GITHUB_ENV
- name: Enable building wheels for pre-release CPython versions
if: github.event_name != 'release'
run: echo CIBW_ENABLE=cpython-prerelease >> $GITHUB_ENV
- name: Build wheels
uses: pypa/cibuildwheel@v2.3.1
- uses: actions/upload-artifact@v2
uses: pypa/cibuildwheel@v3.2.1
- uses: actions/upload-artifact@v4
with:
path: wheelhouse/*.whl
name: wheels
name: wheels-${{matrix.os}}

View file

@ -6,144 +6,90 @@ on:
- main
pull_request:
permissions: {}
jobs:
# Run unittests
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: [3.7, 3.8, 3.9, "3.10"]
parser: [pure, native]
os: [macos-latest, ubuntu-latest, windows-latest]
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.13t"
- "3.14"
- "3.14t"
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v2
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
version: "0.7.13"
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v2
id: cache
- uses: actions/checkout@v4
with:
path: ${{ env.pythonLocation }}
key: ${{ env.pythonLocation }}-${{ hashFiles('requirements.txt', 'requirements-dev.txt', 'setup.py') }}
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: |
pip install --upgrade --upgrade-strategy eager build -r requirements.txt -r requirements-dev.txt
- if: ${{ matrix.parser == 'native' }}
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- if: ${{ matrix.parser == 'native' }}
name: Rust Cache
uses: Swatinem/rust-cache@v1.3.0
with:
working-directory: native
- run: >-
echo LIBCST_PARSER_TYPE=${{ matrix.parser }} >> $GITHUB_ENV
- name: Run Tests
run: python setup.py test
fetch-depth: 0
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- name: Build LibCST
run: uv sync --locked --dev
- name: Native Parser Tests
run: uv run poe test
- name: Coverage
run: uv run coverage report
# Run linters
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v2
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
version: "0.7.13"
python-version: "3.10"
- uses: actions/cache@v2
id: cache
with:
path: ${{ env.pythonLocation }}
key: ${{ env.pythonLocation }}-${{ hashFiles('requirements.txt', 'requirements-dev.txt', 'setup.py') }}
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: |
pip install --upgrade --upgrade-strategy eager build -r requirements.txt -r requirements-dev.txt
- run: flake8
- run: ufmt check .
- run: python3 -m fixit.cli.run_rules
- run: python -m slotscheck libcst
- run: ./check_copyright.sh
- run: uv run poe lint
- run: uv run poe fixtures
# Run pyre typechecker
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v2
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
version: "0.7.13"
python-version: "3.10"
- uses: actions/cache@v2
id: cache
with:
path: ${{ env.pythonLocation }}
key: ${{ env.pythonLocation }}-${{ hashFiles('requirements.txt', 'requirements-dev.txt', 'setup.py') }}
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: |
pip install --upgrade --upgrade-strategy eager build -r requirements.txt -r requirements-dev.txt
- name: Make sure Pyre uses the working copy
run: pip install -e .
- run: pyre --version
- run: pyre -n check
- run: python libcst/tests/test_pyre_integration.py
- run: git diff --exit-code
# Upload test coverage
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v2
with:
python-version: "3.10"
- uses: actions/cache@v2
id: cache
with:
path: ${{ env.pythonLocation }}
key: ${{ env.pythonLocation }}-${{ hashFiles('requirements.txt', 'requirements-dev.txt', 'setup.py') }}
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: |
pip install --upgrade --upgrade-strategy eager build -r requirements.txt -r requirements-dev.txt
- name: Generate Coverage
run: |
coverage run setup.py test
coverage xml -i
- uses: codecov/codecov-action@v2
with:
files: coverage.xml
fail_ci_if_error: true
verbose: true
- name: Archive Coverage
uses: actions/upload-artifact@v2
with:
name: coverage
path: coverage.xml
- run: uv run poe typecheck
# Build the docs
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v2
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
version: "0.7.13"
python-version: "3.10"
- uses: actions/cache@v2
id: cache
with:
path: ${{ env.pythonLocation }}
key: ${{ env.pythonLocation }}-${{ hashFiles('requirements.txt', 'requirements-dev.txt', 'setup.py') }}
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: |
pip install --upgrade --upgrade-strategy eager build -r requirements.txt -r requirements-dev.txt
- uses: ts-graphviz/setup-graphviz@v1
- run: sphinx-build docs/source/ docs/build/
- uses: ts-graphviz/setup-graphviz@v2
- run: uv run --group docs poe docs
- name: Archive Docs
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: sphinx-docs
path: docs/build
@ -156,48 +102,41 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.13t"]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
components: rustfmt, clippy
- name: Rust Cache
uses: Swatinem/rust-cache@v1.3.0
- uses: actions/setup-python@v6
with:
working-directory: native
- uses: actions/setup-python@v2
with:
python-version: "3.10"
python-version: ${{ matrix.python-version }}
- name: test
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path=native/Cargo.toml --release
run: cargo test --manifest-path=native/Cargo.toml --release
- name: test without python
if: matrix.os == 'ubuntu-latest'
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path=native/Cargo.toml --release --no-default-features
run: cargo test --manifest-path=native/Cargo.toml --release --no-default-features
- name: clippy
uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --manifest-path=native/Cargo.toml --all-features
run: cargo clippy --manifest-path=native/Cargo.toml --all-targets --all-features
- name: compile-benchmarks
run: cargo bench --manifest-path=native/Cargo.toml --no-run
rustfmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v4
with:
profile: minimal
toolchain: stable
override: true
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: rustup component add rustfmt
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all --manifest-path=native/Cargo.toml -- --check
- name: format
run: cargo fmt --all --manifest-path=native/Cargo.toml -- --check
build:
# only trigger here for pull requests - regular pushes are handled in pypi_upload
if: ${{ github.event_name == 'pull_request' }}
uses: Instagram/LibCST/.github/workflows/build.yml@main

View file

@ -16,48 +16,45 @@ jobs:
name: Upload wheels to pypi
runs-on: ubuntu-latest
needs: build
permissions:
id-token: write
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Download binary wheels
id: download
uses: actions/download-artifact@v2
uses: actions/download-artifact@v5
with:
name: wheels
pattern: wheels-*
path: wheelhouse
- uses: actions/setup-python@v2
merge-multiple: true
- uses: actions/setup-python@v6
with:
python-version: "3.10"
- uses: actions/cache@v2
id: cache
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
path: ${{ env.pythonLocation }}
key: ${{ env.pythonLocation }}-${{ hashFiles('requirements.txt', 'requirements-dev.txt', 'setup.py') }}
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: |
pip install --upgrade --upgrade-strategy eager build -r requirements.txt -r requirements-dev.txt
- name: Disable scmtools local scheme
run: >-
echo LIBCST_NO_LOCAL_SCHEME=1 >> $GITHUB_ENV
version: "0.7.13"
enable-cache: false
- name: Build a source tarball
env:
LIBCST_NO_LOCAL_SCHEME: 1
OUTDIR: ${{ steps.download.outputs.download-path }}
run: >-
python -m
uv run python -m
build
--sdist
--outdir ${{ steps.download.outputs.download-path }}
--outdir "$OUTDIR"
- name: Publish distribution 📦 to Test PyPI
if: github.event_name == 'push'
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
repository_url: https://test.pypi.org/legacy/
packages_dir: ${{ steps.download.outputs.download-path }}
repository-url: https://test.pypi.org/legacy/
packages-dir: ${{ steps.download.outputs.download-path }}
- name: Publish distribution 📦 to PyPI
if: github.event_name == 'release'
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
repository_url: https://pypi.org/legacy/
packages_dir: ${{ steps.download.outputs.download-path }}
packages-dir: ${{ steps.download.outputs.download-path }}

35
.github/workflows/zizmor.yml vendored Normal file
View file

@ -0,0 +1,35 @@
name: GitHub Actions Security Analysis with zizmor 🌈
on:
push:
branches: ["main"]
pull_request:
branches: ["**"]
jobs:
zizmor:
name: zizmor latest via PyPI
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
- name: Run zizmor 🌈
run: uvx zizmor --format sarif . > results.sarif
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: results.sarif
category: zizmor

4
.gitignore vendored
View file

@ -1,6 +1,7 @@
*.swp
*.swo
*.pyc
*.pyd
*.pyo
*.so
*.egg-info/
@ -17,3 +18,6 @@ libcst/_version.py
.hypothesis/
.python-version
target/
venv/
.venv/
.idea/

View file

@ -2,6 +2,9 @@
"exclude": [
".*\/native\/.*"
],
"ignore_all_errors": [
".venv"
],
"source_directories": [
"."
],

View file

@ -9,12 +9,14 @@ build:
os: ubuntu-20.04
tools:
python: "3"
rust: "1.55"
rust: "1.70"
apt_packages:
- graphviz
python:
install:
- requirements: requirements.txt
- requirements: requirements-dev.txt
- method: pip
path: .
extra_requirements:
- dev

View file

@ -1,3 +1,399 @@
# 1.8.6 - 2025-11-03
## What's Changed
* Update pyproject.toml for 3.14t by @itamaro in https://github.com/Instagram/LibCST/pull/1417
* Update PyO3 to 0.26 by @cjwatson in https://github.com/Instagram/LibCST/pull/1413
* Make CodemodCommand's supported_transforms order deterministic by @frvnkliu in https://github.com/Instagram/LibCST/pull/1424
## New Contributors
* @cjwatson made their first contribution in https://github.com/Instagram/LibCST/pull/1413
* @frvnkliu made their first contribution in https://github.com/Instagram/LibCST/pull/1424
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.8.5...v1.8.6
# 1.8.5 - 2025-09-25
## What's Changed
* fixed: circular import error by @drinkmorewaterr in https://github.com/Instagram/LibCST/pull/1406
# 1.8.4 - 2025-09-09
## What's Changed
* fixed: generate Attribute nodes when applying type annotations by @tungol in https://github.com/Instagram/LibCST/pull/1396
* added: Support parsing of t-strings #1374 by @drinkmorewaterr in https://github.com/Instagram/LibCST/pull/1398
* added: add support for PEP758 by @drinkmorewaterr in https://github.com/Instagram/LibCST/pull/1401
## New Contributors
* @tungol made their first contribution in https://github.com/Instagram/LibCST/pull/1396
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.8.2...v1.8.4
# 1.8.3 - 2025-08-29
## What's Changed
* removed: remove entry points to pure parser by @drinkmorewaterr in https://github.com/Instagram/LibCST/pull/1375
* fixed: fixes match statements to work with PositionProvider by @imsut in https://github.com/Instagram/LibCST/pull/1389
## New Contributors
* @hunterhogan made their first contribution in https://github.com/Instagram/LibCST/pull/1378
* @thomas-serre-sonarsource made their first contribution in https://github.com/Instagram/LibCST/pull/1379
* @imsut made their first contribution in https://github.com/Instagram/LibCST/pull/1389
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.8.2...v1.8.3
# 1.8.2 - 2025-06-13
# Fixed
* fix(dependency): add back typing-extensions for 3.9 by @Lee-W in https://github.com/Instagram/LibCST/pull/1358
## New Contributors
* @Lee-W made their first contribution in https://github.com/Instagram/LibCST/pull/1358
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.8.1...v1.8.2
# 1.8.1 - 2025-06-10
## Added
* add helper to convert nodes to matchers by @zsol in https://github.com/Instagram/LibCST/pull/1351
## Updated
* Avoid raising bare Exception by @zaicruvoir1rominet in https://github.com/Instagram/LibCST/pull/1168
* Upgrade PyYAML-ft version and use new module name by @lysnikolaou in https://github.com/Instagram/LibCST/pull/1353
## New Contributors
* @lysnikolaou made their first contribution in https://github.com/Instagram/LibCST/pull/1353
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.8.0...v1.8.1
# 1.8.0 - 2025-05-27
## Added
* Allow configuring empty formatter lists in codemod CLI by @ngoldbaum in https://github.com/Instagram/LibCST/pull/1319
* Publish several new binary wheels
* macos intel by @hadialqattan in https://github.com/Instagram/LibCST/pull/1316
* windows arm64 by @zsol in https://github.com/Instagram/LibCST/pull/1304
* 3.13 CPython free-threaded by @zsol in https://github.com/Instagram/LibCST/pull/1333
* (only on [test.pypi.org](https://test.pypi.org/project/libcst/#history)) 3.14 and 3.14 CPython free-threaded by @amyreese and @zsol in https://github.com/Instagram/LibCST/pull/1345 and https://github.com/Instagram/LibCST/pull/1331
* Enable support for free-threaded CPython by @zsol in https://github.com/Instagram/LibCST/pull/1295 and https://github.com/Instagram/LibCST/pull/1335
## Updated
* update pyo3 to 0.25 by @ngoldbaum in https://github.com/Instagram/LibCST/pull/1324
* Replace multiprocessing with ProcessPoolExecutor by @zsol in https://github.com/Instagram/LibCST/pull/1294
* Support pipe syntax for Union types in codegen by @zsol in https://github.com/Instagram/LibCST/pull/1336
## New Contributors
* @hadialqattan made their first contribution in https://github.com/Instagram/LibCST/pull/1316
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.7.0...v1.8.0
# 1.7.0 - 2025-03-13
## Added
* add free-threaded CI by @ngoldbaum in https://github.com/Instagram/LibCST/pull/1312
## Updated
* Remove dependency on `chic` and upgrade `annotate-snippets` by @zanieb in https://github.com/Instagram/LibCST/pull/1293
* Update for Pyo3 0.23 by @ngoldbaum in https://github.com/Instagram/LibCST/pull/1289
* Bump PyO3 to 0.23.5 by @mgorny in https://github.com/Instagram/LibCST/pull/1311
## New Contributors
* @zanieb made their first contribution in https://github.com/Instagram/LibCST/pull/1293
* @ngoldbaum made their first contribution in https://github.com/Instagram/LibCST/pull/1289
* @mgorny made their first contribution in https://github.com/Instagram/LibCST/pull/1311
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.6.0...v1.7.0
# 1.6.0 - 2025-01-09
## Fixed
* rename: store state in scratch by @zsol in https://github.com/Instagram/LibCST/pull/1250
* rename: handle imports via a parent module by @zsol in https://github.com/Instagram/LibCST/pull/1251
* rename: Fix imports with aliases by @zsol in https://github.com/Instagram/LibCST/pull/1252
* rename: don't leave trailing commas by @zsol in https://github.com/Instagram/LibCST/pull/1254
* rename: don't eat commas unnecessarily by @zsol in https://github.com/Instagram/LibCST/pull/1256
* rename: fix renaming toplevel names by @zsol in https://github.com/Instagram/LibCST/pull/1260
* bump 3.12 to 3.13 in readme by @khameeteman in https://github.com/Instagram/LibCST/pull/1228
## Added
* Add codemod to convert `typing.Union` to `|` by @yangdanny97 in https://github.com/Instagram/LibCST/pull/1270
* Add codemod to fix variadic callable annotations by @yangdanny97 in https://github.com/Instagram/LibCST/pull/1269
* Add codemod to rename typing aliases of builtins by @yangdanny97 in https://github.com/Instagram/LibCST/pull/1267
* Add typing classifier to pyproject.toml and badge to README by @yangdanny97 in https://github.com/Instagram/LibCST/pull/1272
* Expose TypeAlias and TypeVar related structs in rust library by @Crozzers in https://github.com/Instagram/LibCST/pull/1274
## Updated
* Upgrade pyo3 to 0.22 by @jelmer in https://github.com/Instagram/LibCST/pull/1180
## New Contributors
* @yangdanny97 made their first contribution in https://github.com/Instagram/LibCST/pull/1270
* @Crozzers made their first contribution in https://github.com/Instagram/LibCST/pull/1274
* @jelmer made their first contribution in https://github.com/Instagram/LibCST/pull/1180
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.5.1...v1.6.0
# 1.5.1 - 2024-11-18
## Added
* build wheels for musllinux by @MrMino in https://github.com/Instagram/LibCST/pull/1243
## New Contributors
* @MrMino made their first contribution in https://github.com/Instagram/LibCST/pull/1243
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.5.0...v1.5.1
# 1.5.0 - 2024-10-10
## Added
* FullyQualifiedNameProvider: Optionally consider pyproject.toml files when determining a file's module name and package by @camillol in https://github.com/Instagram/LibCST/pull/1148
* Add validation for If node by @kiri11 in https://github.com/Instagram/LibCST/pull/1177
* include python 3.13 in build by @khameeteman in https://github.com/Instagram/LibCST/pull/1203
## Fixed
* fix various Match statement visitation errors by @zsol in https://github.com/Instagram/LibCST/pull/1161
* Mention codemod -x flag in docs by @kiri11 in https://github.com/Instagram/LibCST/pull/1169
* Clear warnings for each file in codemod cli by @kiri11 in https://github.com/Instagram/LibCST/pull/1184
* Typo fix in codemods_tutorial.rst (trivial) by @wimglenn in https://github.com/Instagram/LibCST/pull/1208
* fix certain matchers breaking under multiprocessing by initializing them late by @kiri11 in https://github.com/Instagram/LibCST/pull/1204
## Updated
* make libcst_native::tokenizer public by @zsol in https://github.com/Instagram/LibCST/pull/1182
* Use `license` instead of `license-file` by @michel-slm in https://github.com/Instagram/LibCST/pull/1189
* Drop codecov from CI and readme by @amyreese in https://github.com/Instagram/LibCST/pull/1192
## New Contributors
* @kiri11 made their first contribution in https://github.com/Instagram/LibCST/pull/1169
* @grievejia made their first contribution in https://github.com/Instagram/LibCST/pull/1174
* @michel-slm made their first contribution in https://github.com/Instagram/LibCST/pull/1189
* @wimglenn made their first contribution in https://github.com/Instagram/LibCST/pull/1208
* @khameeteman made their first contribution in https://github.com/Instagram/LibCST/pull/1203
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.4.0...v1.5.0
# 1.4.0 - 2024-05-22
## Fixed
* Fix Literal parse error in RemoveImportsVisitor by @camillol in https://github.com/Instagram/LibCST/pull/1130
* Don't reset context.scratch between files by @zsol in https://github.com/Instagram/LibCST/pull/1151
* Various documentation fixes
* Typo fix FullRepoManager by @kit1980 in https://github.com/Instagram/LibCST/pull/1138
* ✏️ Fix tiny typo in `docs/source/metadata.rst` by @tiangolo in https://github.com/Instagram/LibCST/pull/1134
* ✏️ Fix typo in `docs/source/scope_tutorial.ipynb` by @tiangolo in https://github.com/Instagram/LibCST/pull/1135
* Update CONTRIBUTING.md by @zaicruvoir1rominet in https://github.com/Instagram/LibCST/pull/1142
## Added
* Add helper functions for common ways of filtering nodes by @zaicruvoir1rominet in https://github.com/Instagram/LibCST/pull/1137
* Dump CST to .dot (graphviz) files by @zaicruvoir1rominet in https://github.com/Instagram/LibCST/pull/1147
* Implement PEP-696 by @thereversiblewheel in https://github.com/Instagram/LibCST/pull/1141
## New Contributors
* @tiangolo made their first contribution in https://github.com/Instagram/LibCST/pull/1134
* @camillol made their first contribution in https://github.com/Instagram/LibCST/pull/1130
* @zaicruvoir1rominet made their first contribution in https://github.com/Instagram/LibCST/pull/1142
* @thereversiblewheel made their first contribution in https://github.com/Instagram/LibCST/pull/1141
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.3.1...v1.4.0
# 1.3.1 - 2024-04-03
## Fixed
* ImportError due to missing `mypy_extensions` dependency by @zsol in https://github.com/Instagram/LibCST/pull/1128
# 1.3.0 - 2024-04-03
## Updated
* Removed dependencies on `typing_extensions` and `typing_inspect` by @zsol in https://github.com/Instagram/LibCST/pull/1126
# 1.2.0 - 2024-02-19
## Updated
* Support running LibCST on Python 3.12 and drop support for running it on 3.8
* remove 3.8 support by @zsol in https://github.com/Instagram/LibCST/pull/1073
* Remove reference to distutils by @zsol in https://github.com/Instagram/LibCST/pull/1099
* Update pyproject.toml for Python 3.12 support by @itamaro in https://github.com/Instagram/LibCST/pull/1038
## Added
* Allow `Element::codegen` to be used by external users by @Wilfred in https://github.com/Instagram/LibCST/pull/1071
## Fixed
* Fix parsing list matchers without explicit brackets by @zsol in https://github.com/Instagram/LibCST/pull/1097
* installing rustc/cargo for mybinder demo by @aleivag in https://github.com/Instagram/LibCST/pull/1083
* fix filepathprovider generic type by @kinto0 in https://github.com/Instagram/LibCST/pull/1036
## New Contributors
* @itamaro made their first contribution in https://github.com/Instagram/LibCST/pull/1039
* @kinto0 made their first contribution in https://github.com/Instagram/LibCST/pull/1036
* @dtolnay made their first contribution in https://github.com/Instagram/LibCST/pull/1063
* @anonymousdouble made their first contribution in https://github.com/Instagram/LibCST/pull/1082
* @aleivag made their first contribution in https://github.com/Instagram/LibCST/pull/1083
* @Wilfred made their first contribution in https://github.com/Instagram/LibCST/pull/1071
* @diliop made their first contribution in https://github.com/Instagram/LibCST/pull/1106
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.1.0...v1.2.0
# 1.1.0 - 2023-10-05
## Added
* PEP 695 support
* parser: PEP 695 - Type Parameter Syntax #1004
* Scope provider: support for type annotations #1014
* PEP 701 support
* parser: support arbitrarily nested f-strings #1026
* parser: Parse multiline expressions in f-strings #1027
* parser: Support files with mixed newlines #1007
* [libcst](https://crates.io/crates/libcst) is now published to crates.io
## Fixed
* codemod/ApplyTypeAnnotationsVisitor: Do not annotate the same variable multiple times #956
* parser: Don't swallow trailing whitespace #976
* codemod/rename: Avoid duplicating import statements when the module name doesn't change #981
## Updated
* cli: Don't gather dirs ending .py #994
* drop support for Python 3.7 #997
* A few parser performance improvements:
* Switch to using thread_local regular expressions to stop mutext contention #996
* Remove need for regex in TextPosition::matches #1002
* Remove Regexes from whitespace parser #1008
# 1.0.1 - 2023-06-07
## Fixed
* Fix type of `evaluated_value` on string to allow bytes by @ljodal in https://github.com/Instagram/LibCST/pull/721
* Fix Sentinal typo by @kit1980 in https://github.com/Instagram/LibCST/pull/948
* Allow no whitespace after lambda body in certain cases by @zsol in https://github.com/Instagram/LibCST/pull/939
* Fix whitespace, fstring, walrus related parse errors (#939, #938, #937,
#936, #935, #934, #933, #932, #931) by @zsol in https://github.com/Instagram/LibCST/pull/940
* Codemod CLI: Print diff only when there is a change by @kit1980 in https://github.com/Instagram/LibCST/pull/945
## New Contributors
* @ljodal made their first contribution in https://github.com/Instagram/LibCST/pull/721
* @kit1980 made their first contribution in https://github.com/Instagram/LibCST/pull/948
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v1.0.0...v1.0.1
# 1.0.0 - 2023-05-25
The first major release of LibCST is essentially the same as 0.4.10, but using the
newer, Rust-based parser implementation by default. The old, pure Python parser is
scheduled for removal in the next (non-patch) release. Until then, it is available with
the `LIBCST_PARSER_TYPE` environment variable set to `pure`.
## Updated
* Switch the default parser implementation to native by @zsol in https://github.com/Instagram/LibCST/pull/929
# 0.4.10 - 2023-05-23
## New Contributors
* @and-semakin made their first contribution in https://github.com/Instagram/LibCST/pull/816
* @carljm made their first contribution in https://github.com/Instagram/LibCST/pull/828
* @sagarbadiyani made their first contribution in https://github.com/Instagram/LibCST/pull/841
* @podtserkovskiy made their first contribution in https://github.com/Instagram/LibCST/pull/894
* @rchen152 made their first contribution in https://github.com/Instagram/LibCST/pull/903
* @Kludex made their first contribution in https://github.com/Instagram/LibCST/pull/913
* @jakkdl made their first contribution in https://github.com/Instagram/LibCST/pull/921
## Added
* Add py3.11 classifier by @and-semakin in https://github.com/Instagram/LibCST/pull/816
* Script to regenerate test fixtures, upgrade to Pyre 0.9.10 by @amyreese in https://github.com/Instagram/LibCST/pull/872
* Allow FullyQualifiedNameProvider to work with absolute paths by @amyreese in https://github.com/Instagram/LibCST/pull/867
* Allow running codemods without configuring in YAML by @akx in https://github.com/Instagram/LibCST/pull/879
* Support PEP 604 in ApplyTypeAnnotationsVisitor by @hauntsaninja in https://github.com/Instagram/LibCST/pull/868
## Fixed
* fix PEP 604 union annotations in decorators by @carljm in https://github.com/Instagram/LibCST/pull/828
* [AddImportsVisitor] Docstring Check Only for the Top Element of the Body by @sagarbadiyani in https://github.com/Instagram/LibCST/pull/841
* Fix [#855](https://github.com/Instagram/LibCST/issues/855) - fail to parse with statement by @stroxler in https://github.com/Instagram/LibCST/pull/861
* Add setuptools-rust to build requirements in setup.py by @amyreese in https://github.com/Instagram/LibCST/pull/873
* Relative imports from '' package are not allowed by @podtserkovskiy in https://github.com/Instagram/LibCST/pull/894
* Use subprocess.DEVNULL instead of opening os.devnull by hand by @akx in https://github.com/Instagram/LibCST/pull/897
* Ensure current Python interpreter is used for subprocesses by @akx in https://github.com/Instagram/LibCST/pull/898
* Fix ApplyTypeAnnotationsVisitor behavior on attribute assignments. by @rchen152 in https://github.com/Instagram/LibCST/pull/903
* Fix spelling and grammar in some comments by @stroxler in https://github.com/Instagram/LibCST/pull/908
* skip escaped backslash in rf-string by @jakkdl in https://github.com/Instagram/LibCST/pull/921
* relax validation rules on decorators by @jakkdl in https://github.com/Instagram/LibCST/pull/926
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v0.4.9...v0.4.10
# 0.4.9 - 2022-11-10
## Updated
* Bump setuptools-rust version by @zsol in https://github.com/Instagram/LibCST/pull/809
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v0.4.8...v0.4.9
# 0.4.8 - 2022-11-10
## New Contributors
* @dhruvmanila made their first contribution in https://github.com/Instagram/LibCST/pull/728
* @vfazio made their first contribution in https://github.com/Instagram/LibCST/pull/801
* @matthewshaer made their first contribution in https://github.com/Instagram/LibCST/pull/807
## Fixed
* Fix parse error message for number parsing by @zzl0 in https://github.com/Instagram/LibCST/pull/724
* Fix problematic doc build, due to the new builder image provided by readthedocs doesn't has the `graphviz-dev` package pre-installed any more by @MapleCCC in https://github.com/Instagram/LibCST/pull/751
* Fix docstring of `FullRepoManager` by @MapleCCC in https://github.com/Instagram/LibCST/pull/750
* Fix bug when `TypeOf` is one of options in `OneOf` / `AllOf` by @MapleCCC in https://github.com/Instagram/LibCST/pull/756
* Tighten the metadata type of `ExpressionContextProvider` by @MapleCCC in https://github.com/Instagram/LibCST/pull/760
* Fix the bug that the use of formatter in codemods has undetermined target Python version, resulting in hard-to-reason-with behavior by @MapleCCC in https://github.com/Instagram/LibCST/pull/771
## Added
* Python 3.11 rutime support
* test using python 3.11 beta versions by @zsol in https://github.com/Instagram/LibCST/pull/723
* Python 3.11 wheels by @vfazio in https://github.com/Instagram/LibCST/pull/801
* Raise informative exception when metadata is unresolved in a metadata-based match by @MapleCCC in https://github.com/Instagram/LibCST/pull/757
* Add AccessorProvider by @matthewshaer in https://github.com/Instagram/LibCST/pull/807
**Full Changelog**: https://github.com/Instagram/LibCST/compare/v0.4.7...v0.4.8
# 0.4.7 - 2022-07-12
## New Contributors
* @Chenguang-Zhu made their first contribution in https://github.com/Instagram/LibCST/pull/720
## Fixed
* Fix get_qualified_names_for matching on prefixes of the given name by @lpetre in https://github.com/Instagram/LibCST/pull/719
## Added
* Implement lazy loading mechanism for expensive metadata providers by @Chenguang-Zhu in https://github.com/Instagram/LibCST/pull/720
# 0.4.6 - 2022-07-04
## New Contributors
- @superbobry made their first contribution in https://github.com/Instagram/LibCST/pull/702
## Fixed
- convert_type_comments now preserves comments following type comments by @superbobry in https://github.com/Instagram/LibCST/pull/702
- QualifiedNameProvider optimizations
- Cache the scope name prefix to prevent scope traversal in a tight loop by @lpetre in https://github.com/Instagram/LibCST/pull/708
- Faster qualified name formatting by @lpetre in https://github.com/Instagram/LibCST/pull/710
- Prevent unnecessary work in Scope.get_qualified_names_for_ by @lpetre in https://github.com/Instagram/LibCST/pull/709
- Fix parsing of parenthesized empty tuples by @zsol in https://github.com/Instagram/LibCST/pull/712
- Support whitespace after ParamSlash by @zsol in https://github.com/Instagram/LibCST/pull/713
- [parser] bail on deeply nested expressions by @zsol in https://github.com/Instagram/LibCST/pull/718
# 0.4.5 - 2022-06-17
## New Contributors
- @zzl0 made their first contribution in https://github.com/Instagram/LibCST/pull/704
## Fixed
- Only skip supported escaped characters in f-strings by @zsol in https://github.com/Instagram/LibCST/pull/700
- Escaping quote characters in raw string literals causes a tokenizer error by @zsol in https://github.com/Instagram/LibCST/issues/668
- Corrected a code example in the documentation by @zzl0 in https://github.com/Instagram/LibCST/pull/703
- Handle multiline strings that start with quotes by @zzl0 in https://github.com/Instagram/LibCST/pull/704
- Fixed a performance regression in libcst.metadata.ScopeProvider by @lpetre in https://github.com/Instagram/LibCST/pull/698
# 0.4.4 - 2022-06-13
## New Contributors

View file

@ -9,12 +9,32 @@ pull requests.
## Pull Requests
We actively welcome your pull requests.
1. Fork the repo and create your branch from `main`.
2. If you've added code that should be tested, add tests.
3. If you've changed APIs, update the documentation.
4. Ensure the test suite passes by `python -m unittest`.
5. Make sure your code lints.
6. If you haven't already, complete the Contributor License Agreement ("CLA").
### Setup Your Environment
1. Install a [Rust toolchain](https://rustup.rs) and [uv](https://docs.astral.sh/uv/)
2. Fork the repo on your side
3. Clone the repo
> git clone [your fork.git] libcst
> cd libcst
4. Sync with the main libcst version package
> git fetch --tags https://github.com/instagram/libcst
5. Setup the env
> uv sync
You are now ready to create your own branch from main, and contribute.
Please provide tests (using unittest), and update the documentation (both docstrings
and sphinx doc), if applicable.
### Before Submitting Your Pull Request
1. Format your code
> uv run poe format
2. Run the type checker
> uv run poe typecheck
3. Test your changes
> uv run poe test
4. Check linters
> uv run poe lint
## Contributor License Agreement ("CLA")
In order to accept your pull request, we need you to submit a CLA. You only need

View file

@ -13,8 +13,8 @@ PSF). These files are:
- libcst/_parser/parso/tests/test_fstring.py
- libcst/_parser/parso/tests/test_tokenize.py
- libcst/_parser/parso/tests/test_utils.py
- libcst_native/src/tokenize/core/mod.rs
- libcst_native/src/tokenize/core/string_types.rs
- native/libcst/src/tokenizer/core/mod.rs
- native/libcst/src/tokenizer/core/string_types.rs
Some Python files have been taken from dataclasses and are therefore Apache
licensed. Modifications on these files are licensed under Apache 2.0 license.

12
MAINTAINERS.md Normal file
View file

@ -0,0 +1,12 @@
# How to make a new release
1. Add a new entry to `CHANGELOG.md` (I normally use the [new release page](https://github.com/Instagram/LibCST/releases/new) to generate a changelog, then manually group)
1. Follow the existing format: `Fixed`, `Added`, `Updated`, `Deprecated`, `Removed`, `New Contributors` sections, and the full changelog link at the bottom.
1. Mention only user-visible changes - improvements to CI, tests, or development workflow aren't noteworthy enough
1. Version bumps are generally not worth mentioning with some notable exceptions (like pyo3)
1. Group related PRs into one bullet point if it makes sense
2. manually bump versions in `Cargo.toml` files in the repo
3. run `cargo update -p libcst`
4. make a new PR with the above changes, get it reviewed and landed
5. make a new release on Github, create a new tag on publish, and copy the contents of the changelog entry in there
6. after publishing, check out the repo at the new tag, and run `cd native; cargo +nightly publish -Z package-workspace -p libcst_derive -p libcst`

View file

@ -1,4 +1,5 @@
include README.rst LICENSE CODE_OF_CONDUCT.md CONTRIBUTING.md requirements.txt requirements-dev.txt docs/source/*.rst libcst/py.typed
include README.rst LICENSE CODE_OF_CONDUCT.md CONTRIBUTING.md docs/source/*.rst libcst/py.typed
include native/Cargo.toml
recursive-include native *
recursive-exclude native/target *

View file

@ -4,13 +4,13 @@
A Concrete Syntax Tree (CST) parser and serializer library for Python
|support-ukraine| |readthedocs-badge| |ci-badge| |codecov-badge| |pypi-badge| |pypi-download| |notebook-badge|
|support-ukraine| |readthedocs-badge| |ci-badge| |pypi-badge| |pypi-download| |notebook-badge| |types-badge|
.. |support-ukraine| image:: https://img.shields.io/badge/Support-Ukraine-FFD500?style=flat&labelColor=005BBB
:alt: Support Ukraine - Help Provide Humanitarian Aid to Ukraine.
:target: https://opensource.fb.com/support-ukraine
.. |readthedocs-badge| image:: https://readthedocs.org/projects/pip/badge/?version=latest&style=flat
.. |readthedocs-badge| image:: https://readthedocs.org/projects/libcst/badge/?version=latest&style=flat
:target: https://libcst.readthedocs.io/en/latest/
:alt: Documentation
@ -18,10 +18,6 @@ A Concrete Syntax Tree (CST) parser and serializer library for Python
:target: https://github.com/Instagram/LibCST/actions/workflows/build.yml?query=branch%3Amain
:alt: Github Actions
.. |codecov-badge| image:: https://codecov.io/gh/Instagram/LibCST/branch/main/graph/badge.svg
:target: https://codecov.io/gh/Instagram/LibCST/branch/main
:alt: CodeCov
.. |pypi-badge| image:: https://img.shields.io/pypi/v/libcst.svg
:target: https://pypi.org/project/libcst
:alt: PYPI
@ -35,9 +31,13 @@ A Concrete Syntax Tree (CST) parser and serializer library for Python
:target: https://mybinder.org/v2/gh/Instagram/LibCST/main?filepath=docs%2Fsource%2Ftutorial.ipynb
:alt: Notebook
.. |types-badge| image:: https://img.shields.io/pypi/types/libcst
:target: https://pypi.org/project/libcst
:alt: PYPI - Types
.. intro-start
LibCST parses Python 3.0 -> 3.11 source code as a CST tree that keeps
LibCST parses Python 3.0 -> 3.14 source code as a CST tree that keeps
all formatting details (comments, whitespaces, parentheses, etc). It's useful for
building automated refactoring (codemod) applications and linters.
@ -62,7 +62,9 @@ Example expression::
1 + 2
CST representation::
CST representation:
.. code-block:: python
BinaryOperation(
left=Integer(
@ -125,7 +127,7 @@ For a more detailed usage example, `see our documentation
Installation
------------
LibCST requires Python 3.7+ and can be easily installed using most common Python
LibCST requires Python 3.9+ and can be easily installed using most common Python
packaging tools. We recommend installing the latest stable release from
`PyPI <https://pypi.org/project/libcst/>`_ with pip:
@ -135,7 +137,7 @@ packaging tools. We recommend installing the latest stable release from
For parsing, LibCST ships with a native extension, so releases are distributed as binary
wheels as well as the source code. If a binary wheel is not available for your system
(Linux/Windows x86/x64 and Mac x64/arm are covered), you'll need a recent
(Linux/Windows x86/x64 and Mac x64/arm are covered), you'll need a recent
`Rust toolchain <https://rustup.rs>`_ for installing.
Further Reading
@ -146,51 +148,8 @@ Further Reading
Development
-----------
You'll need a recent `Rust toolchain <https://rustup.rs>`_ for developing.
See `CONTRIBUTING.md <CONTRIBUTING.md>`_ for more details.
Then, start by setting up and activating a virtualenv:
.. code-block:: shell
git clone git@github.com:Instagram/LibCST.git libcst
cd libcst
python3 -m venv ../libcst-env/ # just an example, put this wherever you want
source ../libcst-env/bin/activate
pip install --upgrade pip # optional, if you have an old system version of pip
pip install -r requirements.txt -r requirements-dev.txt
# If you're done with the virtualenv, you can leave it by running:
deactivate
We use `ufmt <https://ufmt.omnilib.dev/en/stable/>`_ to format code. To format
changes to be conformant, run the following in the root:
.. code-block:: shell
ufmt format && python -m fixit.cli.apply_fix
We use `slotscheck <https://slotscheck.rtfd.io>`_ to check the correctness
of class ``__slots__``. To check that slots are defined properly, run:
.. code-block:: shell
python -m slotscheck libcst
To run all tests, you'll need to do the following in the root:
.. code-block:: shell
python -m unittest
You can also run individual tests by using unittest and specifying a module like
this:
.. code-block:: shell
python -m unittest libcst.tests.test_batched_visitor
See the `unittest documentation <https://docs.python.org/3/library/unittest.html>`_
for more examples of how to run tests.
Building
~~~~~~~~
@ -207,13 +166,11 @@ directory:
cargo build
To build the ``libcst.native`` module and install ``libcst``, run this
from the root:
The ``libcst.native`` module should be rebuilt automatically, but to force it:
.. code-block:: shell
pip uninstall -y libcst
pip install -e .
uv sync --reinstall-package libcst
Type Checking
~~~~~~~~~~~~~
@ -224,10 +181,7 @@ To verify types for the library, do the following in the root:
.. code-block:: shell
pyre check
*Note:* You may need to run the ``pip install -e .`` command prior
to type checking, see the section above on building.
uv run poe typecheck
Generating Documents
~~~~~~~~~~~~~~~~~~~~
@ -236,7 +190,7 @@ To generate documents, do the following in the root:
.. code-block:: shell
sphinx-build docs/source/ docs/build/
uv run --group docs poe docs
Future
======

2
apt.txt Normal file
View file

@ -0,0 +1,2 @@
rustc
cargo

View file

@ -1,30 +0,0 @@
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
set -eu
EXITCODE=0
error() { echo "$1"; EXITCODE=1; }
EXCEPTION_PATTERNS=(
"^native/libcst/tests/fixtures/"
"^libcst/_add_slots\.py$"
"^libcst/tests/test_\(e2e\|fuzz\)\.py$"
"^libcst/_parser/base_parser\.py$"
"^libcst/_parser/parso/utils\.py$"
"^libcst/_parser/parso/pgen2/\(generator\|grammar_parser\)\.py$"
"^libcst/_parser/parso/python/\(py_token\|tokenize\)\.py$"
"^libcst/_parser/parso/tests/test_\(fstring\|tokenize\|utils\)\.py$"
)
while read filename; do \
if ! head -n 16 "$filename" | grep -q "Copyright (c) Meta Platforms, Inc. and affiliates."; then
error "Missing copyright in $filename"
fi
done < <( git ls-tree -r --name-only HEAD | grep "\(.py\|\.sh\|\.rs\)$" | \
grep -v "${EXCEPTION_PATTERNS[@]/#/-e}" )
exit $EXITCODE

View file

@ -1,4 +0,0 @@
coverage:
status:
project: no
patch: yes

View file

@ -26,7 +26,7 @@ then edit the produced ``.libcst.codemod.yaml`` file::
python3 -m libcst.tool initialize .
The file includes provisions for customizing any generated code marker, calling an
external code formatter such as `black <https://pypi.org/project/black/>`_, blackisting
external code formatter such as `black <https://pypi.org/project/black/>`_, blacklisting
patterns of files you never wish to touch and a list of modules that contain valid
codemods that can be executed. If you want to write and run codemods specific to your
repository or organization, you can add an in-repo module location to the list of
@ -135,16 +135,18 @@ replaces any string which matches our string command-line argument with a consta
It also takes care of adding the import required for the constant to be defined properly.
Cool! Let's look at the command-line help for this codemod. Let's assume you saved it
as ``constant_folding.py`` inside ``libcst.codemod.commands``. You can get help for the
as ``constant_folding.py``. You can get help for the
codemod by running the following command::
python3 -m libcst.tool codemod constant_folding.ConvertConstantCommand --help
python3 -m libcst.tool codemod -x constant_folding.ConvertConstantCommand --help
Notice that along with the default arguments, the ``--string`` and ``--constant``
arguments are present in the help, and the command-line description has been updated
with the codemod's description string. You'll notice that the codemod also shows up
on ``libcst.tool list``.
And ``-x`` flag allows to load any module as a codemod in addition to the standard ones.
----------------
Testing Codemods
----------------

View file

@ -71,7 +71,7 @@ master_doc = "index"
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = "en"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
@ -196,6 +196,7 @@ intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
# -- autodoc customization
def strip_class_signature(app, what, name, obj, options, signature, return_annotation):
if what == "class":
@ -218,7 +219,7 @@ def setup(app):
nbsphinx_prolog = r"""
{% set docname = 'docs/source/' + env.doc2path(env.docname, base=None) %}
{% set docname = 'docs/source/' + env.doc2path(env.docname, base=None)|string%}
.. only:: html

View file

@ -32,3 +32,18 @@ Functions that assist in traversing an existing LibCST tree.
.. autofunction:: libcst.helpers.get_full_name_for_node
.. autofunction:: libcst.helpers.get_full_name_for_node_or_raise
.. autofunction:: libcst.helpers.ensure_type
Node fields filtering Helpers
-----------------------------
Function that assist when handling CST nodes' fields.
.. autofunction:: libcst.helpers.filter_node_fields
And lower level functions:
.. autofunction:: libcst.helpers.get_node_fields
.. autofunction:: libcst.helpers.is_whitespace_node_field
.. autofunction:: libcst.helpers.is_syntax_node_field
.. autofunction:: libcst.helpers.is_default_node_field
.. autofunction:: libcst.helpers.get_field_default_value

View file

@ -18,10 +18,10 @@ numbers of nodes through the :class:`~libcst.metadata.PositionProvider`:
.. code-block:: python
class NamePrinter(cst.CSTVisitor):
METADATA_DEPENDENCIES = (cst.PositionProvider,)
METADATA_DEPENDENCIES = (cst.metadata.PositionProvider,)
def visit_Name(self, node: cst.Name) -> None:
pos = self.get_metadata(cst.PositionProvider, node).start
pos = self.get_metadata(cst.metadata.PositionProvider, node).start
print(f"{node.value} found at line {pos.line}, column {pos.column}")
wrapper = cst.metadata.MetadataWrapper(cst.parse_module("x = 1"))
@ -94,7 +94,7 @@ declaring one of :class:`~libcst.metadata.PositionProvider` or
most cases, :class:`~libcst.metadata.PositionProvider` is what you probably
want.
Node positions are is represented with :class:`~libcst.metadata.CodeRange`
Node positions are represented with :class:`~libcst.metadata.CodeRange`
objects. See :ref:`the above example<libcst-metadata-position-example>`.
.. autoclass:: libcst.metadata.PositionProvider
@ -134,7 +134,7 @@ New scopes are created for classes, functions, and comprehensions. Other block
constructs like conditional statements, loops, and try…except don't create their
own scope.
There are five different type of scope in Python:
There are five different types of scopes in Python:
:class:`~libcst.metadata.BuiltinScope`,
:class:`~libcst.metadata.GlobalScope`,
:class:`~libcst.metadata.ClassScope`,
@ -226,6 +226,14 @@ We provide :class:`~libcst.metadata.ParentNodeProvider` for those use cases.
.. autoclass:: libcst.metadata.ParentNodeProvider
:no-undoc-members:
File Path Metadata
------------------
This provides the absolute file path on disk for any module being visited.
Requires an active :class:`~libcst.metadata.FullRepoManager` when using this provider.
.. autoclass:: libcst.metadata.FilePathProvider
:no-undoc-members:
Type Inference Metadata
-----------------------
`Type inference <https://en.wikipedia.org/wiki/Type_inference>`__ is to automatically infer
@ -234,8 +242,8 @@ In Python, type checkers like `Mypy <https://github.com/python/mypy>`_ or
`Pyre <https://pyre-check.org/>`__ analyze `type annotations <https://docs.python.org/3/library/typing.html>`__
and infer types for expressions.
:class:`~libcst.metadata.TypeInferenceProvider` is provided by `Pyre Query API <https://pyre-check.org/docs/querying-pyre.html>`__
which requires `setup watchman <https://pyre-check.org/docs/watchman-integration.html>`_ for incremental typechecking.
:class:`~libcst.metadata.FullRepoManger` is built for manage the inter process communication to Pyre.
which requires `setup watchman <https://pyre-check.org/docs/getting-started/>`_ for incremental typechecking.
:class:`~libcst.metadata.FullRepoManager` is built for manage the inter process communication to Pyre.
.. autoclass:: libcst.metadata.TypeInferenceProvider
:no-undoc-members:

View file

@ -90,7 +90,7 @@
"source": [
"Warn on unused imports and undefined references\n",
"===============================================\n",
"To find all unused imports, we iterate through :attr:`~libcst.metadata.Scope.assignments` and an assignment is unused when its :attr:`~libcst.metadata.BaseAssignment.references` is empty. To find all undefined references, we iterate through :attr:`~libcst.metadata.Scope.accesses` (we focus on :class:`~libcst.Import`/:class:`~libcst.ImportFrom` assignments) and an access is undefined reference when its :attr:`~libcst.metadata.Access.referents` is empty. When reporting the warning to developer, we'll want to report the line number and column offset along with the suggestion to make it more clear. We can get position information from :class:`~libcst.metadata.PositionProvider` and print the warnings as follows.\n"
"To find all unused imports, we iterate through :attr:`~libcst.metadata.Scope.assignments` and an assignment is unused when its :attr:`~libcst.metadata.BaseAssignment.references` is empty. To find all undefined references, we iterate through :attr:`~libcst.metadata.Scope.accesses` (we focus on :class:`~libcst.Import`/:class:`~libcst.ImportFrom` assignments) and an access is undefined reference when its :attr:`~libcst.metadata.Access.referents` is empty. When reporting the warning to the developer, we'll want to report the line number and column offset along with the suggestion to make it more clear. We can get position information from :class:`~libcst.metadata.PositionProvider` and print the warnings as follows.\n"
]
},
{
@ -136,13 +136,13 @@
"Automatically Remove Unused Import\n",
"==================================\n",
"Unused import is a commmon code suggestion provided by lint tool like `flake8 F401 <https://lintlyci.github.io/Flake8Rules/rules/F401.html>`_ ``imported but unused``.\n",
"Even though reporting unused import is already useful, with LibCST we can provide automatic fix to remove unused import. That can make the suggestion more actionable and save developer's time.\n",
"Even though reporting unused imports is already useful, with LibCST we can provide an automatic fix to remove unused imports. That can make the suggestion more actionable and save developer's time.\n",
"\n",
"An import statement may import multiple names, we want to remove those unused names from the import statement. If all the names in the import statement are not used, we remove the entire import.\n",
"To remove the unused name, we implement ``RemoveUnusedImportTransformer`` by subclassing :class:`~libcst.CSTTransformer`. We overwrite ``leave_Import`` and ``leave_ImportFrom`` to modify the import statements.\n",
"When we find the import node in lookup table, we iterate through all ``names`` and keep used names in ``names_to_keep``.\n",
"When we find the import node in the lookup table, we iterate through all ``names`` and keep used names in ``names_to_keep``.\n",
"If ``names_to_keep`` is empty, all names are unused and we remove the entire import node.\n",
"Otherwise, we update the import node and just removing partial names."
"Otherwise, we update the import node and just remove partial names."
]
},
{
@ -195,7 +195,7 @@
"raw_mimetype": "text/restructuredtext"
},
"source": [
"After the transform, we use ``.code`` to generate fixed code and all unused names are fixed as expected! The difflib is used to show only changed part and only import lines are updated as expected."
"After the transform, we use ``.code`` to generate the fixed code and all unused names are fixed as expected! The difflib is used to show only the changed part and only imported lines are updated as expected."
]
},
{

View file

@ -1,24 +1,25 @@
{
"cells": [
{
"cell_type": "raw",
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"cell_type": "raw",
"source": [
"====================\n",
"Parsing and Visiting\n",
"====================\n",
"\n",
"LibCST provides helpers to parse source code string as concrete syntax tree. In order to perform static analysis to identify patterns in the tree or modify the tree programmatically, we can use visitor pattern to traverse the tree. In this tutorial, we demonstrate a common three-step-workflow to build an automated refactoring (codemod) application:\n",
"LibCST provides helpers to parse source code string as a concrete syntax tree. In order to perform static analysis to identify patterns in the tree or modify the tree programmatically, we can use the visitor pattern to traverse the tree. In this tutorial, we demonstrate a common four-step-workflow to build an automated refactoring (codemod) application:\n",
"\n",
"1. `Parse Source Code <#Parse-Source-Code>`_\n",
"2. `Build Visitor or Transformer <#Build-Visitor-or-Transformer>`_\n",
"3. `Generate Source Code <#Generate-Source-Code>`_\n",
"2. `Display The Source Code CST <#Display-Source-Code-CST>`_\n",
"3. `Build Visitor or Transformer <#Build-Visitor-or-Transformer>`_\n",
"4. `Generate Source Code <#Generate-Source-Code>`_\n",
"\n",
"Parse Source Code\n",
"=================\n",
"LibCST provides various helpers to parse source code as concrete syntax tree: :func:`~libcst.parse_module`, :func:`~libcst.parse_expression` and :func:`~libcst.parse_statement` (see :doc:`Parsing <parser>` for more detail). The default :class:`~libcst.CSTNode` repr provides pretty print formatting for reading the tree easily."
"LibCST provides various helpers to parse source code as a concrete syntax tree: :func:`~libcst.parse_module`, :func:`~libcst.parse_expression` and :func:`~libcst.parse_statement` (see :doc:`Parsing <parser>` for more detail)."
]
},
{
@ -41,7 +42,42 @@
"source": [
"import libcst as cst\n",
"\n",
"cst.parse_expression(\"1 + 2\")"
"source_tree = cst.parse_expression(\"1 + 2\")"
]
},
{
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"cell_type": "raw",
"source": [
"|\n",
"Display Source Code CST\n",
"=======================\n",
"The default :class:`~libcst.CSTNode` repr provides pretty print formatting for displaying the entire CST tree."
]
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": "print(source_tree)"
},
{
"metadata": {},
"cell_type": "raw",
"source": "The entire CST tree may be overwhelming at times. To only focus on essential elements of the CST tree, LibCST provides the ``dump`` helper."
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": [
"from libcst.display import dump\n",
"\n",
"print(dump(source_tree))"
]
},
{
@ -50,9 +86,11 @@
"raw_mimetype": "text/restructuredtext"
},
"source": [
" \n",
"|\n",
"Example: add typing annotation from pyi stub file to Python source\n",
"------------------------------------------------------------------\n",
"Python `typing annotation <https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html>`_ was added in Python 3.5. Some Python applications add typing annotations in separate ``pyi`` stub files in order to support old Python versions. When applications decide to stop supporting old Python versions, they'll want to automatically copy the type annotation from a pyi file to a source file. Here we demonstrate how to do that easliy using LibCST. The first step is to parse the pyi stub and source files as trees."
"Python `typing annotation <https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html>`_ was added in Python 3.5. Some Python applications add typing annotations in separate ``pyi`` stub files in order to support old Python versions. When applications decide to stop supporting old Python versions, they'll want to automatically copy the type annotation from a pyi file to a source file. Here we demonstrate how to do that easily using LibCST. The first step is to parse the pyi stub and source files as trees."
]
},
{
@ -68,7 +106,7 @@
" self._replace(type=self.type.name))\n",
"\n",
"def tokenize(code, version_info, start_pos=(1, 0)):\n",
" \"\"\"Generate tokens from a the source code (string).\"\"\"\n",
" \"\"\"Generate tokens from the source code (string).\"\"\"\n",
" lines = split_lines(code, keepends=True)\n",
" return tokenize_lines(lines, version_info, start_pos=start_pos)\n",
"'''\n",
@ -92,10 +130,11 @@
"raw_mimetype": "text/restructuredtext"
},
"source": [
"|\n",
"Build Visitor or Transformer\n",
"============================\n",
"For traversing and modifying the tree, LibCST provides Visitor and Transformer classes similar to the `ast module <https://docs.python.org/3/library/ast.html#ast.NodeVisitor>`_. To implement a visitor (read only) or transformer (read/write), simply implement a subclass of :class:`~libcst.CSTVisitor` or :class:`~libcst.CSTTransformer` (see :doc:`Visitors <visitors>` for more detail).\n",
"In the typing example, we need to implement a visitor to collect typing annotation from the stub tree and a transformer to copy the annotation to the function signature. In the visitor, we implement ``visit_FunctionDef`` to collect annotations. Later in the transformer, we implement ``leave_FunctionDef`` to add the collected annotations."
"In the typing example, we need to implement a visitor to collect typing annotations from the stub tree and a transformer to copy the annotation to the function signature. In the visitor, we implement ``visit_FunctionDef`` to collect annotations. Later in the transformer, we implement ``leave_FunctionDef`` to add the collected annotations."
]
},
{
@ -184,9 +223,10 @@
"raw_mimetype": "text/restructuredtext"
},
"source": [
"|\n",
"Generate Source Code\n",
"====================\n",
"Generating the source code from a cst tree is as easy as accessing the :attr:`~libcst.Module.code` attribute on :class:`~libcst.Module`. After the code generation, we often use `ufmt <https://ufmt.omnilib.dev/en/stable/>`_ to reformate the code to keep a consistent coding style."
"Generating the source code from a cst tree is as easy as accessing the :attr:`~libcst.Module.code` attribute on :class:`~libcst.Module`. After the code generation, we often use `ufmt <https://ufmt.omnilib.dev/en/stable/>`_ to reformat the code to keep a consistent coding style."
]
},
{

View file

@ -4,7 +4,7 @@
# LICENSE file in the root directory of this source tree.
from libcst._batched_visitor import BatchableCSTVisitor, visit_batched
from libcst._exceptions import MetadataException, ParserSyntaxError
from libcst._exceptions import CSTLogicError, MetadataException, ParserSyntaxError
from libcst._flatten_sentinel import FlattenSentinel
from libcst._maybe_sentinel import MaybeSentinel
from libcst._metadata_dependent import MetadataDependent
@ -29,6 +29,7 @@ from libcst._nodes.expression import (
BaseSimpleComp,
BaseSlice,
BaseString,
BaseTemplatedStringContent,
BinaryOperation,
BooleanOperation,
Call,
@ -75,6 +76,9 @@ from libcst._nodes.expression import (
StarredElement,
Subscript,
SubscriptElement,
TemplatedString,
TemplatedStringExpression,
TemplatedStringText,
Tuple,
UnaryOperation,
Yield,
@ -183,6 +187,7 @@ from libcst._nodes.statement import (
MatchValue,
NameItem,
Nonlocal,
ParamSpec,
Pass,
Raise,
Return,
@ -190,6 +195,11 @@ from libcst._nodes.statement import (
SimpleStatementSuite,
Try,
TryStar,
TypeAlias,
TypeParam,
TypeParameters,
TypeVar,
TypeVarTuple,
While,
With,
WithItem,
@ -236,6 +246,7 @@ __all__ = [
"CSTVisitorT",
"FlattenSentinel",
"MaybeSentinel",
"CSTLogicError",
"MetadataException",
"ParserSyntaxError",
"PartialParserConfig",
@ -261,6 +272,7 @@ __all__ = [
"BaseElement",
"BaseExpression",
"BaseFormattedStringContent",
"BaseTemplatedStringContent",
"BaseList",
"BaseNumber",
"BaseSet",
@ -284,6 +296,9 @@ __all__ = [
"FormattedString",
"FormattedStringExpression",
"FormattedStringText",
"TemplatedString",
"TemplatedStringText",
"TemplatedStringExpression",
"From",
"GeneratorExp",
"IfExp",
@ -438,4 +453,10 @@ __all__ = [
"VisitorMetadataProvider",
"MetadataDependent",
"MetadataWrapper",
"TypeVar",
"TypeVarTuple",
"ParamSpec",
"TypeParam",
"TypeParameters",
"TypeAlias",
]

View file

@ -38,19 +38,10 @@ def add_slots(cls: Type[_T]) -> Type[_T]:
# Create the class.
qualname = getattr(cls, "__qualname__", None)
try:
# GenericMeta in py3.6 requires us to track __orig_bases__. This is fixed in py3.7
# by the removal of GenericMeta. We should just be able to use cls.__bases__ in the
# future.
bases = getattr(cls, "__orig_bases__", cls.__bases__)
# pyre-fixme[9]: cls has type `Type[Variable[_T]]`; used as `_T`.
# pyre-fixme[19]: Expected 0 positional arguments.
cls = type(cls)(cls.__name__, bases, cls_dict)
except TypeError:
# We're in py3.7 and should use cls.__bases__
# pyre-fixme[9]: cls has type `Type[Variable[_T]]`; used as `_T`.
# pyre-fixme[19]: Expected 0 positional arguments.
cls = type(cls)(cls.__name__, cls.__bases__, cls_dict)
# pyre-fixme[9]: cls has type `Type[Variable[_T]]`; used as `_T`.
# pyre-fixme[19]: Expected 0 positional arguments.
cls = type(cls)(cls.__name__, cls.__bases__, cls_dict)
if qualname is not None:
cls.__qualname__ = qualname

View file

@ -4,18 +4,11 @@
# LICENSE file in the root directory of this source tree.
from enum import auto, Enum
from typing import Any, Callable, Iterable, Optional, Sequence, Tuple, Union
from typing import Any, Callable, final, Optional, Sequence, Tuple
from typing_extensions import final
from libcst._parser.parso.pgen2.generator import ReservedString
from libcst._parser.parso.python.token import PythonTokenTypes, TokenType
from libcst._parser.types.token import Token
from libcst._tabs import expand_tabs
_EOF_STR: str = "end of file (EOF)"
_INDENT_STR: str = "an indent"
_DEDENT_STR: str = "a dedent"
_NEWLINE_CHARS: str = "\r\n"
@ -23,42 +16,10 @@ class EOFSentinel(Enum):
EOF = auto()
def get_expected_str(
encountered: Union[Token, EOFSentinel],
expected: Union[Iterable[Union[TokenType, ReservedString]], EOFSentinel],
) -> str:
if (
isinstance(encountered, EOFSentinel)
or encountered.type is PythonTokenTypes.ENDMARKER
):
encountered_str = _EOF_STR
elif encountered.type is PythonTokenTypes.INDENT:
encountered_str = _INDENT_STR
elif encountered.type is PythonTokenTypes.DEDENT:
encountered_str = _DEDENT_STR
else:
encountered_str = repr(encountered.string)
class CSTLogicError(Exception):
"""General purpose internal error within LibCST itself."""
if isinstance(expected, EOFSentinel):
expected_names = [_EOF_STR]
else:
expected_names = sorted(
[
repr(el.name) if isinstance(el, TokenType) else repr(el.value)
for el in expected
]
)
if len(expected_names) > 10:
# There's too many possibilities, so it's probably not useful to list them.
# Instead, let's just abbreviate the message.
return f"Unexpectedly encountered {encountered_str}."
else:
if len(expected_names) == 1:
expected_str = expected_names[0]
else:
expected_str = f"{', '.join(expected_names[:-1])}, or {expected_names[-1]}"
return f"Encountered {encountered_str}, but expected {expected_str}."
pass
# pyre-fixme[2]: 'Any' type isn't pyre-strict.

View file

@ -7,14 +7,17 @@ import inspect
from abc import ABC
from contextlib import contextmanager
from typing import (
Callable,
cast,
ClassVar,
Collection,
Generic,
Iterator,
Mapping,
Type,
TYPE_CHECKING,
TypeVar,
Union,
)
if TYPE_CHECKING:
@ -29,7 +32,28 @@ if TYPE_CHECKING:
_T = TypeVar("_T")
_UNDEFINED_DEFAULT = object()
class _UNDEFINED_DEFAULT:
pass
class LazyValue(Generic[_T]):
"""
The class for implementing a lazy metadata loading mechanism that improves the
performance when retriving expensive metadata (e.g., qualified names). Providers
including :class:`~libcst.metadata.QualifiedNameProvider` use this class to load
the metadata of a certain node lazily when calling
:func:`~libcst.MetadataDependent.get_metadata`.
"""
def __init__(self, callable: Callable[[], _T]) -> None:
self.callable = callable
self.return_value: Union[_T, Type[_UNDEFINED_DEFAULT]] = _UNDEFINED_DEFAULT
def __call__(self) -> _T:
if self.return_value is _UNDEFINED_DEFAULT:
self.return_value = self.callable()
return cast(_T, self.return_value)
class MetadataDependent(ABC):
@ -107,6 +131,9 @@ class MetadataDependent(ABC):
)
if default is not _UNDEFINED_DEFAULT:
return cast(_T, self.metadata[key].get(node, default))
value = self.metadata[key].get(node, default)
else:
return cast(_T, self.metadata[key][node])
value = self.metadata[key][node]
if isinstance(value, LazyValue):
value = value()
return cast(_T, value)

View file

@ -8,6 +8,7 @@ from copy import deepcopy
from dataclasses import dataclass, field, fields, replace
from typing import Any, cast, ClassVar, Dict, List, Mapping, Sequence, TypeVar, Union
from libcst import CSTLogicError
from libcst._flatten_sentinel import FlattenSentinel
from libcst._nodes.internal import CodegenState
from libcst._removal_sentinel import RemovalSentinel
@ -109,7 +110,6 @@ def _clone(val: object) -> object:
@dataclass(frozen=True)
class CSTNode(ABC):
__slots__: ClassVar[Sequence[str]] = ()
def __post_init__(self) -> None:
@ -238,7 +238,7 @@ class CSTNode(ABC):
# validate return type of the user-defined `visitor.on_leave` method
if not isinstance(leave_result, (CSTNode, RemovalSentinel, FlattenSentinel)):
raise Exception(
raise CSTValidationError(
"Expected a node of type CSTNode or a RemovalSentinel, "
+ f"but got a return value of {type(leave_result).__name__}"
)
@ -293,8 +293,7 @@ class CSTNode(ABC):
return False
@abstractmethod
def _codegen_impl(self, state: CodegenState) -> None:
...
def _codegen_impl(self, state: CodegenState) -> None: ...
def _codegen(self, state: CodegenState, **kwargs: Any) -> None:
state.before_codegen(self)
@ -384,7 +383,7 @@ class CSTNode(ABC):
new_tree = self.visit(_ChildReplacementTransformer(old_node, new_node))
if isinstance(new_tree, (FlattenSentinel, RemovalSentinel)):
# The above transform never returns *Sentinel, so this isn't possible
raise Exception("Logic error, cannot get a *Sentinal here!")
raise CSTLogicError("Logic error, cannot get a *Sentinel here!")
return new_tree
def deep_remove(
@ -401,7 +400,7 @@ class CSTNode(ABC):
if isinstance(new_tree, FlattenSentinel):
# The above transform never returns FlattenSentinel, so this isn't possible
raise Exception("Logic error, cannot get a FlattenSentinel here!")
raise CSTLogicError("Logic error, cannot get a FlattenSentinel here!")
return new_tree
@ -423,7 +422,7 @@ class CSTNode(ABC):
new_tree = self.visit(_ChildWithChangesTransformer(old_node, changes))
if isinstance(new_tree, (FlattenSentinel, RemovalSentinel)):
# This is impossible with the above transform.
raise Exception("Logic error, cannot get a *Sentinel here!")
raise CSTLogicError("Logic error, cannot get a *Sentinel here!")
return new_tree
def __eq__(self: _CSTNodeSelfT, other: object) -> bool:
@ -471,7 +470,6 @@ class CSTNode(ABC):
class BaseLeaf(CSTNode, ABC):
__slots__ = ()
@property

View file

@ -15,9 +15,9 @@ from tokenize import (
Imagnumber as IMAGNUMBER_RE,
Intnumber as INTNUMBER_RE,
)
from typing import Callable, Generator, Optional, Sequence, Union
from typing import Callable, Generator, Literal, Optional, Sequence, Union
from typing_extensions import Literal
from libcst import CSTLogicError
from libcst._add_slots import add_slots
from libcst._maybe_sentinel import MaybeSentinel
@ -354,7 +354,7 @@ class Name(BaseAssignTargetExpression, BaseDelTargetExpression):
if len(self.value) == 0:
raise CSTValidationError("Cannot have empty name identifier.")
if not self.value.isidentifier():
raise CSTValidationError("Name is not a valid identifier.")
raise CSTValidationError(f"Name {self.value!r} is not a valid identifier.")
def _codegen_impl(self, state: CodegenState) -> None:
with self._parenthesize(state):
@ -535,7 +535,6 @@ StringQuoteLiteral = Literal['"', "'", '"""', "'''"]
class _BasePrefixedString(BaseString, ABC):
__slots__ = ()
@property
@ -656,14 +655,20 @@ class SimpleString(_BasePrefixedString):
if len(quote) == 2:
# Let's assume this is an empty string.
quote = quote[:1]
elif len(quote) == 6:
# Let's assume this is an empty triple-quoted string.
elif 3 < len(quote) <= 6:
# Let's assume this can be one of the following:
# >>> """"foo"""
# '"foo'
# >>> """""bar"""
# '""bar'
# >>> """"""
# ''
quote = quote[:3]
if len(quote) not in {1, 3}:
# We shouldn't get here due to construction validation logic,
# but handle the case anyway.
raise Exception("Invalid string {self.value}")
raise CSTLogicError(f"Invalid string {self.value}")
# pyre-ignore We know via the above validation that we will only
# ever return one of the four string literals.
@ -694,7 +699,7 @@ class SimpleString(_BasePrefixedString):
state.add_token(self.value)
@property
def evaluated_value(self) -> str:
def evaluated_value(self) -> Union[str, bytes]:
"""
Return an :func:`ast.literal_eval` evaluated str of :py:attr:`value`.
"""
@ -953,6 +958,253 @@ class FormattedString(_BasePrefixedString):
state.add_token(self.end)
class BaseTemplatedStringContent(CSTNode, ABC):
"""
The base type for :class:`TemplatedStringText` and
:class:`TemplatedStringExpression`. A :class:`TemplatedString` is composed of a
sequence of :class:`BaseTemplatedStringContent` parts.
"""
__slots__ = ()
@add_slots
@dataclass(frozen=True)
class TemplatedStringText(BaseTemplatedStringContent):
"""
Part of a :class:`TemplatedString` that is not inside curly braces (``{`` or ``}``).
For example, in::
f"ab{cd}ef"
``ab`` and ``ef`` are :class:`TemplatedStringText` nodes, but ``{cd}`` is a
:class:`TemplatedStringExpression`.
"""
#: The raw string value, including any escape characters present in the source
#: code, not including any enclosing quotes.
value: str
def _visit_and_replace_children(
self, visitor: CSTVisitorT
) -> "TemplatedStringText":
return TemplatedStringText(value=self.value)
def _codegen_impl(self, state: CodegenState) -> None:
state.add_token(self.value)
@add_slots
@dataclass(frozen=True)
class TemplatedStringExpression(BaseTemplatedStringContent):
"""
Part of a :class:`TemplatedString` that is inside curly braces (``{`` or ``}``),
including the surrounding curly braces. For example, in::
f"ab{cd}ef"
``{cd}`` is a :class:`TemplatedStringExpression`, but ``ab`` and ``ef`` are
:class:`TemplatedStringText` nodes.
An t-string expression may contain ``conversion`` and ``format_spec`` suffixes that
control how the expression is converted to a string.
"""
#: The expression we will evaluate and render when generating the string.
expression: BaseExpression
#: An optional conversion specifier, such as ``!s``, ``!r`` or ``!a``.
conversion: Optional[str] = None
#: An optional format specifier following the `format specification mini-language
#: <https://docs.python.org/3/library/string.html#formatspec>`_.
format_spec: Optional[Sequence[BaseTemplatedStringContent]] = None
#: Whitespace after the opening curly brace (``{``), but before the ``expression``.
whitespace_before_expression: BaseParenthesizableWhitespace = (
SimpleWhitespace.field("")
)
#: Whitespace after the ``expression``, but before the ``conversion``,
#: ``format_spec`` and the closing curly brace (``}``). Python does not
#: allow whitespace inside or after a ``conversion`` or ``format_spec``.
whitespace_after_expression: BaseParenthesizableWhitespace = SimpleWhitespace.field(
""
)
#: Equal sign for Templated string expression uses self-documenting expressions,
#: such as ``f"{x=}"``. See the `Python 3.8 release notes
#: <https://docs.python.org/3/whatsnew/3.8.html#f-strings-support-for-self-documenting-expressions-and-debugging>`_.
equal: Optional[AssignEqual] = None
def _validate(self) -> None:
if self.conversion is not None and self.conversion not in ("s", "r", "a"):
raise CSTValidationError("Invalid t-string conversion.")
def _visit_and_replace_children(
self, visitor: CSTVisitorT
) -> "TemplatedStringExpression":
format_spec = self.format_spec
return TemplatedStringExpression(
whitespace_before_expression=visit_required(
self,
"whitespace_before_expression",
self.whitespace_before_expression,
visitor,
),
expression=visit_required(self, "expression", self.expression, visitor),
equal=visit_optional(self, "equal", self.equal, visitor),
whitespace_after_expression=visit_required(
self,
"whitespace_after_expression",
self.whitespace_after_expression,
visitor,
),
conversion=self.conversion,
format_spec=(
visit_sequence(self, "format_spec", format_spec, visitor)
if format_spec is not None
else None
),
)
def _codegen_impl(self, state: CodegenState) -> None:
state.add_token("{")
self.whitespace_before_expression._codegen(state)
self.expression._codegen(state)
equal = self.equal
if equal is not None:
equal._codegen(state)
self.whitespace_after_expression._codegen(state)
conversion = self.conversion
if conversion is not None:
state.add_token("!")
state.add_token(conversion)
format_spec = self.format_spec
if format_spec is not None:
state.add_token(":")
for spec in format_spec:
spec._codegen(state)
state.add_token("}")
@add_slots
@dataclass(frozen=True)
class TemplatedString(_BasePrefixedString):
"""
An "t-string". Template strings are a generalization of f-strings,
using a t in place of the f prefix. Instead of evaluating to str,
t-strings evaluate to a new type: Template
T-Strings are defined in 'PEP 750'
>>> import libcst as cst
>>> cst.parse_expression('t"ab{cd}ef"')
TemplatedString(
parts=[
TemplatedStringText(
value='ab',
),
TemplatedStringExpression(
expression=Name(
value='cd',
lpar=[],
rpar=[],
),
conversion=None,
format_spec=None,
whitespace_before_expression=SimpleWhitespace(
value='',
),
whitespace_after_expression=SimpleWhitespace(
value='',
),
equal=None,
),
TemplatedStringText(
value='ef',
),
],
start='t"',
end='"',
lpar=[],
rpar=[],
)
>>>
"""
#: A templated string is composed as a series of :class:`TemplatedStringText` and
#: :class:`TemplatedStringExpression` parts.
parts: Sequence[BaseTemplatedStringContent]
#: The string prefix and the leading quote, such as ``t"``, ``T'``, ``tr"``, or
#: ``t"""``.
start: str = 't"'
#: The trailing quote. This must match the type of quote used in ``start``.
end: Literal['"', "'", '"""', "'''"] = '"'
lpar: Sequence[LeftParen] = ()
#: Sequence of parenthesis for precidence dictation.
rpar: Sequence[RightParen] = ()
def _validate(self) -> None:
super(_BasePrefixedString, self)._validate()
# Validate any prefix
prefix = self.prefix
if prefix not in ("t", "tr", "rt"):
raise CSTValidationError("Invalid t-string prefix.")
# Validate wrapping quotes
starttoken = self.start[len(prefix) :]
if starttoken != self.end:
raise CSTValidationError("t-string must have matching enclosing quotes.")
# Validate valid wrapping quote usage
if starttoken not in ('"', "'", '"""', "'''"):
raise CSTValidationError("Invalid t-string enclosing quotes.")
@property
def prefix(self) -> str:
"""
Returns the string's prefix, if any exists. The prefix can be ``t``,
``tr``, or ``rt``.
"""
prefix = ""
for c in self.start:
if c in ['"', "'"]:
break
prefix += c
return prefix.lower()
@property
def quote(self) -> StringQuoteLiteral:
"""
Returns the quotation used to denote the string. Can be either ``'``,
``"``, ``'''`` or ``\"\"\"``.
"""
return self.end
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TemplatedString":
return TemplatedString(
lpar=visit_sequence(self, "lpar", self.lpar, visitor),
start=self.start,
parts=visit_sequence(self, "parts", self.parts, visitor),
end=self.end,
rpar=visit_sequence(self, "rpar", self.rpar, visitor),
)
def _codegen_impl(self, state: CodegenState) -> None:
with self._parenthesize(state):
state.add_token(self.start)
for part in self.parts:
part._codegen(state)
state.add_token(self.end)
@add_slots
@dataclass(frozen=True)
class ConcatenatedString(BaseString):
@ -1007,7 +1259,7 @@ class ConcatenatedString(BaseString):
elif isinstance(right, FormattedString):
rightbytes = "b" in right.prefix
else:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
if leftbytes != rightbytes:
raise CSTValidationError("Cannot concatenate string and bytes.")
@ -1029,7 +1281,7 @@ class ConcatenatedString(BaseString):
self.right._codegen(state)
@property
def evaluated_value(self) -> Optional[str]:
def evaluated_value(self) -> Union[str, bytes, None]:
"""
Return an :func:`ast.literal_eval` evaluated str of recursively concatenated :py:attr:`left` and :py:attr:`right`
if and only if both :py:attr:`left` and :py:attr:`right` are composed by :class:`SimpleString` or :class:`ConcatenatedString`
@ -1043,7 +1295,11 @@ class ConcatenatedString(BaseString):
right_val = right.evaluated_value
if right_val is None:
return None
return left_val + right_val
if isinstance(left_val, bytes) and isinstance(right_val, bytes):
return left_val + right_val
if isinstance(left_val, str) and isinstance(right_val, str):
return left_val + right_val
return None
@add_slots
@ -1640,9 +1896,9 @@ class Annotation(CSTNode):
#: colon or arrow.
annotation: BaseExpression
whitespace_before_indicator: Union[
BaseParenthesizableWhitespace, MaybeSentinel
] = MaybeSentinel.DEFAULT
whitespace_before_indicator: Union[BaseParenthesizableWhitespace, MaybeSentinel] = (
MaybeSentinel.DEFAULT
)
whitespace_after_indicator: BaseParenthesizableWhitespace = SimpleWhitespace.field(
" "
)
@ -1681,7 +1937,7 @@ class Annotation(CSTNode):
if default_indicator == "->":
state.add_token(" ")
else:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
# Now, output the indicator and the rest of the annotation
state.add_token(default_indicator)
@ -1726,15 +1982,26 @@ class ParamSlash(CSTNode):
.. _PEP 570: https://www.python.org/dev/peps/pep-0570/#specification
"""
# Optional comma that comes after the slash.
#: Optional comma that comes after the slash. This comma doesn't own the whitespace
#: between ``/`` and ``,``.
comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
#: Whitespace after the ``/`` character. This is captured here in case there is a
#: comma.
whitespace_after: BaseParenthesizableWhitespace = SimpleWhitespace.field("")
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ParamSlash":
return ParamSlash(comma=visit_sentinel(self, "comma", self.comma, visitor))
return ParamSlash(
comma=visit_sentinel(self, "comma", self.comma, visitor),
whitespace_after=visit_required(
self, "whitespace_after", self.whitespace_after, visitor
),
)
def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
state.add_token("/")
self.whitespace_after._codegen(state)
comma = self.comma
if comma is MaybeSentinel.DEFAULT and default_comma:
state.add_token(", ")
@ -1963,6 +2230,25 @@ class Parameters(CSTNode):
star_kwarg=visit_optional(self, "star_kwarg", self.star_kwarg, visitor),
)
def _safe_to_join_with_lambda(self) -> bool:
"""
Determine if Parameters need a space after the `lambda` keyword. Returns True
iff it's safe to omit the space between `lambda` and these Parameters.
See also `BaseExpression._safe_to_use_with_word_operator`.
For example: `lambda*_: pass`
"""
if len(self.posonly_params) != 0:
return False
# posonly_ind can't appear if above condition is false
if len(self.params) > 0 and self.params[0].star not in {"*", "**"}:
return False
return True
def _codegen_impl(self, state: CodegenState) -> None: # noqa: C901
# Compute the star existence first so we can ask about whether
# each element is the last in the list or not.
@ -2064,9 +2350,16 @@ class Lambda(BaseExpression):
rpar: Sequence[RightParen] = ()
#: Whitespace after the lambda keyword, but before any argument or the colon.
whitespace_after_lambda: Union[
BaseParenthesizableWhitespace, MaybeSentinel
] = MaybeSentinel.DEFAULT
whitespace_after_lambda: Union[BaseParenthesizableWhitespace, MaybeSentinel] = (
MaybeSentinel.DEFAULT
)
def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool:
if position == ExpressionPosition.LEFT:
return len(self.rpar) > 0 or self.body._safe_to_use_with_word_operator(
position
)
return super()._safe_to_use_with_word_operator(position)
def _validate(self) -> None:
# Validate parents
@ -2095,6 +2388,7 @@ class Lambda(BaseExpression):
if (
isinstance(whitespace_after_lambda, BaseParenthesizableWhitespace)
and whitespace_after_lambda.empty
and not self.params._safe_to_join_with_lambda()
):
raise CSTValidationError(
"Must have at least one space after lambda when specifying params"
@ -2472,6 +2766,12 @@ class IfExp(BaseExpression):
#: Whitespace after the ``else`` keyword, but before the ``orelse`` expression.
whitespace_after_else: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool:
if position == ExpressionPosition.RIGHT:
return self.body._safe_to_use_with_word_operator(position)
else:
return self.orelse._safe_to_use_with_word_operator(position)
def _validate(self) -> None:
# Paren validation and such
super(IfExp, self)._validate()
@ -2550,9 +2850,9 @@ class From(CSTNode):
item: BaseExpression
#: The whitespace at the very start of this node.
whitespace_before_from: Union[
BaseParenthesizableWhitespace, MaybeSentinel
] = MaybeSentinel.DEFAULT
whitespace_before_from: Union[BaseParenthesizableWhitespace, MaybeSentinel] = (
MaybeSentinel.DEFAULT
)
#: The whitespace after the ``from`` keyword, but before the ``item``.
whitespace_after_from: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
@ -2611,9 +2911,9 @@ class Yield(BaseExpression):
rpar: Sequence[RightParen] = ()
#: Whitespace after the ``yield`` keyword, but before the ``value``.
whitespace_after_yield: Union[
BaseParenthesizableWhitespace, MaybeSentinel
] = MaybeSentinel.DEFAULT
whitespace_after_yield: Union[BaseParenthesizableWhitespace, MaybeSentinel] = (
MaybeSentinel.DEFAULT
)
def _validate(self) -> None:
# Paren rules and such
@ -2697,8 +2997,7 @@ class _BaseElementImpl(CSTNode, ABC):
state: CodegenState,
default_comma: bool = False,
default_comma_whitespace: bool = False, # False for a single-item collection
) -> None:
...
) -> None: ...
class BaseElement(_BaseElementImpl, ABC):
@ -3475,7 +3774,7 @@ class BaseSimpleComp(BaseComp, ABC):
#: The expression evaluated during each iteration of the comprehension. This
#: lexically comes before the ``for_in`` clause, but it is semantically the
#: inner-most element, evaluated inside the ``for_in`` clause.
elt: BaseAssignTargetExpression
elt: BaseExpression
#: The ``for ... in ... if ...`` clause that lexically comes after ``elt``. This may
#: be a nested structure for nested comprehensions. See :class:`CompFor` for
@ -3508,7 +3807,7 @@ class GeneratorExp(BaseSimpleComp):
"""
#: The expression evaluated and yielded during each iteration of the generator.
elt: BaseAssignTargetExpression
elt: BaseExpression
#: The ``for ... in ... if ...`` clause that comes after ``elt``. This may be a
#: nested structure for nested comprehensions. See :class:`CompFor` for details.
@ -3559,7 +3858,7 @@ class ListComp(BaseList, BaseSimpleComp):
"""
#: The expression evaluated and stored during each iteration of the comprehension.
elt: BaseAssignTargetExpression
elt: BaseExpression
#: The ``for ... in ... if ...`` clause that comes after ``elt``. This may be a
#: nested structure for nested comprehensions. See :class:`CompFor` for details.
@ -3601,7 +3900,7 @@ class SetComp(BaseSet, BaseSimpleComp):
"""
#: The expression evaluated and stored during each iteration of the comprehension.
elt: BaseAssignTargetExpression
elt: BaseExpression
#: The ``for ... in ... if ...`` clause that comes after ``elt``. This may be a
#: nested structure for nested comprehensions. See :class:`CompFor` for details.
@ -3643,10 +3942,10 @@ class DictComp(BaseDict, BaseComp):
"""
#: The key inserted into the dictionary during each iteration of the comprehension.
key: BaseAssignTargetExpression
key: BaseExpression
#: The value associated with the ``key`` inserted into the dictionary during each
#: iteration of the comprehension.
value: BaseAssignTargetExpression
value: BaseExpression
#: The ``for ... in ... if ...`` clause that lexically comes after ``key`` and
#: ``value``. This may be a nested structure for nested comprehensions. See
@ -3750,6 +4049,15 @@ class NamedExpr(BaseExpression):
rpar=visit_sequence(self, "rpar", self.rpar, visitor),
)
def _safe_to_use_with_word_operator(self, position: ExpressionPosition) -> bool:
if position == ExpressionPosition.LEFT:
return len(self.rpar) > 0 or self.value._safe_to_use_with_word_operator(
position
)
return len(self.lpar) > 0 or self.target._safe_to_use_with_word_operator(
position
)
def _codegen_impl(self, state: CodegenState) -> None:
with self._parenthesize(state):
self.target._codegen(state)

View file

@ -79,7 +79,6 @@ class Module(CSTNode):
has_trailing_newline=self.has_trailing_newline,
)
# pyre-fixme[14]: `visit` overrides method defined in `CSTNode` inconsistently.
def visit(self: _ModuleSelfT, visitor: CSTVisitorT) -> _ModuleSelfT:
"""
Returns the result of running a visitor over this module.

View file

@ -43,8 +43,7 @@ class _BaseOneTokenOp(CSTNode, ABC):
self.whitespace_after._codegen(state)
@abstractmethod
def _get_token(self) -> str:
...
def _get_token(self) -> str: ...
class _BaseTwoTokenOp(CSTNode, ABC):
@ -88,8 +87,7 @@ class _BaseTwoTokenOp(CSTNode, ABC):
self.whitespace_after._codegen(state)
@abstractmethod
def _get_tokens(self) -> Tuple[str, str]:
...
def _get_tokens(self) -> Tuple[str, str]: ...
class BaseUnaryOp(CSTNode, ABC):
@ -115,8 +113,7 @@ class BaseUnaryOp(CSTNode, ABC):
self.whitespace_after._codegen(state)
@abstractmethod
def _get_token(self) -> str:
...
def _get_token(self) -> str: ...
class BaseBooleanOp(_BaseOneTokenOp, ABC):

View file

@ -7,7 +7,9 @@ import inspect
import re
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Optional, Pattern, Sequence, Union
from typing import Literal, Optional, Pattern, Sequence, Union
from libcst import CSTLogicError
from libcst._add_slots import add_slots
from libcst._maybe_sentinel import MaybeSentinel
@ -21,7 +23,6 @@ from libcst._nodes.expression import (
BaseAssignTargetExpression,
BaseDelTargetExpression,
BaseExpression,
Call,
ConcatenatedString,
ExpressionPosition,
From,
@ -49,6 +50,7 @@ from libcst._nodes.op import (
AssignEqual,
BaseAugOp,
BitOr,
Colon,
Comma,
Dot,
ImportStar,
@ -113,8 +115,7 @@ class BaseSmallStatement(CSTNode, ABC):
@abstractmethod
def _codegen_impl(
self, state: CodegenState, default_semicolon: bool = False
) -> None:
...
) -> None: ...
@add_slots
@ -273,9 +274,9 @@ class Return(BaseSmallStatement):
#: Optional whitespace after the ``return`` keyword before the optional
#: value expression.
whitespace_after_return: Union[
SimpleWhitespace, MaybeSentinel
] = MaybeSentinel.DEFAULT
whitespace_after_return: Union[SimpleWhitespace, MaybeSentinel] = (
MaybeSentinel.DEFAULT
)
#: Optional semicolon when this is used in a statement line. This semicolon
#: owns the whitespace on both sides of it when it is used.
@ -599,7 +600,12 @@ class If(BaseCompoundStatement):
#: The whitespace appearing after the test expression but before the colon.
whitespace_after_test: SimpleWhitespace = SimpleWhitespace.field("")
# TODO: _validate
def _validate(self) -> None:
if (
self.whitespace_before_test.empty
and not self.test._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
):
raise CSTValidationError("Must have at least one space after 'if' keyword.")
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "If":
return If(
@ -746,7 +752,10 @@ class AsName(CSTNode):
whitespace_after_as: BaseParenthesizableWhitespace = SimpleWhitespace.field(" ")
def _validate(self) -> None:
if self.whitespace_after_as.empty:
if (
self.whitespace_after_as.empty
and not self.name._safe_to_use_with_word_operator(ExpressionPosition.RIGHT)
):
raise CSTValidationError(
"There must be at least one space between 'as' and name."
)
@ -1158,12 +1167,10 @@ class ImportAlias(CSTNode):
)
try:
self.evaluated_name
except Exception as e:
if str(e) == "Logic error!":
raise CSTValidationError(
"The imported name must be a valid qualified name."
)
raise e
except CSTLogicError as e:
raise CSTValidationError(
"The imported name must be a valid qualified name."
) from e
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ImportAlias":
return ImportAlias(
@ -1192,7 +1199,7 @@ class ImportAlias(CSTNode):
elif isinstance(node, Attribute):
return f"{self._name(node.value)}.{node.attr.value}"
else:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
@property
def evaluated_name(self) -> str:
@ -1619,7 +1626,7 @@ class Decorator(CSTNode):
#: The decorator that will return a new function wrapping the parent
#: of this decorator.
decorator: Union[Name, Attribute, Call]
decorator: BaseExpression
#: Line comments and empty lines before this decorator. The parent
#: :class:`FunctionDef` or :class:`ClassDef` node owns leading lines before
@ -1632,19 +1639,6 @@ class Decorator(CSTNode):
#: Optional trailing comment and newline following the decorator before the next line.
trailing_whitespace: TrailingWhitespace = TrailingWhitespace.field()
def _validate(self) -> None:
decorator = self.decorator
if len(decorator.lpar) > 0 or len(decorator.rpar) > 0:
raise CSTValidationError(
"Cannot have parens around decorator in a Decorator."
)
if isinstance(decorator, Call) and not isinstance(
decorator.func, (Name, Attribute)
):
raise CSTValidationError(
"Decorator call function must be Name or Attribute node."
)
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Decorator":
return Decorator(
leading_lines=visit_sequence(
@ -1700,6 +1694,8 @@ def get_docstring_impl(
evaluated_value = val.evaluated_value
else:
return None
if isinstance(evaluated_value, bytes):
return None
if evaluated_value is not None and clean:
return inspect.cleandoc(evaluated_value)
@ -1747,8 +1743,8 @@ class FunctionDef(BaseCompoundStatement):
#: Whitespace after the ``def`` keyword and before the function name.
whitespace_after_def: SimpleWhitespace = SimpleWhitespace.field(" ")
#: Whitespace after the function name and before the opening parenthesis for
#: the parameters.
#: Whitespace after the function name and before the type parameters or the opening
#: parenthesis for the parameters.
whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("")
#: Whitespace after the opening parenthesis for the parameters but before
@ -1759,6 +1755,13 @@ class FunctionDef(BaseCompoundStatement):
#: the colon.
whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
#: An optional declaration of type parameters.
type_parameters: Optional["TypeParameters"] = None
#: Whitespace between the type parameters and the opening parenthesis for the
#: (non-type) parameters.
whitespace_after_type_parameters: SimpleWhitespace = SimpleWhitespace.field("")
def _validate(self) -> None:
if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
raise CSTValidationError("Cannot have parens around Name in a FunctionDef.")
@ -1767,6 +1770,15 @@ class FunctionDef(BaseCompoundStatement):
"There must be at least one space between 'def' and name."
)
if (
self.type_parameters is None
and not self.whitespace_after_type_parameters.empty
):
raise CSTValidationError(
"whitespace_after_type_parameters must be empty if there are no type "
"parameters in FunctionDef"
)
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "FunctionDef":
return FunctionDef(
leading_lines=visit_sequence(
@ -1786,6 +1798,15 @@ class FunctionDef(BaseCompoundStatement):
whitespace_after_name=visit_required(
self, "whitespace_after_name", self.whitespace_after_name, visitor
),
type_parameters=visit_optional(
self, "type_parameters", self.type_parameters, visitor
),
whitespace_after_type_parameters=visit_required(
self,
"whitespace_after_type_parameters",
self.whitespace_after_type_parameters,
visitor,
),
whitespace_before_params=visit_required(
self, "whitespace_before_params", self.whitespace_before_params, visitor
),
@ -1814,6 +1835,10 @@ class FunctionDef(BaseCompoundStatement):
self.whitespace_after_def._codegen(state)
self.name._codegen(state)
self.whitespace_after_name._codegen(state)
type_params = self.type_parameters
if type_params is not None:
type_params._codegen(state)
self.whitespace_after_type_parameters._codegen(state)
state.add_token("(")
self.whitespace_before_params._codegen(state)
self.params._codegen(state)
@ -1875,19 +1900,34 @@ class ClassDef(BaseCompoundStatement):
#: Whitespace after the ``class`` keyword and before the class name.
whitespace_after_class: SimpleWhitespace = SimpleWhitespace.field(" ")
#: Whitespace after the class name and before the opening parenthesis for
#: the bases and keywords.
#: Whitespace after the class name and before the type parameters or the opening
#: parenthesis for the bases and keywords.
whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("")
#: Whitespace after the closing parenthesis or class name and before
#: the colon.
whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("")
#: An optional declaration of type parameters.
type_parameters: Optional["TypeParameters"] = None
#: Whitespace between type parameters and opening parenthesis for the bases and
#: keywords.
whitespace_after_type_parameters: SimpleWhitespace = SimpleWhitespace.field("")
def _validate_whitespace(self) -> None:
if self.whitespace_after_class.empty:
raise CSTValidationError(
"There must be at least one space between 'class' and name."
)
if (
self.type_parameters is None
and not self.whitespace_after_type_parameters.empty
):
raise CSTValidationError(
"whitespace_after_type_parameters must be empty if there are no type"
"parameters in a ClassDef"
)
def _validate_parens(self) -> None:
if len(self.name.lpar) > 0 or len(self.name.rpar) > 0:
@ -1930,6 +1970,15 @@ class ClassDef(BaseCompoundStatement):
whitespace_after_name=visit_required(
self, "whitespace_after_name", self.whitespace_after_name, visitor
),
type_parameters=visit_optional(
self, "type_parameters", self.type_parameters, visitor
),
whitespace_after_type_parameters=visit_required(
self,
"whitespace_after_type_parameters",
self.whitespace_after_type_parameters,
visitor,
),
lpar=visit_sentinel(self, "lpar", self.lpar, visitor),
bases=visit_sequence(self, "bases", self.bases, visitor),
keywords=visit_sequence(self, "keywords", self.keywords, visitor),
@ -1954,6 +2003,10 @@ class ClassDef(BaseCompoundStatement):
self.whitespace_after_class._codegen(state)
self.name._codegen(state)
self.whitespace_after_name._codegen(state)
type_params = self.type_parameters
if type_params is not None:
type_params._codegen(state)
self.whitespace_after_type_parameters._codegen(state)
lpar = self.lpar
if isinstance(lpar, MaybeSentinel):
if self.bases or self.keywords:
@ -2349,9 +2402,9 @@ class Raise(BaseSmallStatement):
cause: Optional[From] = None
#: Any whitespace appearing between the ``raise`` keyword and the exception.
whitespace_after_raise: Union[
SimpleWhitespace, MaybeSentinel
] = MaybeSentinel.DEFAULT
whitespace_after_raise: Union[SimpleWhitespace, MaybeSentinel] = (
MaybeSentinel.DEFAULT
)
#: Optional semicolon when this is used in a statement line. This semicolon
#: owns the whitespace on both sides of it when it is used.
@ -2805,17 +2858,16 @@ class MatchCase(CSTNode):
self, "whitespace_after_case", self.whitespace_after_case, visitor
),
pattern=visit_required(self, "pattern", self.pattern, visitor),
# pyre-fixme[6]: Expected `SimpleWhitespace` for 4th param but got
# `Optional[SimpleWhitespace]`.
whitespace_before_if=visit_optional(
whitespace_before_if=visit_required(
self, "whitespace_before_if", self.whitespace_before_if, visitor
),
# pyre-fixme[6]: Expected `SimpleWhitespace` for 5th param but got
# `Optional[SimpleWhitespace]`.
whitespace_after_if=visit_optional(
whitespace_after_if=visit_required(
self, "whitespace_after_if", self.whitespace_after_if, visitor
),
guard=visit_optional(self, "guard", self.guard, visitor),
whitespace_before_colon=visit_required(
self, "whitespace_before_colon", self.whitespace_before_colon, visitor
),
body=visit_required(self, "body", self.body, visitor),
)
@ -2834,6 +2886,9 @@ class MatchCase(CSTNode):
state.add_token("if")
self.whitespace_after_if._codegen(state)
guard._codegen(state)
else:
self.whitespace_before_if._codegen(state)
self.whitespace_after_if._codegen(state)
self.whitespace_before_colon._codegen(state)
state.add_token(":")
@ -3022,10 +3077,10 @@ class MatchList(MatchSequence):
patterns: Sequence[Union[MatchSequenceElement, MatchStar]]
#: An optional left bracket. If missing, this is an open sequence pattern.
lbracket: Optional[LeftSquareBracket] = LeftSquareBracket.field()
lbracket: Optional[LeftSquareBracket] = None
#: An optional left bracket. If missing, this is an open sequence pattern.
rbracket: Optional[RightSquareBracket] = RightSquareBracket.field()
rbracket: Optional[RightSquareBracket] = None
#: Parenthesis at the beginning of the node
lpar: Sequence[LeftParen] = ()
@ -3333,6 +3388,7 @@ class MatchClass(MatchPattern):
whitespace_after_kwds=visit_required(
self, "whitespace_after_kwds", self.whitespace_after_kwds, visitor
),
rpar=visit_sequence(self, "rpar", self.rpar, visitor),
)
def _codegen_impl(self, state: CodegenState) -> None:
@ -3369,15 +3425,15 @@ class MatchAs(MatchPattern):
#: Whitespace between ``pattern`` and the ``as`` keyword (if ``pattern`` is not
#: ``None``)
whitespace_before_as: Union[
BaseParenthesizableWhitespace, MaybeSentinel
] = MaybeSentinel.DEFAULT
whitespace_before_as: Union[BaseParenthesizableWhitespace, MaybeSentinel] = (
MaybeSentinel.DEFAULT
)
#: Whitespace between the ``as`` keyword and ``name`` (if ``pattern`` is not
#: ``None``)
whitespace_after_as: Union[
BaseParenthesizableWhitespace, MaybeSentinel
] = MaybeSentinel.DEFAULT
whitespace_after_as: Union[BaseParenthesizableWhitespace, MaybeSentinel] = (
MaybeSentinel.DEFAULT
)
#: Parenthesis at the beginning of the node
lpar: Sequence[LeftParen] = ()
@ -3420,6 +3476,13 @@ class MatchAs(MatchPattern):
state.add_token(" ")
elif isinstance(ws_after, BaseParenthesizableWhitespace):
ws_after._codegen(state)
else:
ws_before = self.whitespace_before_as
if isinstance(ws_before, BaseParenthesizableWhitespace):
ws_before._codegen(state)
ws_after = self.whitespace_after_as
if isinstance(ws_after, BaseParenthesizableWhitespace):
ws_after._codegen(state)
if name is None:
state.add_token("_")
else:
@ -3485,3 +3548,326 @@ class MatchOr(MatchPattern):
pats = self.patterns
for idx, pat in enumerate(pats):
pat._codegen(state, default_separator=idx + 1 < len(pats))
@add_slots
@dataclass(frozen=True)
class TypeVar(CSTNode):
"""
A simple (non-variadic) type variable.
Note: this node represents type a variable when declared using PEP-695 syntax.
"""
#: The name of the type variable.
name: Name
#: An optional bound on the type.
bound: Optional[BaseExpression] = None
#: The colon used to separate the name and bound. If not specified,
#: :class:`MaybeSentinel` will be replaced with a colon if there is a bound,
#: otherwise will be left empty.
colon: Union[Colon, MaybeSentinel] = MaybeSentinel.DEFAULT
def _codegen_impl(self, state: CodegenState) -> None:
with state.record_syntactic_position(self):
self.name._codegen(state)
bound = self.bound
colon = self.colon
if not isinstance(colon, MaybeSentinel):
colon._codegen(state)
else:
if bound is not None:
state.add_token(": ")
if bound is not None:
bound._codegen(state)
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeVar":
return TypeVar(
name=visit_required(self, "name", self.name, visitor),
colon=visit_sentinel(self, "colon", self.colon, visitor),
bound=visit_optional(self, "bound", self.bound, visitor),
)
@add_slots
@dataclass(frozen=True)
class TypeVarTuple(CSTNode):
"""
A variadic type variable.
"""
#: The name of this type variable.
name: Name
#: The (optional) whitespace between the star declaring this type variable as
#: variadic, and the variable's name.
whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("")
def _codegen_impl(self, state: CodegenState) -> None:
with state.record_syntactic_position(self):
state.add_token("*")
self.whitespace_after_star._codegen(state)
self.name._codegen(state)
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeVarTuple":
return TypeVarTuple(
name=visit_required(self, "name", self.name, visitor),
whitespace_after_star=visit_required(
self, "whitespace_after_star", self.whitespace_after_star, visitor
),
)
@add_slots
@dataclass(frozen=True)
class ParamSpec(CSTNode):
"""
A parameter specification.
Note: this node represents a parameter specification when declared using PEP-695
syntax.
"""
#: The name of this parameter specification.
name: Name
#: The (optional) whitespace between the double star declaring this type variable as
#: a parameter specification, and the name.
whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("")
def _codegen_impl(self, state: CodegenState) -> None:
with state.record_syntactic_position(self):
state.add_token("**")
self.whitespace_after_star._codegen(state)
self.name._codegen(state)
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ParamSpec":
return ParamSpec(
name=visit_required(self, "name", self.name, visitor),
whitespace_after_star=visit_required(
self, "whitespace_after_star", self.whitespace_after_star, visitor
),
)
@add_slots
@dataclass(frozen=True)
class TypeParam(CSTNode):
"""
A single type parameter that is contained in a :class:`TypeParameters` list.
"""
#: The actual parameter.
param: Union[TypeVar, TypeVarTuple, ParamSpec]
#: A trailing comma. If one is not provided, :class:`MaybeSentinel` will be replaced
#: with a comma only if a comma is required.
comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT
#: The equal sign used to denote assignment if there is a default.
equal: Union[AssignEqual, MaybeSentinel] = MaybeSentinel.DEFAULT
#: The star used to denote a variadic default
star: Literal["", "*"] = ""
#: The whitespace between the star and the type.
whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("")
#: Any optional default value, used when the argument is not supplied.
default: Optional[BaseExpression] = None
def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None:
self.param._codegen(state)
equal = self.equal
if equal is MaybeSentinel.DEFAULT and self.default is not None:
state.add_token(" = ")
elif isinstance(equal, AssignEqual):
equal._codegen(state)
state.add_token(self.star)
self.whitespace_after_star._codegen(state)
default = self.default
if default is not None:
default._codegen(state)
comma = self.comma
if isinstance(comma, MaybeSentinel):
if default_comma:
state.add_token(", ")
else:
comma._codegen(state)
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeParam":
ret = TypeParam(
param=visit_required(self, "param", self.param, visitor),
equal=visit_sentinel(self, "equal", self.equal, visitor),
star=self.star,
whitespace_after_star=visit_required(
self, "whitespace_after_star", self.whitespace_after_star, visitor
),
default=visit_optional(self, "default", self.default, visitor),
comma=visit_sentinel(self, "comma", self.comma, visitor),
)
return ret
def _validate(self) -> None:
if self.default is None and isinstance(self.equal, AssignEqual):
raise CSTValidationError(
"Must have a default when specifying an AssignEqual."
)
if self.star and not (self.default or isinstance(self.equal, AssignEqual)):
raise CSTValidationError("Star can only be present if a default")
if isinstance(self.star, str) and self.star not in ("", "*"):
raise CSTValidationError("Must specify either '' or '*' for star.")
@add_slots
@dataclass(frozen=True)
class TypeParameters(CSTNode):
"""
Type parameters when specified with PEP-695 syntax.
This node captures all specified parameters that are enclosed with square brackets.
"""
#: The parameters within the square brackets.
params: Sequence[TypeParam] = ()
#: Opening square bracket that marks the start of these parameters.
lbracket: LeftSquareBracket = LeftSquareBracket.field()
#: Closing square bracket that marks the end of these parameters.
rbracket: RightSquareBracket = RightSquareBracket.field()
def _codegen_impl(self, state: CodegenState) -> None:
self.lbracket._codegen(state)
params_len = len(self.params)
for idx, param in enumerate(self.params):
param._codegen(state, default_comma=idx + 1 < params_len)
self.rbracket._codegen(state)
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeParameters":
return TypeParameters(
lbracket=visit_required(self, "lbracket", self.lbracket, visitor),
params=visit_sequence(self, "params", self.params, visitor),
rbracket=visit_required(self, "rbracket", self.rbracket, visitor),
)
@add_slots
@dataclass(frozen=True)
class TypeAlias(BaseSmallStatement):
"""
A type alias statement.
This node represents the ``type`` statement as specified initially by PEP-695.
Example: ``type ListOrSet[T] = list[T] | set[T]``.
"""
#: The name being introduced in this statement.
name: Name
#: Everything on the right hand side of the ``=``.
value: BaseExpression
#: An optional list of type parameters, specified after the name.
type_parameters: Optional[TypeParameters] = None
#: Whitespace between the ``type`` soft keyword and the name.
whitespace_after_type: SimpleWhitespace = SimpleWhitespace.field(" ")
#: Whitespace between the name and the type parameters (if they exist) or the ``=``.
#: If not specified, :class:`MaybeSentinel` will be replaced with a single space if
#: there are no type parameters, otherwise no spaces.
whitespace_after_name: Union[SimpleWhitespace, MaybeSentinel] = (
MaybeSentinel.DEFAULT
)
#: Whitespace between the type parameters and the ``=``. Always empty if there are
#: no type parameters. If not specified, :class:`MaybeSentinel` will be replaced
#: with a single space if there are type parameters.
whitespace_after_type_parameters: Union[SimpleWhitespace, MaybeSentinel] = (
MaybeSentinel.DEFAULT
)
#: Whitespace between the ``=`` and the value.
whitespace_after_equals: SimpleWhitespace = SimpleWhitespace.field(" ")
#: Optional semicolon when this is used in a statement line. This semicolon
#: owns the whitespace on both sides of it when it is used.
semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT
def _validate(self) -> None:
if (
self.type_parameters is None
and self.whitespace_after_type_parameters
not in {
SimpleWhitespace(""),
MaybeSentinel.DEFAULT,
}
):
raise CSTValidationError(
"whitespace_after_type_parameters must be empty when there are no type parameters in a TypeAlias"
)
def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeAlias":
return TypeAlias(
whitespace_after_type=visit_required(
self, "whitespace_after_type", self.whitespace_after_type, visitor
),
name=visit_required(self, "name", self.name, visitor),
whitespace_after_name=visit_sentinel(
self, "whitespace_after_name", self.whitespace_after_name, visitor
),
type_parameters=visit_optional(
self, "type_parameters", self.type_parameters, visitor
),
whitespace_after_type_parameters=visit_sentinel(
self,
"whitespace_after_type_parameters",
self.whitespace_after_type_parameters,
visitor,
),
whitespace_after_equals=visit_required(
self, "whitespace_after_equals", self.whitespace_after_equals, visitor
),
value=visit_required(self, "value", self.value, visitor),
semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor),
)
def _codegen_impl(
self, state: CodegenState, default_semicolon: bool = False
) -> None:
with state.record_syntactic_position(self):
state.add_token("type")
self.whitespace_after_type._codegen(state)
self.name._codegen(state)
ws_after_name = self.whitespace_after_name
if isinstance(ws_after_name, MaybeSentinel):
if self.type_parameters is None:
state.add_token(" ")
else:
ws_after_name._codegen(state)
ws_after_type_params = self.whitespace_after_type_parameters
if self.type_parameters is not None:
self.type_parameters._codegen(state)
if isinstance(ws_after_type_params, MaybeSentinel):
state.add_token(" ")
else:
ws_after_type_params._codegen(state)
state.add_token("=")
self.whitespace_after_equals._codegen(state)
self.value._codegen(state)
semi = self.semicolon
if isinstance(semi, MaybeSentinel):
if default_semicolon:
state.add_token("; ")
else:
semi._codegen(state)

View file

@ -9,7 +9,6 @@ from typing import Any
import libcst as cst
from libcst import parse_expression
from libcst._nodes.tests.base import CSTNodeTest, parse_expression_as
from libcst._parser.entrypoints import is_native
from libcst.metadata import CodeRange
from libcst.testing.utils import data_provider
@ -1184,7 +1183,7 @@ class AtomTest(CSTNodeTest):
)
)
def test_versions(self, **kwargs: Any) -> None:
if is_native() and not kwargs.get("expect_success", True):
if not kwargs.get("expect_success", True):
self.skipTest("parse errors are disabled for native parser")
self.assert_parses(**kwargs)

View file

@ -174,3 +174,18 @@ class BinaryOperationTest(CSTNodeTest):
)
def test_invalid(self, **kwargs: Any) -> None:
self.assert_invalid(**kwargs)
@data_provider(
(
{
"code": '"a"' * 6000,
"parser": parse_expression,
},
{
"code": "[_" + " for _ in _" * 6000 + "]",
"parser": parse_expression,
},
)
)
def test_parse_error(self, **kwargs: Any) -> None:
self.assert_parses(**kwargs, expect_success=False)

View file

@ -112,6 +112,105 @@ class ClassDefCreationTest(CSTNodeTest):
def test_valid(self, **kwargs: Any) -> None:
self.validate_node(**kwargs)
@data_provider(
(
{
"node": cst.ClassDef(
cst.Name("Foo"),
cst.SimpleStatementSuite((cst.Pass(),)),
type_parameters=cst.TypeParameters(
(
cst.TypeParam(
cst.TypeVar(
cst.Name("T"),
bound=cst.Name("int"),
colon=cst.Colon(
whitespace_after=cst.SimpleWhitespace(" ")
),
),
cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")),
),
cst.TypeParam(
cst.TypeVarTuple(cst.Name("Ts")),
cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")),
),
cst.TypeParam(cst.ParamSpec(cst.Name("KW"))),
)
),
),
"code": "class Foo[T: int, *Ts, **KW]: pass\n",
},
{
"node": cst.ClassDef(
cst.Name("Foo"),
cst.SimpleStatementSuite((cst.Pass(),)),
type_parameters=cst.TypeParameters(
params=(
cst.TypeParam(
param=cst.TypeVar(
cst.Name("T"),
bound=cst.Name("str"),
colon=cst.Colon(
whitespace_before=cst.SimpleWhitespace(" "),
whitespace_after=cst.ParenthesizedWhitespace(
empty_lines=(cst.EmptyLine(),),
indent=True,
),
),
),
comma=cst.Comma(cst.SimpleWhitespace(" ")),
),
cst.TypeParam(
cst.ParamSpec(
cst.Name("PS"), cst.SimpleWhitespace(" ")
),
cst.Comma(cst.SimpleWhitespace(" ")),
),
)
),
whitespace_after_type_parameters=cst.SimpleWhitespace(" "),
),
"code": "class Foo[T :\n\nstr ,** PS ,] : pass\n",
},
{
"node": cst.ClassDef(
cst.Name("Foo"),
cst.SimpleStatementSuite((cst.Pass(),)),
type_parameters=cst.TypeParameters(
params=(
cst.TypeParam(
param=cst.TypeVar(
cst.Name("T"),
bound=cst.Name("str"),
colon=cst.Colon(
whitespace_before=cst.SimpleWhitespace(" "),
whitespace_after=cst.ParenthesizedWhitespace(
empty_lines=(cst.EmptyLine(),),
indent=True,
),
),
),
comma=cst.Comma(cst.SimpleWhitespace(" ")),
),
cst.TypeParam(
cst.ParamSpec(
cst.Name("PS"), cst.SimpleWhitespace(" ")
),
cst.Comma(cst.SimpleWhitespace(" ")),
),
)
),
lpar=cst.LeftParen(),
rpar=cst.RightParen(),
whitespace_after_type_parameters=cst.SimpleWhitespace(" "),
),
"code": "class Foo[T :\n\nstr ,** PS ,] (): pass\n",
},
)
)
def test_valid_native(self, **kwargs: Any) -> None:
self.validate_node(**kwargs)
@data_provider(
(
# Basic parenthesis tests.

View file

@ -8,7 +8,6 @@ from typing import Any
import libcst as cst
from libcst import parse_expression
from libcst._nodes.tests.base import CSTNodeTest, parse_expression_as
from libcst._parser.entrypoints import is_native
from libcst.metadata import CodeRange
from libcst.testing.utils import data_provider
@ -188,6 +187,6 @@ class DictTest(CSTNodeTest):
)
)
def test_versions(self, **kwargs: Any) -> None:
if is_native() and not kwargs.get("expect_success", True):
if not kwargs.get("expect_success", True):
self.skipTest("parse errors are disabled for native parser")
self.assert_parses(**kwargs)

View file

@ -26,6 +26,17 @@ class DictCompTest(CSTNodeTest):
"parser": parse_expression,
"expected_position": CodeRange((1, 0), (1, 17)),
},
# non-trivial keys & values in DictComp
{
"node": cst.DictComp(
cst.BinaryOperation(cst.Name("k1"), cst.Add(), cst.Name("k2")),
cst.BinaryOperation(cst.Name("v1"), cst.Add(), cst.Name("v2")),
cst.CompFor(target=cst.Name("a"), iter=cst.Name("b")),
),
"code": "{k1 + k2: v1 + v2 for a in b}",
"parser": parse_expression,
"expected_position": CodeRange((1, 0), (1, 29)),
},
# custom whitespace around colon
{
"node": cst.DictComp(

View file

@ -8,7 +8,6 @@ from typing import Any, Callable
import libcst as cst
from libcst import parse_statement
from libcst._nodes.tests.base import CSTNodeTest, DummyIndentedBlock, parse_statement_as
from libcst._parser.entrypoints import is_native
from libcst.metadata import CodeRange
from libcst.testing.utils import data_provider
@ -623,6 +622,46 @@ class FunctionDefCreationTest(CSTNodeTest):
"code": "@ bar ( )\n",
"expected_position": CodeRange((1, 0), (1, 10)),
},
# Allow nested calls on decorator
{
"node": cst.FunctionDef(
cst.Name("foo"),
cst.Parameters(),
cst.SimpleStatementSuite((cst.Pass(),)),
(cst.Decorator(cst.Call(func=cst.Call(func=cst.Name("bar")))),),
),
"code": "@bar()()\ndef foo(): pass\n",
},
# Allow any expression in decorator
{
"node": cst.FunctionDef(
cst.Name("foo"),
cst.Parameters(),
cst.SimpleStatementSuite((cst.Pass(),)),
(
cst.Decorator(
cst.BinaryOperation(cst.Name("a"), cst.Add(), cst.Name("b"))
),
),
),
"code": "@a + b\ndef foo(): pass\n",
},
# Allow parentheses around decorator
{
"node": cst.FunctionDef(
cst.Name("foo"),
cst.Parameters(),
cst.SimpleStatementSuite((cst.Pass(),)),
(
cst.Decorator(
cst.Name(
"bar", lpar=(cst.LeftParen(),), rpar=(cst.RightParen(),)
)
),
),
),
"code": "@(bar)\ndef foo(): pass\n",
},
# Parameters
{
"node": cst.Parameters(
@ -701,8 +740,6 @@ class FunctionDefCreationTest(CSTNodeTest):
)
)
def test_valid(self, **kwargs: Any) -> None:
if not is_native() and kwargs.get("native_only", False):
self.skipTest("Disabled for native parser")
if "native_only" in kwargs:
kwargs.pop("native_only")
self.validate_node(**kwargs)
@ -771,11 +808,86 @@ class FunctionDefCreationTest(CSTNodeTest):
"parser": parse_statement,
"code": "def foo(*a: *tuple[int,*Ts,...]): pass\n",
},
# Single type variable
{
"node": cst.FunctionDef(
cst.Name("foo"),
cst.Parameters(),
cst.SimpleStatementSuite((cst.Pass(),)),
type_parameters=cst.TypeParameters(
(cst.TypeParam(cst.TypeVar(cst.Name("T"))),)
),
),
"code": "def foo[T](): pass\n",
"parser": parse_statement,
},
# All the type parameters
{
"node": cst.FunctionDef(
cst.Name("foo"),
cst.Parameters(),
cst.SimpleStatementSuite((cst.Pass(),)),
type_parameters=cst.TypeParameters(
(
cst.TypeParam(
cst.TypeVar(
cst.Name("T"),
bound=cst.Name("int"),
colon=cst.Colon(
whitespace_after=cst.SimpleWhitespace(" ")
),
),
cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")),
),
cst.TypeParam(
cst.TypeVarTuple(cst.Name("Ts")),
cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")),
),
cst.TypeParam(cst.ParamSpec(cst.Name("KW"))),
)
),
),
"code": "def foo[T: int, *Ts, **KW](): pass\n",
"parser": parse_statement,
},
# Type parameters with whitespace
{
"node": cst.FunctionDef(
cst.Name("foo"),
cst.Parameters(),
cst.SimpleStatementSuite((cst.Pass(),)),
type_parameters=cst.TypeParameters(
params=(
cst.TypeParam(
param=cst.TypeVar(
cst.Name("T"),
bound=cst.Name("str"),
colon=cst.Colon(
whitespace_before=cst.SimpleWhitespace(" "),
whitespace_after=cst.ParenthesizedWhitespace(
empty_lines=(cst.EmptyLine(),),
indent=True,
),
),
),
comma=cst.Comma(cst.SimpleWhitespace(" ")),
),
cst.TypeParam(
cst.ParamSpec(
cst.Name("PS"), cst.SimpleWhitespace(" ")
),
cst.Comma(cst.SimpleWhitespace(" ")),
),
)
),
whitespace_after_type_parameters=cst.SimpleWhitespace(" "),
),
"code": "def foo[T :\n\nstr ,** PS ,] (): pass\n",
"parser": parse_statement,
},
)
)
def test_valid_native(self, **kwargs: Any) -> None:
if not is_native():
self.skipTest("Disabled for native parser")
self.validate_node(**kwargs)
@data_provider(
@ -922,22 +1034,6 @@ class FunctionDefCreationTest(CSTNodeTest):
),
r"Expecting a star prefix of '\*\*'",
),
# Validate decorator name semantics
(
lambda: cst.FunctionDef(
cst.Name("foo"),
cst.Parameters(),
cst.SimpleStatementSuite((cst.Pass(),)),
(
cst.Decorator(
cst.Name(
"bar", lpar=(cst.LeftParen(),), rpar=(cst.RightParen(),)
)
),
),
),
"Cannot have parens around decorator in a Decorator",
),
)
)
def test_invalid(
@ -951,7 +1047,9 @@ def _parse_statement_force_38(code: str) -> cst.BaseCompoundStatement:
code, config=cst.PartialParserConfig(python_version="3.8")
)
if not isinstance(statement, cst.BaseCompoundStatement):
raise Exception("This function is expecting to parse compound statements only!")
raise ValueError(
"This function is expecting to parse compound statements only!"
)
return statement
@ -1874,6 +1972,36 @@ class FunctionDefParserTest(CSTNodeTest):
),
"code": "def foo(bar, baz, /): pass\n",
},
# Positional only params with whitespace after but no comma
{
"node": cst.FunctionDef(
cst.Name("foo"),
cst.Parameters(
posonly_params=(
cst.Param(
cst.Name("bar"),
star="",
comma=cst.Comma(
whitespace_after=cst.SimpleWhitespace(" ")
),
),
cst.Param(
cst.Name("baz"),
star="",
comma=cst.Comma(
whitespace_after=cst.SimpleWhitespace(" ")
),
),
),
posonly_ind=cst.ParamSlash(
whitespace_after=cst.SimpleWhitespace(" ")
),
),
cst.SimpleStatementSuite((cst.Pass(),)),
),
"code": "def foo(bar, baz, / ): pass\n",
"native_only": True,
},
# Typed positional only params
{
"node": cst.FunctionDef(
@ -2089,7 +2217,7 @@ class FunctionDefParserTest(CSTNodeTest):
},
)
)
def test_valid_38(self, node: cst.CSTNode, code: str) -> None:
def test_valid_38(self, node: cst.CSTNode, code: str, **kwargs: Any) -> None:
self.validate_node(node, code, _parse_statement_force_38)
@data_provider(
@ -2117,7 +2245,7 @@ class FunctionDefParserTest(CSTNodeTest):
)
)
def test_versions(self, **kwargs: Any) -> None:
if is_native() and not kwargs.get("expect_success", True):
if not kwargs.get("expect_success", True):
self.skipTest("parse errors are disabled for native parser")
self.assert_parses(**kwargs)
@ -2136,6 +2264,4 @@ class FunctionDefParserTest(CSTNodeTest):
)
)
def test_parse_error(self, **kwargs: Any) -> None:
if not is_native():
self.skipTest("Skipped for non-native parser")
self.assert_parses(**kwargs, expect_success=False, parser=parse_statement)

View file

@ -3,7 +3,7 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from typing import Any
from typing import Any, Callable
import libcst as cst
from libcst import parse_statement
@ -129,3 +129,21 @@ class IfTest(CSTNodeTest):
)
def test_valid(self, **kwargs: Any) -> None:
self.validate_node(**kwargs)
@data_provider(
(
# Validate whitespace handling
(
lambda: cst.If(
cst.Name("conditional"),
cst.SimpleStatementSuite((cst.Pass(),)),
whitespace_before_test=cst.SimpleWhitespace(""),
),
"Must have at least one space after 'if' keyword.",
),
)
)
def test_invalid(
self, get_node: Callable[[], cst.CSTNode], expected_re: str
) -> None:
self.assert_invalid(get_node, expected_re)

View file

@ -52,6 +52,41 @@ class IfExpTest(CSTNodeTest):
"(foo)if(bar)else(baz)",
CodeRange((1, 0), (1, 21)),
),
(
cst.IfExp(
body=cst.Name("foo"),
whitespace_before_if=cst.SimpleWhitespace(" "),
whitespace_after_if=cst.SimpleWhitespace(" "),
test=cst.Name("bar"),
whitespace_before_else=cst.SimpleWhitespace(" "),
whitespace_after_else=cst.SimpleWhitespace(""),
orelse=cst.IfExp(
body=cst.SimpleString("''"),
whitespace_before_if=cst.SimpleWhitespace(""),
test=cst.Name("bar"),
orelse=cst.Name("baz"),
),
),
"foo if bar else''if bar else baz",
CodeRange((1, 0), (1, 32)),
),
(
cst.GeneratorExp(
elt=cst.IfExp(
body=cst.Name("foo"),
test=cst.Name("bar"),
orelse=cst.SimpleString("''"),
whitespace_after_else=cst.SimpleWhitespace(""),
),
for_in=cst.CompFor(
target=cst.Name("_"),
iter=cst.Name("_"),
whitespace_before=cst.SimpleWhitespace(""),
),
),
"(foo if bar else''for _ in _)",
CodeRange((1, 1), (1, 28)),
),
# Make sure that spacing works
(
cst.IfExp(

View file

@ -30,6 +30,22 @@ class LambdaCreationTest(CSTNodeTest):
),
"code": "lambda bar, baz, /: 5",
},
# Test basic positional only params with extra trailing whitespace
{
"node": cst.Lambda(
cst.Parameters(
posonly_params=(
cst.Param(cst.Name("bar")),
cst.Param(cst.Name("baz")),
),
posonly_ind=cst.ParamSlash(
whitespace_after=cst.SimpleWhitespace(" ")
),
),
cst.Integer("5"),
),
"code": "lambda bar, baz, / : 5",
},
# Test basic positional params
(
cst.Lambda(
@ -287,30 +303,6 @@ class LambdaCreationTest(CSTNodeTest):
),
"at least one space after lambda",
),
(
lambda: cst.Lambda(
cst.Parameters(star_arg=cst.Param(cst.Name("arg"))),
cst.Integer("5"),
whitespace_after_lambda=cst.SimpleWhitespace(""),
),
"at least one space after lambda",
),
(
lambda: cst.Lambda(
cst.Parameters(kwonly_params=(cst.Param(cst.Name("arg")),)),
cst.Integer("5"),
whitespace_after_lambda=cst.SimpleWhitespace(""),
),
"at least one space after lambda",
),
(
lambda: cst.Lambda(
cst.Parameters(star_kwarg=cst.Param(cst.Name("arg"))),
cst.Integer("5"),
whitespace_after_lambda=cst.SimpleWhitespace(""),
),
"at least one space after lambda",
),
(
lambda: cst.Lambda(
cst.Parameters(
@ -928,6 +920,53 @@ class LambdaParserTest(CSTNodeTest):
),
"( lambda : 5 )",
),
# No space between lambda and params
(
cst.Lambda(
cst.Parameters(star_arg=cst.Param(cst.Name("args"), star="*")),
cst.Integer("5"),
whitespace_after_lambda=cst.SimpleWhitespace(""),
),
"lambda*args: 5",
),
(
cst.Lambda(
cst.Parameters(star_kwarg=cst.Param(cst.Name("kwargs"), star="**")),
cst.Integer("5"),
whitespace_after_lambda=cst.SimpleWhitespace(""),
),
"lambda**kwargs: 5",
),
(
cst.Lambda(
cst.Parameters(
star_arg=cst.ParamStar(
comma=cst.Comma(
cst.SimpleWhitespace(""), cst.SimpleWhitespace("")
)
),
kwonly_params=[cst.Param(cst.Name("args"), star="")],
),
cst.Integer("5"),
whitespace_after_lambda=cst.SimpleWhitespace(""),
),
"lambda*,args: 5",
),
(
cst.ListComp(
elt=cst.Lambda(
params=cst.Parameters(),
body=cst.Tuple(()),
colon=cst.Colon(),
),
for_in=cst.CompFor(
target=cst.Name("_"),
iter=cst.Name("_"),
whitespace_before=cst.SimpleWhitespace(""),
),
),
"[lambda:()for _ in _]",
),
)
)
def test_valid(

View file

@ -8,13 +8,11 @@ from typing import Any, Callable
import libcst as cst
from libcst import parse_expression, parse_statement
from libcst._nodes.tests.base import CSTNodeTest, parse_expression_as
from libcst._parser.entrypoints import is_native
from libcst.metadata import CodeRange
from libcst.testing.utils import data_provider
class ListTest(CSTNodeTest):
# A lot of Element/StarredElement tests are provided by the tests for Tuple, so we
# we don't need to duplicate them here.
@data_provider(
@ -127,6 +125,6 @@ class ListTest(CSTNodeTest):
)
)
def test_versions(self, **kwargs: Any) -> None:
if is_native() and not kwargs.get("expect_success", True):
if not kwargs.get("expect_success", True):
self.skipTest("parse errors are disabled for native parser")
self.assert_parses(**kwargs)

View file

@ -3,17 +3,14 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from typing import Any, Callable, Optional
from typing import Any, Callable
import libcst as cst
from libcst import parse_statement
from libcst._nodes.tests.base import CSTNodeTest
from libcst._parser.entrypoints import is_native
from libcst.testing.utils import data_provider
parser: Optional[Callable[[str], cst.CSTNode]] = (
parse_statement if is_native() else None
)
parser: Callable[[str], cst.CSTNode] = parse_statement
class MatchTest(CSTNodeTest):

View file

@ -11,7 +11,6 @@ from libcst._nodes.tests.base import (
parse_expression_as,
parse_statement_as,
)
from libcst._parser.entrypoints import is_native
from libcst.testing.utils import data_provider
@ -70,6 +69,6 @@ class NamedExprTest(CSTNodeTest):
)
)
def test_versions(self, **kwargs: Any) -> None:
if is_native() and not kwargs.get("expect_success", True):
if not kwargs.get("expect_success", True):
self.skipTest("parse errors are disabled for native parser")
self.assert_parses(**kwargs)

View file

@ -8,7 +8,7 @@ from typing import cast, Tuple
import libcst as cst
from libcst import parse_module, parse_statement
from libcst._nodes.tests.base import CSTNodeTest
from libcst._parser.entrypoints import is_native
from libcst.metadata import CodeRange, MetadataWrapper, PositionProvider
from libcst.testing.utils import data_provider
@ -117,7 +117,7 @@ class ModuleTest(CSTNodeTest):
def test_parser(
self, *, code: str, expected: cst.Module, enabled_for_native: bool = True
) -> None:
if is_native() and not enabled_for_native:
if not enabled_for_native:
self.skipTest("Disabled for native parser")
self.assertEqual(parse_module(code), expected)

View file

@ -22,7 +22,9 @@ def _parse_statement_force_38(code: str) -> cst.BaseCompoundStatement:
code, config=cst.PartialParserConfig(python_version="3.8")
)
if not isinstance(statement, cst.BaseCompoundStatement):
raise Exception("This function is expecting to parse compound statements only!")
raise ValueError(
"This function is expecting to parse compound statements only!"
)
return statement
@ -166,6 +168,22 @@ class NamedExprTest(CSTNodeTest):
"parser": _parse_expression_force_38,
"expected_position": None,
},
{
"node": cst.ListComp(
elt=cst.NamedExpr(
cst.Name("_"),
cst.SimpleString("''"),
whitespace_after_walrus=cst.SimpleWhitespace(""),
whitespace_before_walrus=cst.SimpleWhitespace(""),
),
for_in=cst.CompFor(
target=cst.Name("_"),
iter=cst.Name("_"),
whitespace_before=cst.SimpleWhitespace(""),
),
),
"code": "[_:=''for _ in _]",
},
)
)
def test_valid(self, **kwargs: Any) -> None:

View file

@ -95,7 +95,7 @@ class RemovalBehavior(CSTNodeTest):
self, before: str, after: str, visitor: Type[CSTTransformer]
) -> None:
if before.endswith("\n") or after.endswith("\n"):
raise Exception("Test cases should not be newline-terminated!")
raise ValueError("Test cases should not be newline-terminated!")
# Test doesn't have newline termination case
before_module = parse_module(before)

View file

@ -8,12 +8,10 @@ from typing import Any, Callable
import libcst as cst
from libcst import parse_expression
from libcst._nodes.tests.base import CSTNodeTest, parse_expression_as
from libcst._parser.entrypoints import is_native
from libcst.testing.utils import data_provider
class ListTest(CSTNodeTest):
# A lot of Element/StarredElement tests are provided by the tests for Tuple, so we
# we don't need to duplicate them here.
@data_provider(
@ -134,6 +132,6 @@ class ListTest(CSTNodeTest):
)
)
def test_versions(self, **kwargs: Any) -> None:
if is_native() and not kwargs.get("expect_success", True):
if not kwargs.get("expect_success", True):
self.skipTest("parse errors are disabled for native parser")
self.assert_parses(**kwargs)

View file

@ -41,6 +41,33 @@ class SimpleCompTest(CSTNodeTest):
"code": "{a for b in c}",
"parser": parse_expression,
},
# non-trivial elt in GeneratorExp
{
"node": cst.GeneratorExp(
cst.BinaryOperation(cst.Name("a1"), cst.Add(), cst.Name("a2")),
cst.CompFor(target=cst.Name("b"), iter=cst.Name("c")),
),
"code": "(a1 + a2 for b in c)",
"parser": parse_expression,
},
# non-trivial elt in ListComp
{
"node": cst.ListComp(
cst.BinaryOperation(cst.Name("a1"), cst.Add(), cst.Name("a2")),
cst.CompFor(target=cst.Name("b"), iter=cst.Name("c")),
),
"code": "[a1 + a2 for b in c]",
"parser": parse_expression,
},
# non-trivial elt in SetComp
{
"node": cst.SetComp(
cst.BinaryOperation(cst.Name("a1"), cst.Add(), cst.Name("a2")),
cst.CompFor(target=cst.Name("b"), iter=cst.Name("c")),
),
"code": "{a1 + a2 for b in c}",
"parser": parse_expression,
},
# async GeneratorExp
{
"node": cst.GeneratorExp(

View file

@ -0,0 +1,31 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import unittest
import libcst as cst
class TestSimpleString(unittest.TestCase):
def test_quote(self) -> None:
test_cases = [
('"a"', '"'),
("'b'", "'"),
('""', '"'),
("''", "'"),
('"""c"""', '"""'),
("'''d'''", "'''"),
('""""e"""', '"""'),
("''''f'''", "'''"),
('"""""g"""', '"""'),
("'''''h'''", "'''"),
('""""""', '"""'),
("''''''", "'''"),
]
for s, expected_quote in test_cases:
simple_string = cst.SimpleString(s)
actual = simple_string.quote
self.assertEqual(expected_quote, actual)

View file

@ -0,0 +1,183 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from typing import Callable, Optional
import libcst as cst
from libcst import parse_expression
from libcst._nodes.tests.base import CSTNodeTest
from libcst.metadata import CodeRange
from libcst.testing.utils import data_provider
class TemplatedStringTest(CSTNodeTest):
@data_provider(
(
# Simple t-string with only text
(
cst.TemplatedString(
parts=(cst.TemplatedStringText("hello world"),),
),
't"hello world"',
True,
),
# t-string with one expression
(
cst.TemplatedString(
parts=(
cst.TemplatedStringText("hello "),
cst.TemplatedStringExpression(
expression=cst.Name("name"),
),
),
),
't"hello {name}"',
True,
),
# t-string with multiple expressions
(
cst.TemplatedString(
parts=(
cst.TemplatedStringText("a="),
cst.TemplatedStringExpression(expression=cst.Name("a")),
cst.TemplatedStringText(", b="),
cst.TemplatedStringExpression(expression=cst.Name("b")),
),
),
't"a={a}, b={b}"',
True,
CodeRange((1, 0), (1, 15)),
),
# t-string with nested expression
(
cst.TemplatedString(
parts=(
cst.TemplatedStringText("sum="),
cst.TemplatedStringExpression(
expression=cst.BinaryOperation(
left=cst.Name("a"),
operator=cst.Add(),
right=cst.Name("b"),
)
),
),
),
't"sum={a + b}"',
True,
),
# t-string with spacing in expression
(
cst.TemplatedString(
parts=(
cst.TemplatedStringText("x = "),
cst.TemplatedStringExpression(
whitespace_before_expression=cst.SimpleWhitespace(" "),
expression=cst.Name("x"),
whitespace_after_expression=cst.SimpleWhitespace(" "),
),
),
),
't"x = { x }"',
True,
),
# t-string with escaped braces
(
cst.TemplatedString(
parts=(cst.TemplatedStringText("{{foo}}"),),
),
't"{{foo}}"',
True,
),
# t-string with only an expression
(
cst.TemplatedString(
parts=(
cst.TemplatedStringExpression(expression=cst.Name("value")),
),
),
't"{value}"',
True,
),
# t-string with whitespace and newlines
(
cst.TemplatedString(
parts=(
cst.TemplatedStringText("line1\\n"),
cst.TemplatedStringExpression(expression=cst.Name("x")),
cst.TemplatedStringText("\\nline2"),
),
),
't"line1\\n{x}\\nline2"',
True,
),
# t-string with parenthesis (not typical, but test node construction)
(
cst.TemplatedString(
lpar=(cst.LeftParen(),),
parts=(cst.TemplatedStringText("foo"),),
rpar=(cst.RightParen(),),
),
'(t"foo")',
True,
),
# t-string with whitespace in delimiters
(
cst.TemplatedString(
lpar=(cst.LeftParen(whitespace_after=cst.SimpleWhitespace(" ")),),
parts=(cst.TemplatedStringText("foo"),),
rpar=(cst.RightParen(whitespace_before=cst.SimpleWhitespace(" ")),),
),
'( t"foo" )',
True,
),
# Test TemplatedStringText and TemplatedStringExpression individually
(
cst.TemplatedStringText("abc"),
"abc",
False,
CodeRange((1, 0), (1, 3)),
),
(
cst.TemplatedStringExpression(expression=cst.Name("foo")),
"{foo}",
False,
CodeRange((1, 0), (1, 5)),
),
)
)
def test_valid(
self,
node: cst.CSTNode,
code: str,
check_parsing: bool,
position: Optional[CodeRange] = None,
) -> None:
if check_parsing:
self.validate_node(node, code, parse_expression, expected_position=position)
else:
self.validate_node(node, code, expected_position=position)
@data_provider(
(
(
lambda: cst.TemplatedString(
parts=(cst.TemplatedStringText("foo"),),
lpar=(cst.LeftParen(),),
),
"left paren without right paren",
),
(
lambda: cst.TemplatedString(
parts=(cst.TemplatedStringText("foo"),),
rpar=(cst.RightParen(),),
),
"right paren without left paren",
),
)
)
def test_invalid(
self, get_node: Callable[[], cst.CSTNode], expected_re: str
) -> None:
self.assert_invalid(get_node, expected_re)

View file

@ -3,18 +3,15 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from typing import Any, Callable, Optional
from typing import Any, Callable
import libcst as cst
from libcst import parse_statement
from libcst._nodes.tests.base import CSTNodeTest, DummyIndentedBlock
from libcst._parser.entrypoints import is_native
from libcst.metadata import CodeRange
from libcst.testing.utils import data_provider
native_parse_statement: Optional[Callable[[str], cst.CSTNode]] = (
parse_statement if is_native() else None
)
native_parse_statement: Callable[[str], cst.CSTNode] = parse_statement
class TryTest(CSTNodeTest):
@ -347,6 +344,34 @@ class TryTest(CSTNodeTest):
),
"code": "try: pass\nexcept foo()as bar: pass\n",
},
# PEP758 - Multiple exceptions with no parentheses
{
"node": cst.Try(
cst.SimpleStatementSuite((cst.Pass(),)),
handlers=[
cst.ExceptHandler(
cst.SimpleStatementSuite((cst.Pass(),)),
type=cst.Tuple(
elements=[
cst.Element(
value=cst.Name(
value="ValueError",
),
),
cst.Element(
value=cst.Name(
value="RuntimeError",
),
),
],
lpar=[],
rpar=[],
),
)
],
),
"code": "try: pass\nexcept ValueError, RuntimeError: pass\n",
},
)
)
def test_valid(self, **kwargs: Any) -> None:
@ -579,6 +604,38 @@ class TryStarTest(CSTNodeTest):
"parser": native_parse_statement,
"expected_position": CodeRange((1, 0), (5, 13)),
},
# PEP758 - Multiple exceptions with no parentheses
{
"node": cst.TryStar(
cst.SimpleStatementSuite((cst.Pass(),)),
handlers=[
cst.ExceptStarHandler(
cst.SimpleStatementSuite((cst.Pass(),)),
type=cst.Tuple(
elements=[
cst.Element(
value=cst.Name(
value="ValueError",
),
comma=cst.Comma(
whitespace_after=cst.SimpleWhitespace(" ")
),
),
cst.Element(
value=cst.Name(
value="RuntimeError",
),
),
],
lpar=[],
rpar=[],
),
)
],
),
"code": "try: pass\nexcept* ValueError, RuntimeError: pass\n",
"parser": native_parse_statement,
},
)
)
def test_valid(self, **kwargs: Any) -> None:

View file

@ -8,7 +8,6 @@ from typing import Any, Callable
import libcst as cst
from libcst import parse_expression, parse_statement
from libcst._nodes.tests.base import CSTNodeTest, parse_expression_as
from libcst._parser.entrypoints import is_native
from libcst.metadata import CodeRange
from libcst.testing.utils import data_provider
@ -286,6 +285,6 @@ class TupleTest(CSTNodeTest):
)
)
def test_versions(self, **kwargs: Any) -> None:
if is_native() and not kwargs.get("expect_success", True):
if not kwargs.get("expect_success", True):
self.skipTest("parse errors are disabled for native parser")
self.assert_parses(**kwargs)

View file

@ -0,0 +1,252 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from typing import Any
import libcst as cst
from libcst import parse_statement
from libcst._nodes.tests.base import CSTNodeTest
from libcst.metadata import CodeRange
from libcst.testing.utils import data_provider
class TypeAliasCreationTest(CSTNodeTest):
@data_provider(
(
{
"node": cst.TypeAlias(
cst.Name("foo"),
cst.Name("bar"),
),
"code": "type foo = bar",
"expected_position": CodeRange((1, 0), (1, 14)),
},
{
"node": cst.TypeAlias(
cst.Name("foo"),
type_parameters=cst.TypeParameters(
[cst.TypeParam(cst.TypeVar(cst.Name("T")))]
),
value=cst.BinaryOperation(
cst.Name("bar"), cst.BitOr(), cst.Name("baz")
),
),
"code": "type foo[T] = bar | baz",
"expected_position": CodeRange((1, 0), (1, 23)),
},
{
"node": cst.TypeAlias(
cst.Name("foo"),
type_parameters=cst.TypeParameters(
[
cst.TypeParam(
cst.TypeVar(cst.Name("T"), bound=cst.Name("str"))
),
cst.TypeParam(cst.TypeVarTuple(cst.Name("Ts"))),
cst.TypeParam(cst.ParamSpec(cst.Name("KW"))),
]
),
value=cst.BinaryOperation(
cst.Name("bar"), cst.BitOr(), cst.Name("baz")
),
),
"code": "type foo[T: str, *Ts, **KW] = bar | baz",
"expected_position": CodeRange((1, 0), (1, 39)),
},
{
"node": cst.TypeAlias(
cst.Name("foo"),
type_parameters=cst.TypeParameters(
[
cst.TypeParam(
cst.TypeVar(cst.Name("T")), default=cst.Name("str")
),
]
),
value=cst.Name("bar"),
),
"code": "type foo[T = str] = bar",
"expected_position": CodeRange((1, 0), (1, 23)),
},
{
"node": cst.TypeAlias(
cst.Name("foo"),
type_parameters=cst.TypeParameters(
[
cst.TypeParam(
cst.ParamSpec(cst.Name("P")),
default=cst.List(
elements=[
cst.Element(cst.Name("int")),
cst.Element(cst.Name("str")),
]
),
),
]
),
value=cst.Name("bar"),
),
"code": "type foo[**P = [int, str]] = bar",
"expected_position": CodeRange((1, 0), (1, 32)),
},
{
"node": cst.TypeAlias(
cst.Name("foo"),
type_parameters=cst.TypeParameters(
[
cst.TypeParam(
cst.TypeVarTuple(cst.Name("T")),
equal=cst.AssignEqual(),
default=cst.Name("default"),
star="*",
),
]
),
value=cst.Name("bar"),
),
"code": "type foo[*T = *default] = bar",
"expected_position": CodeRange((1, 0), (1, 29)),
},
{
"node": cst.TypeAlias(
cst.Name("foo"),
type_parameters=cst.TypeParameters(
[
cst.TypeParam(
cst.TypeVarTuple(cst.Name("T")),
equal=cst.AssignEqual(),
default=cst.Name("default"),
star="*",
whitespace_after_star=cst.SimpleWhitespace(" "),
),
]
),
value=cst.Name("bar"),
),
"code": "type foo[*T = * default] = bar",
"expected_position": CodeRange((1, 0), (1, 31)),
},
)
)
def test_valid(self, **kwargs: Any) -> None:
self.validate_node(**kwargs)
class TypeAliasParserTest(CSTNodeTest):
@data_provider(
(
{
"node": cst.SimpleStatementLine(
[
cst.TypeAlias(
cst.Name("foo"),
cst.Name("bar"),
whitespace_after_name=cst.SimpleWhitespace(" "),
)
]
),
"code": "type foo = bar\n",
"parser": parse_statement,
},
{
"node": cst.SimpleStatementLine(
[
cst.TypeAlias(
cst.Name("foo"),
cst.Name("bar"),
type_parameters=cst.TypeParameters(
params=[
cst.TypeParam(
cst.TypeVar(
cst.Name("T"), cst.Name("str"), cst.Colon()
),
cst.Comma(),
),
cst.TypeParam(
cst.ParamSpec(
cst.Name("KW"),
whitespace_after_star=cst.SimpleWhitespace(
" "
),
),
cst.Comma(
whitespace_before=cst.SimpleWhitespace(" "),
whitespace_after=cst.SimpleWhitespace(" "),
),
),
],
rbracket=cst.RightSquareBracket(
cst.SimpleWhitespace("")
),
),
whitespace_after_name=cst.SimpleWhitespace(" "),
whitespace_after_type=cst.SimpleWhitespace(" "),
whitespace_after_equals=cst.SimpleWhitespace(" "),
whitespace_after_type_parameters=cst.SimpleWhitespace(" "),
semicolon=cst.Semicolon(
whitespace_before=cst.SimpleWhitespace(" "),
whitespace_after=cst.SimpleWhitespace(" "),
),
)
]
),
"code": "type foo [T:str,** KW , ] = bar ; \n",
"parser": parse_statement,
},
{
"node": cst.SimpleStatementLine(
[
cst.TypeAlias(
cst.Name("foo"),
type_parameters=cst.TypeParameters(
[
cst.TypeParam(
cst.TypeVarTuple(cst.Name("P")),
star="*",
equal=cst.AssignEqual(),
default=cst.Name("default"),
),
]
),
value=cst.Name("bar"),
whitespace_after_name=cst.SimpleWhitespace(" "),
whitespace_after_type_parameters=cst.SimpleWhitespace(" "),
)
]
),
"code": "type foo [*P = *default] = bar\n",
"parser": parse_statement,
},
{
"node": cst.SimpleStatementLine(
[
cst.TypeAlias(
cst.Name("foo"),
type_parameters=cst.TypeParameters(
[
cst.TypeParam(
cst.TypeVarTuple(cst.Name("P")),
star="*",
whitespace_after_star=cst.SimpleWhitespace(
" "
),
equal=cst.AssignEqual(),
default=cst.Name("default"),
),
]
),
value=cst.Name("bar"),
whitespace_after_name=cst.SimpleWhitespace(" "),
whitespace_after_type_parameters=cst.SimpleWhitespace(" "),
)
]
),
"code": "type foo [*P = * default] = bar\n",
"parser": parse_statement,
},
)
)
def test_valid(self, **kwargs: Any) -> None:
self.validate_node(**kwargs)

View file

@ -7,9 +7,7 @@ from typing import Any
import libcst as cst
from libcst import parse_statement, PartialParserConfig
from libcst._maybe_sentinel import MaybeSentinel
from libcst._nodes.tests.base import CSTNodeTest, DummyIndentedBlock, parse_statement_as
from libcst._parser.entrypoints import is_native
from libcst.metadata import CodeRange
from libcst.testing.utils import data_provider
@ -102,6 +100,23 @@ class WithTest(CSTNodeTest):
"code": "with context_mgr() as ctx: pass\n",
"parser": parse_statement,
},
{
"node": cst.With(
(
cst.WithItem(
cst.Call(cst.Name("context_mgr")),
cst.AsName(
cst.Tuple(()),
whitespace_after_as=cst.SimpleWhitespace(""),
whitespace_before_as=cst.SimpleWhitespace(""),
),
),
),
cst.SimpleStatementSuite((cst.Pass(),)),
),
"code": "with context_mgr()as(): pass\n",
"parser": parse_statement,
},
# indentation
{
"node": DummyIndentedBlock(
@ -170,14 +185,14 @@ class WithTest(CSTNodeTest):
cst.WithItem(
cst.Call(
cst.Name("context_mgr"),
lpar=() if is_native() else (cst.LeftParen(),),
rpar=() if is_native() else (cst.RightParen(),),
lpar=(),
rpar=(),
)
),
),
cst.SimpleStatementSuite((cst.Pass(),)),
lpar=(cst.LeftParen() if is_native() else MaybeSentinel.DEFAULT),
rpar=(cst.RightParen() if is_native() else MaybeSentinel.DEFAULT),
lpar=(cst.LeftParen()),
rpar=(cst.RightParen()),
whitespace_after_with=cst.SimpleWhitespace(""),
),
"code": "with(context_mgr()): pass\n",
@ -216,7 +231,7 @@ class WithTest(CSTNodeTest):
rpar=cst.RightParen(whitespace_before=cst.SimpleWhitespace(" ")),
),
"code": ("with ( foo(),\n" " bar(), ): pass\n"), # noqa
"parser": parse_statement if is_native() else None,
"parser": parse_statement,
"expected_position": CodeRange((1, 0), (2, 21)),
},
)
@ -293,7 +308,7 @@ class WithTest(CSTNodeTest):
)
)
def test_versions(self, **kwargs: Any) -> None:
if is_native() and not kwargs.get("expect_success", True):
if not kwargs.get("expect_success", True):
self.skipTest("parse errors are disabled for native parser")
self.assert_parses(**kwargs)

View file

@ -8,7 +8,6 @@ from typing import Any, Callable, Optional
import libcst as cst
from libcst import parse_statement
from libcst._nodes.tests.base import CSTNodeTest, parse_statement_as
from libcst._parser.entrypoints import is_native
from libcst.helpers import ensure_type
from libcst.metadata import CodeRange
from libcst.testing.utils import data_provider
@ -241,6 +240,6 @@ class YieldParsingTest(CSTNodeTest):
)
)
def test_versions(self, **kwargs: Any) -> None:
if is_native() and not kwargs.get("expect_success", True):
if not kwargs.get("expect_success", True):
self.skipTest("parse errors are disabled for native parser")
self.assert_parses(**kwargs)

View file

@ -0,0 +1,53 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from typing import Iterable, Union
from libcst._exceptions import EOFSentinel
from libcst._parser.parso.pgen2.generator import ReservedString
from libcst._parser.parso.python.token import PythonTokenTypes, TokenType
from libcst._parser.types.token import Token
_EOF_STR: str = "end of file (EOF)"
_INDENT_STR: str = "an indent"
_DEDENT_STR: str = "a dedent"
def get_expected_str(
encountered: Union[Token, EOFSentinel],
expected: Union[Iterable[Union[TokenType, ReservedString]], EOFSentinel],
) -> str:
if (
isinstance(encountered, EOFSentinel)
or encountered.type is PythonTokenTypes.ENDMARKER
):
encountered_str = _EOF_STR
elif encountered.type is PythonTokenTypes.INDENT:
encountered_str = _INDENT_STR
elif encountered.type is PythonTokenTypes.DEDENT:
encountered_str = _DEDENT_STR
else:
encountered_str = repr(encountered.string)
if isinstance(expected, EOFSentinel):
expected_names = [_EOF_STR]
else:
expected_names = sorted(
[
repr(el.name) if isinstance(el, TokenType) else repr(el.value)
for el in expected
]
)
if len(expected_names) > 10:
# There's too many possibilities, so it's probably not useful to list them.
# Instead, let's just abbreviate the message.
return f"Unexpectedly encountered {encountered_str}."
else:
if len(expected_names) == 1:
expected_str = expected_names[0]
else:
expected_str = f"{', '.join(expected_names[:-1])}, or {expected_names[-1]}"
return f"Encountered {encountered_str}, but expected {expected_str}."

View file

@ -26,12 +26,8 @@
from dataclasses import dataclass, field
from typing import Generic, Iterable, List, Sequence, TypeVar, Union
from libcst._exceptions import (
EOFSentinel,
get_expected_str,
ParserSyntaxError,
PartialParserSyntaxError,
)
from libcst._exceptions import EOFSentinel, ParserSyntaxError, PartialParserSyntaxError
from libcst._parser._parsing_check import get_expected_str
from libcst._parser.parso.pgen2.generator import DFAState, Grammar, ReservedString
from libcst._parser.parso.python.token import TokenType
from libcst._parser.types.token import Token
@ -103,7 +99,7 @@ class BaseParser(Generic[_TokenT, _TokenTypeT, _NodeT]):
def parse(self) -> _NodeT:
# Ensure that we don't re-use parsers.
if self.__was_parse_called:
raise Exception("Each parser object may only be used to parse once.")
raise ValueError("Each parser object may only be used to parse once.")
self.__was_parse_called = True
for token in self.tokens:
@ -129,11 +125,9 @@ class BaseParser(Generic[_TokenT, _TokenTypeT, _NodeT]):
def convert_nonterminal(
self, nonterminal: str, children: Sequence[_NodeT]
) -> _NodeT:
...
) -> _NodeT: ...
def convert_terminal(self, token: _TokenT) -> _NodeT:
...
def convert_terminal(self, token: _TokenT) -> _NodeT: ...
def _add_token(self, token: _TokenT) -> None:
"""

View file

@ -12,7 +12,8 @@ from tokenize import (
Intnumber as INTNUMBER_RE,
)
from libcst._exceptions import PartialParserSyntaxError
from libcst import CSTLogicError
from libcst._exceptions import ParserSyntaxError, PartialParserSyntaxError
from libcst._maybe_sentinel import MaybeSentinel
from libcst._nodes.expression import (
Arg,
@ -327,7 +328,12 @@ def convert_boolop(
# Convert all of the operations that have no precedence in a loop
for op, rightexpr in grouper(rightexprs, 2):
if op.string not in BOOLOP_TOKEN_LUT:
raise Exception(f"Unexpected token '{op.string}'!")
raise ParserSyntaxError(
f"Unexpected token '{op.string}'!",
lines=config.lines,
raw_line=0,
raw_column=0,
)
leftexpr = BooleanOperation(
left=leftexpr,
# pyre-ignore Pyre thinks that the type of the LUT is CSTNode.
@ -420,7 +426,12 @@ def convert_comp_op(
)
else:
# this should be unreachable
raise Exception(f"Unexpected token '{op.string}'!")
raise ParserSyntaxError(
f"Unexpected token '{op.string}'!",
lines=config.lines,
raw_line=0,
raw_column=0,
)
else:
# A two-token comparison
leftcomp, rightcomp = children
@ -451,7 +462,12 @@ def convert_comp_op(
)
else:
# this should be unreachable
raise Exception(f"Unexpected token '{leftcomp.string} {rightcomp.string}'!")
raise ParserSyntaxError(
f"Unexpected token '{leftcomp.string} {rightcomp.string}'!",
lines=config.lines,
raw_line=0,
raw_column=0,
)
@with_production("star_expr", "'*' expr")
@ -493,7 +509,12 @@ def convert_binop(
# Convert all of the operations that have no precedence in a loop
for op, rightexpr in grouper(rightexprs, 2):
if op.string not in BINOP_TOKEN_LUT:
raise Exception(f"Unexpected token '{op.string}'!")
raise ParserSyntaxError(
f"Unexpected token '{op.string}'!",
lines=config.lines,
raw_line=0,
raw_column=0,
)
leftexpr = BinaryOperation(
left=leftexpr,
# pyre-ignore Pyre thinks that the type of the LUT is CSTNode.
@ -540,7 +561,12 @@ def convert_factor(
)
)
else:
raise Exception(f"Unexpected token '{op.string}'!")
raise ParserSyntaxError(
f"Unexpected token '{op.string}'!",
lines=config.lines,
raw_line=0,
raw_column=0,
)
return WithLeadingWhitespace(
UnaryOperation(operator=opnode, expression=factor.value), op.whitespace_before
@ -651,7 +677,7 @@ def convert_atom_expr_trailer(
)
else:
# This is an invalid trailer, so lets give up
raise Exception("Logic error!")
raise CSTLogicError()
return WithLeadingWhitespace(atom, whitespace_before)
@ -870,9 +896,19 @@ def convert_atom_basic(
Imaginary(child.string), child.whitespace_before
)
else:
raise Exception("Unparseable number {child.string}")
raise ParserSyntaxError(
f"Unparseable number {child.string}",
lines=config.lines,
raw_line=0,
raw_column=0,
)
else:
raise Exception(f"Logic error, unexpected token {child.type.name}")
raise ParserSyntaxError(
f"Logic error, unexpected token {child.type.name}",
lines=config.lines,
raw_line=0,
raw_column=0,
)
@with_production("atom_squarebrackets", "'[' [testlist_comp_list] ']'")
@ -1447,7 +1483,7 @@ def convert_arg_assign_comp_for(
if equal.string == ":=":
val = convert_namedexpr_test(config, children)
if not isinstance(val, WithLeadingWhitespace):
raise Exception(
raise TypeError(
f"convert_namedexpr_test returned {val!r}, not WithLeadingWhitespace"
)
return Arg(value=val.value)

View file

@ -6,6 +6,7 @@
from typing import Any, List, Optional, Sequence, Union
from libcst import CSTLogicError
from libcst._exceptions import PartialParserSyntaxError
from libcst._maybe_sentinel import MaybeSentinel
from libcst._nodes.expression import (
@ -121,7 +122,7 @@ def convert_argslist( # noqa: C901
# Example code:
# def fn(*abc, *): ...
# This should be unreachable, the grammar already disallows it.
raise Exception(
raise ValueError(
"Cannot have multiple star ('*') markers in a single argument "
+ "list."
)
@ -136,7 +137,7 @@ def convert_argslist( # noqa: C901
# Example code:
# def fn(foo, /, *, /, bar): ...
# This should be unreachable, the grammar already disallows it.
raise Exception(
raise ValueError(
"Cannot have multiple slash ('/') markers in a single argument "
+ "list."
)
@ -168,7 +169,7 @@ def convert_argslist( # noqa: C901
# Example code:
# def fn(**kwargs, trailing=None)
# This should be unreachable, the grammar already disallows it.
raise Exception("Cannot have any arguments after a kwargs expansion.")
raise ValueError("Cannot have any arguments after a kwargs expansion.")
elif (
isinstance(param.star, str) and param.star == "*" and param.default is None
):
@ -181,7 +182,7 @@ def convert_argslist( # noqa: C901
# Example code:
# def fn(*first, *second): ...
# This should be unreachable, the grammar already disallows it.
raise Exception(
raise ValueError(
"Expected a keyword argument but found a starred positional "
+ "argument expansion."
)
@ -197,13 +198,13 @@ def convert_argslist( # noqa: C901
# Example code:
# def fn(**first, **second)
# This should be unreachable, the grammar already disallows it.
raise Exception(
raise ValueError(
"Multiple starred keyword argument expansions are not allowed in a "
+ "single argument list"
)
else:
# The state machine should never end up here.
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
return current_param

View file

@ -6,7 +6,8 @@
from typing import Any, Dict, List, Optional, Sequence, Tuple, Type
from libcst._exceptions import PartialParserSyntaxError
from libcst import CSTLogicError
from libcst._exceptions import ParserSyntaxError, PartialParserSyntaxError
from libcst._maybe_sentinel import MaybeSentinel
from libcst._nodes.expression import (
Annotation,
@ -283,7 +284,9 @@ def convert_annassign(config: ParserConfig, children: Sequence[Any]) -> Any:
whitespace_after=parse_simple_whitespace(config, equal.whitespace_after),
)
else:
raise Exception("Invalid parser state!")
raise ParserSyntaxError(
"Invalid parser state!", lines=config.lines, raw_line=0, raw_column=0
)
return AnnAssignPartial(
annotation=Annotation(
@ -319,7 +322,13 @@ def convert_annassign(config: ParserConfig, children: Sequence[Any]) -> Any:
def convert_augassign(config: ParserConfig, children: Sequence[Any]) -> Any:
op, expr = children
if op.string not in AUGOP_TOKEN_LUT:
raise Exception(f"Unexpected token '{op.string}'!")
raise ParserSyntaxError(
f"Unexpected token '{op.string}'!",
lines=config.lines,
raw_line=0,
raw_column=0,
)
return AugAssignPartial(
# pyre-ignore Pyre seems to think that the value of this LUT is CSTNode
operator=AUGOP_TOKEN_LUT[op.string](
@ -447,7 +456,7 @@ def convert_import_relative(config: ParserConfig, children: Sequence[Any]) -> An
# This should be the dotted name, and we can't get more than
# one, but lets be sure anyway
if dotted_name is not None:
raise Exception("Logic error!")
raise CSTLogicError()
dotted_name = child
return ImportRelativePartial(relative=tuple(dots), module=dotted_name)
@ -644,7 +653,7 @@ def convert_raise_stmt(config: ParserConfig, children: Sequence[Any]) -> Any:
item=source.value,
)
else:
raise Exception("Logic error!")
raise CSTLogicError()
return WithLeadingWhitespace(
Raise(whitespace_after_raise=whitespace_after_raise, exc=exc, cause=cause),
@ -893,7 +902,7 @@ def convert_try_stmt(config: ParserConfig, children: Sequence[Any]) -> Any:
if isinstance(clause, Token):
if clause.string == "else":
if orelse is not None:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
orelse = Else(
leading_lines=parse_empty_lines(config, clause.whitespace_before),
whitespace_before_colon=parse_simple_whitespace(
@ -903,7 +912,7 @@ def convert_try_stmt(config: ParserConfig, children: Sequence[Any]) -> Any:
)
elif clause.string == "finally":
if finalbody is not None:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
finalbody = Finally(
leading_lines=parse_empty_lines(config, clause.whitespace_before),
whitespace_before_colon=parse_simple_whitespace(
@ -912,7 +921,7 @@ def convert_try_stmt(config: ParserConfig, children: Sequence[Any]) -> Any:
body=suite,
)
else:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
elif isinstance(clause, ExceptClausePartial):
handlers.append(
ExceptHandler(
@ -927,7 +936,7 @@ def convert_try_stmt(config: ParserConfig, children: Sequence[Any]) -> Any:
)
)
else:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
return Try(
leading_lines=parse_empty_lines(config, trytoken.whitespace_before),
@ -1333,7 +1342,7 @@ def convert_asyncable_stmt(config: ParserConfig, children: Sequence[Any]) -> Any
asynchronous=asyncnode, leading_lines=leading_lines
)
else:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
@with_production("suite", "simple_stmt_suite | indented_suite")

View file

@ -9,7 +9,6 @@ parser. A parser entrypoint should take the source code and some configuration
information
"""
import os
from functools import partial
from typing import Union
@ -17,19 +16,12 @@ from libcst._nodes.base import CSTNode
from libcst._nodes.expression import BaseExpression
from libcst._nodes.module import Module
from libcst._nodes.statement import BaseCompoundStatement, SimpleStatementLine
from libcst._parser.detect_config import convert_to_utf8, detect_config
from libcst._parser.grammar import get_grammar, validate_grammar
from libcst._parser.python_parser import PythonCSTParser
from libcst._parser.detect_config import convert_to_utf8
from libcst._parser.types.config import PartialParserConfig
_DEFAULT_PARTIAL_PARSER_CONFIG: PartialParserConfig = PartialParserConfig()
def is_native() -> bool:
typ = os.environ.get("LIBCST_PARSER_TYPE", None)
return typ == "native"
def _parse(
entrypoint: str,
source: Union[str, bytes],
@ -38,57 +30,21 @@ def _parse(
detect_trailing_newline: bool,
detect_default_newline: bool,
) -> CSTNode:
if is_native():
from libcst.native import parse_expression, parse_module, parse_statement
encoding, source_str = convert_to_utf8(source, partial=config)
encoding, source_str = convert_to_utf8(source, partial=config)
if entrypoint == "file_input":
parse = partial(parse_module, encoding=encoding)
elif entrypoint == "stmt_input":
parse = parse_statement
elif entrypoint == "expression_input":
parse = parse_expression
else:
raise ValueError(f"Unknown parser entry point: {entrypoint}")
from libcst import native
return parse(source_str)
return _pure_python_parse(
entrypoint,
source,
config,
detect_trailing_newline=detect_trailing_newline,
detect_default_newline=detect_default_newline,
)
if entrypoint == "file_input":
parse = partial(native.parse_module, encoding=encoding)
elif entrypoint == "stmt_input":
parse = native.parse_statement
elif entrypoint == "expression_input":
parse = native.parse_expression
else:
raise ValueError(f"Unknown parser entry point: {entrypoint}")
def _pure_python_parse(
entrypoint: str,
source: Union[str, bytes],
config: PartialParserConfig,
*,
detect_trailing_newline: bool,
detect_default_newline: bool,
) -> CSTNode:
detection_result = detect_config(
source,
partial=config,
detect_trailing_newline=detect_trailing_newline,
detect_default_newline=detect_default_newline,
)
validate_grammar()
grammar = get_grammar(config.parsed_python_version, config.future_imports)
parser = PythonCSTParser(
tokens=detection_result.tokens,
config=detection_result.config,
pgen_grammar=grammar,
start_nonterminal=entrypoint,
)
# The parser has an Any return type, we can at least refine it to CSTNode here.
result = parser.parse()
assert isinstance(result, CSTNode)
return result
return parse(source_str)
def parse_module(

View file

@ -319,7 +319,7 @@ def validate_grammar() -> None:
production_name = fn_productions[0].name
expected_name = f"convert_{production_name}"
if fn.__name__ != expected_name:
raise Exception(
raise ValueError(
f"The conversion function for '{production_name}' "
+ f"must be called '{expected_name}', not '{fn.__name__}'."
)
@ -330,7 +330,7 @@ def _get_version_comparison(version: str) -> Tuple[str, PythonVersionInfo]:
return (version[:2], parse_version_string(version[2:].strip()))
if version[:1] in (">", "<"):
return (version[:1], parse_version_string(version[1:].strip()))
raise Exception(f"Invalid version comparison specifier '{version}'")
raise ValueError(f"Invalid version comparison specifier '{version}'")
def _compare_versions(
@ -350,7 +350,7 @@ def _compare_versions(
return actual_version > requested_version
if comparison == "<":
return actual_version < requested_version
raise Exception(f"Invalid version comparison specifier '{comparison}'")
raise ValueError(f"Invalid version comparison specifier '{comparison}'")
def _should_include(
@ -405,7 +405,7 @@ def get_nonterminal_conversions(
if not _should_include_future(fn_production.future, future_imports):
continue
if fn_production.name in conversions:
raise Exception(
raise ValueError(
f"Found duplicate '{fn_production.name}' production in grammar"
)
conversions[fn_production.name] = fn

View file

@ -72,9 +72,9 @@ class DFAState(Generic[_TokenTypeT]):
def __init__(self, from_rule: str, nfa_set: Set[NFAState], final: NFAState) -> None:
self.from_rule = from_rule
self.nfa_set = nfa_set
self.arcs: Mapping[
str, DFAState
] = {} # map from terminals/nonterminals to DFAState
self.arcs: Mapping[str, DFAState] = (
{}
) # map from terminals/nonterminals to DFAState
# In an intermediary step we set these nonterminal arcs (which has the
# same structure as arcs). These don't contain terminals anymore.
self.nonterminal_arcs: Mapping[str, DFAState] = {}
@ -259,7 +259,7 @@ def generate_grammar(bnf_grammar: str, token_namespace: Any) -> Grammar[Any]:
_calculate_tree_traversal(rule_to_dfas)
if start_nonterminal is None:
raise Exception("could not find starting nonterminal!")
raise ValueError("could not find starting nonterminal!")
return Grammar(start_nonterminal, rule_to_dfas, reserved_strings)

View file

@ -27,7 +27,7 @@ try:
ERROR_DEDENT: TokenType = native_token_type.ERROR_DEDENT
except ImportError:
from libcst._parser.parso.python.py_token import ( # noqa F401
from libcst._parser.parso.python.py_token import ( # noqa: F401
PythonTokenTypes,
TokenType,
)

View file

@ -36,6 +36,7 @@ from collections import namedtuple
from dataclasses import dataclass
from typing import Dict, Generator, Iterable, Optional, Pattern, Set, Tuple
from libcst import CSTLogicError
from libcst._parser.parso.python.token import PythonTokenTypes
from libcst._parser.parso.utils import PythonVersionInfo, split_lines
@ -522,14 +523,14 @@ def _tokenize_lines_py36_or_below( # noqa: C901
if contstr: # continued string
if endprog is None:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
endmatch = endprog.match(line)
if endmatch:
pos = endmatch.end(0)
if contstr_start is None:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
if stashed is not None:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
yield PythonToken(STRING, contstr + line[:pos], contstr_start, prefix)
contstr = ""
contline = None
@ -547,7 +548,7 @@ def _tokenize_lines_py36_or_below( # noqa: C901
)
if string:
if stashed is not None:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
yield PythonToken(
FSTRING_STRING,
string,
@ -572,7 +573,7 @@ def _tokenize_lines_py36_or_below( # noqa: C901
pos += quote_length
if fstring_end_token is not None:
if stashed is not None:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
yield fstring_end_token
continue
@ -885,12 +886,12 @@ def _tokenize_lines_py37_or_above( # noqa: C901
if contstr: # continued string
if endprog is None:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
endmatch = endprog.match(line)
if endmatch:
pos = endmatch.end(0)
if contstr_start is None:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
yield PythonToken(STRING, contstr + line[:pos], contstr_start, prefix)
contstr = ""
contline = None

View file

@ -39,8 +39,8 @@ class ParsoUtilsTest(UnitTest):
# Invalid line breaks
("a\vb", ["a\vb"], False),
("a\vb", ["a\vb"], True),
("\x1C", ["\x1C"], False),
("\x1C", ["\x1C"], True),
("\x1c", ["\x1c"], False),
("\x1c", ["\x1c"], True),
)
)
def test_split_lines(self, string, expected_result, keepends):

View file

@ -29,9 +29,9 @@ from typing import Optional, Sequence, Tuple, Union
_NON_LINE_BREAKS = (
"\v", # Vertical Tabulation 0xB
"\f", # Form Feed 0xC
"\x1C", # File Separator
"\x1D", # Group Separator
"\x1E", # Record Separator
"\x1c", # File Separator
"\x1d", # Group Separator
"\x1e", # Record Separator
"\x85", # Next Line (NEL - Equivalent to CR+LF.
# Used to mark end-of-line on some IBM mainframes.)
"\u2028", # Line Separator

View file

@ -39,7 +39,7 @@ def with_production(
# pyre-ignore: Pyre doesn't think that fn has a __name__ attribute
fn_name = fn.__name__
if not fn_name.startswith("convert_"):
raise Exception(
raise ValueError(
"A function with a production must be named 'convert_X', not "
+ f"'{fn_name}'."
)

View file

@ -5,6 +5,7 @@
from typing import List, Optional, Sequence, Tuple, Union
from libcst import CSTLogicError, ParserSyntaxError
from libcst._nodes.whitespace import (
Comment,
COMMENT_RE,
@ -103,10 +104,13 @@ def parse_trailing_whitespace(
) -> TrailingWhitespace:
trailing_whitespace = _parse_trailing_whitespace(config, state)
if trailing_whitespace is None:
raise Exception(
raise ParserSyntaxError(
"Internal Error: Failed to parse TrailingWhitespace. This should never "
+ "happen because a TrailingWhitespace is never optional in the grammar, "
+ "so this error should've been caught by parso first."
+ "so this error should've been caught by parso first.",
lines=config.lines,
raw_line=state.line,
raw_column=state.column,
)
return trailing_whitespace
@ -177,7 +181,9 @@ def _parse_indent(
if state.column == len(line_str) and state.line == len(config.lines):
# We're at EOF, treat this as a failed speculative parse
return False
raise Exception("Internal Error: Column should be 0 when parsing an indent.")
raise CSTLogicError(
"Internal Error: Column should be 0 when parsing an indent."
)
if line_str.startswith(absolute_indent, state.column):
state.column += len(absolute_indent)
return True
@ -206,7 +212,12 @@ def _parse_newline(
newline_str = newline_match.group(0)
state.column += len(newline_str)
if state.column != len(line_str):
raise Exception("Internal Error: Found a newline, but it wasn't the EOL.")
raise ParserSyntaxError(
"Internal Error: Found a newline, but it wasn't the EOL.",
lines=config.lines,
raw_line=state.line,
raw_column=state.column,
)
if state.line < len(config.lines):
# this newline was the end of a line, and there's another line,
# therefore we should move to the next line

View file

@ -10,7 +10,6 @@ from unittest.mock import patch
import libcst as cst
from libcst._nodes.base import CSTValidationError
from libcst._parser.entrypoints import is_native
from libcst.testing.utils import data_provider, UnitTest
@ -174,12 +173,9 @@ class ParseErrorsTest(UnitTest):
parse_fn()
# make sure str() doesn't blow up
self.assertIn("Syntax Error", str(cm.exception))
if not is_native():
self.assertEqual(str(cm.exception), expected)
def test_native_fallible_into_py(self) -> None:
with patch("libcst._nodes.expression.Name._validate") as await_validate:
await_validate.side_effect = CSTValidationError("validate is broken")
with self.assertRaises(Exception) as e:
with self.assertRaises((SyntaxError, cst.ParserSyntaxError)):
cst.parse_module("foo")
self.assertIsInstance(e.exception, (SyntaxError, cst.ParserSyntaxError))

View file

@ -27,9 +27,9 @@ except ImportError:
BaseWhitespaceParserConfig = config_mod.BaseWhitespaceParserConfig
ParserConfig = config_mod.ParserConfig
parser_config_asdict: Callable[
[ParserConfig], Mapping[str, Any]
] = config_mod.parser_config_asdict
parser_config_asdict: Callable[[ParserConfig], Mapping[str, Any]] = (
config_mod.parser_config_asdict
)
class AutoConfig(Enum):

View file

@ -9,4 +9,4 @@ try:
Token = tokenize.Token
except ImportError:
from libcst._parser.types.py_token import Token # noqa F401
from libcst._parser.types.py_token import Token # noqa: F401

View file

@ -40,12 +40,10 @@ class CodeRange:
end: CodePosition
@overload
def __init__(self, start: CodePosition, end: CodePosition) -> None:
...
def __init__(self, start: CodePosition, end: CodePosition) -> None: ...
@overload
def __init__(self, start: Tuple[int, int], end: Tuple[int, int]) -> None:
...
def __init__(self, start: Tuple[int, int], end: Tuple[int, int]) -> None: ...
def __init__(self, start: _CodePositionT, end: _CodePositionT) -> None:
if isinstance(start, tuple) and isinstance(end, tuple):

View file

@ -5,17 +5,20 @@
from typing import (
Any,
ClassVar,
ForwardRef,
get_args,
get_origin,
Iterable,
Literal,
Mapping,
MutableMapping,
MutableSequence,
Tuple,
TypeVar,
Union,
)
from typing_extensions import Literal
from typing_inspect import get_args, get_origin, is_classvar, is_typevar, is_union_type
def is_value_of_type( # noqa: C901 "too complex"
# pyre-fixme[2]: Parameter annotation cannot be `Any`.
@ -48,11 +51,11 @@ def is_value_of_type( # noqa: C901 "too complex"
- Forward Refs -- use `typing.get_type_hints` to resolve these
- Type[...]
"""
if is_classvar(expected_type):
if expected_type is ClassVar or get_origin(expected_type) is ClassVar:
classvar_args = get_args(expected_type)
expected_type = (classvar_args[0] or Any) if classvar_args else Any
if is_typevar(expected_type):
if type(expected_type) is TypeVar:
# treat this the same as Any
# TODO: evaluate bounds
return True
@ -62,13 +65,13 @@ def is_value_of_type( # noqa: C901 "too complex"
if expected_origin_type == Any:
return True
elif is_union_type(expected_type):
elif expected_type is Union or get_origin(expected_type) is Union:
return any(
is_value_of_type(value, subtype) for subtype in expected_type.__args__
)
elif isinstance(expected_origin_type, type(Literal)):
literal_values = get_args(expected_type, evaluate=True)
literal_values = get_args(expected_type)
return any(value == literal for literal in literal_values)
elif isinstance(expected_origin_type, ForwardRef):
@ -82,14 +85,11 @@ def is_value_of_type( # noqa: C901 "too complex"
if not isinstance(value, tuple):
return False
type_args = get_args(expected_type, evaluate=True)
type_args = get_args(expected_type)
if len(type_args) == 0:
# `Tuple` (no subscript) is implicitly `Tuple[Any, ...]`
return True
if type_args is None:
return True
if len(value) != len(type_args):
return False
# TODO: Handle `Tuple[T, ...]` like `Iterable[T]`
@ -106,7 +106,7 @@ def is_value_of_type( # noqa: C901 "too complex"
if not issubclass(type(value), expected_origin_type):
return False
type_args = get_args(expected_type, evaluate=True)
type_args = get_args(expected_type)
if len(type_args) == 0:
# `Mapping` (no subscript) is implicitly `Mapping[Any, Any]`.
return True
@ -143,7 +143,7 @@ def is_value_of_type( # noqa: C901 "too complex"
if not issubclass(type(value), expected_origin_type):
return False
type_args = get_args(expected_type, evaluate=True)
type_args = get_args(expected_type)
if len(type_args) == 0:
# `Iterable` (no subscript) is implicitly `Iterable[Any]`.
return True

View file

@ -25,6 +25,7 @@ if TYPE_CHECKING:
BaseExpression,
BaseFormattedStringContent,
BaseSlice,
BaseTemplatedStringContent,
BinaryOperation,
BooleanOperation,
Call,
@ -71,6 +72,9 @@ if TYPE_CHECKING:
StarredElement,
Subscript,
SubscriptElement,
TemplatedString,
TemplatedStringExpression,
TemplatedStringText,
Tuple,
UnaryOperation,
Yield,
@ -178,6 +182,7 @@ if TYPE_CHECKING:
MatchValue,
NameItem,
Nonlocal,
ParamSpec,
Pass,
Raise,
Return,
@ -185,6 +190,11 @@ if TYPE_CHECKING:
SimpleStatementSuite,
Try,
TryStar,
TypeAlias,
TypeParam,
TypeParameters,
TypeVar,
TypeVarTuple,
While,
With,
WithItem,
@ -201,6 +211,7 @@ if TYPE_CHECKING:
class CSTTypedBaseFunctions:
@mark_no_op
def visit_Add(self, node: "Add") -> Optional[bool]:
pass
@ -1053,6 +1064,22 @@ class CSTTypedBaseFunctions:
def leave_ClassDef_whitespace_before_colon(self, node: "ClassDef") -> None:
pass
@mark_no_op
def visit_ClassDef_type_parameters(self, node: "ClassDef") -> None:
pass
@mark_no_op
def leave_ClassDef_type_parameters(self, node: "ClassDef") -> None:
pass
@mark_no_op
def visit_ClassDef_whitespace_after_type_parameters(self, node: "ClassDef") -> None:
pass
@mark_no_op
def leave_ClassDef_whitespace_after_type_parameters(self, node: "ClassDef") -> None:
pass
@mark_no_op
def visit_Colon(self, node: "Colon") -> Optional[bool]:
pass
@ -2339,6 +2366,26 @@ class CSTTypedBaseFunctions:
def leave_FunctionDef_whitespace_before_colon(self, node: "FunctionDef") -> None:
pass
@mark_no_op
def visit_FunctionDef_type_parameters(self, node: "FunctionDef") -> None:
pass
@mark_no_op
def leave_FunctionDef_type_parameters(self, node: "FunctionDef") -> None:
pass
@mark_no_op
def visit_FunctionDef_whitespace_after_type_parameters(
self, node: "FunctionDef"
) -> None:
pass
@mark_no_op
def leave_FunctionDef_whitespace_after_type_parameters(
self, node: "FunctionDef"
) -> None:
pass
@mark_no_op
def visit_GeneratorExp(self, node: "GeneratorExp") -> Optional[bool]:
pass
@ -4307,6 +4354,34 @@ class CSTTypedBaseFunctions:
def leave_ParamSlash_comma(self, node: "ParamSlash") -> None:
pass
@mark_no_op
def visit_ParamSlash_whitespace_after(self, node: "ParamSlash") -> None:
pass
@mark_no_op
def leave_ParamSlash_whitespace_after(self, node: "ParamSlash") -> None:
pass
@mark_no_op
def visit_ParamSpec(self, node: "ParamSpec") -> Optional[bool]:
pass
@mark_no_op
def visit_ParamSpec_name(self, node: "ParamSpec") -> None:
pass
@mark_no_op
def leave_ParamSpec_name(self, node: "ParamSpec") -> None:
pass
@mark_no_op
def visit_ParamSpec_whitespace_after_star(self, node: "ParamSpec") -> None:
pass
@mark_no_op
def leave_ParamSpec_whitespace_after_star(self, node: "ParamSpec") -> None:
pass
@mark_no_op
def visit_ParamStar(self, node: "ParamStar") -> Optional[bool]:
pass
@ -5111,6 +5186,140 @@ class CSTTypedBaseFunctions:
def leave_SubtractAssign_whitespace_after(self, node: "SubtractAssign") -> None:
pass
@mark_no_op
def visit_TemplatedString(self, node: "TemplatedString") -> Optional[bool]:
pass
@mark_no_op
def visit_TemplatedString_parts(self, node: "TemplatedString") -> None:
pass
@mark_no_op
def leave_TemplatedString_parts(self, node: "TemplatedString") -> None:
pass
@mark_no_op
def visit_TemplatedString_start(self, node: "TemplatedString") -> None:
pass
@mark_no_op
def leave_TemplatedString_start(self, node: "TemplatedString") -> None:
pass
@mark_no_op
def visit_TemplatedString_end(self, node: "TemplatedString") -> None:
pass
@mark_no_op
def leave_TemplatedString_end(self, node: "TemplatedString") -> None:
pass
@mark_no_op
def visit_TemplatedString_lpar(self, node: "TemplatedString") -> None:
pass
@mark_no_op
def leave_TemplatedString_lpar(self, node: "TemplatedString") -> None:
pass
@mark_no_op
def visit_TemplatedString_rpar(self, node: "TemplatedString") -> None:
pass
@mark_no_op
def leave_TemplatedString_rpar(self, node: "TemplatedString") -> None:
pass
@mark_no_op
def visit_TemplatedStringExpression(
self, node: "TemplatedStringExpression"
) -> Optional[bool]:
pass
@mark_no_op
def visit_TemplatedStringExpression_expression(
self, node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def leave_TemplatedStringExpression_expression(
self, node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def visit_TemplatedStringExpression_conversion(
self, node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def leave_TemplatedStringExpression_conversion(
self, node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def visit_TemplatedStringExpression_format_spec(
self, node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def leave_TemplatedStringExpression_format_spec(
self, node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def visit_TemplatedStringExpression_whitespace_before_expression(
self, node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def leave_TemplatedStringExpression_whitespace_before_expression(
self, node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def visit_TemplatedStringExpression_whitespace_after_expression(
self, node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def leave_TemplatedStringExpression_whitespace_after_expression(
self, node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def visit_TemplatedStringExpression_equal(
self, node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def leave_TemplatedStringExpression_equal(
self, node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def visit_TemplatedStringText(self, node: "TemplatedStringText") -> Optional[bool]:
pass
@mark_no_op
def visit_TemplatedStringText_value(self, node: "TemplatedStringText") -> None:
pass
@mark_no_op
def leave_TemplatedStringText_value(self, node: "TemplatedStringText") -> None:
pass
@mark_no_op
def visit_TrailingWhitespace(self, node: "TrailingWhitespace") -> Optional[bool]:
pass
@ -5271,6 +5480,206 @@ class CSTTypedBaseFunctions:
def leave_Tuple_rpar(self, node: "Tuple") -> None:
pass
@mark_no_op
def visit_TypeAlias(self, node: "TypeAlias") -> Optional[bool]:
pass
@mark_no_op
def visit_TypeAlias_name(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def leave_TypeAlias_name(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def visit_TypeAlias_value(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def leave_TypeAlias_value(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def visit_TypeAlias_type_parameters(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def leave_TypeAlias_type_parameters(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def visit_TypeAlias_whitespace_after_type(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def leave_TypeAlias_whitespace_after_type(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def visit_TypeAlias_whitespace_after_name(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def leave_TypeAlias_whitespace_after_name(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def visit_TypeAlias_whitespace_after_type_parameters(
self, node: "TypeAlias"
) -> None:
pass
@mark_no_op
def leave_TypeAlias_whitespace_after_type_parameters(
self, node: "TypeAlias"
) -> None:
pass
@mark_no_op
def visit_TypeAlias_whitespace_after_equals(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def leave_TypeAlias_whitespace_after_equals(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def visit_TypeAlias_semicolon(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def leave_TypeAlias_semicolon(self, node: "TypeAlias") -> None:
pass
@mark_no_op
def visit_TypeParam(self, node: "TypeParam") -> Optional[bool]:
pass
@mark_no_op
def visit_TypeParam_param(self, node: "TypeParam") -> None:
pass
@mark_no_op
def leave_TypeParam_param(self, node: "TypeParam") -> None:
pass
@mark_no_op
def visit_TypeParam_comma(self, node: "TypeParam") -> None:
pass
@mark_no_op
def leave_TypeParam_comma(self, node: "TypeParam") -> None:
pass
@mark_no_op
def visit_TypeParam_equal(self, node: "TypeParam") -> None:
pass
@mark_no_op
def leave_TypeParam_equal(self, node: "TypeParam") -> None:
pass
@mark_no_op
def visit_TypeParam_star(self, node: "TypeParam") -> None:
pass
@mark_no_op
def leave_TypeParam_star(self, node: "TypeParam") -> None:
pass
@mark_no_op
def visit_TypeParam_whitespace_after_star(self, node: "TypeParam") -> None:
pass
@mark_no_op
def leave_TypeParam_whitespace_after_star(self, node: "TypeParam") -> None:
pass
@mark_no_op
def visit_TypeParam_default(self, node: "TypeParam") -> None:
pass
@mark_no_op
def leave_TypeParam_default(self, node: "TypeParam") -> None:
pass
@mark_no_op
def visit_TypeParameters(self, node: "TypeParameters") -> Optional[bool]:
pass
@mark_no_op
def visit_TypeParameters_params(self, node: "TypeParameters") -> None:
pass
@mark_no_op
def leave_TypeParameters_params(self, node: "TypeParameters") -> None:
pass
@mark_no_op
def visit_TypeParameters_lbracket(self, node: "TypeParameters") -> None:
pass
@mark_no_op
def leave_TypeParameters_lbracket(self, node: "TypeParameters") -> None:
pass
@mark_no_op
def visit_TypeParameters_rbracket(self, node: "TypeParameters") -> None:
pass
@mark_no_op
def leave_TypeParameters_rbracket(self, node: "TypeParameters") -> None:
pass
@mark_no_op
def visit_TypeVar(self, node: "TypeVar") -> Optional[bool]:
pass
@mark_no_op
def visit_TypeVar_name(self, node: "TypeVar") -> None:
pass
@mark_no_op
def leave_TypeVar_name(self, node: "TypeVar") -> None:
pass
@mark_no_op
def visit_TypeVar_bound(self, node: "TypeVar") -> None:
pass
@mark_no_op
def leave_TypeVar_bound(self, node: "TypeVar") -> None:
pass
@mark_no_op
def visit_TypeVar_colon(self, node: "TypeVar") -> None:
pass
@mark_no_op
def leave_TypeVar_colon(self, node: "TypeVar") -> None:
pass
@mark_no_op
def visit_TypeVarTuple(self, node: "TypeVarTuple") -> Optional[bool]:
pass
@mark_no_op
def visit_TypeVarTuple_name(self, node: "TypeVarTuple") -> None:
pass
@mark_no_op
def leave_TypeVarTuple_name(self, node: "TypeVarTuple") -> None:
pass
@mark_no_op
def visit_TypeVarTuple_whitespace_after_star(self, node: "TypeVarTuple") -> None:
pass
@mark_no_op
def leave_TypeVarTuple_whitespace_after_star(self, node: "TypeVarTuple") -> None:
pass
@mark_no_op
def visit_UnaryOperation(self, node: "UnaryOperation") -> Optional[bool]:
pass
@ -5493,6 +5902,7 @@ class CSTTypedBaseFunctions:
class CSTTypedVisitorFunctions(CSTTypedBaseFunctions):
@mark_no_op
def leave_Add(self, original_node: "Add") -> None:
pass
@ -5995,6 +6405,10 @@ class CSTTypedVisitorFunctions(CSTTypedBaseFunctions):
def leave_ParamSlash(self, original_node: "ParamSlash") -> None:
pass
@mark_no_op
def leave_ParamSpec(self, original_node: "ParamSpec") -> None:
pass
@mark_no_op
def leave_ParamStar(self, original_node: "ParamStar") -> None:
pass
@ -6109,6 +6523,20 @@ class CSTTypedVisitorFunctions(CSTTypedBaseFunctions):
def leave_SubtractAssign(self, original_node: "SubtractAssign") -> None:
pass
@mark_no_op
def leave_TemplatedString(self, original_node: "TemplatedString") -> None:
pass
@mark_no_op
def leave_TemplatedStringExpression(
self, original_node: "TemplatedStringExpression"
) -> None:
pass
@mark_no_op
def leave_TemplatedStringText(self, original_node: "TemplatedStringText") -> None:
pass
@mark_no_op
def leave_TrailingWhitespace(self, original_node: "TrailingWhitespace") -> None:
pass
@ -6125,6 +6553,26 @@ class CSTTypedVisitorFunctions(CSTTypedBaseFunctions):
def leave_Tuple(self, original_node: "Tuple") -> None:
pass
@mark_no_op
def leave_TypeAlias(self, original_node: "TypeAlias") -> None:
pass
@mark_no_op
def leave_TypeParam(self, original_node: "TypeParam") -> None:
pass
@mark_no_op
def leave_TypeParameters(self, original_node: "TypeParameters") -> None:
pass
@mark_no_op
def leave_TypeVar(self, original_node: "TypeVar") -> None:
pass
@mark_no_op
def leave_TypeVarTuple(self, original_node: "TypeVarTuple") -> None:
pass
@mark_no_op
def leave_UnaryOperation(self, original_node: "UnaryOperation") -> None:
pass
@ -6147,7 +6595,6 @@ class CSTTypedVisitorFunctions(CSTTypedBaseFunctions):
class CSTTypedTransformerFunctions(CSTTypedBaseFunctions):
pass
@mark_no_op
def leave_Add(self, original_node: "Add", updated_node: "Add") -> "BaseBinaryOp":
@ -6925,6 +7372,12 @@ class CSTTypedTransformerFunctions(CSTTypedBaseFunctions):
) -> Union["ParamSlash", MaybeSentinel]:
return updated_node
@mark_no_op
def leave_ParamSpec(
self, original_node: "ParamSpec", updated_node: "ParamSpec"
) -> "ParamSpec":
return updated_node
@mark_no_op
def leave_ParamStar(
self, original_node: "ParamStar", updated_node: "ParamStar"
@ -7101,6 +7554,34 @@ class CSTTypedTransformerFunctions(CSTTypedBaseFunctions):
) -> "BaseAugOp":
return updated_node
@mark_no_op
def leave_TemplatedString(
self, original_node: "TemplatedString", updated_node: "TemplatedString"
) -> "BaseExpression":
return updated_node
@mark_no_op
def leave_TemplatedStringExpression(
self,
original_node: "TemplatedStringExpression",
updated_node: "TemplatedStringExpression",
) -> Union[
"BaseTemplatedStringContent",
FlattenSentinel["BaseTemplatedStringContent"],
RemovalSentinel,
]:
return updated_node
@mark_no_op
def leave_TemplatedStringText(
self, original_node: "TemplatedStringText", updated_node: "TemplatedStringText"
) -> Union[
"BaseTemplatedStringContent",
FlattenSentinel["BaseTemplatedStringContent"],
RemovalSentinel,
]:
return updated_node
@mark_no_op
def leave_TrailingWhitespace(
self, original_node: "TrailingWhitespace", updated_node: "TrailingWhitespace"
@ -7125,6 +7606,38 @@ class CSTTypedTransformerFunctions(CSTTypedBaseFunctions):
) -> "BaseExpression":
return updated_node
@mark_no_op
def leave_TypeAlias(
self, original_node: "TypeAlias", updated_node: "TypeAlias"
) -> Union[
"BaseSmallStatement", FlattenSentinel["BaseSmallStatement"], RemovalSentinel
]:
return updated_node
@mark_no_op
def leave_TypeParam(
self, original_node: "TypeParam", updated_node: "TypeParam"
) -> Union["TypeParam", FlattenSentinel["TypeParam"], RemovalSentinel]:
return updated_node
@mark_no_op
def leave_TypeParameters(
self, original_node: "TypeParameters", updated_node: "TypeParameters"
) -> "TypeParameters":
return updated_node
@mark_no_op
def leave_TypeVar(
self, original_node: "TypeVar", updated_node: "TypeVar"
) -> "TypeVar":
return updated_node
@mark_no_op
def leave_TypeVarTuple(
self, original_node: "TypeVarTuple", updated_node: "TypeVarTuple"
) -> "TypeVarTuple":
return updated_node
@mark_no_op
def leave_UnaryOperation(
self, original_node: "UnaryOperation", updated_node: "UnaryOperation"

View file

@ -3,10 +3,8 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from typing import Any, Callable, cast, TYPE_CHECKING, TypeVar
from typing import Any, Callable, cast, TypeVar
if TYPE_CHECKING:
from libcst._typed_visitor import CSTTypedBaseFunctions # noqa: F401
# pyre-fixme[24]: Generic type `Callable` expects 2 type parameters.
F = TypeVar("F", bound=Callable)

View file

@ -4,7 +4,8 @@
# LICENSE file in the root directory of this source tree.
from typing import TYPE_CHECKING, TypeVar
from pathlib import PurePath
from typing import TYPE_CHECKING, TypeVar, Union
if TYPE_CHECKING:
from libcst._nodes.base import CSTNode # noqa: F401
@ -12,3 +13,4 @@ if TYPE_CHECKING:
CSTNodeT = TypeVar("CSTNodeT", bound="CSTNode")
CSTNodeT_co = TypeVar("CSTNodeT_co", bound="CSTNode", covariant=True)
StrPath = Union[str, PurePath]

View file

@ -7,12 +7,12 @@ import inspect
from collections import defaultdict
from collections.abc import Sequence as ABCSequence
from dataclasses import dataclass, fields, replace
from typing import Dict, Generator, List, Mapping, Sequence, Set, Type, Union
from typing import Dict, Iterator, List, Mapping, Sequence, Set, Type, Union
import libcst as cst
def _get_bases() -> Generator[Type[cst.CSTNode], None, None]:
def _get_bases() -> Iterator[Type[cst.CSTNode]]:
"""
Get all base classes that are subclasses of CSTNode but not an actual
node itself. This allows us to keep our types sane by refering to the
@ -27,11 +27,11 @@ def _get_bases() -> Generator[Type[cst.CSTNode], None, None]:
typeclasses: Sequence[Type[cst.CSTNode]] = sorted(
list(_get_bases()), key=lambda base: base.__name__
_get_bases(), key=lambda base: base.__name__
)
def _get_nodes() -> Generator[Type[cst.CSTNode], None, None]:
def _get_nodes() -> Iterator[Type[cst.CSTNode]]:
"""
Grab all CSTNodes that are not a superclass. Basically, anything that a
person might use to generate a tree.
@ -53,7 +53,7 @@ def _get_nodes() -> Generator[Type[cst.CSTNode], None, None]:
all_libcst_nodes: Sequence[Type[cst.CSTNode]] = sorted(
list(_get_nodes()), key=lambda node: node.__name__
_get_nodes(), key=lambda node: node.__name__
)
node_to_bases: Dict[Type[cst.CSTNode], List[Type[cst.CSTNode]]] = {}
for node in all_libcst_nodes:

View file

@ -8,7 +8,7 @@ from dataclasses import dataclass, fields
from typing import Generator, List, Optional, Sequence, Set, Tuple, Type, Union
import libcst as cst
from libcst import ensure_type, parse_expression
from libcst import CSTLogicError, ensure_type, parse_expression
from libcst.codegen.gather import all_libcst_nodes, typeclasses
CST_DIR: Set[str] = set(dir(cst))
@ -16,6 +16,109 @@ CLASS_RE = r"<class \'(.*?)\'>"
OPTIONAL_RE = r"typing\.Union\[([^,]*?), NoneType]"
class NormalizeUnions(cst.CSTTransformer):
"""
Convert a binary operation with | operators into a Union type.
For example, converts `foo | bar | baz` into `typing.Union[foo, bar, baz]`.
Special case: converts `foo | None` or `None | foo` into `typing.Optional[foo]`.
Also flattens nested typing.Union types.
"""
def leave_Subscript(
self, original_node: cst.Subscript, updated_node: cst.Subscript
) -> cst.Subscript:
# Check if this is a typing.Union
if (
isinstance(updated_node.value, cst.Attribute)
and isinstance(updated_node.value.value, cst.Name)
and updated_node.value.attr.value == "Union"
and updated_node.value.value.value == "typing"
):
# Collect all operands from any nested Unions
operands: List[cst.BaseExpression] = []
for slc in updated_node.slice:
if not isinstance(slc.slice, cst.Index):
continue
value = slc.slice.value
# If this is a nested Union, add its elements
if (
isinstance(value, cst.Subscript)
and isinstance(value.value, cst.Attribute)
and isinstance(value.value.value, cst.Name)
and value.value.attr.value == "Union"
and value.value.value.value == "typing"
):
operands.extend(
nested_slc.slice.value
for nested_slc in value.slice
if isinstance(nested_slc.slice, cst.Index)
)
else:
operands.append(value)
# flatten operands into a Union type
return cst.Subscript(
cst.Attribute(cst.Name("typing"), cst.Name("Union")),
[cst.SubscriptElement(cst.Index(operand)) for operand in operands],
)
return updated_node
def leave_BinaryOperation(
self, original_node: cst.BinaryOperation, updated_node: cst.BinaryOperation
) -> Union[cst.BinaryOperation, cst.Subscript]:
if not updated_node.operator.deep_equals(cst.BitOr()):
return updated_node
def flatten_binary_op(node: cst.BaseExpression) -> List[cst.BaseExpression]:
"""Flatten a binary operation tree into a list of operands."""
if not isinstance(node, cst.BinaryOperation):
# If it's a Union type, extract its elements
if (
isinstance(node, cst.Subscript)
and isinstance(node.value, cst.Attribute)
and isinstance(node.value.value, cst.Name)
and node.value.attr.value == "Union"
and node.value.value.value == "typing"
):
return [
slc.slice.value
for slc in node.slice
if isinstance(slc.slice, cst.Index)
]
return [node]
if not node.operator.deep_equals(cst.BitOr()):
return [node]
left_operands = flatten_binary_op(node.left)
right_operands = flatten_binary_op(node.right)
return left_operands + right_operands
# Flatten the binary operation tree into a list of operands
operands = flatten_binary_op(updated_node)
# Check for Optional case (None in union)
none_count = sum(
1 for op in operands if isinstance(op, cst.Name) and op.value == "None"
)
if none_count == 1 and len(operands) == 2:
# This is an Optional case - find the non-None operand
non_none = next(
op
for op in operands
if not (isinstance(op, cst.Name) and op.value == "None")
)
return cst.Subscript(
cst.Attribute(cst.Name("typing"), cst.Name("Optional")),
[cst.SubscriptElement(cst.Index(non_none))],
)
# Regular Union case
return cst.Subscript(
cst.Attribute(cst.Name("typing"), cst.Name("Union")),
[cst.SubscriptElement(cst.Index(operand)) for operand in operands],
)
class CleanseFullTypeNames(cst.CSTTransformer):
def leave_Call(
self, original_node: cst.Call, updated_node: cst.Call
@ -180,9 +283,9 @@ class AddWildcardsToSequenceUnions(cst.CSTTransformer):
# type blocks, even for sequence types.
return
if len(node.slice) != 1:
raise Exception(
raise ValueError(
"Unexpected number of sequence elements inside Sequence type "
+ "annotation!"
"annotation!"
)
nodeslice = node.slice[0].slice
if isinstance(nodeslice, cst.Index):
@ -265,7 +368,9 @@ def _get_raw_name(node: cst.CSTNode) -> Optional[str]:
if isinstance(node, cst.Name):
return node.value
elif isinstance(node, cst.SimpleString):
return node.evaluated_value
evaluated_value = node.evaluated_value
if isinstance(evaluated_value, str):
return evaluated_value
elif isinstance(node, cst.SubscriptElement):
return _get_raw_name(node.slice)
elif isinstance(node, cst.Index):
@ -344,10 +449,14 @@ def _get_clean_type_from_subscript(
if typecst.value.deep_equals(cst.Name("Sequence")):
# Lets attempt to widen the sequence type and alias it.
if len(typecst.slice) != 1:
raise Exception("Logic error, Sequence shouldn't have more than one param!")
raise CSTLogicError(
"Logic error, Sequence shouldn't have more than one param!"
)
inner_type = typecst.slice[0].slice
if not isinstance(inner_type, cst.Index):
raise Exception("Logic error, expecting Index for only Sequence element!")
raise CSTLogicError(
"Logic error, expecting Index for only Sequence element!"
)
inner_type = inner_type.value
if isinstance(inner_type, cst.Subscript):
@ -355,7 +464,9 @@ def _get_clean_type_from_subscript(
elif isinstance(inner_type, (cst.Name, cst.SimpleString)):
clean_inner_type = _get_clean_type_from_expression(aliases, inner_type)
else:
raise Exception("Logic error, unexpected type in Sequence!")
raise CSTLogicError(
f"Logic error, unexpected type in Sequence: {type(inner_type)}!"
)
return _get_wrapped_union_type(
typecst.deep_replace(inner_type, clean_inner_type),
@ -384,9 +495,12 @@ def _get_clean_type_and_aliases(
typestr = re.sub(OPTIONAL_RE, r"typing.Optional[\1]", typestr)
# Now, parse the expression with LibCST.
cleanser = CleanseFullTypeNames()
typecst = parse_expression(typestr)
typecst = typecst.visit(cleanser)
typecst = typecst.visit(NormalizeUnions())
assert isinstance(typecst, cst.BaseExpression)
typecst = typecst.visit(CleanseFullTypeNames())
assert isinstance(typecst, cst.BaseExpression)
aliases: List[Alias] = []
# Now, convert the type to allow for MetadataMatchType and MatchIfTrue values.
@ -395,7 +509,7 @@ def _get_clean_type_and_aliases(
elif isinstance(typecst, (cst.Name, cst.SimpleString)):
clean_type = _get_clean_type_from_expression(aliases, typecst)
else:
raise Exception("Logic error, unexpected top level type!")
raise CSTLogicError(f"Logic error, unexpected top level type: {type(typecst)}!")
# Now, insert OneOf/AllOf and MatchIfTrue into unions so we can typecheck their usage.
# This allows us to put OneOf[SomeType] or MatchIfTrue[cst.SomeType] into any
@ -441,8 +555,7 @@ generated_code.append("")
generated_code.append("")
generated_code.append("# This file was generated by libcst.codegen.gen_matcher_classes")
generated_code.append("from dataclasses import dataclass")
generated_code.append("from typing import Optional, Sequence, Union")
generated_code.append("from typing_extensions import Literal")
generated_code.append("from typing import Literal, Optional, Sequence, Union")
generated_code.append("import libcst as cst")
generated_code.append("")
generated_code.append(
@ -547,7 +660,7 @@ for node in all_libcst_nodes:
# Make sure to add an __all__ for flake8 and compatibility with "from libcst.matchers import *"
generated_code.append(f"__all__ = {repr(sorted(list(all_exports)))}")
generated_code.append(f"__all__ = {repr(sorted(all_exports))}")
if __name__ == "__main__":

View file

@ -29,7 +29,7 @@ generated_code.append("")
generated_code.append("")
for module, objects in imports.items():
generated_code.append(f"from {module} import (")
generated_code.append(f" {', '.join(sorted(list(objects)))}")
generated_code.append(f" {', '.join(sorted(objects))}")
generated_code.append(")")
# Generate the base visit_ methods

View file

@ -32,7 +32,7 @@ generated_code.append("")
generated_code.append("if TYPE_CHECKING:")
for module, objects in imports.items():
generated_code.append(f" from {module} import ( # noqa: F401")
generated_code.append(f" {', '.join(sorted(list(objects)))}")
generated_code.append(f" {', '.join(sorted(objects))}")
generated_code.append(" )")
@ -87,7 +87,6 @@ for node in sorted(nodebases.keys(), key=lambda node: node.__name__):
generated_code.append("")
generated_code.append("")
generated_code.append("class CSTTypedTransformerFunctions(CSTTypedBaseFunctions):")
generated_code.append(" pass")
for node in sorted(nodebases.keys(), key=lambda node: node.__name__):
name = node.__name__
if name.startswith("Base"):
@ -111,6 +110,7 @@ for node in sorted(nodebases.keys(), key=lambda node: node.__name__):
)
generated_code.append(" return updated_node")
if __name__ == "__main__":
# Output the code
print("\n".join(generated_code))

View file

@ -25,8 +25,11 @@ from libcst.codegen.transforms import (
def format_file(fname: str) -> None:
with open(os.devnull, "w") as devnull:
subprocess.check_call(["ufmt", "format", fname], stdout=devnull, stderr=devnull)
subprocess.check_call(
["ufmt", "format", fname],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
def clean_generated_code(code: str) -> str:
@ -65,12 +68,11 @@ def codegen_visitors() -> None:
# Now, see if the file we generated causes any import errors
# by attempting to run codegen again in a new process.
with open(os.devnull, "w") as devnull:
subprocess.check_call(
["python3", "-m", "libcst.codegen.gen_visitor_functions"],
cwd=base,
stdout=devnull,
)
subprocess.check_call(
[sys.executable, "-m", "libcst.codegen.gen_visitor_functions"],
cwd=base,
stdout=subprocess.DEVNULL,
)
# If it worked, lets format the file
format_file(visitors_file)

View file

@ -3,6 +3,7 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import difflib
import os
import os.path
@ -20,12 +21,20 @@ class TestCodegenClean(UnitTest):
new_code: str,
module_name: str,
) -> None:
self.assertTrue(
old_code == new_code,
f"{module_name} needs new codegen, see "
+ "`python -m libcst.codegen.generate --help` "
+ "for instructions, or run `python -m libcst.codegen.generate all`",
)
if old_code != new_code:
diff = difflib.unified_diff(
old_code.splitlines(keepends=True),
new_code.splitlines(keepends=True),
fromfile="old_code",
tofile="new_code",
)
diff_str = "".join(diff)
self.fail(
f"{module_name} needs new codegen, see "
+ "`python -m libcst.codegen.generate --help` "
+ "for instructions, or run `python -m libcst.codegen.generate all`. "
+ f"Diff:\n{diff_str}"
)
def test_codegen_clean_visitor_functions(self) -> None:
"""
@ -123,3 +132,50 @@ class TestCodegenClean(UnitTest):
# Now that we've done simple codegen, verify that it matches.
self.assert_code_matches(old_code, new_code, "libcst.matchers._return_types")
def test_normalize_unions(self) -> None:
"""
Verifies that NormalizeUnions correctly converts binary operations with |
into Union types, with special handling for Optional cases.
"""
import libcst as cst
from libcst.codegen.gen_matcher_classes import NormalizeUnions
def assert_transforms_to(input_code: str, expected_code: str) -> None:
input_cst = cst.parse_expression(input_code)
expected_cst = cst.parse_expression(expected_code)
result = input_cst.visit(NormalizeUnions())
assert isinstance(
result, cst.BaseExpression
), f"Expected BaseExpression, got {type(result)}"
result_code = cst.Module(body=()).code_for_node(result)
expected_code_str = cst.Module(body=()).code_for_node(expected_cst)
self.assertEqual(
result_code,
expected_code_str,
f"Expected {expected_code_str}, got {result_code}",
)
# Test regular union case
assert_transforms_to("foo | bar | baz", "typing.Union[foo, bar, baz]")
# Test Optional case (None on right)
assert_transforms_to("foo | None", "typing.Optional[foo]")
# Test Optional case (None on left)
assert_transforms_to("None | foo", "typing.Optional[foo]")
# Test case with more than 2 operands including None (should remain Union)
assert_transforms_to("foo | bar | None", "typing.Union[foo, bar, None]")
# Flatten existing Union types
assert_transforms_to(
"typing.Union[foo, typing.Union[bar, baz]]", "typing.Union[foo, bar, baz]"
)
# Merge two kinds of union types
assert_transforms_to(
"foo | typing.Union[bar, baz]", "typing.Union[foo, bar, baz]"
)

View file

@ -8,20 +8,25 @@ Provides helpers for CLI interaction.
"""
import difflib
import functools
import os.path
import re
import subprocess
import sys
import time
import traceback
from dataclasses import dataclass, replace
from multiprocessing import cpu_count, Pool
from concurrent.futures import as_completed, Executor
from copy import deepcopy
from dataclasses import dataclass
from multiprocessing import cpu_count
from pathlib import Path
from typing import Any, AnyStr, cast, Dict, List, Optional, Sequence, Union
from typing import AnyStr, Callable, cast, Dict, List, Optional, Sequence, Type, Union
from warnings import warn
from libcst import parse_module, PartialParserConfig
from libcst.codemod._codemod import Codemod
from libcst.codemod._dummy_pool import DummyPool
from libcst.codemod._context import CodemodContext
from libcst.codemod._dummy_pool import DummyExecutor
from libcst.codemod._runner import (
SkipFile,
SkipReason,
@ -46,7 +51,7 @@ def invoke_formatter(formatter_args: Sequence[str], code: AnyStr) -> AnyStr:
# Make sure there is something to run
if len(formatter_args) == 0:
raise Exception("No formatter configured but code formatting requested.")
raise ValueError("No formatter configured but code formatting requested.")
# Invoke the formatter, giving it the code as stdin and assuming the formatted
# code comes from stdout.
@ -90,7 +95,10 @@ def gather_files(
ret.extend(
str(p)
for p in Path(fd).rglob("*.py*")
if str(p).endswith("py") or (include_stubs and str(p).endswith("pyi"))
if Path.is_file(p)
and (
str(p).endswith("py") or (include_stubs and str(p).endswith("pyi"))
)
)
return sorted(ret)
@ -207,11 +215,52 @@ class ExecutionConfig:
unified_diff: Optional[int] = None
def _execute_transform( # noqa: C901
transformer: Codemod,
def _prepare_context(
repo_root: str,
filename: str,
config: ExecutionConfig,
) -> ExecutionResult:
scratch: Dict[str, object],
repo_manager: Optional[FullRepoManager],
) -> CodemodContext:
# determine the module and package name for this file
try:
module_name_and_package = calculate_module_and_package(repo_root, filename)
mod_name = module_name_and_package.name
pkg_name = module_name_and_package.package
except ValueError as ex:
print(f"Failed to determine module name for {filename}: {ex}", file=sys.stderr)
mod_name = None
pkg_name = None
return CodemodContext(
scratch=scratch,
filename=filename,
full_module_name=mod_name,
full_package_name=pkg_name,
metadata_manager=repo_manager,
)
def _instantiate_transformer(
transformer: Union[Codemod, Type[Codemod]],
repo_root: str,
filename: str,
original_scratch: Dict[str, object],
codemod_kwargs: Dict[str, object],
repo_manager: Optional[FullRepoManager],
) -> Codemod:
if isinstance(transformer, type):
return transformer( # type: ignore
context=_prepare_context(repo_root, filename, {}, repo_manager),
**codemod_kwargs,
)
transformer.context = _prepare_context(
repo_root, filename, deepcopy(original_scratch), repo_manager
)
return transformer
def _check_for_skip(
filename: str, config: ExecutionConfig
) -> Union[ExecutionResult, bytes]:
for pattern in config.blacklist_patterns:
if re.fullmatch(pattern, filename):
return ExecutionResult(
@ -223,48 +272,47 @@ def _execute_transform( # noqa: C901
),
)
try:
with open(filename, "rb") as fp:
oldcode = fp.read()
with open(filename, "rb") as fp:
oldcode = fp.read()
# Skip generated files
if (
not config.include_generated
and config.generated_code_marker.encode("utf-8") in oldcode
):
return ExecutionResult(
filename=filename,
changed=False,
transform_result=TransformSkip(
skip_reason=SkipReason.GENERATED,
skip_description="Generated file.",
),
)
# Somewhat gross hack to provide the filename in the transform's context.
# We do this after the fork so that a context that was initialized with
# some defaults before calling parallel_exec_transform_with_prettyprint
# will be updated per-file.
transformer.context = replace(
transformer.context,
# Skip generated files
if (
not config.include_generated
and config.generated_code_marker.encode("utf-8") in oldcode
):
return ExecutionResult(
filename=filename,
scratch={},
changed=False,
transform_result=TransformSkip(
skip_reason=SkipReason.GENERATED,
skip_description="Generated file.",
),
)
return oldcode
# determine the module and package name for this file
try:
module_name_and_package = calculate_module_and_package(
config.repo_root or ".", filename
)
transformer.context = replace(
transformer.context,
full_module_name=module_name_and_package.name,
full_package_name=module_name_and_package.package,
)
except ValueError as ex:
print(
f"Failed to determine module name for {filename}: {ex}", file=sys.stderr
)
def _execute_transform(
transformer: Union[Codemod, Type[Codemod]],
filename: str,
config: ExecutionConfig,
original_scratch: Dict[str, object],
codemod_args: Optional[Dict[str, object]],
repo_manager: Optional[FullRepoManager],
) -> ExecutionResult:
warnings: list[str] = []
try:
oldcode = _check_for_skip(filename, config)
if isinstance(oldcode, ExecutionResult):
return oldcode
transformer_instance = _instantiate_transformer(
transformer,
config.repo_root or ".",
filename,
original_scratch,
codemod_args or {},
repo_manager,
)
# Run the transform, bail if we failed or if we aren't formatting code
try:
@ -276,55 +324,26 @@ def _execute_transform( # noqa: C901
else PartialParserConfig()
),
)
output_tree = transformer.transform_module(input_tree)
output_tree = transformer_instance.transform_module(input_tree)
newcode = output_tree.bytes
encoding = output_tree.encoding
except KeyboardInterrupt:
return ExecutionResult(
filename=filename, changed=False, transform_result=TransformExit()
)
warnings.extend(transformer_instance.context.warnings)
except SkipFile as ex:
warnings.extend(transformer_instance.context.warnings)
return ExecutionResult(
filename=filename,
changed=False,
transform_result=TransformSkip(
skip_reason=SkipReason.OTHER,
skip_description=str(ex),
warning_messages=transformer.context.warnings,
),
)
except Exception as ex:
return ExecutionResult(
filename=filename,
changed=False,
transform_result=TransformFailure(
error=ex,
traceback_str=traceback.format_exc(),
warning_messages=transformer.context.warnings,
warning_messages=warnings,
),
)
# Call formatter if needed, but only if we actually changed something in this
# file
if config.format_code and newcode != oldcode:
try:
newcode = invoke_formatter(config.formatter_args, newcode)
except KeyboardInterrupt:
return ExecutionResult(
filename=filename,
changed=False,
transform_result=TransformExit(),
)
except Exception as ex:
return ExecutionResult(
filename=filename,
changed=False,
transform_result=TransformFailure(
error=ex,
traceback_str=traceback.format_exc(),
warning_messages=transformer.context.warnings,
),
)
newcode = invoke_formatter(config.formatter_args, newcode)
# Format as unified diff if needed, otherwise save it back
changed = oldcode != newcode
@ -347,13 +366,14 @@ def _execute_transform( # noqa: C901
return ExecutionResult(
filename=filename,
changed=changed,
transform_result=TransformSuccess(
warning_messages=transformer.context.warnings, code=newcode
),
transform_result=TransformSuccess(warning_messages=warnings, code=newcode),
)
except KeyboardInterrupt:
return ExecutionResult(
filename=filename, changed=False, transform_result=TransformExit()
filename=filename,
changed=False,
transform_result=TransformExit(warning_messages=warnings),
)
except Exception as ex:
return ExecutionResult(
@ -362,7 +382,7 @@ def _execute_transform( # noqa: C901
transform_result=TransformFailure(
error=ex,
traceback_str=traceback.format_exc(),
warning_messages=transformer.context.warnings,
warning_messages=warnings,
),
)
@ -415,7 +435,7 @@ class Progress:
operations still to do.
"""
if files_finished <= 0:
if files_finished <= 0 or elapsed_seconds == 0:
# Technically infinite but calculating sounds better.
return "[calculating]"
@ -473,7 +493,7 @@ def _print_parallel_result(
)
# In unified diff mode, the code is a diff we must print.
if unified_diff:
if unified_diff and result.code:
print(result.code)
@ -499,15 +519,8 @@ class ParallelTransformResult:
skips: int
# Unfortunate wrapper required since there is no `istarmap_unordered`...
def _execute_transform_wrap(
job: Dict[str, Any],
) -> ExecutionResult:
return _execute_transform(**job)
def parallel_exec_transform_with_prettyprint( # noqa: C901
transform: Codemod,
transform: Union[Codemod, Type[Codemod]],
files: Sequence[str],
*,
jobs: Optional[int] = None,
@ -523,41 +536,52 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901
blacklist_patterns: Sequence[str] = (),
python_version: Optional[str] = None,
repo_root: Optional[str] = None,
codemod_args: Optional[Dict[str, object]] = None,
) -> ParallelTransformResult:
"""
Given a list of files and an instantiated codemod we should apply to them,
fork and apply the codemod in parallel to all of the files, including any
configured formatter. The ``jobs`` parameter controls the maximum number of
in-flight transforms, and needs to be at least 1. If not included, the number
of jobs will automatically be set to the number of CPU cores. If ``unified_diff``
is set to a number, changes to files will be printed to stdout with
``unified_diff`` lines of context. If it is set to ``None`` or left out, files
themselves will be updated with changes and formatting. If a
``python_version`` is provided, then we will parse each source file using
this version. Otherwise, we will use the version of the currently executing python
Given a list of files and a codemod we should apply to them, fork and apply the
codemod in parallel to all of the files, including any configured formatter. The
``jobs`` parameter controls the maximum number of in-flight transforms, and needs to
be at least 1. If not included, the number of jobs will automatically be set to the
number of CPU cores. If ``unified_diff`` is set to a number, changes to files will
be printed to stdout with ``unified_diff`` lines of context. If it is set to
``None`` or left out, files themselves will be updated with changes and formatting.
If a ``python_version`` is provided, then we will parse each source file using this
version. Otherwise, we will use the version of the currently executing python
binary.
A progress indicator as well as any generated warnings will be printed to stderr.
To supress the interactive progress indicator, set ``hide_progress`` to ``True``.
Files that include the generated code marker will be skipped unless the
``include_generated`` parameter is set to ``True``. Similarly, files that match
a supplied blacklist of regex patterns will be skipped. Warnings for skipping
both blacklisted and generated files will be printed to stderr along with
warnings generated by the codemod unless ``hide_blacklisted`` and
``hide_generated`` are set to ``True``. Files that were successfully codemodded
will not be printed to stderr unless ``show_successes`` is set to ``True``.
A progress indicator as well as any generated warnings will be printed to stderr. To
supress the interactive progress indicator, set ``hide_progress`` to ``True``. Files
that include the generated code marker will be skipped unless the
``include_generated`` parameter is set to ``True``. Similarly, files that match a
supplied blacklist of regex patterns will be skipped. Warnings for skipping both
blacklisted and generated files will be printed to stderr along with warnings
generated by the codemod unless ``hide_blacklisted`` and ``hide_generated`` are set
to ``True``. Files that were successfully codemodded will not be printed to stderr
unless ``show_successes`` is set to ``True``.
To make this API possible, we take an instantiated transform. This is due to
the fact that lambdas are not pickleable and pickling functions is undefined.
This means we're implicitly relying on fork behavior on UNIX-like systems, and
this function will not work on Windows systems. To create a command-line utility
that runs on Windows, please instead see
:func:`~libcst.codemod.exec_transform_with_prettyprint`.
We take a :class:`~libcst.codemod._codemod.Codemod` class, or an instantiated
:class:`~libcst.codemod._codemod.Codemod`. In the former case, the codemod will be
instantiated for each file, with ``codemod_args`` passed in to the constructor.
Passing an already instantiated :class:`~libcst.codemod._codemod.Codemod` is
deprecated, because it leads to sharing of the
:class:`~libcst.codemod._codemod.Codemod` instance across files, which is a common
source of hard-to-track-down bugs when the :class:`~libcst.codemod._codemod.Codemod`
tracks its state on the instance.
"""
if isinstance(transform, Codemod):
warn(
"Passing transformer instances to `parallel_exec_transform_with_prettyprint` "
"is deprecated and will break in a future version. "
"Please pass the transformer class instead.",
DeprecationWarning,
stacklevel=2,
)
# Ensure that we have no duplicates, otherwise we might get race conditions
# on write.
files = sorted(list({os.path.abspath(f) for f in files}))
files = sorted({os.path.abspath(f) for f in files})
total = len(files)
progress = Progress(enabled=not hide_progress, total=total)
@ -569,11 +593,12 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901
)
if jobs < 1:
raise Exception("Must have at least one job to process!")
raise ValueError("Must have at least one job to process!")
if total == 0:
return ParallelTransformResult(successes=0, failures=0, skips=0, warnings=0)
metadata_manager: Optional[FullRepoManager] = None
if repo_root is not None:
# Make sure if there is a root that we have the absolute path to it.
repo_root = os.path.abspath(repo_root)
@ -586,10 +611,7 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901
transform.get_inherited_dependencies(),
)
metadata_manager.resolve_cache()
transform.context = replace(
transform.context,
metadata_manager=metadata_manager,
)
print("Executing codemod...", file=sys.stderr)
config = ExecutionConfig(
@ -603,13 +625,16 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901
python_version=python_version,
)
pool_impl: Callable[[], Executor]
if total == 1 or jobs == 1:
# Simple case, we should not pay for process overhead.
# Let's just use a dummy synchronous pool.
# Let's just use a dummy synchronous executor.
jobs = 1
pool_impl = DummyPool
else:
pool_impl = Pool
pool_impl = DummyExecutor
elif getattr(sys, "_is_gil_enabled", lambda: True)(): # pyre-ignore[16]
from concurrent.futures import ProcessPoolExecutor
pool_impl = functools.partial(ProcessPoolExecutor, max_workers=jobs)
# Warm the parser, pre-fork.
parse_module(
"",
@ -619,25 +644,35 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901
else PartialParserConfig()
),
)
else:
from concurrent.futures import ThreadPoolExecutor
pool_impl = functools.partial(ThreadPoolExecutor, max_workers=jobs)
successes: int = 0
failures: int = 0
warnings: int = 0
skips: int = 0
original_scratch = (
deepcopy(transform.context.scratch) if isinstance(transform, Codemod) else {}
)
with pool_impl(processes=jobs) as p: # type: ignore
args = [
{
"transformer": transform,
"filename": filename,
"config": config,
}
for filename in files
]
with pool_impl() as executor: # type: ignore
try:
for result in p.imap_unordered(
_execute_transform_wrap, args, chunksize=chunksize
):
futures = [
executor.submit(
_execute_transform,
transformer=transform,
filename=filename,
config=config,
original_scratch=original_scratch,
codemod_args=codemod_args,
repo_manager=metadata_manager,
)
for filename in files
]
for future in as_completed(futures):
result = future.result()
# Print an execution result, keep track of failures
_print_parallel_result(
result,

View file

@ -56,9 +56,9 @@ class Codemod(MetadataDependent, ABC):
"""
module = self.context.module
if module is None:
raise Exception(
raise ValueError(
f"Attempted access of {self.__class__.__name__}.module outside of "
+ "transform_module()."
"transform_module()."
)
return module

View file

@ -3,12 +3,14 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
#
from __future__ import annotations
import argparse
import inspect
from abc import ABC, abstractmethod
from typing import Dict, Generator, List, Type, TypeVar
from typing import Dict, Generator, List, Tuple, Type, TypeVar
from libcst import Module
from libcst import CSTNode, Module
from libcst.codemod._codemod import Codemod
from libcst.codemod._context import CodemodContext
from libcst.codemod._visitor import ContextAwareTransformer
@ -65,6 +67,28 @@ class CodemodCommand(Codemod, ABC):
"""
...
# Lightweight wrappers for RemoveImportsVisitor static functions
def remove_unused_import(
self,
module: str,
obj: str | None = None,
asname: str | None = None,
) -> None:
RemoveImportsVisitor.remove_unused_import(self.context, module, obj, asname)
def remove_unused_import_by_node(self, node: CSTNode) -> None:
RemoveImportsVisitor.remove_unused_import_by_node(self.context, node)
# Lightweight wrappers for AddImportsVisitor static functions
def add_needed_import(
self,
module: str,
obj: str | None = None,
asname: str | None = None,
relative: int = 0,
) -> None:
AddImportsVisitor.add_needed_import(self.context, module, obj, asname, relative)
def transform_module(self, tree: Module) -> Module:
# Overrides (but then calls) Codemod's transform_module to provide
# a spot where additional supported transforms can be attached and run.
@ -75,13 +99,13 @@ class CodemodCommand(Codemod, ABC):
# have a static method that other transforms can use which takes
# a context and other optional args and modifies its own context key
# accordingly. We import them here so that we don't have circular imports.
supported_transforms: Dict[str, Type[Codemod]] = {
AddImportsVisitor.CONTEXT_KEY: AddImportsVisitor,
RemoveImportsVisitor.CONTEXT_KEY: RemoveImportsVisitor,
}
supported_transforms: List[Tuple[str, Type[Codemod]]] = [
(AddImportsVisitor.CONTEXT_KEY, AddImportsVisitor),
(RemoveImportsVisitor.CONTEXT_KEY, RemoveImportsVisitor),
]
# For any visitors that we support auto-running, run them here if needed.
for key, transform in supported_transforms.items():
for key, transform in supported_transforms:
if key in self.context.scratch:
# We have work to do, so lets run this.
tree = self._instantiate_and_run(transform, tree)

View file

@ -3,37 +3,47 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import sys
from concurrent.futures import Executor, Future
from types import TracebackType
from typing import Callable, Generator, Iterable, Optional, Type, TypeVar
from typing import Callable, Optional, Type, TypeVar
RetT = TypeVar("RetT")
ArgT = TypeVar("ArgT")
if sys.version_info >= (3, 10):
from typing import ParamSpec
else:
from typing_extensions import ParamSpec
Return = TypeVar("Return")
Params = ParamSpec("Params")
class DummyPool:
class DummyExecutor(Executor):
"""
Synchronous dummy `multiprocessing.Pool` analogue.
Synchronous dummy `concurrent.futures.Executor` analogue.
"""
def __init__(self, processes: Optional[int] = None) -> None:
pass
def imap_unordered(
def submit(
self,
func: Callable[[ArgT], RetT],
iterable: Iterable[ArgT],
chunksize: Optional[int] = None,
) -> Generator[RetT, None, None]:
for args in iterable:
yield func(args)
fn: Callable[Params, Return],
/,
*args: Params.args,
**kwargs: Params.kwargs,
) -> Future[Return]:
future: Future[Return] = Future()
try:
result = fn(*args, **kwargs)
future.set_result(result)
except Exception as exc:
future.set_exception(exc)
return future
def __enter__(self) -> "DummyPool":
def __enter__(self) -> "DummyExecutor":
return self
def __exit__(
self,
exc_type: Optional[Type[Exception]],
exc: Optional[Exception],
tb: Optional[TracebackType],
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> None:
pass

View file

@ -6,7 +6,7 @@
from typing import Mapping
import libcst as cst
from libcst import MetadataDependent
from libcst import MetadataDependent, MetadataException
from libcst.codemod._codemod import Codemod
from libcst.codemod._context import CodemodContext
from libcst.matchers import MatcherDecoratableTransformer, MatcherDecoratableVisitor
@ -69,14 +69,14 @@ class ContextAwareVisitor(MatcherDecoratableVisitor, MetadataDependent):
if dependencies:
wrapper = self.context.wrapper
if wrapper is None:
raise Exception(
raise MetadataException(
f"Attempting to instantiate {self.__class__.__name__} outside of "
+ "an active transform. This means that metadata hasn't been "
+ "calculated and we cannot successfully create this visitor."
)
for dep in dependencies:
if dep not in wrapper._metadata:
raise Exception(
raise MetadataException(
f"Attempting to access metadata {dep.__name__} that was not a "
+ "declared dependency of parent transform! This means it is "
+ "not possible to compute this value. Please ensure that all "
@ -101,7 +101,7 @@ class ContextAwareVisitor(MatcherDecoratableVisitor, MetadataDependent):
"""
module = self.context.module
if module is None:
raise Exception(
raise ValueError(
f"Attempted access of {self.__class__.__name__}.module outside of "
+ "transform_module()."
)

View file

@ -9,6 +9,8 @@ from typing import Generator, List, Optional, Sequence, Set, Tuple
import libcst as cst
import libcst.matchers as m
from libcst import CSTLogicError
from libcst._exceptions import ParserSyntaxError
from libcst.codemod import (
CodemodContext,
ContextAwareTransformer,
@ -23,7 +25,7 @@ def _get_lhs(field: cst.BaseExpression) -> cst.BaseExpression:
elif isinstance(field, (cst.Attribute, cst.Subscript)):
return _get_lhs(field.value)
else:
raise Exception("Unsupported node type!")
raise TypeError("Unsupported node type!")
def _find_expr_from_field_name(
@ -48,7 +50,7 @@ def _find_expr_from_field_name(
if isinstance(lhs, cst.Integer):
index = int(lhs.value)
if index < 0 or index >= len(args):
raise Exception(f"Logic error, arg sequence {index} out of bounds!")
raise CSTLogicError(f"Logic error, arg sequence {index} out of bounds!")
elif isinstance(lhs, cst.Name):
for i, arg in enumerate(args):
kw = arg.keyword
@ -58,10 +60,12 @@ def _find_expr_from_field_name(
index = i
break
if index is None:
raise Exception(f"Logic error, arg name {lhs.value} out of bounds!")
raise CSTLogicError(f"Logic error, arg name {lhs.value} out of bounds!")
if index is None:
raise Exception(f"Logic error, unsupported fieldname expression {fieldname}!")
raise CSTLogicError(
f"Logic error, unsupported fieldname expression {fieldname}!"
)
# Format it!
return field_expr.deep_replace(lhs, args[index].value)
@ -141,7 +145,7 @@ def _get_tokens( # noqa: C901
in_brackets -= 1
if in_brackets < 0:
raise Exception("Stray } in format string!")
raise ValueError("Stray } in format string!")
if in_brackets == 0:
field_name, format_spec, conversion = _get_field(format_accum)
@ -158,9 +162,11 @@ def _get_tokens( # noqa: C901
format_accum += char
if in_brackets > 0:
raise Exception("Stray { in format string!")
raise ParserSyntaxError(
"Stray { in format string!", lines=[string], raw_line=0, raw_column=0
)
if format_accum:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
# Yield the last bit of information
yield (prefix, None, None, None)
@ -188,7 +194,7 @@ class SwitchStringQuotesTransformer(ContextAwareTransformer):
def __init__(self, context: CodemodContext, avoid_quote: str) -> None:
super().__init__(context)
if avoid_quote not in {'"', "'"}:
raise Exception("Must specify either ' or \" single quote to avoid.")
raise ValueError("Must specify either ' or \" single quote to avoid.")
self.avoid_quote: str = avoid_quote
self.replace_quote: str = '"' if avoid_quote == "'" else "'"
@ -219,7 +225,6 @@ class SwitchStringQuotesTransformer(ContextAwareTransformer):
class ConvertFormatStringCommand(VisitorBasedCodemodCommand):
DESCRIPTION: str = "Converts instances of str.format() to f-string."
@staticmethod
@ -271,7 +276,7 @@ class ConvertFormatStringCommand(VisitorBasedCodemodCommand):
inserted_sequence: int = 0
stringnode = cst.ensure_type(extraction["string"], cst.SimpleString)
tokens = _get_tokens(stringnode.raw_value)
for (literal_text, field_name, format_spec, conversion) in tokens:
for literal_text, field_name, format_spec, conversion in tokens:
if literal_text:
fstring.append(cst.FormattedStringText(literal_text))
if field_name is None:
@ -297,7 +302,7 @@ class ConvertFormatStringCommand(VisitorBasedCodemodCommand):
) in format_spec_tokens:
if spec_format_spec is not None:
# This shouldn't be possible, we don't allow it in the spec!
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
if spec_literal_text:
format_spec_parts.append(
cst.FormattedStringText(spec_literal_text)

View file

@ -25,7 +25,9 @@ class ConvertNamedTupleToDataclassCommand(VisitorBasedCodemodCommand):
NamedTuple-specific attributes and methods.
"""
DESCRIPTION: str = "Convert NamedTuple class declarations to Python 3.7 dataclasses using the @dataclass decorator."
DESCRIPTION: str = (
"Convert NamedTuple class declarations to Python 3.7 dataclasses using the @dataclass decorator."
)
METADATA_DEPENDENCIES: Sequence[ProviderT] = (QualifiedNameProvider,)
# The 'NamedTuple' we are interested in

View file

@ -53,12 +53,12 @@ class EscapeStringQuote(cst.CSTTransformer):
original_node.prefix + quo + original_node.raw_value + quo
)
if escaped_string.evaluated_value != original_node.evaluated_value:
raise Exception(
raise ValueError(
f"Failed to escape string:\n original:{original_node.value}\n escaped:{escaped_string.value}"
)
else:
return escaped_string
raise Exception(
raise ValueError(
f"Cannot find a good quote for escaping the SimpleString: {original_node.value}"
)
return original_node
@ -97,9 +97,11 @@ class ConvertPercentFormatStringCommand(VisitorBasedCodemodCommand):
parts.append(cst.FormattedStringText(value=token))
expressions: List[cst.CSTNode] = list(
*itertools.chain(
[elm.value for elm in expr.elements]
if isinstance(expr, cst.Tuple)
else [expr]
(
[elm.value for elm in expr.elements]
if isinstance(expr, cst.Tuple)
else [expr]
)
for expr in exprs
)
)

View file

@ -11,8 +11,6 @@ import functools
import sys
from typing import cast, Dict, List, Optional, Sequence, Set, Tuple, Union
from typing_extensions import TypeAlias
import libcst as cst
import libcst.matchers as m
from libcst.codemod import CodemodContext, VisitorBasedCodemodCommand
@ -52,7 +50,6 @@ def _parse_type_comment(
if type_comment is None:
return None
try:
# pyre-ignore[16]: the ast module stubs do not have full details
return ast.parse(type_comment, "<type_comment>", "eval").body
except SyntaxError:
return None
@ -69,10 +66,7 @@ def _parse_func_type_comment(
) -> Optional["ast.FunctionType"]:
if func_type_comment is None:
return None
return cast(
ast.FunctionType,
ast.parse(func_type_comment, "<func_type_comment>", "func_type"),
)
return ast.parse(func_type_comment, "<func_type_comment>", "func_type")
@functools.lru_cache()
@ -126,6 +120,19 @@ def _is_type_comment(comment: Optional[cst.Comment]) -> bool:
return True
def _strip_type_comment(comment: Optional[cst.Comment]) -> Optional[cst.Comment]:
"""
Remove the type comment while keeping any following comments.
"""
if not _is_type_comment(comment):
return comment
assert comment is not None
idx = comment.value.find("#", 1)
if idx < 0:
return None
return comment.with_changes(value=comment.value[idx:])
class _FailedToApplyAnnotation:
pass
@ -134,9 +141,9 @@ class _ArityError(Exception):
pass
UnpackedBindings: TypeAlias = Union[cst.BaseExpression, List["UnpackedBindings"]]
UnpackedAnnotations: TypeAlias = Union[str, List["UnpackedAnnotations"]]
TargetAnnotationPair: TypeAlias = Tuple[cst.BaseExpression, str]
UnpackedBindings = Union[cst.BaseExpression, List["UnpackedBindings"]]
UnpackedAnnotations = Union[str, List["UnpackedAnnotations"]]
TargetAnnotationPair = Tuple[cst.BaseExpression, str]
class AnnotationSpreader:
@ -504,6 +511,9 @@ class ConvertTypeComments(VisitorBasedCodemodCommand):
self,
node: cst.TrailingWhitespace,
) -> cst.TrailingWhitespace:
trailing_comment = _strip_type_comment(node.comment)
if trailing_comment is not None:
return node.with_changes(comment=trailing_comment)
return node.with_changes(
whitespace=cst.SimpleWhitespace(
""
@ -690,7 +700,7 @@ class ConvertTypeComments(VisitorBasedCodemodCommand):
# able to extract type information. This is done via mutable state and the
# usual visitor pattern.
# (B) we also manually reach down to the first statement inside of the
# funciton body and aggressively strip type comments from leading
# function body and aggressively strip type comments from leading
# whitespaces
#
# PEP 484 underspecifies how to apply type comments to (non-static)
@ -770,7 +780,7 @@ class ConvertTypeComments(VisitorBasedCodemodCommand):
self,
node: cst.FunctionDef,
) -> None:
"Turn off aggressive type comment removal when we've leaved the header."
"Turn off aggressive type comment removal when we've left the header."
self.aggressively_strip_type_comments = False
def leave_IndentedBlock(

View file

@ -0,0 +1,56 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
#
# pyre-strict
import libcst as cst
from libcst.codemod import VisitorBasedCodemodCommand
from libcst.codemod.visitors import RemoveImportsVisitor
from libcst.metadata import QualifiedName, QualifiedNameProvider, QualifiedNameSource
class ConvertUnionToOrCommand(VisitorBasedCodemodCommand):
DESCRIPTION: str = "Convert `Union[A, B]` to `A | B` in Python 3.10+"
METADATA_DEPENDENCIES = (QualifiedNameProvider,)
def leave_Subscript(
self, original_node: cst.Subscript, updated_node: cst.Subscript
) -> cst.BaseExpression:
"""
Given a subscript, check if it's a Union - if so, either flatten the members
into a nested BitOr (if multiple members) or unwrap the type (if only one member).
"""
if not QualifiedNameProvider.has_name(
self,
original_node,
QualifiedName(name="typing.Union", source=QualifiedNameSource.IMPORT),
):
return updated_node
types = [
cst.ensure_type(
cst.ensure_type(s, cst.SubscriptElement).slice, cst.Index
).value
for s in updated_node.slice
]
if len(types) == 1:
return types[0]
else:
replacement = cst.BinaryOperation(
left=types[0], right=types[1], operator=cst.BitOr()
)
for type_ in types[2:]:
replacement = cst.BinaryOperation(
left=replacement, right=type_, operator=cst.BitOr()
)
return replacement
def leave_Module(
self, original_node: cst.Module, updated_node: cst.Module
) -> cst.Module:
RemoveImportsVisitor.remove_unused_import(
self.context, module="typing", obj="Union"
)
return updated_node

View file

@ -11,7 +11,6 @@ from libcst.codemod.visitors import AddImportsVisitor
class EnsureImportPresentCommand(MagicArgsCodemodCommand):
DESCRIPTION: str = (
"Given a module and possibly an entity in that module, add an import "
+ "as long as one does not already exist."

View file

@ -7,6 +7,7 @@ from typing import Dict, Sequence, Union
import libcst
import libcst.matchers as m
from libcst import CSTLogicError
from libcst.codemod import CodemodContext, VisitorBasedCodemodCommand
from libcst.helpers import insert_header_comments
@ -29,12 +30,12 @@ class FixPyreDirectivesCommand(VisitorBasedCodemodCommand):
def visit_Module_header(self, node: libcst.Module) -> None:
if self.in_module_header:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
self.in_module_header = True
def leave_Module_header(self, node: libcst.Module) -> None:
if not self.in_module_header:
raise Exception("Logic error!")
raise CSTLogicError("Logic error!")
self.in_module_header = False
def leave_EmptyLine(

View file

@ -0,0 +1,40 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
#
# pyre-strict
import libcst as cst
import libcst.matchers as m
from libcst.codemod import VisitorBasedCodemodCommand
from libcst.metadata import QualifiedName, QualifiedNameProvider, QualifiedNameSource
class FixVariadicCallableCommmand(VisitorBasedCodemodCommand):
DESCRIPTION: str = (
"Fix incorrect variadic callable type annotations from `Callable[[...], T]` to `Callable[..., T]``"
)
METADATA_DEPENDENCIES = (QualifiedNameProvider,)
def leave_Subscript(
self, original_node: cst.Subscript, updated_node: cst.Subscript
) -> cst.BaseExpression:
if QualifiedNameProvider.has_name(
self,
original_node,
QualifiedName(name="typing.Callable", source=QualifiedNameSource.IMPORT),
):
node_matches = len(updated_node.slice) == 2 and m.matches(
updated_node.slice[0],
m.SubscriptElement(
slice=m.Index(value=m.List(elements=[m.Element(m.Ellipsis())]))
),
)
if node_matches:
slices = list(updated_node.slice)
slices[0] = cst.SubscriptElement(cst.Index(cst.Ellipsis()))
return updated_node.with_changes(slice=slices)
return updated_node

Some files were not shown because too many files have changed in this diff Show more