Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
47e768d
chore(typing): Add typing for `SparkLikeExpr` properties
dangotbanned Mar 5, 2025
7bb9131
chore: fix typing and simplify `SparkLikeExprStringNamespace.to_datet…
dangotbanned Mar 5, 2025
89eeed5
rename function
EdAbati Mar 6, 2025
f730d52
use single chars in set
EdAbati Mar 6, 2025
fda0fec
Merge branch 'main' into spark-expr-typing
dangotbanned Mar 6, 2025
e19dcdf
fix: Remove timezone offset replacement
dangotbanned Mar 6, 2025
a4b619c
test: Adds `test_to_datetime_tz_aware`
dangotbanned Mar 6, 2025
2367125
test: possibly fix `pyarrow` in ci?
dangotbanned Mar 6, 2025
fb48654
test: xfail polars `3.8`, fix false positive pyarrow
dangotbanned Mar 6, 2025
75e48cc
test: narrower xfail, tz-less expected?
dangotbanned Mar 6, 2025
194ef83
test: account for `pyarrow` version changes
dangotbanned Mar 6, 2025
ce3145d
test: maybe fix `pyspark`
dangotbanned Mar 6, 2025
ef015af
Merge branch 'main' into spark-expr-typing
dangotbanned Mar 6, 2025
75e4477
Merge branch 'main' into spark-expr-typing
dangotbanned Mar 7, 2025
4470a99
revert: go back to typing fixes only
dangotbanned Mar 7, 2025
77a264c
chore: ignore `format` shadowing
dangotbanned Mar 7, 2025
fbf2459
Merge remote-tracking branch 'upstream/main' into spark-expr-typing
dangotbanned Mar 7, 2025
fb2820b
Merge branch 'main' into spark-expr-typing
dangotbanned Mar 7, 2025
311c7ea
keep logic the same I hope
dangotbanned Mar 7, 2025
7936aa8
Merge branch 'spark-expr-typing' of https://github.com/narwhals-dev/n…
dangotbanned Mar 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions narwhals/_spark_like/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

if TYPE_CHECKING:
from pyspark.sql import Column
from pyspark.sql import Window
from typing_extensions import Self

from narwhals._spark_like.dataframe import SparkLikeLazyFrame
Expand Down Expand Up @@ -78,7 +79,11 @@ def func(df: SparkLikeLazyFrame) -> Sequence[Column]:
)

@property
def _F(self: Self) -> Any: # noqa: N802
def _F(self: Self): # type: ignore[no-untyped-def] # noqa: ANN202, N802
if TYPE_CHECKING:
from pyspark.sql import functions

return functions
Comment thread
dangotbanned marked this conversation as resolved.
if self._implementation is Implementation.SQLFRAME:
from sqlframe.base.session import _BaseSession

Expand All @@ -91,7 +96,11 @@ def _F(self: Self) -> Any: # noqa: N802
return functions

@property
def _native_dtypes(self: Self) -> Any:
def _native_dtypes(self: Self): # type: ignore[no-untyped-def] # noqa: ANN202
Comment thread
dangotbanned marked this conversation as resolved.
if TYPE_CHECKING:
from pyspark.sql import types

return types
if self._implementation is Implementation.SQLFRAME:
from sqlframe.base.session import _BaseSession

Expand All @@ -104,7 +113,7 @@ def _native_dtypes(self: Self) -> Any:
return types

@property
def _Window(self: Self) -> Any: # noqa: N802
def _Window(self: Self) -> type[Window]: # noqa: N802
if self._implementation is Implementation.SQLFRAME:
from sqlframe.base.session import _BaseSession

Expand Down
48 changes: 15 additions & 33 deletions narwhals/_spark_like/expr_str.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from functools import partial
from typing import TYPE_CHECKING
from typing import overload

if TYPE_CHECKING:
from pyspark.sql import Column
Expand Down Expand Up @@ -107,47 +107,29 @@ def to_lowercase(self: Self) -> SparkLikeExpr:
)

def to_datetime(self: Self, format: str | None) -> SparkLikeExpr: # noqa: A002
is_naive = (
format is not None
and "%s" not in format
and "%z" not in format
and "Z" not in format
)
function = (
self._compliant_expr._F.to_timestamp_ntz
if is_naive
else self._compliant_expr._F.to_timestamp
)
pyspark_format = strptime_to_pyspark_format(format)
format = (
self._compliant_expr._F.lit(pyspark_format) if is_naive else pyspark_format
)
F = self._compliant_expr._F # noqa: N806
if not format:
function = F.to_timestamp
elif is_naive_format(format):
function = partial(
F.to_timestamp_ntz, format=F.lit(strptime_to_pyspark_format(format))
)
else:
format = strptime_to_pyspark_format(format)
function = partial(F.to_timestamp, format=format)
return self._compliant_expr._from_call(
lambda _input: function(
self._compliant_expr._F.replace(
_input,
self._compliant_expr._F.lit("T"),
self._compliant_expr._F.lit(" "),
),
format=format,
),
lambda _input: function(F.replace(_input, F.lit("T"), F.lit(" "))),
"to_datetime",
)


@overload
def strptime_to_pyspark_format(format: None) -> None: ...


@overload
def strptime_to_pyspark_format(format: str) -> str: ...
def is_naive_format(format: str) -> bool: # noqa: A002
return {"s", "z", "Z"}.isdisjoint(format)

@MarcoGorelli MarcoGorelli Mar 7, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't really like how we're changing the logic here, 'z' in a format string means the 'z' literal, it's '%z' we need to check for

the rest looks good though, well done!

PR to address time zone aware parsing: #2166

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @MarcoGorelli

I'm happy for that bit to be reverted and just keep the splitting out into the new function?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure! if you're happy with #2166 to add a test and adjust some logic, happy to then take this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully got it done (311c7ea)



def strptime_to_pyspark_format(format: str | None) -> str | None: # noqa: A002
def strptime_to_pyspark_format(format: str) -> str: # noqa: A002
"""Converts a Python strptime datetime format string to a PySpark datetime format string."""
# Mapping from Python strptime format to PySpark format
if format is None:
return None

# see https://spark.apache.org/docs/latest/sql-ref-datetime-pattern.html
# and https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior
Expand Down