# Decorator function not accepting my argument?

**URL:** <https://forum.inductiveautomation.com/t/decorator-function-not-accepting-my-argument/67609>\
**Category:** Ignition\
**Tags:** ignition81\
**Created:** [December 14, 2022, 1:42pm UTC](https://forum.inductiveautomation.com/t/decorator-function-not-accepting-my-argument/67609 "2022-12-14T13:42:15Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [December 14, 2022, 1:42pm UTC](https://forum.inductiveautomation.com/t/decorator-function-not-accepting-my-argument/67609/1 "2022-12-14T13:42:15Z")

</div>

I wrote a decorator function for handling database transactions and I want to be able to pass a logger to it.

My decorator function looks like

```auto
def handleTransactionAndCreateResponse(inner_func, logger):
	"""
	Runs inner function inside of a transaction, handles errors and returns a response.
	Args:
		inner_func: the function to wrap in a tranasction
		logger: logger, used to log information.
	"""
	def wrapped_func(*args, **kwargs): 
		logger.info("Starting transaction")
		# Other logic here
        return inner_func(*args, **kwargs)
	return wrapped_func

```

And I am calling it like

```auto
LOGGER = system.util.getLogger( __name__ )

@forms2.decorators.handleTransactionAndCreateResponse(LOGGER)
def create(data, tx=None):
    #Named queries here
	return newId

```

However calling that create function, I get `TypeError: handleTransactionAndCreateResponse() takes exactly 2 arguments (1 given)`

So my decorator is expecting that logger to come through, I thought I was feeding it correctly with `@forms2.decorators.handleTransactionAndCreateResponse(LOGGER)` but apparently not. What am I doing wrong here?

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [December 14, 2022, 2:08pm UTC](https://forum.inductiveautomation.com/t/decorator-function-not-accepting-my-argument/67609/2 "2022-12-14T14:08:30Z")

</div>

I was able to make it work following the example here [Script Benchmarking with Decorators Example](https://forum.inductiveautomation.com/t/script-benchmarking-with-decorators-example/63507)

---

<div class="post-metadata">

**Author:** ![pascal.fragnoud](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pascal.fragnoud/32/24728_2.png) [@pascal.fragnoud](https://forum.inductiveautomation.com/u/pascal.fragnoud)\
**Post date:** [December 14, 2022, 7:04pm UTC](https://forum.inductiveautomation.com/t/decorator-function-not-accepting-my-argument/67609/3 "2022-12-14T19:04:40Z")

</div>

Because that's not how you make a parameterized decorator.  
You need to go one level deeper. let's see if I can get this right !

So, decorating a function is equivalent to passing this function to the decorating function:

```python
@decorator
def foo():
    return "foo"
foo()

# is like
def foo()
    return "foo"
decorator(foo)()

```

`decorator` takes a function, and returns a function, which you can call directly.  
Now, with parameters... We gotta make sure we're talking about the good ones, because two things here could take parameters: `decorator` and `foo`.  
let's first make a decorator that doesn't take params, and which decorates a function that does:

```python
def decorator(func):
    def wrapper(*args, **kwargs):
        # do stuff
        ret = func(args, kwargs)
        # more stuff
        return ret
    return wrapper

@decorator
def foo(*args, **kwargs):
    return f"{args}, {kwargs}"

foo("bar", baz="qux")

def foo(*args, **kwargs):
    return f"{args}, {kwargs}"
decorator(foo)("bar", baz="qux")

```

Now if we want a decorator that takes parameters, the call would look like this:

```python
decorator(deco_args)(foo)()
# and not
decorator(foo, deco_args)()

```

We need a function that takes parameters, in which we will define a function that takes a function and returns one, that we will return and that can be used as a decorator.

```python
def parameterized_decorator(deco_args):
    def inner_decorator(func):
        def wrapper(*args, **kwargs):
            # do stuff
            ret = func(args, kwargs)
            # more stuff
            return ret
        return wrapper
    return inner_decorator

```

So, now we can call `parameterized_decorator` with arguments.

```python
inner_deco = parameterized_decorator(deco_args)

```

This returns a function that takes a function and also returns one:

```python
deco = inner_deco(foo)

```

The function returned by this, can then be called with `args` and `kwargs`:

```python
deco(args, kwargs)

```

In the end, we get this:

```python
@decorator(deco_args)
def foo(*args, **kwargs):
    # do the foo
foo(args, kwargs)

# is like
def foo(*args, **kwargs):
    # do the foo
decorator(deco_args)(foo)(args, kwargs)

```

Let's hope this all makes sense !

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [December 14, 2022, 7:08pm UTC](https://forum.inductiveautomation.com/t/decorator-function-not-accepting-my-argument/67609/4 "2022-12-14T19:08:31Z")

</div>

I like metaprogramming, and I _still_ have to re-read the documentation half a dozen times every time I try to write a decorator. I maintain it's just a tricky concept to wrap your head around.

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [December 14, 2022, 7:35pm UTC](https://forum.inductiveautomation.com/t/decorator-function-not-accepting-my-argument/67609/5 "2022-12-14T19:35:28Z")

</div>

> [@pascal.fragnoud](#):
>
> Because that's not how you make a parameterized decorator.

This made me laugh. I figured as much when it wasn't working lol.

I found this and it explained what you just did, I encourage anyone else who want's to learn about decorators to read it [The best explanation of Python decorators I’ve ever seen. (An archived answer from StackOverflow.) · GitHub](https://gist.github.com/Zearin/2f40b7b9cfc51132851a)

I decided to not have a function that returns a function that returns a function in my codebase though. Even though it makes sense to me today that won't be the case a week from now and this seems like a very appropriate spot to use some pre-built tools like `functools` to do the heavy lifting for me.

I'm with you @paul-griffith . I always need a refresher when writing decorators.

---

<div class="post-metadata">

**Author:** ![pascal.fragnoud](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pascal.fragnoud/32/24728_2.png) [@pascal.fragnoud](https://forum.inductiveautomation.com/u/pascal.fragnoud)\
**Post date:** [December 14, 2022, 8:22pm UTC](https://forum.inductiveautomation.com/t/decorator-function-not-accepting-my-argument/67609/6 "2022-12-14T20:22:30Z")

</div>

Yes you can use functools's `partial` to get a similar result.

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [December 14, 2022, 8:24pm UTC](https://forum.inductiveautomation.com/t/decorator-function-not-accepting-my-argument/67609/7 "2022-12-14T20:24:44Z")

</div>

You can do it with partial but the example I followed uses `@functool.wraps` inside the ` __call__ ` method of a class around the wrapped function. I found it much cleaner than `partial` as it also offers opportunity to do an ` __init__ ` and set things if nothing is fed in if you so desired.

---

<div class="post-metadata">

**Author:** ![pascal.fragnoud](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pascal.fragnoud/32/24728_2.png) [@pascal.fragnoud](https://forum.inductiveautomation.com/u/pascal.fragnoud)\
**Post date:** [December 15, 2022, 6:34am UTC](https://forum.inductiveautomation.com/t/decorator-function-not-accepting-my-argument/67609/8 "2022-12-15T06:34:02Z")

</div>

`wraps`'s role is different. It's used to keep the decorated function metadata (docstrings, name, etc...)...  
Since decorators return a new function, those things would be lost if we didn't copy them explicitly to the returned function.  
It doesn't make decorators itself, it helps with keeping them consistent.
