From 0b4016c5b3801d7701cf0209a2cb25be5fd64a3f Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Sun, 26 May 2024 22:57:47 +0100 Subject: [PATCH 001/151] use trusted publishing for pypi (#1154) --- .github/workflows/pypi_upload.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pypi_upload.yml b/.github/workflows/pypi_upload.yml index 6526a002..ee9990cb 100644 --- a/.github/workflows/pypi_upload.yml +++ b/.github/workflows/pypi_upload.yml @@ -16,6 +16,8 @@ jobs: name: Upload wheels to pypi runs-on: ubuntu-latest needs: build + permissions: + id-token: write steps: - uses: actions/checkout@v4 with: @@ -46,14 +48,10 @@ jobs: 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 }} - 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 }} packages-dir: ${{ steps.download.outputs.download-path }} From 47ff8cbf222ca57c8dbd2139885576f58403029a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 20:45:18 +0100 Subject: [PATCH 002/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 260c8655..a61f88b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ dev = [ "hypothesis>=4.36.0", "hypothesmith>=0.0.4", "jupyter>=1.0.0", - "maturin>=0.8.3,<1.6", + "maturin>=0.8.3,<1.7", "nbsphinx>=0.4.2", "prompt-toolkit>=2.0.9", "pyre-check==0.9.18; platform_system != 'Windows'", From 9f6e27600f04f09274b5c86640876600d7fa2d01 Mon Sep 17 00:00:00 2001 From: Camillo Date: Wed, 12 Jun 2024 02:36:50 -0700 Subject: [PATCH 003/151] FullyQualifiedNameProvider: Optionally consider pyproject.toml files when determining a file's module name and package (#1148) --- libcst/helpers/module.py | 20 ++++- libcst/helpers/tests/test_module.py | 75 ++++++++++++++++++- libcst/metadata/base_provider.py | 16 +++- libcst/metadata/file_path_provider.py | 4 +- libcst/metadata/full_repo_manager.py | 7 +- libcst/metadata/name_provider.py | 16 +++- .../metadata/tests/test_metadata_wrapper.py | 8 +- libcst/metadata/tests/test_name_provider.py | 2 +- libcst/metadata/type_inference_provider.py | 12 +-- 9 files changed, 140 insertions(+), 20 deletions(-) diff --git a/libcst/helpers/module.py b/libcst/helpers/module.py index 3c26122d..37e6af08 100644 --- a/libcst/helpers/module.py +++ b/libcst/helpers/module.py @@ -5,7 +5,7 @@ # from dataclasses import dataclass from itertools import islice -from pathlib import PurePath +from pathlib import Path, PurePath from typing import List, Optional from libcst import Comment, EmptyLine, ImportFrom, Module @@ -132,11 +132,25 @@ class ModuleNameAndPackage: def calculate_module_and_package( - repo_root: StrPath, filename: StrPath + repo_root: StrPath, filename: StrPath, use_pyproject_toml: bool = False ) -> ModuleNameAndPackage: # Given an absolute repo_root and an absolute filename, calculate the # python module name for the file. - relative_filename = PurePath(filename).relative_to(repo_root) + if use_pyproject_toml: + # But also look for pyproject.toml files, indicating nested packages in the repo. + abs_repo_root = Path(repo_root).resolve() + abs_filename = Path(filename).resolve() + package_root = abs_filename.parent + while package_root != abs_repo_root: + if (package_root / "pyproject.toml").exists(): + break + if package_root == package_root.parent: + break + package_root = package_root.parent + + relative_filename = abs_filename.relative_to(package_root) + else: + relative_filename = PurePath(filename).relative_to(repo_root) relative_filename = relative_filename.with_suffix("") # handle special cases diff --git a/libcst/helpers/tests/test_module.py b/libcst/helpers/tests/test_module.py index 7260f5cc..815e1fa2 100644 --- a/libcst/helpers/tests/test_module.py +++ b/libcst/helpers/tests/test_module.py @@ -3,7 +3,9 @@ # 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 Optional +from pathlib import Path, PurePath +from typing import Any, Optional +from unittest.mock import patch import libcst as cst from libcst.helpers.common import ensure_type @@ -251,6 +253,77 @@ class ModuleTest(UnitTest): calculate_module_and_package(repo_root, filename), module_and_package ) + @data_provider( + ( + ("foo/foo/__init__.py", ModuleNameAndPackage("foo", "foo")), + ("foo/foo/file.py", ModuleNameAndPackage("foo.file", "foo")), + ( + "foo/foo/sub/subfile.py", + ModuleNameAndPackage("foo.sub.subfile", "foo.sub"), + ), + ("libs/bar/bar/thing.py", ModuleNameAndPackage("bar.thing", "bar")), + ( + "noproj/some/file.py", + ModuleNameAndPackage("noproj.some.file", "noproj.some"), + ), + ) + ) + def test_calculate_module_and_package_using_pyproject_toml( + self, + rel_path: str, + module_and_package: Optional[ModuleNameAndPackage], + ) -> None: + mock_tree: dict[str, Any] = { + "home": { + "user": { + "root": { + "foo": { + "pyproject.toml": "content", + "foo": { + "__init__.py": "content", + "file.py": "content", + "sub": { + "subfile.py": "content", + }, + }, + }, + "libs": { + "bar": { + "pyproject.toml": "content", + "bar": { + "__init__.py": "content", + "thing.py": "content", + }, + } + }, + "noproj": { + "some": { + "file.py": "content", + } + }, + }, + }, + }, + } + repo_root = Path("/home/user/root").resolve() + fake_root: Path = repo_root.parent.parent.parent + + def mock_exists(path: PurePath) -> bool: + parts = path.relative_to(fake_root).parts + subtree = mock_tree + for part in parts: + if (subtree := subtree.get(part)) is None: + return False + return True + + with patch("pathlib.Path.exists", new=mock_exists): + self.assertEqual( + calculate_module_and_package( + repo_root, repo_root / rel_path, use_pyproject_toml=True + ), + module_and_package, + ) + @data_provider( ( # Providing a file outside the root should raise an exception diff --git a/libcst/metadata/base_provider.py b/libcst/metadata/base_provider.py index 1c113f57..2e03416f 100644 --- a/libcst/metadata/base_provider.py +++ b/libcst/metadata/base_provider.py @@ -6,12 +6,12 @@ from pathlib import Path from types import MappingProxyType from typing import ( - Callable, Generic, List, Mapping, MutableMapping, Optional, + Protocol, Type, TYPE_CHECKING, TypeVar, @@ -40,6 +40,18 @@ _ProvidedMetadataT = TypeVar("_ProvidedMetadataT", covariant=True) MaybeLazyMetadataT = Union[LazyValue[_ProvidedMetadataT], _ProvidedMetadataT] +class GenCacheMethod(Protocol): + def __call__( + self, + root_path: Path, + paths: List[str], + *, + timeout: Optional[int] = None, + use_pyproject_toml: bool = False, + ) -> Mapping[str, object]: + ... + + # We can't use an ABCMeta here, because of metaclass conflicts class BaseMetadataProvider(MetadataDependent, Generic[_ProvidedMetadataT]): """ @@ -59,7 +71,7 @@ class BaseMetadataProvider(MetadataDependent, Generic[_ProvidedMetadataT]): #: Implement gen_cache to indicate the metadata provider depends on cache from external #: system. This function will be called by :class:`~libcst.metadata.FullRepoManager` #: to compute required cache object per file path. - gen_cache: Optional[Callable[[Path, List[str], int], Mapping[str, object]]] = None + gen_cache: Optional[GenCacheMethod] = None def __init__(self, cache: object = None) -> None: super().__init__() diff --git a/libcst/metadata/file_path_provider.py b/libcst/metadata/file_path_provider.py index 5ed9baa6..6ab01b5f 100644 --- a/libcst/metadata/file_path_provider.py +++ b/libcst/metadata/file_path_provider.py @@ -4,7 +4,7 @@ # LICENSE file in the root directory of this source tree. from pathlib import Path -from typing import List, Mapping, Optional +from typing import Any, List, Mapping, Optional import libcst as cst from libcst.metadata.base_provider import BatchableMetadataProvider @@ -41,7 +41,7 @@ class FilePathProvider(BatchableMetadataProvider[Path]): @classmethod def gen_cache( - cls, root_path: Path, paths: List[str], timeout: Optional[int] = None + cls, root_path: Path, paths: List[str], **kwargs: Any ) -> Mapping[str, Path]: cache = {path: (root_path / path).resolve() for path in paths} return cache diff --git a/libcst/metadata/full_repo_manager.py b/libcst/metadata/full_repo_manager.py index 83bb6e83..770ba1f6 100644 --- a/libcst/metadata/full_repo_manager.py +++ b/libcst/metadata/full_repo_manager.py @@ -22,6 +22,7 @@ class FullRepoManager: paths: Collection[str], providers: Collection["ProviderT"], timeout: int = 5, + use_pyproject_toml: bool = False, ) -> None: """ Given project root directory with pyre and watchman setup, :class:`~libcst.metadata.FullRepoManager` @@ -38,6 +39,7 @@ class FullRepoManager: self.root_path: Path = Path(repo_root_dir) self._cache: Dict["ProviderT", Mapping[str, object]] = {} self._timeout = timeout + self._use_pyproject_toml = use_pyproject_toml self._providers = providers self._paths: List[str] = list(paths) @@ -65,7 +67,10 @@ class FullRepoManager: handler = provider.gen_cache if handler: cache[provider] = handler( - self.root_path, self._paths, self._timeout + self.root_path, + self._paths, + timeout=self._timeout, + use_pyproject_toml=self._use_pyproject_toml, ) self._cache = cache diff --git a/libcst/metadata/name_provider.py b/libcst/metadata/name_provider.py index 1868fa66..7de76eb5 100644 --- a/libcst/metadata/name_provider.py +++ b/libcst/metadata/name_provider.py @@ -5,7 +5,7 @@ import dataclasses from pathlib import Path -from typing import Collection, List, Mapping, Optional, Union +from typing import Any, Collection, List, Mapping, Optional, Union import libcst as cst from libcst._metadata_dependent import LazyValue, MetadataDependent @@ -112,9 +112,19 @@ class FullyQualifiedNameProvider(BatchableMetadataProvider[Collection[QualifiedN @classmethod def gen_cache( - cls, root_path: Path, paths: List[str], timeout: Optional[int] = None + cls, + root_path: Path, + paths: List[str], + *, + use_pyproject_toml: bool = False, + **kwargs: Any, ) -> Mapping[str, ModuleNameAndPackage]: - cache = {path: calculate_module_and_package(root_path, path) for path in paths} + cache = { + path: calculate_module_and_package( + root_path, path, use_pyproject_toml=use_pyproject_toml + ) + for path in paths + } return cache def __init__(self, cache: ModuleNameAndPackage) -> None: diff --git a/libcst/metadata/tests/test_metadata_wrapper.py b/libcst/metadata/tests/test_metadata_wrapper.py index ee61e14f..9063a99a 100644 --- a/libcst/metadata/tests/test_metadata_wrapper.py +++ b/libcst/metadata/tests/test_metadata_wrapper.py @@ -48,9 +48,13 @@ class MetadataWrapperTest(UnitTest): self.assertNotEqual(hash(mw1), hash(mw3)) self.assertNotEqual(hash(mw2), hash(mw3)) + @staticmethod + def ignore_args(*args: object, **kwargs: object) -> tuple[object, ...]: + return (args, kwargs) + def test_metadata_cache(self) -> None: class DummyMetadataProvider(BatchableMetadataProvider[None]): - gen_cache = tuple + gen_cache = self.ignore_args m = cst.parse_module("pass") mw = MetadataWrapper(m) @@ -60,7 +64,7 @@ class MetadataWrapperTest(UnitTest): mw.resolve(DummyMetadataProvider) class SimpleCacheMetadataProvider(BatchableMetadataProvider[object]): - gen_cache = tuple + gen_cache = self.ignore_args def __init__(self, cache: object) -> None: super().__init__(cache) diff --git a/libcst/metadata/tests/test_name_provider.py b/libcst/metadata/tests/test_name_provider.py index 80215dc6..fbd3631a 100644 --- a/libcst/metadata/tests/test_name_provider.py +++ b/libcst/metadata/tests/test_name_provider.py @@ -54,7 +54,7 @@ def get_fully_qualified_names(file_path: str, module_str: str) -> Set[QualifiedN cst.parse_module(dedent(module_str)), cache={ FullyQualifiedNameProvider: FullyQualifiedNameProvider.gen_cache( - Path(""), [file_path], None + Path(""), [file_path], timeout=None ).get(file_path, "") }, ) diff --git a/libcst/metadata/type_inference_provider.py b/libcst/metadata/type_inference_provider.py index c9c1fc9a..f00c97b6 100644 --- a/libcst/metadata/type_inference_provider.py +++ b/libcst/metadata/type_inference_provider.py @@ -6,7 +6,7 @@ import json import subprocess from pathlib import Path -from typing import Dict, List, Mapping, Optional, Sequence, Tuple, TypedDict +from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple, TypedDict import libcst as cst from libcst._position import CodePosition, CodeRange @@ -50,11 +50,13 @@ class TypeInferenceProvider(BatchableMetadataProvider[str]): METADATA_DEPENDENCIES = (PositionProvider,) - @staticmethod - # pyre-fixme[40]: Static method `gen_cache` cannot override a non-static method - # defined in `cst.metadata.base_provider.BaseMetadataProvider`. + @classmethod def gen_cache( - root_path: Path, paths: List[str], timeout: Optional[int] + cls, + root_path: Path, + paths: List[str], + timeout: Optional[int] = None, + **kwargs: Any, ) -> Mapping[str, object]: params = ",".join(f"path='{root_path / path}'" for path in paths) cmd_args = ["pyre", "--noninteractive", "query", f"types({params})"] From 8b97600fb3db89ae475de8b00736829eadd917c9 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Wed, 12 Jun 2024 17:29:25 +0100 Subject: [PATCH 004/151] 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 --- libcst/_nodes/statement.py | 12 +++++----- libcst/tests/test_roundtrip.py | 24 ++++++++++++++++--- .../libcst/tests/fixtures/malicious_match.py | 2 ++ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/libcst/_nodes/statement.py b/libcst/_nodes/statement.py index 6cad7684..1cb9221f 100644 --- a/libcst/_nodes/statement.py +++ b/libcst/_nodes/statement.py @@ -2854,17 +2854,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), ) @@ -3382,6 +3381,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: diff --git a/libcst/tests/test_roundtrip.py b/libcst/tests/test_roundtrip.py index e3a7a35b..d5da81f2 100644 --- a/libcst/tests/test_roundtrip.py +++ b/libcst/tests/test_roundtrip.py @@ -3,25 +3,43 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. + from pathlib import Path from unittest import TestCase -from libcst import parse_module +from libcst import CSTTransformer, parse_module from libcst._parser.entrypoints import is_native fixtures: Path = Path(__file__).parent.parent.parent / "native/libcst/tests/fixtures" +class NOOPTransformer(CSTTransformer): + pass + + class RoundTripTests(TestCase): - def test_clean_roundtrip(self) -> None: + def _get_fixtures(self) -> list[Path]: if not is_native(): self.skipTest("pure python parser doesn't work with this") self.assertTrue(fixtures.exists(), f"{fixtures} should exist") files = list(fixtures.iterdir()) self.assertGreater(len(files), 0) - for file in files: + return files + + def test_clean_roundtrip(self) -> None: + for file in self._get_fixtures(): with self.subTest(file=str(file)): src = file.read_text(encoding="utf-8") mod = parse_module(src) self.maxDiff = None self.assertEqual(mod.code, src) + + def test_transform_roundtrip(self) -> None: + transformer = NOOPTransformer() + self.maxDiff = None + for file in self._get_fixtures(): + with self.subTest(file=str(file)): + src = file.read_text(encoding="utf-8") + mod = parse_module(src) + new_mod = mod.visit(transformer) + self.assertEqual(src, new_mod.code) diff --git a/native/libcst/tests/fixtures/malicious_match.py b/native/libcst/tests/fixtures/malicious_match.py index 8c46571f..54840022 100644 --- a/native/libcst/tests/fixtures/malicious_match.py +++ b/native/libcst/tests/fixtures/malicious_match.py @@ -37,4 +37,6 @@ match x: case x,y , * more :pass case y.z: pass case 1, 2: pass + case ( Foo ( ) ) : pass + case (lol) if ( True , ) :pass From 7bb00179d9d8a5d66d854e7c1e474ec546a0db86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 19:36:33 +0100 Subject: [PATCH 005/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b60e6b9a..693d4d97 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: with: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.18.0 + uses: pypa/cibuildwheel@v2.18.1 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From 72701e4b40279c729accdf6d93bb03143337dae2 Mon Sep 17 00:00:00 2001 From: Kirill Ignatev Date: Thu, 4 Jul 2024 02:49:15 -0400 Subject: [PATCH 006/151] Mention codemod -x flag in docs (#1169) --- docs/source/codemods_tutorial.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/source/codemods_tutorial.rst b/docs/source/codemods_tutorial.rst index 78365ca9..8fc71bea 100644 --- a/docs/source/codemods_tutorial.rst +++ b/docs/source/codemods_tutorial.rst @@ -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 ---------------- From e20e757159f435417fb4d3a0913264c7d252b847 Mon Sep 17 00:00:00 2001 From: Jia Chen Date: Sat, 20 Jul 2024 01:04:25 -0700 Subject: [PATCH 007/151] Remove uses of `# pyre-placeholder-stub` (#1174) --- libcst/tests/test_fuzz.py | 10 ++++++++++ stubs/hypothesis.pyi | 6 +++++- stubs/hypothesmith.pyi | 6 +++++- stubs/setuptools.pyi | 6 +++++- stubs/typing_inspect.pyi | 6 +++++- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/libcst/tests/test_fuzz.py b/libcst/tests/test_fuzz.py index 66b32276..6ec95136 100644 --- a/libcst/tests/test_fuzz.py +++ b/libcst/tests/test_fuzz.py @@ -50,6 +50,9 @@ class FuzzTest(unittest.TestCase): @unittest.skipUnless( bool(os.environ.get("HYPOTHESIS", False)), "Hypothesis not requested" ) + # pyre-fixme[56]: Pyre was not able to infer the type of the decorator + # `hypothesis.given($parameter$source_code = + # hypothesmith.from_grammar($parameter$start = "file_input"))`. @hypothesis.given(source_code=from_grammar(start="file_input")) def test_parsing_compilable_module_strings(self, source_code: str) -> None: """The `from_grammar()` strategy generates strings from Python's grammar. @@ -77,6 +80,9 @@ class FuzzTest(unittest.TestCase): @unittest.skipUnless( bool(os.environ.get("HYPOTHESIS", False)), "Hypothesis not requested" ) + # pyre-fixme[56]: Pyre was not able to infer the type of the decorator + # `hypothesis.given($parameter$source_code = + # hypothesmith.from_grammar($parameter$start = "eval_input").map(str.strip))`. @hypothesis.given(source_code=from_grammar(start="eval_input").map(str.strip)) def test_parsing_compilable_expression_strings(self, source_code: str) -> None: """Much like statements, but for expressions this time. @@ -105,6 +111,10 @@ class FuzzTest(unittest.TestCase): @unittest.skipUnless( bool(os.environ.get("HYPOTHESIS", False)), "Hypothesis not requested" ) + # pyre-fixme[56]: Pyre was not able to infer the type of the decorator + # `hypothesis.given($parameter$source_code = + # hypothesmith.from_grammar($parameter$start = "single_input").map(lambda + # ($parameter$s) (s.replace(" @hypothesis.given( source_code=from_grammar(start="single_input").map( lambda s: s.replace("\n", "") + "\n" diff --git a/stubs/hypothesis.pyi b/stubs/hypothesis.pyi index 0568b4d1..a8c27975 100644 --- a/stubs/hypothesis.pyi +++ b/stubs/hypothesis.pyi @@ -1 +1,5 @@ -# pyre-placeholder-stub +# pyre-unsafe + +from typing import Any + +def __getattr__(name: str) -> Any: ... diff --git a/stubs/hypothesmith.pyi b/stubs/hypothesmith.pyi index 0568b4d1..a8c27975 100644 --- a/stubs/hypothesmith.pyi +++ b/stubs/hypothesmith.pyi @@ -1 +1,5 @@ -# pyre-placeholder-stub +# pyre-unsafe + +from typing import Any + +def __getattr__(name: str) -> Any: ... diff --git a/stubs/setuptools.pyi b/stubs/setuptools.pyi index 0568b4d1..a8c27975 100644 --- a/stubs/setuptools.pyi +++ b/stubs/setuptools.pyi @@ -1 +1,5 @@ -# pyre-placeholder-stub +# pyre-unsafe + +from typing import Any + +def __getattr__(name: str) -> Any: ... diff --git a/stubs/typing_inspect.pyi b/stubs/typing_inspect.pyi index 0568b4d1..a8c27975 100644 --- a/stubs/typing_inspect.pyi +++ b/stubs/typing_inspect.pyi @@ -1 +1,5 @@ -# pyre-placeholder-stub +# pyre-unsafe + +from typing import Any + +def __getattr__(name: str) -> Any: ... From b0d145dddd32b312b90c1e8550ce68f35643a58a Mon Sep 17 00:00:00 2001 From: Kirill Ignatev Date: Tue, 30 Jul 2024 04:01:07 -0400 Subject: [PATCH 008/151] Add validation for If node (#1177) * Add validation for If node Don't allow no space no parentheses. --- libcst/_nodes/statement.py | 7 ++++++- libcst/_nodes/tests/test_if.py | 20 +++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/libcst/_nodes/statement.py b/libcst/_nodes/statement.py index 1cb9221f..e02ae03c 100644 --- a/libcst/_nodes/statement.py +++ b/libcst/_nodes/statement.py @@ -599,7 +599,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( diff --git a/libcst/_nodes/tests/test_if.py b/libcst/_nodes/tests/test_if.py index e6e4c5ae..7615614e 100644 --- a/libcst/_nodes/tests/test_if.py +++ b/libcst/_nodes/tests/test_if.py @@ -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) From fb9e47585b7c7f2cc34355a7a87e537f9f0ed1c0 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Wed, 31 Jul 2024 12:13:05 +0100 Subject: [PATCH 009/151] make libcst_native::tokenizer public (#1182) --- native/libcst/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/libcst/src/lib.rs b/native/libcst/src/lib.rs index 9bd6bb99..03a97173 100644 --- a/native/libcst/src/lib.rs +++ b/native/libcst/src/lib.rs @@ -5,7 +5,7 @@ use std::cmp::{max, min}; -mod tokenizer; +pub mod tokenizer; pub use tokenizer::whitespace_parser::Config; use tokenizer::{whitespace_parser, TokConfig, Token, TokenIterator}; From 814f243a75fbceeac4c461d6c3ea577de0d9e4b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 12:13:31 +0100 Subject: [PATCH 010/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 693d4d97..237e1fb8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: with: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.18.1 + uses: pypa/cibuildwheel@v2.19.2 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From 56cd1f98628188d0850c33c96dabd3acccc6645a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 12:14:07 +0100 Subject: [PATCH 011/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a61f88b1..0ea330e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ dev = [ "hypothesis>=4.36.0", "hypothesmith>=0.0.4", "jupyter>=1.0.0", - "maturin>=0.8.3,<1.7", + "maturin>=1.7.0,<1.8", "nbsphinx>=0.4.2", "prompt-toolkit>=2.0.9", "pyre-check==0.9.18; platform_system != 'Windows'", From 45234f198c22aed1227c347d2fcc29c2f003d386 Mon Sep 17 00:00:00 2001 From: Kirill Ignatev Date: Mon, 5 Aug 2024 17:41:51 -0400 Subject: [PATCH 012/151] 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 --- libcst/codemod/_cli.py | 31 ++++++++++++------------ libcst/codemod/tests/test_codemod_cli.py | 30 +++++++++++++++++++++++ 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/libcst/codemod/_cli.py b/libcst/codemod/_cli.py index 8bfc11f8..2481bf9d 100644 --- a/libcst/codemod/_cli.py +++ b/libcst/codemod/_cli.py @@ -22,6 +22,7 @@ from typing import Any, AnyStr, cast, Dict, List, Optional, Sequence, Union from libcst import parse_module, PartialParserConfig from libcst.codemod._codemod import Codemod +from libcst.codemod._context import CodemodContext from libcst.codemod._dummy_pool import DummyPool from libcst.codemod._runner import ( SkipFile, @@ -246,30 +247,28 @@ def _execute_transform( # noqa: C901 ), ) - # 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, - filename=filename, - scratch=deepcopy(scratch), - ) - # 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, - ) + 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 + + # Apart from metadata_manager, every field of context should be reset per file + transformer.context = CodemodContext( + scratch=deepcopy(scratch), + filename=filename, + full_module_name=mod_name, + full_package_name=pkg_name, + metadata_manager=transformer.context.metadata_manager, + ) # Run the transform, bail if we failed or if we aren't formatting code try: @@ -420,7 +419,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]" diff --git a/libcst/codemod/tests/test_codemod_cli.py b/libcst/codemod/tests/test_codemod_cli.py index 0309c74a..0fa3dbef 100644 --- a/libcst/codemod/tests/test_codemod_cli.py +++ b/libcst/codemod/tests/test_codemod_cli.py @@ -8,10 +8,12 @@ import platform import subprocess import sys +import tempfile from pathlib import Path from unittest import skipIf from libcst._parser.entrypoints import is_native +from libcst.codemod import CodemodTest from libcst.testing.utils import UnitTest @@ -63,3 +65,31 @@ class TestCodemodCLI(UnitTest): stderr=subprocess.STDOUT, ) assert "Finished codemodding 1 files!" in output + + def test_warning_messages_several_files(self) -> None: + code = """ + def baz() -> str: + return "{}: {}".format(*baz) + """ + with tempfile.TemporaryDirectory() as tmpdir: + p = Path(tmpdir) + (p / "mod1.py").write_text(CodemodTest.make_fixture_data(code)) + (p / "mod2.py").write_text(CodemodTest.make_fixture_data(code)) + (p / "mod3.py").write_text(CodemodTest.make_fixture_data(code)) + output = subprocess.run( + [ + sys.executable, + "-m", + "libcst.tool", + "codemod", + "convert_format_to_fstring.ConvertFormatStringCommand", + str(p), + ], + encoding="utf-8", + stderr=subprocess.PIPE, + ) + # Each module will generate a warning, so we should get 3 warnings in total + self.assertIn( + "- 3 warnings were generated.", + output.stderr, + ) From 5f5fd386b07c71cf18534a0cf0c8d6f3368a4fc7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 09:26:31 +0100 Subject: [PATCH 013/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 237e1fb8..3a57330a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: with: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.19.2 + uses: pypa/cibuildwheel@v2.20.0 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From 52a59471c9a2869af2969f47c226fdfa17b25536 Mon Sep 17 00:00:00 2001 From: Michel Lind Date: Tue, 13 Aug 2024 02:02:12 -0400 Subject: [PATCH 014/151] Use `license` instead of `license-file` (#1189) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- native/libcst/Cargo.toml | 2 +- native/libcst_derive/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 2fd6ced5..81f3bd88 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -10,7 +10,7 @@ authors = ["LibCST Developers"] edition = "2018" rust-version = "1.70" description = "A Python parser and Concrete Syntax Tree library." -license-file = "LICENSE" +license = "MIT AND (MIT AND PSF-2.0)" repository = "https://github.com/Instagram/LibCST" documentation = "https://libcst.rtfd.org" keywords = ["python", "cst", "ast"] diff --git a/native/libcst_derive/Cargo.toml b/native/libcst_derive/Cargo.toml index e2eee2da..47d7ca85 100644 --- a/native/libcst_derive/Cargo.toml +++ b/native/libcst_derive/Cargo.toml @@ -3,7 +3,7 @@ name = "libcst_derive" version = "1.4.0" edition = "2018" description = "Proc macro helpers for libcst." -license-file = "LICENSE" +license = "MIT" repository = "https://github.com/Instagram/LibCST" documentation = "https://libcst.rtfd.org" keywords = ["macros", "python"] From a4203e5c4904360d2fdd437ffb8c2f28aa161ab5 Mon Sep 17 00:00:00 2001 From: Amethyst Reese Date: Thu, 22 Aug 2024 16:06:01 -0700 Subject: [PATCH 015/151] Drop codecov from CI and readme (#1192) * Drop codecov from CI and readme * Remove upload job, move coverage check to test job with hatch --- .github/workflows/ci.yml | 38 ++++++-------------------------------- README.rst | 6 +----- codecov.yml | 4 ---- pyproject.toml | 10 ++++++++-- 4 files changed, 15 insertions(+), 43 deletions(-) delete mode 100644 codecov.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce61c654..5c43745c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,12 +31,17 @@ jobs: toolchain: stable - name: Build LibCST run: hatch -vv env create - - name: Tests + - name: Native Parser Tests run: hatch run test - name: Pure Parser Tests env: + COVERAGE_FILE: .coverage.pure LIBCST_PARSER_TYPE: pure run: hatch run test + - name: Coverage + run: | + hatch run coverage combine .coverage.pure + hatch run coverage report # Run linters lint: @@ -71,37 +76,6 @@ jobs: run: pip install -U hatch - run: hatch run typecheck - # Upload test coverage - coverage: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: actions/setup-python@v5 - with: - cache: pip - cache-dependency-path: "pyproject.toml" - python-version: "3.10" - - name: Install hatch - run: pip install -U hatch - - name: Generate Coverage - run: | - hatch run coverage run setup.py test - hatch run coverage xml -i - - uses: codecov/codecov-action@v4 - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - with: - files: coverage.xml - fail_ci_if_error: true - verbose: true - - name: Archive Coverage - uses: actions/upload-artifact@v4 - with: - name: coverage - path: coverage.xml - # Build the docs docs: runs-on: ubuntu-latest diff --git a/README.rst b/README.rst index 3b3d39ed..90e2f278 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ 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| .. |support-ukraine| image:: https://img.shields.io/badge/Support-Ukraine-FFD500?style=flat&labelColor=005BBB :alt: Support Ukraine - Help Provide Humanitarian Aid to Ukraine. @@ -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 diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index 32f5ee69..00000000 --- a/codecov.yml +++ /dev/null @@ -1,4 +0,0 @@ -coverage: - status: - project: no - patch: yes diff --git a/pyproject.toml b/pyproject.toml index 0ea330e9..f4670704 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ dependencies = ["pyyaml>=5.2"] [project.optional-dependencies] dev = [ "black==23.12.1", - "coverage>=4.5.4", + "coverage[toml]>=4.5.4", "build>=0.10.0", "fixit==2.1.0", "flake8==7.0.0", @@ -51,6 +51,12 @@ Changelog = "https://github.com/Instagram/LibCST/blob/main/CHANGELOG.md" target-version = ["py39"] extend-exclude = '^/native/' # Prepend "^/" to specify root file/folder. See https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#configuration-format +[tool.coverage.report] +fail_under = 93 +precision = 1 +show_missing = true +skip_covered = true + [tool.hatch.envs.default] features = ["dev"] @@ -64,7 +70,7 @@ lint = [ "python -m slotscheck libcst", "python scripts/check_copyright.py", ] -test = ["python --version", "python -m libcst.tests"] +test = ["python --version", "python -m coverage run -m libcst.tests"] typecheck = ["pyre --version", "pyre check"] [tool.slotscheck] From be025613f968e00f41ac1e73d4169762c5065c73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:29:16 -0700 Subject: [PATCH 016/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f4670704..1e825ae1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ dev = [ "pyre-check==0.9.18; platform_system != 'Windows'", "setuptools_scm>=6.0.1", "sphinx-rtd-theme>=0.4.3", - "ufmt==2.6.0", + "ufmt==2.7.0", "usort==1.0.8.post1", "setuptools-rust>=1.5.2", "slotscheck>=0.7.1", From cdf9ef414f2c0a1444cc85c10e7173d8ee2f04a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:29:31 -0700 Subject: [PATCH 017/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1e825ae1..16123a81 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ dev = [ "coverage[toml]>=4.5.4", "build>=0.10.0", "fixit==2.1.0", - "flake8==7.0.0", + "flake8==7.1.1", "Sphinx>=5.1.1", "hypothesis>=4.36.0", "hypothesmith>=0.0.4", From bf5fb4132e04ac87e1f72c86e37c62dc6594dc9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:46:01 -0700 Subject: [PATCH 018/151] 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] * Update formatting --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Amethyst Reese --- libcst/_nodes/base.py | 3 +- libcst/_nodes/expression.py | 27 +++++++------ libcst/_nodes/op.py | 9 ++--- libcst/_nodes/statement.py | 39 +++++++++---------- libcst/_parser/base_parser.py | 6 +-- libcst/_parser/parso/pgen2/generator.py | 6 +-- libcst/_parser/types/config.py | 6 +-- libcst/_position.py | 6 +-- libcst/_typed_visitor.py | 3 ++ .../convert_namedtuple_to_dataclass.py | 4 +- .../convert_percent_format_to_fstring.py | 8 ++-- libcst/codemod/commands/rename.py | 12 +++--- libcst/metadata/base_provider.py | 3 +- libcst/metadata/scope_provider.py | 28 ++++++------- libcst/metadata/tests/test_scope_provider.py | 16 +++++--- libcst/tool.py | 3 +- pyproject.toml | 2 +- 17 files changed, 90 insertions(+), 91 deletions(-) diff --git a/libcst/_nodes/base.py b/libcst/_nodes/base.py index d9689f8f..d043cb64 100644 --- a/libcst/_nodes/base.py +++ b/libcst/_nodes/base.py @@ -292,8 +292,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) diff --git a/libcst/_nodes/expression.py b/libcst/_nodes/expression.py index 75f7da13..a8f2ac3e 100644 --- a/libcst/_nodes/expression.py +++ b/libcst/_nodes/expression.py @@ -1647,9 +1647,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( " " ) @@ -2101,9 +2101,9 @@ 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: @@ -2601,9 +2601,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(" ") @@ -2662,9 +2662,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 @@ -2748,8 +2748,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): diff --git a/libcst/_nodes/op.py b/libcst/_nodes/op.py index e19d24d3..1765f536 100644 --- a/libcst/_nodes/op.py +++ b/libcst/_nodes/op.py @@ -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): diff --git a/libcst/_nodes/statement.py b/libcst/_nodes/statement.py index e02ae03c..e6d6915f 100644 --- a/libcst/_nodes/statement.py +++ b/libcst/_nodes/statement.py @@ -113,8 +113,7 @@ class BaseSmallStatement(CSTNode, ABC): @abstractmethod def _codegen_impl( self, state: CodegenState, default_semicolon: bool = False - ) -> None: - ... + ) -> None: ... @add_slots @@ -273,9 +272,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. @@ -2403,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. @@ -3423,15 +3422,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] = () @@ -3774,16 +3773,16 @@ class TypeAlias(BaseSmallStatement): #: 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_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_after_type_parameters: Union[SimpleWhitespace, MaybeSentinel] = ( + MaybeSentinel.DEFAULT + ) #: Whitespace between the ``=`` and the value. whitespace_after_equals: SimpleWhitespace = SimpleWhitespace.field(" ") diff --git a/libcst/_parser/base_parser.py b/libcst/_parser/base_parser.py index ef9e1519..6ab97ab8 100644 --- a/libcst/_parser/base_parser.py +++ b/libcst/_parser/base_parser.py @@ -129,11 +129,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: """ diff --git a/libcst/_parser/parso/pgen2/generator.py b/libcst/_parser/parso/pgen2/generator.py index 4e20e89d..ae889f33 100644 --- a/libcst/_parser/parso/pgen2/generator.py +++ b/libcst/_parser/parso/pgen2/generator.py @@ -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] = {} diff --git a/libcst/_parser/types/config.py b/libcst/_parser/types/config.py index 1fc32371..289fd8ae 100644 --- a/libcst/_parser/types/config.py +++ b/libcst/_parser/types/config.py @@ -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): diff --git a/libcst/_position.py b/libcst/_position.py index d7ba0d07..e81e9ab4 100644 --- a/libcst/_position.py +++ b/libcst/_position.py @@ -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): diff --git a/libcst/_typed_visitor.py b/libcst/_typed_visitor.py index 742d9f10..e6322353 100644 --- a/libcst/_typed_visitor.py +++ b/libcst/_typed_visitor.py @@ -207,6 +207,7 @@ if TYPE_CHECKING: class CSTTypedBaseFunctions: + @mark_no_op def visit_Add(self, node: "Add") -> Optional[bool]: pass @@ -5763,6 +5764,7 @@ class CSTTypedBaseFunctions: class CSTTypedVisitorFunctions(CSTTypedBaseFunctions): + @mark_no_op def leave_Add(self, original_node: "Add") -> None: pass @@ -6441,6 +6443,7 @@ class CSTTypedVisitorFunctions(CSTTypedBaseFunctions): class CSTTypedTransformerFunctions(CSTTypedBaseFunctions): + @mark_no_op def leave_Add(self, original_node: "Add", updated_node: "Add") -> "BaseBinaryOp": return updated_node diff --git a/libcst/codemod/commands/convert_namedtuple_to_dataclass.py b/libcst/codemod/commands/convert_namedtuple_to_dataclass.py index 91e78048..f1de5b0c 100644 --- a/libcst/codemod/commands/convert_namedtuple_to_dataclass.py +++ b/libcst/codemod/commands/convert_namedtuple_to_dataclass.py @@ -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 diff --git a/libcst/codemod/commands/convert_percent_format_to_fstring.py b/libcst/codemod/commands/convert_percent_format_to_fstring.py index 9908a5b6..501c9621 100644 --- a/libcst/codemod/commands/convert_percent_format_to_fstring.py +++ b/libcst/codemod/commands/convert_percent_format_to_fstring.py @@ -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 ) ) diff --git a/libcst/codemod/commands/rename.py b/libcst/codemod/commands/rename.py index 4bd0ee3d..290f1ac1 100644 --- a/libcst/codemod/commands/rename.py +++ b/libcst/codemod/commands/rename.py @@ -144,9 +144,9 @@ class RenameCommand(VisitorBasedCodemodCommand): self.bypass_import = True if replacement_module != import_alias_full_name: self.scheduled_removals.add(original_node) - new_name_node: Union[ - cst.Attribute, cst.Name - ] = self.gen_name_or_attr_node(replacement_module) + new_name_node: Union[cst.Attribute, cst.Name] = ( + self.gen_name_or_attr_node(replacement_module) + ) new_names.append(cst.ImportAlias(name=new_name_node)) else: new_names.append(import_alias) @@ -198,9 +198,9 @@ class RenameCommand(VisitorBasedCodemodCommand): self.scheduled_removals.add(original_node) continue - new_import_alias_name: Union[ - cst.Attribute, cst.Name - ] = self.gen_name_or_attr_node(replacement_obj) + new_import_alias_name: Union[cst.Attribute, cst.Name] = ( + self.gen_name_or_attr_node(replacement_obj) + ) # Rename on the spot only if this is the only imported name under the module. if len(names) == 1: updated_node = updated_node.with_changes( diff --git a/libcst/metadata/base_provider.py b/libcst/metadata/base_provider.py index 2e03416f..811fed62 100644 --- a/libcst/metadata/base_provider.py +++ b/libcst/metadata/base_provider.py @@ -48,8 +48,7 @@ class GenCacheMethod(Protocol): *, timeout: Optional[int] = None, use_pyproject_toml: bool = False, - ) -> Mapping[str, object]: - ... + ) -> Mapping[str, object]: ... # We can't use an ABCMeta here, because of metaclass conflicts diff --git a/libcst/metadata/scope_provider.py b/libcst/metadata/scope_provider.py index 75f37a06..ac9c4b9d 100644 --- a/libcst/metadata/scope_provider.py +++ b/libcst/metadata/scope_provider.py @@ -199,8 +199,7 @@ class BaseAssignment(abc.ABC): return -1 @abc.abstractmethod - def get_qualified_names_for(self, full_name: str) -> Set[QualifiedName]: - ... + def get_qualified_names_for(self, full_name: str) -> Set[QualifiedName]: ... class Assignment(BaseAssignment): @@ -225,9 +224,11 @@ class Assignment(BaseAssignment): def get_qualified_names_for(self, full_name: str) -> Set[QualifiedName]: return { QualifiedName( - f"{self.scope._name_prefix}.{full_name}" - if self.scope._name_prefix - else full_name, + ( + f"{self.scope._name_prefix}.{full_name}" + if self.scope._name_prefix + else full_name + ), QualifiedNameSource.LOCAL, ) } @@ -306,9 +307,11 @@ class ImportAssignment(Assignment): remaining_name = remaining_name.lstrip(".") results.add( QualifiedName( - f"{real_name}.{remaining_name}" - if remaining_name - else real_name, + ( + f"{real_name}.{remaining_name}" + if remaining_name + else real_name + ), QualifiedNameSource.IMPORT, ) ) @@ -503,19 +506,16 @@ class Scope(abc.ABC): @abc.abstractmethod def _resolve_scope_for_access( self, name: str, from_scope: "Scope" - ) -> Set[BaseAssignment]: - ... + ) -> Set[BaseAssignment]: ... def __hash__(self) -> int: return id(self) @abc.abstractmethod - def record_global_overwrite(self, name: str) -> None: - ... + def record_global_overwrite(self, name: str) -> None: ... @abc.abstractmethod - def record_nonlocal_overwrite(self, name: str) -> None: - ... + def record_nonlocal_overwrite(self, name: str) -> None: ... def get_qualified_names_for( self, node: Union[str, cst.CSTNode] diff --git a/libcst/metadata/tests/test_scope_provider.py b/libcst/metadata/tests/test_scope_provider.py index a2087645..fd23e993 100644 --- a/libcst/metadata/tests/test_scope_provider.py +++ b/libcst/metadata/tests/test_scope_provider.py @@ -653,12 +653,16 @@ class ScopeProviderTest(UnitTest): for assignment in scope_of_outer_f["var"] }, { - outer_f_body_var.targets[0].target - if isinstance(outer_f_body_var, cst.Assign) - else outer_f_body_var, - inner_f_body_var.targets[0].target - if isinstance(inner_f_body_var, cst.Assign) - else inner_f_body_var, + ( + outer_f_body_var.targets[0].target + if isinstance(outer_f_body_var, cst.Assign) + else outer_f_body_var + ), + ( + inner_f_body_var.targets[0].target + if isinstance(inner_f_body_var, cst.Assign) + else inner_f_body_var + ), }, ) diff --git a/libcst/tool.py b/libcst/tool.py index 3c00ba8d..ace15ff6 100644 --- a/libcst/tool.py +++ b/libcst/tool.py @@ -461,8 +461,7 @@ class _SerializerBase(ABC): return f"{comments}{os.linesep}{self._serialize_impl(key, value)}{os.linesep}" @abstractmethod - def _serialize_impl(self, key: str, value: object) -> str: - ... + def _serialize_impl(self, key: str, value: object) -> str: ... class _StrSerializer(_SerializerBase): diff --git a/pyproject.toml b/pyproject.toml index 16123a81..7ff3421a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = ["pyyaml>=5.2"] [project.optional-dependencies] dev = [ - "black==23.12.1", + "black==24.8.0", "coverage[toml]>=4.5.4", "build>=0.10.0", "fixit==2.1.0", From 2e496954276e639d9d8229a4d99b55821f2d7e61 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:21:43 -0700 Subject: [PATCH 019/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index e62007e4..227f0a22 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -407,9 +407,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 81f3bd88..95cb4f1e 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -41,7 +41,7 @@ thiserror = "1.0.37" peg = "0.8.1" chic = "1.2.2" regex = "1.9.3" -memchr = "2.5.0" +memchr = "2.7.4" libcst_derive = { path = "../libcst_derive", version = "1.4.0" } [dev-dependencies] From b552469f1c413e69e8840ff067f5c7dbc1e9d245 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:21:56 -0700 Subject: [PATCH 020/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 6 +++--- native/libcst/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 227f0a22..1685477e 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -319,9 +319,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -360,7 +360,7 @@ dependencies = [ "chic", "criterion", "difference", - "itertools 0.11.0", + "itertools 0.13.0", "libcst_derive", "memchr", "paste", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 95cb4f1e..eab93261 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -48,7 +48,7 @@ libcst_derive = { path = "../libcst_derive", version = "1.4.0" } criterion = { version = "0.5.1", features = ["html_reports"] } difference = "2.0.0" rayon = "1.7.0" -itertools = "0.11.0" +itertools = "0.13.0" [[bench]] name = "parser_benchmark" From 6017c40d19e54e51bba6a79280525a15a8d7de22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:22:20 -0700 Subject: [PATCH 021/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 1685477e..b2ad9036 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -487,9 +487,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.9" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "peg" diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index eab93261..47d2ab33 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -35,7 +35,7 @@ py = ["pyo3", "pyo3/extension-module"] trace = ["peg/trace"] [dependencies] -paste = "1.0.9" +paste = "1.0.15" pyo3 = { version = "0.20", optional = true } thiserror = "1.0.37" peg = "0.8.1" From 07ec61d8b007d8fafa999e4807e69a72855e98ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:22:34 -0700 Subject: [PATCH 022/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 10 +++++----- native/libcst/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index b2ad9036..1a72cd4f 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -795,22 +795,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.41", ] [[package]] diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 47d2ab33..5be6e7fb 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -37,7 +37,7 @@ trace = ["peg/trace"] [dependencies] paste = "1.0.15" pyo3 = { version = "0.20", optional = true } -thiserror = "1.0.37" +thiserror = "1.0.55" peg = "0.8.1" chic = "1.2.2" regex = "1.9.3" From 9f198179f3980b3fa417ade3f265576dcf5cd442 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:23:25 -0700 Subject: [PATCH 023/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c43745c..84a3e3d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,7 +90,7 @@ jobs: python-version: "3.10" - name: Install hatch run: pip install -U hatch - - uses: ts-graphviz/setup-graphviz@v1 + - uses: ts-graphviz/setup-graphviz@v2 - run: hatch run docs - name: Archive Docs uses: actions/upload-artifact@v4 From 38cc0798b2c5384de06f05e52a857c367417b5be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:26:14 -0700 Subject: [PATCH 024/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 57 ++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 1a72cd4f..af9db1c7 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -35,6 +35,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -376,7 +385,7 @@ name = "libcst_derive" version = "1.4.0" dependencies = [ "quote", - "syn 2.0.41", + "syn 2.0.75", "trybuild", ] @@ -548,9 +557,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -601,7 +610,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.41", + "syn 2.0.75", ] [[package]] @@ -613,14 +622,14 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.75", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -721,31 +730,32 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.75", ] [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -769,9 +779,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.41" +version = "2.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" dependencies = [ "proc-macro2", "quote", @@ -823,28 +833,19 @@ dependencies = [ "serde_json", ] -[[package]] -name = "toml" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" -dependencies = [ - "serde", -] - [[package]] name = "trybuild" -version = "1.0.71" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea496675d71016e9bc76aa42d87f16aefd95447cc5818e671e12b2d7e269075d" +checksum = "8419ecd263363827c5730386f418715766f584e2f874d32c23c5b00bd9727e7e" dependencies = [ + "basic-toml", "glob", "once_cell", "serde", "serde_derive", "serde_json", "termcolor", - "toml", ] [[package]] From 47b171b9a7d341d9eafda6d904984fd89294d99d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:40:50 -0700 Subject: [PATCH 025/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 43 ++++++---------------------------------- native/libcst/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index af9db1c7..d805ff11 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -180,16 +180,6 @@ dependencies = [ "itertools 0.10.5", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - [[package]] name = "crossbeam-deque" version = "0.8.1" @@ -276,15 +266,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.2" @@ -312,7 +293,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi", "rustix", "windows-sys", ] @@ -447,16 +428,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi 0.1.19", - "libc", -] - [[package]] name = "once_cell" version = "1.16.0" @@ -636,9 +607,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.7.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -646,14 +617,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] @@ -820,7 +789,7 @@ checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.75", ] [[package]] diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 5be6e7fb..ec7b5897 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -47,7 +47,7 @@ libcst_derive = { path = "../libcst_derive", version = "1.4.0" } [dev-dependencies] criterion = { version = "0.5.1", features = ["html_reports"] } difference = "2.0.0" -rayon = "1.7.0" +rayon = "1.10.0" itertools = "0.13.0" [[bench]] From 77e2a51d35d671e2061ef036bd03ad01562658e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:42:44 -0700 Subject: [PATCH 026/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 12 ++++++------ native/libcst/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index d805ff11..ff5cb7c7 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -473,9 +473,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "peg" -version = "0.8.1" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07f2cafdc3babeebc087e499118343442b742cc7c31b4d054682cc598508554" +checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f" dependencies = [ "peg-macros", "peg-runtime", @@ -483,9 +483,9 @@ dependencies = [ [[package]] name = "peg-macros" -version = "0.8.1" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a90084dc05cf0428428e3d12399f39faad19b0909f64fb9170c9fdd6d9cd49b" +checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426" dependencies = [ "peg-runtime", "proc-macro2", @@ -494,9 +494,9 @@ dependencies = [ [[package]] name = "peg-runtime" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa00462b37ead6d11a82c9d568b26682d78e0477dc02d1966c013af80969739" +checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" [[package]] name = "plotters" diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index ec7b5897..249d2023 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -38,7 +38,7 @@ trace = ["peg/trace"] paste = "1.0.15" pyo3 = { version = "0.20", optional = true } thiserror = "1.0.55" -peg = "0.8.1" +peg = "0.8.4" chic = "1.2.2" regex = "1.9.3" memchr = "2.7.4" From 8c5aa32000485f1f5352a059bebe2c624a8de67d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 18:47:27 -0700 Subject: [PATCH 027/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 8 ++++---- native/libcst/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index ff5cb7c7..620e9b6b 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -774,18 +774,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.55" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.55" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 249d2023..51b15799 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -37,7 +37,7 @@ trace = ["peg/trace"] [dependencies] paste = "1.0.15" pyo3 = { version = "0.20", optional = true } -thiserror = "1.0.55" +thiserror = "1.0.63" peg = "0.8.4" chic = "1.2.2" regex = "1.9.3" From ccf9623ccf03ca905db5bddcc949023f29f836f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 18:47:34 -0700 Subject: [PATCH 028/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 92 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 14 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 620e9b6b..09d04218 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -35,15 +35,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "basic-toml" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" -dependencies = [ - "serde", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -227,6 +218,12 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.2" @@ -260,6 +257,12 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "heck" version = "0.4.1" @@ -272,6 +275,16 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +[[package]] +name = "indexmap" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "indoc" version = "2.0.4" @@ -729,6 +742,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + [[package]] name = "smallvec" version = "1.8.1" @@ -803,18 +825,51 @@ dependencies = [ ] [[package]] -name = "trybuild" -version = "1.0.86" +name = "toml" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419ecd263363827c5730386f418715766f584e2f874d32c23c5b00bd9727e7e" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "trybuild" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207aa50d36c4be8d8c6ea829478be44a372c6a77669937bb39c698e52f1491e8" dependencies = [ - "basic-toml", "glob", - "once_cell", "serde", "serde_derive", "serde_json", "termcolor", + "toml", ] [[package]] @@ -1000,3 +1055,12 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] From 9834694730b868fa36688bad0d429fac8b758990 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 10:26:36 -0700 Subject: [PATCH 029/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 12 ++++++------ native/libcst/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 09d04218..e26b3353 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -649,9 +649,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.3" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -661,9 +661,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -672,9 +672,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustix" diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 51b15799..11e3e362 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -40,7 +40,7 @@ pyo3 = { version = "0.20", optional = true } thiserror = "1.0.63" peg = "0.8.4" chic = "1.2.2" -regex = "1.9.3" +regex = "1.10.6" memchr = "2.7.4" libcst_derive = { path = "../libcst_derive", version = "1.4.0" } From 61b9ac3a6872d97cfa72bcb02dea1a6ca73bf73e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 07:37:01 +0100 Subject: [PATCH 030/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3a57330a..060b3caa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: with: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.20.0 + uses: pypa/cibuildwheel@v2.21.0 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From 0974a416a79b3f1074e838ee8340e412e2e5e1b7 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Wed, 18 Sep 2024 03:23:30 -0500 Subject: [PATCH 031/151] Typo fix in codemods_tutorial.rst (trivial) (#1208) --- docs/source/codemods_tutorial.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/codemods_tutorial.rst b/docs/source/codemods_tutorial.rst index 8fc71bea..6f657fbc 100644 --- a/docs/source/codemods_tutorial.rst +++ b/docs/source/codemods_tutorial.rst @@ -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 `_, blackisting +external code formatter such as `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 From 6a059bec9a41e787f2c0158a2c3680a81d94a99d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:22:57 +0100 Subject: [PATCH 032/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 060b3caa..da29a104 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: with: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.21.0 + uses: pypa/cibuildwheel@v2.21.1 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From 9fd67bca49fbb45b9572c5c2bf5131f2c05245cd Mon Sep 17 00:00:00 2001 From: Kirill Ignatev Date: Wed, 25 Sep 2024 06:29:54 -0400 Subject: [PATCH 033/151] 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 --- libcst/codemod/tests/test_codemod_cli.py | 31 ++++++++ libcst/matchers/_visitors.py | 95 +++++++++++++----------- 2 files changed, 83 insertions(+), 43 deletions(-) diff --git a/libcst/codemod/tests/test_codemod_cli.py b/libcst/codemod/tests/test_codemod_cli.py index 0fa3dbef..8773cd77 100644 --- a/libcst/codemod/tests/test_codemod_cli.py +++ b/libcst/codemod/tests/test_codemod_cli.py @@ -93,3 +93,34 @@ class TestCodemodCLI(UnitTest): "- 3 warnings were generated.", output.stderr, ) + + def test_matcher_decorators_multiprocessing(self) -> None: + file_count = 5 + code = """ + def baz(): # type: int + return 5 + """ + with tempfile.TemporaryDirectory() as tmpdir: + p = Path(tmpdir) + # Using more than chunksize=4 files to trigger multiprocessing + for i in range(file_count): + (p / f"mod{i}.py").write_text(CodemodTest.make_fixture_data(code)) + output = subprocess.run( + [ + sys.executable, + "-m", + "libcst.tool", + "codemod", + # Good candidate since it uses matcher decorators + "convert_type_comments.ConvertTypeComments", + str(p), + "--jobs", + str(file_count), + ], + encoding="utf-8", + stderr=subprocess.PIPE, + ) + self.assertIn( + f"Transformed {file_count} files successfully.", + output.stderr, + ) diff --git a/libcst/matchers/_visitors.py b/libcst/matchers/_visitors.py index 9349c5b5..b9252173 100644 --- a/libcst/matchers/_visitors.py +++ b/libcst/matchers/_visitors.py @@ -60,6 +60,11 @@ CONCRETE_METHODS: Set[str] = { } +def is_property(obj: object, attr_name: str) -> bool: + """Check if obj.attr is a property without evaluating it.""" + return isinstance(getattr(type(obj), attr_name, None), property) + + # pyre-ignore We don't care about Any here, its not exposed. def _match_decorator_unpickler(kwargs: Any) -> "MatchDecoratorMismatch": return MatchDecoratorMismatch(**kwargs) @@ -265,20 +270,22 @@ def _check_types( ) -def _gather_matchers(obj: object) -> Set[BaseMatcherNode]: - visit_matchers: Set[BaseMatcherNode] = set() +def _gather_matchers(obj: object) -> Dict[BaseMatcherNode, Optional[cst.CSTNode]]: + """ + Set of gating matchers that we need to track and evaluate. We use these + in conjunction with the call_if_inside and call_if_not_inside decorators + to determine whether to call a visit/leave function. + """ - for func in dir(obj): - try: - for matcher in getattr(getattr(obj, func), VISIT_POSITIVE_MATCHER_ATTR, []): - visit_matchers.add(cast(BaseMatcherNode, matcher)) - for matcher in getattr(getattr(obj, func), VISIT_NEGATIVE_MATCHER_ATTR, []): - visit_matchers.add(cast(BaseMatcherNode, matcher)) - except Exception: - # This could be a caculated property, and calling getattr() evaluates it. - # We have no control over the implementation detail, so if it raises, we - # should not crash. - pass + visit_matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = {} + + for attr_name in dir(obj): + if not is_property(obj, attr_name): + func = getattr(obj, attr_name) + for matcher in getattr(func, VISIT_POSITIVE_MATCHER_ATTR, []): + visit_matchers[cast(BaseMatcherNode, matcher)] = None + for matcher in getattr(func, VISIT_NEGATIVE_MATCHER_ATTR, []): + visit_matchers[cast(BaseMatcherNode, matcher)] = None return visit_matchers @@ -302,16 +309,12 @@ def _gather_constructed_visit_funcs( ] = {} for funcname in dir(obj): - try: - possible_func = getattr(obj, funcname) - if not ismethod(possible_func): - continue - func = cast(Callable[[cst.CSTNode], None], possible_func) - except Exception: - # This could be a caculated property, and calling getattr() evaluates it. - # We have no control over the implementation detail, so if it raises, we - # should not crash. + if is_property(obj, funcname): continue + possible_func = getattr(obj, funcname) + if not ismethod(possible_func): + continue + func = cast(Callable[[cst.CSTNode], None], possible_func) matchers = getattr(func, CONSTRUCTED_VISIT_MATCHER_ATTR, []) if matchers: # Make sure that we aren't accidentally putting a @visit on a visit_Node. @@ -337,16 +340,12 @@ def _gather_constructed_leave_funcs( ] = {} for funcname in dir(obj): - try: - possible_func = getattr(obj, funcname) - if not ismethod(possible_func): - continue - func = cast(Callable[[cst.CSTNode], None], possible_func) - except Exception: - # This could be a caculated property, and calling getattr() evaluates it. - # We have no control over the implementation detail, so if it raises, we - # should not crash. + if is_property(obj, funcname): continue + possible_func = getattr(obj, funcname) + if not ismethod(possible_func): + continue + func = cast(Callable[[cst.CSTNode], None], possible_func) matchers = getattr(func, CONSTRUCTED_LEAVE_MATCHER_ATTR, []) if matchers: # Make sure that we aren't accidentally putting a @leave on a leave_Node. @@ -448,12 +447,7 @@ class MatcherDecoratableTransformer(CSTTransformer): def __init__(self) -> None: CSTTransformer.__init__(self) - # List of gating matchers that we need to track and evaluate. We use these - # in conjuction with the call_if_inside and call_if_not_inside decorators - # to determine whether or not to call a visit/leave function. - self._matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = { - m: None for m in _gather_matchers(self) - } + self.__matchers: Optional[Dict[BaseMatcherNode, Optional[cst.CSTNode]]] = None # Mapping of matchers to functions. If in the course of visiting the tree, # a node matches one of these matchers, the corresponding function will be # called as if it was a visit_* method. @@ -486,6 +480,16 @@ class MatcherDecoratableTransformer(CSTTransformer): expected_none_return=False, ) + @property + def _matchers(self) -> Dict[BaseMatcherNode, Optional[cst.CSTNode]]: + if self.__matchers is None: + self.__matchers = _gather_matchers(self) + return self.__matchers + + @_matchers.setter + def _matchers(self, value: Dict[BaseMatcherNode, Optional[cst.CSTNode]]) -> None: + self.__matchers = value + def on_visit(self, node: cst.CSTNode) -> bool: # First, evaluate any matchers that we have which we are not inside already. self._matchers = _visit_matchers(self._matchers, node, self) @@ -660,12 +664,7 @@ class MatcherDecoratableVisitor(CSTVisitor): def __init__(self) -> None: CSTVisitor.__init__(self) - # List of gating matchers that we need to track and evaluate. We use these - # in conjuction with the call_if_inside and call_if_not_inside decorators - # to determine whether or not to call a visit/leave function. - self._matchers: Dict[BaseMatcherNode, Optional[cst.CSTNode]] = { - m: None for m in _gather_matchers(self) - } + self.__matchers: Optional[Dict[BaseMatcherNode, Optional[cst.CSTNode]]] = None # Mapping of matchers to functions. If in the course of visiting the tree, # a node matches one of these matchers, the corresponding function will be # called as if it was a visit_* method. @@ -693,6 +692,16 @@ class MatcherDecoratableVisitor(CSTVisitor): expected_none_return=True, ) + @property + def _matchers(self) -> Dict[BaseMatcherNode, Optional[cst.CSTNode]]: + if self.__matchers is None: + self.__matchers = _gather_matchers(self) + return self.__matchers + + @_matchers.setter + def _matchers(self, value: Dict[BaseMatcherNode, Optional[cst.CSTNode]]) -> None: + self.__matchers = value + def on_visit(self, node: cst.CSTNode) -> bool: # First, evaluate any matchers that we have which we are not inside already. self._matchers = _visit_matchers(self._matchers, node, self) From 586b4d74e4e2b1ddc483cf2538d0b9d6eebcfe0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:32:36 +0100 Subject: [PATCH 034/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7ff3421a..af9328f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ dev = [ "pyre-check==0.9.18; platform_system != 'Windows'", "setuptools_scm>=6.0.1", "sphinx-rtd-theme>=0.4.3", - "ufmt==2.7.0", + "ufmt==2.7.3", "usort==1.0.8.post1", "setuptools-rust>=1.5.2", "slotscheck>=0.7.1", From b49e705579ab47498d542771a8e704a070638474 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 09:22:46 +0100 Subject: [PATCH 035/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index da29a104..b780639b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: with: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.21.1 + uses: pypa/cibuildwheel@v2.21.2 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From a2b3456fe9f205b24de3dffb4750d4c70a8e4f0c Mon Sep 17 00:00:00 2001 From: khameeteman <73279381+khameeteman@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:38:27 +0200 Subject: [PATCH 036/151] include python 3.13 in build (#1203) --- .github/workflows/ci.yml | 2 +- pyproject.toml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84a3e3d6..e8ff9207 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: os: [macos-latest, ubuntu-latest, windows-latest] - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 with: diff --git a/pyproject.toml b/pyproject.toml index af9328f3..79ce0ba1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools", "setuptools-scm", "setuptools-rust", "wheel"] [project] name = "libcst" -description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.12 programs." +description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.13 programs." readme = "README.rst" dynamic = ["version"] license = { file = "LICENSE" } @@ -14,6 +14,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] requires-python = ">=3.9" dependencies = ["pyyaml>=5.2"] From dde88a2082fa99b9fb8c5d2544c3eadfb63815fc Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 10 Oct 2024 10:53:01 +0100 Subject: [PATCH 037/151] add changelog entry --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bccbc23..ee0f25b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +# 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 From 527a4b04e15d7e98d2b9c46c68594d8c8a233208 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 10 Oct 2024 10:54:15 +0100 Subject: [PATCH 038/151] bump versions in cargo.toml --- native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 2 +- native/libcst_derive/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index e26b3353..958df42c 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -358,7 +358,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libcst" -version = "1.4.0" +version = "1.5.0" dependencies = [ "chic", "criterion", @@ -376,7 +376,7 @@ dependencies = [ [[package]] name = "libcst_derive" -version = "1.4.0" +version = "1.5.0" dependencies = [ "quote", "syn 2.0.75", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 11e3e362..a1aaadad 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "libcst" -version = "1.4.0" +version = "1.5.0" authors = ["LibCST Developers"] edition = "2018" rust-version = "1.70" diff --git a/native/libcst_derive/Cargo.toml b/native/libcst_derive/Cargo.toml index 47d7ca85..7566c87d 100644 --- a/native/libcst_derive/Cargo.toml +++ b/native/libcst_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libcst_derive" -version = "1.4.0" +version = "1.5.0" edition = "2018" description = "Proc macro helpers for libcst." license = "MIT" From 42df0881baf0e7a388ecaec0bf4cc28ab758f01c Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 10 Oct 2024 11:20:25 +0100 Subject: [PATCH 039/151] Fix doc build error (#1221) Apparently doc2path now returns a path not a string --- docs/source/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index c210fc1d..d3311e90 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -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 From bfd1000289296436e9364013d1ce08808cd19da4 Mon Sep 17 00:00:00 2001 From: Blazej Michalik <6691643+MrMino@users.noreply.github.com> Date: Sun, 17 Nov 2024 11:19:27 +0100 Subject: [PATCH 040/151] ci: build wheels for musllinux (#1243) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b780639b..0d3d7698 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: 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_SKIP: "cp27-* cp34-* cp35-* pp* *-win32 *-win_arm64" CIBW_ARCHS_LINUX: auto aarch64 CIBW_BUILD_VERBOSITY: 1 steps: From 4ff38c039e075d5e3a8ebdb81ff2c8419c281066 Mon Sep 17 00:00:00 2001 From: Blazej Michalik <6691643+MrMino@users.noreply.github.com> Date: Sun, 17 Nov 2024 19:01:34 +0100 Subject: [PATCH 041/151] 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. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0d3d7698..81381f50 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: 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" + CIBW_SKIP: "cp27-* cp34-* cp35-* pp* *-win32 *-win_arm64 *-musllinux_i686 *-musllinux_ppc64le *-musllinux_s390x *-musllinux_armv7l" CIBW_ARCHS_LINUX: auto aarch64 CIBW_BUILD_VERBOSITY: 1 steps: From 4aa92f3857afb79b790d112da80d36fe93bd57ed Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Mon, 18 Nov 2024 20:19:01 +0000 Subject: [PATCH 042/151] Bump version to 1.5.1 (#1246) --- CHANGELOG.md | 11 +++++++++++ native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 2 +- native/libcst_derive/Cargo.toml | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee0f25b6..8e9d5aab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# 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 diff --git a/native/Cargo.lock b/native/Cargo.lock index 958df42c..00409e83 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -358,7 +358,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libcst" -version = "1.5.0" +version = "1.5.1" dependencies = [ "chic", "criterion", @@ -376,7 +376,7 @@ dependencies = [ [[package]] name = "libcst_derive" -version = "1.5.0" +version = "1.5.1" dependencies = [ "quote", "syn 2.0.75", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index a1aaadad..7cdcc61e 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "libcst" -version = "1.5.0" +version = "1.5.1" authors = ["LibCST Developers"] edition = "2018" rust-version = "1.70" diff --git a/native/libcst_derive/Cargo.toml b/native/libcst_derive/Cargo.toml index 7566c87d..9ede217d 100644 --- a/native/libcst_derive/Cargo.toml +++ b/native/libcst_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libcst_derive" -version = "1.5.0" +version = "1.5.1" edition = "2018" description = "Proc macro helpers for libcst." license = "MIT" From 08da127e5434eb42bae5dc71c03363d6a281d4bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:27:11 +0000 Subject: [PATCH 043/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 81381f50..886947d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: with: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.21.2 + uses: pypa/cibuildwheel@v2.22.0 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From 6fdca74c90f790728f20b9710d333f523e3970c6 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 28 Nov 2024 14:59:43 +0000 Subject: [PATCH 044/151] 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. --- libcst/codemod/commands/rename.py | 38 +++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/libcst/codemod/commands/rename.py b/libcst/codemod/commands/rename.py index 290f1ac1..9d710cca 100644 --- a/libcst/codemod/commands/rename.py +++ b/libcst/codemod/commands/rename.py @@ -92,14 +92,38 @@ class RenameCommand(VisitorBasedCodemodCommand): self.old_module: str = old_module self.old_mod_or_obj: str = old_mod_or_obj - self.as_name: Optional[Tuple[str, str]] = None + @property + def as_name(self) -> Optional[Tuple[str, str]]: + if "as_name" not in self.context.scratch: + self.context.scratch["as_name"] = None + return self.context.scratch["as_name"] - # A set of nodes that have been renamed to help with the cleanup of now potentially unused - # imports, during import cleanup in `leave_Module`. - self.scheduled_removals: Set[cst.CSTNode] = set() - # If an import has been renamed while inside an `Import` or `ImportFrom` node, we want to flag - # this so that we do not end up with two of the same import. - self.bypass_import = False + @as_name.setter + def as_name(self, value: Optional[Tuple[str, str]]) -> None: + self.context.scratch["as_name"] = value + + @property + def scheduled_removals(self) -> Set[cst.CSTNode]: + """A set of nodes that have been renamed to help with the cleanup of now potentially unused + imports, during import cleanup in `leave_Module`.""" + if "scheduled_removals" not in self.context.scratch: + self.context.scratch["scheduled_removals"] = set() + return self.context.scratch["scheduled_removals"] + + @scheduled_removals.setter + def scheduled_removals(self, value: Set[cst.CSTNode]) -> None: + self.context.scratch["scheduled_removals"] = value + + @property + def bypass_import(self) -> bool: + """A flag to indicate that an import has been renamed while inside an `Import` or `ImportFrom` node.""" + if "bypass_import" not in self.context.scratch: + self.context.scratch["bypass_import"] = False + return self.context.scratch["bypass_import"] + + @bypass_import.setter + def bypass_import(self, value: bool) -> None: + self.context.scratch["bypass_import"] = value def visit_Import(self, node: cst.Import) -> None: for import_alias in node.names: From 28e0f397b278f061f3c6cef9bf80a0422b7b447e Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 28 Nov 2024 20:02:23 +0000 Subject: [PATCH 045/151] 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` --- libcst/codemod/commands/rename.py | 48 ++++++++++---------- libcst/codemod/commands/tests/test_rename.py | 36 +++++++++++++++ 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/libcst/codemod/commands/rename.py b/libcst/codemod/commands/rename.py index 9d710cca..ae7138c8 100644 --- a/libcst/codemod/commands/rename.py +++ b/libcst/codemod/commands/rename.py @@ -142,29 +142,23 @@ class RenameCommand(VisitorBasedCodemodCommand): ) -> cst.Import: new_names = [] for import_alias in updated_node.names: + # We keep the original import_alias here in case it's used by other symbols. + # It will be removed later in RemoveImportsVisitor if it's unused. + new_names.append(import_alias) import_alias_name = import_alias.name import_alias_full_name = get_full_name_for_node(import_alias_name) if import_alias_full_name is None: raise Exception("Could not parse full name for ImportAlias.name node.") - if isinstance(import_alias_name, cst.Name) and self.old_name.startswith( - import_alias_full_name + "." - ): - # Might, be in use elsewhere in the code, so schedule a potential removal, and add another alias. - new_names.append(import_alias) - replacement_module = self.gen_replacement_module(import_alias_full_name) - self.bypass_import = True - if replacement_module != import_alias_name.value: - self.scheduled_removals.add(original_node) - new_names.append( - cst.ImportAlias(name=cst.Name(value=replacement_module)) - ) - elif isinstance( - import_alias_name, cst.Attribute + if isinstance( + import_alias_name, (cst.Name, cst.Attribute) ) and self.old_name.startswith(import_alias_full_name + "."): - # Same idea as above. - new_names.append(import_alias) replacement_module = self.gen_replacement_module(import_alias_full_name) + if not replacement_module: + # here import_alias_full_name isn't an exact match for old_name + # don't add an import here, it will be handled either in more + # specific import aliases or at the very end + continue self.bypass_import = True if replacement_module != import_alias_full_name: self.scheduled_removals.add(original_node) @@ -172,8 +166,6 @@ class RenameCommand(VisitorBasedCodemodCommand): self.gen_name_or_attr_node(replacement_module) ) new_names.append(cst.ImportAlias(name=new_name_node)) - else: - new_names.append(import_alias) return updated_node.with_changes(names=new_names) @@ -289,10 +281,14 @@ class RenameCommand(VisitorBasedCodemodCommand): if not inside_import_statement: self.scheduled_removals.add(original_node.value) if full_replacement_name == self.new_name: - return updated_node.with_changes( - value=cst.parse_expression(new_value), - attr=cst.Name(value=new_attr.rstrip(".")), - ) + value = cst.parse_expression(new_value) + if new_attr: + return updated_node.with_changes( + value=value, + attr=cst.Name(value=new_attr.rstrip(".")), + ) + assert isinstance(value, (cst.Name, cst.Attribute)) + return value return self.gen_name_or_attr_node(new_attr) @@ -329,8 +325,12 @@ class RenameCommand(VisitorBasedCodemodCommand): if original_name == self.old_mod_or_obj: return self.new_mod_or_obj - elif original_name == ".".join([self.old_module, self.old_mod_or_obj]): - return self.new_name + elif original_name == self.old_name: + return ( + self.new_mod_or_obj + if (not self.bypass_import and self.new_mod_or_obj) + else self.new_name + ) elif original_name.endswith("." + self.old_mod_or_obj): return self.new_mod_or_obj else: diff --git a/libcst/codemod/commands/tests/test_rename.py b/libcst/codemod/commands/tests/test_rename.py index 2f897119..efcfbc6e 100644 --- a/libcst/codemod/commands/tests/test_rename.py +++ b/libcst/codemod/commands/tests/test_rename.py @@ -705,3 +705,39 @@ class TestRenameCommand(CodemodTest): old_name="a.b.qux", new_name="a:b.qux", ) + + def test_import_parent_module(self) -> None: + before = """ + import a + a.b.c(a.b.c.d) + """ + after = """ + from z import c + + c(c.d) + """ + self.assertCodemod(before, after, old_name="a.b.c", new_name="z.c") + + def test_import_parent_module_2(self) -> None: + before = """ + import a.b + a.b.c.d(a.b.c.d.x) + """ + after = """ + from z import c + + c(c.x) + """ + self.assertCodemod(before, after, old_name="a.b.c.d", new_name="z.c") + + def test_import_parent_module_3(self) -> None: + before = """ + import a + a.b.c(a.b.c.d) + """ + after = """ + import z.c + + z.c(z.c.d) + """ + self.assertCodemod(before, after, old_name="a.b.c", new_name="z.c:") From a36432c958fc34bdf28e19f2962ac9f70d35d9f9 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Fri, 29 Nov 2024 11:23:59 +0000 Subject: [PATCH 046/151] 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. --- libcst/codemod/commands/rename.py | 16 ++++++++-- libcst/codemod/commands/tests/test_rename.py | 32 ++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/libcst/codemod/commands/rename.py b/libcst/codemod/commands/rename.py index ae7138c8..aad4cea6 100644 --- a/libcst/codemod/commands/rename.py +++ b/libcst/codemod/commands/rename.py @@ -150,9 +150,7 @@ class RenameCommand(VisitorBasedCodemodCommand): if import_alias_full_name is None: raise Exception("Could not parse full name for ImportAlias.name node.") - if isinstance( - import_alias_name, (cst.Name, cst.Attribute) - ) and self.old_name.startswith(import_alias_full_name + "."): + if self.old_name.startswith(import_alias_full_name + "."): replacement_module = self.gen_replacement_module(import_alias_full_name) if not replacement_module: # here import_alias_full_name isn't an exact match for old_name @@ -166,6 +164,18 @@ class RenameCommand(VisitorBasedCodemodCommand): self.gen_name_or_attr_node(replacement_module) ) new_names.append(cst.ImportAlias(name=new_name_node)) + elif ( + import_alias_full_name == self.new_name + and import_alias.asname is not None + ): + self.bypass_import = True + # TODO: put this into self.scheduled_removals + RemoveImportsVisitor.remove_unused_import( + self.context, + import_alias.evaluated_name, + asname=import_alias.evaluated_alias, + ) + new_names.append(import_alias.with_changes(asname=None)) return updated_node.with_changes(names=new_names) diff --git a/libcst/codemod/commands/tests/test_rename.py b/libcst/codemod/commands/tests/test_rename.py index efcfbc6e..20e1c7d4 100644 --- a/libcst/codemod/commands/tests/test_rename.py +++ b/libcst/codemod/commands/tests/test_rename.py @@ -111,6 +111,27 @@ class TestRenameCommand(CodemodTest): new_name="baz.quux", ) + def test_rename_attr_asname_2(self) -> None: + before = """ + import foo.qux as bar + + def test() -> None: + bar.z(5) + """ + after = """ + import baz.quux + + def test() -> None: + baz.quux.z(5) + """ + + self.assertCodemod( + before, + after, + old_name="foo.qux", + new_name="baz.quux", + ) + def test_rename_module_import(self) -> None: before = """ import a.b @@ -741,3 +762,14 @@ class TestRenameCommand(CodemodTest): z.c(z.c.d) """ self.assertCodemod(before, after, old_name="a.b.c", new_name="z.c:") + + def test_import_parent_module_asname(self) -> None: + before = """ + import a.b as alias + alias.c(alias.c.d) + """ + after = """ + import z + z.c(z.c.d) + """ + self.assertCodemod(before, after, old_name="a.b.c", new_name="z.c") From c05ac74b9a0c6e2cc071e04bbc54686671aae776 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Mon, 2 Dec 2024 10:00:35 +0000 Subject: [PATCH 047/151] refactor: allow scheduled_removals to accept a tuple (#1253) This fixes a TODO --- libcst/codemod/commands/rename.py | 34 ++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/libcst/codemod/commands/rename.py b/libcst/codemod/commands/rename.py index aad4cea6..8b6302c9 100644 --- a/libcst/codemod/commands/rename.py +++ b/libcst/codemod/commands/rename.py @@ -103,15 +103,20 @@ class RenameCommand(VisitorBasedCodemodCommand): self.context.scratch["as_name"] = value @property - def scheduled_removals(self) -> Set[cst.CSTNode]: + def scheduled_removals( + self, + ) -> Set[Union[cst.CSTNode, Tuple[str, Optional[str], Optional[str]]]]: """A set of nodes that have been renamed to help with the cleanup of now potentially unused - imports, during import cleanup in `leave_Module`.""" + imports, during import cleanup in `leave_Module`. Can also contain tuples that can be passed + directly to RemoveImportsVisitor.remove_unused_import().""" if "scheduled_removals" not in self.context.scratch: self.context.scratch["scheduled_removals"] = set() return self.context.scratch["scheduled_removals"] @scheduled_removals.setter - def scheduled_removals(self, value: Set[cst.CSTNode]) -> None: + def scheduled_removals( + self, value: Set[Union[cst.CSTNode, Tuple[str, Optional[str], Optional[str]]]] + ) -> None: self.context.scratch["scheduled_removals"] = value @property @@ -169,11 +174,13 @@ class RenameCommand(VisitorBasedCodemodCommand): and import_alias.asname is not None ): self.bypass_import = True - # TODO: put this into self.scheduled_removals - RemoveImportsVisitor.remove_unused_import( - self.context, - import_alias.evaluated_name, - asname=import_alias.evaluated_alias, + # Add removal tuple instead of calling directly + self.scheduled_removals.add( + ( + import_alias.evaluated_name, + None, + import_alias.evaluated_alias, + ) ) new_names.append(import_alias.with_changes(asname=None)) @@ -307,10 +314,13 @@ class RenameCommand(VisitorBasedCodemodCommand): def leave_Module( self, original_node: cst.Module, updated_node: cst.Module ) -> cst.Module: - for removal_node in self.scheduled_removals: - RemoveImportsVisitor.remove_unused_import_by_node( - self.context, removal_node - ) + for removal in self.scheduled_removals: + if isinstance(removal, tuple): + RemoveImportsVisitor.remove_unused_import( + self.context, removal[0], removal[1], removal[2] + ) + else: + RemoveImportsVisitor.remove_unused_import_by_node(self.context, removal) # If bypass_import is False, we know that no import statements were directly renamed, and the fact # that we have any `self.scheduled_removals` tells us we encountered a matching `old_name` in the code. if not self.bypass_import and self.scheduled_removals: From 8c30fcef305dc59d817f10fe9c48227a7b329d90 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Mon, 2 Dec 2024 10:00:59 +0000 Subject: [PATCH 048/151] 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` --- libcst/codemod/commands/rename.py | 6 ++- libcst/codemod/commands/tests/test_rename.py | 45 ++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/libcst/codemod/commands/rename.py b/libcst/codemod/commands/rename.py index 8b6302c9..9ad4334d 100644 --- a/libcst/codemod/commands/rename.py +++ b/libcst/codemod/commands/rename.py @@ -214,7 +214,7 @@ class RenameCommand(VisitorBasedCodemodCommand): return updated_node else: - new_names = [] + new_names: list[cst.ImportAlias] = [] for import_alias in names: alias_name = get_full_name_for_node(import_alias.name) if alias_name is not None: @@ -252,6 +252,10 @@ class RenameCommand(VisitorBasedCodemodCommand): # This import might be in use elsewhere in the code, so schedule a potential removal. self.scheduled_removals.add(original_node) new_names.append(import_alias) + if isinstance(new_names[-1].comma, cst.Comma): + new_names[-1] = new_names[-1].with_changes( + comma=cst.MaybeSentinel.DEFAULT + ) return updated_node.with_changes(names=new_names) return updated_node diff --git a/libcst/codemod/commands/tests/test_rename.py b/libcst/codemod/commands/tests/test_rename.py index 20e1c7d4..8245a34c 100644 --- a/libcst/codemod/commands/tests/test_rename.py +++ b/libcst/codemod/commands/tests/test_rename.py @@ -382,6 +382,28 @@ class TestRenameCommand(CodemodTest): new_name="d.z", ) + def test_comma_import(self) -> None: + before = """ + import a, b, c + + class Foo(a.z): + bar: b.bar + baz: c.baz + """ + after = """ + import a, b, d + + class Foo(a.z): + bar: b.bar + baz: d.baz + """ + self.assertCodemod( + before, + after, + old_name="c.baz", + new_name="d.baz", + ) + def test_other_import_froms_untouched(self) -> None: before = """ from a import b, c, d @@ -405,6 +427,29 @@ class TestRenameCommand(CodemodTest): new_name="f.b", ) + def test_comma_import_from(self) -> None: + before = """ + from a import b, c, d + + class Foo(b): + bar: c.bar + baz: d.baz + """ + after = """ + from a import b, c + from f import d + + class Foo(b): + bar: c.bar + baz: d.baz + """ + self.assertCodemod( + before, + after, + old_name="a.d", + new_name="f.d", + ) + def test_no_removal_of_import_in_use(self) -> None: before = """ import a From d24192a40ff1dbfaab0dac34d42b1e12c08f763e Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Mon, 2 Dec 2024 16:13:12 +0000 Subject: [PATCH 049/151] 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. --- libcst/codemod/commands/rename.py | 2 +- libcst/codemod/commands/tests/test_rename.py | 32 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/libcst/codemod/commands/rename.py b/libcst/codemod/commands/rename.py index 9ad4334d..f09c41fb 100644 --- a/libcst/codemod/commands/rename.py +++ b/libcst/codemod/commands/rename.py @@ -252,7 +252,7 @@ class RenameCommand(VisitorBasedCodemodCommand): # This import might be in use elsewhere in the code, so schedule a potential removal. self.scheduled_removals.add(original_node) new_names.append(import_alias) - if isinstance(new_names[-1].comma, cst.Comma): + if isinstance(new_names[-1].comma, cst.Comma) and updated_node.rpar is None: new_names[-1] = new_names[-1].with_changes( comma=cst.MaybeSentinel.DEFAULT ) diff --git a/libcst/codemod/commands/tests/test_rename.py b/libcst/codemod/commands/tests/test_rename.py index 8245a34c..4b88ee29 100644 --- a/libcst/codemod/commands/tests/test_rename.py +++ b/libcst/codemod/commands/tests/test_rename.py @@ -450,6 +450,38 @@ class TestRenameCommand(CodemodTest): new_name="f.d", ) + def test_comma_import_from_parens(self) -> None: + before = """ + from a import ( + b, + c, + d, + ) + from x import (y,) + + class Foo(b): + bar: c.bar + baz: d.baz + """ + after = """ + from a import ( + b, + c, + ) + from x import (y,) + from f import d + + class Foo(b): + bar: c.bar + baz: d.baz + """ + self.assertCodemod( + before, + after, + old_name="a.d", + new_name="f.d", + ) + def test_no_removal_of_import_in_use(self) -> None: before = """ import a From b04670c1668fd933ec04b09b22bf482b76029da7 Mon Sep 17 00:00:00 2001 From: khameeteman <73279381+khameeteman@users.noreply.github.com> Date: Sat, 7 Dec 2024 22:33:56 +0100 Subject: [PATCH 050/151] bump 3.12 to 3.13 in readme (#1228) --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 90e2f278..3d0b13be 100644 --- a/README.rst +++ b/README.rst @@ -33,7 +33,7 @@ A Concrete Syntax Tree (CST) parser and serializer library for Python .. intro-start -LibCST parses Python 3.0 -> 3.12 source code as a CST tree that keeps +LibCST parses Python 3.0 -> 3.13 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. From a3b5529bb3fd0bc1d10dd5f1c451625d384075ff Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Wed, 11 Dec 2024 20:30:33 +0000 Subject: [PATCH 051/151] 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. --- libcst/codemod/commands/rename.py | 2 +- libcst/codemod/commands/tests/test_rename.py | 22 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/libcst/codemod/commands/rename.py b/libcst/codemod/commands/rename.py index f09c41fb..ee196582 100644 --- a/libcst/codemod/commands/rename.py +++ b/libcst/codemod/commands/rename.py @@ -347,7 +347,7 @@ class RenameCommand(VisitorBasedCodemodCommand): module_as_name[0] + ".", module_as_name[1] + ".", 1 ) - if original_name == self.old_mod_or_obj: + if self.old_module and original_name == self.old_mod_or_obj: return self.new_mod_or_obj elif original_name == self.old_name: return ( diff --git a/libcst/codemod/commands/tests/test_rename.py b/libcst/codemod/commands/tests/test_rename.py index 4b88ee29..6e8ef936 100644 --- a/libcst/codemod/commands/tests/test_rename.py +++ b/libcst/codemod/commands/tests/test_rename.py @@ -850,3 +850,25 @@ class TestRenameCommand(CodemodTest): z.c(z.c.d) """ self.assertCodemod(before, after, old_name="a.b.c", new_name="z.c") + + def test_push_down_toplevel_names(self) -> None: + before = """ + import foo + foo.baz() + """ + after = """ + import quux.foo + quux.foo.baz() + """ + self.assertCodemod(before, after, old_name="foo", new_name="quux.foo") + + def test_push_down_toplevel_names_with_asname(self) -> None: + before = """ + import foo as bar + bar.baz() + """ + after = """ + import quux.foo + quux.foo.baz() + """ + self.assertCodemod(before, after, old_name="foo", new_name="quux.foo") From 3e4bae471b8fbad42b1102dd14db36a4d8e1e23f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:41:48 +0000 Subject: [PATCH 052/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 79ce0ba1..fc8f851b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ dev = [ "pyre-check==0.9.18; platform_system != 'Windows'", "setuptools_scm>=6.0.1", "sphinx-rtd-theme>=0.4.3", - "ufmt==2.7.3", + "ufmt==2.8.0", "usort==1.0.8.post1", "setuptools-rust>=1.5.2", "slotscheck>=0.7.1", From 230f177c84f6711f17e00851ccfa6a41e63e3caf Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Mon, 16 Dec 2024 10:01:02 +0000 Subject: [PATCH 053/151] ci: audit workflows with zizmor (#1262) https://woodruffw.github.io/zizmor/ --- .github/workflows/build.yml | 1 + .github/workflows/ci.yml | 8 +++++++ .github/workflows/pypi_upload.yml | 1 + .github/workflows/zizmor.yml | 35 +++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 .github/workflows/zizmor.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 886947d0..b14b4210 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - uses: actions/setup-python@v5 with: cache: pip diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e8ff9207..ab290f54 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - uses: actions/setup-python@v5 with: cache: pip @@ -50,6 +51,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - uses: actions/setup-python@v5 with: cache: pip @@ -67,6 +69,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - uses: actions/setup-python@v5 with: cache: pip @@ -83,6 +86,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - uses: actions/setup-python@v5 with: cache: pip @@ -108,6 +112,8 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy @@ -141,6 +147,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: dtolnay/rust-toolchain@stable with: components: rustfmt diff --git a/.github/workflows/pypi_upload.yml b/.github/workflows/pypi_upload.yml index ee9990cb..f03151f7 100644 --- a/.github/workflows/pypi_upload.yml +++ b/.github/workflows/pypi_upload.yml @@ -22,6 +22,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - name: Download binary wheels id: download uses: actions/download-artifact@v4 diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 00000000..dad6232f --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -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@v4 + + - 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@v3 + with: + sarif_file: results.sarif + category: zizmor \ No newline at end of file From d26987202b10534495477a93fad603627ecf28a8 Mon Sep 17 00:00:00 2001 From: Danny Yang Date: Thu, 2 Jan 2025 19:48:55 -0500 Subject: [PATCH 054/151] Add codemod to convert `typing.Union` to `|` (#1270) * add union to or codemod * lint * early return --- .../codemod/commands/convert_union_to_or.py | 56 ++++++++++++ .../tests/test_convert_union_to_or.py | 86 +++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 libcst/codemod/commands/convert_union_to_or.py create mode 100644 libcst/codemod/commands/tests/test_convert_union_to_or.py diff --git a/libcst/codemod/commands/convert_union_to_or.py b/libcst/codemod/commands/convert_union_to_or.py new file mode 100644 index 00000000..96a64314 --- /dev/null +++ b/libcst/codemod/commands/convert_union_to_or.py @@ -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 diff --git a/libcst/codemod/commands/tests/test_convert_union_to_or.py b/libcst/codemod/commands/tests/test_convert_union_to_or.py new file mode 100644 index 00000000..5ba557d2 --- /dev/null +++ b/libcst/codemod/commands/tests/test_convert_union_to_or.py @@ -0,0 +1,86 @@ +# 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 + +from libcst.codemod import CodemodTest +from libcst.codemod.commands.convert_union_to_or import ConvertUnionToOrCommand + + +class TestConvertUnionToOrCommand(CodemodTest): + TRANSFORM = ConvertUnionToOrCommand + + def test_simple_union(self) -> None: + before = """ + from typing import Union + x: Union[int, str] + """ + after = """ + x: int | str + """ + self.assertCodemod(before, after) + + def test_nested_union(self) -> None: + before = """ + from typing import Union + x: Union[int, Union[str, float]] + """ + after = """ + x: int | str | float + """ + self.assertCodemod(before, after) + + def test_single_type_union(self) -> None: + before = """ + from typing import Union + x: Union[int] + """ + after = """ + x: int + """ + self.assertCodemod(before, after) + + def test_union_with_alias(self) -> None: + before = """ + import typing as t + x: t.Union[int, str] + """ + after = """ + import typing as t + x: int | str + """ + self.assertCodemod(before, after) + + def test_union_with_unused_import(self) -> None: + before = """ + from typing import Union, List + x: Union[int, str] + """ + after = """ + from typing import List + x: int | str + """ + self.assertCodemod(before, after) + + def test_union_no_import(self) -> None: + before = """ + x: Union[int, str] + """ + after = """ + x: Union[int, str] + """ + self.assertCodemod(before, after) + + def test_union_in_function(self) -> None: + before = """ + from typing import Union + def foo(x: Union[int, str]) -> Union[float, None]: + ... + """ + after = """ + def foo(x: int | str) -> float | None: + ... + """ + self.assertCodemod(before, after) From 776452f35149e2b05a2e5c19d1b417001b3921e0 Mon Sep 17 00:00:00 2001 From: Danny Yang Date: Thu, 2 Jan 2025 19:49:03 -0500 Subject: [PATCH 055/151] Add codemod to fix variadic callable annotations (#1269) * add fix variadic callable codemod * format --- .../codemod/commands/fix_variadic_callable.py | 40 ++++++++ .../tests/test_fix_variadic_callable.py | 92 +++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 libcst/codemod/commands/fix_variadic_callable.py create mode 100644 libcst/codemod/commands/tests/test_fix_variadic_callable.py diff --git a/libcst/codemod/commands/fix_variadic_callable.py b/libcst/codemod/commands/fix_variadic_callable.py new file mode 100644 index 00000000..85cb0aa0 --- /dev/null +++ b/libcst/codemod/commands/fix_variadic_callable.py @@ -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 diff --git a/libcst/codemod/commands/tests/test_fix_variadic_callable.py b/libcst/codemod/commands/tests/test_fix_variadic_callable.py new file mode 100644 index 00000000..848f0c98 --- /dev/null +++ b/libcst/codemod/commands/tests/test_fix_variadic_callable.py @@ -0,0 +1,92 @@ +# 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 + +from libcst.codemod import CodemodTest +from libcst.codemod.commands.fix_variadic_callable import FixVariadicCallableCommmand + + +class TestFixVariadicCallableCommmand(CodemodTest): + TRANSFORM = FixVariadicCallableCommmand + + def test_callable_typing(self) -> None: + before = """ + from typing import Callable + x: Callable[[...], int] = ... + """ + after = """ + from typing import Callable + x: Callable[..., int] = ... + """ + self.assertCodemod(before, after) + + def test_callable_typing_alias(self) -> None: + before = """ + import typing as t + x: t.Callable[[...], int] = ... + """ + after = """ + import typing as t + x: t.Callable[..., int] = ... + """ + self.assertCodemod(before, after) + + def test_callable_import_alias(self) -> None: + before = """ + from typing import Callable as C + x: C[[...], int] = ... + """ + after = """ + from typing import Callable as C + x: C[..., int] = ... + """ + self.assertCodemod(before, after) + + def test_callable_with_optional(self) -> None: + before = """ + from typing import Callable + def foo(bar: Optional[Callable[[...], int]]) -> Callable[[...], int]: + ... + """ + after = """ + from typing import Callable + def foo(bar: Optional[Callable[..., int]]) -> Callable[..., int]: + ... + """ + self.assertCodemod(before, after) + + def test_callable_with_arguments(self) -> None: + before = """ + from typing import Callable + x: Callable[[int], int] + """ + after = """ + from typing import Callable + x: Callable[[int], int] + """ + self.assertCodemod(before, after) + + def test_callable_with_variadic_arguments(self) -> None: + before = """ + from typing import Callable + x: Callable[[int, int, ...], int] + """ + after = """ + from typing import Callable + x: Callable[[int, int, ...], int] + """ + self.assertCodemod(before, after) + + def test_callable_no_arguments(self) -> None: + before = """ + from typing import Callable + x: Callable + """ + after = """ + from typing import Callable + x: Callable + """ + self.assertCodemod(before, after) From c4e7934253a64b0e4ed8a686a74465c2d444090e Mon Sep 17 00:00:00 2001 From: Danny Yang Date: Sat, 4 Jan 2025 04:59:40 -0500 Subject: [PATCH 056/151] add types classifier and badge (#1272) --- README.rst | 6 +++++- pyproject.toml | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 3d0b13be..78d29820 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ A Concrete Syntax Tree (CST) parser and serializer library for Python -|support-ukraine| |readthedocs-badge| |ci-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. @@ -31,6 +31,10 @@ 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.13 source code as a CST tree that keeps diff --git a/pyproject.toml b/pyproject.toml index fc8f851b..caf5b684 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Typing :: Typed", ] requires-python = ">=3.9" dependencies = ["pyyaml>=5.2"] From 595d7f6aaf6dbd710346b75f4f7f24d18147b827 Mon Sep 17 00:00:00 2001 From: Crozzers Date: Wed, 8 Jan 2025 19:58:37 +0000 Subject: [PATCH 057/151] Expose TypeAlias and TypeVar related structs in rust library (#1274) --- native/libcst/src/nodes/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/native/libcst/src/nodes/mod.rs b/native/libcst/src/nodes/mod.rs index 9fbdb1af..d2bc0220 100644 --- a/native/libcst/src/nodes/mod.rs +++ b/native/libcst/src/nodes/mod.rs @@ -18,7 +18,8 @@ pub use statement::{ MatchPattern, MatchSequence, MatchSequenceElement, MatchSingleton, MatchStar, MatchTuple, MatchValue, NameItem, Nonlocal, OrElse, Pass, Raise, Return, SimpleStatementLine, SimpleStatementSuite, SmallStatement, StarrableMatchSequenceElement, Statement, Suite, Try, - TryStar, While, With, WithItem, + TryStar, TypeAlias, TypeParam, TypeParameters, TypeVar, TypeVarLike, TypeVarTuple, While, + With, WithItem, }; pub(crate) mod expression; From b523b360c1c5e3825faaf92f1396d35118277615 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Wed, 8 Jan 2025 20:02:17 +0000 Subject: [PATCH 058/151] run cargo fmt Summary: Test Plan: --- native/libcst/src/nodes/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native/libcst/src/nodes/mod.rs b/native/libcst/src/nodes/mod.rs index d2bc0220..ce02c86f 100644 --- a/native/libcst/src/nodes/mod.rs +++ b/native/libcst/src/nodes/mod.rs @@ -18,8 +18,8 @@ pub use statement::{ MatchPattern, MatchSequence, MatchSequenceElement, MatchSingleton, MatchStar, MatchTuple, MatchValue, NameItem, Nonlocal, OrElse, Pass, Raise, Return, SimpleStatementLine, SimpleStatementSuite, SmallStatement, StarrableMatchSequenceElement, Statement, Suite, Try, - TryStar, TypeAlias, TypeParam, TypeParameters, TypeVar, TypeVarLike, TypeVarTuple, While, - With, WithItem, + TryStar, TypeAlias, TypeParam, TypeParameters, TypeVar, TypeVarLike, TypeVarTuple, While, With, + WithItem, }; pub(crate) mod expression; From 20837f78243e8e8e3bf5b9c8c4c7ba28a47e6391 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 9 Jan 2025 18:39:18 +0000 Subject: [PATCH 059/151] ci: disable macos intel wheels (#1275) cibuildwheel fails to build these after a recent version upgrade --- .github/workflows/build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b14b4210..a7c34bba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,8 +10,7 @@ jobs: strategy: fail-fast: false matrix: - # macos-13 is an intel runner, macos-14 is apple silicon - os: [macos-13, macos-14, ubuntu-latest, windows-latest] + os: [macos-latest, ubuntu-latest, windows-latest] 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" From d2382d81acb7964800fa9162da6cacb73a5c4581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jelmer=20Vernoo=C4=B3?= Date: Thu, 9 Jan 2025 18:47:12 +0000 Subject: [PATCH 060/151] Upgrade pyo3 to 0.22 (#1180) * Upgrade pyo3 to 0.22 * libcst_native: add optional signature Newer versions of pyo3 warn about missing signatures --- native/Cargo.lock | 93 +++++------------------- native/libcst/Cargo.toml | 2 +- native/libcst/src/nodes/expression.rs | 13 ++-- native/libcst/src/nodes/parser_config.rs | 2 +- native/libcst/src/nodes/traits.rs | 2 +- native/libcst/src/parser/errors.rs | 13 ++-- native/libcst/src/py.rs | 3 +- native/libcst_derive/src/into_py.rs | 15 ++-- 8 files changed, 46 insertions(+), 97 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 00409e83..a1b519ec 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -35,12 +35,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.4.0" @@ -265,9 +259,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -291,15 +285,6 @@ version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "is-terminal" version = "0.4.9" @@ -389,16 +374,6 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" -[[package]] -name = "lock_api" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.17" @@ -453,31 +428,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - [[package]] name = "paste" version = "1.0.15" @@ -550,15 +500,16 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.20.2" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a89dc7a5850d0e983be1ec2a463a171d20990487c3cfcd68b5363f1ee3d6fe0" +checksum = "831e8e819a138c36e212f3af3fd9eeffed6bf1510a805af35b0edee5ffa59433" dependencies = [ "cfg-if", "indoc", "libc", "memoffset 0.9.0", - "parking_lot", + "once_cell", + "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", @@ -567,9 +518,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.20.2" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07426f0d8fe5a601f26293f300afd1a7b1ed5e78b2a705870c5f30893c5163be" +checksum = "1e8730e591b14492a8945cdff32f089250b05f5accecf74aeddf9e8272ce1fa8" dependencies = [ "once_cell", "target-lexicon", @@ -577,9 +528,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.20.2" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb7dec17e17766b46bca4f1a4215a85006b4c2ecde122076c562dd058da6cf1" +checksum = "5e97e919d2df92eb88ca80a037969f44e5e70356559654962cbb3316d00300c6" dependencies = [ "libc", "pyo3-build-config", @@ -587,9 +538,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.20.2" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f738b4e40d50b5711957f142878cfa0f28e054aa0ebdfc3fd137a843f74ed3" +checksum = "eb57983022ad41f9e683a599f2fd13c3664d7063a3ac5714cae4b7bee7d3f206" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -599,12 +550,13 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.20.2" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc910d4851847827daf9d6cdd4a823fbdaab5b8818325c5e97a86da79e8881f" +checksum = "ec480c0c51ddec81019531705acac51bcdbeae563557c982aa8263bb96880372" dependencies = [ "heck", "proc-macro2", + "pyo3-build-config", "quote", "syn 2.0.75", ] @@ -638,15 +590,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "regex" version = "1.10.6" @@ -682,7 +625,7 @@ version = "0.38.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" dependencies = [ - "bitflags 2.4.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -781,9 +724,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.4" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02424087780c9b71cc96799eaeddff35af2bc513278cda5c99fc1f5d026d3c1" +checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2" [[package]] name = "termcolor" diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 7cdcc61e..6e77feca 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -36,7 +36,7 @@ trace = ["peg/trace"] [dependencies] paste = "1.0.15" -pyo3 = { version = "0.20", optional = true } +pyo3 = { version = "0.22", optional = true } thiserror = "1.0.63" peg = "0.8.4" chic = "1.2.2" diff --git a/native/libcst/src/nodes/expression.rs b/native/libcst/src/nodes/expression.rs index 971ef8bc..bc3699bf 100644 --- a/native/libcst/src/nodes/expression.rs +++ b/native/libcst/src/nodes/expression.rs @@ -2524,6 +2524,7 @@ impl<'r, 'a> Inflate<'a> for DeflatedNamedExpr<'r, 'a> { #[cfg(feature = "py")] mod py { + use pyo3::types::PyAnyMethods; use pyo3::types::PyModule; use super::*; @@ -2535,7 +2536,7 @@ mod py { match self { Self::Starred(s) => s.try_into_py(py), Self::Simple { value, comma } => { - let libcst = PyModule::import(py, "libcst")?; + let libcst = PyModule::import_bound(py, "libcst")?; let kwargs = [ Some(("value", value.try_into_py(py)?)), comma @@ -2547,11 +2548,11 @@ mod py { .filter(|x| x.is_some()) .map(|x| x.as_ref().unwrap()) .collect::>() - .into_py_dict(py); + .into_py_dict_bound(py); Ok(libcst .getattr("Element") .expect("no Element found in libcst") - .call((), Some(kwargs))? + .call((), Some(&kwargs))? .into()) } } @@ -2571,7 +2572,7 @@ mod py { whitespace_before_colon, .. } => { - let libcst = PyModule::import(py, "libcst")?; + let libcst = PyModule::import_bound(py, "libcst")?; let kwargs = [ Some(("key", key.try_into_py(py)?)), Some(("value", value.try_into_py(py)?)), @@ -2592,11 +2593,11 @@ mod py { .filter(|x| x.is_some()) .map(|x| x.as_ref().unwrap()) .collect::>() - .into_py_dict(py); + .into_py_dict_bound(py); Ok(libcst .getattr("DictElement") .expect("no Element found in libcst") - .call((), Some(kwargs))? + .call((), Some(&kwargs))? .into()) } } diff --git a/native/libcst/src/nodes/parser_config.rs b/native/libcst/src/nodes/parser_config.rs index 6dfdf544..4a80a048 100644 --- a/native/libcst/src/nodes/parser_config.rs +++ b/native/libcst/src/nodes/parser_config.rs @@ -125,7 +125,7 @@ fn parser_config_asdict<'py>(py: Python<'py>, config: PyRef<'py, ParserConfig>) ("version", config.version.clone_ref(py)), ("future_imports", config.future_imports.clone_ref(py)), ] - .into_py_dict(py) + .into_py_dict_bound(py) } pub fn init_module(_py: Python, m: &PyModule) -> PyResult<()> { diff --git a/native/libcst/src/nodes/traits.rs b/native/libcst/src/nodes/traits.rs index 1dd58132..397f6404 100644 --- a/native/libcst/src/nodes/traits.rs +++ b/native/libcst/src/nodes/traits.rs @@ -170,7 +170,7 @@ pub mod py { .map(|x| x.try_into_py(py)) .collect::>>()? .into_iter(); - Ok(PyTuple::new(py, converted).into()) + Ok(PyTuple::new_bound(py, converted).into()) } } diff --git a/native/libcst/src/parser/errors.rs b/native/libcst/src/parser/errors.rs index 79132ce1..85690c97 100644 --- a/native/libcst/src/parser/errors.rs +++ b/native/libcst/src/parser/errors.rs @@ -28,7 +28,7 @@ pub enum ParserError<'a> { #[cfg(feature = "py")] mod py_error { - use pyo3::types::{IntoPyDict, PyModule}; + use pyo3::types::{IntoPyDict, PyAnyMethods, PyModule}; use pyo3::{IntoPy, PyErr, PyErrArguments, Python}; use super::ParserError; @@ -65,13 +65,14 @@ mod py_error { ("raw_line", (line + 1).into_py(py)), ("raw_column", col.into_py(py)), ] - .into_py_dict(py); - let libcst = PyModule::import(py, "libcst").expect("libcst cannot be imported"); - PyErr::from_value( + .into_py_dict_bound(py); + let libcst = + PyModule::import_bound(py, "libcst").expect("libcst cannot be imported"); + PyErr::from_value_bound( libcst .getattr("ParserSyntaxError") .expect("ParserSyntaxError not found") - .call((), Some(kwargs)) + .call((), Some(&kwargs)) .expect("failed to instantiate"), ) }) @@ -86,7 +87,7 @@ mod py_error { ("raw_line", self.raw_line.into_py(py)), ("raw_column", self.raw_column.into_py(py)), ] - .into_py_dict(py) + .into_py_dict_bound(py) .into_py(py) } } diff --git a/native/libcst/src/py.rs b/native/libcst/src/py.rs index 960fac3a..bd7dfe6d 100644 --- a/native/libcst/src/py.rs +++ b/native/libcst/src/py.rs @@ -8,8 +8,9 @@ use pyo3::prelude::*; #[pymodule] #[pyo3(name = "native")] -pub fn libcst_native(_py: Python, m: &PyModule) -> PyResult<()> { +pub fn libcst_native(_py: Python, m: &Bound) -> PyResult<()> { #[pyfn(m)] + #[pyo3(signature = (source, encoding=None))] fn parse_module(source: String, encoding: Option<&str>) -> PyResult { let m = crate::parse_module(source.as_str(), encoding)?; Python::with_gil(|py| m.try_into_py(py)) diff --git a/native/libcst_derive/src/into_py.rs b/native/libcst_derive/src/into_py.rs index 3894212f..ac656cb4 100644 --- a/native/libcst_derive/src/into_py.rs +++ b/native/libcst_derive/src/into_py.rs @@ -38,12 +38,14 @@ fn impl_into_py_enum(ast: &DeriveInput, e: &DataEnum) -> TokenStream { let kwargs_toks = fields_to_kwargs(&var.fields, true); toks.push(quote! { Self::#varname { #(#fieldnames,)* .. } => { - let libcst = pyo3::types::PyModule::import(py, "libcst")?; + use pyo3::types::PyAnyMethods; + + let libcst = pyo3::types::PyModule::import_bound(py, "libcst")?; let kwargs = #kwargs_toks ; Ok(libcst .getattr(stringify!(#varname)) .expect(stringify!(no #varname found in libcst)) - .call((), Some(kwargs))? + .call((), Some(&kwargs))? .into()) } }) @@ -87,12 +89,13 @@ fn impl_into_py_struct(ast: &DeriveInput, e: &DataStruct) -> TokenStream { #[automatically_derived] impl#generics crate::nodes::traits::py::TryIntoPy for #ident #generics { fn try_into_py(self, py: pyo3::Python) -> pyo3::PyResult { - let libcst = pyo3::types::PyModule::import(py, "libcst")?; + use pyo3::types::PyAnyMethods; + let libcst = pyo3::types::PyModule::import_bound(py, "libcst")?; let kwargs = #kwargs_toks ; Ok(libcst .getattr(stringify!(#ident)) .expect(stringify!(no #ident found in libcst)) - .call((), Some(kwargs))? + .call((), Some(&kwargs))? .into()) } } @@ -162,7 +165,7 @@ fn fields_to_kwargs(fields: &Fields, is_enum: bool) -> quote::__private::TokenSt #(#optional_rust_varnames.map(|x| x.try_into_py(py)).transpose()?.map(|x| (stringify!(#optional_py_varnames), x)),)* }; if empty_kwargs { - quote! { pyo3::types::PyDict::new(py) } + quote! { pyo3::types::PyDict::new_bound(py) } } else { quote! { [ #kwargs_pairs #optional_pairs ] @@ -170,7 +173,7 @@ fn fields_to_kwargs(fields: &Fields, is_enum: bool) -> quote::__private::TokenSt .filter(|x| x.is_some()) .map(|x| x.as_ref().unwrap()) .collect::>() - .into_py_dict(py) + .into_py_dict_bound(py) } } } From 403782d5e99df26512714c31f4cb17540d116aab Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 9 Jan 2025 18:50:02 +0000 Subject: [PATCH 061/151] Cargo.lock changes --- native/Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index a1b519ec..76a33536 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -489,6 +489,12 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "portable-atomic" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" + [[package]] name = "proc-macro2" version = "1.0.86" @@ -694,12 +700,6 @@ dependencies = [ "serde", ] -[[package]] -name = "smallvec" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2" - [[package]] name = "syn" version = "1.0.109" From 6b483c61132297e931696d0542c077d3c0dc794f Mon Sep 17 00:00:00 2001 From: Danny Yang Date: Thu, 9 Jan 2025 13:59:00 -0500 Subject: [PATCH 062/151] Add codemod to rename typing aliases of builtins (#1267) * add codemod to rename typing aliases of builtins * format --- libcst/codemod/commands/rename.py | 2 +- .../commands/rename_typing_generic_aliases.py | 37 +++++++++++++++++++ libcst/codemod/commands/tests/test_rename.py | 13 +++++++ .../test_rename_typing_generic_aliases.py | 33 +++++++++++++++++ 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 libcst/codemod/commands/rename_typing_generic_aliases.py create mode 100644 libcst/codemod/commands/tests/test_rename_typing_generic_aliases.py diff --git a/libcst/codemod/commands/rename.py b/libcst/codemod/commands/rename.py index ee196582..361e33e6 100644 --- a/libcst/codemod/commands/rename.py +++ b/libcst/codemod/commands/rename.py @@ -328,7 +328,7 @@ class RenameCommand(VisitorBasedCodemodCommand): # If bypass_import is False, we know that no import statements were directly renamed, and the fact # that we have any `self.scheduled_removals` tells us we encountered a matching `old_name` in the code. if not self.bypass_import and self.scheduled_removals: - if self.new_module: + if self.new_module and self.new_module != "builtins": new_obj: Optional[str] = ( self.new_mod_or_obj.split(".")[0] if self.new_mod_or_obj else None ) diff --git a/libcst/codemod/commands/rename_typing_generic_aliases.py b/libcst/codemod/commands/rename_typing_generic_aliases.py new file mode 100644 index 00000000..d6906fe9 --- /dev/null +++ b/libcst/codemod/commands/rename_typing_generic_aliases.py @@ -0,0 +1,37 @@ +# 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 +from functools import partial +from typing import cast, Generator + +from libcst.codemod import Codemod, MagicArgsCodemodCommand +from libcst.codemod.commands.rename import RenameCommand + + +class RenameTypingGenericAliases(MagicArgsCodemodCommand): + DESCRIPTION: str = ( + "Rename typing module aliases of builtin generics in Python 3.9+, for example: `typing.List` -> `list`" + ) + + MAPPING: dict[str, str] = { + "typing.List": "builtins.list", + "typing.Tuple": "builtins.tuple", + "typing.Dict": "builtins.dict", + "typing.FrozenSet": "builtins.frozenset", + "typing.Set": "builtins.set", + "typing.Type": "builtins.type", + } + + def get_transforms(self) -> Generator[type[Codemod], None, None]: + for from_type, to_type in self.MAPPING.items(): + yield cast( + type[Codemod], + partial( + RenameCommand, + old_name=from_type, + new_name=to_type, + ), + ) diff --git a/libcst/codemod/commands/tests/test_rename.py b/libcst/codemod/commands/tests/test_rename.py index 6e8ef936..5b6e0128 100644 --- a/libcst/codemod/commands/tests/test_rename.py +++ b/libcst/codemod/commands/tests/test_rename.py @@ -28,6 +28,19 @@ class TestRenameCommand(CodemodTest): self.assertCodemod(before, after, old_name="foo.bar", new_name="baz.qux") + def test_rename_to_builtin(self) -> None: + before = """ + from typing import List + x: List[int] = [] + """ + after = """ + x: list[int] = [] + """ + + self.assertCodemod( + before, after, old_name="typing.List", new_name="builtins.list" + ) + def test_rename_name_asname(self) -> None: before = """ from foo import bar as bla diff --git a/libcst/codemod/commands/tests/test_rename_typing_generic_aliases.py b/libcst/codemod/commands/tests/test_rename_typing_generic_aliases.py new file mode 100644 index 00000000..7a0a83c3 --- /dev/null +++ b/libcst/codemod/commands/tests/test_rename_typing_generic_aliases.py @@ -0,0 +1,33 @@ +# 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 + +from libcst.codemod import CodemodTest +from libcst.codemod.commands.rename_typing_generic_aliases import ( + RenameTypingGenericAliases, +) + + +class TestRenameCommand(CodemodTest): + TRANSFORM = RenameTypingGenericAliases + + def test_rename_typing_generic_alias(self) -> None: + before = """ + from typing import List, Set, Dict, FrozenSet, Tuple + x: List[int] = [] + y: Set[int] = set() + z: Dict[str, int] = {} + a: FrozenSet[str] = frozenset() + b: Tuple[int, str] = (1, "hello") + """ + after = """ + x: list[int] = [] + y: set[int] = set() + z: dict[str, int] = {} + a: frozenset[str] = frozenset() + b: tuple[int, str] = (1, "hello") + """ + self.assertCodemod(before, after) From af136b91acc59745ff875f086e58e8712b97f550 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:59:38 +0000 Subject: [PATCH 063/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/zizmor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index dad6232f..797b8d9b 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -21,7 +21,7 @@ jobs: persist-credentials: false - name: Install the latest version of uv - uses: astral-sh/setup-uv@v4 + uses: astral-sh/setup-uv@v5 - name: Run zizmor 🌈 run: uvx zizmor --format sarif . > results.sarif From 01c29394453d747db049617a8929b98ec6fd1655 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 19:00:34 +0000 Subject: [PATCH 064/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index caf5b684..ea3953a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ dev = [ "usort==1.0.8.post1", "setuptools-rust>=1.5.2", "slotscheck>=0.7.1", - "jinja2==3.1.4", + "jinja2==3.1.5", ] [project.urls] From c825afb87d96a426fcaafde5be2478531e6bbc26 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 9 Jan 2025 19:09:48 +0000 Subject: [PATCH 065/151] Bump to 1.6.0 Summary: Test Plan: --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 4 ++-- native/libcst_derive/Cargo.toml | 2 +- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e9d5aab..c2a559b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +# 1.6.0 - 2024-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 diff --git a/native/Cargo.lock b/native/Cargo.lock index 76a33536..2d37e231 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -343,7 +343,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libcst" -version = "1.5.1" +version = "1.6.0" dependencies = [ "chic", "criterion", @@ -361,7 +361,7 @@ dependencies = [ [[package]] name = "libcst_derive" -version = "1.5.1" +version = "1.6.0" dependencies = [ "quote", "syn 2.0.75", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 6e77feca..b8abcd1f 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "libcst" -version = "1.5.1" +version = "1.6.0" authors = ["LibCST Developers"] edition = "2018" rust-version = "1.70" @@ -42,7 +42,7 @@ peg = "0.8.4" chic = "1.2.2" regex = "1.10.6" memchr = "2.7.4" -libcst_derive = { path = "../libcst_derive", version = "1.4.0" } +libcst_derive = { path = "../libcst_derive", version = "1.6.0" } [dev-dependencies] criterion = { version = "0.5.1", features = ["html_reports"] } diff --git a/native/libcst_derive/Cargo.toml b/native/libcst_derive/Cargo.toml index 9ede217d..b645d93b 100644 --- a/native/libcst_derive/Cargo.toml +++ b/native/libcst_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libcst_derive" -version = "1.5.1" +version = "1.6.0" edition = "2018" description = "Proc macro helpers for libcst." license = "MIT" From 985cec808e6bf2ed859e3f7cdc2da7710ba32776 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Fri, 21 Feb 2025 16:20:49 -0600 Subject: [PATCH 066/151] Remove dependency on `chic` and upgrade `annotate-snippets` (#1293) * Vendor `chic` At https://github.com/yoshuawuyts/chic/commit/0761036492338f6e77a414af572cb8306f46b43d * Remove unused `Error::help` method * Upgrade to `annotate_snippets` 0.9.x Applying https://github.com/yoshuawuyts/chic/commit/27c99b5038c0a028ec9614d4e32dde66faa5ce36 * 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 --- native/Cargo.lock | 29 ++++++++++++++------------- native/libcst/Cargo.toml | 2 +- native/libcst/src/lib.rs | 43 ++++++++++++++++++++++------------------ 3 files changed, 40 insertions(+), 34 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 2d37e231..c241e857 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -19,15 +19,19 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "annotate-snippets" -version = "0.6.1" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7021ce4924a3f25f802b2cccd1af585e39ea1a363a1aa2e72afe54b67a3a7a7" +checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" +dependencies = [ + "anstyle", + "unicode-width", +] [[package]] name = "anstyle" -version = "1.0.2" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "autocfg" @@ -68,15 +72,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chic" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5b5db619f3556839cb2223ae86ff3f9a09da2c5013be42bc9af08c9589bf70c" -dependencies = [ - "annotate-snippets", -] - [[package]] name = "ciborium" version = "0.2.0" @@ -345,7 +340,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" name = "libcst" version = "1.6.0" dependencies = [ - "chic", + "annotate-snippets", "criterion", "difference", "itertools 0.13.0", @@ -821,6 +816,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unindent" version = "0.2.3" diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index b8abcd1f..d4e71f62 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -39,7 +39,7 @@ paste = "1.0.15" pyo3 = { version = "0.22", optional = true } thiserror = "1.0.63" peg = "0.8.4" -chic = "1.2.2" +annotate-snippets = "0.11.5" regex = "1.10.6" memchr = "2.7.4" libcst_derive = { path = "../libcst_derive", version = "1.6.0" } diff --git a/native/libcst/src/lib.rs b/native/libcst/src/lib.rs index 03a97173..93e6bd05 100644 --- a/native/libcst/src/lib.rs +++ b/native/libcst/src/lib.rs @@ -91,32 +91,37 @@ fn bol_offset(source: &str, n: i32) -> usize { pub fn prettify_error(err: ParserError, label: &str) -> std::string::String { match err { ParserError::ParserError(e, module_text) => { + use annotate_snippets::{Level, Renderer, Snippet}; + let loc = e.location; let context = 1; + let line_start = max( + 1, + loc.start_pos + .line + .checked_sub(context as usize) + .unwrap_or(1), + ); let start_offset = bol_offset(module_text, loc.start_pos.line as i32 - context); let end_offset = bol_offset(module_text, loc.end_pos.line as i32 + context + 1); let source = &module_text[start_offset..end_offset]; let start = loc.start_pos.offset - start_offset; let end = loc.end_pos.offset - start_offset; - chic::Error::new(label) - .error( - max( - 1, - loc.start_pos - .line - .checked_sub(context as usize) - .unwrap_or(1), - ), - start, - if start == end { - min(end + 1, end_offset - start_offset + 1) - } else { - end - }, - source, - format!( - "expected {} {} -> {}", - e.expected, loc.start_pos, loc.end_pos + let end = if start == end { + min(end + 1, end_offset - start_offset + 1) + } else { + end + }; + Renderer::styled() + .render( + Level::Error.title(label).snippet( + Snippet::source(source) + .line_start(line_start) + .fold(false) + .annotations(vec![Level::Error.span(start..end).label(&format!( + "expected {} {} -> {}", + e.expected, loc.start_pos, loc.end_pos + ))]), ), ) .to_string() From edd75bfa62ca458a60336783404f2b9dc520b6ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Mar 2025 11:23:36 +0000 Subject: [PATCH 067/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a7c34bba..5d31667c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: with: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.22.0 + uses: pypa/cibuildwheel@v2.23.0 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From eae77997be16bc190c539f5ce3e331be08370b61 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Fri, 7 Mar 2025 14:18:25 +0000 Subject: [PATCH 068/151] ci: install libatomic on linux before rustup (#1301) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5d31667c..596c6e84 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: os: [macos-latest, ubuntu-latest, windows-latest] 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_ALL_LINUX: "yum install -y libatomic; 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" From 64ca5ed8dff0e25cded9e085f861a4ece05be4a6 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Fri, 7 Mar 2025 14:21:41 +0000 Subject: [PATCH 069/151] ci: move cibuildwheel config into pyproject.toml (#1277) --- .github/workflows/build.yml | 10 +--------- pyproject.toml | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 596c6e84..6e6f2cd2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,15 +13,7 @@ jobs: os: [macos-latest, ubuntu-latest, windows-latest] env: SCCACHE_VERSION: 0.2.13 - CIBW_BEFORE_ALL_LINUX: "yum install -y libatomic; 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_i686 *-musllinux_ppc64le *-musllinux_s390x *-musllinux_armv7l" - CIBW_ARCHS_LINUX: auto aarch64 - CIBW_BUILD_VERBOSITY: 1 + GITHUB_WORKSPACE: "${{github.workspace}}" steps: - uses: actions/checkout@v4 with: diff --git a/pyproject.toml b/pyproject.toml index ea3953a8..e12a296b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,3 +80,32 @@ exclude-modules = '^libcst\.(testing|tests)' [tool.ufmt] excludes = ["native/", "stubs/"] + +[tool.cibuildwheel] +build-verbosity = 1 +environment = { PATH = "$PATH:$HOME/.cargo/bin" } +skip = [ + "pp*", + "*-win32", + "*-win_arm64", + "*-musllinux_i686", + "*-musllinux_ppc64le", + "*-musllinux_s390x", + "*-musllinux_armv7l", +] + +[tool.cibuildwheel.linux] +archs = ["auto", "aarch64"] +environment-pass = ["LIBCST_NO_LOCAL_SCHEME", "GITHUB_WORKSPACE"] +before-all = "yum install -y libatomic; curl https://sh.rustup.rs -sSf | env -u CARGO_HOME sh -s -- --default-toolchain stable --profile minimal -y" +before-build = [ + "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", +] + +[tool.cibuildwheel.macos] +before-all = "rustup target add aarch64-apple-darwin x86_64-apple-darwin" + +[tool.cibuildwheel.windows] +before-all = "rustup target add x86_64-pc-windows-msvc i686-pc-windows-msvc" From 5eccb5f08b7c476c42643d1a205fd0ef82b3d797 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Fri, 7 Mar 2025 15:32:39 +0000 Subject: [PATCH 070/151] ci: use native arm github runners (#1303) --- .github/workflows/build.yml | 7 +------ pyproject.toml | 7 ------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6e6f2cd2..812d1577 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-latest, ubuntu-latest, windows-latest] + os: [macos-latest, ubuntu-latest, ubuntu-24.04-arm, windows-latest] env: SCCACHE_VERSION: 0.2.13 GITHUB_WORKSPACE: "${{github.workspace}}" @@ -28,11 +28,6 @@ jobs: if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} run: >- echo LIBCST_NO_LOCAL_SCHEME=1 >> $GITHUB_ENV - - name: Set up QEMU - if: runner.os == 'Linux' - uses: docker/setup-qemu-action@v3 - with: - platforms: all - name: Build wheels uses: pypa/cibuildwheel@v2.23.0 - uses: actions/upload-artifact@v4 diff --git a/pyproject.toml b/pyproject.toml index e12a296b..19b9da1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,14 +95,7 @@ skip = [ ] [tool.cibuildwheel.linux] -archs = ["auto", "aarch64"] -environment-pass = ["LIBCST_NO_LOCAL_SCHEME", "GITHUB_WORKSPACE"] before-all = "yum install -y libatomic; curl https://sh.rustup.rs -sSf | env -u CARGO_HOME sh -s -- --default-toolchain stable --profile minimal -y" -before-build = [ - "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", -] [tool.cibuildwheel.macos] before-all = "rustup target add aarch64-apple-darwin x86_64-apple-darwin" From 727e433539802d14a6f3320be0453213c8afb8a1 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Fri, 7 Mar 2025 08:35:17 -0700 Subject: [PATCH 071/151] 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 --- native/Cargo.lock | 20 +++++----- native/libcst/Cargo.toml | 2 +- native/libcst/src/nodes/expression.rs | 8 ++-- native/libcst/src/nodes/traits.rs | 23 ++---------- native/libcst/src/parser/errors.rs | 54 ++++++++------------------- 5 files changed, 35 insertions(+), 72 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index c241e857..1c6cbf60 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -501,9 +501,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.22.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831e8e819a138c36e212f3af3fd9eeffed6bf1510a805af35b0edee5ffa59433" +checksum = "57fe09249128b3173d092de9523eaa75136bf7ba85e0d69eca241c7939c933cc" dependencies = [ "cfg-if", "indoc", @@ -519,9 +519,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.22.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8730e591b14492a8945cdff32f089250b05f5accecf74aeddf9e8272ce1fa8" +checksum = "1cd3927b5a78757a0d71aa9dff669f903b1eb64b54142a9bd9f757f8fde65fd7" dependencies = [ "once_cell", "target-lexicon", @@ -529,9 +529,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.22.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e97e919d2df92eb88ca80a037969f44e5e70356559654962cbb3316d00300c6" +checksum = "dab6bb2102bd8f991e7749f130a70d05dd557613e39ed2deeee8e9ca0c4d548d" dependencies = [ "libc", "pyo3-build-config", @@ -539,9 +539,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.22.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb57983022ad41f9e683a599f2fd13c3664d7063a3ac5714cae4b7bee7d3f206" +checksum = "91871864b353fd5ffcb3f91f2f703a22a9797c91b9ab497b1acac7b07ae509c7" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -551,9 +551,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.22.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec480c0c51ddec81019531705acac51bcdbeae563557c982aa8263bb96880372" +checksum = "43abc3b80bc20f3facd86cd3c60beed58c3e2aa26213f3cda368de39c60a27e4" dependencies = [ "heck", "proc-macro2", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index d4e71f62..ea56e358 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -36,7 +36,7 @@ trace = ["peg/trace"] [dependencies] paste = "1.0.15" -pyo3 = { version = "0.22", optional = true } +pyo3 = { version = "0.23", optional = true } thiserror = "1.0.63" peg = "0.8.4" annotate-snippets = "0.11.5" diff --git a/native/libcst/src/nodes/expression.rs b/native/libcst/src/nodes/expression.rs index bc3699bf..cbd1e361 100644 --- a/native/libcst/src/nodes/expression.rs +++ b/native/libcst/src/nodes/expression.rs @@ -2536,7 +2536,7 @@ mod py { match self { Self::Starred(s) => s.try_into_py(py), Self::Simple { value, comma } => { - let libcst = PyModule::import_bound(py, "libcst")?; + let libcst = PyModule::import(py, "libcst")?; let kwargs = [ Some(("value", value.try_into_py(py)?)), comma @@ -2548,7 +2548,7 @@ mod py { .filter(|x| x.is_some()) .map(|x| x.as_ref().unwrap()) .collect::>() - .into_py_dict_bound(py); + .into_py_dict(py)?; Ok(libcst .getattr("Element") .expect("no Element found in libcst") @@ -2572,7 +2572,7 @@ mod py { whitespace_before_colon, .. } => { - let libcst = PyModule::import_bound(py, "libcst")?; + let libcst = PyModule::import(py, "libcst")?; let kwargs = [ Some(("key", key.try_into_py(py)?)), Some(("value", value.try_into_py(py)?)), @@ -2593,7 +2593,7 @@ mod py { .filter(|x| x.is_some()) .map(|x| x.as_ref().unwrap()) .collect::>() - .into_py_dict_bound(py); + .into_py_dict(py)?; Ok(libcst .getattr("DictElement") .expect("no Element found in libcst") diff --git a/native/libcst/src/nodes/traits.rs b/native/libcst/src/nodes/traits.rs index 397f6404..df61538b 100644 --- a/native/libcst/src/nodes/traits.rs +++ b/native/libcst/src/nodes/traits.rs @@ -118,7 +118,7 @@ impl<'a, T: Inflate<'a>> Inflate<'a> for Vec { } #[cfg(feature = "py")] pub mod py { - use pyo3::{types::PyAny, types::PyTuple, IntoPy, PyObject, PyResult, Python}; + use pyo3::{types::PyTuple, IntoPyObjectExt, PyObject, PyResult, Python}; // TODO: replace with upstream implementation once // https://github.com/PyO3/pyo3/issues/1813 is resolved @@ -135,7 +135,7 @@ pub mod py { impl TryIntoPy for bool { fn try_into_py(self, py: Python) -> PyResult { - Ok(self.into_py(py)) + self.into_py_any(py) } } @@ -170,28 +170,13 @@ pub mod py { .map(|x| x.try_into_py(py)) .collect::>>()? .into_iter(); - Ok(PyTuple::new_bound(py, converted).into()) - } - } - - impl TryIntoPy for PyTuple { - fn try_into_py(self, py: Python) -> PyResult { - Ok(self.into_py(py)) + PyTuple::new(py, converted)?.into_py_any(py) } } impl<'a> TryIntoPy for &'a str { fn try_into_py(self, py: Python) -> PyResult { - Ok(self.into_py(py)) - } - } - - impl TryIntoPy for &'_ T - where - T: AsRef, - { - fn try_into_py(self, py: Python) -> PyResult { - Ok(self.into_py(py)) + self.into_py_any(py) } } } diff --git a/native/libcst/src/parser/errors.rs b/native/libcst/src/parser/errors.rs index 85690c97..8237cd0b 100644 --- a/native/libcst/src/parser/errors.rs +++ b/native/libcst/src/parser/errors.rs @@ -28,18 +28,11 @@ pub enum ParserError<'a> { #[cfg(feature = "py")] mod py_error { - use pyo3::types::{IntoPyDict, PyAnyMethods, PyModule}; - use pyo3::{IntoPy, PyErr, PyErrArguments, Python}; + use pyo3::types::{IntoPyDict, PyAny, PyAnyMethods, PyModule}; + use pyo3::{Bound, IntoPyObject, PyErr, PyResult, Python}; use super::ParserError; - struct Details { - message: String, - lines: Vec, - raw_line: u32, - raw_column: u32, - } - impl<'a> From> for PyErr { fn from(e: ParserError) -> Self { Python::with_gil(|py| { @@ -59,36 +52,21 @@ mod py_error { line = lines.len() - 1; col = 0; } - let kwargs = [ - ("message", e.to_string().into_py(py)), - ("lines", lines.into_py(py)), - ("raw_line", (line + 1).into_py(py)), - ("raw_column", col.into_py(py)), - ] - .into_py_dict_bound(py); - let libcst = - PyModule::import_bound(py, "libcst").expect("libcst cannot be imported"); - PyErr::from_value_bound( - libcst - .getattr("ParserSyntaxError") - .expect("ParserSyntaxError not found") - .call((), Some(&kwargs)) - .expect("failed to instantiate"), - ) + match || -> PyResult> { + let kwargs = [ + ("message", e.to_string().into_pyobject(py)?.into_any()), + ("lines", lines.into_pyobject(py)?.into_any()), + ("raw_line", (line + 1).into_pyobject(py)?.into_any()), + ("raw_column", col.into_pyobject(py)?.into_any()), + ] + .into_py_dict(py)?; + let libcst = PyModule::import(py, "libcst")?; + libcst.getattr("ParserSyntaxError")?.call((), Some(&kwargs)) + }() { + Ok(py_err_value) => PyErr::from_value(py_err_value), + Err(e) => e, + } }) } } - - impl<'a> PyErrArguments for Details { - fn arguments(self, py: pyo3::Python) -> pyo3::PyObject { - [ - ("message", self.message.into_py(py)), - ("lines", self.lines.into_py(py)), - ("raw_line", self.raw_line.into_py(py)), - ("raw_column", self.raw_column.into_py(py)), - ] - .into_py_dict_bound(py) - .into_py(py) - } - } } From e2e712d43f362ae3b47e11faa1bbb0260810444e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Mar 2025 15:36:26 +0000 Subject: [PATCH 072/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 19b9da1a..b64fb60a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ dev = [ "coverage[toml]>=4.5.4", "build>=0.10.0", "fixit==2.1.0", - "flake8==7.1.1", + "flake8==7.1.2", "Sphinx>=5.1.1", "hypothesis>=4.36.0", "hypothesmith>=0.0.4", From 218e8e5d43429fbf5abed31e4018a26c0103e82b Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Fri, 7 Mar 2025 16:29:53 +0000 Subject: [PATCH 073/151] ci: strip local scheme from uploaded wheels --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index b64fb60a..33143da3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,6 +84,7 @@ excludes = ["native/", "stubs/"] [tool.cibuildwheel] build-verbosity = 1 environment = { PATH = "$PATH:$HOME/.cargo/bin" } +environment-pass = ["LIBCST_NO_LOCAL_SCHEME"] skip = [ "pp*", "*-win32", From cd959d66c0383daec367d3371f58fb4181935f62 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Fri, 7 Mar 2025 17:23:58 +0000 Subject: [PATCH 074/151] ci: pass through LIBCST_NO_LOCAL_SCHEME try #2 --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 33143da3..fb227ce9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,6 +96,7 @@ skip = [ ] [tool.cibuildwheel.linux] +environment-pass = ["LIBCST_NO_LOCAL_SCHEME"] before-all = "yum install -y libatomic; curl https://sh.rustup.rs -sSf | env -u CARGO_HOME sh -s -- --default-toolchain stable --profile minimal -y" [tool.cibuildwheel.macos] From 129d9876d29bc56319c848fe6ed21461c14a7634 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Fri, 7 Mar 2025 18:12:22 +0000 Subject: [PATCH 075/151] ci: force LIBCST_NO_LOCAL_SCHEME in cibuildwheel Summary: Test Plan: --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fb227ce9..637ae895 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,8 +83,7 @@ excludes = ["native/", "stubs/"] [tool.cibuildwheel] build-verbosity = 1 -environment = { PATH = "$PATH:$HOME/.cargo/bin" } -environment-pass = ["LIBCST_NO_LOCAL_SCHEME"] +environment = { PATH = "$PATH:$HOME/.cargo/bin", LIBCST_NO_LOCAL_SCHEME="1" } skip = [ "pp*", "*-win32", From d580469ea515fa6fc6ef38054f3f181ddfea0157 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Wed, 12 Mar 2025 15:57:31 -0600 Subject: [PATCH 076/151] add free-threaded CI (#1312) --- .github/workflows/ci.yml | 51 ++++++++++++++++++++++++++++++++++++++-- pyproject.toml | 7 ++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab290f54..96bb960f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,51 @@ jobs: hatch run coverage combine .coverage.pure hatch run coverage report + # TODO: + # merge into regular CI once hatch has support for creating environments on + # the free-threaded build: https://github.com/pypa/hatch/issues/1931 + free-threaded-tests: + name: "test (${{ matrix.os }}, 3.13t)" + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + # TODO: update to tagged release when there is one + - uses: actions/setup-python@9e62be81b28222addecf85e47571213eb7680449 + with: + cache: pip + cache-dependency-path: "pyproject.toml" + python-version: '3.13t' + - name: Build LibCST + run: | + # Install build-system.requires dependencies + pip install setuptools setuptools-scm setuptools-rust wheel + # Jupyter is annoying to install on free-threaded Python + pip install -e .[dev-without-jupyter] --no-build-isolation + - name: Native Parser Tests + # TODO: remove when native modules declare free-threaded support + env: + PYTHON_GIL: '0' + run: | + python -m coverage run -m libcst.tests + - name: Pure Parser Tests + env: + COVERAGE_FILE: .coverage.pure + LIBCST_PARSER_TYPE: pure + run: | + python -m coverage run -m libcst.tests + - name: Coverage + run: | + python -m coverage combine .coverage.pure + python -m coverage report + + # Run linters lint: runs-on: ubuntu-latest @@ -110,6 +155,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] + python-version: ["3.10", "3.13t"] steps: - uses: actions/checkout@v4 with: @@ -117,9 +163,10 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy - - uses: actions/setup-python@v5 + # TODO: update to tagged release when there is one + - uses: actions/setup-python@9e62be81b28222addecf85e47571213eb7680449 with: - python-version: "3.10" + python-version: ${{ matrix.python-version }} - name: test uses: actions-rs/cargo@v1 with: diff --git a/pyproject.toml b/pyproject.toml index 637ae895..83f208e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,11 @@ dependencies = ["pyyaml>=5.2"] [project.optional-dependencies] dev = [ + "libcst[dev-without-jupyter]", + "jupyter>=1.0.0", + "nbsphinx>=0.4.2", +] +dev-without-jupyter = [ "black==24.8.0", "coverage[toml]>=4.5.4", "build>=0.10.0", @@ -30,9 +35,7 @@ dev = [ "Sphinx>=5.1.1", "hypothesis>=4.36.0", "hypothesmith>=0.0.4", - "jupyter>=1.0.0", "maturin>=1.7.0,<1.8", - "nbsphinx>=0.4.2", "prompt-toolkit>=2.0.9", "pyre-check==0.9.18; platform_system != 'Windows'", "setuptools_scm>=6.0.1", From 17eafc3f437dd770ca47762f2005bbd4e99aa973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Thu, 13 Mar 2025 08:39:55 +0100 Subject: [PATCH 077/151] Bump PyO3 to 0.23.5 (#1311) --- native/Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 1c6cbf60..7352b7f8 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -501,9 +501,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.23.4" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fe09249128b3173d092de9523eaa75136bf7ba85e0d69eca241c7939c933cc" +checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872" dependencies = [ "cfg-if", "indoc", @@ -519,9 +519,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.23.4" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd3927b5a78757a0d71aa9dff669f903b1eb64b54142a9bd9f757f8fde65fd7" +checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb" dependencies = [ "once_cell", "target-lexicon", @@ -529,9 +529,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.23.4" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab6bb2102bd8f991e7749f130a70d05dd557613e39ed2deeee8e9ca0c4d548d" +checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d" dependencies = [ "libc", "pyo3-build-config", @@ -539,9 +539,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.23.4" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91871864b353fd5ffcb3f91f2f703a22a9797c91b9ab497b1acac7b07ae509c7" +checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -551,9 +551,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.23.4" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43abc3b80bc20f3facd86cd3c60beed58c3e2aa26213f3cda368de39c60a27e4" +checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028" dependencies = [ "heck", "proc-macro2", From 5902ccede34e2a6ca5ff3d32ca7e05816029031b Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 13 Mar 2025 09:56:58 +0000 Subject: [PATCH 078/151] Bump version to 1.7.0 (#1313) --- CHANGELOG.md | 19 ++++++++++++++++++- native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 2 +- native/libcst_derive/Cargo.toml | 2 +- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2a559b7..b0ade209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,21 @@ -# 1.6.0 - 2024-01-09 +# 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 diff --git a/native/Cargo.lock b/native/Cargo.lock index 7352b7f8..620ce4ea 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -338,7 +338,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libcst" -version = "1.6.0" +version = "1.7.0" dependencies = [ "annotate-snippets", "criterion", @@ -356,7 +356,7 @@ dependencies = [ [[package]] name = "libcst_derive" -version = "1.6.0" +version = "1.7.0" dependencies = [ "quote", "syn 2.0.75", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index ea56e358..2d3c999b 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "libcst" -version = "1.6.0" +version = "1.7.0" authors = ["LibCST Developers"] edition = "2018" rust-version = "1.70" diff --git a/native/libcst_derive/Cargo.toml b/native/libcst_derive/Cargo.toml index b645d93b..8d5fd2ff 100644 --- a/native/libcst_derive/Cargo.toml +++ b/native/libcst_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libcst_derive" -version = "1.6.0" +version = "1.7.0" edition = "2018" description = "Proc macro helpers for libcst." license = "MIT" From 79f736ac605b147a2b5cd14ab068a43a92f474a8 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Thu, 13 Mar 2025 13:17:02 -0600 Subject: [PATCH 079/151] ci: don't use `--no-build-isolation` for free-threaded CI (#1314) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96bb960f..88e824bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: # Install build-system.requires dependencies pip install setuptools setuptools-scm setuptools-rust wheel # Jupyter is annoying to install on free-threaded Python - pip install -e .[dev-without-jupyter] --no-build-isolation + pip install -e .[dev-without-jupyter] - name: Native Parser Tests # TODO: remove when native modules declare free-threaded support env: From 2c7834eae6a821868c4165ea0f7f99a5bbf5fbc6 Mon Sep 17 00:00:00 2001 From: Hadi Alqattan Date: Sun, 23 Mar 2025 00:24:15 +0300 Subject: [PATCH 080/151] ci: enable macos intel wheels (#1316) --- .github/workflows/build.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 812d1577..b28d8d5f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,8 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-latest, ubuntu-latest, ubuntu-24.04-arm, windows-latest] + # macos-13 is an intel runner, macos-latest is apple silicon + os: [macos-13, macos-latest, ubuntu-latest, ubuntu-24.04-arm, windows-latest] env: SCCACHE_VERSION: 0.2.13 GITHUB_WORKSPACE: "${{github.workspace}}" @@ -24,6 +25,10 @@ jobs: cache: pip cache-dependency-path: "pyproject.toml" python-version: "3.12" + - name: Set MACOSX_DEPLOYMENT_TARGET for Intel MacOS + if: matrix.os == 'macos-13' + run: >- + echo MACOSX_DEPLOYMENT_TARGET=10.12 >> $GITHUB_ENV - name: Disable scmtools local scheme if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} run: >- From cef85096b6bc9ff1889048b8993ba7555cb1c0e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Mar 2025 21:24:55 +0000 Subject: [PATCH 081/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b28d8d5f..982c95dd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ jobs: run: >- echo LIBCST_NO_LOCAL_SCHEME=1 >> $GITHUB_ENV - name: Build wheels - uses: pypa/cibuildwheel@v2.23.0 + uses: pypa/cibuildwheel@v2.23.1 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From 6d31b5ead584c84bde8f48bbd5b9c288687d5a23 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Mon, 31 Mar 2025 22:12:13 -0600 Subject: [PATCH 082/151] use released version of setup-python (#1318) --- .github/workflows/ci.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88e824bb..67333e3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,8 +59,7 @@ jobs: with: fetch-depth: 0 persist-credentials: false - # TODO: update to tagged release when there is one - - uses: actions/setup-python@9e62be81b28222addecf85e47571213eb7680449 + - uses: actions/setup-python@v5 with: cache: pip cache-dependency-path: "pyproject.toml" @@ -163,8 +162,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy - # TODO: update to tagged release when there is one - - uses: actions/setup-python@9e62be81b28222addecf85e47571213eb7680449 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: test From a4804cf07ea9a865a16068700aa3916ae09ff20c Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Wed, 2 Apr 2025 03:19:27 -0600 Subject: [PATCH 083/151] allow configuring empty formatter lists in codemod CLI (#1319) * allow configuring empty formatter lists * appease linter --- libcst/tool.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libcst/tool.py b/libcst/tool.py index ace15ff6..ee4f3673 100644 --- a/libcst/tool.py +++ b/libcst/tool.py @@ -377,7 +377,10 @@ def _codemod_impl(proc_name: str, command_args: List[str]) -> int: # noqa: C901 command_instance = command_class(CodemodContext(), **codemod_args) # Sepcify target version for black formatter - if os.path.basename(config["formatter"][0]) in ("black", "black.exe"): + if any(config["formatter"]) and os.path.basename(config["formatter"][0]) in ( + "black", + "black.exe", + ): parsed_version = parse_version_string(args.python_version) config["formatter"] = [ From 11d6e36450f9062b74bf82f314d42456aabf5625 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Apr 2025 10:19:43 +0100 Subject: [PATCH 084/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 982c95dd..b694f83a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ jobs: run: >- echo LIBCST_NO_LOCAL_SCHEME=1 >> $GITHUB_ENV - name: Build wheels - uses: pypa/cibuildwheel@v2.23.1 + uses: pypa/cibuildwheel@v2.23.2 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From b2406e799c312b6b080003786484e8bb1871f55b Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Mon, 19 May 2025 19:13:17 -0400 Subject: [PATCH 085/151] 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] * update pyo3 to 0.24 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Amethyst Reese --- native/Cargo.lock | 25 ++++++++++++------------ native/libcst/Cargo.toml | 2 +- native/libcst/src/nodes/parser_config.rs | 3 ++- native/libcst_derive/src/into_py.rs | 8 ++++---- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 620ce4ea..c9e67cac 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -501,11 +501,10 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.23.5" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872" +checksum = "f239d656363bcee73afef85277f1b281e8ac6212a1d42aa90e55b90ed43c47a4" dependencies = [ - "cfg-if", "indoc", "libc", "memoffset 0.9.0", @@ -519,9 +518,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.23.5" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb" +checksum = "755ea671a1c34044fa165247aaf6f419ca39caa6003aee791a0df2713d8f1b6d" dependencies = [ "once_cell", "target-lexicon", @@ -529,9 +528,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.23.5" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d" +checksum = "fc95a2e67091e44791d4ea300ff744be5293f394f1bafd9f78c080814d35956e" dependencies = [ "libc", "pyo3-build-config", @@ -539,9 +538,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.23.5" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da" +checksum = "a179641d1b93920829a62f15e87c0ed791b6c8db2271ba0fd7c2686090510214" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -551,9 +550,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.23.5" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028" +checksum = "9dff85ebcaab8c441b0e3f7ae40a6963ecea8a9f5e74f647e33fcf5ec9a1e89e" dependencies = [ "heck", "proc-macro2", @@ -719,9 +718,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.15" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2" +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" [[package]] name = "termcolor" diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 2d3c999b..ae7821f9 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -36,7 +36,7 @@ trace = ["peg/trace"] [dependencies] paste = "1.0.15" -pyo3 = { version = "0.23", optional = true } +pyo3 = { version = "0.25", optional = true } thiserror = "1.0.63" peg = "0.8.4" annotate-snippets = "0.11.5" diff --git a/native/libcst/src/nodes/parser_config.rs b/native/libcst/src/nodes/parser_config.rs index 4a80a048..c10d86d5 100644 --- a/native/libcst/src/nodes/parser_config.rs +++ b/native/libcst/src/nodes/parser_config.rs @@ -125,7 +125,8 @@ fn parser_config_asdict<'py>(py: Python<'py>, config: PyRef<'py, ParserConfig>) ("version", config.version.clone_ref(py)), ("future_imports", config.future_imports.clone_ref(py)), ] - .into_py_dict_bound(py) + .into_py_dict(py) + .unwrap() } pub fn init_module(_py: Python, m: &PyModule) -> PyResult<()> { diff --git a/native/libcst_derive/src/into_py.rs b/native/libcst_derive/src/into_py.rs index ac656cb4..f5470aa1 100644 --- a/native/libcst_derive/src/into_py.rs +++ b/native/libcst_derive/src/into_py.rs @@ -40,7 +40,7 @@ fn impl_into_py_enum(ast: &DeriveInput, e: &DataEnum) -> TokenStream { Self::#varname { #(#fieldnames,)* .. } => { use pyo3::types::PyAnyMethods; - let libcst = pyo3::types::PyModule::import_bound(py, "libcst")?; + let libcst = pyo3::types::PyModule::import(py, "libcst")?; let kwargs = #kwargs_toks ; Ok(libcst .getattr(stringify!(#varname)) @@ -90,7 +90,7 @@ fn impl_into_py_struct(ast: &DeriveInput, e: &DataStruct) -> TokenStream { impl#generics crate::nodes::traits::py::TryIntoPy for #ident #generics { fn try_into_py(self, py: pyo3::Python) -> pyo3::PyResult { use pyo3::types::PyAnyMethods; - let libcst = pyo3::types::PyModule::import_bound(py, "libcst")?; + let libcst = pyo3::types::PyModule::import(py, "libcst")?; let kwargs = #kwargs_toks ; Ok(libcst .getattr(stringify!(#ident)) @@ -165,7 +165,7 @@ fn fields_to_kwargs(fields: &Fields, is_enum: bool) -> quote::__private::TokenSt #(#optional_rust_varnames.map(|x| x.try_into_py(py)).transpose()?.map(|x| (stringify!(#optional_py_varnames), x)),)* }; if empty_kwargs { - quote! { pyo3::types::PyDict::new_bound(py) } + quote! { pyo3::types::PyDict::new(py) } } else { quote! { [ #kwargs_pairs #optional_pairs ] @@ -173,7 +173,7 @@ fn fields_to_kwargs(fields: &Fields, is_enum: bool) -> quote::__private::TokenSt .filter(|x| x.is_some()) .map(|x| x.as_ref().unwrap()) .collect::>() - .into_py_dict_bound(py) + .into_py_dict(py)? } } } From 26139e72decb0d49725a2894834641c0507d3f7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 19:14:19 -0400 Subject: [PATCH 086/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 83f208e4..238bf46d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ dev-without-jupyter = [ "usort==1.0.8.post1", "setuptools-rust>=1.5.2", "slotscheck>=0.7.1", - "jinja2==3.1.5", + "jinja2==3.1.6", ] [project.urls] From 64c761d48612912efe7136d0b98b064e371bb6e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 19:14:36 -0400 Subject: [PATCH 087/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 238bf46d..1d30b075 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ dev-without-jupyter = [ "coverage[toml]>=4.5.4", "build>=0.10.0", "fixit==2.1.0", - "flake8==7.1.2", + "flake8==7.2.0", "Sphinx>=5.1.1", "hypothesis>=4.36.0", "hypothesmith>=0.0.4", From 6e70e1cadc5b8b4066a48bd6be0f72fca32ea4fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 23:51:23 +0000 Subject: [PATCH 088/151] build(deps): bump trybuild from 1.0.99 to 1.0.105 in /native (#1329) --- native/Cargo.lock | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index c9e67cac..f0e2e809 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -722,6 +722,12 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" +[[package]] +name = "target-triple" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790" + [[package]] name = "termcolor" version = "1.1.3" @@ -797,14 +803,15 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.99" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207aa50d36c4be8d8c6ea829478be44a372c6a77669937bb39c698e52f1491e8" +checksum = "1c9bf9513a2f4aeef5fdac8677d7d349c79fdbcc03b9c86da6e9d254f1e43be2" dependencies = [ "glob", "serde", "serde_derive", "serde_json", + "target-triple", "termcolor", "toml", ] From d3386b168f362e63b5b5665e0764a0021a927c90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 19:53:09 -0400 Subject: [PATCH 089/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/zizmor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 797b8d9b..26adcaf2 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -21,7 +21,7 @@ jobs: persist-credentials: false - name: Install the latest version of uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v6 - name: Run zizmor 🌈 run: uvx zizmor --format sarif . > results.sarif From be0b668d089f1e189ebd122f0f7beca31c8d8867 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 20:53:44 -0400 Subject: [PATCH 090/151] 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] * Fix formatting and tests --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Amethyst Reese --- libcst/_parser/parso/tests/test_utils.py | 4 ++-- libcst/_parser/parso/utils.py | 6 +++--- libcst/codemod/commands/rename.py | 2 +- libcst/codemod/tests/test_codemod_cli.py | 2 +- libcst/matchers/_matcher_base.py | 6 +++--- libcst/metadata/scope_provider.py | 2 +- pyproject.toml | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libcst/_parser/parso/tests/test_utils.py b/libcst/_parser/parso/tests/test_utils.py index e033fe9d..1f548ef4 100644 --- a/libcst/_parser/parso/tests/test_utils.py +++ b/libcst/_parser/parso/tests/test_utils.py @@ -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): diff --git a/libcst/_parser/parso/utils.py b/libcst/_parser/parso/utils.py index 7814876c..54517123 100644 --- a/libcst/_parser/parso/utils.py +++ b/libcst/_parser/parso/utils.py @@ -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 diff --git a/libcst/codemod/commands/rename.py b/libcst/codemod/commands/rename.py index 361e33e6..f1fb4693 100644 --- a/libcst/codemod/commands/rename.py +++ b/libcst/codemod/commands/rename.py @@ -15,7 +15,7 @@ from libcst.metadata import QualifiedNameProvider def leave_import_decorator( - method: Callable[..., Union[cst.Import, cst.ImportFrom]] + method: Callable[..., Union[cst.Import, cst.ImportFrom]], ) -> Callable[..., Union[cst.Import, cst.ImportFrom]]: # We want to record any 'as name' that is relevant but only after we leave the corresponding Import/ImportFrom node since # we don't want the 'as name' to interfere with children 'Name' and 'Attribute' nodes. diff --git a/libcst/codemod/tests/test_codemod_cli.py b/libcst/codemod/tests/test_codemod_cli.py index 8773cd77..18dab870 100644 --- a/libcst/codemod/tests/test_codemod_cli.py +++ b/libcst/codemod/tests/test_codemod_cli.py @@ -44,7 +44,7 @@ class TestCodemodCLI(UnitTest): ) else: self.assertIn( - "error: cannot format -: Cannot parse: 13:10: async with AsyncExitStack() as stack:", + "error: cannot format -: Cannot parse for target version Python 3.6: 13:10: async with AsyncExitStack() as stack:", rlt.stderr.decode("utf-8"), ) diff --git a/libcst/matchers/_matcher_base.py b/libcst/matchers/_matcher_base.py index 039694a5..2da2ff4c 100644 --- a/libcst/matchers/_matcher_base.py +++ b/libcst/matchers/_matcher_base.py @@ -800,7 +800,7 @@ class AtLeastN(Generic[_MatcherT], _BaseWildcardNode): def ZeroOrMore( - matcher: Union[_MatcherT, DoNotCareSentinel] = DoNotCareSentinel.DEFAULT + matcher: Union[_MatcherT, DoNotCareSentinel] = DoNotCareSentinel.DEFAULT, ) -> AtLeastN[Union[_MatcherT, DoNotCareSentinel]]: """ Used as a convenience wrapper to :class:`AtLeastN` when ``n`` is equal to ``0``. @@ -903,7 +903,7 @@ class AtMostN(Generic[_MatcherT], _BaseWildcardNode): def ZeroOrOne( - matcher: Union[_MatcherT, DoNotCareSentinel] = DoNotCareSentinel.DEFAULT + matcher: Union[_MatcherT, DoNotCareSentinel] = DoNotCareSentinel.DEFAULT, ) -> AtMostN[Union[_MatcherT, DoNotCareSentinel]]: """ Used as a convenience wrapper to :class:`AtMostN` when ``n`` is equal to ``1``. @@ -1017,7 +1017,7 @@ def _matches_zero_nodes( MatchIfTrue[libcst.CSTNode], _BaseMetadataMatcher, DoNotCareSentinel, - ] + ], ) -> bool: if isinstance(matcher, AtLeastN) and matcher.n == 0: return True diff --git a/libcst/metadata/scope_provider.py b/libcst/metadata/scope_provider.py index ac9c4b9d..909a55b2 100644 --- a/libcst/metadata/scope_provider.py +++ b/libcst/metadata/scope_provider.py @@ -778,7 +778,7 @@ class AnnotationScope(LocalScope): # Attribute(value=Name(value="a"), attr=Name(value="b")) -> ("a.b", "a") # each string has the corresponding CSTNode attached to it def _gen_dotted_names( - node: Union[cst.Attribute, cst.Name] + node: Union[cst.Attribute, cst.Name], ) -> Iterator[Tuple[str, Union[cst.Attribute, cst.Name]]]: if isinstance(node, cst.Name): yield node.value, node diff --git a/pyproject.toml b/pyproject.toml index 1d30b075..6bd6bad1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ dev = [ "nbsphinx>=0.4.2", ] dev-without-jupyter = [ - "black==24.8.0", + "black==25.1.0", "coverage[toml]>=4.5.4", "build>=0.10.0", "fixit==2.1.0", From 9046fba2319dba40e7784eb4a18207b41287695a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 17:32:24 -0700 Subject: [PATCH 091/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 12 ++++++------ native/libcst/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index f0e2e809..ba2f86eb 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -592,9 +592,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -604,9 +604,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -615,9 +615,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index ae7821f9..8b90d85f 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -40,7 +40,7 @@ pyo3 = { version = "0.25", optional = true } thiserror = "1.0.63" peg = "0.8.4" annotate-snippets = "0.11.5" -regex = "1.10.6" +regex = "1.11.1" memchr = "2.7.4" libcst_derive = { path = "../libcst_derive", version = "1.6.0" } From b8fa7577497011ecb37fdd8904058d8312dcd876 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 17:32:31 -0700 Subject: [PATCH 092/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index ba2f86eb..fb8ebae6 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -359,7 +359,7 @@ name = "libcst_derive" version = "1.7.0" dependencies = [ "quote", - "syn 2.0.75", + "syn 2.0.87", "trybuild", ] @@ -545,7 +545,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.75", + "syn 2.0.87", ] [[package]] @@ -558,7 +558,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote", - "syn 2.0.75", + "syn 2.0.87", ] [[package]] @@ -670,7 +670,7 @@ checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.87", ] [[package]] @@ -707,9 +707,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.75" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -754,7 +754,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.87", ] [[package]] From 91a5d7efed875ae948da73f4e766f63d95801960 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 17:38:12 -0700 Subject: [PATCH 093/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b694f83a..65e8683d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ jobs: run: >- echo LIBCST_NO_LOCAL_SCHEME=1 >> $GITHUB_ENV - name: Build wheels - uses: pypa/cibuildwheel@v2.23.2 + uses: pypa/cibuildwheel@v2.23.3 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From 6cfabc9a80cabf27de5700e24f60bc557ed8d59d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 17:49:34 -0700 Subject: [PATCH 094/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 8 ++++---- native/libcst/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index fb8ebae6..bf9b91db 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -739,18 +739,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 8b90d85f..4182a6d1 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -37,7 +37,7 @@ trace = ["peg/trace"] [dependencies] paste = "1.0.15" pyo3 = { version = "0.25", optional = true } -thiserror = "1.0.63" +thiserror = "2.0.12" peg = "0.8.4" annotate-snippets = "0.11.5" regex = "1.11.1" From 88457646b854046fac14fdedbf6a591141a3c4bf Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Wed, 21 May 2025 21:01:18 +0100 Subject: [PATCH 095/151] ci: build windows arm64 wheels (#1304) --- .github/workflows/build.yml | 11 ++++++++++- pyproject.toml | 3 +-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 65e8683d..429e4b82 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,15 @@ jobs: fail-fast: false matrix: # macos-13 is an intel runner, macos-latest is apple silicon - os: [macos-13, macos-latest, ubuntu-latest, ubuntu-24.04-arm, windows-latest] + os: + [ + macos-13, + macos-latest, + ubuntu-latest, + ubuntu-24.04-arm, + windows-latest, + windows-11-arm, + ] env: SCCACHE_VERSION: 0.2.13 GITHUB_WORKSPACE: "${{github.workspace}}" @@ -25,6 +33,7 @@ jobs: cache: pip cache-dependency-path: "pyproject.toml" python-version: "3.12" + - uses: dtolnay/rust-toolchain@stable # zizmor: ignore[unpinned-uses] - name: Set MACOSX_DEPLOYMENT_TARGET for Intel MacOS if: matrix.os == 'macos-13' run: >- diff --git a/pyproject.toml b/pyproject.toml index 6bd6bad1..6bf33d40 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,7 +90,6 @@ environment = { PATH = "$PATH:$HOME/.cargo/bin", LIBCST_NO_LOCAL_SCHEME="1" } skip = [ "pp*", "*-win32", - "*-win_arm64", "*-musllinux_i686", "*-musllinux_ppc64le", "*-musllinux_s390x", @@ -105,4 +104,4 @@ before-all = "yum install -y libatomic; curl https://sh.rustup.rs -sSf | env -u before-all = "rustup target add aarch64-apple-darwin x86_64-apple-darwin" [tool.cibuildwheel.windows] -before-all = "rustup target add x86_64-pc-windows-msvc i686-pc-windows-msvc" +before-all = "rustup target add x86_64-pc-windows-msvc i686-pc-windows-msvc aarch64-pc-windows-msvc" From d002c14d6b7a7121713bd7f30a73a84f4b2b4c1c Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Thu, 22 May 2025 08:18:20 +0100 Subject: [PATCH 096/151] 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` --- libcst/codemod/_cli.py | 20 ++++++++------- libcst/codemod/_dummy_pool.py | 47 ++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/libcst/codemod/_cli.py b/libcst/codemod/_cli.py index 2481bf9d..d091ad8b 100644 --- a/libcst/codemod/_cli.py +++ b/libcst/codemod/_cli.py @@ -14,16 +14,17 @@ import subprocess import sys import time import traceback +from concurrent.futures import as_completed, Executor, ProcessPoolExecutor from copy import deepcopy from dataclasses import dataclass, replace -from multiprocessing import cpu_count, Pool +from multiprocessing import cpu_count from pathlib import Path from typing import Any, AnyStr, cast, Dict, List, Optional, Sequence, Union from libcst import parse_module, PartialParserConfig from libcst.codemod._codemod import Codemod from libcst.codemod._context import CodemodContext -from libcst.codemod._dummy_pool import DummyPool +from libcst.codemod._dummy_pool import DummyExecutor from libcst.codemod._runner import ( SkipFile, SkipReason, @@ -607,13 +608,14 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901 python_version=python_version, ) + pool_impl: type[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 + pool_impl = DummyExecutor else: - pool_impl = Pool + pool_impl = ProcessPoolExecutor # Warm the parser, pre-fork. parse_module( "", @@ -629,7 +631,7 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901 warnings: int = 0 skips: int = 0 - with pool_impl(processes=jobs) as p: # type: ignore + with pool_impl(max_workers=jobs) as executor: # type: ignore args = [ { "transformer": transform, @@ -640,9 +642,9 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901 for filename in files ] try: - for result in p.imap_unordered( - _execute_transform_wrap, args, chunksize=chunksize - ): + futures = [executor.submit(_execute_transform_wrap, arg) for arg in args] + for future in as_completed(futures): + result = future.result() # Print an execution result, keep track of failures _print_parallel_result( result, diff --git a/libcst/codemod/_dummy_pool.py b/libcst/codemod/_dummy_pool.py index c4a24932..34c911bd 100644 --- a/libcst/codemod/_dummy_pool.py +++ b/libcst/codemod/_dummy_pool.py @@ -3,37 +3,50 @@ # 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: + def __init__(self, max_workers: 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 From 52acdf416313012501d31dc004586d5dc0011359 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Sun, 25 May 2025 09:23:10 +0100 Subject: [PATCH 097/151] 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. --- libcst/codemod/_cli.py | 279 +++++++++++++++++++++------------------ libcst/tests/test_e2e.py | 75 ++++++----- libcst/tool.py | 7 +- 3 files changed, 192 insertions(+), 169 deletions(-) diff --git a/libcst/codemod/_cli.py b/libcst/codemod/_cli.py index d091ad8b..71143d69 100644 --- a/libcst/codemod/_cli.py +++ b/libcst/codemod/_cli.py @@ -16,10 +16,11 @@ import time import traceback from concurrent.futures import as_completed, Executor, ProcessPoolExecutor from copy import deepcopy -from dataclasses import dataclass, replace +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, cast, Dict, List, Optional, Sequence, Type, Union +from warnings import warn from libcst import parse_module, PartialParserConfig from libcst.codemod._codemod import Codemod @@ -213,12 +214,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, scratch: Dict[str, object], -) -> ExecutionResult: + 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( @@ -230,45 +271,46 @@ 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.", - ), - ) - - # determine the module and package name for this file - try: - module_name_and_package = calculate_module_and_package( - config.repo_root or ".", 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 - - # Apart from metadata_manager, every field of context should be reset per file - transformer.context = CodemodContext( - scratch=deepcopy(scratch), + # Skip generated files + if ( + not config.include_generated + and config.generated_code_marker.encode("utf-8") in oldcode + ): + return ExecutionResult( filename=filename, - full_module_name=mod_name, - full_package_name=pkg_name, - metadata_manager=transformer.context.metadata_manager, + changed=False, + transform_result=TransformSkip( + skip_reason=SkipReason.GENERATED, + skip_description="Generated file.", + ), + ) + return oldcode + + +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 @@ -281,55 +323,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 @@ -352,13 +365,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( @@ -367,7 +381,7 @@ def _execute_transform( # noqa: C901 transform_result=TransformFailure( error=ex, traceback_str=traceback.format_exc(), - warning_messages=transformer.context.warnings, + warning_messages=warnings, ), ) @@ -504,15 +518,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, @@ -528,38 +535,49 @@ 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({os.path.abspath(f) for f in files}) @@ -579,6 +597,7 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901 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) @@ -591,10 +610,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( @@ -630,19 +646,24 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901 failures: int = 0 warnings: int = 0 skips: int = 0 + original_scratch = ( + deepcopy(transform.context.scratch) if isinstance(transform, Codemod) else {} + ) with pool_impl(max_workers=jobs) as executor: # type: ignore - args = [ - { - "transformer": transform, - "filename": filename, - "config": config, - "scratch": transform.context.scratch, - } - for filename in files - ] try: - futures = [executor.submit(_execute_transform_wrap, arg) for arg in args] + 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 diff --git a/libcst/tests/test_e2e.py b/libcst/tests/test_e2e.py index 6c106b36..e6dfdb5c 100644 --- a/libcst/tests/test_e2e.py +++ b/libcst/tests/test_e2e.py @@ -48,42 +48,45 @@ def temp_workspace() -> Generator[Path, None, None]: class ToolE2ETest(TestCase): def test_leaky_codemod(self) -> None: - with temp_workspace() as tmp: - # File to trigger codemod - example: Path = tmp / "example.py" - example.write_text("""print("Hello")""") - # File that should not be modified - other = tmp / "other.py" - other.touch() - # Just a dir named "dir.py", should be ignored - adir = tmp / "dir.py" - adir.mkdir() + for msg, command in [ + ("instantiated", PrintToPPrintCommand(CodemodContext())), + ("class", PrintToPPrintCommand), + ]: + with self.subTest(msg), temp_workspace() as tmp: + # File to trigger codemod + example: Path = tmp / "example.py" + example.write_text("""print("Hello")""") + # File that should not be modified + other = tmp / "other.py" + other.touch() + # Just a dir named "dir.py", should be ignored + adir = tmp / "dir.py" + adir.mkdir() - # Run command - command_instance = PrintToPPrintCommand(CodemodContext()) - files = gather_files(".") - result = parallel_exec_transform_with_prettyprint( - command_instance, - files, - format_code=False, - hide_progress=True, - ) + # Run command + files = gather_files(".") + result = parallel_exec_transform_with_prettyprint( + command, + files, + format_code=False, + hide_progress=True, + ) - print(result) + print(result) - # Check results - self.assertEqual(2, result.successes) - self.assertEqual(0, result.skips) - self.assertEqual(0, result.failures) - # Expect example.py to be modified - self.assertIn( - "from pprint import pprint", - example.read_text(), - "import missing in example.py", - ) - # Expect other.py to NOT be modified - self.assertNotIn( - "from pprint import pprint", - other.read_text(), - "import found in other.py", - ) + # Check results + self.assertEqual(2, result.successes) + self.assertEqual(0, result.skips) + self.assertEqual(0, result.failures) + # Expect example.py to be modified + self.assertIn( + "from pprint import pprint", + example.read_text(), + "import missing in example.py", + ) + # Expect other.py to NOT be modified + self.assertNotIn( + "from pprint import pprint", + other.read_text(), + "import found in other.py", + ) diff --git a/libcst/tool.py b/libcst/tool.py index ee4f3673..2a04a172 100644 --- a/libcst/tool.py +++ b/libcst/tool.py @@ -374,8 +374,6 @@ def _codemod_impl(proc_name: str, command_args: List[str]) -> int: # noqa: C901 "unified_diff", } } - command_instance = command_class(CodemodContext(), **codemod_args) - # Sepcify target version for black formatter if any(config["formatter"]) and os.path.basename(config["formatter"][0]) in ( "black", @@ -398,7 +396,7 @@ def _codemod_impl(proc_name: str, command_args: List[str]) -> int: # noqa: C901 print("Codemodding from stdin", file=sys.stderr) oldcode = sys.stdin.read() newcode = exec_transform_with_prettyprint( - command_instance, + command_class(CodemodContext(), **codemod_args), # type: ignore oldcode, include_generated=args.include_generated, generated_code_marker=config["generated_code_marker"], @@ -421,7 +419,7 @@ def _codemod_impl(proc_name: str, command_args: List[str]) -> int: # noqa: C901 files = gather_files(args.path, include_stubs=args.include_stubs) try: result = parallel_exec_transform_with_prettyprint( - command_instance, + command_class, files, jobs=args.jobs, unified_diff=args.unified_diff, @@ -436,6 +434,7 @@ def _codemod_impl(proc_name: str, command_args: List[str]) -> int: # noqa: C901 blacklist_patterns=config["blacklist_patterns"], python_version=args.python_version, repo_root=config["repo_root"], + codemod_args=codemod_args, ) except KeyboardInterrupt: print("Interrupted!", file=sys.stderr) From 16ed48d74bb33bb59c18ffa6f31705119044a621 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Sun, 25 May 2025 11:43:18 +0100 Subject: [PATCH 098/151] 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 --- .github/workflows/ci.yml | 56 ++++---------------------------- libcst/codemod/_cli.py | 17 +++++++--- libcst/codemod/_dummy_pool.py | 3 -- native/libcst/src/py.rs | 2 +- pyproject.toml | 61 ++++++++++++++++++----------------- 5 files changed, 52 insertions(+), 87 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 67333e3c..75abc888 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,13 @@ jobs: fail-fast: false matrix: os: [macos-latest, ubuntu-latest, windows-latest] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.13t"] steps: + - uses: actions/setup-python@v5 + with: + python-version: "3.10" + - name: Install hatch + run: pip install -U hatch - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -24,9 +29,6 @@ jobs: cache: pip cache-dependency-path: "pyproject.toml" python-version: ${{ matrix.python-version }} - - name: Install hatch - run: | - pip install -U hatch - uses: actions-rs/toolchain@v1 with: toolchain: stable @@ -44,50 +46,6 @@ jobs: hatch run coverage combine .coverage.pure hatch run coverage report - # TODO: - # merge into regular CI once hatch has support for creating environments on - # the free-threaded build: https://github.com/pypa/hatch/issues/1931 - free-threaded-tests: - name: "test (${{ matrix.os }}, 3.13t)" - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [macos-latest, ubuntu-latest, windows-latest] - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - persist-credentials: false - - uses: actions/setup-python@v5 - with: - cache: pip - cache-dependency-path: "pyproject.toml" - python-version: '3.13t' - - name: Build LibCST - run: | - # Install build-system.requires dependencies - pip install setuptools setuptools-scm setuptools-rust wheel - # Jupyter is annoying to install on free-threaded Python - pip install -e .[dev-without-jupyter] - - name: Native Parser Tests - # TODO: remove when native modules declare free-threaded support - env: - PYTHON_GIL: '0' - run: | - python -m coverage run -m libcst.tests - - name: Pure Parser Tests - env: - COVERAGE_FILE: .coverage.pure - LIBCST_PARSER_TYPE: pure - run: | - python -m coverage run -m libcst.tests - - name: Coverage - run: | - python -m coverage combine .coverage.pure - python -m coverage report - - # Run linters lint: runs-on: ubuntu-latest @@ -139,7 +97,7 @@ jobs: - name: Install hatch run: pip install -U hatch - uses: ts-graphviz/setup-graphviz@v2 - - run: hatch run docs + - run: hatch run docs:docs - name: Archive Docs uses: actions/upload-artifact@v4 with: diff --git a/libcst/codemod/_cli.py b/libcst/codemod/_cli.py index 71143d69..178cabcb 100644 --- a/libcst/codemod/_cli.py +++ b/libcst/codemod/_cli.py @@ -8,18 +8,19 @@ Provides helpers for CLI interaction. """ import difflib +import functools import os.path import re import subprocess import sys import time import traceback -from concurrent.futures import as_completed, Executor, ProcessPoolExecutor +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 AnyStr, cast, Dict, List, Optional, Sequence, Type, Union +from typing import AnyStr, Callable, cast, Dict, List, Optional, Sequence, Type, Union from warnings import warn from libcst import parse_module, PartialParserConfig @@ -624,14 +625,20 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901 python_version=python_version, ) - pool_impl: type[Executor] + 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 executor. jobs = 1 pool_impl = DummyExecutor + elif getattr(sys, "_is_gil_enabled", lambda: False)(): # pyre-ignore[16] + from concurrent.futures import ThreadPoolExecutor + + pool_impl = functools.partial(ThreadPoolExecutor, max_workers=jobs) else: - pool_impl = ProcessPoolExecutor + from concurrent.futures import ProcessPoolExecutor + + pool_impl = functools.partial(ProcessPoolExecutor, max_workers=jobs) # Warm the parser, pre-fork. parse_module( "", @@ -650,7 +657,7 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901 deepcopy(transform.context.scratch) if isinstance(transform, Codemod) else {} ) - with pool_impl(max_workers=jobs) as executor: # type: ignore + with pool_impl() as executor: # type: ignore try: futures = [ executor.submit( diff --git a/libcst/codemod/_dummy_pool.py b/libcst/codemod/_dummy_pool.py index 34c911bd..aa23a7d4 100644 --- a/libcst/codemod/_dummy_pool.py +++ b/libcst/codemod/_dummy_pool.py @@ -22,9 +22,6 @@ class DummyExecutor(Executor): Synchronous dummy `concurrent.futures.Executor` analogue. """ - def __init__(self, max_workers: Optional[int] = None) -> None: - pass - def submit( self, fn: Callable[Params, Return], diff --git a/native/libcst/src/py.rs b/native/libcst/src/py.rs index bd7dfe6d..57da11e7 100644 --- a/native/libcst/src/py.rs +++ b/native/libcst/src/py.rs @@ -6,7 +6,7 @@ use crate::nodes::traits::py::TryIntoPy; use pyo3::prelude::*; -#[pymodule] +#[pymodule(gil_used = false)] #[pyo3(name = "native")] pub fn libcst_native(_py: Python, m: &Bound) -> PyResult<()> { #[pyfn(m)] diff --git a/pyproject.toml b/pyproject.toml index 6bf33d40..bdeb4660 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,33 +18,9 @@ classifiers = [ "Typing :: Typed", ] requires-python = ">=3.9" -dependencies = ["pyyaml>=5.2"] - -[project.optional-dependencies] -dev = [ - "libcst[dev-without-jupyter]", - "jupyter>=1.0.0", - "nbsphinx>=0.4.2", -] -dev-without-jupyter = [ - "black==25.1.0", - "coverage[toml]>=4.5.4", - "build>=0.10.0", - "fixit==2.1.0", - "flake8==7.2.0", - "Sphinx>=5.1.1", - "hypothesis>=4.36.0", - "hypothesmith>=0.0.4", - "maturin>=1.7.0,<1.8", - "prompt-toolkit>=2.0.9", - "pyre-check==0.9.18; platform_system != 'Windows'", - "setuptools_scm>=6.0.1", - "sphinx-rtd-theme>=0.4.3", - "ufmt==2.8.0", - "usort==1.0.8.post1", - "setuptools-rust>=1.5.2", - "slotscheck>=0.7.1", - "jinja2==3.1.6", +dependencies = [ + "pyyaml>=5.2; python_version < '3.13'", + "pyyaml-ft; python_version >= '3.13'", ] [project.urls] @@ -63,10 +39,26 @@ show_missing = true skip_covered = true [tool.hatch.envs.default] -features = ["dev"] +installer = "uv" +dependencies = [ + "black==25.1.0", + "coverage[toml]>=4.5.4", + "build>=0.10.0", + "fixit==2.1.0", + "flake8==7.2.0", + "hypothesis>=4.36.0", + "hypothesmith>=0.0.4", + "maturin>=1.7.0,<1.8", + "prompt-toolkit>=2.0.9", + "pyre-check==0.9.18; platform_system != 'Windows'", + "setuptools_scm>=6.0.1", + "ufmt==2.8.0", + "usort==1.0.8.post1", + "setuptools-rust>=1.5.2", + "slotscheck>=0.7.1", +] [tool.hatch.envs.default.scripts] -docs = "sphinx-build -ab html docs/source docs/build" fixtures = ["python scripts/regenerate-fixtures.py", "git diff --exit-code"] format = "ufmt format libcst scripts" lint = [ @@ -78,6 +70,17 @@ lint = [ test = ["python --version", "python -m coverage run -m libcst.tests"] typecheck = ["pyre --version", "pyre check"] +[tool.hatch.envs.docs] +extra-dependencies = [ + "Sphinx>=5.1.1", + "sphinx-rtd-theme>=0.4.3", + "jupyter>=1.0.0", + "nbsphinx>=0.4.2", + "jinja2==3.1.6", +] +[tool.hatch.envs.docs.scripts] +docs = "sphinx-build -ab html docs/source docs/build" + [tool.slotscheck] exclude-modules = '^libcst\.(testing|tests)' From c224665ed767f2b3c0be0a4c16a14c54be65842d Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Sun, 25 May 2025 11:44:16 +0100 Subject: [PATCH 099/151] ci: start building cp313t wheels (#1333) Closes #1242. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index bdeb4660..27dbb710 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,6 +98,7 @@ skip = [ "*-musllinux_s390x", "*-musllinux_armv7l", ] +enable = ["cpython-freethreading"] [tool.cibuildwheel.linux] environment-pass = ["LIBCST_NO_LOCAL_SCHEME"] From b560ae815c64bd83bfc635cbaac4fa427de933a7 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Sun, 25 May 2025 20:13:12 +0100 Subject: [PATCH 100/151] Threadpool should be used if GIL is disabled. (#1335) --- libcst/codemod/_cli.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libcst/codemod/_cli.py b/libcst/codemod/_cli.py index 178cabcb..0cdec103 100644 --- a/libcst/codemod/_cli.py +++ b/libcst/codemod/_cli.py @@ -631,11 +631,7 @@ def parallel_exec_transform_with_prettyprint( # noqa: C901 # Let's just use a dummy synchronous executor. jobs = 1 pool_impl = DummyExecutor - elif getattr(sys, "_is_gil_enabled", lambda: False)(): # pyre-ignore[16] - from concurrent.futures import ThreadPoolExecutor - - pool_impl = functools.partial(ThreadPoolExecutor, max_workers=jobs) - else: + elif getattr(sys, "_is_gil_enabled", lambda: True)(): # pyre-ignore[16] from concurrent.futures import ProcessPoolExecutor pool_impl = functools.partial(ProcessPoolExecutor, max_workers=jobs) @@ -648,6 +644,10 @@ 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 From 3dc2289bf6e21f36fb461e3ed4cf792994a87bb1 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Mon, 26 May 2025 08:40:54 +0100 Subject: [PATCH 101/151] 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. --- libcst/codegen/gen_matcher_classes.py | 116 ++++++++++++++++++++- libcst/codegen/tests/test_codegen_clean.py | 68 ++++++++++-- 2 files changed, 174 insertions(+), 10 deletions(-) diff --git a/libcst/codegen/gen_matcher_classes.py b/libcst/codegen/gen_matcher_classes.py index b7940f97..7f3cb6f8 100644 --- a/libcst/codegen/gen_matcher_classes.py +++ b/libcst/codegen/gen_matcher_classes.py @@ -16,6 +16,109 @@ CLASS_RE = r"" 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 @@ -357,7 +460,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 Exception( + f"Logic error, unexpected type in Sequence: {type(inner_type)}!" + ) return _get_wrapped_union_type( typecst.deep_replace(inner_type, clean_inner_type), @@ -386,9 +491,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. @@ -397,7 +505,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 Exception(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 diff --git a/libcst/codegen/tests/test_codegen_clean.py b/libcst/codegen/tests/test_codegen_clean.py index 7b71ba36..5ee5903d 100644 --- a/libcst/codegen/tests/test_codegen_clean.py +++ b/libcst/codegen/tests/test_codegen_clean.py @@ -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]" + ) From 50032882d0004df6332c8e67e1d8f94105893fef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 08:41:36 +0100 Subject: [PATCH 102/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 12 ++++++------ native/libcst/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index bf9b91db..a5b4f13f 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -431,9 +431,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "peg" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f" +checksum = "9928cfca101b36ec5163e70049ee5368a8a1c3c6efc9ca9c5f9cc2f816152477" dependencies = [ "peg-macros", "peg-runtime", @@ -441,9 +441,9 @@ dependencies = [ [[package]] name = "peg-macros" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426" +checksum = "6298ab04c202fa5b5d52ba03269fb7b74550b150323038878fe6c372d8280f71" dependencies = [ "peg-runtime", "proc-macro2", @@ -452,9 +452,9 @@ dependencies = [ [[package]] name = "peg-runtime" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" +checksum = "132dca9b868d927b35b5dd728167b2dee150eb1ad686008fc71ccb298b776fca" [[package]] name = "plotters" diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 4182a6d1..d639af1f 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -38,7 +38,7 @@ trace = ["peg/trace"] paste = "1.0.15" pyo3 = { version = "0.25", optional = true } thiserror = "2.0.12" -peg = "0.8.4" +peg = "0.8.5" annotate-snippets = "0.11.5" regex = "1.11.1" memchr = "2.7.4" From 3389d4e231ce47e0ca9b9978e5a479546819194a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 08:42:06 +0100 Subject: [PATCH 103/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index a5b4f13f..52f71ec1 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -563,9 +563,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] From 356ac005867b79f50284b1b103c6c397b376f791 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 08:42:40 +0100 Subject: [PATCH 104/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 52f71ec1..97ae6665 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -359,7 +359,7 @@ name = "libcst_derive" version = "1.7.0" dependencies = [ "quote", - "syn 2.0.87", + "syn 2.0.101", "trybuild", ] @@ -492,9 +492,9 @@ checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -545,7 +545,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.87", + "syn 2.0.101", ] [[package]] @@ -558,7 +558,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote", - "syn 2.0.87", + "syn 2.0.101", ] [[package]] @@ -670,7 +670,7 @@ checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.101", ] [[package]] @@ -707,9 +707,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -754,7 +754,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.101", ] [[package]] From efae53d3652f3683341679b0105ae11ad29f4d0b Mon Sep 17 00:00:00 2001 From: Amethyst Reese Date: Mon, 26 May 2025 03:02:44 -0700 Subject: [PATCH 105/151] Run CI tests on 3.14 (#1331) * Run CI tests on 3.14 * noop commit to retrigger CI --------- Co-authored-by: Zsolt Dollenstein --- .github/workflows/ci.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75abc888..19329a87 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,15 @@ jobs: fail-fast: false matrix: os: [macos-latest, ubuntu-latest, windows-latest] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.13t"] + python-version: + - "3.9" + - "3.10" + - "3.11" + - "3.12" + - "3.13" + - "3.13t" + - "3.14" + - "3.14t" steps: - uses: actions/setup-python@v5 with: @@ -29,6 +37,7 @@ jobs: cache: pip cache-dependency-path: "pyproject.toml" python-version: ${{ matrix.python-version }} + allow-prereleases: true - uses: actions-rs/toolchain@v1 with: toolchain: stable From 1e67a9bb840b07478661925d46fdfc13dd2b05ed Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Tue, 27 May 2025 11:44:16 +0100 Subject: [PATCH 106/151] Build 3.14 wheels for testing (#1345) * Build 3.14 wheels for testing * use cibuildwheel 3 --- .github/workflows/build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 429e4b82..1a6e728b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,8 +42,11 @@ jobs: 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.23.3 + uses: pypa/cibuildwheel@v3.0.0b2 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From ae64e0d534b58bdc89f6bfa41513a24873063b39 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Tue, 27 May 2025 14:15:49 +0100 Subject: [PATCH 107/151] ci: fix zizmor warnings (#1347) --- .github/workflows/build.yml | 4 +--- .github/workflows/ci.yml | 2 ++ .github/workflows/pypi_upload.yml | 5 ++--- zizmor.yml | 5 +++++ 4 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 zizmor.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1a6e728b..dc2e54b3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,10 +30,8 @@ jobs: persist-credentials: false - uses: actions/setup-python@v5 with: - cache: pip - cache-dependency-path: "pyproject.toml" python-version: "3.12" - - uses: dtolnay/rust-toolchain@stable # zizmor: ignore[unpinned-uses] + - uses: dtolnay/rust-toolchain@stable - name: Set MACOSX_DEPLOYMENT_TARGET for Intel MacOS if: matrix.os == 'macos-13' run: >- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19329a87..409f7df1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,8 @@ on: - main pull_request: +permissions: {} + jobs: test: runs-on: ${{ matrix.os }} diff --git a/.github/workflows/pypi_upload.yml b/.github/workflows/pypi_upload.yml index f03151f7..e8798c6e 100644 --- a/.github/workflows/pypi_upload.yml +++ b/.github/workflows/pypi_upload.yml @@ -32,19 +32,18 @@ jobs: merge-multiple: true - uses: actions/setup-python@v5 with: - cache: pip - cache-dependency-path: "pyproject.toml" python-version: "3.10" - name: Install hatch run: pip install -U hatch - name: Build a source tarball env: LIBCST_NO_LOCAL_SCHEME: 1 + OUTDIR: ${{ steps.download.outputs.download-path }} run: >- hatch 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 diff --git a/zizmor.yml b/zizmor.yml new file mode 100644 index 00000000..faf5a0f0 --- /dev/null +++ b/zizmor.yml @@ -0,0 +1,5 @@ +rules: + unpinned-uses: + config: + policies: + "*": ref-pin \ No newline at end of file From 18d4f6aded907bd11b683fa54dad32ca04f84f75 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Tue, 27 May 2025 15:02:58 +0100 Subject: [PATCH 108/151] bump version to 1.8.0 (#1348) --- CHANGELOG.md | 21 +++++++++++++++++++++ MAINTAINERS.md | 11 +++++++++++ native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 4 ++-- native/libcst_derive/Cargo.toml | 2 +- 5 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 MAINTAINERS.md diff --git a/CHANGELOG.md b/CHANGELOG.md index b0ade209..bb50a57a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +# 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 diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 00000000..eb94a2a0 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,11 @@ +# 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. make a new PR with the above changes, get it reviewed and landed +4. make a new release on Github, create a new tag on publish, and copy the contents of the changelog entry in there +5. 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` \ No newline at end of file diff --git a/native/Cargo.lock b/native/Cargo.lock index 97ae6665..d09b42a5 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -338,7 +338,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libcst" -version = "1.7.0" +version = "1.8.0" dependencies = [ "annotate-snippets", "criterion", @@ -356,7 +356,7 @@ dependencies = [ [[package]] name = "libcst_derive" -version = "1.7.0" +version = "1.8.0" dependencies = [ "quote", "syn 2.0.101", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index d639af1f..e9c7411c 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "libcst" -version = "1.7.0" +version = "1.8.0" authors = ["LibCST Developers"] edition = "2018" rust-version = "1.70" @@ -42,7 +42,7 @@ peg = "0.8.5" annotate-snippets = "0.11.5" regex = "1.11.1" memchr = "2.7.4" -libcst_derive = { path = "../libcst_derive", version = "1.6.0" } +libcst_derive = { path = "../libcst_derive", version = "1.8.0" } [dev-dependencies] criterion = { version = "0.5.1", features = ["html_reports"] } diff --git a/native/libcst_derive/Cargo.toml b/native/libcst_derive/Cargo.toml index 8d5fd2ff..78c8554f 100644 --- a/native/libcst_derive/Cargo.toml +++ b/native/libcst_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libcst_derive" -version = "1.7.0" +version = "1.8.0" edition = "2018" description = "Proc macro helpers for libcst." license = "MIT" From 482a2e5f0997273e3fb272346dac275c04e84807 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 03:59:34 +0100 Subject: [PATCH 109/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc2e54b3..3306be71 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: if: github.event_name != 'release' run: echo CIBW_ENABLE=cpython-prerelease >> $GITHUB_ENV - name: Build wheels - uses: pypa/cibuildwheel@v3.0.0b2 + uses: pypa/cibuildwheel@v3.0.0b4 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From 935415a35a88fc4f72793b651f1786d714188a6a Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Tue, 3 Jun 2025 22:38:19 -0700 Subject: [PATCH 110/151] ci: stop using actions-rs actions (#1352) --- .github/workflows/ci.yml | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 409f7df1..87a2ea11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,9 +40,7 @@ jobs: cache-dependency-path: "pyproject.toml" python-version: ${{ matrix.python-version }} allow-prereleases: true - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable + - uses: dtolnay/rust-toolchain@stable - name: Build LibCST run: hatch -vv env create - name: Native Parser Tests @@ -135,26 +133,14 @@ jobs: with: 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 - uses: actions-rs/cargo@v1 - with: - command: bench - args: --manifest-path=native/Cargo.toml --no-run + run: cargo bench --manifest-path=native/Cargo.toml --no-run rustfmt: name: Rustfmt @@ -167,7 +153,5 @@ jobs: 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 From e12eef5810c6e21cf2ef3703b0eae1d1042e4452 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Wed, 4 Jun 2025 14:02:21 -0700 Subject: [PATCH 111/151] add helper to convert nodes to matchers (#1351) * add helper to convert nodes to matchers * suppress type error --- libcst/helpers/matchers.py | 45 +++++++++++++++++++++++ libcst/helpers/tests/test_matchers.py | 53 +++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 libcst/helpers/matchers.py create mode 100644 libcst/helpers/tests/test_matchers.py diff --git a/libcst/helpers/matchers.py b/libcst/helpers/matchers.py new file mode 100644 index 00000000..e641c43c --- /dev/null +++ b/libcst/helpers/matchers.py @@ -0,0 +1,45 @@ +# 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 dataclasses import fields, is_dataclass, MISSING + +from libcst import matchers +from libcst._nodes.base import CSTNode + + +def node_to_matcher( + node: CSTNode, *, match_syntactic_trivia: bool = False +) -> matchers.BaseMatcherNode: + """Convert a concrete node to a matcher.""" + if not is_dataclass(node): + raise ValueError(f"{node} is not a CSTNode") + + attrs = {} + for field in fields(node): + name = field.name + child = getattr(node, name) + if not match_syntactic_trivia and field.name.startswith("whitespace"): + # Not all nodes have whitespace fields, some have multiple, but they all + # start with whitespace* + child = matchers.DoNotCare() + elif field.default is not MISSING and child == field.default: + child = matchers.DoNotCare() + # pyre-ignore[29]: Union[MISSING_TYPE, ...] is not a function. + elif field.default_factory is not MISSING and child == field.default_factory(): + child = matchers.DoNotCare() + elif isinstance(child, (list, tuple)): + child = type(child)( + node_to_matcher(item, match_syntactic_trivia=match_syntactic_trivia) + for item in child + ) + elif hasattr(matchers, type(child).__name__): + child = node_to_matcher( + child, match_syntactic_trivia=match_syntactic_trivia + ) + attrs[name] = child + + matcher = getattr(matchers, type(node).__name__) + return matcher(**attrs) diff --git a/libcst/helpers/tests/test_matchers.py b/libcst/helpers/tests/test_matchers.py new file mode 100644 index 00000000..3f2b9b47 --- /dev/null +++ b/libcst/helpers/tests/test_matchers.py @@ -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 libcst import parse_expression, parse_statement +from libcst.helpers.matchers import node_to_matcher +from libcst.matchers import matches +from libcst.testing.utils import data_provider, UnitTest + + +class MatchersTest(UnitTest): + @data_provider( + ( + ('"some string"',), + ("call(some, **kwargs)",), + ("a[b.c]",), + ("[1 for _ in range(99) if False]",), + ) + ) + def test_reflexive_expressions(self, code: str) -> None: + node = parse_expression(code) + matcher = node_to_matcher(node) + self.assertTrue(matches(node, matcher)) + + @data_provider( + ( + ("def foo(a) -> None: pass",), + ("class F: ...",), + ("foo: bar",), + ) + ) + def test_reflexive_statements(self, code: str) -> None: + node = parse_statement(code) + matcher = node_to_matcher(node) + self.assertTrue(matches(node, matcher)) + + def test_whitespace(self) -> None: + code_ws = parse_expression("(foo , bar )") + code = parse_expression("(foo,bar)") + self.assertTrue( + matches( + code, + node_to_matcher(code_ws), + ) + ) + self.assertFalse( + matches( + code, + node_to_matcher(code_ws, match_syntactic_trivia=True), + ) + ) From ca1f81f0496a6382bce5537fcbea2f9d3610d652 Mon Sep 17 00:00:00 2001 From: zaicruvoir1rominet <105832258+zaicruvoir1rominet@users.noreply.github.com> Date: Sat, 7 Jun 2025 10:53:44 +0200 Subject: [PATCH 112/151] 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 --- libcst/__init__.py | 3 +- libcst/_exceptions.py | 47 ++-------------- libcst/_nodes/base.py | 9 +-- libcst/_nodes/expression.py | 8 ++- libcst/_nodes/statement.py | 14 ++--- libcst/_nodes/tests/test_funcdef.py | 4 +- libcst/_nodes/tests/test_namedexpr.py | 4 +- libcst/_nodes/tests/test_removal_behavior.py | 2 +- libcst/_parser/_parsing_check.py | 53 ++++++++++++++++++ libcst/_parser/base_parser.py | 10 +--- libcst/_parser/conversions/expression.py | 56 +++++++++++++++---- libcst/_parser/conversions/params.py | 13 +++-- libcst/_parser/conversions/statement.py | 29 ++++++---- libcst/_parser/grammar.py | 8 +-- libcst/_parser/parso/pgen2/generator.py | 2 +- libcst/_parser/parso/python/tokenize.py | 15 ++--- libcst/_parser/production_decorator.py | 2 +- libcst/_parser/py_whitespace_parser.py | 19 +++++-- libcst/codegen/gen_matcher_classes.py | 18 +++--- libcst/codemod/_cli.py | 4 +- libcst/codemod/_codemod.py | 4 +- libcst/codemod/_visitor.py | 8 +-- .../commands/convert_format_to_fstring.py | 24 +++++--- .../convert_percent_format_to_fstring.py | 4 +- .../codemod/commands/fix_pyre_directives.py | 5 +- libcst/codemod/commands/rename.py | 6 +- libcst/codemod/visitors/_add_imports.py | 10 ++-- libcst/codemod/visitors/_remove_imports.py | 11 ++-- libcst/display/text.py | 4 +- libcst/helpers/_template.py | 12 ++-- libcst/helpers/common.py | 4 +- libcst/helpers/expression.py | 2 +- libcst/helpers/module.py | 4 +- libcst/matchers/_matcher_base.py | 48 ++++++++-------- libcst/metadata/base_provider.py | 2 +- libcst/metadata/full_repo_manager.py | 2 +- .../tests/test_type_inference_provider.py | 10 +--- libcst/metadata/type_inference_provider.py | 30 +++++----- libcst/tool.py | 10 ++-- 39 files changed, 305 insertions(+), 215 deletions(-) create mode 100644 libcst/_parser/_parsing_check.py diff --git a/libcst/__init__.py b/libcst/__init__.py index 2a8e47b3..4e82bc9e 100644 --- a/libcst/__init__.py +++ b/libcst/__init__.py @@ -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 @@ -242,6 +242,7 @@ __all__ = [ "CSTVisitorT", "FlattenSentinel", "MaybeSentinel", + "CSTLogicError", "MetadataException", "ParserSyntaxError", "PartialParserConfig", diff --git a/libcst/_exceptions.py b/libcst/_exceptions.py index 5359ca3c..4d3dd386 100644 --- a/libcst/_exceptions.py +++ b/libcst/_exceptions.py @@ -4,16 +4,11 @@ # LICENSE file in the root directory of this source tree. from enum import auto, Enum -from typing import Any, Callable, final, Iterable, Optional, Sequence, Tuple, Union +from typing import Any, Callable, final, Optional, Sequence, Tuple -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" @@ -21,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. diff --git a/libcst/_nodes/base.py b/libcst/_nodes/base.py index d043cb64..666fe311 100644 --- a/libcst/_nodes/base.py +++ b/libcst/_nodes/base.py @@ -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 @@ -237,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__}" ) @@ -382,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 *Sentinel here!") + raise CSTLogicError("Logic error, cannot get a *Sentinel here!") return new_tree def deep_remove( @@ -399,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 @@ -421,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: diff --git a/libcst/_nodes/expression.py b/libcst/_nodes/expression.py index a8f2ac3e..00a1b4a7 100644 --- a/libcst/_nodes/expression.py +++ b/libcst/_nodes/expression.py @@ -17,6 +17,8 @@ from tokenize import ( ) from typing import Callable, Generator, Literal, Optional, Sequence, Union +from libcst import CSTLogicError + from libcst._add_slots import add_slots from libcst._maybe_sentinel import MaybeSentinel from libcst._nodes.base import CSTCodegenError, CSTNode, CSTValidationError @@ -666,7 +668,7 @@ class SimpleString(_BasePrefixedString): if len(quote) not in {1, 3}: # We shouldn't get here due to construction validation logic, # but handle the case anyway. - raise Exception(f"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. @@ -1010,7 +1012,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.") @@ -1688,7 +1690,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) diff --git a/libcst/_nodes/statement.py b/libcst/_nodes/statement.py index e6d6915f..1aba38d3 100644 --- a/libcst/_nodes/statement.py +++ b/libcst/_nodes/statement.py @@ -9,6 +9,8 @@ from abc import ABC, abstractmethod from dataclasses import dataclass, field 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 from libcst._nodes.base import CSTNode, CSTValidationError @@ -1165,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( @@ -1199,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: diff --git a/libcst/_nodes/tests/test_funcdef.py b/libcst/_nodes/tests/test_funcdef.py index 087dde19..65a0ff07 100644 --- a/libcst/_nodes/tests/test_funcdef.py +++ b/libcst/_nodes/tests/test_funcdef.py @@ -1052,7 +1052,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 diff --git a/libcst/_nodes/tests/test_namedexpr.py b/libcst/_nodes/tests/test_namedexpr.py index bddd4f3d..6ebcf978 100644 --- a/libcst/_nodes/tests/test_namedexpr.py +++ b/libcst/_nodes/tests/test_namedexpr.py @@ -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 diff --git a/libcst/_nodes/tests/test_removal_behavior.py b/libcst/_nodes/tests/test_removal_behavior.py index 9b1bf619..709b26f5 100644 --- a/libcst/_nodes/tests/test_removal_behavior.py +++ b/libcst/_nodes/tests/test_removal_behavior.py @@ -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) diff --git a/libcst/_parser/_parsing_check.py b/libcst/_parser/_parsing_check.py new file mode 100644 index 00000000..03283c95 --- /dev/null +++ b/libcst/_parser/_parsing_check.py @@ -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}." diff --git a/libcst/_parser/base_parser.py b/libcst/_parser/base_parser.py index 6ab97ab8..d349bb14 100644 --- a/libcst/_parser/base_parser.py +++ b/libcst/_parser/base_parser.py @@ -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: diff --git a/libcst/_parser/conversions/expression.py b/libcst/_parser/conversions/expression.py index 1a46de2a..79d7ad78 100644 --- a/libcst/_parser/conversions/expression.py +++ b/libcst/_parser/conversions/expression.py @@ -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(f"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) diff --git a/libcst/_parser/conversions/params.py b/libcst/_parser/conversions/params.py index 9ac7f1d1..5b29f95d 100644 --- a/libcst/_parser/conversions/params.py +++ b/libcst/_parser/conversions/params.py @@ -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 diff --git a/libcst/_parser/conversions/statement.py b/libcst/_parser/conversions/statement.py index 608f002f..f96c6ea2 100644 --- a/libcst/_parser/conversions/statement.py +++ b/libcst/_parser/conversions/statement.py @@ -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") diff --git a/libcst/_parser/grammar.py b/libcst/_parser/grammar.py index 8e6ade59..ee65ef72 100644 --- a/libcst/_parser/grammar.py +++ b/libcst/_parser/grammar.py @@ -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 diff --git a/libcst/_parser/parso/pgen2/generator.py b/libcst/_parser/parso/pgen2/generator.py index ae889f33..5e83741b 100644 --- a/libcst/_parser/parso/pgen2/generator.py +++ b/libcst/_parser/parso/pgen2/generator.py @@ -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) diff --git a/libcst/_parser/parso/python/tokenize.py b/libcst/_parser/parso/python/tokenize.py index bfd159dd..711a8785 100644 --- a/libcst/_parser/parso/python/tokenize.py +++ b/libcst/_parser/parso/python/tokenize.py @@ -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 diff --git a/libcst/_parser/production_decorator.py b/libcst/_parser/production_decorator.py index 41a817f8..d5ba52de 100644 --- a/libcst/_parser/production_decorator.py +++ b/libcst/_parser/production_decorator.py @@ -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}'." ) diff --git a/libcst/_parser/py_whitespace_parser.py b/libcst/_parser/py_whitespace_parser.py index b1fd9b5e..6b6573a6 100644 --- a/libcst/_parser/py_whitespace_parser.py +++ b/libcst/_parser/py_whitespace_parser.py @@ -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 diff --git a/libcst/codegen/gen_matcher_classes.py b/libcst/codegen/gen_matcher_classes.py index 7f3cb6f8..e6def68c 100644 --- a/libcst/codegen/gen_matcher_classes.py +++ b/libcst/codegen/gen_matcher_classes.py @@ -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)) @@ -283,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): @@ -449,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): @@ -460,7 +464,7 @@ 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( + raise CSTLogicError( f"Logic error, unexpected type in Sequence: {type(inner_type)}!" ) @@ -505,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(f"Logic error, unexpected top level type: {type(typecst)}!") + 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 diff --git a/libcst/codemod/_cli.py b/libcst/codemod/_cli.py index 0cdec103..d9c70d05 100644 --- a/libcst/codemod/_cli.py +++ b/libcst/codemod/_cli.py @@ -51,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. @@ -593,7 +593,7 @@ 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) diff --git a/libcst/codemod/_codemod.py b/libcst/codemod/_codemod.py index c0c3b2c7..e267f154 100644 --- a/libcst/codemod/_codemod.py +++ b/libcst/codemod/_codemod.py @@ -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 diff --git a/libcst/codemod/_visitor.py b/libcst/codemod/_visitor.py index ab915c49..89248838 100644 --- a/libcst/codemod/_visitor.py +++ b/libcst/codemod/_visitor.py @@ -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()." ) diff --git a/libcst/codemod/commands/convert_format_to_fstring.py b/libcst/codemod/commands/convert_format_to_fstring.py index ab98c0ea..43d19bce 100644 --- a/libcst/codemod/commands/convert_format_to_fstring.py +++ b/libcst/codemod/commands/convert_format_to_fstring.py @@ -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 "'" @@ -296,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) diff --git a/libcst/codemod/commands/convert_percent_format_to_fstring.py b/libcst/codemod/commands/convert_percent_format_to_fstring.py index 501c9621..d74624e4 100644 --- a/libcst/codemod/commands/convert_percent_format_to_fstring.py +++ b/libcst/codemod/commands/convert_percent_format_to_fstring.py @@ -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 diff --git a/libcst/codemod/commands/fix_pyre_directives.py b/libcst/codemod/commands/fix_pyre_directives.py index c3ab41b7..a9779d0f 100644 --- a/libcst/codemod/commands/fix_pyre_directives.py +++ b/libcst/codemod/commands/fix_pyre_directives.py @@ -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( diff --git a/libcst/codemod/commands/rename.py b/libcst/codemod/commands/rename.py index f1fb4693..f3accdcd 100644 --- a/libcst/codemod/commands/rename.py +++ b/libcst/codemod/commands/rename.py @@ -153,7 +153,7 @@ class RenameCommand(VisitorBasedCodemodCommand): import_alias_name = import_alias.name import_alias_full_name = get_full_name_for_node(import_alias_name) if import_alias_full_name is None: - raise Exception("Could not parse full name for ImportAlias.name node.") + raise ValueError("Could not parse full name for ImportAlias.name node.") if self.old_name.startswith(import_alias_full_name + "."): replacement_module = self.gen_replacement_module(import_alias_full_name) @@ -286,7 +286,7 @@ class RenameCommand(VisitorBasedCodemodCommand): ) -> Union[cst.Name, cst.Attribute]: full_name_for_node = get_full_name_for_node(original_node) if full_name_for_node is None: - raise Exception("Could not parse full name for Attribute node.") + raise ValueError("Could not parse full name for Attribute node.") full_replacement_name = self.gen_replacement(full_name_for_node) # If a node has no associated QualifiedName, we are still inside an import statement. @@ -368,7 +368,7 @@ class RenameCommand(VisitorBasedCodemodCommand): ) -> Union[cst.Attribute, cst.Name]: name_or_attr_node: cst.BaseExpression = cst.parse_expression(dotted_expression) if not isinstance(name_or_attr_node, (cst.Name, cst.Attribute)): - raise Exception( + raise ValueError( "`parse_expression()` on dotted path returned non-Attribute-or-Name." ) return name_or_attr_node diff --git a/libcst/codemod/visitors/_add_imports.py b/libcst/codemod/visitors/_add_imports.py index f734af5c..eeab43ae 100644 --- a/libcst/codemod/visitors/_add_imports.py +++ b/libcst/codemod/visitors/_add_imports.py @@ -7,7 +7,7 @@ from collections import defaultdict from typing import Dict, List, Optional, Sequence, Set, Tuple, Union import libcst -from libcst import matchers as m, parse_statement +from libcst import CSTLogicError, matchers as m, parse_statement from libcst._nodes.statement import Import, ImportFrom, SimpleStatementLine from libcst.codemod._context import CodemodContext from libcst.codemod._visitor import ContextAwareTransformer @@ -107,7 +107,7 @@ class AddImportsVisitor(ContextAwareTransformer): ) -> List[ImportItem]: imports = context.scratch.get(AddImportsVisitor.CONTEXT_KEY, []) if not isinstance(imports, list): - raise Exception("Logic error!") + raise CSTLogicError("Logic error!") return imports @staticmethod @@ -136,7 +136,7 @@ class AddImportsVisitor(ContextAwareTransformer): """ if module == "__future__" and obj is None: - raise Exception("Cannot import __future__ directly!") + raise ValueError("Cannot import __future__ directly!") imports = AddImportsVisitor._get_imports_from_context(context) imports.append(ImportItem(module, obj, asname, relative)) context.scratch[AddImportsVisitor.CONTEXT_KEY] = imports @@ -157,9 +157,9 @@ class AddImportsVisitor(ContextAwareTransformer): # Verify that the imports are valid for imp in imps: if imp.module == "__future__" and imp.obj_name is None: - raise Exception("Cannot import __future__ directly!") + raise ValueError("Cannot import __future__ directly!") if imp.module == "__future__" and imp.alias is not None: - raise Exception("Cannot import __future__ objects with aliases!") + raise ValueError("Cannot import __future__ objects with aliases!") # Resolve relative imports if we have a module name imps = [imp.resolve_relative(self.context.full_package_name) for imp in imps] diff --git a/libcst/codemod/visitors/_remove_imports.py b/libcst/codemod/visitors/_remove_imports.py index 55940127..b625ee60 100644 --- a/libcst/codemod/visitors/_remove_imports.py +++ b/libcst/codemod/visitors/_remove_imports.py @@ -6,6 +6,7 @@ from typing import Any, Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union import libcst as cst +from libcst import CSTLogicError from libcst.codemod._context import CodemodContext from libcst.codemod._visitor import ContextAwareTransformer, ContextAwareVisitor from libcst.codemod.visitors._gather_unused_imports import GatherUnusedImportsVisitor @@ -45,7 +46,7 @@ class RemovedNodeVisitor(ContextAwareVisitor): self.context.full_package_name, import_node ) if module_name is None: - raise Exception("Cannot look up absolute module from relative import!") + raise ValueError("Cannot look up absolute module from relative import!") # We know any local names will refer to this as an alias if # there is one, and as the original name if there is not one @@ -72,7 +73,9 @@ class RemovedNodeVisitor(ContextAwareVisitor): # Look up the scope for this node, remove the import that caused it to exist. metadata_wrapper = self.context.wrapper if metadata_wrapper is None: - raise Exception("Cannot look up import, metadata is not computed for node!") + raise ValueError( + "Cannot look up import, metadata is not computed for node!" + ) scope_provider = metadata_wrapper.resolve(ScopeProvider) try: scope = scope_provider[node] @@ -185,7 +188,7 @@ class RemoveImportsVisitor(ContextAwareTransformer): ) -> List[Tuple[str, Optional[str], Optional[str]]]: unused_imports = context.scratch.get(RemoveImportsVisitor.CONTEXT_KEY, []) if not isinstance(unused_imports, list): - raise Exception("Logic error!") + raise CSTLogicError("Logic error!") return unused_imports @staticmethod @@ -255,7 +258,7 @@ class RemoveImportsVisitor(ContextAwareTransformer): context.full_package_name, node ) if module_name is None: - raise Exception("Cannot look up absolute module from relative import!") + raise ValueError("Cannot look up absolute module from relative import!") for import_alias in names: RemoveImportsVisitor.remove_unused_import( context, diff --git a/libcst/display/text.py b/libcst/display/text.py index 3c6dc288..0e270009 100644 --- a/libcst/display/text.py +++ b/libcst/display/text.py @@ -8,7 +8,7 @@ from __future__ import annotations import dataclasses from typing import List, Sequence -from libcst import CSTNode +from libcst import CSTLogicError, CSTNode from libcst.helpers import filter_node_fields _DEFAULT_INDENT: str = " " @@ -84,7 +84,7 @@ def _node_repr_recursive( # noqa: C901 else: child_tokens.append("[]") else: - raise Exception("Logic error!") + raise CSTLogicError("Logic error!") # Handle indentation and trailing comma. split_by_line = "".join(child_tokens).split("\n") diff --git a/libcst/helpers/_template.py b/libcst/helpers/_template.py index e3f915a5..e205e0af 100644 --- a/libcst/helpers/_template.py +++ b/libcst/helpers/_template.py @@ -45,12 +45,12 @@ def unmangled_name(var: str) -> Optional[str]: def mangle_template(template: str, template_vars: Set[str]) -> str: if TEMPLATE_PREFIX in template or TEMPLATE_SUFFIX in template: - raise Exception("Cannot parse a template containing reserved strings") + raise ValueError("Cannot parse a template containing reserved strings") for var in template_vars: original = f"{{{var}}}" if original not in template: - raise Exception( + raise ValueError( f'Template string is missing a reference to "{var}" referred to in kwargs' ) template = template.replace(original, mangled_name(var)) @@ -142,7 +142,7 @@ class TemplateTransformer(cst.CSTTransformer): name for name in template_replacements if name not in supported_vars } if unsupported_vars: - raise Exception( + raise ValueError( f'Template replacement for "{next(iter(unsupported_vars))}" is unsupported' ) @@ -350,7 +350,7 @@ class TemplateChecker(cst.CSTVisitor): def visit_Name(self, node: cst.Name) -> None: for var in self.template_vars: if node.value == mangled_name(var): - raise Exception(f'Template variable "{var}" was not replaced properly') + raise ValueError(f'Template variable "{var}" was not replaced properly') def unmangle_nodes( @@ -424,8 +424,8 @@ def parse_template_statement( if not isinstance( new_statement, (cst.SimpleStatementLine, cst.BaseCompoundStatement) ): - raise Exception( - f"Expected a statement but got a {new_statement.__class__.__name__}!" + raise TypeError( + f"Expected a statement but got a {new_statement.__class__.__qualname__}!" ) new_statement.visit(TemplateChecker({name for name in template_replacements})) return new_statement diff --git a/libcst/helpers/common.py b/libcst/helpers/common.py index 16c77669..dee73aa4 100644 --- a/libcst/helpers/common.py +++ b/libcst/helpers/common.py @@ -19,7 +19,7 @@ def ensure_type(node: object, nodetype: Type[T]) -> T: """ if not isinstance(node, nodetype): - raise Exception( - f"Expected a {nodetype.__name__} but got a {node.__class__.__name__}!" + raise ValueError( + f"Expected a {nodetype.__name__} but got a {node.__class__.__qualname__}!" ) return node diff --git a/libcst/helpers/expression.py b/libcst/helpers/expression.py index beb5f324..5ae016cf 100644 --- a/libcst/helpers/expression.py +++ b/libcst/helpers/expression.py @@ -38,5 +38,5 @@ def get_full_name_for_node_or_raise(node: Union[str, cst.CSTNode]) -> str: """ full_name = get_full_name_for_node(node) if full_name is None: - raise Exception(f"Not able to parse full name for: {node}") + raise ValueError(f"Not able to parse full name for: {node}") return full_name diff --git a/libcst/helpers/module.py b/libcst/helpers/module.py index 37e6af08..2b2973bf 100644 --- a/libcst/helpers/module.py +++ b/libcst/helpers/module.py @@ -80,7 +80,7 @@ def get_absolute_module_for_import_or_raise( ) -> str: module = get_absolute_module_for_import(current_module, import_node) if module is None: - raise Exception(f"Unable to compute absolute module for {import_node}") + raise ValueError(f"Unable to compute absolute module for {import_node}") return module @@ -121,7 +121,7 @@ def get_absolute_module_from_package_for_import_or_raise( ) -> str: module = get_absolute_module_from_package_for_import(current_package, import_node) if module is None: - raise Exception(f"Unable to compute absolute module for {import_node}") + raise ValueError(f"Unable to compute absolute module for {import_node}") return module diff --git a/libcst/matchers/_matcher_base.py b/libcst/matchers/_matcher_base.py index 2da2ff4c..1727f0df 100644 --- a/libcst/matchers/_matcher_base.py +++ b/libcst/matchers/_matcher_base.py @@ -29,7 +29,7 @@ from typing import ( import libcst import libcst.metadata as meta -from libcst import FlattenSentinel, MaybeSentinel, RemovalSentinel +from libcst import CSTLogicError, FlattenSentinel, MaybeSentinel, RemovalSentinel from libcst._metadata_dependent import LazyValue @@ -143,7 +143,7 @@ class TypeOf(Generic[_MatcherTypeT], BaseMatcherNode): for option in options: if isinstance(option, TypeOf): if option.initalized: - raise Exception( + raise ValueError( "Cannot chain an uninitalized TypeOf with an initalized one" ) actual_options.extend(option._raw_options) @@ -213,7 +213,7 @@ class OneOf(Generic[_MatcherT], BaseMatcherNode): actual_options: List[_MatcherT] = [] for option in options: if isinstance(option, AllOf): - raise Exception("Cannot use AllOf and OneOf in combination!") + raise ValueError("Cannot use AllOf and OneOf in combination!") elif isinstance(option, (OneOf, TypeOf)): actual_options.extend(option.options) else: @@ -234,7 +234,7 @@ class OneOf(Generic[_MatcherT], BaseMatcherNode): return OneOf(self, other) def __and__(self, other: _OtherNodeT) -> NoReturn: - raise Exception("Cannot use AllOf and OneOf in combination!") + raise ValueError("Cannot use AllOf and OneOf in combination!") def __invert__(self) -> "AllOf[_MatcherT]": # Invert using De Morgan's Law so we don't have to complicate types. @@ -286,9 +286,9 @@ class AllOf(Generic[_MatcherT], BaseMatcherNode): actual_options: List[_MatcherT] = [] for option in options: if isinstance(option, OneOf): - raise Exception("Cannot use AllOf and OneOf in combination!") + raise ValueError("Cannot use AllOf and OneOf in combination!") elif isinstance(option, TypeOf): - raise Exception("Cannot use AllOf and TypeOf in combination!") + raise ValueError("Cannot use AllOf and TypeOf in combination!") elif isinstance(option, AllOf): actual_options.extend(option.options) else: @@ -306,7 +306,7 @@ class AllOf(Generic[_MatcherT], BaseMatcherNode): # pyre-fixme[15]: `__or__` overrides method defined in `type` inconsistently. def __or__(self, other: _OtherNodeT) -> NoReturn: - raise Exception("Cannot use AllOf and OneOf in combination!") + raise ValueError("Cannot use AllOf and OneOf in combination!") def __and__(self, other: _OtherNodeT) -> "AllOf[Union[_MatcherT, _OtherNodeT]]": return AllOf(self, other) @@ -431,7 +431,7 @@ class _ExtractMatchingNode(Generic[_MatcherT]): # that are captured with an and, either all of them will be assigned the # same node, or none of them. It makes more sense to move the SaveMatchedNode # up to wrap the AllOf. - raise Exception( + raise ValueError( ( "Cannot use AllOf with SavedMatchedNode children! Instead, you should " + "use SaveMatchedNode(AllOf(options...))." @@ -447,10 +447,10 @@ class _ExtractMatchingNode(Generic[_MatcherT]): def __invert__(self) -> "_MatcherT": # This doesn't make sense. We don't want to capture a node only if it # doesn't match, since this will never capture anything. - raise Exception( + raise ValueError( ( "Cannot invert a SaveMatchedNode. Instead you should wrap SaveMatchedNode " - + "around your inversion itself" + "around your inversion itself" ) ) @@ -761,7 +761,9 @@ class AtLeastN(Generic[_MatcherT], _BaseWildcardNode): n: int, ) -> None: if n < 0: - raise Exception(f"{self.__class__.__name__} n attribute must be positive") + raise ValueError( + f"{self.__class__.__qualname__} n attribute must be positive" + ) self._n: int = n self._matcher: Union[_MatcherT, DoNotCareSentinel] = matcher @@ -784,13 +786,13 @@ class AtLeastN(Generic[_MatcherT], _BaseWildcardNode): # pyre-fixme[15]: `__or__` overrides method defined in `type` inconsistently. def __or__(self, other: object) -> NoReturn: - raise Exception("AtLeastN cannot be used in a OneOf matcher") + raise ValueError("AtLeastN cannot be used in a OneOf matcher") def __and__(self, other: object) -> NoReturn: - raise Exception("AtLeastN cannot be used in an AllOf matcher") + raise ValueError("AtLeastN cannot be used in an AllOf matcher") def __invert__(self) -> NoReturn: - raise Exception("Cannot invert an AtLeastN matcher!") + raise ValueError("Cannot invert an AtLeastN matcher!") def __repr__(self) -> str: if self._n == 0: @@ -863,7 +865,9 @@ class AtMostN(Generic[_MatcherT], _BaseWildcardNode): n: int, ) -> None: if n < 0: - raise Exception(f"{self.__class__.__name__} n attribute must be positive") + raise ValueError( + f"{self.__class__.__qualname__} n attribute must be positive" + ) self._n: int = n self._matcher: Union[_MatcherT, DoNotCareSentinel] = matcher @@ -887,13 +891,13 @@ class AtMostN(Generic[_MatcherT], _BaseWildcardNode): # pyre-fixme[15]: `__or__` overrides method defined in `type` inconsistently. def __or__(self, other: object) -> NoReturn: - raise Exception("AtMostN cannot be used in a OneOf matcher") + raise ValueError("AtMostN cannot be used in a OneOf matcher") def __and__(self, other: object) -> NoReturn: - raise Exception("AtMostN cannot be used in an AllOf matcher") + raise ValueError("AtMostN cannot be used in an AllOf matcher") def __invert__(self) -> NoReturn: - raise Exception("Cannot invert an AtMostN matcher!") + raise ValueError("Cannot invert an AtMostN matcher!") def __repr__(self) -> str: if self._n == 1: @@ -1158,7 +1162,7 @@ def _sequence_matches( # noqa: C901 else: # There are no other types of wildcard consumers, but we're making # pyre happy with that fact. - raise Exception(f"Logic error unrecognized wildcard {type(matcher)}!") + raise CSTLogicError(f"Logic error unrecognized wildcard {type(matcher)}!") elif isinstance(matcher, _ExtractMatchingNode): # See if the raw matcher matches. If it does, capture the sequence we matched and store it. result = _sequence_matches( @@ -1354,7 +1358,7 @@ def _metadata_matches( # noqa: C901 return None return {} if actual_value == metadata.value else None else: - raise Exception("Logic error!") + raise CSTLogicError("Logic error!") def _node_matches( # noqa: C901 @@ -1918,7 +1922,7 @@ def replace( elif isinstance(tree, meta.MetadataWrapper): return tree.module.deep_clone() else: - raise Exception("Logic error!") + raise CSTLogicError("Logic error!") if isinstance(tree, meta.MetadataWrapper) and metadata_resolver is None: # Provide a convenience for calling replace directly on a MetadataWrapper. @@ -1935,5 +1939,5 @@ def replace( new_tree = tree.visit(replacer) 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 diff --git a/libcst/metadata/base_provider.py b/libcst/metadata/base_provider.py index 811fed62..5d93fbe8 100644 --- a/libcst/metadata/base_provider.py +++ b/libcst/metadata/base_provider.py @@ -77,7 +77,7 @@ class BaseMetadataProvider(MetadataDependent, Generic[_ProvidedMetadataT]): self._computed: MutableMapping["CSTNode", MaybeLazyMetadataT] = {} if self.gen_cache and cache is None: # The metadata provider implementation is responsible to store and use cache. - raise Exception( + raise ValueError( f"Cache is required for initializing {self.__class__.__name__}." ) self.cache = cache diff --git a/libcst/metadata/full_repo_manager.py b/libcst/metadata/full_repo_manager.py index 770ba1f6..ab6430d8 100644 --- a/libcst/metadata/full_repo_manager.py +++ b/libcst/metadata/full_repo_manager.py @@ -85,7 +85,7 @@ class FullRepoManager: MetadataWrapper(module, cache=manager.get_cache_for_path("a.py")) """ if path not in self._paths: - raise Exception( + raise ValueError( "The path needs to be in paths parameter when constructing FullRepoManager for efficient batch processing." ) # Make sure that the cache is available to us. If the user called diff --git a/libcst/metadata/tests/test_type_inference_provider.py b/libcst/metadata/tests/test_type_inference_provider.py index 50ca3458..a0a70a8c 100644 --- a/libcst/metadata/tests/test_type_inference_provider.py +++ b/libcst/metadata/tests/test_type_inference_provider.py @@ -63,17 +63,11 @@ class TypeInferenceProviderTest(UnitTest): @classmethod def setUpClass(cls) -> None: os.chdir(TEST_SUITE_PATH) - try: - subprocess.run(["pyre", "-n", "start", "--no-watchman"]) - except subprocess.TimeoutExpired as exc: - raise exc + subprocess.run(["pyre", "-n", "start", "--no-watchman"]) @classmethod def tearDownClass(cls) -> None: - try: - subprocess.run(["pyre", "-n", "stop"], cwd=TEST_SUITE_PATH) - except subprocess.TimeoutExpired as exc: - raise exc + subprocess.run(["pyre", "-n", "stop"], cwd=TEST_SUITE_PATH) @data_provider( ((TEST_SUITE_PATH / "simple_class.py", TEST_SUITE_PATH / "simple_class.json"),) diff --git a/libcst/metadata/type_inference_provider.py b/libcst/metadata/type_inference_provider.py index f00c97b6..8a90c26b 100644 --- a/libcst/metadata/type_inference_provider.py +++ b/libcst/metadata/type_inference_provider.py @@ -14,6 +14,11 @@ from libcst.metadata.base_provider import BatchableMetadataProvider from libcst.metadata.position_provider import PositionProvider +class TypeInferenceError(Exception): + """An attempt to access inferred type annotation + (through Pyre Query API) failed.""" + + class Position(TypedDict): line: int column: int @@ -60,17 +65,19 @@ class TypeInferenceProvider(BatchableMetadataProvider[str]): ) -> Mapping[str, object]: params = ",".join(f"path='{root_path / path}'" for path in paths) cmd_args = ["pyre", "--noninteractive", "query", f"types({params})"] - try: - stdout, stderr, return_code = run_command(cmd_args, timeout=timeout) - except subprocess.TimeoutExpired as exc: - raise exc - if return_code != 0: - raise Exception(f"stderr:\n {stderr}\nstdout:\n {stdout}") + result = subprocess.run( + cmd_args, capture_output=True, timeout=timeout, text=True + ) + try: - resp = json.loads(stdout)["response"] + result.check_returncode() + resp = json.loads(result.stdout)["response"] except Exception as e: - raise Exception(f"{e}\n\nstderr:\n {stderr}\nstdout:\n {stdout}") + raise TypeInferenceError( + f"{e}\n\nstderr:\n {result.stderr}\nstdout:\n {result.stdout}" + ) from e + return {path: _process_pyre_data(data) for path, data in zip(paths, resp)} def __init__(self, cache: PyreData) -> None: @@ -104,13 +111,6 @@ class TypeInferenceProvider(BatchableMetadataProvider[str]): self._parse_metadata(node) -def run_command( - cmd_args: List[str], timeout: Optional[int] = None -) -> Tuple[str, str, int]: - process = subprocess.run(cmd_args, capture_output=True, timeout=timeout) - return process.stdout.decode(), process.stderr.decode(), process.returncode - - class RawPyreData(TypedDict): path: str types: Sequence[InferredType] diff --git a/libcst/tool.py b/libcst/tool.py index 2a04a172..6dc410cc 100644 --- a/libcst/tool.py +++ b/libcst/tool.py @@ -21,7 +21,7 @@ from typing import Any, Callable, Dict, List, Tuple, Type import yaml -from libcst import LIBCST_VERSION, parse_module, PartialParserConfig +from libcst import CSTLogicError, LIBCST_VERSION, parse_module, PartialParserConfig from libcst._parser.parso.utils import parse_version_string from libcst.codemod import ( CodemodCommand, @@ -191,7 +191,7 @@ def _find_and_load_config(proc_name: str) -> Dict[str, Any]: requires_config = bool(os.environ.get("LIBCST_TOOL_REQUIRE_CONFIG", "")) if requires_config and not found_config: - raise Exception( + raise FileNotFoundError( f"Did not find a {CONFIG_FILE_NAME} in current directory or any " + "parent directory! Perhaps you meant to run this command from a " + "configured subdirectory, or you need to initialize a new project " @@ -391,7 +391,7 @@ def _codemod_impl(proc_name: str, command_args: List[str]) -> int: # noqa: C901 # full-repo metadata since there is no path. if any(p == "-" for p in args.path): if len(args.path) > 1: - raise Exception("Cannot specify multiple paths when reading from stdin!") + raise ValueError("Cannot specify multiple paths when reading from stdin!") print("Codemodding from stdin", file=sys.stderr) oldcode = sys.stdin.read() @@ -478,7 +478,7 @@ class _ListSerializer(_SerializerBase): def _serialize_impl(self, key: str, value: object) -> str: if not isinstance(value, list): - raise Exception("Can only serialize lists!") + raise ValueError("Can only serialize lists!") if self.newlines: values = [f"- {v!r}" for v in value] return f"{key}:{os.linesep}{os.linesep.join(values)}" @@ -539,7 +539,7 @@ def _initialize_impl(proc_name: str, command_args: List[str]) -> int: # For safety, verify that it parses to the identical file. actual_config = yaml.safe_load(config_str) if actual_config != default_config: - raise Exception("Logic error, serialization is invalid!") + raise CSTLogicError("Logic error, serialization is invalid!") config_file = os.path.abspath(os.path.join(args.path, CONFIG_FILE_NAME)) with open(config_file, "w") as fp: From 5a6970a225d3544532507110b421f7c539d14eb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 7 Jun 2025 01:54:21 -0700 Subject: [PATCH 113/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 241 ++++++++------------------------------- native/libcst/Cargo.toml | 2 +- 2 files changed, 48 insertions(+), 195 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index d09b42a5..634b96ef 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -39,12 +39,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "bitflags" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" - [[package]] name = "bumpalo" version = "3.12.0" @@ -57,15 +51,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -101,18 +86,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.0" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d5f1946157a96594eb2d2c10eb7ad9a2b27518cb3000209dec700c35df9197d" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.4.0" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78116e32a042dd73c2901f0dc30790d20ff3447f3e3472fad359e8c3d282bcd6" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstyle", "clap_lex", @@ -120,31 +105,28 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "criterion" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", - "is-terminal", - "itertools 0.10.5", + "itertools 0.13.0", "num-traits", - "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", - "serde_derive", "serde_json", "tinytemplate", "walkdir", @@ -213,27 +195,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "glob" version = "0.3.0" @@ -258,12 +219,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - [[package]] name = "indexmap" version = "2.4.0" @@ -280,17 +235,6 @@ version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys", -] - [[package]] name = "itertools" version = "0.10.5" @@ -317,19 +261,14 @@ checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.149" @@ -359,16 +298,10 @@ name = "libcst_derive" version = "1.8.0" dependencies = [ "quote", - "syn 2.0.101", + "syn", "trybuild", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" - [[package]] name = "log" version = "0.4.17" @@ -458,9 +391,9 @@ checksum = "132dca9b868d927b35b5dd728167b2dee150eb1ad686008fc71ccb298b776fca" [[package]] name = "plotters" -version = "0.3.1" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -471,15 +404,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.2" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.1" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] @@ -545,7 +478,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.101", + "syn", ] [[package]] @@ -558,7 +491,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote", - "syn 2.0.101", + "syn", ] [[package]] @@ -620,17 +553,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] -name = "rustix" -version = "0.38.19" +name = "rustversion" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" @@ -670,7 +596,7 @@ checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] @@ -694,17 +620,6 @@ dependencies = [ "serde", ] -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.101" @@ -754,7 +669,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn", ] [[package]] @@ -818,9 +733,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-width" @@ -847,34 +762,35 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", - "lazy_static", "log", "proc-macro2", "quote", - "syn 1.0.109", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -882,28 +798,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.58" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -940,72 +859,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "winnow" version = "0.6.18" diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index e9c7411c..c09abe37 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -45,7 +45,7 @@ memchr = "2.7.4" libcst_derive = { path = "../libcst_derive", version = "1.8.0" } [dev-dependencies] -criterion = { version = "0.5.1", features = ["html_reports"] } +criterion = { version = "0.6.0", features = ["html_reports"] } difference = "2.0.0" rayon = "1.10.0" itertools = "0.13.0" From 70ccffc543fc137bea916f2659502e59883126d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 7 Jun 2025 01:54:32 -0700 Subject: [PATCH 114/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 6 +++--- native/libcst/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 634b96ef..c80f0b94 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -246,9 +246,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] @@ -282,7 +282,7 @@ dependencies = [ "annotate-snippets", "criterion", "difference", - "itertools 0.13.0", + "itertools 0.14.0", "libcst_derive", "memchr", "paste", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index c09abe37..309eeec2 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -48,7 +48,7 @@ libcst_derive = { path = "../libcst_derive", version = "1.8.0" } criterion = { version = "0.6.0", features = ["html_reports"] } difference = "2.0.0" rayon = "1.10.0" -itertools = "0.13.0" +itertools = "0.14.0" [[bench]] name = "parser_benchmark" From b818c0c98347694d3a6b69259f7de5f318340439 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Sat, 7 Jun 2025 14:06:28 +0100 Subject: [PATCH 115/151] put itertools-0.13.0 back into lockfile --- native/Cargo.lock | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/native/Cargo.lock b/native/Cargo.lock index c80f0b94..2db93c61 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -244,6 +244,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" From 9f3629e58e0bdb89efc7969aa7c9e313d6790d9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 00:33:03 -0700 Subject: [PATCH 116/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3306be71..921e871e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: if: github.event_name != 'release' run: echo CIBW_ENABLE=cpython-prerelease >> $GITHUB_ENV - name: Build wheels - uses: pypa/cibuildwheel@v3.0.0b4 + uses: pypa/cibuildwheel@v3.0.0rc2 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From 0b1a9810ae4508710c6e3872ed9f27058a99e9ca Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Tue, 10 Jun 2025 08:23:03 +0100 Subject: [PATCH 117/151] Use poe as a task runner (#1355) Make `hatch run foo` wrap the corresponding `poe` command. --- pyproject.toml | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 27dbb710..71164512 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ dependencies = [ "hypothesis>=4.36.0", "hypothesmith>=0.0.4", "maturin>=1.7.0,<1.8", + "poethepoet>=0.35.0", "prompt-toolkit>=2.0.9", "pyre-check==0.9.18; platform_system != 'Windows'", "setuptools_scm>=6.0.1", @@ -58,17 +59,27 @@ dependencies = [ "slotscheck>=0.7.1", ] -[tool.hatch.envs.default.scripts] -fixtures = ["python scripts/regenerate-fixtures.py", "git diff --exit-code"] +[tool.poe.tasks] +fixtures = ["regenerate-fixtures", "_assert_no_changes"] +regenerate-fixtures = "python scripts/regenerate-fixtures.py" +_assert_no_changes = "git diff --exit-code" + format = "ufmt format libcst scripts" -lint = [ - "flake8 libcst", - "ufmt check libcst scripts", - "python -m slotscheck libcst", - "python scripts/check_copyright.py", -] -test = ["python --version", "python -m coverage run -m libcst.tests"] -typecheck = ["pyre --version", "pyre check"] +_flake8 = "flake8 libcst" +_ufmt = "ufmt check libcst scripts" +_slotscheck = "python -m slotscheck libcst" +_check_copyright = "python scripts/check_copyright.py" +lint = ["_flake8", "_ufmt", "_slotscheck", "_check_copyright"] +test = "python -m coverage run -m libcst.tests" +typecheck = "pyre check" +docs = "sphinx-build -ab html docs/source docs/build" + +[tool.hatch.envs.default.scripts] +fixtures = "poe fixtures" +format = "poe format" +lint = "poe lint" +test = "poe test" +typecheck = "poe typecheck" [tool.hatch.envs.docs] extra-dependencies = [ @@ -79,7 +90,7 @@ extra-dependencies = [ "jinja2==3.1.6", ] [tool.hatch.envs.docs.scripts] -docs = "sphinx-build -ab html docs/source docs/build" +docs = "poe docs" [tool.slotscheck] exclude-modules = '^libcst\.(testing|tests)' From db38266f1d72f819614bb9ea641ca160ba677c88 Mon Sep 17 00:00:00 2001 From: Lysandros Nikolaou Date: Tue, 10 Jun 2025 18:21:21 +0200 Subject: [PATCH 118/151] 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 --- libcst/tool.py | 5 ++++- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libcst/tool.py b/libcst/tool.py index 6dc410cc..a2164b11 100644 --- a/libcst/tool.py +++ b/libcst/tool.py @@ -19,7 +19,10 @@ import textwrap from abc import ABC, abstractmethod from typing import Any, Callable, Dict, List, Tuple, Type -import yaml +try: + import yaml_ft as yaml # pyre-ignore +except ModuleNotFoundError: + import yaml from libcst import CSTLogicError, LIBCST_VERSION, parse_module, PartialParserConfig from libcst._parser.parso.utils import parse_version_string diff --git a/pyproject.toml b/pyproject.toml index 71164512..3c497f97 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ requires-python = ">=3.9" dependencies = [ "pyyaml>=5.2; python_version < '3.13'", - "pyyaml-ft; python_version >= '3.13'", + "pyyaml-ft>=8.0.0; python_version >= '3.13'", ] [project.urls] From ab12c4c266457196216ea3cc0d3e0509c19b01e8 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Tue, 10 Jun 2025 17:29:03 +0100 Subject: [PATCH 119/151] bump version to 1.8.1 (#1357) --- CHANGELOG.md | 14 ++++++++++++++ native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 4 ++-- native/libcst_derive/Cargo.toml | 2 +- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb50a57a..945d056d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# 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 diff --git a/native/Cargo.lock b/native/Cargo.lock index 2db93c61..9501a525 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -286,7 +286,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libcst" -version = "1.8.0" +version = "1.8.1" dependencies = [ "annotate-snippets", "criterion", @@ -304,7 +304,7 @@ dependencies = [ [[package]] name = "libcst_derive" -version = "1.8.0" +version = "1.8.1" dependencies = [ "quote", "syn", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 309eeec2..2e0b7be3 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "libcst" -version = "1.8.0" +version = "1.8.1" authors = ["LibCST Developers"] edition = "2018" rust-version = "1.70" @@ -42,7 +42,7 @@ peg = "0.8.5" annotate-snippets = "0.11.5" regex = "1.11.1" memchr = "2.7.4" -libcst_derive = { path = "../libcst_derive", version = "1.8.0" } +libcst_derive = { path = "../libcst_derive", version = "1.8.1" } [dev-dependencies] criterion = { version = "0.6.0", features = ["html_reports"] } diff --git a/native/libcst_derive/Cargo.toml b/native/libcst_derive/Cargo.toml index 78c8554f..566f74e2 100644 --- a/native/libcst_derive/Cargo.toml +++ b/native/libcst_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libcst_derive" -version = "1.8.0" +version = "1.8.1" edition = "2018" description = "Proc macro helpers for libcst." license = "MIT" From 8c35ae20efb3e1cb8fcee868bcd407beff5bd294 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Tue, 10 Jun 2025 21:58:40 +0100 Subject: [PATCH 120/151] 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 --- .github/workflows/ci.yml | 56 +- .github/workflows/pypi_upload.yml | 9 +- .pyre_configuration | 3 + CONTRIBUTING.md | 12 +- README.rst | 52 +- pyproject.toml | 60 +- uv.lock | 2773 +++++++++++++++++++++++++++++ 7 files changed, 2844 insertions(+), 121 deletions(-) create mode 100644 uv.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87a2ea11..ce969e24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,35 +25,29 @@ jobs: - "3.14" - "3.14t" steps: - - uses: actions/setup-python@v5 + - name: Install uv + uses: astral-sh/setup-uv@v5 with: - python-version: "3.10" - - name: Install hatch - run: pip install -U hatch + version: "0.7.12" + python-version: ${{ matrix.python-version }} - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@v5 - with: - cache: pip - cache-dependency-path: "pyproject.toml" - python-version: ${{ matrix.python-version }} - allow-prereleases: true - uses: dtolnay/rust-toolchain@stable - name: Build LibCST - run: hatch -vv env create + run: uv sync --locked --dev - name: Native Parser Tests - run: hatch run test + run: uv run poe test - name: Pure Parser Tests env: COVERAGE_FILE: .coverage.pure LIBCST_PARSER_TYPE: pure - run: hatch run test + run: uv run poe test - name: Coverage run: | - hatch run coverage combine .coverage.pure - hatch run coverage report + uv run coverage combine .coverage.pure + uv run coverage report # Run linters lint: @@ -63,15 +57,13 @@ jobs: with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@v5 + - name: Install uv + uses: astral-sh/setup-uv@v5 with: - cache: pip - cache-dependency-path: "pyproject.toml" + version: "0.7.12" python-version: "3.10" - - name: Install hatch - run: pip install -U hatch - - run: hatch run lint - - run: hatch run fixtures + - run: uv run poe lint + - run: uv run poe fixtures # Run pyre typechecker typecheck: @@ -81,14 +73,12 @@ jobs: with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@v5 + - name: Install uv + uses: astral-sh/setup-uv@v5 with: - cache: pip - cache-dependency-path: "pyproject.toml" + version: "0.7.12" python-version: "3.10" - - name: Install hatch - run: pip install -U hatch - - run: hatch run typecheck + - run: uv run poe typecheck # Build the docs docs: @@ -98,15 +88,13 @@ jobs: with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@v5 + - name: Install uv + uses: astral-sh/setup-uv@v5 with: - cache: pip - cache-dependency-path: "pyproject.toml" + version: "0.7.12" python-version: "3.10" - - name: Install hatch - run: pip install -U hatch - uses: ts-graphviz/setup-graphviz@v2 - - run: hatch run docs:docs + - run: uv run --group docs poe docs - name: Archive Docs uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/pypi_upload.yml b/.github/workflows/pypi_upload.yml index e8798c6e..3a16857a 100644 --- a/.github/workflows/pypi_upload.yml +++ b/.github/workflows/pypi_upload.yml @@ -33,14 +33,17 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.10" - - name: Install hatch - run: pip install -U hatch + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + version: "0.7.12" + enable-cache: false - name: Build a source tarball env: LIBCST_NO_LOCAL_SCHEME: 1 OUTDIR: ${{ steps.download.outputs.download-path }} run: >- - hatch run python -m + uv run python -m build --sdist --outdir "$OUTDIR" diff --git a/.pyre_configuration b/.pyre_configuration index ae37b031..cf108076 100644 --- a/.pyre_configuration +++ b/.pyre_configuration @@ -2,6 +2,9 @@ "exclude": [ ".*\/native\/.*" ], + "ignore_all_errors": [ + ".venv" + ], "source_directories": [ "." ], diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c69049cd..2e35431d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ We actively welcome your pull requests. ### Setup Your Environment -1. Install a [Rust toolchain](https://rustup.rs) and [hatch](https://hatch.pypa.io) +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 @@ -19,7 +19,7 @@ We actively welcome your pull requests. 4. Sync with the main libcst version package > git fetch --tags https://github.com/instagram/libcst 5. Setup the env - > hatch env create + > 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 @@ -28,13 +28,13 @@ and sphinx doc), if applicable. ### Before Submitting Your Pull Request 1. Format your code - > hatch run format + > uv run poe format 2. Run the type checker - > hatch run typecheck + > uv run poe typecheck 3. Test your changes - > hatch run test + > uv run poe test 4. Check linters - > hatch run lint + > 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 diff --git a/README.rst b/README.rst index 78d29820..0919ed8f 100644 --- a/README.rst +++ b/README.rst @@ -148,49 +148,7 @@ Further Reading Development ----------- -You'll need a recent `Rust toolchain `_ for developing. - -We recommend using `hatch ` for running tests, linters, -etc. - -Then, start by setting up and building the project: - -.. code-block:: shell - - git clone git@github.com:Instagram/LibCST.git libcst - cd libcst - hatch env create - -To run the project's test suite, you can: - -.. code-block:: shell - - hatch run test - -You can also run individual tests by using unittest and specifying a module like -this: - -.. code-block:: shell - - hatch run python -m unittest libcst.tests.test_batched_visitor - -See the `unittest documentation `_ -for more examples of how to run tests. - -We have multiple linters, including copyright checks and -`slotscheck `_ to check the correctness of class -``__slots__``. To run all of the linters: - -.. code-block:: shell - - hatch run lint - -We use `ufmt `_ to format code. To format -changes to be conformant, run the following in the root: - -.. code-block:: shell - - hatch run format +See `CONTRIBUTING.md `_ for more details. Building ~~~~~~~~ @@ -208,11 +166,11 @@ directory: cargo build -To rebuild the ``libcst.native`` module, from the repo root: +The ``libcst.native`` module should be rebuilt automatically, but to force it: .. code-block:: shell - hatch env prune && hatch env create + uv sync --reinstall-package libcst Type Checking ~~~~~~~~~~~~~ @@ -223,7 +181,7 @@ To verify types for the library, do the following in the root: .. code-block:: shell - hatch run typecheck + uv run poe typecheck Generating Documents ~~~~~~~~~~~~~~~~~~~~ @@ -232,7 +190,7 @@ To generate documents, do the following in the root: .. code-block:: shell - hatch run docs + uv run --group docs poe docs Future ====== diff --git a/pyproject.toml b/pyproject.toml index 3c497f97..b4b15320 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,19 +28,8 @@ Documentation = "https://libcst.readthedocs.io/en/latest/" Github = "https://github.com/Instagram/LibCST" Changelog = "https://github.com/Instagram/LibCST/blob/main/CHANGELOG.md" -[tool.black] -target-version = ["py39"] -extend-exclude = '^/native/' # Prepend "^/" to specify root file/folder. See https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#configuration-format - -[tool.coverage.report] -fail_under = 93 -precision = 1 -show_missing = true -skip_covered = true - -[tool.hatch.envs.default] -installer = "uv" -dependencies = [ +[dependency-groups] +dev = [ "black==25.1.0", "coverage[toml]>=4.5.4", "build>=0.10.0", @@ -58,6 +47,33 @@ dependencies = [ "setuptools-rust>=1.5.2", "slotscheck>=0.7.1", ] +docs = [ + {include-group = "dev"}, + "Sphinx>=5.1.1", + "sphinx-rtd-theme>=0.4.3", + "jupyter>=1.0.0", + "nbsphinx>=0.4.2", + "jinja2==3.1.6", +] + +[tool.black] +target-version = ["py39"] +extend-exclude = '^/native/' # Prepend "^/" to specify root file/folder. See https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#configuration-format + +[tool.coverage.report] +fail_under = 93 +precision = 1 +show_missing = true +skip_covered = true + +[tool.uv] +cache-keys = [ + { file = "pyproject.toml" }, + { git = {commit = true, tags = true}}, + { file = "**/*.rs"}, + { file = "**/Cargo.toml"}, + { file = "**/Cargo.lock"}, +] [tool.poe.tasks] fixtures = ["regenerate-fixtures", "_assert_no_changes"] @@ -74,24 +90,6 @@ test = "python -m coverage run -m libcst.tests" typecheck = "pyre check" docs = "sphinx-build -ab html docs/source docs/build" -[tool.hatch.envs.default.scripts] -fixtures = "poe fixtures" -format = "poe format" -lint = "poe lint" -test = "poe test" -typecheck = "poe typecheck" - -[tool.hatch.envs.docs] -extra-dependencies = [ - "Sphinx>=5.1.1", - "sphinx-rtd-theme>=0.4.3", - "jupyter>=1.0.0", - "nbsphinx>=0.4.2", - "jinja2==3.1.6", -] -[tool.hatch.envs.docs.scripts] -docs = "poe docs" - [tool.slotscheck] exclude-modules = '^libcst\.(testing|tests)' diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..ad68fefe --- /dev/null +++ b/uv.lock @@ -0,0 +1,2773 @@ +version = 1 +revision = 2 +requires-python = ">=3.9" +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version >= '3.11' and python_full_version < '3.13'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] + +[[package]] +name = "alabaster" +version = "0.7.16" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/3e/13dd8e5ed9094e734ac430b5d0eb4f2bb001708a8b7856cbf8e084e001ba/alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65", size = 23776, upload-time = "2024-01-10T00:56:10.189Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/34/d4e1c02d3bee589efb5dfa17f88ea08bdb3e3eac12bc475462aec52ed223/alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92", size = 13511, upload-time = "2024-01-10T00:56:08.388Z" }, +] + +[[package]] +name = "alabaster" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version >= '3.11' and python_full_version < '3.13'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, +] + +[[package]] +name = "anyio" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, +] + +[[package]] +name = "appnope" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, +] + +[[package]] +name = "argon2-cffi" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argon2-cffi-bindings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, +] + +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/e9/184b8ccce6683b0aa2fbb7ba5683ea4b9c5763f1356347f1312c32e3c66e/argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3", size = 1779911, upload-time = "2021-12-01T08:52:55.68Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/13/838ce2620025e9666aa8f686431f67a29052241692a3dd1ae9d3692a89d3/argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367", size = 29658, upload-time = "2021-12-01T09:09:17.016Z" }, + { url = "https://files.pythonhosted.org/packages/b3/02/f7f7bb6b6af6031edb11037639c697b912e1dea2db94d436e681aea2f495/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d", size = 80583, upload-time = "2021-12-01T09:09:19.546Z" }, + { url = "https://files.pythonhosted.org/packages/ec/f7/378254e6dd7ae6f31fe40c8649eea7d4832a42243acaf0f1fff9083b2bed/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae", size = 86168, upload-time = "2021-12-01T09:09:21.445Z" }, + { url = "https://files.pythonhosted.org/packages/74/f6/4a34a37a98311ed73bb80efe422fed95f2ac25a4cacc5ae1d7ae6a144505/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c", size = 82709, upload-time = "2021-12-01T09:09:18.182Z" }, + { url = "https://files.pythonhosted.org/packages/74/2b/73d767bfdaab25484f7e7901379d5f8793cccbb86c6e0cbc4c1b96f63896/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86", size = 83613, upload-time = "2021-12-01T09:09:22.741Z" }, + { url = "https://files.pythonhosted.org/packages/4f/fd/37f86deef67ff57c76f137a67181949c2d408077e2e3dd70c6c42912c9bf/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f", size = 84583, upload-time = "2021-12-01T09:09:24.177Z" }, + { url = "https://files.pythonhosted.org/packages/6f/52/5a60085a3dae8fded8327a4f564223029f5f54b0cb0455a31131b5363a01/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e", size = 88475, upload-time = "2021-12-01T09:09:26.673Z" }, + { url = "https://files.pythonhosted.org/packages/8b/95/143cd64feb24a15fa4b189a3e1e7efbaeeb00f39a51e99b26fc62fbacabd/argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082", size = 27698, upload-time = "2021-12-01T09:09:27.87Z" }, + { url = "https://files.pythonhosted.org/packages/37/2c/e34e47c7dee97ba6f01a6203e0383e15b60fb85d78ac9a15cd066f6fe28b/argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f", size = 30817, upload-time = "2021-12-01T09:09:30.267Z" }, + { url = "https://files.pythonhosted.org/packages/5a/e4/bf8034d25edaa495da3c8a3405627d2e35758e44ff6eaa7948092646fdcc/argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93", size = 53104, upload-time = "2021-12-01T09:09:31.335Z" }, +] + +[[package]] +name = "arrow" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "types-python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/00/0f6e8fcdb23ea632c866620cc872729ff43ed91d284c866b515c6342b173/arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85", size = 131960, upload-time = "2023-09-30T22:11:18.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80", size = 66419, upload-time = "2023-09-30T22:11:16.072Z" }, +] + +[[package]] +name = "asttokens" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978, upload-time = "2024-11-30T04:30:14.439Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918, upload-time = "2024-11-30T04:30:10.946Z" }, +] + +[[package]] +name = "async-lru" +version = "2.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/4d/71ec4d3939dc755264f680f6c2b4906423a304c3d18e96853f0a595dfe97/async_lru-2.0.5.tar.gz", hash = "sha256:481d52ccdd27275f42c43a928b4a50c3bfb2d67af4e78b170e3e0bb39c66e5bb", size = 10380, upload-time = "2025-03-16T17:25:36.919Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/49/d10027df9fce941cb8184e78a02857af36360d33e1721df81c5ed2179a1a/async_lru-2.0.5-py3-none-any.whl", hash = "sha256:ab95404d8d2605310d345932697371a5f40def0487c03d6d0ad9138de52c9943", size = 6069, upload-time = "2025-03-16T17:25:35.422Z" }, +] + +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032, upload-time = "2025-03-13T11:10:22.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, +] + +[[package]] +name = "babel" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067, upload-time = "2025-04-15T17:05:13.836Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285, upload-time = "2025-04-15T17:05:12.221Z" }, +] + +[[package]] +name = "black" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/3b/4ba3f93ac8d90410423fdd31d7541ada9bcee1df32fb90d26de41ed40e1d/black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", size = 1629419, upload-time = "2025-01-29T05:37:06.642Z" }, + { url = "https://files.pythonhosted.org/packages/b4/02/0bde0485146a8a5e694daed47561785e8b77a0466ccc1f3e485d5ef2925e/black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", size = 1461080, upload-time = "2025-01-29T05:37:09.321Z" }, + { url = "https://files.pythonhosted.org/packages/52/0e/abdf75183c830eaca7589144ff96d49bce73d7ec6ad12ef62185cc0f79a2/black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", size = 1766886, upload-time = "2025-01-29T04:18:24.432Z" }, + { url = "https://files.pythonhosted.org/packages/dc/a6/97d8bb65b1d8a41f8a6736222ba0a334db7b7b77b8023ab4568288f23973/black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", size = 1419404, upload-time = "2025-01-29T04:19:04.296Z" }, + { url = "https://files.pythonhosted.org/packages/7e/4f/87f596aca05c3ce5b94b8663dbfe242a12843caaa82dd3f85f1ffdc3f177/black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", size = 1614372, upload-time = "2025-01-29T05:37:11.71Z" }, + { url = "https://files.pythonhosted.org/packages/e7/d0/2c34c36190b741c59c901e56ab7f6e54dad8df05a6272a9747ecef7c6036/black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", size = 1442865, upload-time = "2025-01-29T05:37:14.309Z" }, + { url = "https://files.pythonhosted.org/packages/21/d4/7518c72262468430ead45cf22bd86c883a6448b9eb43672765d69a8f1248/black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", size = 1749699, upload-time = "2025-01-29T04:18:17.688Z" }, + { url = "https://files.pythonhosted.org/packages/58/db/4f5beb989b547f79096e035c4981ceb36ac2b552d0ac5f2620e941501c99/black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", size = 1428028, upload-time = "2025-01-29T04:18:51.711Z" }, + { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988, upload-time = "2025-01-29T05:37:16.707Z" }, + { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985, upload-time = "2025-01-29T05:37:18.273Z" }, + { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816, upload-time = "2025-01-29T04:18:33.823Z" }, + { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860, upload-time = "2025-01-29T04:19:12.944Z" }, + { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" }, + { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" }, + { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b6/ae7507470a4830dbbfe875c701e84a4a5fb9183d1497834871a715716a92/black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0", size = 1628593, upload-time = "2025-01-29T05:37:23.672Z" }, + { url = "https://files.pythonhosted.org/packages/24/c1/ae36fa59a59f9363017ed397750a0cd79a470490860bc7713967d89cdd31/black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f", size = 1460000, upload-time = "2025-01-29T05:37:25.829Z" }, + { url = "https://files.pythonhosted.org/packages/ac/b6/98f832e7a6c49aa3a464760c67c7856363aa644f2f3c74cf7d624168607e/black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e", size = 1765963, upload-time = "2025-01-29T04:18:38.116Z" }, + { url = "https://files.pythonhosted.org/packages/ce/e9/2cb0a017eb7024f70e0d2e9bdb8c5a5b078c5740c7f8816065d06f04c557/black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355", size = 1419419, upload-time = "2025-01-29T04:18:30.191Z" }, + { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, +] + +[[package]] +name = "bleach" +version = "6.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/9a/0e33f5054c54d349ea62c277191c020c2d6ef1d65ab2cb1993f91ec846d1/bleach-6.2.0.tar.gz", hash = "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f", size = 203083, upload-time = "2024-10-29T18:30:40.477Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/55/96142937f66150805c25c4d0f31ee4132fd33497753400734f9dfdcbdc66/bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e", size = 163406, upload-time = "2024-10-29T18:30:38.186Z" }, +] + +[package.optional-dependencies] +css = [ + { name = "tinycss2" }, +] + +[[package]] +name = "build" +version = "1.2.2.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "os_name == 'nt'" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10.2'" }, + { name = "packaging" }, + { name = "pyproject-hooks" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/46/aeab111f8e06793e4f0e421fcad593d547fb8313b50990f31681ee2fb1ad/build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7", size = 46701, upload-time = "2024-10-06T17:22:25.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/c2/80633736cd183ee4a62107413def345f7e6e3c01563dbca1417363cf957e/build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5", size = 22950, upload-time = "2024-10-06T17:22:23.299Z" }, +] + +[[package]] +name = "certifi" +version = "2025.4.26" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, +] + +[[package]] +name = "cffi" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191, upload-time = "2024-09-04T20:43:30.027Z" }, + { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592, upload-time = "2024-09-04T20:43:32.108Z" }, + { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024, upload-time = "2024-09-04T20:43:34.186Z" }, + { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188, upload-time = "2024-09-04T20:43:36.286Z" }, + { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571, upload-time = "2024-09-04T20:43:38.586Z" }, + { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687, upload-time = "2024-09-04T20:43:40.084Z" }, + { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211, upload-time = "2024-09-04T20:43:41.526Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325, upload-time = "2024-09-04T20:43:43.117Z" }, + { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784, upload-time = "2024-09-04T20:43:45.256Z" }, + { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564, upload-time = "2024-09-04T20:43:46.779Z" }, + { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804, upload-time = "2024-09-04T20:43:48.186Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299, upload-time = "2024-09-04T20:43:49.812Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264, upload-time = "2024-09-04T20:43:51.124Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651, upload-time = "2024-09-04T20:43:52.872Z" }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259, upload-time = "2024-09-04T20:43:56.123Z" }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200, upload-time = "2024-09-04T20:43:57.891Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235, upload-time = "2024-09-04T20:44:00.18Z" }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721, upload-time = "2024-09-04T20:44:01.585Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242, upload-time = "2024-09-04T20:44:03.467Z" }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999, upload-time = "2024-09-04T20:44:05.023Z" }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242, upload-time = "2024-09-04T20:44:06.444Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604, upload-time = "2024-09-04T20:44:08.206Z" }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727, upload-time = "2024-09-04T20:44:09.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400, upload-time = "2024-09-04T20:44:10.873Z" }, + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178, upload-time = "2024-09-04T20:44:12.232Z" }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840, upload-time = "2024-09-04T20:44:13.739Z" }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803, upload-time = "2024-09-04T20:44:15.231Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850, upload-time = "2024-09-04T20:44:17.188Z" }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729, upload-time = "2024-09-04T20:44:18.688Z" }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256, upload-time = "2024-09-04T20:44:20.248Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424, upload-time = "2024-09-04T20:44:21.673Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568, upload-time = "2024-09-04T20:44:23.245Z" }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736, upload-time = "2024-09-04T20:44:24.757Z" }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448, upload-time = "2024-09-04T20:44:26.208Z" }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976, upload-time = "2024-09-04T20:44:27.578Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload-time = "2024-09-04T20:44:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload-time = "2024-09-04T20:44:30.289Z" }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ea/8bb50596b8ffbc49ddd7a1ad305035daa770202a6b782fc164647c2673ad/cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", size = 182220, upload-time = "2024-09-04T20:45:01.577Z" }, + { url = "https://files.pythonhosted.org/packages/ae/11/e77c8cd24f58285a82c23af484cf5b124a376b32644e445960d1a4654c3a/cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", size = 178605, upload-time = "2024-09-04T20:45:03.837Z" }, + { url = "https://files.pythonhosted.org/packages/ed/65/25a8dc32c53bf5b7b6c2686b42ae2ad58743f7ff644844af7cdb29b49361/cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", size = 424910, upload-time = "2024-09-04T20:45:05.315Z" }, + { url = "https://files.pythonhosted.org/packages/42/7a/9d086fab7c66bd7c4d0f27c57a1b6b068ced810afc498cc8c49e0088661c/cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", size = 447200, upload-time = "2024-09-04T20:45:06.903Z" }, + { url = "https://files.pythonhosted.org/packages/da/63/1785ced118ce92a993b0ec9e0d0ac8dc3e5dbfbcaa81135be56c69cabbb6/cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", size = 454565, upload-time = "2024-09-04T20:45:08.975Z" }, + { url = "https://files.pythonhosted.org/packages/74/06/90b8a44abf3556599cdec107f7290277ae8901a58f75e6fe8f970cd72418/cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", size = 435635, upload-time = "2024-09-04T20:45:10.64Z" }, + { url = "https://files.pythonhosted.org/packages/bd/62/a1f468e5708a70b1d86ead5bab5520861d9c7eacce4a885ded9faa7729c3/cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", size = 445218, upload-time = "2024-09-04T20:45:12.366Z" }, + { url = "https://files.pythonhosted.org/packages/5b/95/b34462f3ccb09c2594aa782d90a90b045de4ff1f70148ee79c69d37a0a5a/cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", size = 460486, upload-time = "2024-09-04T20:45:13.935Z" }, + { url = "https://files.pythonhosted.org/packages/fc/fc/a1e4bebd8d680febd29cf6c8a40067182b64f00c7d105f8f26b5bc54317b/cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", size = 437911, upload-time = "2024-09-04T20:45:15.696Z" }, + { url = "https://files.pythonhosted.org/packages/e6/c3/21cab7a6154b6a5ea330ae80de386e7665254835b9e98ecc1340b3a7de9a/cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", size = 460632, upload-time = "2024-09-04T20:45:17.284Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b5/fd9f8b5a84010ca169ee49f4e4ad6f8c05f4e3545b72ee041dbbcb159882/cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", size = 171820, upload-time = "2024-09-04T20:45:18.762Z" }, + { url = "https://files.pythonhosted.org/packages/8c/52/b08750ce0bce45c143e1b5d7357ee8c55341b52bdef4b0f081af1eb248c2/cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", size = 181290, upload-time = "2024-09-04T20:45:20.226Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, + { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, + { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, + { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, + { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, + { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, + { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, + { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, + { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, + { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, + { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, + { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, + { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, + { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, + { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/28/f8/dfb01ff6cc9af38552c69c9027501ff5a5117c4cc18dcd27cb5259fa1888/charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4", size = 201671, upload-time = "2025-05-02T08:34:12.696Z" }, + { url = "https://files.pythonhosted.org/packages/32/fb/74e26ee556a9dbfe3bd264289b67be1e6d616329403036f6507bb9f3f29c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7", size = 144744, upload-time = "2025-05-02T08:34:14.665Z" }, + { url = "https://files.pythonhosted.org/packages/ad/06/8499ee5aa7addc6f6d72e068691826ff093329fe59891e83b092ae4c851c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836", size = 154993, upload-time = "2025-05-02T08:34:17.134Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a2/5e4c187680728219254ef107a6949c60ee0e9a916a5dadb148c7ae82459c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597", size = 147382, upload-time = "2025-05-02T08:34:19.081Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/56aca740dda674f0cc1ba1418c4d84534be51f639b5f98f538b332dc9a95/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7", size = 149536, upload-time = "2025-05-02T08:34:21.073Z" }, + { url = "https://files.pythonhosted.org/packages/53/13/db2e7779f892386b589173dd689c1b1e304621c5792046edd8a978cbf9e0/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f", size = 151349, upload-time = "2025-05-02T08:34:23.193Z" }, + { url = "https://files.pythonhosted.org/packages/69/35/e52ab9a276186f729bce7a0638585d2982f50402046e4b0faa5d2c3ef2da/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba", size = 146365, upload-time = "2025-05-02T08:34:25.187Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d8/af7333f732fc2e7635867d56cb7c349c28c7094910c72267586947561b4b/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12", size = 154499, upload-time = "2025-05-02T08:34:27.359Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3d/a5b2e48acef264d71e036ff30bcc49e51bde80219bb628ba3e00cf59baac/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518", size = 157735, upload-time = "2025-05-02T08:34:29.798Z" }, + { url = "https://files.pythonhosted.org/packages/85/d8/23e2c112532a29f3eef374375a8684a4f3b8e784f62b01da931186f43494/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5", size = 154786, upload-time = "2025-05-02T08:34:31.858Z" }, + { url = "https://files.pythonhosted.org/packages/c7/57/93e0169f08ecc20fe82d12254a200dfaceddc1c12a4077bf454ecc597e33/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3", size = 150203, upload-time = "2025-05-02T08:34:33.88Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9d/9bf2b005138e7e060d7ebdec7503d0ef3240141587651f4b445bdf7286c2/charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471", size = 98436, upload-time = "2025-05-02T08:34:35.907Z" }, + { url = "https://files.pythonhosted.org/packages/6d/24/5849d46cf4311bbf21b424c443b09b459f5b436b1558c04e45dbb7cc478b/charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e", size = 105772, upload-time = "2025-05-02T08:34:37.935Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version >= '3.11' and python_full_version < '3.13'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "comm" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/a8/fb783cb0abe2b5fded9f55e5703015cdf1c9c85b3669087c538dd15a6a86/comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e", size = 6210, upload-time = "2024-03-12T16:53:41.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/75/49e5bfe642f71f272236b5b2d2691cf915a7283cc0ceda56357b61daa538/comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3", size = 7180, upload-time = "2024-03-12T16:53:39.226Z" }, +] + +[[package]] +name = "coverage" +version = "7.8.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/07/998afa4a0ecdf9b1981ae05415dad2d4e7716e1b1f00abbd91691ac09ac9/coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27", size = 812759, upload-time = "2025-05-23T11:39:57.856Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/6b/7dd06399a5c0b81007e3a6af0395cd60e6a30f959f8d407d3ee04642e896/coverage-7.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd8ec21e1443fd7a447881332f7ce9d35b8fbd2849e761bb290b584535636b0a", size = 211573, upload-time = "2025-05-23T11:37:47.207Z" }, + { url = "https://files.pythonhosted.org/packages/f0/df/2b24090820a0bac1412955fb1a4dade6bc3b8dcef7b899c277ffaf16916d/coverage-7.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26c2396674816deaeae7ded0e2b42c26537280f8fe313335858ffff35019be", size = 212006, upload-time = "2025-05-23T11:37:50.289Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c4/e4e3b998e116625562a872a342419652fa6ca73f464d9faf9f52f1aff427/coverage-7.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1aec326ed237e5880bfe69ad41616d333712c7937bcefc1343145e972938f9b3", size = 241128, upload-time = "2025-05-23T11:37:52.229Z" }, + { url = "https://files.pythonhosted.org/packages/b1/67/b28904afea3e87a895da850ba587439a61699bf4b73d04d0dfd99bbd33b4/coverage-7.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e818796f71702d7a13e50c70de2a1924f729228580bcba1607cccf32eea46e6", size = 239026, upload-time = "2025-05-23T11:37:53.846Z" }, + { url = "https://files.pythonhosted.org/packages/8c/0f/47bf7c5630d81bc2cd52b9e13043685dbb7c79372a7f5857279cc442b37c/coverage-7.8.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:546e537d9e24efc765c9c891328f30f826e3e4808e31f5d0f87c4ba12bbd1622", size = 240172, upload-time = "2025-05-23T11:37:55.711Z" }, + { url = "https://files.pythonhosted.org/packages/ba/38/af3eb9d36d85abc881f5aaecf8209383dbe0fa4cac2d804c55d05c51cb04/coverage-7.8.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab9b09a2349f58e73f8ebc06fac546dd623e23b063e5398343c5270072e3201c", size = 240086, upload-time = "2025-05-23T11:37:57.724Z" }, + { url = "https://files.pythonhosted.org/packages/9e/64/c40c27c2573adeba0fe16faf39a8aa57368a1f2148865d6bb24c67eadb41/coverage-7.8.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd51355ab8a372d89fb0e6a31719e825cf8df8b6724bee942fb5b92c3f016ba3", size = 238792, upload-time = "2025-05-23T11:37:59.737Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ab/b7c85146f15457671c1412afca7c25a5696d7625e7158002aa017e2d7e3c/coverage-7.8.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0774df1e093acb6c9e4d58bce7f86656aeed6c132a16e2337692c12786b32404", size = 239096, upload-time = "2025-05-23T11:38:01.693Z" }, + { url = "https://files.pythonhosted.org/packages/d3/50/9446dad1310905fb1dc284d60d4320a5b25d4e3e33f9ea08b8d36e244e23/coverage-7.8.2-cp310-cp310-win32.whl", hash = "sha256:00f2e2f2e37f47e5f54423aeefd6c32a7dbcedc033fcd3928a4f4948e8b96af7", size = 214144, upload-time = "2025-05-23T11:38:03.68Z" }, + { url = "https://files.pythonhosted.org/packages/23/ed/792e66ad7b8b0df757db8d47af0c23659cdb5a65ef7ace8b111cacdbee89/coverage-7.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:145b07bea229821d51811bf15eeab346c236d523838eda395ea969d120d13347", size = 215043, upload-time = "2025-05-23T11:38:05.217Z" }, + { url = "https://files.pythonhosted.org/packages/6a/4d/1ff618ee9f134d0de5cc1661582c21a65e06823f41caf801aadf18811a8e/coverage-7.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b99058eef42e6a8dcd135afb068b3d53aff3921ce699e127602efff9956457a9", size = 211692, upload-time = "2025-05-23T11:38:08.485Z" }, + { url = "https://files.pythonhosted.org/packages/96/fa/c3c1b476de96f2bc7a8ca01a9f1fcb51c01c6b60a9d2c3e66194b2bdb4af/coverage-7.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5feb7f2c3e6ea94d3b877def0270dff0947b8d8c04cfa34a17be0a4dc1836879", size = 212115, upload-time = "2025-05-23T11:38:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/f7/c2/5414c5a1b286c0f3881ae5adb49be1854ac5b7e99011501f81c8c1453065/coverage-7.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:670a13249b957bb9050fab12d86acef7bf8f6a879b9d1a883799276e0d4c674a", size = 244740, upload-time = "2025-05-23T11:38:11.947Z" }, + { url = "https://files.pythonhosted.org/packages/cd/46/1ae01912dfb06a642ef3dd9cf38ed4996fda8fe884dab8952da616f81a2b/coverage-7.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bdc8bf760459a4a4187b452213e04d039990211f98644c7292adf1e471162b5", size = 242429, upload-time = "2025-05-23T11:38:13.955Z" }, + { url = "https://files.pythonhosted.org/packages/06/58/38c676aec594bfe2a87c7683942e5a30224791d8df99bcc8439fde140377/coverage-7.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07a989c867986c2a75f158f03fdb413128aad29aca9d4dbce5fc755672d96f11", size = 244218, upload-time = "2025-05-23T11:38:15.631Z" }, + { url = "https://files.pythonhosted.org/packages/80/0c/95b1023e881ce45006d9abc250f76c6cdab7134a1c182d9713878dfefcb2/coverage-7.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2db10dedeb619a771ef0e2949ccba7b75e33905de959c2643a4607bef2f3fb3a", size = 243865, upload-time = "2025-05-23T11:38:17.622Z" }, + { url = "https://files.pythonhosted.org/packages/57/37/0ae95989285a39e0839c959fe854a3ae46c06610439350d1ab860bf020ac/coverage-7.8.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e6ea7dba4e92926b7b5f0990634b78ea02f208d04af520c73a7c876d5a8d36cb", size = 242038, upload-time = "2025-05-23T11:38:19.966Z" }, + { url = "https://files.pythonhosted.org/packages/4d/82/40e55f7c0eb5e97cc62cbd9d0746fd24e8caf57be5a408b87529416e0c70/coverage-7.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ef2f22795a7aca99fc3c84393a55a53dd18ab8c93fb431004e4d8f0774150f54", size = 242567, upload-time = "2025-05-23T11:38:21.912Z" }, + { url = "https://files.pythonhosted.org/packages/f9/35/66a51adc273433a253989f0d9cc7aa6bcdb4855382cf0858200afe578861/coverage-7.8.2-cp311-cp311-win32.whl", hash = "sha256:641988828bc18a6368fe72355df5f1703e44411adbe49bba5644b941ce6f2e3a", size = 214194, upload-time = "2025-05-23T11:38:23.571Z" }, + { url = "https://files.pythonhosted.org/packages/f6/8f/a543121f9f5f150eae092b08428cb4e6b6d2d134152c3357b77659d2a605/coverage-7.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8ab4a51cb39dc1933ba627e0875046d150e88478dbe22ce145a68393e9652975", size = 215109, upload-time = "2025-05-23T11:38:25.137Z" }, + { url = "https://files.pythonhosted.org/packages/77/65/6cc84b68d4f35186463cd7ab1da1169e9abb59870c0f6a57ea6aba95f861/coverage-7.8.2-cp311-cp311-win_arm64.whl", hash = "sha256:8966a821e2083c74d88cca5b7dcccc0a3a888a596a04c0b9668a891de3a0cc53", size = 213521, upload-time = "2025-05-23T11:38:27.123Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2a/1da1ada2e3044fcd4a3254fb3576e160b8fe5b36d705c8a31f793423f763/coverage-7.8.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2f6fe3654468d061942591aef56686131335b7a8325684eda85dacdf311356c", size = 211876, upload-time = "2025-05-23T11:38:29.01Z" }, + { url = "https://files.pythonhosted.org/packages/70/e9/3d715ffd5b6b17a8be80cd14a8917a002530a99943cc1939ad5bb2aa74b9/coverage-7.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76090fab50610798cc05241bf83b603477c40ee87acd358b66196ab0ca44ffa1", size = 212130, upload-time = "2025-05-23T11:38:30.675Z" }, + { url = "https://files.pythonhosted.org/packages/a0/02/fdce62bb3c21649abfd91fbdcf041fb99be0d728ff00f3f9d54d97ed683e/coverage-7.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd0a0a5054be160777a7920b731a0570284db5142abaaf81bcbb282b8d99279", size = 246176, upload-time = "2025-05-23T11:38:32.395Z" }, + { url = "https://files.pythonhosted.org/packages/a7/52/decbbed61e03b6ffe85cd0fea360a5e04a5a98a7423f292aae62423b8557/coverage-7.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da23ce9a3d356d0affe9c7036030b5c8f14556bd970c9b224f9c8205505e3b99", size = 243068, upload-time = "2025-05-23T11:38:33.989Z" }, + { url = "https://files.pythonhosted.org/packages/38/6c/d0e9c0cce18faef79a52778219a3c6ee8e336437da8eddd4ab3dbd8fadff/coverage-7.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9392773cffeb8d7e042a7b15b82a414011e9d2b5fdbbd3f7e6a6b17d5e21b20", size = 245328, upload-time = "2025-05-23T11:38:35.568Z" }, + { url = "https://files.pythonhosted.org/packages/f0/70/f703b553a2f6b6c70568c7e398ed0789d47f953d67fbba36a327714a7bca/coverage-7.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:876cbfd0b09ce09d81585d266c07a32657beb3eaec896f39484b631555be0fe2", size = 245099, upload-time = "2025-05-23T11:38:37.627Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fb/4cbb370dedae78460c3aacbdad9d249e853f3bc4ce5ff0e02b1983d03044/coverage-7.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3da9b771c98977a13fbc3830f6caa85cae6c9c83911d24cb2d218e9394259c57", size = 243314, upload-time = "2025-05-23T11:38:39.238Z" }, + { url = "https://files.pythonhosted.org/packages/39/9f/1afbb2cb9c8699b8bc38afdce00a3b4644904e6a38c7bf9005386c9305ec/coverage-7.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a990f6510b3292686713bfef26d0049cd63b9c7bb17e0864f133cbfd2e6167f", size = 244489, upload-time = "2025-05-23T11:38:40.845Z" }, + { url = "https://files.pythonhosted.org/packages/79/fa/f3e7ec7d220bff14aba7a4786ae47043770cbdceeea1803083059c878837/coverage-7.8.2-cp312-cp312-win32.whl", hash = "sha256:bf8111cddd0f2b54d34e96613e7fbdd59a673f0cf5574b61134ae75b6f5a33b8", size = 214366, upload-time = "2025-05-23T11:38:43.551Z" }, + { url = "https://files.pythonhosted.org/packages/54/aa/9cbeade19b7e8e853e7ffc261df885d66bf3a782c71cba06c17df271f9e6/coverage-7.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:86a323a275e9e44cdf228af9b71c5030861d4d2610886ab920d9945672a81223", size = 215165, upload-time = "2025-05-23T11:38:45.148Z" }, + { url = "https://files.pythonhosted.org/packages/c4/73/e2528bf1237d2448f882bbebaec5c3500ef07301816c5c63464b9da4d88a/coverage-7.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:820157de3a589e992689ffcda8639fbabb313b323d26388d02e154164c57b07f", size = 213548, upload-time = "2025-05-23T11:38:46.74Z" }, + { url = "https://files.pythonhosted.org/packages/1a/93/eb6400a745ad3b265bac36e8077fdffcf0268bdbbb6c02b7220b624c9b31/coverage-7.8.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ea561010914ec1c26ab4188aef8b1567272ef6de096312716f90e5baa79ef8ca", size = 211898, upload-time = "2025-05-23T11:38:49.066Z" }, + { url = "https://files.pythonhosted.org/packages/1b/7c/bdbf113f92683024406a1cd226a199e4200a2001fc85d6a6e7e299e60253/coverage-7.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cb86337a4fcdd0e598ff2caeb513ac604d2f3da6d53df2c8e368e07ee38e277d", size = 212171, upload-time = "2025-05-23T11:38:51.207Z" }, + { url = "https://files.pythonhosted.org/packages/91/22/594513f9541a6b88eb0dba4d5da7d71596dadef6b17a12dc2c0e859818a9/coverage-7.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a4636ddb666971345541b59899e969f3b301143dd86b0ddbb570bd591f1e85", size = 245564, upload-time = "2025-05-23T11:38:52.857Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f4/2860fd6abeebd9f2efcfe0fd376226938f22afc80c1943f363cd3c28421f/coverage-7.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5040536cf9b13fb033f76bcb5e1e5cb3b57c4807fef37db9e0ed129c6a094257", size = 242719, upload-time = "2025-05-23T11:38:54.529Z" }, + { url = "https://files.pythonhosted.org/packages/89/60/f5f50f61b6332451520e6cdc2401700c48310c64bc2dd34027a47d6ab4ca/coverage-7.8.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc67994df9bcd7e0150a47ef41278b9e0a0ea187caba72414b71dc590b99a108", size = 244634, upload-time = "2025-05-23T11:38:57.326Z" }, + { url = "https://files.pythonhosted.org/packages/3b/70/7f4e919039ab7d944276c446b603eea84da29ebcf20984fb1fdf6e602028/coverage-7.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e6c86888fd076d9e0fe848af0a2142bf606044dc5ceee0aa9eddb56e26895a0", size = 244824, upload-time = "2025-05-23T11:38:59.421Z" }, + { url = "https://files.pythonhosted.org/packages/26/45/36297a4c0cea4de2b2c442fe32f60c3991056c59cdc3cdd5346fbb995c97/coverage-7.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:684ca9f58119b8e26bef860db33524ae0365601492e86ba0b71d513f525e7050", size = 242872, upload-time = "2025-05-23T11:39:01.049Z" }, + { url = "https://files.pythonhosted.org/packages/a4/71/e041f1b9420f7b786b1367fa2a375703889ef376e0d48de9f5723fb35f11/coverage-7.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8165584ddedb49204c4e18da083913bdf6a982bfb558632a79bdaadcdafd0d48", size = 244179, upload-time = "2025-05-23T11:39:02.709Z" }, + { url = "https://files.pythonhosted.org/packages/bd/db/3c2bf49bdc9de76acf2491fc03130c4ffc51469ce2f6889d2640eb563d77/coverage-7.8.2-cp313-cp313-win32.whl", hash = "sha256:34759ee2c65362163699cc917bdb2a54114dd06d19bab860725f94ef45a3d9b7", size = 214393, upload-time = "2025-05-23T11:39:05.457Z" }, + { url = "https://files.pythonhosted.org/packages/c6/dc/947e75d47ebbb4b02d8babb1fad4ad381410d5bc9da7cfca80b7565ef401/coverage-7.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:2f9bc608fbafaee40eb60a9a53dbfb90f53cc66d3d32c2849dc27cf5638a21e3", size = 215194, upload-time = "2025-05-23T11:39:07.171Z" }, + { url = "https://files.pythonhosted.org/packages/90/31/a980f7df8a37eaf0dc60f932507fda9656b3a03f0abf188474a0ea188d6d/coverage-7.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:9fe449ee461a3b0c7105690419d0b0aba1232f4ff6d120a9e241e58a556733f7", size = 213580, upload-time = "2025-05-23T11:39:08.862Z" }, + { url = "https://files.pythonhosted.org/packages/8a/6a/25a37dd90f6c95f59355629417ebcb74e1c34e38bb1eddf6ca9b38b0fc53/coverage-7.8.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8369a7c8ef66bded2b6484053749ff220dbf83cba84f3398c84c51a6f748a008", size = 212734, upload-time = "2025-05-23T11:39:11.109Z" }, + { url = "https://files.pythonhosted.org/packages/36/8b/3a728b3118988725f40950931abb09cd7f43b3c740f4640a59f1db60e372/coverage-7.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:159b81df53a5fcbc7d45dae3adad554fdbde9829a994e15227b3f9d816d00b36", size = 212959, upload-time = "2025-05-23T11:39:12.751Z" }, + { url = "https://files.pythonhosted.org/packages/53/3c/212d94e6add3a3c3f412d664aee452045ca17a066def8b9421673e9482c4/coverage-7.8.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6fcbbd35a96192d042c691c9e0c49ef54bd7ed865846a3c9d624c30bb67ce46", size = 257024, upload-time = "2025-05-23T11:39:15.569Z" }, + { url = "https://files.pythonhosted.org/packages/a4/40/afc03f0883b1e51bbe804707aae62e29c4e8c8bbc365c75e3e4ddeee9ead/coverage-7.8.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05364b9cc82f138cc86128dc4e2e1251c2981a2218bfcd556fe6b0fbaa3501be", size = 252867, upload-time = "2025-05-23T11:39:17.64Z" }, + { url = "https://files.pythonhosted.org/packages/18/a2/3699190e927b9439c6ded4998941a3c1d6fa99e14cb28d8536729537e307/coverage-7.8.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46d532db4e5ff3979ce47d18e2fe8ecad283eeb7367726da0e5ef88e4fe64740", size = 255096, upload-time = "2025-05-23T11:39:19.328Z" }, + { url = "https://files.pythonhosted.org/packages/b4/06/16e3598b9466456b718eb3e789457d1a5b8bfb22e23b6e8bbc307df5daf0/coverage-7.8.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4000a31c34932e7e4fa0381a3d6deb43dc0c8f458e3e7ea6502e6238e10be625", size = 256276, upload-time = "2025-05-23T11:39:21.077Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d5/4b5a120d5d0223050a53d2783c049c311eea1709fa9de12d1c358e18b707/coverage-7.8.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:43ff5033d657cd51f83015c3b7a443287250dc14e69910577c3e03bd2e06f27b", size = 254478, upload-time = "2025-05-23T11:39:22.838Z" }, + { url = "https://files.pythonhosted.org/packages/ba/85/f9ecdb910ecdb282b121bfcaa32fa8ee8cbd7699f83330ee13ff9bbf1a85/coverage-7.8.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94316e13f0981cbbba132c1f9f365cac1d26716aaac130866ca812006f662199", size = 255255, upload-time = "2025-05-23T11:39:24.644Z" }, + { url = "https://files.pythonhosted.org/packages/50/63/2d624ac7d7ccd4ebbd3c6a9eba9d7fc4491a1226071360d59dd84928ccb2/coverage-7.8.2-cp313-cp313t-win32.whl", hash = "sha256:3f5673888d3676d0a745c3d0e16da338c5eea300cb1f4ada9c872981265e76d8", size = 215109, upload-time = "2025-05-23T11:39:26.722Z" }, + { url = "https://files.pythonhosted.org/packages/22/5e/7053b71462e970e869111c1853afd642212568a350eba796deefdfbd0770/coverage-7.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:2c08b05ee8d7861e45dc5a2cc4195c8c66dca5ac613144eb6ebeaff2d502e73d", size = 216268, upload-time = "2025-05-23T11:39:28.429Z" }, + { url = "https://files.pythonhosted.org/packages/07/69/afa41aa34147655543dbe96994f8a246daf94b361ccf5edfd5df62ce066a/coverage-7.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:1e1448bb72b387755e1ff3ef1268a06617afd94188164960dba8d0245a46004b", size = 214071, upload-time = "2025-05-23T11:39:30.55Z" }, + { url = "https://files.pythonhosted.org/packages/71/1e/388267ad9c6aa126438acc1ceafede3bb746afa9872e3ec5f0691b7d5efa/coverage-7.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:496948261eaac5ac9cf43f5d0a9f6eb7a6d4cb3bedb2c5d294138142f5c18f2a", size = 211566, upload-time = "2025-05-23T11:39:32.333Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a5/acc03e5cf0bba6357f5e7c676343de40fbf431bb1e115fbebf24b2f7f65e/coverage-7.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eacd2de0d30871eff893bab0b67840a96445edcb3c8fd915e6b11ac4b2f3fa6d", size = 211996, upload-time = "2025-05-23T11:39:34.512Z" }, + { url = "https://files.pythonhosted.org/packages/5b/a2/0fc0a9f6b7c24fa4f1d7210d782c38cb0d5e692666c36eaeae9a441b6755/coverage-7.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b039ffddc99ad65d5078ef300e0c7eed08c270dc26570440e3ef18beb816c1ca", size = 240741, upload-time = "2025-05-23T11:39:36.252Z" }, + { url = "https://files.pythonhosted.org/packages/e6/da/1c6ba2cf259710eed8916d4fd201dccc6be7380ad2b3b9f63ece3285d809/coverage-7.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e49824808d4375ede9dd84e9961a59c47f9113039f1a525e6be170aa4f5c34d", size = 238672, upload-time = "2025-05-23T11:39:38.03Z" }, + { url = "https://files.pythonhosted.org/packages/ac/51/c8fae0dc3ca421e6e2509503696f910ff333258db672800c3bdef256265a/coverage-7.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b069938961dfad881dc2f8d02b47645cd2f455d3809ba92a8a687bf513839787", size = 239769, upload-time = "2025-05-23T11:39:40.24Z" }, + { url = "https://files.pythonhosted.org/packages/59/8e/b97042ae92c59f40be0c989df090027377ba53f2d6cef73c9ca7685c26a6/coverage-7.8.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:de77c3ba8bb686d1c411e78ee1b97e6e0b963fb98b1637658dd9ad2c875cf9d7", size = 239555, upload-time = "2025-05-23T11:39:42.3Z" }, + { url = "https://files.pythonhosted.org/packages/47/35/b8893e682d6e96b1db2af5997fc13ef62219426fb17259d6844c693c5e00/coverage-7.8.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1676628065a498943bd3f64f099bb573e08cf1bc6088bbe33cf4424e0876f4b3", size = 237768, upload-time = "2025-05-23T11:39:44.069Z" }, + { url = "https://files.pythonhosted.org/packages/03/6c/023b0b9a764cb52d6243a4591dcb53c4caf4d7340445113a1f452bb80591/coverage-7.8.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8e1a26e7e50076e35f7afafde570ca2b4d7900a491174ca357d29dece5aacee7", size = 238757, upload-time = "2025-05-23T11:39:46.195Z" }, + { url = "https://files.pythonhosted.org/packages/03/ed/3af7e4d721bd61a8df7de6de9e8a4271e67f3d9e086454558fd9f48eb4f6/coverage-7.8.2-cp39-cp39-win32.whl", hash = "sha256:6782a12bf76fa61ad9350d5a6ef5f3f020b57f5e6305cbc663803f2ebd0f270a", size = 214166, upload-time = "2025-05-23T11:39:47.934Z" }, + { url = "https://files.pythonhosted.org/packages/9d/30/ee774b626773750dc6128354884652507df3c59d6aa8431526107e595227/coverage-7.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1efa4166ba75ccefd647f2d78b64f53f14fb82622bc94c5a5cb0a622f50f1c9e", size = 215050, upload-time = "2025-05-23T11:39:50.252Z" }, + { url = "https://files.pythonhosted.org/packages/69/2f/572b29496d8234e4a7773200dd835a0d32d9e171f2d974f3fe04a9dbc271/coverage-7.8.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:ec455eedf3ba0bbdf8f5a570012617eb305c63cb9f03428d39bf544cb2b94837", size = 203636, upload-time = "2025-05-23T11:39:52.002Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1a/0b9c32220ad694d66062f571cc5cedfa9997b64a591e8a500bb63de1bd40/coverage-7.8.2-py3-none-any.whl", hash = "sha256:726f32ee3713f7359696331a18daf0c3b3a70bb0ae71141b9d3c52be7c595e32", size = 203623, upload-time = "2025-05-23T11:39:53.846Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "dataclasses-json" +version = "0.6.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "marshmallow" }, + { name = "typing-inspect" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/a4/f71d9cf3a5ac257c993b5ca3f93df5f7fb395c725e7f1e6479d2514173c3/dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0", size = 32227, upload-time = "2024-06-09T16:20:19.103Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686, upload-time = "2024-06-09T16:20:16.715Z" }, +] + +[[package]] +name = "debugpy" +version = "1.8.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/75/087fe07d40f490a78782ff3b0a30e3968936854105487decdb33446d4b0e/debugpy-1.8.14.tar.gz", hash = "sha256:7cd287184318416850aa8b60ac90105837bb1e59531898c07569d197d2ed5322", size = 1641444, upload-time = "2025-04-10T19:46:10.981Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/df/156df75a41aaebd97cee9d3870fe68f8001b6c1c4ca023e221cfce69bece/debugpy-1.8.14-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:93fee753097e85623cab1c0e6a68c76308cd9f13ffdf44127e6fab4fbf024339", size = 2076510, upload-time = "2025-04-10T19:46:13.315Z" }, + { url = "https://files.pythonhosted.org/packages/69/cd/4fc391607bca0996db5f3658762106e3d2427beaef9bfd363fd370a3c054/debugpy-1.8.14-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d937d93ae4fa51cdc94d3e865f535f185d5f9748efb41d0d49e33bf3365bd79", size = 3559614, upload-time = "2025-04-10T19:46:14.647Z" }, + { url = "https://files.pythonhosted.org/packages/1a/42/4e6d2b9d63e002db79edfd0cb5656f1c403958915e0e73ab3e9220012eec/debugpy-1.8.14-cp310-cp310-win32.whl", hash = "sha256:c442f20577b38cc7a9aafecffe1094f78f07fb8423c3dddb384e6b8f49fd2987", size = 5208588, upload-time = "2025-04-10T19:46:16.233Z" }, + { url = "https://files.pythonhosted.org/packages/97/b1/cc9e4e5faadc9d00df1a64a3c2d5c5f4b9df28196c39ada06361c5141f89/debugpy-1.8.14-cp310-cp310-win_amd64.whl", hash = "sha256:f117dedda6d969c5c9483e23f573b38f4e39412845c7bc487b6f2648df30fe84", size = 5241043, upload-time = "2025-04-10T19:46:17.768Z" }, + { url = "https://files.pythonhosted.org/packages/67/e8/57fe0c86915671fd6a3d2d8746e40485fd55e8d9e682388fbb3a3d42b86f/debugpy-1.8.14-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:1b2ac8c13b2645e0b1eaf30e816404990fbdb168e193322be8f545e8c01644a9", size = 2175064, upload-time = "2025-04-10T19:46:19.486Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/2b2fd1b1c9569c6764ccdb650a6f752e4ac31be465049563c9eb127a8487/debugpy-1.8.14-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf431c343a99384ac7eab2f763980724834f933a271e90496944195318c619e2", size = 3132359, upload-time = "2025-04-10T19:46:21.192Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ee/b825c87ed06256ee2a7ed8bab8fb3bb5851293bf9465409fdffc6261c426/debugpy-1.8.14-cp311-cp311-win32.whl", hash = "sha256:c99295c76161ad8d507b413cd33422d7c542889fbb73035889420ac1fad354f2", size = 5133269, upload-time = "2025-04-10T19:46:23.047Z" }, + { url = "https://files.pythonhosted.org/packages/d5/a6/6c70cd15afa43d37839d60f324213843174c1d1e6bb616bd89f7c1341bac/debugpy-1.8.14-cp311-cp311-win_amd64.whl", hash = "sha256:7816acea4a46d7e4e50ad8d09d963a680ecc814ae31cdef3622eb05ccacf7b01", size = 5158156, upload-time = "2025-04-10T19:46:24.521Z" }, + { url = "https://files.pythonhosted.org/packages/d9/2a/ac2df0eda4898f29c46eb6713a5148e6f8b2b389c8ec9e425a4a1d67bf07/debugpy-1.8.14-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:8899c17920d089cfa23e6005ad9f22582fd86f144b23acb9feeda59e84405b84", size = 2501268, upload-time = "2025-04-10T19:46:26.044Z" }, + { url = "https://files.pythonhosted.org/packages/10/53/0a0cb5d79dd9f7039169f8bf94a144ad3efa52cc519940b3b7dde23bcb89/debugpy-1.8.14-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6bb5c0dcf80ad5dbc7b7d6eac484e2af34bdacdf81df09b6a3e62792b722826", size = 4221077, upload-time = "2025-04-10T19:46:27.464Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d5/84e01821f362327bf4828728aa31e907a2eca7c78cd7c6ec062780d249f8/debugpy-1.8.14-cp312-cp312-win32.whl", hash = "sha256:281d44d248a0e1791ad0eafdbbd2912ff0de9eec48022a5bfbc332957487ed3f", size = 5255127, upload-time = "2025-04-10T19:46:29.467Z" }, + { url = "https://files.pythonhosted.org/packages/33/16/1ed929d812c758295cac7f9cf3dab5c73439c83d9091f2d91871e648093e/debugpy-1.8.14-cp312-cp312-win_amd64.whl", hash = "sha256:5aa56ef8538893e4502a7d79047fe39b1dae08d9ae257074c6464a7b290b806f", size = 5297249, upload-time = "2025-04-10T19:46:31.538Z" }, + { url = "https://files.pythonhosted.org/packages/4d/e4/395c792b243f2367d84202dc33689aa3d910fb9826a7491ba20fc9e261f5/debugpy-1.8.14-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:329a15d0660ee09fec6786acdb6e0443d595f64f5d096fc3e3ccf09a4259033f", size = 2485676, upload-time = "2025-04-10T19:46:32.96Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f1/6f2ee3f991327ad9e4c2f8b82611a467052a0fb0e247390192580e89f7ff/debugpy-1.8.14-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f920c7f9af409d90f5fd26e313e119d908b0dd2952c2393cd3247a462331f15", size = 4217514, upload-time = "2025-04-10T19:46:34.336Z" }, + { url = "https://files.pythonhosted.org/packages/79/28/b9d146f8f2dc535c236ee09ad3e5ac899adb39d7a19b49f03ac95d216beb/debugpy-1.8.14-cp313-cp313-win32.whl", hash = "sha256:3784ec6e8600c66cbdd4ca2726c72d8ca781e94bce2f396cc606d458146f8f4e", size = 5254756, upload-time = "2025-04-10T19:46:36.199Z" }, + { url = "https://files.pythonhosted.org/packages/e0/62/a7b4a57013eac4ccaef6977966e6bec5c63906dd25a86e35f155952e29a1/debugpy-1.8.14-cp313-cp313-win_amd64.whl", hash = "sha256:684eaf43c95a3ec39a96f1f5195a7ff3d4144e4a18d69bb66beeb1a6de605d6e", size = 5297119, upload-time = "2025-04-10T19:46:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/6f/96ba96545f55b6a675afa08c96b42810de9b18c7ad17446bbec82762127a/debugpy-1.8.14-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:413512d35ff52c2fb0fd2d65e69f373ffd24f0ecb1fac514c04a668599c5ce7f", size = 2077696, upload-time = "2025-04-10T19:46:46.817Z" }, + { url = "https://files.pythonhosted.org/packages/fa/84/f378a2dd837d94de3c85bca14f1db79f8fcad7e20b108b40d59da56a6d22/debugpy-1.8.14-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c9156f7524a0d70b7a7e22b2e311d8ba76a15496fb00730e46dcdeedb9e1eea", size = 3554846, upload-time = "2025-04-10T19:46:48.72Z" }, + { url = "https://files.pythonhosted.org/packages/db/52/88824fe5d6893f59933f664c6e12783749ab537a2101baf5c713164d8aa2/debugpy-1.8.14-cp39-cp39-win32.whl", hash = "sha256:b44985f97cc3dd9d52c42eb59ee9d7ee0c4e7ecd62bca704891f997de4cef23d", size = 5209350, upload-time = "2025-04-10T19:46:50.284Z" }, + { url = "https://files.pythonhosted.org/packages/41/35/72e9399be24a04cb72cfe1284572c9fcd1d742c7fa23786925c18fa54ad8/debugpy-1.8.14-cp39-cp39-win_amd64.whl", hash = "sha256:b1528cfee6c1b1c698eb10b6b096c598738a8238822d218173d21c3086de8123", size = 5241852, upload-time = "2025-04-10T19:46:52.022Z" }, + { url = "https://files.pythonhosted.org/packages/97/1a/481f33c37ee3ac8040d3d51fc4c4e4e7e61cb08b8bc8971d6032acc2279f/debugpy-1.8.14-py2.py3-none-any.whl", hash = "sha256:5cd9a579d553b6cb9759a7908a41988ee6280b961f24f63336835d9418216a20", size = 5256230, upload-time = "2025-04-10T19:46:54.077Z" }, +] + +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, +] + +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, +] + +[[package]] +name = "executing" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693, upload-time = "2025-01-22T15:41:29.403Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702, upload-time = "2025-01-22T15:41:25.929Z" }, +] + +[[package]] +name = "fastjsonschema" +version = "2.21.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/50/4b769ce1ac4071a1ef6d86b1a3fb56cdc3a37615e8c5519e1af96cdac366/fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4", size = 373939, upload-time = "2024-12-02T10:55:15.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/2b/0817a2b257fe88725c25589d89aec060581aabf668707a8d03b2e9e0cb2a/fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667", size = 23924, upload-time = "2024-12-02T10:55:07.599Z" }, +] + +[[package]] +name = "fixit" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "libcst" }, + { name = "moreorless" }, + { name = "packaging" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "trailrunner" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/22/fc513f039c17024fde3fe2ebe3bc93e4972f7717694613b1bc109068bfc1/fixit-2.1.0.tar.gz", hash = "sha256:b31665cb6491d659d8dfef5a6078a7e9f786e299826636d03d6bd91b6f71e95b", size = 219817, upload-time = "2023-10-26T02:37:14.329Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/91/635a1d52f36a546449031c63e54220c8a71e898bcd9cbccfe1181fc1812c/fixit-2.1.0-py3-none-any.whl", hash = "sha256:76b286c0abb9d6a63e5c7d1b6673a041c4356e93d70472e94a9ad2c447da7753", size = 83583, upload-time = "2023-10-26T02:37:12.574Z" }, +] + +[[package]] +name = "flake8" +version = "7.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mccabe" }, + { name = "pycodestyle" }, + { name = "pyflakes" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/c4/5842fc9fc94584c455543540af62fd9900faade32511fab650e9891ec225/flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426", size = 48177, upload-time = "2025-03-29T20:08:39.329Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/5c/0627be4c9976d56b1217cb5187b7504e7fd7d3503f8bfd312a04077bd4f7/flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343", size = 57786, upload-time = "2025-03-29T20:08:37.902Z" }, +] + +[[package]] +name = "fqdn" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015, upload-time = "2021-03-11T07:16:29.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "hypothesis" +version = "6.135.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "sortedcontainers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/59/7022ef95715701cd90ac0cf04582e3507492ab200f370fd7ef12d80dda75/hypothesis-6.135.4.tar.gz", hash = "sha256:c63f6fc56840558c5c5e2441dd91fad1709da60bde756b816d4b89944e50a52f", size = 451895, upload-time = "2025-06-09T02:31:38.766Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/d4/25b3a9f35199eb1904967ca3e6db4afd636911fa39695760b0afac84f38a/hypothesis-6.135.4-py3-none-any.whl", hash = "sha256:6a3b13ce35d43e14aaf6a6ca4cc411e5342be5d05b77977499d07cf6a61e6e71", size = 517950, upload-time = "2025-06-09T02:31:34.463Z" }, +] + +[package.optional-dependencies] +lark = [ + { name = "lark" }, +] + +[[package]] +name = "hypothesmith" +version = "0.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hypothesis", extra = ["lark"] }, + { name = "libcst" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e3/f6/1a64114dee6c46985482c35bdbc12025db59973a0225eec47ac4d306030f/hypothesmith-0.3.3.tar.gz", hash = "sha256:96c14802d6c8e85d8975264176878db54b28d2ed921fdbfedc2e6b8ce3c81716", size = 25529, upload-time = "2024-02-16T20:21:24.511Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/bc/78dcf42c6eaaf7d628f061f1e533a596f5bca2a53be2b714adc5d370d48e/hypothesmith-0.3.3-py3-none-any.whl", hash = "sha256:fdb0172f9de97d09450da40da7da083fdd118bcd2f88b1a2289413d2d496b1b1", size = 19247, upload-time = "2024-02-16T20:20:47.059Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026, upload-time = "2022-07-01T12:21:05.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, +] + +[[package]] +name = "intervaltree" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sortedcontainers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/fb/396d568039d21344639db96d940d40eb62befe704ef849b27949ded5c3bb/intervaltree-3.1.0.tar.gz", hash = "sha256:902b1b88936918f9b2a19e0e5eb7ccb430ae45cde4f39ea4b36932920d33952d", size = 32861, upload-time = "2020-08-03T08:01:11.392Z" } + +[[package]] +name = "ipykernel" +version = "6.29.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "comm" }, + { name = "debugpy" }, + { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "ipython", version = "9.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "matplotlib-inline" }, + { name = "nest-asyncio" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/5c/67594cb0c7055dc50814b21731c22a601101ea3b1b50a9a1b090e11f5d0f/ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215", size = 163367, upload-time = "2024-07-01T14:07:22.543Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/5c/368ae6c01c7628438358e6d337c19b05425727fbb221d2a3c4303c372f42/ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5", size = 117173, upload-time = "2024-07-01T14:07:19.603Z" }, +] + +[[package]] +name = "ipython" +version = "8.18.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version < '3.10'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.10'" }, + { name = "jedi", marker = "python_full_version < '3.10'" }, + { name = "matplotlib-inline", marker = "python_full_version < '3.10'" }, + { name = "pexpect", marker = "python_full_version < '3.10' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version < '3.10'" }, + { name = "pygments", marker = "python_full_version < '3.10'" }, + { name = "stack-data", marker = "python_full_version < '3.10'" }, + { name = "traitlets", marker = "python_full_version < '3.10'" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/b9/3ba6c45a6df813c09a48bac313c22ff83efa26cbb55011218d925a46e2ad/ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27", size = 5486330, upload-time = "2023-11-27T09:58:34.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/6b/d9fdcdef2eb6a23f391251fde8781c38d42acd82abe84d054cb74f7863b0/ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397", size = 808161, upload-time = "2023-11-27T09:58:30.538Z" }, +] + +[[package]] +name = "ipython" +version = "8.37.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version == '3.10.*' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version == '3.10.*'" }, + { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, + { name = "jedi", marker = "python_full_version == '3.10.*'" }, + { name = "matplotlib-inline", marker = "python_full_version == '3.10.*'" }, + { name = "pexpect", marker = "python_full_version == '3.10.*' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version == '3.10.*'" }, + { name = "pygments", marker = "python_full_version == '3.10.*'" }, + { name = "stack-data", marker = "python_full_version == '3.10.*'" }, + { name = "traitlets", marker = "python_full_version == '3.10.*'" }, + { name = "typing-extensions", marker = "python_full_version == '3.10.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088, upload-time = "2025-05-31T16:39:09.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864, upload-time = "2025-05-31T16:39:06.38Z" }, +] + +[[package]] +name = "ipython" +version = "9.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version >= '3.11' and python_full_version < '3.13'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version >= '3.11'" }, + { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" }, + { name = "jedi", marker = "python_full_version >= '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" }, + { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "stack-data", marker = "python_full_version >= '3.11'" }, + { name = "traitlets", marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/09/4c7e06b96fbd203e06567b60fb41b06db606b6a82db6db7b2c85bb72a15c/ipython-9.3.0.tar.gz", hash = "sha256:79eb896f9f23f50ad16c3bc205f686f6e030ad246cc309c6279a242b14afe9d8", size = 4426460, upload-time = "2025-05-31T16:34:55.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/99/9ed3d52d00f1846679e3aa12e2326ac7044b5e7f90dc822b60115fa533ca/ipython-9.3.0-py3-none-any.whl", hash = "sha256:1a0b6dd9221a1f5dddf725b57ac0cb6fddc7b5f470576231ae9162b9b3455a04", size = 605320, upload-time = "2025-05-31T16:34:52.154Z" }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, +] + +[[package]] +name = "ipywidgets" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "comm" }, + { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "ipython", version = "9.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "jupyterlab-widgets" }, + { name = "traitlets" }, + { name = "widgetsnbextension" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3e/48/d3dbac45c2814cb73812f98dd6b38bbcc957a4e7bb31d6ea9c03bf94ed87/ipywidgets-8.1.7.tar.gz", hash = "sha256:15f1ac050b9ccbefd45dccfbb2ef6bed0029d8278682d569d71b8dd96bee0376", size = 116721, upload-time = "2025-05-05T12:42:03.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/6a/9166369a2f092bd286d24e6307de555d63616e8ddb373ebad2b5635ca4cd/ipywidgets-8.1.7-py3-none-any.whl", hash = "sha256:764f2602d25471c213919b8a1997df04bef869251db4ca8efba1b76b1bd9f7bb", size = 139806, upload-time = "2025-05-05T12:41:56.833Z" }, +] + +[[package]] +name = "isoduration" +version = "20.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "arrow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649, upload-time = "2020-11-01T11:00:00.312Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321, upload-time = "2020-11-01T10:59:58.02Z" }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "json5" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/12/be/c6c745ec4c4539b25a278b70e29793f10382947df0d9efba2fa09120895d/json5-0.12.0.tar.gz", hash = "sha256:0b4b6ff56801a1c7dc817b0241bca4ce474a0e6a163bfef3fc594d3fd263ff3a", size = 51907, upload-time = "2025-04-03T16:33:13.201Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/9f/3500910d5a98549e3098807493851eeef2b89cdd3032227558a104dfe926/json5-0.12.0-py3-none-any.whl", hash = "sha256:6d37aa6c08b0609f16e1ec5ff94697e2cbbfbad5ac112afa05794da9ab7810db", size = 36079, upload-time = "2025-04-03T16:33:11.927Z" }, +] + +[[package]] +name = "jsonpointer" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114, upload-time = "2024-06-10T19:24:42.462Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/d3/1cf5326b923a53515d8f3a2cd442e6d7e94fcc444716e879ea70a0ce3177/jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196", size = 353480, upload-time = "2025-05-26T18:48:10.459Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/3d/023389198f69c722d039351050738d6755376c8fd343e91dc493ea485905/jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d", size = 88709, upload-time = "2025-05-26T18:48:08.417Z" }, +] + +[package.optional-dependencies] +format-nongpl = [ + { name = "fqdn" }, + { name = "idna" }, + { name = "isoduration" }, + { name = "jsonpointer" }, + { name = "rfc3339-validator" }, + { name = "rfc3986-validator" }, + { name = "uri-template" }, + { name = "webcolors" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513, upload-time = "2025-04-23T12:34:07.418Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" }, +] + +[[package]] +name = "jupyter" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipykernel" }, + { name = "ipywidgets" }, + { name = "jupyter-console" }, + { name = "jupyterlab" }, + { name = "nbconvert" }, + { name = "notebook" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959, upload-time = "2024-08-30T07:15:48.299Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657, upload-time = "2024-08-30T07:15:47.045Z" }, +] + +[[package]] +name = "jupyter-client" +version = "8.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jupyter-core" }, + { name = "python-dateutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019, upload-time = "2024-09-17T10:44:17.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105, upload-time = "2024-09-17T10:44:15.218Z" }, +] + +[[package]] +name = "jupyter-console" +version = "6.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipykernel" }, + { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "ipython", version = "9.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "pyzmq" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363, upload-time = "2023-03-06T14:13:31.02Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510, upload-time = "2023-03-06T14:13:28.229Z" }, +] + +[[package]] +name = "jupyter-core" +version = "5.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/1b/72906d554acfeb588332eaaa6f61577705e9ec752ddb486f302dafa292d9/jupyter_core-5.8.1.tar.gz", hash = "sha256:0a5f9706f70e64786b75acba995988915ebd4601c8a52e534a40b51c95f59941", size = 88923, upload-time = "2025-05-27T07:38:16.655Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/57/6bffd4b20b88da3800c5d691e0337761576ee688eb01299eae865689d2df/jupyter_core-5.8.1-py3-none-any.whl", hash = "sha256:c28d268fc90fb53f1338ded2eb410704c5449a358406e8a948b75706e24863d0", size = 28880, upload-time = "2025-05-27T07:38:15.137Z" }, +] + +[[package]] +name = "jupyter-events" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema", extra = ["format-nongpl"] }, + { name = "packaging" }, + { name = "python-json-logger" }, + { name = "pyyaml" }, + { name = "referencing" }, + { name = "rfc3339-validator" }, + { name = "rfc3986-validator" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/c3/306d090461e4cf3cd91eceaff84bede12a8e52cd821c2d20c9a4fd728385/jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b", size = 62196, upload-time = "2025-02-03T17:23:41.485Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb", size = 19430, upload-time = "2025-02-03T17:23:38.643Z" }, +] + +[[package]] +name = "jupyter-lsp" +version = "2.2.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jupyter-server" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/b4/3200b0b09c12bc3b72d943d923323c398eff382d1dcc7c0dbc8b74630e40/jupyter-lsp-2.2.5.tar.gz", hash = "sha256:793147a05ad446f809fd53ef1cd19a9f5256fd0a2d6b7ce943a982cb4f545001", size = 48741, upload-time = "2024-04-09T17:59:44.918Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/e0/7bd7cff65594fd9936e2f9385701e44574fc7d721331ff676ce440b14100/jupyter_lsp-2.2.5-py3-none-any.whl", hash = "sha256:45fbddbd505f3fbfb0b6cb2f1bc5e15e83ab7c79cd6e89416b248cb3c00c11da", size = 69146, upload-time = "2024-04-09T17:59:43.388Z" }, +] + +[[package]] +name = "jupyter-server" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "argon2-cffi" }, + { name = "jinja2" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "jupyter-events" }, + { name = "jupyter-server-terminals" }, + { name = "nbconvert" }, + { name = "nbformat" }, + { name = "overrides" }, + { name = "packaging" }, + { name = "prometheus-client" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "pyzmq" }, + { name = "send2trash" }, + { name = "terminado" }, + { name = "tornado" }, + { name = "traitlets" }, + { name = "websocket-client" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/c8/ba2bbcd758c47f1124c4ca14061e8ce60d9c6fd537faee9534a95f83521a/jupyter_server-2.16.0.tar.gz", hash = "sha256:65d4b44fdf2dcbbdfe0aa1ace4a842d4aaf746a2b7b168134d5aaed35621b7f6", size = 728177, upload-time = "2025-05-12T16:44:46.245Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/1f/5ebbced977171d09a7b0c08a285ff9a20aafb9c51bde07e52349ff1ddd71/jupyter_server-2.16.0-py3-none-any.whl", hash = "sha256:3d8db5be3bc64403b1c65b400a1d7f4647a5ce743f3b20dbdefe8ddb7b55af9e", size = 386904, upload-time = "2025-05-12T16:44:43.335Z" }, +] + +[[package]] +name = "jupyter-server-terminals" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "terminado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/d5/562469734f476159e99a55426d697cbf8e7eb5efe89fb0e0b4f83a3d3459/jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269", size = 31430, upload-time = "2024-03-12T14:37:03.049Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa", size = 13656, upload-time = "2024-03-12T14:37:00.708Z" }, +] + +[[package]] +name = "jupyterlab" +version = "4.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-lru" }, + { name = "httpx" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "ipykernel" }, + { name = "jinja2" }, + { name = "jupyter-core" }, + { name = "jupyter-lsp" }, + { name = "jupyter-server" }, + { name = "jupyterlab-server" }, + { name = "notebook-shim" }, + { name = "packaging" }, + { name = "setuptools" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d3/2d/d1678dcf2db66cb4a38a80d9e5fcf48c349f3ac12f2d38882993353ae768/jupyterlab-4.4.3.tar.gz", hash = "sha256:a94c32fd7f8b93e82a49dc70a6ec45a5c18281ca2a7228d12765e4e210e5bca2", size = 23032376, upload-time = "2025-05-26T11:18:00.996Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/4d/7dd5c2ffbb960930452a031dc8410746183c924580f2ab4e68ceb5b3043f/jupyterlab-4.4.3-py3-none-any.whl", hash = "sha256:164302f6d4b6c44773dfc38d585665a4db401a16e5296c37df5cba63904fbdea", size = 12295480, upload-time = "2025-05-26T11:17:56.607Z" }, +] + +[[package]] +name = "jupyterlab-pygments" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900, upload-time = "2023-11-23T09:26:37.44Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884, upload-time = "2023-11-23T09:26:34.325Z" }, +] + +[[package]] +name = "jupyterlab-server" +version = "2.27.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jinja2" }, + { name = "json5" }, + { name = "jsonschema" }, + { name = "jupyter-server" }, + { name = "packaging" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/c9/a883ce65eb27905ce77ace410d83587c82ea64dc85a48d1f7ed52bcfa68d/jupyterlab_server-2.27.3.tar.gz", hash = "sha256:eb36caca59e74471988f0ae25c77945610b887f777255aa21f8065def9e51ed4", size = 76173, upload-time = "2024-07-16T17:02:04.149Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/09/2032e7d15c544a0e3cd831c51d77a8ca57f7555b2e1b2922142eddb02a84/jupyterlab_server-2.27.3-py3-none-any.whl", hash = "sha256:e697488f66c3db49df675158a77b3b017520d772c6e1548c7d9bcc5df7944ee4", size = 59700, upload-time = "2024-07-16T17:02:01.115Z" }, +] + +[[package]] +name = "jupyterlab-widgets" +version = "3.0.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/7d/160595ca88ee87ac6ba95d82177d29ec60aaa63821d3077babb22ce031a5/jupyterlab_widgets-3.0.15.tar.gz", hash = "sha256:2920888a0c2922351a9202817957a68c07d99673504d6cd37345299e971bb08b", size = 213149, upload-time = "2025-05-05T12:32:31.004Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/6a/ca128561b22b60bd5a0c4ea26649e68c8556b82bc70a0c396eebc977fe86/jupyterlab_widgets-3.0.15-py3-none-any.whl", hash = "sha256:d59023d7d7ef71400d51e6fee9a88867f6e65e10a4201605d2d7f3e8f012a31c", size = 216571, upload-time = "2025-05-05T12:32:29.534Z" }, +] + +[[package]] +name = "lark" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/60/bc7622aefb2aee1c0b4ba23c1446d3e30225c8770b38d7aedbfb65ca9d5a/lark-1.2.2.tar.gz", hash = "sha256:ca807d0162cd16cef15a8feecb862d7319e7a09bdb13aef927968e45040fed80", size = 252132, upload-time = "2024-08-13T19:49:00.652Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/00/d90b10b962b4277f5e64a78b6609968859ff86889f5b898c1a778c06ec00/lark-1.2.2-py3-none-any.whl", hash = "sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c", size = 111036, upload-time = "2024-08-13T19:48:58.603Z" }, +] + +[[package]] +name = "libcst" +source = { editable = "." } +dependencies = [ + { name = "pyyaml", marker = "python_full_version < '3.13'" }, + { name = "pyyaml-ft", marker = "python_full_version >= '3.13'" }, +] + +[package.dev-dependencies] +dev = [ + { name = "black" }, + { name = "build" }, + { name = "coverage", extra = ["toml"] }, + { name = "fixit" }, + { name = "flake8" }, + { name = "hypothesis" }, + { name = "hypothesmith" }, + { name = "maturin" }, + { name = "poethepoet" }, + { name = "prompt-toolkit" }, + { name = "pyre-check", marker = "sys_platform != 'win32'" }, + { name = "setuptools-rust" }, + { name = "setuptools-scm" }, + { name = "slotscheck" }, + { name = "ufmt" }, + { name = "usort" }, +] +docs = [ + { name = "black" }, + { name = "build" }, + { name = "coverage", extra = ["toml"] }, + { name = "fixit" }, + { name = "flake8" }, + { name = "hypothesis" }, + { name = "hypothesmith" }, + { name = "jinja2" }, + { name = "jupyter" }, + { name = "maturin" }, + { name = "nbsphinx" }, + { name = "poethepoet" }, + { name = "prompt-toolkit" }, + { name = "pyre-check", marker = "sys_platform != 'win32'" }, + { name = "setuptools-rust" }, + { name = "setuptools-scm" }, + { name = "slotscheck" }, + { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx-rtd-theme" }, + { name = "ufmt" }, + { name = "usort" }, +] + +[package.metadata] +requires-dist = [ + { name = "pyyaml", marker = "python_full_version < '3.13'", specifier = ">=5.2" }, + { name = "pyyaml-ft", marker = "python_full_version >= '3.13'", specifier = ">=8.0.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "black", specifier = "==25.1.0" }, + { name = "build", specifier = ">=0.10.0" }, + { name = "coverage", extras = ["toml"], specifier = ">=4.5.4" }, + { name = "fixit", specifier = "==2.1.0" }, + { name = "flake8", specifier = "==7.2.0" }, + { name = "hypothesis", specifier = ">=4.36.0" }, + { name = "hypothesmith", specifier = ">=0.0.4" }, + { name = "maturin", specifier = ">=1.7.0,<1.8" }, + { name = "poethepoet", specifier = ">=0.35.0" }, + { name = "prompt-toolkit", specifier = ">=2.0.9" }, + { name = "pyre-check", marker = "sys_platform != 'win32'", specifier = "==0.9.18" }, + { name = "setuptools-rust", specifier = ">=1.5.2" }, + { name = "setuptools-scm", specifier = ">=6.0.1" }, + { name = "slotscheck", specifier = ">=0.7.1" }, + { name = "ufmt", specifier = "==2.8.0" }, + { name = "usort", specifier = "==1.0.8.post1" }, +] +docs = [ + { name = "black", specifier = "==25.1.0" }, + { name = "build", specifier = ">=0.10.0" }, + { name = "coverage", extras = ["toml"], specifier = ">=4.5.4" }, + { name = "fixit", specifier = "==2.1.0" }, + { name = "flake8", specifier = "==7.2.0" }, + { name = "hypothesis", specifier = ">=4.36.0" }, + { name = "hypothesmith", specifier = ">=0.0.4" }, + { name = "jinja2", specifier = "==3.1.6" }, + { name = "jupyter", specifier = ">=1.0.0" }, + { name = "maturin", specifier = ">=1.7.0,<1.8" }, + { name = "nbsphinx", specifier = ">=0.4.2" }, + { name = "poethepoet", specifier = ">=0.35.0" }, + { name = "prompt-toolkit", specifier = ">=2.0.9" }, + { name = "pyre-check", marker = "sys_platform != 'win32'", specifier = "==0.9.18" }, + { name = "setuptools-rust", specifier = ">=1.5.2" }, + { name = "setuptools-scm", specifier = ">=6.0.1" }, + { name = "slotscheck", specifier = ">=0.7.1" }, + { name = "sphinx", specifier = ">=5.1.1" }, + { name = "sphinx-rtd-theme", specifier = ">=0.4.3" }, + { name = "ufmt", specifier = "==2.8.0" }, + { name = "usort", specifier = "==1.0.8.post1" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357, upload-time = "2024-10-18T15:20:51.44Z" }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393, upload-time = "2024-10-18T15:20:52.426Z" }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732, upload-time = "2024-10-18T15:20:53.578Z" }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866, upload-time = "2024-10-18T15:20:55.06Z" }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964, upload-time = "2024-10-18T15:20:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977, upload-time = "2024-10-18T15:20:57.189Z" }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366, upload-time = "2024-10-18T15:20:58.235Z" }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091, upload-time = "2024-10-18T15:20:59.235Z" }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065, upload-time = "2024-10-18T15:21:00.307Z" }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514, upload-time = "2024-10-18T15:21:01.122Z" }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344, upload-time = "2024-10-18T15:21:43.721Z" }, + { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389, upload-time = "2024-10-18T15:21:44.666Z" }, + { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607, upload-time = "2024-10-18T15:21:45.452Z" }, + { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728, upload-time = "2024-10-18T15:21:46.295Z" }, + { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826, upload-time = "2024-10-18T15:21:47.134Z" }, + { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843, upload-time = "2024-10-18T15:21:48.334Z" }, + { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219, upload-time = "2024-10-18T15:21:49.587Z" }, + { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946, upload-time = "2024-10-18T15:21:50.441Z" }, + { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063, upload-time = "2024-10-18T15:21:51.385Z" }, + { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506, upload-time = "2024-10-18T15:21:52.974Z" }, +] + +[[package]] +name = "marshmallow" +version = "3.26.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/5e/5e53d26b42ab75491cda89b871dab9e97c840bf12c63ec58a1919710cd06/marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6", size = 221825, upload-time = "2025-02-03T15:32:25.093Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/75/51952c7b2d3873b44a0028b1bd26a25078c18f92f256608e8d1dc61b39fd/marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c", size = 50878, upload-time = "2025-02-03T15:32:22.295Z" }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159, upload-time = "2024-04-15T13:44:44.803Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899, upload-time = "2024-04-15T13:44:43.265Z" }, +] + +[[package]] +name = "maturin" +version = "1.7.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/1e/085ddc0e5b08ae7af7a743a0dd6ed06b22a1332288488f1a333137885150/maturin-1.7.8.tar.gz", hash = "sha256:649c6ef3f0fa4c5f596140d761dc5a4d577c485cc32fb5b9b344a8280352880d", size = 195704, upload-time = "2024-12-04T11:38:23.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/ed/c8bb26e91c879e418ae1b01630722ed20b6fe0e6755be8d538d83666f136/maturin-1.7.8-py3-none-linux_armv6l.whl", hash = "sha256:c6950fd2790acd93265e1501cea66f9249cff19724654424ca75a3b17ebb315b", size = 7515691, upload-time = "2024-12-04T11:37:55.443Z" }, + { url = "https://files.pythonhosted.org/packages/38/7a/573f969315f0b92a09a0a565d45e98812c87796e2e19a7856159ab234faf/maturin-1.7.8-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:f98288d5c382bacf0c076871dfd50c38f1eb2248f417551e98dd6f47f6ee8afa", size = 14434454, upload-time = "2024-12-04T11:37:58.448Z" }, + { url = "https://files.pythonhosted.org/packages/a6/17/46834841fbf19231487f185e68b95ca348cc05cce49be8787e0bc7e9dc47/maturin-1.7.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b2d4e0f674ca29864e6b86c2eb9fee8236d1c7496c25f7300e34229272468f4c", size = 7509122, upload-time = "2024-12-04T11:38:01.355Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8f/bf8b4871eb390a4baef2e0bb5016852c7c0311a9772e2945534cfa2ee40e/maturin-1.7.8-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:6cafb17bf57822bdc04423d9e3e766d42918d474848fe9833e397267514ba891", size = 7598870, upload-time = "2024-12-04T11:38:03.708Z" }, + { url = "https://files.pythonhosted.org/packages/dc/43/c842be67a7c59568082345249b956138ae93d0b2474fb41c186ce26d05e1/maturin-1.7.8-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:2b2bdee0c3a84696b3a809054c43ead1a04b7b3321cbd5b8f5676e4ba4691d0f", size = 7932310, upload-time = "2024-12-04T11:38:05.463Z" }, + { url = "https://files.pythonhosted.org/packages/12/12/42435d05f2d6c75eb621751e6f021d29eb34d18e3b9c5c94d828744c2d54/maturin-1.7.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:b8188b71259fc2bc568d9c8acc186fcfed96f42539bcb55b8e6f4ec26e411f37", size = 7321964, upload-time = "2024-12-04T11:38:07.143Z" }, + { url = "https://files.pythonhosted.org/packages/b4/26/f3272ee985ebf9b3e8c4cd4f4efb022af1e12c9f53aed0dcc9a255399f4e/maturin-1.7.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:a4f58c2a53c2958a1bf090960b08b28e676136cd88ac2f5dfdcf1b14ea54ec06", size = 7408613, upload-time = "2024-12-04T11:38:09.814Z" }, + { url = "https://files.pythonhosted.org/packages/36/7d/be27bcc7d3ac6e6c2136a8ec0cc56f227a292d6cfdde55e095b6c0aa24a9/maturin-1.7.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:c5d6c0c631d1fc646cd3834795e6cfd72ab4271d289df7e0f911261a02bec75f", size = 9496974, upload-time = "2024-12-04T11:38:11.618Z" }, + { url = "https://files.pythonhosted.org/packages/e1/e8/0d7323e9a31c11edf69c4473d73eca74803ce3e2390abf8ae3ac7eb10b04/maturin-1.7.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c23664d19dadcbf800ef70f26afb2e0485a985c62889930934f019c565534c23", size = 10828401, upload-time = "2024-12-04T11:38:14.42Z" }, + { url = "https://files.pythonhosted.org/packages/7e/82/5080e052c0d8c9872f6d4b94cae84c17ed7f2ea270d709210ea6445b655f/maturin-1.7.8-py3-none-win32.whl", hash = "sha256:403eebf1afa6f19b49425f089e39c53b8e597bc86a47f3a76e828dc78d27fa80", size = 6845240, upload-time = "2024-12-04T11:38:17.162Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c9/9b162361ded893f36038c2f8ac6a972ec441c11df8d17c440997eb28090f/maturin-1.7.8-py3-none-win_amd64.whl", hash = "sha256:1ce48d007438b895f8665314b6748ac0dab31e4f32049a60b52281dd2dccbdde", size = 7762332, upload-time = "2024-12-04T11:38:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/fa/40/46d4742db742f69a7fe0054cd7c82bc79b2d70cb8c91f7e737e75c28a5f3/maturin-1.7.8-py3-none-win_arm64.whl", hash = "sha256:cc92a62953205e8945b6cfe6943d6a8576a4442d30d9c67141f944f4f4640e62", size = 6501353, upload-time = "2024-12-04T11:38:21.713Z" }, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, +] + +[[package]] +name = "mistune" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/79/bda47f7dd7c3c55770478d6d02c9960c430b0cf1773b72366ff89126ea31/mistune-3.1.3.tar.gz", hash = "sha256:a7035c21782b2becb6be62f8f25d3df81ccb4d6fa477a6525b15af06539f02a0", size = 94347, upload-time = "2025-03-19T14:27:24.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/4d/23c4e4f09da849e127e9f123241946c23c1e30f45a88366879e064211815/mistune-3.1.3-py3-none-any.whl", hash = "sha256:1a32314113cff28aa6432e99e522677c8587fd83e3d51c29b82a52409c842bd9", size = 53410, upload-time = "2025-03-19T14:27:23.451Z" }, +] + +[[package]] +name = "moreorless" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8d/85/2e4999ac4a21ab3c5f31e2a48e0989a80be3afc512a7983e3253615983d4/moreorless-0.5.0.tar.gz", hash = "sha256:560a04f85006fccd74feaa4b6213a446392ff7b5ec0194a5464b6c30f182fa33", size = 14093, upload-time = "2025-05-04T22:29:59.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/2e/9ea80ca55b73530b7639c6f146a58f636ddfe5a852ad467a44fe3e80d809/moreorless-0.5.0-py3-none-any.whl", hash = "sha256:66228870cd2f14bad5c3c3780aa71e29d3b2d9b5a01c03bfbf105efd4f668ecf", size = 14380, upload-time = "2025-05-04T22:29:57.417Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "nbclient" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "nbformat" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/66/7ffd18d58eae90d5721f9f39212327695b749e23ad44b3881744eaf4d9e8/nbclient-0.10.2.tar.gz", hash = "sha256:90b7fc6b810630db87a6d0c2250b1f0ab4cf4d3c27a299b0cde78a4ed3fd9193", size = 62424, upload-time = "2024-12-19T10:32:27.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/6d/e7fa07f03a4a7b221d94b4d586edb754a9b0dc3c9e2c93353e9fa4e0d117/nbclient-0.10.2-py3-none-any.whl", hash = "sha256:4ffee11e788b4a27fabeb7955547e4318a5298f34342a4bfd01f2e1faaeadc3d", size = 25434, upload-time = "2024-12-19T10:32:24.139Z" }, +] + +[[package]] +name = "nbconvert" +version = "7.16.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "bleach", extra = ["css"] }, + { name = "defusedxml" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jinja2" }, + { name = "jupyter-core" }, + { name = "jupyterlab-pygments" }, + { name = "markupsafe" }, + { name = "mistune" }, + { name = "nbclient" }, + { name = "nbformat" }, + { name = "packaging" }, + { name = "pandocfilters" }, + { name = "pygments" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/59/f28e15fc47ffb73af68a8d9b47367a8630d76e97ae85ad18271b9db96fdf/nbconvert-7.16.6.tar.gz", hash = "sha256:576a7e37c6480da7b8465eefa66c17844243816ce1ccc372633c6b71c3c0f582", size = 857715, upload-time = "2025-01-28T09:29:14.724Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-py3-none-any.whl", hash = "sha256:1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b", size = 258525, upload-time = "2025-01-28T09:29:12.551Z" }, +] + +[[package]] +name = "nbformat" +version = "5.10.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastjsonschema" }, + { name = "jsonschema" }, + { name = "jupyter-core" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749, upload-time = "2024-04-04T11:20:37.371Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" }, +] + +[[package]] +name = "nbsphinx" +version = "0.9.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "jinja2" }, + { name = "nbconvert" }, + { name = "nbformat" }, + { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1e/84/b1856b7651ac34e965aa567a158714c7f3bd42a1b1ce76bf423ffb99872c/nbsphinx-0.9.7.tar.gz", hash = "sha256:abd298a686d55fa894ef697c51d44f24e53aa312dadae38e82920f250a5456fe", size = 180479, upload-time = "2025-03-03T19:46:08.069Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/2d/8c8e635bcc6757573d311bb3c5445426382f280da32b8cd6d82d501ef4a4/nbsphinx-0.9.7-py3-none-any.whl", hash = "sha256:7292c3767fea29e405c60743eee5393682a83982ab202ff98f5eb2db02629da8", size = 31660, upload-time = "2025-03-03T19:46:06.581Z" }, +] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, +] + +[[package]] +name = "notebook" +version = "7.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-server" }, + { name = "jupyterlab" }, + { name = "jupyterlab-server" }, + { name = "notebook-shim" }, + { name = "tornado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/21/4f83b15e483da4f4f63928edd0cb08b6e7d33f8a15c23b116a90c44c6235/notebook-7.4.3.tar.gz", hash = "sha256:a1567481cd3853f2610ee0ecf5dfa12bb508e878ee8f92152c134ef7f0568a76", size = 13881668, upload-time = "2025-05-26T14:27:21.656Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/1b/16c809d799e3ddd7a97c8b43734f79624b74ddef9707e7d92275a13777bc/notebook-7.4.3-py3-none-any.whl", hash = "sha256:9cdeee954e04101cadb195d90e2ab62b7c9286c1d4f858bf3bb54e40df16c0c3", size = 14286402, upload-time = "2025-05-26T14:27:17.339Z" }, +] + +[[package]] +name = "notebook-shim" +version = "0.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-server" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167, upload-time = "2024-02-14T23:35:18.353Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307, upload-time = "2024-02-14T23:35:16.286Z" }, +] + +[[package]] +name = "overrides" +version = "7.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812, upload-time = "2024-01-27T21:01:33.423Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832, upload-time = "2024-01-27T21:01:31.393Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pandocfilters" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454, upload-time = "2024-01-18T20:08:13.726Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663, upload-time = "2024-01-18T20:08:11.28Z" }, +] + +[[package]] +name = "parso" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609, upload-time = "2024-04-05T09:43:55.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650, upload-time = "2024-04-05T09:43:53.299Z" }, +] + +[[package]] +name = "pastel" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/f1/4594f5e0fcddb6953e5b8fe00da8c317b8b41b547e2b3ae2da7512943c62/pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d", size = 7555, upload-time = "2020-09-16T19:21:12.43Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/18/a8444036c6dd65ba3624c63b734d3ba95ba63ace513078e1580590075d21/pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364", size = 5955, upload-time = "2020-09-16T19:21:11.409Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, +] + +[[package]] +name = "poethepoet" +version = "0.35.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pastel" }, + { name = "pyyaml" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/b1/d4f4361b278fae10f6074675385ce3acf53c647f8e6eeba22c652f8ba985/poethepoet-0.35.0.tar.gz", hash = "sha256:b396ae862d7626e680bbd0985b423acf71634ce93a32d8b5f38340f44f5fbc3e", size = 66006, upload-time = "2025-06-09T12:58:18.849Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/08/abc2d7e2400dd8906e3208f9b88ac610f097d7ee0c7a1fa4a157b49a9e86/poethepoet-0.35.0-py3-none-any.whl", hash = "sha256:bed5ae1fd63f179dfa67aabb93fa253d79695c69667c927d8b24ff378799ea75", size = 87164, upload-time = "2025-06-09T12:58:17.084Z" }, +] + +[[package]] +name = "prometheus-client" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/cf/40dde0a2be27cc1eb41e333d1a674a74ce8b8b0457269cc640fd42b07cf7/prometheus_client-0.22.1.tar.gz", hash = "sha256:190f1331e783cf21eb60bca559354e0a4d4378facecf78f5428c39b675d20d28", size = 69746, upload-time = "2025-06-02T14:29:01.152Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/ae/ec06af4fe3ee72d16973474f122541746196aaa16cea6f66d18b963c6177/prometheus_client-0.22.1-py3-none-any.whl", hash = "sha256:cca895342e308174341b2cbf99a56bef291fbc0ef7b9e5412a0f26d653ba7094", size = 58694, upload-time = "2025-06-02T14:29:00.068Z" }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.51" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/6e/9d084c929dfe9e3bfe0c6a47e31f78a25c54627d64a66e884a8bf5474f1c/prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed", size = 428940, upload-time = "2025-04-15T09:18:47.731Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/4f/5249960887b1fbe561d9ff265496d170b55a735b76724f10ef19f9e40716/prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", size = 387810, upload-time = "2025-04-15T09:18:44.753Z" }, +] + +[[package]] +name = "psutil" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, +] + +[[package]] +name = "pycodestyle" +version = "2.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/6e/1f4a62078e4d95d82367f24e685aef3a672abfd27d1a868068fed4ed2254/pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae", size = 39312, upload-time = "2025-03-29T17:33:30.669Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/be/b00116df1bfb3e0bb5b45e29d604799f7b91dd861637e4d448b4e09e6a3e/pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9", size = 31424, upload-time = "2025-03-29T17:33:29.405Z" }, +] + +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, +] + +[[package]] +name = "pyflakes" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/cc/1df338bd7ed1fa7c317081dcf29bf2f01266603b301e6858856d346a12b3/pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b", size = 64175, upload-time = "2025-03-31T13:21:20.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/40/b293a4fa769f3b02ab9e387c707c4cbdc34f073f945de0386107d4e669e6/pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a", size = 63164, upload-time = "2025-03-31T13:21:18.503Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, +] + +[[package]] +name = "pyproject-hooks" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, +] + +[[package]] +name = "pyre-check" +version = "0.9.18" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "dataclasses-json" }, + { name = "intervaltree" }, + { name = "libcst" }, + { name = "psutil" }, + { name = "pyre-extensions" }, + { name = "tabulate" }, + { name = "testslide" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/02/a92e10ecddce435f794493e18e1c0add477e3c307023525a49cffa299163/pyre-check-0.9.18.tar.gz", hash = "sha256:d5eb6db9011a7207189ecd0eaf32951e46cb0769c0f96a78fd0b90e633c9df2c", size = 18030825, upload-time = "2023-02-14T00:59:29.593Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/d9/5207ccd2eff3870b44f11c2db6b6d8e31cbcaca973a1b5ba4ac6d2460e41/pyre_check-0.9.18-py3-none-macosx_10_11_x86_64.whl", hash = "sha256:22633f5af3b986d266451a9e386a32414f8868de0a94226c7766f81eb080c59d", size = 19378418, upload-time = "2023-02-14T00:59:24.891Z" }, + { url = "https://files.pythonhosted.org/packages/33/07/865a1ca2a57fc2e9a0f78e005938a465b8a2ff748538fb5a0c1c19cb661f/pyre_check-0.9.18-py3-none-manylinux1_x86_64.whl", hash = "sha256:5659d4dbd6d1dd3052359861d828419f07d1ced1dad4ce4ca79071d252699c26", size = 23486523, upload-time = "2023-02-14T00:59:21.022Z" }, +] + +[[package]] +name = "pyre-extensions" +version = "0.0.32" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, + { name = "typing-inspect" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/53/5bc2532536e921c48366ad1047c1344ccef6afa5e84053f0f6e20a453767/pyre_extensions-0.0.32.tar.gz", hash = "sha256:5396715f14ea56c4d5fd0a88c57ca7e44faa468f905909edd7de4ad90ed85e55", size = 10852, upload-time = "2024-11-22T19:26:44.152Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/7a/9812cb8be9828ab688203c5ac5f743c60652887f0c00995a6f6f19f912bd/pyre_extensions-0.0.32-py3-none-any.whl", hash = "sha256:a63ba6883ab02f4b1a9f372ed4eb4a2f4c6f3d74879aa2725186fdfcfe3e5c68", size = 12766, upload-time = "2024-11-22T19:26:42.465Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-json-logger" +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/de/d3144a0bceede957f961e975f3752760fbe390d57fbe194baf709d8f1f7b/python_json_logger-3.3.0.tar.gz", hash = "sha256:12b7e74b17775e7d565129296105bbe3910842d9d0eb083fc83a6a617aa8df84", size = 16642, upload-time = "2025-03-07T07:08:27.301Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/20/0f2523b9e50a8052bc6a8b732dfc8568abbdc42010aef03a2d750bdab3b2/python_json_logger-3.3.0-py3-none-any.whl", hash = "sha256:dd980fae8cffb24c13caf6e158d3d61c0d6d22342f932cb6e9deedab3d35eec7", size = 15163, upload-time = "2025-03-07T07:08:25.627Z" }, +] + +[[package]] +name = "pywin32" +version = "310" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/da/a5f38fffbba2fb99aa4aa905480ac4b8e83ca486659ac8c95bce47fb5276/pywin32-310-cp310-cp310-win32.whl", hash = "sha256:6dd97011efc8bf51d6793a82292419eba2c71cf8e7250cfac03bba284454abc1", size = 8848240, upload-time = "2025-03-17T00:55:46.783Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fe/d873a773324fa565619ba555a82c9dabd677301720f3660a731a5d07e49a/pywin32-310-cp310-cp310-win_amd64.whl", hash = "sha256:c3e78706e4229b915a0821941a84e7ef420bf2b77e08c9dae3c76fd03fd2ae3d", size = 9601854, upload-time = "2025-03-17T00:55:48.783Z" }, + { url = "https://files.pythonhosted.org/packages/3c/84/1a8e3d7a15490d28a5d816efa229ecb4999cdc51a7c30dd8914f669093b8/pywin32-310-cp310-cp310-win_arm64.whl", hash = "sha256:33babed0cf0c92a6f94cc6cc13546ab24ee13e3e800e61ed87609ab91e4c8213", size = 8522963, upload-time = "2025-03-17T00:55:50.969Z" }, + { url = "https://files.pythonhosted.org/packages/f7/b1/68aa2986129fb1011dabbe95f0136f44509afaf072b12b8f815905a39f33/pywin32-310-cp311-cp311-win32.whl", hash = "sha256:1e765f9564e83011a63321bb9d27ec456a0ed90d3732c4b2e312b855365ed8bd", size = 8784284, upload-time = "2025-03-17T00:55:53.124Z" }, + { url = "https://files.pythonhosted.org/packages/b3/bd/d1592635992dd8db5bb8ace0551bc3a769de1ac8850200cfa517e72739fb/pywin32-310-cp311-cp311-win_amd64.whl", hash = "sha256:126298077a9d7c95c53823934f000599f66ec9296b09167810eb24875f32689c", size = 9520748, upload-time = "2025-03-17T00:55:55.203Z" }, + { url = "https://files.pythonhosted.org/packages/90/b1/ac8b1ffce6603849eb45a91cf126c0fa5431f186c2e768bf56889c46f51c/pywin32-310-cp311-cp311-win_arm64.whl", hash = "sha256:19ec5fc9b1d51c4350be7bb00760ffce46e6c95eaf2f0b2f1150657b1a43c582", size = 8455941, upload-time = "2025-03-17T00:55:57.048Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ec/4fdbe47932f671d6e348474ea35ed94227fb5df56a7c30cbbb42cd396ed0/pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d", size = 8796239, upload-time = "2025-03-17T00:55:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e5/b0627f8bb84e06991bea89ad8153a9e50ace40b2e1195d68e9dff6b03d0f/pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060", size = 9503839, upload-time = "2025-03-17T00:56:00.8Z" }, + { url = "https://files.pythonhosted.org/packages/1f/32/9ccf53748df72301a89713936645a664ec001abd35ecc8578beda593d37d/pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966", size = 8459470, upload-time = "2025-03-17T00:56:02.601Z" }, + { url = "https://files.pythonhosted.org/packages/1c/09/9c1b978ffc4ae53999e89c19c77ba882d9fce476729f23ef55211ea1c034/pywin32-310-cp313-cp313-win32.whl", hash = "sha256:5d241a659c496ada3253cd01cfaa779b048e90ce4b2b38cd44168ad555ce74ab", size = 8794384, upload-time = "2025-03-17T00:56:04.383Z" }, + { url = "https://files.pythonhosted.org/packages/45/3c/b4640f740ffebadd5d34df35fecba0e1cfef8fde9f3e594df91c28ad9b50/pywin32-310-cp313-cp313-win_amd64.whl", hash = "sha256:667827eb3a90208ddbdcc9e860c81bde63a135710e21e4cb3348968e4bd5249e", size = 9503039, upload-time = "2025-03-17T00:56:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/b4/f4/f785020090fb050e7fb6d34b780f2231f302609dc964672f72bfaeb59a28/pywin32-310-cp313-cp313-win_arm64.whl", hash = "sha256:e308f831de771482b7cf692a1f308f8fca701b2d8f9dde6cc440c7da17e47b33", size = 8458152, upload-time = "2025-03-17T00:56:07.819Z" }, + { url = "https://files.pythonhosted.org/packages/a2/cd/d09d434630edb6a0c44ad5079611279a67530296cfe0451e003de7f449ff/pywin32-310-cp39-cp39-win32.whl", hash = "sha256:851c8d927af0d879221e616ae1f66145253537bbdd321a77e8ef701b443a9a1a", size = 8848099, upload-time = "2025-03-17T00:55:42.415Z" }, + { url = "https://files.pythonhosted.org/packages/93/ff/2a8c10315ffbdee7b3883ac0d1667e267ca8b3f6f640d81d43b87a82c0c7/pywin32-310-cp39-cp39-win_amd64.whl", hash = "sha256:96867217335559ac619f00ad70e513c0fcf84b8a3af9fc2bba3b59b97da70475", size = 9602031, upload-time = "2025-03-17T00:55:44.512Z" }, +] + +[[package]] +name = "pywinpty" +version = "2.0.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/7c/917f9c4681bb8d34bfbe0b79d36bbcd902651aeab48790df3d30ba0202fb/pywinpty-2.0.15.tar.gz", hash = "sha256:312cf39153a8736c617d45ce8b6ad6cd2107de121df91c455b10ce6bba7a39b2", size = 29017, upload-time = "2025-02-03T21:53:23.265Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/b7/855db919ae526d2628f3f2e6c281c4cdff7a9a8af51bb84659a9f07b1861/pywinpty-2.0.15-cp310-cp310-win_amd64.whl", hash = "sha256:8e7f5de756a615a38b96cd86fa3cd65f901ce54ce147a3179c45907fa11b4c4e", size = 1405161, upload-time = "2025-02-03T21:56:25.008Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ac/6884dcb7108af66ad53f73ef4dad096e768c9203a6e6ce5e6b0c4a46e238/pywinpty-2.0.15-cp311-cp311-win_amd64.whl", hash = "sha256:9a6bcec2df2707aaa9d08b86071970ee32c5026e10bcc3cc5f6f391d85baf7ca", size = 1405249, upload-time = "2025-02-03T21:55:47.114Z" }, + { url = "https://files.pythonhosted.org/packages/88/e5/9714def18c3a411809771a3fbcec70bffa764b9675afb00048a620fca604/pywinpty-2.0.15-cp312-cp312-win_amd64.whl", hash = "sha256:83a8f20b430bbc5d8957249f875341a60219a4e971580f2ba694fbfb54a45ebc", size = 1405243, upload-time = "2025-02-03T21:56:52.476Z" }, + { url = "https://files.pythonhosted.org/packages/fb/16/2ab7b3b7f55f3c6929e5f629e1a68362981e4e5fed592a2ed1cb4b4914a5/pywinpty-2.0.15-cp313-cp313-win_amd64.whl", hash = "sha256:ab5920877dd632c124b4ed17bc6dd6ef3b9f86cd492b963ffdb1a67b85b0f408", size = 1405020, upload-time = "2025-02-03T21:56:04.753Z" }, + { url = "https://files.pythonhosted.org/packages/7c/16/edef3515dd2030db2795dbfbe392232c7a0f3dc41b98e92b38b42ba497c7/pywinpty-2.0.15-cp313-cp313t-win_amd64.whl", hash = "sha256:a4560ad8c01e537708d2790dbe7da7d986791de805d89dd0d3697ca59e9e4901", size = 1404151, upload-time = "2025-02-03T21:55:53.628Z" }, + { url = "https://files.pythonhosted.org/packages/47/96/90fa02f19b1eff7469ad7bf0ef8efca248025de9f1d0a0b25682d2aacf68/pywinpty-2.0.15-cp39-cp39-win_amd64.whl", hash = "sha256:d261cd88fcd358cfb48a7ca0700db3e1c088c9c10403c9ebc0d8a8b57aa6a117", size = 1405302, upload-time = "2025-02-03T21:55:40.394Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, + { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" }, + { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" }, + { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593, upload-time = "2024-08-06T20:33:46.63Z" }, + { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, +] + +[[package]] +name = "pyyaml-ft" +version = "8.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/eb/5a0d575de784f9a1f94e2b1288c6886f13f34185e13117ed530f32b6f8a8/pyyaml_ft-8.0.0.tar.gz", hash = "sha256:0c947dce03954c7b5d38869ed4878b2e6ff1d44b08a0d84dc83fdad205ae39ab", size = 141057, upload-time = "2025-06-10T15:32:15.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/ba/a067369fe61a2e57fb38732562927d5bae088c73cb9bb5438736a9555b29/pyyaml_ft-8.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c1306282bc958bfda31237f900eb52c9bedf9b93a11f82e1aab004c9a5657a6", size = 187027, upload-time = "2025-06-10T15:31:48.722Z" }, + { url = "https://files.pythonhosted.org/packages/ad/c5/a3d2020ce5ccfc6aede0d45bcb870298652ac0cf199f67714d250e0cdf39/pyyaml_ft-8.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:30c5f1751625786c19de751e3130fc345ebcba6a86f6bddd6e1285342f4bbb69", size = 176146, upload-time = "2025-06-10T15:31:50.584Z" }, + { url = "https://files.pythonhosted.org/packages/e3/bb/23a9739291086ca0d3189eac7cd92b4d00e9fdc77d722ab610c35f9a82ba/pyyaml_ft-8.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fa992481155ddda2e303fcc74c79c05eddcdbc907b888d3d9ce3ff3e2adcfb0", size = 746792, upload-time = "2025-06-10T15:31:52.304Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c2/e8825f4ff725b7e560d62a3609e31d735318068e1079539ebfde397ea03e/pyyaml_ft-8.0.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cec6c92b4207004b62dfad1f0be321c9f04725e0f271c16247d8b39c3bf3ea42", size = 786772, upload-time = "2025-06-10T15:31:54.712Z" }, + { url = "https://files.pythonhosted.org/packages/35/be/58a4dcae8854f2fdca9b28d9495298fd5571a50d8430b1c3033ec95d2d0e/pyyaml_ft-8.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06237267dbcab70d4c0e9436d8f719f04a51123f0ca2694c00dd4b68c338e40b", size = 778723, upload-time = "2025-06-10T15:31:56.093Z" }, + { url = "https://files.pythonhosted.org/packages/86/ed/fed0da92b5d5d7340a082e3802d84c6dc9d5fa142954404c41a544c1cb92/pyyaml_ft-8.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8a7f332bc565817644cdb38ffe4739e44c3e18c55793f75dddb87630f03fc254", size = 758478, upload-time = "2025-06-10T15:31:58.314Z" }, + { url = "https://files.pythonhosted.org/packages/f0/69/ac02afe286275980ecb2dcdc0156617389b7e0c0a3fcdedf155c67be2b80/pyyaml_ft-8.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d10175a746be65f6feb86224df5d6bc5c049ebf52b89a88cf1cd78af5a367a8", size = 799159, upload-time = "2025-06-10T15:31:59.675Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ac/c492a9da2e39abdff4c3094ec54acac9747743f36428281fb186a03fab76/pyyaml_ft-8.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:58e1015098cf8d8aec82f360789c16283b88ca670fe4275ef6c48c5e30b22a96", size = 158779, upload-time = "2025-06-10T15:32:01.029Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9b/41998df3298960d7c67653669f37710fa2d568a5fc933ea24a6df60acaf6/pyyaml_ft-8.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e64fa5f3e2ceb790d50602b2fd4ec37abbd760a8c778e46354df647e7c5a4ebb", size = 191331, upload-time = "2025-06-10T15:32:02.602Z" }, + { url = "https://files.pythonhosted.org/packages/0f/16/2710c252ee04cbd74d9562ebba709e5a284faeb8ada88fcda548c9191b47/pyyaml_ft-8.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8d445bf6ea16bb93c37b42fdacfb2f94c8e92a79ba9e12768c96ecde867046d1", size = 182879, upload-time = "2025-06-10T15:32:04.466Z" }, + { url = "https://files.pythonhosted.org/packages/9a/40/ae8163519d937fa7bfa457b6f78439cc6831a7c2b170e4f612f7eda71815/pyyaml_ft-8.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c56bb46b4fda34cbb92a9446a841da3982cdde6ea13de3fbd80db7eeeab8b49", size = 811277, upload-time = "2025-06-10T15:32:06.214Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/28d82dbff7f87b96f0eeac79b7d972a96b4980c1e445eb6a857ba91eda00/pyyaml_ft-8.0.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dab0abb46eb1780da486f022dce034b952c8ae40753627b27a626d803926483b", size = 831650, upload-time = "2025-06-10T15:32:08.076Z" }, + { url = "https://files.pythonhosted.org/packages/e8/df/161c4566facac7d75a9e182295c223060373d4116dead9cc53a265de60b9/pyyaml_ft-8.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd48d639cab5ca50ad957b6dd632c7dd3ac02a1abe0e8196a3c24a52f5db3f7a", size = 815755, upload-time = "2025-06-10T15:32:09.435Z" }, + { url = "https://files.pythonhosted.org/packages/05/10/f42c48fa5153204f42eaa945e8d1fd7c10d6296841dcb2447bf7da1be5c4/pyyaml_ft-8.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:052561b89d5b2a8e1289f326d060e794c21fa068aa11255fe71d65baf18a632e", size = 810403, upload-time = "2025-06-10T15:32:11.051Z" }, + { url = "https://files.pythonhosted.org/packages/d5/d2/e369064aa51009eb9245399fd8ad2c562bd0bcd392a00be44b2a824ded7c/pyyaml_ft-8.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3bb4b927929b0cb162fb1605392a321e3333e48ce616cdcfa04a839271373255", size = 835581, upload-time = "2025-06-10T15:32:12.897Z" }, + { url = "https://files.pythonhosted.org/packages/c0/28/26534bed77109632a956977f60d8519049f545abc39215d086e33a61f1f2/pyyaml_ft-8.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:de04cfe9439565e32f178106c51dd6ca61afaa2907d143835d501d84703d3793", size = 171579, upload-time = "2025-06-10T15:32:14.34Z" }, +] + +[[package]] +name = "pyzmq" +version = "26.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "implementation_name == 'pypy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/11/b9213d25230ac18a71b39b3723494e57adebe36e066397b961657b3b41c1/pyzmq-26.4.0.tar.gz", hash = "sha256:4bd13f85f80962f91a651a7356fe0472791a5f7a92f227822b5acf44795c626d", size = 278293, upload-time = "2025-04-04T12:05:44.049Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/b8/af1d814ffc3ff9730f9a970cbf216b6f078e5d251a25ef5201d7bc32a37c/pyzmq-26.4.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:0329bdf83e170ac133f44a233fc651f6ed66ef8e66693b5af7d54f45d1ef5918", size = 1339238, upload-time = "2025-04-04T12:03:07.022Z" }, + { url = "https://files.pythonhosted.org/packages/ee/e4/5aafed4886c264f2ea6064601ad39c5fc4e9b6539c6ebe598a859832eeee/pyzmq-26.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:398a825d2dea96227cf6460ce0a174cf7657d6f6827807d4d1ae9d0f9ae64315", size = 672848, upload-time = "2025-04-04T12:03:08.591Z" }, + { url = "https://files.pythonhosted.org/packages/79/39/026bf49c721cb42f1ef3ae0ee3d348212a7621d2adb739ba97599b6e4d50/pyzmq-26.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d52d62edc96787f5c1dfa6c6ccff9b581cfae5a70d94ec4c8da157656c73b5b", size = 911299, upload-time = "2025-04-04T12:03:10Z" }, + { url = "https://files.pythonhosted.org/packages/03/23/b41f936a9403b8f92325c823c0f264c6102a0687a99c820f1aaeb99c1def/pyzmq-26.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1410c3a3705db68d11eb2424d75894d41cff2f64d948ffe245dd97a9debfebf4", size = 867920, upload-time = "2025-04-04T12:03:11.311Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3e/2de5928cdadc2105e7c8f890cc5f404136b41ce5b6eae5902167f1d5641c/pyzmq-26.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7dacb06a9c83b007cc01e8e5277f94c95c453c5851aac5e83efe93e72226353f", size = 862514, upload-time = "2025-04-04T12:03:13.013Z" }, + { url = "https://files.pythonhosted.org/packages/ce/57/109569514dd32e05a61d4382bc88980c95bfd2f02e58fea47ec0ccd96de1/pyzmq-26.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6bab961c8c9b3a4dc94d26e9b2cdf84de9918931d01d6ff38c721a83ab3c0ef5", size = 1204494, upload-time = "2025-04-04T12:03:14.795Z" }, + { url = "https://files.pythonhosted.org/packages/aa/02/dc51068ff2ca70350d1151833643a598625feac7b632372d229ceb4de3e1/pyzmq-26.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7a5c09413b924d96af2aa8b57e76b9b0058284d60e2fc3730ce0f979031d162a", size = 1514525, upload-time = "2025-04-04T12:03:16.246Z" }, + { url = "https://files.pythonhosted.org/packages/48/2a/a7d81873fff0645eb60afaec2b7c78a85a377af8f1d911aff045d8955bc7/pyzmq-26.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7d489ac234d38e57f458fdbd12a996bfe990ac028feaf6f3c1e81ff766513d3b", size = 1414659, upload-time = "2025-04-04T12:03:17.652Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ea/813af9c42ae21845c1ccfe495bd29c067622a621e85d7cda6bc437de8101/pyzmq-26.4.0-cp310-cp310-win32.whl", hash = "sha256:dea1c8db78fb1b4b7dc9f8e213d0af3fc8ecd2c51a1d5a3ca1cde1bda034a980", size = 580348, upload-time = "2025-04-04T12:03:19.384Z" }, + { url = "https://files.pythonhosted.org/packages/20/68/318666a89a565252c81d3fed7f3b4c54bd80fd55c6095988dfa2cd04a62b/pyzmq-26.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:fa59e1f5a224b5e04dc6c101d7186058efa68288c2d714aa12d27603ae93318b", size = 643838, upload-time = "2025-04-04T12:03:20.795Z" }, + { url = "https://files.pythonhosted.org/packages/91/f8/fb1a15b5f4ecd3e588bfde40c17d32ed84b735195b5c7d1d7ce88301a16f/pyzmq-26.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:a651fe2f447672f4a815e22e74630b6b1ec3a1ab670c95e5e5e28dcd4e69bbb5", size = 559565, upload-time = "2025-04-04T12:03:22.676Z" }, + { url = "https://files.pythonhosted.org/packages/32/6d/234e3b0aa82fd0290b1896e9992f56bdddf1f97266110be54d0177a9d2d9/pyzmq-26.4.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:bfcf82644c9b45ddd7cd2a041f3ff8dce4a0904429b74d73a439e8cab1bd9e54", size = 1339723, upload-time = "2025-04-04T12:03:24.358Z" }, + { url = "https://files.pythonhosted.org/packages/4f/11/6d561efe29ad83f7149a7cd48e498e539ed09019c6cd7ecc73f4cc725028/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9bcae3979b2654d5289d3490742378b2f3ce804b0b5fd42036074e2bf35b030", size = 672645, upload-time = "2025-04-04T12:03:25.693Z" }, + { url = "https://files.pythonhosted.org/packages/19/fd/81bfe3e23f418644660bad1a90f0d22f0b3eebe33dd65a79385530bceb3d/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccdff8ac4246b6fb60dcf3982dfaeeff5dd04f36051fe0632748fc0aa0679c01", size = 910133, upload-time = "2025-04-04T12:03:27.625Z" }, + { url = "https://files.pythonhosted.org/packages/97/68/321b9c775595ea3df832a9516252b653fe32818db66fdc8fa31c9b9fce37/pyzmq-26.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4550af385b442dc2d55ab7717837812799d3674cb12f9a3aa897611839c18e9e", size = 867428, upload-time = "2025-04-04T12:03:29.004Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6e/159cbf2055ef36aa2aa297e01b24523176e5b48ead283c23a94179fb2ba2/pyzmq-26.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f7ffe9db1187a253fca95191854b3fda24696f086e8789d1d449308a34b88", size = 862409, upload-time = "2025-04-04T12:03:31.032Z" }, + { url = "https://files.pythonhosted.org/packages/05/1c/45fb8db7be5a7d0cadea1070a9cbded5199a2d578de2208197e592f219bd/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3709c9ff7ba61589b7372923fd82b99a81932b592a5c7f1a24147c91da9a68d6", size = 1205007, upload-time = "2025-04-04T12:03:32.687Z" }, + { url = "https://files.pythonhosted.org/packages/f8/fa/658c7f583af6498b463f2fa600f34e298e1b330886f82f1feba0dc2dd6c3/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f8f3c30fb2d26ae5ce36b59768ba60fb72507ea9efc72f8f69fa088450cff1df", size = 1514599, upload-time = "2025-04-04T12:03:34.084Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d7/44d641522353ce0a2bbd150379cb5ec32f7120944e6bfba4846586945658/pyzmq-26.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:382a4a48c8080e273427fc692037e3f7d2851959ffe40864f2db32646eeb3cef", size = 1414546, upload-time = "2025-04-04T12:03:35.478Z" }, + { url = "https://files.pythonhosted.org/packages/72/76/c8ed7263218b3d1e9bce07b9058502024188bd52cc0b0a267a9513b431fc/pyzmq-26.4.0-cp311-cp311-win32.whl", hash = "sha256:d56aad0517d4c09e3b4f15adebba8f6372c5102c27742a5bdbfc74a7dceb8fca", size = 579247, upload-time = "2025-04-04T12:03:36.846Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d0/2d9abfa2571a0b1a67c0ada79a8aa1ba1cce57992d80f771abcdf99bb32c/pyzmq-26.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:963977ac8baed7058c1e126014f3fe58b3773f45c78cce7af5c26c09b6823896", size = 644727, upload-time = "2025-04-04T12:03:38.578Z" }, + { url = "https://files.pythonhosted.org/packages/0d/d1/c8ad82393be6ccedfc3c9f3adb07f8f3976e3c4802640fe3f71441941e70/pyzmq-26.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0c8e8cadc81e44cc5088fcd53b9b3b4ce9344815f6c4a03aec653509296fae3", size = 559942, upload-time = "2025-04-04T12:03:40.143Z" }, + { url = "https://files.pythonhosted.org/packages/10/44/a778555ebfdf6c7fc00816aad12d185d10a74d975800341b1bc36bad1187/pyzmq-26.4.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:5227cb8da4b6f68acfd48d20c588197fd67745c278827d5238c707daf579227b", size = 1341586, upload-time = "2025-04-04T12:03:41.954Z" }, + { url = "https://files.pythonhosted.org/packages/9c/4f/f3a58dc69ac757e5103be3bd41fb78721a5e17da7cc617ddb56d973a365c/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1c07a7fa7f7ba86554a2b1bef198c9fed570c08ee062fd2fd6a4dcacd45f905", size = 665880, upload-time = "2025-04-04T12:03:43.45Z" }, + { url = "https://files.pythonhosted.org/packages/fe/45/50230bcfb3ae5cb98bee683b6edeba1919f2565d7cc1851d3c38e2260795/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae775fa83f52f52de73183f7ef5395186f7105d5ed65b1ae65ba27cb1260de2b", size = 902216, upload-time = "2025-04-04T12:03:45.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/59/56bbdc5689be5e13727491ad2ba5efd7cd564365750514f9bc8f212eef82/pyzmq-26.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c760d0226ebd52f1e6b644a9e839b5db1e107a23f2fcd46ec0569a4fdd4e63", size = 859814, upload-time = "2025-04-04T12:03:47.188Z" }, + { url = "https://files.pythonhosted.org/packages/81/b1/57db58cfc8af592ce94f40649bd1804369c05b2190e4cbc0a2dad572baeb/pyzmq-26.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ef8c6ecc1d520debc147173eaa3765d53f06cd8dbe7bd377064cdbc53ab456f5", size = 855889, upload-time = "2025-04-04T12:03:49.223Z" }, + { url = "https://files.pythonhosted.org/packages/e8/92/47542e629cbac8f221c230a6d0f38dd3d9cff9f6f589ed45fdf572ffd726/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3150ef4084e163dec29ae667b10d96aad309b668fac6810c9e8c27cf543d6e0b", size = 1197153, upload-time = "2025-04-04T12:03:50.591Z" }, + { url = "https://files.pythonhosted.org/packages/07/e5/b10a979d1d565d54410afc87499b16c96b4a181af46e7645ab4831b1088c/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4448c9e55bf8329fa1dcedd32f661bf611214fa70c8e02fee4347bc589d39a84", size = 1507352, upload-time = "2025-04-04T12:03:52.473Z" }, + { url = "https://files.pythonhosted.org/packages/ab/58/5a23db84507ab9c01c04b1232a7a763be66e992aa2e66498521bbbc72a71/pyzmq-26.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e07dde3647afb084d985310d067a3efa6efad0621ee10826f2cb2f9a31b89d2f", size = 1406834, upload-time = "2025-04-04T12:03:54Z" }, + { url = "https://files.pythonhosted.org/packages/22/74/aaa837b331580c13b79ac39396601fb361454ee184ca85e8861914769b99/pyzmq-26.4.0-cp312-cp312-win32.whl", hash = "sha256:ba034a32ecf9af72adfa5ee383ad0fd4f4e38cdb62b13624278ef768fe5b5b44", size = 577992, upload-time = "2025-04-04T12:03:55.815Z" }, + { url = "https://files.pythonhosted.org/packages/30/0f/55f8c02c182856743b82dde46b2dc3e314edda7f1098c12a8227eeda0833/pyzmq-26.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:056a97aab4064f526ecb32f4343917a4022a5d9efb6b9df990ff72e1879e40be", size = 640466, upload-time = "2025-04-04T12:03:57.231Z" }, + { url = "https://files.pythonhosted.org/packages/e4/29/073779afc3ef6f830b8de95026ef20b2d1ec22d0324d767748d806e57379/pyzmq-26.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f23c750e485ce1eb639dbd576d27d168595908aa2d60b149e2d9e34c9df40e0", size = 556342, upload-time = "2025-04-04T12:03:59.218Z" }, + { url = "https://files.pythonhosted.org/packages/d7/20/fb2c92542488db70f833b92893769a569458311a76474bda89dc4264bd18/pyzmq-26.4.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:c43fac689880f5174d6fc864857d1247fe5cfa22b09ed058a344ca92bf5301e3", size = 1339484, upload-time = "2025-04-04T12:04:00.671Z" }, + { url = "https://files.pythonhosted.org/packages/58/29/2f06b9cabda3a6ea2c10f43e67ded3e47fc25c54822e2506dfb8325155d4/pyzmq-26.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:902aca7eba477657c5fb81c808318460328758e8367ecdd1964b6330c73cae43", size = 666106, upload-time = "2025-04-04T12:04:02.366Z" }, + { url = "https://files.pythonhosted.org/packages/77/e4/dcf62bd29e5e190bd21bfccaa4f3386e01bf40d948c239239c2f1e726729/pyzmq-26.4.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5e48a830bfd152fe17fbdeaf99ac5271aa4122521bf0d275b6b24e52ef35eb6", size = 902056, upload-time = "2025-04-04T12:04:03.919Z" }, + { url = "https://files.pythonhosted.org/packages/1a/cf/b36b3d7aea236087d20189bec1a87eeb2b66009731d7055e5c65f845cdba/pyzmq-26.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31be2b6de98c824c06f5574331f805707c667dc8f60cb18580b7de078479891e", size = 860148, upload-time = "2025-04-04T12:04:05.581Z" }, + { url = "https://files.pythonhosted.org/packages/18/a6/f048826bc87528c208e90604c3bf573801e54bd91e390cbd2dfa860e82dc/pyzmq-26.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6332452034be001bbf3206ac59c0d2a7713de5f25bb38b06519fc6967b7cf771", size = 855983, upload-time = "2025-04-04T12:04:07.096Z" }, + { url = "https://files.pythonhosted.org/packages/0a/27/454d34ab6a1d9772a36add22f17f6b85baf7c16e14325fa29e7202ca8ee8/pyzmq-26.4.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:da8c0f5dd352136853e6a09b1b986ee5278dfddfebd30515e16eae425c872b30", size = 1197274, upload-time = "2025-04-04T12:04:08.523Z" }, + { url = "https://files.pythonhosted.org/packages/f4/3d/7abfeab6b83ad38aa34cbd57c6fc29752c391e3954fd12848bd8d2ec0df6/pyzmq-26.4.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f4ccc1a0a2c9806dda2a2dd118a3b7b681e448f3bb354056cad44a65169f6d86", size = 1507120, upload-time = "2025-04-04T12:04:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/13/ff/bc8d21dbb9bc8705126e875438a1969c4f77e03fc8565d6901c7933a3d01/pyzmq-26.4.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1c0b5fceadbab461578daf8d1dcc918ebe7ddd2952f748cf30c7cf2de5d51101", size = 1406738, upload-time = "2025-04-04T12:04:12.509Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5d/d4cd85b24de71d84d81229e3bbb13392b2698432cf8fdcea5afda253d587/pyzmq-26.4.0-cp313-cp313-win32.whl", hash = "sha256:28e2b0ff5ba4b3dd11062d905682bad33385cfa3cc03e81abd7f0822263e6637", size = 577826, upload-time = "2025-04-04T12:04:14.289Z" }, + { url = "https://files.pythonhosted.org/packages/c6/6c/f289c1789d7bb6e5a3b3bef7b2a55089b8561d17132be7d960d3ff33b14e/pyzmq-26.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:23ecc9d241004c10e8b4f49d12ac064cd7000e1643343944a10df98e57bc544b", size = 640406, upload-time = "2025-04-04T12:04:15.757Z" }, + { url = "https://files.pythonhosted.org/packages/b3/99/676b8851cb955eb5236a0c1e9ec679ea5ede092bf8bf2c8a68d7e965cac3/pyzmq-26.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:1edb0385c7f025045d6e0f759d4d3afe43c17a3d898914ec6582e6f464203c08", size = 556216, upload-time = "2025-04-04T12:04:17.212Z" }, + { url = "https://files.pythonhosted.org/packages/65/c2/1fac340de9d7df71efc59d9c50fc7a635a77b103392d1842898dd023afcb/pyzmq-26.4.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:93a29e882b2ba1db86ba5dd5e88e18e0ac6b627026c5cfbec9983422011b82d4", size = 1333769, upload-time = "2025-04-04T12:04:18.665Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c7/6c03637e8d742c3b00bec4f5e4cd9d1c01b2f3694c6f140742e93ca637ed/pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45684f276f57110bb89e4300c00f1233ca631f08f5f42528a5c408a79efc4a", size = 658826, upload-time = "2025-04-04T12:04:20.405Z" }, + { url = "https://files.pythonhosted.org/packages/a5/97/a8dca65913c0f78e0545af2bb5078aebfc142ca7d91cdaffa1fbc73e5dbd/pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f72073e75260cb301aad4258ad6150fa7f57c719b3f498cb91e31df16784d89b", size = 891650, upload-time = "2025-04-04T12:04:22.413Z" }, + { url = "https://files.pythonhosted.org/packages/7d/7e/f63af1031eb060bf02d033732b910fe48548dcfdbe9c785e9f74a6cc6ae4/pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be37e24b13026cfedd233bcbbccd8c0bcd2fdd186216094d095f60076201538d", size = 849776, upload-time = "2025-04-04T12:04:23.959Z" }, + { url = "https://files.pythonhosted.org/packages/f6/fa/1a009ce582802a895c0d5fe9413f029c940a0a8ee828657a3bb0acffd88b/pyzmq-26.4.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:237b283044934d26f1eeff4075f751b05d2f3ed42a257fc44386d00df6a270cf", size = 842516, upload-time = "2025-04-04T12:04:25.449Z" }, + { url = "https://files.pythonhosted.org/packages/6e/bc/f88b0bad0f7a7f500547d71e99f10336f2314e525d4ebf576a1ea4a1d903/pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:b30f862f6768b17040929a68432c8a8be77780317f45a353cb17e423127d250c", size = 1189183, upload-time = "2025-04-04T12:04:27.035Z" }, + { url = "https://files.pythonhosted.org/packages/d9/8c/db446a3dd9cf894406dec2e61eeffaa3c07c3abb783deaebb9812c4af6a5/pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:c80fcd3504232f13617c6ab501124d373e4895424e65de8b72042333316f64a8", size = 1495501, upload-time = "2025-04-04T12:04:28.833Z" }, + { url = "https://files.pythonhosted.org/packages/05/4c/bf3cad0d64c3214ac881299c4562b815f05d503bccc513e3fd4fdc6f67e4/pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:26a2a7451606b87f67cdeca2c2789d86f605da08b4bd616b1a9981605ca3a364", size = 1395540, upload-time = "2025-04-04T12:04:30.562Z" }, + { url = "https://files.pythonhosted.org/packages/06/91/21d3af57bc77e86e9d1e5384f256fd25cdb4c8eed4c45c8119da8120915f/pyzmq-26.4.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:a88643de8abd000ce99ca72056a1a2ae15881ee365ecb24dd1d9111e43d57842", size = 1340634, upload-time = "2025-04-04T12:04:47.661Z" }, + { url = "https://files.pythonhosted.org/packages/54/e6/58cd825023e998a0e49db7322b3211e6cf93f0796710b77d1496304c10d1/pyzmq-26.4.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a744ce209ecb557406fb928f3c8c55ce79b16c3eeb682da38ef5059a9af0848", size = 907880, upload-time = "2025-04-04T12:04:49.294Z" }, + { url = "https://files.pythonhosted.org/packages/72/83/619e44a766ef738cb7e8ed8e5a54565627801bdb027ca6dfb70762385617/pyzmq-26.4.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9434540f333332224ecb02ee6278b6c6f11ea1266b48526e73c903119b2f420f", size = 863003, upload-time = "2025-04-04T12:04:51Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6a/a59af31320598bdc63d2c5a3181d14a89673c2c794540678285482e8a342/pyzmq-26.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6c6f0a23e55cd38d27d4c89add963294ea091ebcb104d7fdab0f093bc5abb1c", size = 673432, upload-time = "2025-04-04T12:04:52.611Z" }, + { url = "https://files.pythonhosted.org/packages/29/ae/64dd6c18b08ce2cb009c60f11cf01c87f323acd80344d8b059c0304a7370/pyzmq-26.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6145df55dc2309f6ef72d70576dcd5aabb0fd373311613fe85a5e547c722b780", size = 1205221, upload-time = "2025-04-04T12:04:54.31Z" }, + { url = "https://files.pythonhosted.org/packages/d0/0b/c583ab750957b025244a66948831bc9ca486d11c820da4626caf6480ee1a/pyzmq-26.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2ea81823840ef8c56e5d2f9918e4d571236294fea4d1842b302aebffb9e40997", size = 1515299, upload-time = "2025-04-04T12:04:56.063Z" }, + { url = "https://files.pythonhosted.org/packages/22/ba/95ba76292c49dd9c6dff1f127b4867033020b708d101cba6e4fc5a3d166d/pyzmq-26.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc2abc385dc37835445abe206524fbc0c9e3fce87631dfaa90918a1ba8f425eb", size = 1415366, upload-time = "2025-04-04T12:04:58.241Z" }, + { url = "https://files.pythonhosted.org/packages/6e/65/51abe36169effda26ac7400ffac96f463e09dff40d344cdc2629d9a59162/pyzmq-26.4.0-cp39-cp39-win32.whl", hash = "sha256:41a2508fe7bed4c76b4cf55aacfb8733926f59d440d9ae2b81ee8220633b4d12", size = 580773, upload-time = "2025-04-04T12:04:59.786Z" }, + { url = "https://files.pythonhosted.org/packages/89/68/d9ac94086c63a0ed8d73e9e8aec54b39f481696698a5a939a7207629fb30/pyzmq-26.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:d4000e8255d6cbce38982e5622ebb90823f3409b7ffe8aeae4337ef7d6d2612a", size = 644340, upload-time = "2025-04-04T12:05:01.389Z" }, + { url = "https://files.pythonhosted.org/packages/dc/8f/66c261d657c1b0791ee5b372c90b1646b453adb581fcdc1dc5c94e5b03e3/pyzmq-26.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f6919d9c120488246bdc2a2f96662fa80d67b35bd6d66218f457e722b3ff64", size = 560075, upload-time = "2025-04-04T12:05:02.975Z" }, + { url = "https://files.pythonhosted.org/packages/47/03/96004704a84095f493be8d2b476641f5c967b269390173f85488a53c1c13/pyzmq-26.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:98d948288ce893a2edc5ec3c438fe8de2daa5bbbd6e2e865ec5f966e237084ba", size = 834408, upload-time = "2025-04-04T12:05:04.569Z" }, + { url = "https://files.pythonhosted.org/packages/e4/7f/68d8f3034a20505db7551cb2260248be28ca66d537a1ac9a257913d778e4/pyzmq-26.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9f34f5c9e0203ece706a1003f1492a56c06c0632d86cb77bcfe77b56aacf27b", size = 569580, upload-time = "2025-04-04T12:05:06.283Z" }, + { url = "https://files.pythonhosted.org/packages/9b/a6/2b0d6801ec33f2b2a19dd8d02e0a1e8701000fec72926e6787363567d30c/pyzmq-26.4.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80c9b48aef586ff8b698359ce22f9508937c799cc1d2c9c2f7c95996f2300c94", size = 798250, upload-time = "2025-04-04T12:05:07.88Z" }, + { url = "https://files.pythonhosted.org/packages/96/2a/0322b3437de977dcac8a755d6d7ce6ec5238de78e2e2d9353730b297cf12/pyzmq-26.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3f2a5b74009fd50b53b26f65daff23e9853e79aa86e0aa08a53a7628d92d44a", size = 756758, upload-time = "2025-04-04T12:05:09.483Z" }, + { url = "https://files.pythonhosted.org/packages/c2/33/43704f066369416d65549ccee366cc19153911bec0154da7c6b41fca7e78/pyzmq-26.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:61c5f93d7622d84cb3092d7f6398ffc77654c346545313a3737e266fc11a3beb", size = 555371, upload-time = "2025-04-04T12:05:11.062Z" }, + { url = "https://files.pythonhosted.org/packages/04/52/a70fcd5592715702248306d8e1729c10742c2eac44529984413b05c68658/pyzmq-26.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4478b14cb54a805088299c25a79f27eaf530564a7a4f72bf432a040042b554eb", size = 834405, upload-time = "2025-04-04T12:05:13.3Z" }, + { url = "https://files.pythonhosted.org/packages/25/f9/1a03f1accff16b3af1a6fa22cbf7ced074776abbf688b2e9cb4629700c62/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a28ac29c60e4ba84b5f58605ace8ad495414a724fe7aceb7cf06cd0598d04e1", size = 569578, upload-time = "2025-04-04T12:05:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/76/0c/3a633acd762aa6655fcb71fa841907eae0ab1e8582ff494b137266de341d/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b03c1ceea27c6520124f4fb2ba9c647409b9abdf9a62388117148a90419494", size = 798248, upload-time = "2025-04-04T12:05:17.376Z" }, + { url = "https://files.pythonhosted.org/packages/cd/cc/6c99c84aa60ac1cc56747bed6be8ce6305b9b861d7475772e7a25ce019d3/pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7731abd23a782851426d4e37deb2057bf9410848a4459b5ede4fe89342e687a9", size = 756757, upload-time = "2025-04-04T12:05:19.19Z" }, + { url = "https://files.pythonhosted.org/packages/13/9c/d8073bd898eb896e94c679abe82e47506e2b750eb261cf6010ced869797c/pyzmq-26.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a222ad02fbe80166b0526c038776e8042cd4e5f0dec1489a006a1df47e9040e0", size = 555371, upload-time = "2025-04-04T12:05:20.702Z" }, + { url = "https://files.pythonhosted.org/packages/af/b2/71a644b629e1a93ccae9e22a45aec9d23065dfcc24c399cb837f81cd08c2/pyzmq-26.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:552b0d2e39987733e1e9e948a0ced6ff75e0ea39ab1a1db2fc36eb60fd8760db", size = 834397, upload-time = "2025-04-04T12:05:31.217Z" }, + { url = "https://files.pythonhosted.org/packages/a9/dd/052a25651eaaff8f5fd652fb40a3abb400e71207db2d605cf6faf0eac598/pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd670a8aa843f2ee637039bbd412e0d7294a5e588e1ecc9ad98b0cdc050259a4", size = 569571, upload-time = "2025-04-04T12:05:32.877Z" }, + { url = "https://files.pythonhosted.org/packages/a5/5d/201ca10b5d12ab187a418352c06d70c3e2087310af038b11056aba1359be/pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d367b7b775a0e1e54a59a2ba3ed4d5e0a31566af97cc9154e34262777dab95ed", size = 798243, upload-time = "2025-04-04T12:05:34.91Z" }, + { url = "https://files.pythonhosted.org/packages/bd/d4/2c64e54749536ad1633400f28d71e71e19375d00ce1fe9bb1123364dc927/pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112af16c406e4a93df2caef49f884f4c2bb2b558b0b5577ef0b2465d15c1abc", size = 756751, upload-time = "2025-04-04T12:05:37.12Z" }, + { url = "https://files.pythonhosted.org/packages/08/e6/34d119af43d06a8dcd88bf7a62dac69597eaba52b49ecce76ff06b40f1fd/pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c76c298683f82669cab0b6da59071f55238c039738297c69f187a542c6d40099", size = 745400, upload-time = "2025-04-04T12:05:40.694Z" }, + { url = "https://files.pythonhosted.org/packages/f8/49/b5e471d74a63318e51f30d329b17d2550bdededaab55baed2e2499de7ce4/pyzmq-26.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:49b6ca2e625b46f499fb081aaf7819a177f41eeb555acb05758aa97f4f95d147", size = 555367, upload-time = "2025-04-04T12:05:42.356Z" }, +] + +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, +] + +[[package]] +name = "rfc3986-validator" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760, upload-time = "2019-10-28T16:00:19.144Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242, upload-time = "2019-10-28T16:00:13.976Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/a6/60184b7fc00dd3ca80ac635dd5b8577d444c57e8e8742cecabfacb829921/rpds_py-0.25.1.tar.gz", hash = "sha256:8960b6dac09b62dac26e75d7e2c4a22efb835d827a7278c34f72b2b84fa160e3", size = 27304, upload-time = "2025-05-21T12:46:12.502Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/09/e1158988e50905b7f8306487a576b52d32aa9a87f79f7ab24ee8db8b6c05/rpds_py-0.25.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f4ad628b5174d5315761b67f212774a32f5bad5e61396d38108bd801c0a8f5d9", size = 373140, upload-time = "2025-05-21T12:42:38.834Z" }, + { url = "https://files.pythonhosted.org/packages/e0/4b/a284321fb3c45c02fc74187171504702b2934bfe16abab89713eedfe672e/rpds_py-0.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c742af695f7525e559c16f1562cf2323db0e3f0fbdcabdf6865b095256b2d40", size = 358860, upload-time = "2025-05-21T12:42:41.394Z" }, + { url = "https://files.pythonhosted.org/packages/4e/46/8ac9811150c75edeae9fc6fa0e70376c19bc80f8e1f7716981433905912b/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:605ffe7769e24b1800b4d024d24034405d9404f0bc2f55b6db3362cd34145a6f", size = 386179, upload-time = "2025-05-21T12:42:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ec/87eb42d83e859bce91dcf763eb9f2ab117142a49c9c3d17285440edb5b69/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ccc6f3ddef93243538be76f8e47045b4aad7a66a212cd3a0f23e34469473d36b", size = 400282, upload-time = "2025-05-21T12:42:44.92Z" }, + { url = "https://files.pythonhosted.org/packages/68/c8/2a38e0707d7919c8c78e1d582ab15cf1255b380bcb086ca265b73ed6db23/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f70316f760174ca04492b5ab01be631a8ae30cadab1d1081035136ba12738cfa", size = 521824, upload-time = "2025-05-21T12:42:46.856Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2c/6a92790243569784dde84d144bfd12bd45102f4a1c897d76375076d730ab/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1dafef8df605fdb46edcc0bf1573dea0d6d7b01ba87f85cd04dc855b2b4479e", size = 411644, upload-time = "2025-05-21T12:42:48.838Z" }, + { url = "https://files.pythonhosted.org/packages/eb/76/66b523ffc84cf47db56efe13ae7cf368dee2bacdec9d89b9baca5e2e6301/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0701942049095741a8aeb298a31b203e735d1c61f4423511d2b1a41dcd8a16da", size = 386955, upload-time = "2025-05-21T12:42:50.835Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b9/a362d7522feaa24dc2b79847c6175daa1c642817f4a19dcd5c91d3e2c316/rpds_py-0.25.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e87798852ae0b37c88babb7f7bbbb3e3fecc562a1c340195b44c7e24d403e380", size = 421039, upload-time = "2025-05-21T12:42:52.348Z" }, + { url = "https://files.pythonhosted.org/packages/0f/c4/b5b6f70b4d719b6584716889fd3413102acf9729540ee76708d56a76fa97/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3bcce0edc1488906c2d4c75c94c70a0417e83920dd4c88fec1078c94843a6ce9", size = 563290, upload-time = "2025-05-21T12:42:54.404Z" }, + { url = "https://files.pythonhosted.org/packages/87/a3/2e6e816615c12a8f8662c9d8583a12eb54c52557521ef218cbe3095a8afa/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e2f6a2347d3440ae789505693a02836383426249d5293541cd712e07e7aecf54", size = 592089, upload-time = "2025-05-21T12:42:55.976Z" }, + { url = "https://files.pythonhosted.org/packages/c0/08/9b8e1050e36ce266135994e2c7ec06e1841f1c64da739daeb8afe9cb77a4/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4fd52d3455a0aa997734f3835cbc4c9f32571345143960e7d7ebfe7b5fbfa3b2", size = 558400, upload-time = "2025-05-21T12:42:58.032Z" }, + { url = "https://files.pythonhosted.org/packages/f2/df/b40b8215560b8584baccd839ff5c1056f3c57120d79ac41bd26df196da7e/rpds_py-0.25.1-cp310-cp310-win32.whl", hash = "sha256:3f0b1798cae2bbbc9b9db44ee068c556d4737911ad53a4e5093d09d04b3bbc24", size = 219741, upload-time = "2025-05-21T12:42:59.479Z" }, + { url = "https://files.pythonhosted.org/packages/10/99/e4c58be18cf5d8b40b8acb4122bc895486230b08f978831b16a3916bd24d/rpds_py-0.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:3ebd879ab996537fc510a2be58c59915b5dd63bccb06d1ef514fee787e05984a", size = 231553, upload-time = "2025-05-21T12:43:01.425Z" }, + { url = "https://files.pythonhosted.org/packages/95/e1/df13fe3ddbbea43567e07437f097863b20c99318ae1f58a0fe389f763738/rpds_py-0.25.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5f048bbf18b1f9120685c6d6bb70cc1a52c8cc11bdd04e643d28d3be0baf666d", size = 373341, upload-time = "2025-05-21T12:43:02.978Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/deef4d30fcbcbfef3b6d82d17c64490d5c94585a2310544ce8e2d3024f83/rpds_py-0.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fbb0dbba559959fcb5d0735a0f87cdbca9e95dac87982e9b95c0f8f7ad10255", size = 359111, upload-time = "2025-05-21T12:43:05.128Z" }, + { url = "https://files.pythonhosted.org/packages/bb/7e/39f1f4431b03e96ebaf159e29a0f82a77259d8f38b2dd474721eb3a8ac9b/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4ca54b9cf9d80b4016a67a0193ebe0bcf29f6b0a96f09db942087e294d3d4c2", size = 386112, upload-time = "2025-05-21T12:43:07.13Z" }, + { url = "https://files.pythonhosted.org/packages/db/e7/847068a48d63aec2ae695a1646089620b3b03f8ccf9f02c122ebaf778f3c/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ee3e26eb83d39b886d2cb6e06ea701bba82ef30a0de044d34626ede51ec98b0", size = 400362, upload-time = "2025-05-21T12:43:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/3b/3d/9441d5db4343d0cee759a7ab4d67420a476cebb032081763de934719727b/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89706d0683c73a26f76a5315d893c051324d771196ae8b13e6ffa1ffaf5e574f", size = 522214, upload-time = "2025-05-21T12:43:10.694Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ec/2cc5b30d95f9f1a432c79c7a2f65d85e52812a8f6cbf8768724571710786/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2013ee878c76269c7b557a9a9c042335d732e89d482606990b70a839635feb7", size = 411491, upload-time = "2025-05-21T12:43:12.739Z" }, + { url = "https://files.pythonhosted.org/packages/dc/6c/44695c1f035077a017dd472b6a3253553780837af2fac9b6ac25f6a5cb4d/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45e484db65e5380804afbec784522de84fa95e6bb92ef1bd3325d33d13efaebd", size = 386978, upload-time = "2025-05-21T12:43:14.25Z" }, + { url = "https://files.pythonhosted.org/packages/b1/74/b4357090bb1096db5392157b4e7ed8bb2417dc7799200fcbaee633a032c9/rpds_py-0.25.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:48d64155d02127c249695abb87d39f0faf410733428d499867606be138161d65", size = 420662, upload-time = "2025-05-21T12:43:15.8Z" }, + { url = "https://files.pythonhosted.org/packages/26/dd/8cadbebf47b96e59dfe8b35868e5c38a42272699324e95ed522da09d3a40/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:048893e902132fd6548a2e661fb38bf4896a89eea95ac5816cf443524a85556f", size = 563385, upload-time = "2025-05-21T12:43:17.78Z" }, + { url = "https://files.pythonhosted.org/packages/c3/ea/92960bb7f0e7a57a5ab233662f12152085c7dc0d5468534c65991a3d48c9/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0317177b1e8691ab5879f4f33f4b6dc55ad3b344399e23df2e499de7b10a548d", size = 592047, upload-time = "2025-05-21T12:43:19.457Z" }, + { url = "https://files.pythonhosted.org/packages/61/ad/71aabc93df0d05dabcb4b0c749277881f8e74548582d96aa1bf24379493a/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bffcf57826d77a4151962bf1701374e0fc87f536e56ec46f1abdd6a903354042", size = 557863, upload-time = "2025-05-21T12:43:21.69Z" }, + { url = "https://files.pythonhosted.org/packages/93/0f/89df0067c41f122b90b76f3660028a466eb287cbe38efec3ea70e637ca78/rpds_py-0.25.1-cp311-cp311-win32.whl", hash = "sha256:cda776f1967cb304816173b30994faaf2fd5bcb37e73118a47964a02c348e1bc", size = 219627, upload-time = "2025-05-21T12:43:23.311Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8d/93b1a4c1baa903d0229374d9e7aa3466d751f1d65e268c52e6039c6e338e/rpds_py-0.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:dc3c1ff0abc91444cd20ec643d0f805df9a3661fcacf9c95000329f3ddf268a4", size = 231603, upload-time = "2025-05-21T12:43:25.145Z" }, + { url = "https://files.pythonhosted.org/packages/cb/11/392605e5247bead2f23e6888e77229fbd714ac241ebbebb39a1e822c8815/rpds_py-0.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:5a3ddb74b0985c4387719fc536faced33cadf2172769540c62e2a94b7b9be1c4", size = 223967, upload-time = "2025-05-21T12:43:26.566Z" }, + { url = "https://files.pythonhosted.org/packages/7f/81/28ab0408391b1dc57393653b6a0cf2014cc282cc2909e4615e63e58262be/rpds_py-0.25.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5ffe453cde61f73fea9430223c81d29e2fbf412a6073951102146c84e19e34c", size = 364647, upload-time = "2025-05-21T12:43:28.559Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9a/7797f04cad0d5e56310e1238434f71fc6939d0bc517192a18bb99a72a95f/rpds_py-0.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:115874ae5e2fdcfc16b2aedc95b5eef4aebe91b28e7e21951eda8a5dc0d3461b", size = 350454, upload-time = "2025-05-21T12:43:30.615Z" }, + { url = "https://files.pythonhosted.org/packages/69/3c/93d2ef941b04898011e5d6eaa56a1acf46a3b4c9f4b3ad1bbcbafa0bee1f/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a714bf6e5e81b0e570d01f56e0c89c6375101b8463999ead3a93a5d2a4af91fa", size = 389665, upload-time = "2025-05-21T12:43:32.629Z" }, + { url = "https://files.pythonhosted.org/packages/c1/57/ad0e31e928751dde8903a11102559628d24173428a0f85e25e187defb2c1/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:35634369325906bcd01577da4c19e3b9541a15e99f31e91a02d010816b49bfda", size = 403873, upload-time = "2025-05-21T12:43:34.576Z" }, + { url = "https://files.pythonhosted.org/packages/16/ad/c0c652fa9bba778b4f54980a02962748479dc09632e1fd34e5282cf2556c/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4cb2b3ddc16710548801c6fcc0cfcdeeff9dafbc983f77265877793f2660309", size = 525866, upload-time = "2025-05-21T12:43:36.123Z" }, + { url = "https://files.pythonhosted.org/packages/2a/39/3e1839bc527e6fcf48d5fec4770070f872cdee6c6fbc9b259932f4e88a38/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9ceca1cf097ed77e1a51f1dbc8d174d10cb5931c188a4505ff9f3e119dfe519b", size = 416886, upload-time = "2025-05-21T12:43:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/7a/95/dd6b91cd4560da41df9d7030a038298a67d24f8ca38e150562644c829c48/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c2cd1a4b0c2b8c5e31ffff50d09f39906fe351389ba143c195566056c13a7ea", size = 390666, upload-time = "2025-05-21T12:43:40.065Z" }, + { url = "https://files.pythonhosted.org/packages/64/48/1be88a820e7494ce0a15c2d390ccb7c52212370badabf128e6a7bb4cb802/rpds_py-0.25.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de336a4b164c9188cb23f3703adb74a7623ab32d20090d0e9bf499a2203ad65", size = 425109, upload-time = "2025-05-21T12:43:42.263Z" }, + { url = "https://files.pythonhosted.org/packages/cf/07/3e2a17927ef6d7720b9949ec1b37d1e963b829ad0387f7af18d923d5cfa5/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9fca84a15333e925dd59ce01da0ffe2ffe0d6e5d29a9eeba2148916d1824948c", size = 567244, upload-time = "2025-05-21T12:43:43.846Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e5/76cf010998deccc4f95305d827847e2eae9c568099c06b405cf96384762b/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88ec04afe0c59fa64e2f6ea0dd9657e04fc83e38de90f6de201954b4d4eb59bd", size = 596023, upload-time = "2025-05-21T12:43:45.932Z" }, + { url = "https://files.pythonhosted.org/packages/52/9a/df55efd84403736ba37a5a6377b70aad0fd1cb469a9109ee8a1e21299a1c/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8bd2f19e312ce3e1d2c635618e8a8d8132892bb746a7cf74780a489f0f6cdcb", size = 561634, upload-time = "2025-05-21T12:43:48.263Z" }, + { url = "https://files.pythonhosted.org/packages/ab/aa/dc3620dd8db84454aaf9374bd318f1aa02578bba5e567f5bf6b79492aca4/rpds_py-0.25.1-cp312-cp312-win32.whl", hash = "sha256:e5e2f7280d8d0d3ef06f3ec1b4fd598d386cc6f0721e54f09109a8132182fbfe", size = 222713, upload-time = "2025-05-21T12:43:49.897Z" }, + { url = "https://files.pythonhosted.org/packages/a3/7f/7cef485269a50ed5b4e9bae145f512d2a111ca638ae70cc101f661b4defd/rpds_py-0.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:db58483f71c5db67d643857404da360dce3573031586034b7d59f245144cc192", size = 235280, upload-time = "2025-05-21T12:43:51.893Z" }, + { url = "https://files.pythonhosted.org/packages/99/f2/c2d64f6564f32af913bf5f3f7ae41c7c263c5ae4c4e8f1a17af8af66cd46/rpds_py-0.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:6d50841c425d16faf3206ddbba44c21aa3310a0cebc3c1cdfc3e3f4f9f6f5728", size = 225399, upload-time = "2025-05-21T12:43:53.351Z" }, + { url = "https://files.pythonhosted.org/packages/2b/da/323848a2b62abe6a0fec16ebe199dc6889c5d0a332458da8985b2980dffe/rpds_py-0.25.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:659d87430a8c8c704d52d094f5ba6fa72ef13b4d385b7e542a08fc240cb4a559", size = 364498, upload-time = "2025-05-21T12:43:54.841Z" }, + { url = "https://files.pythonhosted.org/packages/1f/b4/4d3820f731c80fd0cd823b3e95b9963fec681ae45ba35b5281a42382c67d/rpds_py-0.25.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68f6f060f0bbdfb0245267da014d3a6da9be127fe3e8cc4a68c6f833f8a23bb1", size = 350083, upload-time = "2025-05-21T12:43:56.428Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b1/3a8ee1c9d480e8493619a437dec685d005f706b69253286f50f498cbdbcf/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:083a9513a33e0b92cf6e7a6366036c6bb43ea595332c1ab5c8ae329e4bcc0a9c", size = 389023, upload-time = "2025-05-21T12:43:57.995Z" }, + { url = "https://files.pythonhosted.org/packages/3b/31/17293edcfc934dc62c3bf74a0cb449ecd549531f956b72287203e6880b87/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:816568614ecb22b18a010c7a12559c19f6fe993526af88e95a76d5a60b8b75fb", size = 403283, upload-time = "2025-05-21T12:43:59.546Z" }, + { url = "https://files.pythonhosted.org/packages/d1/ca/e0f0bc1a75a8925024f343258c8ecbd8828f8997ea2ac71e02f67b6f5299/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c6564c0947a7f52e4792983f8e6cf9bac140438ebf81f527a21d944f2fd0a40", size = 524634, upload-time = "2025-05-21T12:44:01.087Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/5d0be919037178fff33a6672ffc0afa04ea1cfcb61afd4119d1b5280ff0f/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c4a128527fe415d73cf1f70a9a688d06130d5810be69f3b553bf7b45e8acf79", size = 416233, upload-time = "2025-05-21T12:44:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/05/7c/8abb70f9017a231c6c961a8941403ed6557664c0913e1bf413cbdc039e75/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e1d7a4978ed554f095430b89ecc23f42014a50ac385eb0c4d163ce213c325", size = 390375, upload-time = "2025-05-21T12:44:04.162Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ac/a87f339f0e066b9535074a9f403b9313fd3892d4a164d5d5f5875ac9f29f/rpds_py-0.25.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d74ec9bc0e2feb81d3f16946b005748119c0f52a153f6db6a29e8cd68636f295", size = 424537, upload-time = "2025-05-21T12:44:06.175Z" }, + { url = "https://files.pythonhosted.org/packages/1f/8f/8d5c1567eaf8c8afe98a838dd24de5013ce6e8f53a01bd47fe8bb06b5533/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3af5b4cc10fa41e5bc64e5c198a1b2d2864337f8fcbb9a67e747e34002ce812b", size = 566425, upload-time = "2025-05-21T12:44:08.242Z" }, + { url = "https://files.pythonhosted.org/packages/95/33/03016a6be5663b389c8ab0bbbcca68d9e96af14faeff0a04affcb587e776/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79dc317a5f1c51fd9c6a0c4f48209c6b8526d0524a6904fc1076476e79b00f98", size = 595197, upload-time = "2025-05-21T12:44:10.449Z" }, + { url = "https://files.pythonhosted.org/packages/33/8d/da9f4d3e208c82fda311bff0cf0a19579afceb77cf456e46c559a1c075ba/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1521031351865e0181bc585147624d66b3b00a84109b57fcb7a779c3ec3772cd", size = 561244, upload-time = "2025-05-21T12:44:12.387Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b3/39d5dcf7c5f742ecd6dbc88f6f84ae54184b92f5f387a4053be2107b17f1/rpds_py-0.25.1-cp313-cp313-win32.whl", hash = "sha256:5d473be2b13600b93a5675d78f59e63b51b1ba2d0476893415dfbb5477e65b31", size = 222254, upload-time = "2025-05-21T12:44:14.261Z" }, + { url = "https://files.pythonhosted.org/packages/5f/19/2d6772c8eeb8302c5f834e6d0dfd83935a884e7c5ce16340c7eaf89ce925/rpds_py-0.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7b74e92a3b212390bdce1d93da9f6488c3878c1d434c5e751cbc202c5e09500", size = 234741, upload-time = "2025-05-21T12:44:16.236Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/145ada26cfaf86018d0eb304fe55eafdd4f0b6b84530246bb4a7c4fb5c4b/rpds_py-0.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:dd326a81afe332ede08eb39ab75b301d5676802cdffd3a8f287a5f0b694dc3f5", size = 224830, upload-time = "2025-05-21T12:44:17.749Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ca/d435844829c384fd2c22754ff65889c5c556a675d2ed9eb0e148435c6690/rpds_py-0.25.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a58d1ed49a94d4183483a3ce0af22f20318d4a1434acee255d683ad90bf78129", size = 359668, upload-time = "2025-05-21T12:44:19.322Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/b056f21db3a09f89410d493d2f6614d87bb162499f98b649d1dbd2a81988/rpds_py-0.25.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f251bf23deb8332823aef1da169d5d89fa84c89f67bdfb566c49dea1fccfd50d", size = 345649, upload-time = "2025-05-21T12:44:20.962Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0f/e0d00dc991e3d40e03ca36383b44995126c36b3eafa0ccbbd19664709c88/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dbd586bfa270c1103ece2109314dd423df1fa3d9719928b5d09e4840cec0d72", size = 384776, upload-time = "2025-05-21T12:44:22.516Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a2/59374837f105f2ca79bde3c3cd1065b2f8c01678900924949f6392eab66d/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d273f136e912aa101a9274c3145dcbddbe4bac560e77e6d5b3c9f6e0ed06d34", size = 395131, upload-time = "2025-05-21T12:44:24.147Z" }, + { url = "https://files.pythonhosted.org/packages/9c/dc/48e8d84887627a0fe0bac53f0b4631e90976fd5d35fff8be66b8e4f3916b/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:666fa7b1bd0a3810a7f18f6d3a25ccd8866291fbbc3c9b912b917a6715874bb9", size = 520942, upload-time = "2025-05-21T12:44:25.915Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f5/ee056966aeae401913d37befeeab57a4a43a4f00099e0a20297f17b8f00c/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:921954d7fbf3fccc7de8f717799304b14b6d9a45bbeec5a8d7408ccbf531faf5", size = 411330, upload-time = "2025-05-21T12:44:27.638Z" }, + { url = "https://files.pythonhosted.org/packages/ab/74/b2cffb46a097cefe5d17f94ede7a174184b9d158a0aeb195f39f2c0361e8/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d86373ff19ca0441ebeb696ef64cb58b8b5cbacffcda5a0ec2f3911732a194", size = 387339, upload-time = "2025-05-21T12:44:29.292Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9a/0ff0b375dcb5161c2b7054e7d0b7575f1680127505945f5cabaac890bc07/rpds_py-0.25.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c8980cde3bb8575e7c956a530f2c217c1d6aac453474bf3ea0f9c89868b531b6", size = 418077, upload-time = "2025-05-21T12:44:30.877Z" }, + { url = "https://files.pythonhosted.org/packages/0d/a1/fda629bf20d6b698ae84c7c840cfb0e9e4200f664fc96e1f456f00e4ad6e/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8eb8c84ecea987a2523e057c0d950bcb3f789696c0499290b8d7b3107a719d78", size = 562441, upload-time = "2025-05-21T12:44:32.541Z" }, + { url = "https://files.pythonhosted.org/packages/20/15/ce4b5257f654132f326f4acd87268e1006cc071e2c59794c5bdf4bebbb51/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e43a005671a9ed5a650f3bc39e4dbccd6d4326b24fb5ea8be5f3a43a6f576c72", size = 590750, upload-time = "2025-05-21T12:44:34.557Z" }, + { url = "https://files.pythonhosted.org/packages/fb/ab/e04bf58a8d375aeedb5268edcc835c6a660ebf79d4384d8e0889439448b0/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58f77c60956501a4a627749a6dcb78dac522f249dd96b5c9f1c6af29bfacfb66", size = 558891, upload-time = "2025-05-21T12:44:37.358Z" }, + { url = "https://files.pythonhosted.org/packages/90/82/cb8c6028a6ef6cd2b7991e2e4ced01c854b6236ecf51e81b64b569c43d73/rpds_py-0.25.1-cp313-cp313t-win32.whl", hash = "sha256:2cb9e5b5e26fc02c8a4345048cd9998c2aca7c2712bd1b36da0c72ee969a3523", size = 218718, upload-time = "2025-05-21T12:44:38.969Z" }, + { url = "https://files.pythonhosted.org/packages/b6/97/5a4b59697111c89477d20ba8a44df9ca16b41e737fa569d5ae8bff99e650/rpds_py-0.25.1-cp313-cp313t-win_amd64.whl", hash = "sha256:401ca1c4a20cc0510d3435d89c069fe0a9ae2ee6495135ac46bdd49ec0495763", size = 232218, upload-time = "2025-05-21T12:44:40.512Z" }, + { url = "https://files.pythonhosted.org/packages/89/74/716d42058ef501e2c08f27aa3ff455f6fc1bbbd19a6ab8dea07e6322d217/rpds_py-0.25.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ce4c8e485a3c59593f1a6f683cf0ea5ab1c1dc94d11eea5619e4fb5228b40fbd", size = 373475, upload-time = "2025-05-21T12:44:42.136Z" }, + { url = "https://files.pythonhosted.org/packages/e1/21/3faa9c523e2496a2505d7440b6f24c9166f37cb7ac027cac6cfbda9b4b5f/rpds_py-0.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8222acdb51a22929c3b2ddb236b69c59c72af4019d2cba961e2f9add9b6e634", size = 359349, upload-time = "2025-05-21T12:44:43.813Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1c/c747fe568d21b1d679079b52b926ebc4d1497457510a1773dc5fd4b7b4e2/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4593c4eae9b27d22df41cde518b4b9e4464d139e4322e2127daa9b5b981b76be", size = 386526, upload-time = "2025-05-21T12:44:45.452Z" }, + { url = "https://files.pythonhosted.org/packages/0b/cc/4a41703de4fb291f13660fa3d882cbd39db5d60497c6e7fa7f5142e5e69f/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd035756830c712b64725a76327ce80e82ed12ebab361d3a1cdc0f51ea21acb0", size = 400526, upload-time = "2025-05-21T12:44:47.011Z" }, + { url = "https://files.pythonhosted.org/packages/f1/78/60c980bedcad8418b614f0b4d6d420ecf11225b579cec0cb4e84d168b4da/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:114a07e85f32b125404f28f2ed0ba431685151c037a26032b213c882f26eb908", size = 525726, upload-time = "2025-05-21T12:44:48.838Z" }, + { url = "https://files.pythonhosted.org/packages/3f/37/f2f36b7f1314b3c3200d663decf2f8e29480492a39ab22447112aead4693/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dec21e02e6cc932538b5203d3a8bd6aa1480c98c4914cb88eea064ecdbc6396a", size = 412045, upload-time = "2025-05-21T12:44:50.433Z" }, + { url = "https://files.pythonhosted.org/packages/df/96/e03783e87a775b1242477ccbc35895f8e9b2bbdb60e199034a6da03c2687/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09eab132f41bf792c7a0ea1578e55df3f3e7f61888e340779b06050a9a3f16e9", size = 386953, upload-time = "2025-05-21T12:44:52.092Z" }, + { url = "https://files.pythonhosted.org/packages/7c/7d/1418f4b69bfb4b40481a3d84782113ad7d4cca0b38ae70b982dd5b20102a/rpds_py-0.25.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c98f126c4fc697b84c423e387337d5b07e4a61e9feac494362a59fd7a2d9ed80", size = 421144, upload-time = "2025-05-21T12:44:53.734Z" }, + { url = "https://files.pythonhosted.org/packages/b3/0e/61469912c6493ee3808012e60f4930344b974fcb6b35c4348e70b6be7bc7/rpds_py-0.25.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0e6a327af8ebf6baba1c10fadd04964c1965d375d318f4435d5f3f9651550f4a", size = 563730, upload-time = "2025-05-21T12:44:55.846Z" }, + { url = "https://files.pythonhosted.org/packages/f6/86/6d0a5cc56481ac61977b7c839677ed5c63d38cf0fcb3e2280843a8a6f476/rpds_py-0.25.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bc120d1132cff853ff617754196d0ac0ae63befe7c8498bd67731ba368abe451", size = 592321, upload-time = "2025-05-21T12:44:57.514Z" }, + { url = "https://files.pythonhosted.org/packages/5d/87/d1e2453fe336f71e6aa296452a8c85c2118b587b1d25ce98014f75838a60/rpds_py-0.25.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:140f61d9bed7839446bdd44852e30195c8e520f81329b4201ceead4d64eb3a9f", size = 558162, upload-time = "2025-05-21T12:44:59.564Z" }, + { url = "https://files.pythonhosted.org/packages/ad/92/349f04b1644c5cef3e2e6c53b7168a28531945f9e6fca7425f6d20ddbc3c/rpds_py-0.25.1-cp39-cp39-win32.whl", hash = "sha256:9c006f3aadeda131b438c3092124bd196b66312f0caa5823ef09585a669cf449", size = 219920, upload-time = "2025-05-21T12:45:01.186Z" }, + { url = "https://files.pythonhosted.org/packages/f2/84/3969bef883a3f37ff2213795257cb7b7e93a115829670befb8de0e003031/rpds_py-0.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:a61d0b2c7c9a0ae45732a77844917b427ff16ad5464b4d4f5e4adb955f582890", size = 231452, upload-time = "2025-05-21T12:45:02.85Z" }, + { url = "https://files.pythonhosted.org/packages/78/ff/566ce53529b12b4f10c0a348d316bd766970b7060b4fd50f888be3b3b281/rpds_py-0.25.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b24bf3cd93d5b6ecfbedec73b15f143596c88ee249fa98cefa9a9dc9d92c6f28", size = 373931, upload-time = "2025-05-21T12:45:05.01Z" }, + { url = "https://files.pythonhosted.org/packages/83/5d/deba18503f7c7878e26aa696e97f051175788e19d5336b3b0e76d3ef9256/rpds_py-0.25.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:0eb90e94f43e5085623932b68840b6f379f26db7b5c2e6bcef3179bd83c9330f", size = 359074, upload-time = "2025-05-21T12:45:06.714Z" }, + { url = "https://files.pythonhosted.org/packages/0d/74/313415c5627644eb114df49c56a27edba4d40cfd7c92bd90212b3604ca84/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d50e4864498a9ab639d6d8854b25e80642bd362ff104312d9770b05d66e5fb13", size = 387255, upload-time = "2025-05-21T12:45:08.669Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c8/c723298ed6338963d94e05c0f12793acc9b91d04ed7c4ba7508e534b7385/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c9409b47ba0650544b0bb3c188243b83654dfe55dcc173a86832314e1a6a35d", size = 400714, upload-time = "2025-05-21T12:45:10.39Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/51f1f6aa653c2e110ed482ef2ae94140d56c910378752a1b483af11019ee/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:796ad874c89127c91970652a4ee8b00d56368b7e00d3477f4415fe78164c8000", size = 523105, upload-time = "2025-05-21T12:45:12.273Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a4/7873d15c088ad3bff36910b29ceb0f178e4b3232c2adbe9198de68a41e63/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85608eb70a659bf4c1142b2781083d4b7c0c4e2c90eff11856a9754e965b2540", size = 411499, upload-time = "2025-05-21T12:45:13.95Z" }, + { url = "https://files.pythonhosted.org/packages/90/f3/0ce1437befe1410766d11d08239333ac1b2d940f8a64234ce48a7714669c/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4feb9211d15d9160bc85fa72fed46432cdc143eb9cf6d5ca377335a921ac37b", size = 387918, upload-time = "2025-05-21T12:45:15.649Z" }, + { url = "https://files.pythonhosted.org/packages/94/d4/5551247988b2a3566afb8a9dba3f1d4a3eea47793fd83000276c1a6c726e/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ccfa689b9246c48947d31dd9d8b16d89a0ecc8e0e26ea5253068efb6c542b76e", size = 421705, upload-time = "2025-05-21T12:45:17.788Z" }, + { url = "https://files.pythonhosted.org/packages/b0/25/5960f28f847bf736cc7ee3c545a7e1d2f3b5edaf82c96fb616c2f5ed52d0/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3c5b317ecbd8226887994852e85de562f7177add602514d4ac40f87de3ae45a8", size = 564489, upload-time = "2025-05-21T12:45:19.466Z" }, + { url = "https://files.pythonhosted.org/packages/02/66/1c99884a0d44e8c2904d3c4ec302f995292d5dde892c3bf7685ac1930146/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:454601988aab2c6e8fd49e7634c65476b2b919647626208e376afcd22019eeb8", size = 592557, upload-time = "2025-05-21T12:45:21.362Z" }, + { url = "https://files.pythonhosted.org/packages/55/ae/4aeac84ebeffeac14abb05b3bb1d2f728d00adb55d3fb7b51c9fa772e760/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:1c0c434a53714358532d13539272db75a5ed9df75a4a090a753ac7173ec14e11", size = 558691, upload-time = "2025-05-21T12:45:23.084Z" }, + { url = "https://files.pythonhosted.org/packages/41/b3/728a08ff6f5e06fe3bb9af2e770e9d5fd20141af45cff8dfc62da4b2d0b3/rpds_py-0.25.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f73ce1512e04fbe2bc97836e89830d6b4314c171587a99688082d090f934d20a", size = 231651, upload-time = "2025-05-21T12:45:24.72Z" }, + { url = "https://files.pythonhosted.org/packages/49/74/48f3df0715a585cbf5d34919c9c757a4c92c1a9eba059f2d334e72471f70/rpds_py-0.25.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ee86d81551ec68a5c25373c5643d343150cc54672b5e9a0cafc93c1870a53954", size = 374208, upload-time = "2025-05-21T12:45:26.306Z" }, + { url = "https://files.pythonhosted.org/packages/55/b0/9b01bb11ce01ec03d05e627249cc2c06039d6aa24ea5a22a39c312167c10/rpds_py-0.25.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89c24300cd4a8e4a51e55c31a8ff3918e6651b241ee8876a42cc2b2a078533ba", size = 359262, upload-time = "2025-05-21T12:45:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/a9/eb/5395621618f723ebd5116c53282052943a726dba111b49cd2071f785b665/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:771c16060ff4e79584dc48902a91ba79fd93eade3aa3a12d6d2a4aadaf7d542b", size = 387366, upload-time = "2025-05-21T12:45:30.42Z" }, + { url = "https://files.pythonhosted.org/packages/68/73/3d51442bdb246db619d75039a50ea1cf8b5b4ee250c3e5cd5c3af5981cd4/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:785ffacd0ee61c3e60bdfde93baa6d7c10d86f15655bd706c89da08068dc5038", size = 400759, upload-time = "2025-05-21T12:45:32.516Z" }, + { url = "https://files.pythonhosted.org/packages/b7/4c/3a32d5955d7e6cb117314597bc0f2224efc798428318b13073efe306512a/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a40046a529cc15cef88ac5ab589f83f739e2d332cb4d7399072242400ed68c9", size = 523128, upload-time = "2025-05-21T12:45:34.396Z" }, + { url = "https://files.pythonhosted.org/packages/be/95/1ffccd3b0bb901ae60b1dd4b1be2ab98bb4eb834cd9b15199888f5702f7b/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85fc223d9c76cabe5d0bff82214459189720dc135db45f9f66aa7cffbf9ff6c1", size = 411597, upload-time = "2025-05-21T12:45:36.164Z" }, + { url = "https://files.pythonhosted.org/packages/ef/6d/6e6cd310180689db8b0d2de7f7d1eabf3fb013f239e156ae0d5a1a85c27f/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0be9965f93c222fb9b4cc254235b3b2b215796c03ef5ee64f995b1b69af0762", size = 388053, upload-time = "2025-05-21T12:45:38.45Z" }, + { url = "https://files.pythonhosted.org/packages/4a/87/ec4186b1fe6365ced6fa470960e68fc7804bafbe7c0cf5a36237aa240efa/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8378fa4a940f3fb509c081e06cb7f7f2adae8cf46ef258b0e0ed7519facd573e", size = 421821, upload-time = "2025-05-21T12:45:40.732Z" }, + { url = "https://files.pythonhosted.org/packages/7a/60/84f821f6bf4e0e710acc5039d91f8f594fae0d93fc368704920d8971680d/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:33358883a4490287e67a2c391dfaea4d9359860281db3292b6886bf0be3d8692", size = 564534, upload-time = "2025-05-21T12:45:42.672Z" }, + { url = "https://files.pythonhosted.org/packages/41/3a/bc654eb15d3b38f9330fe0f545016ba154d89cdabc6177b0295910cd0ebe/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1d1fadd539298e70cac2f2cb36f5b8a65f742b9b9f1014dd4ea1f7785e2470bf", size = 592674, upload-time = "2025-05-21T12:45:44.533Z" }, + { url = "https://files.pythonhosted.org/packages/2e/ba/31239736f29e4dfc7a58a45955c5db852864c306131fd6320aea214d5437/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a46c2fb2545e21181445515960006e85d22025bd2fe6db23e76daec6eb689fe", size = 558781, upload-time = "2025-05-21T12:45:46.281Z" }, + { url = "https://files.pythonhosted.org/packages/78/b2/198266f070c6760e0e8cd00f9f2b9c86133ceebbe7c6d114bdcfea200180/rpds_py-0.25.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:50f2c501a89c9a5f4e454b126193c5495b9fb441a75b298c60591d8a2eb92e1b", size = 373973, upload-time = "2025-05-21T12:45:48.081Z" }, + { url = "https://files.pythonhosted.org/packages/13/79/1265eae618f88aa5d5e7122bd32dd41700bafe5a8bcea404e998848cd844/rpds_py-0.25.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d779b325cc8238227c47fbc53964c8cc9a941d5dbae87aa007a1f08f2f77b23", size = 359326, upload-time = "2025-05-21T12:45:49.825Z" }, + { url = "https://files.pythonhosted.org/packages/30/ab/6913b96f3ac072e87e76e45fe938263b0ab0d78b6b2cef3f2e56067befc0/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:036ded36bedb727beeabc16dc1dad7cb154b3fa444e936a03b67a86dc6a5066e", size = 387544, upload-time = "2025-05-21T12:45:51.764Z" }, + { url = "https://files.pythonhosted.org/packages/b0/23/129ed12d25229acc6deb8cbe90baadd8762e563c267c9594eb2fcc15be0c/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:245550f5a1ac98504147cba96ffec8fabc22b610742e9150138e5d60774686d7", size = 400240, upload-time = "2025-05-21T12:45:54.061Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e0/6811a38a5efa46b7ee6ed2103c95cb9abb16991544c3b69007aa679b6944/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff7c23ba0a88cb7b104281a99476cccadf29de2a0ef5ce864959a52675b1ca83", size = 525599, upload-time = "2025-05-21T12:45:56.457Z" }, + { url = "https://files.pythonhosted.org/packages/6c/10/2dc88bcaa0d86bdb59e017a330b1972ffeeb7f5061bb5a180c9a2bb73bbf/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e37caa8cdb3b7cf24786451a0bdb853f6347b8b92005eeb64225ae1db54d1c2b", size = 411154, upload-time = "2025-05-21T12:45:58.525Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d1/a72d522eb7d934fb33e9c501e6ecae00e2035af924d4ff37d964e9a3959b/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2f48ab00181600ee266a095fe815134eb456163f7d6699f525dee471f312cf", size = 388297, upload-time = "2025-05-21T12:46:00.264Z" }, + { url = "https://files.pythonhosted.org/packages/55/90/0dd7169ec74f042405b6b73512200d637a3088c156f64e1c07c18aa2fe59/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e5fc7484fa7dce57e25063b0ec9638ff02a908304f861d81ea49273e43838c1", size = 421894, upload-time = "2025-05-21T12:46:02.065Z" }, + { url = "https://files.pythonhosted.org/packages/37/e9/45170894add451783ed839c5c4a495e050aa8baa06d720364d9dff394dac/rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d3c10228d6cf6fe2b63d2e7985e94f6916fa46940df46b70449e9ff9297bd3d1", size = 564409, upload-time = "2025-05-21T12:46:03.891Z" }, + { url = "https://files.pythonhosted.org/packages/59/d0/31cece9090e76fbdb50c758c165d40da604b03b37c3ba53f010bbfeb130a/rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:5d9e40f32745db28c1ef7aad23f6fc458dc1e29945bd6781060f0d15628b8ddf", size = 592681, upload-time = "2025-05-21T12:46:06.009Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4c/22ef535efb2beec614ba7be83e62b439eb83b0b0d7b1775e22d35af3f9b5/rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:35a8d1a24b5936b35c5003313bc177403d8bdef0f8b24f28b1c4a255f94ea992", size = 558744, upload-time = "2025-05-21T12:46:07.78Z" }, + { url = "https://files.pythonhosted.org/packages/79/ff/f2150efc8daf0581d4dfaf0a2a30b08088b6df900230ee5ae4f7c8cd5163/rpds_py-0.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6099263f526efff9cf3883dfef505518730f7a7a93049b1d90d42e50a22b4793", size = 231305, upload-time = "2025-05-21T12:46:10.52Z" }, +] + +[[package]] +name = "semantic-version" +version = "2.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/31/f2289ce78b9b473d582568c234e104d2a342fd658cc288a7553d83bb8595/semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c", size = 52289, upload-time = "2022-05-26T13:35:23.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177", size = 15552, upload-time = "2022-05-26T13:35:21.206Z" }, +] + +[[package]] +name = "send2trash" +version = "1.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/3a/aec9b02217bb79b87bbc1a21bc6abc51e3d5dcf65c30487ac96c0908c722/Send2Trash-1.8.3.tar.gz", hash = "sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf", size = 17394, upload-time = "2024-04-07T00:01:09.267Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/b0/4562db6223154aa4e22f939003cb92514c79f3d4dccca3444253fd17f902/Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9", size = 18072, upload-time = "2024-04-07T00:01:07.438Z" }, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, +] + +[[package]] +name = "setuptools-rust" +version = "1.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "semantic-version" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/92/bf8589b1a2b6107cf9ec8daa9954c0b7620643fe1f37d31d75e572d995f5/setuptools_rust-1.11.1.tar.gz", hash = "sha256:7dabc4392252ced314b8050d63276e05fdc5d32398fc7d3cce1f6a6ac35b76c0", size = 310804, upload-time = "2025-04-04T14:28:10.576Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/01/37e1376f80578882e4f2d451f57d1fb42a599832057a123f57d9f26395c8/setuptools_rust-1.11.1-py3-none-any.whl", hash = "sha256:5eaaddaed268dc24a527ffa659ce56b22d3cf17b781247b779efd611031fe8ea", size = 28120, upload-time = "2025-04-04T14:28:09.564Z" }, +] + +[[package]] +name = "setuptools-scm" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "packaging" }, + { name = "setuptools" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/19/7ae64b70b2429c48c3a7a4ed36f50f94687d3bfcd0ae2f152367b6410dff/setuptools_scm-8.3.1.tar.gz", hash = "sha256:3d555e92b75dacd037d32bafdf94f97af51ea29ae8c7b234cf94b7a5bd242a63", size = 78088, upload-time = "2025-04-23T11:53:19.739Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/ac/8f96ba9b4cfe3e4ea201f23f4f97165862395e9331a424ed325ae37024a8/setuptools_scm-8.3.1-py3-none-any.whl", hash = "sha256:332ca0d43791b818b841213e76b1971b7711a960761c5bea5fc5cdb5196fbce3", size = 43935, upload-time = "2025-04-23T11:53:17.922Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "slotscheck" +version = "0.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4b/57/6fcb8df11e7c76eb87b23bfa931408e47f051c6161749c531b4060a45516/slotscheck-0.19.1.tar.gz", hash = "sha256:6146b7747f8db335a00a66b782f86011b74b995f61746dc5b36a9e77d5326013", size = 16050, upload-time = "2024-10-19T13:30:53.369Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/32/bd569256267f80b76b87d21a09795741a175778b954bee1d7b1a89852b6f/slotscheck-0.19.1-py3-none-any.whl", hash = "sha256:bff9926f8d6408ea21b6c6bbaa4389cea1682962e73ee4f30084b6d2b89260ee", size = 16995, upload-time = "2024-10-19T13:30:51.23Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "snowballstemmer" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, +] + +[[package]] +name = "sortedcontainers" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/c4/ba2f8066cceb6f23394729afe52f3bf7adec04bf9ed2c820b39e19299111/sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", size = 30594, upload-time = "2021-05-16T22:03:42.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" }, +] + +[[package]] +name = "soupsieve" +version = "2.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418, upload-time = "2025-04-20T18:50:08.518Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677, upload-time = "2025-04-20T18:50:07.196Z" }, +] + +[[package]] +name = "sphinx" +version = "7.4.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "alabaster", version = "0.7.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "babel", marker = "python_full_version < '3.10'" }, + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version < '3.10'" }, + { name = "imagesize", marker = "python_full_version < '3.10'" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jinja2", marker = "python_full_version < '3.10'" }, + { name = "packaging", marker = "python_full_version < '3.10'" }, + { name = "pygments", marker = "python_full_version < '3.10'" }, + { name = "requests", marker = "python_full_version < '3.10'" }, + { name = "snowballstemmer", marker = "python_full_version < '3.10'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.10'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.10'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.10'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.10'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.10'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.10'" }, + { name = "tomli", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911, upload-time = "2024-07-20T14:46:56.059Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/ef/153f6803c5d5f8917dbb7f7fcf6d34a871ede3296fa89c2c703f5f8a6c8e/sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239", size = 3401624, upload-time = "2024-07-20T14:46:52.142Z" }, +] + +[[package]] +name = "sphinx" +version = "8.1.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version >= '3.11' and python_full_version < '3.13'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "babel", marker = "python_full_version >= '3.10'" }, + { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version >= '3.10'" }, + { name = "imagesize", marker = "python_full_version >= '3.10'" }, + { name = "jinja2", marker = "python_full_version >= '3.10'" }, + { name = "packaging", marker = "python_full_version >= '3.10'" }, + { name = "pygments", marker = "python_full_version >= '3.10'" }, + { name = "requests", marker = "python_full_version >= '3.10'" }, + { name = "snowballstemmer", marker = "python_full_version >= '3.10'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.10'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.10'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.10'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.10'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.10'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.10'" }, + { name = "tomli", marker = "python_full_version == '3.10.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125, upload-time = "2024-10-13T20:27:10.448Z" }, +] + +[[package]] +name = "sphinx-rtd-theme" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinxcontrib-jquery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/44/c97faec644d29a5ceddd3020ae2edffa69e7d00054a8c7a6021e82f20335/sphinx_rtd_theme-3.0.2.tar.gz", hash = "sha256:b7457bc25dda723b20b086a670b9953c859eab60a2a03ee8eb2bb23e176e5f85", size = 7620463, upload-time = "2024-11-13T11:06:04.545Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/77/46e3bac77b82b4df5bb5b61f2de98637724f246b4966cfc34bc5895d852a/sphinx_rtd_theme-3.0.2-py2.py3-none-any.whl", hash = "sha256:422ccc750c3a3a311de4ae327e82affdaf59eb695ba4936538552f3b00f4ee13", size = 7655561, upload-time = "2024-11-13T11:06:02.094Z" }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, +] + +[[package]] +name = "sphinxcontrib-jquery" +version = "4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331, upload-time = "2023-03-14T15:01:01.944Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104, upload-time = "2023-03-14T15:01:00.356Z" }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, +] + +[[package]] +name = "stdlibs" +version = "2025.5.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/48/6f/92324b26048ff76b02dbb616d26b51a57e751bac7a7934016bb25a407725/stdlibs-2025.5.10.tar.gz", hash = "sha256:75d55a0b7b070ec44bd7dae5bc1ee1a6cea742122fb4253313cb4ab354f7f0c5", size = 19625, upload-time = "2025-05-11T03:46:42.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/51/a8f17bbb8f01cef657153972a99e382ce5c5e33a1a2df959f3ed2ebe2b89/stdlibs-2025.5.10-py3-none-any.whl", hash = "sha256:25178d9c2b45d2680292413bf59a20293355d45056ec92d32ea6ed349ce9e2a1", size = 57264, upload-time = "2025-05-11T03:46:41.633Z" }, +] + +[[package]] +name = "tabulate" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, +] + +[[package]] +name = "terminado" +version = "0.18.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess", marker = "os_name != 'nt'" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "tornado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701, upload-time = "2024-03-12T14:34:39.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154, upload-time = "2024-03-12T14:34:36.569Z" }, +] + +[[package]] +name = "testslide" +version = "2.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "psutil" }, + { name = "pygments" }, + { name = "typeguard" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ee/6f/c8d6d60a597c693559dab3b3362bd01e2212530e9a163eb0164af81e1ec1/TestSlide-2.7.1.tar.gz", hash = "sha256:d25890d5c383f673fac44a5f9e2561b7118d04f29f2c2b3d4f549e6db94cb34d", size = 50255, upload-time = "2023-03-16T14:09:41.204Z" } + +[[package]] +name = "tinycss2" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085, upload-time = "2024-10-24T14:58:29.895Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.13.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207, upload-time = "2025-06-05T07:13:44.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" }, +] + +[[package]] +name = "tornado" +version = "6.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/89/c72771c81d25d53fe33e3dca61c233b665b2780f21820ba6fd2c6793c12b/tornado-6.5.1.tar.gz", hash = "sha256:84ceece391e8eb9b2b95578db65e920d2a61070260594819589609ba9bc6308c", size = 509934, upload-time = "2025-05-22T18:15:38.788Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/89/f4532dee6843c9e0ebc4e28d4be04c67f54f60813e4bf73d595fe7567452/tornado-6.5.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d50065ba7fd11d3bd41bcad0825227cc9a95154bad83239357094c36708001f7", size = 441948, upload-time = "2025-05-22T18:15:20.862Z" }, + { url = "https://files.pythonhosted.org/packages/15/9a/557406b62cffa395d18772e0cdcf03bed2fff03b374677348eef9f6a3792/tornado-6.5.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9e9ca370f717997cb85606d074b0e5b247282cf5e2e1611568b8821afe0342d6", size = 440112, upload-time = "2025-05-22T18:15:22.591Z" }, + { url = "https://files.pythonhosted.org/packages/55/82/7721b7319013a3cf881f4dffa4f60ceff07b31b394e459984e7a36dc99ec/tornado-6.5.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b77e9dfa7ed69754a54c89d82ef746398be82f749df69c4d3abe75c4d1ff4888", size = 443672, upload-time = "2025-05-22T18:15:24.027Z" }, + { url = "https://files.pythonhosted.org/packages/7d/42/d11c4376e7d101171b94e03cef0cbce43e823ed6567ceda571f54cf6e3ce/tornado-6.5.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:253b76040ee3bab8bcf7ba9feb136436a3787208717a1fb9f2c16b744fba7331", size = 443019, upload-time = "2025-05-22T18:15:25.735Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f7/0c48ba992d875521ac761e6e04b0a1750f8150ae42ea26df1852d6a98942/tornado-6.5.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:308473f4cc5a76227157cdf904de33ac268af770b2c5f05ca6c1161d82fdd95e", size = 443252, upload-time = "2025-05-22T18:15:27.499Z" }, + { url = "https://files.pythonhosted.org/packages/89/46/d8d7413d11987e316df4ad42e16023cd62666a3c0dfa1518ffa30b8df06c/tornado-6.5.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:caec6314ce8a81cf69bd89909f4b633b9f523834dc1a352021775d45e51d9401", size = 443930, upload-time = "2025-05-22T18:15:29.299Z" }, + { url = "https://files.pythonhosted.org/packages/78/b2/f8049221c96a06df89bed68260e8ca94beca5ea532ffc63b1175ad31f9cc/tornado-6.5.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:13ce6e3396c24e2808774741331638ee6c2f50b114b97a55c5b442df65fd9692", size = 443351, upload-time = "2025-05-22T18:15:31.038Z" }, + { url = "https://files.pythonhosted.org/packages/76/ff/6a0079e65b326cc222a54720a748e04a4db246870c4da54ece4577bfa702/tornado-6.5.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5cae6145f4cdf5ab24744526cc0f55a17d76f02c98f4cff9daa08ae9a217448a", size = 443328, upload-time = "2025-05-22T18:15:32.426Z" }, + { url = "https://files.pythonhosted.org/packages/49/18/e3f902a1d21f14035b5bc6246a8c0f51e0eef562ace3a2cea403c1fb7021/tornado-6.5.1-cp39-abi3-win32.whl", hash = "sha256:e0a36e1bc684dca10b1aa75a31df8bdfed656831489bc1e6a6ebed05dc1ec365", size = 444396, upload-time = "2025-05-22T18:15:34.205Z" }, + { url = "https://files.pythonhosted.org/packages/7b/09/6526e32bf1049ee7de3bebba81572673b19a2a8541f795d887e92af1a8bc/tornado-6.5.1-cp39-abi3-win_amd64.whl", hash = "sha256:908e7d64567cecd4c2b458075589a775063453aeb1d2a1853eedb806922f568b", size = 444840, upload-time = "2025-05-22T18:15:36.1Z" }, + { url = "https://files.pythonhosted.org/packages/55/a7/535c44c7bea4578e48281d83c615219f3ab19e6abc67625ef637c73987be/tornado-6.5.1-cp39-abi3-win_arm64.whl", hash = "sha256:02420a0eb7bf617257b9935e2b754d1b63897525d8a289c9d65690d580b4dcf7", size = 443596, upload-time = "2025-05-22T18:15:37.433Z" }, +] + +[[package]] +name = "trailrunner" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pathspec" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/93/630e10bacd897daeb9ff5a408f4e7cb0fc2f243e7e3ef00f9e6cf319b11c/trailrunner-1.4.0.tar.gz", hash = "sha256:3fe61e259e6b2e5192f321c265985b7a0dc18497ced62b2da244f08104978398", size = 15836, upload-time = "2023-03-27T07:54:35.515Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/29/21001afea86bac5016c3940b43de3ce4786b0d8337d4ea79bb903c649ce3/trailrunner-1.4.0-py3-none-any.whl", hash = "sha256:a286d39f2723f28d167347f41cf8f232832648709366e722f55cf5545772a48e", size = 11071, upload-time = "2023-03-27T07:54:32.514Z" }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, +] + +[[package]] +name = "typeguard" +version = "2.13.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/38/c61bfcf62a7b572b5e9363a802ff92559cb427ee963048e1442e3aef7490/typeguard-2.13.3.tar.gz", hash = "sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4", size = 40604, upload-time = "2021-12-10T21:09:39.158Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/bb/d43e5c75054e53efce310e79d63df0ac3f25e34c926be5dffb7d283fb2a8/typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1", size = 17605, upload-time = "2021-12-10T21:09:37.844Z" }, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20250516" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ef/88/d65ed807393285204ab6e2801e5d11fbbea811adcaa979a2ed3b67a5ef41/types_python_dateutil-2.9.0.20250516.tar.gz", hash = "sha256:13e80d6c9c47df23ad773d54b2826bd52dbbb41be87c3f339381c1700ad21ee5", size = 13943, upload-time = "2025-05-16T03:06:58.385Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/3f/b0e8db149896005adc938a1e7f371d6d7e9eca4053a29b108978ed15e0c2/types_python_dateutil-2.9.0.20250516-py3-none-any.whl", hash = "sha256:2b2b3f57f9c6a61fba26a9c0ffb9ea5681c9b83e69cd897c6b5f668d9c0cab93", size = 14356, upload-time = "2025-05-16T03:06:57.249Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" }, +] + +[[package]] +name = "typing-inspect" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78", size = 13825, upload-time = "2023-05-24T20:25:47.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f", size = 8827, upload-time = "2023-05-24T20:25:45.287Z" }, +] + +[[package]] +name = "ufmt" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "black" }, + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "libcst" }, + { name = "moreorless" }, + { name = "tomlkit" }, + { name = "trailrunner" }, + { name = "typing-extensions" }, + { name = "usort" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/f8/c25e242a8e12062172dea4117859757a11339bbc39b1a3c7fb6a6de03bb2/ufmt-2.8.0.tar.gz", hash = "sha256:72c9502915497678de9aeab8aa18604890f14f869f7f378dd26e2878bde84f13", size = 24482, upload-time = "2024-10-25T06:21:57.239Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/4b/3f1b6f566b6cf70ccc5cba9a638fe4459f1e373c34d74df2e40e41871d70/ufmt-2.8.0-py3-none-any.whl", hash = "sha256:47a690811c576ebd3a0e30d77d43b65c84240e5c1611e5cb4a880bdd7f4507c1", size = 28268, upload-time = "2024-10-25T06:21:55.822Z" }, +] + +[[package]] +name = "uri-template" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678, upload-time = "2023-06-21T01:49:05.374Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140, upload-time = "2023-06-21T01:49:03.467Z" }, +] + +[[package]] +name = "urllib3" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, +] + +[[package]] +name = "usort" +version = "1.0.8.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "libcst" }, + { name = "moreorless" }, + { name = "stdlibs" }, + { name = "toml" }, + { name = "trailrunner" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/f4/3ef48b43f2645f2cb4a37d6007e611bc669af44eecfee953c5dd57433011/usort-1.0.8.post1.tar.gz", hash = "sha256:68def75f2b20b97390c552c503e071ee06c65ad502c5f94f3bd03f095cf4dfe6", size = 83215, upload-time = "2024-02-12T04:29:33.632Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/55/cc51ceb3d93763b9d28def24615bc485212525550967ce9e992a455f9ab5/usort-1.0.8.post1-py3-none-any.whl", hash = "sha256:6c57cdf17b458c79f8a61eb3ce8bf3f93e36d3c2edd602b9b2aa16b6875d3255", size = 37281, upload-time = "2024-02-12T04:29:31.693Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301, upload-time = "2024-01-06T02:10:57.829Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, +] + +[[package]] +name = "webcolors" +version = "24.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/29/061ec845fb58521848f3739e466efd8250b4b7b98c1b6c5bf4d40b419b7e/webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6", size = 45064, upload-time = "2024-11-11T07:43:24.224Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/e8/c0e05e4684d13459f93d312077a9a2efbe04d59c393bc2b8802248c908d4/webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9", size = 14934, upload-time = "2024-11-11T07:43:22.529Z" }, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, +] + +[[package]] +name = "websocket-client" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648, upload-time = "2024-04-23T22:16:16.976Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826, upload-time = "2024-04-23T22:16:14.422Z" }, +] + +[[package]] +name = "widgetsnbextension" +version = "4.0.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/53/2e0253c5efd69c9656b1843892052a31c36d37ad42812b5da45c62191f7e/widgetsnbextension-4.0.14.tar.gz", hash = "sha256:a3629b04e3edb893212df862038c7232f62973373869db5084aed739b437b5af", size = 1097428, upload-time = "2025-04-10T13:01:25.628Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/51/5447876806d1088a0f8f71e16542bf350918128d0a69437df26047c8e46f/widgetsnbextension-4.0.14-py3-none-any.whl", hash = "sha256:4875a9eaf72fbf5079dc372a51a9f268fc38d46f767cbf85c43a36da5cb9b575", size = 2196503, upload-time = "2025-04-10T13:01:23.086Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +] From 67ba746bed86e873065cf170c462894fcb367862 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Thu, 12 Jun 2025 18:57:20 +0800 Subject: [PATCH 121/151] fix(dependency): add back typing-extensions for 3.9 (#1358) Missing typing-extensions breaks "from libcst.codemod import CodemodContext" --- pyproject.toml | 1 + uv.lock | 2 ++ 2 files changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index b4b15320..6b78811e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ requires-python = ">=3.9" dependencies = [ "pyyaml>=5.2; python_version < '3.13'", "pyyaml-ft>=8.0.0; python_version >= '3.13'", + "typing-extensions; python_version < '3.10'", ] [project.urls] diff --git a/uv.lock b/uv.lock index ad68fefe..2cc4db06 100644 --- a/uv.lock +++ b/uv.lock @@ -1207,6 +1207,7 @@ source = { editable = "." } dependencies = [ { name = "pyyaml", marker = "python_full_version < '3.13'" }, { name = "pyyaml-ft", marker = "python_full_version >= '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] [package.dev-dependencies] @@ -1257,6 +1258,7 @@ docs = [ requires-dist = [ { name = "pyyaml", marker = "python_full_version < '3.13'", specifier = ">=5.2" }, { name = "pyyaml-ft", marker = "python_full_version >= '3.13'", specifier = ">=8.0.0" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] [package.metadata.requires-dev] From 03285dd4bf655d3bf4d89989dfde0cd885abefc3 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Fri, 13 Jun 2025 21:36:01 +0100 Subject: [PATCH 122/151] bump version to 1.8.2 (#1360) --- CHANGELOG.md | 10 ++++++++++ native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 4 ++-- native/libcst_derive/Cargo.toml | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 945d056d..3a8fb401 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 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 diff --git a/native/Cargo.lock b/native/Cargo.lock index 9501a525..c282c17f 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -286,7 +286,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libcst" -version = "1.8.1" +version = "1.8.2" dependencies = [ "annotate-snippets", "criterion", @@ -304,7 +304,7 @@ dependencies = [ [[package]] name = "libcst_derive" -version = "1.8.1" +version = "1.8.2" dependencies = [ "quote", "syn", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 2e0b7be3..1615a584 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "libcst" -version = "1.8.1" +version = "1.8.2" authors = ["LibCST Developers"] edition = "2018" rust-version = "1.70" @@ -42,7 +42,7 @@ peg = "0.8.5" annotate-snippets = "0.11.5" regex = "1.11.1" memchr = "2.7.4" -libcst_derive = { path = "../libcst_derive", version = "1.8.1" } +libcst_derive = { path = "../libcst_derive", version = "1.8.2" } [dev-dependencies] criterion = { version = "0.6.0", features = ["html_reports"] } diff --git a/native/libcst_derive/Cargo.toml b/native/libcst_derive/Cargo.toml index 566f74e2..f538e46e 100644 --- a/native/libcst_derive/Cargo.toml +++ b/native/libcst_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libcst_derive" -version = "1.8.1" +version = "1.8.2" edition = "2018" description = "Proc macro helpers for libcst." license = "MIT" From 287ab059a02bcaca00e77dceaaefc7c90954eb3b Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Sun, 15 Jun 2025 11:46:04 +0100 Subject: [PATCH 123/151] bump pyo3 to 0.25.1 (#1361) --- native/Cargo.lock | 20 ++++++++++---------- native/libcst/Cargo.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index c282c17f..ee5f0d57 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -443,9 +443,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f239d656363bcee73afef85277f1b281e8ac6212a1d42aa90e55b90ed43c47a4" +checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a" dependencies = [ "indoc", "libc", @@ -460,9 +460,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755ea671a1c34044fa165247aaf6f419ca39caa6003aee791a0df2713d8f1b6d" +checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598" dependencies = [ "once_cell", "target-lexicon", @@ -470,9 +470,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc95a2e67091e44791d4ea300ff744be5293f394f1bafd9f78c080814d35956e" +checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c" dependencies = [ "libc", "pyo3-build-config", @@ -480,9 +480,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a179641d1b93920829a62f15e87c0ed791b6c8db2271ba0fd7c2686090510214" +checksum = "a8725c0a622b374d6cb051d11a0983786448f7785336139c3c94f5aa6bef7e50" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -492,9 +492,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dff85ebcaab8c441b0e3f7ae40a6963ecea8a9f5e74f647e33fcf5ec9a1e89e" +checksum = "4109984c22491085343c05b0dbc54ddc405c3cf7b4374fc533f5c3313a572ccc" dependencies = [ "heck", "proc-macro2", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 1615a584..5b9f2f86 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -36,7 +36,7 @@ trace = ["peg/trace"] [dependencies] paste = "1.0.15" -pyo3 = { version = "0.25", optional = true } +pyo3 = { version = "0.25.1", optional = true } thiserror = "2.0.12" peg = "0.8.5" annotate-snippets = "0.11.5" From 4bc2116d2a6d4c48f263a1838c2b2d8c2d1dbea4 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Sun, 15 Jun 2025 12:39:36 +0100 Subject: [PATCH 124/151] ci: test built wheels (#1359) * bump uv version * bump cibuildwheel to v3 * enable GIL for smoke tests for now --- .github/workflows/build.yml | 2 +- .github/workflows/ci.yml | 12 ++++++++---- .github/workflows/pypi_upload.yml | 2 +- pyproject.toml | 6 ++++++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 921e871e..065ef68f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: if: github.event_name != 'release' run: echo CIBW_ENABLE=cpython-prerelease >> $GITHUB_ENV - name: Build wheels - uses: pypa/cibuildwheel@v3.0.0rc2 + uses: pypa/cibuildwheel@v3.0.0 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce969e24..5e176f65 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - version: "0.7.12" + version: "0.7.13" python-version: ${{ matrix.python-version }} - uses: actions/checkout@v4 with: @@ -60,7 +60,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - version: "0.7.12" + version: "0.7.13" python-version: "3.10" - run: uv run poe lint - run: uv run poe fixtures @@ -76,7 +76,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - version: "0.7.12" + version: "0.7.13" python-version: "3.10" - run: uv run poe typecheck @@ -91,7 +91,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - version: "0.7.12" + version: "0.7.13" python-version: "3.10" - uses: ts-graphviz/setup-graphviz@v2 - run: uv run --group docs poe docs @@ -143,3 +143,7 @@ jobs: - run: rustup component add rustfmt - 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 diff --git a/.github/workflows/pypi_upload.yml b/.github/workflows/pypi_upload.yml index 3a16857a..92145c1d 100644 --- a/.github/workflows/pypi_upload.yml +++ b/.github/workflows/pypi_upload.yml @@ -36,7 +36,7 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@v5 with: - version: "0.7.12" + version: "0.7.13" enable-cache: false - name: Build a source tarball env: diff --git a/pyproject.toml b/pyproject.toml index 6b78811e..3ebaaef1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -109,6 +109,12 @@ skip = [ "*-musllinux_armv7l", ] enable = ["cpython-freethreading"] +test-command = [ + "python --version", + "python -m libcst.tool list", + # TODO: remove the gil once thread-safety issues are resolved + "python -X gil=1 -m libcst.tool codemod remove_unused_imports.RemoveUnusedImportsCommand {project}/libcst/_nodes", +] [tool.cibuildwheel.linux] environment-pass = ["LIBCST_NO_LOCAL_SCHEME"] From 2fb4b2dd58537952b709f3730850b651c1dd4974 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 07:49:28 +0100 Subject: [PATCH 125/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/pypi_upload.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e176f65..5147f457 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: - "3.14t" steps: - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v6 with: version: "0.7.13" python-version: ${{ matrix.python-version }} @@ -58,7 +58,7 @@ jobs: fetch-depth: 0 persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v6 with: version: "0.7.13" python-version: "3.10" @@ -74,7 +74,7 @@ jobs: fetch-depth: 0 persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v6 with: version: "0.7.13" python-version: "3.10" @@ -89,7 +89,7 @@ jobs: fetch-depth: 0 persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v6 with: version: "0.7.13" python-version: "3.10" diff --git a/.github/workflows/pypi_upload.yml b/.github/workflows/pypi_upload.yml index 92145c1d..fd69d5fc 100644 --- a/.github/workflows/pypi_upload.yml +++ b/.github/workflows/pypi_upload.yml @@ -34,7 +34,7 @@ jobs: with: python-version: "3.10" - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v6 with: version: "0.7.13" enable-cache: false From 2931c86e07097ca33afe6269024e462bb996aaf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 16:44:33 -0400 Subject: [PATCH 126/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 065ef68f..bffe5fca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: if: github.event_name != 'release' run: echo CIBW_ENABLE=cpython-prerelease >> $GITHUB_ENV - name: Build wheels - uses: pypa/cibuildwheel@v3.0.0 + uses: pypa/cibuildwheel@v3.0.1 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From aa539604585d263033c777e78d01a438d13e17c9 Mon Sep 17 00:00:00 2001 From: Hunter Hogan Date: Tue, 15 Jul 2025 14:22:23 -0500 Subject: [PATCH 127/151] Fix typos in tutorial.ipynb (#1378) --- docs/source/tutorial.ipynb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/tutorial.ipynb b/docs/source/tutorial.ipynb index 3e6bdac0..1b1ad00d 100644 --- a/docs/source/tutorial.ipynb +++ b/docs/source/tutorial.ipynb @@ -10,7 +10,7 @@ "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 four-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. `Display The Source Code CST <#Display-Source-Code-CST>`_\n", @@ -19,7 +19,7 @@ "\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 ` for more detail)." + "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 ` for more detail)." ] }, { @@ -90,7 +90,7 @@ "|\n", "Example: add typing annotation from pyi stub file to Python source\n", "------------------------------------------------------------------\n", - "Python `typing annotation `_ 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 `_ 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." ] }, { @@ -106,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", @@ -134,7 +134,7 @@ "Build Visitor or Transformer\n", "============================\n", "For traversing and modifying the tree, LibCST provides Visitor and Transformer classes similar to the `ast module `_. 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 ` 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." ] }, { @@ -226,7 +226,7 @@ "|\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 `_ 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 `_ to reformat the code to keep a consistent coding style." ] }, { From 9542fc3882a7b16dc05a7248dcd10647f784f624 Mon Sep 17 00:00:00 2001 From: martin <48778384+drinkmorewaterr@users.noreply.github.com> Date: Wed, 30 Jul 2025 16:27:20 +0000 Subject: [PATCH 128/151] 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 --- .github/workflows/ci.yml | 9 +-- libcst/_nodes/tests/test_atom.py | 3 +- libcst/_nodes/tests/test_binary_op.py | 3 +- libcst/_nodes/tests/test_classdef.py | 3 - libcst/_nodes/tests/test_dict.py | 3 +- libcst/_nodes/tests/test_funcdef.py | 11 +--- libcst/_nodes/tests/test_list.py | 3 +- libcst/_nodes/tests/test_match.py | 7 +- libcst/_nodes/tests/test_matrix_multiply.py | 3 +- libcst/_nodes/tests/test_module.py | 4 +- libcst/_nodes/tests/test_set.py | 3 +- libcst/_nodes/tests/test_try.py | 7 +- libcst/_nodes/tests/test_tuple.py | 3 +- libcst/_nodes/tests/test_type_alias.py | 5 -- libcst/_nodes/tests/test_with.py | 14 ++-- libcst/_nodes/tests/test_yield.py | 3 +- libcst/_parser/entrypoints.py | 69 ++++---------------- libcst/_parser/tests/test_parse_errors.py | 3 - libcst/codemod/tests/test_codemod_cli.py | 15 ++--- libcst/metadata/tests/test_scope_provider.py | 15 ----- libcst/tests/__main__.py | 5 -- libcst/tests/test_roundtrip.py | 4 +- pyproject.toml | 1 + 23 files changed, 40 insertions(+), 156 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5147f457..4b0fa767 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,15 +39,8 @@ jobs: run: uv sync --locked --dev - name: Native Parser Tests run: uv run poe test - - name: Pure Parser Tests - env: - COVERAGE_FILE: .coverage.pure - LIBCST_PARSER_TYPE: pure - run: uv run poe test - name: Coverage - run: | - uv run coverage combine .coverage.pure - uv run coverage report + run: uv run coverage report # Run linters lint: diff --git a/libcst/_nodes/tests/test_atom.py b/libcst/_nodes/tests/test_atom.py index 82f7ab99..a33732c2 100644 --- a/libcst/_nodes/tests/test_atom.py +++ b/libcst/_nodes/tests/test_atom.py @@ -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) diff --git a/libcst/_nodes/tests/test_binary_op.py b/libcst/_nodes/tests/test_binary_op.py index b86af9fe..f6b40daf 100644 --- a/libcst/_nodes/tests/test_binary_op.py +++ b/libcst/_nodes/tests/test_binary_op.py @@ -8,7 +8,6 @@ from typing import Any import libcst as cst from libcst import parse_expression from libcst._nodes.tests.base import CSTNodeTest -from libcst._parser.entrypoints import is_native from libcst.metadata import CodeRange from libcst.testing.utils import data_provider @@ -189,4 +188,4 @@ class BinaryOperationTest(CSTNodeTest): ) ) def test_parse_error(self, **kwargs: Any) -> None: - self.assert_parses(**kwargs, expect_success=not is_native()) + self.assert_parses(**kwargs, expect_success=False) diff --git a/libcst/_nodes/tests/test_classdef.py b/libcst/_nodes/tests/test_classdef.py index cca36fbb..2e026a6c 100644 --- a/libcst/_nodes/tests/test_classdef.py +++ b/libcst/_nodes/tests/test_classdef.py @@ -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 -from libcst._parser.entrypoints import is_native from libcst.metadata import CodeRange from libcst.testing.utils import data_provider @@ -210,8 +209,6 @@ class ClassDefCreationTest(CSTNodeTest): ) ) def test_valid_native(self, **kwargs: Any) -> None: - if not is_native(): - self.skipTest("Disabled for pure python parser") self.validate_node(**kwargs) @data_provider( diff --git a/libcst/_nodes/tests/test_dict.py b/libcst/_nodes/tests/test_dict.py index 1ee33332..47cb0663 100644 --- a/libcst/_nodes/tests/test_dict.py +++ b/libcst/_nodes/tests/test_dict.py @@ -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) diff --git a/libcst/_nodes/tests/test_funcdef.py b/libcst/_nodes/tests/test_funcdef.py index 65a0ff07..4ed7fcc3 100644 --- a/libcst/_nodes/tests/test_funcdef.py +++ b/libcst/_nodes/tests/test_funcdef.py @@ -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 @@ -741,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) @@ -891,8 +888,6 @@ class FunctionDefCreationTest(CSTNodeTest): ) ) def test_valid_native(self, **kwargs: Any) -> None: - if not is_native(): - self.skipTest("Disabled for pure python parser") self.validate_node(**kwargs) @data_provider( @@ -2223,8 +2218,6 @@ class FunctionDefParserTest(CSTNodeTest): ) ) def test_valid_38(self, node: cst.CSTNode, code: str, **kwargs: Any) -> None: - if not is_native() and kwargs.get("native_only", False): - self.skipTest("disabled for pure python parser") self.validate_node(node, code, _parse_statement_force_38) @data_provider( @@ -2252,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) @@ -2271,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) diff --git a/libcst/_nodes/tests/test_list.py b/libcst/_nodes/tests/test_list.py index 43e22df7..2f96124c 100644 --- a/libcst/_nodes/tests/test_list.py +++ b/libcst/_nodes/tests/test_list.py @@ -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 @@ -126,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) diff --git a/libcst/_nodes/tests/test_match.py b/libcst/_nodes/tests/test_match.py index 2f1e4193..2335b7c3 100644 --- a/libcst/_nodes/tests/test_match.py +++ b/libcst/_nodes/tests/test_match.py @@ -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): diff --git a/libcst/_nodes/tests/test_matrix_multiply.py b/libcst/_nodes/tests/test_matrix_multiply.py index 5b4b8668..500b7aab 100644 --- a/libcst/_nodes/tests/test_matrix_multiply.py +++ b/libcst/_nodes/tests/test_matrix_multiply.py @@ -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) diff --git a/libcst/_nodes/tests/test_module.py b/libcst/_nodes/tests/test_module.py index 5b33c6b7..40de8f8e 100644 --- a/libcst/_nodes/tests/test_module.py +++ b/libcst/_nodes/tests/test_module.py @@ -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) diff --git a/libcst/_nodes/tests/test_set.py b/libcst/_nodes/tests/test_set.py index 335a4d3a..699b458a 100644 --- a/libcst/_nodes/tests/test_set.py +++ b/libcst/_nodes/tests/test_set.py @@ -8,7 +8,6 @@ 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 @@ -133,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) diff --git a/libcst/_nodes/tests/test_try.py b/libcst/_nodes/tests/test_try.py index a2e8a433..5704d098 100644 --- a/libcst/_nodes/tests/test_try.py +++ b/libcst/_nodes/tests/test_try.py @@ -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): diff --git a/libcst/_nodes/tests/test_tuple.py b/libcst/_nodes/tests/test_tuple.py index 0055055c..aa3d68bb 100644 --- a/libcst/_nodes/tests/test_tuple.py +++ b/libcst/_nodes/tests/test_tuple.py @@ -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) diff --git a/libcst/_nodes/tests/test_type_alias.py b/libcst/_nodes/tests/test_type_alias.py index aa26103b..865135c1 100644 --- a/libcst/_nodes/tests/test_type_alias.py +++ b/libcst/_nodes/tests/test_type_alias.py @@ -8,7 +8,6 @@ from typing import Any 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.metadata import CodeRange from libcst.testing.utils import data_provider @@ -132,8 +131,6 @@ class TypeAliasCreationTest(CSTNodeTest): ) ) def test_valid(self, **kwargs: Any) -> None: - if not is_native(): - self.skipTest("Disabled in the old parser") self.validate_node(**kwargs) @@ -252,6 +249,4 @@ class TypeAliasParserTest(CSTNodeTest): ) ) def test_valid(self, **kwargs: Any) -> None: - if not is_native(): - self.skipTest("Disabled in the old parser") self.validate_node(**kwargs) diff --git a/libcst/_nodes/tests/test_with.py b/libcst/_nodes/tests/test_with.py index 517ce357..0b396619 100644 --- a/libcst/_nodes/tests/test_with.py +++ b/libcst/_nodes/tests/test_with.py @@ -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 @@ -187,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", @@ -233,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)), }, ) @@ -310,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) diff --git a/libcst/_nodes/tests/test_yield.py b/libcst/_nodes/tests/test_yield.py index 22a18872..e5085b4d 100644 --- a/libcst/_nodes/tests/test_yield.py +++ b/libcst/_nodes/tests/test_yield.py @@ -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) diff --git a/libcst/_parser/entrypoints.py b/libcst/_parser/entrypoints.py index d9cee5e9..d034258c 100644 --- a/libcst/_parser/entrypoints.py +++ b/libcst/_parser/entrypoints.py @@ -9,27 +9,20 @@ parser. A parser entrypoint should take the source code and some configuration information """ -import os from functools import partial from typing import Union +from libcst import native 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") - return typ != "pure" - - def _parse( entrypoint: str, source: Union[str, bytes], @@ -38,57 +31,19 @@ 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}") + 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}") - return parse(source_str) - return _pure_python_parse( - entrypoint, - source, - config, - detect_trailing_newline=detect_trailing_newline, - detect_default_newline=detect_default_newline, - ) - - -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( diff --git a/libcst/_parser/tests/test_parse_errors.py b/libcst/_parser/tests/test_parse_errors.py index 0a058898..7697893d 100644 --- a/libcst/_parser/tests/test_parse_errors.py +++ b/libcst/_parser/tests/test_parse_errors.py @@ -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,8 +173,6 @@ 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: diff --git a/libcst/codemod/tests/test_codemod_cli.py b/libcst/codemod/tests/test_codemod_cli.py index 18dab870..9798b071 100644 --- a/libcst/codemod/tests/test_codemod_cli.py +++ b/libcst/codemod/tests/test_codemod_cli.py @@ -12,7 +12,6 @@ import tempfile from pathlib import Path from unittest import skipIf -from libcst._parser.entrypoints import is_native from libcst.codemod import CodemodTest from libcst.testing.utils import UnitTest @@ -37,16 +36,10 @@ class TestCodemodCLI(UnitTest): stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) - if not is_native(): - self.assertIn( - "ParserSyntaxError: Syntax Error @ 14:11.", - rlt.stderr.decode("utf-8"), - ) - else: - self.assertIn( - "error: cannot format -: Cannot parse for target version Python 3.6: 13:10: async with AsyncExitStack() as stack:", - rlt.stderr.decode("utf-8"), - ) + self.assertIn( + "error: cannot format -: Cannot parse for target version Python 3.6: 13:10: async with AsyncExitStack() as stack:", + rlt.stderr.decode("utf-8"), + ) def test_codemod_external(self) -> None: # Test running the NOOP command as an "external command" diff --git a/libcst/metadata/tests/test_scope_provider.py b/libcst/metadata/tests/test_scope_provider.py index fd23e993..a367de39 100644 --- a/libcst/metadata/tests/test_scope_provider.py +++ b/libcst/metadata/tests/test_scope_provider.py @@ -11,7 +11,6 @@ from unittest import mock import libcst as cst from libcst import ensure_type -from libcst._parser.entrypoints import is_native from libcst.metadata import MetadataWrapper from libcst.metadata.scope_provider import ( _gen_dotted_names, @@ -2029,8 +2028,6 @@ class ScopeProviderTest(UnitTest): ) def test_type_alias_scope(self) -> None: - if not is_native(): - self.skipTest("type aliases are only supported in the native parser") m, scopes = get_scope_metadata_provider( """ type A = C @@ -2052,8 +2049,6 @@ class ScopeProviderTest(UnitTest): self.assertIsInstance(scopes[alias.value], AnnotationScope) def test_type_alias_param(self) -> None: - if not is_native(): - self.skipTest("type parameters are only supported in the native parser") m, scopes = get_scope_metadata_provider( """ B = int @@ -2084,8 +2079,6 @@ class ScopeProviderTest(UnitTest): ) def test_type_alias_tuple_and_paramspec(self) -> None: - if not is_native(): - self.skipTest("type parameters are only supported in the native parser") m, scopes = get_scope_metadata_provider( """ type A[*T] = T @@ -2113,8 +2106,6 @@ class ScopeProviderTest(UnitTest): self.assertEqual(t_refs[0].node, alias_paramspec.value) def test_class_type_params(self) -> None: - if not is_native(): - self.skipTest("type parameters are only supported in the native parser") m, scopes = get_scope_metadata_provider( """ class W[T]: @@ -2149,8 +2140,6 @@ class ScopeProviderTest(UnitTest): self.assertEqual(t_refs_in_g[0].node, g.returns.annotation) def test_nested_class_type_params(self) -> None: - if not is_native(): - self.skipTest("type parameters are only supported in the native parser") m, scopes = get_scope_metadata_provider( """ class Outer: @@ -2168,8 +2157,6 @@ class ScopeProviderTest(UnitTest): ) def test_annotation_refers_to_nested_class(self) -> None: - if not is_native(): - self.skipTest("type parameters are only supported in the native parser") m, scopes = get_scope_metadata_provider( """ class Outer: @@ -2229,8 +2216,6 @@ class ScopeProviderTest(UnitTest): ) def test_body_isnt_subject_to_special_annotation_rule(self) -> None: - if not is_native(): - self.skipTest("type parameters are only supported in the native parser") m, scopes = get_scope_metadata_provider( """ class Outer: diff --git a/libcst/tests/__main__.py b/libcst/tests/__main__.py index 44e6bbe0..df28d1a6 100644 --- a/libcst/tests/__main__.py +++ b/libcst/tests/__main__.py @@ -5,11 +5,6 @@ from unittest import main -from libcst._parser.entrypoints import is_native - if __name__ == "__main__": - parser_type = "native" if is_native() else "pure" - print(f"running tests with {parser_type!r} parser") - main(module=None, verbosity=2) diff --git a/libcst/tests/test_roundtrip.py b/libcst/tests/test_roundtrip.py index d5da81f2..96d1e507 100644 --- a/libcst/tests/test_roundtrip.py +++ b/libcst/tests/test_roundtrip.py @@ -8,7 +8,7 @@ from pathlib import Path from unittest import TestCase from libcst import CSTTransformer, parse_module -from libcst._parser.entrypoints import is_native + fixtures: Path = Path(__file__).parent.parent.parent / "native/libcst/tests/fixtures" @@ -19,8 +19,6 @@ class NOOPTransformer(CSTTransformer): class RoundTripTests(TestCase): def _get_fixtures(self) -> list[Path]: - if not is_native(): - self.skipTest("pure python parser doesn't work with this") self.assertTrue(fixtures.exists(), f"{fixtures} should exist") files = list(fixtures.iterdir()) self.assertGreater(len(files), 0) diff --git a/pyproject.toml b/pyproject.toml index 3ebaaef1..394eca43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,6 +66,7 @@ fail_under = 93 precision = 1 show_missing = true skip_covered = true +omit = ["*/_parser/*"] # temporary while I remove the parser [tool.uv] cache-keys = [ From b395d7ccf7727965e74047283d1cd294abd9b459 Mon Sep 17 00:00:00 2001 From: Thomas Serre <118730793+thomas-serre-sonarsource@users.noreply.github.com> Date: Mon, 4 Aug 2025 23:03:20 +0200 Subject: [PATCH 129/151] Fix noqa comments (#1379) --- libcst/_parser/parso/python/token.py | 2 +- libcst/_parser/types/token.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libcst/_parser/parso/python/token.py b/libcst/_parser/parso/python/token.py index 83731192..164262b9 100644 --- a/libcst/_parser/parso/python/token.py +++ b/libcst/_parser/parso/python/token.py @@ -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, ) diff --git a/libcst/_parser/types/token.py b/libcst/_parser/types/token.py index 32c85ccf..54d904ef 100644 --- a/libcst/_parser/types/token.py +++ b/libcst/_parser/types/token.py @@ -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 From 7090a0db2bfa81489556d9ab7a2cdb16bd2ebaf9 Mon Sep 17 00:00:00 2001 From: Ken Kawamoto Date: Mon, 4 Aug 2025 14:27:13 -0700 Subject: [PATCH 130/151] 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 --- libcst/_nodes/statement.py | 10 ++++ .../metadata/tests/test_position_provider.py | 47 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/libcst/_nodes/statement.py b/libcst/_nodes/statement.py index 1aba38d3..cdc49edc 100644 --- a/libcst/_nodes/statement.py +++ b/libcst/_nodes/statement.py @@ -2886,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(":") @@ -3473,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: diff --git a/libcst/metadata/tests/test_position_provider.py b/libcst/metadata/tests/test_position_provider.py index c479837e..14cecec7 100644 --- a/libcst/metadata/tests/test_position_provider.py +++ b/libcst/metadata/tests/test_position_provider.py @@ -83,6 +83,53 @@ class PositionProviderTest(UnitTest): wrapper = MetadataWrapper(parse_module("pass")) wrapper.visit_batched([ABatchable()]) + def test_match_statement_position_metadata(self) -> None: + test = self + + class MatchPositionVisitor(CSTVisitor): + METADATA_DEPENDENCIES = (PositionProvider,) + + def visit_Match(self, node: cst.Match) -> None: + test.assertEqual( + self.get_metadata(PositionProvider, node), + CodeRange((2, 0), (5, 16)), + ) + + def visit_MatchCase(self, node: cst.MatchCase) -> None: + if ( + isinstance(node.pattern, cst.MatchAs) + and node.pattern.name + and node.pattern.name.value == "b" + ): + test.assertEqual( + self.get_metadata(PositionProvider, node), + CodeRange((3, 4), (3, 16)), + ) + elif ( + isinstance(node.pattern, cst.MatchAs) + and node.pattern.name + and node.pattern.name.value == "c" + ): + test.assertEqual( + self.get_metadata(PositionProvider, node), + CodeRange((4, 4), (4, 16)), + ) + elif isinstance(node.pattern, cst.MatchAs) and not node.pattern.name: + test.assertEqual( + self.get_metadata(PositionProvider, node), + CodeRange((5, 4), (5, 16)), + ) + + code = """ +match status: + case b: pass + case c: pass + case _: pass +""" + + wrapper = MetadataWrapper(parse_module(code)) + wrapper.visit(MatchPositionVisitor()) + class PositionProvidingCodegenStateTest(UnitTest): def test_codegen_initial_position(self) -> None: From 441a7f0c816a00ffdf8522fe70421c2627dac74f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 16 Aug 2025 18:58:52 -0700 Subject: [PATCH 131/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pypi_upload.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pypi_upload.yml b/.github/workflows/pypi_upload.yml index fd69d5fc..de6cc5e1 100644 --- a/.github/workflows/pypi_upload.yml +++ b/.github/workflows/pypi_upload.yml @@ -25,7 +25,7 @@ jobs: persist-credentials: false - name: Download binary wheels id: download - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: pattern: wheels-* path: wheelhouse From 2048e6693c59372576a65fb06719a6e7dc5f0ea3 Mon Sep 17 00:00:00 2001 From: martin <48778384+drinkmorewaterr@users.noreply.github.com> Date: Fri, 29 Aug 2025 15:37:00 -0400 Subject: [PATCH 132/151] bump version to 1.8.3 (#1397) --- CHANGELOG.md | 13 +++++++++++++ native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 4 ++-- native/libcst_derive/Cargo.toml | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a8fb401..4b2bd514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 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 diff --git a/native/Cargo.lock b/native/Cargo.lock index ee5f0d57..7204b9f2 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -286,7 +286,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libcst" -version = "1.8.2" +version = "1.8.3" dependencies = [ "annotate-snippets", "criterion", @@ -304,7 +304,7 @@ dependencies = [ [[package]] name = "libcst_derive" -version = "1.8.2" +version = "1.8.3" dependencies = [ "quote", "syn", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 5b9f2f86..903a231b 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "libcst" -version = "1.8.2" +version = "1.8.3" authors = ["LibCST Developers"] edition = "2018" rust-version = "1.70" @@ -42,7 +42,7 @@ peg = "0.8.5" annotate-snippets = "0.11.5" regex = "1.11.1" memchr = "2.7.4" -libcst_derive = { path = "../libcst_derive", version = "1.8.2" } +libcst_derive = { path = "../libcst_derive", version = "1.8.3" } [dev-dependencies] criterion = { version = "0.6.0", features = ["html_reports"] } diff --git a/native/libcst_derive/Cargo.toml b/native/libcst_derive/Cargo.toml index f538e46e..9541f243 100644 --- a/native/libcst_derive/Cargo.toml +++ b/native/libcst_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libcst_derive" -version = "1.8.2" +version = "1.8.3" edition = "2018" description = "Proc macro helpers for libcst." license = "MIT" From f746afd537910d99bd8226a1c1edbf5bc69630eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 16:23:29 -0400 Subject: [PATCH 133/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 8 ++++---- native/libcst/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 7204b9f2..d978117e 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -514,9 +514,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -524,9 +524,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 903a231b..c13a6b19 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -47,7 +47,7 @@ libcst_derive = { path = "../libcst_derive", version = "1.8.3" } [dev-dependencies] criterion = { version = "0.6.0", features = ["html_reports"] } difference = "2.0.0" -rayon = "1.10.0" +rayon = "1.11.0" itertools = "0.14.0" [[bench]] From e064729b4c04b834754c7be7f43a4dfb38972570 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 16:23:44 -0400 Subject: [PATCH 134/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bffe5fca..aa9c9686 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: if: github.event_name != 'release' run: echo CIBW_ENABLE=cpython-prerelease >> $GITHUB_ENV - name: Build wheels - uses: pypa/cibuildwheel@v3.0.1 + uses: pypa/cibuildwheel@v3.1.4 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From d721a06c3fc3ab9210acd180cec5aaca856e1bc7 Mon Sep 17 00:00:00 2001 From: Stephen Morton Date: Wed, 3 Sep 2025 13:54:44 -0700 Subject: [PATCH 135/151] generate Attribute nodes when applying type annotations (#1396) * 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 --- .../visitors/_apply_type_annotations.py | 11 +++-- .../tests/test_apply_type_annotations.py | 48 +++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/libcst/codemod/visitors/_apply_type_annotations.py b/libcst/codemod/visitors/_apply_type_annotations.py index 9c826cc4..59347420 100644 --- a/libcst/codemod/visitors/_apply_type_annotations.py +++ b/libcst/codemod/visitors/_apply_type_annotations.py @@ -534,15 +534,20 @@ class _TypeCollectorDequalifier(cst.CSTTransformer): def __init__(self, type_collector: "TypeCollector") -> None: self.type_collector = type_collector - def leave_Name(self, original_node: cst.Name, updated_node: cst.Name) -> cst.Name: + def leave_Name( + self, original_node: cst.Name, updated_node: cst.Name + ) -> NameOrAttribute: qualified_name = _get_unique_qualified_name(self.type_collector, original_node) should_qualify = self.type_collector._handle_qualification_and_should_qualify( qualified_name, original_node ) self.type_collector.annotations.names.add(qualified_name) if should_qualify: - qualified_node = cst.parse_module(qualified_name) - return qualified_node # pyre-ignore[7] + parts = qualified_name.split(".") + qualified_node = cst.Name(parts[0]) + for p in parts[1:]: + qualified_node = cst.Attribute(qualified_node, cst.Name(p)) + return qualified_node else: return original_node diff --git a/libcst/codemod/visitors/tests/test_apply_type_annotations.py b/libcst/codemod/visitors/tests/test_apply_type_annotations.py index c7ea5c22..e7b25124 100644 --- a/libcst/codemod/visitors/tests/test_apply_type_annotations.py +++ b/libcst/codemod/visitors/tests/test_apply_type_annotations.py @@ -61,6 +61,28 @@ class TestApplyAnnotationsVisitor(CodemodTest): ) self.assertCodemod(before, after, context_override=context) + def run_test_case_twice( + self, + stub: str, + before: str, + after: str, + ) -> None: + context = CodemodContext() + ApplyTypeAnnotationsVisitor.store_stub_in_context( + context, parse_module(textwrap.dedent(stub.rstrip())) + ) + r1 = ApplyTypeAnnotationsVisitor(context).transform_module( + parse_module(textwrap.dedent(before.rstrip())) + ) + + context = CodemodContext() + ApplyTypeAnnotationsVisitor.store_stub_in_context( + context, parse_module(textwrap.dedent(stub.rstrip())) + ) + r2 = ApplyTypeAnnotationsVisitor(context).transform_module(r1) + assert r1.code == textwrap.dedent(after.rstrip()) + assert r2.code == textwrap.dedent(after.rstrip()) + @data_provider( { "simple": ( @@ -1965,3 +1987,29 @@ class TestApplyAnnotationsVisitor(CodemodTest): ) def test_no_duplicate_annotations(self, stub: str, before: str, after: str) -> None: self.run_simple_test_case(stub=stub, before=before, after=after) + + @data_provider( + { + "qualifier_jank": ( + """ + from module.submodule import B + M: B + class Foo: ... + """, + """ + from module import B + M = B() + class Foo: pass + """, + """ + from module import B + import module.submodule + + M: module.submodule.B = B() + class Foo: pass + """, + ), + } + ) + def test_idempotent(self, stub: str, before: str, after: str) -> None: + self.run_test_case_twice(stub=stub, before=before, after=after) From f40d8351450ca9adfe5270bd86ec7951e83221f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 Sep 2025 21:29:40 -0700 Subject: [PATCH 136/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/pypi_upload.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aa9c9686..9f1c6d9f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.12" - uses: dtolnay/rust-toolchain@stable diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b0fa767..d8d7d244 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,7 +110,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: test diff --git a/.github/workflows/pypi_upload.yml b/.github/workflows/pypi_upload.yml index de6cc5e1..d9d0bcb5 100644 --- a/.github/workflows/pypi_upload.yml +++ b/.github/workflows/pypi_upload.yml @@ -30,7 +30,7 @@ jobs: pattern: wheels-* path: wheelhouse merge-multiple: true - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.10" - name: Install uv From 0c82bfa76166ed38409a97de8974f9aaf3e7e572 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 Sep 2025 21:30:06 -0700 Subject: [PATCH 137/151] build(deps): bump regex from 1.11.1 to 1.11.2 in /native (#1399) 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index d978117e..7d39b6ec 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -534,9 +534,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index c13a6b19..b3abfa63 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -40,7 +40,7 @@ pyo3 = { version = "0.25.1", optional = true } thiserror = "2.0.12" peg = "0.8.5" annotate-snippets = "0.11.5" -regex = "1.11.1" +regex = "1.11.2" memchr = "2.7.4" libcst_derive = { path = "../libcst_derive", version = "1.8.3" } From 48668dfabb25e4bbdd35f81407498f5f5e47bf88 Mon Sep 17 00:00:00 2001 From: martin <48778384+drinkmorewaterr@users.noreply.github.com> Date: Tue, 9 Sep 2025 11:16:20 -0400 Subject: [PATCH 138/151] 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. --- libcst/__init__.py | 8 + libcst/_nodes/expression.py | 247 ++++++++++++ libcst/_nodes/tests/test_template_strings.py | 183 +++++++++ libcst/_typed_visitor.py | 180 +++++++++ libcst/matchers/__init__.py | 377 ++++++++++++++++++ libcst/matchers/_return_types.py | 7 + native/libcst/src/lib.rs | 21 +- native/libcst/src/nodes/expression.rs | 139 +++++++ native/libcst/src/nodes/mod.rs | 8 +- native/libcst/src/parser/grammar.rs | 92 ++++- native/libcst/src/tokenizer/core/mod.rs | 119 ++++-- .../libcst/src/tokenizer/core/string_types.rs | 14 +- native/libcst/src/tokenizer/operators.rs | 2 +- native/libcst/src/tokenizer/tests.rs | 56 ++- native/libcst/tests/fixtures/super_strings.py | 8 + 15 files changed, 1406 insertions(+), 55 deletions(-) create mode 100644 libcst/_nodes/tests/test_template_strings.py diff --git a/libcst/__init__.py b/libcst/__init__.py index 4e82bc9e..0cd54d62 100644 --- a/libcst/__init__.py +++ b/libcst/__init__.py @@ -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, @@ -268,6 +272,7 @@ __all__ = [ "BaseElement", "BaseExpression", "BaseFormattedStringContent", + "BaseTemplatedStringContent", "BaseList", "BaseNumber", "BaseSet", @@ -291,6 +296,9 @@ __all__ = [ "FormattedString", "FormattedStringExpression", "FormattedStringText", + "TemplatedString", + "TemplatedStringText", + "TemplatedStringExpression", "From", "GeneratorExp", "IfExp", diff --git a/libcst/_nodes/expression.py b/libcst/_nodes/expression.py index 00a1b4a7..eb95d9b3 100644 --- a/libcst/_nodes/expression.py +++ b/libcst/_nodes/expression.py @@ -958,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 + #: `_. + 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 + #: `_. + 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): diff --git a/libcst/_nodes/tests/test_template_strings.py b/libcst/_nodes/tests/test_template_strings.py new file mode 100644 index 00000000..6e4c308a --- /dev/null +++ b/libcst/_nodes/tests/test_template_strings.py @@ -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) diff --git a/libcst/_typed_visitor.py b/libcst/_typed_visitor.py index e6322353..8816f619 100644 --- a/libcst/_typed_visitor.py +++ b/libcst/_typed_visitor.py @@ -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, @@ -5182,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 @@ -6385,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 @@ -7402,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" diff --git a/libcst/matchers/__init__.py b/libcst/matchers/__init__.py index 57e1b4c9..2857fee1 100644 --- a/libcst/matchers/__init__.py +++ b/libcst/matchers/__init__.py @@ -142,6 +142,10 @@ class BaseSuite(_NodeABC): pass +class BaseTemplatedStringContent(_NodeABC): + pass + + class BaseUnaryOp(_NodeABC): pass @@ -14283,6 +14287,375 @@ class SubtractAssign(BaseAugOp, BaseMatcherNode): ] = DoNotCare() +BaseTemplatedStringContentMatchType = Union[ + "BaseTemplatedStringContent", + MetadataMatchType, + MatchIfTrue[cst.BaseTemplatedStringContent], +] + + +@dataclass(frozen=True, eq=False, unsafe_hash=False) +class TemplatedString(BaseExpression, BaseString, BaseMatcherNode): + parts: Union[ + Sequence[ + Union[ + BaseTemplatedStringContentMatchType, + DoNotCareSentinel, + OneOf[BaseTemplatedStringContentMatchType], + AllOf[BaseTemplatedStringContentMatchType], + AtLeastN[ + Union[ + BaseTemplatedStringContentMatchType, + DoNotCareSentinel, + OneOf[BaseTemplatedStringContentMatchType], + AllOf[BaseTemplatedStringContentMatchType], + ] + ], + AtMostN[ + Union[ + BaseTemplatedStringContentMatchType, + DoNotCareSentinel, + OneOf[BaseTemplatedStringContentMatchType], + AllOf[BaseTemplatedStringContentMatchType], + ] + ], + ] + ], + DoNotCareSentinel, + MatchIfTrue[Sequence[cst.BaseTemplatedStringContent]], + OneOf[ + Union[ + Sequence[ + Union[ + BaseTemplatedStringContentMatchType, + OneOf[BaseTemplatedStringContentMatchType], + AllOf[BaseTemplatedStringContentMatchType], + AtLeastN[ + Union[ + BaseTemplatedStringContentMatchType, + OneOf[BaseTemplatedStringContentMatchType], + AllOf[BaseTemplatedStringContentMatchType], + ] + ], + AtMostN[ + Union[ + BaseTemplatedStringContentMatchType, + OneOf[BaseTemplatedStringContentMatchType], + AllOf[BaseTemplatedStringContentMatchType], + ] + ], + ] + ], + MatchIfTrue[Sequence[cst.BaseTemplatedStringContent]], + ] + ], + AllOf[ + Union[ + Sequence[ + Union[ + BaseTemplatedStringContentMatchType, + OneOf[BaseTemplatedStringContentMatchType], + AllOf[BaseTemplatedStringContentMatchType], + AtLeastN[ + Union[ + BaseTemplatedStringContentMatchType, + OneOf[BaseTemplatedStringContentMatchType], + AllOf[BaseTemplatedStringContentMatchType], + ] + ], + AtMostN[ + Union[ + BaseTemplatedStringContentMatchType, + OneOf[BaseTemplatedStringContentMatchType], + AllOf[BaseTemplatedStringContentMatchType], + ] + ], + ] + ], + MatchIfTrue[Sequence[cst.BaseTemplatedStringContent]], + ] + ], + ] = DoNotCare() + start: Union[ + strMatchType, DoNotCareSentinel, OneOf[strMatchType], AllOf[strMatchType] + ] = DoNotCare() + end: Union[ + Literal['"', "'", '"""', "'''"], + MetadataMatchType, + MatchIfTrue[Literal['"', "'", '"""', "'''"]], + DoNotCareSentinel, + OneOf[ + Union[ + Literal['"', "'", '"""', "'''"], + MetadataMatchType, + MatchIfTrue[Literal['"', "'", '"""', "'''"]], + ] + ], + AllOf[ + Union[ + Literal['"', "'", '"""', "'''"], + MetadataMatchType, + MatchIfTrue[Literal['"', "'", '"""', "'''"]], + ] + ], + ] = DoNotCare() + lpar: Union[ + Sequence[ + Union[ + LeftParenMatchType, + DoNotCareSentinel, + OneOf[LeftParenMatchType], + AllOf[LeftParenMatchType], + AtLeastN[ + Union[ + LeftParenMatchType, + DoNotCareSentinel, + OneOf[LeftParenMatchType], + AllOf[LeftParenMatchType], + ] + ], + AtMostN[ + Union[ + LeftParenMatchType, + DoNotCareSentinel, + OneOf[LeftParenMatchType], + AllOf[LeftParenMatchType], + ] + ], + ] + ], + DoNotCareSentinel, + MatchIfTrue[Sequence[cst.LeftParen]], + OneOf[ + Union[ + Sequence[ + Union[ + LeftParenMatchType, + OneOf[LeftParenMatchType], + AllOf[LeftParenMatchType], + AtLeastN[ + Union[ + LeftParenMatchType, + OneOf[LeftParenMatchType], + AllOf[LeftParenMatchType], + ] + ], + AtMostN[ + Union[ + LeftParenMatchType, + OneOf[LeftParenMatchType], + AllOf[LeftParenMatchType], + ] + ], + ] + ], + MatchIfTrue[Sequence[cst.LeftParen]], + ] + ], + AllOf[ + Union[ + Sequence[ + Union[ + LeftParenMatchType, + OneOf[LeftParenMatchType], + AllOf[LeftParenMatchType], + AtLeastN[ + Union[ + LeftParenMatchType, + OneOf[LeftParenMatchType], + AllOf[LeftParenMatchType], + ] + ], + AtMostN[ + Union[ + LeftParenMatchType, + OneOf[LeftParenMatchType], + AllOf[LeftParenMatchType], + ] + ], + ] + ], + MatchIfTrue[Sequence[cst.LeftParen]], + ] + ], + ] = DoNotCare() + rpar: Union[ + Sequence[ + Union[ + RightParenMatchType, + DoNotCareSentinel, + OneOf[RightParenMatchType], + AllOf[RightParenMatchType], + AtLeastN[ + Union[ + RightParenMatchType, + DoNotCareSentinel, + OneOf[RightParenMatchType], + AllOf[RightParenMatchType], + ] + ], + AtMostN[ + Union[ + RightParenMatchType, + DoNotCareSentinel, + OneOf[RightParenMatchType], + AllOf[RightParenMatchType], + ] + ], + ] + ], + DoNotCareSentinel, + MatchIfTrue[Sequence[cst.RightParen]], + OneOf[ + Union[ + Sequence[ + Union[ + RightParenMatchType, + OneOf[RightParenMatchType], + AllOf[RightParenMatchType], + AtLeastN[ + Union[ + RightParenMatchType, + OneOf[RightParenMatchType], + AllOf[RightParenMatchType], + ] + ], + AtMostN[ + Union[ + RightParenMatchType, + OneOf[RightParenMatchType], + AllOf[RightParenMatchType], + ] + ], + ] + ], + MatchIfTrue[Sequence[cst.RightParen]], + ] + ], + AllOf[ + Union[ + Sequence[ + Union[ + RightParenMatchType, + OneOf[RightParenMatchType], + AllOf[RightParenMatchType], + AtLeastN[ + Union[ + RightParenMatchType, + OneOf[RightParenMatchType], + AllOf[RightParenMatchType], + ] + ], + AtMostN[ + Union[ + RightParenMatchType, + OneOf[RightParenMatchType], + AllOf[RightParenMatchType], + ] + ], + ] + ], + MatchIfTrue[Sequence[cst.RightParen]], + ] + ], + ] = DoNotCare() + metadata: Union[ + MetadataMatchType, + DoNotCareSentinel, + OneOf[MetadataMatchType], + AllOf[MetadataMatchType], + ] = DoNotCare() + + +@dataclass(frozen=True, eq=False, unsafe_hash=False) +class TemplatedStringExpression(BaseTemplatedStringContent, BaseMatcherNode): + expression: Union[ + BaseExpressionMatchType, + DoNotCareSentinel, + OneOf[BaseExpressionMatchType], + AllOf[BaseExpressionMatchType], + ] = DoNotCare() + conversion: Union[ + Optional[str], + MetadataMatchType, + MatchIfTrue[Optional[str]], + DoNotCareSentinel, + OneOf[Union[Optional[str], MetadataMatchType, MatchIfTrue[Optional[str]]]], + AllOf[Union[Optional[str], MetadataMatchType, MatchIfTrue[Optional[str]]]], + ] = DoNotCare() + format_spec: Union[ + Optional[Sequence["BaseTemplatedStringContent"]], + MetadataMatchType, + MatchIfTrue[Optional[Sequence[cst.BaseTemplatedStringContent]]], + DoNotCareSentinel, + OneOf[ + Union[ + Optional[Sequence["BaseTemplatedStringContent"]], + MetadataMatchType, + MatchIfTrue[Optional[Sequence[cst.BaseTemplatedStringContent]]], + ] + ], + AllOf[ + Union[ + Optional[Sequence["BaseTemplatedStringContent"]], + MetadataMatchType, + MatchIfTrue[Optional[Sequence[cst.BaseTemplatedStringContent]]], + ] + ], + ] = DoNotCare() + whitespace_before_expression: Union[ + BaseParenthesizableWhitespaceMatchType, + DoNotCareSentinel, + OneOf[BaseParenthesizableWhitespaceMatchType], + AllOf[BaseParenthesizableWhitespaceMatchType], + ] = DoNotCare() + whitespace_after_expression: Union[ + BaseParenthesizableWhitespaceMatchType, + DoNotCareSentinel, + OneOf[BaseParenthesizableWhitespaceMatchType], + AllOf[BaseParenthesizableWhitespaceMatchType], + ] = DoNotCare() + equal: Union[ + Optional["AssignEqual"], + MetadataMatchType, + MatchIfTrue[Optional[cst.AssignEqual]], + DoNotCareSentinel, + OneOf[ + Union[ + Optional["AssignEqual"], + MetadataMatchType, + MatchIfTrue[Optional[cst.AssignEqual]], + ] + ], + AllOf[ + Union[ + Optional["AssignEqual"], + MetadataMatchType, + MatchIfTrue[Optional[cst.AssignEqual]], + ] + ], + ] = DoNotCare() + metadata: Union[ + MetadataMatchType, + DoNotCareSentinel, + OneOf[MetadataMatchType], + AllOf[MetadataMatchType], + ] = DoNotCare() + + +@dataclass(frozen=True, eq=False, unsafe_hash=False) +class TemplatedStringText(BaseTemplatedStringContent, BaseMatcherNode): + value: Union[ + strMatchType, DoNotCareSentinel, OneOf[strMatchType], AllOf[strMatchType] + ] = DoNotCare() + metadata: Union[ + MetadataMatchType, + DoNotCareSentinel, + OneOf[MetadataMatchType], + AllOf[MetadataMatchType], + ] = DoNotCare() + + @dataclass(frozen=True, eq=False, unsafe_hash=False) class TrailingWhitespace(BaseMatcherNode): whitespace: Union[ @@ -16122,6 +16495,7 @@ __all__ = [ "BaseStatement", "BaseString", "BaseSuite", + "BaseTemplatedStringContent", "BaseUnaryOp", "BinaryOperation", "BitAnd", @@ -16274,6 +16648,9 @@ __all__ = [ "SubscriptElement", "Subtract", "SubtractAssign", + "TemplatedString", + "TemplatedStringExpression", + "TemplatedStringText", "TrailingWhitespace", "Try", "TryStar", diff --git a/libcst/matchers/_return_types.py b/libcst/matchers/_return_types.py index d49e009c..2f050088 100644 --- a/libcst/matchers/_return_types.py +++ b/libcst/matchers/_return_types.py @@ -20,6 +20,7 @@ from libcst._nodes.expression import ( BaseExpression, BaseFormattedStringContent, BaseSlice, + BaseTemplatedStringContent, BinaryOperation, BooleanOperation, Call, @@ -66,6 +67,9 @@ from libcst._nodes.expression import ( StarredElement, Subscript, SubscriptElement, + TemplatedString, + TemplatedStringExpression, + TemplatedStringText, Tuple, UnaryOperation, Yield, @@ -358,6 +362,9 @@ TYPED_FUNCTION_RETURN_MAPPING: TypingDict[Type[CSTNode], object] = { SubscriptElement: Union[SubscriptElement, RemovalSentinel], Subtract: BaseBinaryOp, SubtractAssign: BaseAugOp, + TemplatedString: BaseExpression, + TemplatedStringExpression: Union[BaseTemplatedStringContent, RemovalSentinel], + TemplatedStringText: Union[BaseTemplatedStringContent, RemovalSentinel], TrailingWhitespace: TrailingWhitespace, Try: Union[BaseStatement, RemovalSentinel], TryStar: Union[BaseStatement, RemovalSentinel], diff --git a/native/libcst/src/lib.rs b/native/libcst/src/lib.rs index 93e6bd05..113d5d02 100644 --- a/native/libcst/src/lib.rs +++ b/native/libcst/src/lib.rs @@ -25,7 +25,7 @@ pub fn tokenize(text: &str) -> Result> { text, &TokConfig { async_hacks: false, - split_fstring: true, + split_ftstring: true, }, ); @@ -191,4 +191,23 @@ mod test { assert_eq!(11, bol_offset("hello\nhello", 3)); assert_eq!(12, bol_offset("hello\nhello\nhello", 3)); } + #[test] + fn test_tstring_basic() { + assert!( + parse_module("t'hello'", None).is_ok(), + "Failed to parse t'hello'" + ); + assert!( + parse_module("t'{hello}'", None).is_ok(), + "Failed to parse t'{{hello}}'" + ); + assert!( + parse_module("t'{hello:r}'", None).is_ok(), + "Failed to parse t'{{hello:r}}'" + ); + assert!( + parse_module("f'line1\\n{hello:r}\\nline2'", None).is_ok(), + "Failed to parse t'line1\\n{{hello:r}}\\nline2'" + ); + } } diff --git a/native/libcst/src/nodes/expression.rs b/native/libcst/src/nodes/expression.rs index cbd1e361..961eee9f 100644 --- a/native/libcst/src/nodes/expression.rs +++ b/native/libcst/src/nodes/expression.rs @@ -474,6 +474,7 @@ pub enum Expression<'a> { SimpleString(Box>), ConcatenatedString(Box>), FormattedString(Box>), + TemplatedString(Box>), NamedExpr(Box>), } @@ -2249,6 +2250,7 @@ pub enum String<'a> { Simple(SimpleString<'a>), Concatenated(ConcatenatedString<'a>), Formatted(FormattedString<'a>), + Templated(TemplatedString<'a>), } impl<'r, 'a> std::convert::From> for DeflatedExpression<'r, 'a> { @@ -2257,6 +2259,7 @@ impl<'r, 'a> std::convert::From> for DeflatedExpression<' DeflatedString::Simple(s) => Self::SimpleString(Box::new(s)), DeflatedString::Concatenated(s) => Self::ConcatenatedString(Box::new(s)), DeflatedString::Formatted(s) => Self::FormattedString(Box::new(s)), + DeflatedString::Templated(s) => Self::TemplatedString(Box::new(s)), } } } @@ -2334,6 +2337,142 @@ impl<'a> Codegen<'a> for SimpleString<'a> { } } +#[cst_node] +pub struct TemplatedStringText<'a> { + pub value: &'a str, +} + +impl<'r, 'a> Inflate<'a> for DeflatedTemplatedStringText<'r, 'a> { + type Inflated = TemplatedStringText<'a>; + fn inflate(self, _config: &Config<'a>) -> Result { + Ok(Self::Inflated { value: self.value }) + } +} + +impl<'a> Codegen<'a> for TemplatedStringText<'a> { + fn codegen(&self, state: &mut CodegenState<'a>) { + state.add_token(self.value); + } +} + +pub(crate) fn make_tstringtext<'r, 'a>(value: &'a str) -> DeflatedTemplatedStringText<'r, 'a> { + DeflatedTemplatedStringText { + value, + _phantom: Default::default(), + } +} + +#[cst_node] +pub struct TemplatedStringExpression<'a> { + // This represents the part of a t-string that is insde the brackets '{' and '}'. + pub expression: Expression<'a>, + pub conversion: Option<&'a str>, + pub format_spec: Option>>, + pub whitespace_before_expression: ParenthesizableWhitespace<'a>, + pub whitespace_after_expression: ParenthesizableWhitespace<'a>, + pub equal: Option>, + + pub(crate) lbrace_tok: TokenRef<'a>, + // This is None if there's an equal sign, otherwise it's the first token of + // (conversion, format spec, right brace) in that order + pub(crate) after_expr_tok: Option>, +} + +impl<'r, 'a> Inflate<'a> for DeflatedTemplatedStringExpression<'r, 'a> { + type Inflated = TemplatedStringExpression<'a>; + fn inflate(mut self, config: &Config<'a>) -> Result { + let whitespace_before_expression = parse_parenthesizable_whitespace( + config, + &mut (*self.lbrace_tok).whitespace_after.borrow_mut(), + )?; + let expression = self.expression.inflate(config)?; + let equal = self.equal.inflate(config)?; + let whitespace_after_expression = if let Some(after_expr_tok) = self.after_expr_tok.as_mut() + { + parse_parenthesizable_whitespace( + config, + &mut after_expr_tok.whitespace_before.borrow_mut(), + )? + } else { + Default::default() + }; + let format_spec = self.format_spec.inflate(config)?; + Ok(Self::Inflated { + expression, + conversion: self.conversion, + format_spec, + whitespace_before_expression, + whitespace_after_expression, + equal, + }) + } +} + +impl<'a> Codegen<'a> for TemplatedStringExpression<'a> { + fn codegen(&self, state: &mut CodegenState<'a>) { + state.add_token("{"); + self.whitespace_before_expression.codegen(state); + self.expression.codegen(state); + if let Some(eq) = &self.equal { + eq.codegen(state); + } + self.whitespace_after_expression.codegen(state); + if let Some(conv) = &self.conversion { + state.add_token("!"); + state.add_token(conv); + } + if let Some(specs) = &self.format_spec { + state.add_token(":"); + for spec in specs { + spec.codegen(state); + } + } + state.add_token("}"); + } +} + +#[cst_node(ParenthesizedNode)] +pub struct TemplatedString<'a> { + pub parts: Vec>, + pub start: &'a str, + pub end: &'a str, + pub lpar: Vec>, + pub rpar: Vec>, +} + +impl<'r, 'a> Inflate<'a> for DeflatedTemplatedString<'r, 'a> { + type Inflated = TemplatedString<'a>; + fn inflate(self, config: &Config<'a>) -> Result { + let lpar = self.lpar.inflate(config)?; + let parts = self.parts.inflate(config)?; + let rpar = self.rpar.inflate(config)?; + Ok(Self::Inflated { + parts, + start: self.start, + end: self.end, + lpar, + rpar, + }) + } +} + +impl<'a> Codegen<'a> for TemplatedString<'a> { + fn codegen(&self, state: &mut CodegenState<'a>) { + self.parenthesize(state, |state| { + state.add_token(self.start); + for part in &self.parts { + part.codegen(state); + } + state.add_token(self.end); + }) + } +} + +#[cst_node(Codegen, Inflate)] +pub enum TemplatedStringContent<'a> { + Text(TemplatedStringText<'a>), + Expression(Box>), +} #[cst_node] pub struct FormattedStringText<'a> { pub value: &'a str, diff --git a/native/libcst/src/nodes/mod.rs b/native/libcst/src/nodes/mod.rs index ce02c86f..e044db94 100644 --- a/native/libcst/src/nodes/mod.rs +++ b/native/libcst/src/nodes/mod.rs @@ -31,7 +31,8 @@ pub use expression::{ Integer, Lambda, LeftCurlyBrace, LeftParen, LeftSquareBracket, List, ListComp, Name, NameOrAttribute, NamedExpr, Param, ParamSlash, ParamStar, Parameters, RightCurlyBrace, RightParen, RightSquareBracket, Set, SetComp, SimpleString, Slice, StarArg, StarredDictElement, - StarredElement, String, Subscript, SubscriptElement, Tuple, UnaryOperation, Yield, YieldValue, + StarredElement, String, Subscript, SubscriptElement, TemplatedString, TemplatedStringContent, + TemplatedStringExpression, Tuple, UnaryOperation, Yield, YieldValue, }; pub(crate) mod op; @@ -78,7 +79,10 @@ pub(crate) mod deflated { DeflatedSlice as Slice, DeflatedStarArg as StarArg, DeflatedStarredDictElement as StarredDictElement, DeflatedStarredElement as StarredElement, DeflatedString as String, DeflatedSubscript as Subscript, - DeflatedSubscriptElement as SubscriptElement, DeflatedTuple as Tuple, + DeflatedSubscriptElement as SubscriptElement, DeflatedTemplatedString as TemplatedString, + DeflatedTemplatedStringContent as TemplatedStringContent, + DeflatedTemplatedStringExpression as TemplatedStringExpression, + DeflatedTemplatedStringText as TemplatedStringText, DeflatedTuple as Tuple, DeflatedUnaryOperation as UnaryOperation, DeflatedYield as Yield, DeflatedYieldValue as YieldValue, }; diff --git a/native/libcst/src/parser/grammar.rs b/native/libcst/src/parser/grammar.rs index 30a4b9bb..76920d66 100644 --- a/native/libcst/src/parser/grammar.rs +++ b/native/libcst/src/parser/grammar.rs @@ -8,6 +8,7 @@ use std::rc::Rc; use crate::expression::make_async; use crate::nodes::deflated::*; use crate::nodes::expression::make_fstringtext; +use crate::nodes::expression::make_tstringtext; use crate::nodes::op::make_importstar; use crate::nodes::traits::ParenthesizedDeflatedNode; use crate::parser::ParserError; @@ -17,7 +18,8 @@ use peg::str::LineCol; use peg::{parser, Parse, ParseElem, RuleResult}; use TokType::{ Async, Await as AWAIT, Dedent, EndMarker, FStringEnd, FStringStart, FStringString, Indent, - Name as NameTok, Newline as NL, Number, String as STRING, + Name as NameTok, Newline as NL, Number, String as STRING, TStringEnd, TStringStart, + TStringString, }; pub type Result<'a, T> = std::result::Result>; @@ -1043,7 +1045,7 @@ parser! { / n:lit("True") { Expression::Name(Box::new(make_name(n))) } / n:lit("False") { Expression::Name(Box::new(make_name(n))) } / n:lit("None") { Expression::Name(Box::new(make_name(n))) } - / &(tok(STRING, "") / tok(FStringStart, "")) s:strings() {s.into()} + / &(tok(STRING, "") / tok(FStringStart, "") / tok(TStringStart, "")) s:strings() {s.into()} / n:tok(Number, "NUMBER") { make_number(n) } / &lit("(") e:(tuple() / group() / (g:genexp() {Expression::GeneratorExp(Box::new(g))})) {e} / &lit("[") e:(list() / listcomp()) {e} @@ -1151,7 +1153,7 @@ parser! { rule strings() -> String<'input, 'a> = s:(str:tok(STRING, "STRING") t:&_ {(make_string(str), t)} - / str:fstring() t:&_ {(String::Formatted(str), t)})+ {? + / str:fstring() t:&_ {(String::Formatted(str), t)} / str:tstring() t:&_ {(String::Templated(str), t)})+ {? make_strings(s) } @@ -1463,6 +1465,34 @@ parser! { rule _f_spec() -> Vec> = (_f_string() / _f_replacement())* + // T-strings + + rule tstring() -> TemplatedString<'input, 'a> + = start:tok(TStringStart, "t\"") + parts:(_t_string() / _t_replacement())* + end:tok(TStringEnd, "\"") { + make_tstring(start.string, parts, end.string) + } + + rule _t_string() -> TemplatedStringContent<'input, 'a> + = t:tok(TStringString, "t-string contents") { + TemplatedStringContent::Text(make_tstringtext(t.string)) + } + + + rule _t_replacement() -> TemplatedStringContent<'input, 'a> + = lb:lit("{") e:annotated_rhs() eq:lit("=")? + conv:(t:lit("!") c:_f_conversion() {(t,c)})? + spec:(t:lit(":") s:_t_spec() {(t,s)})? + rb:lit("}") { + TemplatedStringContent::Expression(Box::new( + make_tstring_expression(lb, e, eq, conv, spec, rb) + )) + } + + rule _t_spec() -> Vec> + = (_t_string() / _t_replacement())* + // CST helpers rule comma() -> Comma<'input, 'a> @@ -2877,6 +2907,48 @@ fn make_strings<'input, 'a>( })) } +fn make_tstring_expression<'input, 'a>( + lbrace_tok: TokenRef<'input, 'a>, + expression: Expression<'input, 'a>, + eq: Option>, + conversion_pair: Option<(TokenRef<'input, 'a>, &'a str)>, + format_pair: Option<( + TokenRef<'input, 'a>, + Vec>, + )>, + rbrace_tok: TokenRef<'input, 'a>, +) -> TemplatedStringExpression<'input, 'a> { + let equal: Option> = eq.map(make_assign_equal); + let (conversion_tok, conversion) = if let Some((t, c)) = conversion_pair { + (Some(t), Some(c)) + } else { + (None, None) + }; + let (format_tok, format_spec) = if let Some((t, f)) = format_pair { + (Some(t), Some(f)) + } else { + (None, None) + }; + let after_expr_tok = if equal.is_some() { + None + } else if let Some(tok) = conversion_tok { + Some(tok) + } else if let Some(tok) = format_tok { + Some(tok) + } else { + Some(rbrace_tok) + }; + + TemplatedStringExpression { + expression, + conversion, + format_spec, + equal, + lbrace_tok, + after_expr_tok, + } +} + fn make_fstring_expression<'input, 'a>( lbrace_tok: TokenRef<'input, 'a>, expression: Expression<'input, 'a>, @@ -2933,6 +3005,20 @@ fn make_fstring<'input, 'a>( } } +fn make_tstring<'input, 'a>( + start: &'a str, + parts: Vec>, + end: &'a str, +) -> TemplatedString<'input, 'a> { + TemplatedString { + start, + parts, + end, + lpar: Default::default(), + rpar: Default::default(), + } +} + fn make_finally<'input, 'a>( finally_tok: TokenRef<'input, 'a>, colon_tok: TokenRef<'input, 'a>, diff --git a/native/libcst/src/tokenizer/core/mod.rs b/native/libcst/src/tokenizer/core/mod.rs index 477bd868..120b6163 100644 --- a/native/libcst/src/tokenizer/core/mod.rs +++ b/native/libcst/src/tokenizer/core/mod.rs @@ -66,8 +66,9 @@ use std::fmt::Debug; use std::fmt::Formatter; use std::rc::Rc; +use crate::tokenizer::core::string_types::FTStringType; use crate::tokenizer::{ - core::string_types::{FStringNode, StringQuoteChar, StringQuoteSize}, + core::string_types::{FTStringNode, StringQuoteChar, StringQuoteSize}, operators::OPERATOR_RE, text_position::{TextPosition, TextPositionSnapshot}, whitespace_parser::State as WhitespaceState, @@ -86,7 +87,7 @@ thread_local! { static SPACE_TAB_FORMFEED_RE: Regex = Regex::new(r"\A[ \f\t]+").expect("regex"); static ANY_NON_NEWLINE_RE: Regex = Regex::new(r"\A[^\r\n]+").expect("regex"); static STRING_PREFIX_RE: Regex = - Regex::new(r"\A(?i)(u|[bf]r|r[bf]|r|b|f)").expect("regex"); + Regex::new(r"\A(?i)(u|[bf]r|r[bft]|r|b|f|t)").expect("regex"); static POTENTIAL_IDENTIFIER_TAIL_RE: Regex = Regex::new(r"\A([a-zA-Z0-9_]|[^\x00-\x7f])+").expect("regex"); static DECIMAL_DOT_DIGIT_RE: Regex = Regex::new(r"\A\.[0-9]").expect("regex"); @@ -118,6 +119,9 @@ pub enum TokType { FStringStart, FStringString, FStringEnd, + TStringStart, + TStringString, + TStringEnd, EndMarker, } @@ -222,8 +226,8 @@ pub struct TokState<'t> { /// /// Supporting this at the tokenizer-level is pretty nasty and adds a lot of complexity. /// Eventually, we should probably support this at the parser-level instead. - split_fstring: bool, - fstring_stack: Vec, + split_ftstring: bool, + ftstring_stack: Vec, missing_nl_before_eof: bool, } @@ -233,7 +237,7 @@ pub struct TokConfig { /// identifiers, depending on if they're being used in the context of an async function. This /// breaks async comprehensions outside of async functions. pub async_hacks: bool, - pub split_fstring: bool, + pub split_ftstring: bool, // Not currently supported: // type_comments: bool, } @@ -272,8 +276,8 @@ impl<'t> TokState<'t> { async_def: false, async_def_indent: 0, async_def_nl: false, - split_fstring: config.split_fstring, - fstring_stack: Vec::new(), + split_ftstring: config.split_ftstring, + ftstring_stack: Vec::new(), missing_nl_before_eof: text.is_empty() || text.as_bytes()[text.len() - 1] != b'\n', } } @@ -285,18 +289,18 @@ impl<'t> TokState<'t> { /// Implementation of `next()`, wrapped by next() to allow for easier error handling. Roughly /// equivalent to `tok_get` in the C source code. fn next_inner(&mut self) -> Result> { - if self.split_fstring { - if let Some(tos) = self.fstring_stack.last() { + if self.split_ftstring { + if let Some(tos) = self.ftstring_stack.last() { if !tos.is_in_expr() { self.start_pos = (&self.text_pos).into(); let is_in_format_spec = tos.is_in_format_spec(); let is_raw_string = tos.is_raw_string; if let Some(tok) = - self.maybe_consume_fstring_string(is_in_format_spec, is_raw_string)? + self.maybe_consume_ftstring_string(is_in_format_spec, is_raw_string)? { return Ok(tok); } - if let Some(tok) = self.maybe_consume_fstring_end() { + if let Some(tok) = self.maybe_consume_ftstring_end() { return Ok(tok); } } @@ -362,8 +366,11 @@ impl<'t> TokState<'t> { Some('\n') => { self.text_pos.next(); self.at_bol = true; - if self.split_fstring - && self.fstring_stack.last().map(|node| node.allow_multiline()) + if self.split_ftstring + && self + .ftstring_stack + .last() + .map(|node| node.allow_multiline()) == Some(false) { Err(TokError::UnterminatedString) @@ -420,7 +427,7 @@ impl<'t> TokState<'t> { Some(ch @ '(') | Some(ch @ '[') | Some(ch @ '{') => { self.text_pos.next(); - if let Some(tos) = self.fstring_stack.last_mut() { + if let Some(tos) = self.ftstring_stack.last_mut() { tos.open_parentheses(); } self.paren_stack.push((ch, self.text_pos.line_number())); @@ -429,7 +436,7 @@ impl<'t> TokState<'t> { Some(closing @ ')') | Some(closing @ ']') | Some(closing @ '}') => { self.text_pos.next(); - if let Some(tos) = self.fstring_stack.last_mut() { + if let Some(tos) = self.ftstring_stack.last_mut() { tos.close_parentheses(); } if let Some((opening, line_number)) = self.paren_stack.pop() { @@ -454,7 +461,7 @@ impl<'t> TokState<'t> { Some(':') if self - .fstring_stack + .ftstring_stack .last() .map(|tos| tos.parentheses_count - tos.format_spec_count == 1) .unwrap_or(false) => @@ -465,9 +472,9 @@ impl<'t> TokState<'t> { // // >>> f'{x:=10}' # Valid, passes '=10' to formatter let tos = self - .fstring_stack + .ftstring_stack .last_mut() - .expect("fstring_stack is not empty"); + .expect("ftstring_stack is not empty"); tos.format_spec_count += 1; self.text_pos.next(); Ok(TokType::Op) @@ -624,20 +631,27 @@ impl<'t> TokState<'t> { } fn consume_identifier_or_prefixed_string(&mut self) -> Result> { - // Process the various legal combinations of b"", r"", u"", and f"". + // Process the various legal combinations of b"", r"", u"",f"", and t"". if STRING_PREFIX_RE.with(|r| self.text_pos.consume(r)) { if let Some('"') | Some('\'') = self.text_pos.peek() { // We found a string, not an identifier. Bail! - if self.split_fstring - && self + if self.split_ftstring { + let res = match self .text_pos .slice_from_start_pos(&self.start_pos) - .contains(&['f', 'F'][..]) - { - return self.consume_fstring_start(); - } else { - return self.consume_string(); + .chars() + .find(|c| matches!(c, 'f' | 'F' | 't' | 'T')) + { + Some('f' | 'F') => Some(FTStringType::FString), + Some('t' | 'T') => Some(FTStringType::TString), + _ => None, + }; + if let Some(str_type) = res { + // Consume the prefix and return the start token + return self.consume_prefixed_string_start(str_type); + } } + return self.consume_string(); } } else { // the next character must be a potential identifier start, aka `[a-zA-Z_]|[^\x00-\x7f]` @@ -880,24 +894,43 @@ impl<'t> TokState<'t> { Ok(TokType::String) } - fn consume_fstring_start(&mut self) -> Result> { + fn consume_prefixed_string_start( + &mut self, + str_type: FTStringType, + ) -> Result> { + // Consumes everything after the (f|t) but before the actual string. let (quote_char, quote_size) = self.consume_open_quote(); let is_raw_string = self .text_pos .slice_from_start_pos(&self.start_pos) .contains(&['r', 'R'][..]); - self.fstring_stack - .push(FStringNode::new(quote_char, quote_size, is_raw_string)); - Ok(TokType::FStringStart) + self.ftstring_stack.push(FTStringNode::new( + quote_char, + quote_size, + is_raw_string, + str_type.clone(), + )); + + match str_type { + FTStringType::FString => Ok(TokType::FStringStart), + FTStringType::TString => Ok(TokType::TStringStart), + } } - fn maybe_consume_fstring_string( + fn maybe_consume_ftstring_string( &mut self, is_in_format_spec: bool, is_raw_string: bool, ) -> Result, TokError<'t>> { - let allow_multiline = - self.fstring_stack.last().map(|node| node.allow_multiline()) == Some(true); + let allow_multiline = self + .ftstring_stack + .last() + .map(|node| node.allow_multiline()) + == Some(true); + let str_type = self + .ftstring_stack + .last() + .map(|node| node.string_type.clone()); let mut in_named_unicode: bool = false; let mut ok_result = Ok(None); // value to return if we reach the end and don't error out 'outer: loop { @@ -910,7 +943,7 @@ impl<'t> TokState<'t> { } (ch @ Some('\''), _) | (ch @ Some('"'), _) => { // see if this actually terminates the most recent fstring - if let Some(node) = self.fstring_stack.last() { + if let Some(node) = self.ftstring_stack.last() { if ch == Some(node.quote_char.into()) { match node.quote_size { StringQuoteSize::Single => { @@ -999,22 +1032,30 @@ impl<'t> TokState<'t> { self.text_pos.next(); } } - ok_result = Ok(Some(TokType::FStringString)); + ok_result = match str_type { + Some(FTStringType::FString) => Ok(Some(TokType::FStringString)), + Some(FTStringType::TString) => Ok(Some(TokType::TStringString)), + None => unreachable!("We should always have a string type"), + }; } ok_result } - fn maybe_consume_fstring_end(&mut self) -> Option { + fn maybe_consume_ftstring_end(&mut self) -> Option { let ch = self.text_pos.peek(); - if let Some(node) = self.fstring_stack.last() { + if let Some(node) = self.ftstring_stack.last() { if ch == Some(node.quote_char.into()) { if node.quote_size == StringQuoteSize::Triple { self.text_pos.consume(node.quote_char.triple_str()); } else { self.text_pos.next(); // already matched } - self.fstring_stack.pop(); - return Some(TokType::FStringEnd); + let tok_type = match node.string_type { + FTStringType::FString => TokType::FStringEnd, + FTStringType::TString => TokType::TStringEnd, + }; + self.ftstring_stack.pop(); + return Some(tok_type); } } None diff --git a/native/libcst/src/tokenizer/core/string_types.rs b/native/libcst/src/tokenizer/core/string_types.rs index b04ccb39..09a51851 100644 --- a/native/libcst/src/tokenizer/core/string_types.rs +++ b/native/libcst/src/tokenizer/core/string_types.rs @@ -67,7 +67,13 @@ impl TryFrom> for StringQuoteChar { } #[derive(Clone)] -pub struct FStringNode { +pub enum FTStringType { + FString, + TString, +} + +#[derive(Clone)] +pub struct FTStringNode { pub quote_char: StringQuoteChar, pub quote_size: StringQuoteSize, pub parentheses_count: usize, @@ -75,13 +81,16 @@ pub struct FStringNode { // In the syntax there can be multiple format_spec's nested: {x:{y:3}} pub format_spec_count: usize, pub is_raw_string: bool, + // ftstring type; either f-string or a t-string + pub string_type: FTStringType, } -impl FStringNode { +impl FTStringNode { pub fn new( quote_char: StringQuoteChar, quote_size: StringQuoteSize, is_raw_string: bool, + string_type: FTStringType, ) -> Self { Self { quote_char, @@ -90,6 +99,7 @@ impl FStringNode { string_start: None, format_spec_count: 0, is_raw_string, + string_type, } } diff --git a/native/libcst/src/tokenizer/operators.rs b/native/libcst/src/tokenizer/operators.rs index 3252f774..51352900 100644 --- a/native/libcst/src/tokenizer/operators.rs +++ b/native/libcst/src/tokenizer/operators.rs @@ -60,7 +60,7 @@ pub const OPERATORS: &[&str] = &[ "->", // RARROW "...", // ELLIPSIS ":=", // COLONEQUAL - // Not a real operator, but needed to support the split_fstring feature + // Not a real operator, but needed to support the split_ftstring feature "!", // The fake operator added by PEP 401. Technically only valid if used with: // diff --git a/native/libcst/src/tokenizer/tests.rs b/native/libcst/src/tokenizer/tests.rs index af79971d..20188f47 100644 --- a/native/libcst/src/tokenizer/tests.rs +++ b/native/libcst/src/tokenizer/tests.rs @@ -11,7 +11,7 @@ use crate::tokenizer::core::{TokConfig, TokError, TokState, TokType}; fn default_config() -> TokConfig { TokConfig { async_hacks: false, - split_fstring: false, + split_ftstring: false, } } @@ -534,7 +534,7 @@ fn test_string_prefix() { Ok(vec![(TokType::String, r#"r'\\'"#)]), ); let config = TokConfig { - split_fstring: true, + split_ftstring: true, ..default_config() }; assert_eq!( @@ -564,9 +564,9 @@ fn test_string_prefix() { } #[test] -fn test_split_fstring() { +fn test_split_ftstring() { let config = TokConfig { - split_fstring: true, + split_ftstring: true, ..default_config() }; @@ -662,7 +662,7 @@ fn test_split_fstring() { #[test] fn test_fstring_escapes() { let config = TokConfig { - split_fstring: true, + split_ftstring: true, ..default_config() }; assert_eq!( @@ -831,7 +831,7 @@ fn test_inconsistent_indentation_at_eof() { #[test] fn test_nested_f_string_specs() { let config = TokConfig { - split_fstring: true, + split_ftstring: true, ..default_config() }; assert_eq!( @@ -857,7 +857,7 @@ fn test_nested_f_string_specs() { #[test] fn test_nested_f_strings() { let config = TokConfig { - split_fstring: true, + split_ftstring: true, ..default_config() }; assert_eq!( @@ -875,3 +875,45 @@ fn test_nested_f_strings() { ]) ) } +#[test] +fn test_can_tokenize_t_string_basic() { + let config = TokConfig { + split_ftstring: true, + ..default_config() + }; + assert_eq!( + tokenize_all("t'Nothing to see here, move along'", &config), + Ok(vec![ + (TokType::TStringStart, "t'"), + (TokType::TStringString, "Nothing to see here, move along"), + (TokType::TStringEnd, "'") + ]) + ) +} +#[test] +fn test_can_tokenize_f_and_t_strings() { + let config = TokConfig { + split_ftstring: true, + ..default_config() + }; + assert_eq!( + tokenize_all("t\"TMiddle{f'FMiddle{t'{2}'}'}\"", &config), + Ok(vec![ + (TokType::TStringStart, "t\""), + (TokType::TStringString, "TMiddle"), + (TokType::Op, "{"), + (TokType::FStringStart, "f'"), + (TokType::FStringString, "FMiddle"), + (TokType::Op, "{"), + (TokType::TStringStart, "t'"), + (TokType::Op, "{"), + (TokType::Number, "2"), + (TokType::Op, "}"), + (TokType::TStringEnd, "'"), + (TokType::Op, "}"), + (TokType::FStringEnd, "'"), + (TokType::Op, "}"), + (TokType::TStringEnd, "\"") + ]) + ) +} diff --git a/native/libcst/tests/fixtures/super_strings.py b/native/libcst/tests/fixtures/super_strings.py index 25eee0e0..369270ab 100644 --- a/native/libcst/tests/fixtures/super_strings.py +++ b/native/libcst/tests/fixtures/super_strings.py @@ -48,3 +48,11 @@ f'some words {a+b:.3f} more words {c+d=} final words' f"{'':*^{1:{1}}}" f"{'':*^{1:{1:{1}}}}" f"{f"{f"{f"{f"{f"{1+1}"}"}"}"}"}" + + +t'Nothing to see here, move along' +t"User {action}: {amount:.2f} {item}" +t"

HTML is code {too}

" +t"value={value!r}" +t"This wrinkles my brain {value:.{precision}f}" +_ = t"everything" + t" is {tstrings}" From 3b5329aa2022d97972d2f58eefe61b759b8e56c1 Mon Sep 17 00:00:00 2001 From: martin <48778384+drinkmorewaterr@users.noreply.github.com> Date: Tue, 9 Sep 2025 11:16:49 -0400 Subject: [PATCH 139/151] feat: add support for PEP758 (#1401) 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 --- libcst/_nodes/tests/test_try.py | 60 +++++++++++++++++++ native/libcst/src/parser/grammar.rs | 37 +++++++++--- .../libcst/tests/fixtures/terrible_tries.py | 22 +++++++ 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/libcst/_nodes/tests/test_try.py b/libcst/_nodes/tests/test_try.py index 5704d098..c5ae2462 100644 --- a/libcst/_nodes/tests/test_try.py +++ b/libcst/_nodes/tests/test_try.py @@ -344,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: @@ -576,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: diff --git a/native/libcst/src/parser/grammar.rs b/native/libcst/src/parser/grammar.rs index 76920d66..86823961 100644 --- a/native/libcst/src/parser/grammar.rs +++ b/native/libcst/src/parser/grammar.rs @@ -554,12 +554,21 @@ parser! { } // Except statement - rule except_block() -> ExceptHandler<'input, 'a> = kw:lit("except") e:expression() a:(k:lit("as") n:name() {(k, n)})? col:lit(":") b:block() { make_except(kw, Some(e), a, col, b) } + / kw:lit("except") e:expression() other:(c:comma() ex:expression() {(c, ex)})+ tc:(c:comma())? + col:lit(":") b:block() { + let tuple = Expression::Tuple(Box::new(Tuple { + elements: comma_separate(expr_to_element(e), other.into_iter().map(|(comma, expr)| (comma, expr_to_element(expr))).collect(), tc), + lpar: vec![], + rpar: vec![], + })); + + make_except(kw, Some(tuple), None, col, b) + } / kw:lit("except") col:lit(":") b:block() { make_except(kw, None, None, col, b) } @@ -569,6 +578,16 @@ parser! { a:(k:lit("as") n:name() {(k, n)})? col:lit(":") b:block() { make_except_star(kw, star, e, a, col, b) } + / kw:lit("except") star:lit("*") e:expression() other:(c:comma() ex:expression() {(c, ex)})+ tc:(c:comma())? + col:lit(":") b:block() { + let tuple = Expression::Tuple(Box::new(Tuple { + elements: comma_separate(expr_to_element(e), other.into_iter().map(|(comma, expr)| (comma, expr_to_element(expr))).collect(), tc), + lpar: vec![], + rpar: vec![], + })); + + make_except_star(kw, star, tuple, None, col, b) + } rule finally_block() -> Finally<'input, 'a> = kw:lit("finally") col:lit(":") b:block() { @@ -1550,22 +1569,22 @@ parser! { rule separated(el: rule, sep: rule) -> (El, Vec<(Sep, El)>) = e:el() rest:(s:sep() e:el() {(s, e)})* {(e, rest)} - rule traced(e: rule) -> T = - &(_* { + rule traced(e: rule) -> T = + &(_* { #[cfg(feature = "trace")] { println!("[PEG_INPUT_START]"); println!("{}", input); println!("[PEG_TRACE_START]"); } - }) - e:e()? {? + }) + e:e()? {? #[cfg(feature = "trace")] - println!("[PEG_TRACE_STOP]"); - e.ok_or("") - } + println!("[PEG_TRACE_STOP]"); + e.ok_or("") + } - } + } } #[allow(clippy::too_many_arguments)] diff --git a/native/libcst/tests/fixtures/terrible_tries.py b/native/libcst/tests/fixtures/terrible_tries.py index 91d6831e..eb5429cc 100644 --- a/native/libcst/tests/fixtures/terrible_tries.py +++ b/native/libcst/tests/fixtures/terrible_tries.py @@ -69,3 +69,25 @@ except foo: pass #9 + +try: + pass +except (foo, bar): + pass + +try: + pass +except foo, bar: + pass + +try: + pass +except (foo, bar), baz: + pass +else: + pass + +try: + pass +except* something, somethingelse: + pass \ No newline at end of file From 47cacb69a32520be52975527e0cc87a4b4d5cd77 Mon Sep 17 00:00:00 2001 From: martin <48778384+drinkmorewaterr@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:14:29 -0400 Subject: [PATCH 140/151] bump version to 1.8.4 (#1402) --- CHANGELOG.md | 12 ++++++++++++ README.rst | 2 +- native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 4 ++-- native/libcst_derive/Cargo.toml | 2 +- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b2bd514..96b415e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# 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 diff --git a/README.rst b/README.rst index 0919ed8f..aaff5f41 100644 --- a/README.rst +++ b/README.rst @@ -37,7 +37,7 @@ A Concrete Syntax Tree (CST) parser and serializer library for Python .. intro-start -LibCST parses Python 3.0 -> 3.13 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. diff --git a/native/Cargo.lock b/native/Cargo.lock index 7d39b6ec..8a05c928 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -286,7 +286,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libcst" -version = "1.8.3" +version = "1.8.4" dependencies = [ "annotate-snippets", "criterion", @@ -304,7 +304,7 @@ dependencies = [ [[package]] name = "libcst_derive" -version = "1.8.3" +version = "1.8.4" dependencies = [ "quote", "syn", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index b3abfa63..42f1444a 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "libcst" -version = "1.8.3" +version = "1.8.4" authors = ["LibCST Developers"] edition = "2018" rust-version = "1.70" @@ -42,7 +42,7 @@ peg = "0.8.5" annotate-snippets = "0.11.5" regex = "1.11.2" memchr = "2.7.4" -libcst_derive = { path = "../libcst_derive", version = "1.8.3" } +libcst_derive = { path = "../libcst_derive", version = "1.8.4" } [dev-dependencies] criterion = { version = "0.6.0", features = ["html_reports"] } diff --git a/native/libcst_derive/Cargo.toml b/native/libcst_derive/Cargo.toml index 9541f243..72c46021 100644 --- a/native/libcst_derive/Cargo.toml +++ b/native/libcst_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libcst_derive" -version = "1.8.3" +version = "1.8.4" edition = "2018" description = "Proc macro helpers for libcst." license = "MIT" From de5635394b5627cb86656147ba2f96e835ed3d44 Mon Sep 17 00:00:00 2001 From: martin <48778384+drinkmorewaterr@users.noreply.github.com> Date: Thu, 25 Sep 2025 23:44:58 -0400 Subject: [PATCH 141/151] fix: circular import error (#1406) * fix: circular import error --- libcst/_parser/entrypoints.py | 3 ++- libcst/tests/test_import.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 libcst/tests/test_import.py diff --git a/libcst/_parser/entrypoints.py b/libcst/_parser/entrypoints.py index d034258c..bab45ece 100644 --- a/libcst/_parser/entrypoints.py +++ b/libcst/_parser/entrypoints.py @@ -12,7 +12,6 @@ information from functools import partial from typing import Union -from libcst import native from libcst._nodes.base import CSTNode from libcst._nodes.expression import BaseExpression from libcst._nodes.module import Module @@ -34,6 +33,8 @@ def _parse( encoding, source_str = convert_to_utf8(source, partial=config) + from libcst import native + if entrypoint == "file_input": parse = partial(native.parse_module, encoding=encoding) elif entrypoint == "stmt_input": diff --git a/libcst/tests/test_import.py b/libcst/tests/test_import.py new file mode 100644 index 00000000..cad8883d --- /dev/null +++ b/libcst/tests/test_import.py @@ -0,0 +1,12 @@ +# 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 unittest import TestCase + + +class TestImport(TestCase): + def test_import_libcst(self) -> None: + import libcst # noqa: F401 From 7c906eb47c02dca8d5b4273a032805516361c8cb Mon Sep 17 00:00:00 2001 From: martin <48778384+drinkmorewaterr@users.noreply.github.com> Date: Fri, 26 Sep 2025 01:03:35 -0400 Subject: [PATCH 142/151] bump version to 1.8.5 (#1407) --- CHANGELOG.md | 6 ++++++ native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 4 ++-- native/libcst_derive/Cargo.toml | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96b415e5..9cd743d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 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 diff --git a/native/Cargo.lock b/native/Cargo.lock index 8a05c928..4ca4809c 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -286,7 +286,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libcst" -version = "1.8.4" +version = "1.8.5" dependencies = [ "annotate-snippets", "criterion", @@ -304,7 +304,7 @@ dependencies = [ [[package]] name = "libcst_derive" -version = "1.8.4" +version = "1.8.5" dependencies = [ "quote", "syn", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 42f1444a..d65a60cc 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "libcst" -version = "1.8.4" +version = "1.8.5" authors = ["LibCST Developers"] edition = "2018" rust-version = "1.70" @@ -42,7 +42,7 @@ peg = "0.8.5" annotate-snippets = "0.11.5" regex = "1.11.2" memchr = "2.7.4" -libcst_derive = { path = "../libcst_derive", version = "1.8.4" } +libcst_derive = { path = "../libcst_derive", version = "1.8.5" } [dev-dependencies] criterion = { version = "0.6.0", features = ["html_reports"] } diff --git a/native/libcst_derive/Cargo.toml b/native/libcst_derive/Cargo.toml index 72c46021..c054c32d 100644 --- a/native/libcst_derive/Cargo.toml +++ b/native/libcst_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libcst_derive" -version = "1.8.4" +version = "1.8.5" edition = "2018" description = "Proc macro helpers for libcst." license = "MIT" From 6f5da5f99873e4c17e32ca221a3ddde3fb1ea354 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Oct 2025 21:46:50 -0700 Subject: [PATCH 143/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/pypi_upload.yml | 2 +- .github/workflows/zizmor.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8d7d244..dd3665ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: - "3.14t" steps: - name: Install uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@v7 with: version: "0.7.13" python-version: ${{ matrix.python-version }} @@ -51,7 +51,7 @@ jobs: fetch-depth: 0 persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@v7 with: version: "0.7.13" python-version: "3.10" @@ -67,7 +67,7 @@ jobs: fetch-depth: 0 persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@v7 with: version: "0.7.13" python-version: "3.10" @@ -82,7 +82,7 @@ jobs: fetch-depth: 0 persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@v7 with: version: "0.7.13" python-version: "3.10" diff --git a/.github/workflows/pypi_upload.yml b/.github/workflows/pypi_upload.yml index d9d0bcb5..04434a24 100644 --- a/.github/workflows/pypi_upload.yml +++ b/.github/workflows/pypi_upload.yml @@ -34,7 +34,7 @@ jobs: with: python-version: "3.10" - name: Install uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@v7 with: version: "0.7.13" enable-cache: false diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 26adcaf2..32d62335 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -21,7 +21,7 @@ jobs: persist-credentials: false - name: Install the latest version of uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@v7 - name: Run zizmor 🌈 run: uvx zizmor --format sarif . > results.sarif From 129b20f4769a2b26b41b262fb88f9aaf8754930b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Oct 2025 21:47:18 -0700 Subject: [PATCH 144/151] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/zizmor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 32d62335..47fdfe00 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -29,7 +29,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: results.sarif category: zizmor \ No newline at end of file From 421f7d34004de18391014319ec6a818322637a8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Oct 2025 21:47:40 -0700 Subject: [PATCH 145/151] build(deps): bump pypa/cibuildwheel from 3.1.4 to 3.2.1 (#1414) 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9f1c6d9f..742a4176 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: if: github.event_name != 'release' run: echo CIBW_ENABLE=cpython-prerelease >> $GITHUB_ENV - name: Build wheels - uses: pypa/cibuildwheel@v3.1.4 + uses: pypa/cibuildwheel@v3.2.1 - uses: actions/upload-artifact@v4 with: path: wheelhouse/*.whl From 73b17d8449034b520e7128c8a4bd100674d1e78c Mon Sep 17 00:00:00 2001 From: Itamar Oren Date: Fri, 24 Oct 2025 13:49:25 -0700 Subject: [PATCH 146/151] Update pyproject.toml for 3.14t (#1417) - Update description to include 3.14 - Add 3.14 and free-threading trove classifiers - Update deps to switch back to pyyaml for 3.14 --- pyproject.toml | 7 +++- uv.lock | 111 ++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 106 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 394eca43..f29b2474 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools", "setuptools-scm", "setuptools-rust", "wheel"] [project] name = "libcst" -description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.13 programs." +description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.14 programs." readme = "README.rst" dynamic = ["version"] license = { file = "LICENSE" } @@ -15,12 +15,15 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: Free Threading", "Typing :: Typed", ] requires-python = ">=3.9" dependencies = [ "pyyaml>=5.2; python_version < '3.13'", - "pyyaml-ft>=8.0.0; python_version >= '3.13'", + "pyyaml-ft>=8.0.0; python_version == '3.13'", + "pyyaml>=6.0.3; python_version >= '3.14'", "typing-extensions; python_version < '3.10'", ] diff --git a/uv.lock b/uv.lock index 2cc4db06..f271e1ea 100644 --- a/uv.lock +++ b/uv.lock @@ -2,7 +2,8 @@ version = 1 revision = 2 requires-python = ">=3.9" resolution-markers = [ - "python_full_version >= '3.13'", + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", "python_full_version >= '3.11' and python_full_version < '3.13'", "python_full_version == '3.10.*'", "python_full_version < '3.10'", @@ -25,7 +26,8 @@ name = "alabaster" version = "1.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13'", + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", "python_full_version >= '3.11' and python_full_version < '3.13'", "python_full_version == '3.10.*'", ] @@ -400,7 +402,8 @@ name = "click" version = "8.2.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13'", + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", "python_full_version >= '3.11' and python_full_version < '3.13'", "python_full_version == '3.10.*'", ] @@ -841,7 +844,8 @@ name = "ipython" version = "9.3.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13'", + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", "python_full_version >= '3.11' and python_full_version < '3.13'", ] dependencies = [ @@ -1062,7 +1066,8 @@ dependencies = [ { name = "jsonschema", extra = ["format-nongpl"] }, { name = "packaging" }, { name = "python-json-logger" }, - { name = "pyyaml" }, + { name = "pyyaml", version = "6.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "pyyaml", version = "6.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "referencing" }, { name = "rfc3339-validator" }, { name = "rfc3986-validator" }, @@ -1205,8 +1210,9 @@ wheels = [ name = "libcst" source = { editable = "." } dependencies = [ - { name = "pyyaml", marker = "python_full_version < '3.13'" }, - { name = "pyyaml-ft", marker = "python_full_version >= '3.13'" }, + { name = "pyyaml", version = "6.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, + { name = "pyyaml", version = "6.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, + { name = "pyyaml-ft", marker = "python_full_version == '3.13.*'" }, { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] @@ -1257,7 +1263,8 @@ docs = [ [package.metadata] requires-dist = [ { name = "pyyaml", marker = "python_full_version < '3.13'", specifier = ">=5.2" }, - { name = "pyyaml-ft", marker = "python_full_version >= '3.13'", specifier = ">=8.0.0" }, + { name = "pyyaml", marker = "python_full_version >= '3.14'", specifier = ">=6.0.3" }, + { name = "pyyaml-ft", marker = "python_full_version == '3.13.*'", specifier = ">=8.0.0" }, { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] @@ -1654,7 +1661,8 @@ version = "0.35.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pastel" }, - { name = "pyyaml" }, + { name = "pyyaml", version = "6.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" }, + { name = "pyyaml", version = "6.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d6/b1/d4f4361b278fae10f6074675385ce3acf53c647f8e6eeba22c652f8ba985/poethepoet-0.35.0.tar.gz", hash = "sha256:b396ae862d7626e680bbd0985b423acf71634ce93a32d8b5f38340f44f5fbc3e", size = 66006, upload-time = "2025-06-09T12:58:18.849Z" } @@ -1859,6 +1867,12 @@ wheels = [ name = "pyyaml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.13.*'", + "python_full_version >= '3.11' and python_full_version < '3.13'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, @@ -1908,6 +1922,82 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, ] +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", +] +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, + { url = "https://files.pythonhosted.org/packages/9f/62/67fc8e68a75f738c9200422bf65693fb79a4cd0dc5b23310e5202e978090/pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da", size = 184450, upload-time = "2025-09-25T21:33:00.618Z" }, + { url = "https://files.pythonhosted.org/packages/ae/92/861f152ce87c452b11b9d0977952259aa7df792d71c1053365cc7b09cc08/pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917", size = 174319, upload-time = "2025-09-25T21:33:02.086Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cd/f0cfc8c74f8a030017a2b9c771b7f47e5dd702c3e28e5b2071374bda2948/pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9", size = 737631, upload-time = "2025-09-25T21:33:03.25Z" }, + { url = "https://files.pythonhosted.org/packages/ef/b2/18f2bd28cd2055a79a46c9b0895c0b3d987ce40ee471cecf58a1a0199805/pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5", size = 836795, upload-time = "2025-09-25T21:33:05.014Z" }, + { url = "https://files.pythonhosted.org/packages/73/b9/793686b2d54b531203c160ef12bec60228a0109c79bae6c1277961026770/pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a", size = 750767, upload-time = "2025-09-25T21:33:06.398Z" }, + { url = "https://files.pythonhosted.org/packages/a9/86/a137b39a611def2ed78b0e66ce2fe13ee701a07c07aebe55c340ed2a050e/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926", size = 727982, upload-time = "2025-09-25T21:33:08.708Z" }, + { url = "https://files.pythonhosted.org/packages/dd/62/71c27c94f457cf4418ef8ccc71735324c549f7e3ea9d34aba50874563561/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7", size = 755677, upload-time = "2025-09-25T21:33:09.876Z" }, + { url = "https://files.pythonhosted.org/packages/29/3d/6f5e0d58bd924fb0d06c3a6bad00effbdae2de5adb5cda5648006ffbd8d3/pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0", size = 142592, upload-time = "2025-09-25T21:33:10.983Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0c/25113e0b5e103d7f1490c0e947e303fe4a696c10b501dea7a9f49d4e876c/pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007", size = 158777, upload-time = "2025-09-25T21:33:15.55Z" }, +] + [[package]] name = "pyyaml-ft" version = "8.0.0" @@ -2347,7 +2437,8 @@ name = "sphinx" version = "8.1.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13'", + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", "python_full_version >= '3.11' and python_full_version < '3.13'", "python_full_version == '3.10.*'", ] From c2169d240b5011ea820dc18d7a2452f182c2c5d3 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Tue, 28 Oct 2025 19:37:35 +0000 Subject: [PATCH 147/151] Update PyO3 to 0.26 (#1413) --- native/Cargo.lock | 25 ++++++++++----------- native/libcst/Cargo.toml | 2 +- native/libcst/src/nodes/expression.rs | 8 +++---- native/libcst/src/nodes/macros.rs | 4 ++-- native/libcst/src/nodes/parser_config.rs | 26 +++++++++++----------- native/libcst/src/nodes/py_cached.rs | 16 +++++++------- native/libcst/src/nodes/traits.rs | 28 ++++++++++++------------ native/libcst/src/parser/errors.rs | 2 +- native/libcst/src/py.rs | 12 +++++----- 9 files changed, 61 insertions(+), 62 deletions(-) diff --git a/native/Cargo.lock b/native/Cargo.lock index 4ca4809c..0ba138ea 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -355,9 +355,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oorandom" @@ -443,9 +443,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a" +checksum = "7ba0117f4212101ee6544044dae45abe1083d30ce7b29c4b5cbdfa2354e07383" dependencies = [ "indoc", "libc", @@ -460,19 +460,18 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598" +checksum = "4fc6ddaf24947d12a9aa31ac65431fb1b851b8f4365426e182901eabfb87df5f" dependencies = [ - "once_cell", "target-lexicon", ] [[package]] name = "pyo3-ffi" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c" +checksum = "025474d3928738efb38ac36d4744a74a400c901c7596199e20e45d98eb194105" dependencies = [ "libc", "pyo3-build-config", @@ -480,9 +479,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8725c0a622b374d6cb051d11a0983786448f7785336139c3c94f5aa6bef7e50" +checksum = "2e64eb489f22fe1c95911b77c44cc41e7c19f3082fc81cce90f657cdc42ffded" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -492,9 +491,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4109984c22491085343c05b0dbc54ddc405c3cf7b4374fc533f5c3313a572ccc" +checksum = "100246c0ecf400b475341b8455a9213344569af29a3c841d29270e53102e0fcf" dependencies = [ "heck", "proc-macro2", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index d65a60cc..4fb029ca 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -36,7 +36,7 @@ trace = ["peg/trace"] [dependencies] paste = "1.0.15" -pyo3 = { version = "0.25.1", optional = true } +pyo3 = { version = "0.26", optional = true } thiserror = "2.0.12" peg = "0.8.5" annotate-snippets = "0.11.5" diff --git a/native/libcst/src/nodes/expression.rs b/native/libcst/src/nodes/expression.rs index 961eee9f..c72d301d 100644 --- a/native/libcst/src/nodes/expression.rs +++ b/native/libcst/src/nodes/expression.rs @@ -2670,8 +2670,8 @@ mod py { use crate::nodes::traits::py::TryIntoPy; // TODO: this could be a derive helper attribute to override the python class name - impl<'a> TryIntoPy for Element<'a> { - fn try_into_py(self, py: pyo3::Python) -> pyo3::PyResult { + impl<'a> TryIntoPy> for Element<'a> { + fn try_into_py(self, py: pyo3::Python) -> pyo3::PyResult> { match self { Self::Starred(s) => s.try_into_py(py), Self::Simple { value, comma } => { @@ -2699,8 +2699,8 @@ mod py { } // TODO: this could be a derive helper attribute to override the python class name - impl<'a> TryIntoPy for DictElement<'a> { - fn try_into_py(self, py: pyo3::Python) -> pyo3::PyResult { + impl<'a> TryIntoPy> for DictElement<'a> { + fn try_into_py(self, py: pyo3::Python) -> pyo3::PyResult> { match self { Self::Starred(s) => s.try_into_py(py), Self::Simple { diff --git a/native/libcst/src/nodes/macros.rs b/native/libcst/src/nodes/macros.rs index c9ed49c1..11028b8c 100644 --- a/native/libcst/src/nodes/macros.rs +++ b/native/libcst/src/nodes/macros.rs @@ -17,8 +17,8 @@ macro_rules! py_import { ( $module_name:expr, $member_name:expr, $getter_fn:ident ) => { paste::paste! { static [] - : pyo3::once_cell::GILOnceCell> - = pyo3::once_cell::GILOnceCell::new(); + : pyo3::once_cell::PyOnceLock>> + = pyo3::once_cell::PyOnceLock::new(); fn $getter_fn<'py>(py: pyo3::Python<'py>) -> pyo3::PyResult<&'py pyo3::PyAny> { Ok([].get_or_init(py, || { diff --git a/native/libcst/src/nodes/parser_config.rs b/native/libcst/src/nodes/parser_config.rs index c10d86d5..e274d8df 100644 --- a/native/libcst/src/nodes/parser_config.rs +++ b/native/libcst/src/nodes/parser_config.rs @@ -29,12 +29,12 @@ impl BaseWhitespaceParserConfig { } #[getter] - fn get_lines(&self, py: Python) -> PyObject { + fn get_lines(&self, py: Python) -> Py { self.lines.to_object(py) } #[getter] - fn get_default_newline(&self, py: Python) -> PyObject { + fn get_default_newline(&self, py: Python) -> Py { self.default_newline.to_object(py) } } @@ -62,23 +62,23 @@ impl BaseWhitespaceParserConfig { } } -// These fields are private and PyObject, since we don't currently care about using them from +// These fields are private and Py, since we don't currently care about using them from // within rust. #[pyclass(extends=BaseWhitespaceParserConfig, module="libcst_native.parser_config")] #[text_signature = "(*, lines, encoding, default_indent, default_newline, has_trailing_newline, version, future_imports)"] pub struct ParserConfig { // lines is inherited #[pyo3(get)] - encoding: PyObject, + encoding: Py, #[pyo3(get)] - default_indent: PyObject, + default_indent: Py, // default_newline is inherited #[pyo3(get)] - has_trailing_newline: PyObject, + has_trailing_newline: Py, #[pyo3(get)] - version: PyObject, + version: Py, #[pyo3(get)] - future_imports: PyObject, + future_imports: Py, } #[pymethods] @@ -86,12 +86,12 @@ impl ParserConfig { #[new] fn new( lines: &PySequence, - encoding: PyObject, - default_indent: PyObject, + encoding: Py, + default_indent: Py, default_newline: &PyString, - has_trailing_newline: PyObject, - version: PyObject, - future_imports: PyObject, + has_trailing_newline: Py, + version: Py, + future_imports: Py, ) -> PyResult<(Self, BaseWhitespaceParserConfig)> { Ok(( Self { diff --git a/native/libcst/src/nodes/py_cached.rs b/native/libcst/src/nodes/py_cached.rs index d2398051..307082da 100644 --- a/native/libcst/src/nodes/py_cached.rs +++ b/native/libcst/src/nodes/py_cached.rs @@ -7,11 +7,11 @@ use pyo3::prelude::*; use std::convert::AsRef; use std::ops::Deref; -/// An immutable wrapper around a rust type T and it's PyObject equivalent. Caches the conversion -/// to and from the PyObject. +/// An immutable wrapper around a rust type T and its Py equivalent. Caches the conversion +/// to and from the Py. pub struct PyCached { native: T, - py_object: PyObject, + py_object: Py, } impl PyCached @@ -31,7 +31,7 @@ where T: FromPyObject<'source>, { fn extract(ob: &'source PyAny) -> PyResult { - Python::with_gil(|py| { + Python::attach(|py| { Ok(PyCached { native: ob.extract()?, py_object: ob.to_object(py), @@ -40,14 +40,14 @@ where } } -impl IntoPy for PyCached { - fn into_py(self, _py: Python) -> PyObject { +impl IntoPy> for PyCached { + fn into_py(self, _py: Python) -> Py { self.py_object } } impl ToPyObject for PyCached { - fn to_object(&self, py: Python) -> PyObject { + fn to_object(&self, py: Python) -> Py { self.py_object.clone_ref(py) } } @@ -71,6 +71,6 @@ where T: ToPyObject, { fn from(val: T) -> Self { - Python::with_gil(|py| Self::new(py, val)) + Python::attach(|py| Self::new(py, val)) } } diff --git a/native/libcst/src/nodes/traits.rs b/native/libcst/src/nodes/traits.rs index df61538b..c15a60e1 100644 --- a/native/libcst/src/nodes/traits.rs +++ b/native/libcst/src/nodes/traits.rs @@ -118,7 +118,7 @@ impl<'a, T: Inflate<'a>> Inflate<'a> for Vec { } #[cfg(feature = "py")] pub mod py { - use pyo3::{types::PyTuple, IntoPyObjectExt, PyObject, PyResult, Python}; + use pyo3::{types::PyTuple, IntoPyObjectExt, Py, PyAny, PyResult, Python}; // TODO: replace with upstream implementation once // https://github.com/PyO3/pyo3/issues/1813 is resolved @@ -133,26 +133,26 @@ pub mod py { // } // } - impl TryIntoPy for bool { - fn try_into_py(self, py: Python) -> PyResult { + impl TryIntoPy> for bool { + fn try_into_py(self, py: Python) -> PyResult> { self.into_py_any(py) } } - impl> TryIntoPy for Box + impl>> TryIntoPy> for Box where - T: TryIntoPy, + T: TryIntoPy>, { - fn try_into_py(self, py: Python) -> PyResult { + fn try_into_py(self, py: Python) -> PyResult> { (*self).try_into_py(py) } } - impl TryIntoPy for Option + impl TryIntoPy> for Option where - T: TryIntoPy, + T: TryIntoPy>, { - fn try_into_py(self, py: Python) -> PyResult { + fn try_into_py(self, py: Python) -> PyResult> { Ok(match self { None => py.None(), Some(x) => x.try_into_py(py)?, @@ -160,11 +160,11 @@ pub mod py { } } - impl TryIntoPy for Vec + impl TryIntoPy> for Vec where - T: TryIntoPy, + T: TryIntoPy>, { - fn try_into_py(self, py: Python) -> PyResult { + fn try_into_py(self, py: Python) -> PyResult> { let converted = self .into_iter() .map(|x| x.try_into_py(py)) @@ -174,8 +174,8 @@ pub mod py { } } - impl<'a> TryIntoPy for &'a str { - fn try_into_py(self, py: Python) -> PyResult { + impl<'a> TryIntoPy> for &'a str { + fn try_into_py(self, py: Python) -> PyResult> { self.into_py_any(py) } } diff --git a/native/libcst/src/parser/errors.rs b/native/libcst/src/parser/errors.rs index 8237cd0b..7fb3b740 100644 --- a/native/libcst/src/parser/errors.rs +++ b/native/libcst/src/parser/errors.rs @@ -35,7 +35,7 @@ mod py_error { impl<'a> From> for PyErr { fn from(e: ParserError) -> Self { - Python::with_gil(|py| { + Python::attach(|py| { let lines = match &e { ParserError::TokenizerError(_, text) | ParserError::ParserError(_, text) => { text.lines().collect::>() diff --git a/native/libcst/src/py.rs b/native/libcst/src/py.rs index 57da11e7..68c03744 100644 --- a/native/libcst/src/py.rs +++ b/native/libcst/src/py.rs @@ -11,21 +11,21 @@ use pyo3::prelude::*; pub fn libcst_native(_py: Python, m: &Bound) -> PyResult<()> { #[pyfn(m)] #[pyo3(signature = (source, encoding=None))] - fn parse_module(source: String, encoding: Option<&str>) -> PyResult { + fn parse_module(source: String, encoding: Option<&str>) -> PyResult> { let m = crate::parse_module(source.as_str(), encoding)?; - Python::with_gil(|py| m.try_into_py(py)) + Python::attach(|py| m.try_into_py(py)) } #[pyfn(m)] - fn parse_expression(source: String) -> PyResult { + fn parse_expression(source: String) -> PyResult> { let expr = crate::parse_expression(source.as_str())?; - Python::with_gil(|py| expr.try_into_py(py)) + Python::attach(|py| expr.try_into_py(py)) } #[pyfn(m)] - fn parse_statement(source: String) -> PyResult { + fn parse_statement(source: String) -> PyResult> { let stm = crate::parse_statement(source.as_str())?; - Python::with_gil(|py| stm.try_into_py(py)) + Python::attach(|py| stm.try_into_py(py)) } Ok(()) From b66c0e28229fd91ac145f1a56d7d884e6f8587b4 Mon Sep 17 00:00:00 2001 From: Frank Liu <94014427+frvnkliu@users.noreply.github.com> Date: Sun, 2 Nov 2025 17:27:32 -0800 Subject: [PATCH 148/151] [CodemodCommand] Make `transform_module` `supported_transforms` order deterministic by using `List` over `Dict` (#1424) --- libcst/codemod/_command.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libcst/codemod/_command.py b/libcst/codemod/_command.py index 55a57247..3671a3fb 100644 --- a/libcst/codemod/_command.py +++ b/libcst/codemod/_command.py @@ -6,7 +6,7 @@ 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.codemod._codemod import Codemod @@ -75,13 +75,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) From 9275a8bf7875d08659ce7b266860138bba633410 Mon Sep 17 00:00:00 2001 From: martin <48778384+drinkmorewaterr@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:48:42 -0500 Subject: [PATCH 149/151] bump version to 1.8.6 (#1425) --- CHANGELOG.md | 13 +++++++++++++ MAINTAINERS.md | 7 ++++--- native/Cargo.lock | 4 ++-- native/libcst/Cargo.toml | 4 ++-- native/libcst_derive/Cargo.toml | 2 +- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cd743d1..f72d53f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 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 diff --git a/MAINTAINERS.md b/MAINTAINERS.md index eb94a2a0..a7d79006 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -6,6 +6,7 @@ 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. make a new PR with the above changes, get it reviewed and landed -4. make a new release on Github, create a new tag on publish, and copy the contents of the changelog entry in there -5. 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` \ No newline at end of file +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` diff --git a/native/Cargo.lock b/native/Cargo.lock index 0ba138ea..16ffd999 100644 --- a/native/Cargo.lock +++ b/native/Cargo.lock @@ -286,7 +286,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libcst" -version = "1.8.5" +version = "1.8.6" dependencies = [ "annotate-snippets", "criterion", @@ -304,7 +304,7 @@ dependencies = [ [[package]] name = "libcst_derive" -version = "1.8.5" +version = "1.8.6" dependencies = [ "quote", "syn", diff --git a/native/libcst/Cargo.toml b/native/libcst/Cargo.toml index 4fb029ca..e4c9f45f 100644 --- a/native/libcst/Cargo.toml +++ b/native/libcst/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "libcst" -version = "1.8.5" +version = "1.8.6" authors = ["LibCST Developers"] edition = "2018" rust-version = "1.70" @@ -42,7 +42,7 @@ peg = "0.8.5" annotate-snippets = "0.11.5" regex = "1.11.2" memchr = "2.7.4" -libcst_derive = { path = "../libcst_derive", version = "1.8.5" } +libcst_derive = { path = "../libcst_derive", version = "1.8.6" } [dev-dependencies] criterion = { version = "0.6.0", features = ["html_reports"] } diff --git a/native/libcst_derive/Cargo.toml b/native/libcst_derive/Cargo.toml index c054c32d..bf9959ab 100644 --- a/native/libcst_derive/Cargo.toml +++ b/native/libcst_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libcst_derive" -version = "1.8.5" +version = "1.8.6" edition = "2018" description = "Proc macro helpers for libcst." license = "MIT" From b75343e74e48b33553c1fa3430677c03baef2b4f Mon Sep 17 00:00:00 2001 From: Frank Liu <94014427+frvnkliu@users.noreply.github.com> Date: Wed, 17 Dec 2025 09:28:24 -0800 Subject: [PATCH 150/151] 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. --- libcst/codemod/_command.py | 26 +- libcst/codemod/tests/test_command_helpers.py | 325 +++++++++++++++++++ 2 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 libcst/codemod/tests/test_command_helpers.py diff --git a/libcst/codemod/_command.py b/libcst/codemod/_command.py index 3671a3fb..b7784d30 100644 --- a/libcst/codemod/_command.py +++ b/libcst/codemod/_command.py @@ -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, 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. diff --git a/libcst/codemod/tests/test_command_helpers.py b/libcst/codemod/tests/test_command_helpers.py new file mode 100644 index 00000000..15c461b6 --- /dev/null +++ b/libcst/codemod/tests/test_command_helpers.py @@ -0,0 +1,325 @@ +# 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 Union + +import libcst as cst +from libcst.codemod import CodemodTest, VisitorBasedCodemodCommand + + +class TestRemoveUnusedImportHelper(CodemodTest): + """Tests for the remove_unused_import helper method in CodemodCommand.""" + + def test_remove_unused_import_simple(self) -> None: + """ + Test that remove_unused_import helper method works correctly. + """ + + class RemoveBarImport(VisitorBasedCodemodCommand): + def visit_Module(self, node: cst.Module) -> None: + # Use the helper method to schedule removal + self.remove_unused_import("bar") + + before = """ + import bar + import baz + + def foo() -> None: + pass + """ + after = """ + import baz + + def foo() -> None: + pass + """ + + self.TRANSFORM = RemoveBarImport + self.assertCodemod(before, after) + + def test_remove_unused_import_from_simple(self) -> None: + """ + Test that remove_unused_import helper method works correctly with from imports. + """ + + class RemoveBarFromImport(VisitorBasedCodemodCommand): + def visit_Module(self, node: cst.Module) -> None: + # Use the helper method to schedule removal + self.remove_unused_import("a.b.c", "bar") + + before = """ + from a.b.c import bar, baz + + def foo() -> None: + baz() + """ + after = """ + from a.b.c import baz + + def foo() -> None: + baz() + """ + + self.TRANSFORM = RemoveBarFromImport + self.assertCodemod(before, after) + + def test_remove_unused_import_with_alias(self) -> None: + """ + Test that remove_unused_import helper method works correctly with aliased imports. + """ + + class RemoveBarAsQuxImport(VisitorBasedCodemodCommand): + def visit_Module(self, node: cst.Module) -> None: + # Use the helper method to schedule removal + self.remove_unused_import("a.b.c", "bar", "qux") + + before = """ + from a.b.c import bar as qux, baz + + def foo() -> None: + baz() + """ + after = """ + from a.b.c import baz + + def foo() -> None: + baz() + """ + + self.TRANSFORM = RemoveBarAsQuxImport + self.assertCodemod(before, after) + + +class TestRemoveUnusedImportByNodeHelper(CodemodTest): + """Tests for the remove_unused_import_by_node helper method in CodemodCommand.""" + + def test_remove_unused_import_by_node_simple(self) -> None: + """ + Test that remove_unused_import_by_node helper method works correctly. + """ + + class RemoveBarCallAndImport(VisitorBasedCodemodCommand): + METADATA_DEPENDENCIES = ( + cst.metadata.QualifiedNameProvider, + cst.metadata.ScopeProvider, + ) + + def leave_SimpleStatementLine( + self, + original_node: cst.SimpleStatementLine, + updated_node: cst.SimpleStatementLine, + ) -> Union[cst.RemovalSentinel, cst.SimpleStatementLine]: + # Remove any statement that calls bar() + if cst.matchers.matches( + updated_node, + cst.matchers.SimpleStatementLine( + body=[cst.matchers.Expr(cst.matchers.Call())] + ), + ): + call = cst.ensure_type(updated_node.body[0], cst.Expr).value + if cst.matchers.matches( + call, cst.matchers.Call(func=cst.matchers.Name("bar")) + ): + # Use the helper method to remove imports referenced by this node + self.remove_unused_import_by_node(original_node) + return cst.RemoveFromParent() + return updated_node + + before = """ + from foo import bar, baz + + def fun() -> None: + bar() + baz() + """ + after = """ + from foo import baz + + def fun() -> None: + baz() + """ + + self.TRANSFORM = RemoveBarCallAndImport + self.assertCodemod(before, after) + + +class TestAddNeededImportHelper(CodemodTest): + """Tests for the add_needed_import helper method in CodemodCommand.""" + + def test_add_needed_import_simple(self) -> None: + """ + Test that add_needed_import helper method works correctly. + """ + + class AddBarImport(VisitorBasedCodemodCommand): + def visit_Module(self, node: cst.Module) -> None: + # Use the helper method to schedule import addition + self.add_needed_import("bar") + + before = """ + def foo() -> None: + pass + """ + after = """ + import bar + + def foo() -> None: + pass + """ + + self.TRANSFORM = AddBarImport + self.assertCodemod(before, after) + + def test_add_needed_import_from_simple(self) -> None: + """ + Test that add_needed_import helper method works correctly with from imports. + """ + + class AddBarFromImport(VisitorBasedCodemodCommand): + def visit_Module(self, node: cst.Module) -> None: + # Use the helper method to schedule import addition + self.add_needed_import("a.b.c", "bar") + + before = """ + def foo() -> None: + pass + """ + after = """ + from a.b.c import bar + + def foo() -> None: + pass + """ + + self.TRANSFORM = AddBarFromImport + self.assertCodemod(before, after) + + def test_add_needed_import_with_alias(self) -> None: + """ + Test that add_needed_import helper method works correctly with aliased imports. + """ + + class AddBarAsQuxImport(VisitorBasedCodemodCommand): + def visit_Module(self, node: cst.Module) -> None: + # Use the helper method to schedule import addition + self.add_needed_import("a.b.c", "bar", "qux") + + before = """ + def foo() -> None: + pass + """ + after = """ + from a.b.c import bar as qux + + def foo() -> None: + pass + """ + + self.TRANSFORM = AddBarAsQuxImport + self.assertCodemod(before, after) + + def test_add_needed_import_relative(self) -> None: + """ + Test that add_needed_import helper method works correctly with relative imports. + """ + + class AddRelativeImport(VisitorBasedCodemodCommand): + def visit_Module(self, node: cst.Module) -> None: + # Use the helper method to schedule relative import addition + self.add_needed_import("c", "bar", relative=2) + + before = """ + def foo() -> None: + pass + """ + after = """ + from ..c import bar + + def foo() -> None: + pass + """ + + self.TRANSFORM = AddRelativeImport + self.assertCodemod(before, after) + + +class TestCombinedHelpers(CodemodTest): + """Tests for combining add_needed_import and remove_unused_import helper methods.""" + + def test_add_and_remove_imports(self) -> None: + """ + Test that both helper methods work correctly when used together. + """ + + class ReplaceBarWithBaz(VisitorBasedCodemodCommand): + def visit_Module(self, node: cst.Module) -> None: + # Add new import and remove old one + self.add_needed_import("new_module", "baz") + self.remove_unused_import("old_module", "bar") + + before = """ + from other_module import qux + from old_module import bar + + def foo() -> None: + pass + """ + after = """ + from other_module import qux + from new_module import baz + + def foo() -> None: + pass + """ + + self.TRANSFORM = ReplaceBarWithBaz + self.assertCodemod(before, after) + + def test_add_and_remove_same_import(self) -> None: + """ + Test that both helper methods work correctly when used together. + """ + + class AddAndRemoveBar(VisitorBasedCodemodCommand): + def visit_Module(self, node: cst.Module) -> None: + # Add new import and remove old one + self.add_needed_import("hello_module", "bar") + self.remove_unused_import("hello_module", "bar") + + self.TRANSFORM = AddAndRemoveBar + + before = """ + from other_module import baz + + def foo() -> None: + pass + """ + # Should remain unchanged + self.assertCodemod(before, before) + + before = """ + from other_module import baz + from hello_module import bar + + def foo() -> None: + bar.func() + """ + self.assertCodemod(before, before) + + before = """ + from other_module import baz + from hello_module import bar + + def foo() -> None: + pass + """ + + after = """ + from other_module import baz + + def foo() -> None: + pass + """ + self.assertCodemod(before, after) From c5e40e87693cdd05f7620f4998830117f5720603 Mon Sep 17 00:00:00 2001 From: martin <48778384+drinkmorewaterr@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:01:40 -0500 Subject: [PATCH 151/151] chore: remove macos-13 from ci (#1433) remove macos-13 from ci --- .github/workflows/build.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 742a4176..0df65636 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,10 +10,8 @@ jobs: strategy: fail-fast: false matrix: - # macos-13 is an intel runner, macos-latest is apple silicon os: [ - macos-13, macos-latest, ubuntu-latest, ubuntu-24.04-arm, @@ -32,10 +30,6 @@ jobs: with: python-version: "3.12" - uses: dtolnay/rust-toolchain@stable - - name: Set MACOSX_DEPLOYMENT_TARGET for Intel MacOS - if: matrix.os == 'macos-13' - run: >- - echo MACOSX_DEPLOYMENT_TARGET=10.12 >> $GITHUB_ENV - name: Disable scmtools local scheme if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} run: >-