Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions bindings/distrdf/python/DistRDF/Backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
import types


def build_backends_submodules(parentmodule: types.ModuleType) -> types.ModuleType:
def build_backends_submodules(parentmodule: types.ModuleType, experimental: bool) -> types.ModuleType:
"""
Helper function to create the submodules of the backends.
"""
for _, module_name, is_pkg in pkgutil.walk_packages(__path__):
from ROOT._distrdf import _raise_warning_if_experimental

for _, module_name, is_pkg in pkgutil.walk_packages(__path__):
if is_pkg:
# The actual python package with the backend implementation
actual = importlib.import_module(__name__ + "." + module_name)
Expand All @@ -37,7 +38,7 @@ def build_backends_submodules(parentmodule: types.ModuleType) -> types.ModuleTyp
dummy.__package__ = parentmodule

# Attached functions
dummy.RDataFrame = actual.RDataFrame
dummy.RDataFrame = _raise_warning_if_experimental(actual.RDataFrame, experimental)

setattr(parentmodule, module_name, dummy)

Expand Down
76 changes: 39 additions & 37 deletions bindings/distrdf/python/DistRDF/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
from __future__ import annotations

import concurrent.futures
import functools
import importlib.util
import logging
import textwrap
import types
import warnings
from typing import TYPE_CHECKING, Iterable
from typing import TYPE_CHECKING, Callable, Iterable

from .Backends import build_backends_submodules
from .LiveVisualize import LiveVisualize
Expand Down Expand Up @@ -208,29 +209,33 @@ def FromSpec(jsonfile: str, *args, **kwargs) -> RDataFrame:
)


class _DeprecatedModule(types.ModuleType):
"""A simple module type to raise a warning before usage."""

def __getattribute__(self, name):
msg_warng = textwrap.dedent(
"""
In ROOT 6.36, the ROOT.RDF.Experimental.Distributed module has become just ROOT.RDF.Distributed. In the
future, the 'Experimental' keyword will be removed, so it is suggested to move to the stable API in user
code. You can now change lines such as:
```
connection = ... # your distributed Dask client or SparkContext
RDataFrame = ROOT.RDF.Experimental.Distributed.[Backend].RDataFrame
df = RDataFrame(..., [daskclient,sparkcontext] = connection)
```
to simply:
```
connection = ... # your distributed Dask client or SparkContext
df = ROOT.RDataFrame(..., executor = connection)
```
"""
)
warnings.warn(msg_warng, FutureWarning)
return super().__getattribute__(name)
def _raise_warning_if_experimental(func: Callable, experimental: bool):
"""A simple function decorator to raise a warning before usage."""

@functools.wraps(func)
def wrapper(*args, **kwargs):
if experimental:
msg_warng = textwrap.dedent(
"""
In ROOT 6.36, the ROOT.RDF.Experimental.Distributed module has become just ROOT.RDF.Distributed. In the
future, the 'Experimental' keyword will be removed, so it is suggested to move to the stable API in user
code. You can now change lines such as:
```
connection = ... # your distributed Dask client or SparkContext
RDataFrame = ROOT.RDF.Experimental.Distributed.[Backend].RDataFrame
df = RDataFrame(..., [daskclient,sparkcontext] = connection)
```
to simply:
```
connection = ... # your distributed Dask client or SparkContext
df = ROOT.RDataFrame(..., executor = connection)
```
"""
)
warnings.warn(msg_warng, FutureWarning, stacklevel=2)
return func(*args, **kwargs)

return wrapper


def create_distributed_module(parentmodule, experimental: bool = False):
Expand All @@ -249,21 +254,18 @@ def create_distributed_module(parentmodule, experimental: bool = False):
# distributed.__loader__ is not defined
distributed.__package__ = parentmodule

distributed = build_backends_submodules(distributed)
distributed = build_backends_submodules(distributed, experimental)

# Inject top-level functions
distributed.initialize = initialize
distributed.RunGraphs = RunGraphs
distributed.VariationsFor = VariationsFor
distributed.LiveVisualize = LiveVisualize
distributed.DistributeHeaders = DistributeHeaders
distributed.DistributeFiles = DistributeFiles
distributed.DistributeSharedLibs = DistributeSharedLibs
distributed.DistributeCppCode = DistributeCppCode
distributed.FromSpec = FromSpec

if experimental:
distributed.__class__ = _DeprecatedModule
distributed.initialize = _raise_warning_if_experimental(initialize, experimental)
distributed.RunGraphs = _raise_warning_if_experimental(RunGraphs, experimental)
distributed.VariationsFor = _raise_warning_if_experimental(VariationsFor, experimental)
distributed.LiveVisualize = _raise_warning_if_experimental(LiveVisualize, experimental)
distributed.DistributeHeaders = _raise_warning_if_experimental(DistributeHeaders, experimental)
distributed.DistributeFiles = _raise_warning_if_experimental(DistributeFiles, experimental)
distributed.DistributeSharedLibs = _raise_warning_if_experimental(DistributeSharedLibs, experimental)
distributed.DistributeCppCode = _raise_warning_if_experimental(DistributeCppCode, experimental)
distributed.FromSpec = _raise_warning_if_experimental(FromSpec, experimental)

return distributed

Expand Down
62 changes: 43 additions & 19 deletions roottest/python/distrdf/backends/check_explicit_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,25 @@ def test_redefine_one_column(self, payload):
class TestDeprecation:
"""Test the deprecation message regarding the 'Experimental' module"""

def test_warning_message(self, payload):
MODULE_WARN = textwrap.dedent(
"""
In ROOT 6.36, the ROOT.RDF.Experimental.Distributed module has become just ROOT.RDF.Distributed. In the
future, the 'Experimental' keyword will be removed, so it is suggested to move to the stable API in user
code. You can now change lines such as:
```
connection = ... # your distributed Dask client or SparkContext
RDataFrame = ROOT.RDF.Experimental.Distributed.[Backend].RDataFrame
df = RDataFrame(..., [daskclient,sparkcontext] = connection)
```
to simply:
```
connection = ... # your distributed Dask client or SparkContext
df = ROOT.RDataFrame(..., executor = connection)
```
"""
)

def test_experimental_module_deprecation(self, payload):
connection, backend = payload
with warnings.catch_warnings(record=True) as warninglist:
if backend == "dask":
Expand All @@ -101,26 +119,32 @@ def test_warning_message(self, payload):
RDataFrame = ROOT.RDF.Experimental.Distributed.Spark.RDataFrame
df = RDataFrame(10, npartitions=2, executor=connection)

msg_warng = textwrap.dedent(
"""
In ROOT 6.36, the ROOT.RDF.Experimental.Distributed module has become just ROOT.RDF.Distributed. In the
future, the 'Experimental' keyword will be removed, so it is suggested to move to the stable API in user
code. You can now change lines such as:
```
connection = ... # your distributed Dask client or SparkContext
RDataFrame = ROOT.RDF.Experimental.Distributed.[Backend].RDataFrame
df = RDataFrame(..., [daskclient,sparkcontext] = connection)
```
to simply:
```
connection = ... # your distributed Dask client or SparkContext
df = ROOT.RDataFrame(..., executor = connection)
```
"""
)
assert len(warninglist) == 1, f"{warninglist}"
assert issubclass(warninglist[0].category, FutureWarning)
assert str(warninglist[0].message == msg_warng)
assert str(warninglist[0].message == self.MODULE_WARN)

assert df.Count().GetValue() == 10

def test_backend_argument_deprecation(self, payload):
connection, backend = payload
with pytest.warns() as warninglist:
if backend == "dask":
RDataFrame = ROOT.RDF.Experimental.Distributed.Dask.RDataFrame
df = RDataFrame(10, npartitions=2, daskclient=connection)
elif backend == "spark":
RDataFrame = ROOT.RDF.Experimental.Distributed.Spark.RDataFrame
df = RDataFrame(10, npartitions=2, sparkcontext=connection)

keyword = "daskclient" if backend == "dask" else "sparkcontext"
keyword_warn = (
f"The keyword argument '{keyword}' is not necessary anymore and will be removed in a future release"
)

assert len(warninglist) == 2, f"{warninglist}"
assert issubclass(warninglist[0].category, FutureWarning)
assert str(warninglist[0].message == self.MODULE_WARN)
assert issubclass(warninglist[1].category, FutureWarning)
assert str(warninglist[1].message == keyword_warn)

assert df.Count().GetValue() == 10

Expand Down
Loading