diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 647d07bc..34c57d0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] - python-version: ["3.9", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "pypy3.9", "pypy3.10"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "pypy3.9", "pypy3.10"] steps: - uses: actions/checkout@v5 @@ -53,7 +53,6 @@ jobs: - uses: actions/setup-python@v6 with: python-version: "3.12" - - run: pip install mypy - run: | - pip install + pip install mypy mypy . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3232a4d0..15e08c27 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,10 @@ exclude: '.git' -default_stages: [commit] +default_stages: [pre-commit] fail_fast: false repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v6.0.0 hooks: - id: trailing-whitespace files: "pypika.*" @@ -20,6 +20,6 @@ repos: - id: debug-statements - repo: https://github.com/psf/black - rev: 23.9.1 + rev: 25.9.0 hooks: - id: black diff --git a/Makefile b/Makefile index aec52ac1..feed3c83 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,8 @@ install: ## Install development dependencies pre-commit install test: ## Run tests - tox + coverage run -m pytest + coverage report -m docs.build: ## Build the documentation $(SPHINXBUILD) $(SOURCEDIR) $(BUILDDIR) -b html -E diff --git a/pypika/queries.py b/pypika/queries.py index 6784bd94..7ad36c06 100644 --- a/pypika/queries.py +++ b/pypika/queries.py @@ -1,7 +1,7 @@ +import sys from copy import copy from functools import reduce -from typing import Any, Generic, List, Optional, Sequence, Tuple as TypedTuple, Type, TypeVar, Union -from typing_extensions import Self, Type +from typing import TYPE_CHECKING, Any, Generic, List, Optional, Sequence, Tuple as TypedTuple, Type, TypeVar, Union from pypika.enums import Dialects, JoinType, ReferenceOption, SetOperation from pypika.terms import ( @@ -30,6 +30,12 @@ ignore_copy, ) +if TYPE_CHECKING: + if sys.version_info >= (3, 11): + from typing import Self + else: + from typing_extensions import Self + __author__ = "Timothy Heys" __email__ = "theys@kayak.com" diff --git a/pypika/terms.py b/pypika/terms.py index 9590a976..f1816312 100644 --- a/pypika/terms.py +++ b/pypika/terms.py @@ -1,5 +1,8 @@ +from __future__ import annotations + import inspect import re +import sys import uuid from datetime import ( date, @@ -21,6 +24,7 @@ Type, TypeVar, Union, + overload, ) from pypika.enums import Arithmetic, Boolean, Comparator, Dialects, Equality, JSONOperators, Matching, Order @@ -35,6 +39,10 @@ ) if TYPE_CHECKING: + if sys.version_info < (3, 11): + from typing_extensions import Self + else: + from typing import Self from pypika.queries import QueryBuilder, Selectable, Table @@ -593,23 +601,30 @@ def __init__(self, alias: Optional[str] = None) -> None: class Criterion(Term): - def __and__(self, other: Any) -> "ComplexCriterion": - if isinstance(other, EmptyCriterion): - return self - return ComplexCriterion(Boolean.and_, self, other) + @overload + def _compare(self, comparator: Comparator, other: EmptyCriterion) -> "Self": + ... - def __or__(self, other: Any) -> "ComplexCriterion": - if isinstance(other, EmptyCriterion): - return self - return ComplexCriterion(Boolean.or_, self, other) + @overload + def _compare(self, comparator: Comparator, other: Any) -> "ComplexCriterion": + ... - def __xor__(self, other: Any) -> "ComplexCriterion": + def _compare(self, comparator: Comparator, other: Any) -> "Self | ComplexCriterion": if isinstance(other, EmptyCriterion): return self - return ComplexCriterion(Boolean.xor_, self, other) + return ComplexCriterion(comparator, self, other) + + def __and__(self, other: Any) -> "Self | ComplexCriterion": + return self._compare(Boolean.and_, other) + + def __or__(self, other: Any) -> "Self | ComplexCriterion": + return self._compare(Boolean.or_, other) + + def __xor__(self, other: Any) -> "Self | ComplexCriterion": + return self._compare(Boolean.xor_, other) @staticmethod - def any(terms: Iterable[Term] = ()) -> "EmptyCriterion": + def any(terms: Iterable[Term] = ()) -> "EmptyCriterion | Term | ComplexCriterion": crit = EmptyCriterion() for term in terms: @@ -618,7 +633,7 @@ def any(terms: Iterable[Term] = ()) -> "EmptyCriterion": return crit @staticmethod - def all(terms: Iterable[Any] = ()) -> "EmptyCriterion": + def all(terms: Iterable[Any] = ()) -> "EmptyCriterion | Any | ComplexCriterion": crit = EmptyCriterion() for term in terms: diff --git a/pypika/utils.py b/pypika/utils.py index 53cab4da..6870188f 100644 --- a/pypika/utils.py +++ b/pypika/utils.py @@ -1,6 +1,11 @@ +import sys from functools import wraps from typing import Any, Callable, List, Optional, Type, TypeVar, Union, overload -from typing_extensions import Concatenate, ParamSpec + +if sys.version_info >= (3, 10): + from typing import Concatenate, ParamSpec +else: + from typing_extensions import Concatenate, ParamSpec __author__ = "Timothy Heys" @@ -45,13 +50,11 @@ class FunctionException(Exception): @overload -def builder(func: Callable[Concatenate[_Self, P], None]) -> Callable[Concatenate[_Self, P], _Self]: - ... +def builder(func: Callable[Concatenate[_Self, P], None]) -> Callable[Concatenate[_Self, P], _Self]: ... @overload -def builder(func: Callable[Concatenate[_Self, P], R]) -> Callable[Concatenate[_Self, P], R]: - ... +def builder(func: Callable[Concatenate[_Self, P], R]) -> Callable[Concatenate[_Self, P], R]: ... def builder(func: Callable[Concatenate[_Self, P], Optional[R]]) -> Callable[Concatenate[_Self, P], Union[_Self, R]]: @@ -121,7 +124,7 @@ def resolve_is_aggregate(values: List[Optional[bool]]) -> Optional[bool]: def format_quotes(value: Any, quote_char: Optional[str]) -> str: if quote_char: - value = value.replace(quote_char, quote_char * 2) + value = str(value).replace(quote_char, quote_char * 2) return "{quote}{value}{quote}".format(value=value, quote=quote_char or "") diff --git a/requirements.txt b/requirements.txt index 948deebe..d1c80adc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -typing_extensions>=4.5.0 +typing_extensions>=4.5.0; python_version<'3.11' diff --git a/setup.py b/setup.py index 115b830a..2bb4eb2a 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ def version(): package_data={"pypika": ["py.typed"]}, include_package_data=True, - install_requires=["typing_extensions>=4.5.0"], + install_requires=["typing_extensions>=4.5.0; python_version<'3.11'"], # Details url="https://github.com/kayak/pypika",