diff --git a/CHANGELOG.md b/CHANGELOG.md index 961edb6..c447d70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ # Changelog +## Pedantic 2.1.7 +- add `transformation` parameter to `create_decorator` to make adding custom behavior easier + ## Pedantic 2.1.6 - Remove `inspect.getsource()` call from `@overrides` diff --git a/docs/pedantic/mixins/with_decorated_methods.html b/docs/pedantic/mixins/with_decorated_methods.html index 7d74b0d..123c874 100644 --- a/docs/pedantic/mixins/with_decorated_methods.html +++ b/docs/pedantic/mixins/with_decorated_methods.html @@ -50,13 +50,23 @@

Module pedantic.mixins.with_decorated_methods

Module pedantic.mixins.with_decorated_methodsFunctions
-def create_decorator(decorator_type: DecoratorType) ‑> Callable[[~T], Callable[[~C], ~C]] +def create_decorator(decorator_type: DecoratorType, transformation: Callable[[~C], ~C] = None) ‑> Callable[[~T], Callable[[~C], ~C]]
-

Creates a new decorator that is parametrized with one argument of an arbitrary type.

+

Creates a new decorator that is parametrized with one argument of an arbitrary type. +You can also pass an arbitrary [transformation] to add custom behavior to the decorator.

Expand source code -
def create_decorator(decorator_type: DecoratorType) -> Callable[[T], Callable[[C], C]]:
-    """ Creates a new decorator that is parametrized with one argument of an arbitrary type. """
+
def create_decorator(
+    decorator_type: DecoratorType,
+    transformation: Callable[[C], C] = None,
+) -> Callable[[T], Callable[[C], C]]:
+    """
+    Creates a new decorator that is parametrized with one argument of an arbitrary type.
+    You can also pass an arbitrary [transformation] to add custom behavior to the decorator.
+    """
 
     def decorator(value: T) -> Callable[[C], C]:
         def fun(f: C) -> C:
             setattr(f, decorator_type, value)
-            return f
+
+            if transformation is None:
+                return f
+
+            return transformation(f)
 
         return fun  # we do not need functools.wraps, because we return the original function here
 
diff --git a/docs/pedantic/tests/test_with_decorated_methods.html b/docs/pedantic/tests/test_with_decorated_methods.html
index abb7340..9427f71 100644
--- a/docs/pedantic/tests/test_with_decorated_methods.html
+++ b/docs/pedantic/tests/test_with_decorated_methods.html
@@ -27,6 +27,7 @@ 

Module pedantic.tests.test_with_decorated_methods Expand source code
import unittest
+from functools import wraps
 
 from pedantic import DecoratorType, create_decorator, WithDecoratedMethods
 
@@ -91,7 +92,35 @@ 

Module pedantic.tests.test_with_decorated_methods instance.m3: 44, } } - assert instance.get_decorated_functions() == expected

+ assert instance.get_decorated_functions() == expected + + + def test_with_custom_transformation(self): + def my_transformation(f): + @wraps(f) + def wrapper(*args, **kwargs): + f(*args, **kwargs) + return 4422 # we add a return value + + return wrapper + + my_decorator = create_decorator(decorator_type=Decorators.BAR, transformation=my_transformation) + + class MyClass(WithDecoratedMethods[Decorators]): + @my_decorator(42) + def m1(self) -> int: + return 1 + + instance = MyClass() + expected = { + Decorators.BAR: { + instance.m1: 42, + }, + Decorators.FOO: {}, + } + assert instance.get_decorated_functions() == expected + + assert instance.m1() == 4422 # check that transformation was applied

@@ -231,7 +260,35 @@

Class variables

instance.m3: 44, } } - assert instance.get_decorated_functions() == expected
+ assert instance.get_decorated_functions() == expected + + + def test_with_custom_transformation(self): + def my_transformation(f): + @wraps(f) + def wrapper(*args, **kwargs): + f(*args, **kwargs) + return 4422 # we add a return value + + return wrapper + + my_decorator = create_decorator(decorator_type=Decorators.BAR, transformation=my_transformation) + + class MyClass(WithDecoratedMethods[Decorators]): + @my_decorator(42) + def m1(self) -> int: + return 1 + + instance = MyClass() + expected = { + Decorators.BAR: { + instance.m1: 42, + }, + Decorators.FOO: {}, + } + assert instance.get_decorated_functions() == expected + + assert instance.m1() == 4422 # check that transformation was applied

Ancestors

    @@ -239,6 +296,43 @@

    Ancestors

Methods

+
+def test_with_custom_transformation(self) +
+
+
+
+ +Expand source code + +
def test_with_custom_transformation(self):
+    def my_transformation(f):
+        @wraps(f)
+        def wrapper(*args, **kwargs):
+            f(*args, **kwargs)
+            return 4422  # we add a return value
+
+        return wrapper
+
+    my_decorator = create_decorator(decorator_type=Decorators.BAR, transformation=my_transformation)
+
+    class MyClass(WithDecoratedMethods[Decorators]):
+        @my_decorator(42)
+        def m1(self) -> int:
+            return 1
+
+    instance = MyClass()
+    expected = {
+        Decorators.BAR: {
+            instance.m1: 42,
+        },
+        Decorators.FOO: {},
+    }
+    assert instance.get_decorated_functions() == expected
+
+    assert instance.m1() == 4422  # check that transformation was applied
+
+
def test_with_decorated_methods_async(self)
@@ -339,6 +433,7 @@

TestWithDecoratedMethods

diff --git a/docs/pedantic/tests/tests_decorated_function.html b/docs/pedantic/tests/tests_decorated_function.html index bff2a6a..c15768d 100644 --- a/docs/pedantic/tests/tests_decorated_function.html +++ b/docs/pedantic/tests/tests_decorated_function.html @@ -33,23 +33,19 @@

Module pedantic.tests.tests_decorated_functionModule pedantic.tests.tests_decorated_functionModule pedantic.tests.tests_decorated_functionModule pedantic.tests.tests_decorated_functionModule pedantic.tests.tests_decorated_functionClasses

class TestDecoratedFunction(unittest.TestCase):
     def test_static_method(self):
-        def f_1():
-            pass
+        def f_1(): pass
 
         deco_f = DecoratedFunction(f_1)
         self.assertFalse(deco_f.is_static_method)
 
         class MyClass:
-            def f_1(self):
-                pass
+            def f_1(self): pass
 
             @staticmethod
-            def f_2():
-                pass
+            def f_2(): pass
 
             @classmethod
-            def f_3(cls):
-                pass
+            def f_3(cls): pass
 
         deco_f_1 = DecoratedFunction(MyClass.f_1)
         deco_f_2 = DecoratedFunction(MyClass.f_2)
@@ -272,20 +239,15 @@ 

Classes

self.assertFalse(deco_f_3.is_static_method) def test_function_wants_args(self): - def f_1(*args, **kwargs): - pass + def f_1(*args, **kwargs): pass - def f_2(a, b, *args, **kwargs): - pass + def f_2(a, b, *args, **kwargs): pass - def f_3(a, b, *args): - pass + def f_3(a, b, *args): pass - def f_4(*args): - pass + def f_4(*args): pass - def f_5(): - pass + def f_5(): pass self.assertTrue(DecoratedFunction(f_1).wants_args) self.assertTrue(DecoratedFunction(f_2).wants_args) @@ -294,53 +256,42 @@

Classes

self.assertFalse(DecoratedFunction(f_5).wants_args) class MyClass: - def f(self): - pass + def f(self): pass @staticmethod - def g(): - pass + def g(): pass self.assertFalse(DecoratedFunction(MyClass.f).wants_args) self.assertFalse(DecoratedFunction(MyClass.g).wants_args) def test_is_property_setter(self): - def f_1(): - pass + def f_1(): pass self.assertFalse(DecoratedFunction(f_1).is_property_setter) class MyClass: _h = 42 - def f_1(self): - pass + def f_1(self): pass @staticmethod - def f_2(): - pass + def f_2(): pass self.assertFalse(DecoratedFunction(MyClass.f_1).is_property_setter) self.assertFalse(DecoratedFunction(MyClass.f_2).is_property_setter) def test_wants_kwargs(self): - def f_1(*args, **kwargs): - pass + def f_1(*args, **kwargs): pass - def f_2(a, b, *args, **kwargs): - pass + def f_2(a, b, *args, **kwargs): pass - def f_3(a, b, *args): - pass + def f_3(a, b, *args): pass - def f_4(*args): - pass + def f_4(*args): pass - def f_5(): - pass + def f_5(): pass - def f_6(a, b, c): - pass + def f_6(a, b, c): pass self.assertFalse(DecoratedFunction(f_1).should_have_kwargs) self.assertFalse(DecoratedFunction(f_2).should_have_kwargs) @@ -350,33 +301,27 @@

Classes

self.assertTrue(DecoratedFunction(f_6).should_have_kwargs) class A: - def f(self): - pass + def f(self): pass @staticmethod - def g(): - pass + def g(): pass - def __compare__(self, other): - pass + def __compare__(self, other): pass self.assertTrue(DecoratedFunction(A.f).should_have_kwargs) self.assertTrue(DecoratedFunction(A.g).should_have_kwargs) self.assertFalse(DecoratedFunction(A.__compare__).should_have_kwargs) def test_instance_method(self): - def h(): - pass + def h(): pass self.assertFalse(DecoratedFunction(h).is_instance_method) class A: - def f(self): - pass + def f(self): pass @staticmethod - def g(): - pass + def g(): pass self.assertTrue(DecoratedFunction(A.f).is_instance_method) self.assertFalse(DecoratedFunction(A.g).is_instance_method) @@ -385,17 +330,14 @@

Classes

def decorator(f): return f - def f_1(): - pass + def f_1(): pass @decorator - def f_2(): - pass + def f_2(): pass @decorator @decorator - def f_3(): - pass + def f_3(): pass @decorator @decorator @@ -424,20 +366,15 @@

Methods

Expand source code
def test_function_wants_args(self):
-    def f_1(*args, **kwargs):
-        pass
+    def f_1(*args, **kwargs): pass
 
-    def f_2(a, b, *args, **kwargs):
-        pass
+    def f_2(a, b, *args, **kwargs): pass
 
-    def f_3(a, b, *args):
-        pass
+    def f_3(a, b, *args): pass
 
-    def f_4(*args):
-        pass
+    def f_4(*args): pass
 
-    def f_5():
-        pass
+    def f_5(): pass
 
     self.assertTrue(DecoratedFunction(f_1).wants_args)
     self.assertTrue(DecoratedFunction(f_2).wants_args)
@@ -446,12 +383,10 @@ 

Methods

self.assertFalse(DecoratedFunction(f_5).wants_args) class MyClass: - def f(self): - pass + def f(self): pass @staticmethod - def g(): - pass + def g(): pass self.assertFalse(DecoratedFunction(MyClass.f).wants_args) self.assertFalse(DecoratedFunction(MyClass.g).wants_args)
@@ -467,18 +402,15 @@

Methods

Expand source code
def test_instance_method(self):
-    def h():
-        pass
+    def h(): pass
 
     self.assertFalse(DecoratedFunction(h).is_instance_method)
 
     class A:
-        def f(self):
-            pass
+        def f(self): pass
 
         @staticmethod
-        def g():
-            pass
+        def g(): pass
 
     self.assertTrue(DecoratedFunction(A.f).is_instance_method)
     self.assertFalse(DecoratedFunction(A.g).is_instance_method)
@@ -494,20 +426,17 @@

Methods

Expand source code
def test_is_property_setter(self):
-    def f_1():
-        pass
+    def f_1(): pass
 
     self.assertFalse(DecoratedFunction(f_1).is_property_setter)
 
     class MyClass:
         _h = 42
 
-        def f_1(self):
-            pass
+        def f_1(self): pass
 
         @staticmethod
-        def f_2():
-            pass
+        def f_2(): pass
 
     self.assertFalse(DecoratedFunction(MyClass.f_1).is_property_setter)
     self.assertFalse(DecoratedFunction(MyClass.f_2).is_property_setter)
@@ -526,17 +455,14 @@

Methods

def decorator(f): return f - def f_1(): - pass + def f_1(): pass @decorator - def f_2(): - pass + def f_2(): pass @decorator @decorator - def f_3(): - pass + def f_3(): pass @decorator @decorator @@ -560,23 +486,19 @@

Methods

Expand source code
def test_static_method(self):
-    def f_1():
-        pass
+    def f_1(): pass
 
     deco_f = DecoratedFunction(f_1)
     self.assertFalse(deco_f.is_static_method)
 
     class MyClass:
-        def f_1(self):
-            pass
+        def f_1(self): pass
 
         @staticmethod
-        def f_2():
-            pass
+        def f_2(): pass
 
         @classmethod
-        def f_3(cls):
-            pass
+        def f_3(cls): pass
 
     deco_f_1 = DecoratedFunction(MyClass.f_1)
     deco_f_2 = DecoratedFunction(MyClass.f_2)
@@ -597,23 +519,17 @@ 

Methods

Expand source code
def test_wants_kwargs(self):
-    def f_1(*args, **kwargs):
-        pass
+    def f_1(*args, **kwargs): pass
 
-    def f_2(a, b, *args, **kwargs):
-        pass
+    def f_2(a, b, *args, **kwargs): pass
 
-    def f_3(a, b, *args):
-        pass
+    def f_3(a, b, *args): pass
 
-    def f_4(*args):
-        pass
+    def f_4(*args): pass
 
-    def f_5():
-        pass
+    def f_5(): pass
 
-    def f_6(a, b, c):
-        pass
+    def f_6(a, b, c): pass
 
     self.assertFalse(DecoratedFunction(f_1).should_have_kwargs)
     self.assertFalse(DecoratedFunction(f_2).should_have_kwargs)
@@ -623,15 +539,12 @@ 

Methods

self.assertTrue(DecoratedFunction(f_6).should_have_kwargs) class A: - def f(self): - pass + def f(self): pass @staticmethod - def g(): - pass + def g(): pass - def __compare__(self, other): - pass + def __compare__(self, other): pass self.assertTrue(DecoratedFunction(A.f).should_have_kwargs) self.assertTrue(DecoratedFunction(A.g).should_have_kwargs) diff --git a/docs/pedantic/tests/tests_main.html b/docs/pedantic/tests/tests_main.html index 480927f..7936e7a 100644 --- a/docs/pedantic/tests/tests_main.html +++ b/docs/pedantic/tests/tests_main.html @@ -32,7 +32,7 @@

Module pedantic.tests.tests_main

sys.path.append(os.getcwd()) -from pedantic.tests.test_retry import TestRetry +from pedantic.tests.test_retry import TestRetry, TestRetryFunc from pedantic.tests.test_with_decorated_methods import TestWithDecoratedMethods from pedantic.tests.validate.test_convert_value import TestConvertValue from pedantic.tests.test_rename_kwargs import TestRenameKwargs @@ -103,6 +103,7 @@

Module pedantic.tests.tests_main

TestGeneratorWrapper, TestRenameKwargs, TestRetry, + TestRetryFunc, TestResolveForwardRef, # validate TestValidatorDatetimeIsoformat, @@ -189,6 +190,7 @@

Functions

TestGeneratorWrapper, TestRenameKwargs, TestRetry, + TestRetryFunc, TestResolveForwardRef, # validate TestValidatorDatetimeIsoformat, diff --git a/docs/pedantic/tests/tests_small_method_decorators.html b/docs/pedantic/tests/tests_small_method_decorators.html index 9e47146..fca3839 100644 --- a/docs/pedantic/tests/tests_small_method_decorators.html +++ b/docs/pedantic/tests/tests_small_method_decorators.html @@ -29,6 +29,7 @@

Module pedantic.tests.tests_small_method_decoratorsimport asyncio import unittest import warnings +from abc import abstractmethod from unittest import IsolatedAsyncioTestCase from pedantic import overrides, timer, count_calls, trace, trace_if_returns, does_same_as_function, deprecated, \ @@ -44,13 +45,11 @@

Module pedantic.tests.tests_small_method_decoratorsModule pedantic.tests.tests_small_method_decoratorsModule pedantic.tests.tests_small_method_decoratorsModule pedantic.tests.tests_small_method_decoratorsModule pedantic.tests.tests_small_method_decoratorsModule pedantic.tests.tests_small_method_decoratorsModule pedantic.tests.tests_small_method_decoratorsClasses

class AsyncSmallDecoratorTests(IsolatedAsyncioTestCase):
     async def test_overrides_async_instance_method(self) -> None:
         class MyClassA:
-            async def operation(self):
-                pass
+            async def operation(self): pass
 
         class MyClassB(MyClassA):
             @overrides(MyClassA)
@@ -380,8 +371,7 @@ 

Classes

with self.assertRaises(expected_exception=PedanticOverrideException): class MyClassB(MyClassA): @overrides(MyClassA) - async def operation(self): - return 42 + async def operation(self): return 42 async def test_count_calls_async(self): @count_calls @@ -471,9 +461,7 @@

Classes

async def test_mock_async(self) -> None: @mock(return_value=42) - async def my_function(a, b, c): - await asyncio.sleep(0) - return a + b + c + async def my_function(a, b, c): return a + b + c assert await my_function(1, 2, 3) == 42 assert await my_function(100, 200, 300) == 42 @@ -617,9 +605,7 @@

Methods

async def test_mock_async(self) -> None:
     @mock(return_value=42)
-    async def my_function(a, b, c):
-        await asyncio.sleep(0)
-        return a + b + c
+    async def my_function(a, b, c): return a + b + c
 
     assert await my_function(1, 2, 3) == 42
     assert await my_function(100, 200, 300) == 42
@@ -636,8 +622,7 @@

Methods

async def test_overrides_async_instance_method(self) -> None:
     class MyClassA:
-        async def operation(self):
-            pass
+        async def operation(self): pass
 
     class MyClassB(MyClassA):
         @overrides(MyClassA)
@@ -665,8 +650,7 @@ 

Methods

with self.assertRaises(expected_exception=PedanticOverrideException): class MyClassB(MyClassA): @overrides(MyClassA) - async def operation(self): - return 42
+ async def operation(self): return 42

@@ -791,13 +775,11 @@

Methods

with self.assertRaises(expected_exception=PedanticOverrideException): class MyClassB(MyClassA): @overrides(MyClassA) - def operation(self): - return 42 + def operation(self): pass def test_overrides_all_good(self): class MyClassA: - def operation(self): - pass + def operation(self): pass class MyClassB(MyClassA): @overrides(MyClassA) @@ -810,8 +792,7 @@

Methods

def test_overrides_static_method(self): class MyClassA: @staticmethod - def operation(): - pass + def operation(): pass class MyClassB(MyClassA): @staticmethod @@ -826,8 +807,8 @@

Methods

def test_overrides_below_property(self): class MyClassA: @property - def operation(self): - return 42 + @abstractmethod + def operation(self): pass class MyClassB(MyClassA): @property @@ -844,13 +825,11 @@

Methods

with self.assertRaises(expected_exception=PedanticOverrideException): @overrides(MyClassA) - def operation(): - return 42 + def operation(): return 42 def test_deprecated_1(self): @deprecated - def old_method(i: int) -> str: - return str(i) + def old_method(i: int) -> str: return str(i) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") @@ -961,8 +940,7 @@

Methods

def test_deprecated_1(self):
     @deprecated
-    def old_method(i: int) -> str:
-        return str(i)
+    def old_method(i: int) -> str: return str(i)
 
     with warnings.catch_warnings(record=True) as w:
         warnings.simplefilter("always")
@@ -1045,8 +1023,7 @@ 

Methods

def test_overrides_all_good(self):
     class MyClassA:
-        def operation(self):
-            pass
+        def operation(self): pass
 
     class MyClassB(MyClassA):
         @overrides(MyClassA)
@@ -1069,8 +1046,8 @@ 

Methods

def test_overrides_below_property(self):
     class MyClassA:
         @property
-        def operation(self):
-            return 42
+        @abstractmethod
+        def operation(self): pass
 
     class MyClassB(MyClassA):
         @property
@@ -1097,8 +1074,7 @@ 

Methods

with self.assertRaises(expected_exception=PedanticOverrideException): @overrides(MyClassA) - def operation(): - return 42
+ def operation(): return 42
@@ -1117,8 +1093,7 @@

Methods

with self.assertRaises(expected_exception=PedanticOverrideException): class MyClassB(MyClassA): @overrides(MyClassA) - def operation(self): - return 42
+ def operation(self): pass
@@ -1133,8 +1108,7 @@

Methods

def test_overrides_static_method(self):
     class MyClassA:
         @staticmethod
-        def operation():
-            pass
+        def operation(): pass
 
     class MyClassB(MyClassA):
         @staticmethod
diff --git a/docs/pedantic/tests/validate/test_validator_is_enum.html b/docs/pedantic/tests/validate/test_validator_is_enum.html
index 1fce569..68a9b09 100644
--- a/docs/pedantic/tests/validate/test_validator_is_enum.html
+++ b/docs/pedantic/tests/validate/test_validator_is_enum.html
@@ -95,8 +95,7 @@ 

Module pedantic.tests.validate.test_validator_is_enumClass variables

def test_validator_is_enum_to_upper_case_disabled(self) -> None: @validate(Parameter(name='x', validators=[IsEnum(MyEnum, convert=False, to_upper_case=False)])) - def foo(x): - return x + def foo(x): print(x) for value in ['red', 'blue', 'Red', 'bLUe']: with self.assertRaises(expected_exception=ParameterException): @@ -422,8 +420,7 @@

Methods

def test_validator_is_enum_to_upper_case_disabled(self) -> None:
     @validate(Parameter(name='x', validators=[IsEnum(MyEnum, convert=False, to_upper_case=False)]))
-    def foo(x):
-        return x
+    def foo(x): print(x)
 
     for value in ['red', 'blue', 'Red', 'bLUe']:
         with self.assertRaises(expected_exception=ParameterException):
diff --git a/pedantic/mixins/with_decorated_methods.py b/pedantic/mixins/with_decorated_methods.py
index cc68528..3ab8f0d 100644
--- a/pedantic/mixins/with_decorated_methods.py
+++ b/pedantic/mixins/with_decorated_methods.py
@@ -22,13 +22,23 @@ class DecoratorType(StrEnum):
 C = TypeVar('C', bound=Callable)
 
 
-def create_decorator(decorator_type: DecoratorType) -> Callable[[T], Callable[[C], C]]:
-    """ Creates a new decorator that is parametrized with one argument of an arbitrary type. """
+def create_decorator(
+    decorator_type: DecoratorType,
+    transformation: Callable[[C], C] = None,
+) -> Callable[[T], Callable[[C], C]]:
+    """
+    Creates a new decorator that is parametrized with one argument of an arbitrary type.
+    You can also pass an arbitrary [transformation] to add custom behavior to the decorator.
+    """
 
     def decorator(value: T) -> Callable[[C], C]:
         def fun(f: C) -> C:
             setattr(f, decorator_type, value)
-            return f
+
+            if transformation is None:
+                return f
+
+            return transformation(f)
 
         return fun  # we do not need functools.wraps, because we return the original function here
 
diff --git a/pedantic/tests/test_with_decorated_methods.py b/pedantic/tests/test_with_decorated_methods.py
index b92a3c4..a1744b8 100644
--- a/pedantic/tests/test_with_decorated_methods.py
+++ b/pedantic/tests/test_with_decorated_methods.py
@@ -1,4 +1,5 @@
 import unittest
+from functools import wraps
 
 from pedantic import DecoratorType, create_decorator, WithDecoratedMethods
 
@@ -64,3 +65,31 @@ async def m3(self) -> None:
             }
         }
         assert instance.get_decorated_functions() == expected
+
+
+    def test_with_custom_transformation(self):
+        def my_transformation(f):
+            @wraps(f)
+            def wrapper(*args, **kwargs):
+                f(*args, **kwargs)
+                return 4422  # we add a return value
+
+            return wrapper
+
+        my_decorator = create_decorator(decorator_type=Decorators.BAR, transformation=my_transformation)
+
+        class MyClass(WithDecoratedMethods[Decorators]):
+            @my_decorator(42)
+            def m1(self) -> int:
+                return 1
+
+        instance = MyClass()
+        expected = {
+            Decorators.BAR: {
+                instance.m1: 42,
+            },
+            Decorators.FOO: {},
+        }
+        assert instance.get_decorated_functions() == expected
+
+        assert instance.m1() == 4422  # check that transformation was applied
diff --git a/setup.py b/setup.py
index 7217121..0d629f1 100644
--- a/setup.py
+++ b/setup.py
@@ -15,7 +15,7 @@ def get_content_from_readme(file_name: str = 'README.md') -> str:
 
 setup(
     name="pedantic",
-    version="2.1.6",
+    version="2.1.7",
     python_requires='>=3.11.0',
     packages=find_packages(),
     install_requires=[],