prouter 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prouter-0.1.0/.github/workflows/cd.yml +42 -0
- prouter-0.1.0/.github/workflows/ci.yml +35 -0
- prouter-0.1.0/.gitignore +34 -0
- prouter-0.1.0/LICENSE +21 -0
- prouter-0.1.0/PKG-INFO +73 -0
- prouter-0.1.0/README.md +64 -0
- prouter-0.1.0/pyproject.toml +42 -0
- prouter-0.1.0/src/prouter/__init__.py +5 -0
- prouter-0.1.0/src/prouter/router.py +334 -0
- prouter-0.1.0/tests/test_smoke.py +12 -0
- prouter-0.1.0/uv.lock +263 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: CD
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
|
|
13
|
+
- name: Install uv
|
|
14
|
+
uses: astral-sh/setup-uv@v5
|
|
15
|
+
|
|
16
|
+
- name: Build distributions
|
|
17
|
+
run: uv build
|
|
18
|
+
|
|
19
|
+
- name: Upload artifacts
|
|
20
|
+
uses: actions/upload-artifact@v4
|
|
21
|
+
with:
|
|
22
|
+
name: dist
|
|
23
|
+
path: dist/
|
|
24
|
+
|
|
25
|
+
publish:
|
|
26
|
+
needs: build
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
environment:
|
|
29
|
+
name: pypi
|
|
30
|
+
url: https://pypi.org/project/prouter/
|
|
31
|
+
permissions:
|
|
32
|
+
# IMPORTANT: required for PyPI trusted publishing (OIDC)
|
|
33
|
+
id-token: write
|
|
34
|
+
steps:
|
|
35
|
+
- name: Download artifacts
|
|
36
|
+
uses: actions/download-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist
|
|
39
|
+
path: dist/
|
|
40
|
+
|
|
41
|
+
- name: Publish to PyPI
|
|
42
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
ci:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v5
|
|
21
|
+
with:
|
|
22
|
+
enable-cache: true
|
|
23
|
+
python-version: "3.12"
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: uv sync --all-extras --dev
|
|
27
|
+
|
|
28
|
+
- name: Ruff lint
|
|
29
|
+
run: uv run ruff check --output-format=github .
|
|
30
|
+
|
|
31
|
+
- name: Ruff format check
|
|
32
|
+
run: uv run ruff format --check .
|
|
33
|
+
|
|
34
|
+
- name: Run tests
|
|
35
|
+
run: uv run pytest
|
prouter-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
*.egg
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
|
|
14
|
+
# uv
|
|
15
|
+
.uv/
|
|
16
|
+
|
|
17
|
+
# Jupyter
|
|
18
|
+
.ipynb_checkpoints/
|
|
19
|
+
|
|
20
|
+
# macOS
|
|
21
|
+
.DS_Store
|
|
22
|
+
|
|
23
|
+
# IDE
|
|
24
|
+
.vscode/
|
|
25
|
+
.idea/
|
|
26
|
+
|
|
27
|
+
# Distribution
|
|
28
|
+
*.whl
|
|
29
|
+
*.tar.gz
|
|
30
|
+
|
|
31
|
+
# CI
|
|
32
|
+
.pytest_cache/
|
|
33
|
+
.ruff_cache/
|
|
34
|
+
|
prouter-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 WISC Lab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
prouter-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: prouter
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Route filesystem paths through pattern -> handler -> pattern rules with visibility.
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: pandas>=3.0.3
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# prouter
|
|
11
|
+
|
|
12
|
+
[](https://pypi.org/project/prouter)
|
|
13
|
+
[](https://kidspeech.wisc.edu/)
|
|
14
|
+

|
|
15
|
+
|
|
16
|
+
[](https://github.com/WISCLab/prouter/actions/workflows/ci.yml)
|
|
17
|
+
[](https://github.com/WISCLab/prouter/actions/workflows/cd.yml)
|
|
18
|
+
|
|
19
|
+
Route filesystem paths through `pattern -> handler -> pattern` rules with visibility.
|
|
20
|
+
|
|
21
|
+
The idea is simple. You define routes, each one an input pattern, a handler that rewrites a path, and an output pattern the result has to match. prouter walks a directory tree and runs every path through the route whose input pattern matches its basename, keeping track of what it did along the way.
|
|
22
|
+
|
|
23
|
+
It never touches the filesystem. Nothing gets moved or renamed. What you get back is a set of CSVs describing the transform it would apply, so you can look it over before committing to anything.
|
|
24
|
+
|
|
25
|
+
The tree is walked bottom-up, deepest paths first and the root last. This is deliberate: renaming children before their parents means a directory rename never invalidates paths you haven't reached yet. The CSVs preserve that order, so applying the rows top to bottom is always safe.
|
|
26
|
+
|
|
27
|
+
If the route-building syntax feels familiar, that's on purpose: it's inspired by LangGraph's way of wiring up nodes.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install prouter
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Requires Python 3.12+.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import re
|
|
41
|
+
from pathlib import Path
|
|
42
|
+
from prouter import GraphBuilder
|
|
43
|
+
|
|
44
|
+
# An input pattern, a handler (Path -> Path), and the output pattern the result must match.
|
|
45
|
+
draft = re.compile(r"(\d+)_draft\.wav")
|
|
46
|
+
final = re.compile(r"\d+_final\.wav")
|
|
47
|
+
|
|
48
|
+
def rename(path: Path) -> Path:
|
|
49
|
+
return path.with_name(path.name.replace("_draft", "_final"))
|
|
50
|
+
|
|
51
|
+
builder = GraphBuilder(root_path=Path("/path/to/draw/from"), results_folder=Path("/Folder/to/save/results"))
|
|
52
|
+
builder.add_route(draft, rename, final)
|
|
53
|
+
|
|
54
|
+
builder.build() # walk the tree, match routes, apply handlers in memory
|
|
55
|
+
builder.save() # write the result CSVs
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
> [!NOTE]
|
|
59
|
+
> When adding a route with `add_route` You can't register the same input pattern twice (a ValueError raises). Matching against those patterns is a `fullmatch` against the whole basename, so a pattern has to account for the entire filename, not just part of it. And at build() time, only one input pattern may match a given path. If two would match, that's ambiguous and prouter raises rather than guess.
|
|
60
|
+
|
|
61
|
+
## Output
|
|
62
|
+
|
|
63
|
+
`save()` drops four CSVs into the folder you give it:
|
|
64
|
+
|
|
65
|
+
- `routable_paths.csv` — matched a route, and the handler's output lined up with the output pattern.
|
|
66
|
+
- `problem_paths.csv` — matched a route, but the handler's output did **not** match the output pattern. These are the ones to look at.
|
|
67
|
+
- `clean_paths.csv` — matched nothing, left alone.
|
|
68
|
+
- `routes.csv` — the routes you configured, on their own.
|
|
69
|
+
|
|
70
|
+
The path CSVs share the same columns: `path`, `node`, `input_pattern`, `output_pattern`, `new_path`. `routes.csv` just has `input_pattern`, `node`, `output_pattern`.
|
|
71
|
+
|
|
72
|
+
> [!NOTE]
|
|
73
|
+
> In those columns, things show up under the names you gave them. Handlers use the function's `__name__`, so `rename` lands in the CSV as `rename`. Patterns are trickier, since a compiled regex has no name of its own, so prouter peeks at the calling frame and recovers the variable you bound it to: `draft` and `final` above come out as `draft` and `final`. Pass a bare `re.compile(...)` inline with no variable and it falls back to the raw regex source.
|
prouter-0.1.0/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# prouter
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/prouter)
|
|
4
|
+
[](https://kidspeech.wisc.edu/)
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
[](https://github.com/WISCLab/prouter/actions/workflows/ci.yml)
|
|
8
|
+
[](https://github.com/WISCLab/prouter/actions/workflows/cd.yml)
|
|
9
|
+
|
|
10
|
+
Route filesystem paths through `pattern -> handler -> pattern` rules with visibility.
|
|
11
|
+
|
|
12
|
+
The idea is simple. You define routes, each one an input pattern, a handler that rewrites a path, and an output pattern the result has to match. prouter walks a directory tree and runs every path through the route whose input pattern matches its basename, keeping track of what it did along the way.
|
|
13
|
+
|
|
14
|
+
It never touches the filesystem. Nothing gets moved or renamed. What you get back is a set of CSVs describing the transform it would apply, so you can look it over before committing to anything.
|
|
15
|
+
|
|
16
|
+
The tree is walked bottom-up, deepest paths first and the root last. This is deliberate: renaming children before their parents means a directory rename never invalidates paths you haven't reached yet. The CSVs preserve that order, so applying the rows top to bottom is always safe.
|
|
17
|
+
|
|
18
|
+
If the route-building syntax feels familiar, that's on purpose: it's inspired by LangGraph's way of wiring up nodes.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install prouter
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Requires Python 3.12+.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import re
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from prouter import GraphBuilder
|
|
34
|
+
|
|
35
|
+
# An input pattern, a handler (Path -> Path), and the output pattern the result must match.
|
|
36
|
+
draft = re.compile(r"(\d+)_draft\.wav")
|
|
37
|
+
final = re.compile(r"\d+_final\.wav")
|
|
38
|
+
|
|
39
|
+
def rename(path: Path) -> Path:
|
|
40
|
+
return path.with_name(path.name.replace("_draft", "_final"))
|
|
41
|
+
|
|
42
|
+
builder = GraphBuilder(root_path=Path("/path/to/draw/from"), results_folder=Path("/Folder/to/save/results"))
|
|
43
|
+
builder.add_route(draft, rename, final)
|
|
44
|
+
|
|
45
|
+
builder.build() # walk the tree, match routes, apply handlers in memory
|
|
46
|
+
builder.save() # write the result CSVs
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
> [!NOTE]
|
|
50
|
+
> When adding a route with `add_route` You can't register the same input pattern twice (a ValueError raises). Matching against those patterns is a `fullmatch` against the whole basename, so a pattern has to account for the entire filename, not just part of it. And at build() time, only one input pattern may match a given path. If two would match, that's ambiguous and prouter raises rather than guess.
|
|
51
|
+
|
|
52
|
+
## Output
|
|
53
|
+
|
|
54
|
+
`save()` drops four CSVs into the folder you give it:
|
|
55
|
+
|
|
56
|
+
- `routable_paths.csv` — matched a route, and the handler's output lined up with the output pattern.
|
|
57
|
+
- `problem_paths.csv` — matched a route, but the handler's output did **not** match the output pattern. These are the ones to look at.
|
|
58
|
+
- `clean_paths.csv` — matched nothing, left alone.
|
|
59
|
+
- `routes.csv` — the routes you configured, on their own.
|
|
60
|
+
|
|
61
|
+
The path CSVs share the same columns: `path`, `node`, `input_pattern`, `output_pattern`, `new_path`. `routes.csv` just has `input_pattern`, `node`, `output_pattern`.
|
|
62
|
+
|
|
63
|
+
> [!NOTE]
|
|
64
|
+
> In those columns, things show up under the names you gave them. Handlers use the function's `__name__`, so `rename` lands in the CSV as `rename`. Patterns are trickier, since a compiled regex has no name of its own, so prouter peeks at the calling frame and recovers the variable you bound it to: `draft` and `final` above come out as `draft` and `final`. Pass a bare `re.compile(...)` inline with no variable and it falls back to the raw regex source.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "prouter"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Route filesystem paths through pattern -> handler -> pattern rules with visibility."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"pandas>=3.0.3",
|
|
9
|
+
]
|
|
10
|
+
[dependency-groups]
|
|
11
|
+
dev = [
|
|
12
|
+
"ruff>=0.9.0",
|
|
13
|
+
"pytest>=8.0",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["hatchling"]
|
|
18
|
+
build-backend = "hatchling.build"
|
|
19
|
+
|
|
20
|
+
[tool.hatch.build.targets.wheel]
|
|
21
|
+
packages = ["src/prouter"]
|
|
22
|
+
|
|
23
|
+
[tool.ruff]
|
|
24
|
+
line-length = 120
|
|
25
|
+
target-version = "py312"
|
|
26
|
+
src = ["src"]
|
|
27
|
+
|
|
28
|
+
[tool.ruff.lint]
|
|
29
|
+
select = [
|
|
30
|
+
"E", # pycodestyle errors
|
|
31
|
+
"F", # pyflakes
|
|
32
|
+
"I", # isort
|
|
33
|
+
"UP", # pyupgrade
|
|
34
|
+
"B", # flake8-bugbear
|
|
35
|
+
"W", # pycodestyle warnings
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[tool.shredguard]
|
|
39
|
+
|
|
40
|
+
[[tool.shredguard.patterns]]
|
|
41
|
+
regex = "_[MF]_"
|
|
42
|
+
description = "Problem Catch"
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import time
|
|
5
|
+
from collections.abc import Callable, Iterator
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
# Metadata/junk entries to skip wholesale (never descended into, never yielded).
|
|
11
|
+
_SKIP_NAMES = frozenset(
|
|
12
|
+
{
|
|
13
|
+
"Thumbs.db", # Windows thumbnail cache
|
|
14
|
+
"Desktop.ini", # Windows folder metadata cache
|
|
15
|
+
".DS_Store", # iOS/macOS Finder metadata cache
|
|
16
|
+
".AppleDouble", # Apple metadata cache
|
|
17
|
+
".Spotlight-V100",
|
|
18
|
+
".Trashes",
|
|
19
|
+
"__MACOSX",
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def bottom_up_traversal(path: Path) -> Iterator[Path]:
|
|
25
|
+
"""Yield every path under ``path`` from the leaves up to the root.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
path: The directory root path to traverse.
|
|
29
|
+
|
|
30
|
+
Yields:
|
|
31
|
+
Each path in the tree, deepest first, ending with ``path``.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
TypeError: If ``path`` is not a ``Path``.
|
|
35
|
+
|
|
36
|
+
Notes:
|
|
37
|
+
Uses ``os.scandir`` so each directory entry's type is read once and
|
|
38
|
+
reused, avoiding a separate ``stat`` per child -- a large saving on
|
|
39
|
+
networked volumes. Symlinks are not followed, so symlink cycles cannot
|
|
40
|
+
cause infinite recursion. Unreadable directories are yielded as leaves
|
|
41
|
+
rather than raising.
|
|
42
|
+
"""
|
|
43
|
+
if not isinstance(path, Path):
|
|
44
|
+
raise TypeError(f"path must be a Path, got {type(path).__name__}")
|
|
45
|
+
|
|
46
|
+
yield from _bottom_up(path)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _bottom_up(path: Path) -> Iterator[Path]:
|
|
50
|
+
"""Recursive post-order worker for :func:`bottom_up_traversal`."""
|
|
51
|
+
if path.name in _SKIP_NAMES:
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
scandir_it = os.scandir(path)
|
|
56
|
+
except NotADirectoryError:
|
|
57
|
+
# ``path`` is a file given as the traversal root: yield it as a leaf.
|
|
58
|
+
yield path
|
|
59
|
+
return
|
|
60
|
+
except OSError:
|
|
61
|
+
# Unreadable/vanished directory (permissions, network drop): yield it,
|
|
62
|
+
# but don't descend.
|
|
63
|
+
yield path
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
with scandir_it:
|
|
67
|
+
for entry in scandir_it:
|
|
68
|
+
if entry.name in _SKIP_NAMES:
|
|
69
|
+
continue
|
|
70
|
+
# ``entry.is_dir()`` is served from the cached type;
|
|
71
|
+
# no extra syscall in the common case.
|
|
72
|
+
if entry.is_dir(follow_symlinks=False):
|
|
73
|
+
yield from _bottom_up(Path(entry.path))
|
|
74
|
+
else:
|
|
75
|
+
yield Path(entry.path)
|
|
76
|
+
yield path
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def validate_uniqueness_and_disjointness(patterns: list[re.Pattern]) -> bool:
|
|
80
|
+
"""Validate that each pattern is unique and that the input and output pattern sets are disjoint.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
patterns: List of compiled regex patterns.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
True if validation passes, otherwise False.
|
|
87
|
+
"""
|
|
88
|
+
pattern_set = set(p.pattern for p in patterns)
|
|
89
|
+
|
|
90
|
+
# Check for duplicates within patterns
|
|
91
|
+
if len(pattern_set) != len(patterns):
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def find_candidates(patterns: list[re.Pattern], path: Path) -> list[re.Pattern]:
|
|
98
|
+
"""Match the base of a path against the patterns and report the match list.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
patterns: List of compiled regex patterns to match against basename.
|
|
102
|
+
path: The path who's basename is to be matched against the patterns.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
A list of patterns that fullmatch the basename of the path, or an empty list if no patterns match.
|
|
106
|
+
"""
|
|
107
|
+
matching_patterns = [] # Populated with patterns matching the basename of the path
|
|
108
|
+
|
|
109
|
+
# Check every pattern against the basename for fullmatch
|
|
110
|
+
for pattern in patterns:
|
|
111
|
+
if pattern.fullmatch(path.name):
|
|
112
|
+
matching_patterns.append(pattern)
|
|
113
|
+
|
|
114
|
+
return matching_patterns
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class GraphBuilder:
|
|
118
|
+
"""Builder for the graph of mappings from path -> input pattern -> node -> output pattern -> new path
|
|
119
|
+
with validation of output and input validation inferrable."""
|
|
120
|
+
|
|
121
|
+
def __init__(self, root_path: Path, results_folder: Path) -> None:
|
|
122
|
+
"""Initialize the GraphBuilder with the necessary components to build the graph."""
|
|
123
|
+
|
|
124
|
+
self.root_path = root_path # All paths beneath the root pass through the graph
|
|
125
|
+
self.results_folder = results_folder # Where to save the graph CSVs
|
|
126
|
+
self.router = {} # Mapping input pattern -> (input_pattern, node, output_pattern)
|
|
127
|
+
self.graph = [] # List of tuples (path, input_pattern, node, output_pattern, new_path)
|
|
128
|
+
self.input_patterns = [] # Faster lookup and validation of input patterns
|
|
129
|
+
self.output_patterns = [] # Faster lookup and validation of output patterns
|
|
130
|
+
self.pattern_names = {} # Compiled pattern -> variable name harvested at add_route time
|
|
131
|
+
|
|
132
|
+
if not self.root_path.exists():
|
|
133
|
+
raise ValueError(f"Path '{self.root_path}' does not exist.")
|
|
134
|
+
if not self.results_folder.exists():
|
|
135
|
+
raise ValueError(f"Path '{self.results_folder}' does not exist.")
|
|
136
|
+
if not self.results_folder.is_dir():
|
|
137
|
+
raise ValueError(f"Path '{self.results_folder}' is not a directory.")
|
|
138
|
+
for filename in [
|
|
139
|
+
"routable_paths.csv",
|
|
140
|
+
"problem_paths.csv",
|
|
141
|
+
"clean_paths.csv",
|
|
142
|
+
"routes.csv",
|
|
143
|
+
]:
|
|
144
|
+
if (self.results_folder / filename).exists():
|
|
145
|
+
raise ValueError(
|
|
146
|
+
f"File '{filename}' already exists in '{self.results_folder}'. "
|
|
147
|
+
"Please remove it to avoid overwriting."
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
print("Initialized GraphBuilder")
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def _harvest_name(pattern: re.Pattern) -> str:
|
|
154
|
+
"""Find the variable name bound to ``pattern`` in the caller of ``add_route``.
|
|
155
|
+
|
|
156
|
+
Walks back two frames (past ``add_route``) to inspect the caller's local
|
|
157
|
+
and global namespaces. Returns the first matching variable name, or the
|
|
158
|
+
regex source if the pattern was passed as an unnamed literal.
|
|
159
|
+
"""
|
|
160
|
+
caller = inspect.currentframe().f_back.f_back
|
|
161
|
+
if caller is not None:
|
|
162
|
+
for namespace in (caller.f_locals, caller.f_globals):
|
|
163
|
+
for name, value in namespace.items():
|
|
164
|
+
if value is pattern:
|
|
165
|
+
return name
|
|
166
|
+
return pattern.pattern
|
|
167
|
+
|
|
168
|
+
def _label(self, pattern: re.Pattern) -> str:
|
|
169
|
+
"""Return the harvested variable name for ``pattern``, else its regex source."""
|
|
170
|
+
return self.pattern_names.get(pattern, pattern.pattern)
|
|
171
|
+
|
|
172
|
+
@staticmethod
|
|
173
|
+
def _format_eta(elapsed: float, completed: int, total: int) -> str:
|
|
174
|
+
if completed <= 0 or total <= 0:
|
|
175
|
+
return "unknown"
|
|
176
|
+
rate = elapsed / completed
|
|
177
|
+
remaining = max(total - completed, 0) * rate
|
|
178
|
+
return f"{remaining:.1f}s"
|
|
179
|
+
|
|
180
|
+
def add_route(self, input_pattern: re.Pattern, node: Callable, output_pattern: re.Pattern) -> None:
|
|
181
|
+
"""add routes for how to transform paths when pattern is met."""
|
|
182
|
+
# Check if route is already present
|
|
183
|
+
if input_pattern in self.router:
|
|
184
|
+
raise ValueError(f"Route for input pattern '{input_pattern.pattern}' already exists.")
|
|
185
|
+
|
|
186
|
+
# Add input to the list of input patterns
|
|
187
|
+
self.input_patterns.append(input_pattern)
|
|
188
|
+
|
|
189
|
+
# Add output to the list of output patterns if not already present
|
|
190
|
+
if output_pattern not in self.output_patterns:
|
|
191
|
+
self.output_patterns.append(output_pattern)
|
|
192
|
+
|
|
193
|
+
# Validate uniqueness and disjointness of patterns
|
|
194
|
+
if not validate_uniqueness_and_disjointness(self.input_patterns + self.output_patterns):
|
|
195
|
+
self.input_patterns.pop() # Remove the last added input pattern
|
|
196
|
+
self.output_patterns.pop() # Remove the last added output pattern
|
|
197
|
+
raise ValueError(f"Input pattern '{input_pattern.pattern}' is not unique.")
|
|
198
|
+
|
|
199
|
+
# Harvest the variable names the caller used, for human-readable CSV output
|
|
200
|
+
self.pattern_names.setdefault(input_pattern, self._harvest_name(input_pattern))
|
|
201
|
+
self.pattern_names.setdefault(output_pattern, self._harvest_name(output_pattern))
|
|
202
|
+
|
|
203
|
+
# Set route for quick lookup during graph building
|
|
204
|
+
self.router[input_pattern] = (input_pattern, node, output_pattern)
|
|
205
|
+
|
|
206
|
+
def build(self) -> dict[Path, list[tuple[re.Pattern, Callable, re.Pattern]]]:
|
|
207
|
+
"""Build the graph by traversing the directory tree and asserting patterns match.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
A list of tuples (path, input_pattern, node, output_pattern, new_path, valid) for each
|
|
211
|
+
matching route and result.
|
|
212
|
+
"""
|
|
213
|
+
# Walking a (possibly networked) tree can take a while with no feedback,
|
|
214
|
+
# so report discovery progress as paths stream in.
|
|
215
|
+
discovery_start = time.perf_counter()
|
|
216
|
+
paths = []
|
|
217
|
+
for path in bottom_up_traversal(self.root_path):
|
|
218
|
+
paths.append(path)
|
|
219
|
+
print(
|
|
220
|
+
f"\rDiscovering paths: {len(paths)} found ({time.perf_counter() - discovery_start:.1f}s)",
|
|
221
|
+
end="",
|
|
222
|
+
flush=True,
|
|
223
|
+
)
|
|
224
|
+
print() # Finish the discovery line
|
|
225
|
+
total = len(paths)
|
|
226
|
+
start = time.perf_counter()
|
|
227
|
+
|
|
228
|
+
for index, path in enumerate(paths, start=1):
|
|
229
|
+
elapsed = time.perf_counter() - start
|
|
230
|
+
eta = self._format_eta(elapsed, index - 1, total)
|
|
231
|
+
print(f"\rBuilding graph: {index}/{total} ({(index / total * 100):.1f}%) - ETA {eta}", end="", flush=True)
|
|
232
|
+
|
|
233
|
+
candidates = find_candidates(self.input_patterns, path)
|
|
234
|
+
if candidates:
|
|
235
|
+
if len(candidates) > 1:
|
|
236
|
+
raise ValueError(
|
|
237
|
+
f"Ambiguous path '{path}' matches multiple patterns: {[p.pattern for p in candidates]}"
|
|
238
|
+
)
|
|
239
|
+
for candidate in candidates: # Skips if candidates is empty
|
|
240
|
+
valid = False
|
|
241
|
+
input_pattern, node, output_pattern = self.router[candidate]
|
|
242
|
+
new_path = node(path)
|
|
243
|
+
if output_pattern.fullmatch(new_path.name):
|
|
244
|
+
valid = True
|
|
245
|
+
self.graph.append((path, input_pattern, node, output_pattern, new_path, valid))
|
|
246
|
+
else:
|
|
247
|
+
self.graph.append((path, "", "", "", "", ""))
|
|
248
|
+
|
|
249
|
+
print() # Finish the progress line
|
|
250
|
+
print(f"Graph built with {len(self.graph)} entries in {(time.perf_counter() - start):.1f}s")
|
|
251
|
+
return self.graph
|
|
252
|
+
|
|
253
|
+
def save(self) -> None:
|
|
254
|
+
"""Save the graph to CSV files at the specified folder."""
|
|
255
|
+
path = self.results_folder
|
|
256
|
+
if not self.router:
|
|
257
|
+
print("No routes to save. Please add routes before saving.")
|
|
258
|
+
return
|
|
259
|
+
if not self.graph:
|
|
260
|
+
print("No graph to save. Please run build() before saving.")
|
|
261
|
+
return
|
|
262
|
+
|
|
263
|
+
# Double Check for output collisons to see if we can't save before processing the graph (unlikely case)
|
|
264
|
+
if not path.exists():
|
|
265
|
+
raise ValueError(f"Path '{path}' does not exist.")
|
|
266
|
+
if not path.is_dir():
|
|
267
|
+
raise ValueError(f"Path '{path}' is not a directory.")
|
|
268
|
+
for filename in [
|
|
269
|
+
"routable_paths.csv",
|
|
270
|
+
"problem_paths.csv",
|
|
271
|
+
"clean_paths.csv",
|
|
272
|
+
"routes.csv",
|
|
273
|
+
]:
|
|
274
|
+
if (path / filename).exists():
|
|
275
|
+
raise ValueError(
|
|
276
|
+
f"File '{filename}' already exists in '{path}'. Please remove it to avoid overwriting."
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Exactly one input_pattern is expected to match each path.
|
|
280
|
+
routable_paths = {"path": [], "node": [], "input_pattern": [], "output_pattern": [], "new_path": []}
|
|
281
|
+
|
|
282
|
+
# Paths that have more than one candidate pattern match are ambiguous and said to be not routable.
|
|
283
|
+
problem_paths = {"path": [], "node": [], "input_pattern": [], "output_pattern": [], "new_path": []}
|
|
284
|
+
|
|
285
|
+
# Paths that have no candidate pattern are ignored and not routable.
|
|
286
|
+
clean_paths = {"path": [], "node": [], "input_pattern": [], "output_pattern": [], "new_path": []}
|
|
287
|
+
|
|
288
|
+
total = len(self.graph)
|
|
289
|
+
start = time.perf_counter()
|
|
290
|
+
|
|
291
|
+
for index, (old_path, input_pattern, node, output_pattern, new_path, valid) in enumerate(self.graph, start=1):
|
|
292
|
+
elapsed = time.perf_counter() - start
|
|
293
|
+
eta = self._format_eta(elapsed, index - 1, total)
|
|
294
|
+
print(f"\rSaving graph: {index}/{total} ({(index / total * 100):.1f}%) - ETA {eta}", end="", flush=True)
|
|
295
|
+
|
|
296
|
+
if input_pattern and output_pattern:
|
|
297
|
+
if valid:
|
|
298
|
+
routable_paths["path"].append(str(old_path))
|
|
299
|
+
routable_paths["node"].append(node.__name__)
|
|
300
|
+
routable_paths["input_pattern"].append(self._label(input_pattern))
|
|
301
|
+
routable_paths["output_pattern"].append(self._label(output_pattern))
|
|
302
|
+
routable_paths["new_path"].append(str(new_path))
|
|
303
|
+
else:
|
|
304
|
+
problem_paths["path"].append(str(old_path))
|
|
305
|
+
problem_paths["node"].append(node.__name__)
|
|
306
|
+
problem_paths["input_pattern"].append(self._label(input_pattern))
|
|
307
|
+
problem_paths["output_pattern"].append(self._label(output_pattern))
|
|
308
|
+
problem_paths["new_path"].append(str(new_path))
|
|
309
|
+
else:
|
|
310
|
+
clean_paths["path"].append(str(old_path))
|
|
311
|
+
clean_paths["node"].append("")
|
|
312
|
+
clean_paths["input_pattern"].append("")
|
|
313
|
+
clean_paths["output_pattern"].append("")
|
|
314
|
+
clean_paths["new_path"].append("")
|
|
315
|
+
|
|
316
|
+
print() # Finish the progress line
|
|
317
|
+
print(f"Graph saved to {path} in {(time.perf_counter() - start):.1f}s")
|
|
318
|
+
print(f" Routable paths: {len(routable_paths['path'])}")
|
|
319
|
+
print(f" Problem paths: {len(problem_paths['path'])}")
|
|
320
|
+
print(f" Clean paths: {len(clean_paths['path'])}")
|
|
321
|
+
pd.DataFrame(routable_paths).to_csv(path / "routable_paths.csv", index=False)
|
|
322
|
+
pd.DataFrame(problem_paths).to_csv(path / "problem_paths.csv", index=False)
|
|
323
|
+
pd.DataFrame(clean_paths).to_csv(path / "clean_paths.csv", index=False)
|
|
324
|
+
|
|
325
|
+
# The set of configured routes, independent of any matched paths.
|
|
326
|
+
routes = {"input_pattern": [], "node": [], "output_pattern": []}
|
|
327
|
+
for input_pattern, node, output_pattern in self.router.values():
|
|
328
|
+
routes["input_pattern"].append(self._label(input_pattern))
|
|
329
|
+
routes["node"].append(node.__name__)
|
|
330
|
+
routes["output_pattern"].append(self._label(output_pattern))
|
|
331
|
+
pd.DataFrame(routes).to_csv(path / "routes.csv", index=False)
|
|
332
|
+
print(f" Routes: {len(routes['input_pattern'])}")
|
|
333
|
+
|
|
334
|
+
print(f"Saved graph to {path}")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Smoke tests: verify the package imports and exposes its public API."""
|
|
2
|
+
|
|
3
|
+
import prouter
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_package_imports():
|
|
7
|
+
assert prouter.__doc__
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_graphbuilder_is_exported():
|
|
11
|
+
assert "GraphBuilder" in prouter.__all__
|
|
12
|
+
assert hasattr(prouter, "GraphBuilder")
|
prouter-0.1.0/uv.lock
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
revision = 3
|
|
3
|
+
requires-python = ">=3.12"
|
|
4
|
+
resolution-markers = [
|
|
5
|
+
"python_full_version >= '3.14' and sys_platform == 'win32'",
|
|
6
|
+
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
|
|
7
|
+
"python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
|
|
8
|
+
"python_full_version < '3.14' and sys_platform == 'win32'",
|
|
9
|
+
"python_full_version < '3.14' and sys_platform == 'emscripten'",
|
|
10
|
+
"python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[[package]]
|
|
14
|
+
name = "colorama"
|
|
15
|
+
version = "0.4.6"
|
|
16
|
+
source = { registry = "https://pypi.org/simple" }
|
|
17
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
|
18
|
+
wheels = [
|
|
19
|
+
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[[package]]
|
|
23
|
+
name = "iniconfig"
|
|
24
|
+
version = "2.3.0"
|
|
25
|
+
source = { registry = "https://pypi.org/simple" }
|
|
26
|
+
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
|
27
|
+
wheels = [
|
|
28
|
+
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[[package]]
|
|
32
|
+
name = "numpy"
|
|
33
|
+
version = "2.4.6"
|
|
34
|
+
source = { registry = "https://pypi.org/simple" }
|
|
35
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" }
|
|
36
|
+
wheels = [
|
|
37
|
+
{ url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" },
|
|
38
|
+
{ url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" },
|
|
39
|
+
{ url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" },
|
|
40
|
+
{ url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" },
|
|
41
|
+
{ url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" },
|
|
42
|
+
{ url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" },
|
|
43
|
+
{ url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" },
|
|
44
|
+
{ url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" },
|
|
45
|
+
{ url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" },
|
|
46
|
+
{ url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" },
|
|
47
|
+
{ url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" },
|
|
48
|
+
{ url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" },
|
|
49
|
+
{ url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" },
|
|
50
|
+
{ url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" },
|
|
51
|
+
{ url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" },
|
|
52
|
+
{ url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" },
|
|
53
|
+
{ url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" },
|
|
54
|
+
{ url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" },
|
|
55
|
+
{ url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" },
|
|
56
|
+
{ url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" },
|
|
57
|
+
{ url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" },
|
|
58
|
+
{ url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" },
|
|
59
|
+
{ url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" },
|
|
60
|
+
{ url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" },
|
|
61
|
+
{ url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" },
|
|
62
|
+
{ url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" },
|
|
63
|
+
{ url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" },
|
|
64
|
+
{ url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" },
|
|
65
|
+
{ url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" },
|
|
66
|
+
{ url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" },
|
|
67
|
+
{ url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" },
|
|
68
|
+
{ url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" },
|
|
69
|
+
{ url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" },
|
|
70
|
+
{ url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" },
|
|
71
|
+
{ url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" },
|
|
72
|
+
{ url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" },
|
|
73
|
+
{ url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" },
|
|
74
|
+
{ url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" },
|
|
75
|
+
{ url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" },
|
|
76
|
+
{ url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" },
|
|
77
|
+
{ url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" },
|
|
78
|
+
{ url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" },
|
|
79
|
+
{ url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" },
|
|
80
|
+
{ url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" },
|
|
81
|
+
{ url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" },
|
|
82
|
+
{ url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" },
|
|
83
|
+
{ url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" },
|
|
84
|
+
{ url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" },
|
|
85
|
+
{ url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" },
|
|
86
|
+
{ url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" },
|
|
87
|
+
{ url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" },
|
|
88
|
+
{ url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" },
|
|
89
|
+
{ url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" },
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
[[package]]
|
|
93
|
+
name = "packaging"
|
|
94
|
+
version = "26.2"
|
|
95
|
+
source = { registry = "https://pypi.org/simple" }
|
|
96
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" }
|
|
97
|
+
wheels = [
|
|
98
|
+
{ url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" },
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
[[package]]
|
|
102
|
+
name = "pandas"
|
|
103
|
+
version = "3.0.3"
|
|
104
|
+
source = { registry = "https://pypi.org/simple" }
|
|
105
|
+
dependencies = [
|
|
106
|
+
{ name = "numpy" },
|
|
107
|
+
{ name = "python-dateutil" },
|
|
108
|
+
{ name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" },
|
|
109
|
+
]
|
|
110
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" }
|
|
111
|
+
wheels = [
|
|
112
|
+
{ url = "https://files.pythonhosted.org/packages/24/f1/392f8c5bfc16f66a0d2d41561c01627c228fe7ed2a0d056ef11315042570/pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09", size = 10357846, upload-time = "2026-05-11T18:52:36.143Z" },
|
|
113
|
+
{ url = "https://files.pythonhosted.org/packages/cf/3d/b16412745651e855f357e5e66930248688378853a6e2698a214e331fba1f/pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4", size = 9899550, upload-time = "2026-05-11T18:52:38.976Z" },
|
|
114
|
+
{ url = "https://files.pythonhosted.org/packages/31/a8/fa2535168fffcedf67f4f6de28d2dd903a747ca7c8ea6989451aaeb3a92f/pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c", size = 10412965, upload-time = "2026-05-11T18:52:41.915Z" },
|
|
115
|
+
{ url = "https://files.pythonhosted.org/packages/65/b6/09b01cdbc15224e2850365192d17b7bdebb8bdbd8780ed221fcdf0d9a515/pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9", size = 10894600, upload-time = "2026-05-11T18:52:45.02Z" },
|
|
116
|
+
{ url = "https://files.pythonhosted.org/packages/c9/a4/2eb28f2fccb4ced4a2c79ab2a5dee9ade1ebf44922ebad6fea158c9f95d4/pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf", size = 11422824, upload-time = "2026-05-11T18:52:48.058Z" },
|
|
117
|
+
{ url = "https://files.pythonhosted.org/packages/f8/45/830bb57f533a4604b355e07edcb8ea18cf88b5f94e5fca92f27052d7c597/pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c", size = 11950889, upload-time = "2026-05-11T18:52:50.905Z" },
|
|
118
|
+
{ url = "https://files.pythonhosted.org/packages/b9/c5/fc1b368f303087d20e8c9bf3d6ceb186263cfac0ade735cd938538bea839/pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc", size = 9755463, upload-time = "2026-05-11T18:52:53.386Z" },
|
|
119
|
+
{ url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" },
|
|
120
|
+
{ url = "https://files.pythonhosted.org/packages/c5/90/62d8302883c44308c477e222c3daf7c813a34c8e96985882fbd53d964352/pandas-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:67b3b64c11910cfa29f4e94a14d3bff9ee693b6fc76055e7cad549cee0aec5fa", size = 10331071, upload-time = "2026-05-11T18:52:58.838Z" },
|
|
121
|
+
{ url = "https://files.pythonhosted.org/packages/7f/ae/6a6493c783a101f165e4356953ba3c74d6f77f0042fa7d753da9dfbb640c/pandas-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39436b377d56d2a2e52d0395bdbee171f01068e99af5250509aceeb929f765c7", size = 9875690, upload-time = "2026-05-11T18:53:01.431Z" },
|
|
122
|
+
{ url = "https://files.pythonhosted.org/packages/62/7c/5df8e9f56c69a2769fbe9382a5ef8f2658c007e376434e1e2cbb57ad895f/pandas-3.0.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4be06d68f9ddcfc645b87534911da79a8fbffc7573c80e0edcf42a5020624d8", size = 10381634, upload-time = "2026-05-11T18:53:04.393Z" },
|
|
123
|
+
{ url = "https://files.pythonhosted.org/packages/99/68/1237369725aa617bb358263d535803e3053fdbc593513ec5ed9c9896b5b6/pandas-3.0.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4eeb6830daf35a71cc09649bd823e2b542dac246cdee9614c6e4bd65028cd6a", size = 10891243, upload-time = "2026-05-11T18:53:07.643Z" },
|
|
124
|
+
{ url = "https://files.pythonhosted.org/packages/25/93/77d108e8af7222b4a503ebde0e30215b1c2e4f8e53a526431890f22d5586/pandas-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1928e07221f82db493cd4af1e23c1bfca524a19a4699887975bff68f49a72bfb", size = 11388659, upload-time = "2026-05-11T18:53:10.634Z" },
|
|
125
|
+
{ url = "https://files.pythonhosted.org/packages/d0/bd/eff5b4399f332ac386c853f6cd2bd3fa2ca0061b9f36ecd9c4d7c4265649/pandas-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51b1fe551acb77dac643c6fda86084d8d446c10fe64b06a9cc29c4cc8540e7f2", size = 11942880, upload-time = "2026-05-11T18:53:13.536Z" },
|
|
126
|
+
{ url = "https://files.pythonhosted.org/packages/2c/20/559ace4200982c3887d0b86bfd0d856a2143ef8ddab63cc07934951a964c/pandas-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:a82d532a3351d435432cd913edbccaf8b8e01d4dd0e5ced5a8d2e8ecd94c7e44", size = 9757091, upload-time = "2026-05-11T18:53:16.306Z" },
|
|
127
|
+
{ url = "https://files.pythonhosted.org/packages/3a/66/69055a09fe200f29f922a3eeec4804611900b95f52d932ece3393c3c0c19/pandas-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:275c14e0fce14a2ec20eee474aecd305478ea3c1e6f6a9d8fe219a165542717e", size = 9057282, upload-time = "2026-05-11T18:53:18.768Z" },
|
|
128
|
+
{ url = "https://files.pythonhosted.org/packages/57/0e/efe801b0e6811e8e650cd21b7f2608e30f08a7067e2bf6e8752b0d56ee3c/pandas-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:46997386d528eb40376ecd6b033cf4a8a1e5282580f68f43de875b78cba2199d", size = 10767016, upload-time = "2026-05-11T18:53:21.227Z" },
|
|
129
|
+
{ url = "https://files.pythonhosted.org/packages/ea/dc/eb55135a1d5f0f0519f28da1f609a206d2cad1f9c35c32d51e38dd7261ae/pandas-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261e308dfb22448384b7580cf719d2f998fe2966c92893c3e77d14008af1f066", size = 10420210, upload-time = "2026-05-11T18:53:23.982Z" },
|
|
130
|
+
{ url = "https://files.pythonhosted.org/packages/c6/3e/b1d5d955ce33ffecb407465a60bc32769d74fcf68224b7ae67ae11d4dea4/pandas-3.0.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd1a5d1def6a46002e964510bdc67c368aa0951df5d1d9f8365336f5a1f490cd", size = 10336126, upload-time = "2026-05-11T18:53:26.731Z" },
|
|
131
|
+
{ url = "https://files.pythonhosted.org/packages/f5/76/a01261711ab60a22d71b862f0de20e4c504bf80457270ad8cb42110f6abc/pandas-3.0.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d72828c20c6d6e83e1e22a6a3b47b326b71664112fa9705dcbccfd7a39b62085", size = 10728051, upload-time = "2026-05-11T18:53:29.125Z" },
|
|
132
|
+
{ url = "https://files.pythonhosted.org/packages/e9/21/ea191195e587b18cf682e97f433f81b2d0fbe341380e80a3e0d6e4403c8e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d26cbe1fcfc12e8fd900e2454163e466b2d3af84f7c75481df7683ffc073d870", size = 11350796, upload-time = "2026-05-11T18:53:32.056Z" },
|
|
133
|
+
{ url = "https://files.pythonhosted.org/packages/64/69/f0eaaf54939f0e8c6768fd06be9af2cef9b36048b96dfb9e1b2c685a807e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e91cec1879ada0624fc3dc9953c5cbd60208e59c0db28f540c5d6d47502422f", size = 11799741, upload-time = "2026-05-11T18:53:34.985Z" },
|
|
134
|
+
{ url = "https://files.pythonhosted.org/packages/45/a4/865e0e510cae5fc2194de4db28be638952de942571ba9125934fd9c01d47/pandas-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:08d789b41f87e0905880e293cedf6197ce71fe67cc081358b1e148a491b9bd13", size = 10499958, upload-time = "2026-05-11T18:53:37.857Z" },
|
|
135
|
+
{ url = "https://files.pythonhosted.org/packages/86/54/effdcc3c0ff7a08037889200e148ebe94c16c4f653be078c7b3675955df1/pandas-3.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3650109c0f22879df8bd6179ab9ee3d7f1d1d4e7e0094a3f0032d9f51e2e64ac", size = 10336065, upload-time = "2026-05-11T18:53:41.099Z" },
|
|
136
|
+
{ url = "https://files.pythonhosted.org/packages/68/10/bf2d6738d72748b961a3751ab89522d58c54efc36a8e1a12161216cd45cf/pandas-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bab900348131a7db1f69a7309ef141fd5680f1487094193bcbbb61791573bf8f", size = 9926101, upload-time = "2026-05-11T18:53:43.515Z" },
|
|
137
|
+
{ url = "https://files.pythonhosted.org/packages/ae/e9/e35cf11c8a136e757b956f5f0efdcaa50aecde85ea055f1898dfc68262f3/pandas-3.0.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba7e08b9ac1d54569cd1e256e3668975ed624d6826f7b68df0342b012007bddb", size = 10457553, upload-time = "2026-05-11T18:53:46.394Z" },
|
|
138
|
+
{ url = "https://files.pythonhosted.org/packages/58/3b/1cdec6772bdbaf7b25dab360c59f03cadf05492dd724c6540af905389b07/pandas-3.0.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d71c63ae4ebdbf70209742096f1fc46a83a0613c99d4b23766cced9ff8cd62a", size = 10914065, upload-time = "2026-05-11T18:53:49.134Z" },
|
|
139
|
+
{ url = "https://files.pythonhosted.org/packages/c4/c2/1ef644445fcd72e3627bceec77e3560636f87ddce4ed841afe76b83b5bf9/pandas-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3a2ec42c98ffa2565a67e08e218d06d72576d758d90facb7c00805194d8f360", size = 11459188, upload-time = "2026-05-11T18:53:52.527Z" },
|
|
140
|
+
{ url = "https://files.pythonhosted.org/packages/7e/49/4d8d4f42cbc9c4adc7a1870f269c02cbd6cd40d059622c06fb298addcbad/pandas-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:335f62418ed562cfc3c49e9e196375c28b729dcef8543abf4f9438e381bf3c76", size = 11982966, upload-time = "2026-05-11T18:53:55.043Z" },
|
|
141
|
+
{ url = "https://files.pythonhosted.org/packages/38/55/792619469bab9882d8bbd5865d45a72f6478762d04a9af4bf0d08c503e95/pandas-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:3c20a521bbb85902f79f7270c80a59e1b5452d96d170c034f207181870f97ac5", size = 9876755, upload-time = "2026-05-11T18:53:58.067Z" },
|
|
142
|
+
{ url = "https://files.pythonhosted.org/packages/2a/af/33c469653b0ba03b50c3a98192d4c07f0c75c66b263ceb097fce0ee97d31/pandas-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:a2d2dff8a04f3917b55ab3910c32990f8ddf7eceba114947838cefa976a68977", size = 9198658, upload-time = "2026-05-11T18:54:00.733Z" },
|
|
143
|
+
{ url = "https://files.pythonhosted.org/packages/a2/fa/b8c257bd76b8bd060c3a9151c1fca05e9b9c5e3af5d0f549c0356f6d143d/pandas-3.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0d589105b3c14645af1738ff279b2995102d8f7a03b0a66dc8d95550eb513e04", size = 10787242, upload-time = "2026-05-11T18:54:03.564Z" },
|
|
144
|
+
{ url = "https://files.pythonhosted.org/packages/54/eb/f19206ffb0bf1919002969aa448b4702c6594845156a6f8050674855aac3/pandas-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13fc1e853d9e04743d11ba75a985ccbc2a317fe07d8af61e445a6fd24dacd6a6", size = 10436369, upload-time = "2026-05-11T18:54:06.311Z" },
|
|
145
|
+
{ url = "https://files.pythonhosted.org/packages/fd/24/c7c39fb4fe22b71a0c2d78bf0c585c600092d85f94f086d2b3b2f6ca27e2/pandas-3.0.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:819959dab7bbd0049c15623fbac4e29a191b9528160a61fb1032242d8ced2d9c", size = 10358306, upload-time = "2026-05-11T18:54:09.085Z" },
|
|
146
|
+
{ url = "https://files.pythonhosted.org/packages/16/ec/dd2a9eb7fa1204df88c0864164e35b228ac581062ac612ba0a67fd812e4c/pandas-3.0.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:60ae316d3fd75d1858d450d0db0103ea2be3e7d4a95ec2f064f7e2ae63f7b028", size = 10758394, upload-time = "2026-05-11T18:54:11.956Z" },
|
|
147
|
+
{ url = "https://files.pythonhosted.org/packages/95/6e/00c61ea8e85b4f6d8d35e11852a1a4998fc7fafc91c6a602d1cc9c972d64/pandas-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd3a518890b400d32f9023722dc9a9a5c969f00b415419a3c06c043f09bb5d7d", size = 11375717, upload-time = "2026-05-11T18:54:14.539Z" },
|
|
148
|
+
{ url = "https://files.pythonhosted.org/packages/31/89/8fc1c268969fac43688d65fd92e67df24bd128d53cb4d2eee534cd307399/pandas-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c39be2d709d01fa972a0cabc522389fceca4f3969332ba25a7d6c5802cf976a", size = 11828897, upload-time = "2026-05-11T18:54:17.146Z" },
|
|
149
|
+
{ url = "https://files.pythonhosted.org/packages/56/3b/e7d20dea247a3e6dc0bd8a6953854afbedc03951def4e7371e05e7263e25/pandas-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4db8c527972a821cf5286b40ccc57642a39bc62e62022b42f99f8a67fca8c3a1", size = 10900855, upload-time = "2026-05-11T18:54:19.72Z" },
|
|
150
|
+
{ url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" },
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
[[package]]
|
|
154
|
+
name = "pluggy"
|
|
155
|
+
version = "1.6.0"
|
|
156
|
+
source = { registry = "https://pypi.org/simple" }
|
|
157
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
|
158
|
+
wheels = [
|
|
159
|
+
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
[[package]]
|
|
163
|
+
name = "prouter"
|
|
164
|
+
version = "0.1.0"
|
|
165
|
+
source = { editable = "." }
|
|
166
|
+
dependencies = [
|
|
167
|
+
{ name = "pandas" },
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
[package.dev-dependencies]
|
|
171
|
+
dev = [
|
|
172
|
+
{ name = "pytest" },
|
|
173
|
+
{ name = "ruff" },
|
|
174
|
+
]
|
|
175
|
+
|
|
176
|
+
[package.metadata]
|
|
177
|
+
requires-dist = [{ name = "pandas", specifier = ">=3.0.3" }]
|
|
178
|
+
|
|
179
|
+
[package.metadata.requires-dev]
|
|
180
|
+
dev = [
|
|
181
|
+
{ name = "pytest", specifier = ">=8.0" },
|
|
182
|
+
{ name = "ruff", specifier = ">=0.9.0" },
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
[[package]]
|
|
186
|
+
name = "pygments"
|
|
187
|
+
version = "2.20.0"
|
|
188
|
+
source = { registry = "https://pypi.org/simple" }
|
|
189
|
+
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
|
|
190
|
+
wheels = [
|
|
191
|
+
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
[[package]]
|
|
195
|
+
name = "pytest"
|
|
196
|
+
version = "9.0.3"
|
|
197
|
+
source = { registry = "https://pypi.org/simple" }
|
|
198
|
+
dependencies = [
|
|
199
|
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
200
|
+
{ name = "iniconfig" },
|
|
201
|
+
{ name = "packaging" },
|
|
202
|
+
{ name = "pluggy" },
|
|
203
|
+
{ name = "pygments" },
|
|
204
|
+
]
|
|
205
|
+
sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
|
|
206
|
+
wheels = [
|
|
207
|
+
{ url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
|
|
208
|
+
]
|
|
209
|
+
|
|
210
|
+
[[package]]
|
|
211
|
+
name = "python-dateutil"
|
|
212
|
+
version = "2.9.0.post0"
|
|
213
|
+
source = { registry = "https://pypi.org/simple" }
|
|
214
|
+
dependencies = [
|
|
215
|
+
{ name = "six" },
|
|
216
|
+
]
|
|
217
|
+
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
|
|
218
|
+
wheels = [
|
|
219
|
+
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
[[package]]
|
|
223
|
+
name = "ruff"
|
|
224
|
+
version = "0.15.16"
|
|
225
|
+
source = { registry = "https://pypi.org/simple" }
|
|
226
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a6/bd/5f7ec371001337d8fa61701c186ff8b613ecac1651848c5950f4c4d5f2e9/ruff-0.15.16.tar.gz", hash = "sha256:d05e78d38c78caf020b03789e25106c93017db5a0cb6e2819885018c61343b78", size = 4714267, upload-time = "2026-06-04T16:33:09.974Z" }
|
|
227
|
+
wheels = [
|
|
228
|
+
{ url = "https://files.pythonhosted.org/packages/0c/42/53ef1c3953f157956db9bf7861e3bc50b9b887ce93300aa48cdba8336fe6/ruff-0.15.16-py3-none-linux_armv6l.whl", hash = "sha256:6ac3c0b3969cc6cf6b158c4e2f8f682acb58e7d700d8a44b65ecdc72d66ab0b2", size = 10709025, upload-time = "2026-06-04T16:32:51.935Z" },
|
|
229
|
+
{ url = "https://files.pythonhosted.org/packages/93/9a/a79159346f19134a956607754e57d8d128f7a4c00f4ad2f7514d224c172c/ruff-0.15.16-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:197c207ed75ffba54a0dec23db4aa939a27a3053073e085e0042433cbdc58e4a", size = 11063550, upload-time = "2026-06-04T16:32:42.24Z" },
|
|
230
|
+
{ url = "https://files.pythonhosted.org/packages/bc/72/3ce2ac000a5299ec238e01f51397b3b653c93b077d9b1bfe8715bb895f20/ruff-0.15.16-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3a39fec45ab316cc23e7558f23fea4a70403ddb5648ea9a4a3854a16973d0071", size = 10421345, upload-time = "2026-06-04T16:32:37.251Z" },
|
|
231
|
+
{ url = "https://files.pythonhosted.org/packages/b0/c2/cc7fad3ec9169373f5b6a18f1917b91080feec40c3f9658334a1d28e2f03/ruff-0.15.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba93191d79003116b95128c9d306e045200fdbd0bccb782b110f3cd1d4abc5cf", size = 10757217, upload-time = "2026-06-04T16:32:54.722Z" },
|
|
232
|
+
{ url = "https://files.pythonhosted.org/packages/69/d2/3474009eaa0a65b31fa7152a2fad5e2f050c640ceb1e6b02ee6922e94c82/ruff-0.15.16-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6ee4b90520630120ef032aa5cc10db483852dff950e78b1d717e2993a61ac8d", size = 10507035, upload-time = "2026-06-04T16:33:05.343Z" },
|
|
233
|
+
{ url = "https://files.pythonhosted.org/packages/ca/81/b7ae6ccbd11f0c8dc3d5d67fc4be9b57ff57ca86ba56152021378e1277f2/ruff-0.15.16-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e4215bc938bc3c8215c1472c1aa437e310fee20cd427335fec9d7e609563628", size = 11255291, upload-time = "2026-06-04T16:32:49.49Z" },
|
|
234
|
+
{ url = "https://files.pythonhosted.org/packages/d9/e1/46e526f1a7cc90857ce6ddf25fbb77eb6568651ac38d71b033af07076dd5/ruff-0.15.16-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c8d26be963b090f10e29abc8b3e74a2a321f6fa34e02424e30b5af89350ecbb", size = 12124922, upload-time = "2026-06-04T16:33:07.821Z" },
|
|
235
|
+
{ url = "https://files.pythonhosted.org/packages/1a/da/5c791b088b596b24d0deb967fa28ae02ad751a140c0b9ea81c5ab915d6c0/ruff-0.15.16-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f198cf4123602a2280ed46c307bcbafe41758d6fee5b456b6b6058ca1514b3b4", size = 11332186, upload-time = "2026-06-04T16:33:02.971Z" },
|
|
236
|
+
{ url = "https://files.pythonhosted.org/packages/72/11/5da87abe20047c8962361473923ebb2f62b595250126aadfad8c20649c1e/ruff-0.15.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb27515fa6240fb586ae82b901a59e67d24acff86f2190b433dc542fe0435aeb", size = 11373541, upload-time = "2026-06-04T16:32:47.007Z" },
|
|
237
|
+
{ url = "https://files.pythonhosted.org/packages/fe/2a/8554754c23a854ae3fd6b507e36ad61ddb121e298c6d5d617dec94ed0f14/ruff-0.15.16-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a267c46ba1593fc26b8eecbea050b39d40c0b6bb7781ee11c90a02cd10032951", size = 11353014, upload-time = "2026-06-04T16:32:34.795Z" },
|
|
238
|
+
{ url = "https://files.pythonhosted.org/packages/62/25/62ea41529ec89f742ea3fed9cb1059c72877ec7cf9b9e99ac9cf3294d1d9/ruff-0.15.16-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:528c68f39a91498a8d50e91ff5985df3d105782bab49cc378e73ac26bff083e8", size = 10737467, upload-time = "2026-06-04T16:32:26.348Z" },
|
|
239
|
+
{ url = "https://files.pythonhosted.org/packages/90/17/334d3ad9de4d40f9dd58fdd09e35ce64553bb501e2f19a839e2fb6be14fc/ruff-0.15.16-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7ed55c58950df60589a9a7a5d2f8fa5f54ebd287163be805adfe6ee95a9de123", size = 10521910, upload-time = "2026-06-04T16:32:32.54Z" },
|
|
240
|
+
{ url = "https://files.pythonhosted.org/packages/4d/bd/3ac7c6ae77a885c1004b3dda2446ea401768d24f851c14b4ad4b24f6639c/ruff-0.15.16-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d482feaf51512b50f9790ceb417a56a61dd1e9d9bf967662b9ed27c01b34f53a", size = 10979190, upload-time = "2026-06-04T16:32:57.492Z" },
|
|
241
|
+
{ url = "https://files.pythonhosted.org/packages/33/d7/609546e6a413c3f216fbf2a50c928f97c80939154f6a0503114094a86191/ruff-0.15.16-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1e15bc8c94513dae2a40cc9ef07c94fdd4ecc9e29dabebeebe170f952322c9e3", size = 11477014, upload-time = "2026-06-04T16:32:44.687Z" },
|
|
242
|
+
{ url = "https://files.pythonhosted.org/packages/74/0d/f2cd247ad32633a5c36e97141a2c21b11c6279f7957bc2ff360b1e08fddd/ruff-0.15.16-py3-none-win32.whl", hash = "sha256:580378f7bd4aa25f72e74aa54948a9622f142b1e509521dd10902e886681cc1e", size = 10735541, upload-time = "2026-06-04T16:32:30.145Z" },
|
|
243
|
+
{ url = "https://files.pythonhosted.org/packages/8b/9e/02e845ef151b1dee585e55c4739f8e1734ae1d9f1221dff65761c162208b/ruff-0.15.16-py3-none-win_amd64.whl", hash = "sha256:408256017284eddf98fff77b29aa4fb30f586042d535b2d9befc6512f400aaec", size = 11843403, upload-time = "2026-06-04T16:32:39.76Z" },
|
|
244
|
+
{ url = "https://files.pythonhosted.org/packages/15/19/016553f86f207450aebebc2b2b5088d086b901cc8186c02ac4284db3bd88/ruff-0.15.16-py3-none-win_arm64.whl", hash = "sha256:8cd61783afb39638a7133ef0d2dfb1e91277593962f81b5a8423eb0b888a6121", size = 11134555, upload-time = "2026-06-04T16:33:00.136Z" },
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
[[package]]
|
|
248
|
+
name = "six"
|
|
249
|
+
version = "1.17.0"
|
|
250
|
+
source = { registry = "https://pypi.org/simple" }
|
|
251
|
+
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
|
|
252
|
+
wheels = [
|
|
253
|
+
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
|
254
|
+
]
|
|
255
|
+
|
|
256
|
+
[[package]]
|
|
257
|
+
name = "tzdata"
|
|
258
|
+
version = "2026.2"
|
|
259
|
+
source = { registry = "https://pypi.org/simple" }
|
|
260
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" }
|
|
261
|
+
wheels = [
|
|
262
|
+
{ url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" },
|
|
263
|
+
]
|