Adjust `is_namespace()` to check `ModuleSpec.loader` (#2410)

This fixes inference when six.moves is imported.

Closes #2409
diff --git a/ChangeLog b/ChangeLog
index c611984..5e4f68f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -19,6 +19,13 @@
 
   Refs pylint-dev/pylint#9442
 
+* Make ``astroid.interpreter._import.util.is_namespace`` only consider modules
+  using a loader set to ``NamespaceLoader`` or ``None`` as namespaces.
+  This fixes a problem that ``six.moves`` brain was not effective if ``six.moves``
+  was already imported.
+
+  Closes #1107
+
 
 What's New in astroid 3.1.1?
 ============================
diff --git a/astroid/interpreter/_import/util.py b/astroid/interpreter/_import/util.py
index a8af9ec..511ec4f 100644
--- a/astroid/interpreter/_import/util.py
+++ b/astroid/interpreter/_import/util.py
@@ -12,6 +12,11 @@
 
 from astroid.const import IS_PYPY
 
+if sys.version_info >= (3, 11):
+    from importlib.machinery import NamespaceLoader
+else:
+    from importlib._bootstrap_external import _NamespaceLoader as NamespaceLoader
+
 
 @lru_cache(maxsize=4096)
 def is_namespace(modname: str) -> bool:
@@ -101,4 +106,7 @@
         found_spec is not None
         and found_spec.submodule_search_locations is not None
         and found_spec.origin is None
+        and (
+            found_spec.loader is None or isinstance(found_spec.loader, NamespaceLoader)
+        )
     )
diff --git a/tests/brain/test_six.py b/tests/brain/test_six.py
index c7c9db7..45a40e5 100644
--- a/tests/brain/test_six.py
+++ b/tests/brain/test_six.py
@@ -29,6 +29,8 @@
         six.moves.urllib_parse #@
         six.moves.urllib_error #@
         six.moves.urllib.request #@
+        from six.moves import StringIO
+        StringIO #@
         """
         )
         assert isinstance(ast_nodes, list)
@@ -64,6 +66,18 @@
         self.assertIsInstance(urlretrieve, nodes.FunctionDef)
         self.assertEqual(urlretrieve.qname(), "urllib.request.urlretrieve")
 
+        StringIO = next(ast_nodes[4].infer())
+        self.assertIsInstance(StringIO, nodes.ClassDef)
+        self.assertEqual(StringIO.qname(), "_io.StringIO")
+        self.assertTrue(StringIO.callable())
+
+    def test_attribute_access_with_six_moves_imported(self) -> None:
+        astroid.MANAGER.clear_cache()
+        astroid.MANAGER._mod_file_cache.clear()
+        import six.moves  # type: ignore[import]  # pylint: disable=import-outside-toplevel,unused-import,redefined-outer-name
+
+        self.test_attribute_access()
+
     def test_from_imports(self) -> None:
         ast_node = builder.extract_node(
             """