We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Sys.module needs to be copied before being iterated over
#### Description
According to https://docs.python.org/3/library/sys.html#sys.modules, one needs to copy `sys.module` to iterate over it. This function [in this file](https://github.com/lmfit/lmfit-py/blob/8781a2dc33288b25fae6f3139595402155e4968b/lmfit/jsonutils.py#L27) does not follow this advice.

It should be replaced by:
```
def find_importer(obj):
    """Find importer of an object."""
    oname = obj.__name__
    for modname, module in sys.modules.copy().items():
        if modname.startswith('__main__'):
            continue
        t = getattr(module, oname, None)
        if t is obj:
            return modname
    return None
```

Please note `sys.modules.items()` -> `sys.modules.copy().items()`

###### A Minimal, Complete, and Verifiable example
I ran into `RuntimeError: dictionary changed size during iteration` with a large project using lmfit. The solution indicated in the python docs solves it. It is not immediate to create an MVE and I hope the python docs is enough to convince to implement the change.

###### Error message:
<!-- If any, paste the *full* error message inside a code block (starting from line Traceback) -->

```
...

obj = <function test_serde_lmfit_modelresult_with_error.<locals>.custom_error at 0x308a292d0>

    def find_importer(obj):
        """Find importer of an object."""
        oname = obj.__name__
>       for modname, module in sys.modules.items():
E       RuntimeError: dictionary changed size during iteration

/.../lib/python3.10/site-packages/lmfit/jsonutils.py:27: RuntimeError
```

###### Version information
Python: 3.10.14 (main, Mar 19 2024, 21:46:16) [Clang 15.0.0 (clang-1500.3.9.4)]

lmfit: 1.3.0, scipy: 1.11.4, numpy: 1.26.3,asteval: 0.9.32, uncertainties: 3.1.6###### Link(s)
<!-- If you started a discussion on the lmfit mailing list, discussion page, or Stack Overflow, please provide the relevant link(s) -->


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lmfit/jsonutils.py`
Content:
```
1 """JSON utilities."""
2 
3 from base64 import b64decode, b64encode
4 from io import StringIO
5 import sys
6 import warnings
7 
8 import dill
9 import numpy as np
10 import uncertainties
11 
12 HAS_DILL = True
13 
14 try:
15     from pandas import DataFrame, Series, read_json
16 except ImportError:
17     DataFrame = Series = type(NotImplemented)
18     read_json = None
19 
20 
21 pyvers = f'{sys.version_info.major}.{sys.version_info.minor}'
22 
23 
24 def find_importer(obj):
25     """Find importer of an object."""
26     oname = obj.__name__
27     for modname, module in sys.modules.items():
28         if modname.startswith('__main__'):
29             continue
30         t = getattr(module, oname, None)
31         if t is obj:
32             return modname
33     return None
34 
35 
36 def import_from(modulepath, objectname):
37     """Wrapper for __import__ for nested objects."""
38     path = modulepath.split('.')
39     top = path.pop(0)
40     parent = __import__(top)
41     while len(path) > 0:
42         parent = getattr(parent, path.pop(0))
43     return getattr(parent, objectname)
44 
45 
46 def encode4js(obj):
47     """Prepare an object for JSON encoding.
48 
49     It has special handling for many Python types, including:
50     - pandas DataFrames and Series
51     - NumPy ndarrays
52     - complex numbers
53 
54     """
55     if isinstance(obj, DataFrame):
56         return dict(__class__='PDataFrame', value=obj.to_json())
57     if isinstance(obj, Series):
58         return dict(__class__='PSeries', value=obj.to_json())
59     if isinstance(obj, uncertainties.core.AffineScalarFunc):
60         return dict(__class__='UFloat', val=obj.nominal_value, err=obj.std_dev)
61     if isinstance(obj, np.ndarray):
62         if 'complex' in obj.dtype.name:
63             val = [(obj.real).tolist(), (obj.imag).tolist()]
64         elif obj.dtype.name == 'object':
65             val = [encode4js(item) for item in obj]
66         else:
67             val = obj.flatten().tolist()
68         return dict(__class__='NDArray', __shape__=obj.shape,
69                     __dtype__=obj.dtype.name, value=val)
70     if isinstance(obj, float):
71         return float(obj)
72     if isinstance(obj, int):
73         return int(obj)
74     if isinstance(obj, str):
75         try:
76             return str(obj)
77         except UnicodeError:
78             return obj
79     if isinstance(obj, complex):
80         return dict(__class__='Complex', value=(obj.real, obj.imag))
81     if isinstance(obj, (tuple, list)):
82         ctype = 'List'
83         if isinstance(obj, tuple):
84             ctype = 'Tuple'
85         val = [encode4js(item) for item in obj]
86         return dict(__class__=ctype, value=val)
87     if isinstance(obj, dict):
88         out = dict(__class__='Dict')
89         for key, val in obj.items():
90             out[encode4js(key)] = encode4js(val)
91         return out
92     if callable(obj):
93         value = str(b64encode(dill.dumps(obj)), 'utf-8')
94         return dict(__class__='Callable', __name__=obj.__name__,
95                     pyversion=pyvers, value=value,
96                     importer=find_importer(obj))
97     return obj
98 
99 
100 def decode4js(obj):
101     """Return decoded Python object from encoded object."""
102     if not isinstance(obj, dict):
103         return obj
104     out = obj
105     classname = obj.pop('__class__', None)
106     if classname is None and isinstance(obj, dict):
107         classname = 'dict'
108     if classname is None:
109         return obj
110     if classname == 'Complex':
111         out = obj['value'][0] + 1j*obj['value'][1]
112     elif classname in ('List', 'Tuple'):
113         out = []
114         for item in obj['value']:
115             out.append(decode4js(item))
116         if classname == 'Tuple':
117             out = tuple(out)
118     elif classname == 'NDArray':
119         if obj['__dtype__'].startswith('complex'):
120             re = np.fromiter(obj['value'][0], dtype='double')
121             im = np.fromiter(obj['value'][1], dtype='double')
122             out = re + 1j*im
123         elif obj['__dtype__'].startswith('object'):
124             val = [decode4js(v) for v in obj['value']]
125             out = np.array(val, dtype=obj['__dtype__'])
126         else:
127             out = np.fromiter(obj['value'], dtype=obj['__dtype__'])
128         out.shape = obj['__shape__']
129     elif classname == 'PDataFrame' and read_json is not None:
130         out = read_json(StringIO(obj['value']))
131     elif classname == 'PSeries' and read_json is not None:
132         out = read_json(StringIO(obj['value']), typ='series')
133     elif classname == 'UFloat':
134         out = uncertainties.ufloat(obj['val'], obj['err'])
135     elif classname == 'Callable':
136         out = obj['__name__']
137         try:
138             out = import_from(obj['importer'], out)
139             unpacked = True
140         except (ImportError, AttributeError):
141             unpacked = False
142         if not unpacked:
143             spyvers = obj.get('pyversion', '?')
144             if not pyvers == spyvers:
145                 msg = f"Could not unpack dill-encoded callable '{out}', saved with Python version {spyvers}"
146                 warnings.warn(msg)
147 
148             try:
149                 out = dill.loads(b64decode(obj['value']))
150             except RuntimeError:
151                 msg = f"Could not unpack dill-encoded callable '{out}`, saved with Python version {spyvers}"
152                 warnings.warn(msg)
153 
154     elif classname in ('Dict', 'dict'):
155         out = {}
156         for key, val in obj.items():
157             out[key] = decode4js(val)
158     return out
159
```
--- 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()

```

diff --git a/lmfit/jsonutils.py b/lmfit/jsonutils.py
--- a/lmfit/jsonutils.py
+++ b/lmfit/jsonutils.py
@@ -24,7 +24,7 @@
 def find_importer(obj):
     """Find importer of an object."""
     oname = obj.__name__
-    for modname, module in sys.modules.items():
+    for modname, module in sys.modules.copy().items():
         if modname.startswith('__main__'):
             continue
         t = getattr(module, oname, None)

{"golden_diff": "diff --git a/lmfit/jsonutils.py b/lmfit/jsonutils.py\n--- a/lmfit/jsonutils.py\n+++ b/lmfit/jsonutils.py\n@@ -24,7 +24,7 @@\n def find_importer(obj):\n     \"\"\"Find importer of an object.\"\"\"\n     oname = obj.__name__\n-    for modname, module in sys.modules.items():\n+    for modname, module in sys.modules.copy().items():\n         if modname.startswith('__main__'):\n             continue\n         t = getattr(module, oname, None)\n", "issue": "Sys.module needs to be copied before being iterated over\n#### Description\r\nAccording to https://docs.python.org/3/library/sys.html#sys.modules, one needs to copy `sys.module` to iterate over it. This function [in this file](https://github.com/lmfit/lmfit-py/blob/8781a2dc33288b25fae6f3139595402155e4968b/lmfit/jsonutils.py#L27) does not follow this advice.\r\n\r\nIt should be replaced by:\r\n```\r\ndef find_importer(obj):\r\n    \"\"\"Find importer of an object.\"\"\"\r\n    oname = obj.__name__\r\n    for modname, module in sys.modules.copy().items():\r\n        if modname.startswith('__main__'):\r\n            continue\r\n        t = getattr(module, oname, None)\r\n        if t is obj:\r\n            return modname\r\n    return None\r\n```\r\n\r\nPlease note `sys.modules.items()` -> `sys.modules.copy().items()`\r\n\r\n###### A Minimal, Complete, and Verifiable example\r\nI ran into `RuntimeError: dictionary changed size during iteration` with a large project using lmfit. The solution indicated in the python docs solves it. It is not immediate to create an MVE and I hope the python docs is enough to convince to implement the change.\r\n\r\n###### Error message:\r\n<!-- If any, paste the *full* error message inside a code block (starting from line Traceback) -->\r\n\r\n```\r\n...\r\n\r\nobj = <function test_serde_lmfit_modelresult_with_error.<locals>.custom_error at 0x308a292d0>\r\n\r\n    def find_importer(obj):\r\n        \"\"\"Find importer of an object.\"\"\"\r\n        oname = obj.__name__\r\n>       for modname, module in sys.modules.items():\r\nE       RuntimeError: dictionary changed size during iteration\r\n\r\n/.../lib/python3.10/site-packages/lmfit/jsonutils.py:27: RuntimeError\r\n```\r\n\r\n###### Version information\r\nPython: 3.10.14 (main, Mar 19 2024, 21:46:16) [Clang 15.0.0 (clang-1500.3.9.4)]\r\n\r\nlmfit: 1.3.0, scipy: 1.11.4, numpy: 1.26.3,asteval: 0.9.32, uncertainties: 3.1.6###### Link(s)\r\n<!-- If you started a discussion on the lmfit mailing list, discussion page, or Stack Overflow, please provide the relevant link(s) -->\r\n\n", "before_files": [{"content": "\"\"\"JSON utilities.\"\"\"\n\nfrom base64 import b64decode, b64encode\nfrom io import StringIO\nimport sys\nimport warnings\n\nimport dill\nimport numpy as np\nimport uncertainties\n\nHAS_DILL = True\n\ntry:\n    from pandas import DataFrame, Series, read_json\nexcept ImportError:\n    DataFrame = Series = type(NotImplemented)\n    read_json = None\n\n\npyvers = f'{sys.version_info.major}.{sys.version_info.minor}'\n\n\ndef find_importer(obj):\n    \"\"\"Find importer of an object.\"\"\"\n    oname = obj.__name__\n    for modname, module in sys.modules.items():\n        if modname.startswith('__main__'):\n            continue\n        t = getattr(module, oname, None)\n        if t is obj:\n            return modname\n    return None\n\n\ndef import_from(modulepath, objectname):\n    \"\"\"Wrapper for __import__ for nested objects.\"\"\"\n    path = modulepath.split('.')\n    top = path.pop(0)\n    parent = __import__(top)\n    while len(path) > 0:\n        parent = getattr(parent, path.pop(0))\n    return getattr(parent, objectname)\n\n\ndef encode4js(obj):\n    \"\"\"Prepare an object for JSON encoding.\n\n    It has special handling for many Python types, including:\n    - pandas DataFrames and Series\n    - NumPy ndarrays\n    - complex numbers\n\n    \"\"\"\n    if isinstance(obj, DataFrame):\n        return dict(__class__='PDataFrame', value=obj.to_json())\n    if isinstance(obj, Series):\n        return dict(__class__='PSeries', value=obj.to_json())\n    if isinstance(obj, uncertainties.core.AffineScalarFunc):\n        return dict(__class__='UFloat', val=obj.nominal_value, err=obj.std_dev)\n    if isinstance(obj, np.ndarray):\n        if 'complex' in obj.dtype.name:\n            val = [(obj.real).tolist(), (obj.imag).tolist()]\n        elif obj.dtype.name == 'object':\n            val = [encode4js(item) for item in obj]\n        else:\n            val = obj.flatten().tolist()\n        return dict(__class__='NDArray', __shape__=obj.shape,\n                    __dtype__=obj.dtype.name, value=val)\n    if isinstance(obj, float):\n        return float(obj)\n    if isinstance(obj, int):\n        return int(obj)\n    if isinstance(obj, str):\n        try:\n            return str(obj)\n        except UnicodeError:\n            return obj\n    if isinstance(obj, complex):\n        return dict(__class__='Complex', value=(obj.real, obj.imag))\n    if isinstance(obj, (tuple, list)):\n        ctype = 'List'\n        if isinstance(obj, tuple):\n            ctype = 'Tuple'\n        val = [encode4js(item) for item in obj]\n        return dict(__class__=ctype, value=val)\n    if isinstance(obj, dict):\n        out = dict(__class__='Dict')\n        for key, val in obj.items():\n            out[encode4js(key)] = encode4js(val)\n        return out\n    if callable(obj):\n        value = str(b64encode(dill.dumps(obj)), 'utf-8')\n        return dict(__class__='Callable', __name__=obj.__name__,\n                    pyversion=pyvers, value=value,\n                    importer=find_importer(obj))\n    return obj\n\n\ndef decode4js(obj):\n    \"\"\"Return decoded Python object from encoded object.\"\"\"\n    if not isinstance(obj, dict):\n        return obj\n    out = obj\n    classname = obj.pop('__class__', None)\n    if classname is None and isinstance(obj, dict):\n        classname = 'dict'\n    if classname is None:\n        return obj\n    if classname == 'Complex':\n        out = obj['value'][0] + 1j*obj['value'][1]\n    elif classname in ('List', 'Tuple'):\n        out = []\n        for item in obj['value']:\n            out.append(decode4js(item))\n        if classname == 'Tuple':\n            out = tuple(out)\n    elif classname == 'NDArray':\n        if obj['__dtype__'].startswith('complex'):\n            re = np.fromiter(obj['value'][0], dtype='double')\n            im = np.fromiter(obj['value'][1], dtype='double')\n            out = re + 1j*im\n        elif obj['__dtype__'].startswith('object'):\n            val = [decode4js(v) for v in obj['value']]\n            out = np.array(val, dtype=obj['__dtype__'])\n        else:\n            out = np.fromiter(obj['value'], dtype=obj['__dtype__'])\n        out.shape = obj['__shape__']\n    elif classname == 'PDataFrame' and read_json is not None:\n        out = read_json(StringIO(obj['value']))\n    elif classname == 'PSeries' and read_json is not None:\n        out = read_json(StringIO(obj['value']), typ='series')\n    elif classname == 'UFloat':\n        out = uncertainties.ufloat(obj['val'], obj['err'])\n    elif classname == 'Callable':\n        out = obj['__name__']\n        try:\n            out = import_from(obj['importer'], out)\n            unpacked = True\n        except (ImportError, AttributeError):\n            unpacked = False\n        if not unpacked:\n            spyvers = obj.get('pyversion', '?')\n            if not pyvers == spyvers:\n                msg = f\"Could not unpack dill-encoded callable '{out}', saved with Python version {spyvers}\"\n                warnings.warn(msg)\n\n            try:\n                out = dill.loads(b64decode(obj['value']))\n            except RuntimeError:\n                msg = f\"Could not unpack dill-encoded callable '{out}`, saved with Python version {spyvers}\"\n                warnings.warn(msg)\n\n    elif classname in ('Dict', 'dict'):\n        out = {}\n        for key, val in obj.items():\n            out[key] = decode4js(val)\n    return out\n", "path": "lmfit/jsonutils.py"}], "after_files": [{"content": "\"\"\"JSON utilities.\"\"\"\n\nfrom base64 import b64decode, b64encode\nfrom io import StringIO\nimport sys\nimport warnings\n\nimport dill\nimport numpy as np\nimport uncertainties\n\nHAS_DILL = True\n\ntry:\n    from pandas import DataFrame, Series, read_json\nexcept ImportError:\n    DataFrame = Series = type(NotImplemented)\n    read_json = None\n\n\npyvers = f'{sys.version_info.major}.{sys.version_info.minor}'\n\n\ndef find_importer(obj):\n    \"\"\"Find importer of an object.\"\"\"\n    oname = obj.__name__\n    for modname, module in sys.modules.copy().items():\n        if modname.startswith('__main__'):\n            continue\n        t = getattr(module, oname, None)\n        if t is obj:\n            return modname\n    return None\n\n\ndef import_from(modulepath, objectname):\n    \"\"\"Wrapper for __import__ for nested objects.\"\"\"\n    path = modulepath.split('.')\n    top = path.pop(0)\n    parent = __import__(top)\n    while len(path) > 0:\n        parent = getattr(parent, path.pop(0))\n    return getattr(parent, objectname)\n\n\ndef encode4js(obj):\n    \"\"\"Prepare an object for JSON encoding.\n\n    It has special handling for many Python types, including:\n    - pandas DataFrames and Series\n    - NumPy ndarrays\n    - complex numbers\n\n    \"\"\"\n    if isinstance(obj, DataFrame):\n        return dict(__class__='PDataFrame', value=obj.to_json())\n    if isinstance(obj, Series):\n        return dict(__class__='PSeries', value=obj.to_json())\n    if isinstance(obj, uncertainties.core.AffineScalarFunc):\n        return dict(__class__='UFloat', val=obj.nominal_value, err=obj.std_dev)\n    if isinstance(obj, np.ndarray):\n        if 'complex' in obj.dtype.name:\n            val = [(obj.real).tolist(), (obj.imag).tolist()]\n        elif obj.dtype.name == 'object':\n            val = [encode4js(item) for item in obj]\n        else:\n            val = obj.flatten().tolist()\n        return dict(__class__='NDArray', __shape__=obj.shape,\n                    __dtype__=obj.dtype.name, value=val)\n    if isinstance(obj, float):\n        return float(obj)\n    if isinstance(obj, int):\n        return int(obj)\n    if isinstance(obj, str):\n        try:\n            return str(obj)\n        except UnicodeError:\n            return obj\n    if isinstance(obj, complex):\n        return dict(__class__='Complex', value=(obj.real, obj.imag))\n    if isinstance(obj, (tuple, list)):\n        ctype = 'List'\n        if isinstance(obj, tuple):\n            ctype = 'Tuple'\n        val = [encode4js(item) for item in obj]\n        return dict(__class__=ctype, value=val)\n    if isinstance(obj, dict):\n        out = dict(__class__='Dict')\n        for key, val in obj.items():\n            out[encode4js(key)] = encode4js(val)\n        return out\n    if callable(obj):\n        value = str(b64encode(dill.dumps(obj)), 'utf-8')\n        return dict(__class__='Callable', __name__=obj.__name__,\n                    pyversion=pyvers, value=value,\n                    importer=find_importer(obj))\n    return obj\n\n\ndef decode4js(obj):\n    \"\"\"Return decoded Python object from encoded object.\"\"\"\n    if not isinstance(obj, dict):\n        return obj\n    out = obj\n    classname = obj.pop('__class__', None)\n    if classname is None and isinstance(obj, dict):\n        classname = 'dict'\n    if classname is None:\n        return obj\n    if classname == 'Complex':\n        out = obj['value'][0] + 1j*obj['value'][1]\n    elif classname in ('List', 'Tuple'):\n        out = []\n        for item in obj['value']:\n            out.append(decode4js(item))\n        if classname == 'Tuple':\n            out = tuple(out)\n    elif classname == 'NDArray':\n        if obj['__dtype__'].startswith('complex'):\n            re = np.fromiter(obj['value'][0], dtype='double')\n            im = np.fromiter(obj['value'][1], dtype='double')\n            out = re + 1j*im\n        elif obj['__dtype__'].startswith('object'):\n            val = [decode4js(v) for v in obj['value']]\n            out = np.array(val, dtype=obj['__dtype__'])\n        else:\n            out = np.fromiter(obj['value'], dtype=obj['__dtype__'])\n        out.shape = obj['__shape__']\n    elif classname == 'PDataFrame' and read_json is not None:\n        out = read_json(StringIO(obj['value']))\n    elif classname == 'PSeries' and read_json is not None:\n        out = read_json(StringIO(obj['value']), typ='series')\n    elif classname == 'UFloat':\n        out = uncertainties.ufloat(obj['val'], obj['err'])\n    elif classname == 'Callable':\n        out = obj['__name__']\n        try:\n            out = import_from(obj['importer'], out)\n            unpacked = True\n        except (ImportError, AttributeError):\n            unpacked = False\n        if not unpacked:\n            spyvers = obj.get('pyversion', '?')\n            if not pyvers == spyvers:\n                msg = f\"Could not unpack dill-encoded callable '{out}', saved with Python version {spyvers}\"\n                warnings.warn(msg)\n\n            try:\n                out = dill.loads(b64decode(obj['value']))\n            except RuntimeError:\n                msg = f\"Could not unpack dill-encoded callable '{out}`, saved with Python version {spyvers}\"\n                warnings.warn(msg)\n\n    elif classname in ('Dict', 'dict'):\n        out = {}\n        for key, val in obj.items():\n            out[key] = decode4js(val)\n    return out\n", "path": "lmfit/jsonutils.py"}]}