mirror of
https://github.com/codeflash-ai/codeflash.git
synced 2026-05-04 18:25:17 +00:00
Merge branch 'main' into pyarrow-comparator
This commit is contained in:
commit
fb5ee232a5
56 changed files with 5212 additions and 521 deletions
28
.claude/rules/architecture.md
Normal file
28
.claude/rules/architecture.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Architecture
|
||||
|
||||
```
|
||||
codeflash/
|
||||
├── main.py # CLI entry point
|
||||
├── cli_cmds/ # Command handling, console output (Rich)
|
||||
├── discovery/ # Find optimizable functions
|
||||
├── context/ # Extract code dependencies and imports
|
||||
├── optimization/ # Generate optimized code via AI
|
||||
│ ├── optimizer.py # Main optimization orchestration
|
||||
│ └── function_optimizer.py # Per-function optimization logic
|
||||
├── verification/ # Run deterministic tests (pytest plugin)
|
||||
├── benchmarking/ # Performance measurement
|
||||
├── github/ # PR creation
|
||||
├── api/ # AI service communication
|
||||
├── code_utils/ # Code parsing, git utilities
|
||||
├── models/ # Pydantic models and types
|
||||
├── languages/ # Multi-language support (Python, JavaScript/TypeScript)
|
||||
├── setup/ # Config schema, auto-detection, first-run experience
|
||||
├── picklepatch/ # Serialization/deserialization utilities
|
||||
├── tracing/ # Function call tracing
|
||||
├── tracer.py # Root-level tracer entry point for profiling
|
||||
├── lsp/ # IDE integration (Language Server Protocol)
|
||||
├── telemetry/ # Sentry, PostHog
|
||||
├── either.py # Functional Result type for error handling
|
||||
├── result/ # Result types and handling
|
||||
└── version.py # Version information
|
||||
```
|
||||
9
.claude/rules/code-style.md
Normal file
9
.claude/rules/code-style.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Code Style
|
||||
|
||||
- **Line length**: 120 characters
|
||||
- **Python**: 3.9+ syntax
|
||||
- **Tooling**: Ruff for linting/formatting, mypy strict mode, prek for pre-commit checks
|
||||
- **Comments**: Minimal - only explain "why", not "what"
|
||||
- **Docstrings**: Do not add unless explicitly requested
|
||||
- **Naming**: NEVER use leading underscores (`_function_name`) - Python has no true private functions, use public names
|
||||
- **Paths**: Always use absolute paths, handle encoding explicitly (UTF-8)
|
||||
6
.claude/rules/git.md
Normal file
6
.claude/rules/git.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Git Commits & Pull Requests
|
||||
|
||||
- Use conventional commit format: `fix:`, `feat:`, `refactor:`, `docs:`, `test:`, `chore:`
|
||||
- Keep commits atomic - one logical change per commit
|
||||
- Commit message body should be concise (1-2 sentences max)
|
||||
- PR titles should also use conventional format
|
||||
11
.claude/rules/source-code.md
Normal file
11
.claude/rules/source-code.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
paths:
|
||||
- "codeflash/**/*.py"
|
||||
---
|
||||
|
||||
# Source Code Rules
|
||||
|
||||
- Use `libcst` for code modification/transformation to preserve formatting. `ast` is acceptable for read-only analysis and parsing.
|
||||
- NEVER use leading underscores for function names (e.g., `_helper`). Python has no true private functions. Always use public names.
|
||||
- Any new feature or bug fix that can be tested automatically must have test cases.
|
||||
- If changes affect existing test expectations, update the tests accordingly. Tests must always pass after changes.
|
||||
15
.claude/rules/testing.md
Normal file
15
.claude/rules/testing.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
paths:
|
||||
- "tests/**"
|
||||
- "codeflash/**/*test*.py"
|
||||
---
|
||||
|
||||
# Testing Conventions
|
||||
|
||||
- Code context extraction and replacement tests must always assert for full string equality, no substring matching.
|
||||
- Use pytest's `tmp_path` fixture for temp directories (it's a `Path` object).
|
||||
- Write temp files inside `tmp_path`, never use `NamedTemporaryFile` (causes Windows file contention).
|
||||
- Always call `.resolve()` on Path objects to ensure absolute paths and resolve symlinks.
|
||||
- Use `.as_posix()` when converting resolved paths to strings (normalizes to forward slashes).
|
||||
- Any new feature or bug fix that can be tested automatically must have test cases.
|
||||
- If changes affect existing test expectations, update the tests accordingly. Tests must always pass after changes.
|
||||
10
.github/workflows/claude.yml
vendored
10
.github/workflows/claude.yml
vendored
|
|
@ -62,6 +62,7 @@ jobs:
|
|||
|
||||
If there are prek issues:
|
||||
- For SAFE auto-fixable issues (formatting, import sorting, trailing whitespace, etc.), run `uv run prek run --from-ref origin/main` again to auto-fix them
|
||||
- For issues that prek cannot auto-fix, do NOT attempt to fix them manually — report them as remaining issues in your summary
|
||||
|
||||
If there are mypy issues:
|
||||
- Fix type annotation issues (missing return types, Optional/None unions, import errors for type hints, incorrect types)
|
||||
|
|
@ -72,6 +73,11 @@ jobs:
|
|||
- Commit with message "style: auto-fix linting issues" or "fix: resolve mypy type errors" as appropriate
|
||||
- Push the changes with `git push`
|
||||
|
||||
IMPORTANT - Verification after fixing:
|
||||
- After committing fixes, run `uv run prek run --from-ref origin/main` ONE MORE TIME to verify all issues are resolved
|
||||
- If errors remain, either fix them or report them honestly as unfixed in your summary
|
||||
- NEVER claim issues are fixed without verifying. If you cannot fix an issue, say so
|
||||
|
||||
Do NOT attempt to fix:
|
||||
- Type errors that require logic changes or refactoring
|
||||
- Complex generic type issues
|
||||
|
|
@ -167,7 +173,7 @@ jobs:
|
|||
2. For each optimization PR:
|
||||
- Check if CI is passing: `gh pr checks <number>`
|
||||
- If all checks pass, merge it: `gh pr merge <number> --squash --delete-branch`
|
||||
claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(gh pr checks:*),Bash(gh pr merge:*),Bash(gh issue view:*),Bash(gh issue list:*),Bash(gh api:*),Bash(uv run prek *),Bash(uv run mypy *),Bash(uv run coverage *),Bash(uv run pytest *),Bash(git status*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git diff *),Bash(git checkout *),Read,Glob,Grep,Edit"'
|
||||
claude_args: '--model claude-opus-4-6 --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(gh pr checks:*),Bash(gh pr merge:*),Bash(gh issue view:*),Bash(gh issue list:*),Bash(gh api:*),Bash(uv run prek *),Bash(uv run mypy *),Bash(uv run coverage *),Bash(uv run pytest *),Bash(git status*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git diff *),Bash(git checkout *),Read,Glob,Grep,Edit"'
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
env:
|
||||
|
|
@ -239,7 +245,7 @@ jobs:
|
|||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
use_foundry: "true"
|
||||
claude_args: '--allowedTools "Read,Edit,Write,Glob,Grep,Bash(git status*),Bash(git diff*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git log*),Bash(git merge*),Bash(git fetch*),Bash(git checkout*),Bash(git branch*),Bash(uv run prek *),Bash(prek *),Bash(uv run ruff *),Bash(uv run pytest *),Bash(uv run mypy *),Bash(uv run coverage *),Bash(gh pr comment*),Bash(gh pr view*),Bash(gh pr diff*),Bash(gh pr merge*),Bash(gh pr close*)"'
|
||||
claude_args: '--model claude-opus-4-6 --allowedTools "Read,Edit,Write,Glob,Grep,Bash(git status*),Bash(git diff*),Bash(git add *),Bash(git commit *),Bash(git push*),Bash(git log*),Bash(git merge*),Bash(git fetch*),Bash(git checkout*),Bash(git branch*),Bash(uv run prek *),Bash(prek *),Bash(uv run ruff *),Bash(uv run pytest *),Bash(uv run mypy *),Bash(uv run coverage *),Bash(gh pr comment*),Bash(gh pr view*),Bash(gh pr diff*),Bash(gh pr merge*),Bash(gh pr close*)"'
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
env:
|
||||
|
|
|
|||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -258,6 +258,10 @@ WARP.MD
|
|||
.mcp.json
|
||||
.tessl/
|
||||
tessl.json
|
||||
|
||||
# Claude Code - track shared rules, ignore local config
|
||||
.claude/*
|
||||
!.claude/rules/
|
||||
**/node_modules/**
|
||||
**/dist-nuitka/**
|
||||
**/.npmrc
|
||||
|
|
|
|||
49
CLAUDE.md
49
CLAUDE.md
|
|
@ -33,55 +33,6 @@ uv run codeflash init # Initialize in a project
|
|||
uv run codeflash --all # Optimize entire codebase
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
codeflash/
|
||||
├── main.py # CLI entry point
|
||||
├── cli_cmds/ # Command handling, console output (Rich)
|
||||
├── discovery/ # Find optimizable functions
|
||||
├── context/ # Extract code dependencies and imports
|
||||
├── optimization/ # Generate optimized code via AI
|
||||
│ ├── optimizer.py # Main optimization orchestration
|
||||
│ └── function_optimizer.py # Per-function optimization logic
|
||||
├── verification/ # Run deterministic tests (pytest plugin)
|
||||
├── benchmarking/ # Performance measurement
|
||||
├── github/ # PR creation
|
||||
├── api/ # AI service communication
|
||||
├── code_utils/ # Code parsing, git utilities
|
||||
├── models/ # Pydantic models and types
|
||||
├── tracing/ # Function call tracing
|
||||
├── lsp/ # IDE integration (Language Server Protocol)
|
||||
├── telemetry/ # Sentry, PostHog
|
||||
├── either.py # Functional Result type for error handling
|
||||
└── result/ # Result types and handling
|
||||
```
|
||||
|
||||
### Key Rules to follow
|
||||
|
||||
- Use libcst, not ast - For Python, always use `libcst` for code parsing/modification to preserve formatting.
|
||||
- Code context extraction and replacement tests must always assert for full string equality, no substring matching.
|
||||
- Any new feature or bug fix that can be tested automatically must have test cases.
|
||||
- If changes affect existing test expectations, update the tests accordingly. Tests must always pass after changes.
|
||||
- NEVER use leading underscores for function names (e.g., `_helper`). Python has no true private functions. Always use public names.
|
||||
|
||||
## Code Style
|
||||
|
||||
- **Line length**: 120 characters
|
||||
- **Python**: 3.9+ syntax
|
||||
- **Tooling**: Ruff for linting/formatting, mypy strict mode, prek for pre-commit checks
|
||||
- **Comments**: Minimal - only explain "why", not "what"
|
||||
- **Docstrings**: Do not add unless explicitly requested
|
||||
- **Naming**: NEVER use leading underscores (`_function_name`) - Python has no true private functions, use public names
|
||||
- **Paths**: Always use absolute paths, handle encoding explicitly (UTF-8)
|
||||
|
||||
## Git Commits & Pull Requests
|
||||
|
||||
- Use conventional commit format: `fix:`, `feat:`, `refactor:`, `docs:`, `test:`, `chore:`
|
||||
- Keep commits atomic - one logical change per commit
|
||||
- Commit message body should be concise (1-2 sentences max)
|
||||
- PR titles should also use conventional format
|
||||
|
||||
<!-- Section below is auto-generated by `tessl install` - do not edit manually -->
|
||||
|
||||
# Agent Rules <!-- tessl-managed -->
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@ class JavaScriptTransformer:
|
|||
|
||||
from pathlib import Path
|
||||
from codeflash.languages.base import LanguageSupport, FunctionInfo, CodeContext
|
||||
from codeflash.languages.treesitter_utils import TreeSitterAnalyzer
|
||||
from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer
|
||||
from codeflash.languages.javascript.transformer import JavaScriptTransformer
|
||||
|
||||
class JavaScriptSupport(LanguageSupport):
|
||||
|
|
@ -523,7 +523,7 @@ class JavaScriptSupport(LanguageSupport):
|
|||
# codeflash/languages/javascript/test_discovery.py
|
||||
|
||||
from pathlib import Path
|
||||
from codeflash.languages.treesitter_utils import TreeSitterAnalyzer
|
||||
from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer
|
||||
|
||||
class JestTestDiscovery:
|
||||
"""Static analysis-based test discovery for Jest."""
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
}
|
||||
},
|
||||
"../../../packages/codeflash": {
|
||||
"version": "0.3.1",
|
||||
"version": "0.7.0",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from codeflash.cli_cmds.cmd_init import init_codeflash, install_github_actions
|
|||
from codeflash.cli_cmds.console import logger
|
||||
from codeflash.cli_cmds.extension import install_vscode_extension
|
||||
from codeflash.code_utils import env_utils
|
||||
from codeflash.code_utils.code_utils import exit_with_message
|
||||
from codeflash.code_utils.code_utils import exit_with_message, normalize_ignore_paths
|
||||
from codeflash.code_utils.config_parser import parse_config_file
|
||||
from codeflash.languages.test_framework import set_current_test_framework
|
||||
from codeflash.lsp.helpers import is_LSP_enabled
|
||||
|
|
@ -284,16 +284,12 @@ def process_pyproject_config(args: Namespace) -> Namespace:
|
|||
|
||||
require_github_app_or_exit(owner, repo_name)
|
||||
|
||||
if hasattr(args, "ignore_paths") and args.ignore_paths is not None:
|
||||
normalized_ignore_paths = []
|
||||
for path in args.ignore_paths:
|
||||
path_obj = Path(path)
|
||||
if path_obj.exists():
|
||||
normalized_ignore_paths.append(path_obj.resolve())
|
||||
# Silently skip non-existent paths (e.g., .next, dist before build)
|
||||
args.ignore_paths = normalized_ignore_paths
|
||||
# Project root path is one level above the specified directory, because that's where the module can be imported from
|
||||
args.module_root = Path(args.module_root).resolve()
|
||||
if hasattr(args, "ignore_paths") and args.ignore_paths is not None:
|
||||
# Normalize ignore paths, supporting both literal paths and glob patterns
|
||||
# Use module_root as base path for resolving relative paths and patterns
|
||||
args.ignore_paths = normalize_ignore_paths(args.ignore_paths, base_path=args.module_root)
|
||||
# If module-root is "." then all imports are relatives to it.
|
||||
# in this case, the ".." becomes outside project scope, causing issues with un-importable paths
|
||||
args.project_root = project_root_from_module_root(args.module_root, pyproject_file_path)
|
||||
|
|
|
|||
|
|
@ -1772,7 +1772,7 @@ def _extract_calling_function_js(source_code: str, function_name: str, ref_line:
|
|||
|
||||
"""
|
||||
try:
|
||||
from codeflash.languages.treesitter_utils import TreeSitterAnalyzer, TreeSitterLanguage
|
||||
from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer, TreeSitterLanguage
|
||||
|
||||
# Try TypeScript first, fall back to JavaScript
|
||||
for lang in [TreeSitterLanguage.TYPESCRIPT, TreeSitterLanguage.TSX, TreeSitterLanguage.JAVASCRIPT]:
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ if TYPE_CHECKING:
|
|||
|
||||
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
|
||||
from codeflash.languages.base import Language, LanguageSupport
|
||||
from codeflash.languages.treesitter_utils import TreeSitterAnalyzer
|
||||
from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer
|
||||
from codeflash.models.models import CodeOptimizationContext, CodeStringsMarkdown, OptimizedCandidate, ValidCode
|
||||
|
||||
ASTNodeT = TypeVar("ASTNodeT", bound=ast.AST)
|
||||
|
|
@ -640,7 +640,7 @@ def _add_global_declarations_for_language(
|
|||
return original_source
|
||||
|
||||
try:
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(module_abspath)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,57 @@ ImportErrorPattern = re.compile(r"ModuleNotFoundError.*$", re.MULTILINE)
|
|||
|
||||
BLACKLIST_ADDOPTS = ("--benchmark", "--sugar", "--codespeed", "--cov", "--profile", "--junitxml", "-n")
|
||||
|
||||
# Characters that indicate a glob pattern
|
||||
GLOB_PATTERN_CHARS = frozenset("*?[")
|
||||
|
||||
|
||||
def is_glob_pattern(path_str: str) -> bool:
|
||||
"""Check if a path string contains glob pattern characters."""
|
||||
return any(char in path_str for char in GLOB_PATTERN_CHARS)
|
||||
|
||||
|
||||
def normalize_ignore_paths(paths: list[str], base_path: Path | None = None) -> list[Path]:
|
||||
"""Normalize ignore paths, expanding glob patterns and resolving paths.
|
||||
|
||||
Accepts a list of path strings that can be either:
|
||||
- Literal paths (relative or absolute): e.g., "node_modules", "/absolute/path"
|
||||
- Glob patterns: e.g., "**/*.test.js", "dist/*", "*.log"
|
||||
|
||||
Args:
|
||||
paths: List of path strings (literal paths or glob patterns).
|
||||
base_path: Base path for resolving relative paths and patterns.
|
||||
If None, uses current working directory.
|
||||
|
||||
Returns:
|
||||
List of resolved Path objects, deduplicated.
|
||||
|
||||
"""
|
||||
if base_path is None:
|
||||
base_path = Path.cwd()
|
||||
|
||||
base_path = base_path.resolve()
|
||||
normalized: set[Path] = set()
|
||||
|
||||
for path_str in paths:
|
||||
if is_glob_pattern(path_str):
|
||||
# It's a glob pattern - expand it
|
||||
# Use base_path as the root for glob expansion
|
||||
pattern_path = base_path / path_str
|
||||
# glob returns an iterator of matching paths
|
||||
for matched_path in base_path.glob(path_str):
|
||||
if matched_path.exists():
|
||||
normalized.add(matched_path.resolve())
|
||||
else:
|
||||
# It's a literal path
|
||||
path_obj = Path(path_str)
|
||||
if not path_obj.is_absolute():
|
||||
path_obj = base_path / path_obj
|
||||
if path_obj.exists():
|
||||
normalized.add(path_obj.resolve())
|
||||
# Silently skip non-existent literal paths (e.g., .next, dist before build)
|
||||
|
||||
return list(normalized)
|
||||
|
||||
|
||||
def unified_diff_strings(code1: str, code2: str, fromfile: str = "original", tofile: str = "modified") -> str:
|
||||
"""Return the unified diff between two code strings as a single string.
|
||||
|
|
|
|||
|
|
@ -361,21 +361,27 @@ def normalize_codeflash_imports(source: str) -> str:
|
|||
return _CODEFLASH_IMPORT_PATTERN.sub(r"import \1 from 'codeflash'", source)
|
||||
|
||||
|
||||
def inject_test_globals(generated_tests: GeneratedTestsList) -> GeneratedTestsList:
|
||||
def inject_test_globals(generated_tests: GeneratedTestsList, test_framework: str = "jest") -> GeneratedTestsList:
|
||||
# TODO: inside the prompt tell the llm if it should import jest functions or it's already injected in the global window
|
||||
"""Inject test globals into all generated tests.
|
||||
|
||||
Args:
|
||||
generated_tests: List of generated tests.
|
||||
test_framework: The test framework being used ("jest", "vitest", or "mocha").
|
||||
|
||||
Returns:
|
||||
Generated tests with test globals injected.
|
||||
|
||||
"""
|
||||
# we only inject test globals for esm modules
|
||||
global_import = (
|
||||
"import { jest, describe, it, expect, beforeEach, afterEach, beforeAll, test } from '@jest/globals'\n"
|
||||
)
|
||||
# Use vitest imports for vitest projects, jest imports for jest projects
|
||||
if test_framework == "vitest":
|
||||
global_import = "import { vi, describe, it, expect, beforeEach, afterEach, beforeAll, test } from 'vitest'\n"
|
||||
else:
|
||||
# Default to jest imports for jest and other frameworks
|
||||
global_import = (
|
||||
"import { jest, describe, it, expect, beforeEach, afterEach, beforeAll, test } from '@jest/globals'\n"
|
||||
)
|
||||
|
||||
for test in generated_tests.generated_tests:
|
||||
test.generated_original_test_source = global_import + test.generated_original_test_source
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ class JavaScriptNormalizer(CodeNormalizer):
|
|||
|
||||
"""
|
||||
try:
|
||||
from codeflash.languages.treesitter_utils import TreeSitterAnalyzer, TreeSitterLanguage
|
||||
from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer, TreeSitterLanguage
|
||||
|
||||
lang_map = {"javascript": TreeSitterLanguage.JAVASCRIPT, "typescript": TreeSitterLanguage.TYPESCRIPT}
|
||||
lang = lang_map.get(self._get_tree_sitter_language(), TreeSitterLanguage.JAVASCRIPT)
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ def _is_js_ts_function_exported(file_path: Path, function_name: str) -> tuple[bo
|
|||
Tuple of (is_exported, export_name). export_name may be 'default' for default exports.
|
||||
|
||||
"""
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
try:
|
||||
source = file_path.read_text(encoding="utf-8")
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ if TYPE_CHECKING:
|
|||
from tree_sitter import Node
|
||||
|
||||
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
|
||||
from codeflash.languages.treesitter_utils import ImportInfo, TreeSitterAnalyzer
|
||||
from codeflash.languages.javascript.treesitter import ImportInfo, TreeSitterAnalyzer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ class ReferenceFinder:
|
|||
List of Reference objects describing each call site.
|
||||
|
||||
"""
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
function_name = function_to_optimize.function_name
|
||||
source_file = function_to_optimize.file_path
|
||||
|
|
@ -168,7 +168,7 @@ class ReferenceFinder:
|
|||
if import_info:
|
||||
# Found an import - mark as visited and search for calls
|
||||
context.visited_files.add(file_path)
|
||||
import_name, original_import = import_info
|
||||
import_name, _original_import = import_info
|
||||
file_refs = self._find_references_in_file(
|
||||
file_path, file_code, function_name, import_name, file_analyzer, include_self=True
|
||||
)
|
||||
|
|
@ -213,7 +213,7 @@ class ReferenceFinder:
|
|||
trigger_check = True
|
||||
if import_info:
|
||||
context.visited_files.add(file_path)
|
||||
import_name, original_import = import_info
|
||||
import_name, _original_import = import_info
|
||||
file_refs = self._find_references_in_file(
|
||||
file_path, file_code, reexport_name, import_name, file_analyzer, include_self=True
|
||||
)
|
||||
|
|
@ -404,7 +404,7 @@ class ReferenceFinder:
|
|||
name_node = node.child_by_field_name("name")
|
||||
if name_node:
|
||||
new_current_function = source_bytes[name_node.start_byte : name_node.end_byte].decode("utf8")
|
||||
elif node.type in ("variable_declarator",):
|
||||
elif node.type == "variable_declarator":
|
||||
# Arrow function or function expression assigned to variable
|
||||
name_node = node.child_by_field_name("name")
|
||||
value_node = node.child_by_field_name("value")
|
||||
|
|
@ -719,7 +719,7 @@ class ReferenceFinder:
|
|||
continue
|
||||
|
||||
# Create a fake ImportInfo to resolve the re-export source
|
||||
from codeflash.languages.treesitter_utils import ImportInfo
|
||||
from codeflash.languages.javascript.treesitter import ImportInfo
|
||||
|
||||
fake_import = ImportInfo(
|
||||
module_path=exp.reexport_source,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from typing import TYPE_CHECKING
|
|||
if TYPE_CHECKING:
|
||||
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
|
||||
from codeflash.languages.base import HelperFunction
|
||||
from codeflash.languages.treesitter_utils import ImportInfo, TreeSitterAnalyzer
|
||||
from codeflash.languages.javascript.treesitter import ImportInfo, TreeSitterAnalyzer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -486,7 +486,7 @@ class MultiFileHelperFinder:
|
|||
|
||||
"""
|
||||
from codeflash.languages.base import HelperFunction
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
try:
|
||||
source = file_path.read_text(encoding="utf-8")
|
||||
|
|
@ -558,7 +558,8 @@ class MultiFileHelperFinder:
|
|||
|
||||
"""
|
||||
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
from codeflash.languages.registry import get_language_support
|
||||
|
||||
if context.current_depth >= context.max_depth:
|
||||
return {}
|
||||
|
|
@ -578,12 +579,15 @@ class MultiFileHelperFinder:
|
|||
imports = analyzer.find_imports(source)
|
||||
|
||||
# Create FunctionToOptimize for the helper
|
||||
# Get language from the language support registry
|
||||
lang_support = get_language_support(file_path)
|
||||
func_info = FunctionToOptimize(
|
||||
function_name=helper.name,
|
||||
file_path=file_path,
|
||||
parents=[],
|
||||
starting_line=helper.start_line,
|
||||
ending_line=helper.end_line,
|
||||
language=str(lang_support.language),
|
||||
)
|
||||
|
||||
# Recursively find helpers
|
||||
|
|
|
|||
|
|
@ -792,7 +792,7 @@ def validate_and_fix_import_style(test_code: str, source_file_path: Path, functi
|
|||
Fixed test code with correct import style.
|
||||
|
||||
"""
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
# Read source file to determine export style
|
||||
try:
|
||||
|
|
@ -901,6 +901,115 @@ def validate_and_fix_import_style(test_code: str, source_file_path: Path, functi
|
|||
return test_code
|
||||
|
||||
|
||||
def fix_import_path_for_test_location(
|
||||
test_code: str, source_file_path: Path, test_file_path: Path, module_root: Path
|
||||
) -> str:
|
||||
"""Fix import paths in generated test code to be relative to test file location.
|
||||
|
||||
The AI may generate tests with import paths that are relative to the module root
|
||||
(e.g., 'apps/web/app/file') instead of relative to where the test file is located
|
||||
(e.g., '../../app/file'). This function fixes such imports.
|
||||
|
||||
Args:
|
||||
test_code: The generated test code.
|
||||
source_file_path: Absolute path to the source file being tested.
|
||||
test_file_path: Absolute path to where the test file will be written.
|
||||
module_root: Root directory of the module/project.
|
||||
|
||||
Returns:
|
||||
Test code with corrected import paths.
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
# Calculate the correct relative import path from test file to source file
|
||||
test_dir = test_file_path.parent
|
||||
try:
|
||||
correct_rel_path = os.path.relpath(source_file_path, test_dir)
|
||||
correct_rel_path = correct_rel_path.replace("\\", "/")
|
||||
# Remove file extension for JS/TS imports
|
||||
for ext in [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"]:
|
||||
if correct_rel_path.endswith(ext):
|
||||
correct_rel_path = correct_rel_path[: -len(ext)]
|
||||
break
|
||||
# Ensure it starts with ./ or ../
|
||||
if not correct_rel_path.startswith("."):
|
||||
correct_rel_path = "./" + correct_rel_path
|
||||
except ValueError:
|
||||
# Can't compute relative path (different drives on Windows)
|
||||
return test_code
|
||||
|
||||
# Try to compute what incorrect path the AI might have generated
|
||||
# The AI often uses module_root-relative paths like 'apps/web/app/...'
|
||||
try:
|
||||
source_rel_to_module = os.path.relpath(source_file_path, module_root)
|
||||
source_rel_to_module = source_rel_to_module.replace("\\", "/")
|
||||
# Remove extension
|
||||
for ext in [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"]:
|
||||
if source_rel_to_module.endswith(ext):
|
||||
source_rel_to_module = source_rel_to_module[: -len(ext)]
|
||||
break
|
||||
except ValueError:
|
||||
return test_code
|
||||
|
||||
# Also check for project root-relative paths (including module_root in path)
|
||||
try:
|
||||
project_root = module_root.parent if module_root.name in ["src", "lib", "app", "web", "apps"] else module_root
|
||||
source_rel_to_project = os.path.relpath(source_file_path, project_root)
|
||||
source_rel_to_project = source_rel_to_project.replace("\\", "/")
|
||||
for ext in [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"]:
|
||||
if source_rel_to_project.endswith(ext):
|
||||
source_rel_to_project = source_rel_to_project[: -len(ext)]
|
||||
break
|
||||
except ValueError:
|
||||
source_rel_to_project = None
|
||||
|
||||
# Source file name (for matching module paths that end with the file name)
|
||||
source_name = source_file_path.stem
|
||||
|
||||
# Patterns to find import statements
|
||||
# ESM: import { func } from 'path' or import func from 'path'
|
||||
esm_import_pattern = re.compile(r"(import\s+(?:{[^}]+}|\w+)\s+from\s+['\"])([^'\"]+)(['\"])")
|
||||
# CommonJS: const { func } = require('path') or const func = require('path')
|
||||
cjs_require_pattern = re.compile(
|
||||
r"((?:const|let|var)\s+(?:{[^}]+}|\w+)\s*=\s*require\s*\(\s*['\"])([^'\"]+)(['\"])"
|
||||
)
|
||||
|
||||
def should_fix_path(import_path: str) -> bool:
|
||||
"""Check if this import path looks like it should point to our source file."""
|
||||
# Skip relative imports that already look correct
|
||||
if import_path.startswith(("./", "../")):
|
||||
return False
|
||||
# Skip package imports (no path separators or start with @)
|
||||
if "/" not in import_path and "\\" not in import_path:
|
||||
return False
|
||||
if import_path.startswith("@") and "/" in import_path:
|
||||
# Could be an alias like @/utils - skip these
|
||||
return False
|
||||
# Check if it looks like it points to our source file
|
||||
if import_path == source_rel_to_module:
|
||||
return True
|
||||
if source_rel_to_project and import_path == source_rel_to_project:
|
||||
return True
|
||||
if import_path.endswith((source_name, "/" + source_name)):
|
||||
return True
|
||||
return False
|
||||
|
||||
def fix_import(match: re.Match[str]) -> str:
|
||||
"""Replace incorrect import path with correct relative path."""
|
||||
prefix = match.group(1)
|
||||
import_path = match.group(2)
|
||||
suffix = match.group(3)
|
||||
|
||||
if should_fix_path(import_path):
|
||||
logger.debug(f"Fixing import path: {import_path} -> {correct_rel_path}")
|
||||
return f"{prefix}{correct_rel_path}{suffix}"
|
||||
return match.group(0)
|
||||
|
||||
test_code = esm_import_pattern.sub(fix_import, test_code)
|
||||
return cjs_require_pattern.sub(fix_import, test_code)
|
||||
|
||||
|
||||
def get_instrumented_test_path(original_path: Path, mode: str) -> Path:
|
||||
"""Generate path for instrumented test file.
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import json
|
|||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
|
|
|||
|
|
@ -416,8 +416,10 @@ def ensure_module_system_compatibility(code: str, target_module_system: str, pro
|
|||
is_esm = has_import or has_export
|
||||
|
||||
# Convert if needed
|
||||
if target_module_system == ModuleSystem.ES_MODULE and is_commonjs and not is_esm:
|
||||
logger.debug("Converting CommonJS to ES Module syntax")
|
||||
# For ESM target: convert any require statements, even if there are also import statements
|
||||
# This handles generated tests that have ESM imports for test globals but CommonJS for the function
|
||||
if target_module_system == ModuleSystem.ES_MODULE and has_require:
|
||||
logger.debug("Converting CommonJS require statements to ES Module syntax")
|
||||
return convert_commonjs_to_esm(code)
|
||||
|
||||
if target_module_system == ModuleSystem.COMMONJS and is_esm and not is_commonjs:
|
||||
|
|
|
|||
|
|
@ -16,12 +16,7 @@ from typing import TYPE_CHECKING
|
|||
from junitparser.xunit2 import JUnitXml
|
||||
|
||||
from codeflash.cli_cmds.console import logger
|
||||
from codeflash.models.models import (
|
||||
FunctionTestInvocation,
|
||||
InvocationId,
|
||||
TestResults,
|
||||
TestType,
|
||||
)
|
||||
from codeflash.models.models import FunctionTestInvocation, InvocationId, TestResults, TestType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import subprocess
|
||||
|
|
@ -127,6 +122,7 @@ def parse_jest_test_xml(
|
|||
# This handles cases where instrumented files are in temp directories
|
||||
instrumented_path_lookup: dict[str, tuple[Path, TestType]] = {}
|
||||
for test_file in test_files.test_files:
|
||||
# Add behavior instrumented file paths
|
||||
if test_file.instrumented_behavior_file_path:
|
||||
# Store both the absolute path and resolved path as keys
|
||||
abs_path = str(test_file.instrumented_behavior_file_path.resolve())
|
||||
|
|
@ -137,18 +133,35 @@ def parse_jest_test_xml(
|
|||
test_file.test_type,
|
||||
)
|
||||
logger.debug(f"Jest XML lookup: registered {abs_path}")
|
||||
# Also add benchmarking file paths (perf-only instrumented tests)
|
||||
if test_file.benchmarking_file_path:
|
||||
bench_abs_path = str(test_file.benchmarking_file_path.resolve())
|
||||
instrumented_path_lookup[bench_abs_path] = (test_file.benchmarking_file_path, test_file.test_type)
|
||||
instrumented_path_lookup[str(test_file.benchmarking_file_path)] = (
|
||||
test_file.benchmarking_file_path,
|
||||
test_file.test_type,
|
||||
)
|
||||
logger.debug(f"Jest XML lookup: registered benchmark {bench_abs_path}")
|
||||
|
||||
# Also build a filename-only lookup for fallback matching
|
||||
# This handles cases where JUnit XML has relative paths that don't match absolute paths
|
||||
# e.g., JUnit has "test/utils__perfinstrumented.test.ts" but lookup has absolute paths
|
||||
filename_lookup: dict[str, tuple[Path, TestType]] = {}
|
||||
for test_file in test_files.test_files:
|
||||
# Add instrumented_behavior_file_path (behavior tests)
|
||||
if test_file.instrumented_behavior_file_path:
|
||||
filename = test_file.instrumented_behavior_file_path.name
|
||||
# Only add if not already present (avoid overwrites in case of duplicate filenames)
|
||||
if filename not in filename_lookup:
|
||||
filename_lookup[filename] = (test_file.instrumented_behavior_file_path, test_file.test_type)
|
||||
logger.debug(f"Jest XML filename lookup: registered {filename}")
|
||||
# Also add benchmarking_file_path (perf-only tests) - these have different filenames
|
||||
# e.g., utils__perfonlyinstrumented.test.ts vs utils__perfinstrumented.test.ts
|
||||
if test_file.benchmarking_file_path:
|
||||
bench_filename = test_file.benchmarking_file_path.name
|
||||
if bench_filename not in filename_lookup:
|
||||
filename_lookup[bench_filename] = (test_file.benchmarking_file_path, test_file.test_type)
|
||||
logger.debug(f"Jest XML filename lookup: registered benchmark file {bench_filename}")
|
||||
|
||||
# Fallback: if JUnit XML doesn't have system-out, use subprocess stdout directly
|
||||
global_stdout = ""
|
||||
|
|
@ -162,6 +175,19 @@ def parse_jest_test_xml(
|
|||
logger.debug(f"Found {marker_count} timing start markers in Jest stdout")
|
||||
else:
|
||||
logger.debug(f"No timing start markers found in Jest stdout (len={len(global_stdout)})")
|
||||
# Check for END markers with duration (perf test markers)
|
||||
end_marker_count = len(jest_end_pattern.findall(global_stdout))
|
||||
if end_marker_count > 0:
|
||||
logger.debug(
|
||||
f"[PERF-DEBUG] Found {end_marker_count} END timing markers with duration in Jest stdout"
|
||||
)
|
||||
# Sample a few markers to verify loop indices
|
||||
end_samples = list(jest_end_pattern.finditer(global_stdout))[:5]
|
||||
for sample in end_samples:
|
||||
groups = sample.groups()
|
||||
logger.debug(f"[PERF-DEBUG] Sample END marker: loopIndex={groups[3]}, duration={groups[5]}")
|
||||
else:
|
||||
logger.debug("[PERF-DEBUG] No END markers with duration found in Jest stdout")
|
||||
except (AttributeError, UnicodeDecodeError):
|
||||
global_stdout = ""
|
||||
|
||||
|
|
@ -184,6 +210,29 @@ def parse_jest_test_xml(
|
|||
key = match.groups()[:5]
|
||||
end_matches_dict[key] = match
|
||||
|
||||
# Debug: log suite-level END marker parsing for perf tests
|
||||
if end_matches_dict:
|
||||
# Get unique loop indices from the parsed END markers
|
||||
loop_indices = sorted({int(k[3]) if k[3].isdigit() else 1 for k in end_matches_dict})
|
||||
logger.debug(
|
||||
f"[PERF-DEBUG] Suite {suite_count}: parsed {len(end_matches_dict)} END markers from suite_stdout, loop_index range: {min(loop_indices)}-{max(loop_indices)}"
|
||||
)
|
||||
|
||||
# Also collect timing markers from testcase-level system-out (Vitest puts output at testcase level)
|
||||
for tc in suite:
|
||||
tc_system_out = tc._elem.find("system-out") # noqa: SLF001
|
||||
if tc_system_out is not None and tc_system_out.text:
|
||||
tc_stdout = tc_system_out.text.strip()
|
||||
logger.debug(f"Vitest testcase system-out found: {len(tc_stdout)} chars, first 200: {tc_stdout[:200]}")
|
||||
end_marker_count = 0
|
||||
for match in jest_end_pattern.finditer(tc_stdout):
|
||||
key = match.groups()[:5]
|
||||
end_matches_dict[key] = match
|
||||
end_marker_count += 1
|
||||
if end_marker_count > 0:
|
||||
logger.debug(f"Found {end_marker_count} END timing markers in testcase system-out")
|
||||
start_matches.extend(jest_start_pattern.finditer(tc_stdout))
|
||||
|
||||
for testcase in suite:
|
||||
testcase_count += 1
|
||||
test_class_path = testcase.classname # For Jest, this is the file path
|
||||
|
|
@ -299,6 +348,13 @@ def parse_jest_test_xml(
|
|||
sanitized_test_name = re.sub(r"[!#: ()\[\]{}|\\/*?^$.+\-]", "_", test_name)
|
||||
matching_starts = [m for m in start_matches if sanitized_test_name in m.group(2)]
|
||||
|
||||
# Debug: log which branch we're taking
|
||||
logger.debug(
|
||||
f"[FLOW-DEBUG] Testcase '{test_name[:50]}': "
|
||||
f"total_start_matches={len(start_matches)}, matching_starts={len(matching_starts)}, "
|
||||
f"total_end_matches={len(end_matches_dict)}"
|
||||
)
|
||||
|
||||
# For performance tests (capturePerf), there are no START markers - only END markers with duration
|
||||
# Check for END markers directly if no START markers found
|
||||
matching_ends_direct = []
|
||||
|
|
@ -309,9 +365,42 @@ def parse_jest_test_xml(
|
|||
# end_key is (module, testName, funcName, loopIndex, invocationId)
|
||||
if len(end_key) >= 2 and sanitized_test_name in end_key[1]:
|
||||
matching_ends_direct.append(end_match)
|
||||
# Debug: log matching results for perf tests
|
||||
if matching_ends_direct:
|
||||
loop_indices = [int(m.groups()[3]) if m.groups()[3].isdigit() else 1 for m in matching_ends_direct]
|
||||
logger.debug(
|
||||
f"[PERF-MATCH] Testcase '{test_name[:40]}': matched {len(matching_ends_direct)} END markers, "
|
||||
f"loop_index range: {min(loop_indices)}-{max(loop_indices)}"
|
||||
)
|
||||
elif end_matches_dict:
|
||||
# No matches but we have END markers - check why
|
||||
sample_keys = list(end_matches_dict.keys())[:3]
|
||||
logger.debug(
|
||||
f"[PERF-MISMATCH] Testcase '{test_name[:40]}': no matches found. "
|
||||
f"sanitized_test_name='{sanitized_test_name[:50]}', "
|
||||
f"sample end_keys={[k[1][:30] if len(k) >= 2 else k for k in sample_keys]}"
|
||||
)
|
||||
|
||||
# Log if we're skipping the matching_ends_direct branch
|
||||
if matching_starts and end_matches_dict:
|
||||
logger.debug(
|
||||
f"[FLOW-SKIP] Testcase '{test_name[:40]}': has {len(matching_starts)} START markers, "
|
||||
f"skipping {len(end_matches_dict)} END markers (behavior test mode)"
|
||||
)
|
||||
|
||||
if not matching_starts and not matching_ends_direct:
|
||||
# No timing markers found - add basic result
|
||||
# No timing markers found - use JUnit XML time attribute as fallback
|
||||
# The time attribute is in seconds (e.g., "0.00077875"), convert to nanoseconds
|
||||
runtime = None
|
||||
try:
|
||||
time_attr = testcase._elem.attrib.get("time") # noqa: SLF001
|
||||
if time_attr:
|
||||
time_seconds = float(time_attr)
|
||||
runtime = int(time_seconds * 1_000_000_000) # Convert seconds to nanoseconds
|
||||
logger.debug(f"Jest XML: using time attribute for {test_name}: {time_seconds}s = {runtime}ns")
|
||||
except (ValueError, TypeError) as e:
|
||||
logger.debug(f"Jest XML: could not parse time attribute: {e}")
|
||||
|
||||
test_results.add(
|
||||
FunctionTestInvocation(
|
||||
loop_index=1,
|
||||
|
|
@ -323,7 +412,7 @@ def parse_jest_test_xml(
|
|||
iteration_id="",
|
||||
),
|
||||
file_name=test_file_path,
|
||||
runtime=None,
|
||||
runtime=runtime,
|
||||
test_framework=test_config.test_framework,
|
||||
did_pass=result,
|
||||
test_type=test_type,
|
||||
|
|
@ -334,11 +423,13 @@ def parse_jest_test_xml(
|
|||
)
|
||||
elif matching_ends_direct:
|
||||
# Performance test format: process END markers directly (no START markers)
|
||||
loop_indices_found = []
|
||||
for end_match in matching_ends_direct:
|
||||
groups = end_match.groups()
|
||||
# groups: (module, testName, funcName, loopIndex, invocationId, durationNs)
|
||||
func_name = groups[2]
|
||||
loop_index = int(groups[3]) if groups[3].isdigit() else 1
|
||||
loop_indices_found.append(loop_index)
|
||||
line_id = groups[4]
|
||||
try:
|
||||
runtime = int(groups[5])
|
||||
|
|
@ -364,6 +455,12 @@ def parse_jest_test_xml(
|
|||
stdout="",
|
||||
)
|
||||
)
|
||||
if loop_indices_found:
|
||||
logger.debug(
|
||||
f"[LOOP-DEBUG] Testcase '{test_name}': processed {len(matching_ends_direct)} END markers, "
|
||||
f"loop_index range: {min(loop_indices_found)}-{max(loop_indices_found)}, "
|
||||
f"total results so far: {len(test_results.test_results)}"
|
||||
)
|
||||
else:
|
||||
# Process each timing marker
|
||||
for match in matching_starts:
|
||||
|
|
@ -415,5 +512,19 @@ def parse_jest_test_xml(
|
|||
f"Jest XML parsing complete: {len(test_results.test_results)} results "
|
||||
f"from {suite_count} suites, {testcase_count} testcases"
|
||||
)
|
||||
# Debug: show loop_index distribution for perf analysis
|
||||
if test_results.test_results:
|
||||
loop_indices = [r.loop_index for r in test_results.test_results]
|
||||
unique_loop_indices = sorted(set(loop_indices))
|
||||
min_idx, max_idx = min(unique_loop_indices), max(unique_loop_indices)
|
||||
logger.debug(
|
||||
f"[LOOP-SUMMARY] Results loop_index: min={min_idx}, max={max_idx}, "
|
||||
f"unique_count={len(unique_loop_indices)}, total_results={len(loop_indices)}"
|
||||
)
|
||||
if max_idx == 1 and len(loop_indices) > 1:
|
||||
logger.warning(
|
||||
f"[LOOP-WARNING] All {len(loop_indices)} results have loop_index=1. "
|
||||
"Perf test markers may not have been parsed correctly."
|
||||
)
|
||||
|
||||
return test_results
|
||||
|
|
|
|||
|
|
@ -14,15 +14,15 @@ from typing import TYPE_CHECKING, Any
|
|||
|
||||
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
|
||||
from codeflash.languages.base import CodeContext, FunctionFilterCriteria, HelperFunction, Language, TestInfo, TestResult
|
||||
from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer, TreeSitterLanguage, get_analyzer_for_file
|
||||
from codeflash.languages.registry import register_language
|
||||
from codeflash.languages.treesitter_utils import TreeSitterAnalyzer, TreeSitterLanguage, get_analyzer_for_file
|
||||
from codeflash.models.models import FunctionParent
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Sequence
|
||||
|
||||
from codeflash.languages.base import ReferenceInfo
|
||||
from codeflash.languages.treesitter_utils import TypeDefinition
|
||||
from codeflash.languages.javascript.treesitter import TypeDefinition
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -2098,6 +2098,10 @@ class JavaScriptSupport:
|
|||
candidate_index=candidate_index,
|
||||
)
|
||||
|
||||
# JavaScript/TypeScript benchmarking uses high max_loops like Python (100,000)
|
||||
# The actual loop count is limited by target_duration_seconds, not max_loops
|
||||
JS_BENCHMARKING_MAX_LOOPS = 100_000
|
||||
|
||||
def run_benchmarking_tests(
|
||||
self,
|
||||
test_paths: Any,
|
||||
|
|
@ -2130,10 +2134,15 @@ class JavaScriptSupport:
|
|||
from codeflash.languages.test_framework import get_js_test_framework_or_default
|
||||
|
||||
framework = test_framework or get_js_test_framework_or_default()
|
||||
logger.debug("run_benchmarking_tests called with framework=%s", framework)
|
||||
|
||||
# Use JS-specific high max_loops - actual loop count is limited by target_duration
|
||||
effective_max_loops = self.JS_BENCHMARKING_MAX_LOOPS
|
||||
|
||||
if framework == "vitest":
|
||||
from codeflash.languages.javascript.vitest_runner import run_vitest_benchmarking_tests
|
||||
|
||||
logger.debug("Dispatching to run_vitest_benchmarking_tests")
|
||||
return run_vitest_benchmarking_tests(
|
||||
test_paths=test_paths,
|
||||
test_env=test_env,
|
||||
|
|
@ -2141,7 +2150,7 @@ class JavaScriptSupport:
|
|||
timeout=timeout,
|
||||
project_root=project_root,
|
||||
min_loops=min_loops,
|
||||
max_loops=max_loops,
|
||||
max_loops=effective_max_loops,
|
||||
target_duration_ms=int(target_duration_seconds * 1000),
|
||||
)
|
||||
|
||||
|
|
@ -2154,7 +2163,7 @@ class JavaScriptSupport:
|
|||
timeout=timeout,
|
||||
project_root=project_root,
|
||||
min_loops=min_loops,
|
||||
max_loops=max_loops,
|
||||
max_loops=effective_max_loops,
|
||||
target_duration_ms=int(target_duration_seconds * 1000),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,9 @@ class ExportInfo:
|
|||
reexport_source: str | None # Module path for re-exports
|
||||
start_line: int
|
||||
end_line: int
|
||||
# Functions passed as arguments to wrapper calls in default exports
|
||||
# e.g., export default curry(traverseEntity) -> ["traverseEntity"]
|
||||
wrapped_default_args: list[str] | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -707,6 +710,7 @@ class TreeSitterAnalyzer:
|
|||
default_export: str | None = None
|
||||
is_reexport = False
|
||||
reexport_source: str | None = None
|
||||
wrapped_default_args: list[str] | None = None
|
||||
|
||||
# Check for re-export source (export { x } from './other')
|
||||
source_node = node.child_by_field_name("source")
|
||||
|
|
@ -726,6 +730,12 @@ class TreeSitterAnalyzer:
|
|||
default_export = self.get_node_text(sibling, source_bytes)
|
||||
elif sibling.type in ("arrow_function", "function_expression", "object", "array"):
|
||||
default_export = "default"
|
||||
elif sibling.type == "call_expression":
|
||||
# Handle wrapped exports: export default curry(traverseEntity)
|
||||
# The default export is the result of the call, but we track
|
||||
# the wrapped function names for export checking
|
||||
default_export = "default"
|
||||
wrapped_default_args = self._extract_call_expression_identifiers(sibling, source_bytes)
|
||||
break
|
||||
|
||||
# Handle named exports: export { a, b as c }
|
||||
|
|
@ -773,8 +783,37 @@ class TreeSitterAnalyzer:
|
|||
reexport_source=reexport_source,
|
||||
start_line=node.start_point[0] + 1,
|
||||
end_line=node.end_point[0] + 1,
|
||||
wrapped_default_args=wrapped_default_args,
|
||||
)
|
||||
|
||||
def _extract_call_expression_identifiers(self, node: Node, source_bytes: bytes) -> list[str]:
|
||||
"""Extract identifier names from arguments of a call expression.
|
||||
|
||||
For patterns like curry(traverseEntity) or compose(fn1, fn2), this extracts
|
||||
the function names passed as arguments: ["traverseEntity"] or ["fn1", "fn2"].
|
||||
|
||||
Args:
|
||||
node: A call_expression node.
|
||||
source_bytes: The source code as bytes.
|
||||
|
||||
Returns:
|
||||
List of identifier names found in the call arguments.
|
||||
|
||||
"""
|
||||
identifiers: list[str] = []
|
||||
|
||||
# Get the arguments node
|
||||
args_node = node.child_by_field_name("arguments")
|
||||
if args_node:
|
||||
for child in args_node.children:
|
||||
if child.type == "identifier":
|
||||
identifiers.append(self.get_node_text(child, source_bytes))
|
||||
# Also handle nested call expressions: compose(curry(fn))
|
||||
elif child.type == "call_expression":
|
||||
identifiers.extend(self._extract_call_expression_identifiers(child, source_bytes))
|
||||
|
||||
return identifiers
|
||||
|
||||
def _extract_commonjs_export(self, node: Node, source_bytes: bytes) -> ExportInfo | None:
|
||||
"""Extract export information from CommonJS module.exports or exports.* patterns.
|
||||
|
||||
|
|
@ -876,6 +915,7 @@ class TreeSitterAnalyzer:
|
|||
"""Check if a function is exported and get its export name.
|
||||
|
||||
For class methods, also checks if the containing class is exported.
|
||||
Also handles wrapped exports like: export default curry(traverseEntity)
|
||||
|
||||
Args:
|
||||
source: The source code to analyze.
|
||||
|
|
@ -901,6 +941,11 @@ class TreeSitterAnalyzer:
|
|||
if name == function_name:
|
||||
return (True, alias if alias else name)
|
||||
|
||||
# Check wrapped default exports: export default curry(traverseEntity)
|
||||
# The function is exported via wrapper, so it's accessible as "default"
|
||||
if export.wrapped_default_args and function_name in export.wrapped_default_args:
|
||||
return (True, "default")
|
||||
|
||||
# For class methods, check if the containing class is exported
|
||||
if class_name:
|
||||
for export in exports:
|
||||
|
|
@ -1580,9 +1625,9 @@ def get_analyzer_for_file(file_path: Path) -> TreeSitterAnalyzer:
|
|||
"""
|
||||
suffix = file_path.suffix.lower()
|
||||
|
||||
if suffix in (".ts",):
|
||||
if suffix == ".ts":
|
||||
return TreeSitterAnalyzer(TreeSitterLanguage.TYPESCRIPT)
|
||||
if suffix in (".tsx",):
|
||||
if suffix == ".tsx":
|
||||
return TreeSitterAnalyzer(TreeSitterLanguage.TSX)
|
||||
# Default to JavaScript for .js, .jsx, .mjs, .cjs
|
||||
return TreeSitterAnalyzer(TreeSitterLanguage.JAVASCRIPT)
|
||||
|
|
@ -23,8 +23,13 @@ if TYPE_CHECKING:
|
|||
def _find_vitest_project_root(file_path: Path) -> Path | None:
|
||||
"""Find the Vitest project root by looking for vitest/vite config or package.json.
|
||||
|
||||
Traverses up from the given file path to find the nearest directory
|
||||
containing vitest.config.js/ts, vite.config.js/ts, or package.json.
|
||||
Traverses up from the given file path to find the directory containing
|
||||
vitest.config.js/ts or vite.config.js/ts. Falls back to package.json only
|
||||
if no vitest/vite config is found in any parent directory.
|
||||
|
||||
In monorepos, package.json may exist at multiple levels (e.g., packages/lib/package.json),
|
||||
but the vitest config with setupFiles is typically at the monorepo root.
|
||||
We need to prioritize finding the actual vitest config to ensure paths resolve correctly.
|
||||
|
||||
Args:
|
||||
file_path: A file path within the Vitest project.
|
||||
|
|
@ -34,8 +39,10 @@ def _find_vitest_project_root(file_path: Path) -> Path | None:
|
|||
|
||||
"""
|
||||
current = file_path.parent if file_path.is_file() else file_path
|
||||
package_json_dir = None # Track first package.json found (fallback)
|
||||
|
||||
while current != current.parent: # Stop at filesystem root
|
||||
# Check for Vitest-specific config files first
|
||||
# Check for Vitest-specific config files first - these should take priority
|
||||
if (
|
||||
(current / "vitest.config.js").exists()
|
||||
or (current / "vitest.config.ts").exists()
|
||||
|
|
@ -45,27 +52,40 @@ def _find_vitest_project_root(file_path: Path) -> Path | None:
|
|||
or (current / "vite.config.ts").exists()
|
||||
or (current / "vite.config.mjs").exists()
|
||||
or (current / "vite.config.mts").exists()
|
||||
or (current / "package.json").exists()
|
||||
):
|
||||
return current
|
||||
# Remember first package.json as fallback, but keep looking for vitest config
|
||||
if package_json_dir is None and (current / "package.json").exists():
|
||||
package_json_dir = current
|
||||
current = current.parent
|
||||
return None
|
||||
|
||||
# No vitest config found, fall back to package.json directory if found
|
||||
return package_json_dir
|
||||
|
||||
|
||||
def _is_vitest_coverage_available(project_root: Path) -> bool:
|
||||
"""Check if Vitest coverage package is available.
|
||||
|
||||
In monorepos, dependencies may be hoisted to the root node_modules.
|
||||
This function searches up the directory tree for the coverage package.
|
||||
|
||||
Args:
|
||||
project_root: The project root directory.
|
||||
project_root: The project root directory (may be a package in a monorepo).
|
||||
|
||||
Returns:
|
||||
True if @vitest/coverage-v8 or @vitest/coverage-istanbul is installed.
|
||||
|
||||
"""
|
||||
node_modules = project_root / "node_modules"
|
||||
return (node_modules / "@vitest" / "coverage-v8").exists() or (
|
||||
node_modules / "@vitest" / "coverage-istanbul"
|
||||
).exists()
|
||||
current = project_root
|
||||
while current != current.parent: # Stop at filesystem root
|
||||
node_modules = current / "node_modules"
|
||||
if node_modules.exists():
|
||||
if (node_modules / "@vitest" / "coverage-v8").exists() or (
|
||||
node_modules / "@vitest" / "coverage-istanbul"
|
||||
).exists():
|
||||
return True
|
||||
current = current.parent
|
||||
return False
|
||||
|
||||
|
||||
def _ensure_runtime_files(project_root: Path) -> None:
|
||||
|
|
@ -97,8 +117,146 @@ def _ensure_runtime_files(project_root: Path) -> None:
|
|||
logger.error(f"Could not install codeflash. Please install it manually: {' '.join(install_cmd)}")
|
||||
|
||||
|
||||
def _find_monorepo_root(start_path: Path) -> Path | None:
|
||||
"""Find the monorepo root by looking for workspace markers.
|
||||
|
||||
Args:
|
||||
start_path: A path within the monorepo.
|
||||
|
||||
Returns:
|
||||
The monorepo root directory, or None if not found.
|
||||
|
||||
"""
|
||||
monorepo_markers = ["pnpm-workspace.yaml", "yarn.lock", "lerna.json", "package-lock.json"]
|
||||
current = start_path if start_path.is_dir() else start_path.parent
|
||||
|
||||
while current != current.parent:
|
||||
# Check for monorepo markers
|
||||
if any((current / marker).exists() for marker in monorepo_markers):
|
||||
# Verify it has node_modules or package.json (it's a real root)
|
||||
if (current / "node_modules").exists() or (current / "package.json").exists():
|
||||
return current
|
||||
current = current.parent
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _is_vitest_workspace(project_root: Path) -> bool:
|
||||
"""Check if the project uses vitest workspace configuration.
|
||||
|
||||
Vitest workspaces have a special structure where the root config
|
||||
points to package-level configs. We shouldn't override these.
|
||||
|
||||
Args:
|
||||
project_root: The project root directory.
|
||||
|
||||
Returns:
|
||||
True if the project appears to use vitest workspace.
|
||||
|
||||
"""
|
||||
vitest_config = project_root / "vitest.config.ts"
|
||||
if not vitest_config.exists():
|
||||
vitest_config = project_root / "vitest.config.js"
|
||||
if not vitest_config.exists():
|
||||
return False
|
||||
|
||||
try:
|
||||
content = vitest_config.read_text()
|
||||
# Check for workspace indicators
|
||||
return "workspace" in content.lower() or "defineWorkspace" in content
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _ensure_codeflash_vitest_config(project_root: Path) -> Path | None:
|
||||
"""Create or find a Codeflash-compatible Vitest config.
|
||||
|
||||
Vitest configs often have restrictive include patterns like 'test/**/*.test.ts'
|
||||
which filter out our generated test files. This function creates a config
|
||||
that overrides the include pattern to accept all test files.
|
||||
|
||||
Note: For workspace projects, we skip creating a custom config as it would
|
||||
conflict with the workspace setup. In those cases, tests should be placed
|
||||
in the correct package's test directory.
|
||||
|
||||
Args:
|
||||
project_root: The project root directory.
|
||||
|
||||
Returns:
|
||||
Path to the Codeflash Vitest config, or None if creation failed/not needed.
|
||||
|
||||
"""
|
||||
# Check for workspace configuration - don't override these
|
||||
monorepo_root = _find_monorepo_root(project_root)
|
||||
if monorepo_root and _is_vitest_workspace(monorepo_root):
|
||||
logger.debug("Detected vitest workspace configuration - skipping custom config")
|
||||
return None
|
||||
|
||||
codeflash_config_path = project_root / "codeflash.vitest.config.mjs"
|
||||
|
||||
# If already exists, use it
|
||||
if codeflash_config_path.exists():
|
||||
logger.debug(f"Using existing Codeflash Vitest config: {codeflash_config_path}")
|
||||
return codeflash_config_path
|
||||
|
||||
# Find the original vitest config to extend
|
||||
original_config = None
|
||||
for config_name in ["vitest.config.ts", "vitest.config.js", "vitest.config.mts", "vitest.config.mjs"]:
|
||||
config_path = project_root / config_name
|
||||
if config_path.exists():
|
||||
original_config = config_name
|
||||
break
|
||||
|
||||
# Also check for vite config with vitest settings
|
||||
if not original_config:
|
||||
for config_name in ["vite.config.ts", "vite.config.js", "vite.config.mts", "vite.config.mjs"]:
|
||||
config_path = project_root / config_name
|
||||
if config_path.exists():
|
||||
original_config = config_name
|
||||
break
|
||||
|
||||
# Create a config that extends the original and overrides include pattern
|
||||
if original_config:
|
||||
config_content = f"""// Auto-generated by Codeflash for test file pattern compatibility
|
||||
import {{ mergeConfig }} from 'vitest/config';
|
||||
import originalConfig from './{original_config}';
|
||||
|
||||
export default mergeConfig(originalConfig, {{
|
||||
test: {{
|
||||
// Override include pattern to match all test files including generated ones
|
||||
include: ['**/*.test.ts', '**/*.test.js', '**/*.test.tsx', '**/*.test.jsx'],
|
||||
}},
|
||||
}});
|
||||
"""
|
||||
else:
|
||||
# No original config found, create a minimal one
|
||||
config_content = """// Auto-generated by Codeflash for test file pattern compatibility
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
// Include all test files including generated ones
|
||||
include: ['**/*.test.ts', '**/*.test.js', '**/*.test.tsx', '**/*.test.jsx'],
|
||||
// Exclude common non-test directories
|
||||
exclude: ['**/node_modules/**', '**/dist/**'],
|
||||
},
|
||||
});
|
||||
"""
|
||||
|
||||
try:
|
||||
codeflash_config_path.write_text(config_content)
|
||||
logger.debug(f"Created Codeflash Vitest config: {codeflash_config_path}")
|
||||
return codeflash_config_path
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to create Codeflash Vitest config: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def _build_vitest_behavioral_command(
|
||||
test_files: list[Path], timeout: int | None = None, output_file: Path | None = None
|
||||
test_files: list[Path],
|
||||
timeout: int | None = None,
|
||||
output_file: Path | None = None,
|
||||
project_root: Path | None = None,
|
||||
) -> list[str]:
|
||||
"""Build Vitest command for behavioral tests.
|
||||
|
||||
|
|
@ -106,6 +264,7 @@ def _build_vitest_behavioral_command(
|
|||
test_files: List of test files to run.
|
||||
timeout: Optional timeout in seconds.
|
||||
output_file: Optional path for JUnit XML output.
|
||||
project_root: Project root directory for --root flag.
|
||||
|
||||
Returns:
|
||||
Command list for subprocess execution.
|
||||
|
|
@ -120,6 +279,14 @@ def _build_vitest_behavioral_command(
|
|||
"--no-file-parallelism", # Serial execution for deterministic timing
|
||||
]
|
||||
|
||||
# For monorepos with restrictive vitest configs (e.g., include: test/**/*.test.ts),
|
||||
# we need to create a custom config that allows all test patterns.
|
||||
# This is done by creating a codeflash.vitest.config.mjs file.
|
||||
if project_root:
|
||||
codeflash_vitest_config = _ensure_codeflash_vitest_config(project_root)
|
||||
if codeflash_vitest_config:
|
||||
cmd.append(f"--config={codeflash_vitest_config}")
|
||||
|
||||
if output_file:
|
||||
# Use dot notation for junit reporter output file when multiple reporters are used
|
||||
# Format: --outputFile.junit=/path/to/file.xml
|
||||
|
|
@ -135,7 +302,10 @@ def _build_vitest_behavioral_command(
|
|||
|
||||
|
||||
def _build_vitest_benchmarking_command(
|
||||
test_files: list[Path], timeout: int | None = None, output_file: Path | None = None
|
||||
test_files: list[Path],
|
||||
timeout: int | None = None,
|
||||
output_file: Path | None = None,
|
||||
project_root: Path | None = None,
|
||||
) -> list[str]:
|
||||
"""Build Vitest command for benchmarking tests.
|
||||
|
||||
|
|
@ -143,6 +313,7 @@ def _build_vitest_benchmarking_command(
|
|||
test_files: List of test files to run.
|
||||
timeout: Optional timeout in seconds.
|
||||
output_file: Optional path for JUnit XML output.
|
||||
project_root: Project root directory for --root flag.
|
||||
|
||||
Returns:
|
||||
Command list for subprocess execution.
|
||||
|
|
@ -157,6 +328,12 @@ def _build_vitest_benchmarking_command(
|
|||
"--no-file-parallelism", # Serial execution for consistent benchmarking
|
||||
]
|
||||
|
||||
# Use codeflash vitest config to override restrictive include patterns
|
||||
if project_root:
|
||||
codeflash_vitest_config = _ensure_codeflash_vitest_config(project_root)
|
||||
if codeflash_vitest_config:
|
||||
cmd.append(f"--config={codeflash_vitest_config}")
|
||||
|
||||
if output_file:
|
||||
# Use dot notation for junit reporter output file when multiple reporters are used
|
||||
cmd.append(f"--outputFile.junit={output_file}")
|
||||
|
|
@ -220,11 +397,20 @@ def run_vitest_behavioral_tests(
|
|||
logger.debug("Vitest coverage package not installed, running without coverage")
|
||||
|
||||
# Build Vitest command
|
||||
vitest_cmd = _build_vitest_behavioral_command(test_files=test_files, timeout=timeout, output_file=result_file_path)
|
||||
vitest_cmd = _build_vitest_behavioral_command(
|
||||
test_files=test_files, timeout=timeout, output_file=result_file_path, project_root=effective_cwd
|
||||
)
|
||||
|
||||
# Add coverage flags only if coverage is available
|
||||
if coverage_available:
|
||||
# Don't pre-create the coverage directory - vitest should create it
|
||||
# Pre-creating an empty directory may cause vitest to delete it
|
||||
logger.debug(f"Coverage will be written to: {coverage_dir}")
|
||||
|
||||
vitest_cmd.extend(["--coverage", "--coverage.reporter=json", f"--coverage.reportsDirectory={coverage_dir}"])
|
||||
# Note: Removed --coverage.enabled=true (redundant) and --coverage.all false
|
||||
# The version mismatch between vitest and @vitest/coverage-v8 can cause
|
||||
# issues with coverage flag parsing. Let vitest use default settings.
|
||||
|
||||
# Set up environment
|
||||
vitest_env = test_env.copy()
|
||||
|
|
@ -251,6 +437,7 @@ def run_vitest_behavioral_tests(
|
|||
cwd=effective_cwd, env=vitest_env, timeout=subprocess_timeout, check=False, text=True, capture_output=True
|
||||
)
|
||||
result = subprocess.run(vitest_cmd, **run_args) # noqa: PLW1510
|
||||
|
||||
# Combine stderr into stdout for timing markers
|
||||
if result.stderr and not result.stdout:
|
||||
result = subprocess.CompletedProcess(
|
||||
|
|
@ -288,8 +475,7 @@ def run_vitest_behavioral_tests(
|
|||
logger.debug(f"Vitest JUnit XML created: {result_file_path} ({file_size} bytes)")
|
||||
if file_size < 200: # Suspiciously small - likely empty or just headers
|
||||
logger.warning(
|
||||
f"Vitest JUnit XML is very small ({file_size} bytes). "
|
||||
f"Content: {result_file_path.read_text()[:500]}"
|
||||
f"Vitest JUnit XML is very small ({file_size} bytes). Content: {result_file_path.read_text()[:500]}"
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
|
|
@ -297,6 +483,26 @@ def run_vitest_behavioral_tests(
|
|||
f"Vitest stdout: {result.stdout[:1000] if result.stdout else '(empty)'}"
|
||||
)
|
||||
|
||||
# Check if coverage file was created
|
||||
if coverage_available and coverage_json_path:
|
||||
if coverage_json_path.exists():
|
||||
cov_size = coverage_json_path.stat().st_size
|
||||
logger.debug(f"Vitest coverage JSON created: {coverage_json_path} ({cov_size} bytes)")
|
||||
else:
|
||||
# Check if the parent directory exists and list its contents
|
||||
cov_parent = coverage_json_path.parent
|
||||
if cov_parent.exists():
|
||||
contents = list(cov_parent.iterdir())
|
||||
logger.warning(
|
||||
f"Vitest coverage JSON not created at {coverage_json_path}. "
|
||||
f"Directory exists with contents: {[f.name for f in contents]}"
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"Vitest coverage JSON not created at {coverage_json_path}. "
|
||||
f"Coverage directory does not exist: {cov_parent}"
|
||||
)
|
||||
|
||||
return result_file_path, result, coverage_json_path, None
|
||||
|
||||
|
||||
|
|
@ -314,6 +520,9 @@ def run_vitest_benchmarking_tests(
|
|||
) -> tuple[Path, subprocess.CompletedProcess]:
|
||||
"""Run Vitest benchmarking tests with external looping from Python.
|
||||
|
||||
NOTE: This function MUST use benchmarking_file_path (perf tests with capturePerf),
|
||||
NOT instrumented_behavior_file_path (behavior tests with capture).
|
||||
|
||||
Uses external process-level looping to run tests multiple times and
|
||||
collect timing data. This matches the Python pytest approach where
|
||||
looping is controlled externally for simplicity.
|
||||
|
|
@ -338,6 +547,26 @@ def run_vitest_benchmarking_tests(
|
|||
# Get performance test files
|
||||
test_files = [Path(file.benchmarking_file_path) for file in test_paths.test_files if file.benchmarking_file_path]
|
||||
|
||||
# Log test file selection
|
||||
total_test_files = len(test_paths.test_files)
|
||||
perf_test_files = len(test_files)
|
||||
logger.debug(
|
||||
f"Vitest benchmark test file selection: {perf_test_files}/{total_test_files} have benchmarking_file_path"
|
||||
)
|
||||
if perf_test_files == 0:
|
||||
logger.warning("No perf test files found! Cannot run benchmarking tests.")
|
||||
for tf in test_paths.test_files:
|
||||
logger.warning(
|
||||
f"Test file: behavior={tf.instrumented_behavior_file_path}, perf={tf.benchmarking_file_path}"
|
||||
)
|
||||
elif perf_test_files < total_test_files:
|
||||
for tf in test_paths.test_files:
|
||||
if not tf.benchmarking_file_path:
|
||||
logger.warning(f"Missing benchmarking_file_path: behavior={tf.instrumented_behavior_file_path}")
|
||||
else:
|
||||
for tf in test_files[:3]: # Log first 3 perf test files
|
||||
logger.debug(f"Using perf test file: {tf}")
|
||||
|
||||
# Use provided project_root, or detect it as fallback
|
||||
if project_root is None and test_files:
|
||||
project_root = _find_vitest_project_root(test_files[0])
|
||||
|
|
@ -350,7 +579,7 @@ def run_vitest_benchmarking_tests(
|
|||
|
||||
# Build Vitest command for performance tests
|
||||
vitest_cmd = _build_vitest_benchmarking_command(
|
||||
test_files=test_files, timeout=timeout, output_file=result_file_path
|
||||
test_files=test_files, timeout=timeout, output_file=result_file_path, project_root=effective_cwd
|
||||
)
|
||||
|
||||
# Base environment setup
|
||||
|
|
@ -368,14 +597,25 @@ def run_vitest_benchmarking_tests(
|
|||
vitest_env["CODEFLASH_PERF_STABILITY_CHECK"] = "true" if stability_check else "false"
|
||||
vitest_env["CODEFLASH_LOOP_INDEX"] = "1"
|
||||
|
||||
# Set test module for marker identification (use first test file as reference)
|
||||
if test_files:
|
||||
test_module_path = str(
|
||||
test_files[0].relative_to(effective_cwd)
|
||||
if test_files[0].is_relative_to(effective_cwd)
|
||||
else test_files[0].name
|
||||
)
|
||||
vitest_env["CODEFLASH_TEST_MODULE"] = test_module_path
|
||||
logger.debug(f"[VITEST-BENCH] Set CODEFLASH_TEST_MODULE={test_module_path}")
|
||||
|
||||
# Total timeout for the entire benchmark run
|
||||
total_timeout = max(120, (target_duration_ms // 1000) + 60, timeout or 120)
|
||||
|
||||
logger.debug(f"Running Vitest benchmarking tests: {' '.join(vitest_cmd)}")
|
||||
logger.debug(f"[VITEST-BENCH] Running Vitest benchmarking tests: {' '.join(vitest_cmd)}")
|
||||
logger.debug(
|
||||
f"Vitest benchmarking config: min_loops={min_loops}, max_loops={max_loops}, "
|
||||
f"[VITEST-BENCH] Config: min_loops={min_loops}, max_loops={max_loops}, "
|
||||
f"target_duration={target_duration_ms}ms, stability_check={stability_check}"
|
||||
)
|
||||
logger.debug(f"[VITEST-BENCH] Environment: CODEFLASH_PERF_LOOP_COUNT={vitest_env.get('CODEFLASH_PERF_LOOP_COUNT')}")
|
||||
|
||||
total_start_time = time.time()
|
||||
|
||||
|
|
@ -400,7 +640,27 @@ def run_vitest_benchmarking_tests(
|
|||
result = subprocess.CompletedProcess(args=vitest_cmd, returncode=-1, stdout="", stderr="Vitest not found")
|
||||
|
||||
wall_clock_seconds = time.time() - total_start_time
|
||||
logger.debug(f"Vitest benchmarking completed in {wall_clock_seconds:.2f}s")
|
||||
logger.debug(f"[VITEST-BENCH] Completed in {wall_clock_seconds:.2f}s, returncode={result.returncode}")
|
||||
|
||||
# Debug: Check for END markers with duration (perf test format)
|
||||
if result.stdout:
|
||||
import re
|
||||
|
||||
perf_end_pattern = re.compile(r"!######[^:]+:[^:]+:[^:]+:(\d+):[^:]+:(\d+)######!")
|
||||
perf_matches = list(perf_end_pattern.finditer(result.stdout))
|
||||
if perf_matches:
|
||||
loop_indices = [int(m.group(1)) for m in perf_matches]
|
||||
logger.debug(
|
||||
f"[VITEST-BENCH] Found {len(perf_matches)} perf END markers in stdout, "
|
||||
f"loop_index range: {min(loop_indices)}-{max(loop_indices)}"
|
||||
)
|
||||
else:
|
||||
logger.debug(f"[VITEST-BENCH] No perf END markers found in stdout (len={len(result.stdout)})")
|
||||
# Check if there are behavior END markers instead
|
||||
behavior_end_pattern = re.compile(r"!######[^:]+:[^:]+:[^:]+:\d+:[^#]+######!")
|
||||
behavior_matches = list(behavior_end_pattern.finditer(result.stdout))
|
||||
if behavior_matches:
|
||||
logger.debug(f"[VITEST-BENCH] Found {len(behavior_matches)} behavior END markers instead (no duration)")
|
||||
|
||||
return result_file_path, result
|
||||
|
||||
|
|
@ -461,6 +721,12 @@ def run_vitest_line_profile_tests(
|
|||
"--no-file-parallelism", # Serial execution for consistent line profiling
|
||||
]
|
||||
|
||||
# Use codeflash vitest config to override restrictive include patterns
|
||||
if effective_cwd:
|
||||
codeflash_vitest_config = _ensure_codeflash_vitest_config(effective_cwd)
|
||||
if codeflash_vitest_config:
|
||||
vitest_cmd.append(f"--config={codeflash_vitest_config}")
|
||||
|
||||
# Use dot notation for junit reporter output file when multiple reporters are used
|
||||
vitest_cmd.append(f"--outputFile.junit={result_file_path}")
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ from collections.abc import Collection
|
|||
from enum import Enum, IntEnum
|
||||
from pathlib import Path
|
||||
from re import Pattern
|
||||
from typing import NamedTuple, Optional, cast
|
||||
from typing import Any, NamedTuple, Optional, cast
|
||||
|
||||
from jedi.api.classes import Name
|
||||
from pydantic import BaseModel, ConfigDict, Field, PrivateAttr, ValidationError, model_validator
|
||||
|
|
@ -172,7 +172,7 @@ class BestOptimization(BaseModel):
|
|||
winning_behavior_test_results: TestResults
|
||||
winning_benchmarking_test_results: TestResults
|
||||
winning_replay_benchmarking_test_results: Optional[TestResults] = None
|
||||
line_profiler_test_results: dict
|
||||
line_profiler_test_results: dict[Any, Any]
|
||||
async_throughput: Optional[int] = None
|
||||
concurrency_metrics: Optional[ConcurrencyMetrics] = None
|
||||
|
||||
|
|
@ -209,7 +209,7 @@ class BenchmarkDetail:
|
|||
f"Benchmark speedup for {self.benchmark_name}::{self.test_function}: {self.speedup_percent:.2f}%\n"
|
||||
)
|
||||
|
||||
def to_dict(self) -> dict[str, any]:
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
return {
|
||||
"benchmark_name": self.benchmark_name,
|
||||
"test_function": self.test_function,
|
||||
|
|
@ -232,20 +232,28 @@ class ProcessedBenchmarkInfo:
|
|||
result += detail.to_string() + "\n"
|
||||
return result
|
||||
|
||||
def to_dict(self) -> dict[str, list[dict[str, any]]]:
|
||||
def to_dict(self) -> dict[str, list[dict[str, Any]]]:
|
||||
return {"benchmark_details": [detail.to_dict() for detail in self.benchmark_details]}
|
||||
|
||||
|
||||
class CodeString(BaseModel):
|
||||
code: str
|
||||
file_path: Optional[Path] = None
|
||||
language: str = "python" # Language for validation - only Python code is validated
|
||||
language: str = "python" # Language for validation
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_code_syntax(self) -> CodeString:
|
||||
"""Validate code syntax for Python only."""
|
||||
"""Validate code syntax for the specified language."""
|
||||
if self.language == "python":
|
||||
validate_python_code(self.code)
|
||||
elif self.language in ("javascript", "typescript"):
|
||||
# Validate JavaScript/TypeScript syntax using language support
|
||||
from codeflash.languages.registry import get_language_support
|
||||
|
||||
lang_support = get_language_support(self.language)
|
||||
if not lang_support.validate_syntax(self.code):
|
||||
msg = f"Invalid {self.language.title()} code"
|
||||
raise ValueError(msg)
|
||||
return self
|
||||
|
||||
|
||||
|
|
@ -272,7 +280,7 @@ markdown_pattern_python_only = re.compile(r"```python:([^\n]+)\n(.*?)\n```", re.
|
|||
class CodeStringsMarkdown(BaseModel):
|
||||
code_strings: list[CodeString] = []
|
||||
language: str = "python" # Language for markdown code block tags
|
||||
_cache: dict = PrivateAttr(default_factory=dict)
|
||||
_cache: dict[str, Any] = PrivateAttr(default_factory=dict)
|
||||
|
||||
@property
|
||||
def flat(self) -> str:
|
||||
|
|
@ -408,7 +416,7 @@ class GeneratedTestsList(BaseModel):
|
|||
|
||||
class TestFile(BaseModel):
|
||||
instrumented_behavior_file_path: Path
|
||||
benchmarking_file_path: Path = None
|
||||
benchmarking_file_path: Optional[Path] = None
|
||||
original_file_path: Optional[Path] = None
|
||||
original_source: Optional[str] = None
|
||||
test_type: TestType
|
||||
|
|
@ -448,6 +456,19 @@ class TestFiles(BaseModel):
|
|||
normalized_benchmark_path = self._normalize_path_for_comparison(test_file.benchmarking_file_path)
|
||||
if normalized == normalized_benchmark_path:
|
||||
return test_file.test_type
|
||||
|
||||
# Fallback: try filename-only matching for JavaScript/TypeScript
|
||||
# Jest/Vitest JUnit XML may have relative paths that don't match absolute paths
|
||||
file_name = file_path.name
|
||||
for test_file in self.test_files:
|
||||
if (
|
||||
test_file.instrumented_behavior_file_path
|
||||
and test_file.instrumented_behavior_file_path.name == file_name
|
||||
):
|
||||
return test_file.test_type
|
||||
if test_file.benchmarking_file_path and test_file.benchmarking_file_path.name == file_name:
|
||||
return test_file.test_type
|
||||
|
||||
return None
|
||||
|
||||
def get_test_type_by_original_file_path(self, file_path: Path) -> TestType | None:
|
||||
|
|
|
|||
|
|
@ -315,7 +315,7 @@ class CandidateProcessor:
|
|||
self.future_all_code_repair,
|
||||
"Repairing {0} candidates",
|
||||
"Added {0} candidates from repair, total candidates now: {1}",
|
||||
lambda: self.future_all_code_repair.clear(),
|
||||
self.future_all_code_repair.clear,
|
||||
)
|
||||
if self.line_profiler_done and not self.refinement_done:
|
||||
return self._process_candidates(
|
||||
|
|
@ -330,7 +330,7 @@ class CandidateProcessor:
|
|||
self.future_adaptive_optimizations,
|
||||
"Applying adaptive optimizations to {0} candidates",
|
||||
"Added {0} candidates from adaptive optimization, total candidates now: {1}",
|
||||
lambda: self.future_adaptive_optimizations.clear(),
|
||||
self.future_adaptive_optimizations.clear,
|
||||
)
|
||||
return None # All done
|
||||
|
||||
|
|
@ -545,15 +545,24 @@ class FunctionOptimizer:
|
|||
]:
|
||||
"""Generate and instrument tests for the function."""
|
||||
n_tests = get_effort_value(EffortKeys.N_GENERATED_TESTS, self.effort)
|
||||
source_file = Path(self.function_to_optimize.file_path)
|
||||
generated_test_paths = [
|
||||
get_test_file_path(
|
||||
self.test_cfg.tests_root, self.function_to_optimize.function_name, test_index, test_type="unit"
|
||||
self.test_cfg.tests_root,
|
||||
self.function_to_optimize.function_name,
|
||||
test_index,
|
||||
test_type="unit",
|
||||
source_file_path=source_file,
|
||||
)
|
||||
for test_index in range(n_tests)
|
||||
]
|
||||
generated_perf_test_paths = [
|
||||
get_test_file_path(
|
||||
self.test_cfg.tests_root, self.function_to_optimize.function_name, test_index, test_type="perf"
|
||||
self.test_cfg.tests_root,
|
||||
self.function_to_optimize.function_name,
|
||||
test_index,
|
||||
test_type="perf",
|
||||
source_file_path=source_file,
|
||||
)
|
||||
for test_index in range(n_tests)
|
||||
]
|
||||
|
|
@ -578,7 +587,7 @@ class FunctionOptimizer:
|
|||
if not is_python():
|
||||
module_system = detect_module_system(self.project_root, self.function_to_optimize.file_path)
|
||||
if module_system == "esm":
|
||||
generated_tests = inject_test_globals(generated_tests)
|
||||
generated_tests = inject_test_globals(generated_tests, self.test_cfg.test_framework)
|
||||
if is_typescript():
|
||||
# disable ts check for typescript tests
|
||||
generated_tests = disable_ts_check(generated_tests)
|
||||
|
|
@ -1906,10 +1915,11 @@ class FunctionOptimizer:
|
|||
return Failure(baseline_result.failure())
|
||||
|
||||
original_code_baseline, test_functions_to_remove = baseline_result.unwrap()
|
||||
if isinstance(original_code_baseline, OriginalCodeBaseline) and (
|
||||
not coverage_critic(original_code_baseline.coverage_results)
|
||||
or not quantity_of_tests_critic(original_code_baseline)
|
||||
):
|
||||
# Check test quantity for all languages
|
||||
quantity_ok = quantity_of_tests_critic(original_code_baseline)
|
||||
# TODO: {Self} Only check coverage for Python - coverage infrastructure not yet reliable for JS/TS
|
||||
coverage_ok = coverage_critic(original_code_baseline.coverage_results) if is_python() else True
|
||||
if isinstance(original_code_baseline, OriginalCodeBaseline) and (not coverage_ok or not quantity_ok):
|
||||
if self.args.override_fixtures:
|
||||
restore_conftest(original_conftest_content)
|
||||
cleanup_paths(paths_to_cleanup)
|
||||
|
|
@ -2093,7 +2103,7 @@ class FunctionOptimizer:
|
|||
formatted_generated_test = format_generated_code(concolic_test_str, self.args.formatter_cmds)
|
||||
generated_tests_str += f"```{code_lang}\n{formatted_generated_test}\n```\n\n"
|
||||
|
||||
existing_tests, replay_tests, concolic_tests = existing_tests_source_for(
|
||||
existing_tests, replay_tests, _ = existing_tests_source_for(
|
||||
self.function_to_optimize.qualified_name_with_modules_from_root(self.project_root),
|
||||
function_to_all_tests,
|
||||
test_cfg=self.test_cfg,
|
||||
|
|
@ -2353,6 +2363,12 @@ class FunctionOptimizer:
|
|||
)
|
||||
console.rule()
|
||||
with progress_bar("Running performance benchmarks..."):
|
||||
logger.debug(
|
||||
f"[BENCHMARK-START] Starting benchmarking tests with {len(self.test_files.test_files)} test files"
|
||||
)
|
||||
for idx, tf in enumerate(self.test_files.test_files):
|
||||
logger.debug(f"[BENCHMARK-FILES] Test file {idx}: perf_file={tf.benchmarking_file_path}")
|
||||
|
||||
if self.function_to_optimize.is_async and is_python():
|
||||
from codeflash.code_utils.instrument_existing_tests import add_async_decorator_to_function
|
||||
|
||||
|
|
@ -2370,6 +2386,7 @@ class FunctionOptimizer:
|
|||
enable_coverage=False,
|
||||
code_context=code_context,
|
||||
)
|
||||
logger.debug(f"[BENCHMARK-DONE] Got {len(benchmarking_results.test_results)} benchmark results")
|
||||
finally:
|
||||
if self.function_to_optimize.is_async:
|
||||
self.write_code_and_helpers(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import enum
|
|||
import math
|
||||
import re
|
||||
import types
|
||||
import weakref
|
||||
from collections import ChainMap, OrderedDict, deque
|
||||
from importlib.util import find_spec
|
||||
from typing import Any, Optional
|
||||
|
|
@ -172,6 +173,17 @@ def comparator(orig: Any, new: Any, superset_obj: bool = False) -> bool:
|
|||
return True
|
||||
return math.isclose(orig, new)
|
||||
|
||||
# Handle weak references (e.g., found in torch.nn.LSTM/GRU modules)
|
||||
if isinstance(orig, weakref.ref):
|
||||
orig_referent = orig()
|
||||
new_referent = new()
|
||||
# Both dead refs are equal, otherwise compare referents
|
||||
if orig_referent is None and new_referent is None:
|
||||
return True
|
||||
if orig_referent is None or new_referent is None:
|
||||
return False
|
||||
return comparator(orig_referent, new_referent, superset_obj)
|
||||
|
||||
if HAS_JAX:
|
||||
import jax # type: ignore # noqa: PGH003
|
||||
import jax.numpy as jnp # type: ignore # noqa: PGH003
|
||||
|
|
|
|||
|
|
@ -325,6 +325,7 @@ def run_benchmarking_tests(
|
|||
pytest_max_loops: int = 100_000,
|
||||
js_project_root: Path | None = None,
|
||||
) -> tuple[Path, subprocess.CompletedProcess]:
|
||||
logger.debug(f"run_benchmarking_tests called: framework={test_framework}, num_files={len(test_paths.test_files)}")
|
||||
# Check if there's a language support for this test framework that implements run_benchmarking_tests
|
||||
language_support = get_language_support_by_framework(test_framework)
|
||||
if language_support is not None and hasattr(language_support, "run_benchmarking_tests"):
|
||||
|
|
|
|||
|
|
@ -9,17 +9,93 @@ from pydantic.dataclasses import dataclass
|
|||
from codeflash.languages import current_language_support, is_javascript
|
||||
|
||||
|
||||
def get_test_file_path(test_dir: Path, function_name: str, iteration: int = 0, test_type: str = "unit") -> Path:
|
||||
def get_test_file_path(
|
||||
test_dir: Path,
|
||||
function_name: str,
|
||||
iteration: int = 0,
|
||||
test_type: str = "unit",
|
||||
source_file_path: Path | None = None,
|
||||
) -> Path:
|
||||
assert test_type in {"unit", "inspired", "replay", "perf"}
|
||||
function_name = function_name.replace(".", "_")
|
||||
# Use appropriate file extension based on language
|
||||
extension = current_language_support().get_test_file_suffix() if is_javascript() else ".py"
|
||||
|
||||
# For JavaScript/TypeScript, place generated tests in a subdirectory that matches
|
||||
# Vitest/Jest include patterns (e.g., test/**/*.test.ts)
|
||||
if is_javascript():
|
||||
# For monorepos, first try to find the package directory from the source file path
|
||||
# e.g., packages/workflow/src/utils.ts -> packages/workflow/test/codeflash-generated/
|
||||
package_test_dir = _find_js_package_test_dir(test_dir, source_file_path)
|
||||
if package_test_dir:
|
||||
test_dir = package_test_dir
|
||||
|
||||
path = test_dir / f"test_{function_name}__{test_type}_test_{iteration}{extension}"
|
||||
if path.exists():
|
||||
return get_test_file_path(test_dir, function_name, iteration + 1, test_type)
|
||||
return get_test_file_path(test_dir, function_name, iteration + 1, test_type, source_file_path)
|
||||
return path
|
||||
|
||||
|
||||
def _find_js_package_test_dir(tests_root: Path, source_file_path: Path | None) -> Path | None:
|
||||
"""Find the appropriate test directory for a JavaScript/TypeScript package.
|
||||
|
||||
For monorepos, this finds the package's test directory from the source file path.
|
||||
For example: packages/workflow/src/utils.ts -> packages/workflow/test/codeflash-generated/
|
||||
|
||||
Args:
|
||||
tests_root: The root tests directory (may be monorepo packages root).
|
||||
source_file_path: Path to the source file being tested.
|
||||
|
||||
Returns:
|
||||
The test directory path, or None if not found.
|
||||
|
||||
"""
|
||||
if source_file_path is None:
|
||||
# No source path provided, check if test_dir itself has a test subdirectory
|
||||
for test_subdir_name in ["test", "tests", "__tests__", "src/__tests__"]:
|
||||
test_subdir = tests_root / test_subdir_name
|
||||
if test_subdir.is_dir():
|
||||
codeflash_test_dir = test_subdir / "codeflash-generated"
|
||||
codeflash_test_dir.mkdir(parents=True, exist_ok=True)
|
||||
return codeflash_test_dir
|
||||
return None
|
||||
|
||||
try:
|
||||
# Resolve paths for reliable comparison
|
||||
tests_root = tests_root.resolve()
|
||||
source_path = Path(source_file_path).resolve()
|
||||
|
||||
# Walk up from the source file to find a directory with package.json or test/ folder
|
||||
package_dir = None
|
||||
|
||||
for parent in source_path.parents:
|
||||
# Stop if we've gone above or reached the tests_root level
|
||||
# For monorepos, tests_root might be /packages/ and we want to search within packages
|
||||
if parent in (tests_root, tests_root.parent):
|
||||
break
|
||||
|
||||
# Check if this looks like a package root
|
||||
has_package_json = (parent / "package.json").exists()
|
||||
has_test_dir = any((parent / d).is_dir() for d in ["test", "tests", "__tests__"])
|
||||
|
||||
if has_package_json or has_test_dir:
|
||||
package_dir = parent
|
||||
break
|
||||
|
||||
if package_dir:
|
||||
# Find the test directory in this package
|
||||
for test_subdir_name in ["test", "tests", "__tests__", "src/__tests__"]:
|
||||
test_subdir = package_dir / test_subdir_name
|
||||
if test_subdir.is_dir():
|
||||
codeflash_test_dir = test_subdir / "codeflash-generated"
|
||||
codeflash_test_dir.mkdir(parents=True, exist_ok=True)
|
||||
return codeflash_test_dir
|
||||
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def delete_multiple_if_name_main(test_ast: ast.Module) -> ast.Module:
|
||||
if_indexes = []
|
||||
for index, node in enumerate(test_ast.body):
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ def generate_tests(
|
|||
if is_javascript():
|
||||
from codeflash.languages.javascript.instrument import (
|
||||
TestingMode,
|
||||
fix_import_path_for_test_location,
|
||||
instrument_generated_js_test,
|
||||
validate_and_fix_import_style,
|
||||
)
|
||||
|
|
@ -76,6 +77,12 @@ def generate_tests(
|
|||
|
||||
source_file = Path(function_to_optimize.file_path)
|
||||
|
||||
# Fix import paths to be relative to test file location
|
||||
# AI may generate imports like 'apps/web/app/file' instead of '../../app/file'
|
||||
generated_test_source = fix_import_path_for_test_location(
|
||||
generated_test_source, source_file, test_path, module_path
|
||||
)
|
||||
|
||||
# Validate and fix import styles (default vs named exports)
|
||||
generated_test_source = validate_and_fix_import_style(
|
||||
generated_test_source, source_file, function_to_optimize.function_name
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
# These version placeholders will be replaced by uv-dynamic-versioning during build.
|
||||
__version__ = "0.20.0"
|
||||
__version__ = "0.20.0.post510.dev0+b8932209"
|
||||
|
|
|
|||
4
packages/codeflash/package-lock.json
generated
4
packages/codeflash/package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "codeflash",
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "codeflash",
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "codeflash",
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.0",
|
||||
"description": "Codeflash - AI-powered code optimization for JavaScript and TypeScript",
|
||||
"main": "runtime/index.js",
|
||||
"types": "runtime/index.d.ts",
|
||||
|
|
|
|||
|
|
@ -839,7 +839,7 @@ function setTestName(name) {
|
|||
resetInvocationCounters();
|
||||
}
|
||||
|
||||
// Jest lifecycle hooks - these run automatically when this module is imported
|
||||
// Jest/Vitest lifecycle hooks - these run automatically when this module is imported
|
||||
if (typeof beforeEach !== 'undefined') {
|
||||
beforeEach(() => {
|
||||
// Get current test name and path from Jest's expect state
|
||||
|
|
@ -854,6 +854,17 @@ if (typeof beforeEach !== 'undefined') {
|
|||
}
|
||||
// Reset invocation counters for each test
|
||||
resetInvocationCounters();
|
||||
|
||||
// For Vitest (no external loop-runner), reset perf state for each test
|
||||
// so each test gets its own time budget for internal looping.
|
||||
// For Jest with loop-runner, CODEFLASH_PERF_CURRENT_BATCH is set,
|
||||
// and we want shared state across the test file.
|
||||
const hasExternalLoopRunner = process.env.CODEFLASH_PERF_CURRENT_BATCH !== undefined;
|
||||
if (!hasExternalLoopRunner) {
|
||||
resetPerfState();
|
||||
// Also reset invocation loop counts so each test starts fresh
|
||||
sharedPerfState.invocationLoopCounts = {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,8 +77,13 @@ module.exports = {
|
|||
incrementBatch: capture.incrementBatch,
|
||||
getCurrentBatch: capture.getCurrentBatch,
|
||||
checkSharedTimeLimit: capture.checkSharedTimeLimit,
|
||||
PERF_BATCH_SIZE: capture.PERF_BATCH_SIZE,
|
||||
PERF_LOOP_COUNT: capture.PERF_LOOP_COUNT,
|
||||
// Getter functions for dynamic env var reading (not constants)
|
||||
getPerfBatchSize: capture.getPerfBatchSize,
|
||||
getPerfLoopCount: capture.getPerfLoopCount,
|
||||
getPerfMinLoops: capture.getPerfMinLoops,
|
||||
getPerfTargetDurationMs: capture.getPerfTargetDurationMs,
|
||||
getPerfStabilityCheck: capture.getPerfStabilityCheck,
|
||||
getPerfCurrentBatch: capture.getPerfCurrentBatch,
|
||||
|
||||
// === Feature Detection ===
|
||||
hasV8: serializer.hasV8,
|
||||
|
|
|
|||
|
|
@ -106,40 +106,63 @@ function installUv() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if git is available
|
||||
*/
|
||||
function hasGit() {
|
||||
try {
|
||||
const result = spawnSync('git', ['--version'], {
|
||||
stdio: 'ignore',
|
||||
shell: true,
|
||||
});
|
||||
return result.status === 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install codeflash Python CLI using uv tool
|
||||
*
|
||||
* Installation priority:
|
||||
* 1. GitHub main branch (if git available) - gets latest features
|
||||
* 2. PyPI (fallback) - stable release
|
||||
*
|
||||
* We prefer GitHub because it has the latest JS/TS support that may not
|
||||
* be published to PyPI yet. uv handles cloning internally in its cache.
|
||||
*/
|
||||
function installCodeflash(uvBin) {
|
||||
logStep('2/3', 'Installing codeflash Python CLI...');
|
||||
|
||||
const GITHUB_REPO = 'git+https://github.com/codeflash-ai/codeflash.git';
|
||||
|
||||
// Priority 1: Install from GitHub (latest features, requires git)
|
||||
if (hasGit()) {
|
||||
try {
|
||||
execSync(`"${uvBin}" tool install --force --python python3.12 "${GITHUB_REPO}"`, {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
logSuccess('codeflash CLI installed from GitHub (latest)');
|
||||
return true;
|
||||
} catch (error) {
|
||||
logWarning(`GitHub installation failed: ${error.message}`);
|
||||
logWarning('Falling back to PyPI...');
|
||||
}
|
||||
} else {
|
||||
logWarning('Git not found, installing from PyPI...');
|
||||
}
|
||||
|
||||
// Priority 2: Install from PyPI (stable release fallback)
|
||||
try {
|
||||
// Use uv tool install to install codeflash in an isolated environment
|
||||
// This avoids conflicts with any existing Python environments
|
||||
execSync(`"${uvBin}" tool install --force --python python3.12 codeflash`, {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
logSuccess('codeflash CLI installed successfully');
|
||||
logSuccess('codeflash CLI installed from PyPI');
|
||||
return true;
|
||||
} catch (error) {
|
||||
// If codeflash is not on PyPI yet, try installing from the local package
|
||||
logWarning('codeflash not found on PyPI, trying local installation...');
|
||||
try {
|
||||
// Try installing from the current codeflash repo if we're in development
|
||||
const cliRoot = path.resolve(__dirname, '..', '..', '..');
|
||||
const pyprojectPath = path.join(cliRoot, 'pyproject.toml');
|
||||
|
||||
if (fs.existsSync(pyprojectPath)) {
|
||||
execSync(`"${uvBin}" tool install --force "${cliRoot}"`, {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
logSuccess('codeflash CLI installed from local source');
|
||||
return true;
|
||||
}
|
||||
} catch (localError) {
|
||||
logError(`Failed to install codeflash: ${localError.message}`);
|
||||
}
|
||||
logError(`Failed to install codeflash: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ dependencies = [
|
|||
"pygls>=2.0.0,<3.0.0",
|
||||
"codeflash-benchmark",
|
||||
"filelock",
|
||||
"pytest-asyncio>=1.2.0",
|
||||
"pytest-asyncio>=0.18.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
|
|
|
|||
0
tests/code_utils/__init__.py
Normal file
0
tests/code_utils/__init__.py
Normal file
209
tests/code_utils/test_normalize_ignore_paths.py
Normal file
209
tests/code_utils/test_normalize_ignore_paths.py
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
"""Tests for normalize_ignore_paths function."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from codeflash.code_utils.code_utils import is_glob_pattern, normalize_ignore_paths
|
||||
|
||||
|
||||
class TestIsGlobPattern:
|
||||
"""Tests for is_glob_pattern function."""
|
||||
|
||||
def test_asterisk_pattern(self) -> None:
|
||||
assert is_glob_pattern("*.py") is True
|
||||
assert is_glob_pattern("**/*.js") is True
|
||||
assert is_glob_pattern("node_modules/*") is True
|
||||
|
||||
def test_question_mark_pattern(self) -> None:
|
||||
assert is_glob_pattern("file?.txt") is True
|
||||
assert is_glob_pattern("test_?.py") is True
|
||||
|
||||
def test_bracket_pattern(self) -> None:
|
||||
assert is_glob_pattern("[abc].txt") is True
|
||||
assert is_glob_pattern("file[0-9].log") is True
|
||||
|
||||
def test_literal_paths(self) -> None:
|
||||
assert is_glob_pattern("node_modules") is False
|
||||
assert is_glob_pattern("src/utils") is False
|
||||
assert is_glob_pattern("/absolute/path") is False
|
||||
assert is_glob_pattern("relative/path/file.py") is False
|
||||
|
||||
|
||||
class TestNormalizeIgnorePaths:
|
||||
"""Tests for normalize_ignore_paths function."""
|
||||
|
||||
def test_empty_list(self) -> None:
|
||||
result = normalize_ignore_paths([])
|
||||
assert result == []
|
||||
|
||||
def test_literal_existing_path(self, tmp_path: Path) -> None:
|
||||
# Create a directory
|
||||
test_dir = tmp_path / "node_modules"
|
||||
test_dir.mkdir()
|
||||
|
||||
result = normalize_ignore_paths(["node_modules"], base_path=tmp_path)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0] == test_dir.resolve()
|
||||
|
||||
def test_literal_nonexistent_path_skipped(self, tmp_path: Path) -> None:
|
||||
# Don't create the directory - should be silently skipped
|
||||
result = normalize_ignore_paths(["nonexistent_dir"], base_path=tmp_path)
|
||||
|
||||
assert result == []
|
||||
|
||||
def test_multiple_literal_paths(self, tmp_path: Path) -> None:
|
||||
# Create directories
|
||||
dir1 = tmp_path / "node_modules"
|
||||
dir2 = tmp_path / "dist"
|
||||
dir1.mkdir()
|
||||
dir2.mkdir()
|
||||
|
||||
result = normalize_ignore_paths(["node_modules", "dist"], base_path=tmp_path)
|
||||
|
||||
assert len(result) == 2
|
||||
assert set(result) == {dir1.resolve(), dir2.resolve()}
|
||||
|
||||
def test_glob_pattern_single_asterisk(self, tmp_path: Path) -> None:
|
||||
# Create test files
|
||||
(tmp_path / "file1.log").touch()
|
||||
(tmp_path / "file2.log").touch()
|
||||
(tmp_path / "file.txt").touch()
|
||||
|
||||
result = normalize_ignore_paths(["*.log"], base_path=tmp_path)
|
||||
|
||||
assert len(result) == 2
|
||||
resolved_names = {p.name for p in result}
|
||||
assert resolved_names == {"file1.log", "file2.log"}
|
||||
|
||||
def test_glob_pattern_double_asterisk(self, tmp_path: Path) -> None:
|
||||
# Create nested structure
|
||||
subdir = tmp_path / "src" / "utils"
|
||||
subdir.mkdir(parents=True)
|
||||
(subdir / "test_helper.py").touch()
|
||||
(tmp_path / "src" / "test_main.py").touch()
|
||||
(tmp_path / "test_root.py").touch()
|
||||
|
||||
result = normalize_ignore_paths(["**/test_*.py"], base_path=tmp_path)
|
||||
|
||||
assert len(result) == 3
|
||||
resolved_names = {p.name for p in result}
|
||||
assert resolved_names == {"test_helper.py", "test_main.py", "test_root.py"}
|
||||
|
||||
def test_glob_pattern_directory_contents(self, tmp_path: Path) -> None:
|
||||
# Create directory with contents
|
||||
node_modules = tmp_path / "node_modules"
|
||||
node_modules.mkdir()
|
||||
(node_modules / "package1").mkdir()
|
||||
(node_modules / "package2").mkdir()
|
||||
|
||||
result = normalize_ignore_paths(["node_modules/*"], base_path=tmp_path)
|
||||
|
||||
assert len(result) == 2
|
||||
resolved_names = {p.name for p in result}
|
||||
assert resolved_names == {"package1", "package2"}
|
||||
|
||||
def test_glob_pattern_no_matches(self, tmp_path: Path) -> None:
|
||||
# Pattern with no matches should return empty list
|
||||
result = normalize_ignore_paths(["*.nonexistent"], base_path=tmp_path)
|
||||
|
||||
assert result == []
|
||||
|
||||
def test_mixed_literal_and_patterns(self, tmp_path: Path) -> None:
|
||||
# Create test structure
|
||||
node_modules = tmp_path / "node_modules"
|
||||
node_modules.mkdir()
|
||||
(tmp_path / "debug.log").touch()
|
||||
(tmp_path / "error.log").touch()
|
||||
|
||||
result = normalize_ignore_paths(["node_modules", "*.log"], base_path=tmp_path)
|
||||
|
||||
assert len(result) == 3
|
||||
resolved_names = {p.name for p in result}
|
||||
assert resolved_names == {"node_modules", "debug.log", "error.log"}
|
||||
|
||||
def test_deduplication(self, tmp_path: Path) -> None:
|
||||
# Create a file that matches multiple patterns
|
||||
(tmp_path / "test.log").touch()
|
||||
|
||||
# Same file should only appear once
|
||||
result = normalize_ignore_paths(["test.log", "*.log"], base_path=tmp_path)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].name == "test.log"
|
||||
|
||||
def test_nested_directory_pattern(self, tmp_path: Path) -> None:
|
||||
# Create nested test directories
|
||||
tests_dir = tmp_path / "src" / "__tests__"
|
||||
tests_dir.mkdir(parents=True)
|
||||
(tests_dir / "test1.js").touch()
|
||||
(tests_dir / "test2.js").touch()
|
||||
|
||||
result = normalize_ignore_paths(["src/__tests__/*.js"], base_path=tmp_path)
|
||||
|
||||
assert len(result) == 2
|
||||
resolved_names = {p.name for p in result}
|
||||
assert resolved_names == {"test1.js", "test2.js"}
|
||||
|
||||
def test_absolute_path_literal(self, tmp_path: Path) -> None:
|
||||
# Create a directory
|
||||
test_dir = tmp_path / "absolute_test"
|
||||
test_dir.mkdir()
|
||||
|
||||
# Use absolute path
|
||||
result = normalize_ignore_paths([str(test_dir)], base_path=tmp_path)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0] == test_dir.resolve()
|
||||
|
||||
def test_relative_path_with_subdirectory(self, tmp_path: Path) -> None:
|
||||
# Create nested directory
|
||||
nested = tmp_path / "src" / "vendor"
|
||||
nested.mkdir(parents=True)
|
||||
|
||||
result = normalize_ignore_paths(["src/vendor"], base_path=tmp_path)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0] == nested.resolve()
|
||||
|
||||
def test_default_base_path_uses_cwd(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
# Change to tmp_path
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# Create a directory
|
||||
test_dir = tmp_path / "test_dir"
|
||||
test_dir.mkdir()
|
||||
|
||||
# Call without base_path
|
||||
result = normalize_ignore_paths(["test_dir"])
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0] == test_dir.resolve()
|
||||
|
||||
def test_bracket_pattern(self, tmp_path: Path) -> None:
|
||||
# Create files matching bracket pattern
|
||||
(tmp_path / "file1.txt").touch()
|
||||
(tmp_path / "file2.txt").touch()
|
||||
(tmp_path / "file3.txt").touch()
|
||||
(tmp_path / "fileA.txt").touch()
|
||||
|
||||
result = normalize_ignore_paths(["file[12].txt"], base_path=tmp_path)
|
||||
|
||||
assert len(result) == 2
|
||||
resolved_names = {p.name for p in result}
|
||||
assert resolved_names == {"file1.txt", "file2.txt"}
|
||||
|
||||
def test_question_mark_pattern(self, tmp_path: Path) -> None:
|
||||
# Create files matching question mark pattern
|
||||
(tmp_path / "test_a.py").touch()
|
||||
(tmp_path / "test_b.py").touch()
|
||||
(tmp_path / "test_ab.py").touch()
|
||||
|
||||
result = normalize_ignore_paths(["test_?.py"], base_path=tmp_path)
|
||||
|
||||
assert len(result) == 2
|
||||
resolved_names = {p.name for p in result}
|
||||
assert resolved_names == {"test_a.py", "test_b.py"}
|
||||
|
|
@ -182,7 +182,9 @@ class TestBenchmarkingTestsDispatch:
|
|||
|
||||
call_kwargs = mock_vitest_runner.call_args.kwargs
|
||||
assert call_kwargs["min_loops"] == 10
|
||||
assert call_kwargs["max_loops"] == 50
|
||||
# JS/TS always uses high max_loops (100_000) regardless of passed value
|
||||
# Actual loop count is limited by target_duration, not max_loops
|
||||
assert call_kwargs["max_loops"] == 100_000
|
||||
assert call_kwargs["target_duration_ms"] == 5000
|
||||
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -950,3 +950,203 @@ def test_filter_functions_non_overlapping_tests_root():
|
|||
|
||||
# Strict check: exactly 2 functions remaining
|
||||
assert count == 2, f"Expected exactly 2 functions, got {count}"
|
||||
|
||||
|
||||
def test_filter_functions_project_inside_tests_folder():
|
||||
"""Test that source files are not filtered when project is inside a folder named 'tests'.
|
||||
|
||||
This is a critical regression test for projects located at paths like:
|
||||
- /home/user/tests/myproject/
|
||||
- /Users/dev/tests/n8n/
|
||||
|
||||
The fix ensures that directory pattern matching (e.g., /tests/) is only checked
|
||||
on the relative path from project_root, not on the full absolute path.
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as outer_temp_dir_str:
|
||||
outer_temp_dir = Path(outer_temp_dir_str)
|
||||
|
||||
# Create a "tests" folder to simulate /home/user/tests/
|
||||
tests_parent_folder = outer_temp_dir / "tests"
|
||||
tests_parent_folder.mkdir()
|
||||
|
||||
# Create project inside the "tests" folder - simulates /home/user/tests/myproject/
|
||||
project_dir = tests_parent_folder / "myproject"
|
||||
project_dir.mkdir()
|
||||
|
||||
# Create source file inside the project
|
||||
src_dir = project_dir / "src"
|
||||
src_dir.mkdir()
|
||||
source_file = src_dir / "utils.py"
|
||||
with source_file.open("w") as f:
|
||||
f.write("""
|
||||
def deep_copy(obj):
|
||||
\"\"\"Deep copy an object.\"\"\"
|
||||
import copy
|
||||
return copy.deepcopy(obj)
|
||||
|
||||
def compare_values(a, b):
|
||||
\"\"\"Compare two values.\"\"\"
|
||||
return a == b
|
||||
""")
|
||||
|
||||
# Create another source file directly in project root
|
||||
root_source_file = project_dir / "main.py"
|
||||
with root_source_file.open("w") as f:
|
||||
f.write("""
|
||||
def main():
|
||||
\"\"\"Main entry point.\"\"\"
|
||||
return 0
|
||||
""")
|
||||
|
||||
# Create actual test files that should be filtered
|
||||
project_tests_dir = project_dir / "test"
|
||||
project_tests_dir.mkdir()
|
||||
test_file = project_tests_dir / "test_utils.py"
|
||||
with test_file.open("w") as f:
|
||||
f.write("""
|
||||
def test_deep_copy():
|
||||
return True
|
||||
""")
|
||||
|
||||
# Discover functions
|
||||
all_functions = {}
|
||||
for file_path in [source_file, root_source_file, test_file]:
|
||||
discovered = find_all_functions_in_file(file_path)
|
||||
all_functions.update(discovered)
|
||||
|
||||
# Test: project at /outer/tests/myproject with tests_root overlapping
|
||||
# This simulates: /home/user/tests/n8n with tests_root = /home/user/tests/n8n
|
||||
with unittest.mock.patch(
|
||||
"codeflash.discovery.functions_to_optimize.get_blocklisted_functions", return_value={}
|
||||
):
|
||||
filtered, count = filter_functions(
|
||||
all_functions,
|
||||
tests_root=project_dir, # Same as project_root (overlapping)
|
||||
ignore_paths=[],
|
||||
project_root=project_dir, # /outer/tests/myproject
|
||||
module_root=project_dir,
|
||||
)
|
||||
|
||||
# Strict check: source files should NOT be filtered even though
|
||||
# the full path contains "/tests/" in the parent directory
|
||||
expected_files = {source_file, root_source_file}
|
||||
actual_files = set(filtered.keys())
|
||||
|
||||
assert actual_files == expected_files, (
|
||||
f"Source files were incorrectly filtered when project is inside 'tests' folder.\n"
|
||||
f"Expected files: {expected_files}\n"
|
||||
f"Got files: {actual_files}\n"
|
||||
f"Project path: {project_dir}\n"
|
||||
f"This indicates the /tests/ pattern matched the parent directory path."
|
||||
)
|
||||
|
||||
# Verify the correct functions are present
|
||||
source_functions = sorted([fn.function_name for fn in filtered.get(source_file, [])])
|
||||
assert source_functions == ["compare_values", "deep_copy"], (
|
||||
f"Expected ['compare_values', 'deep_copy'], got {source_functions}"
|
||||
)
|
||||
|
||||
root_functions = [fn.function_name for fn in filtered.get(root_source_file, [])]
|
||||
assert root_functions == ["main"], (
|
||||
f"Expected ['main'], got {root_functions}"
|
||||
)
|
||||
|
||||
# Strict check: exactly 3 functions (2 from utils.py + 1 from main.py)
|
||||
assert count == 3, (
|
||||
f"Expected exactly 3 functions, got {count}. "
|
||||
f"Some source files may have been incorrectly filtered."
|
||||
)
|
||||
|
||||
# Verify test file was properly filtered (should not be in results)
|
||||
assert test_file not in filtered, (
|
||||
f"Test file {test_file} should have been filtered but wasn't"
|
||||
)
|
||||
|
||||
|
||||
def test_filter_functions_typescript_project_in_tests_folder():
|
||||
"""Test TypeScript-like project structure inside a folder named 'tests'.
|
||||
|
||||
This simulates the n8n project structure:
|
||||
/home/user/tests/n8n/packages/workflow/src/utils.ts
|
||||
|
||||
Ensures that TypeScript source files are not incorrectly filtered
|
||||
when the parent directory happens to be named 'tests'.
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as outer_temp_dir_str:
|
||||
outer_temp_dir = Path(outer_temp_dir_str)
|
||||
|
||||
# Simulate: /home/user/tests/n8n
|
||||
tests_folder = outer_temp_dir / "tests"
|
||||
tests_folder.mkdir()
|
||||
n8n_project = tests_folder / "n8n"
|
||||
n8n_project.mkdir()
|
||||
|
||||
# Simulate: packages/workflow/src/utils.py (using .py for testing)
|
||||
packages_dir = n8n_project / "packages"
|
||||
packages_dir.mkdir()
|
||||
workflow_dir = packages_dir / "workflow"
|
||||
workflow_dir.mkdir()
|
||||
src_dir = workflow_dir / "src"
|
||||
src_dir.mkdir()
|
||||
|
||||
# Source file deep in the monorepo structure
|
||||
utils_file = src_dir / "utils.py"
|
||||
with utils_file.open("w") as f:
|
||||
f.write("""
|
||||
def deep_copy(source):
|
||||
\"\"\"Create a deep copy of the source object.\"\"\"
|
||||
if source is None:
|
||||
return None
|
||||
return source.copy() if hasattr(source, 'copy') else source
|
||||
|
||||
def is_object_empty(obj):
|
||||
\"\"\"Check if an object is empty.\"\"\"
|
||||
return len(obj) == 0 if obj else True
|
||||
""")
|
||||
|
||||
# Create test directory inside the package (simulating packages/workflow/test/)
|
||||
test_dir = workflow_dir / "test"
|
||||
test_dir.mkdir()
|
||||
test_file = test_dir / "utils.test.py"
|
||||
with test_file.open("w") as f:
|
||||
f.write("""
|
||||
def test_deep_copy():
|
||||
return True
|
||||
|
||||
def test_is_object_empty():
|
||||
return True
|
||||
""")
|
||||
|
||||
# Discover functions
|
||||
all_functions = {}
|
||||
for file_path in [utils_file, test_file]:
|
||||
discovered = find_all_functions_in_file(file_path)
|
||||
all_functions.update(discovered)
|
||||
|
||||
# Test with module_root = packages (typical TypeScript monorepo setup)
|
||||
with unittest.mock.patch(
|
||||
"codeflash.discovery.functions_to_optimize.get_blocklisted_functions", return_value={}
|
||||
):
|
||||
filtered, count = filter_functions(
|
||||
all_functions,
|
||||
tests_root=packages_dir, # Overlapping with module_root
|
||||
ignore_paths=[],
|
||||
project_root=n8n_project, # /outer/tests/n8n
|
||||
module_root=packages_dir, # /outer/tests/n8n/packages
|
||||
)
|
||||
|
||||
# Strict check: only the source file should remain
|
||||
assert set(filtered.keys()) == {utils_file}, (
|
||||
f"Expected only {utils_file} but got {set(filtered.keys())}.\n"
|
||||
f"Source files in /outer/tests/n8n/packages/workflow/src/ were incorrectly filtered.\n"
|
||||
f"The /tests/ pattern in the parent path should not affect filtering."
|
||||
)
|
||||
|
||||
# Verify the correct functions are present
|
||||
filtered_functions = sorted([fn.function_name for fn in filtered.get(utils_file, [])])
|
||||
assert filtered_functions == ["deep_copy", "is_object_empty"], (
|
||||
f"Expected ['deep_copy', 'is_object_empty'], got {filtered_functions}"
|
||||
)
|
||||
|
||||
# Strict check: exactly 2 functions
|
||||
assert count == 2, f"Expected exactly 2 functions, got {count}"
|
||||
|
|
@ -8,7 +8,7 @@ to actual file paths, enabling multi-file context extraction.
|
|||
import pytest
|
||||
|
||||
from codeflash.languages.javascript.import_resolver import HelperSearchContext, ImportResolver, MultiFileHelperFinder
|
||||
from codeflash.languages.treesitter_utils import ImportInfo
|
||||
from codeflash.languages.javascript.treesitter import ImportInfo
|
||||
|
||||
|
||||
class TestImportResolver:
|
||||
|
|
@ -286,7 +286,7 @@ class TestExportInfo:
|
|||
@pytest.fixture
|
||||
def js_analyzer(self):
|
||||
"""Create a JavaScript analyzer."""
|
||||
from codeflash.languages.treesitter_utils import TreeSitterAnalyzer, TreeSitterLanguage
|
||||
from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer, TreeSitterLanguage
|
||||
|
||||
return TreeSitterAnalyzer(TreeSitterLanguage.JAVASCRIPT)
|
||||
|
||||
|
|
@ -388,7 +388,7 @@ class TestCommonJSRequire:
|
|||
@pytest.fixture
|
||||
def js_analyzer(self):
|
||||
"""Create a JavaScript analyzer."""
|
||||
from codeflash.languages.treesitter_utils import TreeSitterAnalyzer, TreeSitterLanguage
|
||||
from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer, TreeSitterLanguage
|
||||
|
||||
return TreeSitterAnalyzer(TreeSitterLanguage.JAVASCRIPT)
|
||||
|
||||
|
|
@ -470,14 +470,14 @@ class TestCommonJSExports:
|
|||
@pytest.fixture
|
||||
def js_analyzer(self):
|
||||
"""Create a JavaScript analyzer."""
|
||||
from codeflash.languages.treesitter_utils import TreeSitterAnalyzer, TreeSitterLanguage
|
||||
from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer, TreeSitterLanguage
|
||||
|
||||
return TreeSitterAnalyzer(TreeSitterLanguage.JAVASCRIPT)
|
||||
|
||||
@pytest.fixture
|
||||
def ts_analyzer(self):
|
||||
"""Create a TypeScript analyzer."""
|
||||
from codeflash.languages.treesitter_utils import TreeSitterAnalyzer, TreeSitterLanguage
|
||||
from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer, TreeSitterLanguage
|
||||
|
||||
return TreeSitterAnalyzer(TreeSitterLanguage.TYPESCRIPT)
|
||||
|
||||
|
|
|
|||
312
tests/test_languages/test_javascript_integration.py
Normal file
312
tests/test_languages/test_javascript_integration.py
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
"""E2E tests for JavaScript/TypeScript optimization flow with backend.
|
||||
|
||||
These tests call the actual backend /testgen API endpoint and verify:
|
||||
1. Language parameter is correctly passed to backend
|
||||
2. Backend validates generated code with correct parser (JS vs TS)
|
||||
3. CLI receives and processes tests correctly
|
||||
|
||||
Similar to test_validate_python_code.py but for JavaScript/TypeScript.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from codeflash.api.aiservice import AiServiceClient
|
||||
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
|
||||
from codeflash.languages.base import Language
|
||||
from codeflash.models.models import CodeString, OptimizedCandidateSource
|
||||
|
||||
|
||||
def skip_if_js_not_supported():
|
||||
"""Skip test if JavaScript/TypeScript languages are not supported."""
|
||||
try:
|
||||
from codeflash.languages import get_language_support
|
||||
get_language_support(Language.JAVASCRIPT)
|
||||
except Exception as e:
|
||||
pytest.skip(f"JavaScript/TypeScript language support not available: {e}")
|
||||
|
||||
|
||||
class TestJavaScriptCodeStringValidation:
|
||||
"""Tests for JavaScript CodeString validation - mirrors test_validate_python_code.py."""
|
||||
|
||||
def test_javascript_string(self):
|
||||
"""Test valid JavaScript code string."""
|
||||
skip_if_js_not_supported()
|
||||
code = CodeString(code="console.log('Hello, World!');", language="javascript")
|
||||
assert code.code == "console.log('Hello, World!');"
|
||||
|
||||
def test_valid_javascript_code(self):
|
||||
"""Test that valid JavaScript code passes validation."""
|
||||
skip_if_js_not_supported()
|
||||
valid_code = "const x = 1;\nconst y = x + 2;\nconsole.log(y);"
|
||||
cs = CodeString(code=valid_code, language="javascript")
|
||||
assert cs.code == valid_code
|
||||
|
||||
def test_invalid_javascript_code_syntax(self):
|
||||
"""Test that invalid JavaScript code fails validation."""
|
||||
skip_if_js_not_supported()
|
||||
from pydantic import ValidationError
|
||||
|
||||
invalid_code = "const x = 1;\nconsole.log(x" # Missing parenthesis
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CodeString(code=invalid_code, language="javascript")
|
||||
assert "Invalid Javascript code" in str(exc_info.value)
|
||||
|
||||
def test_empty_javascript_code(self):
|
||||
"""Test that empty code passes validation."""
|
||||
skip_if_js_not_supported()
|
||||
empty_code = ""
|
||||
cs = CodeString(code=empty_code, language="javascript")
|
||||
assert cs.code == empty_code
|
||||
|
||||
|
||||
class TestTypeScriptCodeStringValidation:
|
||||
"""Tests for TypeScript CodeString validation."""
|
||||
|
||||
def test_typescript_string(self):
|
||||
"""Test valid TypeScript code string."""
|
||||
skip_if_js_not_supported()
|
||||
code = CodeString(code="const x: number = 1;", language="typescript")
|
||||
assert code.code == "const x: number = 1;"
|
||||
|
||||
def test_valid_typescript_code(self):
|
||||
"""Test that valid TypeScript code passes validation."""
|
||||
skip_if_js_not_supported()
|
||||
valid_code = "function add(a: number, b: number): number { return a + b; }"
|
||||
cs = CodeString(code=valid_code, language="typescript")
|
||||
assert cs.code == valid_code
|
||||
|
||||
def test_typescript_type_assertion_valid(self):
|
||||
"""TypeScript type assertions should pass TypeScript validation."""
|
||||
skip_if_js_not_supported()
|
||||
ts_code = "const value = 4.9 as unknown as number;"
|
||||
cs = CodeString(code=ts_code, language="typescript")
|
||||
assert cs.code == ts_code
|
||||
|
||||
def test_typescript_type_assertion_invalid_in_javascript(self):
|
||||
"""TypeScript type assertions should FAIL JavaScript validation.
|
||||
|
||||
This is the critical test - TypeScript syntax like 'as unknown as number'
|
||||
should fail when validated as JavaScript.
|
||||
"""
|
||||
skip_if_js_not_supported()
|
||||
from pydantic import ValidationError
|
||||
|
||||
ts_code = "const value = 4.9 as unknown as number;"
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CodeString(code=ts_code, language="javascript")
|
||||
assert "Invalid Javascript code" in str(exc_info.value)
|
||||
|
||||
def test_typescript_interface_valid(self):
|
||||
"""TypeScript interfaces should pass TypeScript validation."""
|
||||
skip_if_js_not_supported()
|
||||
ts_code = "interface User { name: string; age: number; }"
|
||||
cs = CodeString(code=ts_code, language="typescript")
|
||||
assert cs.code == ts_code
|
||||
|
||||
def test_typescript_interface_invalid_in_javascript(self):
|
||||
"""TypeScript interfaces should FAIL JavaScript validation."""
|
||||
skip_if_js_not_supported()
|
||||
from pydantic import ValidationError
|
||||
|
||||
ts_code = "interface User { name: string; age: number; }"
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CodeString(code=ts_code, language="javascript")
|
||||
assert "Invalid Javascript code" in str(exc_info.value)
|
||||
|
||||
def test_typescript_generics_valid(self):
|
||||
"""TypeScript generics should pass TypeScript validation."""
|
||||
skip_if_js_not_supported()
|
||||
ts_code = "function identity<T>(arg: T): T { return arg; }"
|
||||
cs = CodeString(code=ts_code, language="typescript")
|
||||
assert cs.code == ts_code
|
||||
|
||||
def test_typescript_generics_invalid_in_javascript(self):
|
||||
"""TypeScript generics should FAIL JavaScript validation."""
|
||||
skip_if_js_not_supported()
|
||||
from pydantic import ValidationError
|
||||
|
||||
ts_code = "function identity<T>(arg: T): T { return arg; }"
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CodeString(code=ts_code, language="javascript")
|
||||
assert "Invalid Javascript code" in str(exc_info.value)
|
||||
|
||||
|
||||
class TestAiServiceClientJavaScript:
|
||||
"""Tests for AiServiceClient with JavaScript/TypeScript - mirrors test_validate_python_code.py."""
|
||||
|
||||
def test_javascript_generated_candidates_validation(self):
|
||||
"""Test that JavaScript candidates are validated correctly."""
|
||||
skip_if_js_not_supported()
|
||||
ai_service = AiServiceClient()
|
||||
|
||||
# Invalid JavaScript (missing closing parenthesis)
|
||||
code = """```javascript:file.js
|
||||
console.log(name
|
||||
```"""
|
||||
mock_candidates = [{"source_code": code, "explanation": "", "optimization_id": ""}]
|
||||
candidates = ai_service._get_valid_candidates(mock_candidates, OptimizedCandidateSource.OPTIMIZE)
|
||||
assert len(candidates) == 0
|
||||
|
||||
# Valid JavaScript
|
||||
code = """```javascript:file.js
|
||||
console.log('Hello, World!');
|
||||
```"""
|
||||
mock_candidates = [{"source_code": code, "explanation": "", "optimization_id": ""}]
|
||||
candidates = ai_service._get_valid_candidates(mock_candidates, OptimizedCandidateSource.OPTIMIZE)
|
||||
assert len(candidates) == 1
|
||||
assert candidates[0].source_code.code_strings[0].code == "console.log('Hello, World!');"
|
||||
|
||||
def test_typescript_generated_candidates_validation(self):
|
||||
"""Test that TypeScript candidates are validated correctly."""
|
||||
skip_if_js_not_supported()
|
||||
ai_service = AiServiceClient()
|
||||
|
||||
# Valid TypeScript with type annotations
|
||||
code = """```typescript:file.ts
|
||||
function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
```"""
|
||||
mock_candidates = [{"source_code": code, "explanation": "", "optimization_id": ""}]
|
||||
candidates = ai_service._get_valid_candidates(mock_candidates, OptimizedCandidateSource.OPTIMIZE)
|
||||
assert len(candidates) == 1
|
||||
|
||||
def test_typescript_type_assertion_in_candidate(self):
|
||||
"""Test that TypeScript type assertions are valid in TS candidates."""
|
||||
skip_if_js_not_supported()
|
||||
ai_service = AiServiceClient()
|
||||
|
||||
# TypeScript-specific syntax should be valid
|
||||
code = """```typescript:file.ts
|
||||
const value = 4.9 as unknown as number;
|
||||
```"""
|
||||
mock_candidates = [{"source_code": code, "explanation": "", "optimization_id": ""}]
|
||||
candidates = ai_service._get_valid_candidates(mock_candidates, OptimizedCandidateSource.OPTIMIZE)
|
||||
assert len(candidates) == 1
|
||||
|
||||
|
||||
class TestBackendLanguageParameter:
|
||||
"""Tests verifying language parameter flows correctly to backend."""
|
||||
|
||||
def test_testgen_request_includes_typescript_language(self, tmp_path):
|
||||
"""Verify the language parameter is sent as 'typescript' for .ts files."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.languages import current as lang_current
|
||||
|
||||
# Set current language to TypeScript
|
||||
lang_current._current_language = Language.TYPESCRIPT
|
||||
|
||||
ts_file = tmp_path / "utils.ts"
|
||||
ts_file.write_text("""
|
||||
export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
""")
|
||||
|
||||
functions = find_all_functions_in_file(ts_file)
|
||||
func = functions[ts_file][0]
|
||||
|
||||
# Verify function has correct language
|
||||
assert func.language == "typescript"
|
||||
|
||||
ai_client = AiServiceClient()
|
||||
captured_payload = None
|
||||
|
||||
def capture_request(*args, **kwargs):
|
||||
nonlocal captured_payload
|
||||
if 'payload' in kwargs:
|
||||
captured_payload = kwargs['payload']
|
||||
elif len(args) > 1:
|
||||
captured_payload = args[1]
|
||||
# Return a mock response to avoid actual API call
|
||||
from unittest.mock import MagicMock
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"generated_tests": "// test",
|
||||
"instrumented_behavior_tests": "// test",
|
||||
"instrumented_perf_tests": "// test",
|
||||
}
|
||||
return mock_response
|
||||
|
||||
with patch.object(ai_client, 'make_ai_service_request', side_effect=capture_request):
|
||||
ai_client.generate_regression_tests(
|
||||
source_code_being_tested=ts_file.read_text(),
|
||||
function_to_optimize=func,
|
||||
helper_function_names=[],
|
||||
module_path=ts_file,
|
||||
test_module_path=tmp_path / "tests" / "utils.test.ts",
|
||||
test_framework="vitest",
|
||||
test_timeout=30,
|
||||
trace_id="test-language-param-ts",
|
||||
test_index=0,
|
||||
language="typescript",
|
||||
)
|
||||
|
||||
assert captured_payload is not None
|
||||
assert captured_payload.get('language') == 'typescript', \
|
||||
f"Expected language='typescript', got: {captured_payload.get('language')}"
|
||||
|
||||
def test_testgen_request_includes_javascript_language(self, tmp_path):
|
||||
"""Verify the language parameter is sent as 'javascript' for .js files."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.languages import current as lang_current
|
||||
|
||||
# Set current language to JavaScript
|
||||
lang_current._current_language = Language.JAVASCRIPT
|
||||
|
||||
js_file = tmp_path / "utils.js"
|
||||
js_file.write_text("""
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
module.exports = { add };
|
||||
""")
|
||||
|
||||
functions = find_all_functions_in_file(js_file)
|
||||
func = functions[js_file][0]
|
||||
|
||||
# Verify function has correct language
|
||||
assert func.language == "javascript"
|
||||
|
||||
ai_client = AiServiceClient()
|
||||
captured_payload = None
|
||||
|
||||
def capture_request(*args, **kwargs):
|
||||
nonlocal captured_payload
|
||||
if 'payload' in kwargs:
|
||||
captured_payload = kwargs['payload']
|
||||
elif len(args) > 1:
|
||||
captured_payload = args[1]
|
||||
from unittest.mock import MagicMock
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"generated_tests": "// test",
|
||||
"instrumented_behavior_tests": "// test",
|
||||
"instrumented_perf_tests": "// test",
|
||||
}
|
||||
return mock_response
|
||||
|
||||
with patch.object(ai_client, 'make_ai_service_request', side_effect=capture_request):
|
||||
ai_client.generate_regression_tests(
|
||||
source_code_being_tested=js_file.read_text(),
|
||||
function_to_optimize=func,
|
||||
helper_function_names=[],
|
||||
module_path=js_file,
|
||||
test_module_path=tmp_path / "tests" / "utils.test.js",
|
||||
test_framework="jest",
|
||||
test_timeout=30,
|
||||
trace_id="test-language-param-js",
|
||||
test_index=0,
|
||||
language="javascript",
|
||||
)
|
||||
|
||||
assert captured_payload is not None
|
||||
assert captured_payload.get('language') == 'javascript', \
|
||||
f"Expected language='javascript', got: {captured_payload.get('language')}"
|
||||
564
tests/test_languages/test_javascript_optimization_flow.py
Normal file
564
tests/test_languages/test_javascript_optimization_flow.py
Normal file
|
|
@ -0,0 +1,564 @@
|
|||
"""End-to-end tests for JavaScript/TypeScript optimization flow.
|
||||
|
||||
These tests verify the full optimization pipeline including:
|
||||
- Test generation (with mocked backend)
|
||||
- Language parameter propagation
|
||||
- Syntax validation with correct parser
|
||||
- Running and parsing tests
|
||||
|
||||
This is the JavaScript equivalent of test_instrument_tests.py for Python.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
|
||||
from codeflash.languages.base import Language
|
||||
from codeflash.models.models import CodeString, FunctionParent
|
||||
from codeflash.verification.verification_utils import TestConfig
|
||||
|
||||
|
||||
def skip_if_js_not_supported():
|
||||
"""Skip test if JavaScript/TypeScript languages are not supported."""
|
||||
try:
|
||||
from codeflash.languages import get_language_support
|
||||
|
||||
get_language_support(Language.JAVASCRIPT)
|
||||
except Exception as e:
|
||||
pytest.skip(f"JavaScript/TypeScript language support not available: {e}")
|
||||
|
||||
|
||||
class TestLanguageParameterPropagation:
|
||||
"""Tests verifying language parameter is correctly passed through all layers."""
|
||||
|
||||
def test_function_to_optimize_has_correct_language_for_typescript(self, tmp_path):
|
||||
"""Verify FunctionToOptimize has language='typescript' for .ts files."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
|
||||
ts_file = tmp_path / "utils.ts"
|
||||
ts_file.write_text("""
|
||||
export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
""")
|
||||
|
||||
functions = find_all_functions_in_file(ts_file)
|
||||
assert ts_file in functions
|
||||
assert len(functions[ts_file]) == 1
|
||||
assert functions[ts_file][0].language == "typescript"
|
||||
|
||||
def test_function_to_optimize_has_correct_language_for_javascript(self, tmp_path):
|
||||
"""Verify FunctionToOptimize has language='javascript' for .js files."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
|
||||
js_file = tmp_path / "utils.js"
|
||||
js_file.write_text("""
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
""")
|
||||
|
||||
functions = find_all_functions_in_file(js_file)
|
||||
assert js_file in functions
|
||||
assert len(functions[js_file]) == 1
|
||||
assert functions[js_file][0].language == "javascript"
|
||||
|
||||
def test_code_context_preserves_language(self, tmp_path):
|
||||
"""Verify language is preserved in code context extraction."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.context.code_context_extractor import get_code_optimization_context
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.languages import current as lang_current
|
||||
|
||||
lang_current._current_language = Language.TYPESCRIPT
|
||||
|
||||
ts_file = tmp_path / "utils.ts"
|
||||
ts_file.write_text("""
|
||||
export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
""")
|
||||
|
||||
functions = find_all_functions_in_file(ts_file)
|
||||
func = functions[ts_file][0]
|
||||
|
||||
context = get_code_optimization_context(func, tmp_path)
|
||||
|
||||
assert context.read_writable_code is not None
|
||||
assert context.read_writable_code.language == "typescript"
|
||||
|
||||
|
||||
class TestCodeStringSyntaxValidation:
|
||||
"""Tests verifying CodeString validates with correct parser based on language."""
|
||||
|
||||
def test_typescript_code_valid_with_typescript_language(self):
|
||||
"""TypeScript code should pass validation when language='typescript'."""
|
||||
skip_if_js_not_supported()
|
||||
|
||||
ts_code = "const value = 4.9 as unknown as number;"
|
||||
code_string = CodeString(code=ts_code, language="typescript")
|
||||
assert code_string.code == ts_code
|
||||
|
||||
def test_typescript_code_invalid_with_javascript_language(self):
|
||||
"""TypeScript code should FAIL validation when language='javascript'.
|
||||
|
||||
This is the exact bug that was in production - TypeScript code being
|
||||
validated with JavaScript parser.
|
||||
"""
|
||||
skip_if_js_not_supported()
|
||||
from pydantic import ValidationError
|
||||
|
||||
ts_code = "const value = 4.9 as unknown as number;"
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CodeString(code=ts_code, language="javascript")
|
||||
assert "Invalid Javascript code" in str(exc_info.value)
|
||||
|
||||
def test_typescript_interface_valid_with_typescript_language(self):
|
||||
"""TypeScript interface should pass validation when language='typescript'."""
|
||||
skip_if_js_not_supported()
|
||||
|
||||
ts_code = "interface User { name: string; age: number; }"
|
||||
code_string = CodeString(code=ts_code, language="typescript")
|
||||
assert code_string.code == ts_code
|
||||
|
||||
def test_typescript_interface_invalid_with_javascript_language(self):
|
||||
"""TypeScript interface should FAIL validation when language='javascript'."""
|
||||
skip_if_js_not_supported()
|
||||
from pydantic import ValidationError
|
||||
|
||||
ts_code = "interface User { name: string; age: number; }"
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CodeString(code=ts_code, language="javascript")
|
||||
assert "Invalid Javascript code" in str(exc_info.value)
|
||||
|
||||
|
||||
class TestBackendAPIResponseValidation:
|
||||
"""Tests verifying backend API responses are validated with correct parser."""
|
||||
|
||||
def test_testgen_request_includes_correct_language(self, tmp_path):
|
||||
"""Verify test generation request includes the correct language parameter."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.api.aiservice import AiServiceClient
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.languages import current as lang_current
|
||||
|
||||
lang_current._current_language = Language.TYPESCRIPT
|
||||
|
||||
ts_file = tmp_path / "utils.ts"
|
||||
ts_file.write_text("""
|
||||
export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
""")
|
||||
|
||||
functions = find_all_functions_in_file(ts_file)
|
||||
func = functions[ts_file][0]
|
||||
|
||||
# Verify function has correct language
|
||||
assert func.language == "typescript"
|
||||
|
||||
# Mock the AI service request
|
||||
ai_client = AiServiceClient()
|
||||
with patch.object(ai_client, 'make_ai_service_request') as mock_request:
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"generated_tests": "// test code",
|
||||
"instrumented_behavior_tests": "// behavior code",
|
||||
"instrumented_perf_tests": "// perf code",
|
||||
}
|
||||
mock_request.return_value = mock_response
|
||||
|
||||
# Call generate_regression_tests with correct parameters
|
||||
ai_client.generate_regression_tests(
|
||||
source_code_being_tested="export function add(a: number, b: number): number { return a + b; }",
|
||||
function_to_optimize=func,
|
||||
helper_function_names=[],
|
||||
module_path=ts_file,
|
||||
test_module_path=tmp_path / "tests" / "utils.test.ts",
|
||||
test_framework="vitest",
|
||||
test_timeout=30,
|
||||
trace_id="test-trace-id",
|
||||
test_index=0,
|
||||
language=func.language, # This is the key - language should be "typescript"
|
||||
)
|
||||
|
||||
# Verify the request was made with correct language
|
||||
assert mock_request.called, "API request should have been made"
|
||||
call_args = mock_request.call_args
|
||||
payload = call_args[1].get('payload', call_args[0][1] if len(call_args[0]) > 1 else {})
|
||||
assert payload.get('language') == 'typescript', \
|
||||
f"Expected language='typescript', got language='{payload.get('language')}'"
|
||||
|
||||
|
||||
class TestFunctionOptimizerForJavaScript:
|
||||
"""Tests for FunctionOptimizer with JavaScript/TypeScript functions.
|
||||
|
||||
This is the JavaScript equivalent of test_instrument_tests.py tests.
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def js_project(self, tmp_path):
|
||||
"""Create a minimal JavaScript project for testing."""
|
||||
project = tmp_path / "js_project"
|
||||
project.mkdir()
|
||||
|
||||
# Create source file
|
||||
src_file = project / "utils.js"
|
||||
src_file.write_text("""
|
||||
function fibonacci(n) {
|
||||
if (n <= 1) return n;
|
||||
return fibonacci(n - 1) + fibonacci(n - 2);
|
||||
}
|
||||
|
||||
module.exports = { fibonacci };
|
||||
""")
|
||||
|
||||
# Create test file
|
||||
tests_dir = project / "tests"
|
||||
tests_dir.mkdir()
|
||||
test_file = tests_dir / "utils.test.js"
|
||||
test_file.write_text("""
|
||||
const { fibonacci } = require('../utils');
|
||||
|
||||
describe('fibonacci', () => {
|
||||
test('returns 0 for n=0', () => {
|
||||
expect(fibonacci(0)).toBe(0);
|
||||
});
|
||||
|
||||
test('returns 1 for n=1', () => {
|
||||
expect(fibonacci(1)).toBe(1);
|
||||
});
|
||||
|
||||
test('returns 5 for n=5', () => {
|
||||
expect(fibonacci(5)).toBe(5);
|
||||
});
|
||||
});
|
||||
""")
|
||||
|
||||
# Create package.json
|
||||
package_json = project / "package.json"
|
||||
package_json.write_text("""
|
||||
{
|
||||
"name": "test-project",
|
||||
"devDependencies": {
|
||||
"jest": "^29.0.0"
|
||||
}
|
||||
}
|
||||
""")
|
||||
|
||||
return project
|
||||
|
||||
@pytest.fixture
|
||||
def ts_project(self, tmp_path):
|
||||
"""Create a minimal TypeScript project for testing."""
|
||||
project = tmp_path / "ts_project"
|
||||
project.mkdir()
|
||||
|
||||
# Create source file
|
||||
src_file = project / "utils.ts"
|
||||
src_file.write_text("""
|
||||
export function fibonacci(n: number): number {
|
||||
if (n <= 1) return n;
|
||||
return fibonacci(n - 1) + fibonacci(n - 2);
|
||||
}
|
||||
""")
|
||||
|
||||
# Create test file
|
||||
tests_dir = project / "tests"
|
||||
tests_dir.mkdir()
|
||||
test_file = tests_dir / "utils.test.ts"
|
||||
test_file.write_text("""
|
||||
import { fibonacci } from '../utils';
|
||||
|
||||
describe('fibonacci', () => {
|
||||
test('returns 0 for n=0', () => {
|
||||
expect(fibonacci(0)).toBe(0);
|
||||
});
|
||||
|
||||
test('returns 1 for n=1', () => {
|
||||
expect(fibonacci(1)).toBe(1);
|
||||
});
|
||||
});
|
||||
""")
|
||||
|
||||
# Create package.json
|
||||
package_json = project / "package.json"
|
||||
package_json.write_text("""
|
||||
{
|
||||
"name": "test-project",
|
||||
"devDependencies": {
|
||||
"vitest": "^1.0.0"
|
||||
}
|
||||
}
|
||||
""")
|
||||
|
||||
return project
|
||||
|
||||
def test_function_optimizer_instantiation_javascript(self, js_project):
|
||||
"""Test FunctionOptimizer can be instantiated for JavaScript."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.optimization.function_optimizer import FunctionOptimizer
|
||||
|
||||
src_file = js_project / "utils.js"
|
||||
functions = find_all_functions_in_file(src_file)
|
||||
func = functions[src_file][0]
|
||||
|
||||
func_to_optimize = FunctionToOptimize(
|
||||
function_name=func.function_name,
|
||||
file_path=func.file_path,
|
||||
parents=[FunctionParent(name=p.name, type=p.type) for p in func.parents],
|
||||
starting_line=func.starting_line,
|
||||
ending_line=func.ending_line,
|
||||
language=func.language,
|
||||
)
|
||||
|
||||
test_config = TestConfig(
|
||||
tests_root=js_project / "tests",
|
||||
tests_project_rootdir=js_project,
|
||||
project_root_path=js_project,
|
||||
pytest_cmd="jest",
|
||||
)
|
||||
|
||||
optimizer = FunctionOptimizer(
|
||||
function_to_optimize=func_to_optimize,
|
||||
test_cfg=test_config,
|
||||
aiservice_client=MagicMock(),
|
||||
)
|
||||
|
||||
assert optimizer is not None
|
||||
assert optimizer.function_to_optimize.language == "javascript"
|
||||
|
||||
def test_function_optimizer_instantiation_typescript(self, ts_project):
|
||||
"""Test FunctionOptimizer can be instantiated for TypeScript."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.optimization.function_optimizer import FunctionOptimizer
|
||||
|
||||
src_file = ts_project / "utils.ts"
|
||||
functions = find_all_functions_in_file(src_file)
|
||||
func = functions[src_file][0]
|
||||
|
||||
func_to_optimize = FunctionToOptimize(
|
||||
function_name=func.function_name,
|
||||
file_path=func.file_path,
|
||||
parents=[FunctionParent(name=p.name, type=p.type) for p in func.parents],
|
||||
starting_line=func.starting_line,
|
||||
ending_line=func.ending_line,
|
||||
language=func.language,
|
||||
)
|
||||
|
||||
test_config = TestConfig(
|
||||
tests_root=ts_project / "tests",
|
||||
tests_project_rootdir=ts_project,
|
||||
project_root_path=ts_project,
|
||||
pytest_cmd="vitest",
|
||||
)
|
||||
|
||||
optimizer = FunctionOptimizer(
|
||||
function_to_optimize=func_to_optimize,
|
||||
test_cfg=test_config,
|
||||
aiservice_client=MagicMock(),
|
||||
)
|
||||
|
||||
assert optimizer is not None
|
||||
assert optimizer.function_to_optimize.language == "typescript"
|
||||
|
||||
def test_get_code_optimization_context_javascript(self, js_project):
|
||||
"""Test get_code_optimization_context for JavaScript."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.languages import current as lang_current
|
||||
from codeflash.optimization.function_optimizer import FunctionOptimizer
|
||||
|
||||
lang_current._current_language = Language.JAVASCRIPT
|
||||
|
||||
src_file = js_project / "utils.js"
|
||||
functions = find_all_functions_in_file(src_file)
|
||||
func = functions[src_file][0]
|
||||
|
||||
func_to_optimize = FunctionToOptimize(
|
||||
function_name=func.function_name,
|
||||
file_path=func.file_path,
|
||||
parents=[FunctionParent(name=p.name, type=p.type) for p in func.parents],
|
||||
starting_line=func.starting_line,
|
||||
ending_line=func.ending_line,
|
||||
language=func.language,
|
||||
)
|
||||
|
||||
test_config = TestConfig(
|
||||
tests_root=js_project / "tests",
|
||||
tests_project_rootdir=js_project,
|
||||
project_root_path=js_project,
|
||||
pytest_cmd="jest",
|
||||
)
|
||||
|
||||
optimizer = FunctionOptimizer(
|
||||
function_to_optimize=func_to_optimize,
|
||||
test_cfg=test_config,
|
||||
aiservice_client=MagicMock(),
|
||||
)
|
||||
|
||||
result = optimizer.get_code_optimization_context()
|
||||
context = result.unwrap()
|
||||
|
||||
assert context is not None
|
||||
assert context.read_writable_code is not None
|
||||
assert context.read_writable_code.language == "javascript"
|
||||
|
||||
def test_get_code_optimization_context_typescript(self, ts_project):
|
||||
"""Test get_code_optimization_context for TypeScript."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.languages import current as lang_current
|
||||
from codeflash.optimization.function_optimizer import FunctionOptimizer
|
||||
|
||||
lang_current._current_language = Language.TYPESCRIPT
|
||||
|
||||
src_file = ts_project / "utils.ts"
|
||||
functions = find_all_functions_in_file(src_file)
|
||||
func = functions[src_file][0]
|
||||
|
||||
func_to_optimize = FunctionToOptimize(
|
||||
function_name=func.function_name,
|
||||
file_path=func.file_path,
|
||||
parents=[FunctionParent(name=p.name, type=p.type) for p in func.parents],
|
||||
starting_line=func.starting_line,
|
||||
ending_line=func.ending_line,
|
||||
language=func.language,
|
||||
)
|
||||
|
||||
test_config = TestConfig(
|
||||
tests_root=ts_project / "tests",
|
||||
tests_project_rootdir=ts_project,
|
||||
project_root_path=ts_project,
|
||||
pytest_cmd="vitest",
|
||||
)
|
||||
|
||||
optimizer = FunctionOptimizer(
|
||||
function_to_optimize=func_to_optimize,
|
||||
test_cfg=test_config,
|
||||
aiservice_client=MagicMock(),
|
||||
)
|
||||
|
||||
result = optimizer.get_code_optimization_context()
|
||||
context = result.unwrap()
|
||||
|
||||
assert context is not None
|
||||
assert context.read_writable_code is not None
|
||||
assert context.read_writable_code.language == "typescript"
|
||||
|
||||
|
||||
class TestHelperFunctionLanguageAttribute:
|
||||
"""Tests for helper function language attribute (import_resolver.py fix)."""
|
||||
|
||||
def test_helper_functions_have_correct_language_javascript(self, tmp_path):
|
||||
"""Verify helper functions have language='javascript' for .js files."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.languages import current as lang_current, get_language_support
|
||||
from codeflash.optimization.function_optimizer import FunctionOptimizer
|
||||
|
||||
lang_current._current_language = Language.JAVASCRIPT
|
||||
|
||||
# Create a file with helper functions
|
||||
src_file = tmp_path / "main.js"
|
||||
src_file.write_text("""
|
||||
function helper() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
function main() {
|
||||
return helper() * 2;
|
||||
}
|
||||
|
||||
module.exports = { main };
|
||||
""")
|
||||
|
||||
functions = find_all_functions_in_file(src_file)
|
||||
main_func = next(f for f in functions[src_file] if f.function_name == "main")
|
||||
|
||||
func_to_optimize = FunctionToOptimize(
|
||||
function_name=main_func.function_name,
|
||||
file_path=main_func.file_path,
|
||||
parents=[],
|
||||
starting_line=main_func.starting_line,
|
||||
ending_line=main_func.ending_line,
|
||||
language=main_func.language,
|
||||
)
|
||||
|
||||
test_config = TestConfig(
|
||||
tests_root=tmp_path,
|
||||
tests_project_rootdir=tmp_path,
|
||||
project_root_path=tmp_path,
|
||||
pytest_cmd="jest",
|
||||
)
|
||||
|
||||
optimizer = FunctionOptimizer(
|
||||
function_to_optimize=func_to_optimize,
|
||||
test_cfg=test_config,
|
||||
aiservice_client=MagicMock(),
|
||||
)
|
||||
|
||||
result = optimizer.get_code_optimization_context()
|
||||
context = result.unwrap()
|
||||
|
||||
# Verify main function has correct language
|
||||
assert context.read_writable_code.language == "javascript"
|
||||
|
||||
def test_helper_functions_have_correct_language_typescript(self, tmp_path):
|
||||
"""Verify helper functions have language='typescript' for .ts files."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.languages import current as lang_current
|
||||
from codeflash.optimization.function_optimizer import FunctionOptimizer
|
||||
|
||||
lang_current._current_language = Language.TYPESCRIPT
|
||||
|
||||
# Create a file with helper functions
|
||||
src_file = tmp_path / "main.ts"
|
||||
src_file.write_text("""
|
||||
function helper(): number {
|
||||
return 42;
|
||||
}
|
||||
|
||||
export function main(): number {
|
||||
return helper() * 2;
|
||||
}
|
||||
""")
|
||||
|
||||
functions = find_all_functions_in_file(src_file)
|
||||
main_func = next(f for f in functions[src_file] if f.function_name == "main")
|
||||
|
||||
func_to_optimize = FunctionToOptimize(
|
||||
function_name=main_func.function_name,
|
||||
file_path=main_func.file_path,
|
||||
parents=[],
|
||||
starting_line=main_func.starting_line,
|
||||
ending_line=main_func.ending_line,
|
||||
language=main_func.language,
|
||||
)
|
||||
|
||||
test_config = TestConfig(
|
||||
tests_root=tmp_path,
|
||||
tests_project_rootdir=tmp_path,
|
||||
project_root_path=tmp_path,
|
||||
pytest_cmd="vitest",
|
||||
)
|
||||
|
||||
optimizer = FunctionOptimizer(
|
||||
function_to_optimize=func_to_optimize,
|
||||
test_cfg=test_config,
|
||||
aiservice_client=MagicMock(),
|
||||
)
|
||||
|
||||
result = optimizer.get_code_optimization_context()
|
||||
context = result.unwrap()
|
||||
|
||||
# Verify main function has correct language
|
||||
assert context.read_writable_code.language == "typescript"
|
||||
516
tests/test_languages/test_javascript_run_and_parse.py
Normal file
516
tests/test_languages/test_javascript_run_and_parse.py
Normal file
|
|
@ -0,0 +1,516 @@
|
|||
"""End-to-end tests for JavaScript/TypeScript test execution and result parsing.
|
||||
|
||||
These tests verify the FULL optimization pipeline including:
|
||||
- Test instrumentation
|
||||
- Running instrumented tests with Vitest/Jest
|
||||
- Parsing test results (stdout, timing, return values)
|
||||
- Benchmarking with multiple loops
|
||||
|
||||
This is the JavaScript equivalent of test_instrument_tests.py for Python.
|
||||
|
||||
NOTE: These tests require:
|
||||
- Node.js installed
|
||||
- npm packages installed in the test fixture directories
|
||||
- The codeflash npm package
|
||||
|
||||
Tests will be skipped if dependencies are not available.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
|
||||
from codeflash.languages.base import Language
|
||||
from codeflash.models.models import FunctionParent, TestFile, TestFiles, TestType, TestingMode
|
||||
from codeflash.verification.verification_utils import TestConfig
|
||||
|
||||
|
||||
def is_node_available():
|
||||
"""Check if Node.js is available."""
|
||||
try:
|
||||
result = subprocess.run(["node", "--version"], capture_output=True, text=True)
|
||||
return result.returncode == 0
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
|
||||
|
||||
def is_npm_available():
|
||||
"""Check if npm is available."""
|
||||
try:
|
||||
result = subprocess.run(["npm", "--version"], capture_output=True, text=True)
|
||||
return result.returncode == 0
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
|
||||
|
||||
def has_node_modules(project_dir: Path) -> bool:
|
||||
"""Check if node_modules exists in project directory."""
|
||||
return (project_dir / "node_modules").exists()
|
||||
|
||||
|
||||
def install_dependencies(project_dir: Path) -> bool:
|
||||
"""Install npm dependencies in project directory."""
|
||||
if has_node_modules(project_dir):
|
||||
return True
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["npm", "install"],
|
||||
cwd=project_dir,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120
|
||||
)
|
||||
return result.returncode == 0
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def skip_if_js_runtime_not_available():
|
||||
"""Skip test if JavaScript runtime is not available."""
|
||||
if not is_node_available():
|
||||
pytest.skip("Node.js not available")
|
||||
if not is_npm_available():
|
||||
pytest.skip("npm not available")
|
||||
|
||||
|
||||
def skip_if_js_not_supported():
|
||||
"""Skip test if JavaScript/TypeScript languages are not supported."""
|
||||
try:
|
||||
from codeflash.languages import get_language_support
|
||||
get_language_support(Language.JAVASCRIPT)
|
||||
except Exception as e:
|
||||
pytest.skip(f"JavaScript/TypeScript language support not available: {e}")
|
||||
|
||||
|
||||
class TestJavaScriptInstrumentation:
|
||||
"""Tests for JavaScript test instrumentation."""
|
||||
|
||||
@pytest.fixture
|
||||
def js_project_dir(self, tmp_path):
|
||||
"""Create a temporary JavaScript project with Jest."""
|
||||
project_dir = tmp_path / "js_project"
|
||||
project_dir.mkdir()
|
||||
|
||||
# Create source file
|
||||
src_file = project_dir / "math.js"
|
||||
src_file.write_text("""
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function multiply(a, b) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
module.exports = { add, multiply };
|
||||
""")
|
||||
|
||||
# Create test file
|
||||
tests_dir = project_dir / "__tests__"
|
||||
tests_dir.mkdir()
|
||||
test_file = tests_dir / "math.test.js"
|
||||
test_file.write_text("""
|
||||
const { add, multiply } = require('../math');
|
||||
|
||||
describe('math functions', () => {
|
||||
test('add returns sum', () => {
|
||||
expect(add(2, 3)).toBe(5);
|
||||
});
|
||||
|
||||
test('multiply returns product', () => {
|
||||
expect(multiply(2, 3)).toBe(6);
|
||||
});
|
||||
});
|
||||
""")
|
||||
|
||||
# Create package.json
|
||||
package_json = project_dir / "package.json"
|
||||
package_json.write_text("""{
|
||||
"name": "test-project",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"test": "jest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^29.0.0",
|
||||
"jest-junit": "^16.0.0"
|
||||
}
|
||||
}""")
|
||||
|
||||
# Create jest.config.js
|
||||
jest_config = project_dir / "jest.config.js"
|
||||
jest_config.write_text("""
|
||||
module.exports = {
|
||||
testEnvironment: 'node',
|
||||
reporters: ['default', 'jest-junit'],
|
||||
};
|
||||
""")
|
||||
|
||||
return project_dir
|
||||
|
||||
def test_instrument_javascript_test_file(self, js_project_dir):
|
||||
"""Test that JavaScript test instrumentation module can be imported."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.languages import get_language_support
|
||||
# Verify the instrumentation module can be imported
|
||||
from codeflash.languages.javascript.instrument import inject_profiling_into_existing_js_test
|
||||
|
||||
# Get JavaScript support
|
||||
js_support = get_language_support(Language.JAVASCRIPT)
|
||||
|
||||
# Create function info
|
||||
func_info = FunctionToOptimize(
|
||||
function_name="add",
|
||||
file_path=js_project_dir / "math.js",
|
||||
parents=[],
|
||||
starting_line=2,
|
||||
ending_line=4,
|
||||
language="javascript",
|
||||
)
|
||||
|
||||
# Verify function has correct language
|
||||
assert func_info.language == "javascript"
|
||||
|
||||
# Verify test file exists
|
||||
test_file = js_project_dir / "__tests__" / "math.test.js"
|
||||
assert test_file.exists()
|
||||
|
||||
# Note: Full instrumentation test requires call_positions discovery
|
||||
# which is done by the FunctionOptimizer. Here we just verify the
|
||||
# infrastructure is in place.
|
||||
|
||||
|
||||
class TestTypeScriptInstrumentation:
|
||||
"""Tests for TypeScript test instrumentation."""
|
||||
|
||||
@pytest.fixture
|
||||
def ts_project_dir(self, tmp_path):
|
||||
"""Create a temporary TypeScript project with Vitest."""
|
||||
project_dir = tmp_path / "ts_project"
|
||||
project_dir.mkdir()
|
||||
|
||||
# Create source file
|
||||
src_file = project_dir / "math.ts"
|
||||
src_file.write_text("""
|
||||
export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
export function multiply(a: number, b: number): number {
|
||||
return a * b;
|
||||
}
|
||||
""")
|
||||
|
||||
# Create test file
|
||||
tests_dir = project_dir / "tests"
|
||||
tests_dir.mkdir()
|
||||
test_file = tests_dir / "math.test.ts"
|
||||
test_file.write_text("""
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { add, multiply } from '../math';
|
||||
|
||||
describe('math functions', () => {
|
||||
test('add returns sum', () => {
|
||||
expect(add(2, 3)).toBe(5);
|
||||
});
|
||||
|
||||
test('multiply returns product', () => {
|
||||
expect(multiply(2, 3)).toBe(6);
|
||||
});
|
||||
});
|
||||
""")
|
||||
|
||||
# Create package.json
|
||||
package_json = project_dir / "package.json"
|
||||
package_json.write_text("""{
|
||||
"name": "test-project",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "vitest run"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest": "^1.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}""")
|
||||
|
||||
# Create vitest.config.ts
|
||||
vitest_config = project_dir / "vitest.config.ts"
|
||||
vitest_config.write_text("""
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: false,
|
||||
reporters: ['verbose', 'junit'],
|
||||
outputFile: './junit.xml',
|
||||
},
|
||||
});
|
||||
""")
|
||||
|
||||
# Create tsconfig.json
|
||||
tsconfig = project_dir / "tsconfig.json"
|
||||
tsconfig.write_text("""{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}""")
|
||||
|
||||
return project_dir
|
||||
|
||||
def test_instrument_typescript_test_file(self, ts_project_dir):
|
||||
"""Test that TypeScript test instrumentation module can be imported."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.languages import get_language_support
|
||||
# Verify the instrumentation module can be imported
|
||||
from codeflash.languages.javascript.instrument import inject_profiling_into_existing_js_test
|
||||
|
||||
test_file = ts_project_dir / "tests" / "math.test.ts"
|
||||
|
||||
# Get TypeScript support
|
||||
ts_support = get_language_support(Language.TYPESCRIPT)
|
||||
|
||||
# Create function info
|
||||
func_info = FunctionToOptimize(
|
||||
function_name="add",
|
||||
file_path=ts_project_dir / "math.ts",
|
||||
parents=[],
|
||||
starting_line=2,
|
||||
ending_line=4,
|
||||
language="typescript",
|
||||
)
|
||||
|
||||
# Verify function has correct language
|
||||
assert func_info.language == "typescript"
|
||||
|
||||
# Verify test file exists
|
||||
assert test_file.exists()
|
||||
|
||||
# Note: Full instrumentation test requires call_positions discovery
|
||||
# which is done by the FunctionOptimizer. Here we just verify the
|
||||
# infrastructure is in place.
|
||||
|
||||
|
||||
class TestRunAndParseJavaScriptTests:
|
||||
"""Tests for running and parsing JavaScript test results.
|
||||
|
||||
These tests require actual npm dependencies to be installed.
|
||||
They will be skipped if dependencies are not available.
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def vitest_project(self):
|
||||
"""Get the Vitest sample project with dependencies installed."""
|
||||
project_root = Path(__file__).parent.parent.parent
|
||||
vitest_dir = project_root / "code_to_optimize" / "js" / "code_to_optimize_vitest"
|
||||
|
||||
if not vitest_dir.exists():
|
||||
pytest.skip("code_to_optimize_vitest directory not found")
|
||||
|
||||
skip_if_js_runtime_not_available()
|
||||
|
||||
# Try to install dependencies if not present
|
||||
if not has_node_modules(vitest_dir):
|
||||
if not install_dependencies(vitest_dir):
|
||||
pytest.skip("Could not install npm dependencies")
|
||||
|
||||
return vitest_dir
|
||||
|
||||
def test_run_behavioral_tests_vitest(self, vitest_project):
|
||||
"""Test running behavioral tests with Vitest."""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.languages import get_language_support
|
||||
|
||||
ts_support = get_language_support(Language.TYPESCRIPT)
|
||||
|
||||
# Find the fibonacci function
|
||||
fib_file = vitest_project / "fibonacci.ts"
|
||||
functions = find_all_functions_in_file(fib_file)
|
||||
fib_func = next(f for f in functions[fib_file] if f.function_name == "fibonacci")
|
||||
|
||||
# Verify language is correct
|
||||
assert fib_func.language == "typescript"
|
||||
|
||||
# Discover tests
|
||||
test_root = vitest_project / "tests"
|
||||
tests = ts_support.discover_tests(test_root, [fib_func])
|
||||
|
||||
# There should be tests for fibonacci
|
||||
assert len(tests) > 0 or fib_func.qualified_name in tests
|
||||
|
||||
def test_function_optimizer_run_and_parse_typescript(self, vitest_project):
|
||||
"""Test FunctionOptimizer.run_and_parse_tests for TypeScript.
|
||||
|
||||
This is the JavaScript equivalent of the Python test in test_instrument_tests.py.
|
||||
"""
|
||||
skip_if_js_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.languages import current as lang_current
|
||||
from codeflash.optimization.function_optimizer import FunctionOptimizer
|
||||
|
||||
lang_current._current_language = Language.TYPESCRIPT
|
||||
|
||||
# Find the fibonacci function
|
||||
fib_file = vitest_project / "fibonacci.ts"
|
||||
functions = find_all_functions_in_file(fib_file)
|
||||
fib_func_info = next(f for f in functions[fib_file] if f.function_name == "fibonacci")
|
||||
|
||||
# Create FunctionToOptimize
|
||||
func = FunctionToOptimize(
|
||||
function_name=fib_func_info.function_name,
|
||||
file_path=fib_func_info.file_path,
|
||||
parents=[FunctionParent(name=p.name, type=p.type) for p in fib_func_info.parents],
|
||||
starting_line=fib_func_info.starting_line,
|
||||
ending_line=fib_func_info.ending_line,
|
||||
language=fib_func_info.language,
|
||||
)
|
||||
|
||||
# Verify language
|
||||
assert func.language == "typescript"
|
||||
|
||||
# Create test config
|
||||
test_config = TestConfig(
|
||||
tests_root=vitest_project / "tests",
|
||||
tests_project_rootdir=vitest_project,
|
||||
project_root_path=vitest_project,
|
||||
pytest_cmd="vitest",
|
||||
test_framework="vitest",
|
||||
)
|
||||
|
||||
# Create optimizer
|
||||
func_optimizer = FunctionOptimizer(
|
||||
function_to_optimize=func,
|
||||
test_cfg=test_config,
|
||||
aiservice_client=MagicMock(),
|
||||
)
|
||||
|
||||
# Get code context - this should work
|
||||
result = func_optimizer.get_code_optimization_context()
|
||||
context = result.unwrap()
|
||||
|
||||
assert context is not None
|
||||
assert context.read_writable_code.language == "typescript"
|
||||
|
||||
|
||||
class TestTimingMarkerParsing:
|
||||
"""Tests for parsing JavaScript timing markers from test output.
|
||||
|
||||
Note: Timing marker parsing is handled in codeflash/verification/parse_test_output.py,
|
||||
which uses a unified parser for all languages. These tests verify the marker format
|
||||
is correctly recognized.
|
||||
"""
|
||||
|
||||
def test_timing_marker_format(self):
|
||||
"""Test that JavaScript timing markers follow the expected format."""
|
||||
skip_if_js_not_supported()
|
||||
import re
|
||||
|
||||
# The marker format used by codeflash for JavaScript
|
||||
# Start marker: !$######{tag}######$!
|
||||
# End marker: !######{tag}:{duration}######!
|
||||
start_pattern = r'!\$######(.+?)######\$!'
|
||||
end_pattern = r'!######(.+?):(\d+)######!'
|
||||
|
||||
start_marker = "!$######test/math.test.ts:TestMath.test_add:add:1:0_0######$!"
|
||||
end_marker = "!######test/math.test.ts:TestMath.test_add:add:1:0_0:12345######!"
|
||||
|
||||
start_match = re.match(start_pattern, start_marker)
|
||||
end_match = re.match(end_pattern, end_marker)
|
||||
|
||||
assert start_match is not None
|
||||
assert end_match is not None
|
||||
assert start_match.group(1) == "test/math.test.ts:TestMath.test_add:add:1:0_0"
|
||||
assert end_match.group(1) == "test/math.test.ts:TestMath.test_add:add:1:0_0"
|
||||
assert end_match.group(2) == "12345"
|
||||
|
||||
def test_timing_marker_components(self):
|
||||
"""Test parsing components from timing marker tag."""
|
||||
skip_if_js_not_supported()
|
||||
|
||||
# Tag format: {module}:{class}.{test}:{function}:{loop_index}:{invocation_id}
|
||||
tag = "test/math.test.ts:TestMath.test_add:add:1:0_0"
|
||||
parts = tag.split(":")
|
||||
|
||||
assert len(parts) == 5
|
||||
assert parts[0] == "test/math.test.ts" # module/file
|
||||
assert parts[1] == "TestMath.test_add" # class.test
|
||||
assert parts[2] == "add" # function being tested
|
||||
assert parts[3] == "1" # loop index
|
||||
assert parts[4] == "0_0" # invocation id
|
||||
|
||||
|
||||
class TestJavaScriptTestResultParsing:
|
||||
"""Tests for parsing JavaScript test results from JUnit XML."""
|
||||
|
||||
def test_parse_vitest_junit_xml(self, tmp_path):
|
||||
"""Test parsing Vitest JUnit XML output."""
|
||||
skip_if_js_not_supported()
|
||||
|
||||
# Create sample JUnit XML
|
||||
junit_xml = tmp_path / "junit.xml"
|
||||
junit_xml.write_text("""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuites name="vitest tests" tests="2" failures="0" errors="0" time="0.5">
|
||||
<testsuite name="tests/math.test.ts" tests="2" failures="0" errors="0" time="0.5">
|
||||
<testcase classname="tests/math.test.ts" name="add returns sum" time="0.1">
|
||||
</testcase>
|
||||
<testcase classname="tests/math.test.ts" name="multiply returns product" time="0.2">
|
||||
</testcase>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
""")
|
||||
|
||||
# Parse the XML
|
||||
import xml.etree.ElementTree as ET
|
||||
tree = ET.parse(junit_xml)
|
||||
root = tree.getroot()
|
||||
|
||||
# Verify structure
|
||||
testsuites = root if root.tag == "testsuites" else root.find("testsuites")
|
||||
assert testsuites is not None
|
||||
|
||||
testsuite = testsuites.find("testsuite") if testsuites is not None else root.find("testsuite")
|
||||
assert testsuite is not None
|
||||
|
||||
testcases = testsuite.findall("testcase")
|
||||
assert len(testcases) == 2
|
||||
|
||||
def test_parse_jest_junit_xml(self, tmp_path):
|
||||
"""Test parsing Jest JUnit XML output."""
|
||||
skip_if_js_not_supported()
|
||||
|
||||
# Create sample JUnit XML from jest-junit
|
||||
junit_xml = tmp_path / "junit.xml"
|
||||
junit_xml.write_text("""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuites name="jest tests" tests="2" failures="0" time="0.789">
|
||||
<testsuite name="math functions" tests="2" failures="0" time="0.456" timestamp="2024-01-01T00:00:00">
|
||||
<testcase classname="__tests__/math.test.js" name="add returns sum" time="0.123">
|
||||
</testcase>
|
||||
<testcase classname="__tests__/math.test.js" name="multiply returns product" time="0.234">
|
||||
</testcase>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
""")
|
||||
|
||||
# Parse the XML
|
||||
import xml.etree.ElementTree as ET
|
||||
tree = ET.parse(junit_xml)
|
||||
root = tree.getroot()
|
||||
|
||||
# Verify structure
|
||||
testsuites = root if root.tag == "testsuites" else root.find("testsuites")
|
||||
testsuite = testsuites.find("testsuite") if testsuites is not None else root.find("testsuite")
|
||||
assert testsuite is not None
|
||||
|
||||
testcases = testsuite.findall("testcase")
|
||||
assert len(testcases) == 2
|
||||
|
|
@ -654,7 +654,7 @@ describe('Math functions', () => {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -1607,3 +1607,97 @@ module.exports = { MathUtils };
|
|||
f"Replacement result does not match expected.\nExpected:\n{expected_result}\n\nGot:\n{result}"
|
||||
)
|
||||
assert js_support.validate_syntax(result) is True
|
||||
|
||||
|
||||
class TestTypeScriptSyntaxValidation:
|
||||
"""Tests for TypeScript-specific syntax validation.
|
||||
|
||||
These tests ensure that TypeScript code is validated with the TypeScript parser,
|
||||
not the JavaScript parser. This is important because TypeScript has syntax that
|
||||
is invalid in JavaScript (e.g., type assertions, type annotations).
|
||||
"""
|
||||
|
||||
def test_typescript_type_assertion_valid_in_ts(self):
|
||||
"""TypeScript type assertions should be valid in TypeScript."""
|
||||
from codeflash.languages.javascript.support import TypeScriptSupport
|
||||
|
||||
ts_support = TypeScriptSupport()
|
||||
|
||||
# Type assertions are TypeScript-specific
|
||||
ts_code = """
|
||||
const value = 4.9 as unknown as number;
|
||||
const str = "hello" as string;
|
||||
"""
|
||||
assert ts_support.validate_syntax(ts_code) is True
|
||||
|
||||
def test_typescript_type_assertion_invalid_in_js(self, js_support):
|
||||
"""TypeScript type assertions should be invalid in JavaScript."""
|
||||
# This is the code pattern that caused the backend error
|
||||
ts_code = """
|
||||
const value = 4.9 as unknown as number;
|
||||
"""
|
||||
# JavaScript parser should reject TypeScript syntax
|
||||
assert js_support.validate_syntax(ts_code) is False
|
||||
|
||||
def test_typescript_interface_valid_in_ts(self):
|
||||
"""TypeScript interfaces should be valid in TypeScript."""
|
||||
from codeflash.languages.javascript.support import TypeScriptSupport
|
||||
|
||||
ts_support = TypeScriptSupport()
|
||||
|
||||
ts_code = """
|
||||
interface User {
|
||||
name: string;
|
||||
age: number;
|
||||
}
|
||||
"""
|
||||
assert ts_support.validate_syntax(ts_code) is True
|
||||
|
||||
def test_typescript_interface_invalid_in_js(self, js_support):
|
||||
"""TypeScript interfaces should be invalid in JavaScript."""
|
||||
ts_code = """
|
||||
interface User {
|
||||
name: string;
|
||||
age: number;
|
||||
}
|
||||
"""
|
||||
# JavaScript parser should reject TypeScript interface syntax
|
||||
assert js_support.validate_syntax(ts_code) is False
|
||||
|
||||
def test_typescript_generic_function_valid_in_ts(self):
|
||||
"""TypeScript generics should be valid in TypeScript."""
|
||||
from codeflash.languages.javascript.support import TypeScriptSupport
|
||||
|
||||
ts_support = TypeScriptSupport()
|
||||
|
||||
ts_code = """
|
||||
function identity<T>(arg: T): T {
|
||||
return arg;
|
||||
}
|
||||
"""
|
||||
assert ts_support.validate_syntax(ts_code) is True
|
||||
|
||||
def test_typescript_generic_function_invalid_in_js(self, js_support):
|
||||
"""TypeScript generics should be invalid in JavaScript."""
|
||||
ts_code = """
|
||||
function identity<T>(arg: T): T {
|
||||
return arg;
|
||||
}
|
||||
"""
|
||||
assert js_support.validate_syntax(ts_code) is False
|
||||
|
||||
def test_language_property_is_typescript(self):
|
||||
"""TypeScriptSupport should report typescript as language."""
|
||||
from codeflash.languages.base import Language
|
||||
from codeflash.languages.javascript.support import TypeScriptSupport
|
||||
|
||||
ts_support = TypeScriptSupport()
|
||||
assert ts_support.language == Language.TYPESCRIPT
|
||||
assert str(ts_support.language) == "typescript"
|
||||
|
||||
def test_language_property_is_javascript(self, js_support):
|
||||
"""JavaScriptSupport should report javascript as language."""
|
||||
from codeflash.languages.base import Language
|
||||
|
||||
assert js_support.language == Language.JAVASCRIPT
|
||||
assert str(js_support.language) == "javascript"
|
||||
|
|
|
|||
|
|
@ -627,7 +627,7 @@ it('third test', () => {});
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -652,7 +652,7 @@ describe('Suite B', () => {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -676,7 +676,7 @@ describe('Outer', () => {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -700,7 +700,7 @@ describe.skip('skipped describe', () => {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -721,7 +721,7 @@ describe.only('only describe', () => {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -739,7 +739,7 @@ describe('describe single', () => {});
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -758,7 +758,7 @@ describe("describe double", () => {});
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -774,7 +774,7 @@ describe("describe double", () => {});
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -1020,7 +1020,7 @@ describe('日本語テスト', () => {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text(encoding="utf-8")
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -1048,7 +1048,7 @@ test.each([
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -1074,7 +1074,7 @@ describe.each([
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -1099,7 +1099,7 @@ describe('Math operations', () => {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -1457,7 +1457,7 @@ testCases.forEach(name => {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -1485,7 +1485,7 @@ describe('conditional tests', () => {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -1509,7 +1509,7 @@ test('slow test', () => {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -1532,7 +1532,7 @@ test.todo('also needs implementation');
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -1555,7 +1555,7 @@ test.concurrent('concurrent test 2', async () => {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -1654,7 +1654,7 @@ describe('Array', function() {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
@ -1685,7 +1685,7 @@ describe('User', () => {
|
|||
file_path = Path(f.name)
|
||||
|
||||
source = file_path.read_text()
|
||||
from codeflash.languages.treesitter_utils import get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import get_analyzer_for_file
|
||||
|
||||
analyzer = get_analyzer_for_file(file_path)
|
||||
test_names = js_support._find_jest_tests(source, analyzer)
|
||||
|
|
|
|||
|
|
@ -434,8 +434,8 @@ function process() {
|
|||
# Should be converted to ESM
|
||||
assert "import x from './module';" in result
|
||||
|
||||
def test_mixed_code_not_converted(self, tmp_path):
|
||||
"""Test that mixed CJS/ESM code is NOT converted (already has both)."""
|
||||
def test_mixed_code_converted_to_esm(self, tmp_path):
|
||||
"""Test that mixed CJS/ESM code has require converted to import when targeting ESM."""
|
||||
package_json = tmp_path / "package.json"
|
||||
package_json.write_text('{"devDependencies": {"jest": "^29.0.0"}}')
|
||||
|
||||
|
|
@ -447,10 +447,18 @@ function process() {
|
|||
return existing() + helper();
|
||||
}
|
||||
"""
|
||||
# Mixed code has both import and require, so no conversion
|
||||
expected = """\
|
||||
import { existing } from './module.js';
|
||||
import { helper } from './helpers';
|
||||
|
||||
function process() {
|
||||
return existing() + helper();
|
||||
}
|
||||
"""
|
||||
# Mixed code should have require converted to import for ESM target
|
||||
result = ensure_module_system_compatibility(mixed_code, ModuleSystem.ES_MODULE, tmp_path)
|
||||
|
||||
assert result == mixed_code, "Mixed code should not be converted"
|
||||
assert result == expected, "require should be converted to import for ESM target"
|
||||
|
||||
def test_pure_esm_unchanged_for_esm_target(self, tmp_path):
|
||||
"""Test that pure ESM code is unchanged when targeting ESM."""
|
||||
|
|
|
|||
|
|
@ -271,11 +271,14 @@ class TestClearFunctions:
|
|||
# Now Python should not be supported
|
||||
assert not is_language_supported(Language.PYTHON)
|
||||
|
||||
# Re-register by importing
|
||||
# Re-register all languages by importing
|
||||
from codeflash.languages.python.support import PythonSupport
|
||||
from codeflash.languages.javascript.support import JavaScriptSupport, TypeScriptSupport
|
||||
|
||||
# Need to manually register since decorator already ran
|
||||
register_language(PythonSupport)
|
||||
register_language(JavaScriptSupport)
|
||||
register_language(TypeScriptSupport)
|
||||
|
||||
# Should be supported again
|
||||
assert is_language_supported(Language.PYTHON)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from pathlib import Path
|
|||
|
||||
import pytest
|
||||
|
||||
from codeflash.languages.treesitter_utils import TreeSitterAnalyzer, TreeSitterLanguage, get_analyzer_for_file
|
||||
from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer, TreeSitterLanguage, get_analyzer_for_file
|
||||
|
||||
|
||||
class TestTreeSitterLanguage:
|
||||
|
|
@ -545,3 +545,279 @@ function identity<T>(value: T): T {
|
|||
|
||||
assert len(functions) == 1
|
||||
assert functions[0].name == "identity"
|
||||
|
||||
|
||||
class TestExportConstArrowFunctions:
|
||||
"""Tests for export const arrow function pattern - Issue #10.
|
||||
|
||||
Modern TypeScript codebases commonly use:
|
||||
- export const slugify = (str: string) => { return s; }
|
||||
- export const uniqueBy = <T>(array: T[]) => { ... }
|
||||
|
||||
These must be correctly recognized as optimizable functions.
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def ts_analyzer(self):
|
||||
"""Create a TypeScript analyzer."""
|
||||
return TreeSitterAnalyzer(TreeSitterLanguage.TYPESCRIPT)
|
||||
|
||||
def test_export_const_arrow_function_basic(self, ts_analyzer):
|
||||
"""Test finding export const arrow function (basic pattern)."""
|
||||
code = """export const slugify = (str: string) => {
|
||||
return str.toLowerCase();
|
||||
};"""
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
|
||||
assert len(functions) == 1
|
||||
assert functions[0].name == "slugify"
|
||||
assert functions[0].is_arrow is True
|
||||
assert ts_analyzer.has_return_statement(functions[0], code) is True
|
||||
|
||||
def test_export_const_arrow_function_optional_param(self, ts_analyzer):
|
||||
"""Test finding export const arrow function with optional parameter."""
|
||||
code = """export const slugify = (str: string, forDisplayingInput?: boolean) => {
|
||||
if (!str) {
|
||||
return "";
|
||||
}
|
||||
const s = str.toLowerCase();
|
||||
return forDisplayingInput ? s : s.replace(/-+$/, "");
|
||||
};"""
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
|
||||
assert len(functions) == 1
|
||||
assert functions[0].name == "slugify"
|
||||
assert functions[0].is_arrow is True
|
||||
assert ts_analyzer.has_return_statement(functions[0], code) is True
|
||||
|
||||
def test_export_const_generic_arrow_function(self, ts_analyzer):
|
||||
"""Test finding export const arrow function with generics."""
|
||||
code = """export const uniqueBy = <T extends { [key: string]: unknown }>(array: T[], keys: (keyof T)[]) => {
|
||||
return array.filter(
|
||||
(item, index, self) => index === self.findIndex((t) => keys.every((key) => t[key] === item[key]))
|
||||
);
|
||||
};"""
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
|
||||
# Should find uniqueBy, and possibly the inner arrow functions
|
||||
uniqueBy = next((f for f in functions if f.name == "uniqueBy"), None)
|
||||
assert uniqueBy is not None
|
||||
assert uniqueBy.is_arrow is True
|
||||
assert ts_analyzer.has_return_statement(uniqueBy, code) is True
|
||||
|
||||
def test_export_const_arrow_function_is_exported(self, ts_analyzer):
|
||||
"""Test that export const arrow functions are recognized as exported."""
|
||||
code = """export const slugify = (str: string) => {
|
||||
return str.toLowerCase();
|
||||
};"""
|
||||
|
||||
# Check is_function_exported
|
||||
is_exported, export_name = ts_analyzer.is_function_exported(code, "slugify")
|
||||
assert is_exported is True
|
||||
assert export_name == "slugify"
|
||||
|
||||
def test_export_const_with_default_export(self, ts_analyzer):
|
||||
"""Test export const with separate default export."""
|
||||
code = """export const slugify = (str: string) => {
|
||||
return str.toLowerCase();
|
||||
};
|
||||
|
||||
export default slugify;"""
|
||||
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
assert len(functions) == 1
|
||||
assert functions[0].name == "slugify"
|
||||
|
||||
# Should be exported both ways
|
||||
is_named, named_name = ts_analyzer.is_function_exported(code, "slugify")
|
||||
assert is_named is True
|
||||
|
||||
def test_multiple_export_const_functions(self, ts_analyzer):
|
||||
"""Test multiple export const arrow functions in same file."""
|
||||
code = """export const notUndefined = <T>(val: T | undefined): val is T => Boolean(val);
|
||||
|
||||
export const uniqueBy = <T extends { [key: string]: unknown }>(array: T[], keys: (keyof T)[]) => {
|
||||
return array.filter(
|
||||
(item, index, self) => index === self.findIndex((t) => keys.every((key) => t[key] === item[key]))
|
||||
);
|
||||
};"""
|
||||
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
|
||||
# Find the top-level exported functions
|
||||
names = {f.name for f in functions if f.parent_function is None}
|
||||
assert "notUndefined" in names
|
||||
assert "uniqueBy" in names
|
||||
|
||||
def test_export_const_arrow_with_implicit_return(self, ts_analyzer):
|
||||
"""Test export const arrow function with implicit return."""
|
||||
code = """export const double = (n: number) => n * 2;"""
|
||||
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
|
||||
assert len(functions) == 1
|
||||
assert functions[0].name == "double"
|
||||
assert functions[0].is_arrow is True
|
||||
assert ts_analyzer.has_return_statement(functions[0], code) is True
|
||||
|
||||
def test_export_const_async_arrow_function(self, ts_analyzer):
|
||||
"""Test export const async arrow function."""
|
||||
code = """export const fetchData = async (url: string) => {
|
||||
const response = await fetch(url);
|
||||
return response.json();
|
||||
};"""
|
||||
|
||||
functions = ts_analyzer.find_functions(code)
|
||||
|
||||
assert len(functions) == 1
|
||||
assert functions[0].name == "fetchData"
|
||||
assert functions[0].is_arrow is True
|
||||
assert functions[0].is_async is True
|
||||
assert ts_analyzer.has_return_statement(functions[0], code) is True
|
||||
|
||||
def test_non_exported_const_not_exported(self, ts_analyzer):
|
||||
"""Test that non-exported const functions are not marked as exported."""
|
||||
code = """const privateFunc = (x: number) => {
|
||||
return x * 2;
|
||||
};
|
||||
|
||||
export const publicFunc = (x: number) => {
|
||||
return privateFunc(x);
|
||||
};"""
|
||||
|
||||
# privateFunc should not be exported
|
||||
is_private_exported, _ = ts_analyzer.is_function_exported(code, "privateFunc")
|
||||
assert is_private_exported is False
|
||||
|
||||
# publicFunc should be exported
|
||||
is_public_exported, name = ts_analyzer.is_function_exported(code, "publicFunc")
|
||||
assert is_public_exported is True
|
||||
assert name == "publicFunc"
|
||||
|
||||
|
||||
class TestWrappedDefaultExports:
|
||||
"""Tests for wrapped default export pattern - Issue #9.
|
||||
|
||||
Handles patterns like:
|
||||
- export default curry(traverseEntity)
|
||||
- export default compose(fn1, fn2)
|
||||
- export default wrapper(myFunc)
|
||||
|
||||
These must be correctly recognized so the wrapped function is exportable.
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def ts_analyzer(self):
|
||||
"""Create a TypeScript analyzer."""
|
||||
return TreeSitterAnalyzer(TreeSitterLanguage.TYPESCRIPT)
|
||||
|
||||
def test_curry_wrapped_export(self, ts_analyzer):
|
||||
"""Test export default curry(fn) pattern."""
|
||||
code = """import { curry } from 'lodash/fp';
|
||||
|
||||
const traverseEntity = async (visitor, options, entity) => {
|
||||
return entity;
|
||||
};
|
||||
|
||||
export default curry(traverseEntity);"""
|
||||
|
||||
# Check exports parsing
|
||||
exports = ts_analyzer.find_exports(code)
|
||||
assert len(exports) == 1
|
||||
assert exports[0].default_export == "default"
|
||||
assert exports[0].wrapped_default_args == ["traverseEntity"]
|
||||
|
||||
# Check is_function_exported
|
||||
is_exported, export_name = ts_analyzer.is_function_exported(code, "traverseEntity")
|
||||
assert is_exported is True
|
||||
assert export_name == "default"
|
||||
|
||||
def test_compose_wrapped_export(self, ts_analyzer):
|
||||
"""Test export default compose(fn1, fn2) pattern with multiple args."""
|
||||
code = """import { compose } from 'lodash/fp';
|
||||
|
||||
function validateInput(data) { return data; }
|
||||
function processData(data) { return data; }
|
||||
|
||||
export default compose(validateInput, processData);"""
|
||||
|
||||
exports = ts_analyzer.find_exports(code)
|
||||
assert len(exports) == 1
|
||||
assert exports[0].wrapped_default_args == ["validateInput", "processData"]
|
||||
|
||||
# Both functions should be recognized as exported
|
||||
is_exported1, _ = ts_analyzer.is_function_exported(code, "validateInput")
|
||||
is_exported2, _ = ts_analyzer.is_function_exported(code, "processData")
|
||||
assert is_exported1 is True
|
||||
assert is_exported2 is True
|
||||
|
||||
def test_nested_wrapper_export(self, ts_analyzer):
|
||||
"""Test nested wrapper: export default compose(curry(fn))."""
|
||||
code = """export default compose(curry(myFunc));"""
|
||||
|
||||
exports = ts_analyzer.find_exports(code)
|
||||
assert len(exports) == 1
|
||||
assert "myFunc" in exports[0].wrapped_default_args
|
||||
|
||||
is_exported, _ = ts_analyzer.is_function_exported(code, "myFunc")
|
||||
assert is_exported is True
|
||||
|
||||
def test_generic_wrapper_export(self, ts_analyzer):
|
||||
"""Test generic wrapper function."""
|
||||
code = """const myFunction = (x: number) => x * 2;
|
||||
|
||||
export default someWrapper(myFunction);"""
|
||||
|
||||
is_exported, export_name = ts_analyzer.is_function_exported(code, "myFunction")
|
||||
assert is_exported is True
|
||||
assert export_name == "default"
|
||||
|
||||
def test_non_wrapped_function_not_exported(self, ts_analyzer):
|
||||
"""Test that functions not in the wrapper call are not exported."""
|
||||
code = """const helper = (x: number) => x + 1;
|
||||
const main = (x: number) => helper(x) * 2;
|
||||
|
||||
export default curry(main);"""
|
||||
|
||||
# main is wrapped, so it's exported
|
||||
is_main_exported, _ = ts_analyzer.is_function_exported(code, "main")
|
||||
assert is_main_exported is True
|
||||
|
||||
# helper is NOT in the wrapper call, so not exported
|
||||
is_helper_exported, _ = ts_analyzer.is_function_exported(code, "helper")
|
||||
assert is_helper_exported is False
|
||||
|
||||
def test_direct_default_export_still_works(self, ts_analyzer):
|
||||
"""Test that direct default exports still work."""
|
||||
code = """function myFunc() { return 1; }
|
||||
export default myFunc;"""
|
||||
|
||||
is_exported, export_name = ts_analyzer.is_function_exported(code, "myFunc")
|
||||
assert is_exported is True
|
||||
assert export_name == "default"
|
||||
|
||||
def test_strapi_traverse_entity_pattern(self, ts_analyzer):
|
||||
"""Test the exact strapi pattern that was failing."""
|
||||
code = """import { curry } from 'lodash/fp';
|
||||
|
||||
const traverseEntity = async (visitor: Visitor, options: TraverseOptions, entity: Data) => {
|
||||
const { path = { raw: null }, schema, getModel } = options;
|
||||
// ... implementation
|
||||
return copy;
|
||||
};
|
||||
|
||||
const createVisitorUtils = ({ data }: { data: Data }) => ({
|
||||
remove(key: string) { delete data[key]; },
|
||||
set(key: string, value: Data) { data[key] = value; },
|
||||
});
|
||||
|
||||
export default curry(traverseEntity);"""
|
||||
|
||||
# traverseEntity should be recognized as exported
|
||||
is_exported, export_name = ts_analyzer.is_function_exported(code, "traverseEntity")
|
||||
assert is_exported is True
|
||||
assert export_name == "default"
|
||||
|
||||
# createVisitorUtils is NOT wrapped, so not exported via default
|
||||
is_utils_exported, _ = ts_analyzer.is_function_exported(code, "createVisitorUtils")
|
||||
assert is_utils_exported is False
|
||||
|
|
|
|||
446
tests/test_languages/test_typescript_e2e.py
Normal file
446
tests/test_languages/test_typescript_e2e.py
Normal file
|
|
@ -0,0 +1,446 @@
|
|||
"""End-to-end integration tests for TypeScript pipeline.
|
||||
|
||||
Tests the full optimization pipeline for TypeScript:
|
||||
- Function discovery
|
||||
- Code context extraction
|
||||
- Test discovery
|
||||
- Code replacement
|
||||
- Syntax validation with TypeScript parser (not JavaScript)
|
||||
|
||||
This is the TypeScript equivalent of test_javascript_e2e.py.
|
||||
Ensures parity between JavaScript and TypeScript support.
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from codeflash.languages.base import Language
|
||||
|
||||
|
||||
def skip_if_ts_not_supported():
|
||||
"""Skip test if TypeScript language is not supported."""
|
||||
try:
|
||||
from codeflash.languages import get_language_support
|
||||
|
||||
get_language_support(Language.TYPESCRIPT)
|
||||
except Exception as e:
|
||||
pytest.skip(f"TypeScript language support not available: {e}")
|
||||
|
||||
|
||||
class TestTypeScriptFunctionDiscovery:
|
||||
"""Tests for TypeScript function discovery in the main pipeline."""
|
||||
|
||||
@pytest.fixture
|
||||
def ts_project_dir(self):
|
||||
"""Get the TypeScript sample project directory."""
|
||||
project_root = Path(__file__).parent.parent.parent
|
||||
ts_dir = project_root / "code_to_optimize" / "js" / "code_to_optimize_vitest"
|
||||
if not ts_dir.exists():
|
||||
pytest.skip("code_to_optimize_vitest directory not found")
|
||||
return ts_dir
|
||||
|
||||
def test_discover_functions_in_typescript_file(self, ts_project_dir):
|
||||
"""Test discovering functions in a TypeScript file."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
|
||||
fib_file = ts_project_dir / "fibonacci.ts"
|
||||
if not fib_file.exists():
|
||||
pytest.skip("fibonacci.ts not found")
|
||||
|
||||
functions = find_all_functions_in_file(fib_file)
|
||||
|
||||
assert fib_file in functions
|
||||
func_list = functions[fib_file]
|
||||
|
||||
func_names = {f.function_name for f in func_list}
|
||||
assert "fibonacci" in func_names
|
||||
|
||||
# Critical: Verify language is "typescript", not "javascript"
|
||||
for func in func_list:
|
||||
assert func.language == "typescript", \
|
||||
f"Function {func.function_name} should have language='typescript', got '{func.language}'"
|
||||
|
||||
def test_discover_functions_with_type_annotations(self):
|
||||
"""Test discovering TypeScript functions with type annotations."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".ts", mode="w", delete=False) as f:
|
||||
f.write("""
|
||||
export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
export function greet(name: string): string {
|
||||
return `Hello, \${name}!`;
|
||||
}
|
||||
|
||||
interface User {
|
||||
name: string;
|
||||
age: number;
|
||||
}
|
||||
|
||||
export function getUserAge(user: User): number {
|
||||
return user.age;
|
||||
}
|
||||
""")
|
||||
f.flush()
|
||||
file_path = Path(f.name)
|
||||
|
||||
functions = find_all_functions_in_file(file_path)
|
||||
|
||||
assert len(functions.get(file_path, [])) == 3
|
||||
|
||||
for func in functions[file_path]:
|
||||
assert func.language == "typescript"
|
||||
|
||||
def test_get_typescript_files(self, ts_project_dir):
|
||||
"""Test getting TypeScript files from directory."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import get_files_for_language
|
||||
|
||||
files = get_files_for_language(ts_project_dir, Language.TYPESCRIPT)
|
||||
|
||||
ts_files = [f for f in files if f.suffix == ".ts" and "test" not in f.name]
|
||||
assert len(ts_files) >= 1
|
||||
|
||||
|
||||
class TestTypeScriptCodeContext:
|
||||
"""Tests for TypeScript code context extraction."""
|
||||
|
||||
@pytest.fixture
|
||||
def ts_project_dir(self):
|
||||
"""Get the TypeScript sample project directory."""
|
||||
project_root = Path(__file__).parent.parent.parent
|
||||
ts_dir = project_root / "code_to_optimize" / "js" / "code_to_optimize_vitest"
|
||||
if not ts_dir.exists():
|
||||
pytest.skip("code_to_optimize_vitest directory not found")
|
||||
return ts_dir
|
||||
|
||||
def test_extract_code_context_for_typescript(self, ts_project_dir):
|
||||
"""Test extracting code context for a TypeScript function."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.context.code_context_extractor import get_code_optimization_context
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
from codeflash.languages import current as lang_current
|
||||
|
||||
lang_current._current_language = Language.TYPESCRIPT
|
||||
|
||||
fib_file = ts_project_dir / "fibonacci.ts"
|
||||
if not fib_file.exists():
|
||||
pytest.skip("fibonacci.ts not found")
|
||||
|
||||
functions = find_all_functions_in_file(fib_file)
|
||||
func_list = functions[fib_file]
|
||||
|
||||
fib_func = next((f for f in func_list if f.function_name == "fibonacci"), None)
|
||||
assert fib_func is not None
|
||||
|
||||
context = get_code_optimization_context(fib_func, ts_project_dir)
|
||||
|
||||
assert context.read_writable_code is not None
|
||||
# Critical: language should be "typescript", not "javascript"
|
||||
assert context.read_writable_code.language == "typescript"
|
||||
assert len(context.read_writable_code.code_strings) > 0
|
||||
|
||||
|
||||
class TestTypeScriptCodeReplacement:
|
||||
"""Tests for TypeScript code replacement."""
|
||||
|
||||
def test_replace_function_in_typescript_file(self):
|
||||
"""Test replacing a function in a TypeScript file."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.languages import get_language_support
|
||||
from codeflash.languages.base import FunctionInfo
|
||||
|
||||
original_source = """
|
||||
function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function multiply(a: number, b: number): number {
|
||||
return a * b;
|
||||
}
|
||||
"""
|
||||
|
||||
new_function = """function add(a: number, b: number): number {
|
||||
// Optimized version
|
||||
return a + b;
|
||||
}"""
|
||||
|
||||
ts_support = get_language_support(Language.TYPESCRIPT)
|
||||
|
||||
func_info = FunctionInfo(
|
||||
function_name="add",
|
||||
file_path=Path("/tmp/test.ts"),
|
||||
starting_line=2,
|
||||
ending_line=4,
|
||||
language="typescript"
|
||||
)
|
||||
|
||||
result = ts_support.replace_function(original_source, func_info, new_function)
|
||||
|
||||
expected_result = """
|
||||
function add(a: number, b: number): number {
|
||||
// Optimized version
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function multiply(a: number, b: number): number {
|
||||
return a * b;
|
||||
}
|
||||
"""
|
||||
assert result == expected_result
|
||||
|
||||
def test_replace_function_preserves_types(self):
|
||||
"""Test that replacing a function preserves TypeScript type annotations."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.languages import get_language_support
|
||||
from codeflash.languages.base import FunctionInfo
|
||||
|
||||
original_source = """
|
||||
interface Config {
|
||||
timeout: number;
|
||||
retries: number;
|
||||
}
|
||||
|
||||
function processConfig(config: Config): string {
|
||||
return `timeout=\${config.timeout}, retries=\${config.retries}`;
|
||||
}
|
||||
"""
|
||||
|
||||
new_function = """function processConfig(config: Config): string {
|
||||
// Optimized with template caching
|
||||
const { timeout, retries } = config;
|
||||
return `timeout=\${timeout}, retries=\${retries}`;
|
||||
}"""
|
||||
|
||||
ts_support = get_language_support(Language.TYPESCRIPT)
|
||||
|
||||
func_info = FunctionInfo(
|
||||
function_name="processConfig",
|
||||
file_path=Path("/tmp/test.ts"),
|
||||
starting_line=7,
|
||||
ending_line=9,
|
||||
language="typescript"
|
||||
)
|
||||
|
||||
result = ts_support.replace_function(original_source, func_info, new_function)
|
||||
|
||||
# Verify type annotations are preserved
|
||||
assert "config: Config" in result
|
||||
assert ": string" in result
|
||||
assert "interface Config" in result
|
||||
|
||||
|
||||
class TestTypeScriptTestDiscovery:
|
||||
"""Tests for TypeScript test discovery."""
|
||||
|
||||
@pytest.fixture
|
||||
def ts_project_dir(self):
|
||||
"""Get the TypeScript sample project directory."""
|
||||
project_root = Path(__file__).parent.parent.parent
|
||||
ts_dir = project_root / "code_to_optimize" / "js" / "code_to_optimize_vitest"
|
||||
if not ts_dir.exists():
|
||||
pytest.skip("code_to_optimize_vitest directory not found")
|
||||
return ts_dir
|
||||
|
||||
def test_discover_vitest_tests_for_typescript(self, ts_project_dir):
|
||||
"""Test discovering Vitest tests for TypeScript functions."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.languages import get_language_support
|
||||
from codeflash.languages.base import FunctionInfo
|
||||
|
||||
ts_support = get_language_support(Language.TYPESCRIPT)
|
||||
test_root = ts_project_dir / "tests"
|
||||
|
||||
if not test_root.exists():
|
||||
pytest.skip("tests directory not found")
|
||||
|
||||
fib_file = ts_project_dir / "fibonacci.ts"
|
||||
func_info = FunctionInfo(
|
||||
function_name="fibonacci",
|
||||
file_path=fib_file,
|
||||
starting_line=1,
|
||||
ending_line=7,
|
||||
language="typescript"
|
||||
)
|
||||
|
||||
tests = ts_support.discover_tests(test_root, [func_info])
|
||||
|
||||
# Should find tests for the fibonacci function
|
||||
assert func_info.qualified_name in tests or len(tests) > 0
|
||||
|
||||
|
||||
class TestTypeScriptPipelineIntegration:
|
||||
"""Integration tests for the full TypeScript pipeline."""
|
||||
|
||||
def test_function_to_optimize_has_correct_fields(self):
|
||||
"""Test that FunctionToOptimize from TypeScript has all required fields."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.discovery.functions_to_optimize import find_all_functions_in_file
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".ts", mode="w", delete=False) as f:
|
||||
f.write("""
|
||||
class Calculator {
|
||||
add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
subtract(a: number, b: number): number {
|
||||
return a - b;
|
||||
}
|
||||
}
|
||||
|
||||
function standalone(x: number): number {
|
||||
return x * 2;
|
||||
}
|
||||
""")
|
||||
f.flush()
|
||||
file_path = Path(f.name)
|
||||
|
||||
functions = find_all_functions_in_file(file_path)
|
||||
|
||||
assert len(functions.get(file_path, [])) >= 3
|
||||
|
||||
standalone_fn = next((fn for fn in functions[file_path] if fn.function_name == "standalone"), None)
|
||||
assert standalone_fn is not None
|
||||
assert standalone_fn.language == "typescript"
|
||||
assert len(standalone_fn.parents) == 0
|
||||
|
||||
add_fn = next((fn for fn in functions[file_path] if fn.function_name == "add"), None)
|
||||
assert add_fn is not None
|
||||
assert add_fn.language == "typescript"
|
||||
assert len(add_fn.parents) == 1
|
||||
assert add_fn.parents[0].name == "Calculator"
|
||||
|
||||
def test_code_strings_markdown_uses_typescript_tag(self):
|
||||
"""Test that CodeStringsMarkdown uses typescript for code blocks."""
|
||||
from codeflash.models.models import CodeString, CodeStringsMarkdown
|
||||
|
||||
code_strings = CodeStringsMarkdown(
|
||||
code_strings=[
|
||||
CodeString(
|
||||
code="function add(a: number, b: number): number { return a + b; }",
|
||||
file_path=Path("test.ts"),
|
||||
language="typescript"
|
||||
)
|
||||
],
|
||||
language="typescript",
|
||||
)
|
||||
|
||||
markdown = code_strings.markdown
|
||||
assert "```typescript" in markdown
|
||||
|
||||
|
||||
class TestTypeScriptSyntaxValidation:
|
||||
"""Tests for TypeScript-specific syntax validation.
|
||||
|
||||
These tests ensure TypeScript code is validated with the TypeScript parser,
|
||||
not the JavaScript parser. This was the root cause of production issues.
|
||||
"""
|
||||
|
||||
def test_typescript_type_assertion_valid(self):
|
||||
"""TypeScript type assertions should be valid."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.languages import get_language_support
|
||||
|
||||
ts_support = get_language_support(Language.TYPESCRIPT)
|
||||
|
||||
# This is TypeScript-specific syntax that should pass
|
||||
code = "const value = 4.9 as unknown as number;"
|
||||
assert ts_support.validate_syntax(code) is True
|
||||
|
||||
def test_typescript_type_assertion_invalid_in_javascript(self):
|
||||
"""TypeScript type assertions should be INVALID in JavaScript.
|
||||
|
||||
This test would have caught the production bug where TypeScript code
|
||||
was being validated with the JavaScript parser.
|
||||
"""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.languages import get_language_support
|
||||
|
||||
js_support = get_language_support(Language.JAVASCRIPT)
|
||||
|
||||
# This TypeScript syntax should FAIL JavaScript validation
|
||||
code = "const value = 4.9 as unknown as number;"
|
||||
assert js_support.validate_syntax(code) is False
|
||||
|
||||
def test_typescript_interface_valid(self):
|
||||
"""TypeScript interfaces should be valid."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.languages import get_language_support
|
||||
|
||||
ts_support = get_language_support(Language.TYPESCRIPT)
|
||||
|
||||
code = """
|
||||
interface User {
|
||||
name: string;
|
||||
age: number;
|
||||
}
|
||||
"""
|
||||
assert ts_support.validate_syntax(code) is True
|
||||
|
||||
def test_typescript_interface_invalid_in_javascript(self):
|
||||
"""TypeScript interfaces should be INVALID in JavaScript."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.languages import get_language_support
|
||||
|
||||
js_support = get_language_support(Language.JAVASCRIPT)
|
||||
|
||||
code = """
|
||||
interface User {
|
||||
name: string;
|
||||
age: number;
|
||||
}
|
||||
"""
|
||||
assert js_support.validate_syntax(code) is False
|
||||
|
||||
def test_typescript_generic_function_valid(self):
|
||||
"""TypeScript generics should be valid."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.languages import get_language_support
|
||||
|
||||
ts_support = get_language_support(Language.TYPESCRIPT)
|
||||
|
||||
code = "function identity<T>(arg: T): T { return arg; }"
|
||||
assert ts_support.validate_syntax(code) is True
|
||||
|
||||
def test_typescript_generic_function_invalid_in_javascript(self):
|
||||
"""TypeScript generics should be INVALID in JavaScript."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.languages import get_language_support
|
||||
|
||||
js_support = get_language_support(Language.JAVASCRIPT)
|
||||
|
||||
code = "function identity<T>(arg: T): T { return arg; }"
|
||||
assert js_support.validate_syntax(code) is False
|
||||
|
||||
|
||||
class TestTypeScriptCodeStringValidation:
|
||||
"""Tests for CodeString validation with TypeScript."""
|
||||
|
||||
def test_code_string_validates_typescript_with_typescript_parser(self):
|
||||
"""CodeString with language='typescript' should use TypeScript parser."""
|
||||
skip_if_ts_not_supported()
|
||||
from codeflash.models.models import CodeString
|
||||
|
||||
# TypeScript-specific syntax should pass when language='typescript'
|
||||
ts_code = "const value = 4.9 as unknown as number;"
|
||||
cs = CodeString(code=ts_code, language="typescript")
|
||||
assert cs.code == ts_code
|
||||
|
||||
def test_code_string_rejects_typescript_with_javascript_parser(self):
|
||||
"""CodeString with language='javascript' should reject TypeScript syntax."""
|
||||
skip_if_ts_not_supported()
|
||||
from pydantic import ValidationError
|
||||
|
||||
from codeflash.models.models import CodeString
|
||||
|
||||
# TypeScript-specific syntax should FAIL when language='javascript'
|
||||
ts_code = "const value = 4.9 as unknown as number;"
|
||||
with pytest.raises(ValidationError):
|
||||
CodeString(code=ts_code, language="javascript")
|
||||
134
tests/test_validate_javascript_code.py
Normal file
134
tests/test_validate_javascript_code.py
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
"""Tests for JavaScript/TypeScript code validation in CodeString.
|
||||
|
||||
These tests ensure that JavaScript and TypeScript code is validated correctly
|
||||
using the appropriate syntax parser for each language.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from codeflash.api.aiservice import AiServiceClient
|
||||
from codeflash.models.models import CodeString, OptimizedCandidateSource
|
||||
|
||||
|
||||
class TestJavaScriptCodeValidation:
|
||||
"""Tests for JavaScript code validation."""
|
||||
|
||||
def test_valid_javascript_code(self):
|
||||
"""Valid JavaScript code should pass validation."""
|
||||
valid_code = "const x = 1;\nconst y = x + 2;\nconsole.log(y);"
|
||||
cs = CodeString(code=valid_code, language="javascript")
|
||||
assert cs.code == valid_code
|
||||
|
||||
def test_invalid_javascript_code_syntax(self):
|
||||
"""Invalid JavaScript syntax should raise ValidationError."""
|
||||
invalid_code = "const x = 1;\nconsole.log(x" # Missing closing parenthesis
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CodeString(code=invalid_code, language="javascript")
|
||||
assert "Invalid Javascript code" in str(exc_info.value)
|
||||
|
||||
def test_javascript_empty_code(self):
|
||||
"""Empty code is syntactically valid."""
|
||||
empty_code = ""
|
||||
cs = CodeString(code=empty_code, language="javascript")
|
||||
assert cs.code == empty_code
|
||||
|
||||
def test_javascript_arrow_function(self):
|
||||
"""Arrow functions should be valid JavaScript."""
|
||||
code = "const add = (a, b) => a + b;"
|
||||
cs = CodeString(code=code, language="javascript")
|
||||
assert cs.code == code
|
||||
|
||||
|
||||
class TestTypeScriptCodeValidation:
|
||||
"""Tests for TypeScript code validation."""
|
||||
|
||||
def test_valid_typescript_code(self):
|
||||
"""Valid TypeScript code should pass validation."""
|
||||
valid_code = "const x: number = 1;\nconst y: number = x + 2;\nconsole.log(y);"
|
||||
cs = CodeString(code=valid_code, language="typescript")
|
||||
assert cs.code == valid_code
|
||||
|
||||
def test_typescript_type_assertion(self):
|
||||
"""TypeScript type assertions should be valid."""
|
||||
code = "const value = 4.9 as unknown as number;"
|
||||
cs = CodeString(code=code, language="typescript")
|
||||
assert cs.code == code
|
||||
|
||||
def test_typescript_interface(self):
|
||||
"""TypeScript interfaces should be valid."""
|
||||
code = "interface User { name: string; age: number; }"
|
||||
cs = CodeString(code=code, language="typescript")
|
||||
assert cs.code == code
|
||||
|
||||
def test_typescript_generic_function(self):
|
||||
"""TypeScript generics should be valid."""
|
||||
code = "function identity<T>(arg: T): T { return arg; }"
|
||||
cs = CodeString(code=code, language="typescript")
|
||||
assert cs.code == code
|
||||
|
||||
def test_invalid_typescript_code_syntax(self):
|
||||
"""Invalid TypeScript syntax should raise ValidationError."""
|
||||
invalid_code = "const x: number = 1;\nconsole.log(x" # Missing closing parenthesis
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CodeString(code=invalid_code, language="typescript")
|
||||
assert "Invalid Typescript code" in str(exc_info.value)
|
||||
|
||||
def test_typescript_syntax_invalid_as_javascript(self):
|
||||
"""TypeScript-specific syntax should fail when validated as JavaScript."""
|
||||
ts_code = "const value = 4.9 as unknown as number;"
|
||||
# Should pass as TypeScript
|
||||
cs_ts = CodeString(code=ts_code, language="typescript")
|
||||
assert cs_ts.code == ts_code
|
||||
|
||||
# Should fail as JavaScript (type assertions are not valid JS)
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
CodeString(code=ts_code, language="javascript")
|
||||
assert "Invalid Javascript code" in str(exc_info.value)
|
||||
|
||||
|
||||
class TestGeneratedCandidatesValidation:
|
||||
"""Tests for validation of generated optimization candidates."""
|
||||
|
||||
def test_javascript_generated_candidates_validation(self):
|
||||
"""JavaScript optimization candidates should be validated."""
|
||||
ai_service = AiServiceClient()
|
||||
|
||||
# Invalid JavaScript code
|
||||
invalid_code = """```javascript:file.js
|
||||
const x = 1
|
||||
console.log(x
|
||||
```"""
|
||||
mock_candidates = [{"source_code": invalid_code, "explanation": "", "optimization_id": ""}]
|
||||
candidates = ai_service._get_valid_candidates(
|
||||
mock_candidates, OptimizedCandidateSource.OPTIMIZE, language="javascript"
|
||||
)
|
||||
assert len(candidates) == 0
|
||||
|
||||
# Valid JavaScript code
|
||||
valid_code = """```javascript:file.js
|
||||
const x = 1;
|
||||
console.log(x);
|
||||
```"""
|
||||
mock_candidates = [{"source_code": valid_code, "explanation": "", "optimization_id": ""}]
|
||||
candidates = ai_service._get_valid_candidates(
|
||||
mock_candidates, OptimizedCandidateSource.OPTIMIZE, language="javascript"
|
||||
)
|
||||
assert len(candidates) == 1
|
||||
|
||||
def test_typescript_generated_candidates_validation(self):
|
||||
"""TypeScript optimization candidates should be validated."""
|
||||
ai_service = AiServiceClient()
|
||||
|
||||
# TypeScript code with type assertions (valid TS, invalid JS)
|
||||
ts_code = """```typescript:file.ts
|
||||
const value = 4.9 as unknown as number;
|
||||
console.log(value);
|
||||
```"""
|
||||
mock_candidates = [{"source_code": ts_code, "explanation": "", "optimization_id": ""}]
|
||||
|
||||
# Should pass when validated as TypeScript
|
||||
candidates = ai_service._get_valid_candidates(
|
||||
mock_candidates, OptimizedCandidateSource.OPTIMIZE, language="typescript"
|
||||
)
|
||||
assert len(candidates) == 1
|
||||
661
uv.lock
661
uv.lock
|
|
@ -237,15 +237,15 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "blessed"
|
||||
version = "1.29.0"
|
||||
version = "1.30.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "jinxed", marker = "sys_platform == 'win32'" },
|
||||
{ name = "wcwidth" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cb/1f/ed37dbe0fd7f026cfbff0d968b924f1a53411e1c3a4639762467d3b06d24/blessed-1.29.0.tar.gz", hash = "sha256:4938cbfea8280885c853c0700850704aeacb25a97fca56de5e1e30ca63a0f1aa", size = 13950929, upload-time = "2026-02-01T15:26:38.202Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/dd/19/e926a0dbbf93c7aeb15d4dfff0d0e3de02653b3ba540b687307d0819c1ff/blessed-1.30.0.tar.gz", hash = "sha256:4d547019d7b40fc5420ea2ba2bc180fdccc31d6715298e2b49ffa7b020d44667", size = 13948932, upload-time = "2026-02-06T19:40:23.541Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/47/566dcc08f8037511355a49220a3eecb795f955bb6145f419d5e60dbeaa00/blessed-1.29.0-py3-none-any.whl", hash = "sha256:cd5f339a308ac5e97d814c4bcbf7ce9723c465ca73c42744ea72646a0c653f6e", size = 100645, upload-time = "2026-02-01T15:26:35.632Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/b0/8d87c7c8015ce8d4b2c5ee7a82a1d955f10138322c4f0cb387d7d2c1b2e7/blessed-1.30.0-py3-none-any.whl", hash = "sha256:4061a9f10dd22798716c2548ba36385af6a29d856c897f367c6ccc927e0b3a5a", size = 98399, upload-time = "2026-02-06T19:40:20.815Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -427,7 +427,7 @@ dependencies = [
|
|||
{ name = "click", version = "8.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "codeflash-benchmark" },
|
||||
{ name = "coverage", version = "7.10.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
||||
{ name = "coverage", version = "7.13.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "coverage", version = "7.13.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "crosshair-tool" },
|
||||
{ name = "dill" },
|
||||
{ name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
||||
|
|
@ -448,7 +448,7 @@ dependencies = [
|
|||
{ name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
||||
{ name = "platformdirs", version = "4.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "posthog", version = "6.9.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
||||
{ name = "posthog", version = "7.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "posthog", version = "7.8.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pygls" },
|
||||
{ name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
||||
|
|
@ -501,7 +501,7 @@ tests = [
|
|||
{ name = "eval-type-backport" },
|
||||
{ name = "jax", version = "0.4.30", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
||||
{ name = "jax", version = "0.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
|
||||
{ name = "jax", version = "0.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "jax", version = "0.9.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "numba", version = "0.60.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
||||
{ name = "numba", version = "0.63.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
||||
|
|
@ -546,7 +546,7 @@ requires-dist = [
|
|||
{ name = "pydantic", specifier = ">=1.10.1" },
|
||||
{ name = "pygls", specifier = ">=2.0.0,<3.0.0" },
|
||||
{ name = "pytest", specifier = ">=7.0.0" },
|
||||
{ name = "pytest-asyncio", specifier = ">=1.2.0" },
|
||||
{ name = "pytest-asyncio", specifier = ">=0.18.0" },
|
||||
{ name = "pytest-timeout", specifier = ">=2.1.0" },
|
||||
{ name = "rich", specifier = ">=13.8.1" },
|
||||
{ name = "sentry-sdk", specifier = ">=1.40.6,<3.0.0" },
|
||||
|
|
@ -735,7 +735,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.13.3"
|
||||
version = "7.13.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.14' and sys_platform == 'win32'",
|
||||
|
|
@ -752,99 +752,113 @@ resolution-markers = [
|
|||
"python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
|
||||
"python_full_version == '3.10.*'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/11/43/3e4ac666cc35f231fa70c94e9f38459299de1a152813f9d2f60fc5f3ecaf/coverage-7.13.3.tar.gz", hash = "sha256:f7f6182d3dfb8802c1747eacbfe611b669455b69b7c037484bb1efbbb56711ac", size = 826832, upload-time = "2026-02-03T14:02:30.944Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/24/56/95b7e30fa389756cb56630faa728da46a27b8c6eb46f9d557c68fff12b65/coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91", size = 827239, upload-time = "2026-02-09T12:59:03.86Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/07/1c8099563a8a6c389a31c2d0aa1497cee86d6248bb4b9ba5e779215db9f9/coverage-7.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0b4f345f7265cdbdb5ec2521ffff15fa49de6d6c39abf89fc7ad68aa9e3a55f0", size = 219143, upload-time = "2026-02-03T13:59:40.459Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/39/a892d44af7aa092cab70e0cc5cdbba18eeccfe1d6930695dab1742eef9e9/coverage-7.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:96c3be8bae9d0333e403cc1a8eb078a7f928b5650bae94a18fb4820cc993fb9b", size = 219663, upload-time = "2026-02-03T13:59:41.951Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/25/9669dcf4c2bb4c3861469e6db20e52e8c11908cf53c14ec9b12e9fd4d602/coverage-7.13.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d6f4a21328ea49d38565b55599e1c02834e76583a6953e5586d65cb1efebd8f8", size = 246424, upload-time = "2026-02-03T13:59:43.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/68/d9766c4e298aca62ea5d9543e1dd1e4e1439d7284815244d8b7db1840bfb/coverage-7.13.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fc970575799a9d17d5c3fafd83a0f6ccf5d5117cdc9ad6fbd791e9ead82418b0", size = 248228, upload-time = "2026-02-03T13:59:44.816Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/e2/eea6cb4a4bd443741adf008d4cccec83a1f75401df59b6559aca2bdd9710/coverage-7.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:87ff33b652b3556b05e204ae20793d1f872161b0fa5ec8a9ac76f8430e152ed6", size = 250103, upload-time = "2026-02-03T13:59:46.271Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/77/664280ecd666c2191610842177e2fab9e5dbdeef97178e2078fed46a3d2c/coverage-7.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7df8759ee57b9f3f7b66799b7660c282f4375bef620ade1686d6a7b03699e75f", size = 247107, upload-time = "2026-02-03T13:59:48.53Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/df/2a672eab99e0d0eba52d8a63e47dc92245eee26954d1b2d3c8f7d372151f/coverage-7.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f45c9bcb16bee25a798ccba8a2f6a1251b19de6a0d617bb365d7d2f386c4e20e", size = 248143, upload-time = "2026-02-03T13:59:50.027Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/dc/a104e7a87c13e57a358b8b9199a8955676e1703bb372d79722b54978ae45/coverage-7.13.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:318b2e4753cbf611061e01b6cc81477e1cdfeb69c36c4a14e6595e674caadb56", size = 246148, upload-time = "2026-02-03T13:59:52.025Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/89/e113d3a58dc20b03b7e59aed1e53ebc9ca6167f961876443e002b10e3ae9/coverage-7.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:24db3959de8ee394eeeca89ccb8ba25305c2da9a668dd44173394cbd5aa0777f", size = 246414, upload-time = "2026-02-03T13:59:53.859Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/60/a3fd0a6e8d89b488396019a2268b6a1f25ab56d6d18f3be50f35d77b47dc/coverage-7.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:be14d0622125edef21b3a4d8cd2d138c4872bf6e38adc90fd92385e3312f406a", size = 247023, upload-time = "2026-02-03T13:59:55.454Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/fa/de4840bb939dbb22ba0648a6d8069fa91c9cf3b3fca8b0d1df461e885b3d/coverage-7.13.3-cp310-cp310-win32.whl", hash = "sha256:53be4aab8ddef18beb6188f3a3fdbf4d1af2277d098d4e618be3a8e6c88e74be", size = 221751, upload-time = "2026-02-03T13:59:57.383Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/87/233ff8b7ef62fb63f58c78623b50bef69681111e0c4d43504f422d88cda4/coverage-7.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:bfeee64ad8b4aae3233abb77eb6b52b51b05fa89da9645518671b9939a78732b", size = 222686, upload-time = "2026-02-03T13:59:58.825Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/09/1ac74e37cf45f17eb41e11a21854f7f92a4c2d6c6098ef4a1becb0c6d8d3/coverage-7.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5907605ee20e126eeee2abe14aae137043c2c8af2fa9b38d2ab3b7a6b8137f73", size = 219276, upload-time = "2026-02-03T14:00:00.296Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/cb/71908b08b21beb2c437d0d5870c4ec129c570ca1b386a8427fcdb11cf89c/coverage-7.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a88705500988c8acad8b8fd86c2a933d3aa96bec1ddc4bc5cb256360db7bbd00", size = 219776, upload-time = "2026-02-03T14:00:02.414Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/85/c4f3dd69232887666a2c0394d4be21c60ea934d404db068e6c96aa59cd87/coverage-7.13.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bbb5aa9016c4c29e3432e087aa29ebee3f8fda089cfbfb4e6d64bd292dcd1c2", size = 250196, upload-time = "2026-02-03T14:00:04.197Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/cc/560ad6f12010344d0778e268df5ba9aa990aacccc310d478bf82bf3d302c/coverage-7.13.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0c2be202a83dde768937a61cdc5d06bf9fb204048ca199d93479488e6247656c", size = 252111, upload-time = "2026-02-03T14:00:05.639Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/66/3193985fb2c58e91f94cfbe9e21a6fdf941e9301fe2be9e92c072e9c8f8c/coverage-7.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f45e32ef383ce56e0ca099b2e02fcdf7950be4b1b56afaab27b4ad790befe5b", size = 254217, upload-time = "2026-02-03T14:00:07.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/78/f0f91556bf1faa416792e537c523c5ef9db9b1d32a50572c102b3d7c45b3/coverage-7.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6ed2e787249b922a93cd95c671cc9f4c9797a106e81b455c83a9ddb9d34590c0", size = 250318, upload-time = "2026-02-03T14:00:09.224Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/aa/fc654e45e837d137b2c1f3a2cc09b4aea1e8b015acd2f774fa0f3d2ddeba/coverage-7.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:05dd25b21afffe545e808265897c35f32d3e4437663923e0d256d9ab5031fb14", size = 251909, upload-time = "2026-02-03T14:00:10.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/4d/ab53063992add8a9ca0463c9d92cce5994a29e17affd1c2daa091b922a93/coverage-7.13.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:46d29926349b5c4f1ea4fca95e8c892835515f3600995a383fa9a923b5739ea4", size = 249971, upload-time = "2026-02-03T14:00:12.402Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/25/83694b81e46fcff9899694a1b6f57573429cdd82b57932f09a698f03eea5/coverage-7.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:fae6a21537519c2af00245e834e5bf2884699cc7c1055738fd0f9dc37a3644ad", size = 249692, upload-time = "2026-02-03T14:00:13.868Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/ef/d68fc304301f4cb4bf6aefa0045310520789ca38dabdfba9dbecd3f37919/coverage-7.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c672d4e2f0575a4ca2bf2aa0c5ced5188220ab806c1bb6d7179f70a11a017222", size = 250597, upload-time = "2026-02-03T14:00:15.461Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/85/240ad396f914df361d0f71e912ddcedb48130c71b88dc4193fe3c0306f00/coverage-7.13.3-cp311-cp311-win32.whl", hash = "sha256:fcda51c918c7a13ad93b5f89a58d56e3a072c9e0ba5c231b0ed81404bf2648fb", size = 221773, upload-time = "2026-02-03T14:00:17.462Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/71/165b3a6d3d052704a9ab52d11ea64ef3426745de517dda44d872716213a7/coverage-7.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:d1a049b5c51b3b679928dd35e47c4a2235e0b6128b479a7596d0ef5b42fa6301", size = 222711, upload-time = "2026-02-03T14:00:19.449Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/d0/0ddc9c5934cdd52639c5df1f1eb0fdab51bb52348f3a8d1c7db9c600d93a/coverage-7.13.3-cp311-cp311-win_arm64.whl", hash = "sha256:79f2670c7e772f4917895c3d89aad59e01f3dbe68a4ed2d0373b431fad1dcfba", size = 221377, upload-time = "2026-02-03T14:00:20.968Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/44/330f8e83b143f6668778ed61d17ece9dc48459e9e74669177de02f45fec5/coverage-7.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ed48b4170caa2c4420e0cd27dc977caaffc7eecc317355751df8373dddcef595", size = 219441, upload-time = "2026-02-03T14:00:22.585Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/e7/29db05693562c2e65bdf6910c0af2fd6f9325b8f43caf7a258413f369e30/coverage-7.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8f2adf4bcffbbec41f366f2e6dffb9d24e8172d16e91da5799c9b7ed6b5716e6", size = 219801, upload-time = "2026-02-03T14:00:24.186Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/ae/7f8a78249b02b0818db46220795f8ac8312ea4abd1d37d79ea81db5cae81/coverage-7.13.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:01119735c690786b6966a1e9f098da4cd7ca9174c4cfe076d04e653105488395", size = 251306, upload-time = "2026-02-03T14:00:25.798Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/71/a18a53d1808e09b2e9ebd6b47dad5e92daf4c38b0686b4c4d1b2f3e42b7f/coverage-7.13.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8bb09e83c603f152d855f666d70a71765ca8e67332e5829e62cb9466c176af23", size = 254051, upload-time = "2026-02-03T14:00:27.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/0a/eb30f6455d04c5a3396d0696cad2df0269ae7444bb322f86ffe3376f7bf9/coverage-7.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b607a40cba795cfac6d130220d25962931ce101f2f478a29822b19755377fb34", size = 255160, upload-time = "2026-02-03T14:00:29.024Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/7e/a45baac86274ce3ed842dbb84f14560c673ad30535f397d89164ec56c5df/coverage-7.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:44f14a62f5da2e9aedf9080e01d2cda61df39197d48e323538ec037336d68da8", size = 251709, upload-time = "2026-02-03T14:00:30.641Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/df/dd0dc12f30da11349993f3e218901fdf82f45ee44773596050c8f5a1fb25/coverage-7.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:debf29e0b157769843dff0981cc76f79e0ed04e36bb773c6cac5f6029054bd8a", size = 253083, upload-time = "2026-02-03T14:00:32.14Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/32/fc764c8389a8ce95cb90eb97af4c32f392ab0ac23ec57cadeefb887188d3/coverage-7.13.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:824bb95cd71604031ae9a48edb91fd6effde669522f960375668ed21b36e3ec4", size = 251227, upload-time = "2026-02-03T14:00:34.721Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/ca/d025e9da8f06f24c34d2da9873957cfc5f7e0d67802c3e34d0caa8452130/coverage-7.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8f1010029a5b52dc427c8e2a8dbddb2303ddd180b806687d1acd1bb1d06649e7", size = 250794, upload-time = "2026-02-03T14:00:36.278Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/c7/76bf35d5d488ec8f68682eb8e7671acc50a6d2d1c1182de1d2b6d4ffad3b/coverage-7.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cd5dee4fd7659d8306ffa79eeaaafd91fa30a302dac3af723b9b469e549247e0", size = 252671, upload-time = "2026-02-03T14:00:38.368Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/10/1921f1a03a7c209e1cb374f81a6b9b68b03cdb3ecc3433c189bc90e2a3d5/coverage-7.13.3-cp312-cp312-win32.whl", hash = "sha256:f7f153d0184d45f3873b3ad3ad22694fd73aadcb8cdbc4337ab4b41ea6b4dff1", size = 221986, upload-time = "2026-02-03T14:00:40.442Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/7c/f5d93297f8e125a80c15545edc754d93e0ed8ba255b65e609b185296af01/coverage-7.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:03a6e5e1e50819d6d7436f5bc40c92ded7e484e400716886ac921e35c133149d", size = 222793, upload-time = "2026-02-03T14:00:42.106Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/59/c86b84170015b4555ebabca8649bdf9f4a1f737a73168088385ed0f947c4/coverage-7.13.3-cp312-cp312-win_arm64.whl", hash = "sha256:51c4c42c0e7d09a822b08b6cf79b3c4db8333fffde7450da946719ba0d45730f", size = 221410, upload-time = "2026-02-03T14:00:43.726Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/f3/4c333da7b373e8c8bfb62517e8174a01dcc373d7a9083698e3b39d50d59c/coverage-7.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:853c3d3c79ff0db65797aad79dee6be020efd218ac4510f15a205f1e8d13ce25", size = 219468, upload-time = "2026-02-03T14:00:45.829Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/31/0714337b7d23630c8de2f4d56acf43c65f8728a45ed529b34410683f7217/coverage-7.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f75695e157c83d374f88dcc646a60cb94173304a9258b2e74ba5a66b7614a51a", size = 219839, upload-time = "2026-02-03T14:00:47.407Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/99/bd6f2a2738144c98945666f90cae446ed870cecf0421c767475fcf42cdbe/coverage-7.13.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d098709621d0819039f3f1e471ee554f55a0b2ac0d816883c765b14129b5627", size = 250828, upload-time = "2026-02-03T14:00:49.029Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/99/97b600225fbf631e6f5bfd3ad5bcaf87fbb9e34ff87492e5a572ff01bbe2/coverage-7.13.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:16d23d6579cf80a474ad160ca14d8b319abaa6db62759d6eef53b2fc979b58c8", size = 253432, upload-time = "2026-02-03T14:00:50.655Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/5c/abe2b3490bda26bd4f5e3e799be0bdf00bd81edebedc2c9da8d3ef288fa8/coverage-7.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00d34b29a59d2076e6f318b30a00a69bf63687e30cd882984ed444e753990cc1", size = 254672, upload-time = "2026-02-03T14:00:52.757Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/ba/5d1957c76b40daff53971fe0adb84d9c2162b614280031d1d0653dd010c1/coverage-7.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ab6d72bffac9deb6e6cb0f61042e748de3f9f8e98afb0375a8e64b0b6e11746b", size = 251050, upload-time = "2026-02-03T14:00:54.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/dc/dffdf3bfe9d32090f047d3c3085378558cb4eb6778cda7de414ad74581ed/coverage-7.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e129328ad1258e49cae0123a3b5fcb93d6c2fa90d540f0b4c7cdcdc019aaa3dc", size = 252801, upload-time = "2026-02-03T14:00:56.121Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/51/cdf6198b0f2746e04511a30dc9185d7b8cdd895276c07bdb538e37f1cd50/coverage-7.13.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2213a8d88ed35459bda71597599d4eec7c2ebad201c88f0bfc2c26fd9b0dd2ea", size = 250763, upload-time = "2026-02-03T14:00:58.719Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/1a/596b7d62218c1d69f2475b69cc6b211e33c83c902f38ee6ae9766dd422da/coverage-7.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:00dd3f02de6d5f5c9c3d95e3e036c3c2e2a669f8bf2d3ceb92505c4ce7838f67", size = 250587, upload-time = "2026-02-03T14:01:01.197Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/46/52330d5841ff660f22c130b75f5e1dd3e352c8e7baef5e5fef6b14e3e991/coverage-7.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f9bada7bc660d20b23d7d312ebe29e927b655cf414dadcdb6335a2075695bd86", size = 252358, upload-time = "2026-02-03T14:01:02.824Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/8a/e69a5be51923097ba7d5cff9724466e74fe486e9232020ba97c809a8b42b/coverage-7.13.3-cp313-cp313-win32.whl", hash = "sha256:75b3c0300f3fa15809bd62d9ca8b170eb21fcf0100eb4b4154d6dc8b3a5bbd43", size = 222007, upload-time = "2026-02-03T14:01:04.876Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/09/a5a069bcee0d613bdd48ee7637fa73bc09e7ed4342b26890f2df97cc9682/coverage-7.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:a2f7589c6132c44c53f6e705e1a6677e2b7821378c22f7703b2cf5388d0d4587", size = 222812, upload-time = "2026-02-03T14:01:07.296Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/4f/d62ad7dfe32f9e3d4a10c178bb6f98b10b083d6e0530ca202b399371f6c1/coverage-7.13.3-cp313-cp313-win_arm64.whl", hash = "sha256:123ceaf2b9d8c614f01110f908a341e05b1b305d6b2ada98763b9a5a59756051", size = 221433, upload-time = "2026-02-03T14:01:09.156Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/b2/4876c46d723d80b9c5b695f1a11bf5f7c3dabf540ec00d6edc076ff025e6/coverage-7.13.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:cc7fd0f726795420f3678ac82ff882c7fc33770bd0074463b5aef7293285ace9", size = 220162, upload-time = "2026-02-03T14:01:11.409Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/04/9942b64a0e0bdda2c109f56bda42b2a59d9d3df4c94b85a323c1cae9fc77/coverage-7.13.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d358dc408edc28730aed5477a69338e444e62fba0b7e9e4a131c505fadad691e", size = 220510, upload-time = "2026-02-03T14:01:13.038Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/82/5cfe1e81eae525b74669f9795f37eb3edd4679b873d79d1e6c1c14ee6c1c/coverage-7.13.3-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5d67b9ed6f7b5527b209b24b3df9f2e5bf0198c1bbf99c6971b0e2dcb7e2a107", size = 261801, upload-time = "2026-02-03T14:01:14.674Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/ec/a553d7f742fd2cd12e36a16a7b4b3582d5934b496ef2b5ea8abeb10903d4/coverage-7.13.3-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59224bfb2e9b37c1335ae35d00daa3a5b4e0b1a20f530be208fff1ecfa436f43", size = 263882, upload-time = "2026-02-03T14:01:16.343Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/58/8f54a2a93e3d675635bc406de1c9ac8d551312142ff52c9d71b5e533ad45/coverage-7.13.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9306b5299e31e31e0d3b908c66bcb6e7e3ddca143dea0266e9ce6c667346d3", size = 266306, upload-time = "2026-02-03T14:01:18.02Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/be/e593399fd6ea1f00aee79ebd7cc401021f218d34e96682a92e1bae092ff6/coverage-7.13.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:343aaeb5f8bb7bcd38620fd7bc56e6ee8207847d8c6103a1e7b72322d381ba4a", size = 261051, upload-time = "2026-02-03T14:01:19.757Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/e5/e9e0f6138b21bcdebccac36fbfde9cf15eb1bbcea9f5b1f35cd1f465fb91/coverage-7.13.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b2182129f4c101272ff5f2f18038d7b698db1bf8e7aa9e615cb48440899ad32e", size = 263868, upload-time = "2026-02-03T14:01:21.487Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/bf/de72cfebb69756f2d4a2dde35efcc33c47d85cd3ebdf844b3914aac2ef28/coverage-7.13.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:94d2ac94bd0cc57c5626f52f8c2fffed1444b5ae8c9fc68320306cc2b255e155", size = 261498, upload-time = "2026-02-03T14:01:23.097Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/91/4a2d313a70fc2e98ca53afd1c8ce67a89b1944cd996589a5b1fe7fbb3e5c/coverage-7.13.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:65436cde5ecabe26fb2f0bf598962f0a054d3f23ad529361326ac002c61a2a1e", size = 260394, upload-time = "2026-02-03T14:01:24.949Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/83/25113af7cf6941e779eb7ed8de2a677865b859a07ccee9146d4cc06a03e3/coverage-7.13.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:db83b77f97129813dbd463a67e5335adc6a6a91db652cc085d60c2d512746f96", size = 262579, upload-time = "2026-02-03T14:01:26.703Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/19/a5f2b96262977e82fb9aabbe19b4d83561f5d063f18dde3e72f34ffc3b2f/coverage-7.13.3-cp313-cp313t-win32.whl", hash = "sha256:dfb428e41377e6b9ba1b0a32df6db5409cb089a0ed1d0a672dc4953ec110d84f", size = 222679, upload-time = "2026-02-03T14:01:28.553Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/82/ef1747b88c87a5c7d7edc3704799ebd650189a9158e680a063308b6125ef/coverage-7.13.3-cp313-cp313t-win_amd64.whl", hash = "sha256:5badd7e596e6b0c89aa8ec6d37f4473e4357f982ce57f9a2942b0221cd9cf60c", size = 223740, upload-time = "2026-02-03T14:01:30.776Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/4c/a67c7bb5b560241c22736a9cb2f14c5034149ffae18630323fde787339e4/coverage-7.13.3-cp313-cp313t-win_arm64.whl", hash = "sha256:989aa158c0eb19d83c76c26f4ba00dbb272485c56e452010a3450bdbc9daafd9", size = 221996, upload-time = "2026-02-03T14:01:32.495Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/b3/677bb43427fed9298905106f39c6520ac75f746f81b8f01104526a8026e4/coverage-7.13.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c6f6169bbdbdb85aab8ac0392d776948907267fcc91deeacf6f9d55f7a83ae3b", size = 219513, upload-time = "2026-02-03T14:01:34.29Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/53/290046e3bbf8986cdb7366a42dab3440b9983711eaff044a51b11006c67b/coverage-7.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2f5e731627a3d5ef11a2a35aa0c6f7c435867c7ccbc391268eb4f2ca5dbdcc10", size = 219850, upload-time = "2026-02-03T14:01:35.984Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/2b/ab41f10345ba2e49d5e299be8663be2b7db33e77ac1b85cd0af985ea6406/coverage-7.13.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9db3a3285d91c0b70fab9f39f0a4aa37d375873677efe4e71e58d8321e8c5d39", size = 250886, upload-time = "2026-02-03T14:01:38.287Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/2d/b3f6913ee5a1d5cdd04106f257e5fac5d048992ffc2d9995d07b0f17739f/coverage-7.13.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06e49c5897cb12e3f7ecdc111d44e97c4f6d0557b81a7a0204ed70a8b038f86f", size = 253393, upload-time = "2026-02-03T14:01:40.118Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/f6/b1f48810ffc6accf49a35b9943636560768f0812330f7456aa87dc39aff5/coverage-7.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb25061a66802df9fc13a9ba1967d25faa4dae0418db469264fd9860a921dde4", size = 254740, upload-time = "2026-02-03T14:01:42.413Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/d0/e59c54f9be0b61808f6bc4c8c4346bd79f02dd6bbc3f476ef26124661f20/coverage-7.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:99fee45adbb1caeb914da16f70e557fb7ff6ddc9e4b14de665bd41af631367ef", size = 250905, upload-time = "2026-02-03T14:01:44.163Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/f7/5291bcdf498bafbee3796bb32ef6966e9915aebd4d0954123c8eae921c32/coverage-7.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:318002f1fd819bdc1651c619268aa5bc853c35fa5cc6d1e8c96bd9cd6c828b75", size = 252753, upload-time = "2026-02-03T14:01:45.974Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/a9/1dcafa918c281554dae6e10ece88c1add82db685be123e1b05c2056ff3fb/coverage-7.13.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:71295f2d1d170b9977dc386d46a7a1b7cbb30e5405492529b4c930113a33f895", size = 250716, upload-time = "2026-02-03T14:01:48.844Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/bb/4ea4eabcce8c4f6235df6e059fbc5db49107b24c4bdffc44aee81aeca5a8/coverage-7.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5b1ad2e0dc672625c44bc4fe34514602a9fd8b10d52ddc414dc585f74453516c", size = 250530, upload-time = "2026-02-03T14:01:50.793Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/31/4a6c9e6a71367e6f923b27b528448c37f4e959b7e4029330523014691007/coverage-7.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b2beb64c145593a50d90db5c7178f55daeae129123b0d265bdb3cbec83e5194a", size = 252186, upload-time = "2026-02-03T14:01:52.607Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/92/e1451ef6390a4f655dc42da35d9971212f7abbbcad0bdb7af4407897eb76/coverage-7.13.3-cp314-cp314-win32.whl", hash = "sha256:3d1aed4f4e837a832df2f3b4f68a690eede0de4560a2dbc214ea0bc55aabcdb4", size = 222253, upload-time = "2026-02-03T14:01:55.071Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/98/78885a861a88de020c32a2693487c37d15a9873372953f0c3c159d575a43/coverage-7.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9f9efbbaf79f935d5fbe3ad814825cbce4f6cdb3054384cb49f0c0f496125fa0", size = 223069, upload-time = "2026-02-03T14:01:56.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/fb/3784753a48da58a5337972abf7ca58b1fb0f1bda21bc7b4fae992fd28e47/coverage-7.13.3-cp314-cp314-win_arm64.whl", hash = "sha256:31b6e889c53d4e6687ca63706148049494aace140cffece1c4dc6acadb70a7b3", size = 221633, upload-time = "2026-02-03T14:01:58.758Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/f9/75b732d9674d32cdbffe801ed5f770786dd1c97eecedef2125b0d25102dc/coverage-7.13.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c5e9787cec750793a19a28df7edd85ac4e49d3fb91721afcdc3b86f6c08d9aa8", size = 220243, upload-time = "2026-02-03T14:02:01.109Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/7e/2868ec95de5a65703e6f0c87407ea822d1feb3619600fbc3c1c4fa986090/coverage-7.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e5b86db331c682fd0e4be7098e6acee5e8a293f824d41487c667a93705d415ca", size = 220515, upload-time = "2026-02-03T14:02:02.862Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/eb/9f0d349652fced20bcaea0f67fc5777bd097c92369f267975732f3dc5f45/coverage-7.13.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:edc7754932682d52cf6e7a71806e529ecd5ce660e630e8bd1d37109a2e5f63ba", size = 261874, upload-time = "2026-02-03T14:02:04.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/a5/6619bc4a6c7b139b16818149a3e74ab2e21599ff9a7b6811b6afde99f8ec/coverage-7.13.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3a16d6398666510a6886f67f43d9537bfd0e13aca299688a19daa84f543122f", size = 264004, upload-time = "2026-02-03T14:02:06.634Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/b7/90aa3fc645a50c6f07881fca4fd0ba21e3bfb6ce3a7078424ea3a35c74c9/coverage-7.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:303d38b19626c1981e1bb067a9928236d88eb0e4479b18a74812f05a82071508", size = 266408, upload-time = "2026-02-03T14:02:09.037Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/55/08bb2a1e4dcbae384e638f0effef486ba5987b06700e481691891427d879/coverage-7.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:284e06eadfe15ddfee2f4ee56631f164ef897a7d7d5a15bca5f0bb88889fc5ba", size = 260977, upload-time = "2026-02-03T14:02:11.755Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/76/8bd4ae055a42d8fb5dd2230e5cf36ff2e05f85f2427e91b11a27fea52ed7/coverage-7.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d401f0864a1d3198422816878e4e84ca89ec1c1bf166ecc0ae01380a39b888cd", size = 263868, upload-time = "2026-02-03T14:02:13.565Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/f9/ba000560f11e9e32ec03df5aa8477242c2d95b379c99ac9a7b2e7fbacb1a/coverage-7.13.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3f379b02c18a64de78c4ccdddf1c81c2c5ae1956c72dacb9133d7dd7809794ab", size = 261474, upload-time = "2026-02-03T14:02:16.069Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/4b/4de4de8f9ca7af4733bfcf4baa440121b7dbb3856daf8428ce91481ff63b/coverage-7.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:7a482f2da9086971efb12daca1d6547007ede3674ea06e16d7663414445c683e", size = 260317, upload-time = "2026-02-03T14:02:17.996Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/71/5cd8436e2c21410ff70be81f738c0dddea91bcc3189b1517d26e0102ccb3/coverage-7.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:562136b0d401992118d9b49fbee5454e16f95f85b120a4226a04d816e33fe024", size = 262635, upload-time = "2026-02-03T14:02:20.405Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/f8/2834bb45bdd70b55a33ec354b8b5f6062fc90e5bb787e14385903a979503/coverage-7.13.3-cp314-cp314t-win32.whl", hash = "sha256:ca46e5c3be3b195098dd88711890b8011a9fa4feca942292bb84714ce5eab5d3", size = 223035, upload-time = "2026-02-03T14:02:22.323Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/75/f8290f0073c00d9ae14056d2b84ab92dff21d5370e464cb6cb06f52bf580/coverage-7.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:06d316dbb3d9fd44cca05b2dbcfbef22948493d63a1f28e828d43e6cc505fed8", size = 224142, upload-time = "2026-02-03T14:02:24.143Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/01/43ac78dfea8946c4a9161bbc034b5549115cb2b56781a4b574927f0d141a/coverage-7.13.3-cp314-cp314t-win_arm64.whl", hash = "sha256:299d66e9218193f9dc6e4880629ed7c4cd23486005166247c283fb98531656c3", size = 222166, upload-time = "2026-02-03T14:02:26.005Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/fb/70af542d2d938c778c9373ce253aa4116dbe7c0a5672f78b2b2ae0e1b94b/coverage-7.13.3-py3-none-any.whl", hash = "sha256:90a8af9dba6429b2573199622d72e0ebf024d6276f16abce394ad4d181bb0910", size = 211237, upload-time = "2026-02-03T14:02:27.986Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/d4/7827d9ffa34d5d4d752eec907022aa417120936282fc488306f5da08c292/coverage-7.13.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fc31c787a84f8cd6027eba44010517020e0d18487064cd3d8968941856d1415", size = 219152, upload-time = "2026-02-09T12:56:11.974Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/b0/d69df26607c64043292644dbb9dc54b0856fabaa2cbb1eeee3331cc9e280/coverage-7.13.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a32ebc02a1805adf637fc8dec324b5cdacd2e493515424f70ee33799573d661b", size = 219667, upload-time = "2026-02-09T12:56:13.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/a4/c1523f7c9e47b2271dbf8c2a097e7a1f89ef0d66f5840bb59b7e8814157b/coverage-7.13.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e24f9156097ff9dc286f2f913df3a7f63c0e333dcafa3c196f2c18b4175ca09a", size = 246425, upload-time = "2026-02-09T12:56:14.552Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/02/aa7ec01d1a5023c4b680ab7257f9bfde9defe8fdddfe40be096ac19e8177/coverage-7.13.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8041b6c5bfdc03257666e9881d33b1abc88daccaf73f7b6340fb7946655cd10f", size = 248229, upload-time = "2026-02-09T12:56:16.31Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/98/85aba0aed5126d896162087ef3f0e789a225697245256fc6181b95f47207/coverage-7.13.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a09cfa6a5862bc2fc6ca7c3def5b2926194a56b8ab78ffcf617d28911123012", size = 250106, upload-time = "2026-02-09T12:56:18.024Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/72/1db59bd67494bc162e3e4cd5fbc7edba2c7026b22f7c8ef1496d58c2b94c/coverage-7.13.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:296f8b0af861d3970c2a4d8c91d48eb4dd4771bcef9baedec6a9b515d7de3def", size = 252021, upload-time = "2026-02-09T12:56:19.272Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/97/72899c59c7066961de6e3daa142d459d47d104956db43e057e034f015c8a/coverage-7.13.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e101609bcbbfb04605ea1027b10dc3735c094d12d40826a60f897b98b1c30256", size = 247114, upload-time = "2026-02-09T12:56:21.051Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/1f/f1885573b5970235e908da4389176936c8933e86cb316b9620aab1585fa2/coverage-7.13.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aa3feb8db2e87ff5e6d00d7e1480ae241876286691265657b500886c98f38bda", size = 248143, upload-time = "2026-02-09T12:56:22.585Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/cf/e80390c5b7480b722fa3e994f8202807799b85bc562aa4f1dde209fbb7be/coverage-7.13.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4fc7fa81bbaf5a02801b65346c8b3e657f1d93763e58c0abdf7c992addd81a92", size = 246152, upload-time = "2026-02-09T12:56:23.748Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/bf/f89a8350d85572f95412debb0fb9bb4795b1d5b5232bd652923c759e787b/coverage-7.13.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:33901f604424145c6e9c2398684b92e176c0b12df77d52db81c20abd48c3794c", size = 249959, upload-time = "2026-02-09T12:56:25.209Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/6e/612a02aece8178c818df273e8d1642190c4875402ca2ba74514394b27aba/coverage-7.13.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:bb28c0f2cf2782508a40cec377935829d5fcc3ad9a3681375af4e84eb34b6b58", size = 246416, upload-time = "2026-02-09T12:56:26.475Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/98/b5afc39af67c2fa6786b03c3a7091fc300947387ce8914b096db8a73d67a/coverage-7.13.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d107aff57a83222ddbd8d9ee705ede2af2cc926608b57abed8ef96b50b7e8f9", size = 247025, upload-time = "2026-02-09T12:56:27.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/30/2bba8ef0682d5bd210c38fe497e12a06c9f8d663f7025e9f5c2c31ce847d/coverage-7.13.4-cp310-cp310-win32.whl", hash = "sha256:a6f94a7d00eb18f1b6d403c91a88fd58cfc92d4b16080dfdb774afc8294469bf", size = 221758, upload-time = "2026-02-09T12:56:29.051Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/13/331f94934cf6c092b8ea59ff868eb587bc8fe0893f02c55bc6c0183a192e/coverage-7.13.4-cp310-cp310-win_amd64.whl", hash = "sha256:2cb0f1e000ebc419632bbe04366a8990b6e32c4e0b51543a6484ffe15eaeda95", size = 222693, upload-time = "2026-02-09T12:56:30.366Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/ad/b59e5b451cf7172b8d1043dc0fa718f23aab379bc1521ee13d4bd9bfa960/coverage-7.13.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d490ba50c3f35dd7c17953c68f3270e7ccd1c6642e2d2afe2d8e720b98f5a053", size = 219278, upload-time = "2026-02-09T12:56:31.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/17/0cb7ca3de72e5f4ef2ec2fa0089beafbcaaaead1844e8b8a63d35173d77d/coverage-7.13.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:19bc3c88078789f8ef36acb014d7241961dbf883fd2533d18cb1e7a5b4e28b11", size = 219783, upload-time = "2026-02-09T12:56:33.104Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/63/325d8e5b11e0eaf6d0f6a44fad444ae58820929a9b0de943fa377fe73e85/coverage-7.13.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3998e5a32e62fdf410c0dbd3115df86297995d6e3429af80b8798aad894ca7aa", size = 250200, upload-time = "2026-02-09T12:56:34.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/53/c16972708cbb79f2942922571a687c52bd109a7bd51175aeb7558dff2236/coverage-7.13.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8e264226ec98e01a8e1054314af91ee6cde0eacac4f465cc93b03dbe0bce2fd7", size = 252114, upload-time = "2026-02-09T12:56:35.749Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/c2/7ab36d8b8cc412bec9ea2d07c83c48930eb4ba649634ba00cb7e4e0f9017/coverage-7.13.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3aa4e7b9e416774b21797365b358a6e827ffadaaca81b69ee02946852449f00", size = 254220, upload-time = "2026-02-09T12:56:37.796Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/4d/cf52c9a3322c89a0e6febdfbc83bb45c0ed3c64ad14081b9503adee702e7/coverage-7.13.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:71ca20079dd8f27fcf808817e281e90220475cd75115162218d0e27549f95fef", size = 256164, upload-time = "2026-02-09T12:56:39.016Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/e9/eb1dd17bd6de8289df3580e967e78294f352a5df8a57ff4671ee5fc3dcd0/coverage-7.13.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e2f25215f1a359ab17320b47bcdaca3e6e6356652e8256f2441e4ef972052903", size = 250325, upload-time = "2026-02-09T12:56:40.668Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/07/8c1542aa873728f72267c07278c5cc0ec91356daf974df21335ccdb46368/coverage-7.13.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d65b2d373032411e86960604dc4edac91fdfb5dca539461cf2cbe78327d1e64f", size = 251913, upload-time = "2026-02-09T12:56:41.97Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/d7/c62e2c5e4483a748e27868e4c32ad3daa9bdddbba58e1bc7a15e252baa74/coverage-7.13.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94eb63f9b363180aff17de3e7c8760c3ba94664ea2695c52f10111244d16a299", size = 249974, upload-time = "2026-02-09T12:56:43.323Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/9f/4c5c015a6e98ced54efd0f5cf8d31b88e5504ecb6857585fc0161bb1e600/coverage-7.13.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e856bf6616714c3a9fbc270ab54103f4e685ba236fa98c054e8f87f266c93505", size = 253741, upload-time = "2026-02-09T12:56:45.155Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/59/0f4eef89b9f0fcd9633b5d350016f54126ab49426a70ff4c4e87446cabdc/coverage-7.13.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:65dfcbe305c3dfe658492df2d85259e0d79ead4177f9ae724b6fb245198f55d6", size = 249695, upload-time = "2026-02-09T12:56:46.636Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/2c/b7476f938deb07166f3eb281a385c262675d688ff4659ad56c6c6b8e2e70/coverage-7.13.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b507778ae8a4c915436ed5c2e05b4a6cecfa70f734e19c22a005152a11c7b6a9", size = 250599, upload-time = "2026-02-09T12:56:48.13Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/34/c3420709d9846ee3785b9f2831b4d94f276f38884032dca1457fa83f7476/coverage-7.13.4-cp311-cp311-win32.whl", hash = "sha256:784fc3cf8be001197b652d51d3fd259b1e2262888693a4636e18879f613a62a9", size = 221780, upload-time = "2026-02-09T12:56:50.479Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/08/3d9c8613079d2b11c185b865de9a4c1a68850cfda2b357fae365cf609f29/coverage-7.13.4-cp311-cp311-win_amd64.whl", hash = "sha256:2421d591f8ca05b308cf0092807308b2facbefe54af7c02ac22548b88b95c98f", size = 222715, upload-time = "2026-02-09T12:56:51.815Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/1a/54c3c80b2f056164cc0a6cdcb040733760c7c4be9d780fe655f356f433e4/coverage-7.13.4-cp311-cp311-win_arm64.whl", hash = "sha256:79e73a76b854d9c6088fe5d8b2ebe745f8681c55f7397c3c0a016192d681045f", size = 221385, upload-time = "2026-02-09T12:56:53.194Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/81/4ce2fdd909c5a0ed1f6dedb88aa57ab79b6d1fbd9b588c1ac7ef45659566/coverage-7.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:02231499b08dabbe2b96612993e5fc34217cdae907a51b906ac7fca8027a4459", size = 219449, upload-time = "2026-02-09T12:56:54.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/96/5238b1efc5922ddbdc9b0db9243152c09777804fb7c02ad1741eb18a11c0/coverage-7.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40aa8808140e55dc022b15d8aa7f651b6b3d68b365ea0398f1441e0b04d859c3", size = 219810, upload-time = "2026-02-09T12:56:56.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/72/2f372b726d433c9c35e56377cf1d513b4c16fe51841060d826b95caacec1/coverage-7.13.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5b856a8ccf749480024ff3bd7310adaef57bf31fd17e1bfc404b7940b6986634", size = 251308, upload-time = "2026-02-09T12:56:57.858Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/a0/2ea570925524ef4e00bb6c82649f5682a77fac5ab910a65c9284de422600/coverage-7.13.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c048ea43875fbf8b45d476ad79f179809c590ec7b79e2035c662e7afa3192e3", size = 254052, upload-time = "2026-02-09T12:56:59.754Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/ac/45dc2e19a1939098d783c846e130b8f862fbb50d09e0af663988f2f21973/coverage-7.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b7b38448866e83176e28086674fe7368ab8590e4610fb662b44e345b86d63ffa", size = 255165, upload-time = "2026-02-09T12:57:01.287Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/4d/26d236ff35abc3b5e63540d3386e4c3b192168c1d96da5cb2f43c640970f/coverage-7.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:de6defc1c9badbf8b9e67ae90fd00519186d6ab64e5cc5f3d21359c2a9b2c1d3", size = 257432, upload-time = "2026-02-09T12:57:02.637Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/55/14a966c757d1348b2e19caf699415a2a4c4f7feaa4bbc6326a51f5c7dd1b/coverage-7.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7eda778067ad7ffccd23ecffce537dface96212576a07924cbf0d8799d2ded5a", size = 251716, upload-time = "2026-02-09T12:57:04.056Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/33/50116647905837c66d28b2af1321b845d5f5d19be9655cb84d4a0ea806b4/coverage-7.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e87f6c587c3f34356c3759f0420693e35e7eb0e2e41e4c011cb6ec6ecbbf1db7", size = 253089, upload-time = "2026-02-09T12:57:05.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/b4/8efb11a46e3665d92635a56e4f2d4529de6d33f2cb38afd47d779d15fc99/coverage-7.13.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8248977c2e33aecb2ced42fef99f2d319e9904a36e55a8a68b69207fb7e43edc", size = 251232, upload-time = "2026-02-09T12:57:06.879Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/24/8cd73dd399b812cc76bb0ac260e671c4163093441847ffe058ac9fda1e32/coverage-7.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:25381386e80ae727608e662474db537d4df1ecd42379b5ba33c84633a2b36d47", size = 255299, upload-time = "2026-02-09T12:57:08.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/94/0a4b12f1d0e029ce1ccc1c800944a9984cbe7d678e470bb6d3c6bc38a0da/coverage-7.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ee756f00726693e5ba94d6df2bdfd64d4852d23b09bb0bc700e3b30e6f333985", size = 250796, upload-time = "2026-02-09T12:57:10.142Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/44/6002fbf88f6698ca034360ce474c406be6d5a985b3fdb3401128031eef6b/coverage-7.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fdfc1e28e7c7cdce44985b3043bc13bbd9c747520f94a4d7164af8260b3d91f0", size = 252673, upload-time = "2026-02-09T12:57:12.197Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/c6/a0279f7c00e786be75a749a5674e6fa267bcbd8209cd10c9a450c655dfa7/coverage-7.13.4-cp312-cp312-win32.whl", hash = "sha256:01d4cbc3c283a17fc1e42d614a119f7f438eabb593391283adca8dc86eff1246", size = 221990, upload-time = "2026-02-09T12:57:14.085Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/4e/c0a25a425fcf5557d9abd18419c95b63922e897bc86c1f327f155ef234a9/coverage-7.13.4-cp312-cp312-win_amd64.whl", hash = "sha256:9401ebc7ef522f01d01d45532c68c5ac40fb27113019b6b7d8b208f6e9baa126", size = 222800, upload-time = "2026-02-09T12:57:15.944Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/ac/92da44ad9a6f4e3a7debd178949d6f3769bedca33830ce9b1dcdab589a37/coverage-7.13.4-cp312-cp312-win_arm64.whl", hash = "sha256:b1ec7b6b6e93255f952e27ab58fbc68dcc468844b16ecbee881aeb29b6ab4d8d", size = 221415, upload-time = "2026-02-09T12:57:17.497Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/23/aad45061a31677d68e47499197a131eea55da4875d16c1f42021ab963503/coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9", size = 219474, upload-time = "2026-02-09T12:57:19.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/70/9b8b67a0945f3dfec1fd896c5cefb7c19d5a3a6d74630b99a895170999ae/coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac", size = 219844, upload-time = "2026-02-09T12:57:20.66Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/fd/7e859f8fab324cef6c4ad7cff156ca7c489fef9179d5749b0c8d321281c2/coverage-7.13.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93550784d9281e374fb5a12bf1324cc8a963fd63b2d2f223503ef0fd4aa339ea", size = 250832, upload-time = "2026-02-09T12:57:22.007Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/dc/b2442d10020c2f52617828862d8b6ee337859cd8f3a1f13d607dddda9cf7/coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b", size = 253434, upload-time = "2026-02-09T12:57:23.339Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/88/6728a7ad17428b18d836540630487231f5470fb82454871149502f5e5aa2/coverage-7.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b322db1284a2ed3aa28ffd8ebe3db91c929b7a333c0820abec3d838ef5b3525", size = 254676, upload-time = "2026-02-09T12:57:24.774Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/bc/21244b1b8cedf0dff0a2b53b208015fe798d5f2a8d5348dbfece04224fff/coverage-7.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4594c67d8a7c89cf922d9df0438c7c7bb022ad506eddb0fdb2863359ff78242", size = 256807, upload-time = "2026-02-09T12:57:26.125Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/a0/ddba7ed3251cff51006737a727d84e05b61517d1784a9988a846ba508877/coverage-7.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53d133df809c743eb8bce33b24bcababb371f4441340578cd406e084d94a6148", size = 251058, upload-time = "2026-02-09T12:57:27.614Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/55/e289addf7ff54d3a540526f33751951bf0878f3809b47f6dfb3def69c6f7/coverage-7.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76451d1978b95ba6507a039090ba076105c87cc76fc3efd5d35d72093964d49a", size = 252805, upload-time = "2026-02-09T12:57:29.066Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/4e/cc276b1fa4a59be56d96f1dabddbdc30f4ba22e3b1cd42504c37b3313255/coverage-7.13.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f57b33491e281e962021de110b451ab8a24182589be17e12a22c79047935e23", size = 250766, upload-time = "2026-02-09T12:57:30.522Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/44/1093b8f93018f8b41a8cf29636c9292502f05e4a113d4d107d14a3acd044/coverage-7.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1731dc33dc276dafc410a885cbf5992f1ff171393e48a21453b78727d090de80", size = 254923, upload-time = "2026-02-09T12:57:31.946Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/55/ea2796da2d42257f37dbea1aab239ba9263b31bd91d5527cdd6db5efe174/coverage-7.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:bd60d4fe2f6fa7dff9223ca1bbc9f05d2b6697bc5961072e5d3b952d46e1b1ea", size = 250591, upload-time = "2026-02-09T12:57:33.842Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/fa/7c4bb72aacf8af5020675aa633e59c1fbe296d22aed191b6a5b711eb2bc7/coverage-7.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9181a3ccead280b828fae232df12b16652702b49d41e99d657f46cc7b1f6ec7a", size = 252364, upload-time = "2026-02-09T12:57:35.743Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/38/a8d2ec0146479c20bbaa7181b5b455a0c41101eed57f10dd19a78ab44c80/coverage-7.13.4-cp313-cp313-win32.whl", hash = "sha256:f53d492307962561ac7de4cd1de3e363589b000ab69617c6156a16ba7237998d", size = 222010, upload-time = "2026-02-09T12:57:37.25Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/0c/dbfafbe90a185943dcfbc766fe0e1909f658811492d79b741523a414a6cc/coverage-7.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:e6f70dec1cc557e52df5306d051ef56003f74d56e9c4dd7ddb07e07ef32a84dd", size = 222818, upload-time = "2026-02-09T12:57:38.734Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/d1/934918a138c932c90d78301f45f677fb05c39a3112b96fd2c8e60503cdc7/coverage-7.13.4-cp313-cp313-win_arm64.whl", hash = "sha256:fb07dc5da7e849e2ad31a5d74e9bece81f30ecf5a42909d0a695f8bd1874d6af", size = 221438, upload-time = "2026-02-09T12:57:40.223Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/57/ee93ced533bcb3e6df961c0c6e42da2fc6addae53fb95b94a89b1e33ebd7/coverage-7.13.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40d74da8e6c4b9ac18b15331c4b5ebc35a17069410cad462ad4f40dcd2d50c0d", size = 220165, upload-time = "2026-02-09T12:57:41.639Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/e0/969fc285a6fbdda49d91af278488d904dcd7651b2693872f0ff94e40e84a/coverage-7.13.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4223b4230a376138939a9173f1bdd6521994f2aff8047fae100d6d94d50c5a12", size = 220516, upload-time = "2026-02-09T12:57:44.215Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/b8/9531944e16267e2735a30a9641ff49671f07e8138ecf1ca13db9fd2560c7/coverage-7.13.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1d4be36a5114c499f9f1f9195e95ebf979460dbe2d88e6816ea202010ba1c34b", size = 261804, upload-time = "2026-02-09T12:57:45.989Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/f3/e63df6d500314a2a60390d1989240d5f27318a7a68fa30ad3806e2a9323e/coverage-7.13.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:200dea7d1e8095cc6e98cdabe3fd1d21ab17d3cee6dab00cadbb2fe35d9c15b9", size = 263885, upload-time = "2026-02-09T12:57:47.42Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/67/7654810de580e14b37670b60a09c599fa348e48312db5b216d730857ffe6/coverage-7.13.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8eb931ee8e6d8243e253e5ed7336deea6904369d2fd8ae6e43f68abbf167092", size = 266308, upload-time = "2026-02-09T12:57:49.345Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/6f/39d41eca0eab3cc82115953ad41c4e77935286c930e8fad15eaed1389d83/coverage-7.13.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75eab1ebe4f2f64d9509b984f9314d4aa788540368218b858dad56dc8f3e5eb9", size = 267452, upload-time = "2026-02-09T12:57:50.811Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/6d/39c0fbb8fc5cd4d2090811e553c2108cf5112e882f82505ee7495349a6bf/coverage-7.13.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c35eb28c1d085eb7d8c9b3296567a1bebe03ce72962e932431b9a61f28facf26", size = 261057, upload-time = "2026-02-09T12:57:52.447Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/a2/60010c669df5fa603bb5a97fb75407e191a846510da70ac657eb696b7fce/coverage-7.13.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb88b316ec33760714a4720feb2816a3a59180fd58c1985012054fa7aebee4c2", size = 263875, upload-time = "2026-02-09T12:57:53.938Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/d9/63b22a6bdbd17f1f96e9ed58604c2a6b0e72a9133e37d663bef185877cf6/coverage-7.13.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d41eead3cc673cbd38a4417deb7fd0b4ca26954ff7dc6078e33f6ff97bed940", size = 261500, upload-time = "2026-02-09T12:57:56.012Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/bf/69f86ba1ad85bc3ad240e4c0e57a2e620fbc0e1645a47b5c62f0e941ad7f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:fb26a934946a6afe0e326aebe0730cdff393a8bc0bbb65a2f41e30feddca399c", size = 265212, upload-time = "2026-02-09T12:57:57.5Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/f2/5f65a278a8c2148731831574c73e42f57204243d33bedaaf18fa79c5958f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:dae88bc0fc77edaa65c14be099bd57ee140cf507e6bfdeea7938457ab387efb0", size = 260398, upload-time = "2026-02-09T12:57:59.027Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/80/6e8280a350ee9fea92f14b8357448a242dcaa243cb2c72ab0ca591f66c8c/coverage-7.13.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:845f352911777a8e722bfce168958214951e07e47e5d5d9744109fa5fe77f79b", size = 262584, upload-time = "2026-02-09T12:58:01.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/63/01ff182fc95f260b539590fb12c11ad3e21332c15f9799cb5e2386f71d9f/coverage-7.13.4-cp313-cp313t-win32.whl", hash = "sha256:2fa8d5f8de70688a28240de9e139fa16b153cc3cbb01c5f16d88d6505ebdadf9", size = 222688, upload-time = "2026-02-09T12:58:02.736Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/43/89de4ef5d3cd53b886afa114065f7e9d3707bdb3e5efae13535b46ae483d/coverage-7.13.4-cp313-cp313t-win_amd64.whl", hash = "sha256:9351229c8c8407645840edcc277f4a2d44814d1bc34a2128c11c2a031d45a5dd", size = 223746, upload-time = "2026-02-09T12:58:05.362Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/39/7cf0aa9a10d470a5309b38b289b9bb07ddeac5d61af9b664fe9775a4cb3e/coverage-7.13.4-cp313-cp313t-win_arm64.whl", hash = "sha256:30b8d0512f2dc8c8747557e8fb459d6176a2c9e5731e2b74d311c03b78451997", size = 222003, upload-time = "2026-02-09T12:58:06.952Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/11/a9cf762bb83386467737d32187756a42094927150c3e107df4cb078e8590/coverage-7.13.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:300deaee342f90696ed186e3a00c71b5b3d27bffe9e827677954f4ee56969601", size = 219522, upload-time = "2026-02-09T12:58:08.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/28/56e6d892b7b052236d67c95f1936b6a7cf7c3e2634bf27610b8cbd7f9c60/coverage-7.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29e3220258d682b6226a9b0925bc563ed9a1ebcff3cad30f043eceea7eaf2689", size = 219855, upload-time = "2026-02-09T12:58:10.176Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/69/233459ee9eb0c0d10fcc2fe425a029b3fa5ce0f040c966ebce851d030c70/coverage-7.13.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:391ee8f19bef69210978363ca930f7328081c6a0152f1166c91f0b5fdd2a773c", size = 250887, upload-time = "2026-02-09T12:58:12.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/90/2cdab0974b9b5bbc1623f7876b73603aecac11b8d95b85b5b86b32de5eab/coverage-7.13.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0dd7ab8278f0d58a0128ba2fca25824321f05d059c1441800e934ff2efa52129", size = 253396, upload-time = "2026-02-09T12:58:14.615Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/15/ea4da0f85bf7d7b27635039e649e99deb8173fe551096ea15017f7053537/coverage-7.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78cdf0d578b15148b009ccf18c686aa4f719d887e76e6b40c38ffb61d264a552", size = 254745, upload-time = "2026-02-09T12:58:16.162Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/11/bb356e86920c655ca4d61daee4e2bbc7258f0a37de0be32d233b561134ff/coverage-7.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:48685fee12c2eb3b27c62f2658e7ea21e9c3239cba5a8a242801a0a3f6a8c62a", size = 257055, upload-time = "2026-02-09T12:58:17.892Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/0f/9ae1f8cb17029e09da06ca4e28c9e1d5c1c0a511c7074592e37e0836c915/coverage-7.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4e83efc079eb39480e6346a15a1bcb3e9b04759c5202d157e1dd4303cd619356", size = 250911, upload-time = "2026-02-09T12:58:19.495Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/3a/adfb68558fa815cbc29747b553bc833d2150228f251b127f1ce97e48547c/coverage-7.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecae9737b72408d6a950f7e525f30aca12d4bd8dd95e37342e5beb3a2a8c4f71", size = 252754, upload-time = "2026-02-09T12:58:21.064Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/b1/540d0c27c4e748bd3cd0bd001076ee416eda993c2bae47a73b7cc9357931/coverage-7.13.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ae4578f8528569d3cf303fef2ea569c7f4c4059a38c8667ccef15c6e1f118aa5", size = 250720, upload-time = "2026-02-09T12:58:22.622Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/95/383609462b3ffb1fe133014a7c84fc0dd01ed55ac6140fa1093b5af7ebb1/coverage-7.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6fdef321fdfbb30a197efa02d48fcd9981f0d8ad2ae8903ac318adc653f5df98", size = 254994, upload-time = "2026-02-09T12:58:24.548Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/ba/1761138e86c81680bfc3c49579d66312865457f9fe405b033184e5793cb3/coverage-7.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b0f6ccf3dbe577170bebfce1318707d0e8c3650003cb4b3a9dd744575daa8b5", size = 250531, upload-time = "2026-02-09T12:58:26.271Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/8e/05900df797a9c11837ab59c4d6fe94094e029582aab75c3309a93e6fb4e3/coverage-7.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75fcd519f2a5765db3f0e391eb3b7d150cce1a771bf4c9f861aeab86c767a3c0", size = 252189, upload-time = "2026-02-09T12:58:27.807Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/bd/29c9f2db9ea4ed2738b8a9508c35626eb205d51af4ab7bf56a21a2e49926/coverage-7.13.4-cp314-cp314-win32.whl", hash = "sha256:8e798c266c378da2bd819b0677df41ab46d78065fb2a399558f3f6cae78b2fbb", size = 222258, upload-time = "2026-02-09T12:58:29.441Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/4d/1f8e723f6829977410efeb88f73673d794075091c8c7c18848d273dc9d73/coverage-7.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:245e37f664d89861cf2329c9afa2c1fe9e6d4e1a09d872c947e70718aeeac505", size = 223073, upload-time = "2026-02-09T12:58:31.026Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/5b/84100025be913b44e082ea32abcf1afbf4e872f5120b7a1cab1d331b1e13/coverage-7.13.4-cp314-cp314-win_arm64.whl", hash = "sha256:ad27098a189e5838900ce4c2a99f2fe42a0bf0c2093c17c69b45a71579e8d4a2", size = 221638, upload-time = "2026-02-09T12:58:32.599Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/e4/c884a405d6ead1370433dad1e3720216b4f9fd8ef5b64bfd984a2a60a11a/coverage-7.13.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:85480adfb35ffc32d40918aad81b89c69c9cc5661a9b8a81476d3e645321a056", size = 220246, upload-time = "2026-02-09T12:58:34.181Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/5c/4d7ed8b23b233b0fffbc9dfec53c232be2e695468523242ea9fd30f97ad2/coverage-7.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:79be69cf7f3bf9b0deeeb062eab7ac7f36cd4cc4c4dd694bd28921ba4d8596cc", size = 220514, upload-time = "2026-02-09T12:58:35.704Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/6f/3284d4203fd2f28edd73034968398cd2d4cb04ab192abc8cff007ea35679/coverage-7.13.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:caa421e2684e382c5d8973ac55e4f36bed6821a9bad5c953494de960c74595c9", size = 261877, upload-time = "2026-02-09T12:58:37.864Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/aa/b672a647bbe1556a85337dc95bfd40d146e9965ead9cc2fe81bde1e5cbce/coverage-7.13.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14375934243ee05f56c45393fe2ce81fe5cc503c07cee2bdf1725fb8bef3ffaf", size = 264004, upload-time = "2026-02-09T12:58:39.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/a1/aa384dbe9181f98bba87dd23dda436f0c6cf2e148aecbb4e50fc51c1a656/coverage-7.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25a41c3104d08edb094d9db0d905ca54d0cd41c928bb6be3c4c799a54753af55", size = 266408, upload-time = "2026-02-09T12:58:41.852Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/5e/5150bf17b4019bc600799f376bb9606941e55bd5a775dc1e096b6ffea952/coverage-7.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f01afcff62bf9a08fb32b2c1d6e924236c0383c02c790732b6537269e466a72", size = 267544, upload-time = "2026-02-09T12:58:44.093Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/ed/f1de5c675987a4a7a672250d2c5c9d73d289dbf13410f00ed7181d8017dd/coverage-7.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eb9078108fbf0bcdde37c3f4779303673c2fa1fe8f7956e68d447d0dd426d38a", size = 260980, upload-time = "2026-02-09T12:58:45.721Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/e3/fe758d01850aa172419a6743fe76ba8b92c29d181d4f676ffe2dae2ba631/coverage-7.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e086334e8537ddd17e5f16a344777c1ab8194986ec533711cbe6c41cde841b6", size = 263871, upload-time = "2026-02-09T12:58:47.334Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/76/b829869d464115e22499541def9796b25312b8cf235d3bb00b39f1675395/coverage-7.13.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:725d985c5ab621268b2edb8e50dfe57633dc69bda071abc470fed55a14935fd3", size = 261472, upload-time = "2026-02-09T12:58:48.995Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/9e/caedb1679e73e2f6ad240173f55218488bfe043e38da577c4ec977489915/coverage-7.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3c06f0f1337c667b971ca2f975523347e63ec5e500b9aa5882d91931cd3ef750", size = 265210, upload-time = "2026-02-09T12:58:51.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/10/0dd02cb009b16ede425b49ec344aba13a6ae1dc39600840ea6abcb085ac4/coverage-7.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:590c0ed4bf8e85f745e6b805b2e1c457b2e33d5255dd9729743165253bc9ad39", size = 260319, upload-time = "2026-02-09T12:58:53.081Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/8e/234d2c927af27c6d7a5ffad5bd2cf31634c46a477b4c7adfbfa66baf7ebb/coverage-7.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eb30bf180de3f632cd043322dad5751390e5385108b2807368997d1a92a509d0", size = 262638, upload-time = "2026-02-09T12:58:55.258Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/64/e5547c8ff6964e5965c35a480855911b61509cce544f4d442caa759a0702/coverage-7.13.4-cp314-cp314t-win32.whl", hash = "sha256:c4240e7eded42d131a2d2c4dec70374b781b043ddc79a9de4d55ca71f8e98aea", size = 223040, upload-time = "2026-02-09T12:58:56.936Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/96/38086d58a181aac86d503dfa9c47eb20715a79c3e3acbdf786e92e5c09a8/coverage-7.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4c7d3cc01e7350f2f0f6f7036caaf5673fb56b6998889ccfe9e1c1fe75a9c932", size = 224148, upload-time = "2026-02-09T12:58:58.645Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/72/8d10abd3740a0beb98c305e0c3faf454366221c0f37a8bcf8f60020bb65a/coverage-7.13.4-cp314-cp314t-win_arm64.whl", hash = "sha256:23e3f687cf945070d1c90f85db66d11e3025665d8dafa831301a0e0038f3db9b", size = 222172, upload-time = "2026-02-09T12:59:00.396Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/4a/331fe2caf6799d591109bb9c08083080f6de90a823695d412a935622abb2/coverage-7.13.4-py3-none-any.whl", hash = "sha256:1af1641e57cf7ba1bd67d677c9abdbcd6cc2ab7da3bca7fa1e2b7e50e65f2ad0", size = 211242, upload-time = "2026-02-09T12:59:02.032Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1018,7 +1032,7 @@ name = "exceptiongroup"
|
|||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
||||
wheels = [
|
||||
|
|
@ -1094,7 +1108,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "fsspec"
|
||||
version = "2026.1.0"
|
||||
version = "2026.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.14' and sys_platform == 'win32'",
|
||||
|
|
@ -1111,9 +1125,9 @@ resolution-markers = [
|
|||
"python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
|
||||
"python_full_version == '3.10.*'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d5/7d/5df2650c57d47c57232af5ef4b4fdbff182070421e405e0d62c6cdbfaa87/fsspec-2026.1.0.tar.gz", hash = "sha256:e987cb0496a0d81bba3a9d1cee62922fb395e7d4c3b575e57f547953334fe07b", size = 310496, upload-time = "2026-01-09T15:21:35.562Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/01/c9/97cc5aae1648dcb851958a3ddf73ccd7dbe5650d95203ecb4d7720b4cdbf/fsspec-2026.1.0-py3-none-any.whl", hash = "sha256:cb76aa913c2285a3b49bdd5fc55b1d7c708d7208126b60f2eb8194fe1b4cbdcc", size = 201838, upload-time = "2026-01-09T15:21:34.041Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1164,73 +1178,73 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "grpcio"
|
||||
version = "1.76.0"
|
||||
version = "1.78.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b6/e0/318c1ce3ae5a17894d5791e87aea147587c9e702f24122cc7a5c8bbaeeb1/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73", size = 12785182, upload-time = "2025-10-21T16:23:12.106Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/8a/3d098f35c143a89520e568e6539cc098fcd294495910e359889ce8741c84/grpcio-1.78.0.tar.gz", hash = "sha256:7382b95189546f375c174f53a5fa873cef91c4b8005faa05cc5b3beea9c4f1c5", size = 12852416, upload-time = "2026-02-06T09:57:18.093Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/17/ff4795dc9a34b6aee6ec379f1b66438a3789cd1315aac0cbab60d92f74b3/grpcio-1.76.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:65a20de41e85648e00305c1bb09a3598f840422e522277641145a32d42dcefcc", size = 5840037, upload-time = "2025-10-21T16:20:25.069Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/ff/35f9b96e3fa2f12e1dcd58a4513a2e2294a001d64dec81677361b7040c9a/grpcio-1.76.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:40ad3afe81676fd9ec6d9d406eda00933f218038433980aa19d401490e46ecde", size = 11836482, upload-time = "2025-10-21T16:20:30.113Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/1c/8374990f9545e99462caacea5413ed783014b3b66ace49e35c533f07507b/grpcio-1.76.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:035d90bc79eaa4bed83f524331d55e35820725c9fbb00ffa1904d5550ed7ede3", size = 6407178, upload-time = "2025-10-21T16:20:32.733Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/77/36fd7d7c75a6c12542c90a6d647a27935a1ecaad03e0ffdb7c42db6b04d2/grpcio-1.76.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4215d3a102bd95e2e11b5395c78562967959824156af11fa93d18fdd18050990", size = 7075684, upload-time = "2025-10-21T16:20:35.435Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/f7/e3cdb252492278e004722306c5a8935eae91e64ea11f0af3437a7de2e2b7/grpcio-1.76.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:49ce47231818806067aea3324d4bf13825b658ad662d3b25fada0bdad9b8a6af", size = 6611133, upload-time = "2025-10-21T16:20:37.541Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/20/340db7af162ccd20a0893b5f3c4a5d676af7b71105517e62279b5b61d95a/grpcio-1.76.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8cc3309d8e08fd79089e13ed4819d0af72aa935dd8f435a195fd152796752ff2", size = 7195507, upload-time = "2025-10-21T16:20:39.643Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/f0/b2160addc1487bd8fa4810857a27132fb4ce35c1b330c2f3ac45d697b106/grpcio-1.76.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:971fd5a1d6e62e00d945423a567e42eb1fa678ba89072832185ca836a94daaa6", size = 8160651, upload-time = "2025-10-21T16:20:42.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/2c/ac6f98aa113c6ef111b3f347854e99ebb7fb9d8f7bb3af1491d438f62af4/grpcio-1.76.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9d9adda641db7207e800a7f089068f6f645959f2df27e870ee81d44701dd9db3", size = 7620568, upload-time = "2025-10-21T16:20:45.995Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/84/7852f7e087285e3ac17a2703bc4129fafee52d77c6c82af97d905566857e/grpcio-1.76.0-cp310-cp310-win32.whl", hash = "sha256:063065249d9e7e0782d03d2bca50787f53bd0fb89a67de9a7b521c4a01f1989b", size = 3998879, upload-time = "2025-10-21T16:20:48.592Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/30/d3d2adcbb6dd3ff59d6ac3df6ef830e02b437fb5c90990429fd180e52f30/grpcio-1.76.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6ae758eb08088d36812dd5d9af7a9859c05b1e0f714470ea243694b49278e7b", size = 4706892, upload-time = "2025-10-21T16:20:50.697Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/00/8163a1beeb6971f66b4bbe6ac9457b97948beba8dd2fc8e1281dce7f79ec/grpcio-1.76.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2e1743fbd7f5fa713a1b0a8ac8ebabf0ec980b5d8809ec358d488e273b9cf02a", size = 5843567, upload-time = "2025-10-21T16:20:52.829Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/c1/934202f5cf335e6d852530ce14ddb0fef21be612ba9ecbbcbd4d748ca32d/grpcio-1.76.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:a8c2cf1209497cf659a667d7dea88985e834c24b7c3b605e6254cbb5076d985c", size = 11848017, upload-time = "2025-10-21T16:20:56.705Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/0b/8dec16b1863d74af6eb3543928600ec2195af49ca58b16334972f6775663/grpcio-1.76.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:08caea849a9d3c71a542827d6df9d5a69067b0a1efbea8a855633ff5d9571465", size = 6412027, upload-time = "2025-10-21T16:20:59.3Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/64/7b9e6e7ab910bea9d46f2c090380bab274a0b91fb0a2fe9b0cd399fffa12/grpcio-1.76.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f0e34c2079d47ae9f6188211db9e777c619a21d4faba6977774e8fa43b085e48", size = 7075913, upload-time = "2025-10-21T16:21:01.645Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/86/093c46e9546073cefa789bd76d44c5cb2abc824ca62af0c18be590ff13ba/grpcio-1.76.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8843114c0cfce61b40ad48df65abcfc00d4dba82eae8718fab5352390848c5da", size = 6615417, upload-time = "2025-10-21T16:21:03.844Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/b6/5709a3a68500a9c03da6fb71740dcdd5ef245e39266461a03f31a57036d8/grpcio-1.76.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8eddfb4d203a237da6f3cc8a540dad0517d274b5a1e9e636fd8d2c79b5c1d397", size = 7199683, upload-time = "2025-10-21T16:21:06.195Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/d3/4b1f2bf16ed52ce0b508161df3a2d186e4935379a159a834cb4a7d687429/grpcio-1.76.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:32483fe2aab2c3794101c2a159070584e5db11d0aa091b2c0ea9c4fc43d0d749", size = 8163109, upload-time = "2025-10-21T16:21:08.498Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/61/d9043f95f5f4cf085ac5dd6137b469d41befb04bd80280952ffa2a4c3f12/grpcio-1.76.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dcfe41187da8992c5f40aa8c5ec086fa3672834d2be57a32384c08d5a05b4c00", size = 7626676, upload-time = "2025-10-21T16:21:10.693Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/95/fd9a5152ca02d8881e4dd419cdd790e11805979f499a2e5b96488b85cf27/grpcio-1.76.0-cp311-cp311-win32.whl", hash = "sha256:2107b0c024d1b35f4083f11245c0e23846ae64d02f40b2b226684840260ed054", size = 3997688, upload-time = "2025-10-21T16:21:12.746Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/9c/5c359c8d4c9176cfa3c61ecd4efe5affe1f38d9bae81e81ac7186b4c9cc8/grpcio-1.76.0-cp311-cp311-win_amd64.whl", hash = "sha256:522175aba7af9113c48ec10cc471b9b9bd4f6ceb36aeb4544a8e2c80ed9d252d", size = 4709315, upload-time = "2025-10-21T16:21:15.26Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/05/8e29121994b8d959ffa0afd28996d452f291b48cfc0875619de0bde2c50c/grpcio-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:81fd9652b37b36f16138611c7e884eb82e0cec137c40d3ef7c3f9b3ed00f6ed8", size = 5799718, upload-time = "2025-10-21T16:21:17.939Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/75/11d0e66b3cdf998c996489581bdad8900db79ebd83513e45c19548f1cba4/grpcio-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:04bbe1bfe3a68bbfd4e52402ab7d4eb59d72d02647ae2042204326cf4bbad280", size = 11825627, upload-time = "2025-10-21T16:21:20.466Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/50/2f0aa0498bc188048f5d9504dcc5c2c24f2eb1a9337cd0fa09a61a2e75f0/grpcio-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d388087771c837cdb6515539f43b9d4bf0b0f23593a24054ac16f7a960be16f4", size = 6359167, upload-time = "2025-10-21T16:21:23.122Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/e5/bbf0bb97d29ede1d59d6588af40018cfc345b17ce979b7b45424628dc8bb/grpcio-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f8f757bebaaea112c00dba718fc0d3260052ce714e25804a03f93f5d1c6cc11", size = 7044267, upload-time = "2025-10-21T16:21:25.995Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/86/f6ec2164f743d9609691115ae8ece098c76b894ebe4f7c94a655c6b03e98/grpcio-1.76.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:980a846182ce88c4f2f7e2c22c56aefd515daeb36149d1c897f83cf57999e0b6", size = 6573963, upload-time = "2025-10-21T16:21:28.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/bc/8d9d0d8505feccfdf38a766d262c71e73639c165b311c9457208b56d92ae/grpcio-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f92f88e6c033db65a5ae3d97905c8fea9c725b63e28d5a75cb73b49bda5024d8", size = 7164484, upload-time = "2025-10-21T16:21:30.837Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/e6/5d6c2fc10b95edf6df9b8f19cf10a34263b7fd48493936fffd5085521292/grpcio-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4baf3cbe2f0be3289eb68ac8ae771156971848bb8aaff60bad42005539431980", size = 8127777, upload-time = "2025-10-21T16:21:33.577Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/c8/dce8ff21c86abe025efe304d9e31fdb0deaaa3b502b6a78141080f206da0/grpcio-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:615ba64c208aaceb5ec83bfdce7728b80bfeb8be97562944836a7a0a9647d882", size = 7594014, upload-time = "2025-10-21T16:21:41.882Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/42/ad28191ebf983a5d0ecef90bab66baa5a6b18f2bfdef9d0a63b1973d9f75/grpcio-1.76.0-cp312-cp312-win32.whl", hash = "sha256:45d59a649a82df5718fd9527ce775fd66d1af35e6d31abdcdc906a49c6822958", size = 3984750, upload-time = "2025-10-21T16:21:44.006Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/00/7bd478cbb851c04a48baccaa49b75abaa8e4122f7d86da797500cccdd771/grpcio-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:c088e7a90b6017307f423efbb9d1ba97a22aa2170876223f9709e9d1de0b5347", size = 4704003, upload-time = "2025-10-21T16:21:46.244Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/ed/71467ab770effc9e8cef5f2e7388beb2be26ed642d567697bb103a790c72/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2", size = 5807716, upload-time = "2025-10-21T16:21:48.475Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/85/c6ed56f9817fab03fa8a111ca91469941fb514e3e3ce6d793cb8f1e1347b/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468", size = 11821522, upload-time = "2025-10-21T16:21:51.142Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/31/2b8a235ab40c39cbc141ef647f8a6eb7b0028f023015a4842933bc0d6831/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3", size = 6362558, upload-time = "2025-10-21T16:21:54.213Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/64/9784eab483358e08847498ee56faf8ff6ea8e0a4592568d9f68edc97e9e9/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb", size = 7049990, upload-time = "2025-10-21T16:21:56.476Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/94/8c12319a6369434e7a184b987e8e9f3b49a114c489b8315f029e24de4837/grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae", size = 6575387, upload-time = "2025-10-21T16:21:59.051Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/0f/f12c32b03f731f4a6242f771f63039df182c8b8e2cf8075b245b409259d4/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77", size = 7166668, upload-time = "2025-10-21T16:22:02.049Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/2d/3ec9ce0c2b1d92dd59d1c3264aaec9f0f7c817d6e8ac683b97198a36ed5a/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03", size = 8124928, upload-time = "2025-10-21T16:22:04.984Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/74/fd3317be5672f4856bcdd1a9e7b5e17554692d3db9a3b273879dc02d657d/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42", size = 7589983, upload-time = "2025-10-21T16:22:07.881Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/bb/ca038cf420f405971f19821c8c15bcbc875505f6ffadafe9ffd77871dc4c/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f", size = 3984727, upload-time = "2025-10-21T16:22:10.032Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/80/84087dc56437ced7cdd4b13d7875e7439a52a261e3ab4e06488ba6173b0a/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8", size = 4702799, upload-time = "2025-10-21T16:22:12.709Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/46/39adac80de49d678e6e073b70204091e76631e03e94928b9ea4ecf0f6e0e/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62", size = 5808417, upload-time = "2025-10-21T16:22:15.02Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/f5/a4531f7fb8b4e2a60b94e39d5d924469b7a6988176b3422487be61fe2998/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd", size = 11828219, upload-time = "2025-10-21T16:22:17.954Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/1c/de55d868ed7a8bd6acc6b1d6ddc4aa36d07a9f31d33c912c804adb1b971b/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc", size = 6367826, upload-time = "2025-10-21T16:22:20.721Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/64/99e44c02b5adb0ad13ab3adc89cb33cb54bfa90c74770f2607eea629b86f/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a", size = 7049550, upload-time = "2025-10-21T16:22:23.637Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/28/40a5be3f9a86949b83e7d6a2ad6011d993cbe9b6bd27bea881f61c7788b6/grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba", size = 6575564, upload-time = "2025-10-21T16:22:26.016Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/a9/1be18e6055b64467440208a8559afac243c66a8b904213af6f392dc2212f/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09", size = 7176236, upload-time = "2025-10-21T16:22:28.362Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/55/dba05d3fcc151ce6e81327541d2cc8394f442f6b350fead67401661bf041/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc", size = 8125795, upload-time = "2025-10-21T16:22:31.075Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/45/122df922d05655f63930cf42c9e3f72ba20aadb26c100ee105cad4ce4257/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc", size = 7592214, upload-time = "2025-10-21T16:22:33.831Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/6e/0b899b7f6b66e5af39e377055fb4a6675c9ee28431df5708139df2e93233/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e", size = 4062961, upload-time = "2025-10-21T16:22:36.468Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/41/0b430b01a2eb38ee887f88c1f07644a1df8e289353b78e82b37ef988fb64/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e", size = 4834462, upload-time = "2025-10-21T16:22:39.772Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/d5/301e71c7d22a5c7aabf1953dd1106987bd47f883377d528355f898a850f2/grpcio-1.76.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:8ebe63ee5f8fa4296b1b8cfc743f870d10e902ca18afc65c68cf46fd39bb0783", size = 5840371, upload-time = "2025-10-21T16:22:42.468Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/55/e3181adccff8808301dd9214b5e03c6db5a404b5ae8a6ec5768a5a65ed63/grpcio-1.76.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:3bf0f392c0b806905ed174dcd8bdd5e418a40d5567a05615a030a5aeddea692d", size = 11840384, upload-time = "2025-10-21T16:22:45.508Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/36/db1dfe943bce7180f5b6d9be564366ca1024a005e914a1f10212c24a840b/grpcio-1.76.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b7604868b38c1bfd5cf72d768aedd7db41d78cb6a4a18585e33fb0f9f2363fd", size = 6408765, upload-time = "2025-10-21T16:22:48.761Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/79/a8452764aa4b5ca30a970e514ec2fc5cf75451571793f6b276b6807f67dc/grpcio-1.76.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e6d1db20594d9daba22f90da738b1a0441a7427552cc6e2e3d1297aeddc00378", size = 7076220, upload-time = "2025-10-21T16:22:51.546Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/61/4cca38c4e7bb3ac5a1e0be6cf700a4dd85c61cbd8a9c5e076c224967084e/grpcio-1.76.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d099566accf23d21037f18a2a63d323075bebace807742e4b0ac210971d4dd70", size = 6610195, upload-time = "2025-10-21T16:22:54.688Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/3d/3f8bfae264c22c95fa702c35aa2a8105b754b4ace049c66a8b2230c97671/grpcio-1.76.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ebea5cc3aa8ea72e04df9913492f9a96d9348db876f9dda3ad729cfedf7ac416", size = 7193343, upload-time = "2025-10-21T16:22:57.434Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/cd/89f9254782b6cd94aa7c93fde370862877113b7189fb49900eaf9a706c82/grpcio-1.76.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0c37db8606c258e2ee0c56b78c62fc9dee0e901b5dbdcf816c2dd4ad652b8b0c", size = 8161922, upload-time = "2025-10-21T16:23:00.135Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/e0/99eb899d7cb9c676afea70ab6d02a72a9e6ce24d0300f625773fafe6d547/grpcio-1.76.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ebebf83299b0cb1721a8859ea98f3a77811e35dce7609c5c963b9ad90728f886", size = 7617951, upload-time = "2025-10-21T16:23:03.68Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/26/dca1b2bfaa9981cc28fa995730c80eedb0b86c912c30d1b676f08232e6ab/grpcio-1.76.0-cp39-cp39-win32.whl", hash = "sha256:0aaa82d0813fd4c8e589fac9b65d7dd88702555f702fb10417f96e2a2a6d4c0f", size = 3999306, upload-time = "2025-10-21T16:23:06.187Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/d1/fb90564a981eedd3cd87dc6bfd7c249e8a515cfad1ed8e9af73be223cd3b/grpcio-1.76.0-cp39-cp39-win_amd64.whl", hash = "sha256:acab0277c40eff7143c2323190ea57b9ee5fd353d8190ee9652369fae735668a", size = 4708771, upload-time = "2025-10-21T16:23:08.902Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/a8/690a085b4d1fe066130de97a87de32c45062cf2ecd218df9675add895550/grpcio-1.78.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:7cc47943d524ee0096f973e1081cb8f4f17a4615f2116882a5f1416e4cfe92b5", size = 5946986, upload-time = "2026-02-06T09:54:34.043Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/1b/e5213c5c0ced9d2d92778d30529ad5bb2dcfb6c48c4e2d01b1f302d33d64/grpcio-1.78.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:c3f293fdc675ccba4db5a561048cca627b5e7bd1c8a6973ffedabe7d116e22e2", size = 11816533, upload-time = "2026-02-06T09:54:37.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/37/1ba32dccf0a324cc5ace744c44331e300b000a924bf14840f948c559ede7/grpcio-1.78.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:10a9a644b5dd5aec3b82b5b0b90d41c0fa94c85ef42cb42cf78a23291ddb5e7d", size = 6519964, upload-time = "2026-02-06T09:54:40.268Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/f5/c0e178721b818072f2e8b6fde13faaba942406c634009caf065121ce246b/grpcio-1.78.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4c5533d03a6cbd7f56acfc9cfb44ea64f63d29091e40e44010d34178d392d7eb", size = 7198058, upload-time = "2026-02-06T09:54:42.389Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/b2/40d43c91ae9cd667edc960135f9f08e58faa1576dc95af29f66ec912985f/grpcio-1.78.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ff870aebe9a93a85283837801d35cd5f8814fe2ad01e606861a7fb47c762a2b7", size = 6727212, upload-time = "2026-02-06T09:54:44.91Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/88/9da42eed498f0efcfcd9156e48ae63c0cde3bea398a16c99fb5198c885b6/grpcio-1.78.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:391e93548644e6b2726f1bb84ed60048d4bcc424ce5e4af0843d28ca0b754fec", size = 7300845, upload-time = "2026-02-06T09:54:47.562Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/3f/1c66b7b1b19a8828890e37868411a6e6925df5a9030bfa87ab318f34095d/grpcio-1.78.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:df2c8f3141f7cbd112a6ebbd760290b5849cda01884554f7c67acc14e7b1758a", size = 8284605, upload-time = "2026-02-06T09:54:50.475Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/c4/ca1bd87394f7b033e88525384b4d1e269e8424ab441ea2fba1a0c5b50986/grpcio-1.78.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd8cb8026e5f5b50498a3c4f196f57f9db344dad829ffae16b82e4fdbaea2813", size = 7726672, upload-time = "2026-02-06T09:54:53.11Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/09/f16e487d4cc65ccaf670f6ebdd1a17566b965c74fc3d93999d3b2821e052/grpcio-1.78.0-cp310-cp310-win32.whl", hash = "sha256:f8dff3d9777e5d2703a962ee5c286c239bf0ba173877cc68dc02c17d042e29de", size = 4076715, upload-time = "2026-02-06T09:54:55.549Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/32/4ce60d94e242725fd3bcc5673c04502c82a8e87b21ea411a63992dc39f8f/grpcio-1.78.0-cp310-cp310-win_amd64.whl", hash = "sha256:94f95cf5d532d0e717eed4fc1810e8e6eded04621342ec54c89a7c2f14b581bf", size = 4799157, upload-time = "2026-02-06T09:54:59.838Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/c7/d0b780a29b0837bf4ca9580904dfb275c1fc321ded7897d620af7047ec57/grpcio-1.78.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2777b783f6c13b92bd7b716667452c329eefd646bfb3f2e9dabea2e05dbd34f6", size = 5951525, upload-time = "2026-02-06T09:55:01.989Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/b1/96920bf2ee61df85a9503cb6f733fe711c0ff321a5a697d791b075673281/grpcio-1.78.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:9dca934f24c732750389ce49d638069c3892ad065df86cb465b3fa3012b70c9e", size = 11830418, upload-time = "2026-02-06T09:55:04.462Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/0c/7c1528f098aeb75a97de2bae18c530f56959fb7ad6c882db45d9884d6edc/grpcio-1.78.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:459ab414b35f4496138d0ecd735fed26f1318af5e52cb1efbc82a09f0d5aa911", size = 6524477, upload-time = "2026-02-06T09:55:07.111Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/52/e7c1f3688f949058e19a011c4e0dec973da3d0ae5e033909677f967ae1f4/grpcio-1.78.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:082653eecbdf290e6e3e2c276ab2c54b9e7c299e07f4221872380312d8cf395e", size = 7198266, upload-time = "2026-02-06T09:55:10.016Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/61/8ac32517c1e856677282c34f2e7812d6c328fa02b8f4067ab80e77fdc9c9/grpcio-1.78.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85f93781028ec63f383f6bc90db785a016319c561cc11151fbb7b34e0d012303", size = 6730552, upload-time = "2026-02-06T09:55:12.207Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/98/b8ee0158199250220734f620b12e4a345955ac7329cfd908d0bf0fda77f0/grpcio-1.78.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f12857d24d98441af6a1d5c87442d624411db486f7ba12550b07788f74b67b04", size = 7304296, upload-time = "2026-02-06T09:55:15.044Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/0f/7b72762e0d8840b58032a56fdbd02b78fc645b9fa993d71abf04edbc54f4/grpcio-1.78.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5397fff416b79e4b284959642a4e95ac4b0f1ece82c9993658e0e477d40551ec", size = 8288298, upload-time = "2026-02-06T09:55:17.276Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/ae/ae4ce56bc5bb5caa3a486d60f5f6083ac3469228faa734362487176c15c5/grpcio-1.78.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbe6e89c7ffb48518384068321621b2a69cab509f58e40e4399fdd378fa6d074", size = 7730953, upload-time = "2026-02-06T09:55:19.545Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/6e/8052e3a28eb6a820c372b2eb4b5e32d195c661e137d3eca94d534a4cfd8a/grpcio-1.78.0-cp311-cp311-win32.whl", hash = "sha256:6092beabe1966a3229f599d7088b38dfc8ffa1608b5b5cdda31e591e6500f856", size = 4076503, upload-time = "2026-02-06T09:55:21.521Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/62/f22c98c5265dfad327251fa2f840b591b1df5f5e15d88b19c18c86965b27/grpcio-1.78.0-cp311-cp311-win_amd64.whl", hash = "sha256:1afa62af6e23f88629f2b29ec9e52ec7c65a7176c1e0a83292b93c76ca882558", size = 4799767, upload-time = "2026-02-06T09:55:24.107Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/f4/7384ed0178203d6074446b3c4f46c90a22ddf7ae0b3aee521627f54cfc2a/grpcio-1.78.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:f9ab915a267fc47c7e88c387a3a28325b58c898e23d4995f765728f4e3dedb97", size = 5913985, upload-time = "2026-02-06T09:55:26.832Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/ed/be1caa25f06594463f685b3790b320f18aea49b33166f4141bfdc2bfb236/grpcio-1.78.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3f8904a8165ab21e07e58bf3e30a73f4dffc7a1e0dbc32d51c61b5360d26f43e", size = 11811853, upload-time = "2026-02-06T09:55:29.224Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/a7/f06d151afc4e64b7e3cc3e872d331d011c279aaab02831e40a81c691fb65/grpcio-1.78.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:859b13906ce098c0b493af92142ad051bf64c7870fa58a123911c88606714996", size = 6475766, upload-time = "2026-02-06T09:55:31.825Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/a8/4482922da832ec0082d0f2cc3a10976d84a7424707f25780b82814aafc0a/grpcio-1.78.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b2342d87af32790f934a79c3112641e7b27d63c261b8b4395350dad43eff1dc7", size = 7170027, upload-time = "2026-02-06T09:55:34.7Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/bf/f4a3b9693e35d25b24b0b39fa46d7d8a3c439e0a3036c3451764678fec20/grpcio-1.78.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12a771591ae40bc65ba67048fa52ef4f0e6db8279e595fd349f9dfddeef571f9", size = 6690766, upload-time = "2026-02-06T09:55:36.902Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/b9/521875265cc99fe5ad4c5a17010018085cae2810a928bf15ebe7d8bcd9cc/grpcio-1.78.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:185dea0d5260cbb2d224c507bf2a5444d5abbb1fa3594c1ed7e4c709d5eb8383", size = 7266161, upload-time = "2026-02-06T09:55:39.824Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/86/296a82844fd40a4ad4a95f100b55044b4f817dece732bf686aea1a284147/grpcio-1.78.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51b13f9aed9d59ee389ad666b8c2214cc87b5de258fa712f9ab05f922e3896c6", size = 8253303, upload-time = "2026-02-06T09:55:42.353Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/e4/ea3c0caf5468537f27ad5aab92b681ed7cc0ef5f8c9196d3fd42c8c2286b/grpcio-1.78.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd5f135b1bd58ab088930b3c613455796dfa0393626a6972663ccdda5b4ac6ce", size = 7698222, upload-time = "2026-02-06T09:55:44.629Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/47/7f05f81e4bb6b831e93271fb12fd52ba7b319b5402cbc101d588f435df00/grpcio-1.78.0-cp312-cp312-win32.whl", hash = "sha256:94309f498bcc07e5a7d16089ab984d42ad96af1d94b5a4eb966a266d9fcabf68", size = 4066123, upload-time = "2026-02-06T09:55:47.644Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/e7/d6914822c88aa2974dbbd10903d801a28a19ce9cd8bad7e694cbbcf61528/grpcio-1.78.0-cp312-cp312-win_amd64.whl", hash = "sha256:9566fe4ababbb2610c39190791e5b829869351d14369603702e890ef3ad2d06e", size = 4797657, upload-time = "2026-02-06T09:55:49.86Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/a9/8f75894993895f361ed8636cd9237f4ab39ef87fd30db17467235ed1c045/grpcio-1.78.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:ce3a90455492bf8bfa38e56fbbe1dbd4f872a3d8eeaf7337dc3b1c8aa28c271b", size = 5920143, upload-time = "2026-02-06T09:55:52.035Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/06/0b78408e938ac424100100fd081189451b472236e8a3a1f6500390dc4954/grpcio-1.78.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:2bf5e2e163b356978b23652c4818ce4759d40f4712ee9ec5a83c4be6f8c23a3a", size = 11803926, upload-time = "2026-02-06T09:55:55.494Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/93/b59fe7832ff6ae3c78b813ea43dac60e295fa03606d14d89d2e0ec29f4f3/grpcio-1.78.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8f2ac84905d12918e4e55a16da17939eb63e433dc11b677267c35568aa63fc84", size = 6478628, upload-time = "2026-02-06T09:55:58.533Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/df/e67e3734527f9926b7d9c0dde6cd998d1d26850c3ed8eeec81297967ac67/grpcio-1.78.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b58f37edab4a3881bc6c9bca52670610e0c9ca14e2ea3cf9debf185b870457fb", size = 7173574, upload-time = "2026-02-06T09:56:01.786Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/62/cc03fffb07bfba982a9ec097b164e8835546980aec25ecfa5f9c1a47e022/grpcio-1.78.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:735e38e176a88ce41840c21bb49098ab66177c64c82426e24e0082500cc68af5", size = 6692639, upload-time = "2026-02-06T09:56:04.529Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/9a/289c32e301b85bdb67d7ec68b752155e674ee3ba2173a1858f118e399ef3/grpcio-1.78.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2045397e63a7a0ee7957c25f7dbb36ddc110e0cfb418403d110c0a7a68a844e9", size = 7268838, upload-time = "2026-02-06T09:56:08.397Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/79/1be93f32add280461fa4773880196572563e9c8510861ac2da0ea0f892b6/grpcio-1.78.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9f136fbafe7ccf4ac7e8e0c28b31066e810be52d6e344ef954a3a70234e1702", size = 8251878, upload-time = "2026-02-06T09:56:10.914Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/65/793f8e95296ab92e4164593674ae6291b204bb5f67f9d4a711489cd30ffa/grpcio-1.78.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:748b6138585379c737adc08aeffd21222abbda1a86a0dca2a39682feb9196c20", size = 7695412, upload-time = "2026-02-06T09:56:13.593Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/9f/1e233fe697ecc82845942c2822ed06bb522e70d6771c28d5528e4c50f6a4/grpcio-1.78.0-cp313-cp313-win32.whl", hash = "sha256:271c73e6e5676afe4fc52907686670c7cea22ab2310b76a59b678403ed40d670", size = 4064899, upload-time = "2026-02-06T09:56:15.601Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/27/d86b89e36de8a951501fb06a0f38df19853210f341d0b28f83f4aa0ffa08/grpcio-1.78.0-cp313-cp313-win_amd64.whl", hash = "sha256:f2d4e43ee362adfc05994ed479334d5a451ab7bc3f3fee1b796b8ca66895acb4", size = 4797393, upload-time = "2026-02-06T09:56:17.882Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/f2/b56e43e3c968bfe822fa6ce5bca10d5c723aa40875b48791ce1029bb78c7/grpcio-1.78.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:e87cbc002b6f440482b3519e36e1313eb5443e9e9e73d6a52d43bd2004fcfd8e", size = 5920591, upload-time = "2026-02-06T09:56:20.758Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/81/1f3b65bd30c334167bfa8b0d23300a44e2725ce39bba5b76a2460d85f745/grpcio-1.78.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:c41bc64626db62e72afec66b0c8a0da76491510015417c127bfc53b2fe6d7f7f", size = 11813685, upload-time = "2026-02-06T09:56:24.315Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/1c/bbe2f8216a5bd3036119c544d63c2e592bdf4a8ec6e4a1867592f4586b26/grpcio-1.78.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8dfffba826efcf366b1e3ccc37e67afe676f290e13a3b48d31a46739f80a8724", size = 6487803, upload-time = "2026-02-06T09:56:27.367Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/5c/a6b2419723ea7ddce6308259a55e8e7593d88464ce8db9f4aa857aba96fa/grpcio-1.78.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:74be1268d1439eaaf552c698cdb11cd594f0c49295ae6bb72c34ee31abbe611b", size = 7173206, upload-time = "2026-02-06T09:56:29.876Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/1e/b8801345629a415ea7e26c83d75eb5dbe91b07ffe5210cc517348a8d4218/grpcio-1.78.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be63c88b32e6c0f1429f1398ca5c09bc64b0d80950c8bb7807d7d7fb36fb84c7", size = 6693826, upload-time = "2026-02-06T09:56:32.305Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/84/0de28eac0377742679a510784f049738a80424b17287739fc47d63c2439e/grpcio-1.78.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3c586ac70e855c721bda8f548d38c3ca66ac791dc49b66a8281a1f99db85e452", size = 7277897, upload-time = "2026-02-06T09:56:34.915Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/9c/ad8685cfe20559a9edb66f735afdcb2b7d3de69b13666fdfc542e1916ebd/grpcio-1.78.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:35eb275bf1751d2ffbd8f57cdbc46058e857cf3971041521b78b7db94bdaf127", size = 8252404, upload-time = "2026-02-06T09:56:37.553Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/05/33a7a4985586f27e1de4803887c417ec7ced145ebd069bc38a9607059e2b/grpcio-1.78.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:207db540302c884b8848036b80db352a832b99dfdf41db1eb554c2c2c7800f65", size = 7696837, upload-time = "2026-02-06T09:56:40.173Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/77/7382241caf88729b106e49e7d18e3116216c778e6a7e833826eb96de22f7/grpcio-1.78.0-cp314-cp314-win32.whl", hash = "sha256:57bab6deef2f4f1ca76cc04565df38dc5713ae6c17de690721bdf30cb1e0545c", size = 4142439, upload-time = "2026-02-06T09:56:43.258Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/b2/b096ccce418882fbfda4f7496f9357aaa9a5af1896a9a7f60d9f2b275a06/grpcio-1.78.0-cp314-cp314-win_amd64.whl", hash = "sha256:dce09d6116df20a96acfdbf85e4866258c3758180e8c49845d6ba8248b6d0bbb", size = 4929852, upload-time = "2026-02-06T09:56:45.885Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/6c/40a4bba2c753ea8eeb8d776a31e9c54f4e506edf36db93a3db5456725294/grpcio-1.78.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:86f85dd7c947baa707078a236288a289044836d4b640962018ceb9cd1f899af5", size = 5947902, upload-time = "2026-02-06T09:56:48.469Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/4c/ed7664a37a7008be41204c77e0d88bbc4ac531bcf0c27668cd066f9ff6e2/grpcio-1.78.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:de8cb00d1483a412a06394b8303feec5dcb3b55f81d83aa216dbb6a0b86a94f5", size = 11824772, upload-time = "2026-02-06T09:56:51.264Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/5b/45a5c23ba3c4a0f51352366d9b25369a2a51163ab1c93482cb8408726617/grpcio-1.78.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e888474dee2f59ff68130f8a397792d8cb8e17e6b3434339657ba4ee90845a8c", size = 6521579, upload-time = "2026-02-06T09:56:54.967Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/e3/392e647d918004231e3d1c780ed125c48939bfc8f845adb8b5820410da3e/grpcio-1.78.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:86ce2371bfd7f212cf60d8517e5e854475c2c43ce14aa910e136ace72c6db6c1", size = 7199330, upload-time = "2026-02-06T09:56:57.611Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/2f/42a52d78bdbdb3f1310ed690a3511cd004740281ca75d300b7bd6d9d3de3/grpcio-1.78.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0c689c02947d636bc7fab3e30cc3a3445cca99c834dfb77cd4a6cabfc1c5597", size = 6726696, upload-time = "2026-02-06T09:57:00.357Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/83/b3d932a4fbb2dce3056f6df2926fc2d3ddc5d5acbafbec32c84033cf3f23/grpcio-1.78.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ce7599575eeb25c0f4dc1be59cada6219f3b56176f799627f44088b21381a28a", size = 7299076, upload-time = "2026-02-06T09:57:04.124Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/d9/70ea1be55efaf91fd19f7258b1292772a8226cf1b0e237717fba671073cb/grpcio-1.78.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:684083fd383e9dc04c794adb838d4faea08b291ce81f64ecd08e4577c7398adf", size = 8284493, upload-time = "2026-02-06T09:57:06.746Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/2f/3dddccf49e3e75564655b84175fca092d3efd81d2979fc89c4b1c1d879dc/grpcio-1.78.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ab399ef5e3cd2a721b1038a0f3021001f19c5ab279f145e1146bb0b9f1b2b12c", size = 7724340, upload-time = "2026-02-06T09:57:09.453Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/ae/dfdb3183141db787a9363078a98764675996a7c2448883153091fd7c8527/grpcio-1.78.0-cp39-cp39-win32.whl", hash = "sha256:f3d6379493e18ad4d39537a82371c5281e153e963cecb13f953ebac155756525", size = 4077641, upload-time = "2026-02-06T09:57:11.881Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/aa/694b2f505345cfdd234cffb2525aa379a81695e6c02fd40d7e9193e871c6/grpcio-1.78.0-cp39-cp39-win_amd64.whl", hash = "sha256:5361a0630a7fdb58a6a97638ab70e1dae2893c4d08d7aba64ded28bb9e7a29df", size = 4799428, upload-time = "2026-02-06T09:57:14.493Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1675,7 +1689,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "jax"
|
||||
version = "0.9.0"
|
||||
version = "0.9.0.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.14' and sys_platform == 'win32'",
|
||||
|
|
@ -1692,15 +1706,15 @@ resolution-markers = [
|
|||
"python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "jaxlib", version = "0.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "jaxlib", version = "0.9.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "ml-dtypes", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "opt-einsum", marker = "python_full_version >= '3.11'" },
|
||||
{ name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/39/28/13186d20cbece4944577de774269c620b2f5f5ea163748b61e48e423f47f/jax-0.9.0.tar.gz", hash = "sha256:e5ce9d6991333aeaad37729dd8315d29c4b094ea9476a32fb49933b556c723fb", size = 2534495, upload-time = "2026-01-20T23:06:47.099Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/52/40/f85d1feadd8f793fc1bfab726272523ef34b27302b55861ea872ec774019/jax-0.9.0.1.tar.gz", hash = "sha256:e395253449d74354fa813ff9e245acb6e42287431d8a01ff33d92e9ee57d36bd", size = 2534795, upload-time = "2026-02-05T18:47:33.088Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/99/91fd58d495f03495e93b3f6100336d7a1df212aa90ad37d1322b8bb7df71/jax-0.9.0-py3-none-any.whl", hash = "sha256:be1c7c1fa91b338f071805a786f9366d4a08361b711b0568ef5a157226c68915", size = 2955442, upload-time = "2026-01-20T22:27:05.128Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/1e/63ac22ec535e08129e16cb71b7eeeb8816c01d627ea1bc9105e925a71da0/jax-0.9.0.1-py3-none-any.whl", hash = "sha256:3baeaec6dc853394c272eb38a35ffba1972d67cf55d07a76bdb913bcd867e2ca", size = 2955477, upload-time = "2026-02-05T18:45:22.885Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1774,7 +1788,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "jaxlib"
|
||||
version = "0.9.0"
|
||||
version = "0.9.0.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.14' and sys_platform == 'win32'",
|
||||
|
|
@ -1796,28 +1810,28 @@ dependencies = [
|
|||
{ name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/96/71/283ee038a94869e7b3b3a27a594c028a58692e69f338bc30d0a466711830/jaxlib-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2770e2250e7923111f5e28813e593492fde3df40185b50bdc8bc41b866b99a8e", size = 56091849, upload-time = "2026-01-20T22:28:06.682Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/e9/100dd8a23407f5d883b684cd0ab9d23dfb5a889c17ba20ddc67fd6bb78d5/jaxlib-0.9.0-cp311-cp311-manylinux_2_27_aarch64.whl", hash = "sha256:1f21915b43b711f0abb3c35d26356bdcd16a68261d57fc8fefe370bc6dfcab4a", size = 74771277, upload-time = "2026-01-20T22:28:09.928Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/43/056cd98633ab0454c77b1ecebe1d5e21a76c06576a0ba655e9ac582ce985/jaxlib-0.9.0-cp311-cp311-manylinux_2_27_x86_64.whl", hash = "sha256:9f9eba014590435cec5fa1f6af2310a1a05db98d61331d18d6b278ff5424cd33", size = 80323264, upload-time = "2026-01-20T22:28:13.864Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/75/abc1ad61eb08b8f47a3f752d9ab1ea89cdab93a48a279a3ea650ab0be47f/jaxlib-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:67d05eb00b9af04d5213a962c5f0551b0fb521efc27aab63e802233da1c7814b", size = 60482859, upload-time = "2026-01-20T22:28:16.869Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/c1/ed8a199b71627f9c40b84a9fb40bba44bc048e62449633c5ee507066b19c/jaxlib-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c8d13bd74a55f9e5435587730b1ee067b17fc4c29b897f467c095003c63b37d", size = 56102192, upload-time = "2026-01-20T22:28:19.717Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/15/345f16517435b8f1deadd5bbfaae8dde42ed19af6e86ebbca0620279bab4/jaxlib-0.9.0-cp312-cp312-manylinux_2_27_aarch64.whl", hash = "sha256:a19e0ad375161190073d12221b350b3da29f97d9e72b0bc6400d0dbf171cda5f", size = 74771957, upload-time = "2026-01-20T22:28:22.576Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/7c/54ea33501e46b62b80071ed9cb96b0cfb28df88b7907edff0de41e9e9892/jaxlib-0.9.0-cp312-cp312-manylinux_2_27_x86_64.whl", hash = "sha256:93258e8bbfad38f9207a649797e07bf6e7fb30a8dece67b1485f9d550c59121f", size = 80336468, upload-time = "2026-01-20T22:28:25.86Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/fe/1969b10690fb49f1fadcc616367857c54b16949a008778877e359e5da13b/jaxlib-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:c75f8d3ae8e8411fc90e6c18a99971ad4a5da8d200e5ad710d650e7c1d5652e6", size = 60508836, upload-time = "2026-01-20T22:28:29.275Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/1a/a1db3023f66af906c85cdbafad88a0ae02df0d87ac7f749f1c9140ef6e3c/jaxlib-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:abcf50ca9f0f015f756035ff00803f5d04f42964775f936420d7afbf23b61c5c", size = 56102369, upload-time = "2026-01-20T22:28:32.554Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/7e/ad2d0519939a45eb4a3151d5e41bef1d68eec7b3e7f961e09ef3bf2d6607/jaxlib-0.9.0-cp313-cp313-manylinux_2_27_aarch64.whl", hash = "sha256:7da962fcbdfc18093ea371cfb5420fc70b51d3160d15bef0b780e63126ad25c0", size = 74769548, upload-time = "2026-01-20T22:28:35.481Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/d0/77bda8d946e3a854a5a533be7baf439bfffab9adae2a668a4dc645a494cd/jaxlib-0.9.0-cp313-cp313-manylinux_2_27_x86_64.whl", hash = "sha256:6aba184182c45e403b62dc269a04747da55da11d97586269710199204262d4ec", size = 80336519, upload-time = "2026-01-20T22:28:39.067Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/0c/6e7891a226ee3fd717af26fdbc93ef6dcf587849a0020fdfc69ccb38ca7f/jaxlib-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:e81debd335315ef178fa995399653444e620c316abe86bc51477ac012e13efd4", size = 60508118, upload-time = "2026-01-20T22:28:42.51Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/42/75c4cbb31cd56d334839fe5bea132eb5c00f1298f9a04245c0951af5f623/jaxlib-0.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:de5d4920da2eb1182a0dbc01cfee9acf8617b64562a0b8f153fdfbcd00381f35", size = 56213023, upload-time = "2026-01-20T22:28:45.202Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/af/91c23ae50bcfdc1dbeecd8ee4a7e87128a99a58bf6b6f6081215e8674a00/jaxlib-0.9.0-cp313-cp313t-manylinux_2_27_aarch64.whl", hash = "sha256:79caef921ebbbd78c239c3b4ed7644e0605bf454a066ad7700dde6c1e60b4b1e", size = 74887880, upload-time = "2026-01-20T22:28:48.227Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/64/7875e6ee446b558a366b3e9cbf26bab2390d87ac8015bc0dc49b35806b53/jaxlib-0.9.0-cp313-cp313t-manylinux_2_27_x86_64.whl", hash = "sha256:c857f062c720cff924f2237c925d46b8a8e8fde2b829ef2dc44cd4b6a7b8df88", size = 80440020, upload-time = "2026-01-20T22:28:51.378Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/ce/5098b850ab4d667931ed4cd390aee0c866a5cb892270cddb692788168fd6/jaxlib-0.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:30460814e85e6cfda142726ffcb06d421ab60093ca248c7256c2ecfd10a81fd6", size = 56104537, upload-time = "2026-01-20T22:28:54.857Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/66/e6f7e2603a61c9ea0291744321a2e8866d275578cd68594ac40eda315aad/jaxlib-0.9.0-cp314-cp314-manylinux_2_27_aarch64.whl", hash = "sha256:b6b1ea090293c2e62df64c3fd4d46f85f60bb18b8253b3587a62ee53ad14690f", size = 74782881, upload-time = "2026-01-20T22:28:57.675Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/49/b6773eba562747df115d8fbd0716625108e61af0001611a501ea9ad23eab/jaxlib-0.9.0-cp314-cp314-manylinux_2_27_x86_64.whl", hash = "sha256:881db842435ed40663c050745fdecf4f7b4850b1738a7538850d14e894798115", size = 80343444, upload-time = "2026-01-20T23:06:29.728Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/b0/ed338d3d5ff378a214a9e5fedce6c96b4705d921bffded709e01d209b998/jaxlib-0.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:d1077f30bd6608b12234f7d7a0cb0cf93f89c807ad69fee6c9146661f26e6b76", size = 62826259, upload-time = "2026-01-20T23:06:33.381Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/c0/c172876423623282af055b4b46bcea1fd933afc3306455e5eb6e5ef18344/jaxlib-0.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:48e315c70509f7927e379c4dd98d6e2dc2a6198677a6e0c50f1cc44199ba50d3", size = 56210812, upload-time = "2026-01-20T23:06:36.154Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/c1/8d051e9c2860ca228b98ba068daeadd7cdd0e8e06b4a93e27cab880e36ab/jaxlib-0.9.0-cp314-cp314t-manylinux_2_27_aarch64.whl", hash = "sha256:ef8006070aaceab7bec17eaa7e1d1b9d369793ad95927a14a27be216c063cd9c", size = 74885282, upload-time = "2026-01-20T23:06:40.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/9a/b8daeec1fd3f492f9cbd5a8043032be6d24b23394254ff02ba0723f6b2e8/jaxlib-0.9.0-cp314-cp314t-manylinux_2_27_x86_64.whl", hash = "sha256:866ce0a67f59fb0cc70e9b7307e70cf61e8ea3c8bad841796a471162e20056f1", size = 80437354, upload-time = "2026-01-20T23:06:44.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/fd/040321b0f4303ec7b558d69488c6130b1697c33d88dab0a0d2ccd2e0817c/jaxlib-0.9.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff2c550dab210278ed3a3b96454b19108a02e0795625be56dca5a181c9833c9", size = 56092920, upload-time = "2026-02-05T18:46:20.873Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/76/a558cd5e2ac8a2c16fe7f7e429dd5749cef48bc1a89941bb5b72bd3d7de3/jaxlib-0.9.0.1-cp311-cp311-manylinux_2_27_aarch64.whl", hash = "sha256:c4ac3cfd7aaacc37f37a6a332ee009dee39e3b5081bb4b473f410583436be553", size = 74767780, upload-time = "2026-02-05T18:46:23.917Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/49/f72fb26e2feb100fd84d297a17111364b15d5979843f62b7539cd120f9bb/jaxlib-0.9.0.1-cp311-cp311-manylinux_2_27_x86_64.whl", hash = "sha256:dc95ee32ae2bd4ed947ad0218fd6576b50a60ce45b60714d7ff2fd9fa195ed9e", size = 80323754, upload-time = "2026-02-05T18:46:27.405Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/fc/fa3c07d833a60cfb928f7a727fef25059e2e9af1dbc5d09821ad3a728292/jaxlib-0.9.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:2ed35e3300caa228c42897d8fbe961d6e03b797717e44eccbd3a788b5ac5c623", size = 60483840, upload-time = "2026-02-05T18:46:30.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/76/e89fd547f292663d8ce11b3247cd653a220e0d3cedbdbd094f0a8460d735/jaxlib-0.9.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3707bf0a58410da7c053c15ec6efee1fe12e70361416e055e4109b8041f4119b", size = 56104032, upload-time = "2026-02-05T18:46:33.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/92/40d4f0acecb3d6f7078b9eb468e524778a3497d0882c7ecf80509c10b7d3/jaxlib-0.9.0.1-cp312-cp312-manylinux_2_27_aarch64.whl", hash = "sha256:5ea8ebd62165b6f18f89b02fab749e02f5c584c2a1c703f04592d4d803f9e981", size = 74769175, upload-time = "2026-02-05T18:46:36.767Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/89/0dd938e6ed65ee994a49351a13aceaea46235ffbc1db5444d9ba3a279814/jaxlib-0.9.0.1-cp312-cp312-manylinux_2_27_x86_64.whl", hash = "sha256:e0e4a0a24ef98ec021b913991fbda09aeb96481b1bc0e5300a0339aad216b226", size = 80339748, upload-time = "2026-02-05T18:46:40.148Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/02/265e5ccadd65fee2f0716431573d9e512e5c6aecb23f478a7a92053cf219/jaxlib-0.9.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:08733d1431238a7cf9108338ab7be898b97181cba0eef53f2f9fd3de17d20adb", size = 60508788, upload-time = "2026-02-05T18:46:43.209Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/8d/f5a78b4d2a08e2d358e01527a3617af2df67c70231029ce1bdbb814219ff/jaxlib-0.9.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e857cafdd12e18493d96d4a290ed31aa9d99a0dc3056b4b42974c0f342c9bb0c", size = 56103168, upload-time = "2026-02-05T18:46:46.481Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/c3/fd3a9e2f02c1a04a1a00ff74adb6dd09e34040587bbb1b51b0176151dfa1/jaxlib-0.9.0.1-cp313-cp313-manylinux_2_27_aarch64.whl", hash = "sha256:b73b85f927d9b006f07622d5676092eab916645c4804fed6568da5fb4a541dfc", size = 74768692, upload-time = "2026-02-05T18:46:49.571Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/48/34923a6add7dda5fb8f30409a98b638f0dbd2d9571dbbf73db958eaec44a/jaxlib-0.9.0.1-cp313-cp313-manylinux_2_27_x86_64.whl", hash = "sha256:54dd2d34c6bec4f099f888a2f7895069a47c3ba86aaa77b0b78e9c3f9ef948f1", size = 80337646, upload-time = "2026-02-05T18:46:53.299Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/a9/629bed81406902653973d57de5af92842c7da63dfa8fcd84ee490c62ee94/jaxlib-0.9.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:27db7fbc49938f819f2a93fefef0bdc25bd523b499ab4d8a71ed8915c037c0b4", size = 60508306, upload-time = "2026-02-05T18:46:56.441Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/e3/6943589aaa58d9934838e00c6149dd1fc81e0c8555e9fcc9f527648faf5c/jaxlib-0.9.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9312fcfb4c5586802c08bc1b3b2419e48aa2a4cd1356251fe791ad71edc2da2a", size = 56210697, upload-time = "2026-02-05T18:46:59.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/ff/39479759b71f1d281b77050184759ac76dfd23a3ae75132ef92d168099c5/jaxlib-0.9.0.1-cp313-cp313t-manylinux_2_27_aarch64.whl", hash = "sha256:b536512cf84a0cb031196d6d5233f7093745e87eb416e45ad96fbb764b2befed", size = 74882879, upload-time = "2026-02-05T18:47:02.708Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/0d/e41eeddd761110d733688d6493defe776440c8f3d114419a8ecaef55601f/jaxlib-0.9.0.1-cp313-cp313t-manylinux_2_27_x86_64.whl", hash = "sha256:c4dc8828bb236532033717061d132906075452556b12d1ff6ccc10e569435dfe", size = 80438424, upload-time = "2026-02-05T18:47:06.437Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/ec/54b1251cea5c74a2f0d22106f5d1c7dc9e7b6a000d6a81a88deffa34c6fe/jaxlib-0.9.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:43272e52e5c89dbc4f02c7ccb6ffa5d587a09ac8db5163cb0c43e125b7075129", size = 56101484, upload-time = "2026-02-05T18:47:09.46Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/ce/91ba780439aa1e6bae964ea641169e8b9c9349c175fcb1a723b96ba54313/jaxlib-0.9.0.1-cp314-cp314-manylinux_2_27_aarch64.whl", hash = "sha256:82348cee1521d6123038c4c3beeafa2076c8f4ae29a233b8abff9d6dc8b44145", size = 74789558, upload-time = "2026-02-05T18:47:12.394Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/9b/3d7baca233c378b01fa445c9f63b260f592249ff69950baf893cea631b10/jaxlib-0.9.0.1-cp314-cp314-manylinux_2_27_x86_64.whl", hash = "sha256:e61e88032eeb31339c72ead9ed60c6153cd2222512624caadea67c350c78432e", size = 80343053, upload-time = "2026-02-05T18:47:16.042Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/5d/80efe5295133d5114fb7b0f27bdf82bc7a2308356dde6ba77c2afbaa3a36/jaxlib-0.9.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:abd9f127d23705105683448781914f17898b2b6591a051b259e6b947d4dcb93f", size = 62826248, upload-time = "2026-02-05T18:47:19.986Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/a9/f72578daa6af9bed9bda75b842c97581b31a577d7b2072daf8ba3d5a8156/jaxlib-0.9.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b01a75fbac8098cc985f6f1690bfb62f98b0785c84199287e0baaae50fa4238", size = 56209722, upload-time = "2026-02-05T18:47:23.193Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/ea/eefb118305dd5e1b0ad8d942f2bf43616c964d89fe491bec8628173da24d/jaxlib-0.9.0.1-cp314-cp314t-manylinux_2_27_aarch64.whl", hash = "sha256:76f23cbb109e673ea7a90781aca3e02a0c72464410c019fe14fba3c044f2b778", size = 74881382, upload-time = "2026-02-05T18:47:26.703Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/aa/a42fb912fd1f9c83e22dc2577cdfbf1a1b07d6660532cb44724db7a7c479/jaxlib-0.9.0.1-cp314-cp314t-manylinux_2_27_x86_64.whl", hash = "sha256:f80d30dedce96c73a7f5dcb79c4c827a1bde2304f502a56ce7e7f723df2a5398", size = 80438052, upload-time = "2026-02-05T18:47:30.039Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2122,52 +2136,53 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "line-profiler"
|
||||
version = "5.0.0"
|
||||
version = "5.0.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ea/5c/bbe9042ef5cf4c6cad4bf4d6f7975193430eba9191b7278ea114a3993fbb/line_profiler-5.0.0.tar.gz", hash = "sha256:a80f0afb05ba0d275d9dddc5ff97eab637471167ff3e66dcc7d135755059398c", size = 376919, upload-time = "2025-07-23T20:15:41.819Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bf/2a/498665a424404a560b2c6b3a3ea8b4304dbe493ccc3d01a6866c7a38890e/line_profiler-5.0.1.tar.gz", hash = "sha256:3e56c5eee51aa8b82a09d8a35ab19f4100ee45d70eb676c2c58deedcc06c34b1", size = 406557, upload-time = "2026-02-07T05:06:49.814Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/b8/8eb2bd10873a7bb93f412db8f735ff5b708bfcedef6492f2ec0a1f4dc55a/line_profiler-5.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5cd1621ff77e1f3f423dcc2611ef6fba462e791ce01fb41c95dce6d519c48ec8", size = 631005, upload-time = "2025-07-23T20:14:25.307Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/af/a7fd6b2a83fbc10427e1fdd178a0a80621eeb3b29256b1856d459abb7d2a/line_profiler-5.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:17a44491d16309bc39fc6197b376a120ebc52adc3f50b0b6f9baf99af3124406", size = 490943, upload-time = "2025-07-23T20:14:27.396Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/50/dcfc2e5386f5b3177cdad8eaa912482fe6a9218149a5cb5e85e871b55f2c/line_profiler-5.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a36a9a5ea5e37b0969a451f922b4dbb109350981187317f708694b3b5ceac3a5", size = 476487, upload-time = "2025-07-23T20:14:30.818Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/0c/cdcfc640d5b3338891cd336b465c6112d9d5c2f56ced4f9ea3e795b192c6/line_profiler-5.0.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b67e6e292efaf85d9678fe29295b46efd72c0d363b38e6b424df39b6553c49b3", size = 1412553, upload-time = "2025-07-23T20:14:32.07Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/23/bcdf3adf487917cfe431cb009b184c1a81a5099753747fe1a4aee42493f0/line_profiler-5.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9c92c28ee16bf3ba99966854407e4bc927473a925c1629489c8ebc01f8a640", size = 1461495, upload-time = "2025-07-23T20:14:33.396Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/04/1360ff19c4c426352ed820bba315670a6d52b3194fcb80af550a50e09310/line_profiler-5.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:51609cc264df6315cd9b9fa76d822a7b73a4f278dcab90ba907e32dc939ab1c2", size = 2510352, upload-time = "2025-07-23T20:14:34.797Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/af/138966a6edfd95208e92e9cfef79595d6890df31b1749cc0244d36127473/line_profiler-5.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67f9721281655dc2b6763728a63928e3b8a35dfd6160c628a3c599afd0814a71", size = 2445103, upload-time = "2025-07-23T20:14:36.19Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/95/5c9e4771f819b4d81510fa90b20a608bd3f91c268acd72747cd09f905de9/line_profiler-5.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:c2c27ac0c30d35ca1de5aeebe97e1d9c0d582e3d2c4146c572a648bec8efcfac", size = 461940, upload-time = "2025-07-23T20:14:37.422Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/f7/4e0fd2610749136d60f3e168812b5f6c697ffcfbb167b10d4aac24af1223/line_profiler-5.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f32d536c056393b7ca703e459632edc327ff9e0fc320c7b0e0ed14b84d342b7f", size = 634587, upload-time = "2025-07-23T20:14:39.069Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/fe/b5458452c2dbf7a9590b5ad3cf4250710a2554a5a045bfa6395cdea1b2d5/line_profiler-5.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a7da04ffc5a0a1f6653f43b13ad2e7ebf66f1d757174b7e660dfa0cbe74c4fc6", size = 492744, upload-time = "2025-07-23T20:14:40.632Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/e1/b69e20aeea8a11340f8c5d540c88ecf955a3559d8fbd5034cfe5677c69cf/line_profiler-5.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d2746f6b13c19ca4847efd500402d53a5ebb2fe31644ce8af74fbeac5ea4c54c", size = 478101, upload-time = "2025-07-23T20:14:42.306Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/3b/b29e5539b2c98d2bd9f5651f10597dd70e07d5b09bb47cc0aa8d48927d72/line_profiler-5.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b4290319a59730c04cbd03755472d10524130065a20a695dc10dd66ffd92172", size = 1455927, upload-time = "2025-07-23T20:14:44.139Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/1d/dcc75d2cf82bbe6ef65d0f39cc32410e099e7e1cd7f85b121a8d440ce8bc/line_profiler-5.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cd168a8af0032e8e3cb2fbb9ffc7694cdcecd47ec356ae863134df07becb3a2", size = 1508770, upload-time = "2025-07-23T20:14:45.868Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/9f/cbf9d011381c878f848f824190ad833fbfeb5426eb6c42811b5b759d5d54/line_profiler-5.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cbe7b095865d00dda0f53d7d4556c2b1b5d13f723173a85edb206a78779ee07a", size = 2551269, upload-time = "2025-07-23T20:14:47.279Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/86/06999bff316e2522fc1d11fcd3720be81a7c47e94c785a9d93c290ae0415/line_profiler-5.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ff176045ea8a9e33900856db31b0b979357c337862ae4837140c98bd3161c3c7", size = 2491091, upload-time = "2025-07-23T20:14:48.637Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/d1/758f2f569b5d4fdc667b88e88e7424081ba3a1d17fb531042ed7f0f08d7f/line_profiler-5.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:474e0962d02123f1190a804073b308a67ef5f9c3b8379184483d5016844a00df", size = 462954, upload-time = "2025-07-23T20:14:50.094Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/d8/383c37c36f888c4ca82a28ffea27c589988463fc3f0edd6abae221c35275/line_profiler-5.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:729b18c0ac66b3368ade61203459219c202609f76b34190cbb2508b8e13998c8", size = 628109, upload-time = "2025-07-23T20:14:51.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/a3/75a27b1f3e14ae63a2e99f3c7014dbc1e3a37f56c91b63a2fc171e72990d/line_profiler-5.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:438ed24278c428119473b61a473c8fe468ace7c97c94b005cb001137bc624547", size = 489142, upload-time = "2025-07-23T20:14:52.993Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/85/f65cdbfe8537da6fab97c42958109858df846563546b9c234a902a98c313/line_profiler-5.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:920b0076dca726caadbf29f0bfcce0cbcb4d9ff034cd9445a7308f9d556b4b3a", size = 475838, upload-time = "2025-07-23T20:14:54.637Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/ea/cfa53c8ede0ef539cfe767a390d7ccfc015f89c39cc2a8c34e77753fd023/line_profiler-5.0.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53326eaad2d807487dcd45d2e385feaaed81aaf72b9ecd4f53c1a225d658006f", size = 1402290, upload-time = "2025-07-23T20:14:56.775Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/2c/3467cd5051afbc0eb277ee426e8dffdbd1fcdd82f1bc95a0cd8945b6c106/line_profiler-5.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e3995a989cdea022f0ede5db19a6ab527f818c59ffcebf4e5f7a8be4eb8e880", size = 1457827, upload-time = "2025-07-23T20:14:58.158Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/87/d5039608979b37ce3dadfa3eed7bf8bfec53b645acd30ca12c8088cf738d/line_profiler-5.0.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8bf57892a1d3a42273652506746ba9f620c505773ada804367c42e5b4146d6b6", size = 2497423, upload-time = "2025-07-23T20:15:01.015Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/3e/e5e09699e2841b4f41c16d01ff2adfd20fde6cb73cfa512262f0421e15e0/line_profiler-5.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43672085f149f5fbf3f08bba072ad7014dd485282e8665827b26941ea97d2d76", size = 2439733, upload-time = "2025-07-23T20:15:02.582Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/cf/18d8fefabd8a56fb963f944149cadb69be67a479ce6723275cae2c943af5/line_profiler-5.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:446bd4f04e4bd9e979d68fdd916103df89a9d419e25bfb92b31af13c33808ee0", size = 460852, upload-time = "2025-07-23T20:15:03.827Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/eb/bc4420cf68661406c98d590656d72eed6f7d76e45accf568802dc83615ef/line_profiler-5.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9873fabbae1587778a551176758a70a5f6c89d8d070a1aca7a689677d41a1348", size = 624828, upload-time = "2025-07-23T20:15:05.315Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/6e/6e0a4c1009975d27810027427d601acbad75b45947040d0fd80cec5b3e94/line_profiler-5.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2cd6cdb5a4d3b4ced607104dbed73ec820a69018decd1a90904854380536ed32", size = 487651, upload-time = "2025-07-23T20:15:06.961Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/2c/e60e61f24faa0e6eca375bdac9c4b4b37c3267488d7cb1a8c5bd74cf5cdc/line_profiler-5.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:34d6172a3bd14167b3ea2e629d71b08683b17b3bc6eb6a4936d74e3669f875b6", size = 474071, upload-time = "2025-07-23T20:15:08.607Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/d5/6f178e74746f84cc17381f607d191c54772207770d585fda773b868bfe28/line_profiler-5.0.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5edd859be322aa8252253e940ac1c60cca4c385760d90a402072f8f35e4b967", size = 1405434, upload-time = "2025-07-23T20:15:09.862Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/32/ce67bbf81e5c78cc8d606afe6a192fbef30395021b2aaffe15681e186e3f/line_profiler-5.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d4f97b223105eed6e525994f5653061bd981e04838ee5d14e01d17c26185094", size = 1467553, upload-time = "2025-07-23T20:15:11.195Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/c1/431ffb89a351aaa63f8358442e0b9456a3bb745cebdf9c0d7aa4d47affca/line_profiler-5.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4758007e491bee3be40ebcca460596e0e28e7f39b735264694a9cafec729dfa9", size = 2442489, upload-time = "2025-07-23T20:15:12.602Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/9d/e34cc99c8abca3a27911d3542a87361e9c292fa1258d182e4a0a5c442850/line_profiler-5.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:213b19c4b65942db5d477e603c18c76126e3811a39d8bab251d930d8ce82ffba", size = 461377, upload-time = "2025-07-23T20:15:13.871Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/4e/915be6af377c4824486e99abeefb94108c829f3b32f1ead72fc9c6e1e30e/line_profiler-5.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba2142d35a3401d348cb743611bac52ba9db9cf026f8aa82c34d13effb98a71", size = 632303, upload-time = "2025-07-23T20:15:26.796Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/68/d5e9b22ae37d1e65188b754a0979d65fe339057b4d721c153f3fa1d89884/line_profiler-5.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17724b2dff0edb3a4ac402bef6381060a4c424fbaa170e651306495f7c95bba8", size = 491478, upload-time = "2025-07-23T20:15:28.367Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/37/7c4750068fb8977749071f08349ba7c330803f9312724948be9becb1d63d/line_profiler-5.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d2315baca21a9be299b5a0a89f2ce4ed5cfd12ba039a82784a298dd106d3621d", size = 477089, upload-time = "2025-07-23T20:15:29.875Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/b9/6c5beddc9eb5c6a3928c1f3849e75f5ce4fd9c7d81f83664ad20286ee61f/line_profiler-5.0.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:febbfc59502984e2cb0deb27cd163ed71847e36bbb82763f2bf3c9432cc440ab", size = 1409904, upload-time = "2025-07-23T20:15:32.236Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/57/94b284925890a671f5861ab82e3a98e3c4a73295144708fd6ade600d0ac9/line_profiler-5.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:213dc34b1abdcafff944c13e62f2f1d254fc1cb30740ac0257e4567c8bea9a03", size = 1461377, upload-time = "2025-07-23T20:15:34.139Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/a6/0733ba988122b8d077363cfbcb9ed143ceca0dbb3715c37285754c9d1daf/line_profiler-5.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:011ac8167855513cac266d698b34b8ded9c673640d105a715c989fd5f27a298c", size = 2507835, upload-time = "2025-07-23T20:15:36.28Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/59/93db811611fda1d921e56b324469ffb6b9210dd134bd377f52b3250012e2/line_profiler-5.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4646907f588439845d7739d6a5f10ab08a2f8952d65f61145eeb705e8bb4797e", size = 2444523, upload-time = "2025-07-23T20:15:38.531Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/58/3d9355385817d64fc582daec8592eb85f0ea39d577001a2f1ce0971c4b95/line_profiler-5.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cb6dced51bf906ddf2a8d75eda3523cee4cfb0102f54610e8f849630341a281", size = 461954, upload-time = "2025-07-23T20:15:40.281Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/68/0a52f3868aca7722938094003bda3f05a36a5ac72a3faa3b468fb939ffc4/line_profiler-5.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64f099711f4752bc4e3dae762b971ef3016ad7572507db4b22a9a7bb0f4fd05f", size = 657258, upload-time = "2026-02-07T05:05:15.094Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/00/8609a3774a221aa4c48c3d5f3ecf63194e44c931b74a3bad6637057f07c4/line_profiler-5.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c55ae9dd54deda74ddb79a60818a59c32d53e87eb5628eab53183123aca6c53", size = 514405, upload-time = "2026-02-07T05:05:17.253Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/1b/208adab75d25140c6ba4469da3e4d8bf51bb65a0e9e5b04f24dd0e6dadc7/line_profiler-5.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e6502518f7d7c1241b8d72fce275a8d8ac08081335a1bd99ad69fadaf044784d", size = 502984, upload-time = "2026-02-07T05:05:18.866Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/d9/fbc770fa6df84ea32580dae6c46447c07831fac97f3e5e5f3f6182c7d5ab/line_profiler-5.0.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b9dd773b1bde3f9864bb302e8bb78a9b35573448e1b837e8a6d2740580ff18e", size = 1505200, upload-time = "2026-02-07T05:05:20.428Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/12/77c03fcb93b0d206b785ed45f461b29195bdd9cfd609ced3cdfb654287b3/line_profiler-5.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8b28c8902092e1dbc1aa35929e7b5472a5bdb32da1fbd3570c5e78376a71ee86", size = 2530747, upload-time = "2026-02-07T05:05:21.501Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/3d/a001ec8c4154cbfd949bd570036163e8a7dbeca84a8a82c03cf33919bdcd/line_profiler-5.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:3b8bf20a9a15029e833361d6c8bed4397c806725a80a2bfb457ce1d60a918dfe", size = 485432, upload-time = "2026-02-07T05:05:22.617Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/55/0a74021f3ecfe71be86b3263f98890a28902ed0715a841507ac2eb0316db/line_profiler-5.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8103e12337802850af69ad74fa2d540cb24b35905cab5d093e4d5a88f89d7305", size = 656106, upload-time = "2026-02-07T05:05:56.919Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/e4/63b961fe4ce9cd9b05a4710858b32c537ad8364ed84ec52b1a463733b8b9/line_profiler-5.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:029abeda3bedf2205fd9e9f9d35b38d369cc33d5581d875aa27c80b03facd95e", size = 513554, upload-time = "2026-02-07T05:05:59.044Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/2b/0c15fe6ae98340a8315f76a289720b3db7cfd2b43581f07771b39ac59a69/line_profiler-5.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fc5e42b471316fe55fb52f3dd048a359652d3715e302707a4342844ade009166", size = 502698, upload-time = "2026-02-07T05:06:00.11Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/9c/2b0ede405364e23a5ec45100a6c053db40afff36b17d2778541e16766cae/line_profiler-5.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e5fe36bf67e5114b56956017cbdb3e14851afa047aee06a6249c7e4524985d30", size = 1546113, upload-time = "2026-02-07T05:06:01.144Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/c8/80bd62dd8fd4d594cb9bc12f40ade5222c5e18a7073f2003091d53ee264a/line_profiler-5.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:daab2aa2a1c67e7706ab43e13b544eb7c0e2321d7a0646e0380745361e2477ce", size = 2570825, upload-time = "2026-02-07T05:06:02.782Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/75/87a0b452a42783848a82ca67a390f920a5844ef0db092f9029cc42933a72/line_profiler-5.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a717a5eed30311982b8e707eda30384c5532ccbd557d57e40a1dbc5588667c3", size = 486002, upload-time = "2026-02-07T05:06:04.085Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/79/0bf2de84d3680318bf85f3375fe0c296c6d4b1ed02dcad686fa09ced8df1/line_profiler-5.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:6d4b626c948be1d7742ea2314261eccfc4b9f7dfb2adae8ece4409776a9e2511", size = 470516, upload-time = "2026-02-07T05:06:05.227Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/8e/bd5b0cc87203ff280cf01ef65b263472983adad5a0f710cf191e292fc3df/line_profiler-5.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b9b58a4d805ea11a0ea18c75a21b9d3bc1bb69d1f7d9282625386b8b47689b3b", size = 652481, upload-time = "2026-02-07T05:06:06.329Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/26/01d65c99809cdec0566c3f86b4cefec6ba558b261f75dac0b856a1570d7e/line_profiler-5.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a5401dfe1dcd6f01d0f35feff02c96ebd73d2e45058e39ba935e822bde33f191", size = 511256, upload-time = "2026-02-07T05:06:07.847Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/4d/5862629dc59f8154eae76ac0ea2a69c0d11b0b79483957f3c1c6a1af9896/line_profiler-5.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3350cfe27fa71082ac3d773d905b5cff7584a7923a36ea894a4619c0eb40116", size = 501428, upload-time = "2026-02-07T05:06:08.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/1d/adda8aff5cc3e1d8687a128593a562fbf28d650513674aa773381068ce95/line_profiler-5.0.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:348f34f54d68dcb249124d6b6275cbfcaea33920aecdb2f7d536d395abbaeda7", size = 1489869, upload-time = "2026-02-07T05:06:10.034Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/6e/a5f92fb2451982ea49dd1bbc1b4a308aaeda81320583b3593731bc8654e8/line_profiler-5.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4344e66d853824be0f0fa5d99ba6014cb093334e178fac942870bc4a4dd4c146", size = 2501328, upload-time = "2026-02-07T05:06:12.101Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/79/cd66262b78a9f1e6ccd7452f331237c3489fb93191f95fe0b9c4cdac4733/line_profiler-5.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:87d2295feaa5ac933e672d1c5ac5b83e2a1f7ebce25d290f81a7aabb1d46ac1f", size = 484346, upload-time = "2026-02-07T05:06:13.847Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/ba/ec80db0e0b2a46832127f5de5cd6d059d60aeb0daf2a2eddd7a05ff092da/line_profiler-5.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:d3c93c975c1ccbc77db82579e9ec65897d783d53c3456cd2a8a582cae7cb5b81", size = 467828, upload-time = "2026-02-07T05:06:14.896Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/83/23b24ceb224f89725c2baa0be1b889ea9eec84b4ec3835c8f7ff62abf918/line_profiler-5.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6100734916900f6c0ee5ba1cae05e7860c53aac4cd7a016faefd50092be22a14", size = 648194, upload-time = "2026-02-07T05:06:16.59Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/ec/6e71a59baf77b95c38ac07dc6e622f46674a526ea9dbd348ac310c24b358/line_profiler-5.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ef083f93bbb8cd8e7fa49b07e09245195a9be47755e7e353fb526aee9d983427", size = 509348, upload-time = "2026-02-07T05:06:18.195Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/29/ce75d7e9c07e72ffa513424881d0509a559a21a433f462fb197604a0e4ce/line_profiler-5.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b74a89eba20a20222bf6220e017244849cb675125a0e9e7ade5411af3d6c246", size = 499198, upload-time = "2026-02-07T05:06:19.72Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/ae/3bccce627f42151b2bd7389ef1304b9255e38d6c79ae23fbd8c33600ea45/line_profiler-5.0.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f8a74fb63ff4cb1358fa9378daa853396f5868d5c81cad88d17b1f48a761f04", size = 1488964, upload-time = "2026-02-07T05:06:20.86Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/24/0940490a9be8e19ed097da03463547c5a7e066b8612e208e005fd440c3e2/line_profiler-5.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7460781af7e8754850c5dc8b6f1d0133d48aa3a24723cfe9d445dd27d42a798d", size = 2500824, upload-time = "2026-02-07T05:06:22.19Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/53/f73fc9515d3919c9733b88fc9d51b81dba50d74da9e8f357a72ed5c503b7/line_profiler-5.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:20174e74b142d13ccb8836ebabfa1ca4e2cde4d0961f3ee078a3cc64f2832bd6", size = 484852, upload-time = "2026-02-07T05:06:23.467Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/c5/2cdf45c274410632c15a28075ccc865e13b2dd5ae3b11a25313cf8e0d8af/line_profiler-5.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:39ed06465de1dc1eccf1df7dcd90aa100af3f55472ef25fa6c8bd228d8d5f819", size = 467511, upload-time = "2026-02-07T05:06:24.588Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/76/f857c647597bca495dcba3f7edaf986516bde919152f19c71bef47a546fa/line_profiler-5.0.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:8c0cd9f38eaddb506990080593381f276b1942c416e415a933632c4943895df3", size = 653895, upload-time = "2026-02-07T05:06:25.724Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/3c/7688dff38a2bdcf66b990f5d7c496ca41dc63171a3e03a6049488842f786/line_profiler-5.0.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4e901d75109f12a1a65edc2352401875cd51b69bf91537a9555c7691fdc0dd46", size = 514383, upload-time = "2026-02-07T05:06:26.976Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/4a/79513220bc2c4fa2a4e7468b89e18b917e82bc7ea1e7be1b924412f9cd20/line_profiler-5.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5630d495f16babd812f4ef5cba90cf3cf3cc06b10a24f9becfb76a64e511bcbd", size = 505430, upload-time = "2026-02-07T05:06:28.216Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/f4/012446292f1fee6c4a5b7ebf3d5de7741550b8b3e781186a32c333ced1fa/line_profiler-5.0.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d9c0b8d01eddb99ed76f53e2f81cce8ceff68e751370af2bd1fd276fb17570e", size = 1480761, upload-time = "2026-02-07T05:06:29.297Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/e1/48aefe03d27a32b93ffec6aaaab1e0f5d5b94e0a44b3ddf0929c9eeef50c/line_profiler-5.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b76d6f7ab6d2b3018bea10172bbe105624d14f63bde9549c393502ca4ea9fb5", size = 2500278, upload-time = "2026-02-07T05:06:30.782Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/f8/0959ab4ff46a99c9db6d90de90d08bff6d3277fc4b80c9fb5d04300af798/line_profiler-5.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:9bb97a77d8d5ffa8bf0193c5ee4d804dc8360244861f104c10c9e58c95721066", size = 491243, upload-time = "2026-02-07T05:06:32.087Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/6d/91e7e2390c064233c1e64de8d82059212814c29b46f33f554bc7fe0a2711/line_profiler-5.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:c3a4807a30adda81ac246744bf42bff8cc54bcbbe5e3bfff4523b171349c5059", size = 475314, upload-time = "2026-02-07T05:06:33.358Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/ed/0a0c4a2bb84de941e52a46642341552c721d091e0a4d7be5138849de4902/line_profiler-5.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:92b724b3668755967a2174c20b56e7a69ce46aea1935f1605bc7f5f5ed672f15", size = 658806, upload-time = "2026-02-07T05:06:42.141Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/79/b7a36d46cff3f4d17d18e8c3d6e8275ac05559952e25dc4c95e8c4cf7337/line_profiler-5.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75eba02f688601a9ef23d709787c2e2e26f8c46de9b883d60227ef391dd8c513", size = 515234, upload-time = "2026-02-07T05:06:43.211Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/af/a8aaf394f1a15df4cbcfabc228c215dc014082a864f38d4b074fc63caef8/line_profiler-5.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3b3b551c90eff946c38933c76c4c77e2904c403a20dc9eb467b756042066e6a4", size = 503766, upload-time = "2026-02-07T05:06:44.303Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/37/c0c27f093a2352fa5d491a0404beb8b8ea1a56a8e88d61081160ef284da3/line_profiler-5.0.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15c53434bd2885938a46eee75373d5a5fef724803578a2262076ce4693032c6d", size = 1501758, upload-time = "2026-02-07T05:06:45.403Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/55/c2160db00c0c07a044f6f29034bb441c5c3eb29e907590a823cdfede8ad3/line_profiler-5.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ac96cc3c3946a9bfbb723843a0eceeed3295d633fe65960e3ed096d31b065eab", size = 2524435, upload-time = "2026-02-07T05:06:46.716Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/92/262533d5bb1fa81da52d1a6d2dc828c05a578fe4ed4506fb6feaa00f14d6/line_profiler-5.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:f7f17946e5cf2cdcf8406656bebc0ba8fb9550b4a0558bce52e2b8e2c047d1a3", size = 486001, upload-time = "2026-02-07T05:06:48.63Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2425,7 +2440,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "markdown"
|
||||
version = "3.10.1"
|
||||
version = "3.10.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.14' and sys_platform == 'win32'",
|
||||
|
|
@ -2442,9 +2457,9 @@ resolution-markers = [
|
|||
"python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'",
|
||||
"python_full_version == '3.10.*'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b7/b1/af95bcae8549f1f3fd70faacb29075826a0d689a27f232e8cee315efa053/markdown-3.10.1.tar.gz", hash = "sha256:1c19c10bd5c14ac948c53d0d762a04e2fa35a6d58a6b7b1e6bfcbe6fefc0001a", size = 365402, upload-time = "2026-01-21T18:09:28.206Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl", hash = "sha256:867d788939fe33e4b736426f5b9f651ad0c0ae0ecf89df0ca5d1176c70812fe3", size = 107684, upload-time = "2026-01-21T18:09:27.203Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3571,11 +3586,11 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "parso"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d4/de/53e0bcf53d13e005bd8c92e7855142494f41171b34c2536b86187474184d/parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a", size = 401205, upload-time = "2025-08-23T15:15:28.028Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621, upload-time = "2026-02-09T15:45:24.425Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3592,7 +3607,7 @@ name = "pexpect"
|
|||
version = "4.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "ptyprocess", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" },
|
||||
{ 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 = [
|
||||
|
|
@ -3898,7 +3913,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "posthog"
|
||||
version = "7.8.0"
|
||||
version = "7.8.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.14' and sys_platform == 'win32'",
|
||||
|
|
@ -3923,33 +3938,33 @@ dependencies = [
|
|||
{ name = "six", marker = "python_full_version >= '3.10'" },
|
||||
{ name = "typing-extensions", marker = "python_full_version >= '3.10'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/67/39/613f56a5d469e4c4f4e9616f533bd0451ae1b7b70d033201227b9229bf17/posthog-7.8.0.tar.gz", hash = "sha256:5f46730090be503a9d4357905d3260178ed6be4c1f6c666e8d7b44189e11fbb8", size = 167014, upload-time = "2026-01-30T13:43:29.829Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d1/ad/2f116cd9b83dc83ece4328a4efe0bcb80e5c2993837f89a788467d261da8/posthog-7.8.3.tar.gz", hash = "sha256:2b85e818bf818ac2768a890b772b7c12d4f909797226acd9327d66a319dbcf83", size = 167083, upload-time = "2026-02-06T13:16:22.938Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/f6/c3118de9b52fd442c0de92e4ad68326f5ecb327c1d354e0b9a8d0213ce45/posthog-7.8.0-py3-none-any.whl", hash = "sha256:fefa48c560c51ca0acc6261c92a8f61a067a8aa977c8820d0f149eaa4500e4da", size = 192427, upload-time = "2026-01-30T13:43:28.774Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/e5/5a4b060cbb9aa9defb8bfd55d15899b3146fece14147f4d66be80e81955a/posthog-7.8.3-py3-none-any.whl", hash = "sha256:1840796e4f7e14dd91ec5fdeb939712c3383fe9e758cfcdeb0317d8f30f7b901", size = 192528, upload-time = "2026-02-06T13:16:21.385Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prek"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/76/62/4b91c8a343e21fcefabc569a91d08cf8756c554332521af78294acef7c27/prek-0.3.1.tar.gz", hash = "sha256:45abc4ffd3cb2d39c478f47e92e88f050e5a4b7a20915d78e54b1a3d3619ebe4", size = 323141, upload-time = "2026-01-31T13:25:58.128Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d3/f5/ee52def928dd1355c20bcfcf765e1e61434635c33f3075e848e7b83a157b/prek-0.3.2.tar.gz", hash = "sha256:dce0074ff1a21290748ca567b4bda7553ee305a8c7b14d737e6c58364a499364", size = 334229, upload-time = "2026-02-06T13:49:47.539Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/c1/e0e545048e4190245fb4ae375d67684108518f3c69b67b81d62e1cd855c6/prek-0.3.1-py3-none-linux_armv6l.whl", hash = "sha256:1f77d0845cc63cad9c447f7f7d554c1ad188d07dbe02741823d20d58c7312eaf", size = 4285460, upload-time = "2026-01-31T13:25:42.066Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/fe/7636d10e2dafdf2a4a927c989f32ce3f08e99d62ebad7563f0272e74b7f4/prek-0.3.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e21142300d139e8c7f3970dd8aa66391cb43cd17c0c4ee65ff1af48856bb6a4b", size = 4287085, upload-time = "2026-01-31T13:25:40.193Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/7f/62ed57340071e04c02057d64276ec3986baca3ad4759523e2f433dc9be55/prek-0.3.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c09391de7d1994f9402c46cb31671800ea309b1668d839614965706206da3655", size = 3936188, upload-time = "2026-01-31T13:25:47.314Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/17/cb24f462c255f76d130ca110f4fcec09b041c3c770e43960cc3fc9dcc9ce/prek-0.3.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:a0b0a012ef6ef28dee019cf81a95c5759b2c03287c32c1f2adcb5f5fb097138e", size = 4275214, upload-time = "2026-01-31T13:25:38.053Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/85/db155b59d73cf972c8467e4d95def635f9976d5fcbcc790a4bbe9d2e049a/prek-0.3.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4a0e40785d39b8feae0d7ecf5534789811a2614114ab47f4e344a2ebd75ac10", size = 4197982, upload-time = "2026-01-31T13:25:50.034Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/cf/d35c32436692928a9ca53eed3334e30148a60f0faa33c42e8d11b6028fa6/prek-0.3.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac5c2f5e377e3cc5a5ea191deb8255a5823fbaa01b424417fe18ff12c7c800f3", size = 4458572, upload-time = "2026-01-31T13:25:51.46Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/c0/eb36fecb21fe30baa72fb87ccf3a791c32932786c287f95f8972402e9245/prek-0.3.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fe70e97f4dfca57ce142caecd77b90a435abd1c855f9e96041078415d80e89a", size = 4999230, upload-time = "2026-01-31T13:25:44.055Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/f3/ad1a25ea16320e6acd1fedd6bd96a0d22526f5132d9b5adc045996ccca4c/prek-0.3.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b28e921d893771bdd7533cd94d46a10e0cf2855c5e6bf6809b598b5e45baa73", size = 4510376, upload-time = "2026-01-31T13:25:48.563Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/b7/91afdd24be808ccf3b9119f4cf2bd6d02e30683143a62a08f432a3435861/prek-0.3.1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:555610a53c4541976f4fe8602177ecd7a86a931dcb90a89e5826cfc4a6c8f2cb", size = 4273229, upload-time = "2026-01-31T13:25:56.362Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/bb/636c77c5c9fc5eadcc2af975a95b48eeeff2dc833021e222b0e9479b9b47/prek-0.3.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:663a15a31b705db5d01a1c9eb28c6ea417628e6cb68de2dc7b3016ca2f959a99", size = 4301166, upload-time = "2026-01-31T13:25:36.281Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/cf/c928a36173e71b21b252943404a5b3d1ddc1f08c9e0f1d7282a2c62c7325/prek-0.3.1-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:1d03fb5fa37177dc37ccbe474252473adcbde63287a63e9fa3d5745470f95bd8", size = 4188473, upload-time = "2026-01-31T13:25:53.341Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/4c/af8f6a40cb094e88225514b89e8ae05ac69fc479d6b500e4b984f9ef8ae3/prek-0.3.1-py3-none-musllinux_1_1_i686.whl", hash = "sha256:3e20a5b704c06944dca9555a39f1de3622edc8ed2becaf8b3564925d1b7c1c0d", size = 4342239, upload-time = "2026-01-31T13:25:55.179Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/ba/6b0f725c0cf96182ab9622b4d122a01f04de9b2b6e4a6516874390218510/prek-0.3.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:6889acf0c9b0dd7b9cd3510ec36af10a739826d2b9de1e2d021ae6a9f130959a", size = 4618674, upload-time = "2026-01-31T13:25:59.175Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/49/caa320893640c9e72ed3d3e2bab7959faba54cefeea09be18c33d5f75baf/prek-0.3.1-py3-none-win32.whl", hash = "sha256:cc62a4bff79ed835d448098f0667c1a91915cec9cfac6c6246b675f0da46eded", size = 3928699, upload-time = "2026-01-31T13:25:33.168Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/19/15907abe245ae9a120abcebb0c50c6d456ba719eb640f7c57e4f5b2f00e3/prek-0.3.1-py3-none-win_amd64.whl", hash = "sha256:3515140def20bab85e53585b0beb90d894ff2915d2fdb4451b6a4588e16516d8", size = 4296106, upload-time = "2026-01-31T13:25:45.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/5e/9b994b5de36d6aa5caaf09a018d8fe4820db46e4da577c2fd7a1e176b56c/prek-0.3.1-py3-none-win_arm64.whl", hash = "sha256:cfa58365eb36753cff684dc3b00196c1163bb135fe72c6a1c6ebb1a179f5dbdf", size = 4021714, upload-time = "2026-01-31T13:25:34.993Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/69/70a5fc881290a63910494df2677c0fb241d27cfaa435bbcd0de5cd2e2443/prek-0.3.2-py3-none-linux_armv6l.whl", hash = "sha256:4f352f9c3fc98aeed4c8b2ec4dbf16fc386e45eea163c44d67e5571489bd8e6f", size = 4614960, upload-time = "2026-02-06T13:50:05.818Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/15/a82d5d32a2207ccae5d86ea9e44f2b93531ed000faf83a253e8d1108e026/prek-0.3.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4a000cfbc3a6ec7d424f8be3c3e69ccd595448197f92daac8652382d0acc2593", size = 4622889, upload-time = "2026-02-06T13:49:53.662Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/75/ea833b58a12741397017baef9b66a6e443bfa8286ecbd645d14111446280/prek-0.3.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5436bdc2702cbd7bcf9e355564ae66f8131211e65fefae54665a94a07c3d450a", size = 4239653, upload-time = "2026-02-06T13:50:02.88Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/b4/d9c3885987afac6e20df4cb7db14e3b0d5a08a77ae4916488254ebac4d0b/prek-0.3.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:0161b5f584f9e7f416d6cf40a17b98f17953050ff8d8350ec60f20fe966b86b6", size = 4595101, upload-time = "2026-02-06T13:49:49.813Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/a6/1a06473ed83dbc898de22838abdb13954e2583ce229f857f61828384634c/prek-0.3.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e641e8533bca38797eebb49aa89ed0e8db0e61225943b27008c257e3af4d631", size = 4521978, upload-time = "2026-02-06T13:49:41.266Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/5e/c38390d5612e6d86b32151c1d2fdab74a57913473193591f0eb00c894c21/prek-0.3.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfca1810d49d3f9ef37599c958c4e716bc19a1d78a7e88cbdcb332e0b008994f", size = 4829108, upload-time = "2026-02-06T13:49:44.598Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/a6/cecce2ab623747ff65ed990bb0d95fa38449ee19b348234862acf9392fff/prek-0.3.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d69d754299a95a85dc20196f633232f306bee7e7c8cba61791f49ce70404ec", size = 5357520, upload-time = "2026-02-06T13:49:48.512Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/18/d6bcb29501514023c76d55d5cd03bdbc037737c8de8b6bc41cdebfb1682c/prek-0.3.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:539dcb90ad9b20837968539855df6a29493b328a1ae87641560768eed4f313b0", size = 4852635, upload-time = "2026-02-06T13:49:58.347Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/0a/ae46f34ba27ba87aea5c9ad4ac9cd3e07e014fd5079ae079c84198f62118/prek-0.3.2-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:1998db3d0cbe243984736c82232be51318f9192e2433919a6b1c5790f600b5fd", size = 4599484, upload-time = "2026-02-06T13:49:43.296Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/a9/73bfb5b3f7c3583f9b0d431924873928705cdef6abb3d0461c37254a681b/prek-0.3.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:07ab237a5415a3e8c0db54de9d63899bcd947624bdd8820d26f12e65f8d19eb7", size = 4657694, upload-time = "2026-02-06T13:50:01.074Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/bc/0994bc176e1a80110fad3babce2c98b0ac4007630774c9e18fc200a34781/prek-0.3.2-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:0ced19701d69c14a08125f14a5dd03945982edf59e793c73a95caf4697a7ac30", size = 4509337, upload-time = "2026-02-06T13:49:54.891Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/13/e73f85f65ba8f626468e5d1694ab3763111513da08e0074517f40238c061/prek-0.3.2-py3-none-musllinux_1_1_i686.whl", hash = "sha256:ffb28189f976fa111e770ee94e4f298add307714568fb7d610c8a7095cb1ce59", size = 4697350, upload-time = "2026-02-06T13:50:04.526Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/47/98c46dcd580305b9960252a4eb966f1a7b1035c55c363f378d85662ba400/prek-0.3.2-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f63134b3eea14421789a7335d86f99aee277cb520427196f2923b9260c60e5c5", size = 4955860, upload-time = "2026-02-06T13:49:56.581Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/42/1bb4bba3ff47897df11e9dfd774027cdfa135482c961a54e079af0faf45a/prek-0.3.2-py3-none-win32.whl", hash = "sha256:58c806bd1344becd480ef5a5ba348846cc000af0e1fbe854fef91181a2e06461", size = 4267619, upload-time = "2026-02-06T13:49:39.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/11/6665f47a7c350d83de17403c90bbf7a762ef50876ece456a86f64f46fbfb/prek-0.3.2-py3-none-win_amd64.whl", hash = "sha256:70114b48e9eb8048b2c11b4c7715ce618529c6af71acc84dd8877871a2ef71a6", size = 4624324, upload-time = "2026-02-06T13:49:45.922Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/e7/740997ca82574d03426f897fd88afe3fc8a7306b8c7ea342a8bc1c538488/prek-0.3.2-py3-none-win_arm64.whl", hash = "sha256:9144d176d0daa2469a25c303ef6f6fa95a8df015eb275232f5cb53551ecefef0", size = 4336008, upload-time = "2026-02-06T13:49:52.27Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5245,24 +5260,24 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "2.51.0"
|
||||
version = "2.52.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6f/9f/094bbb6be5cf218ab6712c6528310687f3d3fe8818249fcfe1d74192f7c5/sentry_sdk-2.51.0.tar.gz", hash = "sha256:b89d64577075fd8c13088bc3609a2ce77a154e5beb8cba7cc16560b0539df4f7", size = 407447, upload-time = "2026-01-28T10:29:50.962Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/59/eb/1b497650eb564701f9a7b8a95c51b2abe9347ed2c0b290ba78f027ebe4ea/sentry_sdk-2.52.0.tar.gz", hash = "sha256:fa0bec872cfec0302970b2996825723d67390cdd5f0229fb9efed93bd5384899", size = 410273, upload-time = "2026-02-04T15:03:54.706Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/da/df379404d484ca9dede4ad8abead5de828cdcff35623cd44f0351cf6869c/sentry_sdk-2.51.0-py2.py3-none-any.whl", hash = "sha256:e21016d318a097c2b617bb980afd9fc737e1efc55f9b4f0cdc819982c9717d5f", size = 431426, upload-time = "2026-01-28T10:29:48.868Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/63/2c6daf59d86b1c30600bff679d039f57fd1932af82c43c0bde1cbc55e8d4/sentry_sdk-2.52.0-py2.py3-none-any.whl", hash = "sha256:931c8f86169fc6f2752cb5c4e6480f0d516112e78750c312e081ababecbaf2ed", size = 435547, upload-time = "2026-02-04T15:03:51.567Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "80.10.2"
|
||||
version = "82.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/76/95/faf61eb8363f26aa7e1d762267a8d602a1b26d4f3a1e758e92cb3cb8b054/setuptools-80.10.2.tar.gz", hash = "sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70", size = 1200343, upload-time = "2026-01-25T22:38:17.252Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/82/f3/748f4d6f65d1756b9ae577f329c951cda23fb900e4de9f70900ced962085/setuptools-82.0.0.tar.gz", hash = "sha256:22e0a2d69474c6ae4feb01951cb69d515ed23728cf96d05513d36e42b62b37cb", size = 1144893, upload-time = "2026-02-08T15:08:40.206Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/94/b8/f1f62a5e3c0ad2ff1d189590bfa4c46b4f3b6e49cef6f26c6ee4e575394d/setuptools-80.10.2-py3-none-any.whl", hash = "sha256:95b30ddfb717250edb492926c92b5221f7ef3fbcc2b07579bcd4a27da21d0173", size = 1064234, upload-time = "2026-01-25T22:38:15.216Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/c6/76dc613121b793286a3f91621d7b75a2b493e0390ddca50f11993eadf192/setuptools-82.0.0-py3-none-any.whl", hash = "sha256:70b18734b607bd1da571d097d236cfcfacaf01de45717d59e6e04b96877532e0", size = 1003468, upload-time = "2026-02-08T15:08:38.723Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5318,7 +5333,7 @@ dependencies = [
|
|||
{ name = "absl-py", version = "2.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "grpcio" },
|
||||
{ name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
||||
{ name = "markdown", version = "3.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "markdown", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
||||
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
|
||||
{ name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
|
|
@ -5581,7 +5596,7 @@ resolution-markers = [
|
|||
dependencies = [
|
||||
{ name = "cuda-bindings", marker = "python_full_version >= '3.10' and platform_machine == 'x86_64' and sys_platform == 'linux'" },
|
||||
{ name = "filelock", version = "3.20.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "fsspec", version = "2026.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "fsspec", version = "2026.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
||||
{ name = "jinja2", marker = "python_full_version >= '3.10'" },
|
||||
{ name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
|
||||
{ name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
|
|
@ -5606,6 +5621,10 @@ dependencies = [
|
|||
{ name = "typing-extensions", marker = "python_full_version >= '3.10'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/ea/304cf7afb744aa626fa9855245526484ee55aba610d9973a0521c552a843/torch-2.10.0-1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:c37fc46eedd9175f9c81814cc47308f1b42cfe4987e532d4b423d23852f2bf63", size = 79411450, upload-time = "2026-02-06T17:37:35.75Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/d8/9e6b8e7df981a1e3ea3907fd5a74673e791da483e8c307f0b6ff012626d0/torch-2.10.0-1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:f699f31a236a677b3118bc0a3ef3d89c0c29b5ec0b20f4c4bf0b110378487464", size = 79423460, upload-time = "2026-02-06T17:37:39.657Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/2f/0b295dd8d199ef71e6f176f576473d645d41357b7b8aa978cc6b042575df/torch-2.10.0-1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6abb224c2b6e9e27b592a1c0015c33a504b00a0e0938f1499f7f514e9b7bfb5c", size = 79498197, upload-time = "2026-02-06T17:37:27.627Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/1b/af5fccb50c341bd69dc016769503cb0857c1423fbe9343410dfeb65240f2/torch-2.10.0-1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:7350f6652dfd761f11f9ecb590bfe95b573e2961f7a242eccb3c8e78348d26fe", size = 79498248, upload-time = "2026-02-06T17:37:31.982Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/1a/c61f36cfd446170ec27b3a4984f072fd06dab6b5d7ce27e11adb35d6c838/torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d", size = 145992962, upload-time = "2026-01-21T16:24:14.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/60/6662535354191e2d1555296045b63e4279e5a9dbad49acf55a5d38655a39/torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444", size = 915599237, upload-time = "2026-01-21T16:23:25.497Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/b8/66bbe96f0d79be2b5c697b2e0b187ed792a15c6c4b8904613454651db848/torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb", size = 113720931, upload-time = "2026-01-21T16:24:23.743Z" },
|
||||
|
|
@ -5864,26 +5883,26 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "ty"
|
||||
version = "0.0.14"
|
||||
version = "0.0.15"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/af/57/22c3d6bf95c2229120c49ffc2f0da8d9e8823755a1c3194da56e51f1cc31/ty-0.0.14.tar.gz", hash = "sha256:a691010565f59dd7f15cf324cdcd1d9065e010c77a04f887e1ea070ba34a7de2", size = 5036573, upload-time = "2026-01-27T00:57:31.427Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4e/25/257602d316b9333089b688a7a11b33ebc660b74e8dacf400dc3dfdea1594/ty-0.0.15.tar.gz", hash = "sha256:4f9a5b8df208c62dba56e91b93bed8b5bb714839691b8cff16d12c983bfa1174", size = 5101936, upload-time = "2026-02-05T01:06:34.922Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/99/cb/cc6d1d8de59beb17a41f9a614585f884ec2d95450306c173b3b7cc090d2e/ty-0.0.14-py3-none-linux_armv6l.whl", hash = "sha256:32cf2a7596e693094621d3ae568d7ee16707dce28c34d1762947874060fdddaa", size = 10034228, upload-time = "2026-01-27T00:57:53.133Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/96/dd42816a2075a8f31542296ae687483a8d047f86a6538dfba573223eaf9a/ty-0.0.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f971bf9805f49ce8c0968ad53e29624d80b970b9eb597b7cbaba25d8a18ce9a2", size = 9939162, upload-time = "2026-01-27T00:57:43.857Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/b4/73c4859004e0f0a9eead9ecb67021438b2e8e5fdd8d03e7f5aca77623992/ty-0.0.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:45448b9e4806423523268bc15e9208c4f3f2ead7c344f615549d2e2354d6e924", size = 9418661, upload-time = "2026-01-27T00:58:03.411Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/35/839c4551b94613db4afa20ee555dd4f33bfa7352d5da74c5fa416ffa0fd2/ty-0.0.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee94a9b747ff40114085206bdb3205a631ef19a4d3fb89e302a88754cbbae54c", size = 9837872, upload-time = "2026-01-27T00:57:23.718Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/2b/bbecf7e2faa20c04bebd35fc478668953ca50ee5847ce23e08acf20ea119/ty-0.0.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6756715a3c33182e9ab8ffca2bb314d3c99b9c410b171736e145773ee0ae41c3", size = 9848819, upload-time = "2026-01-27T00:57:58.501Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/60/3c0ba0f19c0f647ad9d2b5b5ac68c0f0b4dc899001bd53b3a7537fb247a2/ty-0.0.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89d0038a2f698ba8b6fec5cf216a4e44e2f95e4a5095a8c0f57fe549f87087c2", size = 10324371, upload-time = "2026-01-27T00:57:29.291Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/32/99d0a0b37d0397b0a989ffc2682493286aa3bc252b24004a6714368c2c3d/ty-0.0.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c64a83a2d669b77f50a4957039ca1450626fb474619f18f6f8a3eb885bf7544", size = 10865898, upload-time = "2026-01-27T00:57:33.542Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/88/30b583a9e0311bb474269cfa91db53350557ebec09002bfc3fb3fc364e8c/ty-0.0.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:242488bfb547ef080199f6fd81369ab9cb638a778bb161511d091ffd49c12129", size = 10555777, upload-time = "2026-01-27T00:58:05.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/a2/cb53fb6325dcf3d40f2b1d0457a25d55bfbae633c8e337bde8ec01a190eb/ty-0.0.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4790c3866f6c83a4f424fc7d09ebdb225c1f1131647ba8bdc6fcdc28f09ed0ff", size = 10412913, upload-time = "2026-01-27T00:57:38.834Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/8f/f2f5202d725ed1e6a4e5ffaa32b190a1fe70c0b1a2503d38515da4130b4c/ty-0.0.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:950f320437f96d4ea9a2332bbfb5b68f1c1acd269ebfa4c09b6970cc1565bd9d", size = 9837608, upload-time = "2026-01-27T00:57:55.898Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/ba/59a2a0521640c489dafa2c546ae1f8465f92956fede18660653cce73b4c5/ty-0.0.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4a0ec3ee70d83887f86925bbc1c56f4628bd58a0f47f6f32ddfe04e1f05466df", size = 9884324, upload-time = "2026-01-27T00:57:46.786Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/95/8d2a49880f47b638743212f011088552ecc454dd7a665ddcbdabea25772a/ty-0.0.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a1a4e6b6da0c58b34415955279eff754d6206b35af56a18bb70eb519d8d139ef", size = 10033537, upload-time = "2026-01-27T00:58:01.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/40/4523b36f2ce69f92ccf783855a9e0ebbbd0f0bb5cdce6211ee1737159ed3/ty-0.0.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dc04384e874c5de4c5d743369c277c8aa73d1edea3c7fc646b2064b637db4db3", size = 10495910, upload-time = "2026-01-27T00:57:26.691Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/d5/655beb51224d1bfd4f9ddc0bb209659bfe71ff141bcf05c418ab670698f0/ty-0.0.14-py3-none-win32.whl", hash = "sha256:b20e22cf54c66b3e37e87377635da412d9a552c9bf4ad9fc449fed8b2e19dad2", size = 9507626, upload-time = "2026-01-27T00:57:41.43Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/d9/c569c9961760e20e0a4bc008eeb1415754564304fd53997a371b7cf3f864/ty-0.0.14-py3-none-win_amd64.whl", hash = "sha256:e312ff9475522d1a33186657fe74d1ec98e4a13e016d66f5758a452c90ff6409", size = 10437980, upload-time = "2026-01-27T00:57:36.422Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/0c/186829654f5bfd9a028f6648e9caeb11271960a61de97484627d24443f91/ty-0.0.14-py3-none-win_arm64.whl", hash = "sha256:b6facdbe9b740cb2c15293a1d178e22ffc600653646452632541d01c36d5e378", size = 9885831, upload-time = "2026-01-27T00:57:49.747Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/c5/35626e732b79bf0e6213de9f79aff59b5f247c0a1e3ce0d93e675ab9b728/ty-0.0.15-py3-none-linux_armv6l.whl", hash = "sha256:68e092458516c61512dac541cde0a5e4e5842df00b4e81881ead8f745ddec794", size = 10138374, upload-time = "2026-02-05T01:07:03.804Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/8a/48fd81664604848f79d03879b3ca3633762d457a069b07e09fb1b87edd6e/ty-0.0.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:79f2e75289eae3cece94c51118b730211af4ba5762906f52a878041b67e54959", size = 9947858, upload-time = "2026-02-05T01:06:47.453Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/85/c1ac8e97bcd930946f4c94db85b675561d590b4e72703bf3733419fc3973/ty-0.0.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:112a7b26e63e48cc72c8c5b03227d1db280cfa57a45f2df0e264c3a016aa8c3c", size = 9443220, upload-time = "2026-02-05T01:06:44.98Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/d9/244bc02599d950f7a4298fbc0c1b25cc808646b9577bdf7a83470b2d1cec/ty-0.0.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71f62a2644972975a657d9dc867bf901235cde51e8d24c20311067e7afd44a56", size = 9949976, upload-time = "2026-02-05T01:07:01.515Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/ab/3a0daad66798c91a33867a3ececf17d314ac65d4ae2bbbd28cbfde94da63/ty-0.0.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e48b42be2d257317c85b78559233273b655dd636fc61e7e1d69abd90fd3cba4", size = 9965918, upload-time = "2026-02-05T01:06:54.283Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/4e/e62b01338f653059a7c0cd09d1a326e9a9eedc351a0f0de9db0601658c3d/ty-0.0.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27dd5b52a421e6871c5bfe9841160331b60866ed2040250cb161886478ab3e4f", size = 10424943, upload-time = "2026-02-05T01:07:08.777Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/b5/7aa06655ce69c0d4f3e845d2d85e79c12994b6d84c71699cfb437e0bc8cf/ty-0.0.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76b85c9ec2219e11c358a7db8e21b7e5c6674a1fb9b6f633836949de98d12286", size = 10964692, upload-time = "2026-02-05T01:06:37.103Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/04/36fdfe1f3c908b471e246e37ce3d011175584c26d3853e6c5d9a0364564c/ty-0.0.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e8204c61d8ede4f21f2975dce74efdb80fafb2fae1915c666cceb33ea3c90b", size = 10692225, upload-time = "2026-02-05T01:06:49.714Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/41/5bf882649bd8b64ded5fbce7fb8d77fb3b868de1a3b1a6c4796402b47308/ty-0.0.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af87c3be7c944bb4d6609d6c63e4594944b0028c7bd490a525a82b88fe010d6d", size = 10516776, upload-time = "2026-02-05T01:06:52.047Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/75/66852d7e004f859839c17ffe1d16513c1e7cc04bcc810edb80ca022a9124/ty-0.0.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:50dccf7398505e5966847d366c9e4c650b8c225411c2a68c32040a63b9521eea", size = 9928828, upload-time = "2026-02-05T01:06:56.647Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/72/96bc16c7b337a3ef358fd227b3c8ef0c77405f3bfbbfb59ee5915f0d9d71/ty-0.0.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:bd797b8f231a4f4715110259ad1ad5340a87b802307f3e06d92bfb37b858a8f3", size = 9978960, upload-time = "2026-02-05T01:06:29.567Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/18/d2e316a35b626de2227f832cd36d21205e4f5d96fd036a8af84c72ecec1b/ty-0.0.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9deb7f20e18b25440a9aa4884f934ba5628ef456dbde91819d5af1a73da48af3", size = 10135903, upload-time = "2026-02-05T01:06:59.256Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/d3/b617a79c9dad10c888d7c15cd78859e0160b8772273637b9c4241a049491/ty-0.0.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7b31b3de031255b90a5f4d9cb3d050feae246067c87130e5a6861a8061c71754", size = 10615879, upload-time = "2026-02-05T01:07:06.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/b0/2652a73c71c77296a6343217063f05745da60c67b7e8a8e25f2064167fce/ty-0.0.15-py3-none-win32.whl", hash = "sha256:9362c528ceb62c89d65c216336d28d500bc9f4c10418413f63ebc16886e16cc1", size = 9578058, upload-time = "2026-02-05T01:06:42.928Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/6e/08a4aedebd2a6ce2784b5bc3760e43d1861f1a184734a78215c2d397c1df/ty-0.0.15-py3-none-win_amd64.whl", hash = "sha256:4db040695ae67c5524f59cb8179a8fa277112e69042d7dfdac862caa7e3b0d9c", size = 10457112, upload-time = "2026-02-05T01:06:39.885Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/be/1991f2bc12847ae2d4f1e3ac5dcff8bb7bc1261390645c0755bb55616355/ty-0.0.15-py3-none-win_arm64.whl", hash = "sha256:e5a98d4119e77d6136461e16ae505f8f8069002874ab073de03fbcb1a5e8bf25", size = 9937490, upload-time = "2026-02-05T01:06:32.388Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6040,11 +6059,11 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "types-setuptools"
|
||||
version = "80.10.0.20260124"
|
||||
version = "81.0.0.20260209"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/aa/7e/116539b9610585e34771611e33c88a4c706491fa3565500f5a63139f8731/types_setuptools-80.10.0.20260124.tar.gz", hash = "sha256:1b86d9f0368858663276a0cbe5fe5a9722caf94b5acde8aba0399a6e90680f20", size = 43299, upload-time = "2026-01-24T03:18:39.527Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9e/57/f1f7992d6d7bded78d1f14dc23d59e87601920852bf10ece2325e49bacae/types_setuptools-81.0.0.20260209.tar.gz", hash = "sha256:2c2eb64499b41b672c387f6f45678a28d20a143a81b45a5c77acbfd4da0df3e1", size = 43201, upload-time = "2026-02-09T04:14:15.505Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/7f/016dc5cc718ec6ccaa84fb73ed409ef1c261793fd5e637cdfaa18beb40a9/types_setuptools-80.10.0.20260124-py3-none-any.whl", hash = "sha256:efed7e044f01adb9c2806c7a8e1b6aa3656b8e382379b53d5f26ee3db24d4c01", size = 64333, upload-time = "2026-01-24T03:18:38.344Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/87/90c9143af95850bdaf7eb0d47c59e5c3a8b55fc5a49aca0eb7f98cb964d5/types_setuptools-81.0.0.20260209-py3-none-any.whl", hash = "sha256:4facf71e3f953f8f5ac0020cd6c1b5e493aaff0183e85830bc34870b6abf8475", size = 64194, upload-time = "2026-02-09T04:14:14.278Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6141,37 +6160,36 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "uv"
|
||||
version = "0.9.29"
|
||||
version = "0.10.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/97/71/8a5bf591f3d9674e0a9144567d2e0a16fd04a33b4ab8ecfc902f1551c709/uv-0.9.29.tar.gz", hash = "sha256:140422df01de34dc335bd29827ae6aec6ecb2b92c2ee8ed6bc6dbeee50ac2f4e", size = 3838234, upload-time = "2026-02-03T19:39:06.702Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/09/36/f7fe4de0ad81234ac43938fe39c6ba84595c6b3a1868d786a4d7ad19e670/uv-0.10.0.tar.gz", hash = "sha256:ad01dd614a4bb8eb732da31ade41447026427397c5ad171cc98bd59579ef57ea", size = 3854103, upload-time = "2026-02-05T20:57:55.248Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/83/35/a8d744a2866d176a16c02ead8d277e0b02ae587a68c89cb2b5b9b8bcf602/uv-0.9.29-py3-none-linux_armv6l.whl", hash = "sha256:54fc0056a8f41b43e41c4c677632f751842f5d94b91dea4d547086448a8325eb", size = 21998377, upload-time = "2026-02-03T19:38:24.678Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/82/92b539e445c75706cbc8b9ac00291ee2923602e68109d73dffa9ab412257/uv-0.9.29-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:66a5f5c5ecf62f32b8d71383760a422aa9a2a2798cbb6424fb25ccfa8fd53a81", size = 21032721, upload-time = "2026-02-03T19:38:44.791Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/e8/0489cb87d25a9b06ec3b867fecfd32a9a054dcef8c889662c153d20bba3d/uv-0.9.29-py3-none-macosx_11_0_arm64.whl", hash = "sha256:11aad2d15a9e78551f656886ce604810f872fa2452127216f8ff5d75febae26e", size = 19824587, upload-time = "2026-02-03T19:38:17.32Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/09/8e06484d3f1713170926b356913deb0cf25f14ba6c77d765afdbac33e07c/uv-0.9.29-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:4f118141a84862b96f4a4f2bf5e2436f65a8b572706861e0d4585f4bc87fdac0", size = 21616388, upload-time = "2026-02-03T19:38:52.269Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/da/0c5cfd9d0296c78968fb588ca5a018a6b0e0132bdf3b0fca712cd0ffa938/uv-0.9.29-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:ca5effa2b227a989f341197248551be00919d3dbd13e9d03fabd1af26a9f9d41", size = 21622407, upload-time = "2026-02-03T19:39:12.999Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/3f/7c14c282b3d258a274d382c0e03b13fafac99483590476ceb01ca54e2b9d/uv-0.9.29-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d227644f94c66abf82eb51f33cb03a3e2e50f00d502438bc2f0bae1f4ae0e5a5", size = 21585617, upload-time = "2026-02-03T19:38:21.272Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/d9/4db58a2f5d311a0549d1f0855e1f650364265e743709ef81824cf86c7ae6/uv-0.9.29-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14a6c27f7c61ca1dc9c6edf53d39e9f289531873c8488ed24bd15e49353a485c", size = 22794114, upload-time = "2026-02-03T19:38:59.809Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/41/4d4df6dd7e88bea33557c3b6fd36e054e057cf8dfd64b8e97b4f40c8d170/uv-0.9.29-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5c99fd20ae5a98066c03e06a8f4c5a68e71acf8029d1ab7eba682f7166696b52", size = 24121009, upload-time = "2026-02-03T19:38:13.137Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/ef/9a82a1bf3c5d23dd4ecf3c8778fc8ffc241e671fef519e3e7722884e93ba/uv-0.9.29-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:113cbe21a39fa2cfbe146333141561e015a67dfaec7d12415c7ec6ff9f878754", size = 23655975, upload-time = "2026-02-03T19:38:28.713Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/f5/6158eaf6558962ca6a7c17ecbe14a2434166d5a0dae9712aca16b8520f46/uv-0.9.29-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d36fe3f9de3a37f7d712ee51ebf42d97df7a00ec901b02b6306c7ebbab8c6a76", size = 22881973, upload-time = "2026-02-03T19:39:03.854Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/fa/e725329efb484997fd60018d62f931901f3d25a04b95278845c1ad25b00d/uv-0.9.29-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae09db1bbdad5c38c508876a5903a322951539146f14c7567448bdcdea67e1fe", size = 22760712, upload-time = "2026-02-03T19:38:33.372Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/2f/8a2e4ad9a8024ceb10c04a9c386220d53107e6f3bff7a246fe36622b5342/uv-0.9.29-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:aaf650ddf20a6029a59c136eaeade720655c07bfbbd4e7867cc9b6167b0abae9", size = 21721267, upload-time = "2026-02-03T19:38:09.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/05/8a3b8a190b5ffb9b0d07d10f6f962e29e0f5aa4209415e78bf0514e2394a/uv-0.9.29-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:4095f5763c69d75f324d81e799d90c682f63f4789f7b8ad4297484262ecdeffd", size = 22426985, upload-time = "2026-02-03T19:38:48.4Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/1d/af83aeebb75062c8539ffdeaa7474ff3c7acb6263d6d7ead28219c71f5d8/uv-0.9.29-py3-none-musllinux_1_1_i686.whl", hash = "sha256:52a6934cbbb3dd339c24e8de1cdd0d3239b82ce5e65289e0b13055009abf2bc1", size = 22051690, upload-time = "2026-02-03T19:39:09.552Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/65/fe381859f237a5d2b271bc69215ebc5b87cbfd156ad901927921ef82b2e1/uv-0.9.29-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:367cb2a7ab2138b796caf5b402e343ef47f93329ae5d08a05d7bcfeca51b19e7", size = 22968942, upload-time = "2026-02-03T19:38:05.09Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/04/155263d673c980da9b513673d9a61bb8a5a98547c8e42af3613881ca54e1/uv-0.9.29-py3-none-win32.whl", hash = "sha256:fcb17d9576598f536a04139beefd82187e84db3e6d11a16fa5507f5d3d414f28", size = 20890568, upload-time = "2026-02-03T19:38:40.928Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/0a/450bd74385c4da3d83639946eaf39ca5bbcb69e73a0433d3bcc65af096d0/uv-0.9.29-py3-none-win_amd64.whl", hash = "sha256:b823c17132b851bf452e38f68e5dd39de9b433c39e2cd3aec2a1734b1594c295", size = 23465607, upload-time = "2026-02-03T19:38:37.411Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/2a/0d4a615f36d53a7cf1992351c395b17367783cface5afa5976db4c96675d/uv-0.9.29-py3-none-win_arm64.whl", hash = "sha256:22ab5e68d2d6a283a0a290e9b4a3ce53fef55f6ae197a5f6a58b7f4c605f21c8", size = 21911432, upload-time = "2026-02-03T19:38:55.987Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/69/33fb64aee6ba138b1aaf957e20778e94a8c23732e41cdf68e6176aa2cf4e/uv-0.10.0-py3-none-linux_armv6l.whl", hash = "sha256:38dc0ccbda6377eb94095688c38e5001b8b40dfce14b9654949c1f0b6aa889df", size = 21984662, upload-time = "2026-02-05T20:57:19.076Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/5a/e3ff8a98cfbabc5c2d09bf304d2d9d2d7b2e7d60744241ac5ed762015e5c/uv-0.10.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a165582c1447691109d49d09dccb065d2a23852ff42bf77824ff169909aa85da", size = 21057249, upload-time = "2026-02-05T20:56:48.921Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/77/ec8f24f8d0f19c4fda0718d917bb78b9e6f02a4e1963b401f1c4f4614a54/uv-0.10.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:aefea608971f4f23ac3dac2006afb8eb2b2c1a2514f5fee1fac18e6c45fd70c4", size = 19827174, upload-time = "2026-02-05T20:57:10.581Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/7e/09b38b93208906728f591f66185a425be3acdb97c448460137d0e6ecb30a/uv-0.10.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:d4b621bcc5d0139502789dc299bae8bf55356d07b95cb4e57e50e2afcc5f43e1", size = 21629522, upload-time = "2026-02-05T20:57:29.959Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/f3/48d92c90e869331306979efaa29a44c3e7e8376ae343edc729df0d534dfb/uv-0.10.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:b4bea728a6b64826d0091f95f28de06dd2dc786384b3d336a90297f123b4da0e", size = 21614812, upload-time = "2026-02-05T20:56:58.103Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/43/d0dedfcd4fe6e36cabdbeeb43425cd788604db9d48425e7b659d0f7ba112/uv-0.10.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc0cc2a4bcf9efbff9a57e2aed21c2d4b5a7ec2cc0096e0c33d7b53da17f6a3b", size = 21577072, upload-time = "2026-02-05T20:57:45.455Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/90/b8c9320fd8d86f356e37505a02aa2978ed28f9c63b59f15933e98bce97e5/uv-0.10.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:070ca2f0e8c67ca9a8f70ce403c956b7ed9d51e0c2e9dbbcc4efa5e0a2483f79", size = 22829664, upload-time = "2026-02-05T20:57:22.689Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/9c/2c36b30b05c74b2af0e663e0e68f1d10b91a02a145e19b6774c121120c0b/uv-0.10.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8070c66149c06f9b39092a06f593a2241345ea2b1d42badc6f884c2cc089a1b1", size = 23705815, upload-time = "2026-02-05T20:57:37.604Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/a1/8c7fdb14ab72e26ca872e07306e496a6b8cf42353f9bf6251b015be7f535/uv-0.10.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3db1d5390b3a624de672d7b0f9c9d8197693f3b2d3d9c4d9e34686dcbc34197a", size = 22890313, upload-time = "2026-02-05T20:57:26.35Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/f8/5c152350b1a6d0af019801f91a1bdeac854c33deb36275f6c934f0113cb5/uv-0.10.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82b46db718763bf742e986ebbc7a30ca33648957a0dcad34382970b992f5e900", size = 22769440, upload-time = "2026-02-05T20:56:53.859Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/44/980e5399c6f4943b81754be9b7deb87bd56430e035c507984e17267d6a97/uv-0.10.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:eb95d28590edd73b8fdd80c27d699c45c52f8305170c6a90b830caf7f36670a4", size = 21695296, upload-time = "2026-02-05T20:57:06.732Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/e7/f44ad40275be2087b3910df4678ed62cf0c82eeb3375c4a35037a79747db/uv-0.10.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5871eef5046a81df3f1636a3d2b4ccac749c23c7f4d3a4bae5496cb2876a1814", size = 22424291, upload-time = "2026-02-05T20:57:49.067Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/81/31c0c0a8673140756e71a1112bf8f0fcbb48a4cf4587a7937f5bd55256b6/uv-0.10.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:1af0ec125a07edb434dfaa98969f6184c1313dbec2860c3c5ce2d533b257132a", size = 22109479, upload-time = "2026-02-05T20:57:02.258Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/d1/2eb51bc233bad3d13ad64a0c280fd4d1ebebf5c2939b3900a46670fa2b91/uv-0.10.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:45909b9a734250da05b10101e0a067e01ffa2d94bbb07de4b501e3cee4ae0ff3", size = 22972087, upload-time = "2026-02-05T20:57:52.847Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/f7/49987207b87b5c21e1f0e81c52892813e8cdf7e318b6373d6585773ebcdd/uv-0.10.0-py3-none-win32.whl", hash = "sha256:d5498851b1f07aa9c9af75578b2029a11743cb933d741f84dcbb43109a968c29", size = 20896746, upload-time = "2026-02-05T20:57:33.426Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/b2/1370049596c6ff7fa1fe22fccf86a093982eac81017b8c8aff541d7263b2/uv-0.10.0-py3-none-win_amd64.whl", hash = "sha256:edd469425cd62bcd8c8cc0226c5f9043a94e37ed869da8268c80fdbfd3e5015e", size = 23433041, upload-time = "2026-02-05T20:57:41.41Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/76/1034c46244feafec2c274ac52b094f35d47c94cdb11461c24cf4be8a0c0c/uv-0.10.0-py3-none-win_arm64.whl", hash = "sha256:e90c509749b3422eebb54057434b7119892330d133b9690a88f8a6b0f3116be3", size = 21880261, upload-time = "2026-02-05T20:57:14.724Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.5.3"
|
||||
version = "0.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c2/62/a7c072fbfefb2980a00f99ca994279cb9ecf310cb2e6b2a4d2a28fe192b3/wcwidth-0.5.3.tar.gz", hash = "sha256:53123b7af053c74e9fe2e92ac810301f6139e64379031f7124574212fb3b4091", size = 157587, upload-time = "2026-01-31T03:52:10.92Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/c1/d73f12f8cdb1891334a2ccf7389eed244d3941e74d80dd220badb937f3fb/wcwidth-0.5.3-py3-none-any.whl", hash = "sha256:d584eff31cd4753e1e5ff6c12e1edfdb324c995713f75d26c29807bb84bf649e", size = 92981, upload-time = "2026-01-31T03:52:09.14Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6381,16 +6399,15 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "z3-solver"
|
||||
version = "4.15.4.0"
|
||||
version = "4.15.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8a/8e/0c8f17309549d2e5cde9a3ccefa6365437f1e7bafe71878eaf9478e47b18/z3_solver-4.15.4.0.tar.gz", hash = "sha256:928c29b58c4eb62106da51c1914f6a4a55d0441f8f48a81b9da07950434a8946", size = 5018600, upload-time = "2025-10-29T18:12:03.062Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fd/5d/810ba04f7e7f2f2e5f019dd75237d1a16b7388a0c72f7e532b27dde9f7e2/z3_solver-4.15.7.0.tar.gz", hash = "sha256:a26b91f861b6d13bb76f0ac568d3ef1c0a4801e70a135f80e66b49628565a460", size = 5071448, upload-time = "2026-02-09T01:08:40.767Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/63/33/a3d5d2eaeb0f7b3174d57d405437eabb2075d4d50bd9ea0957696c435c7b/z3_solver-4.15.4.0-py3-none-macosx_13_0_arm64.whl", hash = "sha256:407e825cc9211f95ef46bdc8d151bf630e7ab2d62a21d24cd74c09cc5b73f3aa", size = 37052538, upload-time = "2025-10-29T18:11:46.233Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/84/fd7ffac1551cd9f8d44fe41358f738be670fc4c24dfd514fab503f2cf3e7/z3_solver-4.15.4.0-py3-none-macosx_13_0_x86_64.whl", hash = "sha256:00bd10c5a6a5f6112d3a9a810d0799227e52f76caa860dafa5e00966bb47eb13", size = 39807925, upload-time = "2025-10-29T18:11:49.81Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/c9/bb51a96af0091324c81b803f16c49f719f9f6ea0b0bb52200f5c97ec4892/z3_solver-4.15.4.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e103a6f203f505b8b8b8e5c931cc407c95b61556512d4921c1ddc0b3f41b08e", size = 29268352, upload-time = "2025-10-29T18:11:53.032Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/2e/0b49f7e4e53817cfb09a0f6585012b782dfe0b666e8abefcb4fac0570606/z3_solver-4.15.4.0-py3-none-manylinux_2_34_aarch64.whl", hash = "sha256:62c7e9cbdd711932301f29919ad9158de9b2f58b4d281dd259bbcd0a2f408ba1", size = 27226534, upload-time = "2025-10-29T18:11:55.59Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/91/33de49538444d4aafbe47415c450c2f9abab1733e1226f276b496672f46c/z3_solver-4.15.4.0-py3-none-win32.whl", hash = "sha256:be3bc916545c96ffbf89e00d07104ff14f78336e55db069177a1bfbcc01b269d", size = 13191672, upload-time = "2025-10-29T18:11:58.424Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/d6/a0b135e4419df475177ae78fc93c422430b0fd8875649486f9a5989772e6/z3_solver-4.15.4.0-py3-none-win_amd64.whl", hash = "sha256:00e35b02632ed085ea8199fb230f6015e6fc40554a6680c097bd5f060e827431", size = 16259597, upload-time = "2025-10-29T18:12:01.14Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/1b/d21f292b473c1c40bedf41d113577ae2bb7fcc715f54d42c10b7f2b3a186/z3_solver-4.15.7.0-py3-none-macosx_15_0_arm64.whl", hash = "sha256:a6c967677c67296a8b7c97dff68107f029c576a94cfb4abc9e08bf72e5499e5d", size = 36987369, upload-time = "2026-02-09T01:08:27.585Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/36/132c3d03de2eed160fad123207c981507193b2621e05b2909563775e0ad9/z3_solver-4.15.7.0-py3-none-macosx_15_0_x86_64.whl", hash = "sha256:a9644e958252dfdbdae2f787a8192fe4b8c156e7cf7b0e00a6a59e896a27569d", size = 47560235, upload-time = "2026-02-09T01:08:30.415Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/49/40b0ee7cd2425dfa05bde5776f6aa7e892460a5ca8016171204f9b2d42df/z3_solver-4.15.7.0-py3-none-win32.whl", hash = "sha256:2dd09ac8afde63035d9c0a63b23d448726e374ec588b67b5f5edce9d7e9b1a13", size = 13342998, upload-time = "2026-02-09T01:08:33.84Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/ab/5a60c6ed712eb97749cd758162842cec771cfbe2c37ea43a251dc6fe583b/z3_solver-4.15.7.0-py3-none-win_amd64.whl", hash = "sha256:17f5ccea921d6a11bba5880281048c9f4a1e0c35f76e8ce69e72826c90c230bd", size = 16427563, upload-time = "2026-02-09T01:08:35.884Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/1f/ea28f6b3dec9cbab32cf851b3a529c9fb8332300c7419a55ab68ef5b40ac/z3_solver-4.15.7.0-py3-none-win_arm64.whl", hash = "sha256:9bf1a350598bc92ece90220073fe47c0b0f8cbbeaaf62974de736bd79947f8bd", size = 15082309, upload-time = "2026-02-09T01:08:38.832Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
Loading…
Reference in a new issue