Skip to content

Instantly share code, notes, and snippets.

@realdimas
Last active August 12, 2025 10:30
Show Gist options
  • Save realdimas/c025cdba50cc05e0f644eb71bf7efbb9 to your computer and use it in GitHub Desktop.
Save realdimas/c025cdba50cc05e0f644eb71bf7efbb9 to your computer and use it in GitHub Desktop.

pylance-patcher

A tool to patch the Pylance extension for compatibility with Cursor and other VS Code forks.

Jump to the main script: pylance_patcher.py

Run as a standalone Python script using uv:

uv run --script https://gist.githubusercontent.com/realdimas/c025cdba50cc05e0f644eb71bf7efbb9/raw/pylance_patcher.py --help

Usage

$ uv run --script pylance_patcher.py --help

 Usage: pylance_patcher.py [OPTIONS] [VERSION]

 Download and patch Pylance VS Code extension.

 Supported versions: 2025.4.1, 2025.6.2, 2025.6.101
 The --vscode-version option clamps the required VS Code version if the extension requires a newer
 version. For example, use --vscode-version 1.96 to make the extension compatible with VS Code
 1.96.

╭─ Arguments ──────────────────────────────────────────────────────────────────────────────────────╮
│   version      [VERSION]  Pylance version to patch [default: 2025.6.2]                           │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────────────────╮
│ --output              -o      PATH  Output directory [default: .]                                │
│ --keep-temp                         Keep temporary files                                         │
│ --vscode-version              TEXT  Maximum VS Code version to require (e.g., '1.96')            │
│                                     [default: 1.99]                                              │
│ --install-completion                Install completion for the current shell.                    │
│ --show-completion                   Show completion for the current shell, to copy it or         │
│                                     customize the installation.                                  │
│ --help                -h            Show this message and exit.                                  │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
{
"build": {
"dockerfile": "Dockerfile",
"args": {
"BUILDKIT_INLINE_CACHE": "1" // Speed up subsequent BuildKit builds with caching
}
},
// Refresh dependencies on container creation or content update
"updateContentCommand": [
"uv",
"sync",
"--locked"
],
"customizations": {
"vscode": {
"extensions": [
// Ruff for formatting and linting
"charliermarsh.ruff",
// Dockerfile linting and formatting
"ms-azuretools.vscode-containers",
// TOML linting and formatting
"tombi-toml.tombi",
// Markdown linting and formatting
"DavidAnson.vscode-markdownlint"
],
"settings": {
"[python]": {
// Use Ruff for formatting
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.codeActionsOnSave": {
"source.fixAll.ruff": "explicit",
"source.organizeImports.ruff": "explicit"
}
},
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[dockerfile]": {
"editor.defaultFormatter": "ms-azuretools.vscode-containers"
},
"[toml]": {
"editor.defaultFormatter": "tombi-toml.tombi"
},
"[markdown]": {
"editor.defaultFormatter": "DavidAnson.vscode-markdownlint"
},
"python.testing.pytestEnabled": true,
"editor.formatOnSave": true,
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true
}
}
}
}
# Ignore all files and directories in the Docker build context by default
*
# Exceptions:
# Include files necessary to install scripts
!/.python-version
!/pyproject.toml
!/uv.lock
!/ pylance-patcher.md
!/pylance_patcher.py
# Cache
__pycache__/
.pytest_cache/
.ruff_cache/
# VS Code extension files
*.vsix
# Use Base devcontainer for development
FROM mcr.microsoft.com/devcontainers/base:latest
# Install uv for Python interpreter and dependency management
COPY --from=ghcr.io/astral-sh/uv /uv /uvx /usr/local/bin/
# Install ruff for formatting and linting
COPY --from=ghcr.io/astral-sh/ruff /ruff /usr/local/bin/
# Use /usr/local for Python installation
ENV PYTHON_PATH=/usr/local
ENV UV_PROJECT_ENVIRONMENT="${PYTHON_PATH}"
ENV UV_PYTHON_PREFERENCE=only-system
# Prevent version check in Pyright
ENV PYRIGHT_PYTHON_IGNORE_WARNINGS=1
# Set LC_CTYPE to fix "character ghosting" issue in terminal
# https://github.com/ohmyzsh/ohmyzsh/wiki/FAQ#i-see-duplicate-typed-characters-after-i-complete-a-command
ENV LC_CTYPE=en_US.UTF-8
# Switch to the vscode user
USER vscode
# Key apt cache mounts by target architecture
ARG TARGETPLATFORM
RUN --mount=type=bind,source=.,target=/workspaces/pylance-patcher \
--mount=type=cache,target=/home/vscode/.cache/uv,uid=1000 \
--mount=type=cache,target=/var/lib/apt,sharing=locked,id=apt-lib-${TARGETPLATFORM} \
--mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-cache-${TARGETPLATFORM} \
<<EOF
set -Eeux
# Take over ownership of /usr/local for Python installation
sudo chown -R vscode:vscode /usr/local
# Take ownership of the .cache directory
sudo chown -R vscode:vscode /home/vscode/.cache
# Install specific Python version from .python-version
uv python install --project=/workspaces/pylance-patcher --install-dir=/tmp/python
# Move Python into /usr/local
(cd /tmp/python/* && tar -cf- .) | (cd /usr/local && tar -xf-)
rm -r /tmp/python
# Switch default shell to zsh
sudo chsh -s "$(which zsh)" "$(whoami)"
# Disable OMZ auto-update check
sed -i "/^# zstyle ':omz:update' mode disabled/s/^# //" "${HOME}/.zshrc"
# Persist downloaded .deb packages in the cache
# Ref: https://docs.docker.com/reference/dockerfile/#example-cache-apt-packages
sudo rm /etc/apt/apt.conf.d/docker-clean
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' | sudo tee /etc/apt/apt.conf.d/keep-cache >/dev/null
# Drop system python3 to avoid conflicting with /usr/local/bin/python
sudo apt-get remove -y --purge python3
# Update package lists
sudo apt-get update
# Upgrade all packages to the latest version
export DEBIAN_FRONTEND=noninteractive
export DEBIAN_PRIORITY=critical
sudo --preserve-env=DEBIAN_FRONTEND,DEBIAN_PRIORITY \
apt-get upgrade -y --no-install-recommends \
-o Dpkg::Options::="--force-confdef" \
-o Dpkg::Options::="--force-confnew"
# Remove unneeded packages
sudo apt-get autoremove -y --purge
# Bind-mounted repository could have a different user ID.
# We need to mark the directory as safe for git to resolve the
# "fatal: detected dubious ownership in repository" error.
git config --global --add safe.directory /workspaces/pylance-patcher
# Install dependencies and package
uv sync --all-groups --project /workspaces/pylance-patcher --locked --compile-bytecode --link-mode=copy
# Add command completions
# Shell detection fails under qemu, using "|| true" to suppress hard failures
echo 'pylance-patcher --install-completion' | zsh -s || true
echo 'pylance-patcher --install-completion' | bash -s || true
# Invoke --help as a sanity check to make sure the installed package is runnable
pylance-patcher --help
EOF
# Default container to the workspace root
WORKDIR /workspaces/pylance-patcher
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = "~=3.13.0"
# dependencies = ["typer>=0.12.0", "rich>=14.0.0"]
# ///
"""A tool to patch the Pylance extension for compatibility with Cursor and other VS Code forks."""
import gzip
import json
import shutil
import urllib.error
import urllib.request
import zipfile
from pathlib import Path
from typing import Annotated, TypedDict
import typer
from rich.console import Console
from rich.progress import Progress, SpinnerColumn, TextColumn
console = Console()
app = typer.Typer(context_settings={"help_option_names": ["-h", "--help"]})
def _exit_with_error(message: str) -> None:
"""Print error message and exit with status 1."""
console.print(f"[red]✗[/red] {message}")
raise typer.Exit(1)
class PatchInfo(TypedDict):
"""Type definition for version-specific patch data."""
search: str
replace: str | None
# Version-specific patch data
PATCH_DATA: dict[str, PatchInfo] = {
"2025.4.1": {
# via @caenrige
# https://github.com/VSCodium/vscodium/discussions/1641#discussioncomment-12603240
"search": (
"return(0x0,_0x302dc7[_0x1e5d1c(0x69a)])(_0x17bc9b[_0x1e5d1c(0x120)]),"
"{'client':_0x5d8ccf,'start':()=>{const _0x751a33=_0x1e5d1c;"
"return _0x2bfc9a['sendTelemetryEvent'](_0x1d90e3['EventName'][_0x751a33(0x48e)]),"
"Promise[_0x751a33(0x60e)]();},'stop':()=>Promise[_0x1e5d1c(0x60e)](),"
"'disposables':_0x22650a};"
),
"replace": None, # Will be replaced with spaces
},
"2025.6.2": {
# via @jamesst20
# https://github.com/VSCodium/vscodium/discussions/1641#discussioncomment-13694853
"search": "return _0x1a0cda(_0x18d153);",
"replace": "return true;",
},
"2025.6.101": {
# via @jamesst20
# https://github.com/VSCodium/vscodium/discussions/1641#discussioncomment-13694853
"search": "return _0x4ecf62(_0x4bc45e);",
"replace": "return true;",
},
}
# Supported versions
SUPPORTED_VERSIONS = list(PATCH_DATA.keys())
def download_file(url: str, dest: Path) -> None:
"""Download a file with progress indicator."""
# Validate URL scheme for security
if not url.startswith(("http://", "https://")):
msg = "Only HTTP(S) URLs are allowed"
raise ValueError(msg)
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
console=console,
) as progress:
task = progress.add_task(f"Downloading {dest.name}...", total=None)
urllib.request.urlretrieve(url, dest) # noqa: S310
progress.update(task, completed=True)
def gunzip_file(src: Path, dest: Path) -> None:
"""Decompress a gzipped file."""
with console.status(f"Decompressing {src.name}..."), gzip.open(src, "rb") as f_in, dest.open("wb") as f_out:
shutil.copyfileobj(f_in, f_out)
console.print(f"[green]✓[/green] Decompressed to {dest.name}")
def extract_zip(zip_path: Path, extract_to: Path) -> None:
"""Extract a zip file to a directory."""
with console.status(f"Extracting {zip_path.name}..."), zipfile.ZipFile(zip_path, "r") as zip_ref:
zip_ref.extractall(extract_to)
console.print(f"[green]✓[/green] Extracted to {extract_to.name}")
def patch_extension_bundle(bundle_path: Path, version: str) -> None:
"""Apply version-specific patches to the extension bundle."""
if version not in PATCH_DATA:
_exit_with_error(f"Unsupported version: {version}")
patch_info = PATCH_DATA[version]
search_text = patch_info["search"]
replace_text = patch_info["replace"]
with console.status(f"Patching extension bundle for version {version}..."):
# Read the file
content = bundle_path.read_text(encoding="utf-8")
# Count occurrences
occurrences = content.count(search_text)
if occurrences == 0:
_exit_with_error(f"Pattern not found in bundle for version {version}. The extension may have changed.")
# Replace based on version
replacement = " " * len(search_text) if replace_text is None else replace_text
content = content.replace(search_text, replacement)
# Write back
bundle_path.write_text(content, encoding="utf-8")
console.print(f"[green]✓[/green] Patched {occurrences} occurrence(s) in extension bundle for version {version}")
def compare_vscode_versions(version1: str, version2: str) -> int:
"""Compare two VS Code version strings.
Returns:
-1 if version1 < version2
0 if version1 == version2
1 if version1 > version2
"""
# Extract version numbers, ignoring ^ or ~ prefixes
v1 = version1.lstrip("^~").split(".")
v2 = version2.lstrip("^~").split(".")
# Compare major, minor, patch
for i in range(3):
n1 = int(v1[i]) if i < len(v1) else 0
n2 = int(v2[i]) if i < len(v2) else 0
if n1 < n2:
return -1
if n1 > n2:
return 1
return 0
def create_vsix(source_dir: Path, output_file: Path) -> None:
"""Create a .vsix file from a directory."""
with (
console.status(f"Creating {output_file.name}..."),
zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED) as zipf,
):
for file_path in source_dir.rglob("*"):
if file_path.is_file():
arcname = file_path.relative_to(source_dir)
zipf.write(file_path, arcname)
console.print(f"[green]✓[/green] Created {output_file.name}")
def update_extension_version(extract_dir: Path, version: str) -> None:
"""Update version to include +patched suffix in package.json and manifest."""
# Update version in package.json
package_json_path = extract_dir / "extension" / "package.json"
if package_json_path.exists():
package_content = package_json_path.read_text(encoding="utf-8")
package_content = package_content.replace(f'"version": "{version}"', f'"version": "{version}+patched"')
package_json_path.write_text(package_content, encoding="utf-8")
console.print(f"[green]✓[/green] Updated version in package.json to {version}+patched")
# Update version in extension.vsixmanifest
manifest_path = extract_dir / "extension.vsixmanifest"
if manifest_path.exists():
manifest_content = manifest_path.read_text(encoding="utf-8")
manifest_content = manifest_content.replace(f'Version="{version}"', f'Version="{version}+patched"')
manifest_path.write_text(manifest_content, encoding="utf-8")
console.print(f"[green]✓[/green] Updated version in extension.vsixmanifest to {version}+patched")
def clamp_vscode_version(extract_dir: Path, vscode_version: str | None) -> None:
"""Clamp VS Code version requirement if needed."""
if not vscode_version:
return
package_json_path = extract_dir / "extension" / "package.json"
if not package_json_path.exists():
return
package_data = json.loads(package_json_path.read_text(encoding="utf-8"))
current_vscode_version = package_data.get("engines", {}).get("vscode", "")
# Normalize the user input to match VS Code format
if not vscode_version.startswith("^"):
vscode_target = f"^{vscode_version}"
# Add .0 if only major.minor is provided
if vscode_target.count(".") == 1:
vscode_target += ".0"
else:
vscode_target = vscode_version
if current_vscode_version and compare_vscode_versions(current_vscode_version, vscode_target) > 0:
# Current version requirement is higher than our ceiling, clamp it
package_data["engines"]["vscode"] = vscode_target
package_json_path.write_text(json.dumps(package_data, indent=2) + "\n", encoding="utf-8")
console.print(f"[green]✓[/green] Clamped VS Code version from {current_vscode_version} to {vscode_target}")
else:
console.print(f"[dim]VS Code version {current_vscode_version} is already compatible (≤ {vscode_target})[/dim]")
@app.command()
def patch(
version: Annotated[str, typer.Argument(help="Pylance version to patch", show_choices=True)] = "2025.6.2",
output_dir: Annotated[Path, typer.Option("--output", "-o", help="Output directory")] = Path(),
*,
keep_temp: Annotated[bool, typer.Option("--keep-temp", help="Keep temporary files", is_flag=True)] = False,
vscode_version: Annotated[
str | None,
typer.Option("--vscode-version", help="Maximum VS Code version to require (e.g., '1.96')"),
] = "1.99",
) -> None:
"""Download and patch Pylance VS Code extension.
Supported versions: 2025.4.1, 2025.6.2, 2025.6.101
The --vscode-version option clamps the required VS Code version if the extension
requires a newer version. For example, use --vscode-version 1.96 to make
the extension compatible with VS Code 1.96.
"""
if version not in SUPPORTED_VERSIONS:
console.print(f"[red]✗[/red] Unsupported version: {version}")
console.print(f"Supported versions: {', '.join(SUPPORTED_VERSIONS)}")
raise typer.Exit(1)
console.print(f"[bold]Pylance Patcher[/bold] - Patching version {version}")
console.print()
# Ensure output directory exists
output_dir.mkdir(parents=True, exist_ok=True)
# Define file paths
vsix_gz_path = output_dir / f"ms-python.vscode-pylance-{version}.vsix"
vsix_path = output_dir / f"ms-python.vscode-pylance-{version}.zip"
extract_dir = output_dir / f"ms-python.vscode-pylance-{version}-patched"
patched_vsix = output_dir / f"ms-python.vscode-pylance-{version}-patched.vsix"
try:
# Download the extension
url = f"https://marketplace.visualstudio.com/_apis/public/gallery/publishers/ms-python/vsextensions/vscode-pylance/{version}/vspackage"
download_file(url, vsix_gz_path)
# Decompress (it comes gzipped)
gunzip_file(vsix_gz_path, vsix_path)
# Extract the zip
extract_zip(vsix_path, extract_dir)
# Patch the extension bundle
bundle_path = extract_dir / "extension" / "dist" / "extension.bundle.js"
if not bundle_path.exists():
_exit_with_error(f"Extension bundle not found at expected path: {bundle_path}")
patch_extension_bundle(bundle_path, version)
# Update version to include +patched suffix
update_extension_version(extract_dir, version)
# Clamp VS Code version if needed
clamp_vscode_version(extract_dir, vscode_version)
# Repackage as .vsix
create_vsix(extract_dir, patched_vsix)
# Clean up temporary files unless asked to keep them
if not keep_temp:
with console.status("Cleaning up temporary files..."):
vsix_gz_path.unlink(missing_ok=True)
vsix_path.unlink(missing_ok=True)
shutil.rmtree(extract_dir, ignore_errors=True)
console.print("[green]✓[/green] Cleaned up temporary files")
console.print()
console.print(f"[bold green]Success![/bold green] Patched Pylance {version} extension saved to: {patched_vsix}")
except (OSError, urllib.error.URLError, zipfile.BadZipFile) as e:
console.print(f"[red]✗[/red] Error: {e}")
raise typer.Exit(1) from e
if __name__ == "__main__":
app()
[project]
name = "pylance-patcher"
version = "0.0.0-0"
description = "Patch Pylance to use with for VS Code forks"
readme = " pylance-patcher.md" # Leading space ensures this file appears first in GitHub Gists
requires-python = "~=3.13.0"
dependencies = [
"rich>=14.0.0",
"typer>=0.12.0",
]
[project.scripts]
pylance-patcher = "pylance_patcher:app"
[dependency-groups]
dev = [
"pyright[nodejs]>=1.1.364",
"pytest>=6.0.0",
"ruff>=0.2.0",
]
[build-system]
requires = ["uv_build>=0.7.19,<0.8.0"]
build-backend = "uv_build"
[tool.pyright]
typeCheckingMode = "strict"
[tool.ruff]
line-length = 120
fix = true
unsafe-fixes = true
[tool.ruff.format]
docstring-code-format = true
[tool.ruff.lint]
select = ["ALL"]
ignore = [
# Conflicting with formatter:
# https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
"COM812", # incorrect-line-break-after-operator
"D203", # incorrect-blank-line-before-class
"D213", # multi-line-summary-second-line
]
unfixable = [
"T201", # Do not delete print statements silently
]
[tool.uv.build-backend]
# UV's build backend expects modules in src/<package_name>/ by default.
# Setting namespace=true allows flexible module discovery at the root,
# which is necessary for GitHub Gists since they don't support directories.
namespace = true
module-root = ""
version = 1
revision = 2
requires-python = "==3.13.*"
[[package]]
name = "click"
version = "8.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "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 = "iniconfig"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
]
[[package]]
name = "nodeenv"
version = "1.9.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" },
]
[[package]]
name = "nodejs-wheel-binaries"
version = "22.17.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d3/86/8962d1d24ff480f4dd31871f42c8e0d8e2c851cd558a07ee689261d310ab/nodejs_wheel_binaries-22.17.0.tar.gz", hash = "sha256:529142012fb8fd20817ef70e2ef456274df4f49933292e312c8bbc7285af6408", size = 8068, upload-time = "2025-06-29T20:24:25.002Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5d/53/b942c6da4ff6f87a315033f6ff6fed8fd3c22047d7ff5802badaa5dfc2c2/nodejs_wheel_binaries-22.17.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:6545a6f6d2f736d9c9e2eaad7e599b6b5b2d8fd4cbd2a1df0807cbcf51b9d39b", size = 51003554, upload-time = "2025-06-29T20:23:47.042Z" },
{ url = "https://files.pythonhosted.org/packages/e2/b7/7184a9ad2364912da22f2fe021dc4a3301721131ef7759aeb4a1f19db0b4/nodejs_wheel_binaries-22.17.0-py2.py3-none-macosx_11_0_x86_64.whl", hash = "sha256:4bea5b994dd87c20f8260031ea69a97c3d282e2d4472cc8908636a313a830d00", size = 51936848, upload-time = "2025-06-29T20:23:52.064Z" },
{ url = "https://files.pythonhosted.org/packages/e9/7a/0ea425147b8110b8fd65a6c21cfd3bd130cdec7766604361429ef870d799/nodejs_wheel_binaries-22.17.0-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:885508615274a22499dd5314759c1cf96ba72de03e6485d73b3e5475e7f12662", size = 57925230, upload-time = "2025-06-29T20:23:56.81Z" },
{ url = "https://files.pythonhosted.org/packages/23/5f/10a3f2ac08a839d065d9ccfd6d9df66bc46e100eaf87a8a5cf149eb3fb8e/nodejs_wheel_binaries-22.17.0-py2.py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90f38ce034a602bcab534d55cbe0390521e73e5dcffdd1c4b34354b932172af2", size = 58457829, upload-time = "2025-06-29T20:24:01.945Z" },
{ url = "https://files.pythonhosted.org/packages/ed/a4/d2ca331e16eef0974eb53702df603c54f77b2a7e2007523ecdbf6cf61162/nodejs_wheel_binaries-22.17.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5eed087855b644c87001fe04036213193963ccd65e7f89949e9dbe28e7743d9b", size = 59778054, upload-time = "2025-06-29T20:24:07.14Z" },
{ url = "https://files.pythonhosted.org/packages/be/2b/04e0e7f7305fe2ba30fd4610bfb432516e0f65379fe6c2902f4b7b1ad436/nodejs_wheel_binaries-22.17.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:715f413c81500f0770ea8936ef1fc2529b900da8054cbf6da67cec3ee308dc76", size = 60830079, upload-time = "2025-06-29T20:24:12.21Z" },
{ url = "https://files.pythonhosted.org/packages/ce/67/12070b24b88040c2d694883f3dcb067052f748798f4c63f7c865769a5747/nodejs_wheel_binaries-22.17.0-py2.py3-none-win_amd64.whl", hash = "sha256:51165630493c8dd4acfe1cae1684b76940c9b03f7f355597d55e2d056a572ddd", size = 40117877, upload-time = "2025-06-29T20:24:17.51Z" },
{ url = "https://files.pythonhosted.org/packages/2e/ec/53ac46af423527c23e40c7343189f2bce08a8337efedef4d8a33392cee23/nodejs_wheel_binaries-22.17.0-py2.py3-none-win_arm64.whl", hash = "sha256:fae56d172227671fccb04461d3cd2b26a945c6c7c7fc29edb8618876a39d8b4a", size = 38865278, upload-time = "2025-06-29T20:24:21.065Z" },
]
[[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 = "pluggy"
version = "1.6.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
name = "pygments"
version = "2.19.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
]
[[package]]
name = "pylance-patcher"
version = "0.0.0.post0"
source = { editable = "." }
dependencies = [
{ name = "rich" },
{ name = "typer" },
]
[package.dev-dependencies]
dev = [
{ name = "pyright", extra = ["nodejs"] },
{ name = "pytest" },
{ name = "ruff" },
]
[package.metadata]
requires-dist = [
{ name = "rich", specifier = ">=14.0.0" },
{ name = "typer", specifier = ">=0.12.0" },
]
[package.metadata.requires-dev]
dev = [
{ name = "pyright", extras = ["nodejs"], specifier = ">=1.1.364" },
{ name = "pytest", specifier = ">=6.0.0" },
{ name = "ruff", specifier = ">=0.2.0" },
]
[[package]]
name = "pyright"
version = "1.1.403"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nodeenv" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fe/f6/35f885264ff08c960b23d1542038d8da86971c5d8c955cfab195a4f672d7/pyright-1.1.403.tar.gz", hash = "sha256:3ab69b9f41c67fb5bbb4d7a36243256f0d549ed3608678d381d5f51863921104", size = 3913526, upload-time = "2025-07-09T07:15:52.882Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/49/b6/b04e5c2f41a5ccad74a1a4759da41adb20b4bc9d59a5e08d29ba60084d07/pyright-1.1.403-py3-none-any.whl", hash = "sha256:c0eeca5aa76cbef3fcc271259bbd785753c7ad7bcac99a9162b4c4c7daed23b3", size = 5684504, upload-time = "2025-07-09T07:15:50.958Z" },
]
[package.optional-dependencies]
nodejs = [
{ name = "nodejs-wheel-binaries" },
]
[[package]]
name = "pytest"
version = "8.4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" },
]
[[package]]
name = "rich"
version = "14.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" },
]
[[package]]
name = "ruff"
version = "0.12.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/9b/ce/8d7dbedede481245b489b769d27e2934730791a9a82765cb94566c6e6abd/ruff-0.12.4.tar.gz", hash = "sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873", size = 5131435, upload-time = "2025-07-17T17:27:19.138Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ae/9f/517bc5f61bad205b7f36684ffa5415c013862dee02f55f38a217bdbe7aa4/ruff-0.12.4-py3-none-linux_armv6l.whl", hash = "sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a", size = 10188824, upload-time = "2025-07-17T17:26:31.412Z" },
{ url = "https://files.pythonhosted.org/packages/28/83/691baae5a11fbbde91df01c565c650fd17b0eabed259e8b7563de17c6529/ruff-0.12.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442", size = 10884521, upload-time = "2025-07-17T17:26:35.084Z" },
{ url = "https://files.pythonhosted.org/packages/d6/8d/756d780ff4076e6dd035d058fa220345f8c458391f7edfb1c10731eedc75/ruff-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e", size = 10277653, upload-time = "2025-07-17T17:26:37.897Z" },
{ url = "https://files.pythonhosted.org/packages/8d/97/8eeee0f48ece153206dce730fc9e0e0ca54fd7f261bb3d99c0a4343a1892/ruff-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586", size = 10485993, upload-time = "2025-07-17T17:26:40.68Z" },
{ url = "https://files.pythonhosted.org/packages/49/b8/22a43d23a1f68df9b88f952616c8508ea6ce4ed4f15353b8168c48b2d7e7/ruff-0.12.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb", size = 10022824, upload-time = "2025-07-17T17:26:43.564Z" },
{ url = "https://files.pythonhosted.org/packages/cd/70/37c234c220366993e8cffcbd6cadbf332bfc848cbd6f45b02bade17e0149/ruff-0.12.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c", size = 11524414, upload-time = "2025-07-17T17:26:46.219Z" },
{ url = "https://files.pythonhosted.org/packages/14/77/c30f9964f481b5e0e29dd6a1fae1f769ac3fd468eb76fdd5661936edd262/ruff-0.12.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a", size = 12419216, upload-time = "2025-07-17T17:26:48.883Z" },
{ url = "https://files.pythonhosted.org/packages/6e/79/af7fe0a4202dce4ef62c5e33fecbed07f0178f5b4dd9c0d2fcff5ab4a47c/ruff-0.12.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3", size = 11976756, upload-time = "2025-07-17T17:26:51.754Z" },
{ url = "https://files.pythonhosted.org/packages/09/d1/33fb1fc00e20a939c305dbe2f80df7c28ba9193f7a85470b982815a2dc6a/ruff-0.12.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045", size = 11020019, upload-time = "2025-07-17T17:26:54.265Z" },
{ url = "https://files.pythonhosted.org/packages/64/f4/e3cd7f7bda646526f09693e2e02bd83d85fff8a8222c52cf9681c0d30843/ruff-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57", size = 11277890, upload-time = "2025-07-17T17:26:56.914Z" },
{ url = "https://files.pythonhosted.org/packages/5e/d0/69a85fb8b94501ff1a4f95b7591505e8983f38823da6941eb5b6badb1e3a/ruff-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184", size = 10348539, upload-time = "2025-07-17T17:26:59.381Z" },
{ url = "https://files.pythonhosted.org/packages/16/a0/91372d1cb1678f7d42d4893b88c252b01ff1dffcad09ae0c51aa2542275f/ruff-0.12.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb", size = 10009579, upload-time = "2025-07-17T17:27:02.462Z" },
{ url = "https://files.pythonhosted.org/packages/23/1b/c4a833e3114d2cc0f677e58f1df6c3b20f62328dbfa710b87a1636a5e8eb/ruff-0.12.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1", size = 10942982, upload-time = "2025-07-17T17:27:05.343Z" },
{ url = "https://files.pythonhosted.org/packages/ff/ce/ce85e445cf0a5dd8842f2f0c6f0018eedb164a92bdf3eda51984ffd4d989/ruff-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b", size = 11343331, upload-time = "2025-07-17T17:27:08.652Z" },
{ url = "https://files.pythonhosted.org/packages/35/cf/441b7fc58368455233cfb5b77206c849b6dfb48b23de532adcc2e50ccc06/ruff-0.12.4-py3-none-win32.whl", hash = "sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93", size = 10267904, upload-time = "2025-07-17T17:27:11.814Z" },
{ url = "https://files.pythonhosted.org/packages/ce/7e/20af4a0df5e1299e7368d5ea4350412226afb03d95507faae94c80f00afd/ruff-0.12.4-py3-none-win_amd64.whl", hash = "sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a", size = 11209038, upload-time = "2025-07-17T17:27:14.417Z" },
{ url = "https://files.pythonhosted.org/packages/11/02/8857d0dfb8f44ef299a5dfd898f673edefb71e3b533b3b9d2db4c832dd13/ruff-0.12.4-py3-none-win_arm64.whl", hash = "sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e", size = 10469336, upload-time = "2025-07-17T17:27:16.913Z" },
]
[[package]]
name = "shellingham"
version = "1.5.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
]
[[package]]
name = "typer"
version = "0.16.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "rich" },
{ name = "shellingham" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b", size = 102625, upload-time = "2025-05-26T14:30:31.824Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855", size = 46317, upload-time = "2025-05-26T14:30:30.523Z" },
]
[[package]]
name = "typing-extensions"
version = "4.14.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" },
]
@gbaian10
Copy link

This is very helpful, thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment