Repository: Infinidat/munch

Issue:
munchify broken?
Here is a minimal working example, which is broken.   Is this already fixed?

`munchify((1,2,[{'A': 'B'}])) 
Result: (1, 2, [Munch({'A': 'B'}), Munch({'A': 'B'})])
Expected:  (1, 2, [Munch({'A': 'B'})])`

Discussion:

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3880744..64954bd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -40,11 +40,15 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v3
-    - uses: actions/setup-python@v4
-    # Install the testing dependencies from setup.cfg, including pylint
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v4
+      with:
+        python-version: "3.10"
     - name: Upgrade pip
       run: python -m pip install --upgrade pip
-    - name: Upgrade build tools
+    - name: Upgrade build tools and pytest
       run: pip install --upgrade build wheel setuptools
-    - run: pip install .[testing]
-    - run: pylint munch setup.py tests
+    - name: Install dependencies
+      run: pip install ".[testing,yaml]"
+    - name: Lint
+      run: pylint munch setup.py tests
diff --git a/munch/__init__.py b/munch/__init__.py
index 9d5cde1..c331da0 100644
--- a/munch/__init__.py
+++ b/munch/__init__.py
@@ -442,16 +442,21 @@ def munchify(x, factory=Munch):
     seen = dict()
 
     def munchify_cycles(obj):
+        partial, already_seen = pre_munchify_cycles(obj)
+        if already_seen:
+            return partial
+        return post_munchify(partial, obj)
+
+    def pre_munchify_cycles(obj):
         # If we've already begun munchifying obj, just return the already-created munchified obj
         try:
-            return seen[id(obj)]
+            return seen[id(obj)], True
         except KeyError:
             pass
 
         # Otherwise, first partly munchify obj (but without descending into any lists or dicts) and save that
         seen[id(obj)] = partial = pre_munchify(obj)
-        # Then finish munchifying lists and dicts inside obj (reusing munchified obj if cycles are encountered)
-        return post_munchify(partial, obj)
+        return partial, False
 
     def pre_munchify(obj):
         # Here we return a skeleton of munchified obj, which is enough to save for later (in case
@@ -462,7 +467,7 @@ def munchify(x, factory=Munch):
             return type(obj)()
         elif isinstance(obj, tuple):
             type_factory = getattr(obj, "_make", type(obj))
-            return type_factory(munchify_cycles(item) for item in obj)
+            return type_factory(pre_munchify_cycles(item)[0] for item in obj)
         else:
             return obj
 


Test patch:
diff --git a/tests/test_munch.py b/tests/test_munch.py
index 9114e3d..c80b757 100644
--- a/tests/test_munch.py
+++ b/tests/test_munch.py
@@ -557,3 +557,17 @@ def test_get_default_value(munch_obj):
     munch_cls = type(munch_obj)
     kwargs = {} if munch_cls != DefaultFactoryMunch else {"default_factory": munch_obj.default_factory}
     munch_cls.fromDict(data, **kwargs)
+
+
+def test_munchify_tuple_list():
+    data = ([{'A': 'B'}],)
+    actual = munchify(data)
+    expected = ([Munch(A='B')],)
+    assert actual == expected
+
+
+def test_munchify_tuple_list_more_elements():
+    data = (1, 2, [{'A': 'B'}])
+    actual = munchify(data)
+    expected = (1, 2, [Munch({'A': 'B'})])
+    assert actual == expected