Skip to content

Commit

Permalink
Merge pull request #4 from furkanonder/develop
Browse files Browse the repository at this point in the history
New updates for version 0.2.0
  • Loading branch information
furkanonder authored Sep 10, 2022
2 parents afa6b75 + c6a148a commit 96084e6
Show file tree
Hide file tree
Showing 13 changed files with 313 additions and 60 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Test

on: [push]

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
pyv: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v3

- uses: actions/setup-python@v3
with:
python-version: ${{ matrix.pyv }}

- name: Test
run: |
python -m unittest discover -v
50 changes: 34 additions & 16 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,25 +1,43 @@

repos:
- repo: https://github.com/psf/black
rev: 22.6.0
hooks:
- id: black
args: [--line-length=79]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
- repo: https://github.com/PyCQA/isort
rev: 5.10.1
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: isort
args: ["--profile", "black", "--filter-files"]

- repo: https://github.com/psf/black
rev: 22.3.0
- repo: https://github.com/hakancelikdev/unimport
rev: 0.10.0
hooks:
- id: black
- id: unimport
args: [--remove, --include-star-import, --ignore-init]

- repo: https://github.com/PyCQA/isort
rev: 5.9.2
- repo: https://github.com/PyCQA/docformatter
rev: v1.4
hooks:
- id: isort
- id: docformatter
args: [--in-place]

- repo: https://github.com/hakancelikdev/unimport
rev: 0.8.4
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.7.1
hooks:
- id: unimport
args: [--remove, --include-star-import]
- id: prettier
args: [--prose-wrap=always, --print-width=88]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: end-of-file-fixer
files: "\\.(py|.txt|.yaml|.json|.in|.md|.toml|.cfg|.html|.yml)$"

- repo: https://github.com/asottile/pyupgrade
rev: v2.37.3
hooks:
- id: pyupgrade
args: [--py36-plus]
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</div>

## Installation

_objerve_ can be installed by running `pip install objerve`

## Example Usage
Expand All @@ -24,16 +25,15 @@ class M:
self.baz = 121
```

To watch the changes, you need the add the ```@watch``` as a class decorator and ```watch_dict``` as a class variable.

```watch_dict``` keys should be selected from ```__init__``` or ```class``` variables and the values can be defined as a ```set```, ```get``` and ```del```.
To watch the changes, you need the add the `@watch()` as a class decorator. Within the
arguments of the `watch` decorator you should pass in lists for the keyword arguments of
the attributes you wish to watch.

```python
from objerve import watch

@watch
@watch(set={"foo", "qux"}, get={"bar", "foo"}, delete={"baz"})
class M:
watch_dict = {"foo": "set", "bar": "get", "baz": "del", "qux": "set"}
qux = "blue"

def __init__(self):
Expand All @@ -58,10 +58,13 @@ def get_foo(m):


abc()
m.foo
del m.baz
get_foo(m)
```

Output:

```sh
Set | foo = 89
File "/home/blue/objerve/examples/example.py", line 9, in __init__
Expand Down
4 changes: 2 additions & 2 deletions examples/example.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from objerve import watch


@watch
@watch(set={"foo", "qux"}, get={"bar", "foo"}, delete={"baz"})
class M:
watch_dict = {"foo": "set", "bar": "get", "baz": "del", "qux": "set"}
qux = "blue"

def __init__(self):
Expand All @@ -28,5 +27,6 @@ def get_foo(m):


abc()
m.foo
del m.baz
get_foo(m)
35 changes: 0 additions & 35 deletions objerve.py

This file was deleted.

1 change: 1 addition & 0 deletions objerve/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from objerve.objerve import Hook, watch
92 changes: 92 additions & 0 deletions objerve/color.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import sys

BLACK = "\x1b[30m"
RED = "\x1b[31m"
GREEN = "\x1b[32m"
YELLOW = "\x1b[33m"
BLUE = "\x1b[34m"
MAGENTA = "\x1b[35m"
CYAN = "\x1b[36m"
WHITE = "\x1b[37m"
RESET = "\x1b[0m"


def init_colors():
if sys.platform != "win32":
return True

try:
"""Terminal coloring for Windows is written with Windows Console API
Functions using the following resource.
https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
"""

from ctypes import POINTER, WINFUNCTYPE, WinError, windll
from ctypes.wintypes import BOOL, DWORD, HANDLE

ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
STD_OUTPUT_HANDLE = -11

def err_check(result, func, args) -> tuple:
"""This function is a helper for the error checking.
It is raises an exception when the API call failed.
"""
if not result:
raise WinError()
return args

def get_std_handle() -> WINFUNCTYPE:
"""GetStdHandle retrieves a handle to the specified standard device
(standard input, standard output, or standard error)."""
prototype = WINFUNCTYPE(HANDLE, DWORD)
paramflags = ((1, "nStdHandle"),)
function = prototype(("GetStdHandle", windll.kernel32), paramflags)
function.errcheck = err_check
return function

def get_console_mode() -> WINFUNCTYPE:
"""GetConsoleMode retrieves the current input mode of a console's
input buffer or the current output mode of a console screen
buffer."""
prototype = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))
paramflags = ((1, "hConsoleHandle"), (2, "lpMode"))
function = prototype(
("GetConsoleMode", windll.kernel32), paramflags
)
function.errcheck = err_check
return function

def set_console_mode() -> WINFUNCTYPE:
"""SetConsoleMode sets the input mode of a console's input buffer
or the output mode of a console screen buffer."""
prototype = WINFUNCTYPE(BOOL, HANDLE, DWORD)
paramflags = ((1, "hConsoleHandle"), (1, "dwMode"))
function = prototype(
("SetConsoleMode", windll.kernel32), paramflags
)
function.errcheck = err_check
return function

GetStdHandle = get_std_handle()
GetConsoleMode = get_console_mode()
SetConsoleMode = set_console_mode()

h_out = GetStdHandle(STD_OUTPUT_HANDLE)
dw_mode = GetConsoleMode(h_out) | ENABLE_VIRTUAL_TERMINAL_PROCESSING
SetConsoleMode(h_out, dw_mode)
except OSError:
return False
else:
return True


USE_COLOR = init_colors()


def set_color(color: str, text: str) -> str:
if USE_COLOR:
return f"{color}{text}{RESET}"
else:
return text
55 changes: 55 additions & 0 deletions objerve/objerve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import traceback
from collections import defaultdict

from objerve.color import CYAN, GREEN, YELLOW, set_color


class Hook:
def __init__(self, name, hooks, trace_limit):
self.public_name = name
self.private_name = f"_{name}"
self.hooks = hooks
self.trace_limit = trace_limit

def __set__(self, obj, value):
if "set" in self.hooks:
self.print_stack(CYAN, f"Set | {self.public_name} = {value}")
setattr(obj, self.private_name, value)

def __get__(self, obj, objtype=None):
if obj is None:
return self
else:
value = getattr(obj, self.private_name)
if "get" in self.hooks:
self.print_stack(GREEN, f"Get | {self.public_name} = {value}")
return value

def __delete__(self, instance):
if "delete" in self.hooks:
self.print_stack(YELLOW, f"Delete | {self.public_name}")
delattr(instance, self.private_name)

def print_stack(self, color, msg):
summary, *_ = traceback.extract_stack(limit=self.trace_limit)
print(
set_color(
color, f"{msg}\n{' '.join(traceback.format_list([summary]))}"
)
)


def watch(**kwargs):
attrs = defaultdict(list)
trace_limit = kwargs.pop("trace_limit", 3)

for hook in kwargs:
for attr in kwargs[hook]:
attrs[attr].append(hook)

def inner(cls):
for attr in attrs:
setattr(cls, attr, Hook(attr, attrs[attr], trace_limit))
return cls

return inner
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def get_long_description():

setup(
name="objerve",
version="0.1.0",
version="0.2.0",
long_description=get_long_description(),
long_description_content_type="text/markdown",
description="Tiny observer for the attributes of Python objects.",
Expand All @@ -23,7 +23,7 @@ def get_long_description():
url="https://github.com/furkanonder/objerve",
license="MIT",
python_requires=">=3.0",
py_modules=["objerve"],
packages=["objerve"],
install_requires=[],
extras_require={},
zip_safe=False,
Expand Down
Empty file added tests/__init__.py
Empty file.
Loading

0 comments on commit 96084e6

Please sign in to comment.