Repository: terrapower/armi
Pull request #1466 (2023-11-08 03:40:04)

Use functools for codeTiming decorator.
## What is the change?

Use the `@functools.wraps()` decorator to transfer attributes from the wrapped function to the wrapper function created by `@codeTiming.timed`.

## Why is the change being made?

The `codeTiming.timed` decorator was interfering with the API docs. See `interactBOL` and `interactBOC` under the `LatticePhysicsInterface`, where the docstrings did not make it into the docs.

![image](https://github.com/terrapower/armi/assets/11633797/75451663-1eeb-459a-a3b9-3ddc3a00c413)

This update will get the docstrings to render correctly in the API docs:

![image](https://github.com/terrapower/armi/assets/11633797/6700a19e-8b58-4e13-bb4f-f408bc6546d2)

<!-- MANDATORY: Explain why the change is necessary -->
<!-- Optional: Link to any related GitHub Issues -->

---

## Checklist

<!--
    You (the pull requester) should put an `x` in the boxes below you have completed.
    If you're unsure about any of them, don't hesitate to ask. We're here to help!
    (If a checkbox requires no action for this PR, put an `x` in the box.)
-->

- [x] This PR has only [one purpose or idea](https://terrapower.github.io/armi/developer/tooling.html#one-idea-one-pr).
- [x] [Tests](https://terrapower.github.io/armi/developer/tooling.html#test-it) have been added/updated to verify that the new/changed code works.

<!-- Check the code quality -->

- [x] The code style follows [good practices](https://terrapower.github.io/armi/developer/standards_and_practices.html).
- [x] The commit message(s) follow [good practices](https://terrapower.github.io/armi/developer/tooling.html).

<!-- Check the project-level cruft -->

- [x] The [release notes](https://terrapower.github.io/armi/release/index.html) (location `doc/release/0.X.rst`) are up-to-date with any important changes.
- [x] The [documentation](https://terrapower.github.io/armi/developer/tooling.html#document-it) is still up-to-date in the `doc` folder.
- [x] No [requirements](https://terrapower.github.io/armi/developer/tooling.html#watch-for-requirements) were altered.
- [x] The dependencies are still up-to-date in `pyproject.toml`.



Patch:
diff --git a/armi/utils/codeTiming.py b/armi/utils/codeTiming.py
index 543b0013..b205bbe1 100644
--- a/armi/utils/codeTiming.py
+++ b/armi/utils/codeTiming.py
@@ -16,6 +16,7 @@
 import copy
 import os
 import time
+import functools
 
 
 def timed(*args):
@@ -36,9 +37,7 @@ def timed(*args):
     """
 
     def time_decorator(func):
-        time_decorator.__doc__ = func.__doc__
-        time_decorator.__name__ = func.__name__
-
+        @functools.wraps(func)
         def time_wrapper(*args, **kwargs):
             generated_name = "::".join(
                 [
diff --git a/doc/release/0.2.rst b/doc/release/0.2.rst
index 88341868..631fd05a 100644
--- a/doc/release/0.2.rst
+++ b/doc/release/0.2.rst
@@ -13,6 +13,7 @@ What's new in ARMI
 #. Removed all old ARMI requirements, to start the work fresh. (`PR#1438 <https://github.com/terrapower/armi/pull/1438>`_)
 #. Downgrading Draft PRs as policy. (`PR#1444 <https://github.com/terrapower/armi/pull/1444>`_)
 #. Attempt to set representative block number densities by component if possible. (`PR#1412 <https://github.com/terrapower/armi/pull/1412>`_)
+#. Use functools to preserve function attributes when wrapping with codeTiming.timed (`PR#1466 <https://github.com/terrapower/armi/pull/1466>`_)
 #. TBD
 
 Bug fixes


Test patch:
diff --git a/armi/utils/tests/test_utils.py b/armi/utils/tests/test_utils.py
index d5aac031..19a221ab 100644
--- a/armi/utils/tests/test_utils.py
+++ b/armi/utils/tests/test_utils.py
@@ -36,6 +36,7 @@ from armi.utils import (
     getPreviousTimeNode,
     getCumulativeNodeNum,
     hasBurnup,
+    codeTiming,
 )
 
 
@@ -154,6 +155,19 @@ class TestGeneralUtils(unittest.TestCase):
         self.assertGreater(len(r.core.getAssemblies()), 50)
         self.assertGreater(len(r.core.getBlocks()), 200)
 
+    def test_codeTiming(self):
+        """
+        Test that codeTiming preserves function attributes when it wraps a function
+        """
+
+        @codeTiming.timed
+        def testFunc():
+            """Test function docstring"""
+            pass
+
+        self.assertEqual(getattr(testFunc, "__doc__"), "Test function docstring")
+        self.assertEqual(getattr(testFunc, "__name__"), "testFunc")
+
 
 class CyclesSettingsTests(unittest.TestCase):
     """