Skip to content

Commit dc54f58

Browse files
authored
chore: allow method_logger to directly decorate functions with param in parentheses (#2065)
* chore: allow method_logger to directly decorate functions with params in parentheses * fix format
1 parent 913de1b commit dc54f58

File tree

2 files changed

+62
-39
lines changed

2 files changed

+62
-39
lines changed

bigframes/core/log_adapter.py

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -149,49 +149,61 @@ def wrap(cls):
149149
return wrap(decorated_cls)
150150

151151

152-
def method_logger(method, /, *, custom_base_name: Optional[str] = None):
152+
def method_logger(method=None, /, *, custom_base_name: Optional[str] = None):
153153
"""Decorator that adds logging functionality to a method."""
154154

155-
@functools.wraps(method)
156-
def wrapper(*args, **kwargs):
157-
api_method_name = getattr(method, LOG_OVERRIDE_NAME, method.__name__)
158-
if custom_base_name is None:
159-
qualname_parts = getattr(method, "__qualname__", method.__name__).split(".")
160-
class_name = qualname_parts[-2] if len(qualname_parts) > 1 else ""
161-
base_name = (
162-
class_name if class_name else "_".join(method.__module__.split(".")[1:])
163-
)
164-
else:
165-
base_name = custom_base_name
166-
167-
full_method_name = f"{base_name.lower()}-{api_method_name}"
168-
# Track directly called methods
169-
if len(_call_stack) == 0:
170-
add_api_method(full_method_name)
171-
172-
_call_stack.append(full_method_name)
173-
174-
try:
175-
return method(*args, **kwargs)
176-
except (NotImplementedError, TypeError) as e:
177-
# Log method parameters that are implemented in pandas but either missing (TypeError)
178-
# or not fully supported (NotImplementedError) in BigFrames.
179-
# Logging is currently supported only when we can access the bqclient through
180-
# _block.session.bqclient.
181-
if len(_call_stack) == 1:
182-
submit_pandas_labels(
183-
_get_bq_client(*args, **kwargs),
184-
base_name,
185-
api_method_name,
186-
args,
187-
kwargs,
188-
task=PANDAS_PARAM_TRACKING_TASK,
155+
def outer_wrapper(method):
156+
@functools.wraps(method)
157+
def wrapper(*args, **kwargs):
158+
api_method_name = getattr(method, LOG_OVERRIDE_NAME, method.__name__)
159+
if custom_base_name is None:
160+
qualname_parts = getattr(method, "__qualname__", method.__name__).split(
161+
"."
162+
)
163+
class_name = qualname_parts[-2] if len(qualname_parts) > 1 else ""
164+
base_name = (
165+
class_name
166+
if class_name
167+
else "_".join(method.__module__.split(".")[1:])
189168
)
190-
raise e
191-
finally:
192-
_call_stack.pop()
169+
else:
170+
base_name = custom_base_name
193171

194-
return wrapper
172+
full_method_name = f"{base_name.lower()}-{api_method_name}"
173+
# Track directly called methods
174+
if len(_call_stack) == 0:
175+
add_api_method(full_method_name)
176+
177+
_call_stack.append(full_method_name)
178+
179+
try:
180+
return method(*args, **kwargs)
181+
except (NotImplementedError, TypeError) as e:
182+
# Log method parameters that are implemented in pandas but either missing (TypeError)
183+
# or not fully supported (NotImplementedError) in BigFrames.
184+
# Logging is currently supported only when we can access the bqclient through
185+
# _block.session.bqclient.
186+
if len(_call_stack) == 1:
187+
submit_pandas_labels(
188+
_get_bq_client(*args, **kwargs),
189+
base_name,
190+
api_method_name,
191+
args,
192+
kwargs,
193+
task=PANDAS_PARAM_TRACKING_TASK,
194+
)
195+
raise e
196+
finally:
197+
_call_stack.pop()
198+
199+
return wrapper
200+
201+
if method is None:
202+
# Called with parentheses
203+
return outer_wrapper
204+
205+
# Called without parentheses
206+
return outer_wrapper(method)
195207

196208

197209
def property_logger(prop):

tests/unit/core/test_log_adapter.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,17 @@ def test_method_logging_with_custom_base_name(test_method_w_custom_base):
101101
assert "pandas-method1" in api_methods
102102

103103

104+
def test_method_logging_with_custom_base__logger_as_decorator():
105+
@log_adapter.method_logger(custom_base_name="pandas")
106+
def my_method():
107+
pass
108+
109+
my_method()
110+
111+
api_methods = log_adapter.get_and_reset_api_methods()
112+
assert "pandas-my_method" in api_methods
113+
114+
104115
def test_property_logging(test_instance):
105116
test_instance.my_field
106117

0 commit comments

Comments
 (0)