Skip to content

Commit 8bc4e8f

Browse files
authored
Merge pull request #30 from pyjanitor-devs/issue29-pr
documentation of method_call_ctx_factory
2 parents c60bfd4 + 3b9fe76 commit 8bc4e8f

File tree

3 files changed

+89
-0
lines changed

3 files changed

+89
-0
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ df.row_by_value('x', 10)
121121
# Name: 0, dtype: int64
122122
```
123123

124+
## Registered methods tracing
125+
126+
The pandas_flavor 0.5.0 release introduced [tracing of the registered method calls](/docs/tracing_ext.md). Now it is possible to add additional run-time logic around registered method execution which can be used for some support tasks. This extension was introduced
127+
to allow visualization of [pyjanitor](https://github.com/pyjanitor-devs/pyjanitor) method chains as implemented in [pyjviz](https://github.com/pyjanitor-devs/pyjviz)
128+
129+
124130
## Available Methods
125131

126132
- **register_dataframe_method**: register a method directly with a pandas DataFrame.

docs/tracing_ext-demo.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import pandas as pd
2+
import pandas_flavor as pf
3+
import time
4+
5+
6+
@pf.register_dataframe_method
7+
def my_method(df: pd.DataFrame) -> pd.DataFrame:
8+
print("my_method called")
9+
return df.transpose()
10+
11+
12+
@pf.register_dataframe_method
13+
def another_method(df: pd.DataFrame, new_col_d) -> pd.DataFrame:
14+
print("another_method called")
15+
for col, v in new_col_d.items():
16+
df[col] = v
17+
return df
18+
19+
20+
class tracer:
21+
@staticmethod
22+
def create_tracer(*args):
23+
return tracer()
24+
25+
def __init__(self):
26+
self.method_name = None
27+
self.start_ts = None
28+
self.end_ts = None
29+
30+
def __enter__(self):
31+
return self
32+
33+
def handle_start_method_call(
34+
self, method_name, method_signature, method_args, method_kwagrs
35+
):
36+
self.method_name = method_name
37+
self.start_ts = time.time()
38+
return method_args, method_kwagrs
39+
40+
def handle_end_method_call(self, ret):
41+
self.end_ts = time.time()
42+
43+
def __exit__(self, exc_type, value, traceback):
44+
call_dur = self.end_ts - self.start_ts
45+
print(f"method {self.method_name} took {call_dur} secs to execute")
46+
47+
48+
pf.register.method_call_ctx_factory = tracer.create_tracer
49+
s_df = pd.DataFrame([[i + j for i in range(10)] for j in range(10)])
50+
res_df = s_df.my_method().another_method({"new_col": "new value"})
51+
print(res_df)

docs/tracing_ext.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# method_call_ctx_factory - tracing extention of pandas_flavor
2+
3+
`method_call_ctx_factory` global var defined in [pandas_flavor/register.py](https://github.com/pyjanitor-devs/pandas_flavor/blob/c60bfd43adbcc304b3455055af73ed9fc9ac10d1/pandas_flavor/register.py#L8) is used
4+
to allow the methods registered via `pandas_flavors` to be traced.
5+
6+
Default value of `method_call_ctx_factory` is None.
7+
8+
Starting version 0.5.0 `pandas_flavor` implements the way to pass registered method name, signature and parameters to
9+
be handled by user-defined object when the call is made. To allow this the user of pandas_flavor must set
10+
`method_call_ctx_factory` to refer to function with signature `(method_name: str, method_args: list, method_kwargs: dict) -> tracing_ctx`.
11+
`tracing_ctx` should be class which implements methods with signatures as below:
12+
13+
```python
14+
class tracing_ctx:
15+
def __enter__(self) -> None: pass
16+
def __exit__(self, type, value, traceback): -> None: pass
17+
def handle_start_method_call(self, method_name: str,
18+
method_signature: inspect.Signature,
19+
method_args: list,
20+
method_kwargs: dict) -> (list, dict): pass
21+
def handle_end_method_call(self, method_ret: object) -> None: pass
22+
```
23+
24+
During method call `pandas_flavor` will create object of class `tracing_ctx` then will use that object to enter *with* code block.
25+
`handle_start_method_call` and `handle_end_method_call` will be called before and after actual method call. The input arguments
26+
and return object of actual method call will be passed to corresponding `tracing_ctx` method.
27+
28+
So `handle_start_method_call` and `handle_end_method_call` will have the chance to implement required tracing logic.
29+
Aslo, __exit__ method will be called if any exception would occur. This way it is possible to handle the situation of
30+
actual method ends by raising exception.
31+
32+
The example of tracer class implementation and factory function registration is given in [tracing_ext-demo.py](/docs/tracing_ext-demo.py)

0 commit comments

Comments
 (0)