prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  `atexit.register` does not work
  ### What I did
  ```python3
  from pwn import *
  atexit.register(print, "hello world")
  exit()
  ```
  ### What I expected to see
  ```python3 test.py
  hello world
  ```
  ### What I saw
  Nothing
  
  I noticed this because `asm()`, which adds an `atexit` handler to remove the `/tmp/pwn-asm-XXXXXX` folder, does not in fact remove it, meaning multiple script runs leads to many similar folders.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `pwnlib/atexit.py`
  Content:
  ```
  1 """
  2 Replacement for the Python standard library's atexit.py.
  3 
  4 Whereas the standard :mod:`atexit` module only defines :func:`atexit.register`,
  5 this replacement module also defines :func:`unregister`.
  6 
  7 This module also fixes a the issue that exceptions raised by an exit handler is
  8 printed twice when the standard :mod:`atexit` is used.
  9 """
  10 from __future__ import absolute_import
  11 from __future__ import division
  12 
  13 import sys
  14 import threading
  15 import traceback
  16 
  17 from pwnlib.context import context
  18 
  19 __all__ = ['register', 'unregister']
  20 
  21 _lock = threading.Lock()
  22 _ident = 0
  23 _handlers = {}
  24 
  25 def register(func, *args, **kwargs):
  26     """register(func, *args, **kwargs)
  27 
  28     Registers a function to be called on program termination.  The function will
  29     be called with positional arguments `args` and keyword arguments `kwargs`,
  30     i.e. ``func(*args, **kwargs)``.  The current `context` is recorded and will
  31     be the one used when the handler is run.
  32 
  33     E.g. to suppress logging output from an exit-handler one could write::
  34 
  35       with context.local(log_level = 'error'):
  36         atexit.register(handler)
  37 
  38     An identifier is returned which can be used to unregister the exit-handler.
  39 
  40     This function can be used as a decorator::
  41 
  42       @atexit.register
  43       def handler():
  44         ...
  45 
  46     Notice however that this will bind ``handler`` to the identifier and not the
  47     actual exit-handler.  The exit-handler can then be unregistered with::
  48 
  49       atexit.unregister(handler)
  50 
  51     This function is thread safe.
  52 
  53     """
  54     global _ident
  55     with _lock:
  56         ident = _ident
  57         _ident += 1
  58     _handlers[ident] = (func, args, kwargs, vars(context))
  59     return ident
  60 
  61 def unregister(ident):
  62     """unregister(ident)
  63 
  64     Remove the exit-handler identified by `ident` from the list of registered
  65     handlers.  If `ident` isn't registered this is a no-op.
  66     """
  67     if ident in _handlers:
  68         del _handlers[ident]
  69 
  70 def _run_handlers():
  71     """_run_handlers()
  72 
  73     Run registered exit-handlers.  They run in the reverse order of which they
  74     were registered.
  75 
  76     If a handler raises an exception, it will be printed but nothing else
  77     happens, i.e. other handlers will be run and `sys.excepthook` will not be
  78     called for that reason.
  79     """
  80     context.clear()
  81     for _ident, (func, args, kwargs, ctx) in \
  82         sorted(_handlers.items(), reverse = True):
  83         try:
  84             with context.local(**ctx):
  85                 func(*args, **kwargs)
  86         except SystemExit:
  87             pass
  88         except Exception:
  89             # extract the current exception and rewind the traceback to where it
  90             # originated
  91             typ, val, tb = sys.exc_info()
  92             traceback.print_exception(typ, val, tb.tb_next)
  93 
  94 # if there's already an exitfunc registered be sure to run that too
  95 if hasattr(sys, "exitfunc"):
  96     register(sys.exitfunc)
  97 
  98 sys.exitfunc = _run_handlers
  99
  ```
  --- 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/pwnlib/atexit.py b/pwnlib/atexit.py
  --- a/pwnlib/atexit.py
  +++ b/pwnlib/atexit.py
  @@ -13,6 +13,7 @@
   import sys
   import threading
   import traceback
  +import atexit as std_atexit
   
   from pwnlib.context import context
   
  @@ -95,4 +96,8 @@
   if hasattr(sys, "exitfunc"):
       register(sys.exitfunc)
   
  -sys.exitfunc = _run_handlers
  +if sys.version_info[0] < 3:
  +    sys.exitfunc = _run_handlers
  +else:
  +    std_atexit.register(_run_handlers)
  +
  
verification_info: {"golden_diff": "diff --git a/pwnlib/atexit.py b/pwnlib/atexit.py\n--- a/pwnlib/atexit.py\n+++ b/pwnlib/atexit.py\n@@ -13,6 +13,7 @@\n import sys\n import threading\n import traceback\n+import atexit as std_atexit\n \n from pwnlib.context import context\n \n@@ -95,4 +96,8 @@\n if hasattr(sys, \"exitfunc\"):\n     register(sys.exitfunc)\n \n-sys.exitfunc = _run_handlers\n+if sys.version_info[0] < 3:\n+    sys.exitfunc = _run_handlers\n+else:\n+    std_atexit.register(_run_handlers)\n+\n", "issue": "`atexit.register` does not work\n### What I did\r\n```python3\r\nfrom pwn import *\r\natexit.register(print, \"hello world\")\r\nexit()\r\n```\r\n### What I expected to see\r\n```python3 test.py\r\nhello world\r\n```\r\n### What I saw\r\nNothing\r\n\r\nI noticed this because `asm()`, which adds an `atexit` handler to remove the `/tmp/pwn-asm-XXXXXX` folder, does not in fact remove it, meaning multiple script runs leads to many similar folders.\n", "before_files": [{"content": "\"\"\"\nReplacement for the Python standard library's atexit.py.\n\nWhereas the standard :mod:`atexit` module only defines :func:`atexit.register`,\nthis replacement module also defines :func:`unregister`.\n\nThis module also fixes a the issue that exceptions raised by an exit handler is\nprinted twice when the standard :mod:`atexit` is used.\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\n\nimport sys\nimport threading\nimport traceback\n\nfrom pwnlib.context import context\n\n__all__ = ['register', 'unregister']\n\n_lock = threading.Lock()\n_ident = 0\n_handlers = {}\n\ndef register(func, *args, **kwargs):\n    \"\"\"register(func, *args, **kwargs)\n\n    Registers a function to be called on program termination.  The function will\n    be called with positional arguments `args` and keyword arguments `kwargs`,\n    i.e. ``func(*args, **kwargs)``.  The current `context` is recorded and will\n    be the one used when the handler is run.\n\n    E.g. to suppress logging output from an exit-handler one could write::\n\n      with context.local(log_level = 'error'):\n        atexit.register(handler)\n\n    An identifier is returned which can be used to unregister the exit-handler.\n\n    This function can be used as a decorator::\n\n      @atexit.register\n      def handler():\n        ...\n\n    Notice however that this will bind ``handler`` to the identifier and not the\n    actual exit-handler.  The exit-handler can then be unregistered with::\n\n      atexit.unregister(handler)\n\n    This function is thread safe.\n\n    \"\"\"\n    global _ident\n    with _lock:\n        ident = _ident\n        _ident += 1\n    _handlers[ident] = (func, args, kwargs, vars(context))\n    return ident\n\ndef unregister(ident):\n    \"\"\"unregister(ident)\n\n    Remove the exit-handler identified by `ident` from the list of registered\n    handlers.  If `ident` isn't registered this is a no-op.\n    \"\"\"\n    if ident in _handlers:\n        del _handlers[ident]\n\ndef _run_handlers():\n    \"\"\"_run_handlers()\n\n    Run registered exit-handlers.  They run in the reverse order of which they\n    were registered.\n\n    If a handler raises an exception, it will be printed but nothing else\n    happens, i.e. other handlers will be run and `sys.excepthook` will not be\n    called for that reason.\n    \"\"\"\n    context.clear()\n    for _ident, (func, args, kwargs, ctx) in \\\n        sorted(_handlers.items(), reverse = True):\n        try:\n            with context.local(**ctx):\n                func(*args, **kwargs)\n        except SystemExit:\n            pass\n        except Exception:\n            # extract the current exception and rewind the traceback to where it\n            # originated\n            typ, val, tb = sys.exc_info()\n            traceback.print_exception(typ, val, tb.tb_next)\n\n# if there's already an exitfunc registered be sure to run that too\nif hasattr(sys, \"exitfunc\"):\n    register(sys.exitfunc)\n\nsys.exitfunc = _run_handlers\n", "path": "pwnlib/atexit.py"}], "after_files": [{"content": "\"\"\"\nReplacement for the Python standard library's atexit.py.\n\nWhereas the standard :mod:`atexit` module only defines :func:`atexit.register`,\nthis replacement module also defines :func:`unregister`.\n\nThis module also fixes a the issue that exceptions raised by an exit handler is\nprinted twice when the standard :mod:`atexit` is used.\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\n\nimport sys\nimport threading\nimport traceback\nimport atexit as std_atexit\n\nfrom pwnlib.context import context\n\n__all__ = ['register', 'unregister']\n\n_lock = threading.Lock()\n_ident = 0\n_handlers = {}\n\ndef register(func, *args, **kwargs):\n    \"\"\"register(func, *args, **kwargs)\n\n    Registers a function to be called on program termination.  The function will\n    be called with positional arguments `args` and keyword arguments `kwargs`,\n    i.e. ``func(*args, **kwargs)``.  The current `context` is recorded and will\n    be the one used when the handler is run.\n\n    E.g. to suppress logging output from an exit-handler one could write::\n\n      with context.local(log_level = 'error'):\n        atexit.register(handler)\n\n    An identifier is returned which can be used to unregister the exit-handler.\n\n    This function can be used as a decorator::\n\n      @atexit.register\n      def handler():\n        ...\n\n    Notice however that this will bind ``handler`` to the identifier and not the\n    actual exit-handler.  The exit-handler can then be unregistered with::\n\n      atexit.unregister(handler)\n\n    This function is thread safe.\n\n    \"\"\"\n    global _ident\n    with _lock:\n        ident = _ident\n        _ident += 1\n    _handlers[ident] = (func, args, kwargs, vars(context))\n    return ident\n\ndef unregister(ident):\n    \"\"\"unregister(ident)\n\n    Remove the exit-handler identified by `ident` from the list of registered\n    handlers.  If `ident` isn't registered this is a no-op.\n    \"\"\"\n    if ident in _handlers:\n        del _handlers[ident]\n\ndef _run_handlers():\n    \"\"\"_run_handlers()\n\n    Run registered exit-handlers.  They run in the reverse order of which they\n    were registered.\n\n    If a handler raises an exception, it will be printed but nothing else\n    happens, i.e. other handlers will be run and `sys.excepthook` will not be\n    called for that reason.\n    \"\"\"\n    context.clear()\n    for _ident, (func, args, kwargs, ctx) in \\\n        sorted(_handlers.items(), reverse = True):\n        try:\n            with context.local(**ctx):\n                func(*args, **kwargs)\n        except SystemExit:\n            pass\n        except Exception:\n            # extract the current exception and rewind the traceback to where it\n            # originated\n            typ, val, tb = sys.exc_info()\n            traceback.print_exception(typ, val, tb.tb_next)\n\n# if there's already an exitfunc registered be sure to run that too\nif hasattr(sys, \"exitfunc\"):\n    register(sys.exitfunc)\n\nif sys.version_info[0] < 3:\n    sys.exitfunc = _run_handlers\nelse:\n    std_atexit.register(_run_handlers)\n\n", "path": "pwnlib/atexit.py"}]}