Unverified Kaydet (Commit) 3c768933 authored tarafından BTaskaya's avatar BTaskaya Kaydeden (comit) GitHub

Merge pull request #1 from BTaskaya/v1

V1
# Catlizor
Yet another way to monitor your classes. Watch attribute accesses, method calls and hook actions to them.
## Installation
> Python 3.7+
```
$ pip install catlizor
```
## Usage
`catlizor` is a class decorator that takes your class and modifies it with your specifications. You give the class and the options.
### Options
It is the configuration part of catlizor. You can define your options as python dictionary and then unpack it.
```py
opts = {...}
@catlizor(**opts)
class MyClass:
...
```
Catlizor currently accepting 3 options:
- watch > `Sequence[Dict]`
- sign > `bool`
- reset > `bool`
#### Watch
Watch option can take a sequence of monitoring specification.
```
[
{
'attribs': ['name', 'age'],
'funcs': ['person_info'],
'hooks': {
'pre': [...],
'value': [...],
'post': [...],
}
'exclude': {
'value': ['del', 'set'],
}
}
]
```
#### Monitoring Specification
It consists from;
- attribs > `Sequence[Union[str, Ellipsis]]`
- funcs > `Sequence[Union[str, Ellipsis]]`
- hooks > `Dict[str, Sequence[Callable]]`
- exclude > `Dict[str, Sequence[str]`
##### Attribs
Attributes specifies which attributes of an class you are going to watch. By default catlizor watch all get/set/del operations (you can use exclude option to specify operations).
> If you want to watch all attributes you can use `...` (a.k.a Ellipsis)
##### Funcs
Functions specifies functions to watch.
> If you want to watch all attributes you can use `...` (a.k.a Ellipsis)
##### Hooks
Hooks for operations, by default there are 3 hooks;
- pre
- value (called at call-time, also returns value of result as a keyword argument `value`)
- post
##### Exclude
Exclude operations for attribute access, by default there are 3 operations;
- get
- set
- delete
# Catlizor (v1)
Action Hooks
## Example
Lets import our class decorator
```py
from catlizor import catlizor
```
Then create a configuration to our monitoring setup
```py
def fancy_print(name, *args, **kwargs):
print(f"Access to {args[1]}")
options = {
```
Lets setup up a watching option
```py
'watch': [
```
it will contain a series of watches
```py
{
'attribs': ['name', 'age'],
'hooks': {
'post': [fancy_print]
},
'exclude': {
'post': ['del', 'set']
}
},
```
Our first watch operation will watch name and age attributes and after every get operation happens it will print which attribute accessed.
```py
{
'funcs': ['personal_info'],
'hooks': {
'value': [lambda *a, **k: print(f"Personal info acquired, {k['value']}")]
}
}
]
}
```
The second watch will watch personal_info method and whenever this method called, it print the value. Then create our class
```py
@catlizor(**options)
class Person:
def __init__(self, name, age):
self.age = age
self.name = name
from catlizor import Hook, Catlizor
class TaskManager:
def __init__(self):
self.tasks = {}
def add_task(self, task: str, *items):
self.tasks[task] = items
def pop_task(self):
return self.tasks.popitem()
def personal_info(self):
return f">{self.name} - {self.age}<"
```
def get_tasks(self, task: str):
return self.tasks[task]
@Hook.pre
class PreLoggingHook(Hook):
methods = ['add_task']
callbacks = [lambda result: print(result.args, result.kwargs)]
Lets try this out
```py
me = Person("batuhan", 15)
me.name
me.age
me.personal_info
@Hook.on_call
class PostLoggingHook(Hook):
methods = ['pop_task', 'get_tasks']
callbacks = [lambda result: print(result.result)]
tm_catlizor = Catlizor.hook(TaskManager, PreLoggingHook, PostLoggingHook)
tm = TaskManager()
tm.add_task("süt al", "markete git", "süt reyonuna ulaş")
tm.get_tasks("süt al")
tm.pop_task()
```
Lets check our standard output
Result (stdout);
```
(<__main__.TaskManager object at 0x7fa851743748>, 'süt al', 'markete git', 'süt reyonuna ulaş') {}
('markete git', 'süt reyonuna ulaş')
('süt al', ('markete git', 'süt reyonuna ulaş'))
```
Access to name
Access to age
Access to name
Access to age
Personal info acquired, >batuhan - 15<
```
from catlizor.catlizor import catlizor
from catlizor.catlizor import Catlizor, Hook, CallbackStop
This diff is collapsed.
from catlizor import catlizor
def fancy_print(name, *args, **kwargs):
print(f"Access to {args[1]}")
options = {
'watch': [
{
'attribs': ['name', 'age'],
'hooks': {
'post': [fancy_print]
},
'exclude': {
'post': ['del', 'set']
}
},
{
'funcs': ['personal_info'],
'hooks': {
'value': [lambda *a, **k: print(f"Personal info acquired, {k['value']}")]
}
}
]
}
@catlizor(**options)
class Person:
def __init__(self, name, age):
self.age = age
self.name = name
def personal_info(self):
return f">{self.name} - {self.age}<"
me = Person("batuhan", 15)
me.name
me.age
me.personal_info()
from catlizor import Hook, Catlizor
class TaskManager:
def __init__(self):
self.tasks = {}
def add_task(self, task: str, *items):
self.tasks[task] = items
def pop_task(self):
return self.tasks.popitem()
def get_tasks(self, task: str):
return self.tasks[task]
@Hook.pre
class PreLoggingHook(Hook):
methods = ['add_task']
callbacks = [lambda result: print(result.args, result.kwargs)]
@Hook.on_call
class PostLoggingHook(Hook):
methods = ['pop_task', 'get_tasks']
callbacks = [lambda result: print(result.result)]
tm_catlizor = Catlizor.hook(TaskManager, PreLoggingHook, PostLoggingHook)
tm = TaskManager()
tm.add_task("süt al", "markete git", "süt reyonuna ulaş")
tm.get_tasks("süt al")
tm.pop_task()
tm_catlizor.reset()
tm = TaskManager()
tm.add_task("abc", 1, 2, 3)
from catlizor import Hook, Catlizor, CallbackStop
class TaskManager:
def __init__(self):
self.tasks = {}
def add_task(self, task: str, *items):
self.tasks[task] = items
def pop_task(self):
return self.tasks.popitem()
def get_tasks(self, task: str):
return self.tasks[task]
@Hook.pre
class GoMarketHooks(Hook):
def market_check(result):
if not any(filter(lambda arg: isinstance(arg, str) and 'market' in arg, result.args)):
raise CallbackStop
def market_print(result):
print('Market action !!!')
def other_market_actions(result):
return NotImplemented
methods = ['add_task']
callbacks = [market_check, market_print, other_market_actions]
tm_catlizor = Catlizor.hook(TaskManager, GoMarketHooks)
tm = TaskManager()
tm.add_task('not a m!rket task')
tm.add_task('go to market', 'buy milk')
tm.add_task('back to the home', 'fastly')
tm.add_task('go market again', 'buy eggs')
......@@ -8,11 +8,11 @@ with open(current_dir / 'README.md', encoding='utf-8') as f:
setup(
name = 'catlizor',
version = '0.3',
version = '1.0.0',
author = 'BTaskaya',
author_email = 'batuhanosmantaskaya@gmail.com',
packages = ['catlizor'],
description = "Watch python classes",
description = "Action Hooks.",
long_description = long_description,
long_description_content_type = 'text/markdown',
url = 'https://github.com/btaskaya/catlizor'
......
import unittest
from catlizor import catlizor
from catlizor import Catlizor, Hook
class TestCatlizor(unittest.TestCase):
def setUp(self):
class MyClass:
def __init__(self, name, age=15):
self.name = name
self.age = age
class TaskManager:
def __init__(self):
self.tasks = {}
def add_task(self, task: str, *items):
self.tasks[task] = items
def pop_task(self):
return self.tasks.popitem()
def get_tasks(self, task: str):
return self.tasks[task]
@Hook.pre
class PreHook(Hook):
methods = ['add_task']
callbacks = []
self.cls = MyClass
@Hook.on_call
class OnCallHook(Hook):
methods = ['get_tasks']
callbacks = []
@Hook.post
class PostHook(Hook):
methods = ['pop_task']
callbacks = []
class TestCatlizor(unittest.TestCase):
def test_catlizor(self):
results = []
def callback(result):
nonlocal results
if result.result is not None:
results.append(result.result)
else:
results.append(result.args)
PreHook.callbacks = [callback]
OnCallHook.callbacks = [callback]
PostHook.callbacks = [callback]
PreHook.update_hookspec()
OnCallHook.update_hookspec()
PostHook.update_hookspec()
tm_catlizor = Catlizor.hook(TaskManager, PreHook, OnCallHook, PostHook)
tm = TaskManager()
tm.add_task("a", 1, 2)
tm.get_tasks("a")
tm.pop_task()
self.assertEqual(results, [(tm, 'a', 1, 2), (1,2), {}])
if __name__ == '__main__':
unittest.main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment