done
This commit is contained in:
1435
lib/python3.11/site-packages/narwhals/stable/v1/__init__.py
Normal file
1435
lib/python3.11/site-packages/narwhals/stable/v1/__init__.py
Normal file
File diff suppressed because it is too large
Load Diff
135
lib/python3.11/site-packages/narwhals/stable/v1/_dtypes.py
Normal file
135
lib/python3.11/site-packages/narwhals/stable/v1/_dtypes.py
Normal file
@ -0,0 +1,135 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from narwhals._utils import inherit_doc
|
||||
from narwhals.dtypes import (
|
||||
Array,
|
||||
Binary,
|
||||
Boolean,
|
||||
Categorical,
|
||||
Date,
|
||||
Datetime as NwDatetime,
|
||||
Decimal,
|
||||
DType,
|
||||
Duration as NwDuration,
|
||||
Enum as NwEnum,
|
||||
Field,
|
||||
Float32,
|
||||
Float64,
|
||||
FloatType,
|
||||
Int8,
|
||||
Int16,
|
||||
Int32,
|
||||
Int64,
|
||||
Int128,
|
||||
IntegerType,
|
||||
List,
|
||||
NestedType,
|
||||
NumericType,
|
||||
Object,
|
||||
SignedIntegerType,
|
||||
String,
|
||||
Struct,
|
||||
Time,
|
||||
UInt8,
|
||||
UInt16,
|
||||
UInt32,
|
||||
UInt64,
|
||||
UInt128,
|
||||
Unknown,
|
||||
UnsignedIntegerType,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from datetime import timezone
|
||||
|
||||
from narwhals.typing import TimeUnit
|
||||
|
||||
|
||||
class Datetime(NwDatetime):
|
||||
@inherit_doc(NwDatetime)
|
||||
def __init__(
|
||||
self, time_unit: TimeUnit = "us", time_zone: str | timezone | None = None
|
||||
) -> None:
|
||||
super().__init__(time_unit, time_zone)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__class__)
|
||||
|
||||
|
||||
class Duration(NwDuration):
|
||||
@inherit_doc(NwDuration)
|
||||
def __init__(self, time_unit: TimeUnit = "us") -> None:
|
||||
super().__init__(time_unit)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.__class__)
|
||||
|
||||
|
||||
class Enum(NwEnum):
|
||||
"""A fixed categorical encoding of a unique set of strings.
|
||||
|
||||
Polars has an Enum data type, while pandas and PyArrow do not.
|
||||
|
||||
Examples:
|
||||
>>> import polars as pl
|
||||
>>> import narwhals.stable.v1 as nw
|
||||
>>> data = ["beluga", "narwhal", "orca"]
|
||||
>>> s_native = pl.Series(data, dtype=pl.Enum(data))
|
||||
>>> nw.from_native(s_native, series_only=True).dtype
|
||||
Enum
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super(NwEnum, self).__init__()
|
||||
|
||||
def __eq__(self, other: DType | type[DType]) -> bool: # type: ignore[override]
|
||||
if type(other) is type:
|
||||
return other in {type(self), NwEnum}
|
||||
return isinstance(other, type(self))
|
||||
|
||||
def __hash__(self) -> int: # pragma: no cover
|
||||
return super(NwEnum, self).__hash__()
|
||||
|
||||
def __repr__(self) -> str: # pragma: no cover
|
||||
return super(NwEnum, self).__repr__()
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Array",
|
||||
"Binary",
|
||||
"Boolean",
|
||||
"Categorical",
|
||||
"DType",
|
||||
"Date",
|
||||
"Datetime",
|
||||
"Decimal",
|
||||
"Duration",
|
||||
"Enum",
|
||||
"Field",
|
||||
"Float32",
|
||||
"Float64",
|
||||
"FloatType",
|
||||
"Int8",
|
||||
"Int16",
|
||||
"Int32",
|
||||
"Int64",
|
||||
"Int128",
|
||||
"IntegerType",
|
||||
"List",
|
||||
"NestedType",
|
||||
"NumericType",
|
||||
"Object",
|
||||
"SignedIntegerType",
|
||||
"String",
|
||||
"Struct",
|
||||
"Time",
|
||||
"UInt8",
|
||||
"UInt16",
|
||||
"UInt32",
|
||||
"UInt64",
|
||||
"UInt128",
|
||||
"Unknown",
|
||||
"UnsignedIntegerType",
|
||||
]
|
@ -0,0 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from narwhals._compliant.typing import CompliantNamespaceT_co
|
||||
from narwhals._namespace import Namespace as NwNamespace
|
||||
from narwhals._utils import Version
|
||||
|
||||
__all__ = ["Namespace"]
|
||||
|
||||
|
||||
class Namespace(NwNamespace[CompliantNamespaceT_co], version=Version.V1): ...
|
156
lib/python3.11/site-packages/narwhals/stable/v1/dependencies.py
Normal file
156
lib/python3.11/site-packages/narwhals/stable/v1/dependencies.py
Normal file
@ -0,0 +1,156 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import cudf
|
||||
import dask.dataframe as dd
|
||||
import ibis
|
||||
import modin.pandas as mpd
|
||||
import pandas as pd
|
||||
import polars as pl
|
||||
import pyarrow as pa
|
||||
from typing_extensions import TypeIs
|
||||
|
||||
|
||||
from narwhals.dependencies import (
|
||||
IMPORT_HOOKS,
|
||||
get_cudf,
|
||||
get_dask_dataframe,
|
||||
get_ibis,
|
||||
get_modin,
|
||||
get_numpy,
|
||||
get_pandas,
|
||||
get_polars,
|
||||
get_pyarrow,
|
||||
is_into_dataframe,
|
||||
is_into_series,
|
||||
is_narwhals_dataframe,
|
||||
is_narwhals_lazyframe,
|
||||
is_narwhals_series,
|
||||
is_numpy_array,
|
||||
is_pandas_index,
|
||||
)
|
||||
|
||||
|
||||
def is_pandas_dataframe(df: Any) -> TypeIs[pd.DataFrame]:
|
||||
"""Check whether `df` is a pandas DataFrame without importing pandas."""
|
||||
return ((pd := get_pandas()) is not None and isinstance(df, pd.DataFrame)) or any(
|
||||
(mod := sys.modules.get(module_name, None)) is not None
|
||||
and isinstance(df, mod.pandas.DataFrame)
|
||||
for module_name in IMPORT_HOOKS
|
||||
)
|
||||
|
||||
|
||||
def is_pandas_series(ser: Any) -> TypeIs[pd.Series[Any]]:
|
||||
"""Check whether `ser` is a pandas Series without importing pandas."""
|
||||
return ((pd := get_pandas()) is not None and isinstance(ser, pd.Series)) or any(
|
||||
(mod := sys.modules.get(module_name, None)) is not None
|
||||
and isinstance(ser, mod.pandas.Series)
|
||||
for module_name in IMPORT_HOOKS
|
||||
)
|
||||
|
||||
|
||||
def is_modin_dataframe(df: Any) -> TypeIs[mpd.DataFrame]:
|
||||
"""Check whether `df` is a modin DataFrame without importing modin."""
|
||||
return (mpd := get_modin()) is not None and isinstance(df, mpd.DataFrame)
|
||||
|
||||
|
||||
def is_modin_series(ser: Any) -> TypeIs[mpd.Series]:
|
||||
"""Check whether `ser` is a modin Series without importing modin."""
|
||||
return (mpd := get_modin()) is not None and isinstance(ser, mpd.Series)
|
||||
|
||||
|
||||
def is_cudf_dataframe(df: Any) -> TypeIs[cudf.DataFrame]:
|
||||
"""Check whether `df` is a cudf DataFrame without importing cudf."""
|
||||
return (cudf := get_cudf()) is not None and isinstance(df, cudf.DataFrame)
|
||||
|
||||
|
||||
def is_cudf_series(ser: Any) -> TypeIs[cudf.Series[Any]]:
|
||||
"""Check whether `ser` is a cudf Series without importing cudf."""
|
||||
return (cudf := get_cudf()) is not None and isinstance(ser, cudf.Series)
|
||||
|
||||
|
||||
def is_dask_dataframe(df: Any) -> TypeIs[dd.DataFrame]:
|
||||
"""Check whether `df` is a Dask DataFrame without importing Dask."""
|
||||
return (dd := get_dask_dataframe()) is not None and isinstance(df, dd.DataFrame)
|
||||
|
||||
|
||||
def is_ibis_table(df: Any) -> TypeIs[ibis.Table]:
|
||||
"""Check whether `df` is a Ibis Table without importing Ibis."""
|
||||
return (ibis := get_ibis()) is not None and isinstance(df, ibis.expr.types.Table)
|
||||
|
||||
|
||||
def is_polars_dataframe(df: Any) -> TypeIs[pl.DataFrame]:
|
||||
"""Check whether `df` is a Polars DataFrame without importing Polars."""
|
||||
return (pl := get_polars()) is not None and isinstance(df, pl.DataFrame)
|
||||
|
||||
|
||||
def is_polars_lazyframe(df: Any) -> TypeIs[pl.LazyFrame]:
|
||||
"""Check whether `df` is a Polars LazyFrame without importing Polars."""
|
||||
return (pl := get_polars()) is not None and isinstance(df, pl.LazyFrame)
|
||||
|
||||
|
||||
def is_polars_series(ser: Any) -> TypeIs[pl.Series]:
|
||||
"""Check whether `ser` is a Polars Series without importing Polars."""
|
||||
return (pl := get_polars()) is not None and isinstance(ser, pl.Series)
|
||||
|
||||
|
||||
def is_pyarrow_chunked_array(ser: Any) -> TypeIs[pa.ChunkedArray[Any]]:
|
||||
"""Check whether `ser` is a PyArrow ChunkedArray without importing PyArrow."""
|
||||
return (pa := get_pyarrow()) is not None and isinstance(ser, pa.ChunkedArray)
|
||||
|
||||
|
||||
def is_pyarrow_table(df: Any) -> TypeIs[pa.Table]:
|
||||
"""Check whether `df` is a PyArrow Table without importing PyArrow."""
|
||||
return (pa := get_pyarrow()) is not None and isinstance(df, pa.Table)
|
||||
|
||||
|
||||
def is_pandas_like_dataframe(df: Any) -> bool:
|
||||
"""Check whether `df` is a pandas-like DataFrame without doing any imports.
|
||||
|
||||
By "pandas-like", we mean: pandas, Modin, cuDF.
|
||||
"""
|
||||
return is_pandas_dataframe(df) or is_modin_dataframe(df) or is_cudf_dataframe(df)
|
||||
|
||||
|
||||
def is_pandas_like_series(ser: Any) -> bool:
|
||||
"""Check whether `ser` is a pandas-like Series without doing any imports.
|
||||
|
||||
By "pandas-like", we mean: pandas, Modin, cuDF.
|
||||
"""
|
||||
return is_pandas_series(ser) or is_modin_series(ser) or is_cudf_series(ser)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"get_cudf",
|
||||
"get_ibis",
|
||||
"get_modin",
|
||||
"get_numpy",
|
||||
"get_pandas",
|
||||
"get_polars",
|
||||
"get_pyarrow",
|
||||
"is_cudf_dataframe",
|
||||
"is_cudf_series",
|
||||
"is_dask_dataframe",
|
||||
"is_ibis_table",
|
||||
"is_into_dataframe",
|
||||
"is_into_series",
|
||||
"is_modin_dataframe",
|
||||
"is_modin_series",
|
||||
"is_narwhals_dataframe",
|
||||
"is_narwhals_lazyframe",
|
||||
"is_narwhals_series",
|
||||
"is_numpy_array",
|
||||
"is_pandas_dataframe",
|
||||
"is_pandas_index",
|
||||
"is_pandas_like_dataframe",
|
||||
"is_pandas_like_series",
|
||||
"is_pandas_series",
|
||||
"is_polars_dataframe",
|
||||
"is_polars_lazyframe",
|
||||
"is_polars_series",
|
||||
"is_pyarrow_chunked_array",
|
||||
"is_pyarrow_table",
|
||||
]
|
77
lib/python3.11/site-packages/narwhals/stable/v1/dtypes.py
Normal file
77
lib/python3.11/site-packages/narwhals/stable/v1/dtypes.py
Normal file
@ -0,0 +1,77 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from narwhals.stable.v1._dtypes import (
|
||||
Array,
|
||||
Binary,
|
||||
Boolean,
|
||||
Categorical,
|
||||
Date,
|
||||
Datetime,
|
||||
Decimal,
|
||||
DType,
|
||||
Duration,
|
||||
Enum,
|
||||
Field,
|
||||
Float32,
|
||||
Float64,
|
||||
FloatType,
|
||||
Int8,
|
||||
Int16,
|
||||
Int32,
|
||||
Int64,
|
||||
Int128,
|
||||
IntegerType,
|
||||
List,
|
||||
NestedType,
|
||||
NumericType,
|
||||
Object,
|
||||
SignedIntegerType,
|
||||
String,
|
||||
Struct,
|
||||
Time,
|
||||
UInt8,
|
||||
UInt16,
|
||||
UInt32,
|
||||
UInt64,
|
||||
UInt128,
|
||||
Unknown,
|
||||
UnsignedIntegerType,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"Array",
|
||||
"Binary",
|
||||
"Boolean",
|
||||
"Categorical",
|
||||
"DType",
|
||||
"Date",
|
||||
"Datetime",
|
||||
"Decimal",
|
||||
"Duration",
|
||||
"Enum",
|
||||
"Field",
|
||||
"Float32",
|
||||
"Float64",
|
||||
"FloatType",
|
||||
"Int8",
|
||||
"Int16",
|
||||
"Int32",
|
||||
"Int64",
|
||||
"Int128",
|
||||
"IntegerType",
|
||||
"List",
|
||||
"NestedType",
|
||||
"NumericType",
|
||||
"Object",
|
||||
"SignedIntegerType",
|
||||
"String",
|
||||
"Struct",
|
||||
"Time",
|
||||
"UInt8",
|
||||
"UInt16",
|
||||
"UInt32",
|
||||
"UInt64",
|
||||
"UInt128",
|
||||
"Unknown",
|
||||
"UnsignedIntegerType",
|
||||
]
|
23
lib/python3.11/site-packages/narwhals/stable/v1/selectors.py
Normal file
23
lib/python3.11/site-packages/narwhals/stable/v1/selectors.py
Normal file
@ -0,0 +1,23 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from narwhals.selectors import (
|
||||
all,
|
||||
boolean,
|
||||
by_dtype,
|
||||
categorical,
|
||||
datetime,
|
||||
matches,
|
||||
numeric,
|
||||
string,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"all",
|
||||
"boolean",
|
||||
"by_dtype",
|
||||
"categorical",
|
||||
"datetime",
|
||||
"matches",
|
||||
"numeric",
|
||||
"string",
|
||||
]
|
192
lib/python3.11/site-packages/narwhals/stable/v1/typing.py
Normal file
192
lib/python3.11/site-packages/narwhals/stable/v1/typing.py
Normal file
@ -0,0 +1,192 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import sys
|
||||
from collections.abc import Iterable, Sized
|
||||
|
||||
from narwhals.stable.v1 import DataFrame, LazyFrame
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import TypeAlias
|
||||
else:
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
from narwhals.stable.v1 import Expr, Series
|
||||
|
||||
# All dataframes supported by Narwhals have a
|
||||
# `columns` property. Their similarities don't extend
|
||||
# _that_ much further unfortunately...
|
||||
class NativeFrame(Protocol):
|
||||
@property
|
||||
def columns(self) -> Any: ...
|
||||
|
||||
def join(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||
|
||||
class NativeDataFrame(Sized, NativeFrame, Protocol): ...
|
||||
|
||||
class NativeLazyFrame(NativeFrame, Protocol):
|
||||
def explain(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||
|
||||
class NativeSeries(Sized, Iterable[Any], Protocol):
|
||||
def filter(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||
|
||||
class DataFrameLike(Protocol):
|
||||
def __dataframe__(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||
|
||||
|
||||
IntoExpr: TypeAlias = Union["Expr", str, "Series[Any]"]
|
||||
"""Anything which can be converted to an expression.
|
||||
|
||||
Use this to mean "either a Narwhals expression, or something
|
||||
which can be converted into one". For example, `exprs` in `DataFrame.select` is
|
||||
typed to accept `IntoExpr`, as it can either accept a `nw.Expr`
|
||||
(e.g. `df.select(nw.col('a'))`) or a string which will be interpreted as a
|
||||
`nw.Expr`, e.g. `df.select('a')`.
|
||||
"""
|
||||
|
||||
|
||||
IntoDataFrame: TypeAlias = Union["NativeDataFrame", "DataFrameLike"]
|
||||
"""Anything which can be converted to a Narwhals DataFrame.
|
||||
|
||||
Use this if your function accepts a narwhalifiable object but doesn't care about its backend.
|
||||
|
||||
Examples:
|
||||
>>> import narwhals as nw
|
||||
>>> from narwhals.typing import IntoDataFrame
|
||||
>>> def agnostic_shape(df_native: IntoDataFrame) -> tuple[int, int]:
|
||||
... df = nw.from_native(df_native, eager_only=True)
|
||||
... return df.shape
|
||||
"""
|
||||
|
||||
IntoLazyFrame: TypeAlias = "NativeLazyFrame"
|
||||
|
||||
IntoFrame: TypeAlias = Union["IntoDataFrame", "IntoLazyFrame"]
|
||||
"""Anything which can be converted to a Narwhals DataFrame or LazyFrame.
|
||||
|
||||
Use this if your function can accept an object which can be converted to either
|
||||
`nw.DataFrame` or `nw.LazyFrame` and it doesn't care about its backend.
|
||||
|
||||
Examples:
|
||||
>>> import narwhals as nw
|
||||
>>> from narwhals.typing import IntoFrame
|
||||
>>> def agnostic_columns(df_native: IntoFrame) -> list[str]:
|
||||
... df = nw.from_native(df_native)
|
||||
... return df.collect_schema().names()
|
||||
"""
|
||||
|
||||
Frame: TypeAlias = Union["DataFrame[Any]", "LazyFrame[Any]"]
|
||||
"""Narwhals DataFrame or Narwhals LazyFrame.
|
||||
|
||||
Use this if your function can work with either and your function doesn't care
|
||||
about its backend.
|
||||
|
||||
Examples:
|
||||
>>> import narwhals as nw
|
||||
>>> from narwhals.typing import Frame
|
||||
>>> @nw.narwhalify
|
||||
... def agnostic_columns(df: Frame) -> list[str]:
|
||||
... return df.columns
|
||||
"""
|
||||
|
||||
IntoSeries: TypeAlias = "NativeSeries"
|
||||
"""Anything which can be converted to a Narwhals Series.
|
||||
|
||||
Use this if your function can accept an object which can be converted to `nw.Series`
|
||||
and it doesn't care about its backend.
|
||||
|
||||
Examples:
|
||||
>>> from typing import Any
|
||||
>>> import narwhals as nw
|
||||
>>> from narwhals.typing import IntoSeries
|
||||
>>> def agnostic_to_list(s_native: IntoSeries) -> list[Any]:
|
||||
... s = nw.from_native(s_native)
|
||||
... return s.to_list()
|
||||
"""
|
||||
|
||||
IntoFrameT = TypeVar("IntoFrameT", bound="IntoFrame")
|
||||
"""TypeVar bound to object convertible to Narwhals DataFrame or Narwhals LazyFrame.
|
||||
|
||||
Use this if your function accepts an object which is convertible to `nw.DataFrame`
|
||||
or `nw.LazyFrame` and returns an object of the same type.
|
||||
|
||||
Examples:
|
||||
>>> import narwhals as nw
|
||||
>>> from narwhals.typing import IntoFrameT
|
||||
>>> def agnostic_func(df_native: IntoFrameT) -> IntoFrameT:
|
||||
... df = nw.from_native(df_native)
|
||||
... return df.with_columns(c=nw.col("a") + 1).to_native()
|
||||
"""
|
||||
|
||||
IntoDataFrameT = TypeVar("IntoDataFrameT", bound="IntoDataFrame")
|
||||
"""TypeVar bound to object convertible to Narwhals DataFrame.
|
||||
|
||||
Use this if your function accepts an object which can be converted to `nw.DataFrame`
|
||||
and returns an object of the same class.
|
||||
|
||||
Examples:
|
||||
>>> import narwhals as nw
|
||||
>>> from narwhals.typing import IntoDataFrameT
|
||||
>>> def agnostic_func(df_native: IntoDataFrameT) -> IntoDataFrameT:
|
||||
... df = nw.from_native(df_native, eager_only=True)
|
||||
... return df.with_columns(c=df["a"] + 1).to_native()
|
||||
"""
|
||||
|
||||
IntoLazyFrameT = TypeVar("IntoLazyFrameT", bound="IntoLazyFrame")
|
||||
|
||||
FrameT = TypeVar("FrameT", "DataFrame[Any]", "LazyFrame[Any]")
|
||||
"""TypeVar bound to Narwhals DataFrame or Narwhals LazyFrame.
|
||||
|
||||
Use this if your function accepts either `nw.DataFrame` or `nw.LazyFrame` and returns
|
||||
an object of the same kind.
|
||||
|
||||
Examples:
|
||||
>>> import narwhals as nw
|
||||
>>> from narwhals.typing import FrameT
|
||||
>>> @nw.narwhalify
|
||||
... def agnostic_func(df: FrameT) -> FrameT:
|
||||
... return df.with_columns(c=nw.col("a") + 1)
|
||||
"""
|
||||
|
||||
DataFrameT = TypeVar("DataFrameT", bound="DataFrame[Any]")
|
||||
"""TypeVar bound to Narwhals DataFrame.
|
||||
|
||||
Use this if your function can accept a Narwhals DataFrame and returns a Narwhals
|
||||
DataFrame backed by the same backend.
|
||||
|
||||
Examples:
|
||||
>>> import narwhals as nw
|
||||
>>> from narwhals.typing import DataFrameT
|
||||
>>> @nw.narwhalify
|
||||
>>> def func(df: DataFrameT) -> DataFrameT:
|
||||
... return df.with_columns(c=df["a"] + 1)
|
||||
"""
|
||||
|
||||
IntoSeriesT = TypeVar("IntoSeriesT", bound="IntoSeries")
|
||||
"""TypeVar bound to object convertible to Narwhals Series.
|
||||
|
||||
Use this if your function accepts an object which can be converted to `nw.Series`
|
||||
and returns an object of the same class.
|
||||
|
||||
Examples:
|
||||
>>> import narwhals as nw
|
||||
>>> from narwhals.typing import IntoSeriesT
|
||||
>>> def agnostic_abs(s_native: IntoSeriesT) -> IntoSeriesT:
|
||||
... s = nw.from_native(s_native, series_only=True)
|
||||
... return s.abs().to_native()
|
||||
"""
|
||||
|
||||
|
||||
__all__ = [
|
||||
"DataFrameT",
|
||||
"Frame",
|
||||
"FrameT",
|
||||
"IntoDataFrame",
|
||||
"IntoDataFrameT",
|
||||
"IntoExpr",
|
||||
"IntoFrame",
|
||||
"IntoFrameT",
|
||||
"IntoSeries",
|
||||
"IntoSeriesT",
|
||||
]
|
Reference in New Issue
Block a user