Skip to content

Commit

Permalink
✨ delete extra generic and improve the Accessor
Browse files Browse the repository at this point in the history
  • Loading branch information
H2Sxxa committed Apr 24, 2024
1 parent dbb088c commit 0ec0766
Show file tree
Hide file tree
Showing 14 changed files with 89 additions and 43 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ from typing import Any
from saleyo import MixinOperation, ToolChain
from saleyo.base.typing import M

class MyOperation(MixinOperation[Any, M]):
class MyOperation(MixinOperation[Any]):
def mixin(self, target: M, toolchain: ToolChain = ...) -> None:
...
```
Expand Down
2 changes: 1 addition & 1 deletion src/saleyo/base/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .typing import T, M


class MixinOperation(Generic[T, M], ABC):
class MixinOperation(Generic[T], ABC):
"""
The MixinOperation is the base of All Operation.
Expand Down
2 changes: 1 addition & 1 deletion src/saleyo/base/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def __init__(self, *args: P.args, **kwargs: P.kwargs) -> None:
self.kwargs = kwargs

def __str__(self) -> str:
return f"Arugument(positional: {self.args}, keyword: {self.kwargs} )"
return f"Arugument( positional: {self.args}, keyword: {self.kwargs} )"


class InvokeEvent(Generic[P, RT]):
Expand Down
3 changes: 0 additions & 3 deletions src/saleyo/base/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,8 @@
M = TypeVar("M", Type[Any], ModuleType, Any)
"""
These can be the target of mixin.
Not recommend input Any.
"""


# Alias

NameSpace = Dict[str, Any]
Expand Down
2 changes: 2 additions & 0 deletions src/saleyo/mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class Mixin(Generic[M]):
If the target is a special class, you should custom the toolchain yourself.
It is recommend to use `assert isinstance(self, <target>)` at the head of operation functions, although there may be some performance cost, but it is worth it in most conditions.
Allow to have more than one target, but that's not recommended.
"""

Expand Down
70 changes: 53 additions & 17 deletions src/saleyo/operation/accessor.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
from typing import Generic, Optional
from typing import Callable, Generic, Optional

from ..base.toolchain import ToolChain
from ..base.typing import T, M
from ..base.typing import P, T, M
from ..base.template import MixinOperation


class Accessor(Generic[T, M], MixinOperation[str, M]):
class Accessor(Generic[T], MixinOperation[str]):
"""
Want to access and modify some private varibles or methods? Try use `Accessor`!
The Generic is the type of target varible.
The Generic `T` is the type of target varible, if you want to visit a private function, try to use `FunctionAccessor`.
Notice: The value only available after invoking the `mixin` method.
If the `private` is `True`, will add target class name (like `_Foo`) to the prefix to argument.
If the `private` is `True`, will add target class name (like `_Foo`) to the prefix to argument, if the target is complex, you can set `private` to `False` and provide the true name by yourself.
If you use `@Mixin` and have more than one target classes, the `value` will always be the varible of latest target.
"""
Expand All @@ -26,14 +24,6 @@ def __init__(self, argument: str, level=1, private=True) -> None:
self._inner = None
self._private = private

@staticmethod
def configure(level: int = 1):
return lambda argument: Accessor(
argument=argument,
level=level,
private=True,
)

def mixin(self, target: M, toolchain: ToolChain = ToolChain()) -> None:
self._inner = toolchain.tool_getattr(
target,
Expand All @@ -46,8 +36,54 @@ def mixin(self, target: M, toolchain: ToolChain = ToolChain()) -> None:
)

@property
def value(self) -> Optional[T]:
def value(self) -> T:
"""
Will be None Call Before The `mixin` method call.
Don't use until The `mixin` method call.
"""
assert self._inner
return self._inner

@value.setter
def value(self, value: T):
self._inner = value

@value.deleter
def value(self):
# may cause some problems
del self.value

def __str__(self) -> str:
return f"Accessor {{ value: {self._inner} ({id(self._inner)}) }}"


class FunctionAccessor(Generic[P, T], MixinOperation[str]):
"""
`FunctionAccessor` can be call directly.
It's recommend to provide the Generic `P` and `T`, it can be useful in `__call__`.
If the `private` is `True`, will add target class name (like `_Foo`) to the prefix to argument, if the target is complex, you can set `private` to `False` and provide the true name by yourself.
If you just call in operation functions, you can just use a variable with `Callable[P, T]` type.
```python
something: FunctionAccessor[[str], None] = FunctionAccessor("something")
```
"""

_inner: Optional[Callable[P, T]] = None
_private: bool

def __init__(self, argument: str, level=1, private=True) -> None:
super().__init__(argument, level)
self._private = private

def mixin(self, target: M, toolchain: ToolChain = ToolChain()) -> None:
self._inner = toolchain.tool_getattr(
target,
f"_{target.__name__}{self.argument}" if self._private else self.argument,
)

def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
assert self._inner
return self._inner(*args, **kwargs)
4 changes: 2 additions & 2 deletions src/saleyo/operation/ancestor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from saleyo.base.typing import M


class Ancestor(MixinOperation[Type[Any], M]):
class Ancestor(MixinOperation[Type[Any]]):
"""
Ancestor will add the `argument` to `target.__bases__`.
Expand All @@ -30,7 +30,7 @@ def configure(
reverse=reverse,
)

def mixin(self, target: Type[Any], toolchain: ToolChain = ToolChain()) -> None:
def mixin(self, target: M, toolchain: ToolChain = ToolChain()) -> None:
return toolchain.tool_setattr(
target,
"__bases__",
Expand Down
4 changes: 2 additions & 2 deletions src/saleyo/operation/hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ..base.template import MixinOperation


class Post(MixinOperation[Callable[[T], Optional[RT]], M]):
class Post(MixinOperation[Callable[[T], Optional[RT]]]):
"""
`Post` will call after the target method, and the callable should be decorated as `@staticmethod` and have one argument to receive the result of target method.
Expand Down Expand Up @@ -50,7 +50,7 @@ def post(*args, **kwargs) -> Union[T, RT]:
return toolchain.tool_setattr(target, target_name, post)


class Pre(MixinOperation[Callable[P, Optional[Arguments[P]]], M]):
class Pre(MixinOperation[Callable[P, Optional[Arguments[P]]]]):
"""
`Pre` will call before the target method, and the callable should be decorated as `@staticmethod` and have `*args,**kwargs` to receive the arguments of target method.
Expand Down
4 changes: 2 additions & 2 deletions src/saleyo/operation/intercept.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
_B = InvokeEvent[_PB, Any]


class Intercept(Generic[_PA, _PB, M], MixinOperation[Callable[[_A[_PA]], _B[_PB]], M]):
class Intercept(Generic[_PA, _PB], MixinOperation[Callable[[_A[_PA]], _B[_PB]]]):
"""
The `Intercept` allow you to intercept the arguments before invoking target method.
Expand All @@ -34,7 +34,7 @@ def __init__(
def configure(
level: int = 1,
target_name: Optional[str] = None,
) -> Callable[[Callable[[_A[_PA]], _B[_PB]]], "Intercept[_PA, _PB, M]"]:
) -> Callable[[Callable[[_A[_PA]], _B[_PB]]], "Intercept[_PA, _PB]"]:
return lambda argument: Intercept(
argument=argument,
level=level,
Expand Down
9 changes: 4 additions & 5 deletions src/saleyo/operation/modify.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ..base.template import MixinOperation


class ReName(MixinOperation[str, Any]):
class ReName(MixinOperation[str]):
"""
Rename the target name.
"""
Expand All @@ -22,7 +22,7 @@ def mixin(self, target: M, toolchain: ToolChain = ToolChain()) -> None:
return toolchain.tool_setattr(target, self.new, old)


class Del(MixinOperation[str, M]):
class Del(MixinOperation[str]):
"""
Delete something named `argument` this from target
"""
Expand All @@ -31,7 +31,7 @@ def mixin(self, target: M, toolchain: ToolChain = ToolChain()) -> None:
return toolchain.tool_delattr(target, self.argument)


class Alias(MixinOperation[str, M]):
class Alias(MixinOperation[str]):
"""will copy the `argument` attribute to `alias`"""

alias: str
Expand All @@ -46,9 +46,8 @@ def mixin(self, target: M, toolchain: ToolChain = ToolChain()) -> None:
)


class Insert(MixinOperation[Any, M]):
class Insert(MixinOperation[Any]):
"""Will cover target when target exists."""

def mixin(self, target: M, toolchain: ToolChain = ToolChain()) -> None:
return toolchain.tool_setattr(target, self.argument.__name__, self.argument)

2 changes: 1 addition & 1 deletion src/saleyo/operation/overwrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ..base.template import MixinOperation


class OverWrite(MixinOperation[Callable, M]):
class OverWrite(MixinOperation[Callable]):
"""
OverWrite is rude and it will cover the target method.
Expand Down
2 changes: 1 addition & 1 deletion src/saleyo/operation/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from ..base.template import MixinOperation


class Processor(MixinOperation[Callable[[str], str], M]):
class Processor(MixinOperation[Callable[[str], str]]):
"""
If you want to get the soure code of a method and use `split` and `replace` to modify and redefine it,Try `Processor`.
Expand Down
24 changes: 18 additions & 6 deletions tests/misc_test.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
from saleyo import Mixin, Insert, ReName
from saleyo import Mixin, Insert
from saleyo.operation.accessor import Accessor, FunctionAccessor


class Foo:
def hello(self):
print("hello world")
__value: int = 1

def __increase_value(self) -> None:
self.__value += 1

def get_val(self):
return self.__value


@Mixin(target=Foo)
class MixinFoo:
goodbye = ReName("hello", "goodbye")
value_accessor: Accessor[int] = Accessor("__value")
increase_value: FunctionAccessor[[Foo], None] = FunctionAccessor("__increase_value")

@Insert
def helloworld(self): # type: ignore
self.goodbye()
def helloworld(self):
assert isinstance(self, Foo)
MixinFoo.value_accessor.value += 1
MixinFoo.increase_value(self)


foo: MixinFoo = Foo()
foo.helloworld()
assert isinstance(foo, Foo)
print(foo.get_val())
print(MixinFoo.value_accessor)
2 changes: 1 addition & 1 deletion tests/modify_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

Mixin(str, GCToolChain).apply_from_operations(alias)

print("Hello Saleyo!".do_upper())
print("Hello Saleyo!".do_upper()) # type: ignore
print("Hello Saleyo!".upper())

0 comments on commit 0ec0766

Please sign in to comment.