PySpark-Column-Selectors 0.1.3__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.
- PySparkSelectors/__init__.py +240 -0
- PySparkSelectors/__main__.py +4 -0
- PySparkSelectors/cli.py +18 -0
- PySparkSelectors/models.py +1564 -0
- PySparkSelectors/py.typed +1 -0
- PySparkSelectors/spark_overrides.py +677 -0
- PySparkSelectors/user_functions.py +745 -0
- PySparkSelectors/utils.py +298 -0
- pyspark_column_selectors-0.1.3.dist-info/METADATA +674 -0
- pyspark_column_selectors-0.1.3.dist-info/RECORD +13 -0
- pyspark_column_selectors-0.1.3.dist-info/WHEEL +4 -0
- pyspark_column_selectors-0.1.3.dist-info/entry_points.txt +2 -0
- pyspark_column_selectors-0.1.3.dist-info/licenses/LICENSE +279 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------------
|
|
2
|
+
# Package bootstrap
|
|
3
|
+
# --------------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
"""Public package entry point for PySparkSelectors."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
# import pyspark
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"initialize",
|
|
15
|
+
"BaseSelector",
|
|
16
|
+
"DTypeSelector",
|
|
17
|
+
"IndexSelector",
|
|
18
|
+
"RegexSelector",
|
|
19
|
+
"SelectorFilterCondition",
|
|
20
|
+
"SelectorSelectionOperations",
|
|
21
|
+
"SelectorcolumnOperations",
|
|
22
|
+
"by_dtype",
|
|
23
|
+
"by_index",
|
|
24
|
+
"matches",
|
|
25
|
+
"by_name",
|
|
26
|
+
"exclude",
|
|
27
|
+
"is_selector",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
_INITIALIZED = False
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _ensure_runtime_state() -> None:
|
|
34
|
+
"""Populate the package namespace without re-importing the package graph on each access."""
|
|
35
|
+
global _INITIALIZED
|
|
36
|
+
if _INITIALIZED:
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
from .models import (
|
|
40
|
+
BaseSelector,
|
|
41
|
+
DTypeSelector,
|
|
42
|
+
IndexSelector,
|
|
43
|
+
RegexSelector,
|
|
44
|
+
SelectorcolumnOperations,
|
|
45
|
+
SelectorFilterCondition,
|
|
46
|
+
SelectorSelectionOperations,
|
|
47
|
+
)
|
|
48
|
+
from .user_functions import (
|
|
49
|
+
all,
|
|
50
|
+
alpha,
|
|
51
|
+
alphanumeric,
|
|
52
|
+
binary,
|
|
53
|
+
boolean,
|
|
54
|
+
by_dtype,
|
|
55
|
+
by_index,
|
|
56
|
+
by_name,
|
|
57
|
+
contains,
|
|
58
|
+
date,
|
|
59
|
+
datetime_,
|
|
60
|
+
ends_with,
|
|
61
|
+
exclude,
|
|
62
|
+
first,
|
|
63
|
+
floats,
|
|
64
|
+
integer,
|
|
65
|
+
is_selector,
|
|
66
|
+
last,
|
|
67
|
+
matches,
|
|
68
|
+
numeric,
|
|
69
|
+
starts_with,
|
|
70
|
+
string,
|
|
71
|
+
temporal,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
globals().update(
|
|
75
|
+
{
|
|
76
|
+
"BaseSelector": BaseSelector,
|
|
77
|
+
"DTypeSelector": DTypeSelector,
|
|
78
|
+
"IndexSelector": IndexSelector,
|
|
79
|
+
"RegexSelector": RegexSelector,
|
|
80
|
+
"SelectorFilterCondition": SelectorFilterCondition,
|
|
81
|
+
"SelectorSelectionOperations": SelectorSelectionOperations,
|
|
82
|
+
"SelectorcolumnOperations": SelectorcolumnOperations,
|
|
83
|
+
"by_dtype": by_dtype,
|
|
84
|
+
"by_index": by_index,
|
|
85
|
+
"matches": matches,
|
|
86
|
+
"by_name": by_name,
|
|
87
|
+
"exclude": exclude,
|
|
88
|
+
"is_selector": is_selector,
|
|
89
|
+
"alpha": alpha,
|
|
90
|
+
"alphanumeric": alphanumeric,
|
|
91
|
+
"all": all,
|
|
92
|
+
"binary": binary,
|
|
93
|
+
"boolean": boolean,
|
|
94
|
+
"contains": contains,
|
|
95
|
+
"date": date,
|
|
96
|
+
"datetime_": datetime_,
|
|
97
|
+
"ends_with": ends_with,
|
|
98
|
+
"first": first,
|
|
99
|
+
"floats": floats,
|
|
100
|
+
"integer": integer,
|
|
101
|
+
"last": last,
|
|
102
|
+
"numeric": numeric,
|
|
103
|
+
"starts_with": starts_with,
|
|
104
|
+
"string": string,
|
|
105
|
+
"temporal": temporal,
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
initialize()
|
|
110
|
+
_INITIALIZED = True
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def initialize() -> None:
|
|
114
|
+
"""Initialize the selector patch layer after the package has loaded."""
|
|
115
|
+
from pyspark.sql import DataFrame
|
|
116
|
+
from pyspark.sql import functions as F
|
|
117
|
+
from pyspark.sql.column import Column
|
|
118
|
+
from pyspark.sql.group import GroupedData
|
|
119
|
+
|
|
120
|
+
from .models import SelectorcolumnOperations
|
|
121
|
+
from .spark_overrides import (
|
|
122
|
+
_build_agg_override,
|
|
123
|
+
_build_filter_override,
|
|
124
|
+
_build_with_column_override,
|
|
125
|
+
_build_with_columns_override,
|
|
126
|
+
_make_column_only_wrapper,
|
|
127
|
+
# _make_dispatching_column_wrapper,
|
|
128
|
+
_patch_classes,
|
|
129
|
+
make_expr_based_override,
|
|
130
|
+
make_name_based_override,
|
|
131
|
+
)
|
|
132
|
+
from .utils import _get_column_method
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
from pyspark.sql.connect.group import GroupedData as _ConnectGroupedData
|
|
136
|
+
except ImportError:
|
|
137
|
+
_ConnectGroupedData = None
|
|
138
|
+
|
|
139
|
+
try:
|
|
140
|
+
from pyspark.sql.classic.dataframe import DataFrame as _ClassicDataFrame
|
|
141
|
+
except ImportError:
|
|
142
|
+
_ClassicDataFrame = None
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
from pyspark.sql.connect.dataframe import DataFrame as _ConnectDataFrame
|
|
146
|
+
except ImportError:
|
|
147
|
+
_ConnectDataFrame = None
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
from pyspark.sql.connect.column import Column as _ConnectColumn
|
|
151
|
+
except ImportError:
|
|
152
|
+
_ConnectColumn = None
|
|
153
|
+
|
|
154
|
+
_DATAFRAME_CLASSES = tuple(
|
|
155
|
+
dict.fromkeys(cls for cls in (DataFrame, _ClassicDataFrame, _ConnectDataFrame) if cls is not None)
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
_f_callable_names = {name for name in dir(F) if callable(getattr(F, name, None))}
|
|
159
|
+
|
|
160
|
+
for fx in dir(F):
|
|
161
|
+
myfx = getattr(F, fx)
|
|
162
|
+
if callable(myfx):
|
|
163
|
+
setattr(SelectorcolumnOperations, fx, SelectorcolumnOperations.spark_wrapper(myfx))
|
|
164
|
+
|
|
165
|
+
_column_method_names = set(dir(Column))
|
|
166
|
+
if _ConnectColumn is not None:
|
|
167
|
+
_column_method_names |= set(dir(_ConnectColumn))
|
|
168
|
+
|
|
169
|
+
for _method_name in sorted(_column_method_names):
|
|
170
|
+
if _method_name.startswith("_"):
|
|
171
|
+
continue
|
|
172
|
+
if _method_name in ("prefix", "suffix", "map_alias"):
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
_column_attr = _get_column_method(_method_name)
|
|
177
|
+
except AttributeError:
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
if not callable(_column_attr):
|
|
181
|
+
continue
|
|
182
|
+
|
|
183
|
+
if _method_name in _f_callable_names:
|
|
184
|
+
_column_wrapper = SelectorcolumnOperations._make_dispatching_column_wrapper(_method_name)
|
|
185
|
+
else:
|
|
186
|
+
_column_wrapper = _make_column_only_wrapper(_method_name)
|
|
187
|
+
|
|
188
|
+
setattr(
|
|
189
|
+
SelectorcolumnOperations,
|
|
190
|
+
_method_name,
|
|
191
|
+
SelectorcolumnOperations.spark_wrapper(_column_wrapper),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# alias requires special handling as it isn't being properly overridden
|
|
195
|
+
|
|
196
|
+
make_expr_based_override("select")
|
|
197
|
+
make_expr_based_override("sort", alias_results=False)
|
|
198
|
+
make_expr_based_override("orderBy", alias_results=False)
|
|
199
|
+
make_name_based_override("drop")
|
|
200
|
+
make_name_based_override("groupBy")
|
|
201
|
+
make_name_based_override("groupby")
|
|
202
|
+
|
|
203
|
+
# for _cls in _DATAFRAME_CLASSES:
|
|
204
|
+
# _cls.groupby = _cls.groupBy
|
|
205
|
+
|
|
206
|
+
_GROUPED_DATA_CLASSES = tuple(c for c in (GroupedData, _ConnectGroupedData) if c is not None)
|
|
207
|
+
|
|
208
|
+
_patch_classes(_DATAFRAME_CLASSES, "withColumn", _build_with_column_override)
|
|
209
|
+
_patch_classes(_DATAFRAME_CLASSES, "withColumns", _build_with_columns_override)
|
|
210
|
+
_patch_classes(_GROUPED_DATA_CLASSES, "agg", _build_agg_override)
|
|
211
|
+
_patch_classes(_DATAFRAME_CLASSES, "filter", _build_filter_override)
|
|
212
|
+
_patch_classes(_DATAFRAME_CLASSES, "where", _build_filter_override)
|
|
213
|
+
|
|
214
|
+
# for _cls in _DATAFRAME_CLASSES:
|
|
215
|
+
# _cls.where = _cls.filter
|
|
216
|
+
|
|
217
|
+
# this can be iffy, was causing errors in just file checking, but is being used to handle
|
|
218
|
+
# cast override because the normal function overrides doesn't alway's work in the way expected
|
|
219
|
+
# SelectorcolumnOperations.cast = SelectorcolumnOperations.spark_wrapper(Column.cast)
|
|
220
|
+
globals().setdefault("_INITIALIZED", True)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def __getattr__(name: str) -> Any:
|
|
224
|
+
if name in globals():
|
|
225
|
+
return globals()[name]
|
|
226
|
+
_ensure_runtime_state()
|
|
227
|
+
if name in globals():
|
|
228
|
+
return globals()[name]
|
|
229
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def __dir__() -> list[str]:
|
|
233
|
+
return sorted(set(globals()) | set(__all__))
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
# Importing the package should not require a second manual call to initialize(),
|
|
237
|
+
# but a direct call remains available for explicit runtime patching.
|
|
238
|
+
_ensure_runtime_state()
|
|
239
|
+
|
|
240
|
+
_initialize = initialize
|
PySparkSelectors/cli.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Console script for PySparkSelectors."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
6
|
+
app = typer.Typer()
|
|
7
|
+
console = Console()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@app.command()
|
|
11
|
+
def main() -> None:
|
|
12
|
+
"""Console script for PySparkSelectors."""
|
|
13
|
+
console.print("Replace this message by putting your code into PySparkSelectors.cli.main")
|
|
14
|
+
console.print("See Typer documentation at https://typer.tiangolo.com/")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if __name__ == "__main__":
|
|
18
|
+
app()
|