sparkleframe 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sparkleframe/__init__.py +8 -0
- sparkleframe/activate.py +96 -0
- sparkleframe/base/__init__.py +0 -0
- sparkleframe/base/dataframe.py +20 -0
- sparkleframe/conftest.py +13 -0
- sparkleframe/context.py +86 -0
- sparkleframe/engine.py +16 -0
- sparkleframe/gpt_utils.py +91 -0
- sparkleframe/polarsdf/__init__.py +12 -0
- sparkleframe/polarsdf/column.py +153 -0
- sparkleframe/polarsdf/column_test.py +232 -0
- sparkleframe/polarsdf/dataframe.py +334 -0
- sparkleframe/polarsdf/dataframe_test.py +771 -0
- sparkleframe/polarsdf/functions.py +197 -0
- sparkleframe/polarsdf/functions_test.py +187 -0
- sparkleframe/polarsdf/group.py +38 -0
- sparkleframe/polarsdf/session.py +47 -0
- sparkleframe/polarsdf/session_test.py +37 -0
- sparkleframe/polarsdf/types.py +136 -0
- sparkleframe/tests/__init__.py +0 -0
- sparkleframe/tests/pyspark_test.py +30 -0
- sparkleframe/tests/spark.py +21 -0
- sparkleframe/tests/utils.py +13 -0
- sparkleframe/version.txt +1 -0
- sparkleframe-0.0.1.dist-info/METADATA +22 -0
- sparkleframe-0.0.1.dist-info/RECORD +28 -0
- sparkleframe-0.0.1.dist-info/WHEEL +4 -0
- sparkleframe-0.0.1.dist-info/licenses/LICENSE +201 -0
sparkleframe/__init__.py
ADDED
sparkleframe/activate.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sparkleframe
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# ruff: noqa: F401
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import importlib
|
|
9
|
+
import sys
|
|
10
|
+
from contextlib import contextmanager
|
|
11
|
+
from unittest.mock import MagicMock
|
|
12
|
+
|
|
13
|
+
# Helps users to use `from sparkleframe import Engine` instead of `from sparkleframe.engine import Engine`
|
|
14
|
+
from .engine import Engine
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
NAME_TO_FILE_OVERRIDE = {
|
|
18
|
+
# "DataFrameNaFunctions": "dataframe",
|
|
19
|
+
# "DataFrameStatFunctions": "dataframe",
|
|
20
|
+
# "DataFrameReader": "readwriter",
|
|
21
|
+
# "DataFrameWriter": "readwriter",
|
|
22
|
+
# "GroupedData": "group",
|
|
23
|
+
"SparkSession": "session",
|
|
24
|
+
# "WindowSpec": "window",
|
|
25
|
+
# "UDFRegistration": "udf",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
ACTIVATE_CONFIG = {}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def activate() -> None:
|
|
32
|
+
|
|
33
|
+
pyspark_mock = MagicMock()
|
|
34
|
+
pyspark_mock.__file__ = "pyspark"
|
|
35
|
+
sys.modules["pyspark"] = pyspark_mock
|
|
36
|
+
# pyspark_mock.testing = testing
|
|
37
|
+
# sys.modules["pyspark.testing"] = testing
|
|
38
|
+
|
|
39
|
+
engine = "polarsdf"
|
|
40
|
+
prefix = "Polars"
|
|
41
|
+
engine_module = importlib.import_module(f"sparkleframe.{engine}")
|
|
42
|
+
|
|
43
|
+
sys.modules["pyspark.sql"] = engine_module
|
|
44
|
+
pyspark_mock.sql = engine_module
|
|
45
|
+
types = engine_module.__dict__.copy()
|
|
46
|
+
resolved_files = set()
|
|
47
|
+
for name, obj in types.items():
|
|
48
|
+
# print("name, obj, name.startswith(prefix): ",name, obj, name.startswith(prefix))
|
|
49
|
+
if name.startswith(prefix) or name in [
|
|
50
|
+
"Column",
|
|
51
|
+
"Window",
|
|
52
|
+
"WindowSpec",
|
|
53
|
+
"functions",
|
|
54
|
+
"types",
|
|
55
|
+
"Column",
|
|
56
|
+
"DataFrame",
|
|
57
|
+
"SparkSession",
|
|
58
|
+
]:
|
|
59
|
+
|
|
60
|
+
name_without_prefix = name.replace(prefix, "")
|
|
61
|
+
if name_without_prefix == "Session":
|
|
62
|
+
name_without_prefix = "SparkSession"
|
|
63
|
+
|
|
64
|
+
setattr(engine_module, name_without_prefix, obj)
|
|
65
|
+
|
|
66
|
+
file = NAME_TO_FILE_OVERRIDE.get(name_without_prefix, name_without_prefix).lower()
|
|
67
|
+
|
|
68
|
+
engine_file = importlib.import_module(f"sparkleframe.{engine}.{file}")
|
|
69
|
+
|
|
70
|
+
if engine_file not in resolved_files:
|
|
71
|
+
sys.modules[f"pyspark.sql.{file}"] = engine_file
|
|
72
|
+
resolved_files.add(engine_file)
|
|
73
|
+
|
|
74
|
+
setattr(engine_file, name_without_prefix, obj)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def deactivate() -> None:
|
|
78
|
+
pyspark_imports = [k for k in sys.modules if k.startswith("pyspark")]
|
|
79
|
+
|
|
80
|
+
for k, v in sys.modules.copy().items():
|
|
81
|
+
if k in pyspark_imports:
|
|
82
|
+
del sys.modules[k]
|
|
83
|
+
# Try importing the pyspark imports again and see if pyspark is installed and therefore available
|
|
84
|
+
# if not then nothing will change
|
|
85
|
+
for k in pyspark_imports:
|
|
86
|
+
try:
|
|
87
|
+
sys.modules[k] = importlib.import_module(k)
|
|
88
|
+
except ImportError:
|
|
89
|
+
pass
|
|
90
|
+
ACTIVATE_CONFIG.clear()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@contextmanager
|
|
94
|
+
def activate_context():
|
|
95
|
+
activate()
|
|
96
|
+
yield
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from pandas import DataFrame as PandasDataFrame
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DataFrame:
|
|
7
|
+
def __init__(self, df: Any):
|
|
8
|
+
self._alias = None
|
|
9
|
+
self.df = df
|
|
10
|
+
|
|
11
|
+
def pandas_api(self) -> PandasDataFrame:
|
|
12
|
+
"""Mimics Pandas On Spark dataframe"""
|
|
13
|
+
return self.toPandas()
|
|
14
|
+
|
|
15
|
+
def to_native_df(self) -> "DataFrame":
|
|
16
|
+
"""Return the underlying Polars DataFrame."""
|
|
17
|
+
return self.df
|
|
18
|
+
|
|
19
|
+
def to_spark(self) -> "DataFrame":
|
|
20
|
+
return self
|
sparkleframe/conftest.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
# Skip writing pyc files on a readonly filesystem.
|
|
6
|
+
sys.dont_write_bytecode = True
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture(scope="function", autouse=False)
|
|
10
|
+
def spark():
|
|
11
|
+
from sparkleframe.tests.spark import spark as spark_session
|
|
12
|
+
|
|
13
|
+
return spark_session
|
sparkleframe/context.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import sys
|
|
3
|
+
import types
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from typing import Callable
|
|
6
|
+
|
|
7
|
+
from sparkleframe import Engine
|
|
8
|
+
|
|
9
|
+
SPARKLE_MODULES = [
|
|
10
|
+
"functions",
|
|
11
|
+
"types",
|
|
12
|
+
"session",
|
|
13
|
+
"dataframe",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@contextmanager
|
|
18
|
+
def mock_pyspark_modules(mock_map):
|
|
19
|
+
# Save original pyspark modules
|
|
20
|
+
original_modules = {k: sys.modules[k] for k in list(sys.modules) if k.startswith("pyspark")}
|
|
21
|
+
|
|
22
|
+
# Remove existing pyspark.* modules
|
|
23
|
+
for key in original_modules:
|
|
24
|
+
del sys.modules[key]
|
|
25
|
+
|
|
26
|
+
# Make sure root pyspark module exists
|
|
27
|
+
if "pyspark" not in sys.modules:
|
|
28
|
+
sys.modules["pyspark"] = types.ModuleType("pyspark")
|
|
29
|
+
|
|
30
|
+
# Add parent modules for each mock
|
|
31
|
+
for full_name in mock_map:
|
|
32
|
+
parts = full_name.split(".")
|
|
33
|
+
for i in range(1, len(parts)):
|
|
34
|
+
parent = ".".join(parts[:i])
|
|
35
|
+
if parent not in sys.modules:
|
|
36
|
+
sys.modules[parent] = types.ModuleType(parent)
|
|
37
|
+
|
|
38
|
+
# Inject mocks
|
|
39
|
+
sys.modules.update(mock_map)
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
yield
|
|
43
|
+
finally:
|
|
44
|
+
# Clean up mocked modules
|
|
45
|
+
for key in mock_map:
|
|
46
|
+
pyspark_module_name = key
|
|
47
|
+
if pyspark_module_name in sys.modules:
|
|
48
|
+
del sys.modules[pyspark_module_name]
|
|
49
|
+
|
|
50
|
+
sparkle_module_name = mock_map[key].__name__
|
|
51
|
+
if sparkle_module_name in sys.modules:
|
|
52
|
+
del sys.modules[sparkle_module_name]
|
|
53
|
+
|
|
54
|
+
# Restore and reload original modules
|
|
55
|
+
for name, module in original_modules.items():
|
|
56
|
+
sys.modules[name] = module
|
|
57
|
+
importlib.import_module(name)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_mock_map(engine: Engine) -> dict:
|
|
61
|
+
|
|
62
|
+
sparkleframe_module = f"sparkleframe.{engine.module}"
|
|
63
|
+
engine_module = importlib.import_module(sparkleframe_module)
|
|
64
|
+
|
|
65
|
+
mock_map = {"pyspark.sql": engine_module}
|
|
66
|
+
types = engine_module.__dict__.copy()
|
|
67
|
+
for name, obj in types.items():
|
|
68
|
+
name = engine.clean_class_name(name)
|
|
69
|
+
|
|
70
|
+
if name in SPARKLE_MODULES:
|
|
71
|
+
module_name = obj.__name__.split(".")[-1]
|
|
72
|
+
pyspark_module = f"pyspark.sql.{module_name}"
|
|
73
|
+
sparkle_module = f"{sparkleframe_module}.{module_name}"
|
|
74
|
+
mock_map[pyspark_module] = importlib.import_module(sparkle_module)
|
|
75
|
+
|
|
76
|
+
return mock_map
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def run_with_context(func: Callable, engine: Engine = None):
|
|
80
|
+
engine = engine or Engine.POLARS
|
|
81
|
+
|
|
82
|
+
with mock_pyspark_modules(get_mock_map(engine)):
|
|
83
|
+
for name in SPARKLE_MODULES:
|
|
84
|
+
importlib.import_module(f"sparkleframe.{engine.module}.{name}")
|
|
85
|
+
|
|
86
|
+
return func()
|
sparkleframe/engine.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Engine(Enum):
|
|
5
|
+
POLARS = ("polarsdf", "Polars")
|
|
6
|
+
|
|
7
|
+
@property
|
|
8
|
+
def module(self):
|
|
9
|
+
return self.value[0]
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def class_prefix(self):
|
|
13
|
+
return self.value[1]
|
|
14
|
+
|
|
15
|
+
def clean_class_name(self, klass):
|
|
16
|
+
return klass.replace(self.class_prefix, "") if klass.startswith(self.class_prefix) else klass
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""
|
|
2
|
+
this is my <file_name.py>
|
|
3
|
+
|
|
4
|
+
<content of the file>
|
|
5
|
+
|
|
6
|
+
implement the pyspark <function> function like as defined in <function_url> into my <file_name.py>
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def generate_pyspark_function_instruction(file_path: str, function_name: str, function_url: str) -> str:
|
|
14
|
+
"""
|
|
15
|
+
Generates a formatted message including the contents of a Python file and an instruction
|
|
16
|
+
to implement a specific PySpark function based on its official documentation.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
file_path (str): Path to the Python file.
|
|
20
|
+
function_name (str): Name of the PySpark function to implement.
|
|
21
|
+
function_url (str): URL to the official PySpark documentation for the function.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
str: Formatted message.
|
|
25
|
+
"""
|
|
26
|
+
if not os.path.isfile(file_path):
|
|
27
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
28
|
+
|
|
29
|
+
with open(file_path, "r") as file:
|
|
30
|
+
file_contents = file.read()
|
|
31
|
+
|
|
32
|
+
file_name = os.path.basename(file_path)
|
|
33
|
+
|
|
34
|
+
message = f"""\
|
|
35
|
+
this is the content of my {file_name}:
|
|
36
|
+
|
|
37
|
+
{file_contents}
|
|
38
|
+
|
|
39
|
+
implement the pyspark {function_name} function like as defined in {function_url} into my {file_name}
|
|
40
|
+
"""
|
|
41
|
+
return message
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def generate_test_instruction(file_path: str, function_name: str, compare_output_spark=False) -> str:
|
|
45
|
+
"""
|
|
46
|
+
Generates a formatted message including the contents of a Python file and an instruction
|
|
47
|
+
to implement a specific PySpark function based on its official documentation.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
file_path (str): Path to the Python file.
|
|
51
|
+
function_name (str): Name of the PySpark function to implement.
|
|
52
|
+
function_url (str): URL to the official PySpark documentation for the function.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
str: Formatted message.
|
|
56
|
+
"""
|
|
57
|
+
if not os.path.isfile(file_path):
|
|
58
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
59
|
+
|
|
60
|
+
with open(file_path, "r") as file:
|
|
61
|
+
file_contents = file.read()
|
|
62
|
+
|
|
63
|
+
file_name = os.path.basename(file_path)
|
|
64
|
+
|
|
65
|
+
spark_msg_output = (
|
|
66
|
+
", create a spark dataframe build from PolarDataframe and use another spark dataframe to compare them "
|
|
67
|
+
if compare_output_spark
|
|
68
|
+
else ""
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
message = f"""\
|
|
72
|
+
this is the content of my test file {file_name}:
|
|
73
|
+
|
|
74
|
+
{file_contents}
|
|
75
|
+
|
|
76
|
+
generate a pytest test case, using pytest.parametrize to test the logic `{function_name}` that has been just implemented{spark_msg_output}
|
|
77
|
+
"""
|
|
78
|
+
return message
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
file_path = "./polarsdf/dataframe.py"
|
|
82
|
+
function_name = "fillna"
|
|
83
|
+
pyspark_function_url = (
|
|
84
|
+
"https://spark.apache.org/docs/latest/api/python/reference/pyspark.sql/api/pyspark.sql.DataFrame.fillna.html"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
msg = generate_pyspark_function_instruction(file_path, function_name, pyspark_function_url)
|
|
88
|
+
|
|
89
|
+
msg = generate_test_instruction(file_path, function_name, compare_output_spark=True)
|
|
90
|
+
|
|
91
|
+
print(msg)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# ruff: noqa: F401
|
|
2
|
+
from sparkleframe.polarsdf.column import Column
|
|
3
|
+
from sparkleframe.polarsdf.dataframe import DataFrame
|
|
4
|
+
from sparkleframe.polarsdf.functions import col
|
|
5
|
+
from sparkleframe.polarsdf.session import SparkSession
|
|
6
|
+
from sparkleframe.polarsdf.types import StringType
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"Column",
|
|
10
|
+
"SparkSession",
|
|
11
|
+
"DataFrame",
|
|
12
|
+
]
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
|
|
5
|
+
import polars as pl
|
|
6
|
+
|
|
7
|
+
from sparkleframe.polarsdf.types import DataType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Column:
|
|
11
|
+
def __init__(self, expr_or_name):
|
|
12
|
+
if isinstance(expr_or_name, str):
|
|
13
|
+
self.expr = pl.col(expr_or_name)
|
|
14
|
+
else:
|
|
15
|
+
self.expr = expr_or_name
|
|
16
|
+
|
|
17
|
+
# Arithmetic operations
|
|
18
|
+
def __mul__(self, other):
|
|
19
|
+
return Column(self.expr * _to_expr(other))
|
|
20
|
+
|
|
21
|
+
def __add__(self, other):
|
|
22
|
+
return Column(self.expr + _to_expr(other))
|
|
23
|
+
|
|
24
|
+
def __sub__(self, other):
|
|
25
|
+
return Column(self.expr - _to_expr(other))
|
|
26
|
+
|
|
27
|
+
def __truediv__(self, other):
|
|
28
|
+
return Column(self.expr / _to_expr(other))
|
|
29
|
+
|
|
30
|
+
def __radd__(self, other):
|
|
31
|
+
return Column(_to_expr(other) + self.expr)
|
|
32
|
+
|
|
33
|
+
def __rsub__(self, other):
|
|
34
|
+
return Column(_to_expr(other) - self.expr)
|
|
35
|
+
|
|
36
|
+
def __rmul__(self, other):
|
|
37
|
+
return Column(_to_expr(other) * self.expr)
|
|
38
|
+
|
|
39
|
+
def __rtruediv__(self, other):
|
|
40
|
+
return Column(_to_expr(other) / self.expr)
|
|
41
|
+
|
|
42
|
+
# Comparison operations
|
|
43
|
+
def __eq__(self, other):
|
|
44
|
+
return Column(self.expr == _to_expr(other))
|
|
45
|
+
|
|
46
|
+
def __ne__(self, other):
|
|
47
|
+
return Column(self.expr != _to_expr(other))
|
|
48
|
+
|
|
49
|
+
def __lt__(self, other):
|
|
50
|
+
return Column(self.expr < _to_expr(other))
|
|
51
|
+
|
|
52
|
+
def __le__(self, other):
|
|
53
|
+
return Column(self.expr <= _to_expr(other))
|
|
54
|
+
|
|
55
|
+
def __gt__(self, other):
|
|
56
|
+
return Column(self.expr > _to_expr(other))
|
|
57
|
+
|
|
58
|
+
def __ge__(self, other):
|
|
59
|
+
return Column(self.expr >= _to_expr(other))
|
|
60
|
+
|
|
61
|
+
# Logical operations
|
|
62
|
+
def __and__(self, other):
|
|
63
|
+
return Column(self.expr & _to_expr(other))
|
|
64
|
+
|
|
65
|
+
def __rand__(self, other):
|
|
66
|
+
return Column(_to_expr(other) & self.expr)
|
|
67
|
+
|
|
68
|
+
def __or__(self, other):
|
|
69
|
+
return Column(self.expr | _to_expr(other))
|
|
70
|
+
|
|
71
|
+
def __ror__(self, other):
|
|
72
|
+
return Column(_to_expr(other) | self.expr)
|
|
73
|
+
|
|
74
|
+
def alias(self, name: str) -> Column:
|
|
75
|
+
"""
|
|
76
|
+
Mimics pyspark.sql.Column.alias
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
name (str): Alias name for the column expression
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Column: A new Column with the alias applied
|
|
83
|
+
"""
|
|
84
|
+
return Column(self.expr.alias(name))
|
|
85
|
+
|
|
86
|
+
def cast(self, data_type: DataType) -> Column:
|
|
87
|
+
"""
|
|
88
|
+
Mimics pyspark.sql.Column.cast using Polars' cast().
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
data_type (DataType): A sparkleframe-defined DataType object.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Column: A new Column with the expression casted.
|
|
95
|
+
"""
|
|
96
|
+
if not isinstance(data_type, DataType):
|
|
97
|
+
raise TypeError(f"cast() expects a DataType, got {type(data_type)}")
|
|
98
|
+
return Column(self.expr.cast(data_type.to_native()))
|
|
99
|
+
|
|
100
|
+
def isin(self, *values) -> Column:
|
|
101
|
+
"""
|
|
102
|
+
Mimics pyspark.sql.Column.isin and supports both:
|
|
103
|
+
col("x").isin("a", "b") and col("x").isin(["a", "b"])
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
*values: A list of values or individual arguments.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Column: A Column representing a boolean expression.
|
|
110
|
+
"""
|
|
111
|
+
# If a single iterable (non-str) is passed, use that directly
|
|
112
|
+
if len(values) == 1 and isinstance(values[0], Iterable) and not isinstance(values[0], str):
|
|
113
|
+
value_list = list(values[0])
|
|
114
|
+
else:
|
|
115
|
+
value_list = list(values)
|
|
116
|
+
|
|
117
|
+
return Column(self.expr.is_in(value_list))
|
|
118
|
+
|
|
119
|
+
def isNotNull(self) -> Column:
|
|
120
|
+
"""
|
|
121
|
+
Mimics pyspark.sql.Column.isNotNull
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Column: A Column representing the non-null condition.
|
|
125
|
+
"""
|
|
126
|
+
return Column(self.expr.is_not_null())
|
|
127
|
+
|
|
128
|
+
def rlike(self, pattern: str) -> Column:
|
|
129
|
+
"""
|
|
130
|
+
Mimics pyspark.sql.Column.rlike using Polars' regex matching.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
pattern (str): Regular expression pattern to match.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Column: A new Column representing a boolean expression.
|
|
137
|
+
"""
|
|
138
|
+
if not isinstance(pattern, str):
|
|
139
|
+
raise TypeError(f"rlike() expects a string pattern, got {type(pattern)}")
|
|
140
|
+
|
|
141
|
+
return Column(self.expr.str.contains(pattern))
|
|
142
|
+
|
|
143
|
+
def to_native(self) -> pl.Expr:
|
|
144
|
+
return self.expr
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _to_expr(value):
|
|
148
|
+
if isinstance(value, Column):
|
|
149
|
+
return value.to_native()
|
|
150
|
+
elif isinstance(value, pl.Expr):
|
|
151
|
+
return value
|
|
152
|
+
else:
|
|
153
|
+
return pl.lit(value)
|