diff --git a/pandas/_config/config.py b/pandas/_config/config.py index 47913c2a1cf7d..455f800073c15 100644 --- a/pandas/_config/config.py +++ b/pandas/_config/config.py @@ -48,6 +48,8 @@ """ +from __future__ import annotations + from collections import namedtuple from contextlib import ( ContextDecorator, @@ -57,12 +59,7 @@ from typing import ( Any, Callable, - Dict, Iterable, - List, - Optional, - Tuple, - Type, cast, ) import warnings @@ -73,16 +70,16 @@ RegisteredOption = namedtuple("RegisteredOption", "key defval doc validator cb") # holds deprecated option metadata -_deprecated_options: Dict[str, DeprecatedOption] = {} +_deprecated_options: dict[str, DeprecatedOption] = {} # holds registered option metadata -_registered_options: Dict[str, RegisteredOption] = {} +_registered_options: dict[str, RegisteredOption] = {} # holds the current values for registered options -_global_config: Dict[str, Any] = {} +_global_config: dict[str, Any] = {} # keys which have a special meaning -_reserved_keys: List[str] = ["all"] +_reserved_keys: list[str] = ["all"] class OptionError(AttributeError, KeyError): @@ -194,7 +191,7 @@ def get_default_val(pat: str): class DictWrapper: """ provide attribute-style access to a nested dict""" - def __init__(self, d: Dict[str, Any], prefix: str = ""): + def __init__(self, d: dict[str, Any], prefix: str = ""): object.__setattr__(self, "d", d) object.__setattr__(self, "prefix", prefix) @@ -428,8 +425,8 @@ def register_option( key: str, defval: object, doc: str = "", - validator: Optional[Callable[[Any], Any]] = None, - cb: Optional[Callable[[str], Any]] = None, + validator: Callable[[Any], Any] | None = None, + cb: Callable[[str], Any] | None = None, ) -> None: """ Register an option in the package-wide pandas config object @@ -500,7 +497,7 @@ def register_option( def deprecate_option( - key: str, msg: Optional[str] = None, rkey: Optional[str] = None, removal_ver=None + key: str, msg: str | None = None, rkey: str | None = None, removal_ver=None ) -> None: """ Mark option `key` as deprecated, if code attempts to access this option, @@ -547,7 +544,7 @@ def deprecate_option( # functions internal to the module -def _select_options(pat: str) -> List[str]: +def _select_options(pat: str) -> list[str]: """ returns a list of keys matching `pat` @@ -565,7 +562,7 @@ def _select_options(pat: str) -> List[str]: return [k for k in keys if re.search(pat, k, re.I)] -def _get_root(key: str) -> Tuple[Dict[str, Any], str]: +def _get_root(key: str) -> tuple[dict[str, Any], str]: path = key.split(".") cursor = _global_config for p in path[:-1]: @@ -674,7 +671,7 @@ def pp_options_list(keys: Iterable[str], width=80, _print: bool = False): from itertools import groupby from textwrap import wrap - def pp(name: str, ks: Iterable[str]) -> List[str]: + def pp(name: str, ks: Iterable[str]) -> list[str]: pfx = "- " + name + ".[" if name else "" ls = wrap( ", ".join(ks), @@ -687,7 +684,7 @@ def pp(name: str, ks: Iterable[str]) -> List[str]: ls[-1] = ls[-1] + "]" return ls - ls: List[str] = [] + ls: list[str] = [] singles = [x for x in sorted(keys) if x.find(".") < 0] if singles: ls += pp("", singles) @@ -760,7 +757,7 @@ def inner(key: str, *args, **kwds): # arg in register_option -def is_type_factory(_type: Type[Any]) -> Callable[[Any], None]: +def is_type_factory(_type: type[Any]) -> Callable[[Any], None]: """ Parameters @@ -826,7 +823,7 @@ def inner(x) -> None: return inner -def is_nonnegative_int(value: Optional[int]) -> None: +def is_nonnegative_int(value: int | None) -> None: """ Verify that value is None or a positive int. diff --git a/pandas/_config/display.py b/pandas/_config/display.py index ec819481525da..df2c3ad36c855 100644 --- a/pandas/_config/display.py +++ b/pandas/_config/display.py @@ -2,15 +2,16 @@ Unopinionated display configuration. """ +from __future__ import annotations + import locale import sys -from typing import Optional from pandas._config import config as cf # ----------------------------------------------------------------------------- # Global formatting options -_initial_defencoding: Optional[str] = None +_initial_defencoding: str | None = None def detect_console_encoding() -> str: diff --git a/pandas/_testing/_io.py b/pandas/_testing/_io.py index c77b9172e2091..58ce9b17909bb 100644 --- a/pandas/_testing/_io.py +++ b/pandas/_testing/_io.py @@ -1,11 +1,11 @@ +from __future__ import annotations + import bz2 from functools import wraps import gzip from typing import ( Any, Callable, - Optional, - Tuple, ) import zipfile @@ -272,9 +272,7 @@ def can_connect(url, error_classes=None): # File-IO -def round_trip_pickle( - obj: Any, path: Optional[FilePathOrBuffer] = None -) -> FrameOrSeries: +def round_trip_pickle(obj: Any, path: FilePathOrBuffer | None = None) -> FrameOrSeries: """ Pickle an object and then read it again. @@ -298,7 +296,7 @@ def round_trip_pickle( return pd.read_pickle(temp_path) -def round_trip_pathlib(writer, reader, path: Optional[str] = None): +def round_trip_pathlib(writer, reader, path: str | None = None): """ Write an object to file specified by a pathlib.Path and read it back @@ -327,7 +325,7 @@ def round_trip_pathlib(writer, reader, path: Optional[str] = None): return obj -def round_trip_localpath(writer, reader, path: Optional[str] = None): +def round_trip_localpath(writer, reader, path: str | None = None): """ Write an object to file specified by a py.path LocalPath and read it back. @@ -375,7 +373,7 @@ def write_to_compressed(compression, path, data, dest="test"): ------ ValueError : An invalid compression value was passed in. """ - args: Tuple[Any, ...] = (data,) + args: tuple[Any, ...] = (data,) mode = "wb" method = "write" compress_method: Callable diff --git a/pandas/_testing/_warnings.py b/pandas/_testing/_warnings.py index ee32abe19278e..027a53edb1810 100644 --- a/pandas/_testing/_warnings.py +++ b/pandas/_testing/_warnings.py @@ -1,10 +1,10 @@ +from __future__ import annotations + from contextlib import contextmanager import re from typing import ( - Optional, Sequence, Type, - Union, cast, ) import warnings @@ -12,11 +12,11 @@ @contextmanager def assert_produces_warning( - expected_warning: Optional[Union[Type[Warning], bool]] = Warning, + expected_warning: type[Warning] | bool | None = Warning, filter_level="always", check_stacklevel: bool = True, raise_on_extra_warnings: bool = True, - match: Optional[str] = None, + match: str | None = None, ): """ Context manager for running code expected to either raise a specific @@ -99,8 +99,8 @@ class for all warnings. To check that no warning is returned, def _assert_caught_expected_warning( *, caught_warnings: Sequence[warnings.WarningMessage], - expected_warning: Type[Warning], - match: Optional[str], + expected_warning: type[Warning], + match: str | None, check_stacklevel: bool, ) -> None: """Assert that there was the expected warning among the caught warnings.""" @@ -135,7 +135,7 @@ def _assert_caught_expected_warning( def _assert_caught_no_extra_warnings( *, caught_warnings: Sequence[warnings.WarningMessage], - expected_warning: Optional[Union[Type[Warning], bool]], + expected_warning: type[Warning] | bool | None, ) -> None: """Assert that no extra warnings apart from the expected ones are caught.""" extra_warnings = [] @@ -157,7 +157,7 @@ def _assert_caught_no_extra_warnings( def _is_unexpected_warning( actual_warning: warnings.WarningMessage, - expected_warning: Optional[Union[Type[Warning], bool]], + expected_warning: type[Warning] | bool | None, ) -> bool: """Check if the actual warning issued is unexpected.""" if actual_warning and not expected_warning: diff --git a/pandas/_testing/asserters.py b/pandas/_testing/asserters.py index 4f61ceee4d66b..912039b7571bc 100644 --- a/pandas/_testing/asserters.py +++ b/pandas/_testing/asserters.py @@ -1,7 +1,6 @@ -from typing import ( - Union, - cast, -) +from __future__ import annotations + +from typing import cast import warnings import numpy as np @@ -56,8 +55,8 @@ def assert_almost_equal( left, right, - check_dtype: Union[bool, str] = "equiv", - check_less_precise: Union[bool, int, NoDefault] = no_default, + check_dtype: bool | str = "equiv", + check_less_precise: bool | int | NoDefault = no_default, rtol: float = 1.0e-5, atol: float = 1.0e-8, **kwargs, @@ -169,7 +168,7 @@ def assert_almost_equal( ) -def _get_tol_from_less_precise(check_less_precise: Union[bool, int]) -> float: +def _get_tol_from_less_precise(check_less_precise: bool | int) -> float: """ Return the tolerance equivalent to the deprecated `check_less_precise` parameter. @@ -247,9 +246,9 @@ def assert_dict_equal(left, right, compare_keys: bool = True): def assert_index_equal( left: Index, right: Index, - exact: Union[bool, str] = "equiv", + exact: bool | str = "equiv", check_names: bool = True, - check_less_precise: Union[bool, int, NoDefault] = no_default, + check_less_precise: bool | int | NoDefault = no_default, check_exact: bool = True, check_categorical: bool = True, check_order: bool = True, @@ -429,7 +428,7 @@ def _get_ilevel_values(index, level): assert_categorical_equal(left._values, right._values, obj=f"{obj} category") -def assert_class_equal(left, right, exact: Union[bool, str] = True, obj="Input"): +def assert_class_equal(left, right, exact: bool | str = True, obj="Input"): """ Checks classes are equal. """ diff --git a/pandas/_testing/contexts.py b/pandas/_testing/contexts.py index a14e87c04c913..e20d2d58e499f 100644 --- a/pandas/_testing/contexts.py +++ b/pandas/_testing/contexts.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from contextlib import contextmanager import os from pathlib import Path @@ -8,7 +10,6 @@ from typing import ( IO, Any, - Union, ) import numpy as np @@ -111,7 +112,7 @@ def ensure_clean(filename=None, return_filelike: bool = False, **kwargs: Any): path.touch() - handle_or_str: Union[str, IO] = str(path) + handle_or_str: str | IO = str(path) if return_filelike: kwargs.setdefault("mode", "w+b") handle_or_str = open(path, **kwargs) diff --git a/pandas/compat/_optional.py b/pandas/compat/_optional.py index a26da75d921ef..cf00618a7b2b9 100644 --- a/pandas/compat/_optional.py +++ b/pandas/compat/_optional.py @@ -1,8 +1,9 @@ +from __future__ import annotations + import distutils.version import importlib import sys import types -from typing import Optional import warnings # Update install.rst when updating versions! @@ -63,7 +64,7 @@ def import_optional_dependency( name: str, extra: str = "", errors: str = "raise", - min_version: Optional[str] = None, + min_version: str | None = None, ): """ Import an optional dependency. diff --git a/pandas/core/accessor.py b/pandas/core/accessor.py index 2b6dd379ea47c..c31368f179ef0 100644 --- a/pandas/core/accessor.py +++ b/pandas/core/accessor.py @@ -4,33 +4,30 @@ that can be mixed into or pinned onto other pandas classes. """ -from typing import ( - FrozenSet, - List, - Set, -) +from __future__ import annotations + import warnings from pandas.util._decorators import doc class DirNamesMixin: - _accessors: Set[str] = set() - _hidden_attrs: FrozenSet[str] = frozenset() + _accessors: set[str] = set() + _hidden_attrs: frozenset[str] = frozenset() - def _dir_deletions(self) -> Set[str]: + def _dir_deletions(self) -> set[str]: """ Delete unwanted __dir__ for this object. """ return self._accessors | self._hidden_attrs - def _dir_additions(self) -> Set[str]: + def _dir_additions(self) -> set[str]: """ Add additional __dir__ for this object. """ return {accessor for accessor in self._accessors if hasattr(self, accessor)} - def __dir__(self) -> List[str]: + def __dir__(self) -> list[str]: """ Provide method name lookup and completion. diff --git a/pandas/core/base.py b/pandas/core/base.py index 05ec9d543976a..42f52618eb07b 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -2,15 +2,13 @@ Base and utility classes for pandas objects. """ +from __future__ import annotations + import textwrap from typing import ( TYPE_CHECKING, Any, - Dict, - FrozenSet, - Optional, TypeVar, - Union, cast, ) @@ -64,7 +62,7 @@ if TYPE_CHECKING: from pandas import Categorical -_shared_docs: Dict[str, str] = {} +_shared_docs: dict[str, str] = {} _indexops_doc_kwargs = { "klass": "IndexOpsMixin", "inplace": "", @@ -81,7 +79,7 @@ class PandasObject(DirNamesMixin): """ # results from calls to methods decorated with cache_readonly get added to _cache - _cache: Dict[str, Any] + _cache: dict[str, Any] @property def _constructor(self): @@ -97,7 +95,7 @@ def __repr__(self) -> str: # Should be overwritten by base classes return object.__repr__(self) - def _reset_cache(self, key: Optional[str] = None) -> None: + def _reset_cache(self, key: str | None = None) -> None: """ Reset cached properties. If ``key`` is passed, only clears that key. """ @@ -170,7 +168,7 @@ class SelectionMixin: object sub-classes need to define: obj, exclusions """ - _selection: Optional[IndexLabel] = None + _selection: IndexLabel | None = None _internal_names = ["_cache", "__setstate__"] _internal_names_set = set(_internal_names) @@ -286,7 +284,7 @@ class IndexOpsMixin(OpsMixin): # ndarray compatibility __array_priority__ = 1000 - _hidden_attrs: FrozenSet[str] = frozenset( + _hidden_attrs: frozenset[str] = frozenset( ["tolist"] # tolist is not deprecated, just suppressed in the __dir__ ) @@ -296,7 +294,7 @@ def dtype(self) -> DtypeObj: raise AbstractMethodError(self) @property - def _values(self) -> Union[ExtensionArray, np.ndarray]: + def _values(self) -> ExtensionArray | np.ndarray: # must be defined here as a property for mypy raise AbstractMethodError(self) @@ -437,7 +435,7 @@ def array(self) -> ExtensionArray: def to_numpy( self, - dtype: Optional[Dtype] = None, + dtype: Dtype | None = None, copy: bool = False, na_value=lib.no_default, **kwargs, @@ -1139,7 +1137,7 @@ def _memory_usage(self, deep: bool = False) -> int: """ ), ) - def factorize(self, sort: bool = False, na_sentinel: Optional[int] = -1): + def factorize(self, sort: bool = False, na_sentinel: int | None = -1): return algorithms.factorize(self, sort=sort, na_sentinel=na_sentinel) _shared_docs[ @@ -1257,5 +1255,5 @@ def drop_duplicates(self, keep="first"): return self[~duplicated] # type: ignore[index] @final - def _duplicated(self, keep: Union[str, bool] = "first") -> np.ndarray: + def _duplicated(self, keep: str | bool = "first") -> np.ndarray: return duplicated(self._values, keep=keep) diff --git a/pandas/core/shared_docs.py b/pandas/core/shared_docs.py index 66d84ef85880c..a4ee4bb636450 100644 --- a/pandas/core/shared_docs.py +++ b/pandas/core/shared_docs.py @@ -1,6 +1,6 @@ -from typing import Dict +from __future__ import annotations -_shared_docs: Dict[str, str] = {} +_shared_docs: dict[str, str] = {} _shared_docs[ "aggregate" diff --git a/pandas/io/html.py b/pandas/io/html.py index 7541e5d62fd1e..43584e2cecfc4 100644 --- a/pandas/io/html.py +++ b/pandas/io/html.py @@ -4,18 +4,15 @@ """ +from __future__ import annotations + from collections import abc import numbers import os import re from typing import ( - Dict, - List, - Optional, Pattern, Sequence, - Tuple, - Union, ) from pandas._typing import FilePathOrBuffer @@ -447,7 +444,7 @@ def _expand_colspan_rowspan(self, rows): to subsequent cells. """ all_texts = [] # list of rows, each a list of str - remainder: List[Tuple[int, str, int]] = [] # list of (index, text, nrows) + remainder: list[tuple[int, str, int]] = [] # list of (index, text, nrows) for tr in rows: texts = [] # the output for this row @@ -940,21 +937,21 @@ def _parse(flavor, io, match, attrs, encoding, displayed_only, **kwargs): @deprecate_nonkeyword_arguments(version="2.0") def read_html( io: FilePathOrBuffer, - match: Union[str, Pattern] = ".+", - flavor: Optional[str] = None, - header: Optional[Union[int, Sequence[int]]] = None, - index_col: Optional[Union[int, Sequence[int]]] = None, - skiprows: Optional[Union[int, Sequence[int], slice]] = None, - attrs: Optional[Dict[str, str]] = None, + match: str | Pattern = ".+", + flavor: str | None = None, + header: int | Sequence[int] | None = None, + index_col: int | Sequence[int] | None = None, + skiprows: int | Sequence[int] | slice | None = None, + attrs: dict[str, str] | None = None, parse_dates: bool = False, - thousands: Optional[str] = ",", - encoding: Optional[str] = None, + thousands: str | None = ",", + encoding: str | None = None, decimal: str = ".", - converters: Optional[Dict] = None, + converters: dict | None = None, na_values=None, keep_default_na: bool = True, displayed_only: bool = True, -) -> List[DataFrame]: +) -> list[DataFrame]: r""" Read HTML tables into a ``list`` of ``DataFrame`` objects. diff --git a/pandas/io/spss.py b/pandas/io/spss.py index fb0ecee995463..533cf7a7a6331 100644 --- a/pandas/io/spss.py +++ b/pandas/io/spss.py @@ -1,9 +1,7 @@ +from __future__ import annotations + from pathlib import Path -from typing import ( - Optional, - Sequence, - Union, -) +from typing import Sequence from pandas.compat._optional import import_optional_dependency @@ -15,8 +13,8 @@ def read_spss( - path: Union[str, Path], - usecols: Optional[Sequence[str]] = None, + path: str | Path, + usecols: Sequence[str] | None = None, convert_categoricals: bool = True, ) -> DataFrame: """ diff --git a/pandas/io/sql.py b/pandas/io/sql.py index 4a83814c52ade..d797fa51984d6 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -3,6 +3,8 @@ retrieval and to reduce dependency on DB-specific API. """ +from __future__ import annotations + from contextlib import contextmanager from datetime import ( date, @@ -14,12 +16,8 @@ import re from typing import ( Any, - Dict, Iterator, - List, - Optional, Sequence, - Union, cast, overload, ) @@ -57,7 +55,7 @@ class DatabaseError(IOError): # ----------------------------------------------------------------------------- # -- Helper functions -_SQLALCHEMY_INSTALLED: Optional[bool] = None +_SQLALCHEMY_INSTALLED: bool | None = None def _is_sqlalchemy_connectable(con): @@ -111,7 +109,7 @@ def _process_parse_dates_argument(parse_dates): def _handle_date_column( - col, utc: Optional[bool] = None, format: Optional[Union[str, Dict[str, Any]]] = None + col, utc: bool | None = None, format: str | dict[str, Any] | None = None ): if isinstance(format, dict): # GH35185 Allow custom error values in parse_dates argument of @@ -165,7 +163,7 @@ def _wrap_result( index_col=None, coerce_float: bool = True, parse_dates=None, - dtype: Optional[DtypeArg] = None, + dtype: DtypeArg | None = None, ): """Wrap result set of query in a DataFrame.""" frame = DataFrame.from_records(data, columns=columns, coerce_float=coerce_float) @@ -244,13 +242,13 @@ def read_sql_table( def read_sql_table( table_name: str, con, - schema: Optional[str] = None, - index_col: Optional[Union[str, Sequence[str]]] = None, + schema: str | None = None, + index_col: str | Sequence[str] | None = None, coerce_float: bool = True, parse_dates=None, columns=None, - chunksize: Optional[int] = None, -) -> Union[DataFrame, Iterator[DataFrame]]: + chunksize: int | None = None, +) -> DataFrame | Iterator[DataFrame]: """ Read SQL database table into a DataFrame. @@ -345,7 +343,7 @@ def read_sql_query( params=None, parse_dates=None, chunksize: None = None, - dtype: Optional[DtypeArg] = None, + dtype: DtypeArg | None = None, ) -> DataFrame: ... @@ -359,7 +357,7 @@ def read_sql_query( params=None, parse_dates=None, chunksize: int = 1, - dtype: Optional[DtypeArg] = None, + dtype: DtypeArg | None = None, ) -> Iterator[DataFrame]: ... @@ -371,9 +369,9 @@ def read_sql_query( coerce_float: bool = True, params=None, parse_dates=None, - chunksize: Optional[int] = None, - dtype: Optional[DtypeArg] = None, -) -> Union[DataFrame, Iterator[DataFrame]]: + chunksize: int | None = None, + dtype: DtypeArg | None = None, +) -> DataFrame | Iterator[DataFrame]: """ Read SQL query into a DataFrame. @@ -474,13 +472,13 @@ def read_sql( def read_sql( sql, con, - index_col: Optional[Union[str, Sequence[str]]] = None, + index_col: str | Sequence[str] | None = None, coerce_float: bool = True, params=None, parse_dates=None, columns=None, - chunksize: Optional[int] = None, -) -> Union[DataFrame, Iterator[DataFrame]]: + chunksize: int | None = None, +) -> DataFrame | Iterator[DataFrame]: """ Read SQL query or database table into a DataFrame. @@ -638,13 +636,13 @@ def to_sql( frame, name: str, con, - schema: Optional[str] = None, + schema: str | None = None, if_exists: str = "fail", index: bool = True, index_label=None, - chunksize: Optional[int] = None, - dtype: Optional[DtypeArg] = None, - method: Optional[str] = None, + chunksize: int | None = None, + dtype: DtypeArg | None = None, + method: str | None = None, ) -> None: """ Write records stored in a DataFrame to a SQL database. @@ -717,7 +715,7 @@ def to_sql( ) -def has_table(table_name: str, con, schema: Optional[str] = None): +def has_table(table_name: str, con, schema: str | None = None): """ Check if DataBase has named table. @@ -763,7 +761,7 @@ def _engine_builder(con): def pandasSQL_builder( - con, schema: Optional[str] = None, meta=None, is_cursor: bool = False + con, schema: str | None = None, meta=None, is_cursor: bool = False ): """ Convenience function to return the correct PandasSQL subclass based on the @@ -802,7 +800,7 @@ def __init__( index_label=None, schema=None, keys=None, - dtype: Optional[DtypeArg] = None, + dtype: DtypeArg | None = None, ): self.name = name self.pd_sql = pandas_sql_engine @@ -854,7 +852,7 @@ def create(self): else: self._execute_create() - def _execute_insert(self, conn, keys: List[str], data_iter): + def _execute_insert(self, conn, keys: list[str], data_iter): """ Execute SQL statement inserting data @@ -869,7 +867,7 @@ def _execute_insert(self, conn, keys: List[str], data_iter): data = [dict(zip(keys, row)) for row in data_iter] conn.execute(self.table.insert(), data) - def _execute_insert_multi(self, conn, keys: List[str], data_iter): + def _execute_insert_multi(self, conn, keys: list[str], data_iter): """ Alternative to _execute_insert for DBs support multivalue INSERT. @@ -918,7 +916,7 @@ def insert_data(self): return column_names, data_list - def insert(self, chunksize: Optional[int] = None, method: Optional[str] = None): + def insert(self, chunksize: int | None = None, method: str | None = None): # set insert method if method is None: @@ -957,7 +955,7 @@ def insert(self, chunksize: Optional[int] = None, method: Optional[str] = None): def _query_iterator( self, result, - chunksize: Optional[str], + chunksize: str | None, columns, coerce_float: bool = True, parse_dates=None, @@ -1276,7 +1274,7 @@ def to_sql( index_label=None, schema=None, chunksize=None, - dtype: Optional[DtypeArg] = None, + dtype: DtypeArg | None = None, method=None, ): raise ValueError( @@ -1305,7 +1303,7 @@ class SQLDatabase(PandasSQL): """ - def __init__(self, engine, schema: Optional[str] = None, meta=None): + def __init__(self, engine, schema: str | None = None, meta=None): self.connectable = engine if not meta: from sqlalchemy.schema import MetaData @@ -1329,12 +1327,12 @@ def execute(self, *args, **kwargs): def read_table( self, table_name: str, - index_col: Optional[Union[str, Sequence[str]]] = None, + index_col: str | Sequence[str] | None = None, coerce_float: bool = True, parse_dates=None, columns=None, - schema: Optional[str] = None, - chunksize: Optional[int] = None, + schema: str | None = None, + chunksize: int | None = None, ): """ Read SQL database table into a DataFrame. @@ -1394,7 +1392,7 @@ def _query_iterator( index_col=None, coerce_float=True, parse_dates=None, - dtype: Optional[DtypeArg] = None, + dtype: DtypeArg | None = None, ): """Return generator through chunked result set""" has_read_data = False @@ -1424,12 +1422,12 @@ def _query_iterator( def read_query( self, sql: str, - index_col: Optional[str] = None, + index_col: str | None = None, coerce_float: bool = True, parse_dates=None, params=None, - chunksize: Optional[int] = None, - dtype: Optional[DtypeArg] = None, + chunksize: int | None = None, + dtype: DtypeArg | None = None, ): """ Read SQL query into a DataFrame. @@ -1515,7 +1513,7 @@ def to_sql( index_label=None, schema=None, chunksize=None, - dtype: Optional[DtypeArg] = None, + dtype: DtypeArg | None = None, method=None, ): """ @@ -1629,7 +1627,7 @@ def to_sql( def tables(self): return self.meta.tables - def has_table(self, name: str, schema: Optional[str] = None): + def has_table(self, name: str, schema: str | None = None): if _gt14(): import sqlalchemy as sa @@ -1640,7 +1638,7 @@ def has_table(self, name: str, schema: Optional[str] = None): self.connectable.dialect.has_table, name, schema or self.meta.schema ) - def get_table(self, table_name: str, schema: Optional[str] = None): + def get_table(self, table_name: str, schema: str | None = None): schema = schema or self.meta.schema if schema: tbl = self.meta.tables.get(".".join([schema, table_name])) @@ -1656,7 +1654,7 @@ def get_table(self, table_name: str, schema: Optional[str] = None): return tbl - def drop_table(self, table_name: str, schema: Optional[str] = None): + def drop_table(self, table_name: str, schema: str | None = None): schema = schema or self.meta.schema if self.has_table(table_name, schema): self.meta.reflect(only=[table_name], schema=schema) @@ -1667,9 +1665,9 @@ def _create_sql_schema( self, frame: DataFrame, table_name: str, - keys: Optional[List[str]] = None, - dtype: Optional[DtypeArg] = None, - schema: Optional[str] = None, + keys: list[str] | None = None, + dtype: DtypeArg | None = None, + schema: str | None = None, ): table = SQLTable( table_name, @@ -1928,7 +1926,7 @@ def _query_iterator( index_col=None, coerce_float: bool = True, parse_dates=None, - dtype: Optional[DtypeArg] = None, + dtype: DtypeArg | None = None, ): """Return generator through chunked result set""" has_read_data = False @@ -1961,8 +1959,8 @@ def read_query( coerce_float: bool = True, params=None, parse_dates=None, - chunksize: Optional[int] = None, - dtype: Optional[DtypeArg] = None, + chunksize: int | None = None, + dtype: DtypeArg | None = None, ): args = _convert_params(sql, params) @@ -2008,7 +2006,7 @@ def to_sql( index_label=None, schema=None, chunksize=None, - dtype: Optional[DtypeArg] = None, + dtype: DtypeArg | None = None, method=None, ): """ @@ -2079,7 +2077,7 @@ def to_sql( table.create() table.insert(chunksize, method) - def has_table(self, name: str, schema: Optional[str] = None): + def has_table(self, name: str, schema: str | None = None): # TODO(wesm): unused? # escape = _get_valid_sqlite_name # esc_name = escape(name) @@ -2089,10 +2087,10 @@ def has_table(self, name: str, schema: Optional[str] = None): return len(self.execute(query, [name]).fetchall()) > 0 - def get_table(self, table_name: str, schema: Optional[str] = None): + def get_table(self, table_name: str, schema: str | None = None): return None # not supported in fallback mode - def drop_table(self, name: str, schema: Optional[str] = None): + def drop_table(self, name: str, schema: str | None = None): drop_sql = f"DROP TABLE {_get_valid_sqlite_name(name)}" self.execute(drop_sql) @@ -2101,8 +2099,8 @@ def _create_sql_schema( frame, table_name: str, keys=None, - dtype: Optional[DtypeArg] = None, - schema: Optional[str] = None, + dtype: DtypeArg | None = None, + schema: str | None = None, ): table = SQLiteTable( table_name, @@ -2121,8 +2119,8 @@ def get_schema( name: str, keys=None, con=None, - dtype: Optional[DtypeArg] = None, - schema: Optional[str] = None, + dtype: DtypeArg | None = None, + schema: str | None = None, ): """ Get the SQL db table schema for the given frame. diff --git a/pandas/io/xml.py b/pandas/io/xml.py index abbfe7657624f..8b0055a522e25 100644 --- a/pandas/io/xml.py +++ b/pandas/io/xml.py @@ -2,13 +2,9 @@ :mod:`pandas.io.xml` is a module for reading XML. """ +from __future__ import annotations + import io -from typing import ( - Dict, - List, - Optional, - Union, -) from pandas._typing import ( Buffer, @@ -124,7 +120,7 @@ def __init__( self.compression = compression self.storage_options = storage_options - def parse_data(self) -> List[Dict[str, Optional[str]]]: + def parse_data(self) -> list[dict[str, str | None]]: """ Parse xml data. @@ -134,7 +130,7 @@ def parse_data(self) -> List[Dict[str, Optional[str]]]: raise AbstractMethodError(self) - def _parse_nodes(self) -> List[Dict[str, Optional[str]]]: + def _parse_nodes(self) -> list[dict[str, str | None]]: """ Parse xml nodes. @@ -206,7 +202,7 @@ class _EtreeFrameParser(_XMLFrameParser): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) - def parse_data(self) -> List[Dict[str, Optional[str]]]: + def parse_data(self) -> list[dict[str, str | None]]: from xml.etree.ElementTree import XML if self.stylesheet is not None: @@ -221,9 +217,9 @@ def parse_data(self) -> List[Dict[str, Optional[str]]]: return self._parse_nodes() - def _parse_nodes(self) -> List[Dict[str, Optional[str]]]: + def _parse_nodes(self) -> list[dict[str, str | None]]: elems = self.xml_doc.findall(self.xpath, namespaces=self.namespaces) - dicts: List[Dict[str, Optional[str]]] + dicts: list[dict[str, str | None]] if self.elems_only and self.attrs_only: raise ValueError("Either element or attributes can be parsed not both.") @@ -382,7 +378,7 @@ class _LxmlFrameParser(_XMLFrameParser): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) - def parse_data(self) -> List[Dict[str, Optional[str]]]: + def parse_data(self) -> list[dict[str, str | None]]: """ Parse xml data. @@ -403,9 +399,9 @@ def parse_data(self) -> List[Dict[str, Optional[str]]]: return self._parse_nodes() - def _parse_nodes(self) -> List[Dict[str, Optional[str]]]: + def _parse_nodes(self) -> list[dict[str, str | None]]: elems = self.xml_doc.xpath(self.xpath, namespaces=self.namespaces) - dicts: List[Dict[str, Optional[str]]] + dicts: list[dict[str, str | None]] if self.elems_only and self.attrs_only: raise ValueError("Either element or attributes can be parsed not both.") @@ -583,7 +579,7 @@ def get_data_from_filepath( encoding, compression, storage_options, -) -> Union[str, bytes, Buffer]: +) -> str | bytes | Buffer: """ Extract raw XML data. @@ -622,7 +618,7 @@ def get_data_from_filepath( return filepath_or_buffer -def preprocess_data(data) -> Union[io.StringIO, io.BytesIO]: +def preprocess_data(data) -> io.StringIO | io.BytesIO: """ Convert extracted raw data. @@ -694,7 +690,7 @@ def _parse( lxml = import_optional_dependency("lxml.etree", errors="ignore") - p: Union[_EtreeFrameParser, _LxmlFrameParser] + p: _EtreeFrameParser | _LxmlFrameParser if parser == "lxml": if lxml is not None: @@ -737,14 +733,14 @@ def _parse( @doc(storage_options=_shared_docs["storage_options"]) def read_xml( path_or_buffer: FilePathOrBuffer, - xpath: Optional[str] = "./*", - namespaces: Optional[Union[dict, List[dict]]] = None, - elems_only: Optional[bool] = False, - attrs_only: Optional[bool] = False, - names: Optional[List[str]] = None, - encoding: Optional[str] = "utf-8", - parser: Optional[str] = "lxml", - stylesheet: Optional[FilePathOrBuffer] = None, + xpath: str | None = "./*", + namespaces: dict | list[dict] | None = None, + elems_only: bool | None = False, + attrs_only: bool | None = False, + names: list[str] | None = None, + encoding: str | None = "utf-8", + parser: str | None = "lxml", + stylesheet: FilePathOrBuffer | None = None, compression: CompressionOptions = "infer", storage_options: StorageOptions = None, ) -> DataFrame: diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 05b8885b7d9b7..c55ac6ce228bf 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -1,4 +1,5 @@ -from typing import Optional +from __future__ import annotations + import warnings import numpy as np @@ -93,7 +94,7 @@ _offset_to_period_map[f"W-{_d}"] = f"W-{_d}" -def get_period_alias(offset_str: str) -> Optional[str]: +def get_period_alias(offset_str: str) -> str | None: """ Alias to closest period strings BQ->Q etc. """ @@ -123,7 +124,7 @@ def get_offset(name: str) -> DateOffset: # Period codes -def infer_freq(index, warn: bool = True) -> Optional[str]: +def infer_freq(index, warn: bool = True) -> str | None: """ Infer the most likely frequency given the input index. If the frequency is uncertain, a warning will be printed. @@ -239,7 +240,7 @@ def is_unique(self) -> bool: def is_unique_asi8(self) -> bool: return len(self.deltas_asi8) == 1 - def get_freq(self) -> Optional[str]: + def get_freq(self) -> str | None: """ Find the appropriate frequency string to describe the inferred frequency of self.i8values @@ -313,7 +314,7 @@ def mdiffs(self): def ydiffs(self): return unique_deltas(self.fields["Y"].astype("i8")) - def _infer_daily_rule(self) -> Optional[str]: + def _infer_daily_rule(self) -> str | None: annual_rule = self._get_annual_rule() if annual_rule: nyears = self.ydiffs[0] @@ -345,7 +346,7 @@ def _infer_daily_rule(self) -> Optional[str]: return None - def _get_daily_rule(self) -> Optional[str]: + def _get_daily_rule(self) -> str | None: days = self.deltas[0] / _ONE_DAY if days % 7 == 0: # Weekly @@ -355,7 +356,7 @@ def _get_daily_rule(self) -> Optional[str]: else: return _maybe_add_count("D", days) - def _get_annual_rule(self) -> Optional[str]: + def _get_annual_rule(self) -> str | None: if len(self.ydiffs) > 1: return None @@ -365,7 +366,7 @@ def _get_annual_rule(self) -> Optional[str]: pos_check = self.month_position_check() return {"cs": "AS", "bs": "BAS", "ce": "A", "be": "BA"}.get(pos_check) - def _get_quarterly_rule(self) -> Optional[str]: + def _get_quarterly_rule(self) -> str | None: if len(self.mdiffs) > 1: return None @@ -375,7 +376,7 @@ def _get_quarterly_rule(self) -> Optional[str]: pos_check = self.month_position_check() return {"cs": "QS", "bs": "BQS", "ce": "Q", "be": "BQ"}.get(pos_check) - def _get_monthly_rule(self) -> Optional[str]: + def _get_monthly_rule(self) -> str | None: if len(self.mdiffs) > 1: return None pos_check = self.month_position_check() @@ -397,7 +398,7 @@ def _is_business_daily(self) -> bool: | ((weekdays > 0) & (weekdays <= 4) & (shifts == 1)) ) - def _get_wom_rule(self) -> Optional[str]: + def _get_wom_rule(self) -> str | None: # FIXME: dont leave commented-out # wdiffs = unique(np.diff(self.index.week)) # We also need -47, -49, -48 to catch index spanning year boundary diff --git a/pandas/tseries/holiday.py b/pandas/tseries/holiday.py index ce303928dc8ee..54ac116afe3cf 100644 --- a/pandas/tseries/holiday.py +++ b/pandas/tseries/holiday.py @@ -1,8 +1,9 @@ +from __future__ import annotations + from datetime import ( datetime, timedelta, ) -from typing import List import warnings from dateutil.relativedelta import ( # noqa @@ -384,7 +385,7 @@ class AbstractHolidayCalendar(metaclass=HolidayCalendarMetaClass): Abstract interface to create holidays following certain rules. """ - rules: List[Holiday] = [] + rules: list[Holiday] = [] start_date = Timestamp(datetime(1970, 1, 1)) end_date = Timestamp(datetime(2200, 12, 31)) _cache = None diff --git a/pandas/util/_decorators.py b/pandas/util/_decorators.py index ffc0255ca9de7..835001b3e1829 100644 --- a/pandas/util/_decorators.py +++ b/pandas/util/_decorators.py @@ -1,15 +1,12 @@ +from __future__ import annotations + from functools import wraps import inspect from textwrap import dedent from typing import ( Any, Callable, - List, Mapping, - Optional, - Tuple, - Type, - Union, cast, ) import warnings @@ -22,10 +19,10 @@ def deprecate( name: str, alternative: Callable[..., Any], version: str, - alt_name: Optional[str] = None, - klass: Optional[Type[Warning]] = None, + alt_name: str | None = None, + klass: type[Warning] | None = None, stacklevel: int = 2, - msg: Optional[str] = None, + msg: str | None = None, ) -> Callable[[F], F]: """ Return a new function that emits a deprecation warning on use. @@ -95,8 +92,8 @@ def wrapper(*args, **kwargs) -> Callable[..., Any]: def deprecate_kwarg( old_arg_name: str, - new_arg_name: Optional[str], - mapping: Optional[Union[Mapping[Any, Any], Callable[[Any], Any]]] = None, + new_arg_name: str | None, + mapping: Mapping[Any, Any] | Callable[[Any], Any] | None = None, stacklevel: int = 2, ) -> Callable[[F], F]: """ @@ -214,7 +211,7 @@ def wrapper(*args, **kwargs) -> Callable[..., Any]: return _deprecate_kwarg -def _format_argument_list(allow_args: Union[List[str], int]): +def _format_argument_list(allow_args: list[str] | int): """ Convert the allow_args argument (either string or integer) of `deprecate_nonkeyword_arguments` function to a string describing @@ -259,7 +256,7 @@ def _format_argument_list(allow_args: Union[List[str], int]): def deprecate_nonkeyword_arguments( version: str, - allowed_args: Optional[Union[List[str], int]] = None, + allowed_args: list[str] | int | None = None, stacklevel: int = 2, ) -> Callable: """ @@ -315,7 +312,7 @@ def wrapper(*args, **kwargs): def rewrite_axis_style_signature( - name: str, extra_params: List[Tuple[str, Any]] + name: str, extra_params: list[tuple[str, Any]] ) -> Callable[..., Any]: def decorate(func: F) -> F: @wraps(func) @@ -344,7 +341,7 @@ def wrapper(*args, **kwargs) -> Callable[..., Any]: return decorate -def doc(*docstrings: Union[str, Callable], **params) -> Callable[[F], F]: +def doc(*docstrings: str | Callable, **params) -> Callable[[F], F]: """ A decorator take docstring templates, concatenate them and perform string substitution on it. @@ -366,7 +363,7 @@ def doc(*docstrings: Union[str, Callable], **params) -> Callable[[F], F]: def decorator(decorated: F) -> F: # collecting docstring and docstring templates - docstring_components: List[Union[str, Callable]] = [] + docstring_components: list[str | Callable] = [] if decorated.__doc__: docstring_components.append(dedent(decorated.__doc__)) @@ -472,9 +469,9 @@ def my_dog(has='fleas'): pass """ - addendum: Optional[str] + addendum: str | None - def __init__(self, addendum: Optional[str], join: str = "", indents: int = 0): + def __init__(self, addendum: str | None, join: str = "", indents: int = 0): if indents > 0: self.addendum = indent(addendum, indents=indents) else: @@ -489,7 +486,7 @@ def __call__(self, func: F) -> F: return func -def indent(text: Optional[str], indents: int = 1) -> str: +def indent(text: str | None, indents: int = 1) -> str: if not text or not isinstance(text, str): return "" jointext = "".join(["\n"] + [" "] * indents) diff --git a/pandas/util/_doctools.py b/pandas/util/_doctools.py index d6689fcb8cd01..0d90d9b2871d9 100644 --- a/pandas/util/_doctools.py +++ b/pandas/util/_doctools.py @@ -1,7 +1,4 @@ -from typing import ( - Optional, - Tuple, -) +from __future__ import annotations import numpy as np @@ -24,14 +21,14 @@ def __init__( self.cell_height = cell_height self.font_size = font_size - def _shape(self, df: pd.DataFrame) -> Tuple[int, int]: + def _shape(self, df: pd.DataFrame) -> tuple[int, int]: """ Calculate table shape considering index levels. """ row, col = df.shape return row + df.columns.nlevels, col + df.index.nlevels - def _get_cells(self, left, right, vertical) -> Tuple[int, int]: + def _get_cells(self, left, right, vertical) -> tuple[int, int]: """ Calculate appropriate figure size based on left and right data. """ @@ -137,7 +134,7 @@ def _insert_index(self, data): data.columns = col return data - def _make_table(self, ax, df, title: str, height: Optional[float] = None): + def _make_table(self, ax, df, title: str, height: float | None = None): if df is None: ax.set_visible(False) return diff --git a/pandas/util/_exceptions.py b/pandas/util/_exceptions.py index e70c185628f71..9a6c62df76ef2 100644 --- a/pandas/util/_exceptions.py +++ b/pandas/util/_exceptions.py @@ -1,6 +1,7 @@ +from __future__ import annotations + import contextlib import inspect -from typing import Tuple @contextlib.contextmanager @@ -15,7 +16,7 @@ def rewrite_exception(old_name: str, new_name: str): raise msg = str(err.args[0]) msg = msg.replace(old_name, new_name) - args: Tuple[str, ...] = (msg,) + args: tuple[str, ...] = (msg,) if len(err.args) > 1: args = args + err.args[1:] err.args = args diff --git a/pandas/util/_print_versions.py b/pandas/util/_print_versions.py index cf5af0de5d3b5..6c180f68395db 100644 --- a/pandas/util/_print_versions.py +++ b/pandas/util/_print_versions.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import codecs import json import locale @@ -5,11 +7,6 @@ import platform import struct import sys -from typing import ( - Dict, - Optional, - Union, -) from pandas._typing import JSONSerializable from pandas.compat._optional import ( @@ -19,7 +16,7 @@ ) -def _get_commit_hash() -> Optional[str]: +def _get_commit_hash() -> str | None: """ Use vendored versioneer code to get git hash, which handles git worktree correctly. @@ -30,7 +27,7 @@ def _get_commit_hash() -> Optional[str]: return versions["full-revisionid"] -def _get_sys_info() -> Dict[str, JSONSerializable]: +def _get_sys_info() -> dict[str, JSONSerializable]: """ Returns system information as a JSON serializable dictionary. """ @@ -52,7 +49,7 @@ def _get_sys_info() -> Dict[str, JSONSerializable]: } -def _get_dependency_info() -> Dict[str, JSONSerializable]: +def _get_dependency_info() -> dict[str, JSONSerializable]: """ Returns dependency information as a JSON serializable dictionary. """ @@ -86,14 +83,14 @@ def _get_dependency_info() -> Dict[str, JSONSerializable]: ] deps.extend(list(VERSIONS)) - result: Dict[str, JSONSerializable] = {} + result: dict[str, JSONSerializable] = {} for modname in deps: mod = import_optional_dependency(modname, errors="ignore") result[modname] = get_version(mod) if mod else None return result -def show_versions(as_json: Union[str, bool] = False) -> None: +def show_versions(as_json: str | bool = False) -> None: """ Provide useful information, important for bug reports. diff --git a/pandas/util/_test_decorators.py b/pandas/util/_test_decorators.py index 752ed43849d2b..dd22b5ef5e4ac 100644 --- a/pandas/util/_test_decorators.py +++ b/pandas/util/_test_decorators.py @@ -23,13 +23,12 @@ def test_foo(): For more information, refer to the ``pytest`` documentation on ``skipif``. """ +from __future__ import annotations + from contextlib import contextmanager from distutils.version import LooseVersion import locale -from typing import ( - Callable, - Optional, -) +from typing import Callable import warnings import numpy as np @@ -49,7 +48,7 @@ def test_foo(): ) -def safe_import(mod_name: str, min_version: Optional[str] = None): +def safe_import(mod_name: str, min_version: str | None = None): """ Parameters ---------- @@ -144,7 +143,7 @@ def skip_if_installed(package: str): # TODO: return type, _pytest.mark.structures.MarkDecorator is not public # https://github.com/pytest-dev/pytest/issues/7469 -def skip_if_no(package: str, min_version: Optional[str] = None): +def skip_if_no(package: str, min_version: str | None = None): """ Generic function to help skip tests when required packages are not present on the testing system. @@ -208,7 +207,7 @@ def skip_if_no(package: str, min_version: Optional[str] = None): # TODO: return type, _pytest.mark.structures.MarkDecorator is not public # https://github.com/pytest-dev/pytest/issues/7469 -def skip_if_np_lt(ver_str: str, *args, reason: Optional[str] = None): +def skip_if_np_lt(ver_str: str, *args, reason: str | None = None): if reason is None: reason = f"NumPy {ver_str} or greater required" return pytest.mark.skipif( diff --git a/pandas/util/_validators.py b/pandas/util/_validators.py index 087dccfadcce1..d5d5439ecb8eb 100644 --- a/pandas/util/_validators.py +++ b/pandas/util/_validators.py @@ -2,10 +2,11 @@ Module that contains many useful utilities for validating data or function arguments """ +from __future__ import annotations + from typing import ( Iterable, Sequence, - Union, ) import warnings @@ -383,7 +384,7 @@ def validate_fillna_kwargs(value, method, validate_scalar_dict_value=True): return value, method -def validate_percentile(q: Union[float, Iterable[float]]) -> np.ndarray: +def validate_percentile(q: float | Iterable[float]) -> np.ndarray: """ Validate percentiles (used by describe and quantile). @@ -418,7 +419,7 @@ def validate_percentile(q: Union[float, Iterable[float]]) -> np.ndarray: def validate_ascending( - ascending: Union[Union[bool, int], Sequence[Union[bool, int]]] = True, + ascending: bool | int | Sequence[bool | int] = True, ): """Validate ``ascending`` kwargs for ``sort_index`` method.""" kwargs = {"none_allowed": False, "int_allowed": True}