prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  walltime app function parameter modifies task_record func_name
  **Describe the bug**
  When the walltime special parameter is passed to a Parsl app the `task_record['func_name']` parameter is set to "wrapper" instead of to the function's name. 
  
  **To Reproduce**
  Launch the code below using parsl version 1.2.0:
  ```
  import parsl
  print(parsl.__version__, flush = True)
  from parsl.app.app import python_app, bash_app
  from parsl.config import Config
  from parsl.executors import HighThroughputExecutor
  
  
  @python_app
  def test(stdout='std.out', stderr = 'std.err', walltime = 5):
      from time import sleep
      sleep(1)
      Fail = 1/0
      return 'Hello'
  
  def retry_handler(exception, task_record):
      print(task_record['func_name'], flush = True)
      return 1
  
  if __name__ == '__main__':
  
      config = Config(
          executors = [HighThroughputExecutor()],
          retries = 2,
          retry_handler = retry_handler
      )
      print('Loading Parsl Config', flush = True)
      parsl.load(config)
  
      fut = test()
  
      print(fut.result())
  
  ```
  
  It will print "wrapper" if the walltime parameter is present and test otherwise. 
  
  **Expected behavior**
  The code should print the function's name (test).
  
  **Environment**
  - OS: Linux
  - Python version: 3.10.4
  - Parsl version: 1.2.0
  
  
  **Distributed Environment**
  - Where are you running the Parsl script from ? Local
  - Where do you need the workers to run ? Local
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `parsl/app/python.py`
  Content:
  ```
  1 import logging
  2 
  3 import tblib.pickling_support
  4 tblib.pickling_support.install()
  5 
  6 from parsl.app.app import AppBase
  7 from parsl.app.errors import wrap_error
  8 from parsl.dataflow.dflow import DataFlowKernelLoader
  9 
  10 
  11 logger = logging.getLogger(__name__)
  12 
  13 
  14 def timeout(f, seconds):
  15     def wrapper(*args, **kwargs):
  16         import threading
  17         import ctypes
  18         import parsl.app.errors
  19 
  20         def inject_exception(thread):
  21             ctypes.pythonapi.PyThreadState_SetAsyncExc(
  22                 ctypes.c_long(thread),
  23                 ctypes.py_object(parsl.app.errors.AppTimeout)
  24             )
  25 
  26         thread = threading.current_thread().ident
  27         timer = threading.Timer(seconds, inject_exception, args=[thread])
  28         timer.start()
  29         result = f(*args, **kwargs)
  30         timer.cancel()
  31         return result
  32     return wrapper
  33 
  34 
  35 class PythonApp(AppBase):
  36     """Extends AppBase to cover the Python App."""
  37 
  38     def __init__(self, func, data_flow_kernel=None, cache=False, executors='all', ignore_for_cache=[], join=False):
  39         super().__init__(
  40             wrap_error(func),
  41             data_flow_kernel=data_flow_kernel,
  42             executors=executors,
  43             cache=cache,
  44             ignore_for_cache=ignore_for_cache
  45         )
  46         self.join = join
  47 
  48     def __call__(self, *args, **kwargs):
  49         """This is where the call to a python app is handled.
  50 
  51         Args:
  52              - Arbitrary
  53         Kwargs:
  54              - Arbitrary
  55 
  56         Returns:
  57                    App_fut
  58 
  59         """
  60         invocation_kwargs = {}
  61         invocation_kwargs.update(self.kwargs)
  62         invocation_kwargs.update(kwargs)
  63 
  64         if self.data_flow_kernel is None:
  65             dfk = DataFlowKernelLoader.dfk()
  66         else:
  67             dfk = self.data_flow_kernel
  68 
  69         walltime = invocation_kwargs.get('walltime')
  70         if walltime is not None:
  71             func = timeout(self.func, walltime)
  72         else:
  73             func = self.func
  74 
  75         app_fut = dfk.submit(func, app_args=args,
  76                              executors=self.executors,
  77                              cache=self.cache,
  78                              ignore_for_cache=self.ignore_for_cache,
  79                              app_kwargs=invocation_kwargs,
  80                              join=self.join)
  81 
  82         return app_fut
  83
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/parsl/app/python.py b/parsl/app/python.py
  --- a/parsl/app/python.py
  +++ b/parsl/app/python.py
  @@ -3,6 +3,8 @@
   import tblib.pickling_support
   tblib.pickling_support.install()
   
  +from functools import wraps
  +
   from parsl.app.app import AppBase
   from parsl.app.errors import wrap_error
   from parsl.dataflow.dflow import DataFlowKernelLoader
  @@ -12,6 +14,7 @@
   
   
   def timeout(f, seconds):
  +    @wraps(f)
       def wrapper(*args, **kwargs):
           import threading
           import ctypes
  
verification_info: {"golden_diff": "diff --git a/parsl/app/python.py b/parsl/app/python.py\n--- a/parsl/app/python.py\n+++ b/parsl/app/python.py\n@@ -3,6 +3,8 @@\n import tblib.pickling_support\n tblib.pickling_support.install()\n \n+from functools import wraps\n+\n from parsl.app.app import AppBase\n from parsl.app.errors import wrap_error\n from parsl.dataflow.dflow import DataFlowKernelLoader\n@@ -12,6 +14,7 @@\n \n \n def timeout(f, seconds):\n+    @wraps(f)\n     def wrapper(*args, **kwargs):\n         import threading\n         import ctypes\n", "issue": "walltime app function parameter modifies task_record func_name\n**Describe the bug**\r\nWhen the walltime special parameter is passed to a Parsl app the `task_record['func_name']` parameter is set to \"wrapper\" instead of to the function's name. \r\n\r\n**To Reproduce**\r\nLaunch the code below using parsl version 1.2.0:\r\n```\r\nimport parsl\r\nprint(parsl.__version__, flush = True)\r\nfrom parsl.app.app import python_app, bash_app\r\nfrom parsl.config import Config\r\nfrom parsl.executors import HighThroughputExecutor\r\n\r\n\r\n@python_app\r\ndef test(stdout='std.out', stderr = 'std.err', walltime = 5):\r\n    from time import sleep\r\n    sleep(1)\r\n    Fail = 1/0\r\n    return 'Hello'\r\n\r\ndef retry_handler(exception, task_record):\r\n    print(task_record['func_name'], flush = True)\r\n    return 1\r\n\r\nif __name__ == '__main__':\r\n\r\n    config = Config(\r\n        executors = [HighThroughputExecutor()],\r\n        retries = 2,\r\n        retry_handler = retry_handler\r\n    )\r\n    print('Loading Parsl Config', flush = True)\r\n    parsl.load(config)\r\n\r\n    fut = test()\r\n\r\n    print(fut.result())\r\n\r\n```\r\n\r\nIt will print \"wrapper\" if the walltime parameter is present and test otherwise. \r\n\r\n**Expected behavior**\r\nThe code should print the function's name (test).\r\n\r\n**Environment**\r\n- OS: Linux\r\n- Python version: 3.10.4\r\n- Parsl version: 1.2.0\r\n\r\n\r\n**Distributed Environment**\r\n- Where are you running the Parsl script from ? Local\r\n- Where do you need the workers to run ? Local\r\n\n", "before_files": [{"content": "import logging\n\nimport tblib.pickling_support\ntblib.pickling_support.install()\n\nfrom parsl.app.app import AppBase\nfrom parsl.app.errors import wrap_error\nfrom parsl.dataflow.dflow import DataFlowKernelLoader\n\n\nlogger = logging.getLogger(__name__)\n\n\ndef timeout(f, seconds):\n    def wrapper(*args, **kwargs):\n        import threading\n        import ctypes\n        import parsl.app.errors\n\n        def inject_exception(thread):\n            ctypes.pythonapi.PyThreadState_SetAsyncExc(\n                ctypes.c_long(thread),\n                ctypes.py_object(parsl.app.errors.AppTimeout)\n            )\n\n        thread = threading.current_thread().ident\n        timer = threading.Timer(seconds, inject_exception, args=[thread])\n        timer.start()\n        result = f(*args, **kwargs)\n        timer.cancel()\n        return result\n    return wrapper\n\n\nclass PythonApp(AppBase):\n    \"\"\"Extends AppBase to cover the Python App.\"\"\"\n\n    def __init__(self, func, data_flow_kernel=None, cache=False, executors='all', ignore_for_cache=[], join=False):\n        super().__init__(\n            wrap_error(func),\n            data_flow_kernel=data_flow_kernel,\n            executors=executors,\n            cache=cache,\n            ignore_for_cache=ignore_for_cache\n        )\n        self.join = join\n\n    def __call__(self, *args, **kwargs):\n        \"\"\"This is where the call to a python app is handled.\n\n        Args:\n             - Arbitrary\n        Kwargs:\n             - Arbitrary\n\n        Returns:\n                   App_fut\n\n        \"\"\"\n        invocation_kwargs = {}\n        invocation_kwargs.update(self.kwargs)\n        invocation_kwargs.update(kwargs)\n\n        if self.data_flow_kernel is None:\n            dfk = DataFlowKernelLoader.dfk()\n        else:\n            dfk = self.data_flow_kernel\n\n        walltime = invocation_kwargs.get('walltime')\n        if walltime is not None:\n            func = timeout(self.func, walltime)\n        else:\n            func = self.func\n\n        app_fut = dfk.submit(func, app_args=args,\n                             executors=self.executors,\n                             cache=self.cache,\n                             ignore_for_cache=self.ignore_for_cache,\n                             app_kwargs=invocation_kwargs,\n                             join=self.join)\n\n        return app_fut\n", "path": "parsl/app/python.py"}], "after_files": [{"content": "import logging\n\nimport tblib.pickling_support\ntblib.pickling_support.install()\n\nfrom functools import wraps\n\nfrom parsl.app.app import AppBase\nfrom parsl.app.errors import wrap_error\nfrom parsl.dataflow.dflow import DataFlowKernelLoader\n\n\nlogger = logging.getLogger(__name__)\n\n\ndef timeout(f, seconds):\n    @wraps(f)\n    def wrapper(*args, **kwargs):\n        import threading\n        import ctypes\n        import parsl.app.errors\n\n        def inject_exception(thread):\n            ctypes.pythonapi.PyThreadState_SetAsyncExc(\n                ctypes.c_long(thread),\n                ctypes.py_object(parsl.app.errors.AppTimeout)\n            )\n\n        thread = threading.current_thread().ident\n        timer = threading.Timer(seconds, inject_exception, args=[thread])\n        timer.start()\n        result = f(*args, **kwargs)\n        timer.cancel()\n        return result\n    return wrapper\n\n\nclass PythonApp(AppBase):\n    \"\"\"Extends AppBase to cover the Python App.\"\"\"\n\n    def __init__(self, func, data_flow_kernel=None, cache=False, executors='all', ignore_for_cache=[], join=False):\n        super().__init__(\n            wrap_error(func),\n            data_flow_kernel=data_flow_kernel,\n            executors=executors,\n            cache=cache,\n            ignore_for_cache=ignore_for_cache\n        )\n        self.join = join\n\n    def __call__(self, *args, **kwargs):\n        \"\"\"This is where the call to a python app is handled.\n\n        Args:\n             - Arbitrary\n        Kwargs:\n             - Arbitrary\n\n        Returns:\n                   App_fut\n\n        \"\"\"\n        invocation_kwargs = {}\n        invocation_kwargs.update(self.kwargs)\n        invocation_kwargs.update(kwargs)\n\n        if self.data_flow_kernel is None:\n            dfk = DataFlowKernelLoader.dfk()\n        else:\n            dfk = self.data_flow_kernel\n\n        walltime = invocation_kwargs.get('walltime')\n        if walltime is not None:\n            func = timeout(self.func, walltime)\n        else:\n            func = self.func\n\n        app_fut = dfk.submit(func, app_args=args,\n                             executors=self.executors,\n                             cache=self.cache,\n                             ignore_for_cache=self.ignore_for_cache,\n                             app_kwargs=invocation_kwargs,\n                             join=self.join)\n\n        return app_fut\n", "path": "parsl/app/python.py"}]}