From 8700b8d4fe1ffe8567993201e7234830b6d191c6 Mon Sep 17 00:00:00 2001 From: bowenliang123 Date: Tue, 6 Jan 2026 10:52:16 +0800 Subject: [PATCH] update --- .github/workflows/ci.yml | 42 ++++++++++--------- .github/workflows/python-publish.yml | 4 +- pyobvector/client/collection_schema.py | 4 +- pyobvector/client/fts_index_param.py | 4 +- pyobvector/client/hybrid_search.py | 6 +-- pyobvector/client/milvus_like_client.py | 28 ++++++------- pyobvector/client/ob_client.py | 18 ++++---- pyobvector/client/ob_vec_client.py | 30 ++++++------- pyobvector/client/ob_vec_json_table_client.py | 10 ++--- pyobvector/client/partitions.py | 34 +++++++-------- .../json_table/json_value_returning_func.py | 3 +- pyobvector/json_table/virtual_data_type.py | 2 +- pyobvector/schema/array.py | 9 ++-- pyobvector/schema/geo_srid_point.py | 4 +- pyobvector/schema/gis_func.py | 3 +- pyobvector/schema/reflection.py | 4 +- pyobvector/util/ob_version.py | 3 +- pyproject.toml | 9 +++- tests/__init__.py | 6 +-- tests/test_milvus_like_client.py | 2 - .../test_milvus_like_client_sparse_vector.py | 3 +- tests/test_ob_vec_client.py | 3 +- tests/test_ob_vec_client_sparse_vector.py | 4 +- 23 files changed, 116 insertions(+), 119 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e8fe28..38ee87d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,15 +13,34 @@ jobs: runs-on: ubuntu-latest strategy: matrix: + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + image_tag: ["4.4.1.0-100000032025101610"] + init_sql: ["ALTER SYSTEM ob_vector_memory_limit_percentage = 30; SET GLOBAL ob_query_timeout=100000000;"] + test_filter: ["tests/test_hybrid_search.py::HybridSearchTest"] include: - - image_tag: "4.3.5.3-103000092025080818" + - python-version: 3.12 + image_tag: "4.3.5.3-103000092025080818" init_sql: "ALTER SYSTEM ob_vector_memory_limit_percentage = 30; CREATE USER 'jtuser'@'%'; GRANT SELECT, INSERT, UPDATE, DELETE ON test.* TO 'jtuser'@'%'; FLUSH PRIVILEGES;" test_filter: "-k \"not HybridSearchTest\"" - - image_tag: "4.4.1.0-100000032025101610" - init_sql: "ALTER SYSTEM ob_vector_memory_limit_percentage = 30; SET GLOBAL ob_query_timeout=100000000;" - test_filter: "tests/test_hybrid_search.py::HybridSearchTest" steps: + - name: Check out code + uses: actions/checkout@v6 + + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: uv sync --dev + + - name: Lint + run: uv run ruff check + + - name: Package build test + run: uv build + - name: Free disk space uses: kfir4444/free-disk-space@main with: @@ -32,21 +51,6 @@ jobs: large-packages: true swap-storage: true - - name: Check out code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v3 - with: - python-version: '3.x' - - - name: Install uv - uses: astral-sh/setup-uv@v6 - - - name: Install dependencies - run: | - uv sync --dev - - name: Start OceanBase container uses: oceanbase/setup-oceanbase-ce@v1 with: diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 4b74384..b772995 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -21,10 +21,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v6 with: python-version: '3.x' diff --git a/pyobvector/client/collection_schema.py b/pyobvector/client/collection_schema.py index 526f1e6..94e1cdb 100644 --- a/pyobvector/client/collection_schema.py +++ b/pyobvector/client/collection_schema.py @@ -1,6 +1,6 @@ """FieldSchema & CollectionSchema definition module to be compatible with Milvus.""" import copy -from typing import Optional, List +from typing import Optional from sqlalchemy import Column from .schema_type import DataType, convert_datatype_to_sqltype from .exceptions import * @@ -125,7 +125,7 @@ class CollectionSchema: """ def __init__( self, - fields: Optional[List[FieldSchema]] = None, + fields: Optional[list[FieldSchema]] = None, partitions: Optional[ObPartition] = None, description: str = "", # ignored in oceanbase **kwargs, diff --git a/pyobvector/client/fts_index_param.py b/pyobvector/client/fts_index_param.py index 72bc9ee..052855b 100644 --- a/pyobvector/client/fts_index_param.py +++ b/pyobvector/client/fts_index_param.py @@ -1,6 +1,6 @@ """A module to specify fts index parameters""" from enum import Enum -from typing import List, Optional, Union +from typing import Optional, Union class FtsParser(Enum): """Built-in full-text search parser types supported by OceanBase""" @@ -23,7 +23,7 @@ class FtsIndexParam: def __init__( self, index_name: str, - field_names: List[str], + field_names: list[str], parser_type: Optional[Union[FtsParser, str]] = None, ): self.index_name = index_name diff --git a/pyobvector/client/hybrid_search.py b/pyobvector/client/hybrid_search.py index 83693d8..163e41a 100644 --- a/pyobvector/client/hybrid_search.py +++ b/pyobvector/client/hybrid_search.py @@ -1,7 +1,7 @@ """OceanBase Hybrid Search Client.""" import json import logging -from typing import Dict, Any +from typing import Any from sqlalchemy import text @@ -41,7 +41,7 @@ def __init__( def search( self, index: str, - body: Dict[str, Any], + body: dict[str, Any], **kwargs, ): """Execute hybrid search with parameter compatible with Elasticsearch. @@ -66,7 +66,7 @@ def search( def get_sql( self, index: str, - body: Dict[str, Any], + body: dict[str, Any], ) -> str: """Get the SQL actually to be executed in hybrid search. diff --git a/pyobvector/client/milvus_like_client.py b/pyobvector/client/milvus_like_client.py index df8d595..bcb1b33 100644 --- a/pyobvector/client/milvus_like_client.py +++ b/pyobvector/client/milvus_like_client.py @@ -1,7 +1,7 @@ """Milvus Like Client.""" import logging import json -from typing import Optional, Union, Dict, List +from typing import Optional, Union from sqlalchemy.exc import NoSuchTableError from sqlalchemy import ( @@ -147,7 +147,7 @@ def create_collection( def get_collection_stats( self, collection_name: str, timeout: Optional[float] = None # pylint: disable=unused-argument - ) -> Dict: + ) -> dict: """Get collection row count. Args: @@ -354,12 +354,12 @@ def search( with_dist: bool = False, flter=None, limit: int = 10, - output_fields: Optional[List[str]] = None, + output_fields: Optional[list[str]] = None, search_params: Optional[dict] = None, timeout: Optional[float] = None, # pylint: disable=unused-argument - partition_names: Optional[List[str]] = None, + partition_names: Optional[list[str]] = None, **kwargs, # pylint: disable=unused-argument - ) -> List[dict]: + ) -> list[dict]: """Perform ann search. Note: OceanBase does not support batch search now. `data` & the return value is not a batch. @@ -467,11 +467,11 @@ def query( self, collection_name: str, flter=None, - output_fields: Optional[List[str]] = None, + output_fields: Optional[list[str]] = None, timeout: Optional[float] = None, # pylint: disable=unused-argument - partition_names: Optional[List[str]] = None, + partition_names: Optional[list[str]] = None, **kwargs, # pylint: disable=unused-argument - ) -> List[dict]: + ) -> list[dict]: """Query records. Args: @@ -533,11 +533,11 @@ def get( self, collection_name: str, ids: Union[list, str, int] = None, - output_fields: Optional[List[str]] = None, + output_fields: Optional[list[str]] = None, timeout: Optional[float] = None, # pylint: disable=unused-argument - partition_names: Optional[List[str]] = None, + partition_names: Optional[list[str]] = None, **kwargs, # pylint: disable=unused-argument - ) -> List[dict]: + ) -> list[dict]: """Get records with specified primary field `ids`. Args: @@ -672,7 +672,7 @@ def delete( def insert( self, collection_name: str, - data: Union[Dict, List[Dict]], + data: Union[dict, list[dict]], timeout: Optional[float] = None, partition_name: Optional[str] = "", ) -> ( @@ -700,10 +700,10 @@ def insert( def upsert( self, collection_name: str, - data: Union[Dict, List[Dict]], + data: Union[dict, list[dict]], timeout: Optional[float] = None, # pylint: disable=unused-argument partition_name: Optional[str] = "", - ) -> List[Union[str, int]]: + ) -> list[Union[str, int]]: """Update data in table. If primary key is duplicated, replace it. Args: diff --git a/pyobvector/client/ob_client.py b/pyobvector/client/ob_client.py index b40ade6..6149320 100644 --- a/pyobvector/client/ob_client.py +++ b/pyobvector/client/ob_client.py @@ -1,5 +1,5 @@ import logging -from typing import List, Optional, Dict, Union +from typing import Optional, Union from urllib.parse import quote import sqlalchemy.sql.functions as func_mod @@ -141,8 +141,8 @@ def check_table_exists(self, table_name: str): def create_table( self, table_name: str, - columns: List[Column], - indexes: Optional[List[Index]] = None, + columns: list[Column], + indexes: Optional[list[Index]] = None, partitions: Optional[ObPartition] = None, **kwargs, ): @@ -208,7 +208,7 @@ def drop_index(self, table_name: str, index_name: str): def insert( self, table_name: str, - data: Union[Dict, List[Dict]], + data: Union[dict, list[dict]], partition_name: Optional[str] = "", ): """Insert data into table. @@ -218,7 +218,7 @@ def insert( data (Union[Dict, List[Dict]]): data that will be inserted partition_name (Optional[str]): limit the query to certain partition """ - if isinstance(data, Dict): + if isinstance(data, dict): data = [data] if len(data) == 0: @@ -240,7 +240,7 @@ def insert( def upsert( self, table_name: str, - data: Union[Dict, List[Dict]], + data: Union[dict, list[dict]], partition_name: Optional[str] = "", ): """Update data in table. If primary key is duplicated, replace it. @@ -250,7 +250,7 @@ def upsert( data (Union[Dict, List[Dict]]): data that will be upserted partition_name (Optional[str]): limit the query to certain partition """ - if isinstance(data, Dict): + if isinstance(data, dict): data = [data] if len(data) == 0: @@ -365,8 +365,8 @@ def get( table_name: str, ids: Optional[Union[list, str, int]] = None, where_clause=None, - output_column_name: Optional[List[str]] = None, - partition_names: Optional[List[str]] = None, + output_column_name: Optional[list[str]] = None, + partition_names: Optional[list[str]] = None, n_limits: Optional[int] = None, ): """Get records with specified primary field `ids`. diff --git a/pyobvector/client/ob_vec_client.py b/pyobvector/client/ob_vec_client.py index a95a93e..4ef5a73 100644 --- a/pyobvector/client/ob_vec_client.py +++ b/pyobvector/client/ob_vec_client.py @@ -1,6 +1,6 @@ """OceanBase Vector Store Client.""" import logging -from typing import List, Optional, Union +from typing import Optional, Union import numpy as np from sqlalchemy import ( @@ -60,10 +60,10 @@ def _get_sparse_vector_index_params( def create_table_with_index_params( self, table_name: str, - columns: List[Column], - indexes: Optional[List[Index]] = None, + columns: list[Column], + indexes: Optional[list[Index]] = None, vidxs: Optional[IndexParams] = None, - fts_idxs: Optional[List[FtsIndexParam]] = None, + fts_idxs: Optional[list[FtsIndexParam]] = None, partitions: Optional[ObPartition] = None, ): """Create table with optional index_params. @@ -140,7 +140,7 @@ def create_index( table_name: str, is_vec_index: bool, index_name: str, - column_names: List[str], + column_names: list[str], vidx_params: Optional[str] = None, **kw, ): @@ -274,12 +274,12 @@ def ann_search( distance_func, with_dist: bool = False, topk: int = 10, - output_column_names: Optional[List[str]] = None, - output_columns: Optional[Union[List, tuple]] = None, - extra_output_cols: Optional[List] = None, + output_column_names: Optional[list[str]] = None, + output_columns: Optional[Union[list, tuple]] = None, + extra_output_cols: Optional[list] = None, where_clause=None, - partition_names: Optional[List[str]] = None, - idx_name_hint: Optional[List[str]] = None, + partition_names: Optional[list[str]] = None, + idx_name_hint: Optional[list[str]] = None, distance_threshold: Optional[float] = None, **kwargs, ): # pylint: disable=unused-argument @@ -403,11 +403,11 @@ def post_ann_search( distance_func, with_dist: bool = False, topk: int = 10, - output_column_names: Optional[List[str]] = None, - extra_output_cols: Optional[List] = None, + output_column_names: Optional[list[str]] = None, + extra_output_cols: Optional[list] = None, where_clause=None, - partition_names: Optional[List[str]] = None, - str_list: Optional[List[str]] = None, + partition_names: Optional[list[str]] = None, + str_list: Optional[list[str]] = None, **kwargs, ): # pylint: disable=unused-argument """Perform post ann search. @@ -483,7 +483,7 @@ def precise_search( vec_column_name: str, distance_func, topk: int = 10, - output_column_names: Optional[List[str]] = None, + output_column_names: Optional[list[str]] = None, where_clause=None, **kwargs, ): # pylint: disable=unused-argument diff --git a/pyobvector/client/ob_vec_json_table_client.py b/pyobvector/client/ob_vec_json_table_client.py index e1cc25c..4a4da3c 100644 --- a/pyobvector/client/ob_vec_json_table_client.py +++ b/pyobvector/client/ob_vec_json_table_client.py @@ -1,18 +1,16 @@ import json import logging import re -from typing import Dict, List, Optional, Any, Union +from typing import Optional, Union from sqlalchemy import Column, Integer, String, JSON, Engine, select, text, func, CursorResult from sqlalchemy.dialects.mysql import TINYINT from sqlalchemy.orm import declarative_base, sessionmaker, Session from sqlglot import parse_one, exp, Expression, to_identifier -from sqlglot.expressions import Concat from .ob_vec_client import ObVecClient from ..json_table import ( - OceanBase, ChangeColumn, JsonTableBool, JsonTableTimestamp, @@ -58,7 +56,7 @@ class JsonTableDataTBL(Base): class JsonTableMetadata: def __init__(self, user_id: str): self.user_id = user_id - self.meta_cache: Dict[str, List] = {} + self.meta_cache: dict[str, list] = {} @classmethod def _parse_col_type(cls, col_type: str): @@ -319,7 +317,7 @@ def _handle_create_json_table(self, ast: Expression): def _check_table_exists(self, jtable_name: str) -> bool: return jtable_name in self.jmetadata.meta_cache - def _check_col_exists(self, jtable_name: str, col_name: str) -> Optional[Dict]: + def _check_col_exists(self, jtable_name: str, col_name: str) -> Optional[dict]: if not self._check_table_exists(jtable_name): return None for col_meta in self.jmetadata.meta_cache[jtable_name]: @@ -339,7 +337,7 @@ def _parse_col_datatype(self, expr: Expression) -> str: col_type_str += '(' + ','.join(col_type_params_list) + ')' return col_type_str - def _parse_col_constraints(self, expr: Expression) -> Dict: + def _parse_col_constraints(self, expr: Expression) -> dict: col_has_default = False col_nullable = True for cons in expr: diff --git a/pyobvector/client/partitions.py b/pyobvector/client/partitions.py index c3eed13..00cd6ed 100644 --- a/pyobvector/client/partitions.py +++ b/pyobvector/client/partitions.py @@ -1,5 +1,5 @@ """A module to do compilation of OceanBase Parition Clause.""" -from typing import List, Optional, Union +from typing import Optional, Union import logging from dataclasses import dataclass from .enum import IntEnum @@ -69,11 +69,11 @@ class RangeListPartInfo: Using 100 / `MAXVALUE` when create Range/RangeColumns partition. """ part_name: str - part_upper_bound_expr: Union[List, str, int] + part_upper_bound_expr: Union[list, str, int] def get_part_expr_str(self): """Parse part_upper_bound_expr to text SQL.""" - if isinstance(self.part_upper_bound_expr, List): + if isinstance(self.part_upper_bound_expr, list): return ",".join([str(v) for v in self.part_upper_bound_expr]) if isinstance(self.part_upper_bound_expr, str): return self.part_upper_bound_expr @@ -87,9 +87,9 @@ class ObRangePartition(ObPartition): def __init__( self, is_range_columns: bool, - range_part_infos: List[RangeListPartInfo], + range_part_infos: list[RangeListPartInfo], range_expr: Optional[str] = None, - col_name_list: Optional[List[str]] = None, + col_name_list: Optional[list[str]] = None, ): super().__init__(PartType.RangeColumns if is_range_columns else PartType.Range) self.range_part_infos = range_part_infos @@ -140,9 +140,9 @@ class ObSubRangePartition(ObRangePartition): def __init__( self, is_range_columns: bool, - range_part_infos: List[RangeListPartInfo], + range_part_infos: list[RangeListPartInfo], range_expr: Optional[str] = None, - col_name_list: Optional[List[str]] = None, + col_name_list: Optional[list[str]] = None, ): super().__init__(is_range_columns, range_part_infos, range_expr, col_name_list) self.is_sub = True @@ -176,9 +176,9 @@ class ObListPartition(ObPartition): def __init__( self, is_list_columns: bool, - list_part_infos: List[RangeListPartInfo], + list_part_infos: list[RangeListPartInfo], list_expr: Optional[str] = None, - col_name_list: Optional[List[str]] = None, + col_name_list: Optional[list[str]] = None, ): super().__init__(PartType.ListColumns if is_list_columns else PartType.List) self.list_part_infos = list_part_infos @@ -228,9 +228,9 @@ class ObSubListPartition(ObListPartition): def __init__( self, is_list_columns: bool, - list_part_infos: List[RangeListPartInfo], + list_part_infos: list[RangeListPartInfo], list_expr: Optional[str] = None, - col_name_list: Optional[List[str]] = None, + col_name_list: Optional[list[str]] = None, ): super().__init__(is_list_columns, list_part_infos, list_expr, col_name_list) self.is_sub = True @@ -263,7 +263,7 @@ class ObHashPartition(ObPartition): def __init__( self, hash_expr: str, - hash_part_name_list: List[str] = None, + hash_part_name_list: list[str] = None, part_count: Optional[int] = None, ): super().__init__(PartType.Hash) @@ -308,7 +308,7 @@ class ObSubHashPartition(ObHashPartition): def __init__( self, hash_expr: str, - hash_part_name_list: List[str] = None, + hash_part_name_list: list[str] = None, part_count: Optional[int] = None, ): super().__init__(hash_expr, hash_part_name_list, part_count) @@ -334,8 +334,8 @@ class ObKeyPartition(ObPartition): """Key partition strategy.""" def __init__( self, - col_name_list: List[str], - key_part_name_list: List[str] = None, + col_name_list: list[str], + key_part_name_list: list[str] = None, part_count: Optional[int] = None, ): super().__init__(PartType.Key) @@ -381,8 +381,8 @@ class ObSubKeyPartition(ObKeyPartition): """Key subpartition strategy.""" def __init__( self, - col_name_list: List[str], - key_part_name_list: List[str] = None, + col_name_list: list[str], + key_part_name_list: list[str] = None, part_count: Optional[int] = None, ): super().__init__(col_name_list, key_part_name_list, part_count) diff --git a/pyobvector/json_table/json_value_returning_func.py b/pyobvector/json_table/json_value_returning_func.py index 18f7f9f..f957052 100644 --- a/pyobvector/json_table/json_value_returning_func.py +++ b/pyobvector/json_table/json_value_returning_func.py @@ -1,10 +1,9 @@ import logging import re -from typing import Tuple from sqlalchemy.ext.compiler import compiles from sqlalchemy.sql.functions import FunctionElement -from sqlalchemy import BINARY, Float, Boolean, Text +from sqlalchemy import Text logger = logging.getLogger(__name__) diff --git a/pyobvector/json_table/virtual_data_type.py b/pyobvector/json_table/virtual_data_type.py index 4745b14..4c2443e 100644 --- a/pyobvector/json_table/virtual_data_type.py +++ b/pyobvector/json_table/virtual_data_type.py @@ -2,7 +2,7 @@ from decimal import Decimal, InvalidOperation, ROUND_DOWN from enum import Enum from typing import Optional -from typing_extensions import Annotated +from typing import Annotated from pydantic import BaseModel, Field, AfterValidator, create_model diff --git a/pyobvector/schema/array.py b/pyobvector/schema/array.py index 52f8d1a..3578ef1 100644 --- a/pyobvector/schema/array.py +++ b/pyobvector/schema/array.py @@ -1,6 +1,7 @@ """ARRAY: An extended data type for SQLAlchemy""" import json -from typing import Any, List, Optional, Sequence, Union +from typing import Any, Optional, Union +from collections.abc import Sequence from sqlalchemy.sql.type_api import TypeEngine from sqlalchemy.types import UserDefinedType, String @@ -49,7 +50,7 @@ def _get_list_depth(self, value: Any) -> int: def _validate_dimension(self, value: list[Any]): arr_depth = self._get_list_depth(value) - assert arr_depth == self.dim, "Array dimension mismatch, expected {}, got {}".format(self.dim, arr_depth) + assert arr_depth == self.dim, f"Array dimension mismatch, expected {self.dim}, got {arr_depth}" def bind_processor(self, dialect): item_type = self.item_type @@ -58,7 +59,7 @@ def bind_processor(self, dialect): item_proc = item_type.dialect_impl(dialect).bind_processor(dialect) - def process(value: Optional[Sequence[Any] | str]) -> Optional[str]: + def process(value: Optional[Union[Sequence[Any] | str]]) -> Optional[str]: if value is None: return None if isinstance(value, str): @@ -85,7 +86,7 @@ def result_processor(self, dialect, coltype): item_proc = item_type.dialect_impl(dialect).result_processor(dialect, coltype) - def process(value: Optional[str]) -> Optional[List[Any]]: + def process(value: Optional[str]) -> Optional[list[Any]]: if value is None: return None diff --git a/pyobvector/schema/geo_srid_point.py b/pyobvector/schema/geo_srid_point.py index 74642bb..64bd161 100644 --- a/pyobvector/schema/geo_srid_point.py +++ b/pyobvector/schema/geo_srid_point.py @@ -1,5 +1,5 @@ """Point: OceanBase GIS data type for SQLAlchemy""" -from typing import Tuple, Optional +from typing import Optional from sqlalchemy.types import UserDefinedType, String class POINT(UserDefinedType): @@ -24,7 +24,7 @@ def get_col_spec(self, **kw): # pylint: disable=unused-argument return f"POINT SRID {self.srid}" @classmethod - def to_db(cls, value: Tuple[float, float]): + def to_db(cls, value: tuple[float, float]): """Parse tuple to POINT literal""" return f"POINT({value[0]} {value[1]})" diff --git a/pyobvector/schema/gis_func.py b/pyobvector/schema/gis_func.py index a403c4e..877b00e 100644 --- a/pyobvector/schema/gis_func.py +++ b/pyobvector/schema/gis_func.py @@ -1,7 +1,6 @@ """gis_func: An extended system function in GIS.""" import logging -from typing import Tuple from sqlalchemy.ext.compiler import compiles from sqlalchemy.sql.functions import FunctionElement @@ -30,7 +29,7 @@ def compile_ST_GeomFromText(element, compiler, **kwargs): # pylint: disable=unus for idx, arg in enumerate(element.args): if idx == 0: if ( - (not isinstance(arg, Tuple)) or + (not isinstance(arg, tuple)) or (len(arg) != 2) or (not all(isinstance(x, float) for x in arg)) ): diff --git a/pyobvector/schema/reflection.py b/pyobvector/schema/reflection.py index 2a3331c..df16b1e 100644 --- a/pyobvector/schema/reflection.py +++ b/pyobvector/schema/reflection.py @@ -35,12 +35,12 @@ def _prep_regexes(self): self._re_array_column = _re_compile( r"\s*" - r"%(iq)s(?P(?:%(esc_fq)s|[^%(fq)s])+)%(fq)s\s+" + r"{iq}(?P(?:{esc_fq}|[^{fq}])+){fq}\s+" r"(?P(?i:(?(?:NOT\s+)?NULL))?" r"(?:\s+DEFAULT\s+(?P(?:NULL|'(?:''|[^'])*'|\(.+?\)|[\-\w\.\(\)]+)))?" r"(?:\s+COMMENT\s+'(?P(?:''|[^'])*)')?" - r"\s*,?\s*$" % quotes + r"\s*,?\s*$".format(**quotes) ) self._re_key = _re_compile( diff --git a/pyobvector/util/ob_version.py b/pyobvector/util/ob_version.py index d0b2d51..40ac016 100644 --- a/pyobvector/util/ob_version.py +++ b/pyobvector/util/ob_version.py @@ -1,6 +1,5 @@ """OceanBase cluster version module.""" import copy -from typing import List class ObVersion: @@ -9,7 +8,7 @@ class ObVersion: Attributes: version_nums (List[int]): version number of OceanBase cluster. For example, '4.3.3.0' """ - def __init__(self, version_nums: List[int]): + def __init__(self, version_nums: list[int]): self.version_nums = copy.deepcopy(version_nums) @classmethod diff --git a/pyproject.toml b/pyproject.toml index dbd68d5..0030ab2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,8 @@ Repository = "https://github.com/oceanbase/pyobvector.git" [dependency-groups] dev = [ "pytest>=8.2.2", - "pylint>=3.2.7" + "pylint>=3.2.7", + "ruff>=0.14.10", ] [tool.pytest.ini_options] @@ -33,3 +34,9 @@ log_level = "INFO" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" + +[tool.ruff.lint] +select = [ + "UP", # pyupgrade + "F401", # unused-import +] diff --git a/tests/__init__.py b/tests/__init__.py index f89feb5..2e8ef3e 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,9 +1,5 @@ import unittest -from pyobvector.client import ObVecClient, VecIndexType, IndexParam -from pyobvector.schema import VECTOR, VectorIndex -from sqlalchemy import Column, Integer, Table -from sqlalchemy.sql import func -from sqlalchemy.exc import NoSuchTableError +from pyobvector.client import ObVecClient class ObVecClientTest(unittest.TestCase): diff --git a/tests/test_milvus_like_client.py b/tests/test_milvus_like_client.py index 28af352..9aa8cd9 100644 --- a/tests/test_milvus_like_client.py +++ b/tests/test_milvus_like_client.py @@ -1,7 +1,5 @@ import unittest from pyobvector import * -from sqlalchemy import Column, Integer, Table, func, text -import numpy as np import logging logger = logging.getLogger(__name__) diff --git a/tests/test_milvus_like_client_sparse_vector.py b/tests/test_milvus_like_client_sparse_vector.py index 09652cf..211e874 100644 --- a/tests/test_milvus_like_client_sparse_vector.py +++ b/tests/test_milvus_like_client_sparse_vector.py @@ -1,7 +1,6 @@ import unittest from pyobvector import * -from sqlalchemy import Column, Integer, Table, func, text, Index, String -import numpy as np +from sqlalchemy import text import logging logger = logging.getLogger(__name__) diff --git a/tests/test_ob_vec_client.py b/tests/test_ob_vec_client.py index 01d4155..c4b7180 100644 --- a/tests/test_ob_vec_client.py +++ b/tests/test_ob_vec_client.py @@ -2,7 +2,6 @@ import unittest from pyobvector import * from sqlalchemy import Column, Integer, JSON, String, text, Table -from sqlalchemy import func import logging logger = logging.getLogger(__name__) @@ -275,7 +274,7 @@ def test_array_column(self): self.assertEqual(row[1], ["tag1"]) self.assertEqual(row[2], [[9]]) else: - self.fail("Unexpected row: {}".format(row)) + self.fail(f"Unexpected row: {row}") def test_refresh_metadata(self): test_collection_name = "ob_refresh_metadata_test" diff --git a/tests/test_ob_vec_client_sparse_vector.py b/tests/test_ob_vec_client_sparse_vector.py index 8b5b961..22ea9f1 100644 --- a/tests/test_ob_vec_client_sparse_vector.py +++ b/tests/test_ob_vec_client_sparse_vector.py @@ -1,8 +1,6 @@ -import json import unittest from pyobvector import * -from sqlalchemy import Column, Integer, JSON, String, text, Table -from sqlalchemy import func +from sqlalchemy import Column, Integer, String, text, Table import logging logger = logging.getLogger(__name__)