Repository: conan-io/conan

Issue:
[bug] Conan 1 is broken for Python 3.12
### Environment details

* Operating System+version: Fedora Linux 39
* Compiler+version: GCC 13.2.1
* Conan version: 1.61.0
* Python version: 3.12.0rc3

Note: The `imp` module was removed in Python 3.12. See [here](https://docs.python.org/3/library/imp.html).

### Steps to reproduce

1. `python3 -m pip install conan==1.61`
2. `conan --version`

### Logs

```
conan --version
Traceback (most recent call last):
  File "/home/jordan/.local/bin/conan", line 5, in <module>
    from conans.conan import run
  File "/home/jordan/.local/lib/python3.12/site-packages/conans/conan.py", line 7, in <module>
    from conans.client.command import main
  File "/home/jordan/.local/lib/python3.12/site-packages/conans/client/command.py", line 14, in <module>
    from conans.client.conan_api import Conan, default_manifest_folder, _make_abs_path, ProfileData
  File "/home/jordan/.local/lib/python3.12/site-packages/conans/client/conan_api.py", line 13, in <module>
    from conans.client.cmd.create import create
  File "/home/jordan/.local/lib/python3.12/site-packages/conans/client/cmd/create.py", line 3, in <module>
    from conans.client.cmd.test import install_build_and_test
  File "/home/jordan/.local/lib/python3.12/site-packages/conans/client/cmd/test.py", line 6, in <module>
    from conans.client.manager import deps_install
  File "/home/jordan/.local/lib/python3.12/site-packages/conans/client/manager.py", line 7, in <module>
    from conans.client.installer import BinaryInstaller, call_system_requirements
  File "/home/jordan/.local/lib/python3.12/site-packages/conans/client/installer.py", line 27, in <module>
    from conans.model.graph_lock import GraphLockFile
  File "/home/jordan/.local/lib/python3.12/site-packages/conans/model/graph_lock.py", line 7, in <module>
    from conans.client.graph.python_requires import PyRequires
  File "/home/jordan/.local/lib/python3.12/site-packages/conans/client/graph/python_requires.py", line 5, in <module>
    from conans.client.loader import parse_conanfile
  File "/home/jordan/.local/lib/python3.12/site-packages/conans/client/loader.py", line 2, in <module>
    import imp
ModuleNotFoundError: No module named 'imp'
```


Discussion:
This was fixed for Conan V2 in #10127 but the fix was never backported to Conan V1.
Hi! Thanks a lot for your report. We didn't originally backport this for backwards compatibility reasons - Some corner cases of imp's usage differ from importlib's, so we stuck with the old deprecated api around.

Our plan is to release 1.62 with a check for Python 3.12 to fallback to the newer importlib - Note that this might have unintended differences in corer cases when running under 3.12 in Conan 1 compared to previous Python's versions
@RubenRBS Thanks! I've patched Conan 1 locally to use `importlib` and I haven't been experiencing any problems. I'll let you know if I run into anything. Unfortunately, we're pretty much blocked from upgrading to Conan V2 until the Qt Conan package is upgraded to support it.
Thanks! Hopefully the transition to importlib under Python 3.12 goes smoothly then :)

(Thanks for the qt ping, I've asked the team to try to prioritize it if possible to try to unblock you)

Resolving patch:
diff --git a/conans/client/command.py b/conans/client/command.py
--- a/conans/client/command.py
+++ b/conans/client/command.py
@@ -4,6 +4,7 @@
 import os
 import signal
 import sys
+import textwrap
 from argparse import ArgumentError
 from difflib import get_close_matches
 
@@ -318,6 +319,7 @@ def create(self, *args):
         that the package has been created correctly. Check 'conan test' command
         to know more about 'test_folder' project.
         """
+        self._warn_conan_version()
         parser = argparse.ArgumentParser(description=self.create.__doc__,
                                          prog="conan create",
                                          formatter_class=SmartFormatter)
@@ -469,6 +471,7 @@ def install(self, *args):
         the package is installed, Conan will write the files for the specified
         generators.
         """
+        self._warn_conan_version()
         parser = argparse.ArgumentParser(description=self.install.__doc__,
                                          prog="conan install",
                                          formatter_class=SmartFormatter)
@@ -859,7 +862,7 @@ def build(self, *args):
         using a build helper, like CMake(), the --package-folder will be
         configured as the destination folder for the install step.
         """
-
+        self._warn_conan_version()
         parser = argparse.ArgumentParser(description=self.build.__doc__,
                                          prog="conan build",
                                          formatter_class=SmartFormatter)
@@ -2177,6 +2180,12 @@ def _warn_python_version(self):
                               front=Color.BRIGHT_RED)
             self._out.writeln("*"*width, front=Color.BRIGHT_RED)
 
+    def _warn_conan_version(self):
+        width = 70
+        self._out.writeln(textwrap.fill("Conan 1 is on a deprecation path, "
+                                        "please consider migrating to Conan 2", width),
+                          front=Color.BRIGHT_YELLOW)
+
     def run(self, *args):
         """HIDDEN: entry point for executing commands, dispatcher to class
         methods
diff --git a/conans/client/loader.py b/conans/client/loader.py
--- a/conans/client/loader.py
+++ b/conans/client/loader.py
@@ -1,5 +1,4 @@
 import fnmatch
-import imp
 import inspect
 import os
 import re
@@ -40,6 +39,9 @@ def __init__(self, runner, output, python_requires, generator_manager=None, pyre
         sys.modules["conans"].python_requires = python_requires
         self._cached_conanfile_classes = {}
         self._requester = requester
+        if sys.version_info.major >= 3 and sys.version_info.minor >= 12:
+            from importlib import invalidate_caches
+            invalidate_caches()
 
     def load_basic(self, conanfile_path, lock_python_requires=None, user=None, channel=None,
                    display=""):
@@ -439,8 +441,15 @@ def _parse_conanfile(conan_file_path):
             old_dont_write_bytecode = sys.dont_write_bytecode
             try:
                 sys.dont_write_bytecode = True
-                # FIXME: imp is deprecated in favour of implib
-                loaded = imp.load_source(module_id, conan_file_path)
+                # imp is deprecated in favour of importlib, removed in 3.12
+                if sys.version_info.major >= 3 and sys.version_info.minor >= 12:
+                    from importlib import util as imp_util
+                    spec = imp_util.spec_from_file_location(module_id, conan_file_path)
+                    loaded = imp_util.module_from_spec(spec)
+                    spec.loader.exec_module(loaded)
+                else:
+                    import imp
+                    loaded = imp.load_source(module_id, conan_file_path)
                 sys.dont_write_bytecode = old_dont_write_bytecode
             except ImportError:
                 version_txt = _get_required_conan_version_without_loading(conan_file_path)


Test patch: