Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 13 additions & 16 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,34 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Python 3.10
- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: "3.10"
python-version: "3.11"

- name: Install Poetry
uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
installer-parallel: true

- name: Build project for distribution
run: poetry build
- name: Setup uv
uses: astral-sh/setup-uv@v1
- name: Build project for distribution with uv
run: uv build

- name: Check Version
id: check-version
run: |
[[ "$(poetry version --short)" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] \
|| echo ::set-output name=prerelease::true
VERSION=$(python -c "import tomllib,sys;print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "prerelease=true" >> $GITHUB_OUTPUT
fi

- name: Create Release
uses: ncipollo/release-action@v1
with:
artifacts: "dist/*"
token: ${{ secrets.GITHUB_TOKEN }}
draft: false
prerelease: steps.check-version.outputs.prerelease == 'true'
prerelease: ${{ steps.check-version.outputs.prerelease == 'true' }}
allowUpdates: true

- name: Publish to PyPI
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
run: poetry publish
UV_PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
run: uv publish --token $UV_PYPI_TOKEN
28 changes: 6 additions & 22 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,9 @@ jobs:
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
installer-parallel: true
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v3
with:
path: .venv
key: venv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: |
poetry install --no-interaction --no-root --with redis
- name: Install library
run: poetry install --no-interaction --with redis
- name: Run tests
run: |
source .venv/bin/activate
pytest -q tests/
- name: Setup uv
uses: astral-sh/setup-uv@v1
- name: Sync dependencies with uv (include test and redis extras)
run: uv sync --extra test --extra redis
- name: Run tests with uv
run: uv run pytest -q tests/
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,21 +213,26 @@ async def daily_data():

### 安装开发依赖
```bash
poetry install --with dev
uv sync --extra test
# 如需安装后端支持,可选择性同步对应 extras:
# Redis: uv sync --extra redis
# Memcached: uv sync --extra memcached
# DynamoDB: uv sync --extra dynamodb
# 全部后端: uv sync --extra all
```

### 运行测试
```bash
poetry run pytest
uv run pytest
```

### 运行示例
```bash
# 基本示例
poetry run python examples/basic_usage.py
uv run python examples/basic_usage.py

# FastAPI示例
poetry run python examples/fastapi_example.py
uv run python examples/fastapi_example.py
```

## 许可证
Expand Down
34 changes: 1 addition & 33 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,31 +1,3 @@
[tool.poetry]
name = "use-cache"
version = "0.1.0"
description = "Caching library with multiple backend support"
authors = ["miclon <jcnd@163.com>"]
readme = "README.md"
packages = [{include = "use_cache", from = "src"}]

[tool.poetry.dependencies]
python = "^3.8"
typing-extensions = "^4.1.0"

[tool.poetry.group.redis.dependencies]
redis = "^5.0.0"

[tool.poetry.group.memcached.dependencies]
aiomcache = "^0.8.2"

[tool.poetry.group.dynamodb.dependencies]
aiobotocore = "^2.13.1"

[tool.poetry.group.fastapi.dependencies]
fastapi = "*"
starlette = "*"

[tool.poetry.group.dev.dependencies]
pytest = "^7.0.0"
pytest-asyncio = "^0.21.0"

[project]
name = "use-cache"
Expand All @@ -45,13 +17,9 @@ redis = ["redis>=5.0.0"]
memcached = ["aiomcache>=0.8.2"]
dynamodb = ["aiobotocore>=2.13.1"]
fastapi = ["fastapi", "starlette"]
test = ["pytest>=7.0.0", "pytest-asyncio>=0.21.0"]
all = ["redis>=5.0.0", "aiomcache>=0.8.2", "aiobotocore>=2.13.1", "fastapi", "starlette"]

[build-system]
requires = ["uv_build>=0.8.4,<0.9.0"]
build-backend = "uv_build"

[[tool.uv.index]]
name = "aliyun"
url = "https://mirrors.aliyun.com/pypi/simple/"
default = true
12 changes: 10 additions & 2 deletions src/use_cache/backends/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,16 @@ async def set(self, key: str, value: bytes, expire: Optional[int] = None) -> Non
async def clear(self, namespace: Optional[str] = None, key: Optional[str] = None) -> int:
"""Clear cache by namespace or specific key."""
if namespace:
lua = f"for i, name in ipairs(redis.call('KEYS', '{namespace}:*')) do redis.call('DEL', name); end"
return await self.redis.eval(lua, numkeys=0) # type: ignore[union-attr,no-any-return]
# Lua script returns number of deleted keys to avoid None
lua = (
f"local keys = redis.call('KEYS', '{namespace}:*'); "
f"local count = 0; "
f"for i, name in ipairs(keys) do count = count + redis.call('DEL', name); end; "
f"return count"
)
res = await self.redis.eval(lua, numkeys=0) # type: ignore[union-attr]
# Some clients may return None if nothing deleted; normalize to int
return int(res or 0)
elif key:
return await self.redis.delete(key) # type: ignore[union-attr]
return 0
2 changes: 1 addition & 1 deletion tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ async def foo(x: int) -> int:
class TestRedisBackend:
"""Test RedisBackend functionality when redis server is available."""

@pytest_asyncio.fixture(scope="class")
@pytest_asyncio.fixture(scope="function")
async def redis_client(self):
try:
from redis.asyncio import Redis
Expand Down
Loading