2 Commits

Author SHA1 Message Date
herel c5b69855c2 fix: Use uv to run twine in build_and_upload.sh script
- Fix twine execution to use 'uv run twine' instead of direct execution
- Update twine installation check to use 'uv pip show' instead of 'command -v'
- Ensure script is executable
2025-08-11 14:51:19 +02:00
herel a60f5274b2 feat: Add Ruff linting, pre-commit hooks, and build scripts
- Configure Ruff for linting and formatting with pre-commit hooks
- Add Makefile with convenient commands for development workflow
- Create build and upload scripts for Gitea package registry
- Update README with documentation for new features
- Fix code quality issues identified by Ruff
- Add development dependencies (ruff, pre-commit) to pyproject.toml
- Update Python version requirement to >=3.9
- Add template for Gitea PyPI configuration
- Bump version to 0.3.0
- All tests passing and code properly formatted
2025-08-11 14:49:17 +02:00
16 changed files with 725 additions and 125 deletions
+1 -1
View File
@@ -154,4 +154,4 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear # and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/
+2 -2
View File
@@ -30,7 +30,7 @@ before_script:
- uv sync --dev - uv sync --dev
# Run tests # Run tests
- uv run pytest tests/ -v - uv run pytest tests/ -v
test:python-3.11: test:python-3.11:
extends: .test_base extends: .test_base
image: python:3.11-slim image: python:3.11-slim
@@ -70,4 +70,4 @@ deploy-package:
only: only:
- tags - tags
dependencies: dependencies:
- build-package - build-package
+20
View File
@@ -0,0 +1,20 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.12.8
hooks:
# Run the linter.
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
# Run the formatter.
- id: ruff-format
+16
View File
@@ -0,0 +1,16 @@
# Gitea PyPI Configuration Template
# Save this as ~/.pypirc and update with your credentials
[distutils]
index-servers = gitea
[gitea]
repository = https://gitea.parano.ch/api/packages/herel/pypi
username = herel
password = YOUR_GITEA_PERSONAL_ACCESS_TOKEN
# To create a personal access token in Gitea:
# 1. Go to your Gitea profile settings
# 2. Click on "Applications"
# 3. Generate a new token with package registry permissions
# 4. Copy the token and paste it above as the password
+48
View File
@@ -0,0 +1,48 @@
# Makefile for building and uploading paroles-net-scraper package
.PHONY: build upload clean help lint format check test install-dev
# Default target
help:
@echo "Available targets:"
@echo " build - Build the package"
@echo " upload - Build and upload package to Gitea"
@echo " clean - Clean build artifacts"
@echo " lint - Lint Python code with Ruff"
@echo " format - Format Python code with Ruff"
@echo " check - Check Python code with Ruff"
@echo " test - Run tests"
@echo " install-dev - Install development dependencies"
@echo " help - Show this help message"
# Build the package
build:
uv build
# Upload package to Gitea
upload:
./scripts/build_and_upload.sh
# Clean build artifacts
clean:
rm -rf dist/ *.egg-info/
# Install development dependencies
install-dev:
uv pip install -e .[test]
# Run tests
test:
uv run pytest tests/ -v
# Lint Python code with Ruff
lint:
ruff check . --fix
# Format Python code with Ruff
format:
ruff format .
# Check Python code with Ruff
check:
ruff check .
+118 -4
View File
@@ -67,11 +67,55 @@ The project includes a comprehensive test suite using pytest. To run the tests:
pytest tests/ -v pytest tests/ -v
``` ```
Or if you're using the virtual environment: Or if you're using uv:
```bash ```bash
source .venv/bin/activate uv run pytest tests/ -v
pytest tests/ -v ```
## Code Quality
This project uses Ruff for linting and formatting, and pre-commit hooks to ensure code quality.
### Linting and Formatting
- **Ruff** is used for both linting and formatting
- **pre-commit** hooks are installed to automatically check and format code before committing
To manually run linting:
```bash
ruff check .
```
To automatically fix linting issues:
```bash
ruff check . --fix
```
To format code:
```bash
ruff format .
```
### Pre-commit Hooks
Pre-commit hooks are automatically run when you commit changes. To manually run all pre-commit hooks:
```bash
pre-commit run --all-files
```
### Development Dependencies
To install development dependencies including Ruff and pre-commit:
```bash
uv pip install -e .[dev]
```
Or using Make:
```bash
make install-dev
``` ```
## CI/CD ## CI/CD
@@ -88,6 +132,76 @@ To use the GitLab CI pipeline:
2. Ensure your GitLab runner is configured 2. Ensure your GitLab runner is configured
3. Set up PyPI credentials as CI/CD variables if you want to deploy 3. Set up PyPI credentials as CI/CD variables if you want to deploy
## Building and Uploading to Gitea Package Registry
This project includes scripts to build and upload packages to Gitea's PyPI package registry.
### Prerequisites
1. Create a personal access token in Gitea with package registry permissions
2. Configure `~/.pypirc` with your Gitea credentials:
```ini
[distutils]
index-servers = gitea
[gitea]
repository = https://gitea.parano.ch/api/packages/herel/pypi
username = herel
password = YOUR_GITEA_PERSONAL_ACCESS_TOKEN
```
### Using the Build and Upload Scripts
#### Option 1: Using the shell script
```bash
./scripts/build_and_upload.sh
```
#### Option 2: Using the Python script
```bash
uv run scripts/build_and_upload.py
```
#### Option 3: Using Make
```bash
make upload
```
#### Option 4: Using the installed command
After installing the package in development mode:
```bash
uv pip install -e .
build-and-upload
```
### Manual Build and Upload
If you prefer to build and upload manually:
1. Build the package:
```bash
uv build
```
2. Upload to Gitea:
```bash
twine upload --repository gitea dist/*
```
### Installing from Gitea PyPI
To install a package from Gitea's registry:
```bash
pip install --index-url https://YOUR_GITEA_TOKEN@gitea.parano.ch/api/packages/herel/pypi/simple --no-deps paroles-net-scraper
```
Note: Replace `YOUR_GITEA_TOKEN` with your actual personal access token.
## How it works ## How it works
The package constructs a URL based on the artist name and song title, then scrapes the paroles.net website to extract the lyrics. It uses BeautifulSoup to parse the HTML and extract only the relevant text content while filtering out advertisements and other unwanted content. The package constructs a URL based on the artist name and song title, then scrapes the paroles.net website to extract the lyrics. It uses BeautifulSoup to parse the HTML and extract only the relevant text content while filtering out advertisements and other unwanted content.
@@ -102,4 +216,4 @@ This package is for educational purposes only. Please respect the terms of servi
- requests - requests
- beautifulsoup4 - beautifulsoup4
- pytest (for running tests) - pytest (for running tests)
- uv (for dependency management and packaging) - uv (for dependency management and packaging)
+1 -1
View File
@@ -65,4 +65,4 @@ To update dependencies:
```bash ```bash
uv lock --upgrade uv lock --upgrade
``` ```
+1 -1
View File
@@ -6,4 +6,4 @@ from .paroles_net_scraper import get_song_lyrics, search_song
__version__ = "0.1.0" __version__ = "0.1.0"
__author__ = "Rene Luria" __author__ = "Rene Luria"
__all__ = ["get_song_lyrics", "search_song"] __all__ = ["get_song_lyrics", "search_song"]
+2 -2
View File
@@ -3,8 +3,8 @@
Command line interface for paroles.net scraper Command line interface for paroles.net scraper
""" """
import sys
import os import os
import sys
# Add the package directory to the path # Add the package directory to the path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@@ -12,4 +12,4 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from paroles_net_scraper.paroles_net_scraper import main from paroles_net_scraper.paroles_net_scraper import main
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+86 -55
View File
@@ -3,83 +3,95 @@
Module to fetch song lyrics from paroles.net Module to fetch song lyrics from paroles.net
""" """
import requests
from bs4 import BeautifulSoup
import argparse import argparse
import re import re
import requests
from bs4 import BeautifulSoup
def get_song_lyrics(artist, song_title): def get_song_lyrics(artist, song_title):
""" """
Fetch song lyrics from paroles.net Fetch song lyrics from paroles.net
Args: Args:
artist (str): Name of the artist artist (str): Name of the artist
song_title (str): Title of the song song_title (str): Title of the song
Returns: Returns:
str: Song lyrics or error message str: Song lyrics or error message
""" """
# Format the URL # Format the URL
# Convert artist and song to lowercase and replace spaces with hyphens # Convert artist and song to lowercase and replace spaces with hyphens
formatted_artist = artist.lower().replace(' ', '-').replace('$', 's').replace('&', 'and') formatted_artist = (
formatted_song = song_title.lower().replace(' ', '-').replace('\'', '').replace('"', '') artist.lower().replace(" ", "-").replace("$", "s").replace("&", "and")
)
formatted_song = (
song_title.lower().replace(" ", "-").replace("'", "").replace('"', "")
)
url = f"https://www.paroles.net/{formatted_artist}/paroles-{formatted_song}" url = f"https://www.paroles.net/{formatted_artist}/paroles-{formatted_song}"
try: try:
# Set headers to mimic a browser request # Set headers to mimic a browser request
headers = { headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' "User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
)
} }
# Send GET request # Send GET request
response = requests.get(url, headers=headers) response = requests.get(url, headers=headers)
response.raise_for_status() # Raise exception for bad status codes response.raise_for_status() # Raise exception for bad status codes
# Parse HTML content # Parse HTML content
soup = BeautifulSoup(response.content, 'html.parser') soup = BeautifulSoup(response.content, "html.parser")
# Find the lyrics container # Find the lyrics container
# Looking for the div with class 'song-text' # Looking for the div with class 'song-text'
lyrics_div = soup.find('div', class_='song-text') lyrics_div = soup.find("div", class_="song-text")
if not lyrics_div: if not lyrics_div:
return "Lyrics not found on the page" return "Lyrics not found on the page"
# Extract text content # Extract text content
# Get all text from the div but preserve line breaks # Get all text from the div but preserve line breaks
lyrics_parts = [] lyrics_parts = []
for element in lyrics_div.descendants: for element in lyrics_div.descendants:
if element.name == 'br': if element.name == "br":
lyrics_parts.append('\n') lyrics_parts.append("\n")
elif element.string and element.string.strip(): elif (
# Skip the heading that repeats the song info element.string
if 'Paroles de la chanson' not in element.string: and element.string.strip()
lyrics_parts.append(element.string) and "Paroles de la chanson" not in element.string
):
lyrics_parts.append(element.string)
# Join the parts and clean up # Join the parts and clean up
lyrics = ''.join(lyrics_parts).strip() lyrics = "".join(lyrics_parts).strip()
# Clean up extra whitespace while preserving verse structure # Clean up extra whitespace while preserving verse structure
lines = lyrics.split('\n') lines = lyrics.split("\n")
cleaned_lines = [] cleaned_lines = []
for line in lines: for line in lines:
stripped_line = line.strip() stripped_line = line.strip()
# Skip empty lines and ad content # Skip empty lines and ad content
if stripped_line and not re.match(r'^(Content_\d+|.*Advertisement.*|\d+\s*)$', stripped_line): if stripped_line and not re.match(
r"^(Content_\d+|.*Advertisement.*|\d+\s*)$", stripped_line
):
# Also remove inline ad markers # Also remove inline ad markers
cleaned_line = re.sub(r'^Content_\d+\s*', '', stripped_line) cleaned_line = re.sub(r"^Content_\d+\s*", "", stripped_line)
if cleaned_line: # Only add non-empty lines if cleaned_line: # Only add non-empty lines
cleaned_lines.append(cleaned_line) cleaned_lines.append(cleaned_line)
lyrics = '\n'.join(cleaned_lines).strip() lyrics = "\n".join(cleaned_lines).strip()
if not lyrics: if not lyrics:
return "Could not extract lyrics from the page" return "Could not extract lyrics from the page"
return lyrics return lyrics
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
return f"Error fetching lyrics: {str(e)}" return f"Error fetching lyrics: {str(e)}"
except Exception as e: except Exception as e:
@@ -89,48 +101,65 @@ def get_song_lyrics(artist, song_title):
def search_song(artist, song_title): def search_song(artist, song_title):
""" """
Search for a song on paroles.net and return the first result Search for a song on paroles.net and return the first result
Args: Args:
artist (str): Name of the artist artist (str): Name of the artist
song_title (str): Title of the song song_title (str): Title of the song
Returns: Returns:
str: URL of the first search result or error message str: URL of the first search result or error message
""" """
# Format search URL # Format search URL
search_query = f"{artist} {song_title}" search_query = f"{artist} {song_title}"
search_url = f"https://www.paroles.net/recherche?q={requests.utils.quote(search_query)}" search_url = (
f"https://www.paroles.net/recherche?q={requests.utils.quote(search_query)}"
)
try: try:
headers = { headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' "User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
)
} }
response = requests.get(search_url, headers=headers) response = requests.get(search_url, headers=headers)
response.raise_for_status() response.raise_for_status()
soup = BeautifulSoup(response.content, 'html.parser') soup = BeautifulSoup(response.content, "html.parser")
# Find the first search result link # Find the first search result link
first_result = soup.find('a', href=lambda x: x and '/paroles-' in x) first_result = soup.find("a", href=lambda x: x and "/paroles-" in x)
if first_result: if first_result:
return f"https://www.paroles.net{first_result['href']}" return f"https://www.paroles.net{first_result['href']}"
else: else:
return "No search results found" return "No search results found"
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
return f"Error searching for song: {str(e)}" return f"Error searching for song: {str(e)}"
def main(): def main():
parser = argparse.ArgumentParser(description='Fetch song lyrics from paroles.net') parser = argparse.ArgumentParser(description="Fetch song lyrics from paroles.net")
parser.add_argument('query', help='Artist and song in format "ARTIST - SONG TITLE" or separate artist and song arguments') parser.add_argument(
parser.add_argument('song', nargs='?', help='Song title (optional if using ARTIST - SONG format)') "query",
parser.add_argument('--search', action='store_true', help='Use search functionality instead of direct URL construction') help=(
"Artist and song in format 'ARTIST - SONG TITLE' or "
"separate artist and song arguments"
),
)
parser.add_argument(
"song", nargs="?", help="Song title (optional if using ARTIST - SONG format)"
)
parser.add_argument(
"--search",
action="store_true",
help="Use search functionality instead of direct URL construction",
)
args = parser.parse_args() args = parser.parse_args()
# Handle both input formats: # Handle both input formats:
# 1. Single argument: "ARTIST - SONG TITLE" # 1. Single argument: "ARTIST - SONG TITLE"
# 2. Two arguments: ARTIST SONG_TITLE # 2. Two arguments: ARTIST SONG_TITLE
@@ -139,17 +168,19 @@ def main():
if " - " in args.query: if " - " in args.query:
artist, song = args.query.split(" - ", 1) artist, song = args.query.split(" - ", 1)
else: else:
print("Error: Please provide artist and song in format 'ARTIST - SONG TITLE'") print(
"Error: Please provide artist and song in format 'ARTIST - SONG TITLE'"
)
return return
else: else:
# Two argument format: artist and song provided separately # Two argument format: artist and song provided separately
artist = args.query artist = args.query
song = args.song song = args.song
# Strip any leading/trailing whitespace # Strip any leading/trailing whitespace
artist = artist.strip() artist = artist.strip()
song = song.strip() song = song.strip()
if args.search: if args.search:
# First search for the song to get the correct URL # First search for the song to get the correct URL
search_result = search_song(artist, song) search_result = search_song(artist, song)
@@ -167,4 +198,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+74 -7
View File
@@ -1,9 +1,9 @@
[project] [project]
name = "paroles-net-scraper" name = "paroles-net-scraper"
version = "0.2.0" version = "0.3.0"
description = "A Python package to fetch song lyrics from paroles.net" description = "A Python package to fetch song lyrics from paroles.net"
authors = [ authors = [
{name = "Rene Luria", email = "rene.luria@infomaniak.com"} {name = "Rene Luria", email = "rene@luria.ch"}
] ]
readme = "README.md" readme = "README.md"
license = {text = "MIT"} license = {text = "MIT"}
@@ -11,19 +11,24 @@ dependencies = [
"requests>=2.25.1", "requests>=2.25.1",
"beautifulsoup4>=4.9.3" "beautifulsoup4>=4.9.3"
] ]
requires-python = ">=3.7" requires-python = ">=3.9"
[project.optional-dependencies] [project.optional-dependencies]
test = [ test = [
"pytest>=6.0" "pytest>=6.0"
] ]
dev = [
"ruff>=0.12.8",
"pre-commit>=4.3.0"
]
[project.scripts] [project.scripts]
paroles-scraper = "paroles_net_scraper.cli:main" paroles-scraper = "paroles_net_scraper.cli:main"
build-and-upload = "scripts.build_and_upload:main"
[project.urls] [project.urls]
Homepage = "https://github.com/yourusername/paroles-net-scraper" Homepage = "https://gitea.parano.ch/herel/paroles-net-scraper"
Repository = "https://github.com/yourusername/paroles-net-scraper" Repository = "https://gitea.parano.ch/herel/paroles-net-scraper"
[build-system] [build-system]
requires = ["setuptools>=45", "wheel"] requires = ["setuptools>=45", "wheel"]
@@ -31,7 +36,69 @@ build-backend = "setuptools.build_meta"
[tool.setuptools.packages.find] [tool.setuptools.packages.find]
where = ["."] where = ["."]
include = ["paroles_net_scraper*"] include = ["paroles_net_scraper*", "scripts*"]
[tool.setuptools.package-data] [tool.setuptools.package-data]
paroles_net_scraper = ["py.typed"] paroles_net_scraper = ["py.typed"]
[tool.ruff]
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
lint.select = ["E", "F", "I", "UP", "B", "SIM", "PL"]
lint.extend-ignore = [
"D203", # one-blank-line-before-class (incompatible with D211)
"D212", # multi-line-summary-first-line (incompatible with D213)
]
# Allow autofix for all enabled rules (when `--fix`) is provided.
lint.fixable = ["ALL"]
lint.unfixable = []
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".hg",
".mypy_cache",
".nox",
".pants.d",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"venv",
]
# Same as Black.
line-length = 88
# Allow unused variables when underscore-prefixed.
lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
# Assume Python 3.7
target-version = "py37"
[tool.ruff.lint.mccabe]
# Unlike Flake8, default to a complexity level of 10.
max-complexity = 10
[tool.ruff.format]
# Like Black, use double quotes for strings.
quote-style = "double"
# Like Black, indent with spaces, rather than tabs.
indent-style = "space"
# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
+1
View File
@@ -0,0 +1 @@
# This file makes the scripts directory a Python package
+85
View File
@@ -0,0 +1,85 @@
"""
Build and upload script for paroles-net-scraper package
"""
import importlib.util
import os
import shutil
import subprocess
import sys
from pathlib import Path
def run_command(cmd):
"""Run a command and handle errors."""
print(f"Running: {' '.join(cmd)}")
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
print(f"Error running command: {e}")
sys.exit(1)
def main():
"""Main entry point for the build and upload script."""
# Get project root directory
project_root = Path(__file__).parent.parent.absolute()
os.chdir(project_root)
# Check if we're in the right directory
if not (project_root / "pyproject.toml").exists():
print("Error: pyproject.toml not found.")
sys.exit(1)
# Install twine if not already installed
if importlib.util.find_spec("twine") is None:
print("Installing twine...")
run_command(["uv", "pip", "install", "twine"])
# Clean previous builds
print("Cleaning previous builds...")
dist_dir = project_root / "dist"
if dist_dir.exists():
shutil.rmtree(dist_dir)
# Build the package
print("Building package...")
run_command(["uv", "build"])
# Check if build was successful
if not dist_dir.exists() or not any(dist_dir.iterdir()):
print("Error: Build failed or no files created in dist/")
sys.exit(1)
print("Build successful. Files created:")
for f in dist_dir.iterdir():
print(f" {f.name}")
# Check if .pypirc exists
pypirc_path = Path.home() / ".pypirc"
if not pypirc_path.exists():
print(
"Warning: ~/.pypirc not found. Please create it with your "
"Gitea credentials:"
)
print("")
print("[distutils]")
print("index-servers = gitea")
print("")
print("[gitea]")
print("repository = https://gitea.parano.ch/api/packages/herel/pypi")
print("username = herel")
print("password = YOUR_GITEA_TOKEN")
print("")
print("Replace YOUR_GITEA_TOKEN with a personal access token from Gitea.")
sys.exit(1)
# Upload to Gitea PyPI registry
print("Uploading to Gitea PyPI registry...")
run_command(["twine", "upload", "--repository", "gitea", "dist/*"])
print("Package uploaded successfully to Gitea!")
if __name__ == "__main__":
main()
+68
View File
@@ -0,0 +1,68 @@
#!/bin/bash
# Script to build and upload Python package to Gitea PyPI registry
# Usage: ./scripts/build_and_upload.sh
set -e # Exit on any error
echo "=== Building and Uploading Package to Gitea ==="
# Create scripts directory if it doesn't exist
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
cd "$PROJECT_ROOT"
# Check if we're in the right directory
if [[ ! -f "pyproject.toml" ]]; then
echo "Error: pyproject.toml not found. Please run this script from the project root."
exit 1
fi
# Install twine if not already installed
if ! uv pip show twine &> /dev/null; then
echo "Installing twine..."
uv pip install twine
fi
# Clean previous builds
echo "Cleaning previous builds..."
rm -rf dist/
# Build the package
echo "Building package..."
uv build
# Check if build was successful
if [[ ! -d "dist" ]] || [[ -z "$(ls -A dist)" ]]; then
echo "Error: Build failed or no files created in dist/"
exit 1
fi
echo "Build successful. Files created:"
ls -la dist/
# Upload to Gitea PyPI registry
echo "Uploading to Gitea PyPI registry..."
echo "Note: You need to have ~/.pypirc configured with Gitea credentials"
echo "See: https://docs.gitea.com/usage/packages/pypi"
# Check if .pypirc exists
if [[ ! -f "$HOME/.pypirc" ]]; then
echo "Warning: ~/.pypirc not found. Please create it with your Gitea credentials:"
echo ""
echo "[distutils]"
echo "index-servers = gitea"
echo ""
echo "[gitea]"
echo "repository = https://gitea.parano.ch/api/packages/herel/pypi"
echo "username = herel"
echo "password = YOUR_GITEA_TOKEN"
echo ""
echo "Replace YOUR_GITEA_TOKEN with a personal access token from Gitea."
exit 1
fi
# Upload using twine via uv
uv run twine upload --repository gitea dist/*
echo "Package uploaded successfully to Gitea!"
+36 -17
View File
@@ -2,13 +2,14 @@
Test suite for paroles_net_scraper package Test suite for paroles_net_scraper package
""" """
import sys
import os import os
import sys
from unittest.mock import Mock, patch
import pytest import pytest
from unittest.mock import patch, Mock
# Add the parent directory to the path so we can import the scraper # Add the parent directory to the path so we can import the scraper
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
# Import from the package # Import from the package
from paroles_net_scraper import get_song_lyrics from paroles_net_scraper import get_song_lyrics
@@ -31,14 +32,17 @@ def test_get_song_lyrics_success():
</body> </body>
</html> </html>
""" """
# Mock response object # Mock response object
mock_response = Mock() mock_response = Mock()
mock_response.content = mock_html mock_response.content = mock_html
mock_response.raise_for_status.return_value = None mock_response.raise_for_status.return_value = None
# Mock BeautifulSoup parsing # Mock BeautifulSoup parsing
with patch('paroles_net_scraper.paroles_net_scraper.requests.get', return_value=mock_response): with patch(
"paroles_net_scraper.paroles_net_scraper.requests.get",
return_value=mock_response,
):
lyrics = get_song_lyrics("Test Artist", "Test Song") lyrics = get_song_lyrics("Test Artist", "Test Song")
assert "This is the first line of the song" in lyrics assert "This is the first line of the song" in lyrics
assert "This is the second line of the song" in lyrics assert "This is the second line of the song" in lyrics
@@ -59,20 +63,26 @@ def test_get_song_lyrics_not_found():
</body> </body>
</html> </html>
""" """
# Mock response object # Mock response object
mock_response = Mock() mock_response = Mock()
mock_response.content = mock_html mock_response.content = mock_html
mock_response.raise_for_status.return_value = None mock_response.raise_for_status.return_value = None
with patch('paroles_net_scraper.paroles_net_scraper.requests.get', return_value=mock_response): with patch(
"paroles_net_scraper.paroles_net_scraper.requests.get",
return_value=mock_response,
):
lyrics = get_song_lyrics("Non Existent", "Non Existent Song") lyrics = get_song_lyrics("Non Existent", "Non Existent Song")
assert lyrics == "Lyrics not found on the page" assert lyrics == "Lyrics not found on the page"
def test_get_song_lyrics_request_exception(): def test_get_song_lyrics_request_exception():
"""Test handling of request exceptions""" """Test handling of request exceptions"""
with patch('paroles_net_scraper.paroles_net_scraper.requests.get', side_effect=Exception("Network error")): with patch(
"paroles_net_scraper.paroles_net_scraper.requests.get",
side_effect=Exception("Network error"),
):
lyrics = get_song_lyrics("Test Artist", "Test Song") lyrics = get_song_lyrics("Test Artist", "Test Song")
assert "Error parsing lyrics" in lyrics assert "Error parsing lyrics" in lyrics
@@ -81,19 +91,28 @@ def test_url_formatting():
"""Test URL formatting with special characters""" """Test URL formatting with special characters"""
# This test will check that the URL is properly formatted # This test will check that the URL is properly formatted
# We'll test this by checking the requests.get call arguments # We'll test this by checking the requests.get call arguments
mock_response = Mock() mock_response = Mock()
mock_response.content = "<div class='song-text'></div>" mock_response.content = "<div class='song-text'></div>"
mock_response.raise_for_status.return_value = None mock_response.raise_for_status.return_value = None
with patch('paroles_net_scraper.paroles_net_scraper.requests.get', return_value=mock_response) as mock_get: with patch(
"paroles_net_scraper.paroles_net_scraper.requests.get",
return_value=mock_response,
) as mock_get:
# Test with artist and song containing spaces and special characters # Test with artist and song containing spaces and special characters
get_song_lyrics("Ed Sheeran", "Shape of You") get_song_lyrics("Ed Sheeran", "Shape of You")
expected_url = "https://www.paroles.net/ed-sheeran/paroles-shape-of-you" expected_url = "https://www.paroles.net/ed-sheeran/paroles-shape-of-you"
mock_get.assert_called_once_with(expected_url, headers={ mock_get.assert_called_once_with(
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' expected_url,
}) headers={
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
)
},
)
if __name__ == "__main__": if __name__ == "__main__":
pytest.main([__file__]) pytest.main([__file__])
Generated
+166 -35
View File
@@ -1,5 +1,5 @@
version = 1 version = 1
requires-python = ">=3.7" requires-python = ">=3.9"
[[package]] [[package]]
name = "beautifulsoup4" name = "beautifulsoup4"
@@ -23,6 +23,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216 }, { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216 },
] ]
[[package]]
name = "cfgv"
version = "3.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 },
]
[[package]] [[package]]
name = "charset-normalizer" name = "charset-normalizer"
version = "3.4.3" version = "3.4.3"
@@ -84,17 +93,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224 }, { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224 },
{ url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086 }, { url = "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", size = 100086 },
{ url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400 }, { url = "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", size = 107400 },
{ url = "https://files.pythonhosted.org/packages/22/82/63a45bfc36f73efe46731a3a71cb84e2112f7e0b049507025ce477f0f052/charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c", size = 198805 },
{ url = "https://files.pythonhosted.org/packages/0c/52/8b0c6c3e53f7e546a5e49b9edb876f379725914e1130297f3b423c7b71c5/charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b", size = 142862 },
{ url = "https://files.pythonhosted.org/packages/59/c0/a74f3bd167d311365e7973990243f32c35e7a94e45103125275b9e6c479f/charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4", size = 155104 },
{ url = "https://files.pythonhosted.org/packages/1a/79/ae516e678d6e32df2e7e740a7be51dc80b700e2697cb70054a0f1ac2c955/charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b", size = 152598 },
{ url = "https://files.pythonhosted.org/packages/00/bd/ef9c88464b126fa176f4ef4a317ad9b6f4d30b2cffbc43386062367c3e2c/charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9", size = 147391 },
{ url = "https://files.pythonhosted.org/packages/7a/03/cbb6fac9d3e57f7e07ce062712ee80d80a5ab46614684078461917426279/charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb", size = 145037 },
{ url = "https://files.pythonhosted.org/packages/64/d1/f9d141c893ef5d4243bc75c130e95af8fd4bc355beff06e9b1e941daad6e/charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a", size = 156425 },
{ url = "https://files.pythonhosted.org/packages/c5/35/9c99739250742375167bc1b1319cd1cec2bf67438a70d84b2e1ec4c9daa3/charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942", size = 153734 },
{ url = "https://files.pythonhosted.org/packages/50/10/c117806094d2c956ba88958dab680574019abc0c02bcf57b32287afca544/charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b", size = 148551 },
{ url = "https://files.pythonhosted.org/packages/61/c5/dc3ba772489c453621ffc27e8978a98fe7e41a93e787e5e5bde797f1dddb/charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557", size = 98459 },
{ url = "https://files.pythonhosted.org/packages/05/35/bb59b1cd012d7196fc81c2f5879113971efc226a63812c9cf7f89fe97c40/charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40", size = 105887 },
{ url = "https://files.pythonhosted.org/packages/c2/ca/9a0983dd5c8e9733565cf3db4df2b0a2e9a82659fd8aa2a868ac6e4a991f/charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05", size = 207520 }, { url = "https://files.pythonhosted.org/packages/c2/ca/9a0983dd5c8e9733565cf3db4df2b0a2e9a82659fd8aa2a868ac6e4a991f/charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05", size = 207520 },
{ url = "https://files.pythonhosted.org/packages/39/c6/99271dc37243a4f925b09090493fb96c9333d7992c6187f5cfe5312008d2/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e", size = 147307 }, { url = "https://files.pythonhosted.org/packages/39/c6/99271dc37243a4f925b09090493fb96c9333d7992c6187f5cfe5312008d2/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e", size = 147307 },
{ url = "https://files.pythonhosted.org/packages/e4/69/132eab043356bba06eb333cc2cc60c6340857d0a2e4ca6dc2b51312886b3/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99", size = 160448 }, { url = "https://files.pythonhosted.org/packages/e4/69/132eab043356bba06eb333cc2cc60c6340857d0a2e4ca6dc2b51312886b3/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99", size = 160448 },
@@ -118,6 +116,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
] ]
[[package]]
name = "distlib"
version = "0.4.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 },
]
[[package]] [[package]]
name = "exceptiongroup" name = "exceptiongroup"
version = "1.3.0" version = "1.3.0"
@@ -130,6 +137,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674 }, { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674 },
] ]
[[package]]
name = "filelock"
version = "3.18.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 },
]
[[package]]
name = "identify"
version = "2.6.13"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/82/ca/ffbabe3635bb839aa36b3a893c91a9b0d368cb4d8073e03a12896970af82/identify-2.6.13.tar.gz", hash = "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32", size = 99243 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e7/ce/461b60a3ee109518c055953729bf9ed089a04db895d47e95444071dcdef2/identify-2.6.13-py2.py3-none-any.whl", hash = "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b", size = 99153 },
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "3.10" version = "3.10"
@@ -139,19 +164,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
] ]
[[package]]
name = "importlib-metadata"
version = "6.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.8'" },
{ name = "zipp" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a3/82/f6e29c8d5c098b6be61460371c2c5591f4a335923639edec43b3830650a4/importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4", size = 53569 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ff/94/64287b38c7de4c90683630338cf28f129decbba0a44f0c6db35a873c73c4/importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5", size = 22934 },
]
[[package]] [[package]]
name = "iniconfig" name = "iniconfig"
version = "2.0.0" version = "2.0.0"
@@ -161,6 +173,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
] ]
[[package]]
name = "nodeenv"
version = "1.9.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 },
]
[[package]] [[package]]
name = "packaging" name = "packaging"
version = "24.0" version = "24.0"
@@ -172,7 +193,7 @@ wheels = [
[[package]] [[package]]
name = "paroles-net-scraper" name = "paroles-net-scraper"
version = "0.1.0" version = "0.2.0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "beautifulsoup4" }, { name = "beautifulsoup4" },
@@ -180,6 +201,10 @@ dependencies = [
] ]
[package.optional-dependencies] [package.optional-dependencies]
dev = [
{ name = "pre-commit" },
{ name = "ruff" },
]
test = [ test = [
{ name = "pytest" }, { name = "pytest" },
] ]
@@ -187,22 +212,46 @@ test = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "beautifulsoup4", specifier = ">=4.9.3" }, { name = "beautifulsoup4", specifier = ">=4.9.3" },
{ name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.3.0" },
{ name = "pytest", marker = "extra == 'test'", specifier = ">=6.0" }, { name = "pytest", marker = "extra == 'test'", specifier = ">=6.0" },
{ name = "requests", specifier = ">=2.25.1" }, { name = "requests", specifier = ">=2.25.1" },
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.12.8" },
]
[[package]]
name = "platformdirs"
version = "4.3.8"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567 },
] ]
[[package]] [[package]]
name = "pluggy" name = "pluggy"
version = "1.2.0" version = "1.2.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "importlib-metadata", marker = "python_full_version < '3.8'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8a/42/8f2833655a29c4e9cb52ee8a2be04ceac61bcff4a680fb338cbd3d1e322d/pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3", size = 61613 } sdist = { url = "https://files.pythonhosted.org/packages/8a/42/8f2833655a29c4e9cb52ee8a2be04ceac61bcff4a680fb338cbd3d1e322d/pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3", size = 61613 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/51/32/4a79112b8b87b21450b066e102d6608907f4c885ed7b04c3fdb085d4d6ae/pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849", size = 17695 }, { url = "https://files.pythonhosted.org/packages/51/32/4a79112b8b87b21450b066e102d6608907f4c885ed7b04c3fdb085d4d6ae/pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849", size = 17695 },
] ]
[[package]]
name = "pre-commit"
version = "4.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cfgv" },
{ name = "identify" },
{ name = "nodeenv" },
{ name = "pyyaml" },
{ name = "virtualenv" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965 },
]
[[package]] [[package]]
name = "pytest" name = "pytest"
version = "7.4.4" version = "7.4.4"
@@ -210,7 +259,6 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" }, { name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "importlib-metadata", marker = "python_full_version < '3.8'" },
{ name = "iniconfig" }, { name = "iniconfig" },
{ name = "packaging" }, { name = "packaging" },
{ name = "pluggy" }, { name = "pluggy" },
@@ -221,6 +269,59 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287 }, { url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287 },
] ]
[[package]]
name = "pyyaml"
version = "6.0.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 },
{ url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 },
{ url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 },
{ url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 },
{ url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 },
{ url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 },
{ url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 },
{ url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 },
{ url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 },
{ url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 },
{ url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 },
{ url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 },
{ url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 },
{ url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 },
{ url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 },
{ url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 },
{ url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 },
{ url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 },
{ url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
{ url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
{ url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
{ url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
{ url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
{ url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
{ url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
{ url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
{ url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
{ url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
{ url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
{ url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
{ url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
{ url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
{ url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
{ url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
{ url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
{ url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 },
{ url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 },
{ url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 },
{ url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 },
{ url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 },
{ url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 },
{ url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 },
{ url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 },
{ url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 },
]
[[package]] [[package]]
name = "requests" name = "requests"
version = "2.31.0" version = "2.31.0"
@@ -236,6 +337,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574 }, { url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574 },
] ]
[[package]]
name = "ruff"
version = "0.12.8"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4b/da/5bd7565be729e86e1442dad2c9a364ceeff82227c2dece7c29697a9795eb/ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033", size = 5242373 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c9/1e/c843bfa8ad1114fab3eb2b78235dda76acd66384c663a4e0415ecc13aa1e/ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513", size = 11675315 },
{ url = "https://files.pythonhosted.org/packages/24/ee/af6e5c2a8ca3a81676d5480a1025494fd104b8896266502bb4de2a0e8388/ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc", size = 12456653 },
{ url = "https://files.pythonhosted.org/packages/99/9d/e91f84dfe3866fa648c10512904991ecc326fd0b66578b324ee6ecb8f725/ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec", size = 11659690 },
{ url = "https://files.pythonhosted.org/packages/fe/ac/a363d25ec53040408ebdd4efcee929d48547665858ede0505d1d8041b2e5/ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53", size = 11896923 },
{ url = "https://files.pythonhosted.org/packages/58/9f/ea356cd87c395f6ade9bb81365bd909ff60860975ca1bc39f0e59de3da37/ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f", size = 11477612 },
{ url = "https://files.pythonhosted.org/packages/1a/46/92e8fa3c9dcfd49175225c09053916cb97bb7204f9f899c2f2baca69e450/ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f", size = 13182745 },
{ url = "https://files.pythonhosted.org/packages/5e/c4/f2176a310f26e6160deaf661ef60db6c3bb62b7a35e57ae28f27a09a7d63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609", size = 14206885 },
{ url = "https://files.pythonhosted.org/packages/87/9d/98e162f3eeeb6689acbedbae5050b4b3220754554526c50c292b611d3a63/ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a", size = 13639381 },
{ url = "https://files.pythonhosted.org/packages/81/4e/1b7478b072fcde5161b48f64774d6edd59d6d198e4ba8918d9f4702b8043/ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb", size = 12613271 },
{ url = "https://files.pythonhosted.org/packages/e8/67/0c3c9179a3ad19791ef1b8f7138aa27d4578c78700551c60d9260b2c660d/ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818", size = 12847783 },
{ url = "https://files.pythonhosted.org/packages/4e/2a/0b6ac3dd045acf8aa229b12c9c17bb35508191b71a14904baf99573a21bd/ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea", size = 11702672 },
{ url = "https://files.pythonhosted.org/packages/9d/ee/f9fdc9f341b0430110de8b39a6ee5fa68c5706dc7c0aa940817947d6937e/ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3", size = 11440626 },
{ url = "https://files.pythonhosted.org/packages/89/fb/b3aa2d482d05f44e4d197d1de5e3863feb13067b22c571b9561085c999dc/ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161", size = 12462162 },
{ url = "https://files.pythonhosted.org/packages/18/9f/5c5d93e1d00d854d5013c96e1a92c33b703a0332707a7cdbd0a4880a84fb/ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46", size = 12913212 },
{ url = "https://files.pythonhosted.org/packages/71/13/ab9120add1c0e4604c71bfc2e4ef7d63bebece0cfe617013da289539cef8/ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3", size = 11694382 },
{ url = "https://files.pythonhosted.org/packages/f6/dc/a2873b7c5001c62f46266685863bee2888caf469d1edac84bf3242074be2/ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e", size = 12740482 },
{ url = "https://files.pythonhosted.org/packages/cb/5c/799a1efb8b5abab56e8a9f2a0b72d12bd64bb55815e9476c7d0a2887d2f7/ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749", size = 11884718 },
]
[[package]] [[package]]
name = "soupsieve" name = "soupsieve"
version = "2.4.1" version = "2.4.1"
@@ -273,10 +399,15 @@ wheels = [
] ]
[[package]] [[package]]
name = "zipp" name = "virtualenv"
version = "3.15.0" version = "20.33.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/00/27/f0ac6b846684cecce1ee93d32450c45ab607f65c2e0255f0092032d91f07/zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", size = 18454 } dependencies = [
wheels = [ { name = "distlib" },
{ url = "https://files.pythonhosted.org/packages/5b/fa/c9e82bbe1af6266adf08afb563905eb87cab83fde00a0a08963510621047/zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556", size = 6758 }, { name = "filelock" },
{ name = "platformdirs" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8b/60/4f20960df6c7b363a18a55ab034c8f2bcd5d9770d1f94f9370ec104c1855/virtualenv-20.33.1.tar.gz", hash = "sha256:1b44478d9e261b3fb8baa5e74a0ca3bc0e05f21aa36167bf9cbf850e542765b8", size = 6082160 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ca/ff/ded57ac5ff40a09e6e198550bab075d780941e0b0f83cbeabd087c59383a/virtualenv-20.33.1-py3-none-any.whl", hash = "sha256:07c19bc66c11acab6a5958b815cbcee30891cd1c2ccf53785a28651a0d8d8a67", size = 6060362 },
] ]