Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
c2009ed
docs: link to ML.EVALUATE BQML page for score() methods
ashleyxuu Oct 24, 2023
09ad5e4
feat: label query job with bigframes-api-xx using decorator
ashleyxuu Oct 25, 2023
4f4eb9b
reorganize the commit
ashleyxuu Oct 25, 2023
9ee937c
Merge branch 'main' into ashleyxu-add-api-methods
ashleyxuu Oct 26, 2023
272f0af
test: Log slowest tests durations (#146)
shobsi Oct 26, 2023
0e4c49c
docs: link to ML.EVALUATE BQML page for score() methods (#137)
ashleyxuu Oct 26, 2023
aad2c1a
feat: populate ibis version in user agent (#140)
ashleyxuu Oct 26, 2023
1043d6d
fix: don't override the global logging config (#138)
tswast Oct 26, 2023
1f49ef9
fix: use indexee's session for loc listlike cases (#152)
milkshakeiii Oct 26, 2023
c4c1e6e
feat: add pandas.qcut (#104)
TrevorBergeron Oct 26, 2023
4a27f44
feat: add unstack to series, add level param (#115)
TrevorBergeron Oct 26, 2023
fface57
feat: add `DataFrame.to_pandas_batches()` to download large `DataFram…
tswast Oct 26, 2023
bbc3c69
fix: resolve plotly rendering issue by using ipython html for job pro…
orrbradford Oct 26, 2023
a99d62c
refactor: ArrayValue is now a tree that defers conversion to ibis (#110)
TrevorBergeron Oct 27, 2023
f37d0b0
fix: fix bug with column names under repeated column assignment (#150)
milkshakeiii Oct 27, 2023
aba301c
test: refactor remote function tests (#147)
shobsi Oct 27, 2023
53bb2cd
feat: add dataframe melt (#116)
TrevorBergeron Oct 28, 2023
2bf4bcc
docs: add artithmetic df sample code (#153)
ashleyxuu Oct 30, 2023
343414a
feat: Implement operator `@` for `DataFrame.dot` (#139)
shobsi Oct 30, 2023
4eac10d
fix: fix typo and address comments
ashleyxuu Oct 30, 2023
868d2ad
Merge branch 'main' into ashleyxu-add-api-methods
ashleyxuu Oct 30, 2023
c03a8d9
Merge branch 'main' into ashleyxu-add-api-methods
tswast Nov 2, 2023
39321e4
fix: address comments
ashleyxuu Nov 3, 2023
aebcf11
Remove utils folder and refactor it in core directory
ashleyxuu Nov 3, 2023
72217c2
Merge branch 'main' into ashleyxu-add-api-methods
ashleyxuu Nov 3, 2023
ec526b5
Remove utils folder and refactor it in core directory
ashleyxuu Nov 3, 2023
9edfe31
Merge remote-tracking branch 'origin/ashleyxu-add-api-methods' into a…
ashleyxuu Nov 3, 2023
4baa373
Merge branch 'main' into ashleyxu-add-api-methods
ashleyxuu Nov 3, 2023
3a94c23
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Nov 3, 2023
d84c569
fix merge conflicts
ashleyxuu Nov 3, 2023
308c9a7
Merge remote-tracking branch 'origin/ashleyxu-add-api-methods' into a…
ashleyxuu Nov 3, 2023
4618107
commit the conflicts
ashleyxuu Nov 13, 2023
a87bcb8
redesign the log adapter
ashleyxuu Nov 14, 2023
cf97f8b
resolve conflicts and merge remote-tracking branch 'origin/main' into…
ashleyxuu Nov 14, 2023
53a99f9
Make the global _api_methods and lock threads
ashleyxuu Nov 14, 2023
3cc3599
Merge branch 'main' into ashleyxu-add-api-methods
ashleyxuu Nov 14, 2023
1c3deb5
Make the global _api_methods and lock threads
ashleyxuu Nov 14, 2023
99f423b
merge conflicts
ashleyxuu Nov 14, 2023
115de27
address comments
ashleyxuu Nov 14, 2023
b0adf27
address comments
ashleyxuu Nov 14, 2023
b4ea9e3
Merge remote-tracking branch 'origin/ashleyxu-add-api-methods' into a…
ashleyxuu Nov 14, 2023
df9c9c0
fix error
ashleyxuu Nov 14, 2023
00bb6de
fix None job_config error
ashleyxuu Nov 14, 2023
36fea06
address comments
ashleyxuu Nov 14, 2023
e872d18
Merge branch 'main' into ashleyxu-add-api-methods
ashleyxuu Nov 14, 2023
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
Prev Previous commit
Next Next commit
Make the global _api_methods and lock threads
  • Loading branch information
ashleyxuu committed Nov 14, 2023
commit 53a99f96170b68bb39805189344138b19118fa07
42 changes: 16 additions & 26 deletions bigframes/core/log_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,30 @@

import functools
import threading
from typing import List

_lock = threading.Lock()
MAX_LABELS_COUNT = 64
_api_methods: List = []


def class_logger(api_methods=None):
def class_logger(decorated_cls):
"""Decorator that adds logging functionality to each method of the class."""
for attr_name, attr_value in decorated_cls.__dict__.items():
if callable(attr_value):
setattr(decorated_cls, attr_name, method_logger(attr_value))
return decorated_cls

def decorator(decorated_cls):
for attr_name, attr_value in decorated_cls.__dict__.items():

if callable(attr_value):
setattr(
decorated_cls, attr_name, method_logger(attr_value, decorated_cls)
)

# Initialize or extend _api_methods attribute
decorated_cls._api_methods = getattr(decorated_cls, "_api_methods", [])
if api_methods:
decorated_cls._api_methods.extend(api_methods)

return decorated_cls

return decorator


def method_logger(method, cls):
def method_logger(method):
"""Decorator that adds logging functionality to a method."""

@functools.wraps(method)
def wrapper(*args, **kwargs):
api_method_name = str(method.__name__)
# Track regular and "dunder" methods
if api_method_name.startswith("__") or not api_method_name.startswith("_"):
add_api_method(api_method_name, cls)
add_api_method(api_method_name)
try:
result = method(*args, **kwargs)
return result
Expand All @@ -58,18 +47,19 @@ def wrapper(*args, **kwargs):
return wrapper


def add_api_method(api_method_name, cls):
def add_api_method(api_method_name):
global _lock
global _api_methods
with _lock:
# Push the method to the front of the _api_methods list
cls._api_methods.insert(0, api_method_name)
_api_methods.insert(0, api_method_name)
# Keep the list length within the maximum limit (adjust MAX_LABELS_COUNT as needed)
cls._api_methods = cls._api_methods[:MAX_LABELS_COUNT]
_api_methods = _api_methods[:MAX_LABELS_COUNT]


def get_and_reset_api_methods(cls):
def get_and_reset_api_methods():
global _lock
with _lock:
previous_api_methods = list(cls._api_methods)
cls._api_methods.clear()
previous_api_methods = list(_api_methods)
_api_methods.clear()
return previous_api_methods
3 changes: 1 addition & 2 deletions bigframes/session/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ def _is_query(query_or_table: str) -> bool:
return re.search(r"\s", query_or_table.strip(), re.MULTILINE) is not None


@log_adapter.class_logger
class Session(
third_party_pandas_gbq.GBQIOMixin,
third_party_pandas_parquet.ParquetIOMixin,
Expand Down Expand Up @@ -1387,7 +1386,7 @@ def _start_query(
"""
Starts query job and waits for results.
"""
api_methods = log_adapter.get_and_reset_api_methods(self)
api_methods = log_adapter.get_and_reset_api_methods()
job_config = self._prepare_job_config(job_config)
job_config.labels = bigframes_io.create_job_configs_labels(
job_configs_labels=job_config.labels, api_methods=api_methods
Expand Down
25 changes: 11 additions & 14 deletions tests/unit/core/test_log_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
@pytest.fixture
def test_instance():
# Create a simple class for testing
@log_adapter.class_logger()
@log_adapter.class_logger
class TestClass:
def method1(self):
pass
Expand All @@ -19,29 +19,26 @@ def method2(self):
return TestClass()


def test_class_logger_decorator(test_instance):
# Ensure that the class logger decorator adds _api_methods attribute
assert hasattr(test_instance, "_api_methods")
assert test_instance._api_methods == []

def test_method_logging(test_instance):
test_instance.method1()
test_instance.method2()

def test_add_api_method(test_instance):
# Ensure that add_api_method correctly adds a method to _api_methods
log_adapter.add_api_method("method3", test_instance)
assert test_instance._api_methods == ["method3"]
# Check if the methods were added to the _api_methods list
api_methods = log_adapter.get_and_reset_api_methods()
assert api_methods == ["method2", "method1"]


def test_add_api_method_limit(test_instance):
# Ensure that add_api_method correctly adds a method to _api_methods
for i in range(70):
log_adapter.add_api_method("method3", test_instance)
assert len(test_instance._api_methods) == MAX_LABELS_COUNT
test_instance.method2()
assert len(log_adapter._api_methods) == MAX_LABELS_COUNT


def test_get_and_reset_api_methods(test_instance):
# Ensure that get_and_reset_api_methods returns a copy and resets the list
test_instance.method1()
test_instance.method2()
previous_methods = log_adapter.get_and_reset_api_methods(test_instance)
previous_methods = log_adapter.get_and_reset_api_methods()
assert previous_methods == ["method2", "method1"]
assert test_instance._api_methods == []
assert log_adapter._api_methods == []
55 changes: 53 additions & 2 deletions tests/unit/session/test_io_bigquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import pytest

import bigframes
from bigframes.core import log_adapter
import bigframes.pandas as bpd
import bigframes.session._io.bigquery as io_bq


Expand Down Expand Up @@ -56,6 +58,50 @@ def test_create_job_configs_labels_length_limit_not_met():
assert labels == expected_dict


def test_create_job_configs_labels_log_adaptor_call_method_under_length_limit():
cur_labels = {
"bigframes-api": "read_pandas",
"source": "bigquery-dataframes-temp",
}
df = bpd.DataFrame({"col1": [1, 2], "col2": [3, 4]})
# Test running two methods
df.head()
df.max()
api_methods = log_adapter._api_methods

labels = io_bq.create_job_configs_labels(
job_configs_labels=cur_labels, api_methods=api_methods
)
expected_dict = {
"bigframes-api": "read_pandas",
"source": "bigquery-dataframes-temp",
"recent-bigframes-api-0": "__init__",
"recent-bigframes-api-1": "max",
"recent-bigframes-api-2": "__init__",
"recent-bigframes-api-3": "head",
"recent-bigframes-api-4": "__init__",
}
assert labels is not None
assert len(labels) == 7
assert labels == expected_dict


def test_create_job_configs_labels_length_limit_met_and_labels_is_none():
df = bpd.DataFrame({"col1": [1, 2], "col2": [3, 4]})
# Test running methods more than the labels' length limit
for i in range(66):
df.head()
api_methods = log_adapter._api_methods

labels = io_bq.create_job_configs_labels(
job_configs_labels=None, api_methods=api_methods
)
assert labels is not None
assert len(labels) == 64
assert "head" in labels.values()
assert "__init__" in labels.values()


def test_create_job_configs_labels_length_limit_met():
cur_labels = {
"bigframes-api": "read_pandas",
Expand All @@ -66,14 +112,19 @@ def test_create_job_configs_labels_length_limit_met():
value = f"test{i}"
cur_labels[key] = value
# If cur_labels length is 62, we can only add one label from api_methods
api_methods = ["agg", "series-mode", "head"]
df = bpd.DataFrame({"col1": [1, 2], "col2": [3, 4]})
# Test running two methods
df.head()
df.max()
api_methods = log_adapter._api_methods

labels = io_bq.create_job_configs_labels(
job_configs_labels=cur_labels, api_methods=api_methods
)
assert labels is not None
assert len(labels) == 64
assert "agg" in labels.values()
assert "max" in labels.values()
assert "__init__" in labels.values()
assert "head" not in labels.values()
assert "bigframes-api" in labels.keys()
assert "source" in labels.keys()
Expand Down