Skip to content

Commit 6f951a2

Browse files
committed
updates for quality
1 parent 4cbba45 commit 6f951a2

20 files changed

Lines changed: 1898 additions & 497 deletions

.github/workflows/lint.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: Lint
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
9+
env:
10+
UV_VERSION: "0.7.13"
11+
12+
jobs:
13+
check:
14+
name: Style-check ${{ matrix.python-version }}
15+
runs-on: ubuntu-latest
16+
strategy:
17+
matrix:
18+
python-version:
19+
- "3.9"
20+
- "3.11"
21+
- "3.13"
22+
23+
steps:
24+
- name: Check out repository
25+
uses: actions/checkout@v4
26+
27+
- name: Install Python
28+
uses: actions/setup-python@v5
29+
with:
30+
python-version: ${{ matrix.python-version }}
31+
32+
- name: Install uv
33+
uses: astral-sh/setup-uv@v4
34+
with:
35+
version: ${{ env.UV_VERSION }}
36+
enable-cache: true
37+
python-version: ${{ matrix.python-version }}
38+
cache-dependency-glob: |
39+
pyproject.toml
40+
uv.lock
41+
42+
- name: Install dependencies
43+
run: |
44+
uv sync --frozen
45+
46+
- name: check-sort-import
47+
run: |
48+
make check-sort-imports
49+
50+
- name: check-black-format
51+
run: |
52+
make check-format
53+
54+
- name: check-mypy
55+
run: |
56+
make check-types
57+

.github/workflows/test.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Test Suite
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
workflow_dispatch:
9+
10+
env:
11+
UV_VERSION: "0.7.13"
12+
13+
jobs:
14+
test:
15+
name: Python ${{ matrix.python-version }}
16+
runs-on: ubuntu-latest
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
21+
22+
steps:
23+
- name: Check out repository
24+
uses: actions/checkout@v4
25+
26+
- name: Install Python
27+
uses: actions/setup-python@v5
28+
with:
29+
python-version: ${{ matrix.python-version }}
30+
31+
- name: Install uv
32+
uses: astral-sh/setup-uv@v4
33+
with:
34+
version: ${{ env.UV_VERSION }}
35+
enable-cache: true
36+
python-version: ${{ matrix.python-version }}
37+
cache-dependency-glob: |
38+
pyproject.toml
39+
uv.lock
40+
41+
- name: Install dependencies
42+
run: |
43+
uv sync
44+
45+
- name: Run tests
46+
run: |
47+
make test
48+

.pre-commit-config.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
repos:
2+
- repo: local
3+
hooks:
4+
- id: code-quality-checks
5+
name: Run pre-commit checks (format, sort-imports, check-mypy)
6+
entry: bash -c 'make format && make check-sort-imports && make check-types'
7+
language: system
8+
pass_filenames: false
9+
- repo: https://github.com/codespell-project/codespell
10+
rev: v2.2.6
11+
hooks:
12+
- id: codespell
13+
name: Check spelling
14+
args:
15+
- --write-changes
16+
- --skip=*.pyc,*.pyo,*.lock,*.git,*.mypy_cache,__pycache__,*.egg-info,.pytest_cache,env,venv,.venv
17+

Makefile

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
.PHONY: install format lint test clean check-types check-format check-sort-imports sort-imports build help
2+
.DEFAULT_GOAL := help
3+
4+
# Allow passing arguments to make targets (e.g., make test ARGS="...")
5+
ARGS ?=
6+
7+
install: ## Install the project and all dependencies
8+
@echo "🚀 Installing project dependencies with uv"
9+
uv sync
10+
11+
format: ## Format code with isort and black
12+
@echo "🎨 Formatting code"
13+
uv run isort ./sql_redis ./tests/ --profile black
14+
uv run black ./sql_redis ./tests/
15+
16+
check-format: ## Check code formatting
17+
@echo "🔍 Checking code formatting"
18+
uv run black --check ./sql_redis ./tests/
19+
20+
sort-imports: ## Sort imports with isort
21+
@echo "📦 Sorting imports"
22+
uv run isort ./sql_redis ./tests/ --profile black
23+
24+
check-sort-imports: ## Check import sorting
25+
@echo "🔍 Checking import sorting"
26+
uv run isort ./sql_redis ./tests/ --check-only --profile black
27+
28+
check-types: ## Run mypy type checking
29+
@echo "🔍 Running mypy type checking"
30+
uv run python -m mypy ./sql_redis
31+
32+
lint: format check-types ## Run all linting (format + type check)
33+
34+
test: ## Run tests (pass extra args with ARGS="...")
35+
@echo "🧪 Running tests"
36+
uv run python -m pytest $(ARGS)
37+
38+
test-verbose: ## Run tests with verbose output
39+
@echo "🧪 Running tests (verbose)"
40+
uv run python -m pytest -vv -s $(ARGS)
41+
42+
test-cov: ## Run tests with coverage report
43+
@echo "🧪 Running tests with coverage"
44+
uv run python -m pytest --cov=sql_redis --cov-report=term-missing --cov-report=html $(ARGS)
45+
46+
check: lint test ## Run all checks (lint + test)
47+
48+
build: ## Build wheel and source distribution
49+
@echo "🏗️ Building distribution packages"
50+
uv build
51+
52+
clean: ## Clean up build artifacts and caches
53+
@echo "🧹 Cleaning up directory"
54+
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
55+
find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true
56+
find . -type d -name ".mypy_cache" -exec rm -rf {} + 2>/dev/null || true
57+
find . -type d -name ".coverage" -delete 2>/dev/null || true
58+
find . -type d -name "htmlcov" -exec rm -rf {} + 2>/dev/null || true
59+
find . -type d -name "dist" -exec rm -rf {} + 2>/dev/null || true
60+
find . -type d -name "build" -exec rm -rf {} + 2>/dev/null || true
61+
find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true
62+
find . -type f -name "*.log" -exec rm -rf {} + 2>/dev/null || true
63+
64+
help: ## Show this help message
65+
@echo "Available commands:"
66+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'
67+

pyproject.toml

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,79 @@
22
name = "sql-redis"
33
version = "0.1.0"
44
description = "SQL to Redis command translation utility"
5-
requires-python = ">=3.9"
5+
authors = [{ name = "Redis Inc.", email = "applied.ai@redis.com" }]
6+
requires-python = ">=3.9,<3.14"
7+
readme = "README.md"
8+
license = "MIT"
9+
keywords = [
10+
"sql",
11+
"redis",
12+
"redis-client",
13+
"query-translation",
14+
]
15+
classifiers = [
16+
"Programming Language :: Python :: 3.9",
17+
"Programming Language :: Python :: 3.10",
18+
"Programming Language :: Python :: 3.11",
19+
"Programming Language :: Python :: 3.12",
20+
"Programming Language :: Python :: 3.13",
21+
"License :: OSI Approved :: MIT License",
22+
]
623
dependencies = [
724
"redis>=5.0.0",
825
"sqlglot>=26.0.0",
926
]
1027

11-
[project.optional-dependencies]
12-
dev = [
13-
"pytest>=8.0.0",
14-
"pytest-cov>=4.0.0",
15-
"testcontainers[redis]>=4.0.0",
16-
]
28+
[project.urls]
29+
Homepage = "https://github.com/redis/sql-redis"
30+
Repository = "https://github.com/redis/sql-redis"
1731

1832
[build-system]
1933
requires = ["hatchling"]
2034
build-backend = "hatchling.build"
2135

36+
[dependency-groups]
37+
dev = [
38+
"black>=25.1.0,<26",
39+
"isort>=5.6.4,<6",
40+
"mypy>=1.11.0,<2",
41+
"pytest>=8.0.0,<9",
42+
"pytest-cov>=4.0.0,<5",
43+
"pytest-asyncio>=0.23.6,<0.24",
44+
"pre-commit>=4.1.0,<5",
45+
"codespell>=2.4.1,<3",
46+
"testcontainers[redis]>=4.0.0,<5",
47+
]
48+
49+
[tool.uv]
50+
default-groups = ["dev"]
51+
52+
[tool.black]
53+
target-version = ['py39', 'py310', 'py311', 'py312', 'py313']
54+
exclude = '''
55+
(
56+
| \.egg
57+
| \.git
58+
| \.hg
59+
| \.mypy_cache
60+
| \.nox
61+
| \.tox
62+
| \.venv
63+
| _build
64+
| build
65+
| dist
66+
| setup.py
67+
)
68+
'''
69+
70+
[tool.isort]
71+
profile = "black"
72+
skip = [".git", ".venv", "venv", "env", "build", "dist"]
73+
2274
[tool.pytest.ini_options]
2375
testpaths = ["tests"]
2476
addopts = "--cov=sql_redis --cov-report=term-missing"
77+
asyncio_mode = "auto"
2578

2679
[tool.coverage.run]
2780
source = ["sql_redis"]
@@ -33,3 +86,8 @@ exclude_lines = [
3386
"raise NotImplementedError",
3487
]
3588

89+
[tool.mypy]
90+
warn_unused_configs = true
91+
ignore_missing_imports = true
92+
exclude = ["env", "venv", ".venv"]
93+

sql_redis/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""SQL to Redis command translation utility."""
22

3-
from sql_redis.translator import Translator, TranslatedQuery
3+
from sql_redis.translator import TranslatedQuery, Translator
44

55
__all__ = ["Translator", "TranslatedQuery"]
6-

sql_redis/executor.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ def execute(self, sql: str, *, params: dict | None = None) -> QueryResult:
4444
translated = self._translator.translate(sql)
4545

4646
# Build command list and substitute vector params
47-
cmd = list(translated.to_command_list())
47+
# Use list[str | bytes] to allow bytes for vector params
48+
cmd: list[str | bytes] = list(translated.to_command_list())
4849

4950
# Find any bytes params (vectors) to substitute
50-
vector_param = None
51+
vector_param: bytes | None = None
5152
for value in params.values():
5253
if isinstance(value, bytes):
5354
vector_param = value

sql_redis/query_builder.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,10 @@ def build_numeric_condition(
105105
prefix = "-" if operator == "!=" else ""
106106

107107
if operator == "BETWEEN":
108-
min_val, max_val = value
109-
return f"{prefix}@{field}:[{min_val} {max_val}]"
108+
if isinstance(value, tuple):
109+
min_val, max_val = value
110+
return f"{prefix}@{field}:[{min_val} {max_val}]"
111+
raise ValueError("BETWEEN operator requires a tuple (min, max)")
110112
elif operator == "=":
111113
return f"@{field}:[{value} {value}]"
112114
elif operator == "!=":

sql_redis/translator.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,18 @@ def _build_condition(self, condition: Condition, field_type: str | None) -> str:
152152
value,
153153
)
154154
elif field_type == "NUMERIC":
155+
# Cast value to expected type for numeric conditions
156+
numeric_value: int | float | tuple[int | float, int | float]
157+
if isinstance(condition.value, tuple):
158+
numeric_value = condition.value # type: ignore[assignment]
159+
elif isinstance(condition.value, (int, float)):
160+
numeric_value = condition.value
161+
else:
162+
numeric_value = float(condition.value) # type: ignore[arg-type]
155163
return self._query_builder.build_numeric_condition(
156164
condition.field,
157165
operator,
158-
condition.value,
166+
numeric_value,
159167
)
160168
else:
161169
# GEO, VECTOR, and unknown field types - default to text search

tests/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
"""Tests for sql-redis package."""
2-

0 commit comments

Comments
 (0)