Repository: pyinstaller/pyinstaller

Issue:
Marshal error passing `modulegraph.Alias()` to `find_binary_dependencies()`
<!--
Welcome to the PyInstaller issue tracker! Before creating an issue, please heed the following:

1. This tracker should only be used to report bugs and request features / enhancements to PyInstaller
    - For questions and general support, use the discussions forum.
2. Use the search function before creating a new issue. Duplicates will be closed and directed to
   the original discussion.
3. When making a bug report, make sure you provide all required information. The easier it is for
   maintainers to reproduce, the faster it'll be fixed.
-->

<!-- +++ ONLY TEXT +++ DO NOT POST IMAGES +++ -->

## Description of the issue

Modulegraph has an internal `Alias()` class which simply inherits from `str()` to label known module name compatibility aliases/redirects. If one of these Alias instances find their way anywhere near `PyInstaller.isolated` then a build error occurs because marshal refuses to serialize them.

### Context information (for bug reports)

* Output of `pyinstaller --version`: Anything >= 5.4
* Version of Python: 3.11
* Platform: Windows only
* How you installed Python: python.org

### A minimal example program which shows the error

Simply try to build:
```python
import six.moves.tkinter
```

### Stacktrace / full error message


```python
$ python -m PyInstaller test.py
2874 INFO: PyInstaller: 5.9.0
2874 INFO: Python: 3.11.0
2905 INFO: Platform: Windows-10-10.0.19044-SP0
2905 INFO: wrote C:\Users\scsbuild\PySolFC\test.spec
2905 INFO: UPX is not available.
2921 INFO: Extending PYTHONPATH with paths
['C:\\Users\\scsbuild\\PySolFC']
pygame 2.3.0 (SDL 2.24.2, Python 3.11.0)
Hello from the pygame community. https://www.pygame.org/contribute.html
3515 INFO: checking Analysis
3515 INFO: Building Analysis because Analysis-00.toc is non existent
3515 INFO: Initializing module dependency graph...
3515 INFO: Caching module graph hooks...
3546 INFO: Analyzing base_library.zip ...
6506 INFO: Loading module hook 'hook-heapq.py' from 'C:\\Users\\scsbuild\\PySolFC\\env\\Lib\\site-packages\\PyInstaller\\hooks'...
6641 INFO: Loading module hook 'hook-encodings.py' from 'C:\\Users\\scsbuild\\PySolFC\\env\\Lib\\site-packages\\PyInstaller\\hooks'...
8857 INFO: Loading module hook 'hook-pickle.py' from 'C:\\Users\\scsbuild\\PySolFC\\env\\Lib\\site-packages\\PyInstaller\\hooks'...
11094 INFO: Caching module dependency graph...
11245 INFO: running Analysis Analysis-00.toc
11249 INFO: Adding Microsoft.Windows.Common-Controls to dependent assemblies of final executable
  required by C:\Python\python.exe
11450 INFO: Analyzing C:\Users\scsbuild\PySolFC\test.py
11483 INFO: Processing pre-safe import module hook six.moves from 'C:\\Users\\scsbuild\\PySolFC\\env\\Lib\\site-packages\\PyInstaller\\hooks\\pre_safe_import_module\\hook-six.moves.py'.
11773 INFO: Processing module hooks...
11773 INFO: Loading module hook 'hook-_tkinter.py' from 'C:\\Users\\scsbuild\\PySolFC\\env\\Lib\\site-packages\\PyInstaller\\hooks'...
11780 INFO: checking Tree
11827 INFO: checking Tree
11843 INFO: checking Tree
11859 INFO: Looking for ctypes DLLs
11859 INFO: Analyzing run-time hooks ...
11874 INFO: Including run-time hook 'C:\\Users\\scsbuild\\PySolFC\\env\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py'
11874 INFO: Including run-time hook 'C:\\Users\\scsbuild\\PySolFC\\env\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth__tkinter.py'
11887 INFO: Looking for dynamic libraries
Traceback (most recent call last):
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\isolated\_child.py", line 96, in <module>
    while run_next_command(read_fh, write_fh):
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\isolated\_child.py", line 51, in run_next_command
    args = loads(b64decode(read_fh.readline().strip()))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
EOFError: EOF read where object expectedTraceback (most recent call last):
  File "C:\Python\Lib\pdb.py", line 1768, in main
    pdb._run(target)
  File "C:\Python\Lib\pdb.py", line 1646, in _run
    self.run(target.code)
  File "C:\Python\Lib\bdb.py", line 597, in run
    exec(cmd, globals, locals)
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\__main__.py", line 198, in <module>
    run()
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\__main__.py", line 180, in run
    run_build(pyi_config, spec_file, **vars(args))
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\__main__.py", line 61, in run_build
    PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs)
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\building\build_main.py", line 983, in main
    build(specfile, distpath, workpath, clean_build)
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\building\build_main.py", line 905, in build
    exec(code, spec_namespace)
  File "C:\Users\scsbuild\PySolFC\test.spec", line 7, in <module>
    a = Analysis(
        ^^^^^^^^^
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\building\build_main.py", line 414, in __init__
    self.__postinit__()
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\building\datastruct.py", line 173, in __postinit__
    self.assemble()
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\building\build_main.py", line 701, in assemble
    isolated.call(find_binary_dependencies, list(self.binaries), self.binding_redirects, collected_packages)
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\isolated\_parent.py", line 372, in call
    return isolated.call(function, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\isolated\_parent.py", line 289, in call
    self._send(function.__code__, function.__defaults__, function.__kwdefaults__, args, kwargs)
  File "C:\Users\scsbuild\PySolFC\env\Lib\site-packages\PyInstaller\isolated\_parent.py", line 306, in _send
    self._write_handle.write(b64encode(dumps(object)))
                                       ^^^^^^^^^^^^^
ValueError: unmarshallable object
```

PDB-ing into that exception:

```python
(Pdb) object[2]
['importlib', 'importlib.metadata', 'importlib.resources', 'logging', 'email', 'encodings', 'collections', 'http', 'urllib', 're', 'tkinter']
(Pdb) object[2][-1]
'tkinter'
(Pdb) type(object[2][-1])
<class 'PyInstaller.lib.modulegraph.modulegraph.Alias'>
```


Discussion:
Maybe just a dumb `collected_packages = [str(i) for i in collected_packages]` in [here](https://github.com/pyinstaller/pyinstaller/blob/7230e8bbda4a35f8afee41ac77dae6ed2f1e1348/PyInstaller/building/build_main.py#L695)?
> Maybe just a dumb `collected_packages = [str(i) for i in collected_packages]` in [here](https://github.com/pyinstaller/pyinstaller/blob/7230e8bbda4a35f8afee41ac77dae6ed2f1e1348/PyInstaller/building/build_main.py#L695)?

Sounds good. But we should probably do a `str(node.identifier)` here:
https://github.com/pyinstaller/pyinstaller/blob/7230e8bbda4a35f8afee41ac77dae6ed2f1e1348/PyInstaller/depend/analysis.py#L843-L849

(or perhaps even ensure that `node.identifier` is always a proper `str`, although that's probably wasted effort at this point).


Resolving patch:
diff --git a/PyInstaller/depend/analysis.py b/PyInstaller/depend/analysis.py
--- a/PyInstaller/depend/analysis.py
+++ b/PyInstaller/depend/analysis.py
@@ -844,8 +844,10 @@ def get_collected_packages(self) -> list:
         """
         Return the list of collected python packages.
         """
+        # `node.identifier` might be an instance of `modulegraph.Alias`, hence explicit conversion to `str`.
         return [
-            node.identifier for node in self.iter_graph(start=self._top_script_node) if type(node).__name__ == 'Package'
+            str(node.identifier) for node in self.iter_graph(start=self._top_script_node)
+            if type(node).__name__ == 'Package'
         ]
 
 


Test patch: