Merge pull request #4 from slyons/dev/named_token_typesafe_fix

Fixing typesafe token values for named tokens
This commit is contained in:
Scott Lyons 2024-09-06 11:23:17 -07:00 • committed by GitHub
commit f3e7e82be0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 381 additions and 174 deletions

227
.github/workflows/CD.yaml vendored Normal file
View file

@ -0,0 +1,227 @@
# This file is autogenerated by maturin v1.7.0
# To update, run
#
# maturin generate-ci github
#
name: CD
on:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
unit_test:
name: Unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
- run: cargo test --all-features
rust_integration_test:
name: Rust Integration test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cd tests/rust-test && ./test.sh
python_integration_test:
name: Python Integration test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: getsentry/action-setup-venv@v2.1.0
id: venv
with:
python-version: 3.10.7
- run: cd tests/python-sync && ./test.sh
linux:
runs-on: ${{ matrix.platform.runner }}
defaults:
run:
working-directory: ./szurubooru-client
strategy:
matrix:
platform:
- runner: ubuntu-latest
target: x86_64
- runner: ubuntu-latest
target: x86
- runner: ubuntu-latest
target: aarch64
- runner: ubuntu-latest
target: armv7
- runner: ubuntu-latest
target: ppc64le
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: -F python --release --out dist --find-interpreter -m szurubooru-client/Cargo.toml
sccache: 'true'
manylinux: auto
before-script-linux: |
# If we're running on rhel centos, install needed packages.
if command -v yum &> /dev/null; then
yum update -y && yum install -y perl-core openssl openssl-devel pkgconfig libatomic
# If we're running on i686 we need to symlink libatomic
# in order to build openssl with -latomic flag.
if [[ ! -d "/usr/lib64" ]]; then
ln -s /usr/lib/libatomic.so.1 /usr/lib/libatomic.so
fi
else
# If we're running on debian-based system.
apt update -y && apt-get install -y libssl-dev openssl pkg-config
fi
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-linux-${{ matrix.platform.target }}
path: dist
musllinux:
runs-on: ${{ matrix.platform.runner }}
defaults:
run:
working-directory: ./szurubooru-client
strategy:
matrix:
platform:
- runner: ubuntu-latest
target: x86_64
- runner: ubuntu-latest
target: x86
- runner: ubuntu-latest
target: aarch64
- runner: ubuntu-latest
target: armv7
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: -F python --release --out dist --find-interpreter -m szurubooru-client/Cargo.toml
sccache: 'true'
manylinux: musllinux_1_2
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-musllinux-${{ matrix.platform.target }}
path: dist
windows:
runs-on: ${{ matrix.platform.runner }}
defaults:
run:
working-directory: ./szurubooru-client
strategy:
matrix:
platform:
- runner: windows-latest
target: x64
- runner: windows-latest
target: x86
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
architecture: ${{ matrix.platform.target }}
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: -F python --release --out dist --find-interpreter -m szurubooru-client/Cargo.toml
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-windows-${{ matrix.platform.target }}
path: dist
macos:
runs-on: ${{ matrix.platform.runner }}
defaults:
run:
working-directory: ./szurubooru-client
strategy:
matrix:
platform:
- runner: macos-12
target: x86_64
- runner: macos-14
target: aarch64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: -F python --release --out dist --find-interpreter -m szurubooru-client/Cargo.toml
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-macos-${{ matrix.platform.target }}
path: dist
sdist:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./szurubooru-client
steps:
- uses: actions/checkout@v4
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist -m szurubooru-client/Cargo.toml
- name: Upload sdist
uses: actions/upload-artifact@v4
with:
name: wheels-sdist
path: dist
python_release:
name: Python Release
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [linux, musllinux, windows, macos, sdist]
steps:
- uses: actions/download-artifact@v4
- name: Publish to PyPI
uses: PyO3/maturin-action@v1
env:
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
with:
command: upload
args: --non-interactive --skip-existing wheels-*/*
crate_release:
name: Crates.io Release
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [linux, musllinux, windows, macos, sdist]
steps:
- uses: actions/checkout@v4
- name: Cargo publish
run: cargo publish -p szurubooru-client --token ${CRATES_TOKEN}
env:
CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }}

View file

@ -6,14 +6,7 @@
name: CI
on:
#push:
# branches:
# - main
# - master
# tags:
# - '*'
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@ -47,23 +40,10 @@ jobs:
python-version: 3.10.7
- run: cd tests/python-sync && ./test.sh
linux:
runs-on: ${{ matrix.platform.runner }}
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./szurubooru-client
strategy:
matrix:
platform:
- runner: ubuntu-latest
target: x86_64
- runner: ubuntu-latest
target: x86
- runner: ubuntu-latest
target: aarch64
- runner: ubuntu-latest
target: armv7
- runner: ubuntu-latest
target: ppc64le
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
@ -90,145 +70,3 @@ jobs:
# If we're running on debian-based system.
apt update -y && apt-get install -y libssl-dev openssl pkg-config
fi
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-linux-${{ matrix.platform.target }}
path: dist
musllinux:
runs-on: ${{ matrix.platform.runner }}
defaults:
run:
working-directory: ./szurubooru-client
strategy:
matrix:
platform:
- runner: ubuntu-latest
target: x86_64
- runner: ubuntu-latest
target: x86
- runner: ubuntu-latest
target: aarch64
- runner: ubuntu-latest
target: armv7
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: -F python --release --out dist --find-interpreter -m szurubooru-client/Cargo.toml
sccache: 'true'
manylinux: musllinux_1_2
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-musllinux-${{ matrix.platform.target }}
path: dist
windows:
runs-on: ${{ matrix.platform.runner }}
defaults:
run:
working-directory: ./szurubooru-client
strategy:
matrix:
platform:
- runner: windows-latest
target: x64
- runner: windows-latest
target: x86
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
architecture: ${{ matrix.platform.target }}
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: -F python --release --out dist --find-interpreter -m szurubooru-client/Cargo.toml
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-windows-${{ matrix.platform.target }}
path: dist
macos:
runs-on: ${{ matrix.platform.runner }}
defaults:
run:
working-directory: ./szurubooru-client
strategy:
matrix:
platform:
- runner: macos-12
target: x86_64
- runner: macos-14
target: aarch64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: -F python --release --out dist --find-interpreter -m szurubooru-client/Cargo.toml
sccache: 'true'
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-macos-${{ matrix.platform.target }}
path: dist
sdist:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./szurubooru-client
steps:
- uses: actions/checkout@v4
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist -m szurubooru-client/Cargo.toml
- name: Upload sdist
uses: actions/upload-artifact@v4
with:
name: wheels-sdist
path: dist
python_release:
name: Python Release
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [linux, musllinux, windows, macos, sdist]
steps:
- uses: actions/download-artifact@v4
- name: Publish to PyPI
uses: PyO3/maturin-action@v1
env:
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
with:
command: upload
args: --non-interactive --skip-existing wheels-*/*
crate_release:
name: Crates.io Release
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [linux, musllinux, windows, macos, sdist]
steps:
- uses: actions/checkout@v4
- name: Cargo publish
run: cargo publish -p szurubooru-client --token ${CRATES_TOKEN}
env:
CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }}

View file

@ -1,3 +1,9 @@
## v0.6.1 (2024-09-06)
### Fix
- **tokens**: fixing typesafe named tokens
## v0.6.0 (2024-09-05)
### Feat

2
Cargo.lock generated
View file

@ -1590,7 +1590,7 @@ dependencies = [
[[package]]
name = "szurubooru-client"
version = "0.6.0"
version = "0.6.1"
dependencies = [
"base64",
"bytes",

118
README.md
View file

@ -1,11 +1,13 @@
# szurubooru-client
SzurubooruClient is a wrapper around the excellently-documented Szurubooru API,
including type-safe (if not API-safe) Query and Sort tokens.
including type-safe (if not API-safe) Query and Sort tokens. Includes a Python wrapper.
## Creating a new client
## From Rust
### Basic authentication
### Creating a new client
#### Basic authentication
Please keep in mind that this is not the preferred method of authentication. Tokens
are far superior.
@ -15,7 +17,7 @@ let client = SzurubooruClient::new_with_basic_auth("http://localhost:5001", "myu
"mypassword", true).unwrap();
```
### Token authentication
#### Token authentication
The far superior and more secure means of authentication
```rust
@ -23,4 +25,110 @@ use szurubooru_client::SzurubooruClient;
let client = SzurubooruClient::new_with_token("http://localhost:5001", "myuser", "sz-123456", true).unwrap();
```
For all other methods for making the requests, see the documentation.
For all other methods for making the requests, see the [documentation](https://docs.rs/szurubooru-client/latest).
## From Python
Install the client using `pip install szurubooru-client`
```python
from szurubooru_client import *
from szurubooru_client.tokens import *
from szurubooru_client.models import *
```
### Creating a client
```python
client = SzurubooruSyncClient("http://localhost:9802", username="integration_user",
password="integration_password", allow_insecure=True)
```
### Creating a new tag
```python
foo_tag = client.create_tag("foo", category="default", description="The foo tag")
assert foo_tag.names == ["foo"]
```
### Returning only a subset of fields
```python
# Omit the "description" field
tags = client.list_tags(fields=["version", "names", "category"])
assert tags.results[0].description is None
```
### Uploading from a file path
```python
folly1 = client.create_post(file_path="../folly1.jpg",
tags=["maine_coon", "cat", "folly1"],
safety=PostSafety.Safe)
```
### Searching for an existing post using an image
```python
folly1_search = client.post_for_image("../folly1.jpg")
assert folly1_search is not None
```
### Querying by an anonymous tag
```python
cat_posts = client.list_posts(query=[anonymous_token("cat")])
```
### Querying by a named tag
```python
mc_posts = client.list_posts(query=[named_token(PostNamedToken.Tag, "maine_coon")])
```
### Pagination
```python
posts = client.list_posts(limit=1)
assert posts.total == 4
assert len(posts.results) == 1
posts2 = client.list_posts(limit=1, offset=1)
assert posts.results != posts2.results
```
### Commenting on a post
```python
cat_results = client.list_posts([anonymous_token("cat")])
post_id = cat_results.results[0].id
comment = client.create_comment("Excellent cat!", post_id)
```
### Getting all comments for a post
```python
comment_list = client.list_comments([named_token(CommentNamedToken.Post, post_id)])
assert len(comment_list.results) != 0
```
### Downloading an image to a local path
```python
cat_results = client.list_posts([anonymous_token("cat")])
post_id = cat_results.results[0].id
client.download_image_to_path(post_id, "/tmp/cat.jpg")
```

View file

@ -1,3 +1,9 @@
## v0.6.1 (2024-09-06)
### Fix
- **tokens**: fixing typesafe named tokens
## v0.6.0 (2024-09-05)
### Feat

View file

@ -1,6 +1,6 @@
[package]
name = "szurubooru-client"
version = "0.6.0"
version = "0.6.1"
edition = "2021"
authors = ["Scott Lyons <scottalyons@gmail.com>"]
description = "A wrapper around the Szurubooru API, including type-safe Query and Sort tokens"

View file

@ -326,6 +326,7 @@ impl TagSibling {
all(feature = "python"),
pyclass(eq, eq_int, module = "szurubooru_client.models")
)]
#[strum(serialize_all = "camelCase")]
#[serde(rename_all = "camelCase")]
/// The type of post
pub enum PostType {
@ -352,6 +353,7 @@ pub enum PostType {
all(feature = "python"),
pyclass(eq, eq_int, module = "szurubooru_client.models")
)]
#[strum(serialize_all = "camelCase")]
#[serde(rename_all = "camelCase")]
/// How SFW/NSFW the post is
pub enum PostSafety {
@ -648,11 +650,12 @@ impl NoteResource {
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, AsRefStr, Eq, PartialEq)]
#[cfg_attr(
all(feature = "python"),
pyclass(eq, eq_int, module = "szurubooru_client.models")
)]
#[strum(serialize_all = "camelCase")]
#[serde(rename_all = "camelCase")]
/// The Rank of a given User
pub enum UserRank {
@ -668,11 +671,12 @@ pub enum UserRank {
Administrator,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, AsRefStr, Eq, PartialEq)]
#[cfg_attr(
all(feature = "python"),
pyclass(eq, eq_int, module = "szurubooru_client.models")
)]
#[strum(serialize_all = "camelCase")]
#[serde(rename_all = "camelCase")]
/// The kind of User Avatar
pub enum UserAvatarStyle {
@ -1358,11 +1362,12 @@ pub struct CreateUpdateComment {
pub post_id: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, AsRefStr, Eq, PartialEq)]
#[cfg_attr(
all(feature = "python"),
pyclass(eq, eq_int, module = "szurubooru_client.models")
)]
#[strum(serialize_all = "camelCase")]
#[serde(rename_all = "camelCase")]
/// The kind of snapshot that has been recorded
pub enum SnapshotOperationType {
@ -1376,11 +1381,12 @@ pub enum SnapshotOperationType {
Merged,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize, AsRefStr, Eq, PartialEq)]
#[cfg_attr(
all(feature = "python"),
pyclass(eq, eq_int, module = "szurubooru_client.models")
)]
#[strum(serialize_all = "camelCase")]
#[serde(rename_all = "camelCase")]
/// The kind of resource described by this snapshot
pub enum SnapshotResourceType {

View file

@ -2,6 +2,8 @@
//! warned that the types here help with the Type safety for the Tag names only. It does
//! not guarantee that a given API endpoint will support the given tag.
#[cfg(feature = "python")]
use crate::models::{PostSafety, PostType, SnapshotOperationType, SnapshotResourceType, UserRank};
#[cfg(feature = "python")]
use pyo3::{exceptions::PyValueError, prelude::*};
use std::fmt::Display;
@ -269,6 +271,16 @@ impl QueryToken {
pub fn token_py(key: &Bound<'_, PyAny>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
let value = if let Ok(value) = value.extract::<u32>() {
value.to_string()
} else if let Ok(tv) = value.extract::<PostSafety>() {
tv.as_ref().to_string()
} else if let Ok(tv) = value.extract::<PostType>() {
tv.as_ref().to_string()
} else if let Ok(tv) = value.extract::<UserRank>() {
tv.as_ref().to_string()
} else if let Ok(tv) = value.extract::<SnapshotOperationType>() {
tv.as_ref().to_string()
} else if let Ok(tv) = value.extract::<SnapshotResourceType>() {
tv.as_ref().to_string()
} else {
value.extract::<String>()?
};

View file

@ -157,6 +157,10 @@ def test_creating_posts(client):
cat_posts = client.list_posts(query=[named_token(PostNamedToken.Tag, "maine_coon")])
assert len(cat_posts.results) == 4
logger.info("Querying using typesafe types")
cat_posts = client.list_posts(query=[named_token(PostNamedToken.Safety, PostSafety.Safe)])
assert len(cat_posts.results) == 4
logger.info("Testing pagination")
cat_posts = client.list_posts(limit=1)
assert cat_posts.total == 4