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

Merge pull request #1 from BTaskaya/v1

V1
# Catlizor # Catlizor (v1)
Yet another way to monitor your classes. Watch attribute accesses, method calls and hook actions to them. Action Hooks
## 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
## Example ## 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 ```py
{ from catlizor import Hook, Catlizor
'attribs': ['name', 'age'],
'hooks': { class TaskManager:
'post': [fancy_print] def __init__(self):
}, self.tasks = {}
'exclude': {
'post': ['del', 'set'] def add_task(self, task: str, *items):
} self.tasks[task] = items
},
``` def pop_task(self):
Our first watch operation will watch name and age attributes and after every get operation happens it will print which attribute accessed. return self.tasks.popitem()
```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
def personal_info(self): def get_tasks(self, task: str):
return f">{self.name} - {self.age}<" return self.tasks[task]
```
@Hook.pre
class PreLoggingHook(Hook):
methods = ['add_task']
callbacks = [lambda result: print(result.args, result.kwargs)]
Lets try this out @Hook.on_call
```py class PostLoggingHook(Hook):
me = Person("batuhan", 15) methods = ['pop_task', 'get_tasks']
me.name callbacks = [lambda result: print(result.result)]
me.age
me.personal_info 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: ...@@ -8,11 +8,11 @@ with open(current_dir / 'README.md', encoding='utf-8') as f:
setup( setup(
name = 'catlizor', name = 'catlizor',
version = '0.3', version = '1.0.0',
author = 'BTaskaya', author = 'BTaskaya',
author_email = 'batuhanosmantaskaya@gmail.com', author_email = 'batuhanosmantaskaya@gmail.com',
packages = ['catlizor'], packages = ['catlizor'],
description = "Watch python classes", description = "Action Hooks.",
long_description = long_description, long_description = long_description,
long_description_content_type = 'text/markdown', long_description_content_type = 'text/markdown',
url = 'https://github.com/btaskaya/catlizor' url = 'https://github.com/btaskaya/catlizor'
......
import unittest import unittest
from catlizor import catlizor from catlizor import Catlizor, Hook
class TestCatlizor(unittest.TestCase): class TaskManager:
def setUp(self): def __init__(self):
class MyClass: self.tasks = {}
def __init__(self, name, age=15):
self.name = name def add_task(self, task: str, *items):
self.age = age 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__': if __name__ == '__main__':
unittest.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