Repository: fgmacedo/python-statemachine

Issue:
SignatureAdapter wrap
* Python State Machine version: 2.0.0
* Python version: 3.7.16
* Operating System: MacBook

### Description

I have a decorator class for state machine child class's transitions methods.
The decorator has this function:
```
 def __get__(self, instance, owner):
        return partial(self.__call__, instance)
```
When partial is from functools package.

wrap function crashes on this line:
`sig.__name__ = method.__name__`
When method is functors.partial(decorator_class.__call__)

`AttributeError: 'functools.partial' object has no attribute '__name__'`



Discussion:
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=fgmacedo_python-statemachine&pullRequest=370)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=fgmacedo_python-statemachine&pullRequest=370) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=fgmacedo_python-statemachine&pullRequest=370&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=fgmacedo_python-statemachine&pullRequest=370&metric=new_duplicated_lines_density&view=list)


codecov[bot]: ## [Codecov](https://codecov.io/gh/fgmacedo/python-statemachine/pull/370?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo) Report
Patch coverage: **`100.00`**% and no project coverage change.
> Comparison is base [(`5f6bf4c`)](https://codecov.io/gh/fgmacedo/python-statemachine/commit/5f6bf4cae29ab9e63d21ecdaa8b105ab2830b86d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo) 100.00% compared to head [(`17e2a7f`)](https://codecov.io/gh/fgmacedo/python-statemachine/pull/370?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo) 100.00%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff            @@
##           develop      #370   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           19        19           
  Lines          935       936    +1     
  Branches       154       154           
=========================================
+ Hits           935       936    +1     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `100.00% <100.00%> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/fgmacedo/python-statemachine/pull/370?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo) | Coverage Δ | |
|---|---|---|
| [statemachine/signature.py](https://codecov.io/gh/fgmacedo/python-statemachine/pull/370?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo#diff-c3RhdGVtYWNoaW5lL3NpZ25hdHVyZS5weQ==) | `100.00% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo)

</details>

[:umbrella: View full report in Codecov by Sentry](https://codecov.io/gh/fgmacedo/python-statemachine/pull/370?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Fernando+Macedo).

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=fgmacedo_python-statemachine&pullRequest=370)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=fgmacedo_python-statemachine&pullRequest=370&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=fgmacedo_python-statemachine&pullRequest=370) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=fgmacedo_python-statemachine&pullRequest=370&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=fgmacedo_python-statemachine&pullRequest=370&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/docs/releases/2.0.1.md b/docs/releases/2.0.1.md
new file mode 100644
index 0000000..fab6a72
--- /dev/null
+++ b/docs/releases/2.0.1.md
@@ -0,0 +1,12 @@
+# StateMachine 2.0.1
+
+*Not released yet*
+
+
+StateMachine 2.0.1 is a bugfix release.
+
+
+## Bugfixes
+
+- Fixes [#369](https://github.com/fgmacedo/python-statemachine/issues/336) adding support to wrap
+  methods used as {ref}`Actions` decorated with `functools.partial`.
diff --git a/docs/releases/index.md b/docs/releases/index.md
index e4a62da..2d8c6f5 100644
--- a/docs/releases/index.md
+++ b/docs/releases/index.md
@@ -15,6 +15,7 @@ Below are release notes through StateMachine and its patch releases.
 ```{toctree}
 :maxdepth: 1
 
+2.0.1
 2.0.0
 
 ```
diff --git a/statemachine/signature.py b/statemachine/signature.py
index bef064b..e3dca83 100644
--- a/statemachine/signature.py
+++ b/statemachine/signature.py
@@ -1,4 +1,5 @@
 import itertools
+from functools import partial
 from inspect import BoundArguments
 from inspect import Parameter
 from inspect import Signature
@@ -15,7 +16,9 @@ class SignatureAdapter(Signature):
 
         sig = cls.from_callable(method)
         sig.method = method
-        sig.__name__ = method.__name__
+        sig.__name__ = (
+            method.func.__name__ if isinstance(method, partial) else method.__name__
+        )
         return sig
 
     def __call__(self, *args: Any, **kwargs: Any) -> Any:


Test patch:
diff --git a/tests/test_signature.py b/tests/test_signature.py
index fd9a891..175d2e2 100644
--- a/tests/test_signature.py
+++ b/tests/test_signature.py
@@ -1,4 +1,5 @@
 import inspect
+from functools import partial
 
 import pytest
 
@@ -148,9 +149,17 @@ class TestSignatureAdapter:
     def test_wrap_fn_single_positional_parameter(self, func, args, kwargs, expected):
 
         wrapped_func = SignatureAdapter.wrap(func)
+        assert wrapped_func.__name__ == func.__name__
 
         if inspect.isclass(expected) and issubclass(expected, Exception):
             with pytest.raises(expected):
                 wrapped_func(*args, **kwargs)
         else:
             assert wrapped_func(*args, **kwargs) == expected
+
+    def test_support_for_partial(self):
+        part = partial(positional_and_kw_arguments, event="activated")
+        wrapped_func = SignatureAdapter.wrap(part)
+
+        assert wrapped_func("A", "B") == ("A", "B", "activated")
+        assert wrapped_func.__name__ == positional_and_kw_arguments.__name__